From: Yan, Zheng Date: Mon, 24 Aug 2015 13:07:46 +0000 (+0800) Subject: client: POSIX ACL support X-Git-Tag: v10.0.3~48^2~3^2~18 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=50646711cb4bc952772a2ff1e54a781e9b220cb6;p=ceph.git client: POSIX ACL support Signed-off-by: Yan, Zheng --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d3e174112e..d2c4cfda624 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -898,7 +898,8 @@ if(WITH_LIBCEPHFS) client/MetaRequest.cc client/ClientSnapRealm.cc client/MetaSession.cc - client/Trace.cc) + client/Trace.cc + client/posix_acl.cc) add_library(client ${libclient_srcs}) target_link_libraries(client osdc mds ${LIBEDIT_LIBS}) set(libcephfs_srcs libcephfs.cc) diff --git a/src/client/Client.cc b/src/client/Client.cc index f27c68dde9b..bfc2bb50914 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -98,6 +98,7 @@ using namespace std; #include "MetaSession.h" #include "MetaRequest.h" #include "ObjecterWriteback.h" +#include "posix_acl.h" #include "include/assert.h" #include "include/stat.h" @@ -270,6 +271,10 @@ Client::Client(Messenger *m, MonClient *mc) user_id = cct->_conf->client_mount_uid; group_id = cct->_conf->client_mount_gid; + acl_type = NO_ACL; + if (cct->_conf->client_acl_type == "posix_acl") + acl_type = POSIX_ACL; + lru.lru_set_max(cct->_conf->client_cache_size); lru.lru_set_midpoint(cct->_conf->client_cache_mid); @@ -4818,12 +4823,24 @@ int Client::check_permissions(Inode *in, int flags, int uid, int gid) } } #endif + unsigned want = 0; + if ((flags & O_ACCMODE) == O_WRONLY) + want = MAY_WRITE; + else if ((flags & O_ACCMODE) == O_RDWR) + want = MAY_READ | MAY_WRITE; + else if ((flags & O_ACCMODE) == O_RDONLY) + want = MAY_READ; + + int ret = _posix_acl_permission(in, uid, gid, sgids, sgid_count, want); + if (ret != -EAGAIN) + return ret; // check permissions before doing anything else - int ret = 0; - if (uid != 0 && !in->check_mode(uid, gid, sgids, sgid_count, flags)) { + if (uid == 0 || in->check_mode(uid, gid, sgids, sgid_count, want)) + ret = 0; + else ret = -EACCES; - } + if (sgids) free(sgids); return ret; @@ -5826,8 +5843,8 @@ int Client::_getattr(Inode *in, int mask, int uid, int gid, bool force) return res; } -int Client::_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid, - InodeRef *inp) +int Client::_do_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid, + InodeRef *inp) { int issued = in->caps_issued(); @@ -5986,6 +6003,17 @@ force_request: return res; } +int Client::_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid, + InodeRef *inp) +{ + int ret = _do_setattr(in, attr, mask, uid, gid, inp); + if (ret < 0) + return ret; + if (mask & CEPH_SETATTR_MODE) + ret = _posix_acl_chmod(in, attr->st_mode, uid, gid); + return ret; +} + int Client::setattr(const char *relpath, struct stat *attr, int mask) { Mutex::Locker lock(client_lock); @@ -9162,6 +9190,11 @@ int Client::_getxattr(Inode *in, const char *name, void *value, size_t size, goto out; } + if (acl_type == NO_ACL && !strncmp(name, "system.", 7)) { + r = -EOPNOTSUPP; + goto out; + } + r = _getattr(in, CEPH_STAT_CAP_XATTR, uid, gid, in->xattr_version == 0); if (r == 0) { string n(name); @@ -9255,23 +9288,9 @@ int Client::ll_listxattr(Inode *in, char *names, size_t size, int uid, return _listxattr(in, names, size, uid, gid); } -int Client::_setxattr(Inode *in, const char *name, const void *value, - size_t size, int flags, int uid, int gid) +int Client::_do_setxattr(Inode *in, const char *name, const void *value, + size_t size, int flags, int uid, int gid) { - if (in->snapid != CEPH_NOSNAP) { - return -EROFS; - } - - // same xattrs supported by kernel client - if (strncmp(name, "user.", 5) && - strncmp(name, "security.", 9) && - strncmp(name, "trusted.", 8) && - strncmp(name, "ceph.", 5)) - return -EOPNOTSUPP; - - const VXattr *vxattr = _match_vxattr(in, name); - if (vxattr && vxattr->readonly) - return -EOPNOTSUPP; int xattr_flags = 0; if (!value) @@ -9301,6 +9320,62 @@ int Client::_setxattr(Inode *in, const char *name, const void *value, return res; } +int Client::_setxattr(Inode *in, const char *name, const void *value, + size_t size, int flags, int uid, int gid) +{ + if (in->snapid != CEPH_NOSNAP) { + return -EROFS; + } + + bool posix_acl_xattr = false; + if (acl_type == POSIX_ACL) + posix_acl_xattr = !strncmp(name, "system.", 7); + + if (strncmp(name, "user.", 5) && + strncmp(name, "security.", 9) && + strncmp(name, "trusted.", 8) && + strncmp(name, "ceph.", 5) && + !posix_acl_xattr) + return -EOPNOTSUPP; + + if (posix_acl_xattr) { + if (!strcmp(name, ACL_EA_ACCESS)) { + mode_t new_mode = in->mode; + if (value) { + int ret = posix_acl_equiv_mode(value, size, &new_mode); + if (ret < 0) + return ret; + if (ret == 0) { + value = NULL; + size = 0; + } + if (new_mode != in->mode) { + struct stat attr; + attr.st_mode = new_mode; + ret = _do_setattr(in, &attr, CEPH_SETATTR_MODE, uid, gid, NULL); + if (ret < 0) + return ret; + } + } + } else if (!strcmp(name, ACL_EA_DEFAULT)) { + if (value) { + if (!S_ISDIR(in->mode)) + return -EACCES; + if (!posix_acl_check(value, size)) + return -EINVAL; + } + } else { + return -EOPNOTSUPP; + } + } else { + const VXattr *vxattr = _match_vxattr(in, name); + if (vxattr && vxattr->readonly) + return -EOPNOTSUPP; + } + + return _do_setxattr(in, name, value, size, flags, uid, gid); +} + int Client::check_data_pool_exist(string name, string value, const OSDMap *osdmap) { string tmp; @@ -9382,6 +9457,7 @@ int Client::_removexattr(Inode *in, const char *name, int uid, int gid) // same xattrs supported by kernel client if (strncmp(name, "user.", 5) && + strncmp(name, "system.", 7) && strncmp(name, "security.", 9) && strncmp(name, "trusted.", 8) && strncmp(name, "ceph.", 5)) @@ -9677,13 +9753,20 @@ int Client::_mknod(Inode *dir, const char *name, mode_t mode, dev_t rdev, path.push_dentry(name); req->set_filepath(path); req->set_inode(dir); - req->head.args.mknod.mode = mode; req->head.args.mknod.rdev = rdev; req->dentry_drop = CEPH_CAP_FILE_SHARED; req->dentry_unless = CEPH_CAP_FILE_EXCL; + bufferlist xattrs_bl; + int res = _posix_acl_create(dir, &mode, xattrs_bl, uid, gid); + if (res < 0) + goto fail; + req->head.args.mknod.mode = mode; + if (xattrs_bl.length() > 0) + req->set_data(xattrs_bl); + Dentry *de; - int res = get_or_create(dir, name, &de); + res = get_or_create(dir, name, &de); if (res < 0) goto fail; req->set_dentry(de); @@ -9768,7 +9851,6 @@ int Client::_create(Inode *dir, const char *name, int flags, mode_t mode, req->set_filepath(path); req->set_inode(dir); req->head.args.open.flags = flags | O_CREAT; - req->head.args.open.mode = mode; req->head.args.open.stripe_unit = stripe_unit; req->head.args.open.stripe_count = stripe_count; @@ -9777,11 +9859,17 @@ int Client::_create(Inode *dir, const char *name, int flags, mode_t mode, req->dentry_drop = CEPH_CAP_FILE_SHARED; req->dentry_unless = CEPH_CAP_FILE_EXCL; - bufferlist extra_bl; - inodeno_t created_ino; + mode |= S_IFREG; + bufferlist xattrs_bl; + int res = _posix_acl_create(dir, &mode, xattrs_bl, uid, gid); + if (res < 0) + goto fail; + req->head.args.open.mode = mode; + if (xattrs_bl.length() > 0) + req->set_data(xattrs_bl); Dentry *de; - int res = get_or_create(dir, name, &de); + res = get_or_create(dir, name, &de); if (res < 0) goto fail; req->set_dentry(de); @@ -9837,12 +9925,20 @@ int Client::_mkdir(Inode *dir, const char *name, mode_t mode, int uid, int gid, path.push_dentry(name); req->set_filepath(path); req->set_inode(dir); - req->head.args.mkdir.mode = mode; req->dentry_drop = CEPH_CAP_FILE_SHARED; req->dentry_unless = CEPH_CAP_FILE_EXCL; + mode |= S_IFDIR; + bufferlist xattrs_bl; + int res = _posix_acl_create(dir, &mode, xattrs_bl, uid, gid); + if (res < 0) + goto fail; + req->head.args.mkdir.mode = mode; + if (xattrs_bl.length() > 0) + req->set_data(xattrs_bl); + Dentry *de; - int res = get_or_create(dir, name, &de); + res = get_or_create(dir, name, &de); if (res < 0) goto fail; req->set_dentry(de); @@ -11481,6 +11577,97 @@ int Client::check_pool_perm(Inode *in, int need) return 0; } +int Client::_posix_acl_permission(Inode *in, int uid, int gid, + gid_t *sgids, int sgid_count, unsigned want) +{ + if (acl_type == POSIX_ACL) { + int r = _getattr(in, CEPH_STAT_CAP_XATTR | CEPH_STAT_CAP_MODE, + uid, gid, in->xattr_version == 0); + if (r < 0) + return r; + + if (in->xattrs.count(ACL_EA_ACCESS)) { + const bufferptr& access_acl = in->xattrs[ACL_EA_ACCESS]; + if (in->mode & S_IRWXG) + return posix_acl_permits(access_acl, in->uid, in->gid, + uid, gid, sgids, sgid_count, want); + } + } + return -EAGAIN; +} + +int Client::_posix_acl_chmod(Inode *in, mode_t mode, int uid, int gid) +{ + if (acl_type == NO_ACL) + return 0; + + int r = _getattr(in, CEPH_STAT_CAP_XATTR, uid, gid, in->xattr_version == 0); + if (r < 0) + goto out; + + if (acl_type == POSIX_ACL) { + if (in->xattrs.count(ACL_EA_ACCESS)) { + const bufferptr& access_acl = in->xattrs[ACL_EA_ACCESS]; + bufferptr acl(access_acl.c_str(), access_acl.length()); + r = posix_acl_access_chmod(acl, mode); + if (r < 0) + goto out; + r = _do_setxattr(in, ACL_EA_ACCESS, acl.c_str(), acl.length(), 0, uid, gid); + } else { + r = 0; + } + } +out: + ldout(cct, 10) << __func__ << " ino " << in->ino << " result=" << r << dendl; + return r; +} + +int Client::_posix_acl_create(Inode *dir, mode_t *mode, bufferlist& xattrs_bl, + int uid, int gid) +{ + if (acl_type == NO_ACL) + return 0; + + if (S_ISLNK(*mode)) + return 0; + + int r = _getattr(dir, CEPH_STAT_CAP_XATTR, uid, gid, dir->xattr_version == 0); + if (r < 0) + goto out; + + if (acl_type == POSIX_ACL) { + if (dir->xattrs.count(ACL_EA_DEFAULT)) { + map xattrs; + + const bufferptr& default_acl = dir->xattrs[ACL_EA_DEFAULT]; + bufferptr acl(default_acl.c_str(), default_acl.length()); + r = posix_acl_inherit_mode(acl, mode); + if (r < 0) + goto out; + + if (r > 0) { + r = posix_acl_equiv_mode(acl.c_str(), acl.length(), mode); + if (r < 0) + goto out; + if (r > 0) + xattrs[ACL_EA_ACCESS] = acl; + } + + if (S_ISDIR(*mode)) + xattrs[ACL_EA_DEFAULT] = dir->xattrs[ACL_EA_DEFAULT]; + + r = xattrs.size(); + if (r > 0) + ::encode(xattrs, xattrs_bl); + } else { + r = 0; + } + } +out: + ldout(cct, 10) << __func__ << " dir ino " << dir->ino << " result=" << r << dendl; + return r; +} + void Client::set_filer_flags(int flags) { Mutex::Locker l(client_lock); @@ -11514,6 +11701,7 @@ const char** Client::get_tracked_conf_keys() const static const char* keys[] = { "client_cache_size", "client_cache_mid", + "client_acl_type", NULL }; return keys; @@ -11527,6 +11715,11 @@ void Client::handle_conf_change(const struct md_config_t *conf, lru.lru_set_max(cct->_conf->client_cache_size); lru.lru_set_midpoint(cct->_conf->client_cache_mid); } + if (changed.count("client_acl_type")) { + acl_type = NO_ACL; + if (cct->_conf->client_acl_type == "posix_acl") + acl_type = POSIX_ACL; + } } void intrusive_ptr_add_ref(Inode *in) diff --git a/src/client/Client.h b/src/client/Client.h index 37be3d79454..4dfd2b4dceb 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -268,6 +268,7 @@ protected: client_t whoami; int user_id, group_id; + int acl_type; int get_uid() { if (user_id >= 0) @@ -712,6 +713,7 @@ private: int _rmdir(Inode *dir, const char *name, int uid=-1, int gid=-1); int _symlink(Inode *dir, const char *name, const char *target, int uid=-1, int gid=-1, InodeRef *inp = 0); int _mknod(Inode *dir, const char *name, mode_t mode, dev_t rdev, int uid=-1, int gid=-1, InodeRef *inp = 0); + int _do_setattr(Inode *in, struct stat *attr, int mask, int uid, int gid, InodeRef *inp); int _setattr(Inode *in, struct stat *attr, int mask, int uid=-1, int gid=-1, InodeRef *inp = 0); int _setattr(InodeRef &in, struct stat *attr, int mask, int uid=-1, int gid=-1, InodeRef *inp = 0) { return _setattr(in.get(), attr, mask, uid, gid, inp); @@ -723,6 +725,7 @@ private: int _readlink(Inode *in, char *buf, size_t size); int _getxattr(Inode *in, const char *name, void *value, size_t len, int uid=-1, int gid=-1); int _listxattr(Inode *in, char *names, size_t len, int uid=-1, int gid=-1); + int _do_setxattr(Inode *in, const char *name, const void *value, size_t len, int flags, int uid, int gid); int _setxattr(Inode *in, const char *name, const void *value, size_t len, int flags, int uid=-1, int gid=-1); int _removexattr(Inode *in, const char *nm, int uid=-1, int gid=-1); int _open(Inode *in, int flags, mode_t mode, Fh **fhp, int uid=-1, int gid=-1); @@ -747,6 +750,17 @@ private: int get_or_create(Inode *dir, const char* name, Dentry **pdn, bool expect_null=false); + enum { + NO_ACL = 0, + POSIX_ACL, + }; + + enum { + MAY_EXEC = 1, + MAY_WRITE = 2, + MAY_READ = 4, + }; + int check_permissions(Inode *in, int flags, int uid, int gid); int check_data_pool_exist(string name, string value, const OSDMap *osdmap); @@ -808,6 +822,11 @@ private: void _encode_filelocks(Inode *in, bufferlist& bl); void _release_filelocks(Fh *fh); void _update_lock_state(struct flock *fl, uint64_t owner, ceph_lock_state_t *lock_state); + + int _posix_acl_create(Inode *dir, mode_t *mode, bufferlist& xattrs_bl, int uid, int gid); + int _posix_acl_chmod(Inode *in, mode_t mode, int uid, int gid); + int _posix_acl_permission(Inode *in, int uid, int gid, + gid_t *sgids, int sgid_count, unsigned want); public: int mount(const std::string &mount_root, bool require_mds=false); void unmount(); diff --git a/src/client/Inode.cc b/src/client/Inode.cc index 03e7a07f2cf..ec9dad1472b 100644 --- a/src/client/Inode.cc +++ b/src/client/Inode.cc @@ -279,34 +279,25 @@ Dir *Inode::open_dir() return dir; } -bool Inode::check_mode(uid_t ruid, gid_t rgid, gid_t *sgids, int sgids_count, uint32_t rflags) +bool Inode::check_mode(uid_t ruid, gid_t rgid, gid_t *sgids, int sgids_count, unsigned want) { - unsigned fmode = 0; - - if ((rflags & O_ACCMODE) == O_WRONLY) - fmode = 2; - else if ((rflags & O_ACCMODE) == O_RDWR) - fmode = 6; - else if ((rflags & O_ACCMODE) == O_RDONLY) - fmode = 4; - // if uid is owner, owner entry determines access if (uid == ruid) { - fmode = fmode << 6; + want = want << 6; } else if (gid == rgid) { // if a gid or sgid matches the owning group, group entry determines access - fmode = fmode << 3; + want = want << 3; } else { int i = 0; for (; i < sgids_count; ++i) { if (sgids[i] == gid) { - fmode = fmode << 3; + want = want << 3; break; } } } - return (mode & fmode) == fmode; + return (mode & want) == want; } void Inode::get() { diff --git a/src/client/Inode.h b/src/client/Inode.h index 7b7daa1ec69..52e578aaf9e 100644 --- a/src/client/Inode.h +++ b/src/client/Inode.h @@ -334,7 +334,7 @@ struct Inode { } }; - bool check_mode(uid_t uid, gid_t gid, gid_t *sgids, int sgid_count, uint32_t flags); + bool check_mode(uid_t uid, gid_t gid, gid_t *sgids, int sgid_count, unsigned want); // CAPS -------- void get_open_ref(int mode); diff --git a/src/client/Makefile.am b/src/client/Makefile.am index 8e47f51b1df..5d9624754a8 100644 --- a/src/client/Makefile.am +++ b/src/client/Makefile.am @@ -6,7 +6,8 @@ libclient_la_SOURCES = \ client/MetaRequest.cc \ client/ClientSnapRealm.cc \ client/MetaSession.cc \ - client/Trace.cc + client/Trace.cc \ + client/posix_acl.cc libclient_la_LIBADD = $(LIBOSDC) $(LIBEDIT_LIBS) noinst_LTLIBRARIES += libclient.la @@ -23,7 +24,8 @@ noinst_HEADERS += \ client/SyntheticClient.h \ client/Trace.h \ client/ioctl.h \ - client/ObjecterWriteback.h + client/ObjecterWriteback.h \ + client/posix_acl.h if WITH_FUSE libclient_fuse_la_SOURCES = client/fuse_ll.cc diff --git a/src/client/posix_acl.cc b/src/client/posix_acl.cc new file mode 100644 index 00000000000..36e8562628f --- /dev/null +++ b/src/client/posix_acl.cc @@ -0,0 +1,292 @@ +#include "include/types.h" +#include +#include "posix_acl.h" + +int posix_acl_check(const void *xattr, size_t size) +{ + const acl_ea_header *header; + if (size < sizeof(*header)) + return 0; + header = reinterpret_cast(xattr); + ceph_le32 expected_version; + expected_version = ACL_EA_VERSION; + if (header->a_version != expected_version) + return 0; + + const acl_ea_entry *entry = header->a_entries; + size -= sizeof(*header); + if (size % sizeof(*entry)) + return 0; + + int count = size / sizeof(*entry); + int state = ACL_USER_OBJ; + int needs_mask = 0; + for (int i = 0; i < count; ++i) { + __u16 tag = entry->e_tag; + switch(tag) { + case ACL_USER_OBJ: + if (state == ACL_USER_OBJ) { + state = ACL_USER; + break; + } + return 0; + case ACL_USER: + if (state != ACL_USER) + return 0; + needs_mask = 1; + break; + case ACL_GROUP_OBJ: + if (state == ACL_USER) { + state = ACL_GROUP; + break; + } + return 0; + case ACL_GROUP: + if (state != ACL_GROUP) + return 0; + needs_mask = 1; + break; + case ACL_MASK: + if (state != ACL_GROUP) + return 0; + state = ACL_OTHER; + break; + case ACL_OTHER: + if (state == ACL_OTHER || + (state == ACL_GROUP && !needs_mask)) { + state = 0; + break; + } + // fall-thru + default: + return 0; + } + ++entry; + } + + return state == 0; +} + +int posix_acl_equiv_mode(const void *xattr, size_t size, mode_t *mode_p) +{ + if (!posix_acl_check(xattr, size)) + return -EINVAL; + + int not_equiv = 0; + mode_t mode = 0; + + const acl_ea_header *header = reinterpret_cast(xattr); + const acl_ea_entry *entry = header->a_entries; + int count = (size - sizeof(*header)) / sizeof(*entry); + for (int i = 0; i < count; ++i) { + __u16 tag = entry->e_tag; + __u16 perm = entry->e_perm; + switch(tag) { + case ACL_USER_OBJ: + mode |= (perm & S_IRWXO) << 6; + break; + case ACL_GROUP_OBJ: + mode |= (perm & S_IRWXO) << 3; + break; + case ACL_OTHER: + mode |= perm & S_IRWXO; + break; + case ACL_MASK: + mode = (mode & ~S_IRWXG) | ((perm & S_IRWXO) << 3); + /* fall through */ + case ACL_USER: + case ACL_GROUP: + not_equiv = 1; + break; + default: + return -EINVAL; + } + ++entry; + } + if (mode_p) + *mode_p = (*mode_p & ~ACCESSPERMS) | mode; + return not_equiv; +} + +int posix_acl_inherit_mode(bufferptr& acl, mode_t *mode_p) +{ + if (!posix_acl_check(acl.c_str(), acl.length())) + return -EIO; + + acl_ea_entry *group_entry = NULL, *mask_entry = NULL; + mode_t mode = *mode_p; + int not_equiv = 0; + + acl_ea_header *header = reinterpret_cast(acl.c_str()); + acl_ea_entry *entry = header->a_entries; + int count = (acl.length() - sizeof(*header)) / sizeof(*entry); + for (int i = 0; i < count; ++i) { + __u16 tag = entry->e_tag; + __u16 perm = entry->e_perm; + switch(tag) { + case ACL_USER_OBJ: + perm &= (mode >> 6) | ~S_IRWXO; + mode &= (perm << 6) | ~S_IRWXU; + entry->e_perm = perm; + break; + case ACL_USER: + case ACL_GROUP: + not_equiv = 1; + break; + case ACL_GROUP_OBJ: + group_entry = entry; + break; + case ACL_OTHER: + perm &= mode | ~S_IRWXO; + mode &= perm | ~S_IRWXO; + entry->e_perm = perm; + break; + case ACL_MASK: + mask_entry = entry; + not_equiv = 1; + break; + default: + return -EIO; + + } + ++entry; + } + + if (mask_entry) { + __u16 perm = mask_entry->e_perm; + perm &= (mode >> 3) | ~S_IRWXO; + mode &= (perm << 3) | ~S_IRWXG; + mask_entry->e_perm = perm; + } else { + if (!group_entry) + return -EIO; + __u16 perm = group_entry->e_perm; + perm &= (mode >> 3) | ~S_IRWXO; + mode &= (perm << 3) | ~S_IRWXG; + group_entry->e_perm = perm; + } + + *mode_p = (*mode_p & ~ACCESSPERMS) | mode; + return not_equiv; +} + +int posix_acl_access_chmod(bufferptr& acl, mode_t mode) +{ + if (!posix_acl_check(acl.c_str(), acl.length())) + return -EIO; + + acl_ea_entry *group_entry = NULL, *mask_entry = NULL; + + acl_ea_header *header = reinterpret_cast(acl.c_str()); + acl_ea_entry *entry = header->a_entries; + int count = (acl.length() - sizeof(*header)) / sizeof(*entry); + for (int i = 0; i < count; ++i) { + __u16 tag = entry->e_tag; + switch(tag) { + case ACL_USER_OBJ: + entry->e_perm = (mode & S_IRWXU) >> 6; + break; + case ACL_GROUP_OBJ: + group_entry = entry; + break; + case ACL_MASK: + mask_entry = entry; + break; + case ACL_OTHER: + entry->e_perm = mode & S_IRWXO; + break; + default: + break; + } + ++entry; + } + + if (mask_entry) { + mask_entry->e_perm = (mode & S_IRWXG) >> 3; + } else { + if (!group_entry) + return -EIO; + group_entry->e_perm = (mode & S_IRWXG) >> 3; + } + return 0; +} + +static int in_grouplist(gid_t gid, gid_t *sgids, int sgid_count) +{ + for (int i = 0; i < sgid_count; i++) { + if (sgids[i] == gid) + return 1; + } + return 0; +} + +int posix_acl_permits(const bufferptr& acl, uid_t i_uid, gid_t i_gid, + uid_t uid, gid_t gid, gid_t *sgids, int sgid_count, + unsigned want) +{ + if (!posix_acl_check(acl.c_str(), acl.length())) + return -EIO; + + const acl_ea_header *header = reinterpret_cast(acl.c_str()); + const acl_ea_entry *entry = header->a_entries; + const acl_ea_entry *next_entry; + __u16 perm, tag; + __u32 id; + int group_found = 0; + int idx; + int count = (acl.length() - sizeof(*header)) / sizeof(*entry); + for (idx = 0; idx < count; ++idx) { + tag = entry->e_tag; + perm = entry->e_perm; + switch(tag) { + case ACL_USER_OBJ: + if (i_uid == uid) + goto check_perm; + break; + case ACL_USER: + id = entry->e_id; + if (id == uid) + goto check_mask; + break; + case ACL_GROUP_OBJ: + /* fall through */ + case ACL_GROUP: + id = (tag == ACL_GROUP_OBJ) ? i_gid : entry->e_id; + if (id == gid || in_grouplist(id, sgids, sgid_count)) { + group_found = 1; + if ((perm & want) == want) + goto check_mask; + } + break; + case ACL_MASK: + break; + case ACL_OTHER: + if (group_found) + return -EACCES; + else + goto check_perm; + break; + default: + return -EIO; + } + ++entry; + } + return -EIO; + +check_mask: + next_entry = entry + 1; + for (++idx; idx < count; ++idx) { + tag = next_entry->e_tag; + if (tag == ACL_MASK) { + __u16 mask = next_entry->e_perm; + if ((perm & mask & want) == want) + return 0; + return -EACCES; + } + ++next_entry; + } +check_perm: + if ((perm & want) == want) + return 0; + return -EACCES; +} diff --git a/src/client/posix_acl.h b/src/client/posix_acl.h new file mode 100644 index 00000000000..739bb6551b6 --- /dev/null +++ b/src/client/posix_acl.h @@ -0,0 +1,34 @@ +#ifndef CEPH_POSIX_ACL +#define CEPH_POSIX_ACL + +#define ACL_EA_VERSION 0x0002 + +#define ACL_USER_OBJ 0x01 +#define ACL_USER 0x02 +#define ACL_GROUP_OBJ 0x04 +#define ACL_GROUP 0x08 +#define ACL_MASK 0x10 +#define ACL_OTHER 0x20 + +#define ACL_EA_ACCESS "system.posix_acl_access" +#define ACL_EA_DEFAULT "system.posix_acl_default" + +typedef struct { + ceph_le16 e_tag; + ceph_le16 e_perm; + ceph_le32 e_id; +} acl_ea_entry; + +typedef struct { + ceph_le32 a_version; + acl_ea_entry a_entries[0]; +} acl_ea_header; + +int posix_acl_check(const void *xattr, size_t size); +int posix_acl_equiv_mode(const void *xattr, size_t size, mode_t *mode_p); +int posix_acl_inherit_mode(bufferptr& acl, mode_t *mode_p); +int posix_acl_access_chmod(bufferptr& acl, mode_t mode); +int posix_acl_permits(const bufferptr& acl, uid_t i_uid, gid_t i_gid, + uid_t uid, gid_t gid, gid_t *sgids, int sgid_count, + unsigned want); +#endif diff --git a/src/common/config_opts.h b/src/common/config_opts.h index f7ac5a60dc2..e6c7fbe3930 100644 --- a/src/common/config_opts.h +++ b/src/common/config_opts.h @@ -366,6 +366,7 @@ OPTION(client_debug_inject_tick_delay, OPT_INT, 0) // delay the client tick for OPTION(client_max_inline_size, OPT_U64, 4096) OPTION(client_inject_release_failure, OPT_BOOL, false) // synthetic client bug for testing OPTION(client_inject_fixed_oldest_tid, OPT_BOOL, false) // synthetic client bug for testing +OPTION(client_acl_type, OPT_STR, "") // note: the max amount of "in flight" dirty data is roughly (max - target) OPTION(fuse_use_invalidate_cb, OPT_BOOL, false) // use fuse 2.8+ invalidate callback to keep page cache consistent