First experiments with metadata files (ACLs write only). metadata
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Wed, 11 Sep 2013 14:06:30 +0000 (16:06 +0200)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Wed, 11 Sep 2013 14:06:30 +0000 (16:06 +0200)
src/Vfs.h
src/VfsNs.cpp

index 02ca183..53ee84e 100644 (file)
--- a/src/Vfs.h
+++ b/src/Vfs.h
@@ -19,6 +19,8 @@
   #define wrapCall(RETVAL, ...) dmlite::vfsWrapCallHelper(__func__, (RETVAL) , ##__VA_ARGS__)
 #endif
 
+#define VFS_METAFILE_PREFIX ".#vfs"
+
 namespace dmlite {
 
   /// Concrete factory for DPM wrapper
index 7732a4a..9a0a40e 100644 (file)
@@ -18,6 +18,8 @@
 using namespace dmlite;
 
 
+static void pathGetParts(const std::string &path, std::string &dirName, std::string& baseName);
+
 
 VfsCatalog::VfsCatalog(const std::string& host) throw (DmException): Catalog(),
         hostName_(host)
@@ -75,6 +77,10 @@ ExtendedStat VfsCatalog::extendedStat(const std::string& path, bool follow) thro
 {
   ExtendedStat xStat;
   struct stat  fstat;
+  FILE *f;
+  char buf[100];
+  size_t l;
+  std::string *aclstr;
 
   if (follow)
     wrapCall(stat(path.c_str(), &fstat));
@@ -89,6 +95,21 @@ ExtendedStat VfsCatalog::extendedStat(const std::string& path, bool follow) thro
   xStat.status  = ExtendedStat::kOnline;
   xStat["pool"] = std::string("vfs");
 
+  if ((f = fopen(VFS_METAFILE_PREFIX, "r")) == NULL) {
+    if (errno != ENOENT) vfsThrowErrno("opening directory metadata failed");
+  } else {
+    l = fread(buf, 1, sizeof(buf) - 1, f);
+    if (ferror(f)) vfsThrow(DMLITE_SYSERR(EIO), "reading directory metadata failed");
+    buf[l] = '\0';
+    try {
+      xStat.acl = Acl(std::string(buf));
+    } catch (...) {
+      fclose(f);
+      throw;
+    }
+    wrapCall(fclose(f), "closing directory metadata failed");
+  }
+
   return xStat;
 }
 
@@ -182,12 +203,26 @@ std::string VfsCatalog::readLink(const std::string& path) throw (DmException)
 
 
 
-void VfsCatalog::unlink(const std::string& path) throw (DmException)
+static void rawUnlink(const std::string& path) throw (DmException)
 {
   wrapCall(::unlink(path.c_str()));
 }
 
 
+void VfsCatalog::unlink(const std::string& path) throw (DmException)
+{
+  std::string dir, name;
+
+  pathGetParts(path, dir, name);
+  try {
+    rawUnlink(dir + "/" + VFS_METAFILE_PREFIX + name);
+  } catch (dmlite::DmException& e) {
+    if (e.code() != ENOENT) throw;
+  }
+  rawUnlink(path);
+}
+
+
 
 void VfsCatalog::create(const std::string& path, mode_t mode) throw (DmException)
 {
@@ -240,7 +275,67 @@ void VfsCatalog::setChecksum(const std::string& path,
 
 void VfsCatalog::setAcl(const std::string& path, const Acl& acl) throw (DmException)
 {
-  throw DmException(EACCES, "Write mode not supported");
+  ExtendedStat meta = this->extendedStat(path, false);
+  Acl aclCopy(acl);
+  mode_t oldmode;
+  std::string aclstr, parentPath, fileName;
+  FILE *f;
+
+//FIXME: who can set ACLs?
+
+  // Make sure the owner and group matches!
+  for (size_t i = 0; i < aclCopy.size(); i++) {
+    if (aclCopy[i].type == AclEntry::kUserObj)
+      aclCopy[i].id = meta.stat.st_uid;
+    else if (aclCopy[i].type == AclEntry::kGroupObj)
+      aclCopy[i].id = meta.stat.st_gid;
+    else if (aclCopy[i].type & AclEntry::kDefault && !S_ISDIR(meta.stat.st_mode))
+      vfsThrow(EINVAL, "Defaults can be only applied to directories");
+  }
+
+  // Validate the ACL
+  aclCopy.validate();
+
+  // Update the file mode
+  oldmode = meta.stat.st_mode;
+  for (size_t i = 0; i < aclCopy.size(); i++) {
+    switch (aclCopy[i].type) {
+      case AclEntry::kUserObj:
+        meta.stat.st_mode = (meta.stat.st_mode & 0177077) |
+                            (aclCopy[i].perm << 6);
+        break;
+      case AclEntry::kGroupObj:
+        meta.stat.st_mode = (meta.stat.st_mode & 0177707) |
+                            (aclCopy[i].perm << 3);
+        break;
+      case AclEntry::kMask:
+        meta.stat.st_mode = (meta.stat.st_mode & ~070) |
+                            (meta.stat.st_mode & aclCopy[i].perm << 3);
+        break;
+      case AclEntry::kOther:
+        meta.stat.st_mode = (meta.stat.st_mode & 0177770) |
+                            (aclCopy[i].perm);
+        break;
+      default:
+        continue;
+    }
+  }
+
+  if (oldmode != meta.stat.st_mode) {
+    wrapCall(chmod(path.c_str(), meta.stat.st_mode));
+  }
+
+  aclstr = aclCopy.serialize();
+  if (S_ISDIR(meta.stat.st_mode)) {
+    f = fopen((path + "/" + VFS_METAFILE_PREFIX).c_str(), "w");
+    wrapCall(f, "creating directory metadata failed");
+  } else {
+    pathGetParts(path, parentPath, fileName);
+    f = fopen((parentPath + "/" + VFS_METAFILE_PREFIX + fileName).c_str(), "w");
+    wrapCall(f, "creating file metadata failed");
+  }
+  fprintf(f, "%s", aclstr.c_str());
+  wrapCall(fclose(f), "closing metadata failed");
 }
 
 
@@ -371,7 +466,12 @@ void VfsCatalog::rename(const std::string& oldPath, const std::string& newPath)
 
 void VfsCatalog::removeDir(const std::string& path) throw (DmException)
 {
-  wrapCall(rmdir(path.c_str()));
+  try {
+    VfsCatalog::unlink(path + "/" + VFS_METAFILE_PREFIX);
+  } catch (dmlite::DmException& e) {
+    if (e.code() != ENOENT) throw;
+  }
+  wrapCall(::rmdir(path.c_str()));
 }
 
 
@@ -403,3 +503,11 @@ void VfsCatalog::updateReplica(const Replica& replica) throw (DmException)
 {
   // Nothing
 }
+
+
+static void pathGetParts(const std::string &path, std::string &dirName, std::string& baseName) {
+  std::vector<std::string> components = Url::splitPath(path);
+  baseName = components.back();
+  components.pop_back();
+  dirName = Url::joinPath(components);
+}