Mercurial > hg > octave-nkf > gnulib-hg
changeset 12059:6babf16a67dd
readlink: fix Solaris 9 bug with trailing slash
readlink("link/",buf,len) mistakenly succeeded.
* lib/readlink.c (rpl_readlink): Work around trailing slash bug.
* m4/readlink.m4 (gl_FUNC_READLINK): Detect the bug.
* doc/posix-functions/readlink.texi (readlink): Document this.
* modules/readlink-tests: New test.
* tests/test-readlink.c: Likewise.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Tue, 22 Sep 2009 17:15:04 -0600 |
parents | 36183b482b71 |
children | 95a12a00ea4f |
files | ChangeLog doc/posix-functions/readlink.texi lib/readlink.c m4/readlink.m4 modules/readlink-tests tests/test-readlink.c |
diffstat | 6 files changed, 175 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2009-09-23 Eric Blake <ebb9@byu.net> + readlink: fix Solaris 9 bug with trailing slash + * lib/readlink.c (rpl_readlink): Work around trailing slash bug. + * m4/readlink.m4 (gl_FUNC_READLINK): Detect the bug. + * doc/posix-functions/readlink.texi (readlink): Document this. + * modules/readlink-tests: New test. + * tests/test-readlink.c: Likewise. + readlink: fix cygwin 1.5.x bug with return type * m4/readlink.m4 (gl_FUNC_READLINK): Require correct signature. * lib/unistd.in.h (readlink): Use ssize_t.
--- a/doc/posix-functions/readlink.texi +++ b/doc/posix-functions/readlink.texi @@ -9,6 +9,9 @@ Portability problems fixed by Gnulib: @itemize @item +Some platforms mistakenly succeed on @code{readlink("link/",buf,len)}: +Solaris 9. +@item On some platforms, @code{readlink} returns @code{int} instead of @code{ssize_t}: FreeBSD 6.0, OpenBSD 3.8, Cygwin 1.5.x.
--- a/lib/readlink.c +++ b/lib/readlink.c @@ -47,11 +47,27 @@ # undef readlink /* readlink() wrapper that uses correct types, for systems like cygwin - 1.5.x where readlink returns int. */ + 1.5.x where readlink returns int, and which rejects trailing slash, + for Solaris 9. */ ssize_t rpl_readlink (const char *name, char *buf, size_t bufsize) { +# if READLINK_TRAILING_SLASH_BUG + size_t len = strlen (name); + if (len && name[len - 1] == '/') + { + /* Even if name without the slash is a symlink to a directory, + both lstat() and stat() must resolve the trailing slash to + the directory rather than the symlink. We can therefore + safely use stat() to distinguish between EINVAL and + ENOTDIR/ENOENT, avoiding extra overhead of rpl_lstat(). */ + struct stat st; + if (stat (name, &st) == 0) + errno = EINVAL; + return -1; + } +# endif /* READLINK_TRAILING_SLASH_BUG */ return readlink (name, buf, bufsize); }
--- a/m4/readlink.m4 +++ b/m4/readlink.m4 @@ -1,4 +1,4 @@ -# readlink.m4 serial 6 +# readlink.m4 serial 7 dnl Copyright (C) 2003, 2007, 2009 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -21,7 +21,24 @@ /* Cause compilation failure if original declaration has wrong type. */ ssize_t readlink (const char *, char *, size_t);]])], [gl_cv_decl_readlink_works=yes], [gl_cv_decl_readlink_works=no])]) - if test "$gl_cv_decl_readlink_works" != yes; then + AC_CACHE_CHECK([whether readlink handles trailing slash correctly], + [gl_cv_func_readlink_works], + [# We have readlink, so assume ln -s works. + ln -s conftest.no-such conftest.link + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[#include <unistd.h> +]], [[char buf[20]; + return readlink ("conftest.link/", buf, sizeof buf) != -1;]])], + [gl_cv_func_readlink_works=yes], [gl_cv_func_readlink_works=no], + [gl_cv_func_readlink_works="guessing no"]) + rm -f conftest.link]) + if test "$gl_cv_func_readlink_works" != yes; then + AC_DEFINE([READLINK_TRAILING_SLASH_BUG], [1], [Define to 1 if readlink + fails to recognize a trailing slash.]) + REPLACE_READLINK=1 + AC_LIBOBJ([readlink]) + elif test "$gl_cv_decl_readlink_works" != yes; then REPLACE_READLINK=1 AC_LIBOBJ([readlink]) fi
new file mode 100644 --- /dev/null +++ b/modules/readlink-tests @@ -0,0 +1,11 @@ +Files: +tests/test-readlink.c + +Depends-on: +symlink + +configure.ac: + +Makefile.am: +TESTS += test-readlink +check_PROGRAMS += test-readlink
new file mode 100644 --- /dev/null +++ b/tests/test-readlink.c @@ -0,0 +1,118 @@ +/* Tests of readlink. + Copyright (C) 2009 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Written by Eric Blake <ebb9@byu.net>, 2009. */ + +#include <config.h> + +#include <unistd.h> + +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +#define BASE "test-readlink.t" + +int +main () +{ + char buf[80]; + + /* Remove any leftovers from a previous partial run. */ + ASSERT (system ("rm -rf " BASE "*") == 0); + + /* Sanity checks of failures. Mingw lacks symlink, but readlink can + still distinguish between various errors. */ + memset (buf, 0xff, sizeof buf); + errno = 0; + ASSERT (readlink ("no_such", buf, sizeof buf) == -1); + ASSERT (errno == ENOENT); + errno = 0; + ASSERT (readlink ("no_such/", buf, sizeof buf) == -1); + ASSERT (errno == ENOENT); + errno = 0; + ASSERT (readlink ("", buf, sizeof buf) == -1); + ASSERT (errno == ENOENT); + errno = 0; + ASSERT (readlink (".", buf, sizeof buf) == -1); + ASSERT (errno == EINVAL); + errno = 0; + ASSERT (readlink ("./", buf, sizeof buf) == -1); + ASSERT (errno == EINVAL); + ASSERT (close (creat (BASE "file", 0600)) == 0); + errno = 0; + ASSERT (readlink (BASE "file", buf, sizeof buf) == -1); + ASSERT (errno == EINVAL); + errno = 0; + ASSERT (readlink (BASE "file/", buf, sizeof buf) == -1); + ASSERT (errno == ENOTDIR); + ASSERT (unlink (BASE "file") == 0); + + /* Now test actual symlinks. */ + if (symlink (BASE "dir", BASE "link")) + { + fputs ("skipping test: symlinks not supported on this filesystem\n", + stderr); + return 77; + } + ASSERT (mkdir (BASE "dir", 0700) == 0); + errno = 0; + ASSERT (readlink (BASE "link/", buf, sizeof buf) == -1); + ASSERT (errno == EINVAL); + { + /* Up till now, no readlink has been successful, so buf should be + unchanged. */ + int i; + for (i = 0; i < sizeof buf; i++) + ASSERT (buf[i] == (char) 0xff); + } + { + size_t len = strlen (BASE "dir"); + /* When passing too small of a buffer, expect the truncated + length. However, a size of 0 is not portable enough to + test. */ + ASSERT (readlink (BASE "link", buf, 1) == 1); + ASSERT (buf[0] == BASE[0]); + ASSERT (buf[1] == (char) 0xff); + ASSERT (readlink (BASE "link", buf, len) == len); + ASSERT (strncmp (buf, BASE "dir", len) == 0); + ASSERT (buf[len] == (char) 0xff); + ASSERT (readlink (BASE "link", buf, sizeof buf) == len); + ASSERT (strncmp (buf, BASE "dir", len) == 0); + /* POSIX says rest of buf is unspecified; but in practice, it is + either left alone, or NUL-terminated. */ + ASSERT (buf[len] == '\0' || buf[len] == (char) 0xff); + } + ASSERT (rmdir (BASE "dir") == 0); + ASSERT (unlink (BASE "link") == 0); + + return 0; +}