No reading xattrs in extendedStat when not needed, follow symlinks by default.
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>
Mon, 10 Feb 2014 13:38:17 +0000 (14:38 +0100)
src/VfsNs.cpp
src/VfsNs.h

index 538567d..35d4db4 100644 (file)
@@ -103,7 +103,7 @@ void VfsCatalog::changeDir(const std::string& path) throw (DmException)
   if (vfsCheckPermissions(path, S_IEXEC | S_IREAD))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
 
-  meta = this->extendedStat(path);
+  meta = this->vfsExtendedStat(path);
   if (checkPermissions(this->secCtx_, meta.acl, meta.stat, S_IEXEC | S_IREAD) != 0)
     vfsThrow(EACCES, "not enough permissions for '%s' on '%s'", clientName.c_str(), meta.name.c_str());
 
@@ -203,18 +203,25 @@ ExtendedStat VfsCatalog::extendedStat(const std::string& path, bool follow) thro
 {
   ExtendedStat meta;
   Extensible xattrs;
+  std::string key;
+  Extensible::const_iterator it;
 
   if (vfsCheckPermissions(path, S_IREAD))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
 
   meta = vfsExtendedStat(path, follow);
 
-  // not require working xattrs
-  try {
-    xattrs = vfsGetXattrs(path, getLocalPath(path), follow);
-    meta.copy(xattrs);
-  } catch (DmException e) {
-    if (e.code() != ENOTSUP) throw;
+  //
+  // 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);
+
+  // copy attributes
+  for (it = xattrs.begin(); it != xattrs.end(); it++) {
+    key = it->first;
+    meta[key] = xattrs[key];
   }
 
   meta["pool"] = std::string("vfs");
@@ -253,7 +260,7 @@ bool VfsCatalog::access(const std::string& path, int mode) throw (DmException)
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
 
   try {
-    meta = this->extendedStat(path);
+    meta = this->vfsExtendedStat(path);
 
     perm = 0;
     if (mode & R_OK) perm  = S_IREAD;
@@ -311,7 +318,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->extendedStat(path, true);
+  xStat = this->vfsExtendedStat(path, 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());
 
@@ -384,7 +391,7 @@ std::string VfsCatalog::readLink(const std::string& path) throw (DmException)
 
 void VfsCatalog::unlink(const std::string& path) throw (DmException)
 {
-  std::string parentPath, name;
+  std::string parentPath, name, lpath;
 
   if (vfsCheckPermissions(path, S_IWRITE))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
@@ -396,16 +403,19 @@ void VfsCatalog::unlink(const std::string& path) throw (DmException)
   if (checkPermissions(this->secCtx_, parent.acl, parent.stat, S_IWRITE) != 0)
     vfsThrow(EACCES, "not enough permissions for '%s' on '%s' to unlink '%s'", clientName.c_str(), parentPath.c_str(), path.c_str());
 
+  lpath = getLocalPath(path);
+
   // Sticky bit set ==> only directory or file owner can delete
   if ((parent.stat.st_mode & S_ISVTX) == S_ISVTX) {
-    ExtendedStat file = this->extendedStat(path);
+    // not follow symlinks (remove them instead)
+    ExtendedStat file = this->vfsSimpleStat(name, path, lpath, false);
     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());
     }
   }
 
-  wrapCall(::unlink(getLocalPath(path).c_str()));
+  wrapCall(::unlink(lpath.c_str()));
 }
 
 
@@ -427,8 +437,11 @@ void VfsCatalog::create(const std::string& path, mode_t mode) throw (DmException
   if (checkPermissions(this->secCtx_, parent.acl, parent.stat, S_IWRITE) != 0)
     vfsThrow(EACCES, "need write access for '%s' on '%s' to create '%s'", clientName.c_str(), parentPath.c_str(), path.c_str());
 
+  lpath = getLocalPath(path);
+
   try {
-    file = this->extendedStat(path);
+    // follow symlinks
+    file = this->vfsSimpleStat(name, path, lpath, true);
   } catch (DmException e) {
     code = DMLITE_ERRNO(e.code());
     if (code != ENOENT) throw;
@@ -449,7 +462,6 @@ void VfsCatalog::create(const std::string& path, mode_t mode) throw (DmException
   }
 #endif
 
-  lpath = getLocalPath(path);
   if (code == ENOENT) {
     // Create new
     wrapCall(f = fopen(lpath.c_str(), "w"));
@@ -487,7 +499,7 @@ void VfsCatalog::setMode(const std::string& path, mode_t mode) throw (DmExceptio
   if (vfsCheckPermissions(path, S_IWRITE))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
 
-  meta = this->vfsExtendedStat(path, true);
+  meta = this->vfsExtendedStat(path);
 
   // User has to be the owner, or root
   if (getUid(this->secCtx_) != meta.stat.st_uid &&
@@ -520,7 +532,7 @@ void VfsCatalog::setSize(const std::string& path, size_t newSize) throw (DmExcep
   if (vfsCheckPermissions(path, S_IWRITE))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
 
-  file = this->extendedStat(path);
+  file = this->vfsExtendedStat(path);
   if (S_ISDIR(file.stat.st_mode))
     vfsThrow(EISDIR, "'%s' is directory, can not truncate", path.c_str());
   if (getUid(this->secCtx_) != file.stat.st_uid &&
@@ -555,7 +567,7 @@ void VfsCatalog::utime(const std::string& path, const struct utimbuf* buf) throw
   if (vfsCheckPermissions(path, S_IWRITE))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
 
-  meta = this->extendedStat(path);
+  meta = this->vfsExtendedStat(path);
   if (getUid(this->secCtx_) != meta.stat.st_uid &&
       checkPermissions(this->secCtx_, meta.acl, meta.stat, S_IWRITE) != 0) {
     vfsThrow(EACCES, "not enough permissions for '%s' to modify the time of '%s'", clientName.c_str(), path.c_str());
@@ -608,8 +620,10 @@ void VfsCatalog::updateExtendedAttributes(const std::string& path,
 
   lpath = getLocalPath(path);
 
-  oldXattrs = vfsGetXattrs(path, lpath, true);
+  oldXattrs = meta;
   newXattrs.copy(attr);
+  oldXattrs.erase("pool");
+  newXattrs.erase("pool");
 
   for (it = newXattrs.begin(); it != newXattrs.end(); it++) {
     key = it->first;
@@ -621,6 +635,7 @@ void VfsCatalog::updateExtendedAttributes(const std::string& path,
     // OK, it's working now :-)
     value = Extensible::anyToString(it->second);
 
+    // reconcile
     if (oldXattrs.hasField(key)) {
       oldvalue = Extensible::anyToString(oldXattrs[key]);
       if (oldvalue != value)
@@ -630,8 +645,10 @@ void VfsCatalog::updateExtendedAttributes(const std::string& path,
       vfsSetXattr(path, lpath, key, value, ATTR_CREATE);
     }
   }
+
   for (it = oldXattrs.begin(); it != oldXattrs.end(); it++) {
     key = it->first;
+
     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());
@@ -670,7 +687,7 @@ Directory* VfsCatalog::openDir(const std::string& path) throw (DmException)
   if (vfsCheckPermissions(path, S_IREAD))
     vfsThrow(EACCES, "not enough permissions for '%s'", clientName.c_str());
 
-  meta = this->extendedStat(path);
+  meta = this->vfsExtendedStat(path);
   if (checkPermissions(this->secCtx_, meta.acl, meta.stat, S_IREAD) != 0)
     vfsThrow(EACCES, "not enough permissions for '%s' to read '%s'", clientName.c_str(), path.c_str());
 
@@ -791,7 +808,7 @@ void VfsCatalog::rename(const std::string& oldPath, const std::string& newPath)
 
   // Check sticky
   if (oldParent.stat.st_mode & S_ISVTX) {
-    ExtendedStat old = this->extendedStat(oldPath);
+    ExtendedStat old = this->vfsExtendedStat(oldPath);
     if (getUid(this->secCtx_) != oldParent.stat.st_uid &&
         getUid(this->secCtx_) != old.stat.st_uid &&
         checkPermissions(this->secCtx_, old.acl, old.stat, S_IWRITE) != 0)
@@ -822,7 +839,7 @@ void VfsCatalog::removeDir(const std::string& path) throw (DmException)
     vfsThrow(EINVAL, "can not remove '/'");
 
   ExtendedStat parent = this->getParent(path, &parentPath, &name);
-  ExtendedStat entry = this->extendedStat(path);
+  ExtendedStat entry = this->vfsExtendedStat(path);
   if ((parent.stat.st_mode & S_ISVTX) == S_ISVTX) {
     // Sticky bit set
     if (getUid(this->secCtx_) != entry.stat.st_uid &&
@@ -915,9 +932,9 @@ ExtendedStat VfsCatalog::getParent(const std::string& path,
 
   // Get the files now
   if (!parentPath->empty()) {
-    return this->extendedStat(*parentPath);
+    return this->vfsExtendedStat(*parentPath);
   } else {
-    return this->extendedStat(this->getWorkingDir());
+    return this->vfsExtendedStat(this->getWorkingDir());
   }
 }
 
@@ -955,6 +972,11 @@ int VfsCatalog::checkPermissions(const SecurityContext *context, const Acl &acl,
 
 
 
+///
+/// Get extended attributes.
+///
+/// @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) {
   int len;
   std::string attrValue;
index 7dcf6db..07610de 100644 (file)
@@ -101,7 +101,7 @@ namespace dmlite {
     /// 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) throw (DmException);
+    ExtendedStat vfsExtendedStat(const std::string& path, bool follow = 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);
     void vfsSetXattr(const std::string& path, const std::string& lpath, const std::string key, const std::string value, int flags);