STATIC int linvfs_link( struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct inode *ip; /* inode of guy being linked to */ vnode_t *tdvp; /* target directory for new name/link */ vnode_t *vp; /* vp of name being linked */ int error; ip = old_dentry->d_inode; /* inode being linked to */ if (S_ISDIR(ip->i_mode)) return -EPERM; tdvp = LINVFS_GET_VP(dir); vp = LINVFS_GET_VP(ip); VOP_LINK(tdvp, vp, dentry, NULL, error); if (!error) { VMODIFY(tdvp); VN_HOLD(vp); validate_fields(ip); d_instantiate(dentry, ip); } return -error; }
/* * Hook in SELinux. This is not quite correct yet, what we really need * here (as we do for default ACLs) is a mechanism by which creation of * these attrs can be journalled at inode creation time (along with the * inode, of course, such that log replay can't cause these to be lost). */ STATIC int linvfs_init_security( struct vnode *vp, struct inode *dir) { struct inode *ip = LINVFS_GET_IP(vp); size_t length; void *value; char *name; int error; error = security_inode_init_security(ip, dir, &name, &value, &length); if (error) { if (error == -EOPNOTSUPP) return 0; return -error; } VOP_ATTR_SET(vp, name, value, length, ATTR_SECURE, NULL, error); if (!error) VMODIFY(vp); kfree(name); kfree(value); return error; }
STATIC long xfs_file_ioctl_invis( struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = filp->f_dentry->d_inode; vnode_t *vp = vn_from_inode(inode); int error; VOP_IOCTL(vp, inode, filp, IO_INVIS, cmd, (void __user *)arg, error); VMODIFY(vp); /* NOTE: some of the ioctl's return positive #'s as a * byte count indicating success, such as * readlink_by_handle. So we don't "sign flip" * like most other routines. This means true * errors need to be returned as a negative value. */ return error; }
STATIC long xfs_file_ioctl( struct file *filp, unsigned int cmd, unsigned long p) { int error; struct inode *inode = filp->f_path.dentry->d_inode; bhv_vnode_t *vp = vn_from_inode(inode); error = bhv_vop_ioctl(vp, inode, filp, 0, cmd, (void __user *)p); VMODIFY(vp); /* NOTE: some of the ioctl's return positive #'s as a * byte count indicating success, such as * readlink_by_handle. So we don't "sign flip" * like most other routines. This means true * errors need to be returned as a negative value. */ return error; }
STATIC int linvfs_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int error; vnode_t *vp = LINVFS_GET_VP(inode); unlock_kernel(); VOP_IOCTL(vp, inode, filp, 0, cmd, (void __user *)arg, error); VMODIFY(vp); lock_kernel(); /* NOTE: some of the ioctl's return positive #'s as a * byte count indicating success, such as * readlink_by_handle. So we don't "sign flip" * like most other routines. This means true * errors need to be returned as a negative value. */ return error; }
STATIC int linvfs_mknod( struct inode *dir, struct dentry *dentry, int mode, int rdev) { struct inode *ip; vattr_t va; vnode_t *vp = NULL, *dvp = LINVFS_GET_VP(dir); xfs_acl_t *default_acl = NULL; attrexists_t test_default_acl = _ACL_DEFAULT_EXISTS; int error; if (test_default_acl && test_default_acl(dvp)) { if (!_ACL_ALLOC(default_acl)) return -ENOMEM; if (!_ACL_GET_DEFAULT(dvp, default_acl)) { _ACL_FREE(default_acl); default_acl = NULL; } } #ifdef CONFIG_XFS_POSIX_ACL /* * Conditionally compiled so that the ACL base kernel changes can be * split out into separate patches - remove this once MS_POSIXACL is * accepted, or some other way to implement this exists. */ if (IS_POSIXACL(dir) && !default_acl && has_fs_struct(current)) mode &= ~current->fs->umask; #endif memset(&va, 0, sizeof(va)); va.va_mask = XFS_AT_TYPE|XFS_AT_MODE; va.va_type = IFTOVT(mode); va.va_mode = mode; switch (mode & S_IFMT) { case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: va.va_rdev = XFS_MKDEV(MAJOR(rdev), MINOR(rdev)); va.va_mask |= XFS_AT_RDEV; /*FALLTHROUGH*/ case S_IFREG: VOP_CREATE(dvp, dentry, &va, &vp, NULL, error); break; case S_IFDIR: VOP_MKDIR(dvp, dentry, &va, &vp, NULL, error); break; default: error = EINVAL; break; } if (default_acl) { if (!error) { error = _ACL_INHERIT(vp, &va, default_acl); if (!error) { VMODIFY(vp); } else { struct dentry teardown = {}; int err2; /* Oh, the horror. * If we can't add the ACL we must back out. * ENOSPC can hit here, among other things. */ teardown.d_inode = ip = LINVFS_GET_IP(vp); teardown.d_name = dentry->d_name; vn_mark_bad(vp); if (S_ISDIR(mode)) VOP_RMDIR(dvp, &teardown, NULL, err2); else VOP_REMOVE(dvp, &teardown, NULL, err2); VN_RELE(vp); } } _ACL_FREE(default_acl); } if (!error) { ASSERT(vp); ip = LINVFS_GET_IP(vp); if (S_ISCHR(mode) || S_ISBLK(mode)) ip->i_rdev = to_kdev_t(rdev); else if (S_ISDIR(mode)) validate_fields(ip); d_instantiate(dentry, ip); validate_fields(dir); } return -error; }
STATIC long xfs_compat_ioctl( int mode, struct file *file, unsigned cmd, unsigned long arg) { struct inode *inode = file->f_dentry->d_inode; bhv_vnode_t *vp = vn_from_inode(inode); int error; switch (cmd) { case XFS_IOC_DIOINFO: case XFS_IOC_FSGEOMETRY_V1: case XFS_IOC_FSGEOMETRY: case XFS_IOC_GETVERSION: case XFS_IOC_GETXFLAGS: case XFS_IOC_SETXFLAGS: case XFS_IOC_FSGETXATTR: case XFS_IOC_FSSETXATTR: case XFS_IOC_FSGETXATTRA: case XFS_IOC_FSSETDM: case XFS_IOC_GETBMAP: case XFS_IOC_GETBMAPA: case XFS_IOC_GETBMAPX: /* not handled case XFS_IOC_FD_TO_HANDLE: case XFS_IOC_PATH_TO_HANDLE: case XFS_IOC_PATH_TO_FSHANDLE: case XFS_IOC_OPEN_BY_HANDLE: case XFS_IOC_FSSETDM_BY_HANDLE: case XFS_IOC_READLINK_BY_HANDLE: case XFS_IOC_ATTRLIST_BY_HANDLE: case XFS_IOC_ATTRMULTI_BY_HANDLE: */ case XFS_IOC_FSCOUNTS: case XFS_IOC_SET_RESBLKS: case XFS_IOC_GET_RESBLKS: case XFS_IOC_FSGROWFSDATA: case XFS_IOC_FSGROWFSLOG: case XFS_IOC_FSGROWFSRT: case XFS_IOC_FREEZE: case XFS_IOC_THAW: case XFS_IOC_GOINGDOWN: case XFS_IOC_ERROR_INJECTION: case XFS_IOC_ERROR_CLEARALL: break; #ifdef BROKEN_X86_ALIGNMENT /* xfs_flock_t has wrong u32 vs u64 alignment */ case XFS_IOC_ALLOCSP_32: case XFS_IOC_FREESP_32: case XFS_IOC_ALLOCSP64_32: case XFS_IOC_FREESP64_32: case XFS_IOC_RESVSP_32: case XFS_IOC_UNRESVSP_32: case XFS_IOC_RESVSP64_32: case XFS_IOC_UNRESVSP64_32: arg = xfs_ioctl32_flock(arg); cmd = _NATIVE_IOC(cmd, struct xfs_flock64); break; #else /* These are handled fine if no alignment issues */ case XFS_IOC_ALLOCSP: case XFS_IOC_FREESP: case XFS_IOC_RESVSP: case XFS_IOC_UNRESVSP: case XFS_IOC_ALLOCSP64: case XFS_IOC_FREESP64: case XFS_IOC_RESVSP64: case XFS_IOC_UNRESVSP64: break; /* xfs_bstat_t still has wrong u32 vs u64 alignment */ case XFS_IOC_SWAPEXT: break; case XFS_IOC_FSBULKSTAT_SINGLE: case XFS_IOC_FSBULKSTAT: case XFS_IOC_FSINUMBERS: arg = xfs_ioctl32_bulkstat(arg); break; #endif default: return -ENOIOCTLCMD; } error = bhv_vop_ioctl(vp, inode, file, mode, cmd, (void __user *)arg); VMODIFY(vp); return error; }
STATIC int linvfs_mknod( struct inode *dir, struct dentry *dentry, int mode, int rdev) { struct inode *ip; vattr_t va; vnode_t *vp = NULL, *dvp = LINVFS_GET_VP(dir); xattr_exists_t test_default_acl = _ACL_DEFAULT_EXISTS; int have_default_acl = 0; int error = EINVAL; if (test_default_acl) have_default_acl = test_default_acl(dvp); #ifdef CONFIG_FS_POSIX_ACL /* * Conditionally compiled so that the ACL base kernel changes can be * split out into separate patches - remove this once MS_POSIXACL is * accepted, or some other way to implement this exists. */ if (IS_POSIXACL(dir) && !have_default_acl && has_fs_struct(current)) mode &= ~current->fs->umask; #endif bzero(&va, sizeof(va)); va.va_mask = AT_TYPE|AT_MODE; va.va_type = IFTOVT(mode); va.va_mode = mode; switch (mode & S_IFMT) { case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: va.va_rdev = rdev; va.va_mask |= AT_RDEV; /*FALLTHROUGH*/ case S_IFREG: VOP_CREATE(dvp, dentry, &va, &vp, NULL, error); break; case S_IFDIR: VOP_MKDIR(dvp, dentry, &va, &vp, NULL, error); break; default: error = EINVAL; break; } if (!error) { ASSERT(vp); ip = LINVFS_GET_IP(vp); if (!ip) { VN_RELE(vp); return -ENOMEM; } if (S_ISCHR(mode) || S_ISBLK(mode)) ip->i_rdev = to_kdev_t(rdev); /* linvfs_revalidate_core returns (-) errors */ error = -linvfs_revalidate_core(ip, ATTR_COMM); validate_fields(dir); d_instantiate(dentry, ip); mark_inode_dirty_sync(ip); mark_inode_dirty_sync(dir); } if (!error && have_default_acl) { _ACL_DECL (pdacl); if (!_ACL_ALLOC(pdacl)) { error = -ENOMEM; } else { if (_ACL_GET_DEFAULT(dvp, pdacl)) error = _ACL_INHERIT(vp, &va, pdacl); VMODIFY(vp); _ACL_FREE(pdacl); } } return -error; }
STATIC int linvfs_setxattr( struct dentry *dentry, const char *name, void *data, size_t size, int flags) { int error; int xflags = 0; char *p = (char *)name; struct inode *inode = dentry->d_inode; vnode_t *vp = LINVFS_GET_VP(inode); if (strncmp(name, xfs_namespaces[SYSTEM_NAMES].name, xfs_namespaces[SYSTEM_NAMES].namelen) == 0) { error = -EINVAL; if (flags & XATTR_CREATE) return error; error = -ENOATTR; p += xfs_namespaces[SYSTEM_NAMES].namelen; if (strcmp(p, POSIXACL_ACCESS) == 0) { if (vp->v_flag & VMODIFIED) { error = linvfs_revalidate_core(inode, 0); if (error) return error; } error = xfs_acl_vset(vp, data, size, _ACL_TYPE_ACCESS); if (!error) { VMODIFY(vp); error = linvfs_revalidate_core(inode, 0); } } else if (strcmp(p, POSIXACL_DEFAULT) == 0) { error = linvfs_revalidate_core(inode, 0); if (error) return error; error = xfs_acl_vset(vp, data, size, _ACL_TYPE_DEFAULT); if (!error) { VMODIFY(vp); error = linvfs_revalidate_core(inode, 0); } } else if (strcmp(p, POSIXCAP) == 0) { error = xfs_cap_vset(vp, data, size); } return error; } /* Convert Linux syscall to XFS internal ATTR flags */ if (flags & XATTR_CREATE) xflags |= ATTR_CREATE; if (flags & XATTR_REPLACE) xflags |= ATTR_REPLACE; if (strncmp(name, xfs_namespaces[ROOT_NAMES].name, xfs_namespaces[ROOT_NAMES].namelen) == 0) { if (!capable(CAP_SYS_ADMIN)) return -EPERM; xflags |= ATTR_ROOT; p += xfs_namespaces[ROOT_NAMES].namelen; VOP_ATTR_SET(vp, p, data, size, xflags, NULL, error); return -error; } if (strncmp(name, xfs_namespaces[USER_NAMES].name, xfs_namespaces[USER_NAMES].namelen) == 0) { if (!capable_user_xattr(inode)) return -EPERM; p += xfs_namespaces[USER_NAMES].namelen; VOP_ATTR_SET(vp, p, data, size, xflags, NULL, error); return -error; } return -ENOATTR; }
STATIC int linvfs_mknod( struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { struct inode *ip; vattr_t va; vnode_t *vp = NULL, *dvp = LINVFS_GET_VP(dir); xfs_acl_t *default_acl = NULL; attrexists_t test_default_acl = _ACL_DEFAULT_EXISTS; int error; /* * Irix uses Missed'em'V split, but doesn't want to see * the upper 5 bits of (14bit) major. */ if (!sysv_valid_dev(rdev) || MAJOR(rdev) & ~0x1ff) return -EINVAL; if (test_default_acl && test_default_acl(dvp)) { if (!_ACL_ALLOC(default_acl)) return -ENOMEM; if (!_ACL_GET_DEFAULT(dvp, default_acl)) { _ACL_FREE(default_acl); default_acl = NULL; } } if (IS_POSIXACL(dir) && !default_acl && has_fs_struct(current)) mode &= ~current->fs->umask; memset(&va, 0, sizeof(va)); va.va_mask = XFS_AT_TYPE|XFS_AT_MODE; va.va_mode = mode; switch (mode & S_IFMT) { case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: va.va_rdev = sysv_encode_dev(rdev); va.va_mask |= XFS_AT_RDEV; /*FALLTHROUGH*/ case S_IFREG: VOP_CREATE(dvp, dentry, &va, &vp, NULL, error); break; case S_IFDIR: VOP_MKDIR(dvp, dentry, &va, &vp, NULL, error); break; default: error = EINVAL; break; } if (!error) { error = linvfs_init_security(vp, dir); if (error) cleanup_inode(dvp, vp, dentry, mode); } if (default_acl) { if (!error) { error = _ACL_INHERIT(vp, &va, default_acl); if (!error) VMODIFY(vp); else cleanup_inode(dvp, vp, dentry, mode); } _ACL_FREE(default_acl); } if (!error) { ASSERT(vp); ip = LINVFS_GET_IP(vp); if (S_ISCHR(mode) || S_ISBLK(mode)) ip->i_rdev = rdev; else if (S_ISDIR(mode)) validate_fields(ip); d_instantiate(dentry, ip); validate_fields(dir); } return -error; }