Fix setMode() vs VFS internal xattr: use separated "mode" attribute for the mode...
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Wed, 16 Oct 2013 15:36:24 +0000 (17:36 +0200)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Thu, 17 Oct 2013 19:58:01 +0000 (21:58 +0200)
src/VfsNs.cpp

index 109c145..b881eb8 100644 (file)
@@ -158,12 +158,37 @@ std::string VfsCatalog::getWorkingDir(void) throw (DmException)
 ///                attributes).
 ///
 void VfsCatalog::vfsUpdateStat(ExtendedStat &xStat, Extensible xattrs) throw (DmException) {
+  std::string modeStr;
+  mode_t mode;
+  char *parse;
+
   if (xattrs.hasField(VFS_XATTR "acl"))
     xStat.acl = Acl(xattrs.getString(VFS_XATTR "acl"));
   else
     xStat.acl = Acl();
   xStat.stat.st_gid = xattrs.getUnsigned(VFS_XATTR "group", VFS_GID_NONE);
   xStat.stat.st_uid = xattrs.getUnsigned(VFS_XATTR "owner", VFS_UID_NONE);
+
+  modeStr = xattrs.getString(VFS_XATTR "mode", "");
+  if (!modeStr.empty()) {
+    errno = 0;
+    mode = strtoull(modeStr.c_str(), &parse, 8);
+    if (*parse || errno) {
+#ifdef DEBUG
+      // let's not throw up in ExtendedStat()
+      // ==> only debug
+      vfsThrowErrno("conversion of the mode '%s' failed on '%s'", modeStr.c_str(), xStat.name.c_str());
+#else
+      log(LOG_ERR, "conversion of the mode '%s' failed on '%s'", modeStr.c_str(), xStat.name.c_str());
+#endif
+    } else {
+      // prefer type from the filesystem
+      mode &= ~S_IFMT;
+      mode |= xStat.stat.st_mode & S_IFMT;
+      // use xattr for everything else
+      xStat.stat.st_mode = mode;
+    }
+  }
 }
 
 
@@ -579,6 +604,7 @@ mode_t VfsCatalog::umask(mode_t mask) throw ()
 void VfsCatalog::setMode(const std::string& path, mode_t mode) throw (DmException)
 {
   ExtendedStat meta;
+  char buf[20];
 
   if (vfsCheckPermissions(path, S_IWRITE))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
@@ -590,14 +616,24 @@ void VfsCatalog::setMode(const std::string& path, mode_t mode) throw (DmExceptio
       getUid(this->secCtx_) != 0) {
     vfsThrow(EACCES, "'%s' neither owner nor root, setting the mode of '%s' rejected", clientName.c_str(), path.c_str());
   }
-  // Clean up unwanted bits
+
+  // Clean up unwanted bits (keep type, read/write for owner, correct sticky bit)
   mode &= ~S_IFMT;
-  // Keep type bits
   mode |= (meta.stat.st_mode & S_IFMT);
-  // TODO: buildin resets S_ISGID if not in the group
-  // TODO: update ACL (kUserObj, kGroupObj/kMask, kOther)
+  if (getUid(this->secCtx_) != 0 &&
+      !hasGroup(this->secCtx_->groups, meta.stat.st_gid))
+    mode &= ~S_ISGID;
 
-  wrapCall(chmod(getLocalPath(path).c_str(), mode));
+  //
+  // Set the mode on the filesystem
+  //
+  // But keep read/write for owner.
+  // Filesystem mode couldn't be needed, but let's set it anyway.
+  //
+  wrapCall(chmod(getLocalPath(path).c_str(), mode | S_IRUSR | S_IWUSR));
+
+  snprintf(buf, sizeof buf, "%04o", mode);
+  vfsSetXattr(path, getLocalPath(path), VFS_XATTR "mode", buf, 0);
 }
 
 
@@ -979,7 +1015,8 @@ ExtendedStat* VfsCatalog::readDirx(Directory* dir) throw (DmException)
 
 void VfsCatalog::makeDir(const std::string& path, mode_t mode) throw (DmException)
 {
-  std::string parentPath, name;
+  std::string lpath, parentPath, name;
+  char buf[20];
 
   if (vfsCheckPermissions(path, S_IWRITE))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
@@ -994,11 +1031,19 @@ void VfsCatalog::makeDir(const std::string& path, mode_t mode) throw (DmExceptio
   if (checkPermissions(this->secCtx_, parent.acl, parent.stat, S_IWRITE) != 0)
     vfsThrow(EACCES, "need write access for '%s' on '%s' to create directory '%s'", clientName.c_str(), parentPath.c_str(), name.c_str());
 
+  lpath = getLocalPath(path);
+
   // Clean up unwanted bits
   mode &= ~S_IFMT;
+  mode |= S_IFDIR;
   // TODO: S_ISGID
   // TODO: inherit default ACL
-  wrapCall(mkdir(getLocalPath(path).c_str(), mode));
+
+  // Keep read/write for owner in the mode
+  wrapCall(mkdir(lpath.c_str(), mode | S_IRUSR | S_IWUSR));
+
+  snprintf(buf, sizeof buf, "%04o", mode);
+  vfsSetXattr(path, lpath, VFS_XATTR "mode", buf, 0);
 }
 
 
@@ -1214,6 +1259,7 @@ Extensible VfsCatalog::vfsGetXattrs(const std::string& path, const std::string&
       if (IS_VFS_XATTR(entry->a_name)) {
         if (strcmp(entry->a_name + VFS_XATTR_LENGTH, "acl") == 0 ||
             strcmp(entry->a_name + VFS_XATTR_LENGTH, "group") == 0 ||
+            strcmp(entry->a_name + VFS_XATTR_LENGTH, "mode") == 0 ||
             strcmp(entry->a_name + VFS_XATTR_LENGTH, "owner") == 0)
         {
           attrType = VFS_XATTR_TYPE_PERMS;