11

Do the following paths point to the same disk location?

/home/username
/app/home/username
Mike
  • 223
  • There is no universal answer; it depends on how your system is configured. – G-Man Says 'Reinstate Monica' Jun 02 '15 at 08:24
  • @G-Man - Unix was built on its fs, and that was built on serial numbers - or inodes. (wikipedia even quotes Dennis Ritchie on its etymology). While inodes are device specific, those very same devices get their own superset of serial numbers in device numbers. Even on systems which allow you to hardlink directories - all of the links are still the same directory because they share the same inode on the same device. You should be able to easily test this on any Unix system. – mikeserv Jun 03 '15 at 03:50
  • Related: https://stackoverflow.com/questions/33980224/how-to-check-if-two-paths-are-equal-in-bash – Ciro Santilli OurBigBook.com Jun 09 '18 at 19:31

6 Answers6

6

Change directory to each one in turn and look at the output of pwd -P. With the -P flag, pwd will display the physical current working directory with all symbolic links resolved.

Kusalananda
  • 333,661
4

There are two basic ways to check whether two files are the same. The first one is to check whether their paths are the same. This has no false positives, but plenty of false negatives. An obvious case of false negatives is if symbolic links are involved. You can handle that case by resolving the symbolic links. For existing directories that you have the permission to change into, this can be done with pwd -P.

absolute_path1="$(cd -- "$path1" && pwd -P; echo .)" &&
absolute_path2="$(cd -- "$path2" && pwd -P; echo .)" &&
[ "$absolute_path1" = "$absolute_path2" ]

The extra echo . takes care of the rare case where one of the targets has a file name ending with a newline: if the newline was the last character of the command substitution, it would be stripped, so the snippet would incorrectly report that foo is the same as foo.

There are still potential false negatives. With directories, hard links are not a concern, because most operating systems and filesystems forbid hard-linking directories. What can happen is that the same directory is somehow made available in different locations, such as:

  • mounting the same remote filesystem in two different locations, e.g.

    mount server:/some/where /mnt1
    mount server:/some/where /mnt2
    compare /mnt1 /mnt2
    
  • A bind mount

  • Any stackable filesystem, i.e. a way to present a filesystem in a different way, where you consider the view of the directory to be sufficiently unchanged from the original that it's effectively the same directory. For example, a stackable filesystem that converts file names, and the directory's name is invariant under the conversion.

In these examples, it's debatable whether the directories are really the same. This shows the limit of the notion of “same file” in edge cases.

The second basic way is to check the identification provided by the kernel: device and inode. The stat system call reports two attributes for each file:

  • the device number (st_dev), which is traditionally an identification of the block device (disk partition) that contains the filesystem that the file is on;
  • the inode number (st_ino), which is traditionally a unique number identifying the file within the same filesystem.

Normally, two files are identical if and only if they have the same device and inode numbers. Dash, ksh, bash, zsh, and at least some BusyBox builds (but not plain POSIX sh) have a -ef operator for the test builtin that tests that:

[ "$path1" -ef "$path2" ]

This handles hard links and symbolic links correctly (symbolic links are followed, i.e. a symbolic link to a file is considered the same as the target file).

This method is the simplest to carry out, if your shell (or your test utility) supports -ef. But beware that it has false positives, because not all filesystems report unique inode numbers. For example, with network filesystems, you're at the mercy of the remote host's choice of inode numbers. Many stackable filesystems report the underlying inode, which is not unique if the underlying directory tree spans multiple filesystems.

A different approach is to test for observable equivalence: modify one of the directories and see if the modification is reflected in the other. This approach has in principle no false negatives, but it requires having permission to modify the directories and can disrupt other parts of the system that are using these directories. There can be false positives, because seeing the modifications reflected could indicate that there is a daemon synchronizing the directories very quickly.

  • Great answer! Here's a corroboration, in the Illumos source code of the rm command, there's a check for a race condition, where the tool verifies whether the directory it's about to delete is still the same as the one stat'd earlier. It examines st_ino together with st_dev for both results of stat. – automaciej Sep 08 '21 at 06:10
3
sd(){       set '' "$1" '' "${2?sd() requires two arguments!}"
            while [ "$#" -gt 2 ]
            do    [  "${1:--L}" "${2:--L}" ${3:+"$4"} ] &&
                  [  "${1:--L}" ${3:+"-L"} "${3:-$4}" ] &&
                  [  "${2:-$3}" -ef "$4"/. ] && return  || ${1:+"return"}
                  set "$@" "$2";shift 2;set ! "$@"
            done  3>/dev/null 2>&"$((${#DBG}?2:3))"
    }

sd() First checks if both of its arguments are symlinks, and, if so it next checks if both of those are linked to directories with identical inode and device numbers. If so, sd() returns true

Failing that, it checks if both are not symlinks, and, again, if both are directories with equal device and inode numbers. Failing that, it returns false.

So...

ln -s dir1 ln3; ln -s dir1 ln4; ln -s dir2 ln5

sd dir1 dir2; echo $?
1
sd dir1 ln3;  echo $?
1
sd ln4 ln3;   echo $?
0
sd ln4 ln5;   echo $?
1
mount --bind dir1 dir2
sd dir1 dir2; echo $?
0

It may be desirable to do without the link checking - for example if you wanted to know if a link's target were the same as some directory, or just didn't care if it were a link at all. In that case you would only need the one [ fname1 -ef fname2 ] test.

Oh, I guess I should also mention that this assumes search permissions for all of the arguments. It will fail if the invoking user hasn't the permissions required to run the test.

mikeserv
  • 58,310
1

You can never be sure what mechanism or virtual fileystems or overlay filesystems are there, to sync those folders or link one to another. If it's a simple symbolic link, the inodes differ. To make 100% sure, that its the same location, you can create a file in /home/username:

touch /home/username/.testfile

And then compare the inode numbers of that file in both locations:

diff <(stat -c "%i" /home/username/.testfile) <(stat -c "%i" /app/home/username/.testfile)

If they're the same, then it's the same file, therefore they point to the same location.

Well, even with that you can't be 100% sure. Of course they can have the same inode number by pure coincidence, because in both filesytems the next free inode had the same number...

chaos
  • 48,171
0

Assuming /home/username and /app/home/username are directories, you cannot check the inode number to see if they're the same path. This because directories cannot be hardlinked, and soft links bear a different inode.

You could cd /home/username, touch a file, and see if it appears in the other /app/home/username. There are probably better methods, if someone can come with a better answer please go ahead.

dr_
  • 29,602
  • 1
    This is not quite right. If you mount --bind a directory the mount point will be listed with the same inode. Same if you mount a filesystem at two locations. I've just checked this on a Debian-derived system. –  Jun 02 '15 at 08:38
  • Uh? You can check the inode. If either path is a symlink, of course, you'll need to check its target. – Gilles 'SO- stop being evil' Jun 02 '15 at 22:51
0

Expanding what marco said: on GNU/Linux ls -li /home/username /app/home/username will already give you a good hint. If both inodes are equal chances are they point to the same location. Double check with mount. Triple check by touch-ing a file in /home/username and see if it appears in /app/home/username and with the same inode. Then can be pretty sure it's the same directory.

Test made on a Linux Mint machine, 3.11.0-12-generic kernel :

# ls -ldi /var
2 drwxr-xr-x 13 root root 4096 jan 13  2014 /var

# mkdir /mnt/var
# ls -ldi /mnt/var
266483 drwxr-xr-x 2 root root 4096 jun  2 10:43 /mnt/var

# mount --bind /var /mnt/var
# ls -ldi /mnt/var
2 drwxr-xr-x 2 root root 4096 jun  2 10:43 /mnt/var

# umount /mnt/var
# mount /dev/mapper/mint--vg-var /mnt/var
# ls -ldi /mnt/var
2 drwxr-xr-x 13 root root 4096 jan 13  2014 /mnt/var

Conclusion: I think you can check the inodes to make sure two directories are identical provided they're mount points. If one is a symbolic link, the above doesn't apply, of course. But then it's obvious, right:

# rmdir /mnt/var
# ln -s /var /mnt/
# ls -ldi /mnt/var
266483 lrwxrwxrwx 1 root root 4 jun  2 10:51 /mnt/var -> /var