Skip to content

Commit cb80d90

Browse files
torvaldsbrauner
authored andcommitted
fs: optimize acl_permission_check()
generic_permission() turned out to be costlier than expected. The reason is that acl_permission_check() performs owner checks that involve costly pointer dereferences. There's already code that skips expensive group checks if the group and other permission bits are the same wrt to the mask that is checked. This logic can be extended to also shortcut permission checking in acl_permission_check(). If the inode doesn't have POSIX ACLs than ownership doesn't matter. If there are no missing UGO permissions the permission check can be shortcut. Acked-by: Al Viro <[email protected]> Link: https://lore.kernel.org/all/CAHk-=wgXEoAOFRkDg+grxs+p1U+QjWXLixRGmYEfd=vG+OBuFw@mail.gmail.com Signed-off-by: Linus Torvalds <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent e017671 commit cb80d90

File tree

1 file changed

+41
-0
lines changed

1 file changed

+41
-0
lines changed

fs/namei.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,25 @@ static int check_acl(struct mnt_idmap *idmap,
326326
return -EAGAIN;
327327
}
328328

329+
/*
330+
* Very quick optimistic "we know we have no ACL's" check.
331+
*
332+
* Note that this is purely for ACL_TYPE_ACCESS, and purely
333+
* for the "we have cached that there are no ACLs" case.
334+
*
335+
* If this returns true, we know there are no ACLs. But if
336+
* it returns false, we might still not have ACLs (it could
337+
* be the is_uncached_acl() case).
338+
*/
339+
static inline bool no_acl_inode(struct inode *inode)
340+
{
341+
#ifdef CONFIG_FS_POSIX_ACL
342+
return likely(!READ_ONCE(inode->i_acl));
343+
#else
344+
return true;
345+
#endif
346+
}
347+
329348
/**
330349
* acl_permission_check - perform basic UNIX permission checking
331350
* @idmap: idmap of the mount the inode was found from
@@ -348,6 +367,28 @@ static int acl_permission_check(struct mnt_idmap *idmap,
348367
unsigned int mode = inode->i_mode;
349368
vfsuid_t vfsuid;
350369

370+
/*
371+
* Common cheap case: everybody has the requested
372+
* rights, and there are no ACLs to check. No need
373+
* to do any owner/group checks in that case.
374+
*
375+
* - 'mask&7' is the requested permission bit set
376+
* - multiplying by 0111 spreads them out to all of ugo
377+
* - '& ~mode' looks for missing inode permission bits
378+
* - the '!' is for "no missing permissions"
379+
*
380+
* After that, we just need to check that there are no
381+
* ACL's on the inode - do the 'IS_POSIXACL()' check last
382+
* because it will dereference the ->i_sb pointer and we
383+
* want to avoid that if at all possible.
384+
*/
385+
if (!((mask & 7) * 0111 & ~mode)) {
386+
if (no_acl_inode(inode))
387+
return 0;
388+
if (!IS_POSIXACL(inode))
389+
return 0;
390+
}
391+
351392
/* Are we the owner? If so, ACL's don't matter */
352393
vfsuid = i_uid_into_vfsuid(idmap, inode);
353394
if (likely(vfsuid_eq_kuid(vfsuid, current_fsuid()))) {

0 commit comments

Comments
 (0)