X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=module%2Fspl%2Fspl-vnode.c;h=0784ff2611096304b532901b01b2559c4ae1453b;hb=80093b6f72408aee7e88abbf52537d9fc1feb008;hp=765dc4e2ee4e43327c46f663c1dc9b2e3ac0f7a0;hpb=4295b530eeb68aaacbbeb5b2197ed14bf9295c2b;p=mirror_spl-debian.git diff --git a/module/spl/spl-vnode.c b/module/spl/spl-vnode.c index 765dc4e..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,9 +41,20 @@ 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); +#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); @@ -247,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); @@ -283,12 +301,136 @@ 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) { @@ -316,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); @@ -370,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); @@ -449,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; @@ -464,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_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); @@ -505,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) @@ -514,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; } @@ -553,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); @@ -564,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); @@ -634,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; @@ -658,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; @@ -701,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) @@ -730,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); @@ -790,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; @@ -823,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;