Initial git commit of my new mkfs program. This commit is untested as some refactoring was done (will expect kernel headers in a slkern directory...)
This commit is contained in:
parent
99ffb80a16
commit
d202ee3ce1
107
fsformat.h
Normal file
107
fsformat.h
Normal file
@ -0,0 +1,107 @@
|
||||
// This is NEW CODE to replace old fs.h header, defining on-disk filesystem
|
||||
// layout used by mkfs and the kernel.
|
||||
#ifndef _FSFORMAT_H
|
||||
#define _FSFORMAT_H
|
||||
|
||||
typedef unsigned int fsformat_uint32_t;
|
||||
typedef long long fsformat_int64_t;
|
||||
typedef unsigned short fsformat_uint16_t;
|
||||
typedef short fsformat_int16_t;
|
||||
|
||||
typedef struct fsformat_superblock fsformat_superblock_t;
|
||||
typedef struct fsformat_inode fsformat_inode_t;
|
||||
typedef struct fsformat_dirent_v0 fsformat_dirent_v0_t;
|
||||
typedef struct fsformat_dirent_v1 fsformat_dirent_v1_t;
|
||||
|
||||
// The "old" magic number is used to mark "version 0" or xv6-compatible
|
||||
// filysystems.
|
||||
#define FSFORMAT_MAGIC_OLD 0x10203040
|
||||
// The "new" magic number is used to mark filesystems which are above
|
||||
// or equal to "version 1". These share the same superblock structure
|
||||
// except with extended fields and options.
|
||||
#define FSFORMAT_MAGIC_NEW 0x102030F0
|
||||
|
||||
struct fsformat_superblock {
|
||||
// The first eight fields are compatible with xv6 filesystem (provided
|
||||
// that the block size etc. match your fork of xv6!)
|
||||
|
||||
fsformat_uint32_t magic;
|
||||
fsformat_uint32_t totalblocks;
|
||||
fsformat_uint32_t datablocks;
|
||||
fsformat_uint32_t inodelimit;
|
||||
fsformat_uint32_t logblocks;
|
||||
fsformat_uint32_t firstlogblock;
|
||||
fsformat_uint32_t firstinodeblock;
|
||||
fsformat_uint32_t firstbitmapblock;
|
||||
|
||||
// The rest of the fields will only be usable if the "magic" field is
|
||||
// equal to FSFORMAT_MAGIC_NEW. These are partly used to detect or
|
||||
// confirm filesystem settings such as block size.
|
||||
|
||||
// Simple version number & FLAGS. The lower 8 bits is used for
|
||||
// the simple version (e.g. 1 for version 1), the upper 24 bits may
|
||||
// be used for additional flags, minor versions etc. in later versions
|
||||
// but are set to zero for version 1.
|
||||
fsformat_uint32_t extd_versionflags;
|
||||
// Block size in bytes.
|
||||
fsformat_uint32_t extd_blocksize;
|
||||
// Superblock structure size in bytes (i.e. to detect which extended
|
||||
// fields are usable.
|
||||
fsformat_uint32_t extd_superblocksize;
|
||||
// Inode structure size.
|
||||
fsformat_uint32_t extd_inodestructsize;
|
||||
// Filename length.
|
||||
fsformat_uint32_t extd_filenamesize;
|
||||
// Inode reference size. Either set to 2 or 4, or special numbers for
|
||||
// other special formats in the future.
|
||||
fsformat_uint32_t extd_inoderefsize;
|
||||
|
||||
// Number of direct blocks per inode for storing data in the first
|
||||
// few blocks of a file.
|
||||
fsformat_uint32_t extd_directblocks;
|
||||
// Number of indirect blocks per inode for storing additional data of
|
||||
// large files
|
||||
fsformat_uint32_t extd_indirectblocks;
|
||||
};
|
||||
|
||||
#define FSFORMAT_DIRECTBLOCKS 12
|
||||
|
||||
struct fsformat_inode {
|
||||
fsformat_int16_t type;
|
||||
fsformat_int16_t device_major;
|
||||
fsformat_int16_t device_minor;
|
||||
fsformat_int16_t linkcount;
|
||||
fsformat_uint32_t totalbytes;
|
||||
fsformat_uint32_t addrs[FSFORMAT_DIRECTBLOCKS + 1];
|
||||
};
|
||||
|
||||
#define FSFORMAT_NAMESIZE_OLD 14
|
||||
|
||||
struct fsformat_dirent_v0 {
|
||||
fsformat_uint16_t inodenumber;
|
||||
char filename[FSFORMAT_NAMESIZE_OLD];
|
||||
};
|
||||
|
||||
#define FSFORMAT_NAMESIZE_NEW 120
|
||||
|
||||
struct fsformat_dirent_v1 {
|
||||
fsformat_uint32_t datainode;
|
||||
fsformat_uint32_t metainode;
|
||||
char filename[FSFORMAT_NAMESIZE_NEW];
|
||||
};
|
||||
|
||||
|
||||
// The "big" dirent structure is used for conveying dirent information
|
||||
// to userland, and so allows exceptionally large name sizes and dir
|
||||
// info. This isn't really implemented yet but can be for consistency.
|
||||
#define FSFORMAT_NAMESIZE_BIG 2032
|
||||
|
||||
struct fsformat_dirent_big {
|
||||
fsformat_int64_t datainode;
|
||||
fsformat_int64_t metainode;
|
||||
char filename[FSFORMAT_NAMESIZE_BIG];
|
||||
};
|
||||
|
||||
// From ifndef at top of file:
|
||||
#endif
|
||||
|
701
mkfs.c
Normal file
701
mkfs.c
Normal file
@ -0,0 +1,701 @@
|
||||
// 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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user