Skip to content

Commit 2386ff3

Browse files
committed
refactor(master): allow ACL ops in KV backends
Thread FilesystemOperationContext through all ACL methods (deleteAcl, setAcl, getAcl, applySetAcl, applySetRichAcl) in both IFilesystemNodeOperations and IFilesystemOperations, removing the internal transaction creation that was hardcoded inside each FilesystemOperationsBase method body. Callers (matoclserv, restore) now own the transaction lifecycle, enabling KV backends to participate in ACL operations as part of a single coordinated transaction. Introduce a protected virtual `getAclForAccess()` method to abstract ACL retrieval inside `access()`. The default in-memory implementation reads from `aclStorage`; KV backends can override it to materialise the ACL from the key-value store. Pass `FilesystemOperationContext` through `access()` at all call sites so the new virtual hook receives the context it needs. A caller-supplied `RichACL scratch` buffer is provided so temporary ACLs can be returned without extra allocation. The in-memory base implementation is not affected by the changes. Also document all affected interface declarations according to their implementations. Signed-off-by: guillex <guillex@leil.io>
1 parent 5c742a6 commit 2386ff3

10 files changed

+375
-90
lines changed

src/master/filesystem_node.cc

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,7 +1938,9 @@ void FilesystemNodeOperationsBase::setExtraAttrRecursive(FSNode *node, uint32_t
19381938
fsnodes_update_checksum(node);
19391939
}
19401940

1941-
uint8_t FilesystemNodeOperationsBase::deleteAcl(FSNode *node, AclType type, uint32_t timeStamp) {
1941+
uint8_t FilesystemNodeOperationsBase::deleteAcl(
1942+
[[maybe_unused]] const FilesystemOperationContext &fsOpContext, FSNode *node, AclType type,
1943+
uint32_t timeStamp) {
19421944
if (type == AclType::kRichACL) {
19431945
gMetadata->aclStorage.erase(node->id);
19441946
} else if (type == AclType::kDefault) {
@@ -1979,7 +1981,8 @@ uint8_t FilesystemNodeOperationsBase::deleteAcl(FSNode *node, AclType type, uint
19791981
}
19801982

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

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

1994-
uint8_t FilesystemNodeOperationsBase::setAcl(FSNode *node, const RichACL &acl, uint32_t timeStamp) {
1997+
uint8_t FilesystemNodeOperationsBase::setAcl(
1998+
[[maybe_unused]] const FilesystemOperationContext &fsOpContext, FSNode *node,
1999+
const RichACL &acl, uint32_t timeStamp) {
19952000
if (!acl.checkInheritFlags(node->type == FSNodeType::kDirectory)) {
19962001
return SAUNAFS_ERROR_ENOTSUP;
19972002
}
@@ -2021,8 +2026,9 @@ uint8_t FilesystemNodeOperationsBase::setAcl(FSNode *node, const RichACL &acl, u
20212026
return SAUNAFS_STATUS_OK;
20222027
}
20232028

2024-
uint8_t FilesystemNodeOperationsBase::setAcl(FSNode *node, AclType type,
2025-
const AccessControlList &acl, uint32_t timeStamp) {
2029+
uint8_t FilesystemNodeOperationsBase::setAcl(
2030+
[[maybe_unused]] const FilesystemOperationContext &fsOpContext, FSNode *node, AclType type,
2031+
const AccessControlList &acl, uint32_t timeStamp) {
20262032
if (type != AclType::kDefault && type != AclType::kAccess) {
20272033
return SAUNAFS_ERROR_EINVAL;
20282034
}
@@ -2076,12 +2082,21 @@ int FilesystemNodeOperationsBase::nameCheck(const std::string &name) {
20762082
return 0;
20772083
}
20782084

2079-
int FilesystemNodeOperationsBase::access(const FsContext &context, FSNode *node, uint8_t modeMask) {
2085+
const RichACL *FilesystemNodeOperationsBase::getAclForAccess(
2086+
[[maybe_unused]] const FilesystemOperationContext &fsOpContext, FSNode *node,
2087+
[[maybe_unused]] RichACL &scratch) {
2088+
return gMetadata->aclStorage.get(node->id);
2089+
}
2090+
2091+
int FilesystemNodeOperationsBase::access(const FsContext &context,
2092+
const FilesystemOperationContext &fsOpContext,
2093+
FSNode *node, uint8_t modeMask) {
20802094
if ((context.sesflags() & SESFLAG_NOMASTERPERMCHECK) || context.uid() == 0) {
20812095
return 1; // super user or no permission check
20822096
}
20832097

2084-
const RichACL *nodeAcl = gMetadata->aclStorage.get(node->id);
2098+
RichACL aclScratch; // unused by this implementation, needed for KV backends compatibility
2099+
const RichACL *nodeAcl = getAclForAccess(fsOpContext, node, aclScratch);
20852100

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

2241-
if (context.canCheckPermissions() && !access(context, candidateNode, modeMask)) {
2256+
if (context.canCheckPermissions() && !access(context, fsOpContext, candidateNode, modeMask)) {
22422257
return SAUNAFS_ERROR_EACCES;
22432258
}
22442259

src/master/filesystem_node.h

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,28 @@ class FilesystemNodeOperationsBase : public IFilesystemNodeOperations {
174174
std::string escapeName(const std::string &name) override;
175175

176176
// ACL operations
177-
uint8_t setAcl(FSNode *node, const RichACL &acl, uint32_t timeStamp) override;
178-
uint8_t setAcl(FSNode *node, AclType type, const AccessControlList &acl,
179-
uint32_t timeStamp) override;
177+
178+
/// Stores a RichACL on a node, replacing any previously stored ACL.
179+
/// @see IFilesystemNodeOperations::setAcl
180+
uint8_t setAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
181+
const RichACL &acl, uint32_t timeStamp) override;
182+
183+
/// Merges a POSIX ACL into the node's stored RichACL.
184+
/// @see IFilesystemNodeOperations::getAcl
185+
uint8_t setAcl(const FilesystemOperationContext &fsOpContext, FSNode *node, AclType type,
186+
const AccessControlList &acl, uint32_t timeStamp) override;
187+
180188
#ifndef METARESTORE
181-
uint8_t getAcl(FSNode *node, RichACL &acl) override;
189+
/// Retrieves the stored RichACL for a node.
190+
/// @see IFilesystemNodeOperations::getAcl
191+
uint8_t getAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
192+
RichACL &acl) override;
182193
#endif // METARESTORE
183-
uint8_t deleteAcl(FSNode *node, AclType type, uint32_t timeStamp) override;
194+
195+
/// Removes or prunes the ACL stored on a node according to the ACL type.
196+
/// @see IFilesystemNodeOperations::deleteAcl
197+
uint8_t deleteAcl(const FilesystemOperationContext &fsOpContext, FSNode *node, AclType type,
198+
uint32_t timeStamp) override;
184199

185200
// Recursive operations
186201
#ifndef METARESTORE
@@ -208,7 +223,17 @@ class FilesystemNodeOperationsBase : public IFilesystemNodeOperations {
208223
inode_t *permissionDeniedINodesOut) override;
209224

210225
// Access control operations
211-
int access(const FsContext &context, FSNode *node, uint8_t modeMask) override;
226+
int access(const FsContext &context, const FilesystemOperationContext &fsOpContext,
227+
FSNode *node, uint8_t modeMask) override;
228+
229+
protected:
230+
/// Returns the stored RichACL for @p node, or nullptr if none is present.
231+
/// @p scratch is a caller-supplied buffer that the returned pointer may alias;
232+
/// implementations that need to materialise a temporary ACL (e.g. KV backends)
233+
/// fill @p scratch and return @p &scratch, while the default in-memory
234+
/// implementation ignores @p scratch and returns a pointer into aclStorage.
235+
virtual const RichACL *getAclForAccess(const FilesystemOperationContext &fsOpContext,
236+
FSNode *node, RichACL &scratch);
212237
int stickyAccess(FSNode *parent, FSNode *node, uint32_t uid) override;
213238
int nameCheck(const std::string &name) override;
214239
uint8_t verifySession(const FsContext &context, OperationMode operationMode,

src/master/filesystem_node_operations_interface.h

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -466,13 +466,78 @@ class IFilesystemNodeOperations {
466466
virtual std::string escapeName(const std::string &name) = 0;
467467

468468
// ACL operations
469-
virtual uint8_t setAcl(FSNode *node, const RichACL &acl, uint32_t timeStamp) = 0;
470-
virtual uint8_t setAcl(FSNode *node, AclType type, const AccessControlList &acl,
471-
uint32_t timeStamp) = 0;
469+
470+
/// Stores a RichACL on a node, replacing any previously stored ACL.
471+
///
472+
/// If the ACL is equivalent to the node's POSIX mode bits (RichACL::equivMode returns true),
473+
/// the stored ACL is erased and only the mode bits are updated. Otherwise the ACL is written
474+
/// to storage after resolving the auto-set-mode flag: if kAutoSetMode is set, the flag is
475+
/// cleared and the ACL's mode is derived from the node's current mode bits before storing.
476+
/// In both cases node->mode is updated to reflect the new permission bits, and the node's
477+
/// ctime and checksum are updated.
478+
///
479+
/// @param fsOpContext The filesystem operation context (transaction).
480+
/// @param node The node on which to set the ACL.
481+
/// @param acl The RichACL to store.
482+
/// @param timeStamp The timestamp used to update the node's ctime.
483+
/// @return SAUNAFS_STATUS_OK on success, SAUNAFS_ERROR_ENOTSUP if the ACL's inherit flags
484+
/// are invalid for the node type.
485+
virtual uint8_t setAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
486+
const RichACL &acl, uint32_t timeStamp) = 0;
487+
488+
/// Merges a POSIX ACL into the node's stored RichACL.
489+
///
490+
/// Reads the current RichACL (if any), calls createExplicitInheritance() and
491+
/// removeInheritOnly() to normalise inheritance, then appends the POSIX ACL entries:
492+
/// - kDefault: calls appendDefaultPosixACL() and refreshes the mode bits from the node.
493+
/// - kAccess: calls appendPosixACL() and updates node->mode from the resulting RichACL.
494+
/// The merged ACL is always written back to storage. node->ctime and checksum are updated.
495+
///
496+
/// @param fsOpContext The filesystem operation context (transaction).
497+
/// @param node The node on which to set the ACL.
498+
/// @param type Must be AclType::kAccess or AclType::kDefault.
499+
/// @param acl The POSIX ACL entries to merge in.
500+
/// @param timeStamp The timestamp used to update the node's ctime.
501+
/// @return SAUNAFS_STATUS_OK on success.
502+
/// SAUNAFS_ERROR_EINVAL if type is neither kAccess nor kDefault.
503+
/// SAUNAFS_ERROR_ENOTSUP if type is kDefault and node is not a directory.
504+
virtual uint8_t setAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
505+
AclType type, const AccessControlList &acl, uint32_t timeStamp) = 0;
506+
472507
#ifndef METARESTORE
473-
virtual uint8_t getAcl(FSNode *node, RichACL &acl) = 0;
508+
/// Retrieves the stored RichACL for a node.
509+
///
510+
/// Looks up the ACL in storage. If no ACL is stored for the node (i.e. permissions are
511+
/// fully described by the mode bits alone), returns SAUNAFS_ERROR_ENOATTR. Otherwise
512+
/// copies the stored ACL into @p acl and asserts that node->mode matches the ACL's mode.
513+
///
514+
/// @param fsOpContext The filesystem operation context (transaction).
515+
/// @param node The node whose ACL to retrieve.
516+
/// @param[out] acl Receives the stored RichACL.
517+
/// @return SAUNAFS_STATUS_OK on success, SAUNAFS_ERROR_ENOATTR if no ACL is stored.
518+
virtual uint8_t getAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
519+
RichACL &acl) = 0;
474520
#endif // METARESTORE
475-
virtual uint8_t deleteAcl(FSNode *node, AclType type, uint32_t timeStamp) = 0;
521+
522+
/// Removes or prunes the ACL stored on a node according to the ACL type.
523+
///
524+
/// - kRichACL: removes the entire stored ACL unconditionally.
525+
/// - kDefault: only valid on directories; reads the current ACL, calls
526+
/// createExplicitInheritance() and removeInheritOnly(true) to strip default
527+
/// (inherit-only) entries, then erases the ACL if empty or writes back the pruned ACL.
528+
/// - kAccess: reads the current ACL, calls createExplicitInheritance() and
529+
/// removeInheritOnly(false) to strip access entries, then erases or writes back.
530+
/// In all cases node->ctime and checksum are updated.
531+
///
532+
/// @param fsOpContext The filesystem operation context (transaction).
533+
/// @param node The node whose ACL to delete or prune.
534+
/// @param type The ACL type to remove (kRichACL, kDefault, or kAccess).
535+
/// @param timeStamp The timestamp used to update the node's ctime.
536+
/// @return SAUNAFS_STATUS_OK on success.
537+
/// SAUNAFS_ERROR_ENOTSUP if type is kDefault and node is not a directory.
538+
/// SAUNAFS_ERROR_EINVAL if type is not kRichACL, kDefault, or kAccess.
539+
virtual uint8_t deleteAcl(const FilesystemOperationContext &fsOpContext, FSNode *node,
540+
AclType type, uint32_t timeStamp) = 0;
476541

477542
// Recursive operations
478543
#ifndef METARESTORE
@@ -501,7 +566,8 @@ class IFilesystemNodeOperations {
501566
inode_t *permissionDeniedINodesOut) = 0;
502567

503568
// Access control operations
504-
virtual int access(const FsContext &context, FSNode *node, uint8_t modeMask) = 0;
569+
virtual int access(const FsContext &context, const FilesystemOperationContext &fsOpContext,
570+
FSNode *node, uint8_t modeMask) = 0;
505571
virtual int stickyAccess(FSNode *parent, FSNode *node, uint32_t uid) = 0;
506572
virtual int nameCheck(const std::string &name) = 0;
507573
virtual uint8_t verifySession(const FsContext &context, OperationMode operationMode,

0 commit comments

Comments
 (0)