#include <defs.h>
#include <stdio.h>
#include <string.h>
#include <kmalloc.h>
#include <list.h>
#include <fs.h>
#include <vfs.h>
#include <dev.h>
#include <sfs.h>
#include <inode.h>
#include <iobuf.h>
#include <bitmap.h>
#include <error.h>
#include <assert.h>
static int
sfs_sync(struct fs *fs) {
struct sfs_fs *sfs = fsop_info(fs, sfs);
lock_sfs_fs(sfs);
{
list_entry_t *list = &(sfs->inode_list), *le = list;
while ((le = list_next(le)) != list) {
struct sfs_inode *sin = le2sin(le, inode_link);
vop_fsync(info2node(sin, sfs_inode));
}
}
unlock_sfs_fs(sfs);
int ret;
if (sfs->super_dirty) {
sfs->super_dirty = 0;
if ((ret = sfs_sync_super(sfs)) != 0) {
sfs->super_dirty = 1;
return ret;
}
if ((ret = sfs_sync_freemap(sfs)) != 0) {
sfs->super_dirty = 1;
return ret;
}
}
return 0;
}
static struct inode *
sfs_get_root(struct fs *fs) {
struct inode *node;
int ret;
if ((ret = sfs_load_inode(fsop_info(fs, sfs), &node, SFS_BLKN_ROOT)) != 0) {
panic("load sfs root failed: %e", ret);
}
return node;
}
static int
sfs_unmount(struct fs *fs) {
struct sfs_fs *sfs = fsop_info(fs, sfs);
if (!list_empty(&(sfs->inode_list))) {
return -E_BUSY;
}
assert(!sfs->super_dirty);
bitmap_destroy(sfs->freemap);
kfree(sfs->sfs_buffer);
kfree(sfs->hash_list);
kfree(sfs);
return 0;
}
static void
sfs_cleanup(struct fs *fs) {
struct sfs_fs *sfs = fsop_info(fs, sfs);
uint32_t blocks = sfs->super.blocks, unused_blocks = sfs->super.unused_blocks;
cprintf("sfs: cleanup: '%s' (%d/%d/%d)\n", sfs->super.info,
blocks - unused_blocks, unused_blocks, blocks);
int i, ret;
for (i = 0; i < 32; i ++) {
if ((ret = fsop_sync(fs)) == 0) {
break;
}
}
if (ret != 0) {
warn("sfs: sync error: '%s': %e.\n", sfs->super.info, ret);
}
}
static int
sfs_init_read(struct device *dev, uint32_t blkno, void *blk_buffer) {
struct iobuf __iob, *iob = iobuf_init(&__iob, blk_buffer, SFS_BLKSIZE, blkno * SFS_BLKSIZE);
return dop_io(dev, iob, 0);
}
static int
sfs_init_freemap(struct device *dev, struct bitmap *freemap, uint32_t blkno, uint32_t nblks, void *blk_buffer) {
size_t len;
void *data = bitmap_getdata(freemap, &len);
assert(data != NULL && len == nblks * SFS_BLKSIZE);
while (nblks != 0) {
int ret;
if ((ret = sfs_init_read(dev, blkno, data)) != 0) {
return ret;
}
blkno ++, nblks --, data += SFS_BLKSIZE;
}
return 0;
}
static int
sfs_do_mount(struct device *dev, struct fs **fs_store) {
static_assert(SFS_BLKSIZE >= sizeof(struct sfs_super));
static_assert(SFS_BLKSIZE >= sizeof(struct sfs_disk_inode));
static_assert(SFS_BLKSIZE >= sizeof(struct sfs_disk_entry));
if (dev->d_blocksize != SFS_BLKSIZE) {
return -E_NA_DEV;
}
struct fs *fs;
if ((fs = alloc_fs(sfs)) == NULL) {
return -E_NO_MEM;
}
struct sfs_fs *sfs = fsop_info(fs, sfs);
sfs->dev = dev;
int ret = -E_NO_MEM;
void *sfs_buffer;
if ((sfs->sfs_buffer = sfs_buffer = kmalloc(SFS_BLKSIZE)) == NULL) {
goto failed_cleanup_fs;
}
if ((ret = sfs_init_read(dev, SFS_BLKN_SUPER, sfs_buffer)) != 0) {
goto failed_cleanup_sfs_buffer;
}
ret = -E_INVAL;
struct sfs_super *super = sfs_buffer;
if (super->magic != SFS_MAGIC) {
cprintf("sfs: wrong magic in superblock. (%08x should be %08x).\n",
super->magic, SFS_MAGIC);
goto failed_cleanup_sfs_buffer;
}
if (super->blocks > dev->d_blocks) {
cprintf("sfs: fs has %u blocks, device has %u blocks.\n",
super->blocks, dev->d_blocks);
goto failed_cleanup_sfs_buffer;
}
super->info[SFS_MAX_INFO_LEN] = '\0';
sfs->super = *super;
ret = -E_NO_MEM;
uint32_t i;
list_entry_t *hash_list;
if ((sfs->hash_list = hash_list = kmalloc(sizeof(list_entry_t) * SFS_HLIST_SIZE)) == NULL) {
goto failed_cleanup_sfs_buffer;
}
for (i = 0; i < SFS_HLIST_SIZE; i ++) {
list_init(hash_list + i);
}
struct bitmap *freemap;
uint32_t freemap_size_nbits = sfs_freemap_bits(super);
if ((sfs->freemap = freemap = bitmap_create(freemap_size_nbits)) == NULL) {
goto failed_cleanup_hash_list;
}
uint32_t freemap_size_nblks = sfs_freemap_blocks(super);
if ((ret = sfs_init_freemap(dev, freemap, SFS_BLKN_FREEMAP, freemap_size_nblks, sfs_buffer)) != 0) {
goto failed_cleanup_freemap;
}
uint32_t blocks = sfs->super.blocks, unused_blocks = 0;
for (i = 0; i < freemap_size_nbits; i ++) {
if (bitmap_test(freemap, i)) {
unused_blocks ++;
}
}
assert(unused_blocks == sfs->super.unused_blocks);
sfs->super_dirty = 0;
sem_init(&(sfs->fs_sem), 1);
sem_init(&(sfs->io_sem), 1);
sem_init(&(sfs->mutex_sem), 1);
list_init(&(sfs->inode_list));
cprintf("sfs: mount: '%s' (%d/%d/%d)\n", sfs->super.info,
blocks - unused_blocks, unused_blocks, blocks);
fs->fs_sync = sfs_sync;
fs->fs_get_root = sfs_get_root;
fs->fs_unmount = sfs_unmount;
fs->fs_cleanup = sfs_cleanup;
*fs_store = fs;
return 0;
failed_cleanup_freemap:
bitmap_destroy(freemap);
failed_cleanup_hash_list:
kfree(hash_list);
failed_cleanup_sfs_buffer:
kfree(sfs_buffer);
failed_cleanup_fs:
kfree(fs);
return ret;
}
int
sfs_mount(const char *devname) {
return vfs_mount(devname, sfs_do_mount);
}