/******************************* 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 <unistd.h>
#include <string.h>
#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <strings.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"
/******************************************************************************
* 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;
struct stat statStruct;
DirToWrite newTree;
off_t svdOffset;
off_t pRealRootDrOffset;
int pRootDirSize;
off_t sRealRootDrOffset;
int sRootDirSize;
off_t lPathTable9660Loc;
off_t mPathTable9660Loc;
int pathTable9660Size;
off_t lPathTableJolietLoc;
off_t mPathTableJolietLoc;
int pathTableJolietSize;
off_t bootCatalogSectorNumberOffset;
off_t currPos;
volInfo->writeProgressFunction = progressFunction;
volInfo->stopOperation = false;
volInfo->estimatedIsoSize = bk_estimate_iso_size(volInfo, filenameTypes);
progressFunction(volInfo, 0);
rc = stat(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 */
bzero(&newTree, 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_WRONLY | 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);
close(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);
close(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);
close(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);
close(volInfo->imageForWriting);
unlink(newImagePathAndName);
return rc;
}
wcSeekSet(volInfo, currPos);
/* write el torito booting catalog */
rc = writeElToritoBootCatalog(volInfo, &(volInfo->bootRecordSectorNumberOffset));
if(rc <= 0)
{
freeDirToWriteContents(&newTree);
close(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;
lseek(volInfo->imageForReading, volInfo->bootRecordOffset, SEEK_SET);
srcFileOpened = false;
}
else
{
srcFile = open(volInfo->bootRecordPathAndName, O_RDONLY);
if(srcFile == -1)
{
freeDirToWriteContents(&newTree);
close(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)
close(srcFile);
close(volInfo->imageForWriting);
unlink(newImagePathAndName);
return rc;
}
wcSeekSet(volInfo, currPos);
/* file contents */
rc = writeByteBlockFromFile(srcFile, volInfo, volInfo->bootRecordSize);
if(rc < 0)
{
freeDirToWriteContents(&newTree);
if(srcFileOpened)
close(srcFile);
close(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)
close(srcFile);
close(volInfo->imageForWriting);
unlink(newImagePathAndName);
return rc;
}
if(srcFileOpened)
close(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);
close(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);
close(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);
close(volInfo->imageForWriting);
unlink(newImagePathAndName);
return rc;
}
pathTable9660Size = rc;
mPathTable9660Loc = wcSeekTell(volInfo);
rc = writePathTable(volInfo, &newTree, false, FNTYPE_9660);
if(rc <= 0)
{
freeDirToWriteContents(&newTree);
close(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);
close(volInfo->imageForWriting);
unlink(newImagePathAndName);
return rc;
}
pathTableJolietSize = rc;
mPathTableJolietLoc = wcSeekTell(volInfo);
rc = writePathTable(volInfo, &newTree, false, FNTYPE_JOLIET);
if(rc <= 0)
{
freeDirToWriteContents(&newTree);
close(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);
close(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);
close(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);
close(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);
close(volInfo->imageForWriting);
unlink(newImagePathAndName);
return rc;
}
}
printf("freeing memory\n");fflush(NULL);
freeDirToWriteContents(&newTree);
close(volInfo->imageForWriting);
return 1;
}
/******************************************************************************
* bootInfoTableChecksum()
* Calculate the checksum to be written into the boot info table.
* */
int bootInfoTableChecksum(int oldImage, FileToWrite* file, unsigned* checksum)
{
ssize_t rc;
int rc2;
int srcFile;
unsigned char* contents;
unsigned count;
if(file->size % 4 != 0)
return BKERROR_WRITE_BOOT_FILE_4;
contents = malloc(file->size);
if(contents == NULL)
return BKERROR_OUT_OF_MEMORY;
if(file->onImage)
/* read file from original image */
{
lseek(oldImage, file->offset, SEEK_SET);
rc = read(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);
if(srcFile == -1)
{
free(contents);
return BKERROR_OPEN_READ_FAILED;
}
rc = read(srcFile, contents, file->size);
if(rc == -1 || rc != (int)(file->size))
{
close(srcFile);
free(contents);
return BKERROR_READ_GENERIC;
}
rc2 = close(srcFile);
if(rc2 < 0)
{
free(contents);
return BKERROR_EXOTIC;
}
}
*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 = read(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 = read(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;
off_t startPos;
int numUnusedBytes;
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;
off_t startPos;
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 < 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,
off_t* bootRecordSectorNumberOffset)
{
unsigned char buffer[NBYTES_LOGICAL_BLOCK];
int rc;
bzero(buffer, 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, off_t* bootCatalogSectorNumberOffset)
{
char buffer[NBYTES_LOGICAL_BLOCK];
int rc;
bzero(buffer, 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;
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 */
{
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 */
{
lseek(volInfo->imageForReading, 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 */
struct stat statStruct;
rc = stat(FILETW_PTR(child)->pathAndName, &statStruct);
if(rc != 0)
return BKERROR_STAT_FAILED;
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);
if(srcFile == -1)
return BKERROR_OPEN_READ_FAILED;
rc = writeByteBlockFromFile(srcFile,
volInfo, FILETW_PTR(child)->size);
if(rc < 0)
{
close(srcFile);
return rc;
}
rc = close(srcFile);
if(rc < 0)
return BKERROR_EXOTIC;
}
/* 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);
if(volInfo->bootMediaType != BOOT_MEDIA_NONE &&
volInfo->bootRecordIsVisible &&
FILETW_PTR(child)->origFile == volInfo->bootRecordOnImage)
/* this file is the boot record. assume it's isolinux and write the
* boot info table */
{
unsigned char bootInfoTable[56];
unsigned checksum;
bzero(bootInfoTable, 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, int fieldSize)
{
char jolietName[512]; /* don't see why would ever want
* to write a longer one */
int srcCount;
int 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)
{
off_t startPos;
int fullNameLen;
unsigned char CErecord[28];
bool fitsInOneNM;
int firstNMlen;
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;
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, int 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 */
bzero(&(record[20]), 16);
rc = wcWrite(volInfo, (char*)record, 36);
if(rc <= 0)
return rc;
return 1;
}
int writeRockSL(VolInfo* volInfo, SymLinkToWrite* symlink, bool doWrite)
{
int stringCount;
int targetLen;
int numBytesNeeded;
int numBytesToSkip;
unsigned char* record;
int recordCount;
int rc;
targetLen = strlen(symlink->target);
/* figure out how much room i need */
numBytesNeeded = 0;
numBytesToSkip = 0;
stringCount = 0;
while(stringCount < targetLen)
{
int numBytesToSkip;
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 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)
{
int numBytesToSkip;
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 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, off_t rootDrLocation,
unsigned rootDrSize, off_t lPathTableLoc,
off_t mPathTableLoc, unsigned pathTableSize,
time_t creationTime, bool isPrimary)
{
int rc;
int count;
unsigned char byte;
unsigned char aString[129];
unsigned anUnsigned;
unsigned short anUnsignedShort;
size_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 = lseek(volInfo->imageForWriting, 0, SEEK_CUR);
lseek(volInfo->imageForWriting, 0, SEEK_END);
anUnsigned = lseek(volInfo->imageForWriting, 0, SEEK_CUR) /
NBYTES_LOGICAL_BLOCK;
lseek(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;
}
syntax highlighted by Code2HTML, v. 0.9.1