summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/bkisofs-cli.c162
-rw-r--r--lib/bkisofs/Makefile29
-rw-r--r--lib/bkisofs/bk.h287
-rw-r--r--lib/bkisofs/bkAdd.c451
-rw-r--r--lib/bkisofs/bkAdd.h3
-rw-r--r--lib/bkisofs/bkCache.c85
-rw-r--r--lib/bkisofs/bkCache.h4
-rw-r--r--lib/bkisofs/bkDelete.c151
-rw-r--r--lib/bkisofs/bkDelete.h10
-rw-r--r--lib/bkisofs/bkError.c98
-rw-r--r--lib/bkisofs/bkError.h129
-rw-r--r--lib/bkisofs/bkExtract.c490
-rw-r--r--lib/bkisofs/bkExtract.h9
-rw-r--r--lib/bkisofs/bkGet.c257
-rw-r--r--lib/bkisofs/bkGet.h3
-rw-r--r--lib/bkisofs/bkInternal.h105
-rw-r--r--lib/bkisofs/bkIoWrappers.c84
-rw-r--r--lib/bkisofs/bkIoWrappers.h10
-rw-r--r--lib/bkisofs/bkLink.c276
-rw-r--r--lib/bkisofs/bkLink.h10
-rw-r--r--lib/bkisofs/bkMangle.c616
-rw-r--r--lib/bkisofs/bkMangle.h7
-rw-r--r--lib/bkisofs/bkMisc.c36
-rw-r--r--lib/bkisofs/bkMisc.h1
-rw-r--r--lib/bkisofs/bkPath.c375
-rw-r--r--lib/bkisofs/bkPath.h16
-rw-r--r--lib/bkisofs/bkRead.c1265
-rw-r--r--lib/bkisofs/bkRead.h19
-rw-r--r--lib/bkisofs/bkRead7x.c85
-rw-r--r--lib/bkisofs/bkRead7x.h17
-rw-r--r--lib/bkisofs/bkSet.c311
-rw-r--r--lib/bkisofs/bkSet.h1
-rw-r--r--lib/bkisofs/bkSort.c117
-rw-r--r--lib/bkisofs/bkSort.h2
-rw-r--r--lib/bkisofs/bkTime.c124
-rw-r--r--lib/bkisofs/bkTime.h3
-rw-r--r--lib/bkisofs/bkWrite.c2547
-rw-r--r--lib/bkisofs/bkWrite.h41
-rw-r--r--lib/bkisofs/bkWrite7x.c116
-rw-r--r--lib/bkisofs/bkWrite7x.h17
-rw-r--r--lib/bkisofs/example.c131
41 files changed, 8500 insertions, 0 deletions
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 <stdio.h>
+#include <time.h>
+#include <string.h>
+
+/* 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 <in.iso> <out.iso> <action1> <arg1> [<arg2>] [<action2> <arg2> [etc.]]\nValid actions:\n\tadd <path> <source>\n\treplace <path> <source>\n\tdelete <path>\n\textract <path> <destination>\n\tprint\n\twrite <filename>");
+ 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<argv; i++) {
+ if (strcmp(argc[i], "replace") == 0 || strcmp(argc[i], "delete") == 0) {
+ printf("%s %s\n", argc[i], argc[i+1]);
+ rc = bk_delete(&volInfo, argc[i+1]);
+ if (strcmp(argc[i], "delete") == 0)
+ i++;
+ }
+ if (strcmp(argc[i], "add") == 0 || strcmp(argc[i], "replace") == 0) {
+ printf("%s %s -> %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 <stdbool.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <sys/timeb.h>
+#include <stdio.h>
+
+#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 <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#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 <string.h>
+#include <stdio.h>
+#include <sys/timeb.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#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 <strings.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#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 <stdio.h>
+#include <sys/types.h>
+
+#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 <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <stdio.h>
+
+#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 <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#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 <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#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 <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+
+#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 <strings.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+#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 <time.h>
+
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#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 <stdio.h>
+
+#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 <strings.h>
+#include <string.h>
+#include <stdio.h>
+
+#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 <string.h>
+#include <sys/types.h>
+
+#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 <string.h>
+#include <stdio.h>
+#include <time.h>
+
+/* 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 <strings.h>
+#include <string.h>
+#include <time.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#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 <stdio.h>
+#include <time.h>
+
+/* 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);
+}