Implement setAcl() using xatrs, update setMode(), setOwner() and create().
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Sat, 19 Oct 2013 19:15:59 +0000 (21:15 +0200)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Sun, 23 Feb 2014 13:16:19 +0000 (14:16 +0100)
src/VfsNs.cpp
src/VfsNs.h

index aac7ead..6efe32f 100644 (file)
@@ -543,6 +543,8 @@ void VfsCatalog::create(const std::string& path, mode_t mode) throw (DmException
   std::string parentPath, name, lpath;
   int code = DMLITE_SUCCESS;
   ExtendedStat file;
+  gid_t egid;
+  Acl newAcl;
 
   if (vfsCheckPermissions(path, S_IWRITE))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
@@ -564,25 +566,29 @@ void VfsCatalog::create(const std::string& path, mode_t mode) throw (DmException
     if (code != ENOENT) throw;
   }
 
-#if 0
-  // Effective gid
-  gid_t egid;
-  if (parent.stat.st_mode & S_ISGID) {
-    egid = parent.stat.st_gid;
-    mode |= S_ISGID;
-  } else {
-    egid = getGid(this->secCtx_);
-  }
-  // Generate inherited ACL's if there are defaults
-  if (parent.acl.has(AclEntry::kDefault | AclEntry::kUserObj)) {
-    newFile.acl = Acl(parent.acl, getUid(this->secCtx_), egid, mode, &newFile.stat.st_mode);
-  }
-#endif
-
   if (code == ENOENT) {
+    // Cleanup mode
+    mode = (mode & ~S_IFMT) | S_IFREG;
+
+    // Effective gid
+    if (parent.stat.st_mode & S_ISGID) {
+      egid = parent.stat.st_gid;
+      mode |= S_ISGID;
+    }  else {
+      egid = getGid(this->secCtx_);
+    }
+
+    // Generate inherited ACL's if there are defaults
+    if (parent.acl.has(AclEntry::kDefault | AclEntry::kUserObj))
+      newAcl = Acl(parent.acl, getUid(this->secCtx_), egid, mode, &mode);
+
     // Create new
     wrapCall(f = fopen(lpath.c_str(), "w"));
     wrapCall(fclose(f));
+    vfsSetOwner(path, lpath, getUid(this->secCtx_), egid, true);
+    vfsSetMode(path, lpath, mode);
+    if (!newAcl.empty())
+      vfsSetAcl(path, lpath, newAcl);
   } else {
     // Truncate
     if (S_ISDIR(file.stat.st_mode))
@@ -590,11 +596,10 @@ void VfsCatalog::create(const std::string& path, mode_t mode) throw (DmException
     if (getUid(this->secCtx_) != file.stat.st_uid &&
        checkPermissions(this->secCtx_, file.acl, file.stat, S_IWRITE) != 0) {
       vfsThrow(EACCES, "not enough permissions for '%s' to truncate '%s'", clientName.c_str(), path.c_str());
+    // TODO: check replicas
     }
     wrapCall(truncate(lpath.c_str(), 0));
   }
-
-  setMode(path, mode);
 }
 
 
@@ -612,6 +617,7 @@ mode_t VfsCatalog::umask(mode_t mask) throw ()
 void VfsCatalog::setMode(const std::string& path, mode_t mode) throw (DmException)
 {
   ExtendedStat meta;
+  std::string lpath;
 
   if (vfsCheckPermissions(path, S_IWRITE))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
@@ -633,9 +639,31 @@ void VfsCatalog::setMode(const std::string& path, mode_t mode) throw (DmExceptio
       !hasGroup(this->secCtx_->groups, meta.stat.st_gid))
     mode &= ~S_ISGID;
 
-  // TODO: update ACL, setAcl().
+  // Update the ACL
+  if (!meta.acl.empty()) {
+    for (size_t i = 0; i < meta.acl.size(); ++i) {
+      switch (meta.acl[i].type) {
+        case AclEntry::kUserObj:
+          meta.acl[i].perm = mode >> 6 & 07;
+          break;
+        case AclEntry::kGroupObj:
+        case AclEntry::kMask:
+          meta.acl[i].perm = mode >> 3 & 07;
+          break;
+        case AclEntry::kOther:
+          meta.acl[i].perm = mode & 07;
+          break;
+        default:
+          continue;
+      }
+    }
+  }
+
+  lpath = getLocalPath(path);
 
-  vfsSetMode(path, getLocalPath(path), mode);
+  vfsSetMode(path, lpath, mode);
+  if (!meta.acl.empty())
+    vfsSetAcl(path, lpath, meta.acl);
 }
 
 
@@ -644,7 +672,6 @@ void VfsCatalog::setOwner(const std::string& path, uid_t newUid, gid_t newGid, b
 {
   ExtendedStat meta;
   std::string lpath;
-  char buf[10];
 
   if (vfsCheckPermissions(path, S_IWRITE))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
@@ -681,24 +708,23 @@ void VfsCatalog::setOwner(const std::string& path, uid_t newUid, gid_t newGid, b
   lpath = getLocalPath(path);
 
   try {
-    if (newUid != meta.stat.st_uid) {
-      snprintf(buf, sizeof buf, "%u", newUid);
-      vfsSetXattr(path, lpath, VFS_XATTR "owner", buf, ATTR_DONTFOLLOW);
-    }
-    if (newGid != meta.stat.st_gid) {
-      snprintf(buf, sizeof buf, "%u", newGid);
-      vfsSetXattr(path, lpath, VFS_XATTR "group", buf, ATTR_DONTFOLLOW);
-    }
+    vfsSetOwner(path, lpath, meta, newUid, newGid, followSymLink);
 
     // Update the ACL's if there is any
     if (!meta.acl.empty()) {
       for (size_t i = 0; i < meta.acl.size(); i++) {
-        if (meta.acl[i].type == AclEntry::kUserObj)
-          meta.acl[i].id = newUid;
-        else if (meta.acl[i].type == AclEntry::kGroupObj)
-          meta.acl[i].id = newGid;
+        switch (meta.acl[i].type) {
+          case AclEntry::kUserObj:
+            meta.acl[i].id = newUid;
+            break;
+          case AclEntry::kGroupObj:
+            meta.acl[i].id = newGid;
+            break;
+          default:
+            break;
+        }
       }
-      vfsSetXattr(path, lpath, VFS_XATTR "acl", buf, ATTR_DONTFOLLOW);
+      vfsSetXattr(path, lpath, VFS_XATTR "acl", meta.acl.serialize(), followSymLink ? 0 : ATTR_DONTFOLLOW);
     }
   } catch (DmException& e) {
   // xattrs are not supported on symbolic links
@@ -767,7 +793,68 @@ void VfsCatalog::setChecksum(const std::string& path,
 
 void VfsCatalog::setAcl(const std::string& path, const Acl& acl) throw (DmException)
 {
-  throw DmException(EACCES, "Write mode not supported");
+  ExtendedStat meta;
+  Acl aclCopy(acl);
+  std::string lpath;
+
+  if (vfsCheckPermissions(path, S_IWRITE))
+    vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
+
+  meta = this->vfsExtendedStat(path);
+
+  // User has to be the owner, or root
+  if (getUid(this->secCtx_) != meta.stat.st_uid &&
+      getUid(this->secCtx_) != 0) {
+    vfsThrow(EACCES, "'%s' neither owner nor root, setting ACL of '%s' rejected", clientName.c_str(), path.c_str());
+  }
+
+  // Make sure the owner and group matches!
+  for (size_t i = 0; i < aclCopy.size(); ++i) {
+    switch (aclCopy[i].type) {
+      case AclEntry::kUserObj:
+        aclCopy[i].id = meta.stat.st_uid;
+        break;
+      case AclEntry::kGroupObj:
+        aclCopy[i].id = meta.stat.st_gid;
+        break;
+      default:
+        if (aclCopy[i].type & AclEntry::kDefault && !S_ISDIR(meta.stat.st_mode))
+          vfsThrow(EINVAL, "defaults can be only applied to directories");
+        break;
+    }
+  }
+
+  // Validate the ACL
+  aclCopy.validate();
+
+  // Update the file mode
+  for (size_t i = 0; i < aclCopy.size(); ++i) {
+    switch (aclCopy[i].type) {
+      case AclEntry::kUserObj:
+        meta.stat.st_mode = (meta.stat.st_mode & 0177077) |
+                            (aclCopy[i].perm << 6);
+        break;
+      case AclEntry::kGroupObj:
+        meta.stat.st_mode = (meta.stat.st_mode & 0177707) |
+                            (aclCopy[i].perm << 3);
+        break;
+      case AclEntry::kMask:
+        meta.stat.st_mode = (meta.stat.st_mode & ~070) |
+                            (meta.stat.st_mode & aclCopy[i].perm << 3);
+        break;
+      case AclEntry::kOther:
+        meta.stat.st_mode = (meta.stat.st_mode & 0177770) |
+                            (aclCopy[i].perm);
+        break;
+      default:
+        continue;
+    }
+  }
+
+  lpath = getLocalPath(path);
+
+  vfsSetMode(path, lpath, meta.stat.st_mode);
+  vfsSetAcl(path, lpath, aclCopy);
 }
 
 
@@ -1427,3 +1514,34 @@ void VfsCatalog::vfsSetMode(const std::string& path, const std::string& lpath, m
   snprintf(buf, sizeof buf, "%04o", mode);
   vfsSetXattr(path, lpath, VFS_XATTR "mode", buf, 0);
 }
+
+
+
+void VfsCatalog::vfsSetOwner(const std::string& path, const std::string& lpath, uid_t newUid, gid_t newGid, bool followSymLink) throw (DmException) {
+  char buf[10];
+
+  if (newUid != (uid_t)-1) {
+    snprintf(buf, sizeof buf, "%u", newUid);
+    vfsSetXattr(path, lpath, VFS_XATTR "owner", buf, followSymLink ? 0 : ATTR_DONTFOLLOW);
+  }
+  if (newGid != (gid_t)-1) {
+    snprintf(buf, sizeof buf, "%u", newGid);
+    vfsSetXattr(path, lpath, VFS_XATTR "group", buf, followSymLink ? 0 : ATTR_DONTFOLLOW);
+  }
+}
+
+
+
+void VfsCatalog::vfsSetOwner(const std::string& path, const std::string& lpath, const ExtendedStat& meta, uid_t newUid, gid_t newGid, bool followSymLink) throw (DmException)
+{
+  vfsSetOwner(path, lpath, newUid != meta.stat.st_uid ? newUid : (uid_t)-1, newGid != meta.stat.st_gid ? newGid : (gid_t)-1, followSymLink);
+}
+
+
+
+void VfsCatalog::vfsSetAcl(const std::string& path, const std::string& lpath, Acl acl) throw (DmException) {
+  if (acl.empty())
+    vfsRemoveXattr(path, lpath, VFS_XATTR "acl", 0);
+  else
+    vfsSetXattr(path, lpath, VFS_XATTR "acl", acl.serialize(), 0);
+}
index ece5e1f..9d602c7 100644 (file)
@@ -119,6 +119,9 @@ namespace dmlite {
     bool vfsEvalRegex(regex_t *allowRegex, regex_t *denyRegex, const char *subj);
     int vfsCheckPermissions(const std::string& path, mode_t mode);
     void vfsSetMode(const std::string& path, const std::string& lpath, mode_t mode) throw (DmException);
+    void vfsSetOwner(const std::string& path, const std::string& lpath, uid_t newUid, gid_t newGid, bool followSymLink) throw (DmException);
+    void vfsSetOwner(const std::string& path, const std::string& lpath, const ExtendedStat& meta, uid_t newUid, gid_t newGid, bool followSymLink) throw (DmException);
+    void vfsSetAcl(const std::string& path, const std::string& lpath, Acl acl) throw (DmException);
 
     StackInstance* si_;
     const SecurityContext* secCtx_;