1

I want, only using "basic" commands (for maximum portability) (i.e., something that would work on AIX / Linux / etc., not just something using a recent nicety ^^), to find all the files (symlinks, hardlinks and combinations thereof) pointing to a specific file/dir.

Be careful to not rush to answer find / -ls | grep .... : it will miss many cases. See my links below, mixing hardlinks, symlinks, relative-and-absolute paths (and also play with symlinks "././././." possibilities)

ie

  • hardlinks and symlinks can be "nested", ad-inifinitum...
  • some symlinks could be with the full path, others with a relative path,
  • those paths (relative or absolute) could be very complex (ex: /tmp/././../etc/file )
  • a symlink could lead to a file, which hardlink to another, which is a symlink to a third, which ends up [after some more iteration] to the final destination...

In the end, I "just" need to find out a way to know what is the "final destination" of any file/link (ie, which inode will be accessed in the end?). But it's really tough (unless some magical function will tell me "the final destination inode is : ...". That's what I need!)

I thought I could simply use '-H' or '-L' options of find, but (I'm probably dumb...) it didn't work... yet.

Any info welcomed (but please, using find/ls/etc, not some "nice utility only available on linux")

Try to create some different links to the "/tmp/A" directory, and find a way to find and list them all:

mkdir /tmp/A    /tmp/B
ln -s /tmp/A/   /tmp/B/D  #absolute symlink, with a training "/"
ln -s ../A      /tmp/B/E     #relative symlink
ln -s /tmp/A/.  /home/F  #absolute symlink, with a trailing "/."
ln -s ../tmp/A/./.  /var/L  #let's get craaaaaaazy! symlinks are "fun"...
ln    /tmp/B/D  /etc/G    #Hard-link to the symlink file /tmp/B/D, which itself symlink to /tmp/A/ witch ends up being the same as "/tmp/A" in most cases.
ln    /etc/G    /etc/G2   #and a duplicate of the previous one, just to be sure [so /etc/G, /etc/G2, and /tmp/B/D have same inode, but it's a different inode from the final destination... :(
ln -s etc/G /thatsenough   #symlink again, for further chaining

and then some tries:

find -H / -ls   # show inodes of the links and symlinks, not of the "final" directory
find -L / -ls   # same thing (I do try that on an OLD AIX ...)
find -H / -inum 123456 -ls  #if /tmp/A/. has inode 123456. This lists *only* hardlinks to /tmp/A, ie will only print a subset (and /tmp/A have to be a file in that case)

I expected to see the final inode (123456) in front of all the paths, in one of those invocation (I also added '-follow' to both), but I always see the inodes of the links, not of the "final destination" (i.e., /tmp/A)

What could I use to find out the "final inode" I end up accessing? [the OS manages it, but can a "simple" command tell me beforehand "through that file, you will open that final inode!"?]

4
  • to be thorough: if you can add files in the "final destination dir" or edit "the final destination file", it's possible: create a very_specific_filename inside that dir, and for i in $(find / -print) ; do [ -f $i/very_specific_filename ] ; done (for a dir), or ... do grep "specific_string" "$i" ... (for a file). This works, and is easy. But for this you need to edit the destination file/dir. I want a solution where you can NOT change/edit the destination file/dir [ex for forensics: I want to find all, even well hidden, links to a file or dir, and can't afford to change them for that] Commented Nov 7, 2013 at 18:39
  • 3
    find / -exec ls -iLd {} | grep inodenum . There are always going to be cases you can't catch, such as a symlink that points to a symlink on a file system that's only occasionally mounted and whose contents are not under your control. Commented Nov 7, 2013 at 18:52
  • @MarkPlotnick: +1. I'm sorry I didn't try out your solution [I was thinking that it would only show up the link's inode instead of the destination's one... But it does work! At least for directories! (and now my workaround looks silly ^^). I'll now see if it works for the convulated cases I pointed above [i just tested on a very smaller subset of special cases] Commented Nov 8, 2013 at 9:35
  • @MarkPlotnick: it's indeed what I was looking for ! follows any combination of hard-links&symlinks up to the final destination and print that final destination's inode! Please post it as an answer, so I can give its due credit! Commented Nov 8, 2013 at 9:50

4 Answers 4

2

The question is not uniformly answerable. For example, by chaining just a few symlinks in a tree, I can create infinitely many possible paths that resolve to the same inode.

Your desire for complete portability is also unlikely to succeed. For example, even "find" differs in the wild between GNU, BSD, POSIX and other variants (and of course between versions of those variants).

But what you want may be something akin to find / -samefile /absolute/path/to/file.

This is available in GNU find >= 4.2.11.

3
  • Consider setting a reasonably high -maxdepth to avoid inifinite recursion if necessary. Commented Nov 7, 2013 at 21:57
  • find -samefile isn't enough: it doesn't follow symbolic links. Adding -L would follow symlinks, but then find would also recurse into symlinks to directories, which is not desirable here. Commented Nov 7, 2013 at 22:38
  • thanks for the answer, but I hope someone knows about a "magic" command that will be able to tell "This inodeX hardlink/symlink is linked to inodeY+inodeZ... files [including those files reached by following several 'hops', and avoiding loops...]". Actually I have a nice workaround when the destdir is a directory, but not when it's not a directory. I'll post it. Commented Nov 8, 2013 at 9:10
1

ls has a -L option that will effectively chase symlinks and show the perms, owner, inode, etc. of the ultimate object. [It does this by doing stat(target) instead of lstat(target)]. For best results, run as root or as someone who has read access to relevant mounted filesystems.

So in your case, try the following:

find / -exec ls -iLd {} | grep inodenum

[copied from my comment, per request of the OP.]

3
  • thanks, +1 (actually -L didn't work for me with "find", but your version using it in "ls" instead of "find" does work!) Commented Nov 8, 2013 at 23:17
  • 1
    Thanks, I indeed meant to say ls. Answer fixed. Commented Nov 9, 2013 at 2:50
  • 1
    Note that it's likely to return false positives as an inodenum is only unique per-filesystem and you're grepping the inodenum in the file name as well (ls -iL1qd {} | grep '^inodenum[[:blank:]]' would make it more reliable). Commented Oct 7, 2015 at 12:10
0

update: Mark Plottnick's answer (under my question) was much simpler and works for files & directories, and gives the final destination's inode following any combination of links in-between !
It was what I was looking for [unless I forgot special cases in my test directory].
I'm glad the final solution is so elegant and portable! Mark, please post it so that I can give you the checkmark ^^

I let my own workaround below, which now looks very silly ^^

===

I found some incredibly easy workaround when the destination file is a directory! Just add a "/." after its name, and you'll have the final destination's directory inode! And you'll still print the originating path!

find / -print | xargs -n 1 ls -ild '{}'/. 2>/dev/null | grep inodeyouwant

note: it's very heavy in "try to do this on a file without knowing if it's a symlink or hardlink at all", so I'll update with one that only look for hard-links+symlinks.

But now I need to figure out a way to do this for "not-leading-to-a-directory-in-the-end" file[s] (ie, links/symlinks/chain-of-those that in the end point to something that is not a directory) (once again, without modifying that final destination!)

I'll update when I found a nice way for that

0

With GNU find:

find -L . \( ! -xtype l -o -prune \) -samefile /path/to/the/file -print

Would report the different canonical paths that lead to the same file as /path/to/the/file

With zsh:

printf '%s\n' **/*(De:'[[ $REPLY -ef /path/to/the/file ]]')

The [ file -ef other-file ] is not POSIX yet but is widely supported and there's talk of including it in the next version of the spec.

find . -exec sh -c '
  for file do
    [ "$file" -ef /path/to/the/file ] && printf "%s\n" "$file"
  done' sh {} +

Should work as long as sh is based on AT&T ksh, pdksh, bash, zsh, ash or yash at least (and I don't know of any POSIX sh implementation that is not based on any of those).

Not the answer you're looking for? Browse other questions tagged .