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:
planetbeing 2008-04-27 10:25:00 -04:00
Родитель 2fb2b51d31
Коммит 75ad992658
13 изменённых файлов: 1061 добавлений и 24 удалений

Просмотреть файл

@ -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:

236
dmg/dmgfile.c Normal file
Просмотреть файл

@ -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;
}

22
dmg/dmgfile.h Normal file
Просмотреть файл

@ -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);

23
hdutil/Makefile Normal file
Просмотреть файл

@ -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

733
hdutil/hdutil.c Normal file
Просмотреть файл

@ -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;
};