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;
+ }
+ }
}