oskit/oskit-20020317/linux/fs/filesystem.c

455 lines
10 KiB
C
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 1997-2000 The University of Utah and the Flux Group.
*
* This file is part of the OSKit Linux Glue Libraries, which are free
* software, also known as "open source;" you can redistribute them and/or
* modify them under the terms of the GNU General Public License (GPL),
* version 2, as published by the Free Software Foundation (FSF).
*
* The OSKit is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GPL for more details. You should have
* received a copy of the GPL along with the OSKit; see the file COPYING. If
* not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA.
*/
/*
* Implements the oskit_filesystem_t COM interface.
*/
#include <oskit/io/blkio.h>
#include <oskit/fs/filesystem.h>
#include <oskit/fs/dir.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#include <assert.h>
#include "current.h"
#include "dev.h"
#include "errno.h"
#include "types.h"
static OSKIT_COMDECL
filesystem_query(oskit_filesystem_t *f,
const struct oskit_guid *iid,
void **out_ihandle)
{
gfilesystem_t *fs;
fs = (gfilesystem_t *)f;
if (!verify_fs(fs, fs->sb)) return OSKIT_E_INVALIDARG;
if (memcmp(iid, &oskit_iunknown_iid, sizeof(*iid)) == 0 ||
memcmp(iid, &oskit_filesystem_iid, sizeof(*iid)) == 0) {
*out_ihandle = &fs->fsi;
++fs->count;
return 0;
}
*out_ihandle = NULL;
return OSKIT_E_NOINTERFACE;
}
static OSKIT_COMDECL_U
filesystem_addref(oskit_filesystem_t *f)
{
gfilesystem_t *fs;
fs = (gfilesystem_t *)f;
VERIFY_OR_PANIC(fs, "filesystem");
return ++fs->count;
}
/*
* This needs to undo what fs_linux_mount and gfilesystem_create do.
*/
static OSKIT_COMDECL_U
filesystem_release(oskit_filesystem_t *f)
{
gfilesystem_t *fs;
oskit_error_t err;
unsigned newcount;
fs = (gfilesystem_t *)f;
VERIFY_OR_PANIC(fs, "filesystem");
newcount = --fs->count;
if (newcount == 0) {
/* Temporarily bump the count so the unmount won't fail. */
fs->count++;
err = oskit_filesystem_unmount(f);
assert(err == 0);
kfree(fs);
}
return newcount;
}
/*
* Obtain filesystem statistics.
*
* This is basically the innards of Linux's fs/open.c:sys_statfs.
*/
static OSKIT_COMDECL
filesystem_statfs(oskit_filesystem_t *f, oskit_statfs_t *out_stats)
{
gfilesystem_t *fs;
oskit_error_t err;
struct statfs stats;
struct task_struct ts;
unsigned long flags;
fs = (gfilesystem_t *)f;
if (!verify_fs(fs, fs->sb)) return OSKIT_E_INVALIDARG;
if (fs->sb->s_op->statfs == NULL)
return OSKIT_E_INVALIDARG;
err = fs_linux_create_current(&ts);
if (err)
return err;
fs->sb->s_op->statfs(fs->sb, &stats, sizeof stats);
fs_linux_destroy_current();
out_stats->bsize = stats.f_bsize;
out_stats->frsize = stats.f_bsize;
out_stats->blocks = stats.f_blocks;
out_stats->bfree = stats.f_bfree;
out_stats->bavail = stats.f_bavail;
out_stats->files = stats.f_files;
out_stats->ffree = stats.f_ffree;
out_stats->favail = stats.f_ffree;
out_stats->fsid = stats.f_fsid.val[0];
flags = fs->sb->s_flags;
out_stats->namemax = stats.f_namelen;
out_stats->flag = 0;
if (flags & MS_RDONLY) out_stats->flag |= OSKIT_FS_RDONLY;
if (flags & MS_NOEXEC) out_stats->flag |= OSKIT_FS_NOEXEC;
if (flags & MS_NOSUID) out_stats->flag |= OSKIT_FS_NOSUID;
if (flags & MS_NODEV) out_stats->flag |= OSKIT_FS_NODEV;
return 0;
}
/*
* Sync the filesystem, optionally waiting for writes to complete.
*
* In Linux, sync_dev doesn't wait but fsync_dev does.
*/
static OSKIT_COMDECL
filesystem_sync(oskit_filesystem_t *f, oskit_bool_t wait)
{
gfilesystem_t *fs;
oskit_error_t err;
struct task_struct ts;
kdev_t dev;
fs = (gfilesystem_t *)f;
if (!verify_fs(fs, fs->sb)) return OSKIT_E_INVALIDARG;
err = fs_linux_create_current(&ts);
if (err)
return err;
dev = fs->sb->s_dev;
if (wait && fsync_dev(dev) != 0)
/* fsync_dev can return 1 if there was an IO error. */
return OSKIT_EIO;
else
sync_dev(dev);
fs_linux_destroy_current();
return 0;
}
/*
* Get a handle to the root dir.
*
* Linux stores a pointer to the dentry of the root dir in the
* super_block struct as s_root.
*/
static OSKIT_COMDECL
filesystem_getroot(oskit_filesystem_t *f, oskit_dir_t **out_dir)
{
gfilesystem_t *fs;
gdir_t *dir;
oskit_error_t err;
fs = (gfilesystem_t *)f;
if (!verify_fs(fs, fs->sb)) return OSKIT_E_INVALIDARG;
if (out_dir == NULL)
return OSKIT_E_INVALIDARG;
err = gfile_lookup(fs, fs->sb->s_root, (gfile_t **)&dir);
if (err)
return err;
*out_dir = &dir->diri;
return 0;
}
/*
* Remount the filesystem.
*
* This is basically the innards of Linux's fs/super.c:do_remount_sb.
*/
static OSKIT_COMDECL
filesystem_remount(oskit_filesystem_t *f, oskit_u32_t flags)
{
gfilesystem_t *fs;
oskit_error_t err;
struct task_struct ts;
struct super_block *sb;
int linuxerr;
fs = (gfilesystem_t *)f;
if (!verify_fs(fs, fs->sb)) return OSKIT_E_INVALIDARG;
err = fs_linux_create_current(&ts);
if (err)
return err;
sb = fs->sb;
if (sb->s_op && sb->s_op->remount_fs) {
linuxerr = sb->s_op->remount_fs(sb, &flags, 0);
if (linuxerr)
return errno_to_oskit_error(-linuxerr);
}
sb->s_flags = (sb->s_flags & ~MS_RMT_MASK) |
(flags & MS_RMT_MASK);
fs_linux_destroy_current();
return 0;
}
/*
* Unmount the filesystem regardless of if there are still references to us.
* This is like shortcutting oskit_fileystem_release and is discouraged.
* Any references to this fs or files within it after unmount will yield
* undefined results.
*
* This is intended to be like Linux's fs/super.c:sys_umount and do_umount,
* mostly the latter.
*/
static OSKIT_COMDECL
filesystem_unmount(oskit_filesystem_t *f)
{
gfilesystem_t *fs;
oskit_error_t err;
struct task_struct ts;
struct super_block *sb;
kdev_t dev;
fs = (gfilesystem_t *)f;
if (!verify_fs(fs, fs->sb)) return OSKIT_E_INVALIDARG;
err = fs_linux_create_current(&ts);
if (err)
return err;
sb = fs->sb;
dev = sb->s_dev;
if (sb->s_op->umount_begin)
sb->s_op->umount_begin(sb);
/*
* Shrink dcache, then fsync. This guarantees that if the
* filesystem is quiescent at this point, then (a) only the
* root entry should be in use and (b) that root entry is
* clean.
*/
shrink_dcache_sb(sb);
fsync_dev(dev);
err = d_umount(sb);
if (err)
return err;
if (sb->s_op) {
if (sb->s_op->put_super)
sb->s_op->put_super(sb);
}
/* Forget any remaining inodes */
if (invalidate_inodes(sb)) {
printk("VFS: Busy inodes after unmount. "
"Self-destruct in 5 seconds. Have a nice day...\n");
}
sb->s_dev = 0; /* Free the superblock */
fsync_dev(dev);
fs_linux_devtab_delete(dev);
fs_linux_destroy_current();
return 0;
}
static OSKIT_COMDECL
filesystem_lookupi(oskit_filesystem_t *f, oskit_ino_t ino,
oskit_file_t **out_file)
{
gfilesystem_t *fs;
oskit_error_t err = 0;
struct inode *inode;
struct dentry *dentry;
struct task_struct ts;
gfile_t *file;
fs = (gfilesystem_t *)f;
if (!verify_fs(fs, fs->sb)) return OSKIT_E_INVALIDARG;
/**/ err = fs_linux_create_current(&ts);
if (err)
return err;
/* Get the dentry. */
/**/ inode = iget(fs->sb, ino);
if (inode == NULL) {
fs_linux_destroy_current();
return OSKIT_E_FAIL;
}
/* Create a dentry. This is kinda bogus, but so is lookup by inode */
/**/ dentry = d_alloc(NULL, &(const struct qstr) { "", 0, 0 });
if (dentry == NULL) {
iput(inode);
fs_linux_destroy_current();
return OSKIT_E_FAIL;
}
d_instantiate(dentry, inode);
dentry->d_sb = inode->i_sb;
/* Make a file from the dentry. */
/**/ err = gfile_lookup(fs, dentry, &file);
if (err) {
iput(inode);
fs_linux_destroy_current();
return err;
}
*out_file = &file->filei;
iput(inode); /* `file' has a ref */
fs_linux_destroy_current();
return err;
}
static struct oskit_filesystem_ops fs_ops = {
filesystem_query,
filesystem_addref,
filesystem_release,
filesystem_statfs,
filesystem_sync,
filesystem_getroot,
filesystem_remount,
filesystem_unmount,
filesystem_lookupi,
};
/*
* Create and initialize a gfilesystem_t.
*/
static oskit_error_t
gfilesystem_create(struct super_block *sb, gfilesystem_t **out_fs)
{
gfilesystem_t *fs;
if (out_fs == NULL)
return OSKIT_E_INVALIDARG;
fs = kmalloc(sizeof(gfilesystem_t), GFP_KERNEL);
if (fs == NULL)
return OSKIT_ENOMEM;
fs->fsi.ops = &fs_ops;
fs->count = 1;
fs->sb = sb;
*out_fs = fs;
return 0;
}
/*
* This is a front end for Linux's mount_root,
* which fills in a super_block struct based on what it finds in the partition.
*
* oskit_filesystem_release is the counterpart to this.
*/
oskit_error_t
fs_linux_mount(oskit_blkio_t *bio, oskit_u32_t flags,
oskit_filesystem_t **out_fs)
{
int linux_flags;
kdev_t dev;
struct super_block *sb;
int linux_err;
struct task_struct ts;
oskit_error_t err;
gfilesystem_t *fs;
if (out_fs == NULL)
return OSKIT_E_INVALIDARG;
/*
* Insert this device into the devtab and get a kdev_t for it.
*/
err = fs_linux_devtab_insert(bio, &dev);
if (err)
return err;
/*
* Convert `flags' into Linux form.
*/
linux_flags = 0;
if (flags & OSKIT_FS_RDONLY) linux_flags |= MS_RDONLY;
if (flags & OSKIT_FS_NOEXEC) linux_flags |= MS_NOEXEC;
if (flags & OSKIT_FS_NOSUID) linux_flags |= MS_NOSUID;
if (flags & OSKIT_FS_NODEV) linux_flags |= MS_NODEV;
/*
* Call a modified version of Linux's mount_root that takes
* parameters rather then using ROOT_DEV and root_mountflags.
*/
err = fs_linux_create_current(&ts);
if (err) {
fs_linux_devtab_delete(dev);
return err;
}
linux_err = mount_root(dev, linux_flags, &sb);
if (linux_err) {
printk("%s: Linux mount_root failed %d\n",
__FUNCTION__, linux_err);
fs_linux_devtab_delete(dev);
fs_linux_destroy_current();
return OSKIT_EIO;
}
fs_linux_destroy_current();
/*
* Now, create something to return.
*/
err = gfilesystem_create(sb, &fs);
if (err) {
/* XXX need to split unmount functionality out of
filesystem_unmount and do that here.
Will still need current to be valid. */
fs_linux_devtab_delete(dev);
return err;
}
*out_fs = &fs->fsi;
return 0;
}