From 34b01fa4d3614698ac2b1af74a7f56b986fd97c4 Mon Sep 17 00:00:00 2001 From: Eudyptula Date: Tue, 21 Jul 2009 16:02:48 -0400 Subject: Added bkisofs ISO manipulation library and wrote CLI wrapper for it --- lib/bkisofs-cli.c | 162 +++ lib/bkisofs/Makefile | 29 + lib/bkisofs/bk.h | 287 +++++ lib/bkisofs/bkAdd.c | 451 ++++++++ lib/bkisofs/bkAdd.h | 3 + lib/bkisofs/bkCache.c | 85 ++ lib/bkisofs/bkCache.h | 4 + lib/bkisofs/bkDelete.c | 151 +++ lib/bkisofs/bkDelete.h | 10 + lib/bkisofs/bkError.c | 98 ++ lib/bkisofs/bkError.h | 129 +++ lib/bkisofs/bkExtract.c | 490 +++++++++ lib/bkisofs/bkExtract.h | 9 + lib/bkisofs/bkGet.c | 257 +++++ lib/bkisofs/bkGet.h | 3 + lib/bkisofs/bkInternal.h | 105 ++ lib/bkisofs/bkIoWrappers.c | 84 ++ lib/bkisofs/bkIoWrappers.h | 10 + lib/bkisofs/bkLink.c | 276 +++++ lib/bkisofs/bkLink.h | 10 + lib/bkisofs/bkMangle.c | 616 +++++++++++ lib/bkisofs/bkMangle.h | 7 + lib/bkisofs/bkMisc.c | 36 + lib/bkisofs/bkMisc.h | 1 + lib/bkisofs/bkPath.c | 375 +++++++ lib/bkisofs/bkPath.h | 16 + lib/bkisofs/bkRead.c | 1265 ++++++++++++++++++++++ lib/bkisofs/bkRead.h | 19 + lib/bkisofs/bkRead7x.c | 85 ++ lib/bkisofs/bkRead7x.h | 17 + lib/bkisofs/bkSet.c | 311 ++++++ lib/bkisofs/bkSet.h | 1 + lib/bkisofs/bkSort.c | 117 ++ lib/bkisofs/bkSort.h | 2 + lib/bkisofs/bkTime.c | 124 +++ lib/bkisofs/bkTime.h | 3 + lib/bkisofs/bkWrite.c | 2547 ++++++++++++++++++++++++++++++++++++++++++++ lib/bkisofs/bkWrite.h | 41 + lib/bkisofs/bkWrite7x.c | 116 ++ lib/bkisofs/bkWrite7x.h | 17 + lib/bkisofs/example.c | 131 +++ 41 files changed, 8500 insertions(+) create mode 100644 lib/bkisofs-cli.c create mode 100644 lib/bkisofs/Makefile create mode 100644 lib/bkisofs/bk.h create mode 100644 lib/bkisofs/bkAdd.c create mode 100644 lib/bkisofs/bkAdd.h create mode 100644 lib/bkisofs/bkCache.c create mode 100644 lib/bkisofs/bkCache.h create mode 100644 lib/bkisofs/bkDelete.c create mode 100644 lib/bkisofs/bkDelete.h create mode 100644 lib/bkisofs/bkError.c create mode 100644 lib/bkisofs/bkError.h create mode 100644 lib/bkisofs/bkExtract.c create mode 100644 lib/bkisofs/bkExtract.h create mode 100644 lib/bkisofs/bkGet.c create mode 100644 lib/bkisofs/bkGet.h create mode 100644 lib/bkisofs/bkInternal.h create mode 100644 lib/bkisofs/bkIoWrappers.c create mode 100644 lib/bkisofs/bkIoWrappers.h create mode 100644 lib/bkisofs/bkLink.c create mode 100644 lib/bkisofs/bkLink.h create mode 100644 lib/bkisofs/bkMangle.c create mode 100644 lib/bkisofs/bkMangle.h create mode 100644 lib/bkisofs/bkMisc.c create mode 100644 lib/bkisofs/bkMisc.h create mode 100644 lib/bkisofs/bkPath.c create mode 100644 lib/bkisofs/bkPath.h create mode 100644 lib/bkisofs/bkRead.c create mode 100644 lib/bkisofs/bkRead.h create mode 100644 lib/bkisofs/bkRead7x.c create mode 100644 lib/bkisofs/bkRead7x.h create mode 100644 lib/bkisofs/bkSet.c create mode 100644 lib/bkisofs/bkSet.h create mode 100644 lib/bkisofs/bkSort.c create mode 100644 lib/bkisofs/bkSort.h create mode 100644 lib/bkisofs/bkTime.c create mode 100644 lib/bkisofs/bkTime.h create mode 100644 lib/bkisofs/bkWrite.c create mode 100644 lib/bkisofs/bkWrite.h create mode 100644 lib/bkisofs/bkWrite7x.c create mode 100644 lib/bkisofs/bkWrite7x.h create mode 100644 lib/bkisofs/example.c diff --git a/lib/bkisofs-cli.c b/lib/bkisofs-cli.c new file mode 100644 index 0000000..5977c64 --- /dev/null +++ b/lib/bkisofs-cli.c @@ -0,0 +1,162 @@ +/****************************************************************************** + * example.c + * Example for using bkisofs + * Author: Andrew Smith + * Compile with: cc example.c bk.a -o example + * */ + +#include +#include +#include + +/* need to include bk.h for access to bkisofs functions and structures */ +#include "bkisofs/bk.h" + +void addProgressUpdaterCbk(VolInfo* volInfo); +void fatalError(const char* message); +void printNameAndContents(BkFileBase* item, int numSpaces); +void readProgressUpdaterCbk(VolInfo* volInfo); +void writeProgressUpdaterCbk(VolInfo* volInfo, double percentComplete); + +int main( int argv, char** argc) +{ + /* A variable of type VolInfo stores information about an image */ + VolInfo volInfo; + /* bk functions return ints that need to be checked to see whether + * the functions were successful or not */ + int rc; + + if(argv < 5) { + puts("Usage: ingenue-bkisofs [] [ [etc.]]\nValid actions:\n\tadd \n\treplace \n\tdelete \n\textract \n\tprint\n\twrite "); + exit(1); + } + + /* initialise volInfo, set it up to scan for duplicate files */ + rc = bk_init_vol_info(&volInfo, true); + if(rc <= 0) + fatalError(bk_get_error_string(rc)); + + /* open the iso file (supplied as argument 1) */ + rc = bk_open_image(&volInfo, argc[1]); + if(rc <= 0) + fatalError(bk_get_error_string(rc)); + + /* read information about the volume (required before reading directory tree) */ + rc = bk_read_vol_info(&volInfo); + if(rc <= 0) + fatalError(bk_get_error_string(rc)); + + /* read the directory tree */ + if(volInfo.filenameTypes & FNTYPE_ROCKRIDGE) + rc = bk_read_dir_tree(&volInfo, FNTYPE_ROCKRIDGE, true, readProgressUpdaterCbk); + else if(volInfo.filenameTypes & FNTYPE_JOLIET) + rc = bk_read_dir_tree(&volInfo, FNTYPE_JOLIET, false, readProgressUpdaterCbk); + else + rc = bk_read_dir_tree(&volInfo, FNTYPE_9660, false, readProgressUpdaterCbk); + if(rc <= 0) + fatalError(bk_get_error_string(rc)); + + unsigned short i=3; + char* slash; + for (i=3; i %s\n", argc[i], argc[i+2], argc[i+1]); + if (argc[i+1][strlen(argc[i+1])-1] == '/') { + rc = bk_add(&volInfo, argc[i+2], argc[i+1], addProgressUpdaterCbk); + } else { + slash=strrchr(argc[i+1], '/'); + *slash='\0'; + rc = bk_add_as(&volInfo, argc[i+2], argc[i+1], slash+1, addProgressUpdaterCbk); + } + i+=2; + } else if (strcmp(argc[i], "rename") == 0) { + printf("%s %s -> %s\n", argc[i], argc[i+1], argc[i+2]); + rc = bk_rename(&volInfo, argc[i++], argc[i++]); + } else if (strcmp(argc[i], "write") == 0) { + printf("%s %s\n", argc[i], argc[i+1]); + rc = bk_write_image(argc[i+1], &volInfo, time(NULL), volInfo.filenameTypes, writeProgressUpdaterCbk); + } else if (strcmp(argc[i], "print") == 0) { + printNameAndContents(BK_BASE_PTR( &(volInfo.dirTree) ), 0); + } else if (strcmp(argc[i], "extract") == 0) { + printf("%s %s -> %s\n", argc[i], argc[i+1], argc[i+2]); + if (argc[i+1][strlen(argc[i+1])-1] == '/') { + rc = bk_extract(&volInfo, argc[i+1], argc[i+2], true, NULL); + } else { + slash=strrchr(argc[i+1], '/'); + *slash='\0'; + rc = bk_extract_as(&volInfo, argc[i+1], argc[i+2], slash+1, true, NULL); + } + i+=2; + } + if (rc <= 0) + fatalError(bk_get_error_string(rc)); + } + + /* we're finished with this ISO, so clean up */ + bk_destroy_vol_info(&volInfo); + + return 0; +} + +/* you can use this to update a progress bar or something */ +void addProgressUpdaterCbk(VolInfo* volInfo) +{ + printf("Add progress updater\n"); +} + +void fatalError(const char* message) +{ + printf("Fatal error: %s\n", message); + exit(1); +} + +void printNameAndContents(BkFileBase* base, int numSpaces) +{ + int count; + + /* print the spaces (indentation, for prettyness) */ + for(count = 0; count < numSpaces; count++) + printf(" "); + + if(IS_DIR(base->posixFileMode)) + { + /* print name of the directory */ + printf("%s (directory)\n", base->name); + + /* print all the directory's children */ + BkFileBase* child = BK_DIR_PTR(base)->children; + while(child != NULL) + { + printNameAndContents(child, numSpaces + 2); + child = child->next; + } + } + else if(IS_REG_FILE(base->posixFileMode)) + { + /* print name and size of the file */ + printf("%s (regular file), size %u\n", base->name, BK_FILE_PTR(base)->size); + } + else if(IS_SYMLINK(base->posixFileMode)) + { + /* print name and target of the symbolic link */ + printf("%s -> %s (symbolic link)\n", base->name, BK_SYMLINK_PTR(base)->target); + } +} + +/* you can use this to update a progress bar or something */ +void readProgressUpdaterCbk(VolInfo* volInfo) +{ + // printf("Read progress updater\n"); // We don't like progress +} + +/* you can use this to update a progress bar or something */ +void writeProgressUpdaterCbk(VolInfo* volInfo, double percentComplete) +{ + printf("Write progress updater: ~%.2lf%% complete\n", percentComplete); +} diff --git a/lib/bkisofs/Makefile b/lib/bkisofs/Makefile new file mode 100644 index 0000000..f565fcb --- /dev/null +++ b/lib/bkisofs/Makefile @@ -0,0 +1,29 @@ +# CC, AR, RM defined in parent makefile + +OBJECTS = bkRead7x.o bkAdd.o bkDelete.o bkExtract.o bkRead.o bkPath.o bkMangle.o bkWrite.o bkWrite7x.o bkTime.o bkSort.o bkError.o bkGet.o bkSet.o bkCache.o bkLink.o bkMisc.o bkIoWrappers.o + +# -DDEBUG and -g only used during development +CFLAGS += -Wall -pedantic -std=gnu99 -Wundef -Wcast-align -W -Wpointer-arith -Wwrite-strings -Wno-unused-parameter + +# the _FILE_OFFSET_BITS=64 is to enable stat() for large files +CPPFLAGS = -D_FILE_OFFSET_BITS=64 + +ifdef WINDOWS_BUILD + CPPFLAGS += -DWINDOWS_BUILD +endif + +bk.a: $(OBJECTS) + @echo 'Creating bk.a' + @$(AR) -cr bk.a $(OBJECTS) + +# static pattern rule +$(OBJECTS): %.o: %.c %.h Makefile bk.h bkInternal.h + @echo 'Compiling' $< + @$(CC) $< $(CFLAGS) $(CPPFLAGS) -c -o $@ + +example: example.c + $(CC) $(CFLAGS) $(CPPFLAGS) example.c bk.a -o example + +clean: + $(RM) *.o bk.a + diff --git a/lib/bkisofs/bk.h b/lib/bkisofs/bk.h new file mode 100644 index 0000000..e1ed67f --- /dev/null +++ b/lib/bkisofs/bk.h @@ -0,0 +1,287 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +/********************************* PURPOSE ************************************ +* bk.h +* This header file is the public interface to bkisofs. +******************************** END PURPOSE *********************************/ + +#ifndef bk_h +#define bk_h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "bkError.h" + +#ifdef WINDOWS_BUILD + /* on windows i can't get an off_t to be 64 bits */ + typedef long long bk_off_t; + #define bk_lseek _lseeki64 + typedef struct _stati64 BkStatStruct; +#else + typedef off_t bk_off_t; + #define bk_lseek lseek + typedef struct stat BkStatStruct; +#endif + +/* can be |ed */ +#define FNTYPE_9660 1 +#define FNTYPE_ROCKRIDGE 2 +#define FNTYPE_JOLIET 4 + +/* many library functions rely on this being at least 256 */ +#define NCHARS_FILE_ID_MAX_STORE 256 + +/* maximum length of the target of a symbolic link +* !! this is used for both the number of characters in the path and the number +* of bytes in the SL record, that should probably be fixed */ +#define NCHARS_SYMLINK_TARGET_MAX 251 + +/* maximum number of bytes to read from a file for comparing it quickly +* with others (is it likely to be a hard link or not) */ +#define MAX_NBYTES_HARDLINK_HEAD 32 + +/* options for VolInfo.bootMediaType */ +#define BOOT_MEDIA_NONE 0 +#define BOOT_MEDIA_NO_EMULATION 1 +#define BOOT_MEDIA_1_2_FLOPPY 2 /* 1228800 byte floppy disk image */ +#define BOOT_MEDIA_1_44_FLOPPY 3 /* 1474560 byte floppy disk image */ +#define BOOT_MEDIA_2_88_FLOPPY 4 /* 2949120 byte floppy disk image */ +#define BOOT_MEDIA_HARD_DISK 5 + +#define READ_WRITE_BUFFER_SIZE 102400 + +/* warning message string lengths in VolInfo */ +#define BK_WARNING_MAX_LEN 512 + +#define IS_DIR(posix) ((posix & 0770000) == 0040000) +#define IS_REG_FILE(posix) ((posix & 0770000) == 0100000) +#define IS_SYMLINK(posix) ((posix & 0770000) == 0120000) + +#define BK_BASE_PTR(item) ((BkFileBase*)(item)) +#define BK_DIR_PTR(item) ((BkDir*)(item)) +#define BK_FILE_PTR(item) ((BkFile*)(item)) +#define BK_SYMLINK_PTR(item) ((BkSymLink*)(item)) + +/******************************************************************************* +* BkFileBase +* Linked list node. +* All files, directories, links need this. */ +typedef struct BkFileBase +{ + char original9660name[15]; /* 8.3 + ";1" max */ + char name[NCHARS_FILE_ID_MAX_STORE]; /* '\0' terminated */ + unsigned posixFileMode; /* file type and permissions */ + + struct BkFileBase* next; + +} BkFileBase; + +/******************************************************************************* +* BkDir +* Linked list node. +* Information about a directory and it's contents. */ +typedef struct BkDir +{ + BkFileBase base; /* intended to be accessed using a cast */ + + BkFileBase* children; /* child directories, files, etc. */ + +} BkDir; + +/******************************************************************************* +* BkHardLink +* Linked list node. +* Information about a hard link (where to find a certain file). +* This is for internal use but is defined here because BkFile references it. +* You don't need to use this structure, please ignore it. */ +typedef struct BkHardLink +{ + bool onImage; + bk_off_t position; /* if on image */ + char* pathAndName; /* if on filesystem, full path + filename + * is to be freed whenever the BkHardLink is freed */ + unsigned size; /* size of the file being pointed to */ + int headSize; + unsigned char head[MAX_NBYTES_HARDLINK_HEAD]; + bool alreadyCounted; /* for estimateIsoSize() */ + + unsigned extentNumberWrittenTo; /* only set once one file is written */ + + struct BkHardLink* next; + +} BkHardLink; + +/******************************************************************************* +* BkFile +* Linked list node. +* Information about a file, whether on the image or on the filesystem. */ +typedef struct BkFile +{ + BkFileBase base; /* intended to be accessed using a cast */ + + unsigned size; /* in bytes, don't need bk_off_t because it's stored + * in a 32bit unsigned int on the iso */ + BkHardLink* location; /* basically a copy of the following variables */ + bool onImage; + bk_off_t position; /* if on image, in bytes */ + char* pathAndName; /* if on filesystem, full path + filename + * is to be freed whenever the File is freed */ + +} BkFile; + +/******************************************************************************* +* BkSymLink +* Linked list node. +* Information about a symbolic link. */ +typedef struct BkSymLink +{ + BkFileBase base; /* intended to be accessed using a cast */ + + char target[NCHARS_SYMLINK_TARGET_MAX]; + +} BkSymLink; + +/******************************************************************************* +* VolInfo +* Information about a volume (one image). +* Strings are '\0' terminated. */ +typedef struct VolInfo +{ + /* private bk use */ + unsigned filenameTypes; + bk_off_t pRootDrOffset; /* primary (9660 and maybe rockridge) */ + bk_off_t sRootDrOffset; /* secondary (joliet), 0 if does not exist */ + bk_off_t bootRecordSectorNumberOffset; + int imageForReading; + ino_t imageForReadingInode; /* to know which file was open for reading + * (filename is not reliable) */ + const BkFile* bootRecordOnImage; /* if visible, pointer to the file in the + * directory tree */ + char warningMessage[BK_WARNING_MAX_LEN]; + bool rootRead; /* did i read the root record inside volume descriptor? */ + bool stopOperation; /* cancel current opertion */ + int imageForWriting; + void(*progressFunction)(struct VolInfo*); + void(*writeProgressFunction)(struct VolInfo*, double); + struct timeb lastTimeCalledProgress; + bk_off_t estimatedIsoSize; + BkHardLink* fileLocations; /* list of where to find regular files */ + char readWriteBuffer[READ_WRITE_BUFFER_SIZE]; + char readWriteBuffer2[READ_WRITE_BUFFER_SIZE]; + + /* public use, read only */ + time_t creationTime; + BkDir dirTree; + unsigned char bootMediaType; + unsigned bootRecordSize; /* in bytes */ + bool bootRecordIsOnImage; /* unused if visible (flag below) */ + bk_off_t bootRecordOffset; /* if on image */ + char* bootRecordPathAndName; /* if on filesystem */ + bool bootRecordIsVisible; /* whether boot record is a visible file + * on the image */ + bool scanForDuplicateFiles; /* whether to check every file for uniqueness + * to decide is it a hard link or not */ + + /* public use, read/write */ + char volId[33]; + char publisher[129]; + char dataPreparer[129]; + unsigned posixFileDefaults; /* for extracting */ + unsigned posixDirDefaults; /* for extracting or creating on iso */ + bool(*warningCbk)(const char*); + bool followSymLinks; /* whether to stat the link itself rather + * than the file it's pointing to */ + +} VolInfo; + +/* public bkisofs functions */ + +/* adding */ +int bk_add_boot_record(VolInfo* volInfo, const char* srcPathAndName, + int bootMediaType); +int bk_add(VolInfo* volInfo, const char* srcPathAndName, + const char* destPathStr, void(*progressFunction)(VolInfo*)); +int bk_add_as(VolInfo* volInfo, const char* srcPathAndName, + const char* destPathStr, const char* nameToUse, + void(*progressFunction)(VolInfo*)); +int bk_create_dir(VolInfo* volInfo, const char* destPathStr, + const char* newDirName); + +/* deleting */ +void bk_delete_boot_record(VolInfo* volInfo); +int bk_delete(VolInfo* volInfo, const char* pathAndName); + +/* extracting */ +int bk_extract_boot_record(VolInfo* volInfo, const char* destPathAndName, + unsigned destFilePerms); +int bk_extract(VolInfo* volInfo, const char* srcPathAndName, + const char* destDir, bool keepPermissions, + void(*progressFunction)(VolInfo*)); +int bk_extract_as(VolInfo* volInfo, const char* srcPathAndName, + const char* destDir, const char* nameToUse, + bool keepPermissions, void(*progressFunction)(VolInfo*)); + +/* getters */ +bk_off_t bk_estimate_iso_size(const VolInfo* volInfo, int filenameTypes); +time_t bk_get_creation_time(const VolInfo* volInfo); +int bk_get_dir_from_string(const VolInfo* volInfo, const char* pathStr, + BkDir** dirFoundPtr); +int bk_get_permissions(VolInfo* volInfo, const char* pathAndName, + mode_t* permissions); +const char* bk_get_publisher(const VolInfo* volInfo); +const char* bk_get_volume_name(const VolInfo* volInfo); +const char* bk_get_error_string(int errorId); + +/* setters */ +void bk_cancel_operation(VolInfo* volInfo); +void bk_destroy_vol_info(VolInfo* volInfo); +int bk_init_vol_info(VolInfo* volInfo, bool scanForDuplicateFiles); +int bk_rename(VolInfo* volInfo, const char* srcPathAndName, + const char* newName); +int bk_set_boot_file(VolInfo* volInfo, const char* srcPathAndName); +void bk_set_follow_symlinks(VolInfo* volInfo, bool doFollow); +int bk_set_permissions(VolInfo* volInfo, const char* pathAndName, + mode_t permissions); +int bk_set_publisher(VolInfo* volInfo, const char* publisher); +int bk_set_vol_name(VolInfo* volInfo, const char* volName); + +/* reading */ +int bk_open_image(VolInfo* volInfo, const char* filename); +int bk_read_dir_tree(VolInfo* volInfo, int filenameType, + bool keepPosixPermissions, + void(*progressFunction)(VolInfo*)); +int bk_read_vol_info(VolInfo* volInfo); + +/* writing */ +int bk_write_image(const char* newImagePathAndName, VolInfo* volInfo, + time_t creationTime, int filenameTypes, + void(*progressFunction)(VolInfo*, double)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/bkisofs/bkAdd.c b/lib/bkisofs/bkAdd.c new file mode 100644 index 0000000..06b13d0 --- /dev/null +++ b/lib/bkisofs/bkAdd.c @@ -0,0 +1,451 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "bk.h" +#include "bkPath.h" +#include "bkAdd.h" +#include "bkError.h" +#include "bkGet.h" +#include "bkMangle.h" +#include "bkLink.h" +#include "bkMisc.h" +#include "bkSet.h" +#include "bkIoWrappers.h" + +int add(VolInfo* volInfo, const char* srcPathAndName, BkDir* destDir, + const char* nameToUse) +{ + int rc; + char lastName[NCHARS_FILE_ID_MAX_STORE]; + BkFileBase* oldHead; /* of the children list */ + BkStatStruct statStruct; + + if(volInfo->stopOperation) + return BKERROR_OPER_CANCELED_BY_USER; + + maybeUpdateProgress(volInfo); + + if(nameToUse == NULL) + { + rc = getLastNameFromPath(srcPathAndName, lastName); + if(rc <= 0) + return rc; + } + else + { + if(strlen(nameToUse) > NCHARS_FILE_ID_MAX_STORE - 1) + return BKERROR_MAX_NAME_LENGTH_EXCEEDED; + strcpy(lastName, nameToUse); + } + + if(strcmp(lastName, ".") == 0 || strcmp(lastName, "..") == 0) + return BKERROR_NAME_INVALID; + + if( !nameIsValid(lastName) ) + return BKERROR_NAME_INVALID_CHAR; + + oldHead = destDir->children; + + /* windows doesn't have symbolic links */ + if(volInfo->followSymLinks) + rc = bkStat(srcPathAndName, &statStruct); + else + rc = lstat(srcPathAndName, &statStruct); + if(rc == -1) + return BKERROR_STAT_FAILED; + + if( IS_DIR(statStruct.st_mode) ) + { + BkDir* newDir; + + newDir = malloc(sizeof(BkDir)); + if(newDir == NULL) + return BKERROR_OUT_OF_MEMORY; + + memset(newDir, 0, sizeof(BkDir)); + + strcpy(BK_BASE_PTR(newDir)->name, lastName); + + BK_BASE_PTR(newDir)->posixFileMode = statStruct.st_mode; + + BK_BASE_PTR(newDir)->next = oldHead; + + newDir->children = NULL; + + /* ADD dir contents */ + rc = addDirContents(volInfo, srcPathAndName, newDir); + if(rc < 0) + { + free(newDir); + return rc; + } + /* END ADD dir contents */ + + destDir->children = BK_BASE_PTR(newDir); + } + else if( IS_REG_FILE(statStruct.st_mode) ) + { + BkFile* newFile; + + if(statStruct.st_size > 0xFFFFFFFF) + /* size won't fit in a 32bit variable on the iso */ + return BKERROR_ADD_FILE_TOO_BIG; + + newFile = malloc(sizeof(BkFile)); + if(newFile == NULL) + return BKERROR_OUT_OF_MEMORY; + + memset(newFile, 0, sizeof(BkFile)); + + strcpy(BK_BASE_PTR(newFile)->name, lastName); + + BK_BASE_PTR(newFile)->posixFileMode = statStruct.st_mode; + + BK_BASE_PTR(newFile)->next = oldHead; + + newFile->size = statStruct.st_size; + + newFile->onImage = false; + + newFile->position = 0; + + newFile->pathAndName = malloc(strlen(srcPathAndName) + 1); + strcpy(newFile->pathAndName, srcPathAndName); + + if( volInfo->scanForDuplicateFiles) + { + BkHardLink* newLink; + + rc = findInHardLinkTable(volInfo, 0, newFile->pathAndName, + statStruct.st_size, false, &newLink); + if(rc < 0) + { + free(newFile); + return rc; + } + + if(newLink == NULL) + /* not found */ + { + rc = addToHardLinkTable(volInfo, 0, newFile->pathAndName, + statStruct.st_size, false, &newLink); + if(rc < 0) + { + free(newFile); + return rc; + } + } + + newFile->location = newLink; + } + + destDir->children = BK_BASE_PTR(newFile); + } + else if( IS_SYMLINK(statStruct.st_mode) ) + { + BkSymLink* newSymLink; + ssize_t numChars; + + newSymLink = malloc(sizeof(BkSymLink)); + if(newSymLink == NULL) + return BKERROR_OUT_OF_MEMORY; + + memset(newSymLink, 0, sizeof(BkSymLink)); + + strcpy(BK_BASE_PTR(newSymLink)->name, lastName); + + BK_BASE_PTR(newSymLink)->posixFileMode = statStruct.st_mode; + + BK_BASE_PTR(newSymLink)->next = oldHead; + + numChars = readlink(srcPathAndName, newSymLink->target, + NCHARS_SYMLINK_TARGET_MAX - 1); + if(numChars == -1) + { + free(newSymLink); + return BKERROR_OPEN_READ_FAILED; + } + newSymLink->target[numChars] = '\0'; + + destDir->children = BK_BASE_PTR(newSymLink); + } + else + return BKERROR_NO_SPECIAL_FILES; + + return 1; +} + +int addDirContents(VolInfo* volInfo, const char* srcPath, BkDir* destDir) +{ + int rc; + int srcPathLen; + char* newSrcPathAndName; + + /* vars to read contents of a dir on fs */ + DIR* srcDir; + struct dirent* dirEnt; + + srcPathLen = strlen(srcPath); + + /* including the new name and the possibly needed trailing '/' */ + newSrcPathAndName = malloc(srcPathLen + NCHARS_FILE_ID_MAX_STORE + 2); + if(newSrcPathAndName == NULL) + return BKERROR_OUT_OF_MEMORY; + + strcpy(newSrcPathAndName, srcPath); + + if(srcPath[srcPathLen - 1] != '/') + { + strcat(newSrcPathAndName, "/"); + srcPathLen++; + } + + srcDir = opendir(srcPath); + if(srcDir == NULL) + { + free(newSrcPathAndName); + return BKERROR_OPENDIR_FAILED; + } + + /* it may be possible but in any case very unlikely that readdir() will fail + * if it does, it returns NULL (same as end of dir) */ + while( (dirEnt = readdir(srcDir)) != NULL ) + { + if( strcmp(dirEnt->d_name, ".") == 0 || strcmp(dirEnt->d_name, "..") == 0 ) + /* ignore "." and ".." */ + continue; + + if(strlen(dirEnt->d_name) > NCHARS_FILE_ID_MAX_STORE - 1) + { + closedir(srcDir); + free(newSrcPathAndName); + + return BKERROR_MAX_NAME_LENGTH_EXCEEDED; + } + + /* append file/dir name */ + strcpy(newSrcPathAndName + srcPathLen, dirEnt->d_name); + + rc = add(volInfo, newSrcPathAndName, destDir, NULL); + if(rc <= 0 && rc != BKWARNING_OPER_PARTLY_FAILED) + { + bool goOn; + + if(volInfo->warningCbk != NULL && !volInfo->stopOperation) + /* perhaps the user wants to ignore this failure */ + { + snprintf(volInfo->warningMessage, BK_WARNING_MAX_LEN, + "Failed to add item '%s': '%s'", + dirEnt->d_name, + bk_get_error_string(rc)); + goOn = volInfo->warningCbk(volInfo->warningMessage); + rc = BKWARNING_OPER_PARTLY_FAILED; + } + else + goOn = false; + + if(goOn) + continue; + else + { + volInfo->stopOperation = true; + closedir(srcDir); + free(newSrcPathAndName); + return rc; + } + } + } + + free(newSrcPathAndName); + + rc = closedir(srcDir); + if(rc != 0) + /* exotic error */ + return BKERROR_EXOTIC; + + return 1; +} + +int bk_add(VolInfo* volInfo, const char* srcPathAndName, + const char* destPathStr, void(*progressFunction)(VolInfo*)) +{ + return bk_add_as(volInfo, srcPathAndName, destPathStr, NULL, + progressFunction); +} + +int bk_add_as(VolInfo* volInfo, const char* srcPathAndName, + const char* destPathStr, const char* nameToUse, + void(*progressFunction)(VolInfo*)) +{ + int rc; + NewPath destPath; + char lastName[NCHARS_FILE_ID_MAX_STORE]; + + /* vars to find the dir in the tree */ + BkDir* destDirInTree; + bool dirFound; + + volInfo->progressFunction = progressFunction; + + rc = makeNewPathFromString(destPathStr, &destPath); + if(rc <= 0) + { + freePathContents(&destPath); + return rc; + } + + rc = getLastNameFromPath(srcPathAndName, lastName); + if(rc <= 0) + { + freePathContents(&destPath); + return rc; + } + + dirFound = findDirByNewPath(&destPath, &(volInfo->dirTree), &destDirInTree); + if(!dirFound) + { + freePathContents(&destPath); + return BKERROR_DIR_NOT_FOUND_ON_IMAGE; + } + + freePathContents(&destPath); + + if(itemIsInDir(lastName, destDirInTree)) + return BKERROR_DUPLICATE_ADD; + + volInfo->stopOperation = false; + + rc = add(volInfo, srcPathAndName, destDirInTree, nameToUse); + if(rc <= 0) + return rc; + + return 1; +} + +/******************************************************************************* +* bk_add_boot_record() +* Source boot file must be exactly the right size if floppy emulation requested. +* */ +int bk_add_boot_record(VolInfo* volInfo, const char* srcPathAndName, + int bootMediaType) +{ + BkStatStruct statStruct; + int rc; + + if(bootMediaType != BOOT_MEDIA_NO_EMULATION && + bootMediaType != BOOT_MEDIA_1_2_FLOPPY && + bootMediaType != BOOT_MEDIA_1_44_FLOPPY && + bootMediaType != BOOT_MEDIA_2_88_FLOPPY) + { + return BKERROR_ADD_UNKNOWN_BOOT_MEDIA; + } + + rc = bkStat(srcPathAndName, &statStruct); + if(rc == -1) + return BKERROR_STAT_FAILED; + + if(statStruct.st_size > 0xFFFFFFFF) + /* size won't fit in a 32bit variable on the iso */ + return BKERROR_ADD_FILE_TOO_BIG; + + if( (bootMediaType == BOOT_MEDIA_1_2_FLOPPY && + statStruct.st_size != 1228800) || + (bootMediaType == BOOT_MEDIA_1_44_FLOPPY && + statStruct.st_size != 1474560) || + (bootMediaType == BOOT_MEDIA_2_88_FLOPPY && + statStruct.st_size != 2949120) ) + { + return BKERROR_ADD_BOOT_RECORD_WRONG_SIZE; + } + + volInfo->bootMediaType = bootMediaType; + + volInfo->bootRecordSize = statStruct.st_size; + + volInfo->bootRecordIsOnImage = false; + + /* make copy of the source path and name */ + if(volInfo->bootRecordPathAndName != NULL) + free(volInfo->bootRecordPathAndName); + volInfo->bootRecordPathAndName = malloc(strlen(srcPathAndName) + 1); + if(volInfo->bootRecordPathAndName == NULL) + { + volInfo->bootMediaType = BOOT_MEDIA_NONE; + return BKERROR_OUT_OF_MEMORY; + } + strcpy(volInfo->bootRecordPathAndName, srcPathAndName); + + /* this is the wrong function to use if you want a visible one */ + volInfo->bootRecordIsVisible = false; + + return 1; +} + +/******************************************************************************* +* bk_create_dir() +* +* */ +int bk_create_dir(VolInfo* volInfo, const char* destPathStr, + const char* newDirName) +{ + size_t nameLen; + BkDir* destDir; + int rc; + BkFileBase* oldHead; + BkDir* newDir; + + nameLen = strlen(newDirName); + if(nameLen > NCHARS_FILE_ID_MAX_STORE - 1) + return BKERROR_MAX_NAME_LENGTH_EXCEEDED; + if(nameLen == 0) + return BKERROR_BLANK_NAME; + + if(strcmp(newDirName, ".") == 0 || strcmp(newDirName, "..") == 0) + return BKERROR_NAME_INVALID; + + if( !nameIsValid(newDirName) ) + return BKERROR_NAME_INVALID_CHAR; + + rc = getDirFromString(&(volInfo->dirTree), destPathStr, &destDir); + if(rc <= 0) + return rc; + + if(itemIsInDir(newDirName, destDir)) + return BKERROR_DUPLICATE_CREATE_DIR; + + oldHead = destDir->children; + + newDir = malloc(sizeof(BkDir)); + if(newDir == NULL) + return BKERROR_OUT_OF_MEMORY; + + strcpy(BK_BASE_PTR(newDir)->name, newDirName); + + BK_BASE_PTR(newDir)->posixFileMode = volInfo->posixDirDefaults; + + BK_BASE_PTR(newDir)->next = oldHead; + + newDir->children = NULL; + + destDir->children = BK_BASE_PTR(newDir); + + return 1; +} diff --git a/lib/bkisofs/bkAdd.h b/lib/bkisofs/bkAdd.h new file mode 100644 index 0000000..e7a1743 --- /dev/null +++ b/lib/bkisofs/bkAdd.h @@ -0,0 +1,3 @@ +int add(VolInfo* volInfo, const char* srcPathAndName, BkDir* destDir, + const char* nameToUse); +int addDirContents(VolInfo* volInfo, const char* srcPath, BkDir* destDir); diff --git a/lib/bkisofs/bkCache.c b/lib/bkisofs/bkCache.c new file mode 100644 index 0000000..174e86b --- /dev/null +++ b/lib/bkisofs/bkCache.c @@ -0,0 +1,85 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +/****************************************************************************** +* 31 dec 2006: these functions turned out to be so heavy on cpu usage that +* performance went down significantly for writing to anything, including +* the local filesystem. So now they don't do anything. +* 07 Jan 2007: deleted the said caching functions +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "bkInternal.h" +#include "bkCache.h" +#include "bkIoWrappers.h" + +int wcSeekForward(VolInfo* volInfo, bk_off_t numBytes) +{ + bkSeekSet(volInfo->imageForWriting, numBytes, SEEK_CUR); + + return 1; +} + +int wcSeekSet(VolInfo* volInfo, bk_off_t position) +{ + bkSeekSet(volInfo->imageForWriting, position, SEEK_SET); + + return 1; +} + +bk_off_t wcSeekTell(VolInfo* volInfo) +{ + return bkSeekTell(volInfo->imageForWriting); +} + +int wcWrite(VolInfo* volInfo, const char* block, size_t numBytes) +{ + ssize_t rc; + rc = bkWrite(volInfo->imageForWriting, block, numBytes); + if(rc == -1) + return BKERROR_WRITE_GENERIC; + + if(volInfo->writeProgressFunction != NULL) + { + struct timeb timeNow; + ftime(&timeNow); + + if(timeNow.time - volInfo->lastTimeCalledProgress.time >= 1 || + timeNow.millitm - volInfo->lastTimeCalledProgress.millitm >= 100) + { + BkStatStruct statStruct; + double percentComplete; + + bkFstat(volInfo->imageForWriting, &statStruct); + percentComplete = (double)statStruct.st_size * 100 / + volInfo->estimatedIsoSize + 1; + + /* estimate isn't perfect */ + if(percentComplete > 100) + percentComplete = 100; + else if (percentComplete < 0) + percentComplete = 0; + + volInfo->writeProgressFunction(volInfo, percentComplete); + volInfo->lastTimeCalledProgress = timeNow; + } + } + + return 1; +} diff --git a/lib/bkisofs/bkCache.h b/lib/bkisofs/bkCache.h new file mode 100644 index 0000000..be85aed --- /dev/null +++ b/lib/bkisofs/bkCache.h @@ -0,0 +1,4 @@ +int wcSeekForward(VolInfo* volInfo, bk_off_t numBytes); +int wcSeekSet(VolInfo* volInfo, bk_off_t position); +bk_off_t wcSeekTell(VolInfo* volInfo); +int wcWrite(VolInfo* volInfo, const char* block, size_t numBytes); diff --git a/lib/bkisofs/bkDelete.c b/lib/bkisofs/bkDelete.c new file mode 100644 index 0000000..e8ab0b0 --- /dev/null +++ b/lib/bkisofs/bkDelete.c @@ -0,0 +1,151 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "bk.h" +#include "bkInternal.h" +#include "bkPath.h" +#include "bkError.h" +#include "bkDelete.h" + +/******************************************************************************* +* bk_delete_boot_record() +* deletes whatever reference to a boot record volinfo has +* */ +void bk_delete_boot_record(VolInfo* volInfo) +{ + volInfo->bootMediaType = BOOT_MEDIA_NONE; + + if(volInfo->bootRecordPathAndName != NULL) + { + free(volInfo->bootRecordPathAndName); + volInfo->bootRecordPathAndName = NULL; + } +} + +int bk_delete(VolInfo* volInfo, const char* pathAndName) +{ + int rc; + NewPath path; + bool dirFound; + BkDir* parentDir; + + rc = makeNewPathFromString(pathAndName, &path); + if(rc <= 0) + { + freePathContents(&path); + return rc; + } + + if(path.numChildren == 0) + { + freePathContents(&path); + return BKERROR_DELETE_ROOT; + } + + /* i want the parent directory */ + path.numChildren--; + dirFound = findDirByNewPath(&path, &(volInfo->dirTree), &parentDir); + path.numChildren++; + if(!dirFound) + { + freePathContents(&path); + return BKERROR_DIR_NOT_FOUND_ON_IMAGE; + } + + deleteNode(volInfo, parentDir, path.children[path.numChildren - 1]); + + freePathContents(&path); + + return 1; +} + +void deleteNode(VolInfo* volInfo, BkDir* parentDir, char* nodeToDeleteName) +{ + BkFileBase** childPtr; + BkFileBase* nodeToFree; + + childPtr = &(parentDir->children); + while(*childPtr != NULL) + { + if( strcmp((*childPtr)->name, nodeToDeleteName) == 0 ) + { + nodeToFree = *childPtr; + + *childPtr = (*childPtr)->next; + + if( IS_DIR(nodeToFree->posixFileMode) ) + { + deleteDirContents(volInfo, BK_DIR_PTR(nodeToFree)); + } + else if ( IS_REG_FILE(nodeToFree->posixFileMode) ) + { + deleteRegFileContents(volInfo, BK_FILE_PTR(nodeToFree)); + } + /* else the free below will be enough */ + + free(nodeToFree); + + break; + } + + childPtr = &((*childPtr)->next); + } +} + +/******************************************************************************* +* deleteDirContents() +* deletes all the contents of a directory +* recursive +* */ +void deleteDirContents(VolInfo* volInfo, BkDir* dir) +{ + BkFileBase* child; + BkFileBase* nextChild; + + child = dir->children; + while(child != NULL) + { + nextChild = child->next; + + deleteNode(volInfo, dir, child->name); + + child = nextChild; + } +} + +/* delete the contents of the BkFile structure, not the actual file contents */ +void deleteRegFileContents(VolInfo* volInfo, BkFile* file) +{ + if( file->onImage ) + free( file->pathAndName ); + + /* check whether file is being used as a boot record */ + if(volInfo->bootMediaType != BOOT_MEDIA_NONE && + volInfo->bootMediaType == BOOT_MEDIA_NO_EMULATION) + { + if(volInfo->bootRecordIsVisible && + volInfo->bootRecordOnImage == file) + { + /* and stop using it. perhaps insert a hook here one day to + * let the user know the boot record has been/will be deleted */ + bk_delete_boot_record(volInfo); + } + } +} diff --git a/lib/bkisofs/bkDelete.h b/lib/bkisofs/bkDelete.h new file mode 100644 index 0000000..90d92cb --- /dev/null +++ b/lib/bkisofs/bkDelete.h @@ -0,0 +1,10 @@ +#ifndef bkDelete_h +#define bkDelete_h + +#include "bkInternal.h" + +void deleteDirContents(VolInfo* volInfo, BkDir* dir); +void deleteNode(VolInfo* volInfo, BkDir* parentDir, char* nodeToDeleteName); +void deleteRegFileContents(VolInfo* volInfo, BkFile* file); + +#endif diff --git a/lib/bkisofs/bkError.c b/lib/bkisofs/bkError.c new file mode 100644 index 0000000..d21ebac --- /dev/null +++ b/lib/bkisofs/bkError.c @@ -0,0 +1,98 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include +#include + +#include "bkInternal.h" +#include "bkError.h" + +struct MessageStruct +{ + int number; + const char* text; +}; + +const struct MessageStruct messageStructs[] = +{ + { BKERROR_READ_GENERIC, BKERROR_READ_GENERIC_TEXT }, + { BKERROR_DIR_NOT_FOUND_ON_IMAGE, BKERROR_DIR_NOT_FOUND_ON_IMAGE_TEXT }, + { BKERROR_MAX_NAME_LENGTH_EXCEEDED, BKERROR_MAX_NAME_LENGTH_EXCEEDED_TEXT }, + { BKERROR_STAT_FAILED, BKERROR_STAT_FAILED_TEXT }, + { BKERROR_TARGET_NOT_A_DIR, BKERROR_TARGET_NOT_A_DIR_TEXT }, + { BKERROR_OUT_OF_MEMORY, BKERROR_OUT_OF_MEMORY_TEXT }, + { BKERROR_OPENDIR_FAILED, BKERROR_OPENDIR_FAILED_TEXT }, + { BKERROR_EXOTIC, BKERROR_EXOTIC_TEXT }, + { BKERROR_FIXME, BKERROR_FIXME_TEXT }, + { BKERROR_FILE_NOT_FOUND_ON_IMAGE, BKERROR_FILE_NOT_FOUND_ON_IMAGE_TEXT }, + { BKERROR_MKDIR_FAILED, BKERROR_MKDIR_FAILED_TEXT }, + { BKERROR_OPEN_WRITE_FAILED, BKERROR_OPEN_WRITE_FAILED_TEXT }, + { BKERROR_WRITE_GENERIC, BKERROR_WRITE_GENERIC_TEXT }, + { BKERROR_MANGLE_TOO_MANY_COL, BKERROR_MANGLE_TOO_MANY_COL_TEXT }, + { BKERROR_MISFORMED_PATH, BKERROR_MISFORMED_PATH_TEXT }, + { BKERROR_INVALID_UCS2, BKERROR_INVALID_UCS2_TEXT }, + { BKERROR_UNKNOWN_FILENAME_TYPE, BKERROR_UNKNOWN_FILENAME_TYPE_TEXT }, + { BKERROR_RR_FILENAME_MISSING, BKERROR_RR_FILENAME_MISSING_TEXT }, + { BKERROR_VD_NOT_PRIMARY, BKERROR_VD_NOT_PRIMARY_TEXT }, + { BKERROR_SANITY, BKERROR_SANITY_TEXT }, + { BKERROR_OPEN_READ_FAILED, BKERROR_OPEN_READ_FAILED_TEXT }, + { BKERROR_DIRNAME_NEED_TRAILING_SLASH, BKERROR_DIRNAME_NEED_TRAILING_SLASH_TEXT }, + { BKERROR_EXTRACT_ROOT, BKERROR_EXTRACT_ROOT_TEXT }, + { BKERROR_DELETE_ROOT, BKERROR_DELETE_ROOT_TEXT }, + { BKERROR_DUPLICATE_ADD, BKERROR_DUPLICATE_ADD_TEXT }, + { BKERROR_DUPLICATE_EXTRACT, BKERROR_DUPLICATE_EXTRACT_TEXT }, + { BKERROR_NO_SPECIAL_FILES, BKERROR_NO_SPECIAL_FILES_TEXT }, + { BKERROR_NO_POSIX_PRESENT, BKERROR_NO_POSIX_PRESENT_TEXT }, + { BKERROR_EXTRACT_ABSENT_BOOT_RECORD, BKERROR_EXTRACT_ABSENT_BOOT_RECORD_TEXT }, + { BKERROR_EXTRACT_UNKNOWN_BOOT_MEDIA, BKERROR_EXTRACT_UNKNOWN_BOOT_MEDIA_TEXT }, + { BKERROR_ADD_UNKNOWN_BOOT_MEDIA, BKERROR_ADD_UNKNOWN_BOOT_MEDIA_TEXT }, + { BKERROR_ADD_BOOT_RECORD_WRONG_SIZE, BKERROR_ADD_BOOT_RECORD_WRONG_SIZE_TEXT }, + { BKERROR_WRITE_BOOT_FILE_4, BKERROR_WRITE_BOOT_FILE_4_TEXT }, + { BKERROR_DUPLICATE_CREATE_DIR, BKERROR_DUPLICATE_CREATE_DIR_TEXT }, + { BKERROR_NAME_INVALID_CHAR, BKERROR_NAME_INVALID_CHAR_TEXT }, + { BKERROR_BLANK_NAME, BKERROR_BLANK_NAME_TEXT }, + { BKERROR_ADD_FILE_TOO_BIG, BKERROR_ADD_FILE_TOO_BIG_TEXT }, + { BKERROR_SAVE_OVERWRITE, BKERROR_SAVE_OVERWRITE_TEXT }, + { BKERROR_OPER_CANCELED_BY_USER, BKERROR_OPER_CANCELED_BY_USER_TEXT }, + { BKERROR_WRONG_EXTRACT_FILE, BKERROR_WRONG_EXTRACT_FILE_TEXT }, + { BKERROR_WRITE_CACHE_OVERFLOWED, BKERROR_WRITE_CACHE_OVERFLOWED_TEXT }, + { BKERROR_CREATE_SYMLINK_FAILED, BKERROR_CREATE_SYMLINK_FAILED_TEXT }, + { BKERROR_SYMLINK_TARGET_TOO_LONG, BKERROR_SYMLINK_TARGET_TOO_LONG_TEXT }, + { BKERROR_HARD_LINK_CALL_PARAMS, BKERROR_HARD_LINK_CALL_PARAMS_TEXT }, + { BKERROR_NAME_INVALID, BKERROR_NAME_INVALID_TEXT }, + { BKERROR_DUPLICATE_RENAME, BKERROR_DUPLICATE_RENAME_TEXT }, + { BKERROR_GET_PERM_BAD_PARAM, BKERROR_GET_PERM_BAD_PARAM_TEXT }, + { BKERROR_EDITED_EXTRACT_TOO_BIG, BKERROR_EDITED_EXTRACT_TOO_BIG_TEXT }, + { BKERROR_EDITED_WRITE_TOO_BIG, BKERROR_EDITED_WRITE_TOO_BIG_TEXT }, + + { BKWARNING_OPER_PARTLY_FAILED, BKWARNING_OPER_PARTLY_FAILED_TEXT }, + + { BKERROR_END, BKERROR_END_TEXT } +}; + +const char* bk_get_error_string(int errorId) +{ + int count; + + for(count = 0; messageStructs[count].number != BKERROR_END; count++) + { + if(messageStructs[count].number == errorId) + break; + } + + if(messageStructs[count].number == BKERROR_END) + printf("unknown error %d used\n", errorId); + + return messageStructs[count].text; +} diff --git a/lib/bkisofs/bkError.h b/lib/bkisofs/bkError.h new file mode 100644 index 0000000..4263866 --- /dev/null +++ b/lib/bkisofs/bkError.h @@ -0,0 +1,129 @@ +/****************************************************************************** +* errnum.h +* this file contains #defined ints for return codes (errors and warnings) +* */ + +/* error codes in between these numbers */ +#define BKERROR_MAX_ID -1001 +#define BKERROR_MIN_ID -10000 + +/* warning codes in between these numbers */ +#define BKWARNING_MAX_ID -10001 +#define BKWARNING_MIN_ID -20000 + +/* #define IS_ERROR(number) ( (((number) >= BKERROR_MIN_ID) && ((number) <= BKERROR_MAX_ID)) ? true : false ) +* #define IS_WARNING(number) ( (((number) >= BKWARNING_MIN_ID) && ((number) <= BKWARNING_MAX_ID)) ? true : false )*/ + +#define BKERROR_READ_GENERIC -1001 +#define BKERROR_READ_GENERIC_TEXT "Failed to read expected number of bytes" +#define BKERROR_DIR_NOT_FOUND_ON_IMAGE -1002 +#define BKERROR_DIR_NOT_FOUND_ON_IMAGE_TEXT "Directory not found on image" +#define BKERROR_MAX_NAME_LENGTH_EXCEEDED -1003 +#define BKERROR_MAX_NAME_LENGTH_EXCEEDED_TEXT "Maximum file/directory name length exceeded" +#define BKERROR_STAT_FAILED -1004 +#define BKERROR_STAT_FAILED_TEXT "Failed to stat file/directory" +#define BKERROR_TARGET_NOT_A_DIR -1005 +#define BKERROR_TARGET_NOT_A_DIR_TEXT "Target not a directory (UI problem)" +#define BKERROR_OUT_OF_MEMORY -1006 +#define BKERROR_OUT_OF_MEMORY_TEXT "Out of memory" +#define BKERROR_OPENDIR_FAILED -1007 +#define BKERROR_OPENDIR_FAILED_TEXT "Failed to open directory for listing" +#define BKERROR_EXOTIC -1008 +#define BKERROR_EXOTIC_TEXT "Some really exotic problem happened" +#define BKERROR_FIXME -1009 +#define BKERROR_FIXME_TEXT "Incomplete/broken something that the author needs to fix, please report bug" +#define BKERROR_FILE_NOT_FOUND_ON_IMAGE -1010 +#define BKERROR_FILE_NOT_FOUND_ON_IMAGE_TEXT "File not found on image" +#define BKERROR_MKDIR_FAILED -1011 +#define BKERROR_MKDIR_FAILED_TEXT "Failed to create directory on the filesystem" +#define BKERROR_OPEN_WRITE_FAILED -1012 +#define BKERROR_OPEN_WRITE_FAILED_TEXT "Failed to open file on the filesystem for writing" +#define BKERROR_WRITE_GENERIC -1013 +#define BKERROR_WRITE_GENERIC_TEXT "Failed to write expected number of bytes (disk full?)" +#define BKERROR_MANGLE_TOO_MANY_COL -1014 +#define BKERROR_MANGLE_TOO_MANY_COL_TEXT "Too many collisons while mangling filenames (too many files/directories with a similar name)" +#define BKERROR_MISFORMED_PATH -1015 +#define BKERROR_MISFORMED_PATH_TEXT "Misformed path" +#define BKERROR_INVALID_UCS2 -1016 +#define BKERROR_INVALID_UCS2_TEXT "Invalid UCS-2 string" +#define BKERROR_UNKNOWN_FILENAME_TYPE -1017 +#define BKERROR_UNKNOWN_FILENAME_TYPE_TEXT "Unknown filename type" +#define BKERROR_RR_FILENAME_MISSING -1018 +#define BKERROR_RR_FILENAME_MISSING_TEXT "Rockridge filename missing when expected on image" +#define BKERROR_VD_NOT_PRIMARY -1019 +#define BKERROR_VD_NOT_PRIMARY_TEXT "First volume descriptor type not primary like ISO9660 requires" +#define BKERROR_SANITY -1020 +#define BKERROR_SANITY_TEXT "Internal library failure (sanity check), please report bug" +#define BKERROR_OPEN_READ_FAILED -1021 +#define BKERROR_OPEN_READ_FAILED_TEXT "Failed to open file on the filesystem for reading" +#define BKERROR_DIRNAME_NEED_TRAILING_SLASH -1022 +#define BKERROR_DIRNAME_NEED_TRAILING_SLASH_TEXT "String specifying directory name must end with '/'" +#define BKERROR_EXTRACT_ROOT -1023 +#define BKERROR_EXTRACT_ROOT_TEXT "Extracting root of iso not allowed" +#define BKERROR_DELETE_ROOT -1024 +#define BKERROR_DELETE_ROOT_TEXT "Deleting root of iso not allowed" +#define BKERROR_DUPLICATE_ADD -1025 +#define BKERROR_DUPLICATE_ADD_TEXT "Cannot add item because another item with the same name already exists in this directory" +#define BKERROR_DUPLICATE_EXTRACT -1026 +#define BKERROR_DUPLICATE_EXTRACT_TEXT "Cannot extract item because another item with the same name already exists in this directory" +#define BKERROR_NO_SPECIAL_FILES -1027 +#define BKERROR_NO_SPECIAL_FILES_TEXT "Special files (device files and such) are not supported" +#define BKERROR_NO_POSIX_PRESENT -1028 +#define BKERROR_NO_POSIX_PRESENT_TEXT "No posix extentions found" +#define BKERROR_EXTRACT_ABSENT_BOOT_RECORD -1029 +#define BKERROR_EXTRACT_ABSENT_BOOT_RECORD_TEXT "Cannot extract boot record because there isn't one one the image" +#define BKERROR_EXTRACT_UNKNOWN_BOOT_MEDIA -1030 +#define BKERROR_EXTRACT_UNKNOWN_BOOT_MEDIA_TEXT "Unable to extract boot record of unknown media type" +#define BKERROR_ADD_UNKNOWN_BOOT_MEDIA -1031 +#define BKERROR_ADD_UNKNOWN_BOOT_MEDIA_TEXT "Unable to add boot record with unknown media type" +#define BKERROR_ADD_BOOT_RECORD_WRONG_SIZE -1032 +#define BKERROR_ADD_BOOT_RECORD_WRONG_SIZE_TEXT "Size of boot record on the filesystem does not match the size requested via the boot record type parameter" +#define BKERROR_WRITE_BOOT_FILE_4 -1033 +#define BKERROR_WRITE_BOOT_FILE_4_TEXT "Size of no emulation boot record visible on image must be divisible by 4 so i can do a checksum (invalid boot file?)" +#define BKERROR_DUPLICATE_CREATE_DIR -1034 +#define BKERROR_DUPLICATE_CREATE_DIR_TEXT "Cannot create directory because another file or directory with the same name exists" +#define BKERROR_NAME_INVALID_CHAR -1035 +#define BKERROR_NAME_INVALID_CHAR_TEXT "Name contains invalid character(s)" +#define BKERROR_BLANK_NAME -1036 +#define BKERROR_BLANK_NAME_TEXT "Name cannot be blank" +#define BKERROR_ADD_FILE_TOO_BIG -1037 +#define BKERROR_ADD_FILE_TOO_BIG_TEXT "Cannot add file larger than 4294967295 bytes because the ISO filesystem does not support such large files" +#define BKERROR_SAVE_OVERWRITE -1038 +#define BKERROR_SAVE_OVERWRITE_TEXT "Cannot overwrite original image when saving" +#define BKERROR_OPER_CANCELED_BY_USER -1039 +#define BKERROR_OPER_CANCELED_BY_USER_TEXT "You have canceled the operation" +#define BKERROR_NOT_DIR_IN_PATH -1040 +#define BKERROR_NOT_DIR_IN_PATH_TEXT "One of the names in the path is not a directory" +#define BKERROR_WRONG_EXTRACT_FILE -1041 +#define BKERROR_WRONG_EXTRACT_FILE_TEXT "Tried to extract something that's not a file using the file extracting function" +#define BKERROR_NOT_REG_FILE_FOR_BR -1042 +#define BKERROR_NOT_REG_FILE_FOR_BR_TEXT "Can only use a regular file as a boot record" +#define BKERROR_WRITE_CACHE_OVERFLOWED -1043 +#define BKERROR_WRITE_CACHE_OVERFLOWED_TEXT "Write cache overflowed, please report bug" +#define BKERROR_CREATE_SYMLINK_FAILED -1044 +#define BKERROR_CREATE_SYMLINK_FAILED_TEXT "Failed to create symbolic link" +#define BKERROR_SYMLINK_TARGET_TOO_LONG -1045 +#define BKERROR_SYMLINK_TARGET_TOO_LONG_TEXT "Too many characters in target of a symbolic link" +#define BKERROR_HARD_LINK_CALL_PARAMS -1046 +#define BKERROR_HARD_LINK_CALL_PARAMS_TEXT "Call to a hard link function with both a 0 offset and a NULL filename not allowed" +#define BKERROR_NAME_INVALID -1047 +#define BKERROR_NAME_INVALID_TEXT "Invalid file/directory name" +#define BKERROR_RENAME_ROOT -1048 +#define BKERROR_RENAME_ROOT_TEXT "Cannot rename the root directory" +#define BKERROR_ITEM_NOT_FOUND_ON_IMAGE -1049 +#define BKERROR_ITEM_NOT_FOUND_ON_IMAGE_TEXT "Item not found on image" +#define BKERROR_DUPLICATE_RENAME -1050 +#define BKERROR_DUPLICATE_RENAME_TEXT "Cannot rename item because another file or directory with the same name exists" +#define BKERROR_GET_PERM_BAD_PARAM -1051 +#define BKERROR_GET_PERM_BAD_PARAM_TEXT "bk_get_permissions() called with NULL mode_t*" +#define BKERROR_EDITED_EXTRACT_TOO_BIG -1052 +#define BKERROR_EDITED_EXTRACT_TOO_BIG_TEXT "You edited the file and it's now too big for the .iso format to handle, so can't extract it" +#define BKERROR_EDITED_WRITE_TOO_BIG -1053 +#define BKERROR_EDITED_WRITE_TOO_BIG_TEXT "You edited the file and it's now too big for the .iso format to handle, so can't write it" + +#define BKWARNING_OPER_PARTLY_FAILED -10001 +#define BKWARNING_OPER_PARTLY_FAILED_TEXT "Operation was only partially successful or perhaps completely unsuccessful" + +/* do not make up #defines with numbers lower then this */ +#define BKERROR_END -1000000 +#define BKERROR_END_TEXT "Double oops, unusable error number used" diff --git a/lib/bkisofs/bkExtract.c b/lib/bkisofs/bkExtract.c new file mode 100644 index 0000000..bda153c --- /dev/null +++ b/lib/bkisofs/bkExtract.c @@ -0,0 +1,490 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "bk.h" +#include "bkInternal.h" +#include "bkExtract.h" +#include "bkPath.h" +#include "bkError.h" +#include "bkMisc.h" +#include "bkIoWrappers.h" + +/******************************************************************************* +* bk_extract_boot_record() +* Extracts the el torito boot record to the file destPathAndName, with +* permissions destFilePerms. +* */ +int bk_extract_boot_record(VolInfo* volInfo, const char* destPathAndName, + unsigned destFilePerms) +{ + int srcFile; /* returned by open() */ + bool srcFileWasOpened; + int destFile; /* returned by open() */ + int rc; + + if(volInfo->bootMediaType == BOOT_MEDIA_NONE) + return BKERROR_EXTRACT_ABSENT_BOOT_RECORD; + + if(volInfo->bootMediaType != BOOT_MEDIA_NO_EMULATION && + volInfo->bootMediaType != BOOT_MEDIA_1_2_FLOPPY && + volInfo->bootMediaType != BOOT_MEDIA_1_44_FLOPPY && + volInfo->bootMediaType != BOOT_MEDIA_2_88_FLOPPY) + { + return BKERROR_EXTRACT_UNKNOWN_BOOT_MEDIA; + } + + /* SET source file (open if needed) */ + if(volInfo->bootRecordIsVisible) + /* boot record is a file in the tree */ + { + if(volInfo->bootRecordOnImage->onImage) + { + srcFile = volInfo->imageForReading; + readSeekSet(volInfo, volInfo->bootRecordOnImage->position, SEEK_SET); + srcFileWasOpened = false; + } + else + { + srcFile = open(volInfo->bootRecordOnImage->pathAndName, O_RDONLY, 0); + if(srcFile == -1) + return BKERROR_OPEN_READ_FAILED; + srcFileWasOpened = true; + } + } + else + /* boot record is not a file in the tree */ + { + if(volInfo->bootRecordIsOnImage) + { + srcFile = volInfo->imageForReading; + readSeekSet(volInfo, volInfo->bootRecordOffset, SEEK_SET); + srcFileWasOpened = false; + } + else + { + srcFile = open(volInfo->bootRecordPathAndName, O_RDONLY, 0); + if(srcFile == -1) + return BKERROR_OPEN_READ_FAILED; + srcFileWasOpened = true; + } + } + /* END SET source file (open if needed) */ + + destFile = open(destPathAndName, O_WRONLY | O_CREAT | O_TRUNC, + destFilePerms); + if(destFile == -1) + { + if(srcFileWasOpened) + bkClose(srcFile); + return BKERROR_OPEN_WRITE_FAILED; + } + + rc = copyByteBlock(volInfo, srcFile, destFile, volInfo->bootRecordSize); + + bkClose(destFile); + if(srcFileWasOpened) + bkClose(srcFile); + + if(rc <= 0) + return rc; + + return 1; +} + +int bk_extract(VolInfo* volInfo, const char* srcPathAndName, + const char* destDir, bool keepPermissions, + void(*progressFunction)(VolInfo*)) +{ + return bk_extract_as(volInfo, srcPathAndName, destDir, NULL, + keepPermissions, progressFunction); +} + +int bk_extract_as(VolInfo* volInfo, const char* srcPathAndName, + const char* destDir, const char* nameToUse, + bool keepPermissions, void(*progressFunction)(VolInfo*)) +{ + int rc; + NewPath srcPath; + BkDir* parentDir; + bool dirFound; + + volInfo->progressFunction = progressFunction; + volInfo->stopOperation = false; + + rc = makeNewPathFromString(srcPathAndName, &srcPath); + if(rc <= 0) + { + freePathContents(&srcPath); + return rc; + } + + if(srcPath.numChildren == 0) + { + freePathContents(&srcPath); + return BKERROR_EXTRACT_ROOT; + } + + /* i want the parent directory */ + srcPath.numChildren--; + dirFound = findDirByNewPath(&srcPath, &(volInfo->dirTree), &parentDir); + srcPath.numChildren++; + if(!dirFound) + { + freePathContents(&srcPath); + return BKERROR_DIR_NOT_FOUND_ON_IMAGE; + } + + rc = extract(volInfo, parentDir, srcPath.children[srcPath.numChildren - 1], + destDir, nameToUse, keepPermissions); + + freePathContents(&srcPath); + + if(rc <= 0) + { + return rc; + } + + return 1; +} + +int copyByteBlock(VolInfo* volInfo, int src, int dest, unsigned numBytes) +{ + int rc; + int count; + int numBlocks; + int sizeLastBlock; + + numBlocks = numBytes / READ_WRITE_BUFFER_SIZE; + sizeLastBlock = numBytes % READ_WRITE_BUFFER_SIZE; + + maybeUpdateProgress(volInfo); + if(volInfo->stopOperation) + return BKERROR_OPER_CANCELED_BY_USER; + + for(count = 0; count < numBlocks; count++) + { + maybeUpdateProgress(volInfo); + if(volInfo->stopOperation) + return BKERROR_OPER_CANCELED_BY_USER; + + rc = bkRead(src, volInfo->readWriteBuffer, READ_WRITE_BUFFER_SIZE); + if(rc != READ_WRITE_BUFFER_SIZE) + return BKERROR_READ_GENERIC; + rc = bkWrite(dest, volInfo->readWriteBuffer, READ_WRITE_BUFFER_SIZE); + if(rc <= 0) + return rc; + } + + if(sizeLastBlock > 0) + { + rc = bkRead(src, volInfo->readWriteBuffer, sizeLastBlock); + if(rc != sizeLastBlock) + return BKERROR_READ_GENERIC; + rc = bkWrite(dest, volInfo->readWriteBuffer, sizeLastBlock); + if(rc <= 0) + return rc; + } + + return 1; +} + +int extract(VolInfo* volInfo, BkDir* parentDir, char* nameToExtract, + const char* destDir, const char* nameToUse, bool keepPermissions) +{ + BkFileBase* child; + int rc; + + child = parentDir->children; + while(child != NULL) + { + if(volInfo->stopOperation) + return BKERROR_OPER_CANCELED_BY_USER; + + if(strcmp(child->name, nameToExtract) == 0) + { + if( IS_DIR(child->posixFileMode) ) + { + rc = extractDir(volInfo, BK_DIR_PTR(child), destDir, + nameToUse, keepPermissions); + } + else if ( IS_REG_FILE(child->posixFileMode) ) + { + rc = extractFile(volInfo, BK_FILE_PTR(child), destDir, + nameToUse, keepPermissions); + } + else if ( IS_SYMLINK(child->posixFileMode) ) + { + rc = extractSymlink(BK_SYMLINK_PTR(child), destDir, + nameToUse); + } + else + { + rc = 1; + printf("trying to extract something that's not a file, " + "symlink or directory, ignored\n");fflush(NULL); + } + + if(rc <= 0) + { + bool goOn; + + if(volInfo->warningCbk != NULL && !volInfo->stopOperation) + /* perhaps the user wants to ignore this failure */ + { + snprintf(volInfo->warningMessage, BK_WARNING_MAX_LEN, + "Failed to extract item '%s': '%s'", + child->name, + bk_get_error_string(rc)); + goOn = volInfo->warningCbk(volInfo->warningMessage); + rc = BKWARNING_OPER_PARTLY_FAILED; + } + else + goOn = false; + + if(!goOn) + { + volInfo->stopOperation = true; + return rc; + } + } + } + + child = child->next; + } + + return 1; +} + +int extractDir(VolInfo* volInfo, BkDir* srcDir, const char* destDir, + const char* nameToUse, bool keepPermissions) +{ + int rc; + BkFileBase* child; + + /* vars to create destination dir */ + char* newDestDir; + unsigned destDirPerms; + + /* CREATE destination dir on filesystem */ + /* 1 for '\0' */ + if(nameToUse == NULL) + newDestDir = malloc(strlen(destDir) + strlen(BK_BASE_PTR(srcDir)->name) + 2); + else + newDestDir = malloc(strlen(destDir) + strlen(nameToUse) + 2); + if(newDestDir == NULL) + return BKERROR_OUT_OF_MEMORY; + + strcpy(newDestDir, destDir); + if(destDir[strlen(destDir) - 1] != '/') + strcat(newDestDir, "/"); + + if(nameToUse == NULL) + strcat(newDestDir, BK_BASE_PTR(srcDir)->name); + else + strcat(newDestDir, nameToUse); + + if(keepPermissions) + destDirPerms = BK_BASE_PTR(BK_BASE_PTR(srcDir))->posixFileMode; + else + destDirPerms = volInfo->posixDirDefaults; + /* want to make sure user has write and execute permissions to new directory + * so that can extract stuff into it */ + destDirPerms |= 0300; + + if(access(newDestDir, F_OK) == 0) + { + free(newDestDir); + return BKERROR_DUPLICATE_EXTRACT; + } + + rc = mkdir(newDestDir, destDirPerms); + if(rc == -1) + { + free(newDestDir); + return BKERROR_MKDIR_FAILED; + } + /* END CREATE destination dir on filesystem */ + + /* EXTRACT children */ + child = srcDir->children; + while(child != NULL) + { + rc = extract(volInfo, srcDir, child->name, newDestDir, + NULL, keepPermissions); + if(rc <= 0) + { + free(newDestDir); + return rc; + } + + child = child->next; + } + /* END EXTRACT children */ + + free(newDestDir); + + return 1; +} + +int extractFile(VolInfo* volInfo, BkFile* srcFileInTree, const char* destDir, + const char* nameToUse, bool keepPermissions) +{ + int srcFile; + bool srcFileWasOpened; + char* destPathAndName; + unsigned destFilePerms; + int destFile; /* returned by open() */ + int rc; + BkStatStruct statStruct; + + if(srcFileInTree->onImage) + { + srcFile = volInfo->imageForReading; + bkSeekSet(volInfo->imageForReading, srcFileInTree->position, SEEK_SET); + srcFileWasOpened = false; + } + else + { + srcFile = open(srcFileInTree->pathAndName, O_RDONLY, 0); + if(srcFile == -1) + return BKERROR_OPEN_READ_FAILED; + srcFileWasOpened = true; + + /* UPDATE the file's size, in case it's changed since we added it */ + rc = bkStat(srcFileInTree->pathAndName, &statStruct); + if(rc != 0) + return BKERROR_STAT_FAILED; + + if(statStruct.st_size > 0xFFFFFFFF) + /* size won't fit in a 32bit variable on the iso */ + return BKERROR_EDITED_EXTRACT_TOO_BIG; + + srcFileInTree->size = statStruct.st_size; + /* UPDATE the file's size, in case it's changed since we added it */ + } + + if(nameToUse == NULL) + destPathAndName = malloc(strlen(destDir) + + strlen(BK_BASE_PTR(srcFileInTree)->name) + 2); + else + destPathAndName = malloc(strlen(destDir) + strlen(nameToUse) + 2); + if(destPathAndName == NULL) + { + if(srcFileWasOpened) + bkClose(srcFile); + return BKERROR_OUT_OF_MEMORY; + } + + strcpy(destPathAndName, destDir); + if(destDir[strlen(destDir) - 1] != '/') + strcat(destPathAndName, "/"); + if(nameToUse == NULL) + strcat(destPathAndName, BK_BASE_PTR(srcFileInTree)->name); + else + strcat(destPathAndName, nameToUse); + + if(access(destPathAndName, F_OK) == 0) + { + if(srcFileWasOpened) + bkClose(srcFile); + free(destPathAndName); + return BKERROR_DUPLICATE_EXTRACT; + } + + /* WRITE file */ + if(keepPermissions) + destFilePerms = BK_BASE_PTR(srcFileInTree)->posixFileMode; + else + destFilePerms = volInfo->posixFileDefaults; + + destFile = open(destPathAndName, O_WRONLY | O_CREAT | O_TRUNC, destFilePerms); + if(destFile == -1) + { + if(srcFileWasOpened) + bkClose(srcFile); + free(destPathAndName); + return BKERROR_OPEN_WRITE_FAILED; + } + + free(destPathAndName); + + rc = copyByteBlock(volInfo, srcFile, destFile, srcFileInTree->size); + if(rc < 0) + { + bkClose(destFile); + if(srcFileWasOpened) + bkClose(srcFile); + return rc; + } + + bkClose(destFile); + if(destFile == -1) + { + if(srcFileWasOpened) + bkClose(srcFile); + return BKERROR_EXOTIC; + } + /* END WRITE file */ + + if(srcFileWasOpened) + bkClose(srcFile); + + return 1; +} + +int extractSymlink(BkSymLink* srcLink, const char* destDir, + const char* nameToUse) +{ + char* destPathAndName; + int rc; + + if(nameToUse == NULL) + destPathAndName = malloc(strlen(destDir) + + strlen(BK_BASE_PTR(srcLink)->name) + 2); + else + destPathAndName = malloc(strlen(destDir) + strlen(nameToUse) + 2); + if(destPathAndName == NULL) + return BKERROR_OUT_OF_MEMORY; + + strcpy(destPathAndName, destDir); + if(destDir[strlen(destDir) - 1] != '/') + strcat(destPathAndName, "/"); + if(nameToUse == NULL) + strcat(destPathAndName, BK_BASE_PTR(srcLink)->name); + else + strcat(destPathAndName, nameToUse); + + if(access(destPathAndName, F_OK) == 0) + { + free(destPathAndName); + return BKERROR_DUPLICATE_EXTRACT; + } + + rc = symlink(srcLink->target, destPathAndName); + if(rc == -1) + { + free(destPathAndName); + return BKERROR_CREATE_SYMLINK_FAILED; + } + + free(destPathAndName); + + return 1; +} diff --git a/lib/bkisofs/bkExtract.h b/lib/bkisofs/bkExtract.h new file mode 100644 index 0000000..b29596e --- /dev/null +++ b/lib/bkisofs/bkExtract.h @@ -0,0 +1,9 @@ +int copyByteBlock(VolInfo* volInfo, int src, int dest, unsigned numBytes); +int extract(VolInfo* volInfo, BkDir* parentDir, char* nameToExtract, + const char* destDir, const char* nameToUse, bool keepPermissions); +int extractDir(VolInfo* volInfo, BkDir* srcDir, const char* destDir, + const char* nameToUse, bool keepPermissions); +int extractFile(VolInfo* volInfo, BkFile* srcFileInTree, const char* destDir, + const char* nameToUse, bool keepPermissions); +int extractSymlink(BkSymLink* srcLink, const char* destDir, + const char* nameToUse); diff --git a/lib/bkisofs/bkGet.c b/lib/bkisofs/bkGet.c new file mode 100644 index 0000000..8bf0d36 --- /dev/null +++ b/lib/bkisofs/bkGet.c @@ -0,0 +1,257 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include +#include +#include + +#include "bk.h" +#include "bkInternal.h" +#include "bkError.h" +#include "bkGet.h" +#include "bkPath.h" + +/******************************************************************************* +* bk_estimate_iso_size() +* Public function +* Estimates the size of the directory trees + file contents on the iso +* */ +bk_off_t bk_estimate_iso_size(const VolInfo* volInfo, int filenameTypes) +{ + /* reset alreadyCounted flags */ + BkHardLink* currentLink; + currentLink = volInfo->fileLocations; + while(currentLink != NULL) + { + currentLink->alreadyCounted = false; + + currentLink = currentLink->next; + } + + return estimateIsoSize(&(volInfo->dirTree), filenameTypes); +} + +/******************************************************************************* +* bk_get_creation_time() +* Public function +* */ +time_t bk_get_creation_time(const VolInfo* volInfo) +{ + return volInfo->creationTime; +} + +/******************************************************************************* +* bk_get_dir_from_string() +* public function +* gets a pointer to a Dir in tree described by the string pathStr +* */ +int bk_get_dir_from_string(const VolInfo* volInfo, const char* pathStr, + BkDir** dirFoundPtr) +{ + return getDirFromString(&(volInfo->dirTree), pathStr, dirFoundPtr); +} + +/******************************************************************************* +* bk_get_permissions() +* public function +* gets the permissions (not all of the posix info) for an item (file, dir, etc.) +* */ +int bk_get_permissions(VolInfo* volInfo, const char* pathAndName, + mode_t* permissions) +{ + int rc; + NewPath srcPath; + BkFileBase* base; + bool itemFound; + + if(permissions == NULL) + return BKERROR_GET_PERM_BAD_PARAM; + + rc = makeNewPathFromString(pathAndName, &srcPath); + if(rc <= 0) + { + freePathContents(&srcPath); + return rc; + } + + itemFound = findBaseByNewPath(&srcPath, &(volInfo->dirTree), &base); + + freePathContents(&srcPath); + + if(!itemFound) + return BKERROR_ITEM_NOT_FOUND_ON_IMAGE; + + *permissions = base->posixFileMode & 0777; + + return 1; +} + +/******************************************************************************* +* bk_get_publisher() +* Public function +* Returns a pointer to the string in volInfo that holds the volume name. +* */ +const char* bk_get_publisher(const VolInfo* volInfo) +{ + return volInfo->publisher; +} + +/******************************************************************************* +* bk_get_volume_name() +* Public function +* Returns a pointer to the string in volInfo that holds the volume name. +* */ +const char* bk_get_volume_name(const VolInfo* volInfo) +{ + return volInfo->volId; +} + +/******************************************************************************* +* estimateIsoSize() +* Recursive +* Estimate the size of the directory trees + file contents on the iso +* */ +bk_off_t estimateIsoSize(const BkDir* tree, int filenameTypes) +{ + bk_off_t estimateDrSize; + bk_off_t thisDirSize; + int numItems; /* files and directories */ + BkFileBase* child; + + thisDirSize = 0; + numItems = 0; + + child = tree->children; + while(child != NULL) + { + if(IS_DIR(child->posixFileMode)) + { + thisDirSize += estimateIsoSize(BK_DIR_PTR(child), filenameTypes); + } + else if(IS_REG_FILE(child->posixFileMode)) + { + if(BK_FILE_PTR(child)->location == NULL || + !BK_FILE_PTR(child)->location->alreadyCounted) + { + thisDirSize += BK_FILE_PTR(child)->size; + thisDirSize += BK_FILE_PTR(child)->size % NBYTES_LOGICAL_BLOCK; + } + if(BK_FILE_PTR(child)->location != NULL) + BK_FILE_PTR(child)->location->alreadyCounted = true; + } + + numItems++; + + child = child->next; + } + + estimateDrSize = 70; + if(filenameTypes & FNTYPE_JOLIET) + estimateDrSize += 70; + if(filenameTypes & FNTYPE_ROCKRIDGE) + estimateDrSize += 70; + + thisDirSize += 68 + (estimateDrSize * numItems); + thisDirSize += NBYTES_LOGICAL_BLOCK - (68 + (estimateDrSize * numItems)) % NBYTES_LOGICAL_BLOCK; + + return thisDirSize; +} + +/******************************************************************************* +* getDirFromString() +* recursive +* gets a pointer to a Dir in tree described by the string pathStr +* */ +int getDirFromString(const BkDir* tree, const char* pathStr, BkDir** dirFoundPtr) +{ + size_t count; + size_t pathStrLen; + bool stopLooking; + /* name of the directory in the path this instance of the function works on */ + char* currentDirName; + BkFileBase* child; + int rc; + + pathStrLen = strlen(pathStr); + + if(pathStrLen == 1 && pathStr[0] == '/') + /* root, special case */ + { + /* cast to prevent compiler const warning */ + *dirFoundPtr = (BkDir*)tree; + return 1; + } + + if(pathStrLen < 3 || pathStr[0] != '/' || pathStr[1] == '/' || + pathStr[pathStrLen - 1] != '/') + return BKERROR_MISFORMED_PATH; + + stopLooking = false; + for(count = 2; count < pathStrLen && !stopLooking; count++) + /* find the first directory in the path */ + { + if(pathStr[count] == '/') + /* found it */ + { + /* make a copy of the string to use with strcmp */ + currentDirName = (char*)malloc(count); + if(currentDirName == NULL) + return BKERROR_OUT_OF_MEMORY; + + strncpy(currentDirName, &(pathStr[1]), count - 1); + currentDirName[count - 1] = '\0'; + + child = tree->children; + while(child != NULL && !stopLooking) + /* each child directory in tree */ + { + if( strcmp(child->name, currentDirName) == 0 && + IS_DIR(child->posixFileMode) ) + /* found the right child directory */ + { + if(pathStr[count + 1] == '\0') + /* this is the directory i'm looking for */ + { + *dirFoundPtr = BK_DIR_PTR(child); + stopLooking = true; + rc = 1; + } + else + /* intermediate directory, go further down the tree */ + { + rc = getDirFromString(BK_DIR_PTR(child), + &(pathStr[count]), dirFoundPtr); + if(rc <= 0) + { + free(currentDirName); + return rc; + } + stopLooking = true; + } + + } + + child = child->next; + } + + free(currentDirName); + + if(!stopLooking) + return BKERROR_DIR_NOT_FOUND_ON_IMAGE; + } /* if(found it) */ + } /* for(find the first directory in the path) */ + + /* can't see how i could get here but to keep the compiler happy */ + return 1; +} diff --git a/lib/bkisofs/bkGet.h b/lib/bkisofs/bkGet.h new file mode 100644 index 0000000..9546da1 --- /dev/null +++ b/lib/bkisofs/bkGet.h @@ -0,0 +1,3 @@ +bk_off_t estimateIsoSize(const BkDir* tree, int filenameTypes); +int getDirFromString(const BkDir* tree, const char* pathStr, + BkDir** dirFoundPtr); diff --git a/lib/bkisofs/bkInternal.h b/lib/bkisofs/bkInternal.h new file mode 100644 index 0000000..59c8139 --- /dev/null +++ b/lib/bkisofs/bkInternal.h @@ -0,0 +1,105 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +/********************************* PURPOSE ************************************ +* bkInternal.h +* This header file is for #defines and structures only used by bkisofs +******************************** END PURPOSE *********************************/ + +#ifndef bkInternal_h +#define bkInternal_h + +#include "bk.h" + +/* number of logical sectors in system area (in practice always 16) */ +#define NLS_SYSTEM_AREA 16 +/* number of bytes in a logical block (in practice always 2048) */ +#define NBYTES_LOGICAL_BLOCK 2048 + +/******************************************************************************* +* Joliet allows max 128 bytes +* + 2 separator1 (9660, just in case) +* + 2 separator2 (9660, just in case) +* + 10 version (9660, just in case) +* = 142 bytes (71 characters) +* Only a max of 64 characters of this will be stored. (plus '\0') */ +#define NCHARS_FILE_ID_MAX_JOLIET 65 + +#define NBYTES_FILE_ID_MAX_9660 15 /* 8.3 + ";1" */ + +#define BASETW_PTR(item) ((BaseToWrite*)(item)) +#define DIRTW_PTR(item) ((DirToWrite*)(item)) +#define FILETW_PTR(item) ((FileToWrite*)(item)) +#define SYMLINKTW_PTR(item) ((SymLinkToWrite*)(item)) + +#define WRITE_CACHE_SIZE 1048576 + +typedef struct +{ + unsigned numChildren; + char** children; + +} NewPath; + +typedef struct BaseToWrite +{ + char name9660[NBYTES_FILE_ID_MAX_9660]; /* 8.3 + ";1" max */ + char nameRock[NCHARS_FILE_ID_MAX_STORE]; + char nameJoliet[NCHARS_FILE_ID_MAX_JOLIET]; + unsigned posixFileMode; + bk_off_t extentLocationOffset; /* where on image to write location of extent */ + unsigned extentNumber; /* extent number */ + bk_off_t extentLocationOffset2; /* for svd (joliet) */ + bk_off_t offsetForCE; /* if the name won't fit inside the directory record */ + + struct BaseToWrite* next; + +} BaseToWrite; + +typedef struct DirToWrite +{ + BaseToWrite base; + + unsigned extentNumber2; /* for svd (joliet) */ + unsigned dataLength; /* bytes, including blank */ + unsigned dataLength2; /* for svd (joliet) */ + struct BaseToWrite* children; + +} DirToWrite; + +typedef struct FileToWrite +{ + BaseToWrite base; + + unsigned size; /* in bytes */ + BkHardLink* location; /* basically a copy of the following variables */ + bool onImage; + unsigned offset; /* if on image, in bytes */ + char* pathAndName; /* if on filesystem, full path + filename + * is to be freed by whenever the File is freed */ + BkFile* origFile; /* this pointer only has one purpose: to potentially + * identify this file as the boot record. it will never + * be dereferenced, just compared to. */ + +} FileToWrite; + +typedef struct SymLinkToWrite +{ + BaseToWrite base; + + char target[NCHARS_SYMLINK_TARGET_MAX]; + +} SymLinkToWrite; + +#endif diff --git a/lib/bkisofs/bkIoWrappers.c b/lib/bkisofs/bkIoWrappers.c new file mode 100644 index 0000000..67ca41d --- /dev/null +++ b/lib/bkisofs/bkIoWrappers.c @@ -0,0 +1,84 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include +#include +#include + +#include "bkInternal.h" +#include "bkIoWrappers.h" + +void bkClose(int file) +{ + close(file); +} + +int bkFstat(int file, BkStatStruct* statStruct) +{ + return fstat(file, statStruct); +} + +size_t bkRead(int file, void* dest, size_t numBytes) +{ + return read(file, dest, numBytes); +} + +/****************************************************************************** +* bkSeekSet() +* */ +bk_off_t bkSeekSet(int file, bk_off_t offset, int origin) +{ + return lseek(file, offset, origin); +} + +/****************************************************************************** +* bkSeekTell() +* */ +bk_off_t bkSeekTell(int file) +{ + return lseek(file, 0, SEEK_CUR); +} + +int bkStat(const char* pathAndName, BkStatStruct* statStruct) +{ + return stat(pathAndName, statStruct); +} + +size_t bkWrite(int file, const void* src, size_t numBytes) +{ + return write(file, src, numBytes); +} + +size_t readRead(VolInfo* volInfo, void* dest, size_t numBytes) +{ + return read(volInfo->imageForReading, dest, numBytes); +} + +/****************************************************************************** +* readSeekSet() +* Seek set for reading from the iso +* */ +bk_off_t readSeekSet(VolInfo* volInfo, bk_off_t offset, int origin) +{ + return lseek(volInfo->imageForReading, offset, origin); +} + +/****************************************************************************** +* readSeekTell() +* Seek tell for reading from the iso +* */ +bk_off_t readSeekTell(VolInfo* volInfo) +{ + return lseek(volInfo->imageForReading, 0, SEEK_CUR); +} diff --git a/lib/bkisofs/bkIoWrappers.h b/lib/bkisofs/bkIoWrappers.h new file mode 100644 index 0000000..9032789 --- /dev/null +++ b/lib/bkisofs/bkIoWrappers.h @@ -0,0 +1,10 @@ +void bkClose(int file); +int bkFstat(int file, BkStatStruct* statStruct); +size_t bkRead(int file, void* dest, size_t numBytes); +bk_off_t bkSeekSet(int file, bk_off_t offset, int origin); +bk_off_t bkSeekTell(int file); +int bkStat(const char* pathAndName, BkStatStruct* statStruct); +size_t bkWrite(int file, const void* src, size_t numBytes); +size_t readRead(VolInfo* volInfo, void* dest, size_t numBytes); +bk_off_t readSeekSet(VolInfo* volInfo, bk_off_t offset, int origin); +bk_off_t readSeekTell(VolInfo* volInfo); diff --git a/lib/bkisofs/bkLink.c b/lib/bkisofs/bkLink.c new file mode 100644 index 0000000..eaab8ff --- /dev/null +++ b/lib/bkisofs/bkLink.c @@ -0,0 +1,276 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "bkInternal.h" +#include "bkLink.h" +#include "bkIoWrappers.h" + +int addToHardLinkTable(VolInfo* volInfo, bk_off_t position, char* pathAndName, + unsigned size, bool onImage, BkHardLink** newLink) +{ + int rc; + + *newLink = malloc(sizeof(BkHardLink)); + if(*newLink == NULL) + return BKERROR_OUT_OF_MEMORY; + + memset(*newLink, 0, sizeof(BkHardLink)); + + (*newLink)->onImage = onImage; + (*newLink)->position = position; + if(pathAndName != NULL) + { + (*newLink)->pathAndName = malloc(strlen(pathAndName) + 1); + if((*newLink)->pathAndName == NULL) + return BKERROR_OUT_OF_MEMORY; + strcpy((*newLink)->pathAndName, pathAndName); + } + (*newLink)->size = size; + (*newLink)->next = volInfo->fileLocations; + + if(size < MAX_NBYTES_HARDLINK_HEAD) + (*newLink)->headSize = size; + else + (*newLink)->headSize = MAX_NBYTES_HARDLINK_HEAD; + + rc = readFileHead(volInfo, position, pathAndName, (*newLink)->onImage, + (*newLink)->head, (*newLink)->headSize); + if(rc <= 0) + return rc; + + volInfo->fileLocations = *newLink; + + return 1; +} + +/* returns 2 if yes 1 if not +* works even if file1 == file2 */ +int filesAreSame(VolInfo* volInfo, int file1, bk_off_t posFile1, + int file2, bk_off_t posFile2, unsigned size) +{ + bk_off_t origPosFile1; + bk_off_t origPosFile2; + int numBlocks; + int sizeLastBlock; + int count; + int rc; + bool sameSoFar; + + if(size == 0) + return 2; + + origPosFile1 = bkSeekTell(file1); + origPosFile2 = bkSeekTell(file2); + + numBlocks = size / READ_WRITE_BUFFER_SIZE; + sizeLastBlock = size % READ_WRITE_BUFFER_SIZE; + + sameSoFar = true; + for(count = 0; count < numBlocks; count++) + { + bkSeekSet(file1, posFile1, SEEK_SET); + rc = bkRead(file1, volInfo->readWriteBuffer, READ_WRITE_BUFFER_SIZE); + if(rc != READ_WRITE_BUFFER_SIZE) + return BKERROR_READ_GENERIC; + posFile1 = bkSeekTell(file1); + + bkSeekSet(file2, posFile2, SEEK_SET); + rc = bkRead(file2, volInfo->readWriteBuffer2, READ_WRITE_BUFFER_SIZE); + if(rc != READ_WRITE_BUFFER_SIZE) + return BKERROR_READ_GENERIC; + posFile2 = bkSeekTell(file2); + + if( memcmp(volInfo->readWriteBuffer, volInfo->readWriteBuffer2, + READ_WRITE_BUFFER_SIZE) != 0 ) + { + sameSoFar = false; + break; + } + } + + if(sameSoFar && sizeLastBlock > 0) + { + bkSeekSet(file1, posFile1, SEEK_SET); + rc = bkRead(file1, volInfo->readWriteBuffer, sizeLastBlock); + if(rc != sizeLastBlock) + return BKERROR_READ_GENERIC; + + bkSeekSet(file2, posFile2, SEEK_SET); + rc = bkRead(file2, volInfo->readWriteBuffer2, sizeLastBlock); + if(rc != sizeLastBlock) + return BKERROR_READ_GENERIC; + + if(memcmp(volInfo->readWriteBuffer, volInfo->readWriteBuffer2, sizeLastBlock) != 0) + sameSoFar = false; + } + + bkSeekSet(file1, origPosFile1, SEEK_SET); + bkSeekSet(file2, origPosFile2, SEEK_SET); + + if(sameSoFar) + return 2; + else + return 1; +} + +/* returns 2 if found 1 if not found */ +int findInHardLinkTable(VolInfo* volInfo, bk_off_t position, + char* pathAndName, unsigned size, + bool onImage, BkHardLink** foundLink) +{ + BkHardLink* currentLink; + unsigned char head[MAX_NBYTES_HARDLINK_HEAD]; + int headSize; + int rc; + + *foundLink = NULL; + + if(size < MAX_NBYTES_HARDLINK_HEAD) + headSize = size; + else + headSize = MAX_NBYTES_HARDLINK_HEAD; + + rc = readFileHead(volInfo, position, pathAndName, onImage, head, headSize); + if(rc <= 0) + return rc; + + currentLink = volInfo->fileLocations; + while(currentLink != NULL) + { + if(size == currentLink->size) + { + if( memcmp(head, currentLink->head, headSize) == 0 ) + { + int origFile; + int origFileWasOpened; + bk_off_t origFileOffset; + int newFile; + bool newFileWasOpened; + bk_off_t newFileOffset; + + /* set up for reading original file */ + if(currentLink->onImage) + { + origFile = volInfo->imageForReading; + origFileWasOpened = false; + origFileOffset = currentLink->position; + } + else + { + origFile = open(pathAndName, O_RDONLY, 0); + if(origFile == -1) + return BKERROR_OPEN_READ_FAILED; + origFileWasOpened = true; + origFileOffset = 0; + } + + /* set up for reading new file */ + if(onImage) + { + newFile = volInfo->imageForReading; + newFileWasOpened = false; + newFileOffset = position; + } + else + { + newFile = open(pathAndName, O_RDONLY, 0); + if(newFile == -1) + { + if(origFileWasOpened) + bkClose(origFile); + return BKERROR_OPEN_READ_FAILED; + } + newFileWasOpened = true; + newFileOffset = 0; + } + + rc = filesAreSame(volInfo, origFile, origFileOffset, + newFile, newFileOffset, size); + + if(origFileWasOpened) + bkClose(origFile); + if(newFileWasOpened) + bkClose(newFile); + + if(rc < 0) + return rc; + + if(rc == 2) + { + *foundLink = currentLink; + return 2; + } + } + } + + currentLink = currentLink->next; + } + + return 1; +} + +int readFileHead(VolInfo* volInfo, bk_off_t position, char* pathAndName, + bool onImage, unsigned char* dest, int numBytes) +{ + int srcFile; + bool srcFileWasOpened; + bk_off_t origPos; + int rc; + + if(onImage) + { + srcFile = volInfo->imageForReading; + origPos = bkSeekTell(volInfo->imageForReading); + bkSeekSet(volInfo->imageForReading, position, SEEK_SET); + srcFileWasOpened = false; + } + else + { + srcFile = open(pathAndName, O_RDONLY, 0); + if(srcFile == -1) + return BKERROR_OPEN_READ_FAILED; + srcFileWasOpened = true; + } + + rc = bkRead(srcFile, dest, numBytes); + + if(srcFileWasOpened) + bkClose(srcFile); + else + bkSeekSet(volInfo->imageForReading, origPos, SEEK_SET); + + if(rc != numBytes) + return BKERROR_READ_GENERIC; + + return 1; +} + +void resetWriteStatus(BkHardLink* fileLocations) +{ + BkHardLink* currentNode; + + currentNode = fileLocations; + while(currentNode != NULL) + { + currentNode->extentNumberWrittenTo = 0; + + currentNode = currentNode->next; + } +} diff --git a/lib/bkisofs/bkLink.h b/lib/bkisofs/bkLink.h new file mode 100644 index 0000000..31a3b6a --- /dev/null +++ b/lib/bkisofs/bkLink.h @@ -0,0 +1,10 @@ +int addToHardLinkTable(VolInfo* volInfo, bk_off_t position, char* pathAndName, + unsigned size, bool onImage, BkHardLink** newLink); +int filesAreSame(VolInfo* volInfo, int file1, bk_off_t posFile1, + int file2, bk_off_t posFile2, unsigned size); +int findInHardLinkTable(VolInfo* volInfo, bk_off_t position, + char* pathAndName, unsigned size, + bool onImage, BkHardLink** foundLink); +int readFileHead(VolInfo* volInfo, bk_off_t position, char* pathAndName, + bool onImage, unsigned char* dest, int numBytes); +void resetWriteStatus(BkHardLink* fileLocations); diff --git a/lib/bkisofs/bkMangle.c b/lib/bkisofs/bkMangle.c new file mode 100644 index 0000000..ddc3563 --- /dev/null +++ b/lib/bkisofs/bkMangle.c @@ -0,0 +1,616 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* The Samba Project - http://samba.org/ +* - most of the filename mangling code +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "bk.h" +#include "bkInternal.h" +#include "bkMangle.h" +#include "bkError.h" + +/* length of aaa in aaa~xxxx.bbb */ +#define NCHARS_9660_BASE 3 + +/* +* note that some unsigned ints in mangling functions are +* required to be 32 bits long for the hashing to work +* see the samba code for details +*/ + +/****************************************************************************** +* charIsValid9660() +* +* */ +bool charIsValid9660(char theChar) +{ + if( (theChar >= '0' && theChar <= '9') || + (theChar >= 'a' && theChar <= 'z') || + (theChar >= 'A' && theChar <= 'Z') || + strchr("._-$~", theChar) ) + { + return true; + } + else + return false; +} + +/****************************************************************************** +* charIsValidJoliet() +* +* */ +bool charIsValidJoliet(char theChar) +{ + /* can be any ascii char between decimal 32 and 126 + * except '*' (42) '/' (47), ':' (58), ';' (59), '?' (63) and '\' (92) */ + if(theChar < 32 || theChar > 126 || + theChar == 42 || theChar == 47 || theChar == 58 || + theChar == 59 || theChar == 63 || theChar == 92) + return false; + else + return true; +} + +/* + hash a string of the specified length. The string does not need to be + null terminated + + this hash needs to be fast with a low collision rate (what hash doesn't?) +*/ +unsigned hashString(const char *str, unsigned int length) +{ + unsigned value; + unsigned i; + + static const unsigned fnv1Prime = 0x01000193; + + /* Set the initial value from the key size. */ + /* fnv1 of the string: idra@samba.org 2002 */ + value = 0xa6b93095; + for (i = 0; i < length; i++) + { + value *= (unsigned)fnv1Prime; + value ^= (unsigned)(str[i]); + } + + /* note that we force it to a 31 bit hash, to keep within the limits + of the 36^6 mangle space */ + return value & ~0x80000000; +} + +/****************************************************************************** +* mangleDir() +* Mangles the filenames from origDir and puts the results into newDir, whcich +* it also creates. +* filenameTypes is all types required in the end +* */ +int mangleDir(const BkDir* origDir, DirToWrite* newDir, int filenameTypes) +{ + int rc; + bool haveCollisions; + int numTimesTried; + int num9660Collisions; + const int name9660len = 13; + char newName9660[name9660len]; /* for remangling */ + int numJolietCollisions; + char newNameJoliet[NCHARS_FILE_ID_MAX_JOLIET]; /* for remangling */ + + BkFileBase* currentOrigChild; + BaseToWrite** currentNewChild; + + /* for counting collisions */ + BaseToWrite* currentChild; + BaseToWrite* currentChildToCompare; + + /* MANGLE all names, create new children list */ + currentOrigChild = origDir->children; + currentNewChild = &(newDir->children); + while(currentOrigChild != NULL) + { + if( IS_DIR(currentOrigChild->posixFileMode) ) + { + *currentNewChild = malloc(sizeof(DirToWrite)); + if(*currentNewChild == NULL) + return BKERROR_OUT_OF_MEMORY; + + memset(*currentNewChild, 0, sizeof(DirToWrite)); + } + else if( IS_REG_FILE(currentOrigChild->posixFileMode) ) + { + *currentNewChild = malloc(sizeof(FileToWrite)); + if(*currentNewChild == NULL) + return BKERROR_OUT_OF_MEMORY; + + memset(*currentNewChild, 0, sizeof(FileToWrite)); + } + else if( IS_SYMLINK(currentOrigChild->posixFileMode) ) + { + *currentNewChild = malloc(sizeof(SymLinkToWrite)); + if(*currentNewChild == NULL) + return BKERROR_OUT_OF_MEMORY; + + memset(*currentNewChild, 0, sizeof(SymLinkToWrite)); + } + else + return BKERROR_NO_SPECIAL_FILES; + + if(currentOrigChild->original9660name[0] != '\0') + strncpy((*currentNewChild)->name9660, currentOrigChild->original9660name, NBYTES_FILE_ID_MAX_9660); + else + shortenNameFor9660(currentOrigChild->name, (*currentNewChild)->name9660); + + if(filenameTypes | FNTYPE_ROCKRIDGE) + strncpy((*currentNewChild)->nameRock, currentOrigChild->name, NCHARS_FILE_ID_MAX_STORE); + else + (*currentNewChild)->nameRock[0] = '\0'; + + if(filenameTypes | FNTYPE_JOLIET) + mangleNameForJoliet(currentOrigChild->name, (*currentNewChild)->nameJoliet, false); + else + (*currentNewChild)->nameJoliet[0] = '\0'; + + (*currentNewChild)->posixFileMode = currentOrigChild->posixFileMode; + + if( IS_DIR(currentOrigChild->posixFileMode) ) + { + rc = mangleDir(BK_DIR_PTR(currentOrigChild), DIRTW_PTR(*currentNewChild), + filenameTypes); + if(rc < 0) + { + free(*currentNewChild); + *currentNewChild = NULL; + return rc; + } + } + else if( IS_REG_FILE(currentOrigChild->posixFileMode) ) + { + BkFile* origFile = BK_FILE_PTR(currentOrigChild); + FileToWrite* newFile = FILETW_PTR(*currentNewChild); + + newFile->size = origFile->size; + + newFile->location = origFile->location; + + newFile->onImage = origFile->onImage; + + newFile->offset = origFile->position; + + if( !origFile->onImage ) + { + newFile->pathAndName = malloc(strlen(origFile->pathAndName) + 1); + if( newFile->pathAndName == NULL ) + { + free(*currentNewChild); + *currentNewChild = NULL; + return BKERROR_OUT_OF_MEMORY; + } + + strcpy(newFile->pathAndName, origFile->pathAndName); + } + + newFile->origFile = origFile; + } + else /* if( IS_SYMLINK(currentOrigChild->posixFileMode) ) */ + { + strncpy(SYMLINKTW_PTR(*currentNewChild)->target, + BK_SYMLINK_PTR(currentOrigChild)->target, NCHARS_SYMLINK_TARGET_MAX); + } + + currentOrigChild = currentOrigChild->next; + currentNewChild = &((*currentNewChild)->next); + } + /* END MANGLE all names, create new children list */ + + haveCollisions = true; + numTimesTried = 0; + while(haveCollisions && numTimesTried < 50000) /* random big number */ + { + haveCollisions = false; + + currentChild = newDir->children; + while(currentChild != NULL) + { + num9660Collisions = 0; + numJolietCollisions = 0; + + currentChildToCompare = newDir->children; + while(currentChildToCompare != NULL) + { + if(strcmp(currentChild->name9660, + currentChildToCompare->name9660) == 0) + { + num9660Collisions++; + } + + if(strcmp(currentChild->nameJoliet, + currentChildToCompare->nameJoliet) == 0) + { + numJolietCollisions++; + } + + currentChildToCompare = currentChildToCompare->next; + } + + if(num9660Collisions != 1) + { + haveCollisions = true; + + if( IS_DIR(currentChild->posixFileMode) ) + mangleNameFor9660(currentChild->name9660, newName9660, true); + else + mangleNameFor9660(currentChild->name9660, newName9660, false); + + strcpy(currentChild->name9660, newName9660); + } + + if(numJolietCollisions != 1) + { + haveCollisions = true; + + mangleNameForJoliet(currentChild->nameJoliet, newNameJoliet, true); + + strcpy(currentChild->nameJoliet, newNameJoliet); + } + + + currentChild = currentChild->next; + } + + numTimesTried++; + } + + if(haveCollisions) + return BKERROR_MANGLE_TOO_MANY_COL; + + return 1; +} + +/****************************************************************************** +* mangleNameFor9660() +* Convert a long filename into an ISO9660 acceptable form: +* see charIsValid9660(), 8 chars max for directories and 8.3 chars +* for files. Extension is kept if it's shorter then 4 chars. +* 3 chars from the original name are kept, the rest is filled with ~XXXX where +* the XXXX is a random string (but still with valid characters). +* */ +void mangleNameFor9660(const char* origName, char* newName, bool isADir) +{ + char* dot_p; + int i; + char base[7]; /* max 6 chars */ + char extension[4]; /* max 3 chars */ + int extensionLen; + unsigned hash; + unsigned v; + /* these are the characters we use in the 8.3 hash. Must be 36 chars long */ + static const char* baseChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + /* FIND extension */ + if(isADir) + { + dot_p = NULL; + } + else + { + dot_p = strrchr(origName, '.'); + + if(dot_p) + { + /* if the extension contains any illegal characters or + is too long (> 3) or zero length then we treat it as part + of the prefix */ + for(i = 0; i < 4 && dot_p[i + 1] != '\0'; i++) + { + if( !charIsValid9660(dot_p[i + 1]) ) + { + dot_p = NULL; + break; + } + } + + if(i == 0 || i == 4 || dot_p == origName) + dot_p = NULL; + } + } + /* END FIND extension */ + + /* GET base */ + /* the leading characters in the mangled name is taken from + * the first characters of the name, if they are ascii otherwise + * '_' is used */ + for(i = 0; i < NCHARS_9660_BASE && origName[i] != '\0'; i++) + { + base[i] = origName[i]; + + if ( !charIsValid9660(origName[i]) ) + base[i] = '_'; + + base[i] = toupper(base[i]); + } + + /* make sure base doesn't contain part of the extension */ + if(dot_p != NULL) + { + if(i > dot_p - origName) + i = dot_p - origName; + } + + /* fixed length */ + while(i < NCHARS_9660_BASE) + { + base[i] = '_'; + + i++; + } + + base[NCHARS_9660_BASE] = '\0'; + /* END GET base */ + + /* GET extension */ + /* the extension of the mangled name is taken from the first 3 + * ascii chars after the dot */ + extensionLen = 0; + if(dot_p) + { + for(i = 1; extensionLen < 3 && dot_p[i] != '\0'; i++) + { + extension[extensionLen] = toupper(dot_p[i]); + + extensionLen++; + } + } + + extension[extensionLen] = '\0'; + /* END GET extension */ + + /* find the hash for this prefix */ + hash = hashString(origName, strlen(origName)); + + /* now form the mangled name. */ + for(i = 0; i < NCHARS_9660_BASE; i++) + { + newName[i] = base[i]; + } + + newName[NCHARS_9660_BASE] = '~'; + + v = hash; + newName[7] = baseChars[v % 36]; + for(i = 6; i > NCHARS_9660_BASE; i--) + { + v = v / 36; + newName[i] = baseChars[v % 36]; + } + + /* add the extension and terminate string */ + if(extensionLen > 0) + { + newName[8] = '.'; + + strcpy(newName + 9, extension); + } + else + { + newName[8] = '\0'; + } + + printf("remangled '%s' -> '%s'\n", origName, newName); +} + +void mangleNameForJoliet(const char* origName, char* newName, bool appendHash) +{ + char* dot_p; + int i; + char base[NCHARS_FILE_ID_MAX_JOLIET]; /* '\0' terminated */ + char extension[6]; /* max 3 chars */ + int extensionLen; + unsigned hash; + unsigned v; + char hashStr[5]; /* '\0' terminated */ + /* these are the characters we use in the 8.3 hash. Must be 36 chars long */ + static const char* baseChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + /* FIND extension candidate */ + dot_p = strrchr(origName, '.'); + + if(dot_p) + { + /* if the extension contains any illegal characters or + is too long (> 5) or zero length then we treat it as part + of the prefix */ + for(i = 0; i < 6 && dot_p[i + 1] != '\0'; i++) + { + if( !charIsValidJoliet(dot_p[i + 1]) ) + { + dot_p = NULL; + break; + } + } + + if(i == 0 || i == 6 || dot_p == origName) + dot_p = NULL; + } + /* END FIND extension candidate */ + + /* GET base */ + /* The leading characters in the mangled name are taken from + * the first characters of the name if they are allowed, otherwise + * '_' is used */ + for(i = 0; i < NCHARS_FILE_ID_MAX_JOLIET - 1 && origName[i] != '\0'; i++) + { + base[i] = origName[i]; + + if ( !charIsValidJoliet(origName[i]) ) + base[i] = '_'; + } + + /* make sure base doesn't contain part of the extension */ + if(dot_p != NULL) + { + if(i > dot_p - origName) + i = dot_p - origName; + } + + base[i] = '\0'; + /* END GET base */ + + /* GET extension */ + /* the extension of the mangled name is taken from the first 3 + ascii chars after the dot */ + extensionLen = 0; + if(dot_p) + { + for(i = 1; extensionLen < 5 && dot_p[i] != '\0'; i++) + { + extension[extensionLen] = dot_p[i]; + + extensionLen++; + } + } + + extension[extensionLen] = '\0'; + /* END GET extension */ + + /* FIND the hash for this prefix */ + hash = hashString(origName, strlen(origName)); + + hashStr[4] = '\0'; + v = hash; + hashStr[3] = baseChars[v % 36]; + for(i = 2; i >= 0; i--) + { + v = v / 36; + hashStr[i] = baseChars[v % 36]; + } + /* END FIND the hash for this prefix */ + + /* ASSEMBLE name */ + strcpy(newName, base); + + if(appendHash) + { + /* max name len - '~' - hash - '.' - extension */ + if(strlen(newName) >= NCHARS_FILE_ID_MAX_JOLIET - 1 - 1 - 4 - 1 - 5) + newName[NCHARS_FILE_ID_MAX_JOLIET - 1 - 1 - 4 - 1 - 5] = '\0'; + + strcat(newName, "~"); + strcat(newName, hashStr); + } + if(extensionLen > 0) + { + strcat(newName, "."); + strcat(newName, extension); + } + /* END ASSEMBLE name */ + + if(appendHash) + printf("joliet mangle '%s' -> '%s'\n", origName, newName); +} + +/****************************************************************************** +* shortenNameFor9660() +* Same as mangleNameFor9660() but without the ~XXXX. +* */ +void shortenNameFor9660(const char* origName, char* newName) +{ + char* dot_p; + int i; + char base[9]; /* max 9 chars */ + char extension[4]; /* max 3 chars */ + int extensionLen; + + /* FIND extension */ + /* ISO9660 requires that directories have no dots ('.') but some isolinux + * cds have the kernel in a directory with a dot so i need to allow dots in + * directories :( */ + /*if(isADir) + { + dot_p = NULL; + } + else + {*/ + dot_p = strrchr(origName, '.'); + + if(dot_p) + { + /* if the extension contains any illegal characters or + is too long (> 3) or zero length then we treat it as part + of the prefix */ + for(i = 0; i < 4 && dot_p[i + 1] != '\0'; i++) + { + if( !charIsValid9660(dot_p[i + 1]) ) + { + dot_p = NULL; + break; + } + } + + if(i == 0 || i == 4 || dot_p == origName) + dot_p = NULL; + } + /*}*/ + /* END FIND extension */ + + /* GET base */ + /* the leading characters in the mangled name is taken from + * the first characters of the name, if they are allowed otherwise + * '_' is used */ + for(i = 0; i < 8 && origName[i] != '\0'; i++) + { + base[i] = origName[i]; + + if ( !charIsValid9660(origName[i]) ) + base[i] = '_'; + + base[i] = toupper(base[i]); + } + + /* make sure base doesn't contain part of the extension */ + if(dot_p != NULL) + { + if(i > dot_p - origName) + i = dot_p - origName; + } + + base[i] = '\0'; + /* END GET base */ + + /* GET extension */ + /* the extension of the mangled name is taken from the first 3 + ascii chars after the dot */ + extensionLen = 0; + if(dot_p) + { + for(i = 1; extensionLen < 3 && dot_p[i] != '\0'; i++) + { + extension[extensionLen] = toupper(dot_p[i]); + + extensionLen++; + } + } + + extension[extensionLen] = '\0'; + /* END GET extension */ + + strcpy(newName, base); + if(extensionLen > 0) + { + strcat(newName, "."); + strcat(newName, extension); + } +} diff --git a/lib/bkisofs/bkMangle.h b/lib/bkisofs/bkMangle.h new file mode 100644 index 0000000..43878a6 --- /dev/null +++ b/lib/bkisofs/bkMangle.h @@ -0,0 +1,7 @@ +bool charIsValid9660(char theChar); +bool charIsValidJoliet(char theChar); +unsigned hashString(const char *str, unsigned int length); +int mangleDir(const BkDir* origDir, DirToWrite* newDir, int filenameTypes); +void mangleNameFor9660(const char* origName, char* newName, bool isADir); +void mangleNameForJoliet(const char* origName, char* newName, bool appendHash); +void shortenNameFor9660(const char* origName, char* newName); diff --git a/lib/bkisofs/bkMisc.c b/lib/bkisofs/bkMisc.c new file mode 100644 index 0000000..eedba7d --- /dev/null +++ b/lib/bkisofs/bkMisc.c @@ -0,0 +1,36 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include + +#include "bkInternal.h" +#include "bkMisc.h" + +void maybeUpdateProgress(VolInfo* volInfo) +{ + struct timeb timeNow; + + if(volInfo->progressFunction == NULL) + return; + + ftime(&timeNow); + + if(timeNow.time - volInfo->lastTimeCalledProgress.time >= 1 || + timeNow.millitm - volInfo->lastTimeCalledProgress.millitm >= 100) + { + volInfo->progressFunction(volInfo); + + volInfo->lastTimeCalledProgress = timeNow; + } +} diff --git a/lib/bkisofs/bkMisc.h b/lib/bkisofs/bkMisc.h new file mode 100644 index 0000000..d41abdd --- /dev/null +++ b/lib/bkisofs/bkMisc.h @@ -0,0 +1 @@ +void maybeUpdateProgress(VolInfo* volInfo); diff --git a/lib/bkisofs/bkPath.c b/lib/bkisofs/bkPath.c new file mode 100644 index 0000000..49bdabb --- /dev/null +++ b/lib/bkisofs/bkPath.c @@ -0,0 +1,375 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* Henrique Pinto +* - fixed bug that caused crash in makeNewPathFromString() +******************************************************************************/ + +#include +#include +#include +#include + +#include "bk.h" +#include "bkInternal.h" +#include "bkError.h" +#include "bkPath.h" +#include "bkMangle.h" + +/****************************************************************************** +* nameIsValid9660() +* Checks each character in name to see whether it's allowed in the more strict +* ISO9660 fields. +* */ +bool nameIsValid9660(const char* name) +{ + size_t count; + size_t nameLen; + + nameLen = strlen(name); + + for(count = 0; count < nameLen; count++) + { + if(!charIsValid9660(name[count])) + return false; + } + + return true; +} + +bool findBaseByNewPath(NewPath* path, BkDir* tree, BkFileBase** base) +{ + BkDir* parentDir; + bool dirFound; + BkFileBase* child; + + /* parent directory */ + path->numChildren--; + dirFound = findDirByNewPath(path, tree, &parentDir); + path->numChildren++; + if(!dirFound) + return false; + + child = parentDir->children; + while(child != NULL) + { + if(strcmp(child->name, path->children[path->numChildren - 1]) == 0) + { + *base = child; + return true; + } + + child = child->next; + } + + return false; +} + +bool findDirByNewPath(const NewPath* path, BkDir* tree, BkDir** dir) +{ + bool dirFound; + unsigned count; + BkFileBase* child; + + *dir = tree; + for(count = 0; count < path->numChildren; count++) + /* each directory in the path */ + { + child = (*dir)->children; + dirFound = false; + while(child != NULL && !dirFound) + /* find the directory */ + { + if(strcmp(child->name, path->children[count]) == 0) + { + if( !IS_DIR(child->posixFileMode) ) + return false; + + dirFound = true; + *dir = BK_DIR_PTR(child); + } + else + child = child->next; + } + if(!dirFound) + return false; + } + + return true; +} + +/****************************************************************************** +* freeDirToWriteContents() +* Recursively deletes all the dynamically allocated contents of dir. +* */ +void freeDirToWriteContents(DirToWrite* dir) +{ + BaseToWrite* currentChild; + BaseToWrite* nextChild; + + currentChild = dir->children; + while(currentChild != NULL) + { + nextChild = currentChild->next; + + if( IS_DIR(currentChild->posixFileMode) ) + { + freeDirToWriteContents(DIRTW_PTR(currentChild)); + } + else if( IS_REG_FILE(currentChild->posixFileMode) ) + { + if(!FILETW_PTR(currentChild)->onImage) + free(FILETW_PTR(currentChild)->pathAndName); + } + + free(currentChild); + + currentChild = nextChild; + } +} + +void freePathContents(NewPath* path) +{ + unsigned count; + + for(count = 0; count < path->numChildren; count++) + { + /* if the path was not allocated properly (maybe ran out of memory) + * the first unallocated item is null */ + if(path->children[count] == NULL) + break; + + free(path->children[count]); + } + + if(path->children != NULL) + free(path->children); +} + +int getLastNameFromPath(const char* srcPathAndName, char* lastName) +{ + size_t count; + size_t srcLen; + size_t lastCharIndex; + size_t firstCharIndex; + bool lastCharFound; + int count2; + + srcLen = strlen(srcPathAndName); + + /* FIND the last name */ + lastCharIndex = srcLen; + lastCharFound = false; + for(count = srcLen; /* unsigned */; count--) + { + if(srcPathAndName[count] != '/') + { + if(!lastCharFound) + { + lastCharIndex = count; + lastCharFound = true; + + firstCharIndex = lastCharIndex; + } + else + { + firstCharIndex = count; + } + } + else + { + if(lastCharFound) + break; + } + + if(count == 0) + break; + } + if(!lastCharFound) + return BKERROR_MISFORMED_PATH; + /* END FIND the last name */ + + if(lastCharIndex - firstCharIndex > NCHARS_FILE_ID_MAX_STORE - 1) + return BKERROR_MAX_NAME_LENGTH_EXCEEDED; + + /* copy the name */ + for(count = firstCharIndex, count2 = 0; count <= lastCharIndex; count++, count2++) + { + lastName[count2] = srcPathAndName[count]; + } + lastName[count2] = '\0'; + + return 1; +} + +int makeNewPathFromString(const char* strPath, NewPath* pathPath) +{ + size_t count; + size_t pathStrLen; + unsigned numChildrenDone; + int nextChildLen; + const char* nextChild; + + pathStrLen = strlen(strPath); + pathPath->numChildren = 0; + pathPath->children = NULL; + + if(strPath[0] != '/') + return BKERROR_MISFORMED_PATH; + + /* count number of children */ + for(count = 1; count < pathStrLen; count++) + { + if(strPath[count] != '/' && strPath[count - 1] == '/') + pathPath->numChildren++; + } + + if(pathPath->numChildren == 0) + { + pathPath->children = NULL; + return 1; + } + + pathPath->children = (char**)malloc(sizeof(char*) * pathPath->numChildren); + if(pathPath->children == NULL) + return BKERROR_OUT_OF_MEMORY; + + numChildrenDone = 0; + nextChildLen = 0; + nextChild = &(strPath[1]); + for(count = 1; count <= pathStrLen; count++) + { + if(strPath[count] == '/' || (strPath[count] == '\0' && strPath[count - 1] != '/')) + { + if(strPath[count] == '/' && strPath[count - 1] == '/') + /* double slash */ + { + nextChild = &(strPath[count + 1]); + continue; + } + else + /* this is the end of the string or the slash following a dir name */ + { + pathPath->children[numChildrenDone] = (char*)malloc(nextChildLen + 1); + if(pathPath->children[numChildrenDone] == NULL) + return BKERROR_OUT_OF_MEMORY; + + strncpy(pathPath->children[numChildrenDone], nextChild, nextChildLen); + pathPath->children[numChildrenDone][nextChildLen] = '\0'; + + numChildrenDone++; + nextChildLen = 0; + + nextChild = &(strPath[count + 1]); + } + } + else + { + nextChildLen++; + } + } + + if(numChildrenDone != pathPath->numChildren) + return BKERROR_SANITY; + + return 1; +} + +/****************************************************************************** +* nameIsValid() +* Checks each character in name to see whether it's allowed in an identifier +* */ +bool nameIsValid(const char* name) +{ + size_t count; + size_t nameLen; + + nameLen = strlen(name); + + for(count = 0; count < nameLen; count++) + { + /* can be any ascii char between decimal 32 and 126 except '/' (47) */ + if(name[count] < 32 || name[count] > 126 || name[count] == 47) + return false; + } + + return true; +} + +#ifdef DEBUG +void printDirToWrite(DirToWrite* dir, int level, int filenameTypes) +{ + BaseToWrite* child; + int count; + + child = dir->children; + while(child != NULL) + { + if(IS_DIR(child->posixFileMode)) + { + if(filenameTypes & FNTYPE_9660) + { + for(count = 0; count < level; count ++) + printf(" "); + printf("dir9 '%s'\n", child->name9660);fflush(NULL); + } + + if(filenameTypes & FNTYPE_JOLIET) + { + for(count = 0; count < level; count ++) + printf(" "); + printf("dirJ '%s'\n", child->nameJoliet);fflush(NULL); + } + + if(filenameTypes & FNTYPE_ROCKRIDGE) + { + for(count = 0; count < level; count ++) + printf(" "); + printf("dirR '%s'\n", child->nameRock);fflush(NULL); + } + + printDirToWrite(DIRTW_PTR(child), level + 1, filenameTypes); + } + + child = child->next; + } + + child = dir->children; + while(child != NULL) + { + if(!IS_DIR(child->posixFileMode)) + { + if(filenameTypes & FNTYPE_9660) + { + for(count = 0; count < level; count ++) + printf(" "); + printf("file9 '%s'\n", child->name9660);fflush(NULL); + } + + if(filenameTypes & FNTYPE_JOLIET) + { + for(count = 0; count < level; count ++) + printf(" "); + printf("fileJ '%s'\n", child->nameJoliet);fflush(NULL); + } + + if(filenameTypes & FNTYPE_ROCKRIDGE) + { + for(count = 0; count < level; count ++) + printf(" "); + printf("fileR '%s'\n", child->nameRock);fflush(NULL); + } + } + + child = child->next; + } +} +#endif /* DEBUG */ diff --git a/lib/bkisofs/bkPath.h b/lib/bkisofs/bkPath.h new file mode 100644 index 0000000..8cf06d4 --- /dev/null +++ b/lib/bkisofs/bkPath.h @@ -0,0 +1,16 @@ +#ifndef bkPath_h +#define bkPath_h + +#include "bkInternal.h" + +bool findDirByNewPath(const NewPath* path, BkDir* tree, BkDir** dir); +bool findBaseByNewPath(NewPath* path, BkDir* tree, BkFileBase** base); +void freeDirToWriteContents(DirToWrite* dir); +void freePathContents(NewPath* path); +int getLastNameFromPath(const char* srcPathAndName, char* lastName); +int makeNewPathFromString(const char* strPath, NewPath* pathPath); +bool nameIsValid(const char* name); +bool nameIsValid9660(const char* name); +void printDirToWrite(DirToWrite* dir, int level, int filenameTypes); + +#endif diff --git a/lib/bkisofs/bkRead.c b/lib/bkisofs/bkRead.c new file mode 100644 index 0000000..f825ee3 --- /dev/null +++ b/lib/bkisofs/bkRead.c @@ -0,0 +1,1265 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "bk.h" +#include "bkInternal.h" +#include "bkRead.h" +#include "bkRead7x.h" +#include "bkTime.h" +#include "bkError.h" +#include "bkLink.h" +#include "bkMisc.h" +#include "bkIoWrappers.h" + +/* numbers as recorded on image */ +#define VDTYPE_BOOT 0 +#define VDTYPE_PRIMARY 1 +#define VDTYPE_SUPPLEMENTARY 2 +#define VDTYPE_VOLUMEPARTITION 3 +#define VDTYPE_TERMINATOR 255 + +/* for el torito boot images */ +#define NBYTES_VIRTUAL_SECTOR 512 + +/* this function is really just for use in readRockridgeSymlink() +* returns number of chars appended +* destMaxLen doesn't include '\0' +* if maxSrcLen is -1 tries to copy all of it */ +size_t appendStringIfHaveRoom(char* dest, const char* src, size_t destMaxLen, + size_t destCharsAlreadyUsed, int maxSrcLen) +{ + size_t srcLen; + + if(maxSrcLen == -1) + srcLen = strlen(src); + else + srcLen = maxSrcLen; + + if(destCharsAlreadyUsed + srcLen > destMaxLen) + return 0; + + strncat(dest, src, srcLen); + + return srcLen; +} + +/******************************************************************************* +* bk_open_image() +* +* */ +int bk_open_image(VolInfo* volInfo, const char* filename) +{ + size_t len; + + volInfo->imageForReading = open(filename, O_RDONLY, 0); + if(volInfo->imageForReading == -1) + { + volInfo->imageForReading = 0; + return BKERROR_OPEN_READ_FAILED; + } + + int rc; + BkStatStruct statStruct; + + /* record inode number */ + rc = bkStat(filename, &statStruct); + if(rc == -1) + return BKERROR_STAT_FAILED; + + volInfo->imageForReadingInode = statStruct.st_ino; + + /* skip the first 150 sectors if the image is an NRG */ + len = strlen(filename); + if( (filename[len - 3] == 'N' || filename[len - 3] == 'n') && + (filename[len - 2] == 'R' || filename[len - 2] == 'r') && + (filename[len - 1] == 'G' || filename[len - 1] == 'g') ) + { + readSeekSet(volInfo, NBYTES_LOGICAL_BLOCK * 16, SEEK_SET); + } + + return 1; +} + +/******************************************************************************* +* bk_read_dir_tree() +* filenameType can be only one (do not | more then one) +* */ +int bk_read_dir_tree(VolInfo* volInfo, int filenameType, + bool keepPosixPermissions, + void(*progressFunction)(VolInfo*)) +{ + volInfo->progressFunction = progressFunction; + + if(filenameType == FNTYPE_ROCKRIDGE || filenameType == FNTYPE_9660) + readSeekSet(volInfo, volInfo->pRootDrOffset, SEEK_SET); + else /* if(filenameType == FNTYPE_JOLIET) */ + readSeekSet(volInfo, volInfo->sRootDrOffset, SEEK_SET); + + return readDir(volInfo, &(volInfo->dirTree), + filenameType, keepPosixPermissions); +} + +/******************************************************************************* +* bk_read_vol_info() +* public function to read volume information +* assumes pvd is first descriptor in set +* */ +int bk_read_vol_info(VolInfo* volInfo) +{ + int rc; + unsigned char vdType; /* to check what descriptor follows */ + bool haveMorePvd; /* to skip extra pvds */ + unsigned char escapeSequence[3]; /* only interested in a joliet sequence */ + char timeString[17]; /* for creation time */ + bk_off_t locationOfNextDescriptor; + unsigned bootCatalogLocation; /* logical sector number */ + char elToritoSig[24]; + unsigned char bootMediaType; + unsigned short bootRecordSize; + unsigned bootRecordSectorNumber; + + /* vars for checking rockridge */ + unsigned realRootLoc; /* location of the root dr inside root dir */ + unsigned char recordLen; /* length of rood dr */ + unsigned char sPsUentry[7]; /* su entry SP */ + + /* will always have this unless image is broken */ + volInfo->filenameTypes = FNTYPE_9660; + + /* might not have supplementary descriptor */ + volInfo->sRootDrOffset = 0; + + /* skip system area */ + readSeekSet(volInfo, NLS_SYSTEM_AREA * NBYTES_LOGICAL_BLOCK, SEEK_SET); + + /* READ PVD */ + /* make sure pvd exists */ + rc = read711(volInfo, &vdType); + if(rc != 1) + return BKERROR_READ_GENERIC; + + /* first descriptor must be primary */ + if(vdType != VDTYPE_PRIMARY) + return BKERROR_VD_NOT_PRIMARY; + + readSeekSet(volInfo, 39, SEEK_CUR); + + rc = readRead(volInfo, volInfo->volId, 32); + if(rc != 32) + return BKERROR_READ_GENERIC; + volInfo->volId[32] = '\0'; + stripSpacesFromEndOfString(volInfo->volId); + + readSeekSet(volInfo, 84, SEEK_CUR); + + /* am now at root dr */ + volInfo->pRootDrOffset = readSeekTell(volInfo); + + /* SEE if rockridge exists */ + readSeekSet(volInfo, 2, SEEK_CUR); + + rc = read733(volInfo, &realRootLoc); + if(rc != 8) + return BKERROR_READ_GENERIC; + realRootLoc *= NBYTES_LOGICAL_BLOCK; + + readSeekSet(volInfo, realRootLoc, SEEK_SET); + + rc = read711(volInfo, &recordLen); + if(rc != 1) + return BKERROR_READ_GENERIC; + + if(recordLen >= 41) + /* a minimum root with SP su field */ + { + /* root dr has filename length of 1 */ + readSeekSet(volInfo, 33, SEEK_CUR); + + /* a rockridge root dr has an SP su entry here */ + + rc = readRead(volInfo, &sPsUentry, 7); + if(rc != 7) + return BKERROR_READ_GENERIC; + + if( sPsUentry[0] == 0x53 && sPsUentry[1] == 0x50 && + sPsUentry[2] == 7 && + sPsUentry[4] == 0xBE && sPsUentry[5] == 0xEF ) + /* rockridge it is */ + { + volInfo->filenameTypes |= FNTYPE_ROCKRIDGE; + } + } + + /* go back to where it was before trying rockridge */ + readSeekSet(volInfo, volInfo->pRootDrOffset, SEEK_SET); + /* END SEE if rockridge exists */ + + readSeekSet(volInfo, 162, SEEK_CUR); + + rc = readRead(volInfo, volInfo->publisher, 128); + if(rc != 128) + return BKERROR_READ_GENERIC; + volInfo->publisher[128] = '\0'; + stripSpacesFromEndOfString(volInfo->publisher); + + rc = readRead(volInfo, volInfo->dataPreparer, 128); + if(rc != 128) + return BKERROR_READ_GENERIC; + volInfo->dataPreparer[128] = '\0'; + stripSpacesFromEndOfString(volInfo->dataPreparer); + + readSeekSet(volInfo, 239, SEEK_CUR); + + rc = readRead(volInfo, timeString, 17); + if(rc != 17) + return BKERROR_READ_GENERIC; + + longStringToEpoch(timeString, &(volInfo->creationTime)); + + /* skip the rest of the extent */ + readSeekSet(volInfo, 1218, SEEK_CUR); + /* END READ PVD */ + + /* SKIP all extra copies of pvd */ + haveMorePvd = true; + while(haveMorePvd) + { + rc = read711(volInfo, &vdType); + if(rc != 1) + return BKERROR_READ_GENERIC; + + if(vdType == VDTYPE_PRIMARY) + { + readSeekSet(volInfo, 2047, SEEK_CUR); + } + else + { + readSeekSet(volInfo, -1, SEEK_CUR); + haveMorePvd = false; + } + } + /* END SKIP all extra copies of pvd */ + + /* TRY read boot record */ + + locationOfNextDescriptor = readSeekTell(volInfo) + 2048; + + rc = read711(volInfo, &vdType); + if(rc != 1) + return BKERROR_READ_GENERIC; + + if(vdType == VDTYPE_BOOT) + { + + readSeekSet(volInfo, 6, SEEK_CUR); + + rc = readRead(volInfo, elToritoSig, 24); + if(rc != 24) + return BKERROR_READ_GENERIC; + elToritoSig[23] = '\0'; /* just in case */ + + if(strcmp(elToritoSig, "EL TORITO SPECIFICATION") == 0) + /* el torito confirmed */ + { + readSeekSet(volInfo, 40, SEEK_CUR); + + rc = read731(volInfo, &bootCatalogLocation); + if(rc != 4) + return BKERROR_READ_GENERIC; + + readSeekSet(volInfo, bootCatalogLocation * NBYTES_LOGICAL_BLOCK, SEEK_SET); + + /* skip validation entry */ + readSeekSet(volInfo, 32, SEEK_CUR); + + /* skip boot indicator */ + readSeekSet(volInfo, 1, SEEK_CUR); + + rc = readRead(volInfo, &bootMediaType, 1); + if(rc != 1) + return BKERROR_READ_GENERIC; + if(bootMediaType == 0) + volInfo->bootMediaType = BOOT_MEDIA_NO_EMULATION; + else if(bootMediaType == 1) + volInfo->bootMediaType = BOOT_MEDIA_1_2_FLOPPY; + else if(bootMediaType == 2) + volInfo->bootMediaType = BOOT_MEDIA_1_44_FLOPPY; + else if(bootMediaType == 3) + volInfo->bootMediaType = BOOT_MEDIA_2_88_FLOPPY; + else if(bootMediaType == 4) + { + /* !! print warning */ + printf("hard disk boot emulation not supported\n"); + volInfo->bootMediaType = BOOT_MEDIA_NONE; + } + else + { + /* !! print warning */ + printf("unknown boot media type on iso\n"); + volInfo->bootMediaType = BOOT_MEDIA_NONE; + } + + /* skip load segment, system type and unused byte */ + readSeekSet(volInfo, 4, SEEK_CUR); + + rc = read721(volInfo, &bootRecordSize); + if(rc != 2) + return BKERROR_READ_GENERIC; + volInfo->bootRecordSize = bootRecordSize; + + if(volInfo->bootMediaType == BOOT_MEDIA_NO_EMULATION) + volInfo->bootRecordSize *= NBYTES_VIRTUAL_SECTOR; + else if(volInfo->bootMediaType == BOOT_MEDIA_1_2_FLOPPY) + volInfo->bootRecordSize = 1200 * 1024; + else if(volInfo->bootMediaType == BOOT_MEDIA_1_44_FLOPPY) + volInfo->bootRecordSize = 1440 * 1024; + else if(volInfo->bootMediaType == BOOT_MEDIA_2_88_FLOPPY) + volInfo->bootRecordSize = 2880 * 1024; + + volInfo->bootRecordIsOnImage = true; + + rc = read731(volInfo, &bootRecordSectorNumber); + if(rc != 4) + return BKERROR_READ_GENERIC; + volInfo->bootRecordOffset = bootRecordSectorNumber * + NBYTES_LOGICAL_BLOCK; + } + else + /* !! print warning */ + printf("err, boot record not el torito\n"); + + /* go to the sector after the boot record */ + readSeekSet(volInfo, locationOfNextDescriptor, SEEK_SET); + } + else + /* not boot record */ + { + /* go back */ + readSeekSet(volInfo, -1, SEEK_CUR); + } + /* END TRY read boot record */ + + /* TRY read svd */ + rc = read711(volInfo, &vdType); + if(rc != 1) + return BKERROR_READ_GENERIC; + + if(vdType == VDTYPE_SUPPLEMENTARY) + /* make sure it's joliet (by escape sequence) */ + { + readSeekSet(volInfo, 87, SEEK_CUR); + + rc = readRead(volInfo, escapeSequence, 3); + if(rc != 3) + return BKERROR_READ_GENERIC; + + if( (escapeSequence[0] == 0x25 && escapeSequence[1] == 0x2F && + escapeSequence[2] == 0x40) || + (escapeSequence[0] == 0x25 && escapeSequence[1] == 0x2F && + escapeSequence[2] == 0x43) || + (escapeSequence[0] == 0x25 && escapeSequence[1] == 0x2F && + escapeSequence[2] == 0x45) ) + /* is indeed joliet */ + { + readSeekSet(volInfo, 65, SEEK_CUR); + + volInfo->sRootDrOffset = readSeekTell(volInfo); + + volInfo->filenameTypes |= FNTYPE_JOLIET; + } + } + /* END TRY read svd */ + + return 1; +} + +/******************************************************************************* +* dirDrFollows() +* checks whether the next directory record is for a directory (not a file) +* */ +bool dirDrFollows(VolInfo* volInfo) +{ + unsigned char fileFlags; + bk_off_t origPos; + int rc; + + origPos = readSeekTell(volInfo); + + readSeekSet(volInfo, 25, SEEK_CUR); + + rc = read711(volInfo, &fileFlags); + if(rc != 1) + return BKERROR_READ_GENERIC; + + readSeekSet(volInfo, origPos, SEEK_SET); + + if((fileFlags >> 1 & 1) == 1) + return true; + else + return false; +} + +/******************************************************************************* +* haveNextRecordInSector() +* If a directory record won't fit into what's left in a logical block, the rest +* of the block is filled with 0s. This function checks whether that's the case. +* If the next byte is zero returns false otherwise true +* File position remains unchanged +* Also returns false on read error */ +bool haveNextRecordInSector(VolInfo* volInfo) +{ + bk_off_t origPos; + char testByte; + int rc; + + origPos = readSeekTell(volInfo); + + rc = readRead(volInfo, &testByte, 1); + if(rc != 1) + return false; + + readSeekSet(volInfo, origPos, SEEK_SET); + + return (testByte == 0) ? false : true; +} + +/******************************************************************************* +* readDir() +* Reads a directory record for a directory (not a file) +* Do not use this to read self or parent records unless it's the following: +* - if the root dr (inside vd) is read, it's filename will be "" +* filenameType can be only one (do not | more then one) +* +* note to self: directory identifiers do not end with ";1" +* +* */ +int readDir(VolInfo* volInfo, BkDir* dir, int filenameType, + bool keepPosixPermissions) +{ + int rc; + unsigned char recordLength; + unsigned locExtent; /* to know where to go before readDir9660() */ + unsigned lenExtent; /* parameter to readDirContents() */ + unsigned char lenFileId9660; /* also len joliet fileid (bytes) */ + int lenSU; /* calculated as recordLength - 33 - lenFileId9660 */ + bk_off_t origPos; + + /* should anything fail, will still be safe to delete dir, this also + * needs to be done before calling readDirContents() (now is good) */ + dir->children = NULL; + + if(volInfo->stopOperation) + return BKERROR_OPER_CANCELED_BY_USER; + + maybeUpdateProgress(volInfo); + + rc = readRead(volInfo, &recordLength, 1); + if(rc != 1) + return BKERROR_READ_GENERIC; + + readSeekSet(volInfo, 1, SEEK_CUR); + + rc = read733(volInfo, &locExtent); + if(rc != 8) + return BKERROR_READ_GENERIC; + + rc = read733(volInfo, &lenExtent); + if(rc != 8) + return BKERROR_READ_GENERIC; + + readSeekSet(volInfo, 14, SEEK_CUR); + + rc = readRead(volInfo, &lenFileId9660, 1); + if(rc != 1) + return BKERROR_READ_GENERIC; + + lenSU = recordLength - 33 - lenFileId9660; + if(lenFileId9660 % 2 == 0) + lenSU -= 1; + + /* READ directory name */ + if(volInfo->rootRead) + { + bk_off_t posBeforeName = readSeekTell(volInfo); + + rc = readRead(volInfo, BK_BASE_PTR(dir)->name, lenFileId9660); + if(rc != lenFileId9660) + return BKERROR_READ_GENERIC; + BK_BASE_PTR(dir)->name[lenFileId9660] = '\0'; + + /* record 9660 name for writing later */ + strncpy(BK_BASE_PTR(dir)->original9660name, BK_BASE_PTR(dir)->name, 14); + BK_BASE_PTR(dir)->original9660name[14] = '\0'; + + /* skip padding field if it's there */ + if(lenFileId9660 % 2 == 0) + readSeekSet(volInfo, 1, SEEK_CUR); + + if(filenameType != FNTYPE_9660) + readSeekSet(volInfo, posBeforeName, SEEK_SET); + } + + if(filenameType == FNTYPE_JOLIET) + { + if(volInfo->rootRead) + { + char nameAsOnDisk[UCHAR_MAX]; + /* in the worst possible case i'll use 129 bytes for this: */ + char nameInAscii[UCHAR_MAX]; + int ucsCount, byteCount; + + /* ucs2 byte count must be even */ + if(lenFileId9660 % 2 != 0) + return BKERROR_INVALID_UCS2; + + rc = readRead(volInfo, nameAsOnDisk, lenFileId9660); + if(rc != lenFileId9660) + return BKERROR_READ_GENERIC; + + for(ucsCount = 1, byteCount = 0; ucsCount < lenFileId9660; + ucsCount += 2, byteCount += 1) + { + nameInAscii[byteCount] = nameAsOnDisk[ucsCount]; + } + nameInAscii[byteCount] = '\0'; + + strncpy(BK_BASE_PTR(dir)->name, nameInAscii, lenFileId9660); + BK_BASE_PTR(dir)->name[lenFileId9660] = '\0'; + + /* padding field */ + if(lenFileId9660 % 2 == 0) + readSeekSet(volInfo, 1, SEEK_CUR); + } + } + else if(filenameType == FNTYPE_ROCKRIDGE) + { + if(volInfo->rootRead) + { + /* skip 9660 filename */ + readSeekSet(volInfo, lenFileId9660, SEEK_CUR); + /* skip padding field */ + if(lenFileId9660 % 2 == 0) + readSeekSet(volInfo, 1, SEEK_CUR); + + rc = readRockridgeFilename(volInfo, BK_BASE_PTR(dir)->name, lenSU, 0); + if(rc < 0) + return rc; + } + } + else if(filenameType != FNTYPE_9660) + return BKERROR_UNKNOWN_FILENAME_TYPE; + /* END READ directory name */ + + if(keepPosixPermissions) + { + if( !(volInfo->rootRead) ) + { + unsigned char realRootRecordLen; + + origPos = readSeekTell(volInfo); + + /* go to real root record */ + readSeekSet(volInfo, locExtent * NBYTES_LOGICAL_BLOCK, SEEK_SET); + + /* read record length */ + readRead(volInfo, &realRootRecordLen, 1); + if(rc != 1) + return BKERROR_READ_GENERIC; + + /* go to sys use fields */ + readSeekSet(volInfo, 33, SEEK_CUR); + + rc = readPosixFileMode(volInfo, &(BK_BASE_PTR(dir)->posixFileMode), realRootRecordLen - 34); + if(rc <= 0) + return rc; + + /* return */ + readSeekSet(volInfo, origPos, SEEK_SET); + } + else + { + rc = readPosixFileMode(volInfo, &(BK_BASE_PTR(dir)->posixFileMode), lenSU); + if(rc <= 0) + return rc; + } + } + else + { + /* this is good for root also */ + BK_BASE_PTR(dir)->posixFileMode = volInfo->posixDirDefaults; + } + + readSeekSet(volInfo, lenSU, SEEK_CUR); + + origPos = readSeekTell(volInfo); + + readSeekSet(volInfo, locExtent * NBYTES_LOGICAL_BLOCK, SEEK_SET); + + volInfo->rootRead = true; + + rc = readDirContents(volInfo, dir, lenExtent, filenameType, keepPosixPermissions); + if(rc < 0) + return rc; + + readSeekSet(volInfo, origPos, SEEK_SET); + + return recordLength; +} + +/******************************************************************************* +* readDirContents() +* Reads the extent pointed to from a directory record of a directory. +* size is number of bytes +* */ +int readDirContents(VolInfo* volInfo, BkDir* dir, unsigned size, + int filenameType, bool keepPosixPermissions) +{ + int rc; + unsigned bytesRead = 0; + unsigned childrenBytesRead; + BkFileBase** nextChild; /* pointer to pointer to modify pointer :) */ + + /* skip self and parent */ + rc = skipDR(volInfo); + if(rc <= 0) + return rc; + bytesRead += rc; + rc = skipDR(volInfo); + if(rc <= 0) + return rc; + bytesRead += rc; + + nextChild = &(dir->children); + childrenBytesRead = 0; + while(childrenBytesRead + bytesRead < size) + { + if(haveNextRecordInSector(volInfo)) + /* read it */ + { + int recordLength; + + if( dirDrFollows(volInfo) ) + /* directory descriptor record */ + { + *nextChild = malloc(sizeof(BkDir)); + if(*nextChild == NULL) + return BKERROR_OUT_OF_MEMORY; + + memset(*nextChild, 0, sizeof(BkDir)); + + recordLength = readDir(volInfo, BK_DIR_PTR(*nextChild), + filenameType, keepPosixPermissions); + if(recordLength < 0) + return recordLength; + } + else + /* file descriptor record */ + { + BkFileBase* specialFile; + + /* assume it's a file for now */ + *nextChild = malloc(sizeof(BkFile)); + if(*nextChild == NULL) + return BKERROR_OUT_OF_MEMORY; + + memset(*nextChild, 0, sizeof(BkFile)); + + recordLength = readFileInfo(volInfo, BK_FILE_PTR(*nextChild), + filenameType, keepPosixPermissions, + &specialFile); + if(recordLength < 0) + return recordLength; + + if(specialFile != NULL) + /* it's a special file, replace the allocated BkFile */ + { + free(*nextChild); + *nextChild = specialFile; + } + } + + childrenBytesRead += recordLength; + + nextChild = &((*nextChild)->next); + *nextChild = NULL; + } + else + /* read zeroes until get to next record (that would be in the next + * sector btw) or get to the end of data (dir->self.dataLength) */ + { + char testByte; + bk_off_t origPos; + + do + { + origPos = readSeekTell(volInfo); + + rc = readRead(volInfo, &testByte, 1); + if(rc != 1) + return BKERROR_READ_GENERIC; + + if(testByte != 0) + { + readSeekSet(volInfo, origPos, SEEK_SET); + break; + } + + childrenBytesRead += 1; + + } while (childrenBytesRead + bytesRead < size); + } + } + + return bytesRead; +} + +/******************************************************************************* +* readFileInfo() +* Reads the directory record for a file +* */ +int readFileInfo(VolInfo* volInfo, BkFile* file, int filenameType, + bool keepPosixPermissions, BkFileBase** specialFile) +{ + int rc; + unsigned char recordLength; + unsigned locExtent; /* block num where the file is */ + unsigned lenExtent; /* in bytes */ + unsigned char lenFileId9660; /* also len joliet fileid (bytes) */ + int lenSU; /* calculated as recordLength - 33 - lenFileId9660 */ + bk_off_t posBeforeName; + char nameAsOnDisk[UCHAR_MAX + 1]; + + /* so if anything failes it's still safe to delete file */ + file->pathAndName = NULL; + + if(volInfo->stopOperation) + return BKERROR_OPER_CANCELED_BY_USER; + + maybeUpdateProgress(volInfo); + + *specialFile = NULL; + + rc = readRead(volInfo, &recordLength, 1); + if(rc != 1) + return BKERROR_READ_GENERIC; + + readSeekSet(volInfo, 1, SEEK_CUR); + + rc = read733(volInfo, &locExtent); + if(rc != 8) + return BKERROR_READ_GENERIC; + + rc = read733(volInfo, &lenExtent); + if(rc != 8) + return BKERROR_READ_GENERIC; + + /* The length of isolinux.bin given in the initial/default entry of + * the el torito boot catalog does not match the actual length of the file + * but apparently when executed by the bios that's not a problem. + * However, if i ever want to read that file myself, i need + * the length proper. + * So i'm looking for a file that starts in the same logical sector as the + * boot record from the initial/default entry. */ + if(volInfo->bootMediaType == BOOT_MEDIA_NO_EMULATION && + locExtent == volInfo->bootRecordOffset / NBYTES_LOGICAL_BLOCK) + { + volInfo->bootRecordSize = lenExtent; + + volInfo->bootRecordIsVisible = true; + volInfo->bootRecordOnImage = file; + } + + readSeekSet(volInfo, 14, SEEK_CUR); + + rc = readRead(volInfo, &lenFileId9660, 1); + if(rc != 1) + return BKERROR_READ_GENERIC; + + lenSU = recordLength - 33 - lenFileId9660; + if(lenFileId9660 % 2 == 0) + lenSU -= 1; + + /* READ 9660 name */ + posBeforeName = readSeekTell(volInfo); + + rc = readRead(volInfo, nameAsOnDisk, lenFileId9660); + if(rc != lenFileId9660) + return BKERROR_READ_GENERIC; + nameAsOnDisk[lenFileId9660] = '\0'; + + /* record 9660 name for writing later */ + strncpy(BK_BASE_PTR(file)->original9660name, nameAsOnDisk, 14); + BK_BASE_PTR(file)->original9660name[14] = '\0'; + + removeCrapFromFilename(nameAsOnDisk, lenFileId9660); + + strncpy(BK_BASE_PTR(file)->name, nameAsOnDisk, NCHARS_FILE_ID_MAX_STORE - 1); + BK_BASE_PTR(file)->name[NCHARS_FILE_ID_MAX_STORE - 1] = '\0'; + + /* padding field */ + if(lenFileId9660 % 2 == 0) + readSeekSet(volInfo, 1, SEEK_CUR); + + if(filenameType != FNTYPE_9660) + readSeekSet(volInfo, posBeforeName, SEEK_SET); + /* END READ 9660 name */ + + if(filenameType == FNTYPE_JOLIET) + { + char nameAsOnDisk[UCHAR_MAX]; + /* in the worst possible case i'll use 129 bytes for this: */ + char nameInAscii[UCHAR_MAX]; + int ucsCount, byteCount; + + if(lenFileId9660 % 2 != 0) + return BKERROR_INVALID_UCS2; + + rc = readRead(volInfo, nameAsOnDisk, lenFileId9660); + if(rc != lenFileId9660) + return BKERROR_READ_GENERIC; + + for(ucsCount = 1, byteCount = 0; ucsCount < lenFileId9660; + ucsCount += 2, byteCount += 1) + { + nameInAscii[byteCount] = nameAsOnDisk[ucsCount]; + } + + removeCrapFromFilename(nameInAscii, lenFileId9660 / 2); + + if( strlen(nameInAscii) > NCHARS_FILE_ID_MAX_STORE - 1 ) + return BKERROR_MAX_NAME_LENGTH_EXCEEDED; + + strncpy(BK_BASE_PTR(file)->name, nameInAscii, NCHARS_FILE_ID_MAX_STORE - 1); + BK_BASE_PTR(file)->name[NCHARS_FILE_ID_MAX_STORE - 1] = '\0'; + + /* padding field */ + if(lenFileId9660 % 2 == 0) + readSeekSet(volInfo, 1, SEEK_CUR); + } + else if(filenameType == FNTYPE_ROCKRIDGE) + { + /* skip 9660 filename */ + readSeekSet(volInfo, lenFileId9660, SEEK_CUR); + /* skip padding field */ + if(lenFileId9660 % 2 == 0) + readSeekSet(volInfo, 1, SEEK_CUR); + + rc = readRockridgeFilename(volInfo, BK_BASE_PTR(file)->name, lenSU, 0); + if(rc < 0) + return rc; + } + else if(filenameType != FNTYPE_9660) + return BKERROR_UNKNOWN_FILENAME_TYPE; + + if(keepPosixPermissions) + { + rc = readPosixFileMode(volInfo, &(BK_BASE_PTR(file)->posixFileMode), lenSU); + if(rc < 0) + return rc; + } + else + { + BK_BASE_PTR(file)->posixFileMode = volInfo->posixFileDefaults; + } + //~ printf("'%s' %X\n", BK_BASE_PTR(file)->name, BK_BASE_PTR(file)->posixFileMode); + rc = readRockridgeSymlink(volInfo, (BkSymLink**)specialFile, lenSU); + if(rc < 0) + return rc; + + if(*specialFile != NULL) + /* the file is actually a symbolic link */ + { + strcpy((*specialFile)->name, BK_BASE_PTR(file)->name); + strcpy((*specialFile)->original9660name, BK_BASE_PTR(file)->original9660name); + /* apparently permissions for symbolic links are never used */ + (*specialFile)->posixFileMode = 0120777; + } + + if(volInfo->scanForDuplicateFiles) + { + BkHardLink* newLink; + + rc = findInHardLinkTable(volInfo, locExtent * NBYTES_LOGICAL_BLOCK, NULL, + lenExtent, true, &newLink); + if(rc < 0) + return rc; + + if(newLink == NULL) + /* not found */ + { + rc = addToHardLinkTable(volInfo, locExtent * NBYTES_LOGICAL_BLOCK, + NULL, lenExtent, true, &newLink); + if(rc < 0) + return rc; + } + + file->location = newLink; + } + + readSeekSet(volInfo, lenSU, SEEK_CUR); + + file->onImage = true; + file->position = ((bk_off_t)locExtent) * ((bk_off_t)NBYTES_LOGICAL_BLOCK); + file->size = lenExtent; + + return recordLength; +} + +/******************************************************************************* +* readPosixFileMode() +* looks for the PX system use field and gets the permissions field out of it +* */ +int readPosixFileMode(VolInfo* volInfo, unsigned* posixFileMode, int lenSU) +{ + bk_off_t origPos; + unsigned char* suFields; + int rc; + bool foundPosix; + bool foundCE; + int count; + unsigned logicalBlockOfCE; + unsigned offsetInLogicalBlockOfCE; + unsigned lengthOfCE; /* in bytes */ + + suFields = malloc(lenSU); + if(suFields == NULL) + return BKERROR_OUT_OF_MEMORY; + + origPos = readSeekTell(volInfo); + + rc = readRead(volInfo, suFields, lenSU); + + if(rc != lenSU) + return BKERROR_READ_GENERIC; + count = 0; + foundPosix = false; + foundCE = false; + while(count < lenSU && !foundPosix) + { + if(suFields[count] == 0) + /* not an SU field, mkisofs sometimes has a trailing 0 in the directory + * record and this is the easiest way to ignore it */ + break; + + if(suFields[count] == 'P' && suFields[count + 1] == 'X') + { + //~ printf("%X %X %X %X\n", *(suFields + count + 4), *(suFields + count + 5), *(suFields + count + 6), *(suFields + count + 7)); + read733FromCharArray(suFields + count + 4, posixFileMode); + + /* not interested in anything else from this field */ + + foundPosix = true; + } + else if(suFields[count] == 'C' && suFields[count + 1] == 'E') + { + foundCE = true; + read733FromCharArray(suFields + count + 4, &logicalBlockOfCE); + read733FromCharArray(suFields + count + 12, &offsetInLogicalBlockOfCE); + read733FromCharArray(suFields + count + 20, &lengthOfCE); + } + + /* skip su record */ + count += suFields[count + 2]; + } + + free(suFields); + readSeekSet(volInfo, origPos, SEEK_SET); + + if(!foundPosix) + { + if(!foundCE) + return BKERROR_NO_POSIX_PRESENT; + else + { + readSeekSet(volInfo, logicalBlockOfCE * NBYTES_LOGICAL_BLOCK + + offsetInLogicalBlockOfCE, SEEK_SET); + rc = readPosixFileMode(volInfo, posixFileMode, lengthOfCE); + + readSeekSet(volInfo, origPos, SEEK_SET); + + return rc; + } + } + + return 1; +} + +/******************************************************************************* +* readRockridgeFilename() +* Finds the NM entry in the system use fields and reads a maximum of +* NCHARS_FILE_ID_MAX_STORE characters from it (truncates if necessary). +* The continue system use field is not implemented so if the name is not in +* this directory record, the function returns a failure. +* Leaves the file pointer where it was. +*/ +int readRockridgeFilename(VolInfo* volInfo, char* dest, int lenSU, + unsigned numCharsReadAlready) +{ + bk_off_t origPos; + unsigned char* suFields; + int rc; + int count; + int lengthThisNM; + int usableLenThisNM; + bool foundName; + bool nameContinues; /* in another NM entry */ + bool foundCE; + unsigned logicalBlockOfCE; + unsigned offsetInLogicalBlockOfCE; + unsigned lengthOfCE; /* in bytes */ + + suFields = malloc(lenSU); + if(suFields == NULL) + return BKERROR_OUT_OF_MEMORY; + + origPos = readSeekTell(volInfo); + + rc = readRead(volInfo, suFields, lenSU); + if(rc != lenSU) + { + free(suFields); + return BKERROR_READ_GENERIC; + } + + count = 0; + foundName = false; + nameContinues = false; + foundCE = false; + while(count < lenSU) + { + if(suFields[count] == 0) + /* not an SU field, mkisofs sometimes has a trailing 0 in the directory + * record and this is the easiest way to ignore it */ + break; + + if(suFields[count] == 'N' && suFields[count + 1] == 'M') + { + lengthThisNM = suFields[count + 2] - 5; + + /* the data structures cannot handle filenames longer than + * NCHARS_FILE_ID_MAX_STORE so in case the image contains an + * invalid, long filename, truncate it rather than corrupt memory */ + if(lengthThisNM + numCharsReadAlready > NCHARS_FILE_ID_MAX_STORE - 1) + usableLenThisNM = NCHARS_FILE_ID_MAX_STORE - numCharsReadAlready - 1; + else + usableLenThisNM = lengthThisNM; + + strncpy(dest + numCharsReadAlready, (char*)suFields + count + 5, usableLenThisNM); + dest[usableLenThisNM + numCharsReadAlready] = '\0'; + + numCharsReadAlready += usableLenThisNM; + + foundName = true; + nameContinues = suFields[count + 4] & 0x01; /* NM 'continue' flag */ + } + else if(suFields[count] == 'C' && suFields[count + 1] == 'E') + { + foundCE = true; + read733FromCharArray(suFields + count + 4, &logicalBlockOfCE); + read733FromCharArray(suFields + count + 12, &offsetInLogicalBlockOfCE); + read733FromCharArray(suFields + count + 20, &lengthOfCE); + } + + /* skip su record */ + count += suFields[count + 2]; + } + + free(suFields); + readSeekSet(volInfo, origPos, SEEK_SET); + + if( !foundName || (foundName && nameContinues) ) + { + if(!foundCE) + return BKERROR_RR_FILENAME_MISSING; + else + { + readSeekSet(volInfo, + logicalBlockOfCE * NBYTES_LOGICAL_BLOCK + offsetInLogicalBlockOfCE, + SEEK_SET); + rc = readRockridgeFilename(volInfo, dest, lengthOfCE, numCharsReadAlready); + + readSeekSet(volInfo, origPos, SEEK_SET); + + return rc; + } + } + else + return 1; +} + +/* if no SL record is found does not return failure */ +int readRockridgeSymlink(VolInfo* volInfo, BkSymLink** dest, int lenSU) +{ + bk_off_t origPos; + unsigned char* suFields; + int rc; + int count; + int count2; + + suFields = malloc(lenSU); + if(suFields == NULL) + return BKERROR_OUT_OF_MEMORY; + + origPos = readSeekTell(volInfo); + + rc = readRead(volInfo, suFields, lenSU); + if(rc != lenSU) + { + free(suFields); + return BKERROR_READ_GENERIC; + } + + count = 0; + while(count < lenSU) + { + if(suFields[count] == 0) + /* not an SU field, mkisofs sometimes has a trailing 0 in the directory + * record and this is the easiest way to ignore it */ + break; + + if(suFields[count] == 'S' && suFields[count + 1] == 'L') + { + size_t numCharsUsed; /* in dest->target, not including '\0' */ + + *dest = malloc(sizeof(BkSymLink)); + if(*dest == NULL) + return BKERROR_OUT_OF_MEMORY; + + memset(*dest, 0, sizeof(BkSymLink)); + + numCharsUsed = 0; + (*dest)->target[0] = '\0'; + /* read sym link component records and assemble (*dest)->target + * right now component records cannot spawn multiple SL entries */ + count2 = count + 5; + while(count2 < count + suFields[count + 2]) + { + if(suFields[count2] & 0x02) + { + numCharsUsed += appendStringIfHaveRoom((*dest)->target, + ".", NCHARS_SYMLINK_TARGET_MAX - 1, + numCharsUsed, -1); + } + else if(suFields[count2] & 0x04) + { + numCharsUsed += appendStringIfHaveRoom((*dest)->target, + "..", NCHARS_SYMLINK_TARGET_MAX - 1, + numCharsUsed, -1); + } + else if(suFields[count2] & 0x08) + { + strcpy((*dest)->target, "/"); + numCharsUsed = 1; + } + + /* if bits 1-5 are set there is no component content */ + if( !(suFields[count2] & 0x3E) ) + { + numCharsUsed += appendStringIfHaveRoom((*dest)->target, + (char*)(suFields + count2 + 2), + NCHARS_SYMLINK_TARGET_MAX - 1, + numCharsUsed, suFields[count2 + 1]); + } + + /* next component record */ + count2 += suFields[count2 + 1] + 2; + + if(count2 < count + suFields[count + 2]) + /* another component record follows, insert separator */ + { + numCharsUsed += appendStringIfHaveRoom((*dest)->target, + "/", NCHARS_SYMLINK_TARGET_MAX - 1, + numCharsUsed, -1); + } + } + + /* ignore any other SU fields */ + break; + } + + /* skip su field */ + count += suFields[count + 2]; + } + + free(suFields); + readSeekSet(volInfo, origPos, SEEK_SET); + + return 1; +} + +/******************************************************************************* +* removeCrapFromFilename() +* filenames as read from 9660 Sometimes end with ;1 (terminator+version num) +* this removes the useless ending and terminates the destination with a '\0' +* */ +void removeCrapFromFilename(char* filename, int length) +{ + int count; + bool stop = false; + + for(count = 0; count < length && !stop; count++) + { + if(filename[count] == ';') + { + filename[count] = '\0'; + stop = true; + } + } + + /* if did not get a ';' terminate string anyway */ + filename[count] = '\0'; +} + +/******************************************************************************* +* skipDR() +* Seek past a directory entry. Good for skipping "." and ".." +* */ +int skipDR(VolInfo* volInfo) +{ + unsigned char dRLen; + int rc; + + rc = read711(volInfo, &dRLen); + if(rc <= 0) + return BKERROR_READ_GENERIC; + + readSeekSet(volInfo, dRLen - 1, SEEK_CUR); + + return dRLen; +} + +/******************************************************************************* +* stripSpacesFromEndOfString +* Some strings in the ISO volume are padded with spaces (hopefully on the right) +* this function removes them. +* */ +void stripSpacesFromEndOfString(char* str) +{ + size_t count; + + for(count = strlen(str) - 1; str[count] == ' '; count--) + { + str[count] = '\0'; + + if(count == 0) /* unsigned */ + break; + } +} diff --git a/lib/bkisofs/bkRead.h b/lib/bkisofs/bkRead.h new file mode 100644 index 0000000..30b3bc6 --- /dev/null +++ b/lib/bkisofs/bkRead.h @@ -0,0 +1,19 @@ +size_t appendStringIfHaveRoom(char* dest, const char* src, size_t destMaxLen, + size_t destCharsAlreadyUsed, int maxSrcLen); +bool dirDrFollows(VolInfo* volInfo); +bool haveNextRecordInSector(VolInfo* volInfo); +int readDir(VolInfo* volInfo, BkDir* dir, int filenameType, + bool keepPosixPermissions); +int readDirContents(VolInfo* volInfo, BkDir* dir, unsigned size, + int filenameType, bool keepPosixPermissions); +int readFileInfo(VolInfo* volInfo, BkFile* file, int filenameType, + bool keepPosixPermissions, BkFileBase** specialFile); +unsigned char readNextRecordLen(int image); +int readPosixFileMode(VolInfo* volInfo, unsigned* posixPermissions, + int lenSU); +int readRockridgeFilename(VolInfo* volInfo, char* dest, int lenSU, + unsigned numCharsReadAlready); +int readRockridgeSymlink(VolInfo* volInfo, BkSymLink** dest, int lenSU); +void removeCrapFromFilename(char* filename, int length); +int skipDR(VolInfo* volInfo); +void stripSpacesFromEndOfString(char* str); diff --git a/lib/bkisofs/bkRead7x.c b/lib/bkisofs/bkRead7x.c new file mode 100644 index 0000000..c21d13e --- /dev/null +++ b/lib/bkisofs/bkRead7x.c @@ -0,0 +1,85 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include + +#include "bkRead7x.h" +#include "bk.h" +#include "bkIoWrappers.h" + +int read711(VolInfo* volInfo, unsigned char* value) +{ + return readRead(volInfo, value, 1); +} + +int read721(VolInfo* volInfo, unsigned short* value) +{ + int rc; + unsigned char array[2]; + + rc = readRead(volInfo, array, 2); + if(rc != 2) + return rc; + + *value = array[1]; + *value <<= 8; + *value |= array[0]; + + return rc; +} + +int read731(VolInfo* volInfo, unsigned* value) +{ + int rc; + unsigned char array[4]; + + rc = readRead(volInfo, array, 4); + if(rc != 4) + return rc; + + *value = array[3]; + *value <<= 8; + *value |= array[2]; + *value <<= 8; + *value |= array[1]; + *value <<= 8; + *value |= array[0]; + + return rc; +} + +int read733(VolInfo* volInfo, unsigned* value) +{ + int rc; + unsigned char both[8]; + + rc = readRead(volInfo, both, 8); + if(rc != 8) + return rc; + + read733FromCharArray(both, value); + + return rc; +} + +void read733FromCharArray(unsigned char* array, unsigned* value) +{ + *value = array[3]; + *value <<= 8; + *value |= array[2]; + *value <<= 8; + *value |= array[1]; + *value <<= 8; + *value |= array[0]; +} diff --git a/lib/bkisofs/bkRead7x.h b/lib/bkisofs/bkRead7x.h new file mode 100644 index 0000000..3ca8d4b --- /dev/null +++ b/lib/bkisofs/bkRead7x.h @@ -0,0 +1,17 @@ +/******************************************************************************* +* bkRead7x +* functions to read simple variables as described in sections 7.x of iso9660 +* not including filenames (7.4, 7.5, 7.6) +* +* if they are stored in both byte orders, the appropriate one is read into +* the parameter but the return is 2x the size of that variable +* +* */ + +#include "bk.h" + +int read711(VolInfo* volInfo, unsigned char* value); +int read721(VolInfo* volInfo, unsigned short* value); +int read731(VolInfo* volInfo, unsigned* value); +int read733(VolInfo* volInfo, unsigned* value); +void read733FromCharArray(unsigned char* array, unsigned* value); diff --git a/lib/bkisofs/bkSet.c b/lib/bkisofs/bkSet.c new file mode 100644 index 0000000..66b498a --- /dev/null +++ b/lib/bkisofs/bkSet.c @@ -0,0 +1,311 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include +#include +#include + +#include "bk.h" +#include "bkSet.h" +#include "bkDelete.h" +#include "bkPath.h" +#include "bkError.h" +#include "bkIoWrappers.h" + +void bk_cancel_operation(VolInfo* volInfo) +{ + volInfo->stopOperation = true; +} + +/******************************************************************************* +* bk_destroy_vol_info() +* Frees any memory refered to by volinfo. +* If an image was open for reading closes it. +* Does not reinitialize the structure. +* */ +void bk_destroy_vol_info(VolInfo* volInfo) +{ + BkHardLink* currentLink; + BkHardLink* nextLink; + + deleteDirContents(volInfo, &(volInfo->dirTree)); + + if(volInfo->bootRecordPathAndName != NULL) + free(volInfo->bootRecordPathAndName); + + if(volInfo->imageForReading > 0) + bkClose(volInfo->imageForReading); + + currentLink = volInfo->fileLocations; + while(currentLink != NULL) + { + nextLink = currentLink->next; + free(currentLink); + currentLink = nextLink; + } +} + +/******************************************************************************* +* bk_init_vol_info() +* +* */ +int bk_init_vol_info(VolInfo* volInfo, bool scanForDuplicateFiles) +{ + memset(volInfo, 0, sizeof(VolInfo)); + + volInfo->dirTree.base.posixFileMode = 040755; + volInfo->posixFileDefaults = 0100644; + volInfo->posixDirDefaults = 040755; + + volInfo->scanForDuplicateFiles = scanForDuplicateFiles; + + return 1; +} + +/******************************************************************************* +* bk_rename() +* Rename the file/dir. +* */ +int bk_rename(VolInfo* volInfo, const char* srcPathAndName, + const char* newName) +{ + int rc; + NewPath srcPath; + BkDir* parentDir; + bool dirFound; + BkFileBase* child; + bool done; + size_t newNameLen; + + newNameLen = strlen(newName); + + if(newNameLen > NCHARS_FILE_ID_MAX_STORE - 1) + return BKERROR_MAX_NAME_LENGTH_EXCEEDED; + if(newNameLen == 0) + return BKERROR_BLANK_NAME; + if( !nameIsValid(newName) ) + return BKERROR_NAME_INVALID_CHAR; + + rc = makeNewPathFromString(srcPathAndName, &srcPath); + if(rc <= 0) + { + freePathContents(&srcPath); + return rc; + } + + if(srcPath.numChildren == 0) + { + freePathContents(&srcPath); + return BKERROR_RENAME_ROOT; + } + + if( strcmp(srcPath.children[srcPath.numChildren - 1], newName) == 0 ) + /* rename to the same name, ignore silently */ + return 1; + + /* i want the parent directory */ + srcPath.numChildren--; + dirFound = findDirByNewPath(&srcPath, &(volInfo->dirTree), &parentDir); + srcPath.numChildren++; + if(!dirFound) + { + freePathContents(&srcPath); + return BKERROR_DIR_NOT_FOUND_ON_IMAGE; + } + + done = false; + + child = parentDir->children; + while(child != NULL && !done) + { + if(itemIsInDir(newName, parentDir)) + return BKERROR_DUPLICATE_RENAME; + + if(strcmp(child->name, srcPath.children[srcPath.numChildren - 1]) == 0) + { + strcpy(child->name, newName); + + done = true; + } + + child = child->next; + } + + freePathContents(&srcPath); + + if(done) + return 1; + else + return BKERROR_ITEM_NOT_FOUND_ON_IMAGE; +} + +/******************************************************************************* +* bk_set_boot_file() +* Set a file on the image to be the no-emulation boot record for el torito. +* */ +int bk_set_boot_file(VolInfo* volInfo, const char* srcPathAndName) +{ + int rc; + NewPath path; + BkDir* srcDirInTree; + BkFileBase* child; + bool found; + + rc = makeNewPathFromString(srcPathAndName, &path); + if(rc <= 0) + { + freePathContents(&path); + return rc; + } + + path.numChildren--; + found = findDirByNewPath(&path, &(volInfo->dirTree), &srcDirInTree); + if(!found) + return BKERROR_DIR_NOT_FOUND_ON_IMAGE; + path.numChildren++; + + /* FIND the file */ + found = false; + child = srcDirInTree->children; + while(child != NULL && !found) + { + if(strcmp(child->name, path.children[path.numChildren - 1]) == 0) + { + if( !IS_REG_FILE(child->posixFileMode) ) + { + freePathContents(&path); + return BKERROR_NOT_REG_FILE_FOR_BR; + } + + found = true; + + volInfo->bootMediaType = BOOT_MEDIA_NO_EMULATION; + + volInfo->bootRecordSize = BK_FILE_PTR(child)->size; + + if(volInfo->bootRecordPathAndName != NULL) + { + free(volInfo->bootRecordPathAndName); + volInfo->bootRecordPathAndName = NULL; + } + + volInfo->bootRecordIsVisible = true; + + volInfo->bootRecordOnImage = BK_FILE_PTR(child); + } + + child = child->next; + } + if(!found) + { + freePathContents(&path); + return BKERROR_FILE_NOT_FOUND_ON_IMAGE; + } + /* END FIND the file */ + + freePathContents(&path); + + return 1; +} + +void bk_set_follow_symlinks(VolInfo* volInfo, bool doFollow) +{ + volInfo->followSymLinks = doFollow; +} + +/******************************************************************************* +* bk_get_sermissions() +* public function +* sets the permissions (not all of the posix info) for an item (file, dir, etc.) +* */ +int bk_set_permissions(VolInfo* volInfo, const char* pathAndName, + mode_t permissions) +{ + int rc; + NewPath srcPath; + BkFileBase* base; + bool itemFound; + + rc = makeNewPathFromString(pathAndName, &srcPath); + if(rc <= 0) + { + freePathContents(&srcPath); + return rc; + } + + itemFound = findBaseByNewPath(&srcPath, &(volInfo->dirTree), &base); + + freePathContents(&srcPath); + + if(!itemFound) + return BKERROR_ITEM_NOT_FOUND_ON_IMAGE; + + /* set all permission bits in posixFileMode to 0 */ + base->posixFileMode &= ~0777; + + /* copy permissions into posixFileMode */ + base->posixFileMode |= permissions & 0777; + + return 1; +} + +/******************************************************************************* +* bk_set_publisher() +* Copies publisher into volInfo, a maximum of 128 characters. +* */ +int bk_set_publisher(VolInfo* volInfo, const char* publisher) +{ + /* unfortunately some disks (e.g. Fedora 7) don't follow this rule + if( !nameIsValid9660(publisher) ) + return BKERROR_NAME_INVALID_CHAR;*/ + + strncpy(volInfo->publisher, publisher, 128); + + return 1; +} + +/******************************************************************************* +* bk_set_vol_name() +* Copies volName into volInfo, a maximum of 32 characters. +* */ +int bk_set_vol_name(VolInfo* volInfo, const char* volName) +{ + /* unfortunately some disks (e.g. Fedora 7) don't follow this rule + if( !nameIsValid9660(volName) ) + return BKERROR_NAME_INVALID_CHAR;*/ + + strncpy(volInfo->volId, volName, 32); + + return 1; +} + +/******************************************************************************* +* itemIsInDir() +* checks the contents of a directory (files and dirs) to see whether it +* has an item named +* */ +bool itemIsInDir(const char* name, const BkDir* dir) +{ + BkFileBase* child; + + child = dir->children; + while(child != NULL) + { + if(strcmp(child->name, name) == 0) + return true; + child = child->next; + } + + return false; +} diff --git a/lib/bkisofs/bkSet.h b/lib/bkisofs/bkSet.h new file mode 100644 index 0000000..c700b0f --- /dev/null +++ b/lib/bkisofs/bkSet.h @@ -0,0 +1 @@ +bool itemIsInDir(const char* name, const BkDir* dir); diff --git a/lib/bkisofs/bkSort.c b/lib/bkisofs/bkSort.c new file mode 100644 index 0000000..82c8646 --- /dev/null +++ b/lib/bkisofs/bkSort.c @@ -0,0 +1,117 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include +#include + +#include "bk.h" +#include "bkInternal.h" +#include "bkSort.h" + +/* strings cannot be equal */ +bool rightIsBigger(char* leftStr, char* rightStr) +{ + size_t leftLen; + size_t rightLen; + size_t count; + bool resultFound; + bool rc; + + leftLen = strlen(leftStr); + rightLen = strlen(rightStr); + + resultFound = false; + for(count = 0; count < leftLen && count < rightLen && !resultFound; count++) + { + if(rightStr[count] > leftStr[count]) + { + resultFound = true; + rc = true; + } + else if(rightStr[count] < leftStr[count]) + { + resultFound = true; + rc = false; + } + } + + if(!resultFound) + /* strings are the same up to the length of the shorter one */ + { + if(rightLen > leftLen) + rc = true; + else + rc = false; + } + + return rc; +} + +void sortDir(DirToWrite* dir, int filenameType) +{ + BaseToWrite* child; + BaseToWrite** outerPtr; + BaseToWrite** innerPtr; + + child = dir->children; + while(child != NULL) + { + if(IS_DIR(child->posixFileMode)) + sortDir(DIRTW_PTR(child), filenameType); + + child = child->next; + } + + outerPtr = &(dir->children); + while(*outerPtr != NULL) + { + innerPtr = &((*outerPtr)->next); + while(*innerPtr != NULL) + { + if( (filenameType & FNTYPE_JOLIET && + rightIsBigger((*innerPtr)->nameJoliet, (*outerPtr)->nameJoliet)) || + (filenameType & FNTYPE_9660 && + rightIsBigger((*innerPtr)->name9660, (*outerPtr)->name9660)) ) + { + BaseToWrite* outer = *outerPtr; + BaseToWrite* inner = *innerPtr; + + if( (*outerPtr)->next != *innerPtr ) + { + BaseToWrite* oldInnerNext = inner->next; + + *outerPtr = inner; + *innerPtr = outer; + + inner->next = outer->next; + outer->next = oldInnerNext; + } + else + { + BaseToWrite* oldInnerNext = inner->next; + + *outerPtr = inner; + innerPtr = &(inner->next); + + inner->next = outer; + outer->next = oldInnerNext; + } + } + + innerPtr = &((*innerPtr)->next); + } + + outerPtr = &((*outerPtr)->next); + } +} diff --git a/lib/bkisofs/bkSort.h b/lib/bkisofs/bkSort.h new file mode 100644 index 0000000..055c80c --- /dev/null +++ b/lib/bkisofs/bkSort.h @@ -0,0 +1,2 @@ +bool rightIsBigger(char* leftStr, char* rightStr); +void sortDir(DirToWrite* dir, int filenameType); diff --git a/lib/bkisofs/bkTime.c b/lib/bkisofs/bkTime.c new file mode 100644 index 0000000..3c87237 --- /dev/null +++ b/lib/bkisofs/bkTime.c @@ -0,0 +1,124 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +#include +#include +#include + +/* epoch time -> 8.4.26.1 */ +void epochToLongString(time_t epoch, char* longString) +{ + struct tm* timeStruct; + + localtime(&epoch); + timeStruct = gmtime(&epoch); + + sprintf(longString, "%4d%02d%02d%02d%02d%02d%02d", + timeStruct->tm_year + 1900, + timeStruct->tm_mon + 1, + timeStruct->tm_mday, + timeStruct->tm_hour, + timeStruct->tm_min, + timeStruct->tm_sec, + 0); + + /* this may not be 7.1.1 but since it's 0 who cares */ + longString[16] = 0; +} + +/* epoch time -> 9.1.5 */ +void epochToShortString(time_t epoch, char* shortString) +{ + struct tm* timeStruct; + + localtime(&epoch); + timeStruct = gmtime(&epoch); + + shortString[0] = timeStruct->tm_year; + shortString[1] = timeStruct->tm_mon + 1; + shortString[2] = timeStruct->tm_mday; + shortString[3] = timeStruct->tm_hour; + shortString[4] = timeStruct->tm_min; + shortString[5] = timeStruct->tm_sec; + + /* gmt offset */ + shortString[6] = 0; +} + +/* 8.4.26.1 -> epoch time */ +void longStringToEpoch(const char* longString, time_t* epoch) +{ + char str[5]; + int number; + struct tm timeStruct; + + /* no daylight savings setting available */ + timeStruct.tm_isdst = -1; + + /* YEAR */ + strncpy(str, longString, 4); + str[4] = '\0'; + + sscanf(str, "%d", &number); + + timeStruct.tm_year = number - 1900; + /* END YEAR */ + + /* MONTH */ + strncpy(str, longString + 4, 2); + str[2] = '\0'; + + sscanf(str, "%d", &number); + + timeStruct.tm_mon = number - 1; + /* END MONTH */ + + /* DAY */ + strncpy(str, longString + 6, 2); + str[2] = '\0'; + + sscanf(str, "%d", &number); + + timeStruct.tm_mday = number; + /* END DAY */ + + /* HOUR */ + strncpy(str, longString + 8, 2); + str[2] = '\0'; + + sscanf(str, "%d", &number); + + timeStruct.tm_hour = number; + /* END HOUR */ + + /* MINUTE */ + strncpy(str, longString + 10, 2); + str[2] = '\0'; + + sscanf(str, "%d", &number); + + timeStruct.tm_min = number; + /* END MINUTE */ + + /* SECOND */ + strncpy(str, longString + 12, 2); + str[2] = '\0'; + + sscanf(str, "%d", &number); + + timeStruct.tm_sec = number; + /* END SECOND */ + + *epoch = mktime(&timeStruct); +} diff --git a/lib/bkisofs/bkTime.h b/lib/bkisofs/bkTime.h new file mode 100644 index 0000000..3e2251c --- /dev/null +++ b/lib/bkisofs/bkTime.h @@ -0,0 +1,3 @@ +void epochToLongString(time_t epoch, char* longString); +void epochToShortString(time_t epoch, char* shortString); +void longStringToEpoch(const char* longString, time_t* epoch); diff --git a/lib/bkisofs/bkWrite.c b/lib/bkisofs/bkWrite.c new file mode 100644 index 0000000..031cb30 --- /dev/null +++ b/lib/bkisofs/bkWrite.c @@ -0,0 +1,2547 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +/****************************************************************************** +* Functions in this file write to volInfo.imageForWriting and are probably +* unsutable for anything else. +******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "bk.h" +#include "bkInternal.h" +#include "bkWrite7x.h" +#include "bkTime.h" +#include "bkWrite.h" +#include "bkMangle.h" +#include "bkError.h" +#include "bkSort.h" +#include "bkPath.h" +#include "bkCache.h" +#include "bkRead7x.h" +#include "bkLink.h" +#include "bkIoWrappers.h" + +/****************************************************************************** +* bk_write_image() +* Writes everything from first to last byte of the iso. +* Public function. +* */ +int bk_write_image(const char* newImagePathAndName, VolInfo* volInfo, + time_t creationTime, int filenameTypes, + void(*progressFunction)(VolInfo*, double)) +{ + int rc; + DirToWrite newTree; + bk_off_t svdOffset; + bk_off_t pRealRootDrOffset; + int pRootDirSize; + bk_off_t sRealRootDrOffset; + int sRootDirSize; + bk_off_t lPathTable9660Loc; + bk_off_t mPathTable9660Loc; + int pathTable9660Size; + bk_off_t lPathTableJolietLoc; + bk_off_t mPathTableJolietLoc; + int pathTableJolietSize; + bk_off_t bootCatalogSectorNumberOffset; + bk_off_t currPos; + + volInfo->writeProgressFunction = progressFunction; + volInfo->stopOperation = false; + + volInfo->estimatedIsoSize = bk_estimate_iso_size(volInfo, filenameTypes); + progressFunction(volInfo, 0); + + BkStatStruct statStruct; + rc = bkStat(newImagePathAndName, &statStruct); + if(rc == 0 && statStruct.st_ino == volInfo->imageForReadingInode) + return BKERROR_SAVE_OVERWRITE; + + /* because mangleDir works on dir's children i need to + * copy the root manually */ + memset(&newTree, 0, sizeof(DirToWrite)); + newTree.base.posixFileMode = volInfo->dirTree.base.posixFileMode; + + printf("mangling\n");fflush(NULL); + /* create tree to write */ + rc = mangleDir(&(volInfo->dirTree), &newTree, filenameTypes); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + return rc; + } + + printf("opening '%s' for writing\n", newImagePathAndName);fflush(NULL); + volInfo->imageForWriting = open(newImagePathAndName, + O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + if(volInfo->imageForWriting == -1) + { + freeDirToWriteContents(&newTree); + return BKERROR_OPEN_WRITE_FAILED; + } + + printf("writing blank at %X\n", (int)wcSeekTell(volInfo));fflush(NULL); + /* system area, always zeroes */ + rc = writeByteBlock(volInfo, 0, NBYTES_LOGICAL_BLOCK * NLS_SYSTEM_AREA); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + + /* skip pvd (1 block), write it after files */ + wcSeekForward(volInfo, NBYTES_LOGICAL_BLOCK); + + if(volInfo->bootMediaType != BOOT_MEDIA_NONE) + { + /* el torito volume descriptor */ + rc = writeElToritoVd(volInfo, &bootCatalogSectorNumberOffset); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + } + + if(filenameTypes & FNTYPE_JOLIET) + /* skip svd (1 block), write it after pvd */ + { + svdOffset = wcSeekTell(volInfo); + wcSeekForward(volInfo, NBYTES_LOGICAL_BLOCK); + } + + printf("writing terminator at %X\n", (int)wcSeekTell(volInfo));fflush(NULL); + /* volume descriptor set terminator */ + rc = writeVdsetTerminator(volInfo); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + + if(volInfo->bootMediaType != BOOT_MEDIA_NONE) + { + /* write boot catalog sector number */ + currPos = wcSeekTell(volInfo); + wcSeekSet(volInfo, bootCatalogSectorNumberOffset); + rc = write731(volInfo, currPos / NBYTES_LOGICAL_BLOCK); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + wcSeekSet(volInfo, currPos); + + /* write el torito booting catalog */ + rc = writeElToritoBootCatalog(volInfo, &(volInfo->bootRecordSectorNumberOffset)); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + } + + /* MAYBE write boot record file now */ + if(volInfo->bootMediaType != BOOT_MEDIA_NONE && + !volInfo->bootRecordIsVisible) + { + int blankSize; + int srcFile; /* either the old image or the boot record file on + * the regular filesystem */ + bool srcFileOpened; + + /* set up source file pointer */ + if(volInfo->bootRecordIsOnImage) + { + srcFile = volInfo->imageForReading; + bkSeekSet(volInfo->imageForReading, volInfo->bootRecordOffset, SEEK_SET); + srcFileOpened = false; + } + else + { + srcFile = open(volInfo->bootRecordPathAndName, O_RDONLY, 0); + if(srcFile == -1) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return BKERROR_OPEN_READ_FAILED; + } + srcFileOpened = true; + } + + /* write boot record sector number */ + currPos = wcSeekTell(volInfo); + wcSeekSet(volInfo, volInfo->bootRecordSectorNumberOffset); + + rc = write731(volInfo, currPos / NBYTES_LOGICAL_BLOCK); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + if(srcFileOpened) + bkClose(srcFile); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + wcSeekSet(volInfo, currPos); + + /* file contents */ + rc = writeByteBlockFromFile(srcFile, volInfo, volInfo->bootRecordSize); + if(rc < 0) + { + freeDirToWriteContents(&newTree); + if(srcFileOpened) + bkClose(srcFile); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + + blankSize = NBYTES_LOGICAL_BLOCK - + volInfo->bootRecordSize % NBYTES_LOGICAL_BLOCK; + + /* fill the last sector with 0s */ + rc = writeByteBlock(volInfo, 0x00, blankSize); + if(rc < 0) + { + freeDirToWriteContents(&newTree); + if(srcFileOpened) + bkClose(srcFile); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + + if(srcFileOpened) + bkClose(srcFile); + } + /* END MAYBE write boot record file now */ + + printf("sorting 9660\n"); + sortDir(&newTree, FNTYPE_9660); + + pRealRootDrOffset = wcSeekTell(volInfo); + + printf("writing primary directory tree at %X\n", (int)wcSeekTell(volInfo));fflush(NULL); + /* 9660 and maybe rockridge dir tree */ + rc = writeDir(volInfo, &newTree, 0, 0, 0, creationTime, + filenameTypes & (FNTYPE_9660 | FNTYPE_ROCKRIDGE), true); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + + pRootDirSize = rc; + + /* joliet dir tree */ + if(filenameTypes & FNTYPE_JOLIET) + { + printf("sorting joliet\n"); + sortDir(&newTree, FNTYPE_JOLIET); + + printf("writing supplementary directory tree at %X\n", (int)wcSeekTell(volInfo));fflush(NULL); + sRealRootDrOffset = wcSeekTell(volInfo); + + rc = writeDir(volInfo, &newTree, 0, 0, 0, creationTime, + FNTYPE_JOLIET, true); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + + sRootDirSize = rc; + } + + printf("writing 9660 path tables at %X\n", (int)wcSeekTell(volInfo));fflush(NULL); + + lPathTable9660Loc = wcSeekTell(volInfo); + rc = writePathTable(volInfo, &newTree, true, FNTYPE_9660); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + pathTable9660Size = rc; + + mPathTable9660Loc = wcSeekTell(volInfo); + rc = writePathTable(volInfo, &newTree, false, FNTYPE_9660); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + + if(filenameTypes & FNTYPE_JOLIET) + { + printf("writing joliet path tables at %X\n", (int)wcSeekTell(volInfo));fflush(NULL); + lPathTableJolietLoc = wcSeekTell(volInfo); + rc = writePathTable(volInfo, &newTree, true, FNTYPE_JOLIET); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + pathTableJolietSize = rc; + + mPathTableJolietLoc = wcSeekTell(volInfo); + rc = writePathTable(volInfo, &newTree, false, FNTYPE_JOLIET); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + } + + printf("writing files at %X\n", (int)wcSeekTell(volInfo));fflush(NULL); + resetWriteStatus(volInfo->fileLocations); + /* all files and offsets/sizes */ + rc = writeFileContents(volInfo, &newTree, filenameTypes); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + + if(filenameTypes & FNTYPE_ROCKRIDGE) + { + printf("writing long NMs at %X\n", (int)wcSeekTell(volInfo));fflush(NULL); + rc = writeLongNMsInDir(volInfo, &newTree); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + } + + wcSeekSet(volInfo, NBYTES_LOGICAL_BLOCK * NLS_SYSTEM_AREA); + + printf("writing pvd at %X\n", (int)wcSeekTell(volInfo));fflush(NULL); + rc = writeVolDescriptor(volInfo, pRealRootDrOffset, + pRootDirSize, lPathTable9660Loc, mPathTable9660Loc, + pathTable9660Size, creationTime, true); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + + if(filenameTypes & FNTYPE_JOLIET) + { + wcSeekSet(volInfo, svdOffset); + + printf("writing svd at %X\n", (int)wcSeekTell(volInfo));fflush(NULL); + rc = writeVolDescriptor(volInfo, sRealRootDrOffset, + sRootDirSize, lPathTableJolietLoc, mPathTableJolietLoc, + pathTableJolietSize, creationTime, false); + if(rc <= 0) + { + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + unlink(newImagePathAndName); + return rc; + } + } + + printf("freeing memory\n");fflush(NULL); + freeDirToWriteContents(&newTree); + bkClose(volInfo->imageForWriting); + + return 1; +} + +/****************************************************************************** +* bootInfoTableChecksum() +* Calculate the checksum to be written into the boot info table. +* */ +int bootInfoTableChecksum(int oldImage, FileToWrite* file, unsigned* checksum) +{ + int numTrailingBytes; /* to make sure the file size is divisible by 4 */ + ssize_t rc; + int srcFile; + unsigned char* contents; + unsigned count; + + numTrailingBytes = file->size % 4; + + contents = malloc(file->size + numTrailingBytes); + if(contents == NULL) + return BKERROR_OUT_OF_MEMORY; + + /* make sure the extra bytes i added are 0s */ + memset(contents + file->size, 0, numTrailingBytes); + + if(file->onImage) + /* read file from original image */ + { + bkSeekSet(oldImage, file->offset, SEEK_SET); + + rc = bkRead(oldImage, contents, file->size); + if(rc == -1 || rc != (int)(file->size)) + { + free(contents); + return BKERROR_READ_GENERIC; + } + } + else + /* read file from fs */ + { + srcFile = open(file->pathAndName, O_RDONLY, 0); + if(srcFile == -1) + { + free(contents); + return BKERROR_OPEN_READ_FAILED; + } + + rc = bkRead(srcFile, contents, file->size); + + bkClose(srcFile); + + if(rc == -1 || rc != (int)(file->size)) + { + free(contents); + return BKERROR_READ_GENERIC; + } + } + + *checksum = 0; + /* do 32 bit checksum starting from byte 64 + * because i check above that the file is divisible by 4 i will not be + * reading wrong memory */ + for(count = 64; count < file->size; count += 4) + { + unsigned toAdd; + + toAdd = *(contents + count) | (*(contents + count + 1) << 8) | + (*(contents + count + 2) << 16) | (*(contents + count + 3) << 24); + + *checksum += toAdd; + } + + free(contents); + + return 1; +} + +/****************************************************************************** +* countDirsOnLevel() +* a 'level' is described in ecma119 6.8.2 +* it's needed for path tables, don't remember exactly what for +* */ +int countDirsOnLevel(const DirToWrite* dir, int targetLevel, int thisLevel) +{ + BaseToWrite* child; + int sum; + + if(targetLevel == thisLevel) + { + return 1; + } + else + { + sum = 0; + + child = dir->children; + while(child != NULL) + { + if( IS_DIR(child->posixFileMode) ) + sum += countDirsOnLevel(DIRTW_PTR(child), targetLevel, thisLevel + 1); + + child = child->next; + } + + return sum; + } +} + +/****************************************************************************** +* countTreeHeight() +* caller should set heightSoFar to 1 +* */ +int countTreeHeight(const DirToWrite* dir, int heightSoFar) +{ + BaseToWrite* child; + int maxHeight; + int thisHeight; + + maxHeight = heightSoFar; + child = dir->children; + while(child != NULL) + { + if( IS_DIR(child->posixFileMode) ) + { + thisHeight = countTreeHeight(DIRTW_PTR(child), heightSoFar + 1); + + if(thisHeight > maxHeight) + maxHeight = thisHeight; + } + + child = child->next; + } + + return maxHeight; +} + +/****************************************************************************** +* elToritoChecksum() +* Algorithm: the sum of all words, including the checksum must trunkate to +* a 16-bit 0x0000 +* */ +unsigned short elToritoChecksum(const unsigned char* record) +{ + short sum; + int i; + + sum = 0; + for(i = 0; i < 32; i += 2) + { + sum += *(record + i) | (*(record + i + 1) << 8); + } + + return 0xFFFF - sum + 1; +} + +/****************************************************************************** +* writeByteBlock() +* Fills numBytes with byteToWrite. + +* */ +int writeByteBlock(VolInfo* volInfo, unsigned char byteToWrite, int numBytes) +{ + int rc; + int count; + int numBlocks; + int sizeLastBlock; + + memset(volInfo->readWriteBuffer, byteToWrite, READ_WRITE_BUFFER_SIZE); + + numBlocks = numBytes / READ_WRITE_BUFFER_SIZE; + sizeLastBlock = numBytes % READ_WRITE_BUFFER_SIZE; + + for(count = 0; count < numBlocks; count++) + { + rc = wcWrite(volInfo, volInfo->readWriteBuffer, READ_WRITE_BUFFER_SIZE); + if(rc <= 0) + return rc; + } + + if(sizeLastBlock > 0) + { + rc = wcWrite(volInfo, volInfo->readWriteBuffer, sizeLastBlock); + if(rc <= 0) + return rc; + } + + return 1; +} + +/****************************************************************************** +* writeByteBlockFromFile() +* copies numBytes from src into the image to write in blocks of 10K +* */ +int writeByteBlockFromFile(int src, VolInfo* volInfo, unsigned numBytes) +{ + int rc; + int count; + int numBlocks; + int sizeLastBlock; + + numBlocks = numBytes / READ_WRITE_BUFFER_SIZE; + sizeLastBlock = numBytes % READ_WRITE_BUFFER_SIZE; + + for(count = 0; count < numBlocks; count++) + { + if(volInfo->stopOperation) + return BKERROR_OPER_CANCELED_BY_USER; + + rc = bkRead(src, volInfo->readWriteBuffer, READ_WRITE_BUFFER_SIZE); + if(rc != READ_WRITE_BUFFER_SIZE) + return BKERROR_READ_GENERIC; + rc = wcWrite(volInfo, volInfo->readWriteBuffer, READ_WRITE_BUFFER_SIZE); + if(rc <= 0) + return rc; + } + + if(sizeLastBlock > 0) + { + rc = bkRead(src, volInfo->readWriteBuffer, sizeLastBlock); + if(rc != sizeLastBlock) + return BKERROR_READ_GENERIC; + rc = wcWrite(volInfo, volInfo->readWriteBuffer, sizeLastBlock); + if(rc <= 0) + return rc; + } + + return 1; +} + +/****************************************************************************** +* writeDir() +* Writes the contents of a directory. Also writes locations and sizes of +* directory records for directories but not for files. +* Returns data length of the dir written. +* */ +int writeDir(VolInfo* volInfo, DirToWrite* dir, int parentLbNum, + int parentNumBytes, int parentPosix, time_t recordingTime, + int filenameTypes, bool isRoot) +{ + int rc; + + bk_off_t startPos; + int numUnusedBytes; + bk_off_t endPos; + + DirToWrite selfDir; /* will have a different filename */ + DirToWrite parentDir; + + BaseToWrite* child; + + if(wcSeekTell(volInfo) % NBYTES_LOGICAL_BLOCK != 0) + return BKERROR_SANITY; + + /* names other then 9660 are not used for self and parent */ + selfDir.base.name9660[0] = 0x00; + selfDir.base.posixFileMode = dir->base.posixFileMode; + + parentDir.base.name9660[0] = 0x01; + parentDir.base.name9660[1] = '\0'; + if(isRoot) + parentDir.base.posixFileMode = selfDir.base.posixFileMode; + else + parentDir.base.posixFileMode = parentPosix; + + startPos = wcSeekTell(volInfo); + + if( startPos % NBYTES_LOGICAL_BLOCK != 0 ) + /* this should never happen */ + return BKERROR_SANITY; + + if(filenameTypes & FNTYPE_JOLIET) + dir->extentNumber2 = startPos / NBYTES_LOGICAL_BLOCK; + else + dir->base.extentNumber = startPos / NBYTES_LOGICAL_BLOCK; + + /* write self */ + if(isRoot) + { + rc = writeDr(volInfo, BASETW_PTR(&selfDir), recordingTime, true, true, true, filenameTypes); + if(rc < 0) + return rc; + + if(filenameTypes & FNTYPE_JOLIET) + dir->base.extentLocationOffset2 = selfDir.base.extentLocationOffset2; + else + dir->base.extentLocationOffset = selfDir.base.extentLocationOffset; + } + else + { + rc = writeDr(volInfo, BASETW_PTR(&selfDir), recordingTime, true, true, false, filenameTypes); + if(rc < 0) + return rc; + } + if(rc < 0) + return rc; + + /* write parent */ + rc = writeDr(volInfo, BASETW_PTR(&parentDir), recordingTime, true, true, false, filenameTypes); + if(rc < 0) + return rc; + + child = dir->children; + + /* WRITE children drs */ + while(child != NULL) + { + if(IS_DIR(child->posixFileMode)) + { + rc = writeDr(volInfo, child, recordingTime, + true, false, false, filenameTypes); + } + else + { + rc = writeDr(volInfo, child, recordingTime, + false, false, false, filenameTypes); + } + if(rc < 0) + return rc; + + child = child->next; + } + /* END WRITE children drs */ + + /* write blank to conclude extent */ + numUnusedBytes = NBYTES_LOGICAL_BLOCK - + wcSeekTell(volInfo) % NBYTES_LOGICAL_BLOCK; + rc = writeByteBlock(volInfo, 0x00, numUnusedBytes); + if(rc < 0) + return rc; + + if(filenameTypes & FNTYPE_JOLIET) + dir->dataLength2 = wcSeekTell(volInfo) - startPos; + else + dir->dataLength = wcSeekTell(volInfo) - startPos; + + /* write subdirectories */ + child = dir->children; + while(child != NULL) + { + if(IS_DIR(child->posixFileMode)) + { + if(filenameTypes & FNTYPE_JOLIET) + { + rc = writeDir(volInfo, DIRTW_PTR(child), dir->extentNumber2, + dir->dataLength2, BASETW_PTR(dir)->posixFileMode, recordingTime, + filenameTypes, false); + } + else + { + rc = writeDir(volInfo, DIRTW_PTR(child), BASETW_PTR(dir)->extentNumber, + dir->dataLength, BASETW_PTR(dir)->posixFileMode, recordingTime, + filenameTypes, false); + } + if(rc < 0) + return rc; + } + + child = child->next; + } + + endPos = wcSeekTell(volInfo); + + /* SELF extent location and size */ + if(filenameTypes & FNTYPE_JOLIET) + wcSeekSet(volInfo, selfDir.base.extentLocationOffset2); + else + wcSeekSet(volInfo, selfDir.base.extentLocationOffset); + + if(filenameTypes & FNTYPE_JOLIET) + { + rc = write733(volInfo, dir->extentNumber2); + if(rc <= 0) + return rc; + + rc = write733(volInfo, dir->dataLength2); + if(rc <= 0) + return rc; + } + else + { + rc = write733(volInfo, BASETW_PTR(dir)->extentNumber); + if(rc <= 0) + return rc; + + rc = write733(volInfo, dir->dataLength); + if(rc <= 0) + return rc; + } + /* END SELF extent location and size */ + + /* PARENT extent location and size */ + if(filenameTypes & FNTYPE_JOLIET) + wcSeekSet(volInfo, parentDir.base.extentLocationOffset2); + else + wcSeekSet(volInfo, parentDir.base.extentLocationOffset); + + if(parentLbNum == 0) + /* root, parent is same as self */ + { + if(filenameTypes & FNTYPE_JOLIET) + { + rc = write733(volInfo, dir->extentNumber2); + if(rc <= 0) + return rc; + + rc = write733(volInfo, dir->dataLength2); + if(rc <= 0) + return rc; + } + else + { + rc = write733(volInfo, BASETW_PTR(dir)->extentNumber); + if(rc <= 0) + return rc; + + rc = write733(volInfo, dir->dataLength); + if(rc <= 0) + return rc; + } + } + else + /* normal parent */ + { + rc = write733(volInfo, parentLbNum); + if(rc <= 0) + return rc; + + rc = write733(volInfo, parentNumBytes); + if(rc <= 0) + return rc; + } + /* END PARENT extent location and size */ + + /* ALL subdir extent locations and sizes */ + child = dir->children; + while(child != NULL) + { + if(IS_DIR(child->posixFileMode)) + { + if(filenameTypes & FNTYPE_JOLIET) + { + wcSeekSet(volInfo, child->extentLocationOffset2); + + rc = write733(volInfo, DIRTW_PTR(child)->extentNumber2); + if(rc <= 0) + return rc; + + rc = write733(volInfo, DIRTW_PTR(child)->dataLength2); + if(rc <= 0) + return rc; + } + else + { + wcSeekSet(volInfo, child->extentLocationOffset); + + rc = write733(volInfo, child->extentNumber); + if(rc <= 0) + return rc; + + rc = write733(volInfo, DIRTW_PTR(child)->dataLength); + if(rc <= 0) + return rc; + } + } + + child = child->next; + } + /* END ALL subdir extent locations and sizes */ + + wcSeekSet(volInfo, endPos); + + if(filenameTypes & FNTYPE_JOLIET) + return dir->dataLength2; + else + return dir->dataLength; +} + +/****************************************************************************** +* writeDr() +* Writes a directory record. +* Note that it uses only the members of DirToWrite and FileToWrite that are +* the same. +* */ +int writeDr(VolInfo* volInfo, BaseToWrite* node, time_t recordingTime, bool isADir, + bool isSelfOrParent, bool isFirstRecord, int filenameTypes) +{ + int rc; + unsigned char byte; + char aString[256]; + unsigned short aShort; + bk_off_t startPos; + bk_off_t endPos; + unsigned char lenFileId; + unsigned char recordLen; + + /* look at the end of the function for an explanation */ + writeDrStartLabel: + + startPos = wcSeekTell(volInfo); + + /* record length is recorded in the end */ + wcSeekForward(volInfo, 1); + + /* extended attribute record length */ + byte = 0; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + if(filenameTypes & FNTYPE_JOLIET) + node->extentLocationOffset2 = wcSeekTell(volInfo); + else + node->extentLocationOffset = wcSeekTell(volInfo); + + /* location of extent not recorded in this function */ + wcSeekForward(volInfo, 8); + + /* data length not recorded in this function */ + wcSeekForward(volInfo, 8); + + /* RECORDING time and date */ + epochToShortString(recordingTime, aString); + + rc = write711(volInfo, aString[0]); + if(rc <= 0) + return rc; + rc = write711(volInfo, aString[1]); + if(rc <= 0) + return rc; + rc = write711(volInfo, aString[2]); + if(rc <= 0) + return rc; + rc = write711(volInfo, aString[3]); + if(rc <= 0) + return rc; + rc = write711(volInfo, aString[4]); + if(rc <= 0) + return rc; + rc = write711(volInfo, aString[5]); + if(rc <= 0) + return rc; + rc = write711(volInfo, aString[6]); + if(rc <= 0) + return rc; + /* END RECORDING time and date */ + + /* FILE flags */ + if(isADir) + /* (only directory bit on) */ + byte = 0x02; + else + /* nothing on */ + byte = 0x00; + + rc = wcWrite(volInfo, (char*)&byte, 1); + if(rc <= 0) + return rc; + /* END FILE flags */ + + /* file unit size (always 0, non-interleaved mode) */ + byte = 0; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* interleave gap size (also always 0, non-interleaved mode) */ + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* volume sequence number (always 1) */ + aShort = 1; + rc = write723(volInfo, aShort); + if(rc <= 0) + return rc; + + /* LENGTH of file identifier */ + if(isSelfOrParent) + lenFileId = 1; + else + { + if(filenameTypes & FNTYPE_JOLIET) + lenFileId = 2 * strlen(node->nameJoliet); + else + /*if(isADir) see microsoft comment below */ + lenFileId = strlen(node->name9660); + /*else + lenFileId = strlen(node->name9660) + 2; */ + } + + rc = write711(volInfo, lenFileId); + if(rc <= 0) + return rc; + /* END LENGTH of file identifier */ + + /* FILE identifier */ + if(isSelfOrParent) + { + /* that byte has 0x00 or 0x01 */ + rc = write711(volInfo, node->name9660[0]); + if(rc <= 0) + return rc; + } + else + { + if(filenameTypes & FNTYPE_JOLIET) + { + rc = writeJolietStringField(volInfo, node->nameJoliet, + 2 * strlen(node->nameJoliet)); + if(rc < 0) + return rc; + } + else + { + /* ISO9660 requires ";1" after the filename (not directory name) + * but the windows NT/2K boot loaders cannot find NTLDR inside + * the I386 directory because they are looking for "NTLDR" not + * "NTLDR;1". i guess if microsoft can do it, i can do it. filenames + * on images written by me do not end with ";1" + if(isADir) + {*/ + /* the name */ + rc = wcWrite(volInfo, node->name9660, lenFileId); + if(rc <= 0) + return rc; + /*} + else + { + rc = writeWrapper(image, node->name9660, lenFileId - 2); + if(rc <= 0) + return rc; + + rc = writeWrapper(image, ";1", 2); + if(rc <= 0) + return rc; + }*/ + } + } + /* END FILE identifier */ + + /* padding field */ + if(lenFileId % 2 == 0) + { + byte = 0; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + } + + if(filenameTypes & FNTYPE_ROCKRIDGE) + { + if(isFirstRecord) + { + rc = writeRockSP(volInfo); + if(rc < 0) + return rc; + + rc = writeRockER(volInfo); + if(rc < 0) + return rc; + } + + rc = writeRockPX(volInfo, node->posixFileMode, isADir); + if(rc < 0) + return rc; + + if(!isSelfOrParent) + { + if(wcSeekTell(volInfo) - startPos < (int)strlen(node->nameRock) + 5) + /* have no room for the NM entry in this directory record */ + { + node->offsetForCE = wcSeekTell(volInfo); + /* leave room for CE entry */ + wcSeekForward(volInfo, 28); + } + else + { + rc = writeRockNM(volInfo, node->nameRock, strlen(node->nameRock), false); + if(rc < 0) + return rc; + } + + if(IS_SYMLINK(node->posixFileMode)) + { + rc = writeRockSL(volInfo, SYMLINKTW_PTR(node), true); + if(rc < 0) + return rc; + } + } + } + + /* RECORD length */ + endPos = wcSeekTell(volInfo); + + wcSeekSet(volInfo, startPos); + + recordLen = endPos - startPos; + rc = write711(volInfo, recordLen); + if(rc <= 0) + return rc; + + wcSeekSet(volInfo, endPos); + /* END RECORD length */ + + /* the goto is good! really! + * if, after writing the record we see that the record is in two logical + * sectors (that's not allowed by iso9660) we erase the record just + * written, write zeroes to the end of the first logical sector + * (as required by iso9660) and restart the function, which will write + * the same record again but at the beginning of the next logical sector + * yeah, so don't complain :) */ + + if(endPos / NBYTES_LOGICAL_BLOCK > startPos / NBYTES_LOGICAL_BLOCK) + /* crossed a logical sector boundary while writing the record */ + { + wcSeekSet(volInfo, startPos); + + /* overwrite a piece of the record written in this function + * (the piece that's in the first sector) with zeroes */ + rc = writeByteBlock(volInfo, 0x00, recordLen - endPos % NBYTES_LOGICAL_BLOCK); + if(rc < 0) + return rc; + + goto writeDrStartLabel; + } + + return 1; +} + +/****************************************************************************** +* writeElToritoBootCatalog() +* Write the el torito boot catalog (validation entry and inital/default entry). +* Returns the offset where the boot record sector number should +* be written (7.3.1). +* */ +int writeElToritoBootCatalog(VolInfo* volInfo, + bk_off_t* bootRecordSectorNumberOffset) +{ + unsigned char buffer[NBYTES_LOGICAL_BLOCK]; + int rc; + + memset(buffer, 0, NBYTES_LOGICAL_BLOCK); + + if(wcSeekTell(volInfo) % NBYTES_LOGICAL_BLOCK != 0) + /* file pointer not at sector boundary */ + return BKERROR_SANITY; + + /* SETUP VALIDATION entry (first 20 bytes of boot catalog) */ + /* header, must be 1 */ + buffer[0] = 1; + /* platform id, 0 for x86 (bzero at start took care of this) */ + /* 2 bytes reserved, must be 0 (bzero at start took care of this) */ + /* 24 bytes id string for manufacturer/developer of cdrom */ + strncpy((char*)&(buffer[4]), "Edited with ISO Master", 22); + /* key byte 0x55 */ + buffer[30] = 0x55; + /* key byte 0xAA */ + buffer[31] = 0xAA; + + /* checksum */ + write721ToByteArray(&(buffer[28]), elToritoChecksum(buffer)); + /* END SETUP VALIDATION validation entry (first 20 bytes of boot catalog) */ + + /* SETUP INITIAL entry (next 20 bytes of boot catalog) */ + /* boot indicator. 0x88 = bootable */ + buffer[32] = 0x88; + /* boot media type */ + if(volInfo->bootMediaType == BOOT_MEDIA_NO_EMULATION) + buffer[33] = 0; + else if(volInfo->bootMediaType == BOOT_MEDIA_1_2_FLOPPY) + buffer[33] = 1; + else if(volInfo->bootMediaType == BOOT_MEDIA_1_44_FLOPPY) + buffer[33] = 2; + else if(volInfo->bootMediaType == BOOT_MEDIA_2_88_FLOPPY) + buffer[33] = 3; + else if(volInfo->bootMediaType == BOOT_MEDIA_HARD_DISK) + buffer[33] = 4; + /* load segment leave it at 0 */ + /* system type, leave it at 0 */ + /* 1 byte unused, leave it at 0 */ + /* sector count. i have yet to see a boot record with a sector count + * that's not 4 */ + write721ToByteArray(&(buffer[38]), 4); + /* logical block number of boot record file. this is not known until + * after that file is written */ + *bootRecordSectorNumberOffset = wcSeekTell(volInfo) + 40; + /* the rest is unused, leave it at 0 */ + /* END SETUP INITIAL entry (next 20 bytes of boot catalog) */ + + rc = wcWrite(volInfo, (char*)buffer, NBYTES_LOGICAL_BLOCK); + if(rc <= 0) + return rc; + + return 1; +} + +/****************************************************************************** +* writeElToritoVd() +* Write the el torito volume descriptor. +* Returns the offset where the boot catalog sector number should +* be written (7.3.1). +* */ +int writeElToritoVd(VolInfo* volInfo, bk_off_t* bootCatalogSectorNumberOffset) +{ + char buffer[NBYTES_LOGICAL_BLOCK]; + int rc; + + memset(buffer, 0, NBYTES_LOGICAL_BLOCK); + + if(wcSeekTell(volInfo) % NBYTES_LOGICAL_BLOCK != 0) + /* file pointer not at sector boundary */ + return BKERROR_SANITY; + + /* SETUP BOOT record volume descriptor sector */ + /* boot record indicator, must be 0 (bzero at start took care of this) */ + /* iso9660 identifier, must be "CD001" */ + strncpy((char*)buffer + 1, "CD001", 5); + /* version, must be 1 */ + buffer[6] = 1; + /* boot system identifier, must be 32 bytes "EL TORITO SPECIFICATION" + * padded with 0x00 (bzero at start took care of this) */ + strncpy(&(buffer[7]), "EL TORITO SPECIFICATION", 23); + /* unused 32 bytes, must be 0 (bzero at start took care of this) */ + /* boot catalog location, 4 byte intel format. written later. */ + *bootCatalogSectorNumberOffset = wcSeekTell(volInfo) + 71; + /*write731ToByteArray(&(buffer[71]), bootCatalogSectorNumber);*/ + /* the rest of this sector is unused, must be set to 0 */ + /* END SETUP BOOT record volume descriptor sector */ + + rc = wcWrite(volInfo, buffer, NBYTES_LOGICAL_BLOCK); + if(rc <= 0) + return rc; + + return 1; +} + +/****************************************************************************** +* writeFileContents() +* Write file contents into an extent and also write the file's location and +* size into the directory records back in the tree. +* Also write location and size for symbolic links. +* */ +int writeFileContents(VolInfo* volInfo, DirToWrite* dir, int filenameTypes) +{ + int rc; + + BaseToWrite* child; + int numUnusedBytes; + int srcFile; + bk_off_t endPos; + + child = dir->children; + while(child != NULL) + /* each file in current directory */ + { + if(volInfo->stopOperation) + return BKERROR_OPER_CANCELED_BY_USER; + + if(wcSeekTell(volInfo) % NBYTES_LOGICAL_BLOCK != 0) + return BKERROR_SANITY; + + if( IS_REG_FILE(child->posixFileMode) ) + { + bool needToCopy = true; + + child->extentNumber = wcSeekTell(volInfo) / NBYTES_LOGICAL_BLOCK; + if(volInfo->scanForDuplicateFiles) + { + if(FILETW_PTR(child)->location->extentNumberWrittenTo == 0) + /* file not yet written */ + { + FILETW_PTR(child)->location->extentNumberWrittenTo = child->extentNumber; + } + else + { + child->extentNumber = FILETW_PTR(child)->location->extentNumberWrittenTo; + needToCopy = false; + } + } + + if(volInfo->bootMediaType != BOOT_MEDIA_NONE && + volInfo->bootRecordIsVisible && + FILETW_PTR(child)->origFile == volInfo->bootRecordOnImage) + /* this file is the boot record. write its sector number in + * the boot catalog */ + { + bk_off_t currPos; + + currPos = wcSeekTell(volInfo); + + wcSeekSet(volInfo, volInfo->bootRecordSectorNumberOffset); + rc = write731(volInfo, child->extentNumber); + if(rc <= 0) + return rc; + + wcSeekSet(volInfo, currPos); + } + + if(needToCopy) + { + if(FILETW_PTR(child)->onImage) + /* copy file from original image to new one */ + { + readSeekSet(volInfo, FILETW_PTR(child)->offset, + SEEK_SET); + + rc = writeByteBlockFromFile(volInfo->imageForReading, + volInfo, FILETW_PTR(child)->size); + if(rc < 0) + return rc; + } + else + /* copy file from fs to new image */ + { + /* UPDATE the file's size, in case it's changed since we added it */ + BkStatStruct statStruct; + + rc = bkStat(FILETW_PTR(child)->pathAndName, &statStruct); + if(rc != 0) + return BKERROR_STAT_FAILED; + + if(statStruct.st_size > 0xFFFFFFFF) + /* size won't fit in a 32bit variable on the iso */ + return BKERROR_EDITED_WRITE_TOO_BIG; + + FILETW_PTR(child)->size = statStruct.st_size; + /* UPDATE the file's size, in case it's changed since we added it */ + + srcFile = open(FILETW_PTR(child)->pathAndName, O_RDONLY, 0); + if(srcFile == -1) + return BKERROR_OPEN_READ_FAILED; + + rc = writeByteBlockFromFile(srcFile, + volInfo, FILETW_PTR(child)->size); + + bkClose(srcFile); + + if(rc < 0) + return rc; + } + + /* fill extent with zeroes */ + numUnusedBytes = NBYTES_LOGICAL_BLOCK - + wcSeekTell(volInfo) % NBYTES_LOGICAL_BLOCK; + rc = writeByteBlock(volInfo, 0x00, numUnusedBytes); + if(rc < 0) + return rc; + } + + endPos = wcSeekTell(volInfo); + + bool isIsolinux; + rc = wroteIsolinuxBootRecord(volInfo, FILETW_PTR(child), &isIsolinux); + if(rc < 0) + return rc; + + if(isIsolinux) + /* write the boot info table for the isolinux boot record */ + { + unsigned char bootInfoTable[56]; + unsigned checksum; + + memset(bootInfoTable, 0, 56); + + /* go to the offset in the file where the boot info table is */ + wcSeekSet(volInfo, child->extentNumber * + NBYTES_LOGICAL_BLOCK + 8); + + /* sector number of pvd */ + write731ToByteArray(bootInfoTable, 16); + /* sector number of boot file (this one) */ + write731ToByteArray(bootInfoTable + 4, child->extentNumber); + /* boot file length in bytes */ + write731ToByteArray(bootInfoTable + 8, FILETW_PTR(child)->size); + /* 32 bit checksum (the sum of all the 32-bit words in the boot + * file starting at byte offset 64 */ + rc = bootInfoTableChecksum(volInfo->imageForReading, FILETW_PTR(child), &checksum); + if(rc <= 0) + return rc; + write731ToByteArray(bootInfoTable + 12, checksum); + /* the rest is reserved, leave at zero */ + + rc = wcWrite(volInfo, (char*)bootInfoTable, 56); + if(rc <= 0) + return rc; + } + + /* WRITE file location and size */ + wcSeekSet(volInfo, child->extentLocationOffset); + + rc = write733(volInfo, child->extentNumber); + if(rc <= 0) + return rc; + + rc = write733(volInfo, FILETW_PTR(child)->size); + if(rc <= 0) + return rc; + + if(filenameTypes & FNTYPE_JOLIET) + /* also update location and size on joliet tree */ + { + wcSeekSet(volInfo, child->extentLocationOffset2); + + rc = write733(volInfo, child->extentNumber); + if(rc <= 0) + return rc; + + rc = write733(volInfo, FILETW_PTR(child)->size); + if(rc <= 0) + return rc; + } + + wcSeekSet(volInfo, endPos); + /* END WRITE file location and size */ + } + else if( IS_DIR(child->posixFileMode) ) + { + rc = writeFileContents(volInfo, DIRTW_PTR(child), filenameTypes); + if(rc < 0) + return rc; + } + else if( IS_SYMLINK(child->posixFileMode) ) + { + /* WRITE symlink location and size (0) */ + endPos = wcSeekTell(volInfo); + + wcSeekSet(volInfo, child->extentLocationOffset); + + rc = write733(volInfo, 0); + if(rc <= 0) + return rc; + + rc = write733(volInfo, 0); + if(rc <= 0) + return rc; + + if(filenameTypes & FNTYPE_JOLIET) + /* also update location and size on joliet tree */ + { + wcSeekSet(volInfo, child->extentLocationOffset2); + + rc = write733(volInfo, 0); + if(rc <= 0) + return rc; + + rc = write733(volInfo, 0); + if(rc <= 0) + return rc; + } + + wcSeekSet(volInfo, endPos); + /* END WRITE symlink location and size (0) */ + } + + child = child->next; + + } /* while(nextFile != NULL) */ + + return 1; +} + +/* field size must be even. !!check all calls to make sure */ +int writeJolietStringField(VolInfo* volInfo, const char* name, size_t fieldSize) +{ + char jolietName[512]; /* don't see why would ever want + * to write a longer one */ + int srcCount; + size_t destCount; + int rc; + + srcCount = 0; + destCount = 0; + while(name[srcCount] != '\0' && destCount < fieldSize) + { + /* first byte zero */ + jolietName[destCount] = 0x00; + /* second byte character */ + jolietName[destCount + 1] = name[srcCount]; + + srcCount += 1; + destCount += 2; + } + + while(destCount < fieldSize) + /* pad with ucs2 spaces */ + { + jolietName[destCount] = 0x00; + jolietName[destCount + 1] = ' '; + + destCount += 2; + } + + rc = wcWrite(volInfo, jolietName, destCount); + if(rc <= 0) + return rc; + + return 1; +} + +/* write NM that won't fit in a directory record */ +int writeLongNM(VolInfo* volInfo, BaseToWrite* node) +{ + bk_off_t startPos; + size_t fullNameLen; + unsigned char CErecord[28]; + bool fitsInOneNM; + size_t firstNMlen; + bk_off_t endPos; + int rc; + int lenOfCE; + + startPos = wcSeekTell(volInfo); + + fullNameLen = strlen(node->nameRock); + + /* should have checked for this before getting into this function */ + if(fullNameLen > 255) + return BKERROR_SANITY; + + if(fullNameLen > 250) + { + fitsInOneNM = false; + firstNMlen = 250; + } + else + { + fitsInOneNM = true; + firstNMlen = fullNameLen; + } + + /* NM record(s) */ + if(fitsInOneNM) + { + rc = writeRockNM(volInfo, node->nameRock, firstNMlen, false); + if(rc <= 0) + return rc; + } + else + { + rc = writeRockNM(volInfo, node->nameRock, firstNMlen, true); + if(rc <= 0) + return rc; + rc = writeRockNM(volInfo, node->nameRock + firstNMlen, fullNameLen - firstNMlen, false); + if(rc <= 0) + return rc; + } + + lenOfCE = wcSeekTell(volInfo) - startPos; + + /* write blank to conclude extent */ + rc = writeByteBlock(volInfo, 0x00, NBYTES_LOGICAL_BLOCK - + wcSeekTell(volInfo) % NBYTES_LOGICAL_BLOCK); + if(rc < 0) + return rc; + + endPos = wcSeekTell(volInfo); + + /* CE record back in the directory record */ + wcSeekSet(volInfo, node->offsetForCE); + + CErecord[0] = 'C'; + CErecord[1] = 'E'; + CErecord[2] = 28; /* length */ + CErecord[3] = 1; /* version */ + write733ToByteArray(CErecord + 4, startPos / NBYTES_LOGICAL_BLOCK); /* block location */ + /* i'm always using 1 logical block per name */ + write733ToByteArray(CErecord + 12, 0); /* offset to start */ + write733ToByteArray(CErecord + 20, lenOfCE); /* length */ + + rc = wcWrite(volInfo, (char*)CErecord, CErecord[2]); + if(rc <= 0) + return rc; + /* END CE record back in the directory record */ + + wcSeekSet(volInfo, endPos); + + return 1; +} + +/* write all NMs in the tree that won't fit in directory records */ +int writeLongNMsInDir(VolInfo* volInfo, DirToWrite* dir) +{ + BaseToWrite* child; + int rc; + + child = dir->children; + while(child != NULL) + { + if(child->offsetForCE != 0) + { + rc = writeLongNM(volInfo, child); + if(rc <= 0) + return rc; + } + + if( IS_DIR(child->posixFileMode) ) + { + rc = writeLongNMsInDir(volInfo, DIRTW_PTR(child)); + if(rc <= 0) + return rc; + } + + child = child->next; + } + + return 1; +} + +/* returns path table size (number of bytes not counting the blank) */ +int writePathTable(VolInfo* volInfo, const DirToWrite* tree, bool isTypeL, + int filenameType) +{ + int treeHeight; + int count; + int level; + int* dirsPerLevel; /* a dynamic array of the number of dirs per level */ + int numDirsSoFar; + bk_off_t origPos; + int numBytesWritten; + int rc; + + origPos = wcSeekTell(volInfo); + + if(origPos % NBYTES_LOGICAL_BLOCK != 0) + return BKERROR_SANITY; + + treeHeight = countTreeHeight(tree, 1); + + dirsPerLevel = malloc(sizeof(int) * treeHeight); + if(dirsPerLevel == NULL) + return BKERROR_OUT_OF_MEMORY; + + for(count = 0; count < treeHeight; count++) + { + dirsPerLevel[count] = countDirsOnLevel(tree, count + 1, 1); + } + + for(level = 1; level <= treeHeight; level++) + { + if(level == 1) + /* numDirsSoFar = parent dir num */ + numDirsSoFar = 1; + else if(level == 2) + numDirsSoFar = 1; + else + { + /* ex. when i am on level 4 i want number of dirs on levels 1 + 2 */ + numDirsSoFar = 0; + for(count = 0; count < level - 2; count++) + { + numDirsSoFar += dirsPerLevel[count]; + } + } + + rc = writePathTableRecordsOnLevel(volInfo, tree, isTypeL, filenameType, + level, 1, &numDirsSoFar); + if(rc < 0) + { + free(dirsPerLevel); + return rc; + } + } + + numBytesWritten = wcSeekTell(volInfo) - origPos; + + /* blank to conclude extent */ + rc = writeByteBlock(volInfo, 0x00, NBYTES_LOGICAL_BLOCK - + numBytesWritten % NBYTES_LOGICAL_BLOCK); + if(rc < 0) + { + free(dirsPerLevel); + return rc; + } + + free(dirsPerLevel); + + return numBytesWritten; +} + +int writePathTableRecordsOnLevel(VolInfo* volInfo, const DirToWrite* dir, + bool isTypeL, int filenameType, + int targetLevel, int thisLevel, + int* parentDirNum) +{ + int rc; + BaseToWrite* child; + + unsigned char fileIdLen; + unsigned char byte; + unsigned exentLocation; + unsigned short parentDirId; /* copy of *parentDirNum */ + static const char rootId = 0x00; + + if(thisLevel == targetLevel) + /* write path table record */ + { + /* LENGTH of directory identifier */ + if(targetLevel == 1) + /* root */ + fileIdLen = 1; + else + { + if(filenameType & FNTYPE_JOLIET) + { + fileIdLen = 2 * strlen(BASETW_PTR(dir)->nameJoliet); + } + else + { + fileIdLen = strlen(BASETW_PTR(dir)->name9660); + } + } + + rc = write711(volInfo, fileIdLen); + if(rc <= 0) + return rc; + /* END LENGTH of directory identifier */ + + /* extended attribute record length */ + byte = 0; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* LOCATION of extent */ + if(filenameType & FNTYPE_JOLIET) + exentLocation = dir->extentNumber2; + else + exentLocation = BASETW_PTR(dir)->extentNumber; + + if(isTypeL) + rc = write731(volInfo, exentLocation); + else + rc = write732(volInfo, exentLocation); + if(rc <= 0) + return rc; + /* END LOCATION of extent */ + + /* PARENT directory number */ + parentDirId = *parentDirNum; + + if(isTypeL) + rc = write721(volInfo, parentDirId); + else + rc = write722(volInfo, parentDirId); + + if(rc <= 0) + return rc; + /* END PARENT directory number */ + + /* DIRECTORY identifier */ + if(targetLevel == 1) + /* root */ + { + rc = wcWrite(volInfo, &rootId, 1); + if(rc <= 0) + return rc; + } + else + { + if(filenameType & FNTYPE_JOLIET) + { + rc = writeJolietStringField(volInfo, BASETW_PTR(dir)->nameJoliet, fileIdLen); + if(rc < 0) + return rc; + } + else + { + rc = wcWrite(volInfo, BASETW_PTR(dir)->name9660, fileIdLen); + if(rc <= 0) + return rc; + } + } + /* END DIRECTORY identifier */ + + /* padding field */ + if(fileIdLen % 2 != 0) + { + byte = 0; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + } + + } + else /* if(thisLevel < targetLevel) */ + { + child = dir->children; + while(child != NULL) + { + if( IS_DIR(child->posixFileMode) ) + { + if(thisLevel == targetLevel - 2) + /* am now going throught the list of dirs where the parent is */ + { + if(targetLevel != 2) + /* first and second level have the same parent: 1 */ + { + (*parentDirNum)++; + } + } + + rc = writePathTableRecordsOnLevel(volInfo, DIRTW_PTR(child), isTypeL, + filenameType, targetLevel, + thisLevel + 1, parentDirNum); + if(rc < 0) + return rc; + } + + child = child->next; + } + } + + return 1; +} + +/* This doesn't need support for CE because it's only written in one place, +* the root 'self' directory record. */ +int writeRockER(VolInfo* volInfo) +{ + int rc; + char record[46]; + + /* identification */ + record[0] = 'E'; + record[1] = 'R'; + + /* record length */ + record[2] = 46; + + /* entry version */ + record[3] = 1; + + /* extension identifier length */ + record[4] = 10; + + /* extension descriptor length */ + record[5] = 10; + + /* extension source length */ + record[6] = 18; + + /* extension version */ + record[7] = 1; + + /* extension identifier */ + strncpy(&(record[8]), "IEEE_P1282", 10); + + /* extension descriptor */ + strncpy(&(record[18]), "DRAFT_1_12", 10); + + /* extension source */ + strncpy(&(record[28]), "ADOPTED_1994_07_08", 18); + + rc = wcWrite(volInfo, record, 46); + if(rc <= 0) + return rc; + + return 1; +} + +int writeRockNM(VolInfo* volInfo, char* name, size_t nameLen, bool doesContinue) +{ + int rc; + char recordStart[5]; + + /* identification */ + recordStart[0] = 'N'; + recordStart[1] = 'M'; + + /* record length */ + recordStart[2] = 5 + nameLen; + + /* entry version */ + recordStart[3] = 1; + + /* flags */ + if(doesContinue) + recordStart[4] = 0x01; + else + recordStart[4] = 0; + + rc = wcWrite(volInfo, recordStart, 5); + if(rc <= 0) + return rc; + + rc = wcWrite(volInfo, name, nameLen); + if(rc <= 0) + return rc; + + return 1; +} + +/* the slackware cd has 36 byte PX entries, missing the file serial number +* so i will do the same */ +int writeRockPX(VolInfo* volInfo, unsigned posixFileMode, bool isADir) +{ + int rc; + unsigned char record[36]; + unsigned posixFileLinks; + + /* identification */ + record[0] = 'P'; + record[1] = 'X'; + + /* record length */ + record[2] = 36; + + /* entry version */ + record[3] = 1; + + /* posix file mode */ + write733ToByteArray(&(record[4]), posixFileMode); + + /* POSIX file links */ + /* + * this i think is number of subdirectories + 2 (self and parent) + * and 1 for a file + * it's probably not used on read-only filesystems + * to add it, i would need to pass the number of links in a parent dir + * recursively in writeDir(). brrrrr. + */ + if(isADir) + posixFileLinks = 2; + else + posixFileLinks = 1; + + write733ToByteArray(&(record[12]), posixFileLinks); + /* END POSIX file links */ + + /* posix file user id, posix file group id */ + memset(&(record[20]), 0, 16); + + rc = wcWrite(volInfo, (char*)record, 36); + if(rc <= 0) + return rc; + + return 1; +} + +int writeRockSL(VolInfo* volInfo, SymLinkToWrite* symlink, bool doWrite) +{ + size_t stringCount; + size_t targetLen; + size_t numBytesNeeded; + size_t numBytesToSkip; + unsigned char* record; + size_t recordCount; + int rc; + + targetLen = strlen(symlink->target); + + /* figure out how much room i need */ + numBytesNeeded = 0; + numBytesToSkip = 0; + stringCount = 0; + while(stringCount < targetLen) + { + char* nextSlash; + + if(symlink->target[stringCount] == '/') + /* root (/) */ + { + numBytesNeeded += 2; + numBytesToSkip = 1; + } + else if( symlink->target[stringCount] == '.' && + (stringCount + 1 == targetLen || symlink->target[stringCount + 1] == '/') ) + /* current (.) */ + { + numBytesNeeded += 2; + numBytesToSkip = 2; + } + else if( symlink->target[stringCount] == '.' && + stringCount + 1 < targetLen && symlink->target[stringCount + 1] == '.' ) + /* parent (..) */ + { + numBytesNeeded += 2; + numBytesToSkip = 3; + } + else + /* regular filename */ + { + nextSlash = strchr(symlink->target + stringCount, '/'); + if(nextSlash != NULL) + numBytesToSkip = nextSlash - (symlink->target + stringCount); + else + numBytesToSkip = targetLen - stringCount; + + numBytesNeeded += 2 + numBytesToSkip; + + numBytesToSkip += 1; + } + + stringCount += numBytesToSkip; + } + + if(!doWrite) + return (int)(5 + numBytesNeeded); + + if(numBytesNeeded > NCHARS_SYMLINK_TARGET_MAX - 1) + return BKERROR_SYMLINK_TARGET_TOO_LONG; + + record = malloc(5 + numBytesNeeded); + if(record == NULL) + return BKERROR_OUT_OF_MEMORY; + + record[0] = 'S'; + record[1] = 'L'; + record[2] = 5 + numBytesNeeded; /* length */ + record[3] = 1; /* version */ + record[4] = 0x00; /* flags */ + + /* write SL */ + numBytesToSkip = 0; + stringCount = 0; + recordCount = 5; + while(stringCount < targetLen) + { + char* nextSlash; + + if(symlink->target[stringCount] == '/') + /* root (/) */ + { + numBytesToSkip = 1; + record[recordCount] = 0x08; + record[recordCount + 1] = 0; + recordCount += 2; + } + else if( symlink->target[stringCount] == '.' && + (stringCount + 1 == targetLen || symlink->target[stringCount + 1] == '/') ) + /* current (.) */ + { + numBytesToSkip = 2; + record[recordCount] = 0x02; + record[recordCount + 1] = 0; + recordCount += 2; + } + else if( symlink->target[stringCount] == '.' && + stringCount + 1 < targetLen && symlink->target[stringCount + 1] == '.' ) + /* parent (..) */ + { + numBytesToSkip = 3; + record[recordCount] = 0x04; + record[recordCount + 1] = 0; + recordCount += 2; + } + else + /* regular filename */ + { + nextSlash = strchr(symlink->target + stringCount, '/'); + if(nextSlash != NULL) + numBytesToSkip = nextSlash - (symlink->target + stringCount); + else + numBytesToSkip = targetLen - stringCount; + + record[recordCount] = 0x00; + record[recordCount + 1] = numBytesToSkip; + strncpy((char*)record + recordCount + 2, symlink->target + stringCount, numBytesToSkip); + recordCount += 2 + numBytesToSkip; + + numBytesToSkip += 1; + } + + /* + separator */ + stringCount += numBytesToSkip; + } + + if(recordCount != numBytesNeeded + 5) + { + free(record); + return BKERROR_SANITY; + } + + rc = wcWrite(volInfo, (char*)record, recordCount); + if(rc <= 0) + { + free(record); + return rc; + } + + free(record); + + return (int)(5 + numBytesNeeded); +} + +/* This doesn't need support for CE because it's only written in one place, +* the root 'self' directory record. */ +int writeRockSP(VolInfo* volInfo) +{ + int rc; + unsigned char record[7]; + + /* identification */ + record[0] = 'S'; + record[1] = 'P'; + + /* record length */ + record[2] = 7; + + /* entry version */ + record[3] = 1; + + /* check bytes */ + record[4] = 0xBE; + record[5] = 0xEF; + + /* bytes skipped */ + record[6] = 0; + + rc = wcWrite(volInfo, (char*)record, 7); + if(rc <= 0) + return rc; + + return 1; +} + +int writeVdsetTerminator(VolInfo* volInfo) +{ + int rc; + unsigned char byte; + unsigned char aString[6]; + + /* volume descriptor type */ + byte = 255; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* standard identifier */ + strcpy((char*)aString, "CD001"); + rc = wcWrite(volInfo, (char*)aString, 5); + if(rc <= 0) + return rc; + + /* volume descriptor version */ + byte = 1; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + rc = writeByteBlock(volInfo, 0, 2041); + if(rc < 0) + return rc; + + return 1; +} + +/* +* -has to be called after the files were written so that the +* volume size is recorded properly +* -rootdr location, size are in bytes +* -note strings are not terminated on image +*/ +int writeVolDescriptor(VolInfo* volInfo, bk_off_t rootDrLocation, + unsigned rootDrSize, bk_off_t lPathTableLoc, + bk_off_t mPathTableLoc, unsigned pathTableSize, + time_t creationTime, bool isPrimary) +{ + int rc; + size_t count; + + unsigned char byte; + unsigned char aString[129]; + unsigned anUnsigned; + unsigned short anUnsignedShort; + bk_off_t currPos; + + /* VOLUME descriptor type */ + if(isPrimary) + byte = 1; + else + byte = 2; + /* END VOLUME descriptor type */ + + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* standard identifier */ + strcpy((char*)aString, "CD001"); + rc = wcWrite(volInfo, (char*)aString, 5); + if(rc <= 0) + return rc; + + /* volume descriptor version (always 1) */ + byte = 1; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* primary: unused field + * supplementary: volume flags, 0x00 */ + byte = 0; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* system identifier (32 spaces) */ + if(isPrimary) + { + strcpy((char*)aString, " "); + rc = wcWrite(volInfo, (char*)aString, 32); + if(rc <= 0) + return rc; + } + else + { + rc = writeJolietStringField(volInfo, "", 32); + if(rc < 0) + return rc; + } + + /* VOLUME identifier */ + if(isPrimary) + { + strcpy((char*)aString, volInfo->volId); + + for(count = strlen((char*)aString); count < 32; count++) + aString[count] = ' '; + + rc = wcWrite(volInfo, (char*)aString, 32); + if(rc <= 0) + return rc; + } + else + { + rc = writeJolietStringField(volInfo, volInfo->volId, 32); + if(rc < 0) + return rc; + } + /* END VOLUME identifier */ + + /* unused field */ + rc = writeByteBlock(volInfo, 0, 8); + if(rc < 0) + return rc; + + /* VOLUME space size (number of logical blocks, absolutely everything) */ + /* it's safe to not use wcSeek() here since everything is left as it is */ + currPos = bkSeekTell(volInfo->imageForWriting); + + bkSeekSet(volInfo->imageForWriting, 0, SEEK_END); + anUnsigned = bkSeekTell(volInfo->imageForWriting) / + NBYTES_LOGICAL_BLOCK; + + bkSeekSet(volInfo->imageForWriting, currPos, SEEK_SET); + + rc = write733(volInfo, anUnsigned); + if(rc <= 0) + return rc; + /* END VOLUME space size (number of logical blocks, absolutely everything) */ + + /* primary: unused field + * joliet: escape sequences */ + if(isPrimary) + { + rc = writeByteBlock(volInfo, 0, 32); + if(rc < 0) + return rc; + } + else + { + /* this is the only joliet field that's padded with 0x00 instead of ' ' */ + aString[0] = 0x25; + aString[1] = 0x2F; + aString[2] = 0x45; + + rc = wcWrite(volInfo, (char*)aString, 3); + if(rc <= 0) + return rc; + + rc = writeByteBlock(volInfo, 0, 29); + if(rc < 0) + return rc; + } + + /* volume set size (always 1) */ + anUnsignedShort = 1; + rc = write723(volInfo, anUnsignedShort); + if(rc <= 0) + return rc; + + /* volume sequence number (also always 1) */ + rc = write723(volInfo, anUnsignedShort); + if(rc <= 0) + return rc; + + /* logical block size (always 2048) */ + anUnsignedShort = NBYTES_LOGICAL_BLOCK; + rc = write723(volInfo, anUnsignedShort); + if(rc <= 0) + return rc; + + /* path table size */ + anUnsigned = pathTableSize; + rc = write733(volInfo, anUnsigned); + if(rc <= 0) + return rc; + + /* location of occurence of type l path table */ + anUnsigned = lPathTableLoc / NBYTES_LOGICAL_BLOCK; + rc = write731(volInfo, anUnsigned); + if(rc <= 0) + return rc; + + /* location of optional occurence of type l path table */ + anUnsigned = 0; + rc = write731(volInfo, anUnsigned); + if(rc <= 0) + return rc; + + /* location of occurence of type m path table */ + anUnsigned = mPathTableLoc / NBYTES_LOGICAL_BLOCK; + rc = write732(volInfo, anUnsigned); + if(rc <= 0) + return rc; + + /* location of optional occurence of type m path table */ + anUnsigned = 0; + rc = write732(volInfo, anUnsigned); + if(rc <= 0) + return rc; + + /* ROOT dr */ + /* record length (always 34 here) */ + byte = 34; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* extended attribute record length (always none) */ + byte = 0; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* location of extent */ + anUnsigned = rootDrLocation / NBYTES_LOGICAL_BLOCK; + rc = write733(volInfo, anUnsigned); + if(rc <= 0) + return rc; + + /* data length */ + rc = write733(volInfo, rootDrSize); + if(rc <= 0) + return rc; + + /* recording time */ + epochToShortString(creationTime, (char*)aString); + rc = wcWrite(volInfo, (char*)aString, 7); + if(rc <= 0) + return rc; + + /* file flags (always binary 00000010 here) */ + byte = 0x02; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* file unit size (not in interleaved mode -> 0) */ + byte = 0; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* interleave gap size (not in interleaved mode -> 0) */ + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* volume sequence number */ + anUnsignedShort = 1; + rc = write723(volInfo, anUnsignedShort); + if(rc <= 0) + return rc; + + /* length of file identifier */ + byte = 1; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* file identifier */ + byte = 0; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + /* END ROOT dr */ + + /* volume set identidier */ + if(isPrimary) + { + rc = writeByteBlock(volInfo, ' ', 128); + if(rc < 0) + return rc; + } + else + { + rc = writeJolietStringField(volInfo, "", 128); + if(rc < 0) + return rc; + } + + /* PUBLISHER identifier */ + strcpy((char*)aString, volInfo->publisher); + + if(isPrimary) + { + for(count = strlen((char*)aString); count < 128; count++) + aString[count] = ' '; + + rc = wcWrite(volInfo, (char*)aString, 128); + if(rc <= 0) + return rc; + } + else + { + rc = writeJolietStringField(volInfo, (char*)aString, 128); + if(rc < 0) + return rc; + } + /* PUBLISHER identifier */ + + /* DATA preparer identifier */ + if(isPrimary) + { + rc = wcWrite(volInfo, "ISO Master", 10); + if(rc <= 0) + return rc; + + rc = writeByteBlock(volInfo, ' ', 118); + if(rc < 0) + return rc; + } + else + { + rc = writeJolietStringField(volInfo, "ISO Master", 128); + if(rc < 0) + return rc; + } + /* END DATA preparer identifier */ + + /* application identifier, copyright file identifier, abstract file + * identifier, bibliographic file identifier (128 + 3*37) */ + if(isPrimary) + { + rc = writeByteBlock(volInfo, ' ', 239); + if(rc < 0) + return rc; + } + else + { + /* application id */ + rc = writeJolietStringField(volInfo, "", 128); + if(rc < 0) + return rc; + + /* 18 ucs2 spaces + 0x00 */ + for(count = 0; count < 3; count++) + { + rc = writeJolietStringField(volInfo, "", 36); + if(rc < 0) + return rc; + + byte = 0x00; + rc = wcWrite(volInfo, (char*)&byte, 1); + if(rc <= 0) + return rc; + } + } + + /* VOLUME creation date */ + epochToLongString(creationTime, (char*)aString); + + rc = wcWrite(volInfo, (char*)aString, 17); + if(rc <= 0) + return rc; + /* END VOLUME creation date */ + + /* volume modification date (same as creation) */ + rc = wcWrite(volInfo, (char*)aString, 17); + if(rc <= 0) + return rc; + + /* VOLUME expiration date (none) */ + rc = writeByteBlock(volInfo, '0', 16); + if(rc < 0) + return rc; + + byte = 0; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + /* END VOLUME expiration date (none) */ + + /* volume effective date (same as creation) */ + rc = wcWrite(volInfo, (char*)aString, 17); + if(rc <= 0) + return rc; + + /* file structure version */ + byte = 1; + rc = write711(volInfo, byte); + if(rc <= 0) + return rc; + + /* reserved, applications use, reserved */ + rc = writeByteBlock(volInfo, 0, 1166); + if(rc < 0) + return rc; + + return 1; +} + +/****************************************************************************** +* wroteIsolinuxBootRecord() +* Check whether the file already written to the new iso was a boot record. +* */ +int wroteIsolinuxBootRecord(VolInfo* volInfo, FileToWrite* file, + bool* isIsolinux) +{ + *isIsolinux = false; + + if(volInfo->bootMediaType == BOOT_MEDIA_NO_EMULATION && + volInfo->bootRecordIsVisible && + file->origFile == volInfo->bootRecordOnImage) + /* Likely true, do one more check to make sure. The extra check + * is needed for Windows XP isos with SP2 added by c't slipstreamer */ + { + bk_off_t origPos; + int rc; + char fourBytes[4]; + + origPos = wcSeekTell(volInfo); + + wcSeekSet(volInfo, BASETW_PTR(file)->extentNumber * + NBYTES_LOGICAL_BLOCK + 8); + + rc = bkRead(volInfo->imageForWriting, fourBytes, 4); + if(rc != 4) + return BKERROR_READ_GENERIC; + + if(fourBytes[0] == 16 && fourBytes[1] == 0 && + fourBytes[2] == 0 && fourBytes[3] == 0) + *isIsolinux = true; + + wcSeekSet(volInfo, origPos); + } + + return 1; +} diff --git a/lib/bkisofs/bkWrite.h b/lib/bkisofs/bkWrite.h new file mode 100644 index 0000000..05dca20 --- /dev/null +++ b/lib/bkisofs/bkWrite.h @@ -0,0 +1,41 @@ +#ifndef bkwrite_h +#define bkwrite_h + +int bootInfoTableChecksum(int oldImage, FileToWrite* file, unsigned* checksum); +int countDirsOnLevel(const DirToWrite* dir, int targetLevel, int thisLevel); +int countTreeHeight(const DirToWrite* dir, int heightSoFar); +unsigned short elToritoChecksum(const unsigned char* record); +int writeByteBlock(VolInfo* volInfo, unsigned char byteToWrite, int numBytes); +int writeByteBlockFromFile(int src, VolInfo* volInfo, unsigned numBytes); +int writeDir(VolInfo* volInfo, DirToWrite* dir, int parentLbNum, + int parentNumBytes, int parentPosix, time_t recordingTime, + int filenameTypes, bool isRoot); +int writeDr(VolInfo* volInfo, BaseToWrite* dir, time_t recordingTime, bool isADir, + bool isSelfOrParent, bool isFirstRecord, int filenameTypes); +int writeElToritoBootCatalog(VolInfo* volInfo, + bk_off_t* bootRecordSectorNumberOffset); +int writeElToritoVd(VolInfo* volInfo, bk_off_t* bootCatalogSectorNumberOffset); +int writeFileContents(VolInfo* volInfo, DirToWrite* dir, int filenameTypes); +int writeJolietStringField(VolInfo* volInfo, const char* name, size_t fieldSize); +int writeLongNM(VolInfo* volInfo, BaseToWrite* dir); +int writeLongNMsInDir(VolInfo* volInfo, DirToWrite* dir); +int writePathTable(VolInfo* volInfo, const DirToWrite* tree, bool isTypeL, + int filenameType); +int writePathTableRecordsOnLevel(VolInfo* volInfo, const DirToWrite* dir, + bool isTypeL, int filenameType, + int targetLevel, int thisLevel, + int* parentDirNum); +int writeRockER(VolInfo* volInfo); +int writeRockNM(VolInfo* volInfo, char* name, size_t nameLen, bool doesContinue); +int writeRockPX(VolInfo* volInfo, unsigned posixFileMode, bool isADir); +int writeRockSL(VolInfo* volInfo, SymLinkToWrite* symlink, bool doWrite); +int writeRockSP(VolInfo* volInfo); +int writeVdsetTerminator(VolInfo* volInfo); +int writeVolDescriptor(VolInfo* volInfo, bk_off_t rootDrLocation, + unsigned rootDrSize, bk_off_t lPathTableLoc, + bk_off_t mPathTableLoc, unsigned pathTableSize, + time_t creationTime, bool isPrimary); +int wroteIsolinuxBootRecord(VolInfo* volInfo, FileToWrite* file, + bool* isIsolinux); + +#endif diff --git a/lib/bkisofs/bkWrite7x.c b/lib/bkisofs/bkWrite7x.c new file mode 100644 index 0000000..69766de --- /dev/null +++ b/lib/bkisofs/bkWrite7x.c @@ -0,0 +1,116 @@ +/******************************* LICENCE ************************************** +* Any code in this file may be redistributed or modified under the terms of +* the GNU General Public Licence as published by the Free Software +* Foundation; version 2 of the licence. +****************************** END LICENCE ***********************************/ + +/****************************************************************************** +* Author: +* Andrew Smith, http://littlesvr.ca/misc/contactandrew.php +* +* Contributors: +* +******************************************************************************/ + +/****************************************************************************** +* Functions in this file write to volInfo.imageForWriting and are probably +* unsutable for anything else, except for the ones that write to arrays. +******************************************************************************/ + +#include "bkInternal.h" +#include "bkWrite7x.h" +#include "bkCache.h" +#include "bkError.h" + +int write711(VolInfo* volInfo, unsigned char value) +{ + return wcWrite(volInfo, (char*)&value, 1); +} + +int write721(VolInfo* volInfo, unsigned short value) +{ + unsigned char preparedValue[2]; + + write721ToByteArray(preparedValue, value); + + return wcWrite(volInfo, (char*)preparedValue, 2); +} + +void write721ToByteArray(unsigned char* dest, unsigned short value) +{ + dest[0] = value & 0xFF; + dest[1] = (value >> 8) & 0xFF; +} + +int write722(VolInfo* volInfo, unsigned short value) +{ + char preparedValue[2]; + + preparedValue[0] = (value >> 8) & 0xFF; + preparedValue[1] = value & 0xFF; + + return wcWrite(volInfo, preparedValue, 2); +} + +int write723(VolInfo* volInfo, unsigned short value) +{ + char preparedValue[4]; + + preparedValue[0] = value & 0xFF; + preparedValue[1] = (value >> 8) & 0xFF; + preparedValue[2] = preparedValue[1]; + preparedValue[3] = preparedValue[0]; + + return wcWrite(volInfo, preparedValue, 4); +} + +int write731(VolInfo* volInfo, unsigned value) +{ + unsigned char preparedValue[4]; + + write731ToByteArray(preparedValue, value); + + return wcWrite(volInfo, (char*)preparedValue, 4); +} + +void write731ToByteArray(unsigned char* dest, unsigned value) +{ + dest[0] = value & 0xFF; + dest[1] = (value >> 8) & 0xFF; + dest[2] = (value >> 16) & 0xFF; + dest[3] = value >> 24; +} + +int write732(VolInfo* volInfo, unsigned value) +{ + char preparedValue[4]; + + preparedValue[0] = (value >> 24); + preparedValue[1] = (value >> 16) & 0xFF; + preparedValue[2] = (value >> 8) & 0xFF; + preparedValue[3] = value & 0xFF; + + return wcWrite(volInfo, preparedValue, 4); +} + +int write733(VolInfo* volInfo, unsigned value) +{ + char preparedValue[8]; + + write733ToByteArray((unsigned char*)preparedValue, value); + + return wcWrite(volInfo, preparedValue, 8); +} + +void write733ToByteArray(unsigned char* dest, unsigned value) +{ + dest[0] = value & 0xFF; + dest[1] = (value >> 8) & 0xFF; + dest[2] = (value >> 16) & 0xFF; + dest[3] = value >> 24; + + dest[4] = dest[3]; + dest[5] = dest[2]; + dest[6] = dest[1]; + dest[7] = dest[0]; +} diff --git a/lib/bkisofs/bkWrite7x.h b/lib/bkisofs/bkWrite7x.h new file mode 100644 index 0000000..b0ab236 --- /dev/null +++ b/lib/bkisofs/bkWrite7x.h @@ -0,0 +1,17 @@ +/******************************************************************************* +* bkWrite7x +* functions to write simple variables as described in sections 7.x of iso9660 +* not including filenames (7.4, 7.5, 7.6) +* +* */ + +int write711(VolInfo* volInfo, unsigned char value); +int write721(VolInfo* volInfo, unsigned short value); +void write721ToByteArray(unsigned char* dest, unsigned short value); +int write722(VolInfo* volInfo, unsigned short value); +int write723(VolInfo* volInfo, unsigned short value); +int write731(VolInfo* volInfo, unsigned value); +void write731ToByteArray(unsigned char* dest, unsigned value); +int write732(VolInfo* volInfo, unsigned value); +int write733(VolInfo* volInfo, unsigned value); +void write733ToByteArray(unsigned char* dest, unsigned value); diff --git a/lib/bkisofs/example.c b/lib/bkisofs/example.c new file mode 100644 index 0000000..4767664 --- /dev/null +++ b/lib/bkisofs/example.c @@ -0,0 +1,131 @@ +/****************************************************************************** +* example.c +* Example for using bkisofs +* Author: Andrew Smith +* Compile with: cc example.c bk.a -o example +* */ + +#include +#include + +/* need to include bk.h for access to bkisofs functions and structures */ +#include "bk.h" + +void addProgressUpdaterCbk(VolInfo* volInfo); +void fatalError(const char* message); +void printNameAndContents(BkFileBase* item, int numSpaces); +void readProgressUpdaterCbk(VolInfo* volInfo); +void writeProgressUpdaterCbk(VolInfo* volInfo, double percentComplete); + +int main( int argv, char** argc) +{ + /* A variable of type VolInfo stores information about an image */ + VolInfo volInfo; + /* bk functions return ints that need to be checked to see whether + * the functions were successful or not */ + int rc; + + if(argv != 2) + fatalError("Usage: example myfile.iso"); + + /* initialise volInfo, set it up to scan for duplicate files */ + rc = bk_init_vol_info(&volInfo, true); + if(rc <= 0) + fatalError(bk_get_error_string(rc)); + + /* open the iso file (supplied as argument 1) */ + rc = bk_open_image(&volInfo, argc[1]); + if(rc <= 0) + fatalError(bk_get_error_string(rc)); + + /* read information about the volume (required before reading directory tree) */ + rc = bk_read_vol_info(&volInfo); + if(rc <= 0) + fatalError(bk_get_error_string(rc)); + + /* read the directory tree */ + if(volInfo.filenameTypes & FNTYPE_ROCKRIDGE) + rc = bk_read_dir_tree(&volInfo, FNTYPE_ROCKRIDGE, true, readProgressUpdaterCbk); + else if(volInfo.filenameTypes & FNTYPE_JOLIET) + rc = bk_read_dir_tree(&volInfo, FNTYPE_JOLIET, false, readProgressUpdaterCbk); + else + rc = bk_read_dir_tree(&volInfo, FNTYPE_9660, false, readProgressUpdaterCbk); + if(rc <= 0) + fatalError(bk_get_error_string(rc)); + + /* add the file /etc/fstab to the root of the image */ + rc = bk_add(&volInfo, "/etc/fstab", "/", addProgressUpdaterCbk); + if(rc <= 0) + fatalError(bk_get_error_string(rc)); + + /* print the entire directory tree */ + printNameAndContents(BK_BASE_PTR( &(volInfo.dirTree) ), 0); + + /* save the new ISO as /tmp/example.iso */ + /* note that bkisofs will print some stuff to stdout when writing an ISO */ + rc = bk_write_image("/tmp/example.iso", &volInfo, time(NULL), + FNTYPE_9660 | FNTYPE_ROCKRIDGE | FNTYPE_JOLIET, + writeProgressUpdaterCbk); + + /* we're finished with this ISO, so clean up */ + bk_destroy_vol_info(&volInfo); + + return 0; +} + +/* you can use this to update a progress bar or something */ +void addProgressUpdaterCbk(VolInfo* volInfo) +{ + printf("Add progress updater\n"); +} + +void fatalError(const char* message) +{ + printf("Fatal error: %s\n", message); + exit(1); +} + +void printNameAndContents(BkFileBase* base, int numSpaces) +{ + int count; + + /* print the spaces (indentation, for prettyness) */ + for(count = 0; count < numSpaces; count++) + printf(" "); + + if(IS_DIR(base->posixFileMode)) + { + /* print name of the directory */ + printf("%s (directory)\n", base->name); + + /* print all the directory's children */ + BkFileBase* child = BK_DIR_PTR(base)->children; + while(child != NULL) + { + printNameAndContents(child, numSpaces + 2); + child = child->next; + } + } + else if(IS_REG_FILE(base->posixFileMode)) + { + /* print name and size of the file */ + printf("%s (regular file), size %u\n", base->name, BK_FILE_PTR(base)->size); + } + else if(IS_SYMLINK(base->posixFileMode)) + { + /* print name and target of the symbolic link */ + printf("%s -> %s (symbolic link)\n", base->name, BK_SYMLINK_PTR(base)->target); + } +} + +/* you can use this to update a progress bar or something */ +void readProgressUpdaterCbk(VolInfo* volInfo) +{ + printf("Read progress updater\n"); +} + +/* you can use this to update a progress bar or something */ +void writeProgressUpdaterCbk(VolInfo* volInfo, double percentComplete) +{ + printf("Write progress updater: ~%.2lf%% complete\n", percentComplete); +} -- cgit v1.2.3-65-gdbad