bluraybackup/bluraybackup.c

245 lines
5.5 KiB
C

#include <bluray.h>
#include <errno.h>
#include <filesystem.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
/* function declarations */
static void basename(char dst[], const char src[]);
static int copy_dir(const char path[]);
static int copy_file(const char src[], const char dst[]);
static void open_bluray(const char device[], const char keyfile[]);
static void print_help(void);
static void print_version(void);
static void print_usage(void);
/* variables */
static BLURAY *bluray;
void
basename(char dst[], const char src[])
{
int i;
size_t len, offset;
len = strlen(src);
for (i = len - 1; i >= 0; i--) {
offset = i + 1;
if (src[i] == '/' && offset < len && (len - offset) < 256) {
strcpy(dst, src + offset);
return;
}
}
if (len < 256)
strcpy(dst, src);
else
strcpy(dst, "");
}
int
copy_dir(const char path[])
{
int all_good, read;
struct bd_dir_s *dir;
BD_DIRENT *dirent;
char *new_path;
all_good = 1;
read = 0;
dir = bd_open_dir(bluray, path);
if (dir == NULL) {
fprintf(stderr, "Can't open Blu-ray dir %s.\n", path);
return 0;
}
if (strcmp(path, "") != 0 &&
mkdir(path, 0755) == -1 && errno != EEXIST) {
fprintf(stderr, "Can't create destination dir %s.\n", path);
return 0;
}
dirent = malloc(sizeof(BD_DIRENT));
if (dirent == NULL) {
fprintf(stderr, "Can't allocate memory for BD_DIRENT.\n");
dir->close(dir);
return 0;
}
do {
read = dir->read(dir, dirent);
if (read == -1) {
break;
// [libbluray-devel] UDF dir read does not return 1 on EOF
// https://mailman.videolan.org/pipermail/libbluray-devel/2023-September/003297.html
// https://code.videolan.org/videolan/libbluray/-/blob/master/src/libbluray/disc/udf_fs.c#L110
// https://code.videolan.org/videolan/libudfread/-/blob/master/src/udfread.c#L1375
//fprintf(stderr, "Can't read Blu-ray dir %s.\n", path);
//return 0;
}
new_path = malloc(sizeof(char) * (strlen(path) + strlen(dirent->d_name) + 2));
if (new_path == NULL) {
fprintf(stderr, "Can't allocate memory for new_path.\n");
all_good = 0;
break;
}
strcpy(new_path, path);
if (strcmp(path, "") != 0)
strcat(new_path, "/");
strcat(new_path, dirent->d_name);
if (strchr(dirent->d_name, '.') == NULL) {
if (!copy_dir(new_path))
all_good = 0;
} else {
if (!copy_file(new_path, new_path))
all_good = 0;
}
free(new_path);
} while (read == 0);
free(dirent);
dir->close(dir);
return all_good;
}
int
copy_file(const char src[], const char dst[])
{
struct bd_file_s *src_file;
uint8_t bytes[6144];
FILE *dst_file;
int64_t i, read, written;
src_file = bd_open_file_dec(bluray, src);
if (src_file == NULL) {
fprintf(stderr, "Can't open Blu-ray file %s.\n", src);
return 0;
}
dst_file = fopen(dst, "w");
if (dst_file == NULL) {
fprintf(stderr, "Can't open destination file %s.\n", dst);
return 0;
}
src_file->seek(src_file, 0, SEEK_SET);
fseek(dst_file, 0, SEEK_SET);
do {
read = src_file->read(src_file, bytes, 6144);
for (i = 0; i < read && read > 0; i++) {
written = fputc(bytes[i], dst_file);
if (written == EOF) {
fprintf(stderr, "Destination write error on %s.\n", dst);
return 0;
}
}
}
while (read > 0);
if (read < 0) {
fprintf(stderr, "Blu-ray read error on %s.\n", src);
}
fclose(dst_file);
src_file->close(src_file);
return 1;
}
int
main(int argc, char *argv[])
{
int success = 0;
char dst[256];
if (argc == 2) {
if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
print_help();
else if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version"))
print_version();
}
if (argc < 3 || argc > 5) {
print_usage();
return 1;
}
open_bluray(argv[1], argv[2]);
switch (argc) {
case 3:
success = copy_dir("");
break;
case 4:
basename(dst, argv[3]);
if (!strcmp(dst, "")) {
fprintf(stderr, "Invalid file name.\n");
return 1;
}
success = copy_file(argv[3], dst);
break;
case 5:
success = copy_file(argv[3], argv[4]);
break;
}
bd_close(bluray);
return !success;
}
void
open_bluray(const char device[], const char keyfile[])
{
bluray = bd_open(device, keyfile);
if (bluray == NULL) {
fprintf(stderr, "Can't open device %s.\n", device);
exit(1);
}
}
void
print_help(void)
{
print_usage();
fprintf(stdout, "\n");
fprintf(stdout, "Without FILE, the whole disc will be copied to the current directory.\n");
fprintf(stdout, "FILE must be a file path relative to the disc root.\n");
fprintf(stdout, "\n");
fprintf(stdout, "Without DEST, the base FILE will be saved to the current directory.\n");
fprintf(stdout, "DEST path must include file name.\n");
fprintf(stdout, "\n");
fprintf(stdout, "\"bluraybackup -h\" or \"--help\" prints this help text.\n");
fprintf(stdout, "\"bluraybackup -v\" or \"--version\" prints version and license information.\n");
fprintf(stdout, "\n");
fprintf(stdout, "bluraybackup exits with 0 when the whole disc or FILE has been copied,\n");
fprintf(stdout, "otherwise it exits with 1.\n");
exit(1);
}
void
print_usage(void)
{
fprintf(stdout, "Usage: bluraybackup <DEVICE> <KEYFILE> [FILE] [DEST]\n");
}
void
print_version(void)
{
fprintf(stdout, "bluraybackup " VERSION "\n");
fprintf(stdout, "Copyright (c) 2023 Matteo Bini\n");
fprintf(stdout, "License: GPLv3+ <https://www.gnu.org/licenses/gpl.html>.\n");
fprintf(stdout, "This is free software: you are free to change and redistribute it.\n");
fprintf(stdout, "There is NO WARRANTY, to the extent permitted by law.\n");
fprintf(stdout, "\n");
fprintf(stdout, "Written by Matteo Bini.\n");
exit(1);
}