/// 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;
+ }
+ }
}
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());
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);
}
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());
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);
}
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;