482 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			482 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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 for the vnode table.
 | 
						|
 */
 | 
						|
static
 | 
						|
int
 | 
						|
sfs_sync_vnodes(struct sfs_fs *sfs)
 | 
						|
{
 | 
						|
	unsigned i, num;
 | 
						|
 | 
						|
	/* 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);
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Sync routine for the freemap.
 | 
						|
 */
 | 
						|
static
 | 
						|
int
 | 
						|
sfs_sync_freemap(struct sfs_fs *sfs)
 | 
						|
{
 | 
						|
	int result;
 | 
						|
 | 
						|
	if (sfs->sfs_freemapdirty) {
 | 
						|
		result = sfs_freemapio(sfs, UIO_WRITE);
 | 
						|
		if (result) {
 | 
						|
			return result;
 | 
						|
		}
 | 
						|
		sfs->sfs_freemapdirty = false;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Sync routine for the superblock.
 | 
						|
 */
 | 
						|
static
 | 
						|
int
 | 
						|
sfs_sync_superblock(struct sfs_fs *sfs)
 | 
						|
{
 | 
						|
	int result;
 | 
						|
 | 
						|
	if (sfs->sfs_superdirty) {
 | 
						|
		result = sfs_writeblock(sfs, SFS_SUPER_BLOCK, &sfs->sfs_sb,
 | 
						|
					sizeof(sfs->sfs_sb));
 | 
						|
		if (result) {
 | 
						|
			return result;
 | 
						|
		}
 | 
						|
		sfs->sfs_superdirty = false;
 | 
						|
	}
 | 
						|
	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;
 | 
						|
	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;
 | 
						|
 | 
						|
	/* If any vnodes need to be written, write them. */
 | 
						|
	result = sfs_sync_vnodes(sfs);
 | 
						|
	if (result) {
 | 
						|
		vfs_biglock_release();
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
 | 
						|
	/* If the free block map needs to be written, write it. */
 | 
						|
	result = sfs_sync_freemap(sfs);
 | 
						|
	if (result) {
 | 
						|
		vfs_biglock_release();
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
 | 
						|
	/* If the superblock needs to be written, write it. */
 | 
						|
	result = sfs_sync_superblock(sfs);
 | 
						|
	if (result) {
 | 
						|
		vfs_biglock_release();
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
 | 
						|
	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->sfs_device = NULL;
 | 
						|
		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->sfs_device = NULL;
 | 
						|
		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->sfs_device = NULL;
 | 
						|
		sfs_fs_destroy(sfs);
 | 
						|
		vfs_biglock_release();
 | 
						|
		return ENOMEM;
 | 
						|
	}
 | 
						|
	result = sfs_freemapio(sfs, UIO_READ);
 | 
						|
	if (result) {
 | 
						|
		sfs->sfs_device = NULL;
 | 
						|
		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);
 | 
						|
}
 |