Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions src/master/filesystem_node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1938,7 +1938,9 @@ void FilesystemNodeOperationsBase::setExtraAttrRecursive(FSNode *node, uint32_t
fsnodes_update_checksum(node);
}

uint8_t FilesystemNodeOperationsBase::deleteAcl(FSNode *node, AclType type, uint32_t timeStamp) {
uint8_t FilesystemNodeOperationsBase::deleteAcl(
[[maybe_unused]] const FilesystemOperationContext &fsOpContext, FSNode *node, AclType type,
uint32_t timeStamp) {
if (type == AclType::kRichACL) {
gMetadata->aclStorage.erase(node->id);
} else if (type == AclType::kDefault) {
Expand Down Expand Up @@ -1979,7 +1981,8 @@ uint8_t FilesystemNodeOperationsBase::deleteAcl(FSNode *node, AclType type, uint
}

#ifndef METARESTORE
uint8_t FilesystemNodeOperationsBase::getAcl(FSNode *node, RichACL &acl) {
uint8_t FilesystemNodeOperationsBase::getAcl(
[[maybe_unused]] const FilesystemOperationContext &fsOpContext, FSNode *node, RichACL &acl) {
const RichACL *richAcl = gMetadata->aclStorage.get(node->id);

if (!richAcl) { return SAUNAFS_ERROR_ENOATTR; }
Expand All @@ -1991,7 +1994,9 @@ uint8_t FilesystemNodeOperationsBase::getAcl(FSNode *node, RichACL &acl) {
}
#endif

uint8_t FilesystemNodeOperationsBase::setAcl(FSNode *node, const RichACL &acl, uint32_t timeStamp) {
uint8_t FilesystemNodeOperationsBase::setAcl(
[[maybe_unused]] const FilesystemOperationContext &fsOpContext, FSNode *node,
const RichACL &acl, uint32_t timeStamp) {
if (!acl.checkInheritFlags(node->type == FSNodeType::kDirectory)) {
return SAUNAFS_ERROR_ENOTSUP;
}
Expand Down Expand Up @@ -2021,8 +2026,9 @@ uint8_t FilesystemNodeOperationsBase::setAcl(FSNode *node, const RichACL &acl, u
return SAUNAFS_STATUS_OK;
}

uint8_t FilesystemNodeOperationsBase::setAcl(FSNode *node, AclType type,
const AccessControlList &acl, uint32_t timeStamp) {
uint8_t FilesystemNodeOperationsBase::setAcl(
[[maybe_unused]] const FilesystemOperationContext &fsOpContext, FSNode *node, AclType type,
const AccessControlList &acl, uint32_t timeStamp) {
if (type != AclType::kDefault && type != AclType::kAccess) {
return SAUNAFS_ERROR_EINVAL;
}
Expand Down Expand Up @@ -2076,12 +2082,21 @@ int FilesystemNodeOperationsBase::nameCheck(const std::string &name) {
return 0;
}

int FilesystemNodeOperationsBase::access(const FsContext &context, FSNode *node, uint8_t modeMask) {
const RichACL *FilesystemNodeOperationsBase::getAclForAccess(
[[maybe_unused]] const FilesystemOperationContext &fsOpContext, FSNode *node,
[[maybe_unused]] std::optional<RichACL> &scratch) {
return gMetadata->aclStorage.get(node->id);
}

int FilesystemNodeOperationsBase::access(const FsContext &context,
const FilesystemOperationContext &fsOpContext,
FSNode *node, uint8_t modeMask) {
if ((context.sesflags() & SESFLAG_NOMASTERPERMCHECK) || context.uid() == 0) {
return 1; // super user or no permission check
}

const RichACL *nodeAcl = gMetadata->aclStorage.get(node->id);
std::optional<RichACL> aclScratch; // not constructed here; KV backends emplace() as needed
const RichACL *nodeAcl = getAclForAccess(fsOpContext, node, aclScratch);

// If ACLs are present, use it for permission checking
if (nodeAcl != nullptr) {
Expand Down Expand Up @@ -2238,7 +2253,7 @@ uint8_t FilesystemNodeOperationsBase::getNodeForOperation(
return SAUNAFS_ERROR_EPERM;
}

if (context.canCheckPermissions() && !access(context, candidateNode, modeMask)) {
if (context.canCheckPermissions() && !access(context, fsOpContext, candidateNode, modeMask)) {
return SAUNAFS_ERROR_EACCES;
}

Expand Down
40 changes: 34 additions & 6 deletions src/master/filesystem_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

#include "common/platform.h"

#include <optional>

#include "master/filesystem_node_operations_interface.h"
#include "master/filesystem_node_types.h"
#include "master/fs_context.h"
Expand Down Expand Up @@ -174,13 +176,28 @@ class FilesystemNodeOperationsBase : public IFilesystemNodeOperations {
std::string escapeName(const std::string &name) override;

// ACL operations
uint8_t setAcl(FSNode *node, const RichACL &acl, uint32_t timeStamp) override;
uint8_t setAcl(FSNode *node, AclType type, const AccessControlList &acl,
uint32_t timeStamp) override;

/// Stores a RichACL on a node, replacing any previously stored ACL.
/// @see IFilesystemNodeOperations::setAcl
uint8_t setAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
const RichACL &acl, uint32_t timeStamp) override;

/// Merges a POSIX ACL into the node's stored RichACL.
/// @see IFilesystemNodeOperations::setAcl
uint8_t setAcl(const FilesystemOperationContext &fsOpContext, FSNode *node, AclType type,
const AccessControlList &acl, uint32_t timeStamp) override;

#ifndef METARESTORE
uint8_t getAcl(FSNode *node, RichACL &acl) override;
/// Retrieves the stored RichACL for a node.
/// @see IFilesystemNodeOperations::getAcl
uint8_t getAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
RichACL &acl) override;
#endif // METARESTORE
uint8_t deleteAcl(FSNode *node, AclType type, uint32_t timeStamp) override;

/// Removes or prunes the ACL stored on a node according to the ACL type.
/// @see IFilesystemNodeOperations::deleteAcl
uint8_t deleteAcl(const FilesystemOperationContext &fsOpContext, FSNode *node, AclType type,
uint32_t timeStamp) override;

// Recursive operations
#ifndef METARESTORE
Expand Down Expand Up @@ -208,7 +225,18 @@ class FilesystemNodeOperationsBase : public IFilesystemNodeOperations {
inode_t *permissionDeniedINodesOut) override;

// Access control operations
int access(const FsContext &context, FSNode *node, uint8_t modeMask) override;
int access(const FsContext &context, const FilesystemOperationContext &fsOpContext,
FSNode *node, uint8_t modeMask) override;

protected:
/// Returns the stored RichACL for @p node, or nullptr if none is present.
/// @p scratch is an optional caller-supplied buffer; implementations that need
/// to materialise a temporary ACL (e.g. KV backends) emplace into @p scratch
/// and return &scratch->value(), while the default in-memory implementation
/// leaves @p scratch empty and returns a pointer into aclStorage directly.
/// The caller avoids constructing a RichACL when it is not needed.
virtual const RichACL *getAclForAccess(const FilesystemOperationContext &fsOpContext,
FSNode *node, std::optional<RichACL> &scratch);
int stickyAccess(FSNode *parent, FSNode *node, uint32_t uid) override;
int nameCheck(const std::string &name) override;
uint8_t verifySession(const FsContext &context, OperationMode operationMode,
Expand Down
78 changes: 72 additions & 6 deletions src/master/filesystem_node_operations_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,13 +466,78 @@ class IFilesystemNodeOperations {
virtual std::string escapeName(const std::string &name) = 0;

// ACL operations
virtual uint8_t setAcl(FSNode *node, const RichACL &acl, uint32_t timeStamp) = 0;
virtual uint8_t setAcl(FSNode *node, AclType type, const AccessControlList &acl,
uint32_t timeStamp) = 0;

/// Stores a RichACL on a node, replacing any previously stored ACL.
///
/// If the ACL is equivalent to the node's POSIX mode bits (RichACL::equivMode returns true),
/// the stored ACL is erased and only the mode bits are updated. Otherwise the ACL is written
/// to storage after resolving the auto-set-mode flag: if kAutoSetMode is set, the flag is
/// cleared and the ACL's mode is derived from the node's current mode bits before storing.
/// In both cases node->mode is updated to reflect the new permission bits, and the node's
/// ctime and checksum are updated.
///
/// @param fsOpContext The filesystem operation context (transaction).
/// @param node The node on which to set the ACL.
/// @param acl The RichACL to store.
/// @param timeStamp The timestamp used to update the node's ctime.
/// @return SAUNAFS_STATUS_OK on success, SAUNAFS_ERROR_ENOTSUP if the ACL's inherit flags
/// are invalid for the node type.
virtual uint8_t setAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
const RichACL &acl, uint32_t timeStamp) = 0;

/// Merges a POSIX ACL into the node's stored RichACL.
///
/// Reads the current RichACL (if any), calls createExplicitInheritance() and
/// removeInheritOnly() to normalise inheritance, then appends the POSIX ACL entries:
/// - kDefault: calls appendDefaultPosixACL() and refreshes the mode bits from the node.
/// - kAccess: calls appendPosixACL() and updates node->mode from the resulting RichACL.
/// The merged ACL is always written back to storage. node->ctime and checksum are updated.
///
/// @param fsOpContext The filesystem operation context (transaction).
/// @param node The node on which to set the ACL.
/// @param type Must be AclType::kAccess or AclType::kDefault.
/// @param acl The POSIX ACL entries to merge in.
/// @param timeStamp The timestamp used to update the node's ctime.
/// @return SAUNAFS_STATUS_OK on success.
/// SAUNAFS_ERROR_EINVAL if type is neither kAccess nor kDefault.
/// SAUNAFS_ERROR_ENOTSUP if type is kDefault and node is not a directory.
virtual uint8_t setAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
AclType type, const AccessControlList &acl, uint32_t timeStamp) = 0;

#ifndef METARESTORE
virtual uint8_t getAcl(FSNode *node, RichACL &acl) = 0;
/// Retrieves the stored RichACL for a node.
///
/// Looks up the ACL in storage. If no ACL is stored for the node (i.e. permissions are
/// fully described by the mode bits alone), returns SAUNAFS_ERROR_ENOATTR. Otherwise
/// copies the stored ACL into @p acl and asserts that node->mode matches the ACL's mode.
///
/// @param fsOpContext The filesystem operation context (transaction).
/// @param node The node whose ACL to retrieve.
/// @param[out] acl Receives the stored RichACL.
/// @return SAUNAFS_STATUS_OK on success, SAUNAFS_ERROR_ENOATTR if no ACL is stored.
virtual uint8_t getAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
RichACL &acl) = 0;
#endif // METARESTORE
virtual uint8_t deleteAcl(FSNode *node, AclType type, uint32_t timeStamp) = 0;

/// Removes or prunes the ACL stored on a node according to the ACL type.
///
/// - kRichACL: removes the entire stored ACL unconditionally.
/// - kDefault: only valid on directories; reads the current ACL, calls
/// createExplicitInheritance() and removeInheritOnly(true) to strip default
/// (inherit-only) entries, then erases the ACL if empty or writes back the pruned ACL.
/// - kAccess: reads the current ACL, calls createExplicitInheritance() and
/// removeInheritOnly(false) to strip access entries, then erases or writes back.
/// In all cases node->ctime and checksum are updated.
///
/// @param fsOpContext The filesystem operation context (transaction).
/// @param node The node whose ACL to delete or prune.
/// @param type The ACL type to remove (kRichACL, kDefault, or kAccess).
/// @param timeStamp The timestamp used to update the node's ctime.
/// @return SAUNAFS_STATUS_OK on success.
/// SAUNAFS_ERROR_ENOTSUP if type is kDefault and node is not a directory.
/// SAUNAFS_ERROR_EINVAL if type is not kRichACL, kDefault, or kAccess.
virtual uint8_t deleteAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
AclType type, uint32_t timeStamp) = 0;

// Recursive operations
#ifndef METARESTORE
Expand Down Expand Up @@ -501,7 +566,8 @@ class IFilesystemNodeOperations {
inode_t *permissionDeniedINodesOut) = 0;

// Access control operations
virtual int access(const FsContext &context, FSNode *node, uint8_t modeMask) = 0;
virtual int access(const FsContext &context, const FilesystemOperationContext &fsOpContext,
FSNode *node, uint8_t modeMask) = 0;
virtual int stickyAccess(FSNode *parent, FSNode *node, uint32_t uid) = 0;
virtual int nameCheck(const std::string &name) = 0;
virtual uint8_t verifySession(const FsContext &context, OperationMode operationMode,
Expand Down
Loading