2023-09-22 16:58:10 +00:00
|
|
|
#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;
|
2023-09-24 19:20:13 +00:00
|
|
|
size_t len, offset;
|
2023-09-22 16:58:10 +00:00
|
|
|
|
|
|
|
len = strlen(src);
|
|
|
|
|
|
|
|
for (i = len - 1; i >= 0; i--) {
|
|
|
|
offset = i + 1;
|
2023-09-24 19:20:13 +00:00
|
|
|
if (src[i] == '/' && offset < len && (len - offset) < 256) {
|
2023-09-22 16:58:10 +00:00
|
|
|
strcpy(dst, src + offset);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-24 19:20:13 +00:00
|
|
|
if (len < 256)
|
|
|
|
strcpy(dst, src);
|
|
|
|
else
|
|
|
|
strcpy(dst, "");
|
2023-09-22 16:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2023-09-23 11:45:48 +00:00
|
|
|
fprintf(stderr, "Can't open Blu-ray dir %s.\n", path);
|
2023-09-22 16:58:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(path, "") != 0 &&
|
|
|
|
mkdir(path, 0755) == -1 && errno != EEXIST) {
|
2023-09-23 11:45:48 +00:00
|
|
|
fprintf(stderr, "Can't create destination dir %s.\n", path);
|
2023-09-22 16:58:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
dirent = malloc(sizeof(BD_DIRENT));
|
2023-09-24 19:20:13 +00:00
|
|
|
if (dirent == NULL) {
|
|
|
|
fprintf(stderr, "Can't allocate memory for BD_DIRENT.\n");
|
|
|
|
dir->close(dir);
|
|
|
|
return 0;
|
|
|
|
}
|
2023-09-22 16:58:10 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
read = dir->read(dir, dirent);
|
|
|
|
if (read == -1) {
|
|
|
|
break;
|
2023-11-02 09:58:14 +00:00
|
|
|
// [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
|
2023-09-23 11:45:48 +00:00
|
|
|
//fprintf(stderr, "Can't read Blu-ray dir %s.\n", path);
|
2023-09-22 16:58:10 +00:00
|
|
|
//return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_path = malloc(sizeof(char) * (strlen(path) + strlen(dirent->d_name) + 2));
|
2023-09-24 19:20:13 +00:00
|
|
|
if (new_path == NULL) {
|
|
|
|
fprintf(stderr, "Can't allocate memory for new_path.\n");
|
|
|
|
all_good = 0;
|
|
|
|
break;
|
|
|
|
}
|
2023-09-22 16:58:10 +00:00
|
|
|
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) {
|
2023-09-23 11:45:48 +00:00
|
|
|
fprintf(stderr, "Can't open Blu-ray file %s.\n", src);
|
2023-09-22 16:58:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
dst_file = fopen(dst, "w");
|
|
|
|
if (dst_file == NULL) {
|
2023-09-23 11:45:48 +00:00
|
|
|
fprintf(stderr, "Can't open destination file %s.\n", dst);
|
2023-09-22 16:58:10 +00:00
|
|
|
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) {
|
2023-09-23 11:45:48 +00:00
|
|
|
fprintf(stderr, "Destination write error on %s.\n", dst);
|
2023-09-22 16:58:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (read > 0);
|
|
|
|
if (read < 0) {
|
2023-09-23 11:45:48 +00:00
|
|
|
fprintf(stderr, "Blu-ray read error on %s.\n", src);
|
2023-09-22 16:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(dst_file);
|
|
|
|
src_file->close(src_file);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
2023-09-24 19:20:13 +00:00
|
|
|
int success = 0;
|
2023-09-22 16:58:10 +00:00
|
|
|
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) {
|
2023-09-23 11:45:48 +00:00
|
|
|
case 3:
|
|
|
|
success = copy_dir("");
|
2023-09-22 16:58:10 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
basename(dst, argv[3]);
|
2023-09-24 19:20:13 +00:00
|
|
|
if (!strcmp(dst, "")) {
|
|
|
|
fprintf(stderr, "Invalid file name.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2023-09-22 16:58:10 +00:00
|
|
|
success = copy_file(argv[3], dst);
|
|
|
|
break;
|
2023-09-23 11:45:48 +00:00
|
|
|
case 5:
|
|
|
|
success = copy_file(argv[3], argv[4]);
|
2023-09-22 16:58:10 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bd_close(bluray);
|
|
|
|
|
|
|
|
return !success;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
open_bluray(const char device[], const char keyfile[])
|
|
|
|
{
|
|
|
|
bluray = bd_open(device, keyfile);
|
|
|
|
if (bluray == NULL) {
|
2023-09-23 11:45:48 +00:00
|
|
|
fprintf(stderr, "Can't open device %s.\n", device);
|
2023-09-22 16:58:10 +00:00
|
|
|
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);
|
|
|
|
}
|