X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=module%2Fspl%2Fspl-vnode.c;h=0784ff2611096304b532901b01b2559c4ae1453b;hb=80093b6f72408aee7e88abbf52537d9fc1feb008;hp=9bfead8cf44a0c431d1106ebdd341981e9e0882c;hpb=b17edc10a9c66543bef54b08e4655832aefe8939;p=mirror_spl-debian.git diff --git a/module/spl/spl-vnode.c b/module/spl/spl-vnode.c index 9bfead8..0784ff2 100644 --- a/module/spl/spl-vnode.c +++ b/module/spl/spl-vnode.c @@ -6,7 +6,7 @@ * UCRL-CODE-235197 * * This file is part of the SPL, Solaris Porting Layer. - * For details, see . + * For details, see . * * 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 #include +#include #include #ifdef SS_DEBUG_SUBSYS @@ -39,11 +41,22 @@ EXPORT_SYMBOL(rootdir); static spl_kmem_cache_t *vn_cache; static spl_kmem_cache_t *vn_file_cache; -static spinlock_t vn_file_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(vn_file_lock); static LIST_HEAD(vn_file_list); -static vtype_t -vn_get_sol_type(umode_t mode) +#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) { if (S_ISREG(mode)) return VREG; @@ -70,7 +83,36 @@ vn_get_sol_type(umode_t mode) return VCHR; return VNON; -} /* vn_get_sol_type() */ +} /* vn_mode_to_vtype() */ +EXPORT_SYMBOL(vn_mode_to_vtype); + +mode_t +vn_vtype_to_mode(vtype_t vtype) +{ + if (vtype == VREG) + return S_IFREG; + + if (vtype == VDIR) + return S_IFDIR; + + if (vtype == VCHR) + return S_IFCHR; + + if (vtype == VBLK) + return S_IFBLK; + + if (vtype == VFIFO) + return S_IFIFO; + + if (vtype == VLNK) + return S_IFLNK; + + if (vtype == VSOCK) + return S_IFSOCK; + + return VNON; +} /* vn_vtype_to_mode() */ +EXPORT_SYMBOL(vn_vtype_to_mode); vnode_t * vn_alloc(int flag) @@ -104,6 +146,7 @@ vn_open(const char *path, uio_seg_t seg, int flags, int mode, struct file *fp; struct kstat stat; int rc, saved_umask = 0; + gfp_t saved_gfp; vnode_t *vp; SENTRY; @@ -133,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); @@ -145,9 +192,13 @@ vn_open(const char *path, uio_seg_t seg, int flags, int mode, SRETURN(ENOMEM); } + saved_gfp = mapping_gfp_mask(fp->f_mapping); + mapping_set_gfp_mask(fp->f_mapping, saved_gfp & ~(__GFP_IO|__GFP_FS)); + mutex_enter(&vp->v_lock); - vp->v_type = vn_get_sol_type(stat.mode); + vp->v_type = vn_mode_to_vtype(stat.mode); vp->v_file = fp; + vp->v_gfp_mask = saved_gfp; *vpp = vp; mutex_exit(&vp->v_lock); @@ -213,6 +264,7 @@ vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off, rc = vfs_read(fp, addr, len, &offset); set_fs(saved_fs); + fp->f_pos = offset; if (rc < 0) SRETURN(-rc); @@ -237,6 +289,7 @@ vn_close(vnode_t *vp, int flags, int x1, int x2, void *x3, void *x4) ASSERT(vp); ASSERT(vp->v_file); + mapping_set_gfp_mask(vp->v_file->f_mapping, vp->v_gfp_mask); rc = filp_close(vp->v_file, 0); vn_free(vp); @@ -248,16 +301,141 @@ EXPORT_SYMBOL(vn_close); * proposed seek. We perform minimal checking and allow vn_rdwr() to catch * anything more serious. */ int -vn_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct) +vn_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, void *ct) { return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0); } 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) { - return lookup_one_len(nd->last.name, nd->nd_dentry, nd->last.len); + return lookup_one_len((const char *)nd->last.name, + nd->nd_dentry, nd->last.len); } /* lookup_hash() */ static void @@ -280,7 +458,7 @@ vn_remove(const char *path, uio_seg_t seg, int flags) ASSERT(seg == UIO_SYSSPACE); ASSERT(flags == RMFILE); - rc = path_lookup(path, LOOKUP_PARENT, &nd); + rc = spl_kern_path_parent(path, &nd); if (rc) SGOTO(exit, rc); @@ -288,11 +466,7 @@ vn_remove(const char *path, uio_seg_t seg, int flags) if (nd.last_type != LAST_NORM) SGOTO(exit1, rc); -#ifdef HAVE_INODE_I_MUTEX - mutex_lock_nested(&nd.nd_dentry->d_inode->i_mutex, I_MUTEX_PARENT); -#else - down(&nd.nd_dentry->d_inode->i_sem); -#endif /* HAVE_INODE_I_MUTEX */ + spl_inode_lock_nested(nd.nd_dentry->d_inode, I_MUTEX_PARENT); dentry = vn_lookup_hash(&nd); rc = PTR_ERR(dentry); if (!IS_ERR(dentry)) { @@ -311,11 +485,8 @@ vn_remove(const char *path, uio_seg_t seg, int flags) exit2: dput(dentry); } -#ifdef HAVE_INODE_I_MUTEX - mutex_unlock(&nd.nd_dentry->d_inode->i_mutex); -#else - up(&nd.nd_dentry->d_inode->i_sem); -#endif /* HAVE_INODE_I_MUTEX */ + + spl_inode_unlock(nd.nd_dentry->d_inode); if (inode) iput(inode); /* truncate the inode here */ exit1: @@ -341,11 +512,11 @@ vn_rename(const char *oldname, const char *newname, int x1) int rc = 0; SENTRY; - rc = path_lookup(oldname, LOOKUP_PARENT, &oldnd); + rc = spl_kern_path_parent(oldname, &oldnd); if (rc) SGOTO(exit, rc); - rc = path_lookup(newname, LOOKUP_PARENT, &newnd); + rc = spl_kern_path_parent(newname, &newnd); if (rc) SGOTO(exit1, rc); @@ -420,12 +591,13 @@ 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) { struct file *fp; - struct kstat stat; + struct kstat stat; int rc; SENTRY; @@ -435,29 +607,30 @@ 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_get_sol_type(stat.mode); + 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; vap->va_size = stat.size; - vap->va_blocksize = stat.blksize; - vap->va_atime.tv_sec = stat.atime.tv_sec; - vap->va_atime.tv_usec = stat.atime.tv_nsec / NSEC_PER_USEC; - vap->va_mtime.tv_sec = stat.mtime.tv_sec; - vap->va_mtime.tv_usec = stat.mtime.tv_nsec / NSEC_PER_USEC; - vap->va_ctime.tv_sec = stat.ctime.tv_sec; - vap->va_ctime.tv_usec = stat.ctime.tv_nsec / NSEC_PER_USEC; + vap->va_blksize = stat.blksize; + vap->va_atime = stat.atime; + vap->va_mtime = stat.mtime; + vap->va_ctime = stat.ctime; vap->va_rdev = stat.rdev; - vap->va_blocks = stat.blocks; + vap->va_nblocks = stat.blocks; - SRETURN(0); + SRETURN(0); } EXPORT_SYMBOL(vn_getattr); @@ -476,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) @@ -485,7 +712,7 @@ file_find(int fd) ASSERT(spin_is_locked(&vn_file_lock)); list_for_each_entry(fp, &vn_file_list, f_list) { - if (fd == fp->f_fd) { + if (fd == fp->f_fd && fp->f_task == current) { ASSERT(atomic_read(&fp->f_ref) != 0); return fp; } @@ -524,6 +751,7 @@ vn_getf(int fd) mutex_enter(&fp->f_lock); fp->f_fd = fd; + fp->f_task = current; fp->f_offset = 0; atomic_inc(&fp->f_ref); @@ -535,11 +763,16 @@ 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); - vp->v_type = vn_get_sol_type(stat.mode); + vp->v_type = vn_mode_to_vtype(stat.mode); vp->v_file = lfp; mutex_exit(&vp->v_lock); @@ -605,53 +838,42 @@ 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; - - write_lock(&fs->lock); - old_pwd = fs->pwd; - fs->pwd = *path; - path_get(path); - write_unlock(&fs->lock); - - 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); - } + struct path old_pwd; + +# ifdef HAVE_FS_STRUCT_SPINLOCK + spin_lock(&fs->lock); + old_pwd = fs->pwd; + fs->pwd = *path; + path_get(path); + spin_unlock(&fs->lock); +# else + write_lock(&fs->lock); + old_pwd = fs->pwd; + fs->pwd = *path; + path_get(path); + write_unlock(&fs->lock); +# endif /* HAVE_FS_STRUCT_SPINLOCK */ + + if (old_pwd.dentry) + path_put(&old_pwd); } -# 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; @@ -664,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) @@ -693,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); @@ -753,26 +959,51 @@ 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", sizeof(struct vnode), 64, vn_cache_constructor, vn_cache_destructor, - NULL, NULL, NULL, 0); + NULL, NULL, NULL, KMC_KMEM); vn_file_cache = kmem_cache_create("spl_vn_file_cache", sizeof(file_t), 64, vn_file_cache_constructor, vn_file_cache_destructor, - NULL, NULL, NULL, 0); + NULL, NULL, NULL, KMC_KMEM); SRETURN(0); } /* vn_init() */ void -vn_fini(void) +spl_vn_fini(void) { file_t *fp, *next_fp; int leaked = 0; @@ -786,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;