Does `ln -sf` overwrite existing files which are only symbolic links

9,007

Solution 1

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.

Solution 2

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.

Share:
9,007

Related videos on Youtube

Tim
Author by

Tim

Elitists are oppressive, anti-intellectual, ultra-conservative, and cancerous to the society, environment, and humanity. Please help make Stack Exchange a better place. Expose elite supremacy, elitist brutality, and moderation injustice to https://stackoverflow.com/contact (complicit community managers), in comments, to meta, outside Stack Exchange, and by legal actions. Push back and don't let them normalize their behaviors. Changes always happen from the bottom up. Thank you very much! Just a curious self learner. Almost always upvote replies. Thanks for enlightenment! Meanwhile, Corruption and abuses have been rampantly coming from elitists. Supportive comments have been removed and attacks are kept to control the direction of discourse. Outright vicious comments have been removed only to conceal atrocities. Systematic discrimination has been made into policies. Countless users have been harassed, persecuted, and suffocated. Q&A sites are for everyone to learn and grow, not for elitists to indulge abusive oppression, and cover up for each other. https://softwareengineering.stackexchange.com/posts/419086/revisions https://math.meta.stackexchange.com/q/32539/ (https://i.stack.imgur.com/4knYh.png) and https://math.meta.stackexchange.com/q/32548/ (https://i.stack.imgur.com/9gaZ2.png) https://meta.stackexchange.com/posts/353417/timeline (The moderators defended continuous harassment comments showing no reading and understanding of my post) https://cs.stackexchange.com/posts/125651/timeline (a PLT academic had trouble with the books I am reading and disparaged my self learning posts, and a moderator with long abusive history added more insults.) https://stackoverflow.com/posts/61679659/revisions (homework libels) Much more that have happened.

Updated on September 18, 2022

Comments

  • Tim
    Tim almost 2 years

    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?

    • Admin
      Admin almost 8 years
      Do you think it would be a wise idea to have a simple flag effectively turn ln into rm -rf ?
    • Admin
      Admin almost 8 years
      what do you mean by 'have a simple flag turn ln into rm -rf '?
    • Admin
      Admin almost 8 years
      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.
    • Admin
      Admin almost 8 years
      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.
    • Admin
      Admin almost 8 years
      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.
  • Tim
    Tim almost 8 years
    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?
  • Ludwig Schulze
    Ludwig Schulze almost 8 years
    @Tim the source doesn't seems to have other checks.