// This is NEW CODE by Zak to replace the old xv6 mkfs program. #include #include #include #include #include #include // These are the similar to in the old mkfs program but will be slowly // replaced. The stat structure needs to be #defined away (or replaced // with some other name) because "stat" will likely be defined already // on whatever operating system mkfs is running on. #define stat kernel_stat #include "slkern/types.h" #include "slkern/fs.h" #include "slkern/stat.h" #include "slkern/param.h" #include "fsformat.h" typedef struct mkfs_job mkfs_job_t; struct mkfs_job { int device; int rootinodenumber; int fsversion; // Filesystem version, 0 for compatibility 1 is standard int blocksize; // The block size, currently 4096 is the standard int totalblocks; // Total number of blocks in the filesystem we're making int inodecount; // Number of inodes (i.e. maximum number of files/dirs) int specialblocks; // Should be 2, boot block and superblock at beginning int logblocks; // Size of the log area in blocks int inodeblocks; // Size of the inode area in blocks int bitmapblocks; // Size of the used/free bitmap in blocks int metablocks; // Total number of special/log/inode/bitmap blocks int datablocks; // Total number of data blocks (not including metadata) int block_nextfree; // Next free block to allocate int inode_nextfree; // Next free inode to allocate int inodesize; int inodesperblock; int inoderefsize; // Set to 2 for small fs or v1, set 4 for normal int filenamesize; // Set to 14 bytes for v0 or flexible for v1+ int directblocks; // Number of simple "direct" blocks of file data int indirectblocks; // Number of blocks-of-blocks for more file data // The superblock is configured using MKFS_DISKORDER* macros for byte order. fsformat_superblock_t* superblock; }; // The mkfs_io_* functions will need to be modified if mkfs is used inside the // kernel or in some other circumstances than running on Linux. Currently the // same options are used internally as for the old filesystem code. int mkfs_io_opendevice(mkfs_job_t* job, char* filename) { return open(filename, O_CREAT | O_RDWR | O_TRUNC, 0666); } int mkfs_io_closedevice(mkfs_job_t* job) { close(job->device); return 0; } // This is only used internally by the other IO functions and need not exist // on other device IO implementations. int mkfs_io_seekblock(mkfs_job_t* job, int blocknumber) { int offset = job->blocksize * blocknumber; if (lseek(job->device, offset, 0) == offset) { return 0; // Good, seek'ed to write offset } return -1; // Bad, seek failed } int mkfs_io_readblock(mkfs_job_t* job, int blocknumber, void* buffer) { if (mkfs_io_seekblock(job, blocknumber) != 0) { return -1; } else if (read(job->device, buffer, job->blocksize) != job->blocksize) { return -1; } return 0; } int mkfs_io_writeblock(mkfs_job_t* job, int blocknumber, void* buffer) { if (mkfs_io_seekblock(job, blocknumber) != 0) { return -1; } else if (write(job->device, buffer, job->blocksize) != job->blocksize) { return -1; } return 0; } // These should be changed if mkfs is ever ported to a big-endian system, but // otherwise can simply fall through: unsigned short mkfs_diskorder16(unsigned short x) { return x; } unsigned int mkfs_diskorder32(unsigned int x) { return x; } unsigned long long mkfs_diskorder64(unsigned long long x) { return x; } #define MKFS_DISKORDER16(x) mkfs_diskorder16(x) #define MKFS_DISKORDER32(x) mkfs_diskorder32(x) #define MKFS_DISKORDER64(x) mkfs_diskorder64(x) #define MKFS_INODEBLOCK(job, inodenumber) \ (((inodenumber) / job->inodesperblock) + MKFS_DISKORDER32(job->superblock->firstinodeblock)) #define MKFS_INODEINDEX(job, inodenumber) \ ((inodenumber) % job->inodesperblock) mkfs_job_t* mkfs_job_alloc(int fsversion, char* filename, int blocksize, int totalblocks, int logblocks, int inodecount, int directblocks, int indirectblocks, int inoderefsize, int filenamesize) { if (fsversion == 0) { // Check that inode/filename sizes match v0 or else fail. if (inoderefsize != 2 || filenamesize != 14) { return NULL; } } else { // Filesystems >= v1 support 2-byte (16-bit) or 4-byte (32-bit) refs if (inoderefsize != 2 && inoderefsize != 4) { return NULL; } } mkfs_job_t* result = malloc(sizeof(mkfs_job_t)); if (result) { result->fsversion = fsversion; result->blocksize = blocksize; result->totalblocks = totalblocks; result->device = mkfs_io_opendevice(result, filename); if (result->device < 0) { free(result); return NULL; } result->inoderefsize = inoderefsize; result->filenamesize = filenamesize; result->directblocks = directblocks; result->indirectblocks = indirectblocks; result->inodesize = 12 + ((directblocks+indirectblocks) * 4); result->inodesperblock = blocksize / result->inodesize; result->specialblocks = 2; result->logblocks = logblocks; result->inodeblocks = (inodecount / result->inodesperblock) + 1; // TODO: Stabilise inode structure and it's size result->bitmapblocks = (totalblocks / (blocksize*8)) + 1; result->metablocks = result->specialblocks + result->logblocks + result->inodeblocks + result->bitmapblocks; result->datablocks = result->totalblocks - result->metablocks; result->inodecount = inodecount; result->block_nextfree = result->metablocks; result->inode_nextfree = 1; result->superblock = malloc(sizeof(fsformat_superblock_t)); result->superblock->magic = MKFS_DISKORDER32(fsversion == 0 ? FSFORMAT_MAGIC_OLD : FSFORMAT_MAGIC_NEW); result->superblock->totalblocks = MKFS_DISKORDER32(result->totalblocks); result->superblock->datablocks = MKFS_DISKORDER32(result->datablocks); result->superblock->inodelimit = MKFS_DISKORDER32(result->inodecount); result->superblock->logblocks = MKFS_DISKORDER32(result->logblocks); result->superblock->firstlogblock = MKFS_DISKORDER32(result->specialblocks); result->superblock->firstinodeblock = MKFS_DISKORDER32(result->specialblocks + result->logblocks); result->superblock->firstbitmapblock = MKFS_DISKORDER32(result->specialblocks + result->logblocks + result->inodeblocks); // Set the extended flags only for version 1. if (fsversion >= 1) { result->superblock->extd_versionflags = MKFS_DISKORDER32(fsversion); result->superblock->extd_inodestructsize = MKFS_DISKORDER32((int) sizeof(fsformat_inode_t)); result->superblock->extd_blocksize = MKFS_DISKORDER32(blocksize); result->superblock->extd_superblocksize = MKFS_DISKORDER32((int) sizeof(fsformat_superblock_t)); result->superblock->extd_inoderefsize = MKFS_DISKORDER32(inoderefsize); result->superblock->extd_filenamesize = MKFS_DISKORDER32(filenamesize); result->superblock->extd_directblocks = MKFS_DISKORDER32(directblocks); result->superblock->extd_indirectblocks = MKFS_DISKORDER32(indirectblocks); } } return result; } unsigned char* mkfs_allocblockbuffer(mkfs_job_t* job) { return calloc(1, job->blocksize); } void mkfs_freeblockbuffer(mkfs_job_t* job, unsigned char* buffer) { free(buffer); } // Marks all blocks up to #blocksused as in-use void mkfs_markused(mkfs_job_t* job, int blocksused) { unsigned char* buffer = mkfs_allocblockbuffer(job); printf("mkfs_markused(job, %d)\n", blocksused); if (blocksused >= (job->blocksize * 8)) { fprintf(stderr, "mkfs_markused: This function only works for creating small disks that won't use more than 1 page of the free/used bitmap\n"); exit(-1); } // Should already be cleared by mkfs_allocblockbuffer: memset(buf, 0, job->blocksize) for (int blocknum = 0; blocknum < blocksused; blocknum++) { int idx = blocknum / 8; int mask = 1 << (blocknum % 8); buffer[idx] |= mask; } // NOTE: The old code did not seem to consistently use disk endian functions. printf("mkfs_markused: Writing bitmap block %d\n", MKFS_DISKORDER32(job->superblock->firstbitmapblock)); mkfs_io_writeblock(job, MKFS_DISKORDER32(job->superblock->firstbitmapblock), buffer); mkfs_freeblockbuffer(job, buffer); } void mkfs_readinode(mkfs_job_t* job, unsigned int inodenumber, fsformat_inode_t* inode) { unsigned int blocknumber = MKFS_INODEBLOCK(job, inodenumber); unsigned int inodeindex = MKFS_INODEINDEX(job, inodenumber); void* buffer = mkfs_allocblockbuffer(job); fsformat_inode_t* inodebuffer = (fsformat_inode_t*) buffer; fsformat_inode_t* diskcopy = inodebuffer + inodeindex; mkfs_io_readblock(job, blocknumber, buffer); memcpy(inode, diskcopy, sizeof(fsformat_inode_t)); mkfs_freeblockbuffer(job, buffer); } void mkfs_writeinode(mkfs_job_t* job, unsigned int inodenumber, fsformat_inode_t* inode) { unsigned int blocknumber = MKFS_INODEBLOCK(job, inodenumber); unsigned int inodeindex = MKFS_INODEINDEX(job, inodenumber); void* buffer = mkfs_allocblockbuffer(job); fsformat_inode_t* inodebuffer = (fsformat_inode_t*) buffer; fsformat_inode_t* diskcopy = inodebuffer + inodeindex; mkfs_io_readblock(job, blocknumber, buffer); memcpy(diskcopy, inode, sizeof(fsformat_inode_t)); mkfs_io_writeblock(job, blocknumber, buffer); mkfs_freeblockbuffer(job, buffer); } unsigned int mkfs_allocinode(mkfs_job_t* job, unsigned short inodetype) { fsformat_inode_t inode; unsigned int inodenumber = job->inode_nextfree; job->inode_nextfree++; // The mkfs program just allocates inodes sequentially. memset(&inode, 0, sizeof(fsformat_inode_t)); inode.type = MKFS_DISKORDER16(inodetype); // I guess the number of links would generally be 1 for anything created by // mkfs. inode.linkcount = MKFS_DISKORDER16(1); // Size is initially set to zero regardless of type. // Note that byte order won't matter for zero values but is used by convention inode.totalbytes = MKFS_DISKORDER32(0); mkfs_writeinode(job, inodenumber, &inode); return inodenumber; } #define MKFS_MIN(x,y) \ (((x) >= (y)) ? (y) : (x)) void mkfs_appenddata(mkfs_job_t* job, unsigned int inodenumber, void* data, int size) { unsigned char* buffer = mkfs_allocblockbuffer(job); unsigned char* bytedata = (unsigned char*) data; fsformat_inode_t inode; // Load the inode and the offset, these will be updated & saved at the end. mkfs_readinode(job, inodenumber, &inode); unsigned int offset = MKFS_DISKORDER32(inode.totalbytes); int countdown = size; while (countdown > 0) { unsigned int blocktableindex = offset / job->blocksize; unsigned int block; // TODO: Check blockpart < limit (MAXFILE? check how this is defined) if (blocktableindex < NDIRECT) { // The block is indexed directly block = MKFS_DISKORDER32(inode.addrs[blocktableindex]); if (block == 0) { // The data block doesn't exist yet, so allocate it. block = job->block_nextfree; job->block_nextfree++; inode.addrs[blocktableindex] = MKFS_DISKORDER32(block); } } else { // The block is NOT indexed directly, so get/create an indirect table. // This is stored at the end of the directly-indexed block numbers. unsigned int indirectblock; if (job->fsversion == 0) { // Version 0 stores large files as a block-of-block-indexes indirectblock = MKFS_DISKORDER32(inode.addrs[job->directblocks]); if (indirectblock == 0) { // The "indirect" page of pointers is not allocated yet, so allocate it. indirectblock = job->block_nextfree; job->block_nextfree++; inode.addrs[job->directblocks] = MKFS_DISKORDER32(indirectblock); } } else { // Version >= 1 uses an additional layer of indirection, so // offsets into large files are indexed by a table-o-tables. // This works like the regular system but with an additional // layer, with the "tableotables" being stored where the old // table would be (and the old being stored in the new table...) unsigned int tableotables = MKFS_DISKORDER32(inode.addrs[job->directblocks]); if (tableotables == 0) { // The "indirect" page of pointers is not allocated yet, so allocate it. tableotables = job->block_nextfree; job->block_nextfree++; inode.addrs[job->directblocks] = MKFS_DISKORDER32(tableotables); } void* ttbuffer = mkfs_allocblockbuffer(job); unsigned int* ttids = (unsigned int*) ttbuffer; mkfs_io_readblock(job, tableotables, ttbuffer); indirectblock = MKFS_DISKORDER32(ttids[(blocktableindex - job->directblocks) / (job->blocksize / 4)]); if (indirectblock == 0) { // The "indirect" page of pointers is not allocated yet, so allocate it. indirectblock = job->block_nextfree; job->block_nextfree++; ttids[(blocktableindex - job->directblocks) / (job->blocksize / 4)] = MKFS_DISKORDER32(indirectblock); mkfs_io_writeblock(job, tableotables, ttbuffer); } mkfs_freeblockbuffer(job, ttbuffer); printf("Used tableotables %d got indirectblock %d\n", tableotables, indirectblock); } void* indirectbuffer = mkfs_allocblockbuffer(job); unsigned int* indirectids = (unsigned int*) indirectbuffer; mkfs_io_readblock(job, indirectblock, indirectbuffer); block = MKFS_DISKORDER32(indirectids[(blocktableindex - job->directblocks) % (job->blocksize / 4)]); if (block == 0) { // The data block doesn't exist yet, so allocate it. block = job->block_nextfree; job->block_nextfree++; // And be sure to store the block index in the indirect table indirectids[(blocktableindex - job->directblocks) % (job->blocksize / 4)] = MKFS_DISKORDER32(block); mkfs_io_writeblock(job, indirectblock, indirectbuffer); } // The lookup is now complete so the buffer of indirect pointers is freed: mkfs_freeblockbuffer(job, indirectbuffer); printf("Final block %d\n", block); } // Find the size of a chunk remaining unsigned int chunksize = MKFS_MIN(((blocktableindex + 1) * job->blocksize) - offset, countdown); mkfs_io_readblock(job, block, buffer); bcopy(bytedata, buffer + offset - (blocktableindex * job->blocksize), chunksize); mkfs_io_writeblock(job, block, buffer); countdown -= chunksize; offset += chunksize; bytedata += chunksize; } // Save the inode and offset updated from those loaded at the start. inode.totalbytes = MKFS_DISKORDER32(offset); mkfs_writeinode(job, inodenumber, &inode); mkfs_freeblockbuffer(job, buffer); } // Creates a new directory entry structure for a given filename+inode combo and // appends it to the given directory (it's appended as though normal data). void mkfs_appenddirent(mkfs_job_t* job, unsigned int dirinodenumber, char* name, unsigned int newinodenumber) { if (job->fsversion == 0) { // Reserve a dirent structure and initialise the entire thing to zero. fsformat_dirent_v0_t entry; memset(&entry, 0, sizeof(fsformat_dirent_v0_t)); // Configure the fields in the dirent structure. entry.inodenumber = MKFS_DISKORDER16(newinodenumber); // TODO: This and some other fields should probably be extended to 32-bit or other/configurable sizes in future versions strncpy(entry.filename, name, FSFORMAT_NAMESIZE_OLD); // And finally append it to the directory as though it's normal file data. mkfs_appenddata(job, dirinodenumber, &entry, sizeof(fsformat_dirent_v0_t)); } else { // Reserve a dirent structure and initialise the entire thing to zero. fsformat_dirent_v1_t entry; memset(&entry, 0, sizeof(fsformat_dirent_v1_t)); // Configure the fields in the dirent structure. entry.datainode = MKFS_DISKORDER32(newinodenumber); // TODO: This and some other fields should probably be extended to 32-bit or other/configurable sizes in future versions entry.metainode = MKFS_DISKORDER32(0xFFFFFFFF); strncpy(entry.filename, name, FSFORMAT_NAMESIZE_NEW); // And finally append it to the directory as though it's normal file data. mkfs_appenddata(job, dirinodenumber, &entry, sizeof(fsformat_dirent_v1_t)); } } // Creates a new file inode and appends it as a directory entry to the given // directory. Note that the size of a new file begins at zero and data is added // afterwards with calls to mkfs_appenddata. This function returns the new inode // number after creating it and adding it to the directory. // TODO: Add another function to test adding subdirectories, which should // otherwise work similarly (see mkfs_newfs for code to create the root // directory, but obviously ".." would be set to parent...) unsigned int mkfs_newfile(mkfs_job_t* job, unsigned int dirinodenumber, char* name) { unsigned int result = mkfs_allocinode(job, T_FILE); mkfs_appenddirent(job, dirinodenumber, name, result); return result; } // Initialises a new filesystem, returning the inode number of the root // directory. This should perform the work of "mkfs" in a simple mkfs job, then // afterwards files/dirs can be appended. unsigned int mkfs_newfs(mkfs_job_t* job) { // NOTE: This code assumes that mkfs_allocbuffer returns zeroed memory like // the current implementation does. unsigned char* buffer = mkfs_allocblockbuffer(job); // Write zeroed blocks throughout while the buffer is filled with zeroes. for (int blocknumber = 0; blocknumber < job->totalblocks; blocknumber++) { mkfs_io_writeblock(job, blocknumber, buffer); } // The buffer should still be zeroed. memcpy(buffer, job->superblock, sizeof(fsformat_superblock_t)); mkfs_io_writeblock(job, 1, buffer); // Write the superblock at block #1 int result = mkfs_allocinode(job, T_DIR); // This becomes the root directory. // TODO: Check that result is ROOTINO and if there's any need to make that configurable mkfs_appenddirent(job, result, ".", result); mkfs_appenddirent(job, result, "..", result); job->rootinodenumber = result; // Store this for consistency so mkfs_finish is guaranteed to use the same value. return result; // Return the new root inode number. } // Finishes the workflow of the "mkfs" program: Finalises the root directory, // fills the free/used bitmap and closes the output. void mkfs_finish(mkfs_job_t* job) { // Adjust the root directory size to the nearest block boundary (I'm not sure // why this needs to be done but I may update comments when I find out...). // First load the root directory and get it's size value: fsformat_inode_t inode; mkfs_readinode(job, job->rootinodenumber, &inode); unsigned int rootdirsize = MKFS_DISKORDER32(inode.totalbytes); // Increment size to block boundary. NOTE: This is a conceptually clear but // mechanically expensive way to align to boundaries, it could be replaced // if dealing with extra-large blocks. while ((rootdirsize % job->blocksize) != 0) { rootdirsize++; } // Then set the size value and save the root directory again. inode.totalbytes = MKFS_DISKORDER32(rootdirsize); mkfs_writeinode(job, job->rootinodenumber, &inode); // Fill the free/used bitmap up to the number of blocks we've used. NOTE: This // assumes we've used blocks contiguously from the start of the disk and not // placed a few at the end or any other silly business! mkfs_markused(job, job->block_nextfree); // We're just about done here, time to knock off for smoko break. mkfs_io_closedevice(job); } int usage(int argc, char** argv, int argerr, const char* error) { FILE* o = error ? stderr : stdout; char* progname = argv[0]; fprintf(o, "This program '%s' creates a new filesystem image.\n", progname); fprintf(o, "It is designed to support new iterations on simple filesystem design.\n"); fprintf(o, "It supports a 'version 0' filesystem somewhat-compatible with xv6 (depending on block size etc.),\n"); fprintf(o, "and a 'version 1' filesystem for in-development versions. A true 'version 2' doesn't exist yet but\n"); fprintf(o, "will be based on stable iterations of version 1.\n"); fprintf(o, "USAGE:\n"); fprintf(o, " %s -v0|-v1 [--blocksize ] --blocks --output [--allowempty] [--defaulttype ] [[--type ] [--arrayname ] [--filename ] resource1 [[--type ] [--arrayname ] [--filename ] resource2 [...]]]\n\n", progname); fprintf(o, "EXAMPLES:\n"); fprintf(o, " %s -v0 --output testfs.img --blocksize 1024 --blocks 20000 --allow-empty", progname); return error == NULL ? 0 : -1; } #define MAX_RESOURCES 100 FILE* files[MAX_RESOURCES]; char* types[MAX_RESOURCES]; char* filenames[MAX_RESOURCES]; unsigned long sizes[MAX_RESOURCES]; #define BUFFERSIZE 1000 unsigned char buffer[BUFFERSIZE]; int main(int argc, char** argv) { char* defaulttype = "RESOURCE"; char* nexttype = NULL; char* nextfilename = NULL; int nresources = 0; int allowempty = 0; int skip_underscores = 0; int version = -1; int blocksize = -1; int blocks = -1; int logblocks = -1; int inodes = -1; char* outputname = NULL; fprintf(stderr, "SecureLang mkfs for slkern & xv6 filesystems\n"); int argi = 1; while (argi < argc) { if (!strcmp(argv[argi], "--usage")) { usage(argc, argv, argi, NULL); return 0; } else if (!strcmp(argv[argi], "--type")) { if (argi + 1 >= argc) { return usage(argc, argv, argi, "Missing argument after --type"); } argi++; nexttype = argv[argi]; } else if (!strcmp(argv[argi], "--filename")) { if (argi + 1 >= argc) { return usage(argc, argv, argi, "Missing argument after --filename"); } argi++; nextfilename = argv[argi]; } else if (!strcmp(argv[argi], "--output") || (argv[argi][0] == '-' && argv[argi][1] == 'o' && argv[argi][2] == 0)) { if (outputname != NULL) { usage(argc, argv, argi, "Can't specify output more than once"); return -1; } if (argi + 1 >= argc) { usage(argc, argv, argi, "Missing argument after --output"); return -1; } argi++; outputname = argv[argi]; } else if (argv[argi][0] == '-' && argv[argi][1] == 'o') { if (outputname != NULL) { usage(argc, argv, argi, "Can't specify output more than once"); return -1; } outputname = argv[argi]+2; } else if (!strcmp(argv[argi], "--allowempty")) { allowempty = 1; } else if (!strcmp(argv[argi], "-v0")) { if (version != -1) { return usage(argc, argv, argi, "Version number has already been set"); } version = 0; } else if (!strcmp(argv[argi], "-v1")) { if (version != -1) { return usage(argc, argv, argi, "Version number has already been set"); } version = 1; } else if (!strcmp(argv[argi], "-v2")) { return usage(argc, argv, argi, "There is no -v2 format (yet...), this is an early version"); } else if (!strcmp(argv[argi], "--skip_underscores")) { skip_underscores = 1; } else if (!strcmp(argv[argi], "--blocksize")) { if (argi + 1 >= argc) { return usage(argc, argv, argi, "Missing argument after --blocksize"); } else if (blocksize != -1) { return usage(argc, argv, argi, "Block size has already been set"); } else { argi++; blocksize = atoi(argv[argi]); } } else if (!strcmp(argv[argi], "--blocks")) { if (argi + 1 >= argc) { return usage(argc, argv, argi, "Missing argument after --blocks"); } else if (blocks != -1) { return usage(argc, argv, argi, "Filesystem size has already been set"); } else { argi++; blocks = atoi(argv[argi]); } } else if (!strcmp(argv[argi], "--logblocks")) { if (argi + 1 >= argc) { return usage(argc, argv, argi, "Missing argument after --logblocks"); } else if (logblocks != -1) { return usage(argc, argv, argi, "Log size has already been set"); } else { argi++; blocks = atoi(argv[argi]); } } else if (!strcmp(argv[argi], "--inodes")) { if (argi + 1 >= argc) { return usage(argc, argv, argi, "Missing argument after --inodes"); } else if (inodes != -1) { } else { argi++; blocks = atoi(argv[argi]); } } else { if (nresources + 1 >= MAX_RESOURCES) { return usage(argc, argv, argi, "Too many resources"); } files[nresources] = fopen(argv[argi], "rb"); if (nexttype) { types[nresources] = nexttype; nexttype = NULL; } else { types[nresources] = defaulttype; } if (nextfilename) { filenames[nresources] = nextfilename; nextfilename = NULL; } else { char* lastsegment = argv[argi]; char* s = lastsegment; while (*s) { if (*s == '/' || *s == '\\' || *s == ':' || (skip_underscores && *s == '_')) { lastsegment = s+1; } s++; } filenames[nresources] = lastsegment; } nresources++; } argi++; } if (version < 0) { return usage(argc, argv, argi, "Expected filesystem version to be defined, e.g. -v0"); } else if (blocks < 0) { return usage(argc, argv, argi, "Expected filesystem size (or number of blocks) to be defined, e.g. --blocks 20000"); } else if (outputname == NULL) { return usage(argc, argv, argi, "Expected output name to be defined, e.g. --output testfs.img"); } else if (nresources < 1 && !allowempty) { return usage(argc, argv, argi, "Expected some resources to add or --allowempty to create an empty filesystem"); } // Apply the defaults from the old xv6 filesystem & mkfs code or new defaults // if optional settings aren't applied. if (blocksize == -1) { if (version == 0) { fprintf(stderr, "Using default block size of 1024 for xv6 compatibility, but the default may be changed (e.g. 4096) as long as it matches the kernel's BSIZE"); blocksize = 1024; } else { blocksize = 4096; } } if (logblocks == -1) { logblocks = 30; // 10 possible pages in a filesystem write * 3 } if (inodes == -1) { inodes = 200; // 200 files or directories, including special files etc. } printf("Filesystem parameters: block size: %d, number of blocks: %d, log blocks: %d, inodes: %d\n", blocksize, blocks, logblocks, inodes); // Allocate and initialise the job structure with the appropriate sizings. mkfs_job_t* job = mkfs_job_alloc(version, outputname, blocksize, blocks, logblocks, inodes, 12, 1, 2, 14); // Then create the new filesystem resulting in an inode for the root directory int rootdirectory = mkfs_newfs(job); // Then add the files ("resources") to the filesystem for (int resi = 0; resi < nresources; resi++) { if (!strcmp(types[resi], defaulttype)) { fprintf(stderr, "NOTE: Type name '%s' is ignored by current versions of mkfs but may be used as a hint in the future.\n", types[resi]); } FILE* f = files[resi]; unsigned long sz = 0; size_t nread; // Create the file in the new filesyste, returning it's inode. int fileinode = mkfs_newfile(job, rootdirectory, filenames[resi]); // Read from the existing file and append to the file in the new filesystem, // this happens in arbitrary blocks based on whatever size is reserved for // the buffer (which shouldn't impact the output just the number of cycles). while ((nread = fread(buffer, 1, BUFFERSIZE, f)) > 0) { /*int nwritten = */ mkfs_appenddata(job, fileinode, buffer, nread); //if (nwritten != nread) { // fprintf(stderr, "Error while appending to file '%s' (possibly out of space or beyond per-file size limit etc.)\n", filenames[resi]); // exit(-1); //} sz += (unsigned long) nread; } fprintf(stderr, "Added %ld bytes in '%s'\n", sz, filenames[resi]); sizes[resi] = sz; // Note, this is unnecessary except if you want to print totals etc. fclose(f); } // Finalise & save any filesystem information. mkfs_finish(job); exit(0); }