slmkfs/mkfs.c

702 lines
28 KiB
C

// This is NEW CODE by Zak to replace the old xv6 mkfs program.
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
// 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 <nbytes>] --blocks <count> --output <filename> [--allowempty] [--defaulttype <name>] [[--type <typestring>] [--arrayname <name>] [--filename <name>] resource1 [[--type <typestring>] [--arrayname <name>] [--filename <name>] 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);
}