Subject: bin/34662: readlink doesn't grok -f, and there's no alternative (+fix)
To: None <gnats-admin@netbsd.org, netbsd-bugs@netbsd.org>
From: None <martijnb@atlas.ipv6.stack.nl>
List: netbsd-bugs
Date: 09/29/2006 11:55:00
>Number: 34662
>Category: bin
>Synopsis: readlink doesn't grok -f, and there's no alternative (+fix)
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: bin-bug-people
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Fri Sep 29 11:55:00 +0000 2006
>Originator: martijnb@atlas.ipv6.stack.nl
>Release: NetBSD 4.99.3
>Organization:
>Environment:
System: NetBSD atlas.ipv6.stack.nl 4.99.3 NetBSD 4.99.3 (ATLAS) #4: Thu Sep 28 13:29:00 CEST 2006 martijnb@atlas.ipv6.stack.nl:/usr/obj/sys/arch/amd64/compile/ATLAS amd64
Architecture: x86_64
Machine: amd64
>Description:
Currently, there is no userland utility to query the absolute pathname
of a given filename by means of realpath(3). Other UNIX-like operatingsystems
either offer a this by means of a realpath(1) binary (Most notably: FreeBSD,
Gentoo Linux), others (OpenBSD, all other Linux distributions) have a '-f'
argument to readlink(1) which does the same: Call realpath(3) on the given
argument. To my best knowledge, NetBSD offers neither.
>How-To-Repeat:
observation. There are programs out there relying on the availability
of this functionality.
>Fix:
There's three possible approaches:
1) Do what FreeBSD does: leave stat/readlink as they are, and write a
small wrapper program to call realpath(3).
2) Do what OpenBSD/Linux does: Make readlink a seperate program (It is
currently a symlink to stat) and handle the '-f' case accordingly.
3) Keep the current situation (readlink being a symlink to stat), add
yet another format option to stat and update the readlink stub to
accept a '-f' argument.
I considered #3 to be the best option, and introduced a "%y" format option.
Index: stat.1
===================================================================
RCS file: /cvsroot/src/usr.bin/stat/stat.1,v
retrieving revision 1.18
diff -u -r1.18 stat.1
--- stat.1 26 Jun 2005 10:16:46 -0000 1.18
+++ stat.1 29 Sep 2006 11:30:32 -0000
@@ -54,7 +54,7 @@
.Op Fl t Ar timefmt
.Op Ar
.Nm readlink
-.Op Fl n
+.Op Fl nf
.Op Ar
.Sh DESCRIPTION
The
@@ -71,9 +71,19 @@
When invoked as
.Nm readlink ,
only the target of the symbolic link is printed.
-If the given argument is not a symbolic link,
+If the given argument is not a symbolic link and the
+.Fl f
+option is not specified,
.Nm readlink
-will print nothing and exit with an error.
+will print nothing and exit with an error. If the
+.Fl f
+option is specified, the output is canonicalized by following every symlink
+in every component of the given path recursively.
+.Nm readlink
+will resolve both absolute and relative paths, and return the absolute pathname
+corresponding to
+.Ar file .
+In this case, the argument does not need to be a symbolic link.
.Pp
The information displayed is obtained by calling
.Xr lstat 2
@@ -379,7 +389,7 @@
Inode generation number.
.El
.Pp
-The following four field specifiers are not drawn directly from the
+The following five field specifiers are not drawn directly from the
data in struct stat, but are:
.Bl -tag -width Ds
.It Cm N
@@ -390,6 +400,8 @@
or in a more descriptive form if the sub field specifier
.Cm H
is given.
+.It Cm y
+The absolute pathname corresponding to the file.
.It Cm Y
The target of a symbolic link.
.It Cm Z
Index: stat.c
===================================================================
RCS file: /cvsroot/src/usr.bin/stat/stat.c,v
retrieving revision 1.23
diff -u -r1.23 stat.c
--- stat.c 23 Jun 2005 03:13:24 -0000 1.23
+++ stat.c 29 Sep 2006 11:30:32 -0000
@@ -176,6 +176,7 @@
#define SHOW_st_flags 'f'
#define SHOW_st_gen 'v'
#define SHOW_symlink 'Y'
+#define SHOW_realpath 'y'
#define SHOW_filetype 'T'
#define SHOW_filename 'N'
#define SHOW_sizerdev 'Z'
@@ -219,8 +220,8 @@
if (strcmp(getprogname(), "readlink") == 0) {
am_readlink = 1;
- options = "n";
- synopsis = "[-n] [file ...]";
+ options = "nf";
+ synopsis = "[-nf] [file ...]";
statfmt = "%Y";
fmtchar = 'f';
quiet = 1;
@@ -244,6 +245,10 @@
quiet = 1;
break;
case 'f':
+ if(am_readlink) {
+ statfmt="%y";
+ break;
+ }
statfmt = optarg;
/* FALLTHROUGH */
case 'l':
@@ -511,6 +516,7 @@
fmtcase(what, SHOW_st_flags);
fmtcase(what, SHOW_st_gen);
fmtcase(what, SHOW_symlink);
+ fmtcase(what, SHOW_realpath);
fmtcase(what, SHOW_filetype);
fmtcase(what, SHOW_filename);
fmtcase(what, SHOW_sizerdev);
@@ -784,6 +790,21 @@
ofmt = FMTF_UNSIGNED;
break;
#endif /* HAVE_STRUCT_STAT_ST_GEN */
+ case SHOW_realpath:
+ small = 0;
+ data = 0;
+ snprintf(path, sizeof(path), " -> ");
+ if (realpath(file, path + 4) == NULL ) {
+ linkfail = 1;
+ l = 0;
+ path[0] = '\0';
+ }
+ sdata = path + (ofmt == FMTF_STRING ? 0: 4);
+
+ formats = FMTF_STRING;
+ if (ofmt == 0)
+ ofmt = FMTF_STRING;
+ break;
case SHOW_symlink:
small = 0;
data = 0;
>Unformatted: