Example script:

#!/bin/sh -e 
sudo useradd -m user_a
sudo useradd -m user_b -g user_a
sudo chmod g+w /home/user_a

set +e
sudo su user_a <<EOF
umask 027
>> file_a 
>> file_b
>> file_c
ls -l file_*

sudo su user_b <<EOF
umask 000
rm -f file_*
ls -l ~user_a/
set -x
mv ~user_a/file_a .
cp ~user_a/file_b .
ln ~user_a/file_c .
set +x
ls -l ~/
sudo userdel  -r user_b
sudo userdel  -r user_a


-rw-r----- 1 user_a user_a 0 Jul 11 12:26 file_a
-rw-r----- 1 user_a user_a 0 Jul 11 12:26 file_b
-rw-r----- 1 user_a user_a 0 Jul 11 12:26 file_c
total 0
-rw-r----- 1 user_a user_a 0 Jul 11 12:26 file_a
-rw-r----- 1 user_a user_a 0 Jul 11 12:26 file_b
-rw-r----- 1 user_a user_a 0 Jul 11 12:26 file_c
+ mv /home/user_a/file_a .
+ cp /home/user_a/file_b .
+ ln /home/user_a/file_c .
ln: failed to create hard link ‘./file_c’ => ‘/home/user_a/file_c’: Operation not permitted
+ set +x
total 0
-rw-r----- 1 user_a user_a 0 Jul 11 12:26 file_a
-rw-r----- 1 user_b user_a 0 Jul 11 12:26 file_b
userdel: user_b mail spool (/var/mail/user_b) not found
userdel: user_a mail spool (/var/mail/user_a) not found
  • Is this specific to some specific system or in general? – ilkkachu Jul 11 '17 at 14:08

Which system are you running? On Linux, that behaviour is configurable, through /proc/sys/fs/protected_hardlinks (or sysctl fs.protected_hardlinks).

The behaviour is described in proc(5):

/proc/sys/fs/protected_hardlinks (since Linux 3.6)
When the value in this file is 0, no restrictions are placed on the creation of hard links (i.e., this is the historical behavior before Linux 3.6). When the value in this file is 1, a hard link can be created to a target file only if one of the following conditions is true:

  • The calling process has the CAP_FOWNER capability ...
  • The filesystem UID of the process creating the link matches the owner (UID) of the target file ...
  • All of the following conditions are true:
    • the target is a regular file;
    • the target file does not have its set-user-ID mode bit enabled;
    • the target file does not have both its set-group-ID and group-executable mode bits enabled; and
    • the caller has permission to read and write the target file (either via the file's permissions mask or because it has suitable capabilities).

And the rationale for that should be clear:

The default value in this file is 0. Setting the value to 1 prevents a longstanding class of security issues caused by hard-link-based time-of-check, time-of-use races, most commonly seen in world-writable directories such as /tmp.

On Debian systems protected_hardlinks and the similar protected_symlinks default to one, so making a link without write access to the file doesn't work:

$ ls -ld . ./foo
drwxrwxr-x 2 root itvirta 4096 Jul 11 16:43 ./
-rw-r--r-- 1 root root       4 Jul 11 16:43 ./foo
$ mv foo bar
$ ln bar bar2
ln: failed to create hard link 'bar2' => 'bar': Operation not permitted

Setting protected_hardlinks to zero lifts the restriction:

# echo 0 >  /proc/sys/fs/protected_hardlinks 
$ ln bar bar2
$ ls -l bar bar2
-rw-r--r-- 2 root root 4 Jul 11 16:43 bar
-rw-r--r-- 2 root root 4 Jul 11 16:43 bar2
  • Thanks for the answer. lwn.net/Articles/503671 says "The solution is to permit hardlinks to only be created when the user is already the existing file's owner, or if they already have read/write access to the existing file." Doesn't have having read permissions satisfy that requirement. What are the security issues of creating a link to a file I have read permissions to? – PSkocik Jul 14 '17 at 14:08
  • @PSKocik: If I can create hard links in /tmp of files that I do not own, then I can hard link any publicly readable file owned by root into /tmp, and give that file the name of my choice. Perhaps this could be exploited somehow? [Aside: Thinking abstractly, perhaps the concept of filesystem ownership should include the concept of linking rights?] – mpb Mar 25 '18 at 20:04

Normally, every file has a directory entry (there are exceptions, but no need to go into that).

The directory entry is essentially a name+number pair; the name of the file, and a number called the i-number. This is an index into something called the i-list, which contains all the real details about each file, in structures called i-nodes.

The i-node contains, among other things, details of who owns the file. It is effectively 'owned' by the owner of the file. One of the fields in it is a use count, which is normally set to 1.

When a hard link is made, it's just another name+number pair, indistinguishable from the first. At that point the use count is increased by 1; when the hard link (or the original directory entry, because they both have equal status) is removed, the use count is decreased by 1. When it becomes zero, the file disappears. You can see from this that making a hard link would mean you having to be able to change the i-node when you weren't its owner.

This was a problem for quite a few years on UNIX, and was one of the reasons the symbolic link (or symlink, sometimes called a soft link) was introduced. It is a name+name pair, basically just providing an alias for the original file. Nothing is changed in the i-node; it's just a redirection. The downside is that the original file can be deleted without the symlink disappearing, and it ends up pointing to a non-existent file.

  • 1
    The story is more complicated than this. Yes it's true that you can't change metadata on the file if you are not the owner of the file, but traditionally this didn't apply to the link count. See ilkkachu's answer for the correct answer. – Johan Myréen Jul 11 '17 at 14:08

From man 2 link : "... both names refer to the same file (and so have the same permissions and ownership)"

Even though you have permission to read/copy/move the file, you can't create two links to the same file with different permissions or ownership, as the mode, uid and gid are stored in the inode to which you are creating links from two different directories, not in the directory entry itself.


$ touch file_a
$ touch file_b
$ ln file_a file_A
$ ls -iln
total 0
1310731 -rw-rw-r-- 2 1000 1000 0 Jul 11 08:45 file_a
1310731 -rw-rw-r-- 2 1000 1000 0 Jul 11 08:45 file_A
1320710 -rw-rw-r-- 1 1000 1000 0 Jul 11 08:45 file_b

======= ===================================== ======
^       ^                                     ^
|       |                                     \-- names, stored in directory
|       \-- file metadata, stored in inode
\-- inode number, stored in directory and pointing to inode

In this example, file_a and file_A have the same inode number, so necessarily have all the same attributes stored in an inode.

  • 1
    The OP was not trying to create a link with different permissions or ownership, just a link to the file. See @ilkkachu's answer for the correct explanation. – Johan Myréen Jul 11 '17 at 14:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.