may_linkat(9)
π may_linkat(9)
ok so bestie, we're in the Kernel API Manual today and this page is all about may_linkat β the little permission bouncer standing at the door of hardlink creation. Let's get into it β¨
π NAME π
may_linkat β the vibe check for creating a hardlink π
πΈ SYNOPSIS πΈ
int may_linkat (struct mnt_idmap *idmap , const struct path *link );
π¦ ARGUMENTS π¦
idmapβ the idmap of the mount that the inode was found from. yeah, it matters, keep reading πlinkβ the source you're trying to hardlink from
π DESCRIPTION π
so here's the tea: this function blocks the hardlink when all of these are true at once:
sysctl_protected_hardlinksis enabled βfsuiddoes not match the inode π ββοΈ- the hardlink source is unsafe (peep
safe_hardlink_sourceabove for that whole situation) - no
CAP_FOWNERin a namespace with the inode owner uid mapped
all four, together, at the same time. that's the combo that gets you blocked, no cap π
now, if the inode was found through an idmapped mount, you literally have to pass the idmap of the vfsmount through idmap. bestie this is not optional. the function will then take care of mapping the inode according to idmap before it does any permission checking π«Ά
on non-idmapped mounts, or if the permission checking is supposed to happen on the raw inode, just pass nop_mnt_idmap and you're golden β¨
returns 0 if successful, -ve on error. slay, simple.
ngl the whole point is: don't let randos hardlink to files they don't own. it's giving security, it's giving protected hardlinks, it's giving main character energy π‘οΈ
π₯ SEE ALSO π₯
Kernel file ./fs/namei.c
check_acl(9), acl_permission_check(9), generic_permission(9), do_inode_permission(9), sb_permission(9), inode_permission(9), path_get(9), path_put(9), path_connected(9), try_to_unlazy(9), try_to_unlazy_next(9), complete_walk(9), may_follow_link(9), safe_hardlink_source(9), may_create_in_sticky(9), lookup_fast(9), start_dirop(9), end_dirop(9), kern_path_parent(9), vfs_path_parent_lookup(9), vfs_path_lookup(9), try_lookup_noperm(9), lookup_noperm(9), lookup_one(9), lookup_one_unlocked(9), lookup_one_positive_killable(9), lookup_one_positive_unlocked(9), lookup_noperm_unlocked(9), start_creating(9), start_removing(9), start_creating_killable(9), start_removing_killable(9), start_creating_noperm(9), start_removing_noperm(9), start_creating_dentry(9), start_removing_dentry(9), __start_renaming(9), start_renaming(9), start_renaming_dentry(9), start_renaming_two_dentries(9), vfs_prepare_mode(9), vfs_create(9), atomic_open(9), vfs_lookup_open(9), vfs_tmpfile(9), kernel_tmpfile_open(9), end_creating_path(9), dentry_create(9), vfs_mknod(9), vfs_mkdir(9), vfs_rmdir(9), vfs_unlink(9), vfs_symlink(9), vfs_link(9), vfs_rename(9), vfs_readlink(9), vfs_get_link(9), page_get_link(9), page_put_link(9)
(yeah that's a lot of links. iykyk. go read them all, they're iconic π)
that's the whole page, bestie! π this beauty lives in the ./fs source tree and it's dated October 2026 β catch you in the kernel, mwah π
π¦ where this page comes from
- nixpkgs unstable: linux-manual 7.3.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms