linux/fs/hostfs/hostfs_kern.c
Paolo 'Blaisorblade' Giarrusso daa35edc0a [PATCH] uml: remove empty hostfs_truncate method
Calling truncate() on hostfs spits a kernel warning "Something isn't
implemented here", but it still works fine.

Indeed, hostfs i_op->truncate doesn't do anything.  But hostfs_setattr() ->
set_attr() correctly detects ATTR_SIZE and calls truncate() on the host.  So
we should be safe (using ftruncate() may be better, in case the file is
unlinked on the host, but we aren't sure to have the file open for writing,
and reopening it would cause the same races; plus nobody should expect UML to
be so careful).

So, the warning is wrong, because the current implementation is working.  Al,
am I correct, and can the warning be therefore dropped?

CC: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-09-30 12:41:18 -07:00

1034 lines
22 KiB
C

/*
* Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com)
* Licensed under the GPL
*
* Ported the filesystem routines to 2.5.
* 2003-02-10 Petr Baudis <pasky@ucw.cz>
*/
#include <linux/stddef.h>
#include <linux/fs.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/blkdev.h>
#include <linux/list.h>
#include <linux/statfs.h>
#include <linux/kdev_t.h>
#include <asm/uaccess.h>
#include "hostfs.h"
#include "kern_util.h"
#include "kern.h"
#include "user_util.h"
#include "init.h"
struct hostfs_inode_info {
char *host_filename;
int fd;
int mode;
struct inode vfs_inode;
};
static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
{
return(list_entry(inode, struct hostfs_inode_info, vfs_inode));
}
#define FILE_HOSTFS_I(file) HOSTFS_I((file)->f_dentry->d_inode)
int hostfs_d_delete(struct dentry *dentry)
{
return(1);
}
struct dentry_operations hostfs_dentry_ops = {
.d_delete = hostfs_d_delete,
};
/* Changed in hostfs_args before the kernel starts running */
static char *root_ino = "/";
static int append = 0;
#define HOSTFS_SUPER_MAGIC 0x00c0ffee
static struct inode_operations hostfs_iops;
static struct inode_operations hostfs_dir_iops;
static struct address_space_operations hostfs_link_aops;
#ifndef MODULE
static int __init hostfs_args(char *options, int *add)
{
char *ptr;
ptr = strchr(options, ',');
if(ptr != NULL)
*ptr++ = '\0';
if(*options != '\0')
root_ino = options;
options = ptr;
while(options){
ptr = strchr(options, ',');
if(ptr != NULL)
*ptr++ = '\0';
if(*options != '\0'){
if(!strcmp(options, "append"))
append = 1;
else printf("hostfs_args - unsupported option - %s\n",
options);
}
options = ptr;
}
return(0);
}
__uml_setup("hostfs=", hostfs_args,
"hostfs=<root dir>,<flags>,...\n"
" This is used to set hostfs parameters. The root directory argument\n"
" is used to confine all hostfs mounts to within the specified directory\n"
" tree on the host. If this isn't specified, then a user inside UML can\n"
" mount anything on the host that's accessible to the user that's running\n"
" it.\n"
" The only flag currently supported is 'append', which specifies that all\n"
" files opened by hostfs will be opened in append mode.\n\n"
);
#endif
static char *dentry_name(struct dentry *dentry, int extra)
{
struct dentry *parent;
char *root, *name;
int len;
len = 0;
parent = dentry;
while(parent->d_parent != parent){
len += parent->d_name.len + 1;
parent = parent->d_parent;
}
root = HOSTFS_I(parent->d_inode)->host_filename;
len += strlen(root);
name = kmalloc(len + extra + 1, GFP_KERNEL);
if(name == NULL) return(NULL);
name[len] = '\0';
parent = dentry;
while(parent->d_parent != parent){
len -= parent->d_name.len + 1;
name[len] = '/';
strncpy(&name[len + 1], parent->d_name.name,
parent->d_name.len);
parent = parent->d_parent;
}
strncpy(name, root, strlen(root));
return(name);
}
static char *inode_name(struct inode *ino, int extra)
{
struct dentry *dentry;
dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias);
return(dentry_name(dentry, extra));
}
static int read_name(struct inode *ino, char *name)
{
/* The non-int inode fields are copied into ints by stat_file and
* then copied into the inode because passing the actual pointers
* in and having them treated as int * breaks on big-endian machines
*/
int err;
int i_mode, i_nlink, i_blksize;
unsigned long long i_size;
unsigned long long i_ino;
unsigned long long i_blocks;
err = stat_file(name, &i_ino, &i_mode, &i_nlink, &ino->i_uid,
&ino->i_gid, &i_size, &ino->i_atime, &ino->i_mtime,
&ino->i_ctime, &i_blksize, &i_blocks);
if(err)
return(err);
ino->i_ino = i_ino;
ino->i_mode = i_mode;
ino->i_nlink = i_nlink;
ino->i_size = i_size;
ino->i_blksize = i_blksize;
ino->i_blocks = i_blocks;
return(0);
}
static char *follow_link(char *link)
{
int len, n;
char *name, *resolved, *end;
len = 64;
while(1){
n = -ENOMEM;
name = kmalloc(len, GFP_KERNEL);
if(name == NULL)
goto out;
n = do_readlink(link, name, len);
if(n < len)
break;
len *= 2;
kfree(name);
}
if(n < 0)
goto out_free;
if(*name == '/')
return(name);
end = strrchr(link, '/');
if(end == NULL)
return(name);
*(end + 1) = '\0';
len = strlen(link) + strlen(name) + 1;
resolved = kmalloc(len, GFP_KERNEL);
if(resolved == NULL){
n = -ENOMEM;
goto out_free;
}
sprintf(resolved, "%s%s", link, name);
kfree(name);
kfree(link);
return(resolved);
out_free:
kfree(name);
out:
return(ERR_PTR(n));
}
static int read_inode(struct inode *ino)
{
char *name;
int err = 0;
/* Unfortunately, we are called from iget() when we don't have a dentry
* allocated yet.
*/
if(list_empty(&ino->i_dentry))
goto out;
err = -ENOMEM;
name = inode_name(ino, 0);
if(name == NULL)
goto out;
if(file_type(name, NULL, NULL) == OS_TYPE_SYMLINK){
name = follow_link(name);
if(IS_ERR(name)){
err = PTR_ERR(name);
goto out;
}
}
err = read_name(ino, name);
kfree(name);
out:
return(err);
}
int hostfs_statfs(struct super_block *sb, struct kstatfs *sf)
{
/* do_statfs uses struct statfs64 internally, but the linux kernel
* struct statfs still has 32-bit versions for most of these fields,
* so we convert them here
*/
int err;
long long f_blocks;
long long f_bfree;
long long f_bavail;
long long f_files;
long long f_ffree;
err = do_statfs(HOSTFS_I(sb->s_root->d_inode)->host_filename,
&sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files,
&f_ffree, &sf->f_fsid, sizeof(sf->f_fsid),
&sf->f_namelen, sf->f_spare);
if(err) return(err);
sf->f_blocks = f_blocks;
sf->f_bfree = f_bfree;
sf->f_bavail = f_bavail;
sf->f_files = f_files;
sf->f_ffree = f_ffree;
sf->f_type = HOSTFS_SUPER_MAGIC;
return(0);
}
static struct inode *hostfs_alloc_inode(struct super_block *sb)
{
struct hostfs_inode_info *hi;
hi = kmalloc(sizeof(*hi), GFP_KERNEL);
if(hi == NULL)
return(NULL);
*hi = ((struct hostfs_inode_info) { .host_filename = NULL,
.fd = -1,
.mode = 0 });
inode_init_once(&hi->vfs_inode);
return(&hi->vfs_inode);
}
static void hostfs_delete_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
if(HOSTFS_I(inode)->fd != -1) {
close_file(&HOSTFS_I(inode)->fd);
HOSTFS_I(inode)->fd = -1;
}
clear_inode(inode);
}
static void hostfs_destroy_inode(struct inode *inode)
{
if(HOSTFS_I(inode)->host_filename)
kfree(HOSTFS_I(inode)->host_filename);
/*XXX: This should not happen, probably. The check is here for
* additional safety.*/
if(HOSTFS_I(inode)->fd != -1) {
close_file(&HOSTFS_I(inode)->fd);
printk(KERN_DEBUG "Closing host fd in .destroy_inode\n");
}
kfree(HOSTFS_I(inode));
}
static void hostfs_read_inode(struct inode *inode)
{
read_inode(inode);
}
static struct super_operations hostfs_sbops = {
.alloc_inode = hostfs_alloc_inode,
.drop_inode = generic_delete_inode,
.delete_inode = hostfs_delete_inode,
.destroy_inode = hostfs_destroy_inode,
.read_inode = hostfs_read_inode,
.statfs = hostfs_statfs,
};
int hostfs_readdir(struct file *file, void *ent, filldir_t filldir)
{
void *dir;
char *name;
unsigned long long next, ino;
int error, len;
name = dentry_name(file->f_dentry, 0);
if(name == NULL) return(-ENOMEM);
dir = open_dir(name, &error);
kfree(name);
if(dir == NULL) return(-error);
next = file->f_pos;
while((name = read_dir(dir, &next, &ino, &len)) != NULL){
error = (*filldir)(ent, name, len, file->f_pos,
ino, DT_UNKNOWN);
if(error) break;
file->f_pos = next;
}
close_dir(dir);
return(0);
}
int hostfs_file_open(struct inode *ino, struct file *file)
{
char *name;
int mode = 0, r = 0, w = 0, fd;
mode = file->f_mode & (FMODE_READ | FMODE_WRITE);
if((mode & HOSTFS_I(ino)->mode) == mode)
return(0);
/* The file may already have been opened, but with the wrong access,
* so this resets things and reopens the file with the new access.
*/
if(HOSTFS_I(ino)->fd != -1){
close_file(&HOSTFS_I(ino)->fd);
HOSTFS_I(ino)->fd = -1;
}
HOSTFS_I(ino)->mode |= mode;
if(HOSTFS_I(ino)->mode & FMODE_READ)
r = 1;
if(HOSTFS_I(ino)->mode & FMODE_WRITE)
w = 1;
if(w)
r = 1;
name = dentry_name(file->f_dentry, 0);
if(name == NULL)
return(-ENOMEM);
fd = open_file(name, r, w, append);
kfree(name);
if(fd < 0) return(fd);
FILE_HOSTFS_I(file)->fd = fd;
return(0);
}
int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync)
{
return fsync_file(HOSTFS_I(dentry->d_inode)->fd, datasync);
}
static struct file_operations hostfs_file_fops = {
.llseek = generic_file_llseek,
.read = generic_file_read,
.sendfile = generic_file_sendfile,
.aio_read = generic_file_aio_read,
.aio_write = generic_file_aio_write,
.readv = generic_file_readv,
.writev = generic_file_writev,
.write = generic_file_write,
.mmap = generic_file_mmap,
.open = hostfs_file_open,
.release = NULL,
.fsync = hostfs_fsync,
};
static struct file_operations hostfs_dir_fops = {
.llseek = generic_file_llseek,
.readdir = hostfs_readdir,
.read = generic_read_dir,
};
int hostfs_writepage(struct page *page, struct writeback_control *wbc)
{
struct address_space *mapping = page->mapping;
struct inode *inode = mapping->host;
char *buffer;
unsigned long long base;
int count = PAGE_CACHE_SIZE;
int end_index = inode->i_size >> PAGE_CACHE_SHIFT;
int err;
if (page->index >= end_index)
count = inode->i_size & (PAGE_CACHE_SIZE-1);
buffer = kmap(page);
base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT;
err = write_file(HOSTFS_I(inode)->fd, &base, buffer, count);
if(err != count){
ClearPageUptodate(page);
goto out;
}
if (base > inode->i_size)
inode->i_size = base;
if (PageError(page))
ClearPageError(page);
err = 0;
out:
kunmap(page);
unlock_page(page);
return err;
}
int hostfs_readpage(struct file *file, struct page *page)
{
char *buffer;
long long start;
int err = 0;
start = (long long) page->index << PAGE_CACHE_SHIFT;
buffer = kmap(page);
err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer,
PAGE_CACHE_SIZE);
if(err < 0) goto out;
memset(&buffer[err], 0, PAGE_CACHE_SIZE - err);
flush_dcache_page(page);
SetPageUptodate(page);
if (PageError(page)) ClearPageError(page);
err = 0;
out:
kunmap(page);
unlock_page(page);
return(err);
}
int hostfs_prepare_write(struct file *file, struct page *page,
unsigned int from, unsigned int to)
{
char *buffer;
long long start, tmp;
int err;
start = (long long) page->index << PAGE_CACHE_SHIFT;
buffer = kmap(page);
if(from != 0){
tmp = start;
err = read_file(FILE_HOSTFS_I(file)->fd, &tmp, buffer,
from);
if(err < 0) goto out;
}
if(to != PAGE_CACHE_SIZE){
start += to;
err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer + to,
PAGE_CACHE_SIZE - to);
if(err < 0) goto out;
}
err = 0;
out:
kunmap(page);
return(err);
}
int hostfs_commit_write(struct file *file, struct page *page, unsigned from,
unsigned to)
{
struct address_space *mapping = page->mapping;
struct inode *inode = mapping->host;
char *buffer;
long long start;
int err = 0;
start = (long long) (page->index << PAGE_CACHE_SHIFT) + from;
buffer = kmap(page);
err = write_file(FILE_HOSTFS_I(file)->fd, &start, buffer + from,
to - from);
if(err > 0) err = 0;
if(!err && (start > inode->i_size))
inode->i_size = start;
kunmap(page);
return(err);
}
static struct address_space_operations hostfs_aops = {
.writepage = hostfs_writepage,
.readpage = hostfs_readpage,
.set_page_dirty = __set_page_dirty_nobuffers,
.prepare_write = hostfs_prepare_write,
.commit_write = hostfs_commit_write
};
static int init_inode(struct inode *inode, struct dentry *dentry)
{
char *name;
int type, err = -ENOMEM;
int maj, min;
dev_t rdev = 0;
if(dentry){
name = dentry_name(dentry, 0);
if(name == NULL)
goto out;
type = file_type(name, &maj, &min);
/*Reencode maj and min with the kernel encoding.*/
rdev = MKDEV(maj, min);
kfree(name);
}
else type = OS_TYPE_DIR;
err = 0;
if(type == OS_TYPE_SYMLINK)
inode->i_op = &page_symlink_inode_operations;
else if(type == OS_TYPE_DIR)
inode->i_op = &hostfs_dir_iops;
else inode->i_op = &hostfs_iops;
if(type == OS_TYPE_DIR) inode->i_fop = &hostfs_dir_fops;
else inode->i_fop = &hostfs_file_fops;
if(type == OS_TYPE_SYMLINK)
inode->i_mapping->a_ops = &hostfs_link_aops;
else inode->i_mapping->a_ops = &hostfs_aops;
switch (type) {
case OS_TYPE_CHARDEV:
init_special_inode(inode, S_IFCHR, rdev);
break;
case OS_TYPE_BLOCKDEV:
init_special_inode(inode, S_IFBLK, rdev);
break;
case OS_TYPE_FIFO:
init_special_inode(inode, S_IFIFO, 0);
break;
case OS_TYPE_SOCK:
init_special_inode(inode, S_IFSOCK, 0);
break;
}
out:
return(err);
}
int hostfs_create(struct inode *dir, struct dentry *dentry, int mode,
struct nameidata *nd)
{
struct inode *inode;
char *name;
int error, fd;
error = -ENOMEM;
inode = iget(dir->i_sb, 0);
if(inode == NULL) goto out;
error = init_inode(inode, dentry);
if(error)
goto out_put;
error = -ENOMEM;
name = dentry_name(dentry, 0);
if(name == NULL)
goto out_put;
fd = file_create(name,
mode & S_IRUSR, mode & S_IWUSR, mode & S_IXUSR,
mode & S_IRGRP, mode & S_IWGRP, mode & S_IXGRP,
mode & S_IROTH, mode & S_IWOTH, mode & S_IXOTH);
if(fd < 0)
error = fd;
else error = read_name(inode, name);
kfree(name);
if(error)
goto out_put;
HOSTFS_I(inode)->fd = fd;
HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE;
d_instantiate(dentry, inode);
return(0);
out_put:
iput(inode);
out:
return(error);
}
struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry,
struct nameidata *nd)
{
struct inode *inode;
char *name;
int err;
err = -ENOMEM;
inode = iget(ino->i_sb, 0);
if(inode == NULL)
goto out;
err = init_inode(inode, dentry);
if(err)
goto out_put;
err = -ENOMEM;
name = dentry_name(dentry, 0);
if(name == NULL)
goto out_put;
err = read_name(inode, name);
kfree(name);
if(err == -ENOENT){
iput(inode);
inode = NULL;
}
else if(err)
goto out_put;
d_add(dentry, inode);
dentry->d_op = &hostfs_dentry_ops;
return(NULL);
out_put:
iput(inode);
out:
return(ERR_PTR(err));
}
static char *inode_dentry_name(struct inode *ino, struct dentry *dentry)
{
char *file;
int len;
file = inode_name(ino, dentry->d_name.len + 1);
if(file == NULL) return(NULL);
strcat(file, "/");
len = strlen(file);
strncat(file, dentry->d_name.name, dentry->d_name.len);
file[len + dentry->d_name.len] = '\0';
return(file);
}
int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from)
{
char *from_name, *to_name;
int err;
if((from_name = inode_dentry_name(ino, from)) == NULL)
return(-ENOMEM);
to_name = dentry_name(to, 0);
if(to_name == NULL){
kfree(from_name);
return(-ENOMEM);
}
err = link_file(to_name, from_name);
kfree(from_name);
kfree(to_name);
return(err);
}
int hostfs_unlink(struct inode *ino, struct dentry *dentry)
{
char *file;
int err;
if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM);
if(append)
return(-EPERM);
err = unlink_file(file);
kfree(file);
return(err);
}
int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to)
{
char *file;
int err;
if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM);
err = make_symlink(file, to);
kfree(file);
return(err);
}
int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode)
{
char *file;
int err;
if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM);
err = do_mkdir(file, mode);
kfree(file);
return(err);
}
int hostfs_rmdir(struct inode *ino, struct dentry *dentry)
{
char *file;
int err;
if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM);
err = do_rmdir(file);
kfree(file);
return(err);
}
int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
struct inode *inode;
char *name;
int err = -ENOMEM;
inode = iget(dir->i_sb, 0);
if(inode == NULL)
goto out;
err = init_inode(inode, dentry);
if(err)
goto out_put;
err = -ENOMEM;
name = dentry_name(dentry, 0);
if(name == NULL)
goto out_put;
init_special_inode(inode, mode, dev);
err = do_mknod(name, mode, dev);
if(err)
goto out_free;
err = read_name(inode, name);
kfree(name);
if(err)
goto out_put;
d_instantiate(dentry, inode);
return(0);
out_free:
kfree(name);
out_put:
iput(inode);
out:
return(err);
}
int hostfs_rename(struct inode *from_ino, struct dentry *from,
struct inode *to_ino, struct dentry *to)
{
char *from_name, *to_name;
int err;
if((from_name = inode_dentry_name(from_ino, from)) == NULL)
return(-ENOMEM);
if((to_name = inode_dentry_name(to_ino, to)) == NULL){
kfree(from_name);
return(-ENOMEM);
}
err = rename_file(from_name, to_name);
kfree(from_name);
kfree(to_name);
return(err);
}
int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd)
{
char *name;
int r = 0, w = 0, x = 0, err;
if (desired & MAY_READ) r = 1;
if (desired & MAY_WRITE) w = 1;
if (desired & MAY_EXEC) x = 1;
name = inode_name(ino, 0);
if (name == NULL) return(-ENOMEM);
if (S_ISCHR(ino->i_mode) || S_ISBLK(ino->i_mode) ||
S_ISFIFO(ino->i_mode) || S_ISSOCK(ino->i_mode))
err = 0;
else
err = access_file(name, r, w, x);
kfree(name);
if(!err)
err = generic_permission(ino, desired, NULL);
return err;
}
int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct hostfs_iattr attrs;
char *name;
int err;
err = inode_change_ok(dentry->d_inode, attr);
if (err)
return err;
if(append)
attr->ia_valid &= ~ATTR_SIZE;
attrs.ia_valid = 0;
if(attr->ia_valid & ATTR_MODE){
attrs.ia_valid |= HOSTFS_ATTR_MODE;
attrs.ia_mode = attr->ia_mode;
}
if(attr->ia_valid & ATTR_UID){
attrs.ia_valid |= HOSTFS_ATTR_UID;
attrs.ia_uid = attr->ia_uid;
}
if(attr->ia_valid & ATTR_GID){
attrs.ia_valid |= HOSTFS_ATTR_GID;
attrs.ia_gid = attr->ia_gid;
}
if(attr->ia_valid & ATTR_SIZE){
attrs.ia_valid |= HOSTFS_ATTR_SIZE;
attrs.ia_size = attr->ia_size;
}
if(attr->ia_valid & ATTR_ATIME){
attrs.ia_valid |= HOSTFS_ATTR_ATIME;
attrs.ia_atime = attr->ia_atime;
}
if(attr->ia_valid & ATTR_MTIME){
attrs.ia_valid |= HOSTFS_ATTR_MTIME;
attrs.ia_mtime = attr->ia_mtime;
}
if(attr->ia_valid & ATTR_CTIME){
attrs.ia_valid |= HOSTFS_ATTR_CTIME;
attrs.ia_ctime = attr->ia_ctime;
}
if(attr->ia_valid & ATTR_ATIME_SET){
attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET;
}
if(attr->ia_valid & ATTR_MTIME_SET){
attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET;
}
name = dentry_name(dentry, 0);
if(name == NULL) return(-ENOMEM);
err = set_attr(name, &attrs);
kfree(name);
if(err)
return(err);
return(inode_setattr(dentry->d_inode, attr));
}
int hostfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
generic_fillattr(dentry->d_inode, stat);
return(0);
}
static struct inode_operations hostfs_iops = {
.create = hostfs_create,
.link = hostfs_link,
.unlink = hostfs_unlink,
.symlink = hostfs_symlink,
.mkdir = hostfs_mkdir,
.rmdir = hostfs_rmdir,
.mknod = hostfs_mknod,
.rename = hostfs_rename,
.permission = hostfs_permission,
.setattr = hostfs_setattr,
.getattr = hostfs_getattr,
};
static struct inode_operations hostfs_dir_iops = {
.create = hostfs_create,
.lookup = hostfs_lookup,
.link = hostfs_link,
.unlink = hostfs_unlink,
.symlink = hostfs_symlink,
.mkdir = hostfs_mkdir,
.rmdir = hostfs_rmdir,
.mknod = hostfs_mknod,
.rename = hostfs_rename,
.permission = hostfs_permission,
.setattr = hostfs_setattr,
.getattr = hostfs_getattr,
};
int hostfs_link_readpage(struct file *file, struct page *page)
{
char *buffer, *name;
long long start;
int err;
start = page->index << PAGE_CACHE_SHIFT;
buffer = kmap(page);
name = inode_name(page->mapping->host, 0);
if(name == NULL) return(-ENOMEM);
err = do_readlink(name, buffer, PAGE_CACHE_SIZE);
kfree(name);
if(err == PAGE_CACHE_SIZE)
err = -E2BIG;
else if(err > 0){
flush_dcache_page(page);
SetPageUptodate(page);
if (PageError(page)) ClearPageError(page);
err = 0;
}
kunmap(page);
unlock_page(page);
return(err);
}
static struct address_space_operations hostfs_link_aops = {
.readpage = hostfs_link_readpage,
};
static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
{
struct inode *root_inode;
char *name, *data = d;
int err;
sb->s_blocksize = 1024;
sb->s_blocksize_bits = 10;
sb->s_magic = HOSTFS_SUPER_MAGIC;
sb->s_op = &hostfs_sbops;
if((data == NULL) || (*data == '\0'))
data = root_ino;
err = -ENOMEM;
name = kmalloc(strlen(data) + 1, GFP_KERNEL);
if(name == NULL)
goto out;
strcpy(name, data);
root_inode = iget(sb, 0);
if(root_inode == NULL)
goto out_free;
err = init_inode(root_inode, NULL);
if(err)
goto out_put;
HOSTFS_I(root_inode)->host_filename = name;
err = -ENOMEM;
sb->s_root = d_alloc_root(root_inode);
if(sb->s_root == NULL)
goto out_put;
err = read_inode(root_inode);
if(err){
/* No iput in this case because the dput does that for us */
dput(sb->s_root);
sb->s_root = NULL;
goto out_free;
}
return(0);
out_put:
iput(root_inode);
out_free:
kfree(name);
out:
return(err);
}
static struct super_block *hostfs_read_sb(struct file_system_type *type,
int flags, const char *dev_name,
void *data)
{
return(get_sb_nodev(type, flags, data, hostfs_fill_sb_common));
}
static struct file_system_type hostfs_type = {
.owner = THIS_MODULE,
.name = "hostfs",
.get_sb = hostfs_read_sb,
.kill_sb = kill_anon_super,
.fs_flags = 0,
};
static int __init init_hostfs(void)
{
return(register_filesystem(&hostfs_type));
}
static void __exit exit_hostfs(void)
{
unregister_filesystem(&hostfs_type);
}
module_init(init_hostfs)
module_exit(exit_hostfs)
MODULE_LICENSE("GPL");
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/