]> git.proxmox.com Git - mirror_spl-debian.git/blobdiff - module/spl/spl-vnode.c
Imported Upstream version 0.6.2
[mirror_spl-debian.git] / module / spl / spl-vnode.c
index 354e8798b9709b5a1ac80d053e0afc7a1fdaff71..0784ff2611096304b532901b01b2559c4ae1453b 100644 (file)
@@ -6,7 +6,7 @@
  *  UCRL-CODE-235197
  *
  *  This file is part of the SPL, Solaris Porting Layer.
- *  For details, see <http://github.com/behlendorf/spl/>.
+ *  For details, see <http://zfsonlinux.org/>.
  *
  *  The SPL is free software; you can redistribute it and/or modify it
  *  under the terms of the GNU General Public License as published by the
@@ -24,7 +24,9 @@
  *  Solaris Porting Layer (SPL) Vnode Implementation.
 \*****************************************************************************/
 
+#include <sys/cred.h>
 #include <sys/vnode.h>
+#include <linux/falloc.h>
 #include <spl-debug.h>
 
 #ifdef SS_DEBUG_SUBSYS
@@ -42,6 +44,17 @@ static spl_kmem_cache_t *vn_file_cache;
 static DEFINE_SPINLOCK(vn_file_lock);
 static LIST_HEAD(vn_file_list);
 
+#ifdef HAVE_KERN_PATH_PARENT_HEADER
+#ifndef HAVE_KERN_PATH_PARENT_SYMBOL
+kern_path_parent_t kern_path_parent_fn = SYMBOL_POISON;
+EXPORT_SYMBOL(kern_path_parent_fn);
+#endif /* HAVE_KERN_PATH_PARENT_SYMBOL */
+#endif /* HAVE_KERN_PATH_PARENT_HEADER */
+
+#ifdef HAVE_KERN_PATH_LOCKED
+kern_path_locked_t kern_path_locked_fn = SYMBOL_POISON;
+#endif /* HAVE_KERN_PATH_LOCKED */
+
 vtype_t
 vn_mode_to_vtype(mode_t mode)
 {
@@ -163,7 +176,11 @@ vn_open(const char *path, uio_seg_t seg, int flags, int mode,
        if (IS_ERR(fp))
                SRETURN(-PTR_ERR(fp));
 
-       rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
+#ifdef HAVE_2ARGS_VFS_GETATTR
+       rc = vfs_getattr(&fp->f_path, &stat);
+#else
+       rc = vfs_getattr(fp->f_path.mnt, fp->f_dentry, &stat);
+#endif
        if (rc) {
                filp_close(fp, 0);
                SRETURN(-rc);
@@ -290,6 +307,130 @@ vn_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, void *ct)
 }
 EXPORT_SYMBOL(vn_seek);
 
+#ifdef HAVE_KERN_PATH_LOCKED
+/* Based on do_unlinkat() from linux/fs/namei.c */
+int
+vn_remove(const char *path, uio_seg_t seg, int flags)
+{
+       struct dentry *dentry;
+       struct path parent;
+       struct inode *inode = NULL;
+       int rc = 0;
+       SENTRY;
+
+       ASSERT(seg == UIO_SYSSPACE);
+       ASSERT(flags == RMFILE);
+
+       dentry = spl_kern_path_locked(path, &parent);
+       rc = PTR_ERR(dentry);
+       if (!IS_ERR(dentry)) {
+               if (parent.dentry->d_name.name[parent.dentry->d_name.len])
+                       SGOTO(slashes, rc = 0);
+
+               inode = dentry->d_inode;
+               if (!inode)
+                       SGOTO(slashes, rc = 0);
+
+               if (inode)
+                       ihold(inode);
+
+               rc = vfs_unlink(parent.dentry->d_inode, dentry);
+exit1:
+               dput(dentry);
+       } else {
+               return (-rc);
+       }
+
+       spl_inode_unlock(parent.dentry->d_inode);
+       if (inode)
+               iput(inode);    /* truncate the inode here */
+
+       path_put(&parent);
+       SRETURN(-rc);
+
+slashes:
+       rc = !dentry->d_inode ? -ENOENT :
+           S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+       SGOTO(exit1, rc);
+} /* vn_remove() */
+EXPORT_SYMBOL(vn_remove);
+
+/* Based on do_rename() from linux/fs/namei.c */
+int
+vn_rename(const char *oldname, const char *newname, int x1)
+{
+       struct dentry *old_dir, *new_dir;
+       struct dentry *old_dentry, *new_dentry;
+       struct dentry *trap;
+       struct path old_parent, new_parent;
+       int rc = 0;
+       SENTRY;
+
+       old_dentry = spl_kern_path_locked(oldname, &old_parent);
+       if (IS_ERR(old_dentry))
+               SGOTO(exit, rc = PTR_ERR(old_dentry));
+
+       spl_inode_unlock(old_parent.dentry->d_inode);
+
+       new_dentry = spl_kern_path_locked(newname, &new_parent);
+       if (IS_ERR(new_dentry))
+               SGOTO(exit2, rc = PTR_ERR(new_dentry));
+
+       spl_inode_unlock(new_parent.dentry->d_inode);
+
+       rc = -EXDEV;
+       if (old_parent.mnt != new_parent.mnt)
+               SGOTO(exit3, rc);
+
+       old_dir = old_parent.dentry;
+       new_dir = new_parent.dentry;
+       trap = lock_rename(new_dir, old_dir);
+
+       /* source should not be ancestor of target */
+       rc = -EINVAL;
+       if (old_dentry == trap)
+               SGOTO(exit4, rc);
+
+       /* target should not be an ancestor of source */
+       rc = -ENOTEMPTY;
+       if (new_dentry == trap)
+               SGOTO(exit4, rc);
+
+       /* source must exist */
+       rc = -ENOENT;
+       if (!old_dentry->d_inode)
+               SGOTO(exit4, rc);
+
+       /* unless the source is a directory trailing slashes give -ENOTDIR */
+       if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
+               rc = -ENOTDIR;
+               if (old_dentry->d_name.name[old_dentry->d_name.len])
+                       SGOTO(exit4, rc);
+               if (new_dentry->d_name.name[new_dentry->d_name.len])
+                       SGOTO(exit4, rc);
+       }
+
+#ifdef HAVE_4ARGS_VFS_RENAME
+       rc = vfs_rename(old_dir->d_inode, old_dentry,
+                       new_dir->d_inode, new_dentry);
+#else
+       rc = vfs_rename(old_dir->d_inode, old_dentry, oldnd.nd_mnt,
+                       new_dir->d_inode, new_dentry, newnd.nd_mnt);
+#endif /* HAVE_4ARGS_VFS_RENAME */
+exit4:
+       unlock_rename(new_dir, old_dir);
+exit3:
+       dput(new_dentry);
+       path_put(&new_parent);
+exit2:
+       dput(old_dentry);
+       path_put(&old_parent);
+exit:
+       SRETURN(-rc);
+}
+EXPORT_SYMBOL(vn_rename);
+
+#else
 static struct dentry *
 vn_lookup_hash(struct nameidata *nd)
 {
@@ -450,6 +591,7 @@ exit:
         SRETURN(-rc);
 }
 EXPORT_SYMBOL(vn_rename);
+#endif /* HAVE_KERN_PATH_LOCKED */
 
 int
 vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4)
@@ -465,14 +607,18 @@ vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4)
 
        fp = vp->v_file;
 
-        rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
+#ifdef HAVE_2ARGS_VFS_GETATTR
+       rc = vfs_getattr(&fp->f_path, &stat);
+#else
+       rc = vfs_getattr(fp->f_path.mnt, fp->f_dentry, &stat);
+#endif
        if (rc)
                SRETURN(-rc);
 
        vap->va_type          = vn_mode_to_vtype(stat.mode);
        vap->va_mode          = stat.mode;
-       vap->va_uid           = stat.uid;
-       vap->va_gid           = stat.gid;
+       vap->va_uid           = KUID_TO_SUID(stat.uid);
+       vap->va_gid           = KGID_TO_SGID(stat.gid);
        vap->va_fsid          = 0;
        vap->va_nodeid        = stat.ino;
        vap->va_nlink         = stat.nlink;
@@ -503,6 +649,60 @@ int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4)
 } /* vn_fsync() */
 EXPORT_SYMBOL(vn_fsync);
 
+int vn_space(vnode_t *vp, int cmd, struct flock *bfp, int flag,
+    offset_t offset, void *x6, void *x7)
+{
+       int error = EOPNOTSUPP;
+       SENTRY;
+
+       if (cmd != F_FREESP || bfp->l_whence != 0)
+               SRETURN(EOPNOTSUPP);
+
+       ASSERT(vp);
+       ASSERT(vp->v_file);
+       ASSERT(bfp->l_start >= 0 && bfp->l_len > 0);
+
+#ifdef FALLOC_FL_PUNCH_HOLE
+       /*
+        * When supported by the underlying file system preferentially
+        * use the fallocate() callback to preallocate the space.
+        */
+       error = -spl_filp_fallocate(vp->v_file,
+           FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
+           bfp->l_start, bfp->l_len);
+       if (error == 0)
+               SRETURN(0);
+#endif
+
+#ifdef HAVE_INODE_TRUNCATE_RANGE
+       if (vp->v_file->f_dentry && vp->v_file->f_dentry->d_inode &&
+           vp->v_file->f_dentry->d_inode->i_op &&
+           vp->v_file->f_dentry->d_inode->i_op->truncate_range) {
+               off_t end = bfp->l_start + bfp->l_len;
+               /*
+                * Judging from the code in shmem_truncate_range(),
+                * it seems the kernel expects the end offset to be
+                * inclusive and aligned to the end of a page.
+                */
+               if (end % PAGE_SIZE != 0) {
+                       end &= ~(off_t)(PAGE_SIZE - 1);
+                       if (end <= bfp->l_start)
+                               SRETURN(0);
+               }
+               --end;
+
+               vp->v_file->f_dentry->d_inode->i_op->truncate_range(
+                       vp->v_file->f_dentry->d_inode,
+                       bfp->l_start, end
+               );
+               SRETURN(0);
+       }
+#endif
+
+       SRETURN(error);
+}
+EXPORT_SYMBOL(vn_space);
+
 /* Function must be called while holding the vn_file_lock */
 static file_t *
 file_find(int fd)
@@ -563,7 +763,12 @@ vn_getf(int fd)
        if (vp == NULL)
                SGOTO(out_fget, rc);
 
-        if (vfs_getattr(lfp->f_vfsmnt, lfp->f_dentry, &stat))
+#ifdef HAVE_2ARGS_VFS_GETATTR
+       rc = vfs_getattr(&lfp->f_path, &stat);
+#else
+       rc = vfs_getattr(lfp->f_path.mnt, lfp->f_dentry, &stat);
+#endif
+        if (rc)
                SGOTO(out_vnode, rc);
 
        mutex_enter(&vp->v_lock);
@@ -633,10 +838,12 @@ vn_releasef(int fd)
 EXPORT_SYMBOL(releasef);
 
 #ifndef HAVE_SET_FS_PWD
-# ifdef HAVE_2ARGS_SET_FS_PWD
-/* Used from 2.6.25 - 2.6.31+ */
 void
+#  ifdef HAVE_SET_FS_PWD_WITH_CONST
+set_fs_pwd(struct fs_struct *fs, const struct path *path)
+#  else
 set_fs_pwd(struct fs_struct *fs, struct path *path)
+#  endif
 {
        struct path old_pwd;
 
@@ -657,37 +864,16 @@ set_fs_pwd(struct fs_struct *fs, struct path *path)
        if (old_pwd.dentry)
                path_put(&old_pwd);
 }
-# else
-/* Used from 2.6.11 - 2.6.24 */
-void
-set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt, struct dentry *dentry)
-{
-        struct dentry *old_pwd;
-        struct vfsmount *old_pwdmnt;
-
-        write_lock(&fs->lock);
-        old_pwd = fs->pwd;
-        old_pwdmnt = fs->pwdmnt;
-        fs->pwdmnt = mntget(mnt);
-        fs->pwd = dget(dentry);
-        write_unlock(&fs->lock);
-
-        if (old_pwd) {
-                dput(old_pwd);
-                mntput(old_pwdmnt);
-        }
-}
-# endif /* HAVE_2ARGS_SET_FS_PWD */
 #endif /* HAVE_SET_FS_PWD */
 
 int
 vn_set_pwd(const char *filename)
 {
-#if defined(HAVE_2ARGS_SET_FS_PWD) && defined(HAVE_USER_PATH_DIR)
+#ifdef HAVE_USER_PATH_DIR
         struct path path;
 #else
         struct nameidata nd;
-#endif /* HAVE_2ARGS_SET_FS_PWD */
+#endif /* HAVE_USER_PATH_DIR */
         mm_segment_t saved_fs;
         int rc;
         SENTRY;
@@ -700,7 +886,6 @@ vn_set_pwd(const char *filename)
         saved_fs = get_fs();
         set_fs(get_ds());
 
-#ifdef HAVE_2ARGS_SET_FS_PWD
 # ifdef HAVE_USER_PATH_DIR
         rc = user_path_dir(filename, &path);
         if (rc)
@@ -729,21 +914,6 @@ dput_and_out:
 dput_and_out:
         path_put(&nd.path);
 # endif /* HAVE_USER_PATH_DIR */
-#else
-        rc = __user_walk(filename,
-                         LOOKUP_FOLLOW|LOOKUP_DIRECTORY|LOOKUP_CHDIR, &nd);
-        if (rc)
-                SGOTO(out, rc);
-
-        rc = vfs_permission(&nd, MAY_EXEC);
-        if (rc)
-                SGOTO(dput_and_out, rc);
-
-        set_fs_pwd(current->fs, nd.nd_mnt, nd.nd_dentry);
-
-dput_and_out:
-        vn_path_release(&nd);
-#endif /* HAVE_2ARGS_SET_FS_PWD */
 out:
        set_fs(saved_fs);
 
@@ -789,8 +959,33 @@ vn_file_cache_destructor(void *buf, void *cdrarg)
        mutex_destroy(&fp->f_lock);
 } /* vn_file_cache_destructor() */
 
+int spl_vn_init_kallsyms_lookup(void)
+{
+#ifdef HAVE_KERN_PATH_PARENT_HEADER
+#ifndef HAVE_KERN_PATH_PARENT_SYMBOL
+       kern_path_parent_fn = (kern_path_parent_t)
+               spl_kallsyms_lookup_name("kern_path_parent");
+       if (!kern_path_parent_fn) {
+               printk(KERN_ERR "Error: Unknown symbol kern_path_parent\n");
+               return -EFAULT;
+       }
+#endif /* HAVE_KERN_PATH_PARENT_SYMBOL */
+#endif /* HAVE_KERN_PATH_PARENT_HEADER */
+
+#ifdef HAVE_KERN_PATH_LOCKED
+        kern_path_locked_fn = (kern_path_locked_t)
+                spl_kallsyms_lookup_name("kern_path_locked");
+        if (!kern_path_locked_fn) {
+                printk(KERN_ERR "Error: Unknown symbol kern_path_locked\n");
+                return -EFAULT;
+        }
+#endif
+
+       return (0);
+}
+
 int
-vn_init(void)
+spl_vn_init(void)
 {
        SENTRY;
        vn_cache = kmem_cache_create("spl_vn_cache",
@@ -808,7 +1003,7 @@ vn_init(void)
 } /* vn_init() */
 
 void
-vn_fini(void)
+spl_vn_fini(void)
 {
         file_t *fp, *next_fp;
        int leaked = 0;
@@ -822,13 +1017,12 @@ vn_fini(void)
                leaked++;
        }
 
-       kmem_cache_destroy(vn_file_cache);
-       vn_file_cache = NULL;
        spin_unlock(&vn_file_lock);
 
        if (leaked > 0)
                SWARN("Warning %d files leaked\n", leaked);
 
+       kmem_cache_destroy(vn_file_cache);
        kmem_cache_destroy(vn_cache);
 
        SEXIT;