Implementation of setOwner() using xattrs.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Thu, 17 Oct 2013 19:55:24 +0000 (21:55 +0200)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Sun, 23 Feb 2014 13:16:18 +0000 (14:16 +0100)
src/VfsNs.cpp

index fee71c8..bf15ee9 100644 (file)
@@ -579,7 +579,73 @@ void VfsCatalog::setMode(const std::string& path, mode_t mode) throw (DmExceptio
 
 void VfsCatalog::setOwner(const std::string& path, uid_t newUid, gid_t newGid, bool followSymLink) throw (DmException)
 {
-  vfsThrow(ENOSYS, "changing owner not supported");
+  ExtendedStat meta;
+  std::string lpath;
+  char buf[10];
+
+  if (vfsCheckPermissions(path, S_IWRITE))
+    vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
+
+  meta = this->vfsExtendedStat(path);
+
+  // If -1, no changes
+  if (newUid == (uid_t)-1)
+    newUid = meta.stat.st_uid;
+  if (newGid == (gid_t)-1)
+    newGid = meta.stat.st_gid;
+
+  // Make sense to do anything?
+  if (newUid == meta.stat.st_uid && newGid == meta.stat.st_gid)
+    return;
+
+  // If root, skip all checks
+  if (getUid(this->secCtx_) != 0) {
+    // Only root can change the owner
+    if (meta.stat.st_uid != newUid)
+      vfsThrow(EPERM, "'%s' not root user, setting owner on '%s' rejected", clientName.c_str(), path.c_str());
+
+    // If the group is changing...
+    if (meta.stat.st_gid != newGid) {
+      // The user has to be the owner
+      if (meta.stat.st_uid != getUid(this->secCtx_))
+        vfsThrow(EPERM, "'%s' neither root nor owner, setting group on '%s' rejected", clientName.c_str(), path.c_str());
+      // AND it has to belong to that group
+      if (!hasGroup(this->secCtx_->groups, newGid))
+        vfsThrow(EPERM, "'%s' does not belong to group %d, setting group on '%s' rejected", clientName.c_str(), newGid, path.c_str());
+    }
+  }
+
+  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);
+    }
+
+    // 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;
+      }
+      vfsSetXattr(path, lpath, VFS_XATTR "acl", buf, ATTR_DONTFOLLOW);
+    }
+  } catch (DmException& e) {
+  // xattrs are not supported on symbolic links
+  // ==> ignore error for symlinks when followSymLink=false
+    if (!followSymLink && S_ISLNK(meta.stat.st_mode)) {
+      debug("ignored xattr errors on symbolic link '%s'", lpath.c_str());
+    } else {
+      throw;
+    }
+  }
 }