From 7b7a19c95f236cd18f48e44b7cc6794d21c0369f Mon Sep 17 00:00:00 2001
From: Bernhard Voelker <mail@bernhard-voelker.de>
Date: Fri, 20 Jul 2018 10:07:13 +0200
Subject: [PATCH 181/224] find: fix -printf %Y output to 'N' for broken links

The format %Y shall output 'N' for broken links (ENOENT), 'L' for ELOOP,
and '?' for any other error when stat()ing the symlink target.

The %Y implementation implicitly fell back to lstat(); therefore it
output 'l' for dangling symlinks.

  $ ln -s ENOENT DANGLE
  $ find -D stat DANGLE -printf '%y %Y %p\n'
  fallback_stat(): stat(DANGLE) failed; falling back on lstat()
  l l DANGLE

* find/print.c (do_fprintf): For %Y, always follow links to determine
the type of the symlink target.
* find/testsuite/find.gnu/printf-symlink.exp: Add a test case for broken
links and the ELOOP case ...
* find/testsuite/find.gnu/printf-symlink.xo: ... with the expected output.
* NEWS (Bug fixes): Mention the fix.

Bug introduced in version v4.5.8 with commit '25e7c5fbf9'.
---
 NEWS                                       | 4 ++++
 find/print.c                               | 9 +++++----
 find/testsuite/find.gnu/printf-symlink.exp | 5 ++++-
 find/testsuite/find.gnu/printf-symlink.xo  | 4 ++++
 4 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/find/print.c b/find/print.c
index 3af3e91d..545df96a 100644
--- a/find/print.c
+++ b/find/print.c
@@ -1175,11 +1175,12 @@ do_fprintf (struct format_val *dest,
             if (S_ISLNK (stat_buf->st_mode))
               {
                 struct stat sbuf;
-                /* If we would normally follow links, do not do so.
-                 * If we would normally not follow links, do so.
+                /* %Y needs to stat the symlink target regardless of
+                 * whether we would normally follow symbolic links or not.
+                 * (Actually we do not even come here when following_links()
+                 *  other than the ENOENT case.)
                  */
-                if ((following_links () ? optionp_stat : optionl_stat)
-                    (state.rel_pathname, &sbuf) != 0)
+                if (fstatat (state.cwd_dir_fd, state.rel_pathname, &sbuf, 0) != 0)
                   {
                     if ( errno == ENOENT )
                       {
diff --git a/find/testsuite/find.gnu/printf-symlink.exp b/find/testsuite/find.gnu/printf-symlink.exp
index 6acc3e68..453f07fc 100644
--- a/find/testsuite/find.gnu/printf-symlink.exp
+++ b/find/testsuite/find.gnu/printf-symlink.exp
@@ -2,5 +2,8 @@ exec rm -rf tmp
 exec mkdir tmp
 exec touch tmp/file
 exec ln -s file tmp/LINK
-find_start p {tmp/LINK  -printf "RESULT: %y %Y %p\n" -printf "RESULT2: %Y %y %p\n" }
+exec ln -s ENOENT tmp/DANGLE
+exec ln -s SELF tmp/SELF
+exec ls -ls tmp
+find_start p {tmp/LINK tmp/DANGLE tmp/SELF -printf "RESULT: %y %Y %p\n" -printf "RESULT2: %Y %y %p\n" }
 exec rm -rf tmp
diff --git a/find/testsuite/find.gnu/printf-symlink.xo b/find/testsuite/find.gnu/printf-symlink.xo
index cc2b69c4..08eb83c6 100644
--- a/find/testsuite/find.gnu/printf-symlink.xo
+++ b/find/testsuite/find.gnu/printf-symlink.xo
@@ -1,2 +1,6 @@
 RESULT: l f tmp/LINK
 RESULT2: f l tmp/LINK
+RESULT: l N tmp/DANGLE
+RESULT2: N l tmp/DANGLE
+RESULT: l L tmp/SELF
+RESULT2: L l tmp/SELF
-- 
2.19.1

