int btrfs_init_acl(struct btrfs_trans_handle *trans, struct inode *inode, struct inode *dir) { struct posix_acl *acl = NULL; int ret = 0; /* this happens with subvols */ if (!dir) return 0; if (!S_ISLNK(inode->i_mode)) { if (IS_POSIXACL(dir)) { acl = btrfs_get_acl(dir, ACL_TYPE_DEFAULT); if (IS_ERR(acl)) return PTR_ERR(acl); } if (!acl) inode->i_mode &= ~current_umask(); } if (IS_POSIXACL(dir) && acl) { struct posix_acl *clone; mode_t mode; if (S_ISDIR(inode->i_mode)) { ret = btrfs_set_acl(trans, inode, acl, ACL_TYPE_DEFAULT); if (ret) goto failed; } clone = posix_acl_clone(acl, GFP_NOFS); ret = -ENOMEM; if (!clone) goto failed; mode = inode->i_mode; ret = posix_acl_create_masq(clone, &mode); if (ret >= 0) { inode->i_mode = mode; if (ret > 0) { /* we need an acl */ ret = btrfs_set_acl(trans, inode, clone, ACL_TYPE_ACCESS); } } posix_acl_release(clone); } failed: posix_acl_release(acl); return ret; }
struct posix_acl *get_acl(struct inode *inode, int type) { struct posix_acl *acl; acl = get_cached_acl(inode, type); if (acl != ACL_NOT_CACHED) return acl; if (!IS_POSIXACL(inode)) return NULL; /* * A filesystem can force a ACL callback by just never filling the * ACL cache. But normally you'd fill the cache either at inode * instantiation time, or on the first ->get_acl call. * * If the filesystem doesn't have a get_acl() function at all, we'll * just create the negative cache entry. */ if (!inode->i_op->get_acl) { set_cached_acl(inode, type, NULL); return NULL; } return inode->i_op->get_acl(inode, type); }
static int gfs2_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_holder i_gh; int error; error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh); if (error) return error; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out; error = inode_change_ok(inode, attr); if (error) goto out; if (attr->ia_valid & ATTR_SIZE) error = setattr_size(inode, attr); else if (attr->ia_valid & (ATTR_UID | ATTR_GID)) error = setattr_chown(inode, attr); else if ((attr->ia_valid & ATTR_MODE) && IS_POSIXACL(inode)) error = gfs2_acl_chmod(ip, attr); else error = gfs2_setattr_simple(ip, attr); out: gfs2_glock_dq_uninit(&i_gh); if (!error) mark_inode_dirty(inode); return error; }
static int ovl_create_upper(struct dentry *dentry, struct inode *inode, struct cattr *attr, struct dentry *hardlink) { struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct dentry *newdentry; int err; if (!hardlink && !IS_POSIXACL(udir)) attr->mode &= ~current_umask(); inode_lock_nested(udir, I_MUTEX_PARENT); newdentry = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(newdentry); if (IS_ERR(newdentry)) goto out_unlock; err = ovl_create_real(udir, newdentry, attr, hardlink, false); if (err) goto out_dput; if (ovl_type_merge(dentry->d_parent)) { /* Setting opaque here is just an optimization, allow to fail */ ovl_set_opaque(dentry, newdentry); } ovl_instantiate(dentry, inode, newdentry, !!hardlink); newdentry = NULL; out_dput: dput(newdentry); out_unlock: inode_unlock(udir); return err; }
static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name, const void *value, size_t size, int flags, int type) { int ret; struct posix_acl *acl = NULL; if (!inode_owner_or_capable(dentry->d_inode)) return -EPERM; if (!IS_POSIXACL(dentry->d_inode)) return -EOPNOTSUPP; if (value) { acl = posix_acl_from_xattr(&init_user_ns, value, size); if (IS_ERR(acl)) return PTR_ERR(acl); if (acl) { ret = posix_acl_valid(acl); if (ret) goto out; } } ret = btrfs_set_acl(NULL, dentry->d_inode, acl, type); out: posix_acl_release(acl); return ret; }
static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name, const void *value, size_t size, int flags, int type) { int ret; struct posix_acl *acl = NULL; if (!is_owner_or_cap(dentry->d_inode)) return -EPERM; if (!IS_POSIXACL(dentry->d_inode)) return -EOPNOTSUPP; if (value) { acl = posix_acl_from_xattr(value, size); if (acl == NULL) { value = NULL; size = 0; } else if (IS_ERR(acl)) { return PTR_ERR(acl); } } ret = btrfs_set_acl(NULL, dentry->d_inode, acl, type); posix_acl_release(acl); return ret; }
static int ovl_create_upper(struct dentry *dentry, struct inode *inode, struct kstat *stat, const char *link, struct dentry *hardlink) { struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct dentry *newdentry; int err; if (!hardlink && !IS_POSIXACL(udir)) stat->mode &= ~current_umask(); inode_lock_nested(udir, I_MUTEX_PARENT); newdentry = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len); err = PTR_ERR(newdentry); if (IS_ERR(newdentry)) goto out_unlock; err = ovl_create_real(udir, newdentry, stat, link, hardlink, false); if (err) goto out_dput; ovl_instantiate(dentry, inode, newdentry, !!hardlink); newdentry = NULL; out_dput: dput(newdentry); out_unlock: inode_unlock(udir); return err; }
int btrfs_acl_chmod(struct inode *inode) { struct posix_acl *acl, *clone; int ret = 0; if (S_ISLNK(inode->i_mode)) return -EOPNOTSUPP; if (!IS_POSIXACL(inode)) return 0; acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS); if (IS_ERR(acl) || !acl) return PTR_ERR(acl); clone = posix_acl_clone(acl, GFP_KERNEL); posix_acl_release(acl); if (!clone) return -ENOMEM; ret = posix_acl_chmod_masq(clone, inode->i_mode); if (!ret) ret = btrfs_set_acl(NULL, inode, clone, ACL_TYPE_ACCESS); posix_acl_release(clone); return ret; }
/* * btrfs_init_acl is already generally called under fs_mutex, so the locking * stuff has been fixed to work with that. If the locking stuff changes, we * need to re-evaluate the acl locking stuff. */ int btrfs_init_acl(struct btrfs_trans_handle *trans, struct inode *inode, struct inode *dir) { struct posix_acl *acl = NULL; int ret = 0; /* this happens with subvols */ if (!dir) return 0; if (!S_ISLNK(inode->i_mode)) { if (IS_POSIXACL(dir)) { acl = btrfs_get_acl(dir, ACL_TYPE_DEFAULT); if (IS_ERR(acl)) return PTR_ERR(acl); } if (!acl) inode->i_mode &= ~current_umask(); } if (IS_POSIXACL(dir) && acl) { if (S_ISDIR(inode->i_mode)) { ret = btrfs_set_acl(trans, inode, acl, ACL_TYPE_DEFAULT); if (ret) goto failed; } ret = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode); if (ret < 0) return ret; if (ret > 0) { /* we need an acl */ ret = btrfs_set_acl(trans, inode, acl, ACL_TYPE_ACCESS); } else if (ret < 0) { cache_no_acl(inode); } } else { cache_no_acl(inode); } failed: posix_acl_release(acl); return ret; }
STATIC int xfs_vn_mknod( struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) { struct inode *inode; struct xfs_inode *ip = NULL; struct posix_acl *default_acl = NULL; struct xfs_name name; int error; if (S_ISCHR(mode) || S_ISBLK(mode)) { if (unlikely(!sysv_valid_dev(rdev) || MAJOR(rdev) & ~0x1ff)) return -EINVAL; rdev = sysv_encode_dev(rdev); } else { rdev = 0; } if (IS_POSIXACL(dir)) { default_acl = xfs_get_acl(dir, ACL_TYPE_DEFAULT); if (IS_ERR(default_acl)) return PTR_ERR(default_acl); if (!default_acl) mode &= ~current_umask(); } xfs_dentry_to_name(&name, dentry); error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip); if (unlikely(error)) goto out_free_acl; inode = VFS_I(ip); error = xfs_init_security(inode, dir, &dentry->d_name); if (unlikely(error)) goto out_cleanup_inode; if (default_acl) { error = -xfs_inherit_acl(inode, default_acl); default_acl = NULL; if (unlikely(error)) goto out_cleanup_inode; } d_instantiate(dentry, inode); return -error; out_cleanup_inode: xfs_cleanup_inode(dir, inode, dentry); out_free_acl: posix_acl_release(default_acl); return -error; }
static int __maybe_unused ovl_posix_acl_xattr_set(const struct xattr_handler *handler, struct dentry *dentry, struct inode *inode, const char *name, const void *value, size_t size, int flags) { struct dentry *workdir = ovl_workdir(dentry); struct inode *realinode = ovl_inode_real(inode); struct posix_acl *acl = NULL; int err; /* Check that everything is OK before copy-up */ if (value) { acl = posix_acl_from_xattr(&init_user_ns, value, size); if (IS_ERR(acl)) return PTR_ERR(acl); } err = -EOPNOTSUPP; if (!IS_POSIXACL(d_inode(workdir))) goto out_acl_release; if (!realinode->i_op->set_acl) goto out_acl_release; if (handler->flags == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode)) { err = acl ? -EACCES : 0; goto out_acl_release; } err = -EPERM; if (!inode_owner_or_capable(inode)) goto out_acl_release; posix_acl_release(acl); /* * Check if sgid bit needs to be cleared (actual setacl operation will * be done with mounter's capabilities and so that won't do it for us). */ if (unlikely(inode->i_mode & S_ISGID) && handler->flags == ACL_TYPE_ACCESS && !in_group_p(inode->i_gid) && !capable_wrt_inode_uidgid(inode, CAP_FSETID)) { struct iattr iattr = { .ia_valid = ATTR_KILL_SGID }; err = ovl_setattr(dentry, &iattr); if (err) return err; } err = ovl_xattr_set(dentry, inode, handler->name, value, size, flags); if (!err) ovl_copyattr(ovl_inode_real(inode), inode); return err; out_acl_release: posix_acl_release(acl); return err; }
/* * Inode operation get_posix_acl(). * * inode->i_sem: don't care * BKL: held */ struct posix_acl * ext3_get_acl(struct inode *inode, int type) { const size_t max_size = ext3_acl_size(EXT3_ACL_MAX_ENTRIES); struct ext3_inode_info *ei = EXT3_I(inode); int name_index; char *value; struct posix_acl *acl; int retval; if (!IS_POSIXACL(inode)) return 0; switch(type) { case ACL_TYPE_ACCESS: if (ei->i_acl != EXT3_ACL_NOT_CACHED) return posix_acl_dup(ei->i_acl); name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS; break; case ACL_TYPE_DEFAULT: if (ei->i_default_acl != EXT3_ACL_NOT_CACHED) return posix_acl_dup(ei->i_default_acl); name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT; break; default: return ERR_PTR(-EINVAL); } value = kmalloc(max_size, GFP_KERNEL); if (!value) return ERR_PTR(-ENOMEM); retval = ext3_xattr_get(inode, name_index, "", value, max_size); acl = ERR_PTR(retval); if (retval > 0) acl = ext3_acl_from_disk(value, retval); else if (retval == -ENODATA || retval == -ENOSYS) acl = NULL; kfree(value); if (!IS_ERR(acl)) { switch(type) { case ACL_TYPE_ACCESS: ei->i_acl = posix_acl_dup(acl); break; case ACL_TYPE_DEFAULT: ei->i_default_acl = posix_acl_dup(acl); break; } } return acl; }
struct posix_acl *btrfs_get_acl(struct inode *inode, int type) { int size; const char *name; char *value = NULL; struct posix_acl *acl; if (!IS_POSIXACL(inode)) return NULL; acl = get_cached_acl(inode, type); if (acl != ACL_NOT_CACHED) return acl; switch (type) { case ACL_TYPE_ACCESS: name = POSIX_ACL_XATTR_ACCESS; break; case ACL_TYPE_DEFAULT: name = POSIX_ACL_XATTR_DEFAULT; break; default: BUG(); } size = __btrfs_getxattr(inode, name, "", 0); if (size > 0) { value = kzalloc(size, GFP_NOFS); if (!value) return ERR_PTR(-ENOMEM); size = __btrfs_getxattr(inode, name, value, size); if (size > 0) { acl = posix_acl_from_xattr(value, size); if (IS_ERR(acl)) { kfree(value); return acl; } set_cached_acl(inode, type, acl); } kfree(value); } else if (size == -ENOENT || size == -ENODATA || size == 0) { /* FIXME, who returns -ENOENT? I think nobody */ acl = NULL; set_cached_acl(inode, type, acl); } else { acl = ERR_PTR(-EIO); } return acl; }
/* * Set the Access and/or Default ACL of a file. */ static __be32 nfsacld_proc_setacl(struct svc_rqst * rqstp, struct nfsd3_setaclargs *argp, struct nfsd_attrstat *resp) { struct inode *inode; svc_fh *fh; __be32 nfserr = 0; int error; dprintk("nfsd: SETACL(2acl) %s\n", SVCFH_fmt(&argp->fh)); fh = fh_copy(&resp->fh, &argp->fh); nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); if (nfserr) goto out; inode = fh->fh_dentry->d_inode; if (!IS_POSIXACL(inode) || !inode->i_op->set_acl) { error = -EOPNOTSUPP; goto out_errno; } error = fh_want_write(fh); if (error) goto out_errno; error = inode->i_op->set_acl(inode, argp->acl_access, ACL_TYPE_ACCESS); if (error) goto out_drop_write; error = inode->i_op->set_acl(inode, argp->acl_default, ACL_TYPE_DEFAULT); if (error) goto out_drop_write; fh_drop_write(fh); nfserr = fh_getattr(fh, &resp->stat); out: /* argp->acl_{access,default} may have been allocated in nfssvc_decode_setaclargs. */ posix_acl_release(argp->acl_access); posix_acl_release(argp->acl_default); return nfserr; out_drop_write: fh_drop_write(fh); out_errno: nfserr = nfserrno(error); goto out; }
struct posix_acl *ovl_get_acl(struct inode *inode, int type) { struct inode *realinode = ovl_inode_real(inode); const struct cred *old_cred; struct posix_acl *acl; if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode)) return NULL; old_cred = ovl_override_creds(inode->i_sb); acl = get_acl(realinode, type); revert_creds(old_cred); return acl; }
static int btrfs_xattr_acl_get(struct dentry *dentry, const char *name, void *value, size_t size, int type) { struct posix_acl *acl; int ret = 0; if (!IS_POSIXACL(dentry->d_inode)) return -EOPNOTSUPP; acl = btrfs_get_acl(dentry->d_inode, type); if (IS_ERR(acl)) return PTR_ERR(acl); if (acl == NULL) return -ENODATA; ret = posix_acl_to_xattr(&init_user_ns, acl, value, size); posix_acl_release(acl); return ret; }
/* * Set the Access and/or Default ACL of a file. */ static __be32 nfsd3_proc_setacl(struct svc_rqst * rqstp, struct nfsd3_setaclargs *argp, struct nfsd3_attrstat *resp) { struct inode *inode; svc_fh *fh; __be32 nfserr = 0; int error; fh = fh_copy(&resp->fh, &argp->fh); nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); if (nfserr) goto out; inode = d_inode(fh->fh_dentry); if (!IS_POSIXACL(inode) || !inode->i_op->set_acl) { error = -EOPNOTSUPP; goto out_errno; } error = fh_want_write(fh); if (error) goto out_errno; error = inode->i_op->set_acl(inode, argp->acl_access, ACL_TYPE_ACCESS); if (error) goto out_drop_write; error = inode->i_op->set_acl(inode, argp->acl_default, ACL_TYPE_DEFAULT); out_drop_write: fh_drop_write(fh); out_errno: nfserr = nfserrno(error); out: /* argp->acl_{access,default} may have been allocated in nfs3svc_decode_setaclargs. */ posix_acl_release(argp->acl_access); posix_acl_release(argp->acl_default); RETURN_STATUS(nfserr); }
int btrfs_acl_chmod(struct inode *inode) { struct posix_acl *acl; int ret = 0; if (S_ISLNK(inode->i_mode)) return -EOPNOTSUPP; if (!IS_POSIXACL(inode)) return 0; acl = btrfs_get_acl(inode, ACL_TYPE_ACCESS); if (IS_ERR_OR_NULL(acl)) return PTR_ERR(acl); ret = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); if (ret) return ret; ret = btrfs_set_acl(NULL, inode, acl, ACL_TYPE_ACCESS); posix_acl_release(acl); return ret; }
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 xfs_vn_mknod( struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { struct inode *inode; struct xfs_inode *ip = NULL; xfs_acl_t *default_acl = NULL; struct xfs_name name; int (*test_default_acl)(struct inode *) = _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 (unlikely(!sysv_valid_dev(rdev) || MAJOR(rdev) & ~0x1ff)) return -EINVAL; if (test_default_acl && test_default_acl(dir)) { if (!_ACL_ALLOC(default_acl)) { return -ENOMEM; } if (!_ACL_GET_DEFAULT(dir, default_acl)) { _ACL_FREE(default_acl); default_acl = NULL; } } xfs_dentry_to_name(&name, dentry); if (IS_POSIXACL(dir) && !default_acl) mode &= ~current->fs->umask; switch (mode & S_IFMT) { case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: rdev = sysv_encode_dev(rdev); case S_IFREG: error = xfs_create(XFS_I(dir), &name, mode, rdev, &ip, NULL); break; case S_IFDIR: error = xfs_mkdir(XFS_I(dir), &name, mode, &ip, NULL); break; default: error = EINVAL; break; } if (unlikely(error)) goto out_free_acl; inode = VFS_I(ip); error = xfs_init_security(inode, dir); if (unlikely(error)) goto out_cleanup_inode; if (default_acl) { error = _ACL_INHERIT(inode, mode, default_acl); if (unlikely(error)) goto out_cleanup_inode; _ACL_FREE(default_acl); } d_instantiate(dentry, inode); return -error; out_cleanup_inode: xfs_cleanup_inode(dir, inode, dentry); out_free_acl: if (default_acl) _ACL_FREE(default_acl); return -error; }
STATIC int xfs_vn_mknod( struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) { struct inode *ip; bhv_vnode_t *vp = NULL, *dvp = vn_from_inode(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 (unlikely(!sysv_valid_dev(rdev) || MAJOR(rdev) & ~0x1ff)) return -EINVAL; if (unlikely(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 && xfs_has_fs_struct(current)) mode &= ~current->fs->umask; switch (mode & S_IFMT) { case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: rdev = sysv_encode_dev(rdev); case S_IFREG: error = xfs_create(XFS_I(dir), dentry, mode, rdev, &vp, NULL); break; case S_IFDIR: error = xfs_mkdir(XFS_I(dir), dentry, mode, &vp, NULL); break; default: error = EINVAL; break; } if (unlikely(!error)) { error = xfs_init_security(vp, dir); if (error) xfs_cleanup_inode(dir, vp, dentry, mode); } if (unlikely(default_acl)) { if (!error) { error = _ACL_INHERIT(vp, mode, default_acl); if (!error) xfs_iflags_set(XFS_I(vp), XFS_IMODIFIED); else xfs_cleanup_inode(dir, vp, dentry, mode); } _ACL_FREE(default_acl); } if (likely(!error)) { ASSERT(vp); ip = vn_to_inode(vp); if (S_ISDIR(mode)) xfs_validate_fields(ip); d_instantiate(dentry, ip); xfs_validate_fields(dir); } 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 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; }
struct posix_acl *get_acl(struct inode *inode, int type) { void *sentinel; struct posix_acl **p; struct posix_acl *acl; /* * The sentinel is used to detect when another operation like * set_cached_acl() or forget_cached_acl() races with get_acl(). * It is guaranteed that is_uncached_acl(sentinel) is true. */ acl = get_cached_acl(inode, type); if (!is_uncached_acl(acl)) return acl; if (!IS_POSIXACL(inode)) return NULL; sentinel = uncached_acl_sentinel(current); p = acl_by_type(inode, type); /* * If the ACL isn't being read yet, set our sentinel. Otherwise, the * current value of the ACL will not be ACL_NOT_CACHED and so our own * sentinel will not be set; another task will update the cache. We * could wait for that other task to complete its job, but it's easier * to just call ->get_acl to fetch the ACL ourself. (This is going to * be an unlikely race.) */ if (cmpxchg(p, ACL_NOT_CACHED, sentinel) != ACL_NOT_CACHED) /* fall through */ ; /* * Normally, the ACL returned by ->get_acl will be cached. * A filesystem can prevent that by calling * forget_cached_acl(inode, type) in ->get_acl. * * If the filesystem doesn't have a get_acl() function at all, we'll * just create the negative cache entry. */ if (!inode->i_op->get_acl) { set_cached_acl(inode, type, NULL); return NULL; } acl = inode->i_op->get_acl(inode, type); if (IS_ERR(acl)) { /* * Remove our sentinel so that we don't block future attempts * to cache the ACL. */ cmpxchg(p, sentinel, ACL_NOT_CACHED); return acl; } /* * Cache the result, but only if our sentinel is still in place. */ posix_acl_dup(acl); if (unlikely(cmpxchg(p, sentinel, acl) != sentinel)) posix_acl_release(acl); return acl; }