]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
client: POSIX ACL support
authorYan, Zheng <zyan@redhat.com>
Mon, 24 Aug 2015 13:07:46 +0000 (21:07 +0800)
committerYan, Zheng <zyan@redhat.com>
Tue, 12 Jan 2016 09:21:00 +0000 (17:21 +0800)
Signed-off-by: Yan, Zheng <zyan@redhat.com>
src/CMakeLists.txt
src/client/Client.cc
src/client/Client.h
src/client/Inode.cc
src/client/Inode.h
src/client/Makefile.am
src/client/posix_acl.cc [new file with mode: 0644]
src/client/posix_acl.h [new file with mode: 0644]
src/common/config_opts.h

index 3d3e174112e303282869259334c9e209994681ed..d2c4cfda6242d23ee64b78eada28b9d8d650bcf2 100644 (file)
@@ -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)
index f27c68dde9b927e9d2d681c9401825a80ba8783b..bfc2bb509143f434712cfe9e546f400a98e9b344 100644 (file)
@@ -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<string, bufferptr> 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)
index 37be3d7945458a6b09614fd2f1cc7444fe36d253..4dfd2b4dceb0efa2ea1b77558fa2ed5f7ce11a28 100644 (file)
@@ -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();
index 03e7a07f2cf0c65e67f4465806347c3bd019f5ee..ec9dad1472bfc26138b41128d72cc7e61e25c2ec 100644 (file)
@@ -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() {
index 7b7daa1ec692fb53a4905e575bf8fcf008687eed..52e578aaf9e53b5d0328881418f244f8079609db 100644 (file)
@@ -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);
index 8e47f51b1dfba697086e88a6b13bdcb928d26f66..5d9624754a8340aea7b647067dbbb3b1c3dd5de0 100644 (file)
@@ -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 (file)
index 0000000..36e8562
--- /dev/null
@@ -0,0 +1,292 @@
+#include "include/types.h"
+#include <sys/stat.h>
+#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<const acl_ea_header*>(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<const acl_ea_header*>(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_ea_header*>(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_ea_header*>(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<const acl_ea_header*>(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 (file)
index 0000000..739bb65
--- /dev/null
@@ -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
index f7ac5a60dc2c0564f03f793e04e92e79be66f90d..e6c7fbe393091078eec2f474493e148d3490fa2c 100644 (file)
@@ -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