Initial support for VFS-related metadata in User Extended Attributes.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Thu, 17 Oct 2013 19:44:45 +0000 (21:44 +0200)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Sun, 23 Feb 2014 13:16:18 +0000 (14:16 +0100)
src/VfsNs.cpp
src/VfsNs.h

index 3ff977c..658546e 100644 (file)
 #include "Vfs.h"
 #include "VfsNs.h"
 
+#define VFS_XATTR "#vfs."
+#define VFS_XATTR_LENGTH 5
+#define VFS_XATTR_TYPE_PERMS 1
+#define VFS_XATTR_TYPE_NONPERMS 2
+#define VFS_XATTR_TYPE_USER 4
+#define VFS_XATTR_TYPE_ALL 7
+#define IS_VFS_XATTR(NAME) (strncmp((NAME), VFS_XATTR, VFS_XATTR_LENGTH) == 0)
 #define IS_VFS_FILE(NAME) (strncmp((NAME), VFS_FILE, VFS_FILE_LENGTH) == 0)
 
+// force everything to current owner for now
+#define VFS_UID_NONE xStat.stat.st_uid
+#define VFS_GID_NONE xStat.stat.st_gid
+
 using namespace dmlite;
 
 
@@ -139,6 +150,24 @@ std::string VfsCatalog::getWorkingDir(void) throw (DmException)
 
 
 ///
+/// Update ExtendedStat fields related to permissions.
+/// Requires user extended attributes retrieved with VFS_XATTR_TYPE_PERMS or
+/// VFS_XATTR_TYPE_ALL.
+///
+/// @param xStat   Updated ExtendedStat class.
+/// @param xattrs  User extended attributes (requires acl, group, and owner VFS
+///                attributes).
+///
+void VfsCatalog::vfsUpdateStat(ExtendedStat &xStat, Extensible xattrs) throw (DmException) {
+  if (xattrs.hasField(VFS_XATTR "acl"))
+    xStat.acl = Acl(xattrs.getString(VFS_XATTR "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);
+}
+
+
+
+///
 /// Do simple stat()/lstat() without setting st_nlink and checking permissions.
 /// Optionally retrieves also the information related to permissions in
 /// optimized way (using only attributes related to permissions).
@@ -146,10 +175,12 @@ std::string VfsCatalog::getWorkingDir(void) throw (DmException)
 /// @param name    file name
 /// @param path    local disk namespace path
 /// @param follow  follow symlinks (to use stat() or lstat())
+/// @param perms   get information related to permissions
 //
-ExtendedStat VfsCatalog::vfsSimpleStat(const std::string& name, const std::string& path, const std::string& lpath, bool follow) throw (DmException) {
+ExtendedStat VfsCatalog::vfsSimpleStat(const std::string& name, const std::string& path, const std::string& lpath, bool follow, bool perms) throw (DmException) {
   ExtendedStat xStat;
   struct stat  fstat;
+  Extensible xattrs;
 
   if (follow)
     wrapCall(stat(lpath.c_str(), &fstat), "could not stat '%s'", path.c_str());
@@ -162,6 +193,15 @@ ExtendedStat VfsCatalog::vfsSimpleStat(const std::string& name, const std::strin
   xStat.status  = ExtendedStat::kOnline;
   xStat.acl     = Acl();
 
+  if (perms) {
+    xattrs = vfsGetXattrs(path, lpath, true, VFS_XATTR_TYPE_PERMS);
+    vfsUpdateStat(xStat, xattrs);
+  } else {
+    xStat.acl     = Acl();
+    xStat.stat.st_uid = VFS_UID_NONE;
+    xStat.stat.st_gid = VFS_GID_NONE;
+  }
+
   return xStat;
 }
 
@@ -174,8 +214,9 @@ ExtendedStat VfsCatalog::vfsSimpleStat(const std::string& name, const std::strin
 ///
 /// @param path    public namespace path
 /// @param follow  follow symlinks
+/// @param parms   get information related to permissions
 ///
-ExtendedStat VfsCatalog::vfsExtendedStat(const std::string& path, bool follow) throw (DmException) {
+ExtendedStat VfsCatalog::vfsExtendedStat(const std::string& path, bool follow, bool perms) throw (DmException) {
   ExtendedStat meta;
   std::vector<std::string> components;
   std::string dir;
@@ -191,13 +232,13 @@ ExtendedStat VfsCatalog::vfsExtendedStat(const std::string& path, bool follow) t
     if (i < 2) dir += components[i];
     else dir = dir + "/" + components[i];
 
-    meta = vfsSimpleStat(components[i], dir, getLocalPath(dir), true);
+    meta = vfsSimpleStat(components[i], dir, getLocalPath(dir), true, true);
 
     if (checkPermissions(this->secCtx_, meta.acl, meta.stat, S_IEXEC) != 0)
       vfsThrow(EACCES, "not enough permissions for '%s' to list '%s'", clientName.c_str(), meta.name.c_str());
   }
 
-  return vfsSimpleStat(components.back(), path, getLocalPath(path), follow);
+  return vfsSimpleStat(components.back(), path, getLocalPath(path), follow, perms);
 }
 
 
@@ -212,21 +253,24 @@ ExtendedStat VfsCatalog::extendedStat(const std::string& path, bool follow) thro
   if (vfsCheckPermissions(path, S_IREAD))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
 
-  meta = vfsExtendedStat(path, follow);
+  meta = vfsExtendedStat(path, follow, false);
 
   //
   // User extended attributes are not supported on symlinks. We need the same
   // owner, permissions and other atributes like the original file anyway.
   // ==> always follow the symlinks.
   //
-  xattrs = vfsGetXattrs(path, getLocalPath(path), true);
+  xattrs = vfsGetXattrs(path, getLocalPath(path), true, VFS_XATTR_TYPE_ALL);
 
-  // copy attributes
+  // copy non-VFS attributes
   for (it = xattrs.begin(); it != xattrs.end(); it++) {
     key = it->first;
-    meta[key] = xattrs[key];
+    if (!IS_VFS_XATTR(key.c_str()))
+      meta[key] = xattrs[key];
   }
 
+  vfsUpdateStat(meta, xattrs);
+
   meta["pool"] = std::string("vfs");
 
 #if 0
@@ -326,7 +370,7 @@ std::vector<Replica> VfsCatalog::getReplicas(const std::string& path) throw (DmE
   if (vfsCheckPermissions(path, S_IREAD))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
 
-  xStat = this->vfsExtendedStat(path, true);
+  xStat = this->vfsExtendedStat(path, true, true);
   if (checkPermissions(this->secCtx_, xStat.acl, xStat.stat, S_IREAD) != 0)
     vfsThrow(EACCES, "not enough permissions for '%s' to read '%s'", clientName.c_str(), path.c_str());
 
@@ -381,7 +425,7 @@ std::string VfsCatalog::readLink(const std::string& path) throw (DmException)
   if (vfsCheckPermissions(path, S_IREAD))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
 
-  meta = this->vfsExtendedStat(path, false);
+  meta = this->vfsExtendedStat(path, false, true);
   if (checkPermissions(this->secCtx_, meta.acl, meta.stat, S_IREAD) != 0)
     vfsThrow(EACCES, "not enough permissions for '%s' on '%s'", clientName.c_str(), path.c_str());
 
@@ -416,7 +460,7 @@ void VfsCatalog::unlink(const std::string& path) throw (DmException)
   // Sticky bit set ==> only directory or file owner can delete
   if ((parent.stat.st_mode & S_ISVTX) == S_ISVTX) {
     // not follow symlinks (remove them instead)
-    ExtendedStat file = this->vfsSimpleStat(name, path, lpath, false);
+    ExtendedStat file = this->vfsSimpleStat(name, path, lpath, false, true);
     if (getUid(this->secCtx_) != file.stat.st_uid &&
         getUid(this->secCtx_) != parent.stat.st_uid) {
       vfsThrow(EACCES, "not enough permissions for '%s' to unlink '%s' (sticky bit set)", clientName.c_str(), path.c_str());
@@ -449,7 +493,7 @@ void VfsCatalog::create(const std::string& path, mode_t mode) throw (DmException
 
   try {
     // follow symlinks
-    file = this->vfsSimpleStat(name, path, lpath, true);
+    file = this->vfsSimpleStat(name, path, lpath, true, true);
   } catch (DmException e) {
     code = DMLITE_ERRNO(e.code());
     if (code != ENOENT) throw;
@@ -636,6 +680,10 @@ void VfsCatalog::updateExtendedAttributes(const std::string& path,
   for (it = newXattrs.begin(); it != newXattrs.end(); it++) {
     key = it->first;
 
+    // not synchronize anything VFS plugin related
+    if (IS_VFS_XATTR(key.c_str()))
+      vfsThrow(EPERM, "change VFS plugin attribute not permitted (%s)", key.c_str());
+
     // used to be bug in Extensible? bool type "true" not mapped to true
     //if (it->second.type() == typeid(bool)) value = Extensible::anyToBoolean(it->second) ? std::string("1") : std::string("0");
     //else value = Extensible::anyToString(it->second);
@@ -657,6 +705,10 @@ void VfsCatalog::updateExtendedAttributes(const std::string& path,
   for (it = oldXattrs.begin(); it != oldXattrs.end(); it++) {
     key = it->first;
 
+    // not synchronize anything VFS plugin related
+    if (IS_VFS_XATTR(key.c_str()))
+      vfsThrow(DMLITE_SYSERR(EINVAL), "internal error: got VFS plugin attribute from extendedStat(), this should not happen");
+
     value = Extensible::anyToString(it->second);
     debug("removing '%s' = '%s' from '%s'", key.c_str(), value.c_str(), lpath.c_str());
     wrapCall(attr_remove(lpath.c_str(), key.c_str(), 0), "could not remove '%s' on '%s'", key.c_str(), path.c_str());
@@ -1035,13 +1087,18 @@ int VfsCatalog::checkPermissions(const SecurityContext *context, const Acl &acl,
 ///
 /// @param path    local disk namespace path
 /// @param follow  follow symlinks (not well supported, it should be always true), XXX: dangling symlinks fails
-Extensible VfsCatalog::vfsGetXattrs(const std::string& path, const std::string& lpath, bool follow) throw (DmException) {
+/// @param amount  what to retrieve, bitmask:
+///   VFS_XATTR_TYPE_PERMS    = 1: permissions related attributes (#vfs.acl, #vfs.owner, #vfs.group, ...),
+///   VFS_XATTR_TYPE_NONPERMS = 2: other VFS plugin related attributes (#vfs.replica*, ...),
+///   VFS_XATTR_TYPE_USER     = 4: user attributes
+Extensible VfsCatalog::vfsGetXattrs(const std::string& path, const std::string& lpath, bool follow, int amount) throw (DmException) {
   int len;
   std::string attrValue;
   Extensible xattrs;
   attrlist_cursor_t attr_cursor;
   attrlist_t *list;
   attrlist_ent_t *entry;
+  int attrType;
 
   list = (attrlist_t *)this->buffer;
   memset(&attr_cursor, 0, sizeof attr_cursor);
@@ -1049,13 +1106,25 @@ Extensible VfsCatalog::vfsGetXattrs(const std::string& path, const std::string&
     wrapCall(attr_list(lpath.c_str(), this->buffer, sizeof this->buffer, follow ? 0 : ATTR_DONTFOLLOW, &attr_cursor), "could not get list of extended attributes on '%s'", path.c_str());
     for (int i = 0; i < list->al_count; i++) {
       entry = ATTR_ENTRY(this->buffer, i);
-      // "selinux" is returned in the list, but failing to get the value
-      if (strcmp(entry->a_name, "selinux") == 0) {
-        //debug("skipping attribute '%s'", entry->a_name);
+      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, "owner") == 0)
+        {
+          attrType = VFS_XATTR_TYPE_PERMS;
+        } else {
+          attrType = VFS_XATTR_TYPE_NONPERMS;
+        }
       } else {
+        // "selinux" is returned in the list, but failing to get the value
+        if (strcmp(entry->a_name, "selinux") == 0) attrType = 0;
+        else attrType = VFS_XATTR_TYPE_USER;
+      }
+
+      if ((attrType & amount) != 0) {
         len = sizeof this->xattrValue;
         wrapCall(attr_get(lpath.c_str(), entry->a_name, this->xattrValue, &len, follow ? 0 : ATTR_DONTFOLLOW), "could not get extended attribute '%s' on '%s'", entry->a_name, path.c_str());
-        debug("'%s' = '%.*s'", entry->a_name, len, this->xattrValue);
+        debug("'%s' (type %d) = '%.*s'", entry->a_name, attrType, len, this->xattrValue);
         attrValue.assign(this->xattrValue, len);
         xattrs[entry->a_name] = attrValue;
       }
index 33f9558..33c1e4f 100644 (file)
@@ -106,10 +106,11 @@ namespace dmlite {
     /// Get path on the local disk according to "public" path.
     /// Relative paths are unchanged.
     const std::string getLocalPath(const std::string &path);
-    ExtendedStat vfsSimpleStat(const std::string& name, const std::string& path, const std::string& lpath, bool follow) throw (DmException);
-    ExtendedStat vfsExtendedStat(const std::string& path, bool follow = true) throw (DmException);
+    void vfsUpdateStat(ExtendedStat &xStat, Extensible xattrs) throw (DmException);
+    ExtendedStat vfsSimpleStat(const std::string& name, const std::string& path, const std::string& lpath, bool follow, bool perms) throw (DmException);
+    ExtendedStat vfsExtendedStat(const std::string& path, bool follow = true, bool perms = true) throw (DmException);
     PrivateDir* vfsOpenDir(const std::string& lpath, const std::string& path) throw (DmException);
-    Extensible vfsGetXattrs(const std::string& path, const std::string& lpath, bool follow) throw (DmException);
+    Extensible vfsGetXattrs(const std::string& path, const std::string& lpath, bool follow, int amount) throw (DmException);
     void vfsSetXattr(const std::string& path, const std::string& lpath, const std::string key, const std::string value, int flags);
     regex_t *vfsCompileRegex(const char *name, const std::string value) throw (DmException);
     bool vfsEvalRegex(regex_t *allowRegex, regex_t *denyRegex, const char *subj);