X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=module%2Fspl%2Fspl-vnode.c;h=346e63c0f1613a4af068338595db08de59fdc439;hb=ec06701b5473894514b25e4054dd5597e25a22e8;hp=a0fed32017296abb797ebfa00b0b2722a933fd7e;hpb=bcb15891ab394e11615eee08bba1fd85ac32e158;p=mirror_spl-debian.git diff --git a/module/spl/spl-vnode.c b/module/spl/spl-vnode.c index a0fed32..346e63c 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,15 +24,11 @@ * Solaris Porting Layer (SPL) Vnode Implementation. \*****************************************************************************/ +#include #include +#include #include -#include - -#ifdef SS_DEBUG_SUBSYS -#undef SS_DEBUG_SUBSYS -#endif - -#define SS_DEBUG_SUBSYS SS_VNODE +#include vnode_t *rootdir = (vnode_t *)0xabcd1234; EXPORT_SYMBOL(rootdir); @@ -43,17 +39,6 @@ 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) { @@ -78,9 +63,6 @@ vn_mode_to_vtype(mode_t mode) if (S_ISSOCK(mode)) return VSOCK; - if (S_ISCHR(mode)) - return VCHR; - return VNON; } /* vn_mode_to_vtype() */ EXPORT_SYMBOL(vn_mode_to_vtype); @@ -117,7 +99,6 @@ vnode_t * vn_alloc(int flag) { vnode_t *vp; - SENTRY; vp = kmem_cache_alloc(vn_cache, flag); if (vp != NULL) { @@ -125,16 +106,14 @@ vn_alloc(int flag) vp->v_type = 0; } - SRETURN(vp); + return (vp); } /* vn_alloc() */ EXPORT_SYMBOL(vn_alloc); void vn_free(vnode_t *vp) { - SENTRY; kmem_cache_free(vn_cache, vp); - SEXIT; } /* vn_free() */ EXPORT_SYMBOL(vn_free); @@ -147,7 +126,6 @@ vn_open(const char *path, uio_seg_t seg, int flags, int mode, int rc, saved_umask = 0; gfp_t saved_gfp; vnode_t *vp; - SENTRY; ASSERT(flags & (FWRITE | FREAD)); ASSERT(seg == UIO_SYSSPACE); @@ -173,18 +151,24 @@ vn_open(const char *path, uio_seg_t seg, int flags, int mode, (void)xchg(¤t->fs->umask, saved_umask); if (IS_ERR(fp)) - SRETURN(-PTR_ERR(fp)); + return (-PTR_ERR(fp)); - rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat); +#if defined(HAVE_4ARGS_VFS_GETATTR) + rc = vfs_getattr(&fp->f_path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT); +#elif defined(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); + return (-rc); } vp = vn_alloc(KM_SLEEP); if (!vp) { filp_close(fp, 0); - SRETURN(ENOMEM); + return (ENOMEM); } saved_gfp = mapping_gfp_mask(fp->f_mapping); @@ -197,7 +181,7 @@ vn_open(const char *path, uio_seg_t seg, int flags, int mode, *vpp = vp; mutex_exit(&vp->v_lock); - SRETURN(0); + return (0); } /* vn_open() */ EXPORT_SYMBOL(vn_open); @@ -207,20 +191,19 @@ vn_openat(const char *path, uio_seg_t seg, int flags, int mode, { char *realpath; int len, rc; - SENTRY; ASSERT(vp == rootdir); len = strlen(path) + 2; - realpath = kmalloc(len, GFP_KERNEL); + realpath = kmalloc(len, kmem_flags_convert(KM_SLEEP)); if (!realpath) - SRETURN(ENOMEM); + return (ENOMEM); (void)snprintf(realpath, len, "/%s", path); rc = vn_open(realpath, seg, flags, mode, vpp, x1, x2); kfree(realpath); - SRETURN(rc); + return (rc); } /* vn_openat() */ EXPORT_SYMBOL(vn_openat); @@ -232,14 +215,12 @@ vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off, mm_segment_t saved_fs; struct file *fp; int rc; - SENTRY; ASSERT(uio == UIO_WRITE || uio == UIO_READ); ASSERT(vp); ASSERT(vp->v_file); ASSERT(seg == UIO_SYSSPACE); ASSERT((ioflag & ~FAPPEND) == 0); - ASSERT(x2 == RLIM64_INFINITY); fp = vp->v_file; @@ -262,16 +243,16 @@ vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off, fp->f_pos = offset; if (rc < 0) - SRETURN(-rc); + return (-rc); if (residp) { *residp = len - rc; } else { if (rc != len) - SRETURN(EIO); + return (EIO); } - SRETURN(0); + return (0); } /* vn_rdwr() */ EXPORT_SYMBOL(vn_rdwr); @@ -279,7 +260,6 @@ int vn_close(vnode_t *vp, int flags, int x1, int x2, void *x3, void *x4) { int rc; - SENTRY; ASSERT(vp); ASSERT(vp->v_file); @@ -288,7 +268,7 @@ vn_close(vnode_t *vp, int flags, int x1, int x2, void *x3, void *x4) rc = filp_close(vp->v_file, 0); vn_free(vp); - SRETURN(-rc); + return (-rc); } /* vn_close() */ EXPORT_SYMBOL(vn_close); @@ -302,7 +282,89 @@ vn_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, void *ct) } EXPORT_SYMBOL(vn_seek); -#ifdef HAVE_KERN_PATH_LOCKED +/* + * spl_basename() takes a NULL-terminated string s as input containing a path. + * It returns a char pointer to a string and a length that describe the + * basename of the path. If the basename is not "." or "/", it will be an index + * into the string. While the string should be NULL terminated, the section + * referring to the basename is not. spl_basename is dual-licensed GPLv2+ and + * CC0. Anyone wishing to reuse it in another codebase may pick either license. + */ +static void +spl_basename(const char *s, const char **str, int *len) +{ + size_t i, end; + + ASSERT(str); + ASSERT(len); + + if (!s || !*s) { + *str = "."; + *len = 1; + return; + } + + i = strlen(s) - 1; + + while (i && s[i--] == '/'); + + if (i == 0) { + *str = "/"; + *len = 1; + return; + } + + end = i; + + for (end = i; i; i--) { + if (s[i] == '/') { + *str = &s[i+1]; + *len = end - i + 1; + return; + } + } + + *str = s; + *len = end + 1; +} + +static struct dentry * +spl_kern_path_locked(const char *name, struct path *path) +{ + struct path parent; + struct dentry *dentry; + const char *basename; + int len; + int rc; + + ASSERT(name); + ASSERT(path); + + spl_basename(name, &basename, &len); + + /* We do not accept "." or ".." */ + if (len <= 2 && basename[0] == '.') + if (len == 1 || basename[1] == '.') + return (ERR_PTR(-EACCES)); + + rc = kern_path(name, LOOKUP_PARENT, &parent); + if (rc) + return (ERR_PTR(rc)); + + /* use I_MUTEX_PARENT because vfs_unlink needs it */ + spl_inode_lock_nested(parent.dentry->d_inode, I_MUTEX_PARENT); + + dentry = lookup_one_len(basename, parent.dentry, len); + if (IS_ERR(dentry)) { + spl_inode_unlock(parent.dentry->d_inode); + path_put(&parent); + } else { + *path = parent; + } + + return (dentry); +} + /* Based on do_unlinkat() from linux/fs/namei.c */ int vn_remove(const char *path, uio_seg_t seg, int flags) @@ -311,7 +373,6 @@ vn_remove(const char *path, uio_seg_t seg, int flags) struct path parent; struct inode *inode = NULL; int rc = 0; - SENTRY; ASSERT(seg == UIO_SYSSPACE); ASSERT(flags == RMFILE); @@ -319,19 +380,28 @@ vn_remove(const char *path, uio_seg_t seg, int flags) 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); + if (parent.dentry->d_name.name[parent.dentry->d_name.len]) { + rc = 0; + goto slashes; + } inode = dentry->d_inode; - if (!inode) - SGOTO(slashes, rc = 0); - - if (inode) - ihold(inode); + if (inode) { + atomic_inc(&inode->i_count); + } else { + rc = 0; + goto slashes; + } +#ifdef HAVE_2ARGS_VFS_UNLINK rc = vfs_unlink(parent.dentry->d_inode, dentry); +#else + rc = vfs_unlink(parent.dentry->d_inode, dentry, NULL); +#endif /* HAVE_2ARGS_VFS_UNLINK */ exit1: dput(dentry); + } else { + return (-rc); } spl_inode_unlock(parent.dentry->d_inode); @@ -339,12 +409,12 @@ exit1: iput(inode); /* truncate the inode here */ path_put(&parent); - SRETURN(-rc); + return (-rc); slashes: rc = !dentry->d_inode ? -ENOENT : S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR; - SGOTO(exit1, rc); + goto exit1; } /* vn_remove() */ EXPORT_SYMBOL(vn_remove); @@ -357,23 +427,26 @@ vn_rename(const char *oldname, const char *newname, int x1) 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)); + if (IS_ERR(old_dentry)) { + rc = PTR_ERR(old_dentry); + goto exit; + } 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)); + if (IS_ERR(new_dentry)) { + rc = PTR_ERR(new_dentry); + goto exit2; + } spl_inode_unlock(new_parent.dentry->d_inode); rc = -EXDEV; if (old_parent.mnt != new_parent.mnt) - SGOTO(exit3, rc); + goto exit3; old_dir = old_parent.dentry; new_dir = new_parent.dentry; @@ -382,34 +455,37 @@ vn_rename(const char *oldname, const char *newname, int x1) /* source should not be ancestor of target */ rc = -EINVAL; if (old_dentry == trap) - SGOTO(exit4, rc); + goto exit4; /* target should not be an ancestor of source */ rc = -ENOTEMPTY; if (new_dentry == trap) - SGOTO(exit4, rc); + goto exit4; /* source must exist */ rc = -ENOENT; if (!old_dentry->d_inode) - SGOTO(exit4, rc); + goto exit4; /* 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); + goto exit4; if (new_dentry->d_name.name[new_dentry->d_name.len]) - SGOTO(exit4, rc); + goto exit4; } -#ifdef HAVE_4ARGS_VFS_RENAME +#if defined(HAVE_4ARGS_VFS_RENAME) rc = vfs_rename(old_dir->d_inode, old_dentry, - new_dir->d_inode, new_dentry); + new_dir->d_inode, new_dentry); +#elif defined(HAVE_5ARGS_VFS_RENAME) + rc = vfs_rename(old_dir->d_inode, old_dentry, + new_dir->d_inode, new_dentry, NULL); #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 */ + rc = vfs_rename(old_dir->d_inode, old_dentry, + new_dir->d_inode, new_dentry, NULL, 0); +#endif exit4: unlock_rename(new_dir, old_dir); exit3: @@ -419,180 +495,16 @@ exit2: dput(old_dentry); path_put(&old_parent); exit: - SRETURN(-rc); + return (-rc); } EXPORT_SYMBOL(vn_rename); -#else -static struct dentry * -vn_lookup_hash(struct nameidata *nd) -{ - return lookup_one_len((const char *)nd->last.name, - nd->nd_dentry, nd->last.len); -} /* lookup_hash() */ - -static void -vn_path_release(struct nameidata *nd) -{ - dput(nd->nd_dentry); - mntput(nd->nd_mnt); -} - -/* Modified do_unlinkat() from linux/fs/namei.c, only uses exported symbols */ -int -vn_remove(const char *path, uio_seg_t seg, int flags) -{ - struct dentry *dentry; - struct nameidata nd; - struct inode *inode = NULL; - int rc = 0; - SENTRY; - - ASSERT(seg == UIO_SYSSPACE); - ASSERT(flags == RMFILE); - - rc = spl_kern_path_parent(path, &nd); - if (rc) - SGOTO(exit, rc); - - rc = -EISDIR; - if (nd.last_type != LAST_NORM) - SGOTO(exit1, rc); - - 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)) { - /* Why not before? Because we want correct rc value */ - if (nd.last.name[nd.last.len]) - SGOTO(slashes, rc); - - inode = dentry->d_inode; - if (inode) - atomic_inc(&inode->i_count); -#ifdef HAVE_2ARGS_VFS_UNLINK - rc = vfs_unlink(nd.nd_dentry->d_inode, dentry); -#else - rc = vfs_unlink(nd.nd_dentry->d_inode, dentry, nd.nd_mnt); -#endif /* HAVE_2ARGS_VFS_UNLINK */ -exit2: - dput(dentry); - } - - spl_inode_unlock(nd.nd_dentry->d_inode); - if (inode) - iput(inode); /* truncate the inode here */ -exit1: - vn_path_release(&nd); -exit: - SRETURN(-rc); - -slashes: - rc = !dentry->d_inode ? -ENOENT : - S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR; - SGOTO(exit2, rc); -} /* vn_remove() */ -EXPORT_SYMBOL(vn_remove); - -/* Modified do_rename() from linux/fs/namei.c, only uses exported symbols */ -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 nameidata oldnd, newnd; - int rc = 0; - SENTRY; - - rc = spl_kern_path_parent(oldname, &oldnd); - if (rc) - SGOTO(exit, rc); - - rc = spl_kern_path_parent(newname, &newnd); - if (rc) - SGOTO(exit1, rc); - - rc = -EXDEV; - if (oldnd.nd_mnt != newnd.nd_mnt) - SGOTO(exit2, rc); - - old_dir = oldnd.nd_dentry; - rc = -EBUSY; - if (oldnd.last_type != LAST_NORM) - SGOTO(exit2, rc); - - new_dir = newnd.nd_dentry; - if (newnd.last_type != LAST_NORM) - SGOTO(exit2, rc); - - trap = lock_rename(new_dir, old_dir); - - old_dentry = vn_lookup_hash(&oldnd); - - rc = PTR_ERR(old_dentry); - if (IS_ERR(old_dentry)) - SGOTO(exit3, 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 (oldnd.last.name[oldnd.last.len]) - SGOTO(exit4, rc); - if (newnd.last.name[newnd.last.len]) - SGOTO(exit4, rc); - } - - /* source should not be ancestor of target */ - rc = -EINVAL; - if (old_dentry == trap) - SGOTO(exit4, rc); - - new_dentry = vn_lookup_hash(&newnd); - rc = PTR_ERR(new_dentry); - if (IS_ERR(new_dentry)) - SGOTO(exit4, rc); - - /* target should not be an ancestor of source */ - rc = -ENOTEMPTY; - if (new_dentry == trap) - SGOTO(exit5, 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 */ -exit5: - dput(new_dentry); -exit4: - dput(old_dentry); -exit3: - unlock_rename(new_dir, old_dir); -exit2: - vn_path_release(&newnd); -exit1: - vn_path_release(&oldnd); -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; int rc; - SENTRY; ASSERT(vp); ASSERT(vp->v_file); @@ -600,14 +512,21 @@ 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); +#if defined(HAVE_4ARGS_VFS_GETATTR) + rc = vfs_getattr(&fp->f_path, &stat, STATX_BASIC_STATS, + AT_STATX_SYNC_AS_STAT); +#elif defined(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); + return (-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; @@ -619,14 +538,15 @@ vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4) vap->va_rdev = stat.rdev; vap->va_nblocks = stat.blocks; - SRETURN(0); + return (0); } EXPORT_SYMBOL(vn_getattr); int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4) { int datasync = 0; - SENTRY; + int error; + int fstrans; ASSERT(vp); ASSERT(vp->v_file); @@ -634,7 +554,19 @@ int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4) if (flags & FDSYNC) datasync = 1; - SRETURN(-spl_filp_fsync(vp->v_file, datasync)); + /* + * May enter XFS which generates a warning when PF_FSTRANS is set. + * To avoid this the flag is cleared over vfs_sync() and then reset. + */ + fstrans = __spl_pf_fstrans_check(); + if (fstrans) + current->flags &= ~(__SPL_PF_FSTRANS); + + error = -spl_filp_fsync(vp->v_file, datasync); + if (fstrans) + current->flags |= __SPL_PF_FSTRANS; + + return (error); } /* vn_fsync() */ EXPORT_SYMBOL(vn_fsync); @@ -642,23 +574,39 @@ int vn_space(vnode_t *vp, int cmd, struct flock *bfp, int flag, offset_t offset, void *x6, void *x7) { int error = EOPNOTSUPP; - SENTRY; +#ifdef FALLOC_FL_PUNCH_HOLE + int fstrans; +#endif if (cmd != F_FREESP || bfp->l_whence != 0) - SRETURN(EOPNOTSUPP); + return (EOPNOTSUPP); ASSERT(vp); ASSERT(vp->v_file); ASSERT(bfp->l_start >= 0 && bfp->l_len > 0); #ifdef FALLOC_FL_PUNCH_HOLE - if (vp->v_file->f_op->fallocate) { - error = -vp->v_file->f_op->fallocate(vp->v_file, - FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, - bfp->l_start, bfp->l_len); - if (!error) - SRETURN(0); - } + /* + * May enter XFS which generates a warning when PF_FSTRANS is set. + * To avoid this the flag is cleared over vfs_sync() and then reset. + */ + fstrans = __spl_pf_fstrans_check(); + if (fstrans) + current->flags &= ~(__SPL_PF_FSTRANS); + + /* + * 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 (fstrans) + current->flags |= __SPL_PF_FSTRANS; + + if (error == 0) + return (0); #endif #ifdef HAVE_INODE_TRUNCATE_RANGE @@ -674,7 +622,7 @@ int vn_space(vnode_t *vp, int cmd, struct flock *bfp, int flag, if (end % PAGE_SIZE != 0) { end &= ~(off_t)(PAGE_SIZE - 1); if (end <= bfp->l_start) - SRETURN(0); + return (0); } --end; @@ -682,24 +630,24 @@ int vn_space(vnode_t *vp, int cmd, struct flock *bfp, int flag, vp->v_file->f_dentry->d_inode, bfp->l_start, end ); - SRETURN(0); + return (0); } #endif - SRETURN(error); + return (error); } EXPORT_SYMBOL(vn_space); /* Function must be called while holding the vn_file_lock */ static file_t * -file_find(int fd) +file_find(int fd, struct task_struct *task) { file_t *fp; ASSERT(spin_is_locked(&vn_file_lock)); list_for_each_entry(fp, &vn_file_list, f_list) { - if (fd == fp->f_fd && fp->f_task == current) { + if (fd == fp->f_fd && fp->f_task == task) { ASSERT(atomic_read(&fp->f_ref) != 0); return fp; } @@ -716,16 +664,31 @@ vn_getf(int fd) file_t *fp; vnode_t *vp; int rc = 0; - SENTRY; + + if (fd < 0) + return (NULL); /* Already open just take an extra reference */ spin_lock(&vn_file_lock); - fp = file_find(fd); + fp = file_find(fd, current); if (fp) { + lfp = fget(fd); + fput(fp->f_file); + /* + * areleasef() can cause us to see a stale reference when + * userspace has reused a file descriptor before areleasef() + * has run. fput() the stale reference and replace it. We + * retain the original reference count such that the concurrent + * areleasef() will decrement its reference and terminate. + */ + if (lfp != fp->f_file) { + fp->f_file = lfp; + fp->f_vnode->v_file = lfp; + } atomic_inc(&fp->f_ref); spin_unlock(&vn_file_lock); - SRETURN(fp); + return (fp); } spin_unlock(&vn_file_lock); @@ -733,7 +696,7 @@ vn_getf(int fd) /* File was not yet opened create the object and setup */ fp = kmem_cache_alloc(vn_file_cache, KM_SLEEP); if (fp == NULL) - SGOTO(out, rc); + goto out; mutex_enter(&fp->f_lock); @@ -744,14 +707,21 @@ vn_getf(int fd) lfp = fget(fd); if (lfp == NULL) - SGOTO(out_mutex, rc); + goto out_mutex; vp = vn_alloc(KM_SLEEP); if (vp == NULL) - SGOTO(out_fget, rc); + goto out_fget; - if (vfs_getattr(lfp->f_vfsmnt, lfp->f_dentry, &stat)) - SGOTO(out_vnode, rc); +#if defined(HAVE_4ARGS_VFS_GETATTR) + rc = vfs_getattr(&lfp->f_path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT); +#elif defined(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) + goto out_vnode; mutex_enter(&vp->v_lock); vp->v_type = vn_mode_to_vtype(stat.mode); @@ -767,7 +737,7 @@ vn_getf(int fd) spin_unlock(&vn_file_lock); mutex_exit(&fp->f_lock); - SRETURN(fp); + return (fp); out_vnode: vn_free(vp); @@ -777,7 +747,7 @@ out_mutex: mutex_exit(&fp->f_lock); kmem_cache_free(vn_file_cache, fp); out: - SRETURN(NULL); + return (NULL); } /* getf() */ EXPORT_SYMBOL(getf); @@ -795,17 +765,26 @@ static void releasef_locked(file_t *fp) void vn_releasef(int fd) +{ + areleasef(fd, P_FINFO(current)); +} +EXPORT_SYMBOL(releasef); + +void +vn_areleasef(int fd, uf_info_t *fip) { file_t *fp; - SENTRY; + struct task_struct *task = (struct task_struct *)fip; + + if (fd < 0) + return; spin_lock(&vn_file_lock); - fp = file_find(fd); + fp = file_find(fd, task); if (fp) { atomic_dec(&fp->f_ref); if (atomic_read(&fp->f_ref) > 0) { spin_unlock(&vn_file_lock); - SEXIT; return; } @@ -814,70 +793,44 @@ vn_releasef(int fd) } spin_unlock(&vn_file_lock); - SEXIT; return; } /* releasef() */ -EXPORT_SYMBOL(releasef); +EXPORT_SYMBOL(areleasef); -#ifndef HAVE_SET_FS_PWD -# ifdef HAVE_2ARGS_SET_FS_PWD -/* Used from 2.6.25 - 2.6.31+ */ -void -set_fs_pwd(struct fs_struct *fs, struct path *path) + +static void +#ifdef HAVE_SET_FS_PWD_WITH_CONST +vn_set_fs_pwd(struct fs_struct *fs, const struct path *path) +#else +vn_set_fs_pwd(struct fs_struct *fs, struct path *path) +#endif /* HAVE_SET_FS_PWD_WITH_CONST */ { struct path old_pwd; -# ifdef HAVE_FS_STRUCT_SPINLOCK +#ifdef HAVE_FS_STRUCT_SPINLOCK spin_lock(&fs->lock); old_pwd = fs->pwd; fs->pwd = *path; path_get(path); spin_unlock(&fs->lock); -# else +#else write_lock(&fs->lock); old_pwd = fs->pwd; fs->pwd = *path; path_get(path); write_unlock(&fs->lock); -# endif /* HAVE_FS_STRUCT_SPINLOCK */ +#endif /* HAVE_FS_STRUCT_SPINLOCK */ 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) struct path path; -#else - struct nameidata nd; -#endif /* HAVE_2ARGS_SET_FS_PWD */ mm_segment_t saved_fs; int rc; - SENTRY; /* * user_path_dir() and __user_walk() both expect 'filename' to be @@ -887,54 +840,22 @@ 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) - SGOTO(out, rc); + goto out; rc = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); if (rc) - SGOTO(dput_and_out, rc); + goto dput_and_out; - set_fs_pwd(current->fs, &path); + vn_set_fs_pwd(current->fs, &path); dput_and_out: path_put(&path); -# 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.path); - -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); - SRETURN(-rc); + return (-rc); } /* vn_set_pwd() */ EXPORT_SYMBOL(vn_set_pwd); @@ -976,47 +897,21 @@ 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 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, KMC_KMEM); + NULL, NULL, NULL, 0); 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, KMC_KMEM); - SRETURN(0); + NULL, NULL, NULL, 0); + return (0); } /* vn_init() */ void @@ -1024,7 +919,6 @@ spl_vn_fini(void) { file_t *fp, *next_fp; int leaked = 0; - SENTRY; spin_lock(&vn_file_lock); @@ -1037,11 +931,10 @@ spl_vn_fini(void) spin_unlock(&vn_file_lock); if (leaked > 0) - SWARN("Warning %d files leaked\n", leaked); + printk(KERN_WARNING "WARNING: %d vnode files leaked\n", leaked); kmem_cache_destroy(vn_file_cache); kmem_cache_destroy(vn_cache); - SEXIT; return; } /* vn_fini() */