Why permission denied upon symbolic link update to new target with permissions OK?

5,607

It appears as if the GNU ln implementation on Linux uses the stat() function to determine whether the target exists or not. This function is required to resolve symbolic links, so when the target of the pre-existing link is not accessible, the function returns EACCESS ("permission denied") and the utility fails. This has been verified with strace to be true on a Ubuntu Linux system.

To make the GNU ln use lstat() instead, which does not resolve symbolic links, you should call it with its (non-standard) -n option (GNU additionally uses --no-dereference as an alias for -n).

ln -s -n -f ../../../raw_data/CHIP_TEST/BM50.2.fastq 50ATC_Rep2.fastq

Reading the POSIX specification for ln, I can't really make out whether GNU ln does this for some undefined or unspecified behaviour in the specification or not, but it is possible that it uses the fact that...

If the destination path exists and was created by a previous step, it is unspecified whether ln shall write a diagnostic message to standard error, do nothing more with the current source_file, and go on to any remaining source_files; or will continue processing the current source_file.

The "unspecified" bit here may give GNU ln the license to behave as it does, at least if we allow ourselves to interpret "a previous step" as "the destination path is a symbolic link".

The GNU documentation for the -n option is mostly concerned about the case when the target is a symbolic link to a directory:

'-n'
'--no-dereference'
     Do not treat the last operand specially when it is a symbolic link
     to a directory.  Instead, treat it as if it were a normal file.

     When the destination is an actual directory (not a symlink to one),
     there is no ambiguity.  The link is created in that directory.  But
     when the specified destination is a symlink to a directory, there
     are two ways to treat the user's request.  'ln' can treat the
     destination just as it would a normal directory and create the link
     in it.  On the other hand, the destination can be viewed as a
     non-directory--as the symlink itself.  In that case, 'ln' must
     delete or backup that symlink before creating the new link.  The
     default is to treat a destination that is a symlink to a directory
     just like a directory.

     This option is weaker than the '--no-target-directory' ('-T')
     option, so it has no effect if both options are given.

The default behaviour of GNU ln when the target is a symbolic link to a directory, is to put the new symbolic link inside that directory (i.e., it dereferences the link to the directory). When the target of the pre-existing link is not accessible, it chooses to emit a diagnostic message and fail (allowed by the standard text).

OpenBSD ln (and presumably ln on other BSD systems), on the other hand, will behave like GNU ln when the target is a symbolic link to an accessible directory, but will unlink and recreate the symbolic link as requested if the target of the pre-existing link is not accessible. I.e., it chooses to continue with the operation (allowed by the standard text).

Also, GNU ln on OpenBSD behaves like OpenBSD's native ln, which is mildly interesting.

Removing the pre-existing symbolic link with rm is not an issue whatsoever, as you appear to have write and executable permissions for the directory it's located in.

Share:
5,607
bli
Author by

bli

Updated on September 18, 2022

Comments

  • bli
    bli over 1 year

    On a Linux machine (a computing cluster, actually), I copied a folder from another user (who granted me permissions to do so using the appropriate chmod).

    This folder contains symbolic links to files I cannot access. I want to update them so that they point to copies of the same files, that I own.

    However, when I try to do so using ln -sf, I get Permission denied.

    Why is that so?

    That's the link:

    $ ls -l 50ATC_Rep2.fastq
    lrwxrwxrwx 1 bli cifs-BioIT 55 21 nov.  13:45 50ATC_Rep2.fastq -> /pasteur/homes/mmazzuol/Raw_data/CHIP_TEST/BM50.2.fastq
    

    I don't have permission to access its target, but I have a copy of it. That's the new target I want:

    $ ls -l ../../../raw_data/CHIP_TEST/BM50.2.fastq
    -rwxr-xr-x 1 bli cifs-BioIT 4872660831 21 nov.  14:00 ../../../raw_data/CHIP_TEST/BM50.2.fastq
    

    And that's what happens when I try ln -sf:

    $ ln -sf ../../../raw_data/CHIP_TEST/BM50.2.fastq 50ATC_Rep2.fastq
    ln: accessing `50ATC_Rep2.fastq': Permission denied
    

    It seems that the permissions of the current target is what counts, not the permissions on the link itself.

    I can circumvent the problem by first deleting the link, then re-creating it:

    $ rm 50ATC_Rep2.fastq
    rm: remove symbolic link `50ATC_Rep2.fastq'? y
    $ ln -s ../../../raw_data/CHIP_TEST/BM50.2.fastq 50ATC_Rep2.fastq
    $ ls -l 50ATC_Rep2.fastq
    lrwxrwxrwx 1 bli cifs-BioIT 40 21 nov.  18:57 50ATC_Rep2.fastq -> ../../../raw_data/CHIP_TEST/BM50.2.fastq
    

    Why can I delete the link, but not update it?

    • NickD
      NickD over 4 years
      Who owns the directory containing the links?
  • Winampah
    Winampah over 2 years
    OK that's interesting. Now, here's what I am experiencing. I have to go back and forth between distros to test hardware compatibility. I was on MX (Debian based) yesterday and now I am on Xubuntu. It seems like my symbolic links that were created on MX distro are not being recognized properly by Xubuntu distro. (despite me using the same username and same group name, user id is also the same: it is 1000) Despite using chown -R and chmod -R 777 (temporarily for tests) the application I need to run does not see the links in its directory. But the interesting part is: on Debian it does.
  • Winampah
    Winampah over 2 years
    Also, Thunar (file explorer) shows the links just fine. But the application itself can't see them (the symbolic links appear but they say they have 0 KB in size) Thunar shows the file size correctly.
  • Kusalananda
    Kusalananda over 2 years
    @Winampah I think your issue is probably unrelated to the issue in this question. You may well want to ask a separate question about your issue.