Create a Hard Link from a File Handle on Unix

Create a hard link from a file handle on Unix?

Not generally, no. [Edit: since Linux 3.11 there is now linkat; see safsaf32's answer. This does not work on POSIX systems in general since POSIX linkat is restricted to directories only.] There are security considerations here: someone can pass to you an open file descriptor that you could not normally open on your own, e.g.:

mkdir lock; chmod 700 lock
echo secret contents > lock/in
sudoish cmd < lock/in

Here cmd runs as a user who has no permission to open the input file (lock/in) by name, but can still read from it. If cmd could create a new name on the same file system, it could pass the file contents on to a later process. (Obviously it can copy those contents, so this issue is more of a "pass the contents on by mistake" thing than "pass the contents on, on purpose".)

That said, people have come up with ways of "relinking" files by inode/vnode internally (it's pretty easy to do inside most file systems), so you could make your own private system call for it. The descriptor must refer to a real file on the appropriate mount point, of course—there's no way of "relinking" a pipe or socket or device into becoming a regular file.

Otherwise you're stuck with "catch signals and clean up and hope for the best", or a similar trick, "fork off a subprocess, run it, and if it succeeds/fails, take appropriate move/clean-up action".


Edit to add historical note: the above lock example is not particularly good, but back in the days of V6 Unix, MDQS used a fancier version of this trick. Bits and pieces of MDQS survive in various forms today.

Hard link and Symbolic links in Unix

Yes, and no :-)

In UNIX, the contents of a file are distinct from the directory entries for that file. You can have multiple directory entries point to the same contents (look up inode for a description of how this works) and, here's the tricky bit:

All those directory entries are equal. Even though one may have been created first, there's nothing special about it. If you remove it, the contents don't disappear, just the directory entry. The contents will disappear once the inode has zero directory entries pointing to it (and all processes close the file - I've been bitten before by trying to clear up disk space deleting log files only to find that, because a process still has the file open, the file contents aren't recovered even though no directory entries point to them).

That's for hard links.

Soft links are a bit trickier. They do create a "file" of sorts (a separate inode), containing the path to the target file. And those links are not equal. Deleting the original will leave you with a soft link pointing nowhere.

Because inodes are unique on a given filesystem, hard links cannot refer to data on a different filesystem.

Soft links do not have that limitation since they store the path to the target file, not its inode.

The following transcript may help:

$ echo hello >f1
$ ln f1 f2
$ ln -s f1 f3
$ ls -ial f*
7385 -rw-r--r-- 2 pax None 6 May 11 14:09 f1
7385 -rw-r--r-- 2 pax None 6 May 11 14:09 f2
4672 lrwxrwxrwx 1 pax None 6 May 11 14:09 f3 -> f1
$ cat f1
hello
$ cat f2
hello
$ cat f3
hello
$ rm f1
$ ls -ial f*
7385 -rw-r--r-- 2 pax None 6 May 11 14:09 f2
4672 lrwxrwxrwx 1 pax None 6 May 11 14:09 f3 -> f1
$ cat f1
cat: f1: No such file or directory
$ cat f2
hello
$ cat f3
cat: f3: No such file or directory

I've used only the last four digits of the inode number to keep the entry short (and not hit you with inode numbers like 43910096366994672) but you can see that f1 and f2 have the exact same inode whereas f3 is different. You can also see that the contents of the file created originally as f1 survive its deletion because f2 is still referencing it.

However, because f3 is referencing the f1 name rather than its inode, you get an error trying to use it.


Aside: You've gotta love it when UNIX toys with you like this:

$ ls f*
f2 f3
$ cat f3 # What the ...?
cat: f3: No such file or directory

Almost as much fun as creating a file called spacebackspacex and then watching somebody try to delete it :-)

Can we create a hard link to a symbolic link in UNIX?

It works on Linux. We use this regularly in some mission-critical code. As Jonathan notes, this is not guaranteed by the Posix standard, so you should double-check that it works for your system.

$ date >a
$ ln -s a b
$ ln b c
$ ls -li
total 4
396074 -rw-rw-r-- 1 ec2-user ec2-user 29 Oct 27 07:15 a
396345 lrwxrwxrwx 2 ec2-user ec2-user 1 Oct 27 07:16 b -> a
396345 lrwxrwxrwx 2 ec2-user ec2-user 1 Oct 27 07:16 c -> a

You can use the --physical flag to enforce this.

   -P, --physical
make hard links directly to symbolic links

How to create hard link in Linux from a C program

As per the test input shown by you

$ ./a.out     ln      file1     file2
^ ^ ^ ^
| | | |
argv[0] ..[1] ..[2] ..[3]

in your code

        myargs[1] = argv[3];
myargs[2] = argv[4];

should read

        myargs[1] = argv[2];
myargs[2] = argv[3];

That said, it is always better and advisable to use the argv[n] after checking argc against n+1.

Regarding Hard Link

Back in the days of 7th Edition (or Version 7) UNIX, there were no system calls mkdir(2) and rmdir(2). The mkdir(1) program was SUID root, and used the mknod(2) system call to create the directory and the link(2) system call to make the entries for . and .. in the new directory. The link(2) system call only allowed root to do that. Consequently, way back then (circa 1978), it was possible for the superuser to create links to directories, but only the superuser was permitted to do so to ensure that there were no problems with cycles or other missing links. There were diagnostic programs to pick up the pieces if the system crashed while a directory was partly created, for example.


You can find the Unix 7th Edition manuals at Bell Labs. Sections 2 and 3 are devoid of mkdir(2) and rmdir(2). You used the mknod(2) system call to make the directory:

NAME


mknod – make a directory or a special file

SYNOPSIS

mknod(name, mode, addr)
char *name;

DESCRIPTION


Mknod creates a new file whose name is the null-terminated string pointed to by name. The mode of
the new file (including directory and special file bits) is initialized from mode. (The protection part of
the mode is modified by the process’s mode mask; see umask(2)). The first block pointer of the i-node
is initialized from addr. For ordinary files and directories addr is normally zero. In the case of a special
file, addr specifies which special file.

Mknod may be invoked only by the super-user.

SEE ALSO


mkdir(1), mknod(1), filsys(5)

DIAGNOSTICS


Zero is returned if the file has been made; – 1 if the file already exists or if the user is not the superuser.

The entry for link(2) states:

DIAGNOSTICS


Zero is returned when a link is made; – 1 is returned when name1 cannot be found; when name2 already
exists; when the directory of name2 cannot be written; when an attempt is made to link to a directory by
a user other than the super-user; when an attempt is made to link to a file on another file system; when a
file has too many links.

The entry for unlink(2) states:

DIAGNOSTICS


Zero is normally returned; – 1 indicates that the file does not exist, that its directory cannot be written,
or that the file contains pure procedure text that is currently in use. Write permission is not required on
the file itself. It is also illegal to unlink a directory (except for the super-user).

The manual page for the ln(1) command noted:

It is forbidden to link to a directory or to link across file systems.

The manual page for the mkdir(1) command notes:

Standard entries, '.', for the directory itself, and '..'
for its parent, are made automatically.

This would not be worthy of comment were it not that it was possible to create directories without those links.


Nowadays, the mkdir(2) and rmdir(2) system calls are standard and permit any user to create and remove directories, preserving the correct semantics. There is no longer a need to permit users to create hard links to directories. This is doubly true since symbolic links were introduced - they were not in 7th Edition UNIX, but were in the BSD versions of UNIX from quite early on.


With normal directories, the .. entry unambiguously links back to the (single, solitary) parent directory. If you have two hard links (two names) for the same directory in different directories, where does the .. entry point? Presumably, to the original parent directory - and presumably there is no way to get to the 'other' parent directory from the linked directory. That's an asymmetry that can cause trouble. Normally, if you do:

chdir("./subdir");
chdir("..");

(where ./subdir is not a symbolic link), then you will be back in the directory you started from. If ./subdir is a hard link to a directory somewhere else, then you will be in a different directory from where you started after the second chdir(). You'd have to show that with a pair of stat() calls before and after the chdir() operations shown.

How would you create a hard link (Linux) from within an application without passing arguments to the ln utility directly?

I assuming you mean, how do I create a hard link without shelling out the the ln command.

Short answer,

using Mono.Unix.Native;
Mono.Unix.Native.Syscall.link(string oldname, string newname).

Ultimately you do this by invoking the link() function

Looking at the man page: link() creates a new link (also known as a hard link) to an existing file. The linkat() function is also documented which might be a better choice for you.

int link(const char *oldpath, const char *newpath);

Clearly, not a C# friendly system call (they rarely are). So, as far as calling it from C# is concerned, you can either go through creating the necessary steps to call it from C# (translating the .h constants, etc.) -- or you can just call link() directly from a C/C++ interface module in your project, and then call that interface from your C# code. Since Unix system calls are not C++ based, you don't run into name mangling problems.

To call external functions, you use pinvoke technology (also included in Microsofts .Net framework). A bit long too replicate everything in that reference, but the key features are pretty simple.

Link to the shared library using DllImport, and declare the external functions you need. E.g.

[DllImport ("libc.so")]
public static int link(string oldpath, string newpath)

You might also find an os interface module written in C# that translates system call errors into more natural C# exceptions is a better choice in the long term. To be honest, linking the .so file and invoking the Linux system calls is not that hard and you might conclude you prefer this approach over a separate C/C++ module.

If you ever have to migrate your mono code to a windows platform, using an os interface module will simplify your life. BTW, you can also create hard links on Windows on NTFS file systems, but few Windows people use them, much less expect them.

And finally, the real answer: The Mono.Unix NameSpace is a nice choice for most system calls, and link() is not an exception. If you look in the SysCall class of Mono.Unix.Native you should find the link() method.

using Mono.Unix.Native;
SysCall.Link(ExistingName, NewLinkName);

Unfortunately, the Mono docs do not use a RESTful interface, so I can't give you are direct link the correct page. Start at and then drill down using the tree interface

I thought a teach you to fish answer was more appropriate in this case. You could download the mono native source and see exactly how mono does it.

Noticed the comment by Diego - His comment answer was include within my longer answer.

What is the difference between a symbolic link and a hard link?

Underneath the file system, files are represented by inodes. (Or is it multiple inodes? Not sure.)

A file in the file system is basically a link to an inode.

A hard link, then, just creates another file with a link to the same underlying inode.

When you delete a file, it removes one link to the underlying inode. The inode is only deleted (or deletable/over-writable) when all links to the inode have been deleted.

A symbolic link is a link to another name in the file system.

Once a hard link has been made the link is to the inode. Deleting, renaming, or moving the original file will not affect the hard link as it links to the underlying inode. Any changes to the data on the inode is reflected in all files that refer to that inode.

Note: Hard links are only valid within the same File System. Symbolic links can span file systems as they are simply the name of another file.

The links to a file

Because there is nothing special about the first hard link (soft links are different, they're really just special files containing the textual target) to a file, it's just a directory entry that refers to the inode behind it.

This is a consequence of the decision to separate data (inode) from references to the data (directory entry).

So don't think of the file being one directory entry, with other files linked to it. Think instead of the data in the file being the thing, with as many directory entries as you need referring to it. When you hard-link directory entry a to an existing b, you're really linking it to the inode behind b, and they are equals in every sense of the word. The data is only ever destroyed when the final hard link to it is disconnected.



Related Topics



Leave a reply



Submit