5

From the coreutils ln manual:

Normally ln does not remove existing files. Use the --force (-f) option to remove them
unconditionally, the --interactive (-i) option to remove them conditionally,
and the --backup (-b) option to rename them.

$ mkdir output

I can understand this failing:

$ ln -sT /etc/passwd output
ln: failed to create symbolic link ‘output’: File exists

But why does adding -f also fail:

$ ln -sfT /etc/passwd output
ln: ‘output’: cannot overwrite directory

Does -f overwrite existing files which are only symbolic links, but not files of other types (directories, regular files, ...)?

Can -T be used when the last argument, (i.e. the target file argument), is an existing directory, with the intention to overwrite the directory into a link?

  • 2
    Do you think it would be a wise idea to have a simple flag effectively turn ln into rm -rf ? – PSkocik Jul 3 '16 at 1:35
  • what do you mean by 'have a simple flag turn ln into rm -rf '? – Tim Jul 3 '16 at 1:36
  • ln -fT would have to remove the target directory forcibly and recursively, to the effect of running rm -rf on it. That's a very dangerous operation, better left to rm, away from commands like ln which tend to create more than they destroy. – PSkocik Jul 3 '16 at 1:41
  • It is even more interesting if you leave -T out but ask for a relative link. e.g. mkdir output; ln -sf this/that output. It results in a (broken) link that -> this/that. – grochmal Jul 3 '16 at 1:43
  • You should probably fix the title to more accurately describe the question. In the body you don't ask about either overwriting files or existing symbolic links, it asks about directories which aren't in the title at all. – MAP Jul 11 '16 at 7:51
9

It can remove files, but directories are not "files".

➜  lab touch file        
➜  lab mkdir dir
➜  lab ln -sfT /home file
➜  lab ln -sfT /home dir 
ln: dir: cannot overwrite directory

This is seen in the source:

  if (remove_existing_files || interactive || backup_type != no_backups)
    {
      dest_lstat_ok = (lstat (dest, &dest_stats) == 0);
      if (!dest_lstat_ok && errno != ENOENT)
        {
          error (0, errno, _("failed to access %s"), quoteaf (dest));
          return false;
        }
    }
[...]
  if (dest_lstat_ok)
    {
      if (S_ISDIR (dest_stats.st_mode))
        {
          error (0, 0, _("%s: cannot overwrite directory"), quotef (dest));
          return false;
        }
      if (interactive)
        {
          fprintf (stderr, _("%s: replace %s? "), program_name, quoteaf (dest));
          if (!yesno ())
            return true;
          remove_existing_files = true;
        }

dest_lstat_ok boolean which starts as false becomes true, the first if statement is called since remove_existing_files is true due the --force flag, which in turn allows the second if statement to be checked. It refuses to remove directories because it's expecting a file.

If you don't set the -T which makes ln to not treat the directory as not a directory, ln would just created a symlink under the directory with the basename of the source.

  • thanks. so it doesn't work when the existing file is a directory. does it work when the existing file is a file which is not a directory but other file types? – Tim Jul 5 '16 at 10:45
  • @Tim the source doesn't seems to have other checks. – Braiam Jul 5 '16 at 18:13
8

In UNIX, directories are special (I feel myself channeling The Church Lady from SNL). Directories contain other files, so deleting them requires a different operation. Even when a directory is empty it still has two files in it (. and ..) so deleting a directory can't be done until it's really empty and the link counts on the relevant files have been updated.

In the early days of UNIX (my first experience is with 6th edition from Bell Labs) there were two different commands (rm and rmdir) for regular files and directories, which reflected the underlying fact that they were two different system calls. rm was simple, it just removed the entry whose name you gave it from the directory, and decremented the ref count on the file it pointed to (of course, the file is not actually deleted unless the ref count goes to 0). rmdir required much more (not actually in the app, but in the system call), it had to go into the directory and find the . and .. entries, go to those inodes and decrement the ref count, and then remove the entry in the parent and decrement that ref count (the same one it just decremented for . in the directory itself, which should then be 0). All of that straddled several different disk sectors, and so had to be carefully tuned to make the possibility of an abort (i.e. system crash) at any point be recoverable by fsck.

Of course, in more modern UNIX systems the constraints of the hardware (i.e. 64Kbyte max program size, that is a "K") have been eased and you can now do rm -r and a lot of that underlying special nature of directories is less apparent, but it's still there. I remember needing to delete a large tree on the 6th edition machine which involved going into each directory, deleting all the files, going back to the parent and doing rmdir and essentially doing all the recursion down all the directory trees manually. We did think about a script to help with this, but it came up so seldom in those days and it was sufficiently dangerous that we decided that requiring someone to go to all that effort would help prevent catastrophic mistakes.

The first time you get a question that says "I typed 'sudo rm -rf /' instead of ./ how do I recover?" you may understand why we were being cautious.

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.