readlink bugs

Bernhard Fischer rep.nop at aon.at
Fri Oct 20 03:20:33 PDT 2006


On Fri, Oct 20, 2006 at 10:13:31AM +0200, Bernhard Fischer wrote:
>On Thu, Oct 19, 2006 at 07:30:56PM +0200, Denis Vlasenko wrote:
>>On Wednesday 18 October 2006 07:41, Alexander Griesser wrote:
>
>>Bingo! static busybox doesn't fflush stdout!
>>This workaround made readlink work:
>
>No! :)
>
>>int readlink_main(int argc, char **argv)
>>{
>>        char *buf;
>>        unsigned opt = ENABLE_FEATURE_READLINK_FOLLOW ?
>>                        getopt32(argc, argv, "f") : 0;
>>
>>        if (argc != (ENABLE_FEATURE_READLINK_FOLLOW ? optind + 1 : 2))
>>                        bb_show_usage();
>>
>>        if (opt & READLINK_FLAG_f)
>>                buf = realpath(argv[optind], bb_common_bufsiz1);
>>        else
>>                buf = xreadlink(argv[ENABLE_FEATURE_READLINK_FOLLOW ? optind : 1]);
>>
>>        if (!buf)
>>                return EXIT_FAILURE;
>>        puts(buf);
>->+       fflush(stdout);
>>
>>        if (ENABLE_FEATURE_CLEAN_UP && buf != bb_common_bufsiz1)
>>                free(buf);
>>
>->        return EXIT_SUCCESS;
>+	bb_fflush_stdout_and_exit(EXIT_SUCCESS);
>>}
>>
>>Need to take a look how glibc managed to botch it...
>
>Please don't botch busybox but reuse the stuff that is already there ;)
>   text    data     bss     dec     hex filename
>     98       0       0      98      62 debianutils/readlink.o.orig
>    110       0       0     110      6e debianutils/readlink.o.vda
>    103       0       0     103      67 debianutils/readlink.o.bb_fflush
>
>cheers,

hm. Please see the questions in the attached patch that results in
shrinking it back to under 100 bytes.

   text    data     bss     dec     hex filename
     94       0       0      94      5e debianutils/readlink.o.attached

Attached patchlet also contains an extension to getopt32's
opt_complementary; =N to check for exactly N arguments.
Rational: Several applets do something to the effect of
(if argc != mynumber)
	bb_show_usage();
While it seems that saying opt_complementary="-1?1" for mynumber=1
works, it doubles the size as compared to the proposed "=1" string.
Needless to say that the impl of that =N support in getopt32.c that i
have attached is surprisingly big, but i didn't look closely on how to
keep it down to a reasonable size increase:
$ size libbb/getopt32.o*
   text    data     bss     dec     hex filename
    979       4       0     983     3d7 libbb/getopt32.o.oorig
   1014       4       0    1018     3fa libbb/getopt32.o

Is this =N support something that you consider useful?


A few observations about the current readlink (bugs):
$ rm -f no ../no ; touch yep
$ for i in '' ./busybox;do $i readlink no;echo ret=$?;done
ret=1
readlink: no: No such file or directory
ret=1
$ emits wrong perror (see comment in the patch)


$ for i in '' ./busybox;do $i readlink yep;echo ret=$?;done
ret=1
readlink: yep: Invalid argument
ret=1
$ again, emits perror wrongly


$ for i in '' ./busybox;do $i readlink -f no;echo ret=$?;done
/tmp/busybox/no
ret=0
ret=1
$ does not emit nor normalize fname (see comment)


$ for i in '' ./busybox;do $i readlink -f ../no;echo ret=$?;done
/tmp/no
ret=0
ret=1
$ same as for ./no

-------------- next part --------------
Index: debianutils/readlink.c
===================================================================
--- debianutils/readlink.c	(revision 16410)
+++ debianutils/readlink.c	(working copy)
@@ -18,16 +18,35 @@
 int readlink_main(int argc, char **argv)
 {
 	char *buf;
-	unsigned opt = ENABLE_FEATURE_READLINK_FOLLOW ?
+	unsigned opt;
+	char *fname;
+
+	/* We need exactly one non-option argument.  */
+	USE_FEATURE_READLINK_FOLLOW(opt_complementary = "-1?1";)
+	opt = ENABLE_FEATURE_READLINK_FOLLOW ?
 			getopt32(argc, argv, "f") : 0;
 
-	if (argc != (ENABLE_FEATURE_READLINK_FOLLOW ? optind + 1 : 2))
+	/* Regardless of follow support, opt is now 0 if -f was not seen,
+	 * and 1 otherwise.  */
+	SKIP_FEATURE_READLINK_FOLLOW(if (argc !=  opt + 2)
 			bb_show_usage();
+	)
+	/* Caching the array deref saved one byte for me and was optimized away
+	 * if follow support is turned off.  */
+	fname = argv[opt+1];
 
-	if (opt & READLINK_FLAG_f)
-		buf = realpath(argv[optind], bb_common_bufsiz1);
-	else
-		buf = xreadlink(argv[ENABLE_FEATURE_READLINK_FOLLOW ? optind : 1]);
+	if (opt & READLINK_FLAG_f) {
+		buf = realpath(fname, bb_common_bufsiz1);
+		/* If it is not a file, then we have to emit "$CWD/fname".  */
+/* XXX: we'd need to normalize relative filenames WRT CWD here..
+ * -f ../nosuchfile becomes simplify_path(getcwd() ## fname
+ */
+	} else {
+/* XXX: Funnily, this a) calls bb_perror_msg which we do not want and
+ * b) exits with a failure which we want neither. Sad but we apparently
+ * have to spin our own version here, no? */
+		buf = xreadlink(fname);
+	}
 
 	if (!buf)
 		return EXIT_FAILURE;
@@ -36,5 +55,5 @@ int readlink_main(int argc, char **argv)
 	if (ENABLE_FEATURE_CLEAN_UP && buf != bb_common_bufsiz1)
 		free(buf);
 
-	return EXIT_SUCCESS;
+	bb_fflush_stdout_and_exit(EXIT_SUCCESS);
 }
Index: libbb/getopt32.c
===================================================================
--- libbb/getopt32.c	(revision 16410)
+++ libbb/getopt32.c	(working copy)
@@ -188,6 +188,10 @@ Special characters:
         by a single digit (0-9) means that at least N non-option
         arguments must be present on the command line
 
+ "=N"   An equal sign as the first char in a opt_complementary group followed
+        by a single digit (0-9) means that exactly N non-option
+        arguments must be present on the command line
+
  "V-"   An option with dash before colon or end-of-line results in
         bb_show_usage being called if this option is encountered.
         This is typically used to implement "print verbose usage message
@@ -400,6 +404,11 @@ getopt32(int argc, char **argv, const ch
 			}
 			continue;
 		}
+		if (*s == '=') {
+			min_arg = max_arg = c - '0';
+			s++;
+			continue;
+		}
 		for (on_off = complementary; on_off->opt; on_off++)
 			if (on_off->opt == *s)
 				break;


More information about the busybox mailing list