702 lines
28 KiB
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);
|
||
|
}
|