Nearly missed this one. ls initializes a struct stat to all NUL bytes. It calls stat or lstat on a dangling symlink, and that fails. ls then tests stat.st_ino.
Sometimes, it's 0, but sometimes it's the inode of the symlink, possibly depending on how the package was configured/built or the environment. I haven't determined precisely what makes the difference, but ls (from coreutils-7.6) built some way prints 0 as the inode number, and other ways, it prints the inode of the dangling symlink. >From f7db178fdff1ebb113841035b55b103e074b5f6f Mon Sep 17 00:00:00 2001 From: Jim Meyering <[email protected]> Date: Tue, 29 Sep 2009 07:28:01 +0200 Subject: [PATCH] ls: don't use an undefined struct stat after failed stat/lstat * src/ls.c (gobble_file): After a failed stat/lstat call, clear the f->stat buffer, since the syscall may have modified it, and we may need to know that stat.st_ino is zero. * NEWS (Bug fixes): Mention it. --- NEWS | 4 ++++ src/ls.c | 6 ++++++ 2 files changed, 10 insertions(+), 0 deletions(-) diff --git a/NEWS b/NEWS index 1a0c847..d5270c8 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,10 @@ GNU coreutils NEWS -*- outline -*- ls: cannot access d/s: No such file or directory ? s + ls -Li no longer relies on unspecified behavior of stat/lstat. + Before this change, "ls -Li dangling-symlink" would mistakenly + print the inode number of the symlink under some conditions. + ** Portability On Solaris 9, many commands would mistakenly treat file/ the same as diff --git a/src/ls.c b/src/ls.c index c8e8abb..dc2f86e 100644 --- a/src/ls.c +++ b/src/ls.c @@ -2777,6 +2777,12 @@ gobble_file (char const *name, enum filetype type, ino_t inode, if (err != 0) { + /* The failed stat/lstat may have modified f->stat. Clear it, + since we may use at least its st_ino member, e.g., + when trying to print the inode of dangling symlink: + mkdir d; ln -s no-such d/s; ls -Li d */ + memset (&f->stat, 0, sizeof (f->stat)); + /* Failure to stat a command line argument leads to an exit status of 2. For other files, stat failure provokes an exit status of 1. */ -- 1.6.5.rc2.177.ga9dd6
