Added hdutil, which is a cool way of directly accessing a HFS filesystem compressed into a DMG encrypted with FileVault. Surprisingly not that slow.
This commit is contained in:
Родитель
2fb2b51d31
Коммит
75ad992658
10
Makefile
10
Makefile
|
@ -1,8 +1,4 @@
|
|||
all: hfs dmg
|
||||
|
||||
dmg: dmg/dmg
|
||||
|
||||
hfs: hfs/hfsplus
|
||||
all: hfs/hfsplus dmg/dmg hdutil/hdutil
|
||||
|
||||
dmg/dmg:
|
||||
cd dmg; make
|
||||
|
@ -10,9 +6,13 @@ dmg/dmg:
|
|||
hfs/hfsplus:
|
||||
cd hfs; make
|
||||
|
||||
hdutil/hdutil:
|
||||
cd hdutil; make
|
||||
|
||||
clean:
|
||||
cd dmg; make clean
|
||||
cd hfs; make clean
|
||||
cd hdutil; make clean
|
||||
|
||||
dist-clean: clean
|
||||
-cd dmg/zlib-1.2.3; make clean
|
||||
|
|
|
@ -33,13 +33,16 @@ purposes.
|
|||
USING
|
||||
-----
|
||||
|
||||
The targets of the current repository are two command-line utilities that
|
||||
The targets of the current repository are three command-line utilities that
|
||||
demonstrate the usage of the library functions (except cmd_grow, which really
|
||||
ought to be moved to catalog.c). To make compilation simpler, a complete,
|
||||
unmodified copy of the zlib distribution is included. The dmg portion of the
|
||||
code has dependencies on the HFS+ portion of the code
|
||||
code has dependencies on the HFS+ portion of the code. The "hdutil" section
|
||||
contains a version of the HFS+ utility that supports directly reading from
|
||||
dmgs. It is separate from the HFS+ utility in order that the hfs directory
|
||||
does not have dependencies on the dmg directory.
|
||||
|
||||
The makefile in the root folder will make both utilities.
|
||||
The makefile in the root folder will make all utilities.
|
||||
|
||||
### HFS+
|
||||
|
||||
|
@ -54,3 +57,7 @@ The makefile in the root folder will make both utilities.
|
|||
cd ..
|
||||
make
|
||||
|
||||
### hdutil
|
||||
cd hdiutil
|
||||
make
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
DMGOBJS=dmg.o base64.o resources.o checksum.o udif.o partition.o io.o abstractfile.o filevault.o zlib-1.2.3/libz.a
|
||||
DMGOBJS=dmg.o base64.o resources.o checksum.o udif.o partition.o io.o abstractfile.o filevault.o dmgfile.o zlib-1.2.3/libz.a
|
||||
HFSOBJS=../hfs/volume.o ../hfs/btree.o ../hfs/extents.o ../hfs/rawfile.o ../hfs/catalog.o ../hfs/flatfile.o ../hfs/utility.o
|
||||
CFLAGS=-D_FILE_OFFSET_BITS=64 -DHAVE_CRYPT
|
||||
LIBRARIES=-lcrypto
|
||||
|
@ -8,7 +8,7 @@ all: dmg
|
|||
dmg: $(DMGOBJS) $(HFSOBJS)
|
||||
$(CC) $(CFLAGS) $(DMGOBJS) $(HFSOBJS) $(LIBRARIES) -o dmg
|
||||
|
||||
%.o: %.c dmg.h filevault.h
|
||||
%.o: %.c dmg.h filevault.h dmgfile.h
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
zlib-1.2.3/Makefile:
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* dmgfile.c
|
||||
* libdmg-hfsplus
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "zlib.h"
|
||||
|
||||
#include "dmg.h"
|
||||
#include "dmgfile.h"
|
||||
|
||||
static void cacheRun(DMG* dmg, BLKXTable* blkx, int run) {
|
||||
size_t bufferSize;
|
||||
z_stream strm;
|
||||
void* inBuffer;
|
||||
int ret;
|
||||
size_t have;
|
||||
|
||||
if(dmg->runData) {
|
||||
free(dmg->runData);
|
||||
}
|
||||
|
||||
bufferSize = SECTOR_SIZE * blkx->decompressBufferRequested;
|
||||
|
||||
dmg->runData = (void*) malloc(bufferSize);
|
||||
inBuffer = (void*) malloc(bufferSize);
|
||||
memset(dmg->runData, 0, bufferSize);
|
||||
|
||||
ASSERT(dmg->dmg->seek(dmg->dmg, blkx->dataStart + blkx->runs[run].compOffset) == 0, "fseeko");
|
||||
|
||||
switch(blkx->runs[run].type) {
|
||||
case BLOCK_ZLIB:
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = 0;
|
||||
strm.next_in = Z_NULL;
|
||||
|
||||
ASSERT(inflateInit(&strm) == Z_OK, "inflateInit");
|
||||
|
||||
ASSERT((strm.avail_in = dmg->dmg->read(dmg->dmg, inBuffer, blkx->runs[run].compLength)) == blkx->runs[run].compLength, "fread");
|
||||
strm.next_in = inBuffer;
|
||||
|
||||
do {
|
||||
strm.avail_out = bufferSize;
|
||||
strm.next_out = dmg->runData;
|
||||
ASSERT((ret = inflate(&strm, Z_NO_FLUSH)) != Z_STREAM_ERROR, "inflate/Z_STREAM_ERROR");
|
||||
if(ret != Z_OK && ret != Z_BUF_ERROR && ret != Z_STREAM_END) {
|
||||
ASSERT(FALSE, "inflate");
|
||||
}
|
||||
have = bufferSize - strm.avail_out;
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
ASSERT(inflateEnd(&strm) == Z_OK, "inflateEnd");
|
||||
break;
|
||||
case BLOCK_RAW:
|
||||
ASSERT((have = dmg->dmg->read(dmg->dmg, dmg->runData, blkx->runs[run].compLength)) == blkx->runs[run].compLength, "fread");
|
||||
break;
|
||||
case BLOCK_IGNORE:
|
||||
break;
|
||||
case BLOCK_COMMENT:
|
||||
break;
|
||||
case BLOCK_TERMINATOR:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
dmg->runStart = (blkx->runs[run].sectorStart + blkx->firstSectorNumber) * SECTOR_SIZE;
|
||||
dmg->runEnd = dmg->runStart + (blkx->runs[run].sectorCount * SECTOR_SIZE);
|
||||
}
|
||||
|
||||
static void cacheOffset(DMG* dmg, off_t location) {
|
||||
int i;
|
||||
int j;
|
||||
uint64_t sector;
|
||||
|
||||
sector = (uint64_t)(location / SECTOR_SIZE);
|
||||
|
||||
for(i = 0; i < dmg->numBLKX; i++) {
|
||||
if(sector >= dmg->blkx[i]->firstSectorNumber && sector < (dmg->blkx[i]->firstSectorNumber + dmg->blkx[i]->sectorCount)) {
|
||||
for(j = 0; j < dmg->blkx[i]->blocksRunCount; j++) {
|
||||
if(sector >= (dmg->blkx[i]->firstSectorNumber + dmg->blkx[i]->runs[j].sectorStart) &&
|
||||
sector < (dmg->blkx[i]->firstSectorNumber + dmg->blkx[i]->runs[j].sectorStart + dmg->blkx[i]->runs[j].sectorCount)) {
|
||||
cacheRun(dmg, dmg->blkx[i], j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int dmgFileRead(io_func* io, off_t location, size_t size, void *buffer) {
|
||||
DMG* dmg;
|
||||
size_t toRead;
|
||||
|
||||
dmg = (DMG*) io->data;
|
||||
|
||||
location += dmg->offset;
|
||||
|
||||
if(size == 0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if(location < dmg->runStart || location >= dmg->runEnd) {
|
||||
cacheOffset(dmg, location);
|
||||
}
|
||||
|
||||
if((location + size) > dmg->runEnd) {
|
||||
toRead = dmg->runEnd - location;
|
||||
} else {
|
||||
toRead = size;
|
||||
}
|
||||
|
||||
memcpy(buffer, (void*)((uint8_t*)dmg->runData + (uint32_t)(location - dmg->runStart)), toRead);
|
||||
size -= toRead;
|
||||
location += toRead;
|
||||
buffer = (void*)((uint8_t*)buffer + toRead);
|
||||
|
||||
if(size > 0) {
|
||||
return dmgFileRead(io, location, size, buffer);
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static int dmgFileWrite(io_func* io, off_t location, size_t size, void *buffer) {
|
||||
fprintf(stderr, "Error: writing to DMGs is not supported (impossible to achieve with compressed images and retain asr multicast ordering).\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static void closeDmgFile(io_func* io) {
|
||||
DMG* dmg;
|
||||
|
||||
dmg = (DMG*) io->data;
|
||||
|
||||
if(dmg->runData) {
|
||||
free(dmg->runData);
|
||||
}
|
||||
|
||||
free(dmg->blkx);
|
||||
releaseResources(dmg->resources);
|
||||
dmg->dmg->close(dmg->dmg);
|
||||
free(dmg);
|
||||
free(io);
|
||||
}
|
||||
|
||||
io_func* openDmgFile(AbstractFile* abstractIn) {
|
||||
off_t fileLength;
|
||||
UDIFResourceFile resourceFile;
|
||||
DMG* dmg;
|
||||
ResourceData* blkx;
|
||||
ResourceData* curData;
|
||||
int i;
|
||||
|
||||
io_func* toReturn;
|
||||
|
||||
if(abstractIn == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fileLength = abstractIn->getLength(abstractIn);
|
||||
abstractIn->seek(abstractIn, fileLength - sizeof(UDIFResourceFile));
|
||||
readUDIFResourceFile(abstractIn, &resourceFile);
|
||||
|
||||
dmg = (DMG*) malloc(sizeof(DMG));
|
||||
dmg->dmg = abstractIn;
|
||||
dmg->resources = readResources(abstractIn, &resourceFile);
|
||||
dmg->numBLKX = 0;
|
||||
|
||||
blkx = (getResourceByKey(dmg->resources, "blkx"))->data;
|
||||
|
||||
curData = blkx;
|
||||
while(curData != NULL) {
|
||||
dmg->numBLKX++;
|
||||
curData = curData->next;
|
||||
}
|
||||
|
||||
dmg->blkx = (BLKXTable**) malloc(sizeof(BLKXTable*) * dmg->numBLKX);
|
||||
|
||||
i = 0;
|
||||
while(blkx != NULL) {
|
||||
dmg->blkx[i] = (BLKXTable*)(blkx->data);
|
||||
i++;
|
||||
blkx = blkx->next;
|
||||
}
|
||||
|
||||
dmg->offset = 0;
|
||||
|
||||
dmg->runData = NULL;
|
||||
cacheOffset(dmg, 0);
|
||||
|
||||
toReturn = (io_func*) malloc(sizeof(io_func));
|
||||
|
||||
toReturn->data = dmg;
|
||||
toReturn->read = &dmgFileRead;
|
||||
toReturn->write = &dmgFileWrite;
|
||||
toReturn->close = &closeDmgFile;
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
io_func* openDmgFilePartition(AbstractFile* abstractIn, int partition) {
|
||||
io_func* toReturn;
|
||||
Partition* partitions;
|
||||
int numPartitions;
|
||||
int i;
|
||||
|
||||
toReturn = openDmgFile(abstractIn);
|
||||
|
||||
if(toReturn == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
partitions = (Partition*) malloc(sizeof(Partition));
|
||||
toReturn->read(toReturn, SECTOR_SIZE, SECTOR_SIZE, partitions);
|
||||
flipPartitionMultiple(partitions, FALSE, FALSE);
|
||||
numPartitions = partitions->pmMapBlkCnt;
|
||||
partitions = (Partition*) realloc(partitions, numPartitions * SECTOR_SIZE);
|
||||
toReturn->read(toReturn, SECTOR_SIZE, numPartitions * SECTOR_SIZE, partitions);
|
||||
flipPartition(partitions, FALSE);
|
||||
|
||||
if(partition >= 0) {
|
||||
((DMG*)toReturn->data)->offset = partitions[partition].pmPyPartStart * SECTOR_SIZE;
|
||||
} else {
|
||||
for(i = 0; i < numPartitions; i++) {
|
||||
if(strcmp((char*)partitions[i].pmPartName, "Mac_OS_X") == 0) {
|
||||
((DMG*)toReturn->data)->offset = partitions[i].pmPyPartStart * SECTOR_SIZE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* dmgfile.h
|
||||
* libdmg-hfsplus
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../hfs/common.h"
|
||||
#include "dmg.h"
|
||||
|
||||
io_func* openDmgFile(AbstractFile* dmg);
|
||||
io_func* openDmgFilePartition(AbstractFile* dmg, int partition);
|
||||
|
||||
typedef struct DMG {
|
||||
AbstractFile* dmg;
|
||||
ResourceKey* resources;
|
||||
uint32_t numBLKX;
|
||||
BLKXTable** blkx;
|
||||
void* runData;
|
||||
uint64_t runStart;
|
||||
uint64_t runEnd;
|
||||
uint64_t offset;
|
||||
} DMG;
|
|
@ -202,6 +202,9 @@ AbstractFile* createAbstractFileFromFileVault(AbstractFile* file, const char* ke
|
|||
uint8_t hmacKey[20];
|
||||
|
||||
int i;
|
||||
|
||||
if(file == NULL)
|
||||
return NULL;
|
||||
|
||||
file->seek(file, 0);
|
||||
file->read(file, &signature, sizeof(uint64_t));
|
||||
|
|
|
@ -101,8 +101,6 @@ const char* plistFooter = "</dict>\n</plist>\n";
|
|||
static void flipSizeResource(unsigned char* data, char out) {
|
||||
SizeResource* size;
|
||||
|
||||
printf("flipping size\n");
|
||||
|
||||
size = (SizeResource*) data;
|
||||
|
||||
FLIPENDIAN(size->version);
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
UTILOBJS=hdutil.o
|
||||
DMGOBJS=../dmg/base64.o ../dmg/resources.o ../dmg/checksum.o ../dmg/udif.o ../dmg/partition.o ../dmg/io.o ../dmg/abstractfile.o ../dmg/filevault.o ../dmg/dmgfile.o ../dmg/zlib-1.2.3/libz.a
|
||||
HFSOBJS=../hfs/volume.o ../hfs/btree.o ../hfs/extents.o ../hfs/rawfile.o ../hfs/catalog.o ../hfs/flatfile.o ../hfs/utility.o
|
||||
CFLAGS=-D_FILE_OFFSET_BITS=64 -DHAVE_CRYPT
|
||||
LIBRARIES=-lcrypto
|
||||
|
||||
all: util
|
||||
|
||||
util: $(DMGOBJS) $(HFSOBJS) $(UTILOBJS)
|
||||
$(CC) $(CFLAGS) $(DMGOBJS) $(HFSOBJS) $(UTILOBJS) $(LIBRARIES) -o hdutil
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
../dmg/zlib-1.2.3/Makefile:
|
||||
cd ../dmg/zlib-1.2.3; ./configure
|
||||
|
||||
../dmg/zlib-1.2.3/libz.a: ../dmg/zlib-1.2.3/Makefile
|
||||
cd ../dmg/zlib-1.2.3; make
|
||||
|
||||
clean:
|
||||
-rm *.o
|
||||
-rm hdutil
|
|
@ -0,0 +1,733 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "../hfs/hfsplus.h"
|
||||
#include "../dmg/dmgfile.h"
|
||||
#include "../dmg/filevault.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
|
||||
char endianness;
|
||||
|
||||
void displayFolder(HFSCatalogNodeID folderID, Volume* volume) {
|
||||
CatalogRecordList* list;
|
||||
CatalogRecordList* theList;
|
||||
HFSPlusCatalogFolder* folder;
|
||||
HFSPlusCatalogFile* file;
|
||||
time_t fileTime;
|
||||
struct tm *date;
|
||||
|
||||
theList = list = getFolderContents(folderID, volume);
|
||||
|
||||
while(list != NULL) {
|
||||
if(list->record->recordType == kHFSPlusFolderRecord) {
|
||||
folder = (HFSPlusCatalogFolder*)list->record;
|
||||
printf("%06o ", folder->permissions.fileMode);
|
||||
printf("%3d ", folder->permissions.ownerID);
|
||||
printf("%3d ", folder->permissions.groupID);
|
||||
printf("%12d ", folder->valence);
|
||||
fileTime = APPLE_TO_UNIX_TIME(folder->contentModDate);
|
||||
} else if(list->record->recordType == kHFSPlusFileRecord) {
|
||||
file = (HFSPlusCatalogFile*)list->record;
|
||||
printf("%06o ", file->permissions.fileMode);
|
||||
printf("%3d ", file->permissions.ownerID);
|
||||
printf("%3d ", file->permissions.groupID);
|
||||
printf("%12lld ", file->dataFork.logicalSize);
|
||||
fileTime = APPLE_TO_UNIX_TIME(file->contentModDate);
|
||||
}
|
||||
|
||||
date = localtime(&fileTime);
|
||||
if(date != NULL) {
|
||||
printf("%2d/%2d/%4d %02d:%02d ", date->tm_mon, date->tm_mday, date->tm_year + 1900, date->tm_hour, date->tm_min);
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printUnicode(&list->name);
|
||||
printf("\n");
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
releaseCatalogRecordList(theList);
|
||||
}
|
||||
|
||||
void displayFileLSLine(HFSPlusCatalogFile* file, const char* name) {
|
||||
time_t fileTime;
|
||||
struct tm *date;
|
||||
|
||||
printf("%06o ", file->permissions.fileMode);
|
||||
printf("%3d ", file->permissions.ownerID);
|
||||
printf("%3d ", file->permissions.groupID);
|
||||
printf("%12d ", file->dataFork.logicalSize);
|
||||
fileTime = APPLE_TO_UNIX_TIME(file->contentModDate);
|
||||
date = localtime(&fileTime);
|
||||
if(date != NULL) {
|
||||
printf("%2d/%2d/%4d %2d:%02d ", date->tm_mon, date->tm_mday, date->tm_year + 1900, date->tm_hour, date->tm_min);
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
printf("%s\n", name);
|
||||
}
|
||||
|
||||
void cmd_ls(Volume* volume, int argc, const char *argv[]) {
|
||||
HFSPlusCatalogRecord* record;
|
||||
char* name;
|
||||
|
||||
if(argc > 1)
|
||||
record = getRecordFromPath(argv[1], volume, &name, NULL);
|
||||
else
|
||||
record = getRecordFromPath("/", volume, &name, NULL);
|
||||
|
||||
if(record != NULL) {
|
||||
if(record->recordType == kHFSPlusFolderRecord)
|
||||
displayFolder(((HFSPlusCatalogFolder*)record)->folderID, volume);
|
||||
else
|
||||
displayFileLSLine((HFSPlusCatalogFile*)record, name);
|
||||
} else {
|
||||
printf("No such file or directory\n");
|
||||
}
|
||||
|
||||
free(record);
|
||||
}
|
||||
|
||||
|
||||
#define BUFSIZE 1024*1024
|
||||
|
||||
void writeToFile(HFSPlusCatalogFile* file, FILE* output, Volume* volume) {
|
||||
unsigned char buffer[BUFSIZE];
|
||||
io_func* io;
|
||||
off_t curPosition;
|
||||
size_t bytesLeft;
|
||||
|
||||
io = openRawFile(file->fileID, &file->dataFork, (HFSPlusCatalogRecord*)file, volume);
|
||||
if(io == NULL) {
|
||||
panic("error opening file");
|
||||
return;
|
||||
}
|
||||
|
||||
curPosition = 0;
|
||||
bytesLeft = file->dataFork.logicalSize;
|
||||
|
||||
while(bytesLeft > 0) {
|
||||
if(bytesLeft > BUFSIZE) {
|
||||
if(!READ(io, curPosition, BUFSIZE, buffer)) {
|
||||
panic("error reading");
|
||||
}
|
||||
if(fwrite(buffer, BUFSIZE, 1, output) != 1) {
|
||||
panic("error writing");
|
||||
}
|
||||
curPosition += BUFSIZE;
|
||||
bytesLeft -= BUFSIZE;
|
||||
} else {
|
||||
if(!READ(io, curPosition, bytesLeft, buffer)) {
|
||||
panic("error reading");
|
||||
}
|
||||
if(fwrite(buffer, bytesLeft, 1, output) != 1) {
|
||||
panic("error writing");
|
||||
}
|
||||
curPosition += bytesLeft;
|
||||
bytesLeft -= bytesLeft;
|
||||
}
|
||||
}
|
||||
CLOSE(io);
|
||||
}
|
||||
|
||||
void writeToHFSFile(HFSPlusCatalogFile* file, FILE* input, Volume* volume) {
|
||||
unsigned char buffer[BUFSIZE];
|
||||
io_func* io;
|
||||
off_t curPosition;
|
||||
off_t bytesLeft;
|
||||
|
||||
fseeko(input, 0, SEEK_END);
|
||||
bytesLeft = ftello(input);
|
||||
fseeko(input, 0, SEEK_SET);
|
||||
|
||||
io = openRawFile(file->fileID, &file->dataFork, (HFSPlusCatalogRecord*)file, volume);
|
||||
if(io == NULL) {
|
||||
panic("error opening file");
|
||||
return;
|
||||
}
|
||||
|
||||
curPosition = 0;
|
||||
|
||||
allocate((RawFile*)io->data, bytesLeft);
|
||||
|
||||
while(bytesLeft > 0) {
|
||||
if(bytesLeft > BUFSIZE) {
|
||||
if(fread(buffer, BUFSIZE, 1, input) != 1) {
|
||||
panic("error reading");
|
||||
}
|
||||
if(!WRITE(io, curPosition, BUFSIZE, buffer)) {
|
||||
panic("error writing");
|
||||
}
|
||||
curPosition += BUFSIZE;
|
||||
bytesLeft -= BUFSIZE;
|
||||
} else {
|
||||
if(fread(buffer, (size_t)bytesLeft, 1, input) != 1) {
|
||||
panic("error reading");
|
||||
}
|
||||
if(!WRITE(io, curPosition, (size_t)bytesLeft, buffer)) {
|
||||
panic("error reading");
|
||||
}
|
||||
curPosition += bytesLeft;
|
||||
bytesLeft -= bytesLeft;
|
||||
}
|
||||
}
|
||||
|
||||
CLOSE(io);
|
||||
}
|
||||
|
||||
void cmd_cat(Volume* volume, int argc, const char *argv[]) {
|
||||
HFSPlusCatalogRecord* record;
|
||||
|
||||
record = getRecordFromPath(argv[1], volume, NULL, NULL);
|
||||
|
||||
if(record != NULL) {
|
||||
if(record->recordType == kHFSPlusFileRecord)
|
||||
writeToFile((HFSPlusCatalogFile*)record, stdout, volume);
|
||||
else
|
||||
printf("Not a file\n");
|
||||
} else {
|
||||
printf("No such file or directory\n");
|
||||
}
|
||||
|
||||
free(record);
|
||||
}
|
||||
|
||||
void cmd_extract(Volume* volume, int argc, const char *argv[]) {
|
||||
HFSPlusCatalogRecord* record;
|
||||
FILE *outFile;
|
||||
|
||||
if(argc < 3) {
|
||||
printf("Not enough arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
outFile = fopen(argv[2], "wb");
|
||||
|
||||
if(outFile == NULL) {
|
||||
printf("cannot create file");
|
||||
}
|
||||
|
||||
record = getRecordFromPath(argv[1], volume, NULL, NULL);
|
||||
|
||||
if(record != NULL) {
|
||||
if(record->recordType == kHFSPlusFileRecord)
|
||||
writeToFile((HFSPlusCatalogFile*)record, outFile, volume);
|
||||
else
|
||||
printf("Not a file\n");
|
||||
} else {
|
||||
printf("No such file or directory\n");
|
||||
}
|
||||
|
||||
fclose(outFile);
|
||||
free(record);
|
||||
}
|
||||
|
||||
void cmd_mv(Volume* volume, int argc, const char *argv[]) {
|
||||
if(argc > 2) {
|
||||
move(argv[1], argv[2], volume);
|
||||
} else {
|
||||
printf("Not enough arguments");
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_mkdir(Volume* volume, int argc, const char *argv[]) {
|
||||
if(argc > 1) {
|
||||
newFolder(argv[1], volume);
|
||||
} else {
|
||||
printf("Not enough arguments");
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_add(Volume* volume, int argc, const char *argv[]) {
|
||||
FILE *inFile;
|
||||
HFSPlusCatalogRecord* record;
|
||||
|
||||
if(argc < 3) {
|
||||
printf("Not enough arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
inFile = fopen(argv[1], "rb");
|
||||
|
||||
if(inFile == NULL) {
|
||||
printf("file to add not found");
|
||||
}
|
||||
|
||||
record = getRecordFromPath(argv[2], volume, NULL, NULL);
|
||||
|
||||
if(record != NULL) {
|
||||
if(record->recordType == kHFSPlusFileRecord)
|
||||
writeToHFSFile((HFSPlusCatalogFile*)record, inFile, volume);
|
||||
else
|
||||
printf("Not a file\n");
|
||||
} else {
|
||||
newFile(argv[2], volume);
|
||||
record = getRecordFromPath(argv[2], volume, NULL, NULL);
|
||||
writeToHFSFile((HFSPlusCatalogFile*)record, inFile, volume);
|
||||
}
|
||||
|
||||
fclose(inFile);
|
||||
free(record);
|
||||
}
|
||||
|
||||
void cmd_rm(Volume* volume, int argc, const char *argv[]) {
|
||||
if(argc > 1) {
|
||||
removeFile(argv[1], volume);
|
||||
} else {
|
||||
printf("Not enough arguments");
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_chmod(Volume* volume, int argc, const char *argv[]) {
|
||||
int mode;
|
||||
|
||||
if(argc > 2) {
|
||||
sscanf(argv[1], "%o", &mode);
|
||||
chmodFile(argv[2], mode, volume);
|
||||
} else {
|
||||
printf("Not enough arguments");
|
||||
}
|
||||
}
|
||||
|
||||
void TestByteOrder()
|
||||
{
|
||||
short int word = 0x0001;
|
||||
char *byte = (char *) &word;
|
||||
endianness = byte[0] ? IS_LITTLE_ENDIAN : IS_BIG_ENDIAN;
|
||||
}
|
||||
|
||||
void extractAllInFolder(HFSCatalogNodeID folderID, Volume* volume) {
|
||||
CatalogRecordList* list;
|
||||
CatalogRecordList* theList;
|
||||
char cwd[1024];
|
||||
char* name;
|
||||
HFSPlusCatalogFolder* folder;
|
||||
HFSPlusCatalogFile* file;
|
||||
FILE* outFile;
|
||||
struct stat status;
|
||||
|
||||
ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory");
|
||||
|
||||
theList = list = getFolderContents(folderID, volume);
|
||||
|
||||
while(list != NULL) {
|
||||
name = unicodeToAscii(&list->name);
|
||||
if(strncmp(name, ".HFS+ Private Directory Data", sizeof(".HFS+ Private Directory Data") - 1) == 0 || name[0] == '\0') {
|
||||
free(name);
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(list->record->recordType == kHFSPlusFolderRecord) {
|
||||
folder = (HFSPlusCatalogFolder*)list->record;
|
||||
printf("folder: %s\n", name);
|
||||
if(stat(name, &status) != 0) {
|
||||
ASSERT(mkdir(name, 0755) == 0, "mkdir");
|
||||
}
|
||||
ASSERT(chdir(name) == 0, "chdir");
|
||||
extractAllInFolder(folder->folderID, volume);
|
||||
ASSERT(chdir(cwd) == 0, "chdir");
|
||||
} else if(list->record->recordType == kHFSPlusFileRecord) {
|
||||
printf("file: %s\n", name);
|
||||
file = (HFSPlusCatalogFile*)list->record;
|
||||
outFile = fopen(name, "wb");
|
||||
if(outFile != NULL) {
|
||||
writeToFile(file, outFile, volume);
|
||||
fclose(outFile);
|
||||
} else {
|
||||
printf("WARNING: cannot fopen %s\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
free(name);
|
||||
list = list->next;
|
||||
}
|
||||
releaseCatalogRecordList(theList);
|
||||
}
|
||||
|
||||
void cmd_extractall(Volume* volume, int argc, const char *argv[]) {
|
||||
HFSPlusCatalogRecord* record;
|
||||
char cwd[1024];
|
||||
char* name;
|
||||
|
||||
ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory");
|
||||
|
||||
if(argc > 1)
|
||||
record = getRecordFromPath(argv[1], volume, &name, NULL);
|
||||
else
|
||||
record = getRecordFromPath("/", volume, &name, NULL);
|
||||
|
||||
if(argc > 2) {
|
||||
ASSERT(chdir(argv[2]) == 0, "chdir");
|
||||
}
|
||||
|
||||
if(record != NULL) {
|
||||
if(record->recordType == kHFSPlusFolderRecord)
|
||||
extractAllInFolder(((HFSPlusCatalogFolder*)record)->folderID, volume);
|
||||
else
|
||||
printf("Not a folder\n");
|
||||
} else {
|
||||
printf("No such file or directory\n");
|
||||
}
|
||||
free(record);
|
||||
|
||||
ASSERT(chdir(cwd) == 0, "chdir");
|
||||
}
|
||||
|
||||
void removeAllInFolder(HFSCatalogNodeID folderID, Volume* volume, const char* parentName) {
|
||||
CatalogRecordList* list;
|
||||
CatalogRecordList* theList;
|
||||
char fullName[1024];
|
||||
char* name;
|
||||
char* pathComponent;
|
||||
int pathLen;
|
||||
char isRoot;
|
||||
|
||||
HFSPlusCatalogFolder* folder;
|
||||
theList = list = getFolderContents(folderID, volume);
|
||||
|
||||
strcpy(fullName, parentName);
|
||||
pathComponent = fullName + strlen(fullName);
|
||||
|
||||
isRoot = FALSE;
|
||||
if(strcmp(fullName, "/") == 0) {
|
||||
isRoot = TRUE;
|
||||
}
|
||||
|
||||
while(list != NULL) {
|
||||
name = unicodeToAscii(&list->name);
|
||||
if(isRoot && (name[0] == '\0' || strncmp(name, ".HFS+ Private Directory Data", sizeof(".HFS+ Private Directory Data") - 1) == 0)) {
|
||||
free(name);
|
||||
list = list->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
strcpy(pathComponent, name);
|
||||
pathLen = strlen(fullName);
|
||||
|
||||
if(list->record->recordType == kHFSPlusFolderRecord) {
|
||||
folder = (HFSPlusCatalogFolder*)list->record;
|
||||
fullName[pathLen] = '/';
|
||||
fullName[pathLen + 1] = '\0';
|
||||
removeAllInFolder(folder->folderID, volume, fullName);
|
||||
} else {
|
||||
printf("%s\n", fullName);
|
||||
removeFile(fullName, volume);
|
||||
}
|
||||
|
||||
free(name);
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
releaseCatalogRecordList(theList);
|
||||
|
||||
if(!isRoot) {
|
||||
*(pathComponent - 1) = '\0';
|
||||
printf("%s\n", fullName);
|
||||
removeFile(fullName, volume);
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_rmall(Volume* volume, int argc, const char *argv[]) {
|
||||
HFSPlusCatalogRecord* record;
|
||||
char* name;
|
||||
char initPath[1024];
|
||||
int lastCharOfPath;
|
||||
|
||||
if(argc > 1) {
|
||||
record = getRecordFromPath(argv[1], volume, &name, NULL);
|
||||
strcpy(initPath, argv[1]);
|
||||
lastCharOfPath = strlen(argv[1]) - 1;
|
||||
if(argv[1][lastCharOfPath] != '/') {
|
||||
initPath[lastCharOfPath + 1] = '/';
|
||||
initPath[lastCharOfPath + 2] = '\0';
|
||||
}
|
||||
} else {
|
||||
record = getRecordFromPath("/", volume, &name, NULL);
|
||||
initPath[0] = '/';
|
||||
initPath[1] = '\0';
|
||||
}
|
||||
|
||||
if(record != NULL) {
|
||||
if(record->recordType == kHFSPlusFolderRecord) {
|
||||
removeAllInFolder(((HFSPlusCatalogFolder*)record)->folderID, volume, initPath);
|
||||
} else {
|
||||
printf("Not a folder\n");
|
||||
}
|
||||
} else {
|
||||
printf("No such file or directory\n");
|
||||
}
|
||||
free(record);
|
||||
}
|
||||
|
||||
void addAllInFolder(HFSCatalogNodeID folderID, Volume* volume, const char* parentName) {
|
||||
CatalogRecordList* list;
|
||||
CatalogRecordList* theList;
|
||||
char cwd[1024];
|
||||
char fullName[1024];
|
||||
char* pathComponent;
|
||||
int pathLen;
|
||||
|
||||
char* name;
|
||||
|
||||
DIR* dir;
|
||||
DIR* tmp;
|
||||
|
||||
HFSCatalogNodeID cnid;
|
||||
|
||||
struct dirent* ent;
|
||||
|
||||
FILE* file;
|
||||
HFSPlusCatalogFile* outFile;
|
||||
|
||||
strcpy(fullName, parentName);
|
||||
pathComponent = fullName + strlen(fullName);
|
||||
|
||||
ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory");
|
||||
|
||||
theList = list = getFolderContents(folderID, volume);
|
||||
|
||||
ASSERT((dir = opendir(cwd)) != NULL, "opendir");
|
||||
|
||||
while((ent = readdir(dir)) != NULL) {
|
||||
if(ent->d_name[0] == '.' && (ent->d_name[1] == '\0' || (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
strcpy(pathComponent, ent->d_name);
|
||||
pathLen = strlen(fullName);
|
||||
|
||||
cnid = 0;
|
||||
list = theList;
|
||||
while(list != NULL) {
|
||||
name = unicodeToAscii(&list->name);
|
||||
if(strcmp(name, ent->d_name) == 0) {
|
||||
cnid = (list->record->recordType == kHFSPlusFolderRecord) ? (((HFSPlusCatalogFolder*)list->record)->folderID)
|
||||
: (((HFSPlusCatalogFile*)list->record)->fileID);
|
||||
free(name);
|
||||
break;
|
||||
}
|
||||
free(name);
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
if((tmp = opendir(ent->d_name)) != NULL) {
|
||||
closedir(tmp);
|
||||
printf("folder: %s\n", fullName);
|
||||
|
||||
if(cnid == 0) {
|
||||
cnid = newFolder(fullName, volume);
|
||||
}
|
||||
|
||||
fullName[pathLen] = '/';
|
||||
fullName[pathLen + 1] = '\0';
|
||||
ASSERT(chdir(ent->d_name) == 0, "chdir");
|
||||
addAllInFolder(cnid, volume, fullName);
|
||||
ASSERT(chdir(cwd) == 0, "chdir");
|
||||
} else {
|
||||
printf("file: %s\n", fullName);
|
||||
if(cnid == 0) {
|
||||
cnid = newFile(fullName, volume);
|
||||
}
|
||||
file = fopen(ent->d_name, "rb");
|
||||
ASSERT(file != NULL, "fopen");
|
||||
outFile = (HFSPlusCatalogFile*)getRecordByCNID(cnid, volume);
|
||||
writeToHFSFile(outFile, file, volume);
|
||||
fclose(file);
|
||||
free(outFile);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
releaseCatalogRecordList(theList);
|
||||
}
|
||||
|
||||
void cmd_addall(Volume* volume, int argc, const char *argv[]) {
|
||||
HFSPlusCatalogRecord* record;
|
||||
char* name;
|
||||
char cwd[1024];
|
||||
char initPath[1024];
|
||||
int lastCharOfPath;
|
||||
|
||||
ASSERT(getcwd(cwd, 1024) != NULL, "cannot get current working directory");
|
||||
|
||||
if(argc < 2) {
|
||||
printf("Not enough arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
if(chdir(argv[1]) != 0) {
|
||||
printf("Cannot open that directory: %s\n", argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(argc > 2) {
|
||||
record = getRecordFromPath(argv[2], volume, &name, NULL);
|
||||
strcpy(initPath, argv[2]);
|
||||
lastCharOfPath = strlen(argv[2]) - 1;
|
||||
if(argv[2][lastCharOfPath] != '/') {
|
||||
initPath[lastCharOfPath + 1] = '/';
|
||||
initPath[lastCharOfPath + 2] = '\0';
|
||||
}
|
||||
} else {
|
||||
record = getRecordFromPath("/", volume, &name, NULL);
|
||||
record = getRecordFromPath("/", volume, &name, NULL);
|
||||
initPath[0] = '/';
|
||||
initPath[1] = '\0';
|
||||
}
|
||||
|
||||
if(record != NULL) {
|
||||
if(record->recordType == kHFSPlusFolderRecord)
|
||||
addAllInFolder(((HFSPlusCatalogFolder*)record)->folderID, volume, initPath);
|
||||
else
|
||||
printf("Not a folder\n");
|
||||
} else {
|
||||
printf("No such file or directory\n");
|
||||
}
|
||||
|
||||
ASSERT(chdir(cwd) == 0, "chdir");
|
||||
free(record);
|
||||
|
||||
}
|
||||
|
||||
void cmd_grow(Volume* volume, int argc, const char *argv[]) {
|
||||
uint64_t newSize;
|
||||
uint32_t newBlocks;
|
||||
uint32_t blocksToGrow;
|
||||
uint64_t newMapSize;
|
||||
uint64_t i;
|
||||
unsigned char zero;
|
||||
|
||||
zero = 0;
|
||||
|
||||
if(argc < 2) {
|
||||
printf("Not enough arguments\n");
|
||||
return;
|
||||
}
|
||||
|
||||
newSize = 0;
|
||||
sscanf(argv[1], "%lld", &newSize);
|
||||
newBlocks = newSize / volume->volumeHeader->blockSize;
|
||||
|
||||
if(newBlocks <= volume->volumeHeader->totalBlocks) {
|
||||
printf("Cannot shrink volume\n");
|
||||
return;
|
||||
}
|
||||
|
||||
blocksToGrow = newBlocks - volume->volumeHeader->totalBlocks;
|
||||
newMapSize = newBlocks / 8;
|
||||
|
||||
if(volume->volumeHeader->allocationFile.logicalSize < newMapSize) {
|
||||
if(volume->volumeHeader->freeBlocks
|
||||
< ((newMapSize - volume->volumeHeader->allocationFile.logicalSize) / volume->volumeHeader->blockSize)) {
|
||||
printf("Not enough room to allocate new allocation map blocks\n");
|
||||
}
|
||||
|
||||
allocate((RawFile*) (volume->allocationFile->data), newMapSize);
|
||||
}
|
||||
|
||||
/* unreserve last block */
|
||||
setBlockUsed(volume, volume->volumeHeader->totalBlocks - 1, 0);
|
||||
/* don't need to increment freeBlocks because we will allocate another alternate volume header later on */
|
||||
|
||||
/* "unallocate" the new blocks */
|
||||
for(i = ((volume->volumeHeader->totalBlocks / 8) + 1); i < newMapSize; i++) {
|
||||
ASSERT(WRITE(volume->allocationFile, i, 1, &zero), "WRITE");
|
||||
}
|
||||
|
||||
/* grow backing store size */
|
||||
ASSERT(WRITE(volume->image, newSize - 1, 1, &zero), "WRITE");
|
||||
|
||||
/* write new volume information */
|
||||
volume->volumeHeader->totalBlocks = newBlocks;
|
||||
volume->volumeHeader->freeBlocks += blocksToGrow;
|
||||
|
||||
/* reserve last block */
|
||||
setBlockUsed(volume, volume->volumeHeader->totalBlocks - 1, 1);
|
||||
|
||||
updateVolume(volume);
|
||||
|
||||
printf("grew volume: %lld -> %lld (%d)\n", newSize - (blocksToGrow * volume->volumeHeader->blockSize), newSize, blocksToGrow);
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
io_func* io;
|
||||
Volume* volume;
|
||||
AbstractFile* image;
|
||||
int argOff;
|
||||
|
||||
TestByteOrder();
|
||||
|
||||
if(argc < 3) {
|
||||
printf("usage: %s <image-file> (-k <key>) <ls|cat|mv|mkdir|add|rm|chmod|extract|extractall|rmall|addall> <arguments>\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
argOff = 2;
|
||||
|
||||
if(strstr(argv[1], ".dmg")) {
|
||||
image = createAbstractFileFromFile(fopen(argv[1], "rb"));
|
||||
if(argc > 3) {
|
||||
if(strcmp(argv[2], "-k") == 0) {
|
||||
image = createAbstractFileFromFileVault(image, argv[3]);
|
||||
argOff = 4;
|
||||
}
|
||||
}
|
||||
io = openDmgFilePartition(image, -1);
|
||||
} else {
|
||||
io = openFlatFile(argv[1]);
|
||||
}
|
||||
|
||||
if(io == NULL) {
|
||||
fprintf(stderr, "error: Cannot open image-file.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
volume = openVolume(io);
|
||||
if(volume == NULL) {
|
||||
fprintf(stderr, "error: Cannot open volume.\n");
|
||||
CLOSE(io);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(argc > argOff) {
|
||||
if(strcmp(argv[argOff], "ls") == 0) {
|
||||
cmd_ls(volume, argc - argOff, argv + argOff);
|
||||
} else if(strcmp(argv[argOff], "cat") == 0) {
|
||||
cmd_cat(volume, argc - argOff, argv + argOff);
|
||||
} else if(strcmp(argv[argOff], "mv") == 0) {
|
||||
cmd_mv(volume, argc - argOff, argv + argOff);
|
||||
} else if(strcmp(argv[argOff], "mkdir") == 0) {
|
||||
cmd_mkdir(volume, argc - argOff, argv + argOff);
|
||||
} else if(strcmp(argv[argOff], "add") == 0) {
|
||||
cmd_add(volume, argc - argOff, argv + argOff);
|
||||
} else if(strcmp(argv[argOff], "rm") == 0) {
|
||||
cmd_rm(volume, argc - argOff, argv + argOff);
|
||||
} else if(strcmp(argv[argOff], "chmod") == 0) {
|
||||
cmd_chmod(volume, argc - argOff, argv + argOff);
|
||||
} else if(strcmp(argv[argOff], "extract") == 0) {
|
||||
cmd_extract(volume, argc - argOff, argv + argOff);
|
||||
} else if(strcmp(argv[argOff], "extractall") == 0) {
|
||||
cmd_extractall(volume, argc - argOff, argv + argOff);
|
||||
} else if(strcmp(argv[argOff], "rmall") == 0) {
|
||||
cmd_rmall(volume, argc - argOff, argv + argOff);
|
||||
} else if(strcmp(argv[argOff], "addall") == 0) {
|
||||
cmd_addall(volume, argc - argOff, argv + argOff);
|
||||
} else if(strcmp(argv[argOff], "grow") == 0) {
|
||||
cmd_grow(volume, argc - argOff, argv + argOff);
|
||||
}
|
||||
}
|
||||
|
||||
closeVolume(volume);
|
||||
CLOSE(io);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -669,11 +669,13 @@ int main(int argc, const char *argv[]) {
|
|||
|
||||
io = openFlatFile(argv[1]);
|
||||
if(io == NULL) {
|
||||
fprintf(stderr, "error: Cannot open image-file.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
volume = openVolume(io);
|
||||
if(volume == NULL) {
|
||||
fprintf(stderr, "error: Cannot open volume.\n");
|
||||
CLOSE(io);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef HFSPLUS_H
|
||||
#define HFSPLUS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -477,3 +480,5 @@ int addToBTree(BTree* tree, BTKey* searchKey, size_t length, unsigned char* cont
|
|||
|
||||
int removeFromBTree(BTree* tree, BTKey* searchKey);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
activeExecutable = 63F921E40DC4909A0056EA77 /* dmg */;
|
||||
activeTarget = 637FAC690DC4958E00D1D35F /* all */;
|
||||
addToTargets = (
|
||||
63F921E20DC4909A0056EA77 /* dmg */,
|
||||
);
|
||||
codeSenseManager = 63F91F970DC48F810056EA77 /* Code sense */;
|
||||
executables = (
|
||||
|
@ -71,14 +72,14 @@
|
|||
PBXFileDataSource_Warnings_ColumnID,
|
||||
);
|
||||
};
|
||||
PBXPerProjectTemplateStateSaveDate = 230988791;
|
||||
PBXWorkspaceStateSaveDate = 230988791;
|
||||
PBXPerProjectTemplateStateSaveDate = 230991082;
|
||||
PBXWorkspaceStateSaveDate = 230991082;
|
||||
};
|
||||
perUserProjectItems = {
|
||||
63DCCB0C0DC49C00005D833C /* PBXBookmark */ = 63DCCB0C0DC49C00005D833C /* PBXBookmark */;
|
||||
63DCCB0F0DC49C84005D833C /* PBXTextBookmark */ = 63DCCB0F0DC49C84005D833C /* PBXTextBookmark */;
|
||||
63DCCB100DC49C84005D833C /* PBXTextBookmark */ = 63DCCB100DC49C84005D833C /* PBXTextBookmark */;
|
||||
63EFCCEB0DC49BA00031F8B4 /* PBXTextBookmark */ = 63EFCCEB0DC49BA00031F8B4 /* PBXTextBookmark */;
|
||||
63DCCB0C0DC49C00005D833C = 63DCCB0C0DC49C00005D833C /* PBXBookmark */;
|
||||
63DCCB0F0DC49C84005D833C = 63DCCB0F0DC49C84005D833C /* PBXTextBookmark */;
|
||||
63DCCB100DC49C84005D833C = 63DCCB100DC49C84005D833C /* PBXTextBookmark */;
|
||||
63EFCCEB0DC49BA00031F8B4 = 63EFCCEB0DC49BA00031F8B4 /* PBXTextBookmark */;
|
||||
};
|
||||
sourceControlManager = 63F91F960DC48F810056EA77 /* Source Control */;
|
||||
userBuildSettings = {
|
||||
|
@ -141,16 +142,17 @@
|
|||
};
|
||||
63F920C90DC48FFC0056EA77 /* dmg.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {987, 8400}}";
|
||||
sepNavSelRange = "{15975, 0}";
|
||||
sepNavVisRange = "{15886, 160}";
|
||||
sepNavIntBoundsRect = "{{0, 0}, {1160, 8050}}";
|
||||
sepNavSelRange = "{14237, 0}";
|
||||
sepNavVisRange = "{14571, 1385}";
|
||||
sepNavWindowFrame = "{{15, 164}, {1150, 833}}";
|
||||
};
|
||||
};
|
||||
63F920CB0DC48FFC0056EA77 /* filevault.c */ = {
|
||||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {1091, 3570}}";
|
||||
sepNavIntBoundsRect = "{{0, 0}, {1091, 3598}}";
|
||||
sepNavSelRange = "{1569, 0}";
|
||||
sepNavVisRange = "{2111, 1579}";
|
||||
sepNavVisRange = "{2111, 1571}";
|
||||
sepNavWindowFrame = "{{15, 164}, {1150, 833}}";
|
||||
};
|
||||
};
|
||||
|
@ -166,7 +168,7 @@
|
|||
uiCtxt = {
|
||||
sepNavIntBoundsRect = "{{0, 0}, {1091, 4648}}";
|
||||
sepNavSelRange = "{8494, 0}";
|
||||
sepNavVisRange = "{7662, 1270}";
|
||||
sepNavVisRange = "{7584, 1304}";
|
||||
sepNavWindowFrame = "{{15, 164}, {1150, 833}}";
|
||||
};
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
63E264B70DC4A546004B29C4 /* dmgfile.c in Sources */ = {isa = PBXBuildFile; fileRef = 63E264B60DC4A546004B29C4 /* dmgfile.c */; };
|
||||
63F921EA0DC490C20056EA77 /* abstractfile.c in Sources */ = {isa = PBXBuildFile; fileRef = 63F920C60DC48FFC0056EA77 /* abstractfile.c */; };
|
||||
63F921EB0DC490C20056EA77 /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = 63F920C70DC48FFC0056EA77 /* base64.c */; };
|
||||
63F921EC0DC490C20056EA77 /* checksum.c in Sources */ = {isa = PBXBuildFile; fileRef = 63F920C80DC48FFC0056EA77 /* checksum.c */; };
|
||||
|
@ -88,6 +89,8 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
63E264B50DC4A546004B29C4 /* dmgfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dmgfile.h; sourceTree = "<group>"; };
|
||||
63E264B60DC4A546004B29C4 /* dmgfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = dmgfile.c; sourceTree = "<group>"; };
|
||||
63F920C60DC48FFC0056EA77 /* abstractfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = abstractfile.c; sourceTree = "<group>"; };
|
||||
63F920C70DC48FFC0056EA77 /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = "<group>"; };
|
||||
63F920C80DC48FFC0056EA77 /* checksum.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = checksum.c; sourceTree = "<group>"; };
|
||||
|
@ -217,6 +220,8 @@
|
|||
63F920D20DC48FFC0056EA77 /* zconf.h */,
|
||||
63F920D30DC48FFC0056EA77 /* zlib-1.2.3 */,
|
||||
63F921C00DC48FFC0056EA77 /* zlib.h */,
|
||||
63E264B50DC4A546004B29C4 /* dmgfile.h */,
|
||||
63E264B60DC4A546004B29C4 /* dmgfile.c */,
|
||||
);
|
||||
name = dmg;
|
||||
path = ../../dmg;
|
||||
|
@ -385,6 +390,7 @@
|
|||
63F921EE0DC490CA0056EA77 /* io.c in Sources */,
|
||||
63F921EC0DC490C20056EA77 /* checksum.c in Sources */,
|
||||
63F921ED0DC490C20056EA77 /* dmg.c in Sources */,
|
||||
63E264B70DC4A546004B29C4 /* dmgfile.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче