uml: make hostfs_setattr() support operations on unlinked open files
[safe/jmp/linux-2.6] / fs / hostfs / hostfs_kern.c
index e6c63d9..4e8bb2f 100644 (file)
@@ -8,22 +8,18 @@
 
 #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/root_dev.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 "2_5compat.h"
 #include "init.h"
 
 struct hostfs_inode_info {
@@ -38,7 +34,7 @@ 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)
+#define FILE_HOSTFS_I(file) HOSTFS_I((file)->f_path.dentry->d_inode)
 
 int hostfs_d_delete(struct dentry *dentry)
 {
@@ -50,14 +46,14 @@ struct dentry_operations hostfs_dentry_ops = {
 };
 
 /* Changed in hostfs_args before the kernel starts running */
-static char *root_ino = "/";
+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;
+static const struct inode_operations hostfs_iops;
+static const struct inode_operations hostfs_dir_iops;
+static const struct address_space_operations hostfs_link_aops;
 
 #ifndef MODULE
 static int __init hostfs_args(char *options, int *add)
@@ -151,7 +147,7 @@ static int read_name(struct inode *ino, char *name)
 
        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);
+                       &ino->i_ctime, &i_blksize, &i_blocks, -1);
        if(err)
                return(err);
 
@@ -159,10 +155,7 @@ static int read_name(struct inode *ino, char *name)
        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;
-       if((ino->i_sb->s_dev == ROOT_DEV) && (ino->i_uid == getuid()))
-               ino->i_uid = 0;
        return(0);
 }
 
@@ -244,7 +237,7 @@ static int read_inode(struct inode *ino)
        return(err);
 }
 
-int hostfs_statfs(struct super_block *sb, struct kstatfs *sf)
+int hostfs_statfs(struct dentry *dentry, 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,
@@ -257,7 +250,7 @@ int hostfs_statfs(struct super_block *sb, struct kstatfs *sf)
        long long f_files;
        long long f_ffree;
 
-       err = do_statfs(HOSTFS_I(sb->s_root->d_inode)->host_filename,
+       err = do_statfs(HOSTFS_I(dentry->d_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);
@@ -288,6 +281,7 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb)
 
 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;
@@ -297,8 +291,7 @@ static void hostfs_delete_inode(struct inode *inode)
 
 static void hostfs_destroy_inode(struct inode *inode)
 {
-       if(HOSTFS_I(inode)->host_filename)
-               kfree(HOSTFS_I(inode)->host_filename);
+       kfree(HOSTFS_I(inode)->host_filename);
 
        /*XXX: This should not happen, probably. The check is here for
         * additional safety.*/
@@ -315,7 +308,7 @@ static void hostfs_read_inode(struct inode *inode)
        read_inode(inode);
 }
 
-static struct super_operations hostfs_sbops = {
+static const struct super_operations hostfs_sbops = {
        .alloc_inode    = hostfs_alloc_inode,
        .drop_inode     = generic_delete_inode,
        .delete_inode   = hostfs_delete_inode,
@@ -331,7 +324,7 @@ int hostfs_readdir(struct file *file, void *ent, filldir_t filldir)
        unsigned long long next, ino;
        int error, len;
 
-       name = dentry_name(file->f_dentry, 0);
+       name = dentry_name(file->f_path.dentry, 0);
        if(name == NULL) return(-ENOMEM);
        dir = open_dir(name, &error);
        kfree(name);
@@ -372,7 +365,7 @@ int hostfs_file_open(struct inode *ino, struct file *file)
        if(w)
                r = 1;
 
-       name = dentry_name(file->f_dentry, 0);
+       name = dentry_name(file->f_path.dentry, 0);
        if(name == NULL)
                return(-ENOMEM);
 
@@ -386,25 +379,23 @@ int hostfs_file_open(struct inode *ino, struct file *file)
 
 int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync)
 {
-       return(0);
+       return fsync_file(HOSTFS_I(dentry->d_inode)->fd, datasync);
 }
 
-static struct file_operations hostfs_file_fops = {
+static const struct file_operations hostfs_file_fops = {
        .llseek         = generic_file_llseek,
-       .read           = generic_file_read,
+       .read           = do_sync_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,
+       .write          = do_sync_write,
        .mmap           = generic_file_mmap,
        .open           = hostfs_file_open,
        .release        = NULL,
        .fsync          = hostfs_fsync,
 };
 
-static struct file_operations hostfs_dir_fops = {
+static const struct file_operations hostfs_dir_fops = {
        .llseek         = generic_file_llseek,
        .readdir        = hostfs_readdir,
        .read           = generic_read_dir,
@@ -506,11 +497,16 @@ int hostfs_commit_write(struct file *file, struct page *page, unsigned from,
        long long start;
        int err = 0;
 
-       start = (long long) (page->index << PAGE_CACHE_SHIFT) + from;
+       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;
+
+       /* Actually, if !err, write_file has added to-from to start, so, despite
+        * the appearance, we are comparing i_size against the _last_ written
+        * location, as we should. */
+
        if(!err && (start > inode->i_size))
                inode->i_size = start;
 
@@ -518,7 +514,7 @@ int hostfs_commit_write(struct file *file, struct page *page, unsigned from,
        return(err);
 }
 
-static struct address_space_operations hostfs_aops = {
+static const struct address_space_operations hostfs_aops = {
        .writepage      = hostfs_writepage,
        .readpage       = hostfs_readpage,
        .set_page_dirty = __set_page_dirty_nobuffers,
@@ -758,7 +754,7 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
                goto out_put;
 
        init_special_inode(inode, mode, dev);
-       err = do_mknod(name, mode, dev);
+       err = do_mknod(name, mode, MAJOR(dev), MINOR(dev));
        if(err)
                goto out_free;
 
@@ -796,11 +792,6 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from,
        return(err);
 }
 
-void hostfs_truncate(struct inode *ino)
-{
-       not_implemented();
-}
-
 int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd)
 {
        char *name;
@@ -829,6 +820,8 @@ int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
        char *name;
        int err;
 
+       int fd = HOSTFS_I(dentry->d_inode)->fd;
+
        err = inode_change_ok(dentry->d_inode, attr);
        if (err)
                return err;
@@ -842,16 +835,10 @@ int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
                attrs.ia_mode = attr->ia_mode;
        }
        if(attr->ia_valid & ATTR_UID){
-               if((dentry->d_inode->i_sb->s_dev == ROOT_DEV) &&
-                  (attr->ia_uid == 0))
-                       attr->ia_uid = getuid();
                attrs.ia_valid |= HOSTFS_ATTR_UID;
                attrs.ia_uid = attr->ia_uid;
        }
        if(attr->ia_valid & ATTR_GID){
-               if((dentry->d_inode->i_sb->s_dev == ROOT_DEV) &&
-                  (attr->ia_gid == 0))
-                       attr->ia_gid = getgid();
                attrs.ia_valid |= HOSTFS_ATTR_GID;
                attrs.ia_gid = attr->ia_gid;
        }
@@ -879,7 +866,7 @@ int hostfs_setattr(struct dentry *dentry, struct iattr *attr)
        }
        name = dentry_name(dentry, 0);
        if(name == NULL) return(-ENOMEM);
-       err = set_attr(name, &attrs);
+       err = set_attr(name, &attrs, fd);
        kfree(name);
        if(err)
                return(err);
@@ -894,7 +881,7 @@ int hostfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
        return(0);
 }
 
-static struct inode_operations hostfs_iops = {
+static const struct inode_operations hostfs_iops = {
        .create         = hostfs_create,
        .link           = hostfs_link,
        .unlink         = hostfs_unlink,
@@ -903,13 +890,12 @@ static struct inode_operations hostfs_iops = {
        .rmdir          = hostfs_rmdir,
        .mknod          = hostfs_mknod,
        .rename         = hostfs_rename,
-       .truncate       = hostfs_truncate,
        .permission     = hostfs_permission,
        .setattr        = hostfs_setattr,
        .getattr        = hostfs_getattr,
 };
 
-static struct inode_operations hostfs_dir_iops = {
+static const struct inode_operations hostfs_dir_iops = {
        .create         = hostfs_create,
        .lookup         = hostfs_lookup,
        .link           = hostfs_link,
@@ -919,7 +905,6 @@ static struct inode_operations hostfs_dir_iops = {
        .rmdir          = hostfs_rmdir,
        .mknod          = hostfs_mknod,
        .rename         = hostfs_rename,
-       .truncate       = hostfs_truncate,
        .permission     = hostfs_permission,
        .setattr        = hostfs_setattr,
        .getattr        = hostfs_getattr,
@@ -928,10 +913,8 @@ static struct inode_operations hostfs_dir_iops = {
 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);
@@ -950,14 +933,14 @@ int hostfs_link_readpage(struct file *file, struct page *page)
        return(err);
 }
 
-static struct address_space_operations hostfs_link_aops = {
+static const 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;
+       char *host_root_path, *req_root = d;
        int err;
 
        sb->s_blocksize = 1024;
@@ -965,15 +948,17 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
        sb->s_magic = HOSTFS_SUPER_MAGIC;
        sb->s_op = &hostfs_sbops;
 
-       if((data == NULL) || (*data == '\0'))
-               data = root_ino;
+       /* NULL is printed as <NULL> by sprintf: avoid that. */
+       if (req_root == NULL)
+               req_root = "";
 
        err = -ENOMEM;
-       name = kmalloc(strlen(data) + 1, GFP_KERNEL);
-       if(name == NULL)
+       host_root_path = kmalloc(strlen(root_ino) + 1
+                                + strlen(req_root) + 1, GFP_KERNEL);
+       if(host_root_path == NULL)
                goto out;
 
-       strcpy(name, data);
+       sprintf(host_root_path, "%s/%s", root_ino, req_root);
 
        root_inode = iget(sb, 0);
        if(root_inode == NULL)
@@ -983,7 +968,10 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
        if(err)
                goto out_put;
 
-       HOSTFS_I(root_inode)->host_filename = name;
+       HOSTFS_I(root_inode)->host_filename = host_root_path;
+       /* Avoid that in the error path, iput(root_inode) frees again
+        * host_root_path through hostfs_destroy_inode! */
+       host_root_path = NULL;
 
        err = -ENOMEM;
        sb->s_root = d_alloc_root(root_inode);
@@ -991,24 +979,28 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
                goto out_put;
 
        err = read_inode(root_inode);
-       if(err)
-               goto out_put;
+       if(err){
+                /* No iput in this case because the dput does that for us */
+                dput(sb->s_root);
+                sb->s_root = NULL;
+               goto out;
+        }
 
        return(0);
 
  out_put:
-       iput(root_inode);
+        iput(root_inode);
  out_free:
-       kfree(name);
+       kfree(host_root_path);
  out:
        return(err);
 }
 
-static struct super_block *hostfs_read_sb(struct file_system_type *type,
-                                            int flags, const char *dev_name,
-                                            void *data)
+static int hostfs_read_sb(struct file_system_type *type,
+                         int flags, const char *dev_name,
+                         void *data, struct vfsmount *mnt)
 {
-       return(get_sb_nodev(type, flags, data, hostfs_fill_sb_common));
+       return get_sb_nodev(type, flags, data, hostfs_fill_sb_common, mnt);
 }
 
 static struct file_system_type hostfs_type = {