#include #include #include #include #include #include #include /* 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 [FILE] [DEST]\n"); } void print_version(void) { fprintf(stdout, "bluraybackup " VERSION "\n"); fprintf(stdout, "Copyright (c) 2023 Matteo Bini\n"); fprintf(stdout, "License: GPLv3+ .\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); }