From 65de3f5e239acef4981d3ede215adccf49108643 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Franti=C5=A1ek=20Dvo=C5=99=C3=A1k?= Date: Thu, 17 Oct 2013 21:44:45 +0200 Subject: [PATCH] No reading xattrs in extendedStat when not needed, follow symlinks by default. --- src/VfsNs.cpp | 68 +++++++++++++++++++++++++++++++++++++++-------------------- src/VfsNs.h | 2 +- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/VfsNs.cpp b/src/VfsNs.cpp index 538567d..35d4db4 100644 --- a/src/VfsNs.cpp +++ b/src/VfsNs.cpp @@ -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 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; diff --git a/src/VfsNs.h b/src/VfsNs.h index 7dcf6db..07610de 100644 --- a/src/VfsNs.h +++ b/src/VfsNs.h @@ -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); -- 1.8.2.3