/* Copyright (C) 2015-2019 The University of Notre Dame This software is distributed under the GNU General Public License. See the file LICENSE for details. */ #include "kernel/types.h" #include "kernel/error.h" #include "kmalloc.h" #include "diskfs.h" #include "string.h" #include "fs.h" #include "fs_internal.h" #include "bcache.h" #include "page.h" /* Read or write a block from the raw device, starting from zero. */ static int diskfs_block_read(struct device *d, struct diskfs_block *b, uint32_t blockno ) { return bcache_read(d, b->data, 1, blockno) ? DISKFS_BLOCK_SIZE : -1; } static int diskfs_block_write(struct device *d, struct diskfs_block *b, uint32_t blockno ) { return bcache_write(d, b->data, 1, blockno) ? DISKFS_BLOCK_SIZE : -1; } /* Read or write a bitmap block, starting from the bitmap offset. */ static int diskfs_bitmap_block_read(struct fs_volume *v, struct diskfs_block *b, uint32_t blockno ) { if(blockno>=v->disk.bitmap_blocks) return KERROR_OUT_OF_SPACE; return diskfs_block_read(v->device,b,v->disk.bitmap_start+blockno); } static int diskfs_bitmap_block_write(struct fs_volume *v, struct diskfs_block *b, uint32_t blockno ) { if(blockno>=v->disk.bitmap_blocks) return KERROR_OUT_OF_SPACE; return diskfs_block_write(v->device,b,v->disk.bitmap_start+blockno); } /* Read or write an inode block, starting from the inode block offset. */ static int diskfs_inode_block_read(struct fs_volume *v, struct diskfs_block *b, uint32_t blockno ) { if(blockno>=v->disk.inode_blocks) return KERROR_OUT_OF_SPACE; return diskfs_block_read(v->device,b,v->disk.inode_start+blockno); } static int diskfs_inode_block_write(struct fs_volume *v, struct diskfs_block *b, uint32_t blockno ) { if(blockno>=v->disk.inode_blocks) return KERROR_OUT_OF_SPACE; return diskfs_block_write(v->device,b,v->disk.inode_start+blockno); } /* Read or write a data block, starting from the data block offset. */ static int diskfs_data_block_read(struct fs_volume *v, struct diskfs_block *b, uint32_t blockno ) { if(blockno>=v->disk.data_blocks) return KERROR_OUT_OF_SPACE; return diskfs_block_read(v->device,b,v->disk.data_start+blockno); } static int diskfs_data_block_write(struct fs_volume *v, struct diskfs_block *b, uint32_t blockno ) { if(blockno>=v->disk.data_blocks) return KERROR_OUT_OF_SPACE; return diskfs_block_write(v->device,b,v->disk.data_start+blockno); } /* Allocate a new data block by scanning the bitmap. If available, return the block number. If nothing available, return zero. */ static uint32_t diskfs_data_block_alloc( struct fs_volume *v ) { struct diskfs_block *b = page_alloc(0); struct diskfs_superblock *s= &v->disk; int i, j, k; for(i=0;ibitmap_blocks;i++) { diskfs_bitmap_block_read(v,b,i); for(j=0;jdata[j]!=0xff) { for(k=0;k<8;k++) { if(!((1<data[j])) { int blockno = i*DISKFS_BLOCK_SIZE+j*8+k; // Never allocate block zero; if(blockno==0) continue; // Do not exceet the actual number of blocks if(blockno>=v->disk.data_blocks) break; b->data[j] |= 1<data[bitmap_byte] &= ~(1<disk.inode_blocks;i++) { diskfs_inode_block_read(v,b,i); for(j=0;jinodes[j].inuse) { int inumber = i * DISKFS_INODES_PER_BLOCK + j; b->inodes[j].inuse = 1; diskfs_inode_block_write(v,b,i); page_free(b); return inumber; } } } printf("diskfs: warning: out of inodes!\n"); page_free(b); return 0; } static void diskfs_inumber_free( struct fs_volume *v, int inumber ) { int inode_block = inumber / DISKFS_INODES_PER_BLOCK; struct diskfs_block *b = page_alloc(0); diskfs_inode_block_read(v,b,inode_block); b->inodes[inumber%DISKFS_INODES_PER_BLOCK].inuse = 0; diskfs_inode_block_write(v,b,inode_block); page_free(b); } int diskfs_inode_load( struct fs_volume *v, int inumber, struct diskfs_inode *inode ) { struct diskfs_block *b = page_alloc(0); int inode_block = inumber / DISKFS_INODES_PER_BLOCK; int inode_position = inumber % DISKFS_INODES_PER_BLOCK; diskfs_inode_block_read(v,b,inode_block); memcpy(inode,&b->inodes[inode_position],sizeof(*inode)); page_free(b); return 1; } int diskfs_inode_save( struct fs_volume *v, int inumber, struct diskfs_inode *inode ) { struct diskfs_block *b = page_alloc(0); int inode_block = inumber / DISKFS_INODES_PER_BLOCK; int inode_position = inumber % DISKFS_INODES_PER_BLOCK; diskfs_inode_block_read(v,b,inode_block); memcpy(&b->inodes[inode_position],inode,sizeof(*inode)); diskfs_inode_block_write(v,b,inode_block); page_free(b); return 1; } int diskfs_inode_read( struct fs_dirent *d, struct diskfs_block *b, uint32_t block ) { int actual; if(blockdisk.direct[block]; } else { diskfs_data_block_read(d->volume,b,d->disk.indirect); actual = b->pointers[block-DISKFS_DIRECT_POINTERS]; } return diskfs_data_block_read(d->volume,b,actual); } int diskfs_inode_write( struct fs_dirent *d, struct diskfs_block *b, uint32_t block ) { int actual; struct diskfs_inode *i = &d->disk; if(blockdirect[block]; if(actual==0) { actual = diskfs_data_block_alloc(d->volume); if(actual==0) return KERROR_OUT_OF_SPACE; i->direct[block] = actual; diskfs_inode_save(d->volume,d->inumber,i); } } else { struct diskfs_block *iblock = page_alloc(0); if(i->indirect==0) { actual = diskfs_data_block_alloc(d->volume); if(actual==0) { page_free(iblock); return KERROR_OUT_OF_SPACE; } i->indirect = actual; diskfs_inode_save(d->volume,d->inumber,i); memset(iblock,0,DISKFS_BLOCK_SIZE); diskfs_data_block_write(d->volume,iblock,i->indirect); } diskfs_data_block_read(d->volume,iblock,i->indirect); actual = iblock->pointers[block-DISKFS_DIRECT_POINTERS]; if(actual==0) { actual = diskfs_data_block_alloc(d->volume); if(actual==0) { page_free(iblock); return KERROR_OUT_OF_SPACE; } iblock->pointers[block-DISKFS_DIRECT_POINTERS] = actual; diskfs_data_block_write(d->volume,iblock,i->indirect); } page_free(iblock); } return diskfs_data_block_write(d->volume,b,actual); } struct fs_dirent * diskfs_dirent_create( struct fs_volume *volume, int inumber, int type ) { struct fs_dirent *d = kmalloc(sizeof(*d)); memset(d,0,sizeof(*d)); diskfs_inode_load(volume,inumber,&d->disk); d->volume = volume; d->size = d->disk.size; d->inumber = inumber; d->refcount = 1; d->isdir = type==DISKFS_ITEM_DIR; return d; } int diskfs_dirent_close( struct fs_dirent *d ) { // XXX check if inode dirty first diskfs_inode_save(d->volume,d->inumber,&d->disk); return 0; } /* Returns true if two strings a and b (with lengths) have the same contents. Note that diskfs_item.name is not null-terminated but has diskfs_item.name_length characters. When comparing to a null-terminated string, we must check the length first and then the bytes of the string. */ static int diskfs_name_equals( const char *a, int alength, const char *b, int blength ) { return alength==blength && !strncmp(a,b,alength); } struct fs_dirent * diskfs_dirent_lookup( struct fs_dirent *d, const char *name ) { struct diskfs_block *b = page_alloc(0); int i, j; int nblocks = d->size / DISKFS_BLOCK_SIZE; if(d->size%DISKFS_BLOCK_SIZE) nblocks++; int name_length = strlen(name); for(i=0;iitems[j]; if(r->type!=DISKFS_ITEM_BLANK && diskfs_name_equals(name,name_length,r->name,r->name_length)) { int inumber = r->inumber; page_free(b); return diskfs_dirent_create(d->volume,inumber,r->type); } } } page_free(b); return 0; } int diskfs_dirent_list( struct fs_dirent *d, char *buffer, int length ) { struct diskfs_block *b = page_alloc(0); int nblocks = d->size / DISKFS_BLOCK_SIZE; if(d->size%DISKFS_BLOCK_SIZE) nblocks++; int i,j; int total = 0; for(i=0;iitems[j]; switch(r->type) { case DISKFS_ITEM_FILE: case DISKFS_ITEM_DIR: memcpy(buffer,r->name,r->name_length); buffer[r->name_length] = 0; buffer += r->name_length + 1; length -= r->name_length + 1; total += r->name_length + 1; break; case DISKFS_ITEM_BLANK: break; } } } page_free(b); return total; } int diskfs_dirent_resize( struct fs_dirent *d, uint32_t size ) { d->size = d->disk.size = size; return 0; } static int diskfs_dirent_add( struct fs_dirent *d, const char *name, int type, int inumber ) { struct diskfs_block *b = page_alloc(0); int i, j; int nblocks = d->size / DISKFS_BLOCK_SIZE; if(d->size%DISKFS_BLOCK_SIZE) nblocks++; for(i=0;iitems[j]; if(r->type==DISKFS_ITEM_BLANK) { r->type = type; r->inumber = inumber; r->name_length = strlen(name); memcpy(r->name,name,r->name_length); /* Save the modified data block. */ diskfs_inode_write(d,b,i); /* If this increased the logical size, update that too. */ uint32_t newsize = (i*DISKFS_BLOCK_SIZE) + (j+1)*sizeof(struct diskfs_item); if(newsize>d->size) { diskfs_dirent_resize(d,newsize); diskfs_inode_save(d->volume,d->inumber,&d->disk); } page_free(b); return 0; } } } memset(b->data,0,DISKFS_BLOCK_SIZE); struct diskfs_item *r = &b->items[0]; r->inumber = inumber; r->type = type; r->name_length = strlen(name); memcpy(r->name,name,r->name_length); diskfs_dirent_resize(d,d->size+sizeof(*r)); diskfs_inode_write(d,b,i); diskfs_inode_save(d->volume,d->inumber,&d->disk); page_free(b); return 0; } struct fs_dirent * diskfs_dirent_create_file_or_dir( struct fs_dirent *d, const char *name, int type ) { if(strlen(name)>DISKFS_NAME_MAX) return 0; // KERROR_NAME_TOO_LONG struct fs_dirent *t = diskfs_dirent_lookup(d,name); if(t) { diskfs_dirent_close(t); return 0; } int inumber = diskfs_inumber_alloc(d->volume); if(inumber==0) { return 0; // KERROR_OUT_OF_SPACE } struct diskfs_inode inode; memset(&inode,0,sizeof(inode)); inode.inuse = 1; inode.size = 0; diskfs_inode_save(d->volume,inumber,&inode); diskfs_dirent_add(d,name,type,inumber); return diskfs_dirent_create(d->volume,inumber,type); } struct fs_dirent * diskfs_dirent_create_file( struct fs_dirent *d, const char *name ) { return diskfs_dirent_create_file_or_dir(d,name,DISKFS_ITEM_FILE); } struct fs_dirent * diskfs_dirent_create_dir( struct fs_dirent *d, const char *name ) { return diskfs_dirent_create_file_or_dir(d,name,DISKFS_ITEM_DIR); } void diskfs_inode_delete( struct fs_volume *v, struct diskfs_inode *node, int inumber ) { int size = 0; int i; // XXX check for errors in here for(i=0;idirect[i]); size += v->block_size; if(size>=node->size) break; } if(sizesize) { struct diskfs_block *b = page_alloc(0); diskfs_data_block_read(v,b,node->indirect); for(i=0;ipointers[i]); size += v->block_size; if(size>=node->size) break; } page_free(b); } memset(node,sizeof(*node),0); diskfs_inode_save(v,inumber,node); diskfs_inumber_free(v,inumber); } int diskfs_dirent_remove( struct fs_dirent *d, const char *name ) { struct diskfs_block *b = page_alloc(0); int name_length = strlen(name); int i, j; int nblocks = d->size / DISKFS_BLOCK_SIZE; if(d->size%DISKFS_BLOCK_SIZE) nblocks++; for(i=0;iitems[j]; if(r->type!=DISKFS_ITEM_BLANK && r->name_length==name_length && diskfs_name_equals(name,name_length,r->name,r->name_length)) { if(r->type==DISKFS_ITEM_DIR) { struct diskfs_inode inode; diskfs_inode_load(d->volume,r->inumber,&inode); if(inode.size>0) { page_free(b); return KERROR_NOT_EMPTY; } } int inumber = r->inumber; r->type = DISKFS_ITEM_BLANK; diskfs_inode_write(d,b,i); diskfs_inode_delete(d->volume,&d->disk,inumber); page_free(b); return 0; } } } return KERROR_NOT_FOUND; } int diskfs_dirent_write_block( struct fs_dirent *d, const char *data, uint32_t blockno ) { return diskfs_inode_write(d,(void*)data,blockno); } int diskfs_dirent_read_block( struct fs_dirent *d, char *data, uint32_t blockno ) { return diskfs_inode_read(d,(void*)data,blockno); } extern struct fs disk_fs; struct fs_volume * diskfs_volume_open( struct device *device ) { struct diskfs_block *b = page_alloc(0); printf("diskfs: opening device %s unit %d\n",device_name(device),device_unit(device)); diskfs_block_read(device,b,0); struct diskfs_superblock *sb = &b->superblock; if(sb->magic!=DISKFS_MAGIC) { printf("diskfs: no filesystem found!\n"); page_free(b); return 0; } struct fs_volume *v = kmalloc(sizeof(*v)); v->fs = &disk_fs; v->device = device; v->block_size = device_block_size(device); v->refcount = 1; v->disk = *sb; page_free(b); printf("diskfs: %d bitmap blocks, %d inode blocks, %d data blocks\n", v->disk.bitmap_blocks, v->disk.inode_blocks, v->disk.data_blocks); return v; } struct fs_dirent * diskfs_volume_root( struct fs_volume *v ) { return diskfs_dirent_create(v,0,DISKFS_ITEM_DIR); } int diskfs_volume_close( struct fs_volume *v ) { return 0; } int diskfs_volume_format( struct device *device ) { struct diskfs_block *b = page_alloc(1); struct diskfs_superblock sb; int nblocks = device_nblocks(device); printf("diskfs: formatting device %s unit %d\n",device_name(device),device_unit(device)); sb.magic = DISKFS_MAGIC; sb.block_size = DISKFS_BLOCK_SIZE; sb.inode_blocks = 1024 / sizeof(struct diskfs_inode); int remaining_blocks = nblocks - sb.inode_blocks; sb.bitmap_blocks = 1 + remaining_blocks / (DISKFS_BLOCK_SIZE*8); sb.data_blocks = remaining_blocks - sb.bitmap_blocks; sb.inode_start = 1; sb.bitmap_start = sb.inode_start + sb.inode_blocks; sb.data_start = sb.bitmap_start + sb.bitmap_blocks; printf("diskfs: %d inode blocks, %d bitmap blocks, %d data blocks\n", sb.inode_blocks, sb.bitmap_blocks, sb.data_blocks ); memset(b,0,DISKFS_BLOCK_SIZE); b->superblock = sb; printf("diskfs: writing superblock\n"); diskfs_block_write(device,b,0); memset(b,0,DISKFS_BLOCK_SIZE); int i; printf("diskfs: writing %d inode blocks\n",sb.inode_blocks); for(i=sb.inode_blocks-1;i>=0;i--) { diskfs_block_write(device,b,sb.inode_start+i); } printf("diskfs: writing %d bitmap blocks\n",sb.bitmap_blocks); for(i=sb.bitmap_blocks-1;i>=0;i--) { diskfs_block_write(device,b,sb.bitmap_start+i); } printf("diskfs: creating root directory\n"); // Mark the zeroth and first blocks as used. b->data[0] = 0x03; diskfs_block_write(device,b,sb.bitmap_start); // Set up the zeroth inode as the root directory with a single direct block. memset(b,0,DISKFS_BLOCK_SIZE); b->inodes[0].inuse = 1; b->inodes[0].size = sizeof(struct diskfs_item); b->inodes[0].direct[0] = 1; diskfs_block_write(device,b,sb.inode_start); // Create the first directory entry as dot and write it to the first block. memset(b,0,DISKFS_BLOCK_SIZE); b->items[0].inumber = 0; b->items[0].type = DISKFS_ITEM_DIR; b->items[0].name_length = 1; b->items[0].name[0] = '.'; diskfs_block_write(device,b,sb.data_start+1); page_free(b); printf("diskfs: flushing buffer cache\n"); bcache_flush_device(device); return 0; } struct fs_ops diskfs_ops = { .volume_open = diskfs_volume_open, .volume_close = diskfs_volume_close, .volume_format = diskfs_volume_format, .volume_root = diskfs_volume_root, .lookup = diskfs_dirent_lookup, .mkdir = diskfs_dirent_create_dir, .mkfile = diskfs_dirent_create_file, .read_block = diskfs_dirent_read_block, .write_block = diskfs_dirent_write_block, .list = diskfs_dirent_list, .remove = diskfs_dirent_remove, .resize = diskfs_dirent_resize, .close = diskfs_dirent_close }; struct fs disk_fs = { "diskfs", &diskfs_ops, 0 }; int diskfs_init(void) { fs_register(&disk_fs); return 0; }