NetBSD Problem Report #47333

From www@NetBSD.org  Sat Dec 15 15:20:55 2012
Return-Path: <www@NetBSD.org>
Received: from mail.netbsd.org (mail.netbsd.org [149.20.53.66])
	by www.NetBSD.org (Postfix) with ESMTP id 9BBBC63E529
	for <gnats-bugs@gnats.NetBSD.org>; Sat, 15 Dec 2012 15:20:55 +0000 (UTC)
Message-Id: <20121215152054.AA92C63E529@www.NetBSD.org>
Date: Sat, 15 Dec 2012 15:20:54 +0000 (UTC)
From: tobiasu@tmux.org
Reply-To: tobiasu@tmux.org
To: gnats-bugs@NetBSD.org
Subject: stat -L undocumented behavior
X-Send-Pr-Version: www-1.0

>Number:         47333
>Category:       bin
>Synopsis:       stat -L undocumented behavior
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    bin-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat Dec 15 15:25:01 +0000 2012
>Last-Modified:  Sat May 03 21:05:01 +0000 2025
>Originator:     Tobias Ulmer
>Release:        
>Organization:
>Environment:
>Description:
"stat -L" claims to "Use stat(2) instead of lstat(2).  The information reported by stat will refer to the target of file, if file is a symbolic link, and not to file itself."

This is fine, and could for example be used to detect broken symlinks. Except it's not what it does. In case of a broken symlink it will fall back to lstat(2) and return the symlink info.

The only way to figure this out would be to compare the output of stat (without -L) or realize that symlinks can't show up with "stat -L" - and it must be broken (which requires one to be well caffeinated!).

The change was introduced quite some time ago:
http://cvsweb.netbsd.org/bsdweb.cgi/src/usr.bin/stat/stat.c?only_with_tag=MAIN#rev1.18

I argue that this should be reverted. Going by the very explicit documentation, the code is in error. Aside from that, stat(1) seems to be a designed as a simple scriptable wrapper around stat(2), not a higher-level tool like find(1) with all sorts of smarts. It makes scripting harder, because you either don't want to hear about symlinks at all, or you are interested in broken symlinks and would like to do something based on that information.

So, what does everyone else do? GNU stat prints an error message (-L is one of the few "standard" options). FreeBSD and OpenBSD use NetBSD's stat.
>How-To-Repeat:
ln -s doesnotexist foo
stat -L foo
<stat should print errno and return != 0>
>Fix:
Revert http://cvsweb.netbsd.org/bsdweb.cgi/src/usr.bin/stat/stat.c.diff?r1=1.17&r2=1.18&only_with_tag=MAIN

>Audit-Trail:
From: David Laight <david@l8s.co.uk>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: bin/47333: stat -L undocumented behavior
Date: Sat, 15 Dec 2012 16:43:47 +0000

 On Sat, Dec 15, 2012 at 03:25:01PM +0000, tobiasu@tmux.org wrote:
 > >Number:         47333
 > >Category:       bin
 > >Synopsis:       stat -L undocumented behavior
 ...
 > "stat -L" claims to "Use stat(2) instead of lstat(2).
 > The information reported by stat will refer to the target of file,
 > if file is a symbolic link, and not to file itself."
 > 
 > This is fine, and could for example be used to detect broken symlinks.
 > Except it's not what it does. In case of a broken symlink it will fall
 > back to lstat(2) and return the symlink info.

 So you can detect that by noticing that the output of 'stat -L' is
 still a symlink.

 The man page needs fixing.

 	David

 -- 
 David Laight: david@l8s.co.uk

From: Tobias Ulmer <tobiasu@tmux.org>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: bin/47333: stat -L undocumented behavior
Date: Sat, 15 Dec 2012 19:13:17 +0100

 On Sat, Dec 15, 2012 at 04:30:13PM +0000, David Laight wrote:
 > The following reply was made to PR bin/47333; it has been noted by GNATS.
 > 
 > From: David Laight <david@l8s.co.uk>
 > To: gnats-bugs@NetBSD.org
 > Cc: 
 > Subject: Re: bin/47333: stat -L undocumented behavior
 > Date: Sat, 15 Dec 2012 16:43:47 +0000
 > 
 >  On Sat, Dec 15, 2012 at 03:25:01PM +0000, tobiasu@tmux.org wrote:
 >  > >Number:         47333
 >  > >Category:       bin
 >  > >Synopsis:       stat -L undocumented behavior
 >  ...
 >  > "stat -L" claims to "Use stat(2) instead of lstat(2).
 >  > The information reported by stat will refer to the target of file,
 >  > if file is a symbolic link, and not to file itself."
 >  > 
 >  > This is fine, and could for example be used to detect broken symlinks.
 >  > Except it's not what it does. In case of a broken symlink it will fall
 >  > back to lstat(2) and return the symlink info.
 >  
 >  So you can detect that by noticing that the output of 'stat -L' is
 >  still a symlink.
 >  
 >  The man page needs fixing.

 Yes, that is easy. Yes one can hack around it, but the whole point of -L
 is to use stat(2) - not lstat(2), right?  Also why be gratuitously
 incompatible with GNU stat? Is there an actual use case?

 Once it's documented, it would be very hard to remove again. For now
 it's simply a bug.

 >  
 >  	David
 >  
 >  -- 
 >  David Laight: david@l8s.co.uk
 >  

From: David Holland <dholland-bugs@netbsd.org>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: bin/47333: stat -L undocumented behavior
Date: Sat, 15 Dec 2012 20:02:35 +0000

 On Sat, Dec 15, 2012 at 04:30:13PM +0000, David Laight wrote:
  >  On Sat, Dec 15, 2012 at 03:25:01PM +0000, tobiasu@tmux.org wrote:
  >  > >Number:         47333
  >  > >Category:       bin
  >  > >Synopsis:       stat -L undocumented behavior
  >  ...
  >  > "stat -L" claims to "Use stat(2) instead of lstat(2).
  >  > The information reported by stat will refer to the target of file,
  >  > if file is a symbolic link, and not to file itself."
  >  > 
  >  > This is fine, and could for example be used to detect broken symlinks.
  >  > Except it's not what it does. In case of a broken symlink it will fall
  >  > back to lstat(2) and return the symlink info.
  >  
  >  So you can detect that by noticing that the output of 'stat -L' is
  >  still a symlink.
  >  
  >  The man page needs fixing.

 Please check the standards (since this is where this program came
 from, it isn't either native or historical) before proposing things
 like this.

 -- 
 David A. Holland
 dholland@netbsd.org

From: Tobias Ulmer <tobiasu@tmux.org>
To: gnats-bugs@NetBSD.org
Cc: 
Subject: Re: bin/47333: stat -L undocumented behavior
Date: Sat, 15 Dec 2012 21:56:37 +0100

 On Sat, Dec 15, 2012 at 08:05:02PM +0000, David Holland wrote:
 > The following reply was made to PR bin/47333; it has been noted by GNATS.
 > 
 > From: David Holland <dholland-bugs@netbsd.org>
 > To: gnats-bugs@NetBSD.org
 > Cc: 
 > Subject: Re: bin/47333: stat -L undocumented behavior
 > Date: Sat, 15 Dec 2012 20:02:35 +0000
 > 
 >  On Sat, Dec 15, 2012 at 04:30:13PM +0000, David Laight wrote:
 >   >  On Sat, Dec 15, 2012 at 03:25:01PM +0000, tobiasu@tmux.org wrote:
 >   >  > >Number:         47333
 >   >  > >Category:       bin
 >   >  > >Synopsis:       stat -L undocumented behavior
 >   >  ...
 >   >  > "stat -L" claims to "Use stat(2) instead of lstat(2).
 >   >  > The information reported by stat will refer to the target of file,
 >   >  > if file is a symbolic link, and not to file itself."
 >   >  > 
 >   >  > This is fine, and could for example be used to detect broken symlinks.
 >   >  > Except it's not what it does. In case of a broken symlink it will fall
 >   >  > back to lstat(2) and return the symlink info.
 >   >  
 >   >  So you can detect that by noticing that the output of 'stat -L' is
 >   >  still a symlink.
 >   >  
 >   >  The man page needs fixing.
 >  
 >  Please check the standards (since this is where this program came
 >  from, it isn't either native or historical) before proposing things
 >  like this.

 After a bit more digging, it turns out that the original stat was
 devised by Michael Meskes. It was later integrated into coreutils.

 See here for history:
 http://fossies.org/linux/misc/stat-3.3.tar.gz:a/stat-3.3/README

 >  
 >  -- 
 >  David A. Holland
 >  dholland@netbsd.org
 >  

From: "Jeremy C. Reed" <reed@reedmedia.net>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: bin/47333: stat -L undocumented behavior
Date: Fri, 2 May 2025 19:16:14 +0000 (UTC)

 This ticket is over a decade old. But the change is over two decades
 so maybe we should just document it.
 I don't see any Unix spec for this. But here is example from Linux 
 coreutils:

 reed@ns1:~$ ln -s doesnotexist linktest
 reed@ns1:~$ stat linktest 
   File: linktest -> doesnotexist
   Size: 12              Blocks: 0          IO Block: 4096   symbolic link
 Device: 8,1     Inode: 129555      Links: 1
 Access: (0777/lrwxrwxrwx)  Uid: ( 1000/    reed)   Gid: ( 1000/    reed)
 Access: 2025-05-02 21:09:33.291973078 +0200
 Modify: 2025-05-02 21:09:29.739973078 +0200
 Change: 2025-05-02 21:09:29.739973078 +0200
  Birth: 2025-05-02 21:09:29.739973078 +0200
 reed@ns1:~$ stat -L linktest 
 stat: cannot statx 'linktest': No such file or directory
 reed@ns1:~$ ls -l linktest 
 lrwxrwxrwx 1 reed reed 12 May  2 21:09 linktest -> doesnotexist

From: Robert Elz <kre@munnari.OZ.AU>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: bin/47333: stat -L undocumented behavior
Date: Sat, 03 May 2025 17:55:11 +0700

     Date:        Fri,  2 May 2025 19:20:01 +0000 (UTC)
     From:        "Jeremy C. Reed via gnats" <gnats-admin@NetBSD.org>
     Message-ID:  <20250502192001.4F7D71A923C@mollari.NetBSD.org>

   |  This ticket is over a decade old. But the change is over two decades
   |  so maybe we should just document it.

 That's what we should do, making it return an error makes it
 harder to tell the difference between

 	stat -L doesnotexist
 and
 	stat -L linktest

 (using the names from the test).

 That is, in this example:

 	reed@ns1:~$ stat -L linktest 
 	stat: cannot statx 'linktest': No such file or directory

 is it linktest that does not exist, or the target of linktest?
 The only way to know is to do another command.

 The test for a symlink which points to non-existent files is just

 	test "$(stat -Lf%t linkname)" = @

 That's simple, easy, and unambiguous, and only requires a single
 command (regardless of what "linkname" might happen to be).

 kre

From: Robert Elz <kre@munnari.OZ.AU>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: bin/47333: stat -L undocumented behavior
Date: Sat, 03 May 2025 19:55:44 +0700

     Date:        Sat,  3 May 2025 11:00:04 +0000 (UTC)
     From:        "Robert Elz via gnats" <gnats-admin@NetBSD.org>
     Message-ID:  <20250503110004.84B551A923E@mollari.NetBSD.org>

   |  	test "$(stat -Lf%t linkname)" = @

 Oops, transcription error between test and e-mail (I didn't cut&paste
 that one), I meant:

 	test "$(stat -Lf%T linkname)" = @

 kre

From: "Robert Elz" <kre@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/47333 CVS commit: src/usr.bin/stat
Date: Sat, 3 May 2025 21:00:58 +0000

 Module Name:	src
 Committed By:	kre
 Date:		Sat May  3 21:00:58 UTC 2025

 Modified Files:
 	src/usr.bin/stat: stat.1

 Log Message:
 PR bin/47333

 Note that -L will fall back to use lstat() if the stat() requested
 returns ENOENT (if the following lstat() fails, the ENOENT from
 the lstat() is returned - that generally indicates that the original
 ENOENT came from the filename passed to stat() rather than the
 value of the symlink.   (The man page doesn't say all of that.)

 If "stat -L name" returns data from a symlink (eg: if -f %T is @)
 then name must refer to a symlink which points to nothing.
 (The man page does say that.)


 To generate a diff of this commit:
 cvs rdiff -u -r1.47 -r1.48 src/usr.bin/stat/stat.1

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.47 2022/09/11 19:34:41 kim Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2025 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.