Created
September 14, 2021 14:36
-
-
Save neozeed/c9e9f6b07b673025fe40aa4c95955976 to your computer and use it in GitHub Desktop.
heavily modified filesystem code from Shoebill to extract a file(s) from a raw SYSV filesystem
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Might work with MinGW/Linux? I am pretty sure I pulled the endian stuff, leaning on winsock. Maybe Linux will figure it out, not sure. | |
// Anyways Im too lazy to read how to init this stuff and bypass the apple partition map | |
// Instead I have it open an apple disk, and then when it's time to read a filesystem | |
// I close the file handle and patch it (lol) to open another filesystem. | |
// It doesn't work on the s1505_cp3540.dd image, but oddly enough I was able to get files | |
// from other 68k SYSVr2 and SYSVr3 filesystems without issue. | |
// Define JASON to turn on my horrible modifications | |
// As always the real hard work was all Peter's. | |
#define JASON | |
#pragma warning(suppress : 4996) | |
#pragma comment(lib, "ws2_32.lib") | |
#define _CRT_SECURE_NO_WARNINGS | |
/* | |
* Copyright (c) 2014, Peter Rutenbar <pruten@gmail.com> | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright notice, this | |
* list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright notice, | |
* this list of conditions and the following disclaimer in the documentation | |
* and/or other materials provided with the distribution. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | |
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <fcntl.h> | |
#include <string.h> | |
#include <ws2tcpip.h> | |
/////////////////////////////////////////////////////////////////////////////////// | |
// endian flipper | |
#define slog printf | |
#define fix_endian(x) do { \ | |
switch (sizeof(x)) { \ | |
case 1: break; \ | |
case 2: (x) = ntohs(x); break; \ | |
case 4: (x) = ntohl(x); break; \ | |
case 8: (x) = ntohll(x); break; \ | |
default: printf("bogus size"); \ | |
}} while (0) | |
#ifdef __GNUC__ | |
#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__)) | |
#endif | |
#ifdef _MSC_VER | |
#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop)) | |
#endif | |
/////////////////////////////////////////////////////////////////////////////////// | |
//Memory Allocator | |
/////////////////////////////////////////////////////////////////////////////////// | |
#define POOL_START_MAGIC 0x231eb4af | |
#define POOL_END_MAGIC 0xb09f39f1 | |
#define POOL_ALLOC_TYPE 0 | |
#define POOL_CHILD_LINK 1 | |
#define POOL_HEAD 2 | |
typedef struct _alloc_pool_t { | |
uint32_t start_magic; | |
struct _alloc_pool_t* prev, * next; | |
union { | |
struct { | |
uint64_t size; | |
} alloc; | |
struct { | |
struct _alloc_pool_t* child; // pointer to the child's HEAD | |
} child_link; | |
struct { | |
struct _alloc_pool_t* parent_link; // pointer to the parent's CHILD_LINK | |
} head; | |
} t; | |
uint32_t type; | |
uint32_t end_magic; | |
} alloc_pool_t; | |
////////////////////////////////////////////////////////////////////////////////// | |
// alloc_pool.c | |
///////////// | |
static void _check_pool(alloc_pool_t* pool) | |
{ | |
} | |
static alloc_pool_t* _ptr_to_header(void* ptr) | |
{ | |
alloc_pool_t* apt = (alloc_pool_t*)ptr; | |
return &apt[-1]; | |
} | |
void* p_alloc(alloc_pool_t* pool, uint64_t size) | |
{ | |
alloc_pool_t* buf = calloc(sizeof(alloc_pool_t) + size, 1); | |
buf->type = POOL_ALLOC_TYPE; | |
buf->t.alloc.size = size; | |
buf->start_magic = POOL_START_MAGIC; | |
buf->end_magic = POOL_END_MAGIC; | |
buf->next = pool->next; | |
buf->prev = pool; | |
if (pool->next) | |
pool->next->prev = buf; | |
pool->next = buf; | |
return &buf[1]; | |
} | |
void* p_realloc(void* ptr, uint64_t size) | |
{ | |
alloc_pool_t* header = _ptr_to_header(ptr); | |
//assert(header->start_magic == POOL_START_MAGIC); | |
//assert(header->end_magic == POOL_END_MAGIC); | |
//assert(header->type == POOL_ALLOC_TYPE); | |
alloc_pool_t* new_header = realloc(header, size + sizeof(alloc_pool_t)); | |
if (new_header) { | |
new_header->t.alloc.size = size; | |
if (new_header->next) | |
new_header->next->prev = new_header; | |
if (new_header->prev) | |
new_header->prev->next = new_header; | |
return &new_header[1]; | |
} | |
return NULL; | |
} | |
/* | |
* Free *any* kind of alloc_pool_t header | |
*/ | |
static void _p_free_any(alloc_pool_t* header) | |
{ | |
//assert(header->start_magic == POOL_START_MAGIC); | |
//assert(header->end_magic == POOL_END_MAGIC); | |
if (header->next) | |
header->next->prev = header->prev; | |
if (header->prev) | |
header->prev->next = header->next; | |
free(header); | |
} | |
/* | |
* Free an alloc_pool allocation (but not HEAD or CHILD_LINK) | |
*/ | |
void p_free(void* ptr) | |
{ | |
alloc_pool_t* header = _ptr_to_header(ptr); | |
//assert(header->type == POOL_ALLOC_TYPE); | |
memset(ptr, 0xaa, header->t.alloc.size); | |
_p_free_any(header); | |
} | |
void p_free_pool(alloc_pool_t* pool) | |
{ | |
while (pool->prev) | |
pool = pool->prev; | |
while (pool) { | |
alloc_pool_t* cur = pool; | |
pool = cur->next; | |
//assert(cur->start_magic == POOL_START_MAGIC); | |
//assert(cur->end_magic == POOL_END_MAGIC); | |
switch (cur->type) { | |
case POOL_ALLOC_TYPE: | |
_p_free_any(cur); | |
break; | |
case POOL_CHILD_LINK: { | |
// p_free_pool will free and unlink cur | |
// (its parent's CHILD_LINK) | |
p_free_pool(cur->t.child_link.child); | |
break; | |
} | |
case POOL_HEAD: { | |
if (cur->t.head.parent_link) { | |
//assert(cur->t.head.parent_link->type == POOL_CHILD_LINK); | |
_p_free_any(cur->t.head.parent_link); | |
} | |
_p_free_any(cur); | |
break; | |
} | |
default: | |
printf("unknown POOL_ type"); | |
} | |
} | |
} | |
alloc_pool_t* p_new_pool(alloc_pool_t* parent_pool) | |
{ | |
alloc_pool_t* pool = calloc(sizeof(alloc_pool_t), 1); | |
pool->start_magic = POOL_START_MAGIC; | |
pool->end_magic = POOL_END_MAGIC; | |
pool->type = POOL_HEAD; | |
if (parent_pool) { | |
alloc_pool_t* link = _ptr_to_header(p_alloc(parent_pool, 0)); | |
link->type = POOL_CHILD_LINK; | |
link->t.child_link.child = pool; // child_link.child points to the child's HEAD | |
pool->t.head.parent_link = link; // head.parent_link points to the parent's CHILD_LINK | |
} | |
else | |
pool->t.head.parent_link = NULL; | |
return pool; | |
} | |
////////////////////////////////////////////////////////////////////////////////// | |
/////////////////////////////////////////////////////////////////////////////////// | |
typedef struct PACK ( { | |
uint32_t magic; | |
uint8_t cluster; | |
uint8_t type; | |
uint16_t inode; | |
// no no no SLICErrr rrrrrCUR (NOPE, sys-V compiler orders these bits backwards) | |
// yes RUCrrrrr rrrSLICE | |
uint8_t dummy : 5; | |
uint8_t crit : 1; | |
uint8_t usr : 1; | |
uint8_t root : 1; | |
uint8_t slice : 5; | |
uint8_t dummy2 : 3; | |
uint16_t dummy3; | |
uint32_t tmade; | |
uint32_t tmount; | |
uint32_t tunmount; | |
/* "Alternate block map" */ | |
uint32_t abm_size; | |
uint32_t abm_ents; | |
uint32_t abm_start; | |
}) block_zero_t; | |
typedef struct PACK({ | |
uint8_t pmSig[2]; | |
uint16_t pmSigPad; | |
uint32_t pmMapBlkCnt; | |
uint32_t pmPyPartStart; | |
uint32_t pmPartBlkCnt; | |
char pmPartName[32]; | |
char pmPartType[32]; | |
uint32_t pmLgDataStart; | |
uint32_t pmDataCnt; | |
uint32_t pmPartStatus; | |
uint32_t pmLgBootStart; | |
uint32_t pmBootSize; | |
uint32_t pmBootAddr; | |
uint32_t pmBootAddr2; | |
uint32_t pmBootEntry; | |
uint32_t pmBootEntry2; | |
uint32_t pmBootCksum; | |
char pmProcessor[16]; | |
block_zero_t bz; | |
}) apple_partition_map_t; | |
typedef struct PACK( { | |
uint8_t sbSig[2]; // device signature | |
uint16_t sbBlkSize; // block size of the device | |
uint32_t sbBlkCount; // number of blocks on the device | |
uint16_t sbDevType; // reserved | |
uint16_t sbDevId; // reserved | |
uint32_t sbData; // reserved | |
uint16_t sbDrvrCount; // nnumber of driver descriptor entries | |
uint32_t ddBlock; // first driver's starting block | |
uint16_t ddSize; // size of the driver, in 512-byte blocks | |
uint16_t ddType; // operating system type (MacOS = 1) | |
}) driver_descriptor_record_t; | |
typedef struct { | |
struct _disk_t* disk; | |
char* error_str; | |
alloc_pool_t* pool; | |
uint32_t start_block, num_blocks; | |
char name[33], type[33]; | |
} partition_t; | |
typedef struct _disk_t { | |
// -- fill these out -- | |
const char* path; | |
char* error_str; | |
// -- "private" -- | |
alloc_pool_t* pool; | |
FILE* f; | |
uint32_t block_size; | |
driver_descriptor_record_t ddr; | |
uint32_t num_partitions; | |
apple_partition_map_t* partition_maps; | |
partition_t* partitions; | |
} disk_t; | |
static void disk_get_block(disk_t* disk, uint8_t buf[512], uint32_t blockno) | |
{ | |
const uint32_t block_size = disk->block_size; | |
//assert(0 == fseek(disk->f, block_size * blockno, SEEK_SET)); | |
fseek(disk->f, block_size * blockno, SEEK_SET); | |
//assert(fread(buf, block_size, 1, disk->f) == 1); | |
fread(buf, block_size, 1, disk->f); | |
} | |
static void part_get_block(partition_t* part, uint8_t buf[512], uint32_t blockno) | |
{ | |
uint32_t newblock; | |
#ifdef JASON | |
newblock = blockno - 1; | |
#else | |
newblock = blockno; | |
#endif | |
//assert(blockno < part->num_blocks); | |
disk_get_block(part->disk, buf, part->start_block + newblock); | |
} | |
static uint8_t disk_load_partition_map(disk_t* disk, apple_partition_map_t* apm, uint32_t idx) | |
{ | |
uint8_t block[512]; | |
disk_get_block(disk, block, 1 + idx); | |
memcpy(apm, block, sizeof(apple_partition_map_t)); | |
fix_endian(apm->pmSigPad); | |
fix_endian(apm->pmMapBlkCnt); | |
fix_endian(apm->pmPyPartStart); | |
fix_endian(apm->pmPartBlkCnt); | |
fix_endian(apm->pmLgDataStart); | |
fix_endian(apm->pmDataCnt); | |
fix_endian(apm->pmPartStatus); | |
fix_endian(apm->pmLgBootStart); | |
fix_endian(apm->pmBootSize); | |
fix_endian(apm->pmBootAddr); | |
fix_endian(apm->pmBootAddr2); | |
fix_endian(apm->pmBootEntry); | |
fix_endian(apm->pmBootEntry2); | |
fix_endian(apm->pmBootCksum); | |
fix_endian(apm->bz.magic); | |
fix_endian(apm->bz.inode); | |
fix_endian(apm->bz.tmade); | |
fix_endian(apm->bz.tmount); | |
fix_endian(apm->bz.tunmount); | |
fix_endian(apm->bz.abm_size); | |
fix_endian(apm->bz.abm_ents); | |
fix_endian(apm->bz.abm_start); | |
if (memcmp(apm->pmSig, "PM", 2) != 0) { | |
sprintf(disk->error_str, "partition index %u has bad magic %02x%02x", | |
idx, apm->pmSig[0], apm->pmSig[1]); | |
return 0; | |
} | |
return 1; | |
} | |
static void close_disk(disk_t* disk) | |
{ | |
fclose(disk->f); | |
p_free_pool(disk->pool); | |
} | |
static disk_t* open_disk(const char* disk_path, char* error_str) | |
{ | |
disk_t* disk; | |
uint8_t block[512]; | |
apple_partition_map_t apm; | |
uint32_t i; | |
alloc_pool_t* pool = p_new_pool(NULL); | |
FILE* f; | |
disk = p_alloc(pool, sizeof(disk_t)); | |
disk->pool = pool; | |
disk->block_size = 512; | |
disk->error_str = error_str; | |
disk->path = disk_path; | |
f = fopen(disk_path, "rb"); | |
if (f == NULL) { | |
sprintf(error_str, "Can't open that path"); | |
goto fail; | |
} | |
disk->f = f; | |
// Load the driver descriptor record | |
disk_get_block(disk, block, 0); | |
memcpy(&disk->ddr, block, sizeof(disk->ddr)); | |
fix_endian(disk->ddr.sbBlkSize); | |
fix_endian(disk->ddr.sbBlkCount); | |
fix_endian(disk->ddr.sbDevType); | |
fix_endian(disk->ddr.sbDevId); | |
fix_endian(disk->ddr.sbData); | |
fix_endian(disk->ddr.sbDrvrCount); | |
fix_endian(disk->ddr.ddBlock); | |
fix_endian(disk->ddr.ddSize); | |
fix_endian(disk->ddr.ddType); | |
// If the DDR block exists, (it doesn't have to necessarially) | |
if (memcmp(disk->ddr.sbSig, "ER", 2) == 0) { | |
// Can't handle non-512 byte block sizes | |
if (disk->ddr.sbBlkSize != 512) { | |
sprintf(error_str, "This disk uses blkSize=%u and I can't handle that", | |
disk->ddr.sbBlkSize); | |
goto fail; | |
} | |
} | |
// slog("sizeof(apple_part_map_t) = %lu\n", sizeof(apple_partition_map_t)); | |
// Load the partition maps | |
if (!disk_load_partition_map(disk, &apm, 0)) | |
goto fail; | |
else if ((apm.pmMapBlkCnt > 256) || (apm.pmMapBlkCnt == 0)) { | |
sprintf(error_str, "Crazy number of partitions on this disk %u", apm.pmMapBlkCnt); | |
goto fail; | |
} | |
disk->num_partitions = apm.pmMapBlkCnt; | |
disk->partition_maps = p_alloc(disk->pool, disk->num_partitions * sizeof(apple_partition_map_t)); | |
disk->partitions = p_alloc(disk->pool, disk->num_partitions * sizeof(partition_t)); | |
for (i = 0; i < disk->num_partitions; i++) { | |
if (!disk_load_partition_map(disk, &disk->partition_maps[i], i)) | |
goto fail; | |
memset(&disk->partitions[i], 0, sizeof(partition_t)); | |
disk->partitions[i].disk = disk; | |
disk->partitions[i].pool = disk->pool; | |
disk->partitions[i].error_str = error_str; | |
disk->partitions[i].start_block = disk->partition_maps[i].pmPyPartStart; | |
disk->partitions[i].num_blocks = disk->partition_maps[i].pmPartBlkCnt; | |
memcpy(disk->partitions[i].name, disk->partition_maps[i].pmPartName, 32); | |
memcpy(disk->partitions[i].type, disk->partition_maps[i].pmPartType, 32); | |
slog("%u type:%s name:%s\n", i, disk->partitions[i].type, disk->partitions[i].name); | |
slog("bz_magic=0x%08x slice=%u\n", disk->partition_maps[i].bz.magic, disk->partition_maps[i].bz.slice); | |
} | |
return disk; | |
fail: | |
if (f) fclose(f); | |
p_free_pool(pool); | |
return NULL; | |
} | |
/*static uint8_t translate_aux_to_apm_partition_num(disk_t *disk, | |
uint32_t aux_idx, | |
uint32_t *apm_idx) | |
{ | |
uint32_t i, aux_i = 0; | |
for (i=0; i<disk->num_partitions; i++) { | |
partition_t *part = &disk->partitions[i]; | |
if (strcmp("Apple_UNIX_SVR2", part->type) != 0) | |
continue; | |
else if (strstr(part->name, "Eschatology") != NULL) | |
continue; | |
else if (aux_i == aux_idx) { | |
*apm_idx = i; | |
return 1; | |
} | |
aux_i++; | |
} | |
return 0; | |
}*/ | |
static int32_t find_root_partition_number(disk_t* disk, uint8_t clus_num) | |
{ | |
/* | |
* See the man page for bzb for the full scoop. | |
* Basically, A/UX partition 0 is the first partition with | |
* ** type="Apple_UNIX_SVR2", | |
* ** a matching cluster number, and | |
* ** the root bit set | |
* | |
* A/UX partition 1 is the first partition with | |
* ** type="Apple_UNIX_SVR2", | |
* ** a matching cluster number, and | |
* ** the swap filesystem type | |
* | |
* A/UX partition 2 is the next partition with | |
* ** type="Apple_UNIX_SVR2", | |
* ** a matching cluster number | |
* ** and the user bit set | |
* | |
* The logic is probably even more arbitrary | |
* and complicated than that. Why doesn't A/UX | |
* just use the native APM partition numbers? | |
*/ | |
uint32_t i; | |
for (i = 0; i < disk->num_partitions; i++) { | |
partition_t* part = &disk->partitions[i]; | |
apple_partition_map_t* apm = &disk->partition_maps[i]; | |
// slog("%u magic=0x%08x root=%u type=%s\n", i, apm->bz.magic, apm->bz.root, part->type); | |
if (apm->bz.magic != 0xabadbabe) | |
continue; | |
if (!apm->bz.root) | |
continue; | |
if (apm->bz.cluster != clus_num) | |
continue; | |
// slice==0 -> This partition doesn't prefer any particular slice number | |
// slice==N -> This partition prefers slice number (N-1) | |
// (I think) | |
/*if (apm->bz.slice != 0 && apm->bz.slice != 1) | |
continue;*/ | |
if (strcmp("Apple_UNIX_SVR2", part->type) != 0) | |
continue; | |
return (int32_t)i; | |
} | |
return -1; | |
} | |
#pragma mark SVFS stuff | |
/* --- SVFS stuff --- */ | |
typedef struct PACK( { | |
uint16_t isize; | |
uint32_t fsize; | |
uint16_t nfree; | |
uint32_t free[50]; | |
uint16_t ninode; | |
uint16_t inode[100]; | |
uint8_t flock; | |
uint8_t ilock; | |
uint8_t fmod; | |
uint8_t ronly; | |
uint32_t time; | |
uint16_t dinfo[4]; | |
uint32_t tfree; | |
uint16_t tinode; | |
uint8_t fname[6]; | |
uint8_t fpack[6]; | |
uint32_t _unused[13]; | |
uint32_t state; | |
uint16_t lasti; | |
uint16_t nbehind; | |
uint32_t magic; | |
uint32_t type; | |
}) svfs_superblock_t; | |
typedef struct PACK( { | |
uint16_t mode; | |
uint16_t nlink; | |
uint16_t uid; | |
uint16_t gid; | |
uint32_t size; | |
uint8_t __addr[39]; | |
uint8_t gen; | |
uint32_t atime; | |
uint32_t mtime; | |
uint32_t ctime; | |
uint32_t addr[13]; | |
} ) svfs_inode_t; | |
typedef struct { | |
char* error_str; | |
alloc_pool_t* pool; | |
partition_t* part; | |
svfs_superblock_t superblock; | |
uint32_t blocksize; // SVFS can use 512, 1024, 2048, 4096-byte block sizes | |
} svfs_t; | |
static uint8_t svfs_read_block(svfs_t* mount, uint8_t* block, uint32_t blockno) | |
{ | |
const uint32_t sectors_per_block = mount->blocksize / 512; | |
const uint32_t start_sector = blockno * sectors_per_block; | |
uint32_t i; | |
// slog("sectors_per_block = %u, start_sector=%u\n", sectors_per_block, start_sector); | |
for (i = 0; i < sectors_per_block; i++) { | |
part_get_block(mount->part, &block[i * 512], start_sector + i); | |
} | |
return 1; | |
} | |
static uint8_t svfs_load_inode(svfs_t* mount, svfs_inode_t* inode, uint32_t inum) | |
{ | |
uint32_t i; | |
uint8_t block[4096]; | |
const uint32_t max_inode_blocks = mount->superblock.isize - 2; | |
const uint32_t max_inodes = (max_inode_blocks * mount->blocksize) / 64; | |
if (inum > max_inodes) { | |
sprintf(mount->error_str, "svfs_load_inode: inode %u too big (max=%u)", inum, max_inodes); | |
return 0; | |
} | |
const uint32_t inum_byte_idx_in_partition = ((inum - 1) * 64) + (2 * mount->blocksize); | |
const uint32_t inum_block = inum_byte_idx_in_partition / mount->blocksize; | |
const uint32_t inum_byte_idx_in_block = inum_byte_idx_in_partition % mount->blocksize; | |
if (!svfs_read_block(mount, block, inum_block)) | |
return 0; | |
memcpy(inode, &block[inum_byte_idx_in_block], 64); | |
fix_endian(inode->mode); | |
fix_endian(inode->nlink); | |
fix_endian(inode->uid); | |
fix_endian(inode->gid); | |
fix_endian(inode->size); | |
fix_endian(inode->atime); | |
fix_endian(inode->mtime); | |
fix_endian(inode->ctime); | |
for (i = 0; i < 13; i++) { | |
uint32_t addr = inode->__addr[i * 3 + 0]; | |
addr = (addr << 8) + inode->__addr[i * 3 + 1]; | |
addr = (addr << 8) + inode->__addr[i * 3 + 2]; | |
inode->addr[i] = addr; | |
} | |
return 1; | |
} | |
static uint8_t svfs_read_level(svfs_t* mount, | |
svfs_inode_t* inode, | |
uint8_t* buf, | |
uint32_t* len, | |
uint32_t* indirects, | |
uint32_t level) | |
{ | |
uint8_t* tmp = p_alloc(mount->pool, mount->blocksize); | |
const uint32_t num_indirects = mount->blocksize / 4; | |
uint32_t i; | |
for (i = 0; (i < num_indirects) && (*len < inode->size); i++) { | |
uint32_t chunk_size = inode->size - *len; | |
if (chunk_size > mount->blocksize) | |
chunk_size = mount->blocksize; | |
const uint32_t addr = ntohl(indirects[i]); | |
if (!svfs_read_block(mount, tmp, addr)) { | |
sprintf(mount->error_str, "couldn't read svfs block num %u at L%u", addr, level); | |
goto fail; | |
} | |
if (level == 1) { | |
memcpy(buf + *len, tmp, chunk_size); | |
*len += chunk_size; | |
} | |
else { | |
if (!svfs_read_level(mount, inode, buf, len, (uint32_t*)tmp, level - 1)) | |
goto fail; | |
} | |
} | |
p_free(tmp); | |
return 1; | |
fail: | |
p_free(tmp); | |
return 0; | |
} | |
static uint8_t* svfs_read_inode_data(svfs_t* mount, svfs_inode_t* inode) | |
{ | |
uint8_t* tmp = p_alloc(mount->pool, mount->blocksize); | |
uint8_t* buf = p_alloc(mount->pool, inode->size); | |
uint32_t i, len = 0; | |
// The first 10 block pointers in the inode point to data | |
// The addr[10] is a L1 block pointer, [11] is L2, and [12] is L3 | |
for (i = 0; (len < inode->size) && (i < 10); i++) { | |
uint32_t chunk_size = inode->size - len; | |
if (chunk_size > mount->blocksize) | |
chunk_size = mount->blocksize; | |
if (!svfs_read_block(mount, tmp, inode->addr[i])) { | |
sprintf(mount->error_str, "couldn't read svfs block num %u at L0", inode->addr[i]); | |
goto fail; | |
} | |
memcpy(buf + len, tmp, chunk_size); | |
len += chunk_size; | |
} | |
if (!svfs_read_block(mount, tmp, inode->addr[10])) { | |
sprintf(mount->error_str, "couldn't read svfs L1 block ptr %u", inode->addr[10]); | |
goto fail; | |
} | |
else if (!svfs_read_level(mount, inode, buf, &len, (uint32_t*)tmp, 1)) | |
goto fail; | |
if (!svfs_read_block(mount, tmp, inode->addr[11])) { | |
sprintf(mount->error_str, "couldn't read svfs L2 block ptr %u", inode->addr[11]); | |
goto fail; | |
} | |
else if (!svfs_read_level(mount, inode, buf, &len, (uint32_t*)tmp, 2)) | |
goto fail; | |
if (!svfs_read_block(mount, tmp, inode->addr[12])) { | |
sprintf(mount->error_str, "couldn't read svfs L3 block ptr %u", inode->addr[12]); | |
goto fail; | |
} | |
else if (!svfs_read_level(mount, inode, buf, &len, (uint32_t*)tmp, 3)) | |
goto fail; | |
p_free(tmp); | |
return buf; | |
fail: | |
p_free(tmp); | |
p_free(buf); | |
return NULL; | |
} | |
static svfs_t* svfs_mount(partition_t* part) | |
{ | |
//assert(sizeof(svfs_superblock_t) == 512); | |
uint32_t i; | |
svfs_t* mount = p_alloc(part->pool, sizeof(svfs_t)); | |
mount->pool = part->pool; | |
mount->error_str = part->error_str; | |
mount->part = part; | |
#ifdef JASON | |
fclose(part->disk->f); | |
part->disk->f = fopen("c:\\temp\\aux07\\r3v8-s1.img", "rb"); | |
part->start_block = 0; | |
printf("refile %d\n", part->disk->f); | |
#endif | |
part_get_block(part, (uint8_t*)&mount->superblock, 1); | |
fix_endian(mount->superblock.isize); | |
fix_endian(mount->superblock.fsize); | |
fix_endian(mount->superblock.nfree); | |
for (i = 0; i < 50; i++) { | |
fix_endian(mount->superblock.free[i]); | |
} | |
fix_endian(mount->superblock.ninode); | |
for (i = 0; i < 100; i++) { | |
fix_endian(mount->superblock.inode[i]); | |
} | |
fix_endian(mount->superblock.time); | |
for (i = 0; i < 4; i++) { | |
fix_endian(mount->superblock.dinfo[i]); | |
} | |
fix_endian(mount->superblock.tfree); | |
fix_endian(mount->superblock.tinode); | |
fix_endian(mount->superblock.state); | |
fix_endian(mount->superblock.lasti); | |
fix_endian(mount->superblock.nbehind); | |
fix_endian(mount->superblock.magic); | |
fix_endian(mount->superblock.type); | |
if (mount->superblock.magic != 0xfd187e20) { | |
sprintf(part->error_str, "Magic doesn't match svfs"); | |
goto fail; | |
} | |
// It is SVFS! | |
const uint32_t type = mount->superblock.type; | |
if ((type != 1) && (type != 2) && (type != 4) && (type != 8)) { | |
sprintf(part->error_str, "Unknown SVFS type (%u)", type); | |
goto fail; | |
} | |
mount->blocksize = 512 * type; | |
return mount; | |
fail: | |
if (mount) p_free(mount); | |
return NULL; | |
} | |
typedef struct PACK( { | |
uint16_t inum; | |
char name[14]; | |
} ) svfs_dir_entry_t; | |
svfs_inode_t* svfs_traverse_path(svfs_t* mount, const char* _path) | |
{ | |
uint32_t i; | |
uint16_t inum = 2; // 2 == root | |
svfs_inode_t* inode = p_alloc(mount->pool, sizeof(svfs_inode_t)); | |
char* path = p_alloc(mount->pool, strlen(_path) + 1); | |
strcpy(path, _path); | |
if (!svfs_load_inode(mount, inode, inum)) | |
goto fail; | |
char* last, * elem; | |
for (elem = strtok(path, "/", &last); | |
elem; | |
elem = strtok(NULL, "/", &last)) { | |
//slog("elem = [%s]\n", elem); | |
const uint32_t num_entries = inode->size / 16; | |
//slog("inode size = %u\n", inode->size); | |
svfs_dir_entry_t* dir = (svfs_dir_entry_t*)svfs_read_inode_data(mount, inode); | |
if (!dir) | |
goto fail; | |
for (i = 0; i < num_entries; i++) { | |
if (strncmp(dir[i].name, elem, 14) == 0) { | |
inum = ntohs(dir[i].inum); | |
if (!svfs_load_inode(mount, inode, inum)) { | |
p_free(dir); | |
goto fail; | |
} | |
break; | |
} | |
} | |
p_free(dir); | |
if (i == num_entries) { | |
sprintf(mount->error_str, "'%s' in '%s' doesn't exist", elem, _path); | |
goto fail; | |
} | |
} | |
//slog("final inode size = %u\n", inode->size); | |
p_free(path); | |
return inode; | |
fail: | |
p_free(inode); | |
p_free(path); | |
return NULL; | |
} | |
/* --- UFS stuff --- */ | |
#pragma mark UFS stuff | |
typedef struct PACK( { | |
uint32_t link; | |
uint32_t rlink; | |
uint32_t sblkno; | |
uint32_t cblkno; | |
uint32_t iblkno; | |
uint32_t dblkno; | |
uint32_t cgoffset; | |
uint32_t cgmask; | |
uint32_t time; | |
uint32_t size; | |
uint32_t dsize; | |
uint32_t ncg; | |
uint32_t bsize; | |
uint32_t fsize; | |
uint32_t frag; | |
uint32_t minfree; | |
uint32_t rotdelay; | |
uint32_t rps; | |
uint32_t bmask; | |
uint32_t fmask; | |
uint32_t bshift; | |
uint32_t fshift; | |
uint32_t maxcontig; | |
uint32_t maxbpg; | |
uint32_t fragshift; | |
uint32_t fsbtodb; | |
uint32_t sbsize; | |
uint32_t csmask; | |
uint32_t csshift; | |
uint32_t nindir; | |
uint32_t inopb; | |
uint32_t nspf; | |
uint32_t optim; | |
uint32_t dummy[2]; | |
uint32_t state; | |
uint32_t id[2]; | |
uint32_t csaddr; | |
uint32_t cssize; | |
uint32_t cgsize; | |
uint32_t ntrak; | |
uint32_t nsect; | |
uint32_t spc; | |
uint32_t ncyl; | |
uint32_t cpg; | |
uint32_t ipg; | |
uint32_t fpg; | |
uint32_t csum_ndir; | |
uint32_t csum_nbfree; | |
uint32_t csum_nifree; | |
uint32_t csum_nffree; | |
uint8_t fmod; | |
uint8_t clean; | |
uint8_t ronly; | |
uint8_t flags; | |
uint8_t fsmnt[500]; | |
uint8_t fname[6]; | |
uint8_t fpack[6]; | |
uint32_t cgrotor; | |
uint32_t dummy2[32]; | |
uint32_t cpc; | |
uint16_t postbl[32][8]; | |
uint32_t magic; | |
} ) ufs_superblock_t; | |
typedef struct PACK( { | |
uint32_t link; | |
uint32_t rlink; | |
uint32_t time; | |
uint32_t cgx; | |
uint16_t ncyl; | |
uint16_t niblk; | |
uint32_t ndblk; | |
uint32_t csum_ndir; | |
uint32_t csum_nbfree; | |
uint32_t csum_nifree; | |
uint32_t csum_nffree; | |
uint32_t rotor; | |
uint32_t frotor; | |
uint32_t irotor; | |
uint32_t frsum[8]; | |
uint32_t btot[32]; | |
uint16_t b[32][8]; | |
uint8_t iused[256]; | |
uint32_t magic; | |
} ) ufs_cylinder_group_t; | |
typedef struct { | |
char* error_str; | |
alloc_pool_t* pool; | |
partition_t* part; | |
uint32_t frag_size; | |
uint32_t block_size; | |
uint32_t frag_per_block; | |
ufs_superblock_t superblock; | |
uint32_t num_groups; | |
ufs_cylinder_group_t* groups; | |
} ufs_t; | |
typedef struct PACK( { | |
uint16_t mode; | |
uint16_t nlink; | |
uint16_t uid; | |
uint16_t gid; | |
uint32_t size_hi; // UFS stores size as a uint64_t, but A/UX apparently only reads/writes | |
uint32_t size; // to the low bits, so sometimes the hi bits contain garbage | |
uint32_t atime; | |
uint32_t dummy; | |
uint32_t mtime; | |
uint32_t dummy2; | |
uint32_t ctime; | |
uint32_t dummy3; | |
uint32_t direct[12]; | |
uint32_t indirect[3]; | |
uint32_t flags; | |
uint32_t blocks; | |
uint32_t gen; | |
uint32_t dummy4[4]; | |
} ) ufs_inode_t; | |
/* | |
* I truly don't understand the concept behind cgoffset/cgmask, | |
* but this is apparently the algorithm for finding the fragment-offset | |
* into a cylinder group. | |
*/ | |
#define ufs_group_base(mount, num) ( \ | |
((mount)->superblock.fpg * (num)) + \ | |
((mount)->superblock.cgoffset * \ | |
((num) & ~(mount)->superblock.cgmask))) | |
static uint8_t ufs_read_frag(ufs_t* mount, uint8_t* frag, uint32_t fragno) | |
{ | |
const uint32_t sectors_per_frag = mount->frag_size / 512; | |
const uint32_t start_sector = fragno * sectors_per_frag; | |
uint32_t i; | |
for (i = 0; i < sectors_per_frag; i++) { | |
part_get_block(mount->part, &frag[i * 512], start_sector + i); | |
} | |
return 1; | |
} | |
static uint8_t ufs_read_block(ufs_t* mount, uint8_t* block, uint32_t blockno) | |
{ | |
uint32_t i; | |
/* | |
* block numbers and fragment numbers are identical - they both refer | |
* to fragment numbers. But if we're reading a "block", then we're reading | |
* mount->frag_per_block fragments starting at that block address. | |
*/ | |
//assert((blockno % mount->frag_per_block) == 0); // This had better align to a block boundary | |
for (i = 0; i < mount->frag_per_block; i++) { | |
if (!ufs_read_frag(mount, block + i * mount->frag_size, blockno + i)) | |
return 0; | |
} | |
return 1; | |
} | |
static uint8_t ufs_load_cylinder_group(ufs_t* mount, uint32_t frag_offset, ufs_cylinder_group_t* group) | |
{ | |
uint32_t numfrags = sizeof(ufs_cylinder_group_t) / mount->frag_size; | |
numfrags += ((sizeof(ufs_cylinder_group_t) % mount->frag_size) != 0); | |
uint8_t* buf = p_alloc(mount->pool, (numfrags + 1) * mount->frag_size); | |
uint32_t i; | |
for (i = 0; i <= numfrags; i++) | |
ufs_read_frag(mount, &buf[i * mount->frag_size], frag_offset + i); | |
memcpy(group, buf, sizeof(ufs_cylinder_group_t)); | |
fix_endian(group->link); | |
fix_endian(group->rlink); | |
fix_endian(group->time); | |
fix_endian(group->cgx); | |
fix_endian(group->ncyl); | |
fix_endian(group->niblk); | |
fix_endian(group->ndblk); | |
fix_endian(group->csum_ndir); | |
fix_endian(group->csum_nbfree); | |
fix_endian(group->csum_nifree); | |
fix_endian(group->csum_nffree); | |
fix_endian(group->rotor); | |
fix_endian(group->frotor); | |
fix_endian(group->irotor); | |
for (i = 0; i < 8; i++) | |
fix_endian(group->frsum[i]); | |
for (i = 0; i < (32 * 8); i++) | |
fix_endian(group->b[i / 8][i % 8]); | |
fix_endian(group->magic); | |
p_free(buf); | |
return 1; | |
fail: | |
p_free(buf); | |
return 0; | |
} | |
static uint8_t ufs_load_inode(ufs_t* mount, ufs_inode_t* inode, uint32_t inum) | |
{ | |
//assert(sizeof(ufs_inode_t) == 128); | |
/* Which cylinder group is this inode in? */ | |
const uint32_t group_num = inum / mount->superblock.ipg; | |
/* Index of this inode in its cylinder group's inode table */ | |
const uint32_t group_ino_offset = inum % mount->superblock.ipg; | |
/* Fragment address that contains inode */ | |
const uint32_t frag_addr = ufs_group_base(mount, group_num) + | |
mount->superblock.iblkno + | |
((group_ino_offset * 128) / mount->frag_size); | |
/* Byte offset into the fragment where the inode begins */ | |
const uint32_t frag_offset = (group_ino_offset * 128) % mount->frag_size; | |
uint32_t i; | |
uint8_t* buf = p_alloc(mount->pool, mount->frag_size); | |
// slog("group_num = %u, ino_offset=%u, addr = 0x%08x, offset = 0x%08x\n", group_num, group_ino_offset, frag_addr, frag_offset); | |
// slog("mount->superblock.iblkno = 0x%08x\n", mount->superblock.iblkno); | |
if (!ufs_read_frag(mount, buf, frag_addr)) | |
goto fail; | |
memcpy(inode, buf + frag_offset, 128); | |
fix_endian(inode->mode); | |
fix_endian(inode->nlink); | |
fix_endian(inode->uid); | |
fix_endian(inode->gid); | |
fix_endian(inode->size_hi); | |
fix_endian(inode->size); | |
fix_endian(inode->atime); | |
fix_endian(inode->mtime); | |
fix_endian(inode->ctime); | |
for (i = 0; i < 12; i++) | |
fix_endian(inode->direct[i]); | |
for (i = 0; i < 3; i++) | |
fix_endian(inode->indirect[i]); | |
fix_endian(inode->flags); | |
fix_endian(inode->blocks); | |
fix_endian(inode->gen); | |
p_free(buf); | |
return 1; | |
fail: | |
if (buf) | |
p_free(buf); | |
return 0; | |
} | |
static uint8_t ufs_read_level(ufs_t* mount, | |
ufs_inode_t* inode, | |
uint8_t* buf, | |
uint64_t* len, | |
uint32_t indirect_blockno, | |
uint32_t level) | |
{ | |
if (inode->size <= *len) | |
return 1; | |
uint32_t* table = p_alloc(mount->pool, mount->block_size); | |
uint8_t* block = NULL; | |
const uint32_t num_pointers = mount->block_size / 4; | |
uint32_t i; | |
if (!ufs_read_block(mount, (uint8_t*)table, indirect_blockno)) | |
goto fail; | |
// for (i=0; i<num_pointers; i++) | |
// slog("%u 0x%08x\n", i, ntohl(table[i])); | |
if (level == 1) | |
block = p_alloc(mount->pool, mount->block_size); | |
for (i = 0; (i < num_pointers) && (inode->size > *len); i++) { | |
const uint32_t blockno = ntohl(table[i]); | |
if (level == 1) { | |
// direct block | |
uint64_t chunk_size = inode->size - *len; | |
if (chunk_size > mount->block_size) chunk_size = mount->block_size; | |
// Which block are we reading, and at which byte-offset into it does our data exist | |
const uint32_t block_addr = (blockno / mount->frag_per_block) * mount->frag_per_block; | |
const uint32_t block_offset = (blockno - block_addr) * mount->frag_size; | |
slog("L%u: raw_blkno=0x%08x len=0x%08x blockno:0x%08x chunk_size=0x%08x\n", level - 1, blockno, (uint32_t)*len, block_addr, (uint32_t)chunk_size); | |
// If the chunk_size is a whole block, then we better be reading in a whole block | |
if (chunk_size == mount->block_size) { | |
// slog("block_offset = 0x%x\n", block_offset); | |
//assert(block_offset == 0); | |
} | |
if (!ufs_read_block(mount, block, block_addr)) | |
goto fail; | |
memcpy(buf + *len, block + block_offset, chunk_size); | |
(*len) += chunk_size; | |
} | |
else { | |
// indirect block | |
if (!ufs_read_level(mount, inode, buf, len, blockno, level - 1)) | |
goto fail; | |
} | |
} | |
if (block) | |
p_free(block); | |
p_free(table); | |
return 1; | |
fail: | |
if (block) | |
p_free(block); | |
p_free(table); | |
return 0; | |
} | |
static uint8_t* ufs_read_inode_data(ufs_t* mount, ufs_inode_t* inode) | |
{ | |
uint32_t i, j; | |
uint8_t* block = p_alloc(mount->pool, mount->block_size); | |
uint8_t* buf = p_alloc(mount->pool, inode->size); | |
uint64_t len = 0; | |
/* Read in direct blocks */ | |
for (i = 0; (i < 12) && (len < inode->size); i++) { | |
// How many bytes are we reading from this block? | |
uint64_t chunk_size = inode->size - len; | |
if (chunk_size > mount->block_size) chunk_size = mount->block_size; | |
// Which block are we reading, and at which byte-offset into it does our data exist | |
const uint32_t block_addr = (inode->direct[i] / mount->frag_per_block) * mount->frag_per_block; | |
const uint32_t block_offset = (inode->direct[i] - block_addr) * mount->frag_size; | |
// slog("block_addr=0x%08x, block_offset") | |
// If the chunk_size is a whole block, then we better be reading in a whole block | |
// if (chunk_size == mount->block_size) | |
// assert(block_offset == 0); | |
if (!ufs_read_block(mount, block, block_addr)) | |
goto fail; | |
memcpy(buf + len, block + block_offset, chunk_size); | |
len += chunk_size; | |
// slog("direct block %u = 0x%08x\n", i, inode->direct[i]); | |
} | |
for (i = 0; i < 3; i++) { | |
if (!ufs_read_level(mount, inode, buf, &len, inode->indirect[i], i + 1)) | |
goto fail; | |
} | |
p_free(block); | |
return buf; | |
fail: | |
p_free(block); | |
p_free(buf); | |
return NULL; | |
} | |
typedef struct PACK( { | |
uint32_t inum; | |
uint16_t len; | |
uint16_t namelen; | |
char name[1]; | |
} ) ufs_dir_t; | |
ufs_inode_t* ufs_traverse_path(ufs_t* mount, const char* _path) | |
{ | |
uint32_t i; | |
uint32_t inum = 2; // 2 == root | |
ufs_inode_t* inode = p_alloc(mount->pool, sizeof(ufs_inode_t)); | |
char* path = p_alloc(mount->pool, strlen(_path) + 1); | |
strcpy(path, _path); | |
if (!ufs_load_inode(mount, inode, inum)) | |
goto fail; | |
char* last, * elem; | |
for (elem = strtok(path, "/", &last); | |
elem; | |
elem = strtok(NULL, "/", &last)) { | |
uint32_t next_inum = 0; | |
uint8_t* dir = ufs_read_inode_data(mount, inode); | |
if (!dir) | |
goto fail; | |
for (i = 0; inode->size; ) { | |
ufs_dir_t* entry = (ufs_dir_t*)&dir[i]; | |
fix_endian(entry->inum); | |
fix_endian(entry->len); | |
fix_endian(entry->namelen); | |
if (entry->inum == 0) | |
break; | |
if ((entry->namelen == strlen(elem)) && | |
(strncmp(elem, entry->name, entry->namelen) == 0)) { | |
next_inum = entry->inum; | |
break; | |
} | |
i += entry->len; | |
} | |
p_free(dir); | |
if (next_inum == 0) { | |
sprintf(mount->error_str, "'%s' in '%s' doesn't exist", elem, _path); | |
goto fail; | |
} | |
inum = next_inum; | |
if (!ufs_load_inode(mount, inode, inum)) | |
goto fail; | |
} | |
p_free(path); | |
return inode; | |
fail: | |
p_free(inode); | |
p_free(path); | |
return NULL; | |
} | |
static ufs_t* ufs_mount(partition_t* part) | |
{ | |
ufs_t* mount = p_alloc(part->pool, sizeof(ufs_t)); | |
uint8_t* buf = p_alloc(part->pool, 32 * 512); | |
uint32_t i; | |
mount->pool = part->pool; | |
mount->part = part; | |
mount->error_str = part->error_str; | |
for (i = 0; i < 4; i++) | |
part_get_block(part, &buf[i * 512], 16 + i); | |
memcpy(&mount->superblock, buf, sizeof(ufs_superblock_t)); | |
fix_endian(mount->superblock.link); | |
fix_endian(mount->superblock.rlink); | |
fix_endian(mount->superblock.sblkno); | |
fix_endian(mount->superblock.cblkno); | |
fix_endian(mount->superblock.iblkno); | |
fix_endian(mount->superblock.dblkno); | |
fix_endian(mount->superblock.cgoffset); | |
fix_endian(mount->superblock.cgmask); | |
fix_endian(mount->superblock.time); | |
fix_endian(mount->superblock.size); | |
fix_endian(mount->superblock.dsize); | |
fix_endian(mount->superblock.ncg); | |
fix_endian(mount->superblock.bsize); | |
fix_endian(mount->superblock.fsize); | |
fix_endian(mount->superblock.frag); | |
fix_endian(mount->superblock.minfree); | |
fix_endian(mount->superblock.rotdelay); | |
fix_endian(mount->superblock.rps); | |
fix_endian(mount->superblock.bmask); | |
fix_endian(mount->superblock.fmask); | |
fix_endian(mount->superblock.bshift); | |
fix_endian(mount->superblock.fshift); | |
fix_endian(mount->superblock.maxcontig); | |
fix_endian(mount->superblock.maxbpg); | |
fix_endian(mount->superblock.fragshift); | |
fix_endian(mount->superblock.fsbtodb); | |
fix_endian(mount->superblock.sbsize); | |
fix_endian(mount->superblock.csmask); | |
fix_endian(mount->superblock.csshift); | |
fix_endian(mount->superblock.nindir); | |
fix_endian(mount->superblock.inopb); | |
fix_endian(mount->superblock.nspf); | |
fix_endian(mount->superblock.optim); | |
fix_endian(mount->superblock.state); | |
fix_endian(mount->superblock.id[0]); | |
fix_endian(mount->superblock.id[1]); | |
fix_endian(mount->superblock.csaddr); | |
fix_endian(mount->superblock.cssize); | |
fix_endian(mount->superblock.cgsize); | |
fix_endian(mount->superblock.ntrak); | |
fix_endian(mount->superblock.nsect); | |
fix_endian(mount->superblock.spc); | |
fix_endian(mount->superblock.ncyl); | |
fix_endian(mount->superblock.cpg); | |
fix_endian(mount->superblock.ipg); | |
fix_endian(mount->superblock.fpg); | |
fix_endian(mount->superblock.csum_ndir); | |
fix_endian(mount->superblock.csum_nbfree); | |
fix_endian(mount->superblock.csum_nifree); | |
fix_endian(mount->superblock.csum_nffree); | |
fix_endian(mount->superblock.cgrotor); | |
fix_endian(mount->superblock.cpc); | |
for (i = 0; i < (32 * 8); i++) | |
fix_endian(mount->superblock.postbl[i / 8][i % 8]); | |
fix_endian(mount->superblock.magic); | |
if (mount->superblock.magic != 0x00011954) { | |
sprintf(part->error_str, "Magic doesn't match ufs"); | |
goto fail; | |
} | |
// It is UFS! | |
mount->frag_size = mount->superblock.fsize; | |
mount->frag_per_block = mount->superblock.frag; | |
mount->block_size = mount->frag_size * mount->frag_per_block; | |
//assert(mount->block_size == mount->superblock.bsize); | |
mount->num_groups = mount->superblock.ncg; | |
mount->groups = (ufs_cylinder_group_t*)p_alloc(mount->pool, | |
mount->num_groups * sizeof(ufs_cylinder_group_t)); | |
for (i = 0; i < mount->num_groups; i++) { | |
uint32_t group_base = ufs_group_base(mount, i) + mount->superblock.cblkno; | |
ufs_load_cylinder_group(mount, group_base, &mount->groups[i]); | |
if ((mount->groups[i].cgx != i) || (mount->groups[i].magic != 0x00090255)) { | |
sprintf(mount->error_str, "bad cylinder group %u frag_offset=0x%x", i, group_base); | |
goto fail; | |
} | |
} | |
if (buf) | |
p_free(buf); | |
return mount; | |
fail: | |
if (mount) { | |
if (mount->groups) | |
p_free(mount->groups); | |
p_free(mount); | |
} | |
if (buf) | |
p_free(buf); | |
return NULL; | |
} | |
/* --- Public interfaces --- */ | |
#pragma mark Public interfaces | |
uint8_t* shoebill_extract_kernel(const char* disk_path, const char* kernel_path, char* error_str, uint32_t* len) | |
{ | |
uint8_t* pool_data, * kernel_data = NULL; | |
disk_t* disk; | |
svfs_t* svfs_mount_obj; | |
ufs_t* ufs_mount_obj; | |
int32_t apm_part_num; | |
strcpy(error_str, ""); | |
disk = open_disk(disk_path, error_str); | |
if (!disk) | |
goto done; | |
apm_part_num = find_root_partition_number(disk, 0); | |
if (apm_part_num == -1) { | |
sprintf(error_str, "Couldn't find root partition"); | |
goto done; | |
} | |
slog("apm_part_num = %u\n", apm_part_num); | |
#ifdef JASONzzzz | |
/// //////////////////////// | |
{ | |
svfs_mount_obj = svfs_mount(&disk->partitions[apm_part_num]); | |
for (int k = 2; k < 1000; k++) { | |
svfs_inode_t* inode = p_alloc(svfs_mount_obj->pool, sizeof(svfs_inode_t)); | |
svfs_load_inode(svfs_mount_obj, inode, k); | |
uint8_t* pool_data = svfs_read_inode_data(svfs_mount_obj, inode); | |
if (pool_data && inode->size > 0) | |
{ | |
char fname[64]; | |
sprintf(fname, "c:\\temp\\result\\result%d", k); | |
FILE* f = fopen(fname, "wb"); | |
printf("k %d has %d data\n", k, inode->size); | |
fwrite(pool_data, inode->size, 1, f); | |
fclose(f); | |
} | |
} | |
} | |
printf("\n"); | |
return 0; | |
/// //////////////////////// | |
#endif | |
svfs_mount_obj = svfs_mount(&disk->partitions[apm_part_num]); | |
if (svfs_mount_obj) { | |
svfs_inode_t* inode = svfs_traverse_path(svfs_mount_obj, kernel_path); | |
if (!inode) | |
goto done; | |
pool_data = svfs_read_inode_data(svfs_mount_obj, inode); | |
if (!pool_data) | |
goto done; | |
kernel_data = malloc(inode->size); | |
memcpy(kernel_data, pool_data, inode->size); | |
*len = inode->size; | |
goto done; | |
} | |
ufs_mount_obj = ufs_mount(&disk->partitions[apm_part_num]); | |
if (ufs_mount_obj) { | |
ufs_inode_t* inode = ufs_traverse_path(ufs_mount_obj, kernel_path); | |
if (!inode) | |
goto done; | |
pool_data = ufs_read_inode_data(ufs_mount_obj, inode); | |
if (!pool_data) | |
goto done; | |
kernel_data = malloc(inode->size); | |
memcpy(kernel_data, pool_data, inode->size); | |
*len = inode->size; | |
goto done; | |
} | |
sprintf(error_str, "I can read the partition map, but the filesystem doesn't seem to be UFS or SVFS"); | |
done: | |
if (strlen(error_str)) | |
slog("error: [%s]\n", error_str); | |
if (disk) | |
close_disk(disk); | |
return kernel_data; | |
} | |
int main (int argc, char **argv) | |
{ | |
uint8_t *buf; | |
uint32_t size; | |
char error_str[1024]; | |
//buf = shoebill_extract_kernel(argv[1], argv[2], error_str, &size); | |
buf = shoebill_extract_kernel("c:\\temp\\aux07\\aux_beta_compacted.img", "/sysV68", error_str, &size); | |
if (!buf) | |
return 0; | |
FILE *f = fopen("result", "wb"); | |
fwrite(buf, size, 1, f); | |
fclose(f); | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment