/* * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009, 2014 * The President and Fellows of Harvard College. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * SFS filesystem * * Filesystem-level interface routines. */ #include <types.h> #include <kern/errno.h> #include <lib.h> #include <array.h> #include <bitmap.h> #include <uio.h> #include <vfs.h> #include <device.h> #include <sfs.h> #include "sfsprivate.h" /* Shortcuts for the size macros in kern/sfs.h */ #define SFS_FS_NBLOCKS(sfs) ((sfs)->sfs_sb.sb_nblocks) #define SFS_FS_FREEMAPBITS(sfs) SFS_FREEMAPBITS(SFS_FS_NBLOCKS(sfs)) #define SFS_FS_FREEMAPBLOCKS(sfs) SFS_FREEMAPBLOCKS(SFS_FS_NBLOCKS(sfs)) /* * Routine for doing I/O (reads or writes) on the free block bitmap. * We always do the whole bitmap at once; writing individual sectors * might or might not be a worthwhile optimization. * * The free block bitmap consists of SFS_FREEMAPBLOCKS 512-byte * sectors of bits, one bit for each sector on the filesystem. The * number of blocks in the bitmap is thus rounded up to the nearest * multiple of 512*8 = 4096. (This rounded number is SFS_FREEMAPBITS.) * This means that the bitmap will (in general) contain space for some * number of invalid sectors that are actually beyond the end of the * disk device. This is ok. These sectors are supposed to be marked * "in use" by mksfs and never get marked "free". * * The sectors used by the superblock and the bitmap itself are * likewise marked in use by mksfs. */ static int sfs_freemapio(struct sfs_fs *sfs, enum uio_rw rw) { uint32_t j, freemapblocks; char *freemapdata; int result; /* Number of blocks in the free block bitmap. */ freemapblocks = SFS_FS_FREEMAPBLOCKS(sfs); /* Pointer to our freemap data in memory. */ freemapdata = bitmap_getdata(sfs->sfs_freemap); /* For each block in the free block bitmap... */ for (j=0; j<freemapblocks; j++) { /* Get a pointer to its data */ void *ptr = freemapdata + j*SFS_BLOCKSIZE; /* and read or write it. The freemap starts at sector 2. */ if (rw == UIO_READ) { result = sfs_readblock(sfs, SFS_FREEMAP_START+j, ptr, SFS_BLOCKSIZE); } else { result = sfs_writeblock(sfs, SFS_FREEMAP_START+j, ptr, SFS_BLOCKSIZE); } /* If we failed, stop. */ if (result) { return result; } } return 0; } /* * Sync routine. This is what gets invoked if you do FS_SYNC on the * sfs filesystem structure. */ static int sfs_sync(struct fs *fs) { struct sfs_fs *sfs; unsigned i, num; int result; vfs_biglock_acquire(); /* * Get the sfs_fs from the generic abstract fs. * * Note that the abstract struct fs, which is all the VFS * layer knows about, is actually a member of struct sfs_fs. * The pointer in the struct fs points back to the top of the * struct sfs_fs - essentially the same object. This can be a * little confusing at first. * * The following diagram may help: * * struct sfs_fs <-------------\ * : | * : sfs_absfs (struct fs) | <------\ * : : | | * : : various members | | * : : | | * : : fs_data ----------/ | * : : ...|... * : . VFS . * : . layer . * : other members ....... * : * : * * This construct is repeated with vnodes and devices and other * similar things all over the place in OS/161, so taking the * time to straighten it out in your mind is worthwhile. */ sfs = fs->fs_data; /* Go over the array of loaded vnodes, syncing as we go. */ num = vnodearray_num(sfs->sfs_vnodes); for (i=0; i<num; i++) { struct vnode *v = vnodearray_get(sfs->sfs_vnodes, i); VOP_FSYNC(v); } /* If the free block map needs to be written, write it. */ if (sfs->sfs_freemapdirty) { result = sfs_freemapio(sfs, UIO_WRITE); if (result) { vfs_biglock_release(); return result; } sfs->sfs_freemapdirty = false; } /* If the superblock needs to be written, write it. */ if (sfs->sfs_superdirty) { result = sfs_writeblock(sfs, SFS_SUPER_BLOCK, &sfs->sfs_sb, sizeof(sfs->sfs_sb)); if (result) { vfs_biglock_release(); return result; } sfs->sfs_superdirty = false; } vfs_biglock_release(); return 0; } /* * Routine to retrieve the volume name. Filesystems can be referred * to by their volume name followed by a colon as well as the name * of the device they're mounted on. */ static const char * sfs_getvolname(struct fs *fs) { struct sfs_fs *sfs = fs->fs_data; const char *ret; vfs_biglock_acquire(); ret = sfs->sfs_sb.sb_volname; vfs_biglock_release(); return ret; } /* * Destructor for struct sfs_fs. */ static void sfs_fs_destroy(struct sfs_fs *sfs) { if (sfs->sfs_freemap != NULL) { bitmap_destroy(sfs->sfs_freemap); } vnodearray_destroy(sfs->sfs_vnodes); KASSERT(sfs->sfs_device == NULL); kfree(sfs); } /* * Unmount code. * * VFS calls FS_SYNC on the filesystem prior to unmounting it. */ static int sfs_unmount(struct fs *fs) { struct sfs_fs *sfs = fs->fs_data; vfs_biglock_acquire(); /* Do we have any files open? If so, can't unmount. */ if (vnodearray_num(sfs->sfs_vnodes) > 0) { vfs_biglock_release(); return EBUSY; } /* We should have just had sfs_sync called. */ KASSERT(sfs->sfs_superdirty == false); KASSERT(sfs->sfs_freemapdirty == false); /* The vfs layer takes care of the device for us */ sfs->sfs_device = NULL; /* Destroy the fs object; once we start nuking stuff we can't fail. */ sfs_fs_destroy(sfs); /* nothing else to do */ vfs_biglock_release(); return 0; } /* * File system operations table. */ static const struct fs_ops sfs_fsops = { .fsop_sync = sfs_sync, .fsop_getvolname = sfs_getvolname, .fsop_getroot = sfs_getroot, .fsop_unmount = sfs_unmount, }; /* * Basic constructor for struct sfs_fs. This initializes all fields * but skips stuff that requires reading the volume, like allocating * the freemap. */ static struct sfs_fs * sfs_fs_create(void) { struct sfs_fs *sfs; /* * Make sure our on-disk structures aren't messed up */ COMPILE_ASSERT(sizeof(struct sfs_superblock)==SFS_BLOCKSIZE); COMPILE_ASSERT(sizeof(struct sfs_dinode)==SFS_BLOCKSIZE); COMPILE_ASSERT(SFS_BLOCKSIZE % sizeof(struct sfs_direntry) == 0); /* Allocate object */ sfs = kmalloc(sizeof(struct sfs_fs)); if (sfs==NULL) { goto fail; } /* * Fill in fields */ /* abstract vfs-level fs */ sfs->sfs_absfs.fs_data = sfs; sfs->sfs_absfs.fs_ops = &sfs_fsops; /* superblock */ /* (ignore sfs_super, we'll read in over it shortly) */ sfs->sfs_superdirty = false; /* device we mount on */ sfs->sfs_device = NULL; /* vnode table */ sfs->sfs_vnodes = vnodearray_create(); if (sfs->sfs_vnodes == NULL) { goto cleanup_object; } /* freemap */ sfs->sfs_freemap = NULL; sfs->sfs_freemapdirty = false; return sfs; cleanup_object: kfree(sfs); fail: return NULL; } /* * Mount routine. * * The way mount works is that you call vfs_mount and pass it a * filesystem-specific mount routine. Said routine takes a device and * hands back a pointer to an abstract filesystem. You can also pass * a void pointer through. * * This organization makes cleanup on error easier. Hint: it may also * be easier to synchronize correctly; it is important not to get two * filesystems with the same name mounted at once, or two filesystems * mounted on the same device at once. */ static int sfs_domount(void *options, struct device *dev, struct fs **ret) { int result; struct sfs_fs *sfs; vfs_biglock_acquire(); /* We don't pass any options through mount */ (void)options; /* * We can't mount on devices with the wrong sector size. * * (Note: for all intents and purposes here, "sector" and * "block" are interchangeable terms. Technically a filesystem * block may be composed of several hardware sectors, but we * don't do that in sfs.) */ if (dev->d_blocksize != SFS_BLOCKSIZE) { vfs_biglock_release(); kprintf("sfs: Cannot mount on device with blocksize %zu\n", dev->d_blocksize); return ENXIO; } sfs = sfs_fs_create(); if (sfs == NULL) { vfs_biglock_release(); return ENOMEM; } /* Set the device so we can use sfs_readblock() */ sfs->sfs_device = dev; /* Load superblock */ result = sfs_readblock(sfs, SFS_SUPER_BLOCK, &sfs->sfs_sb, sizeof(sfs->sfs_sb)); if (result) { sfs_fs_destroy(sfs); vfs_biglock_release(); return result; } /* Make some simple sanity checks */ if (sfs->sfs_sb.sb_magic != SFS_MAGIC) { kprintf("sfs: Wrong magic number in superblock " "(0x%x, should be 0x%x)\n", sfs->sfs_sb.sb_magic, SFS_MAGIC); sfs_fs_destroy(sfs); vfs_biglock_release(); return EINVAL; } if (sfs->sfs_sb.sb_nblocks > dev->d_blocks) { kprintf("sfs: warning - fs has %u blocks, device has %u\n", sfs->sfs_sb.sb_nblocks, dev->d_blocks); } /* Ensure null termination of the volume name */ sfs->sfs_sb.sb_volname[sizeof(sfs->sfs_sb.sb_volname)-1] = 0; /* Load free block bitmap */ sfs->sfs_freemap = bitmap_create(SFS_FS_FREEMAPBITS(sfs)); if (sfs->sfs_freemap == NULL) { sfs_fs_destroy(sfs); vfs_biglock_release(); return ENOMEM; } result = sfs_freemapio(sfs, UIO_READ); if (result) { sfs_fs_destroy(sfs); vfs_biglock_release(); return result; } /* Hand back the abstract fs */ *ret = &sfs->sfs_absfs; vfs_biglock_release(); return 0; } /* * Actual function called from high-level code to mount an sfs. */ int sfs_mount(const char *device) { return vfs_mount(device, NULL, sfs_domount); }