Mercurial > hg > octave-nkf > gnulib-hg
changeset 12138:e86f65584aae
areadlinkat-with-size: new module
* modules/areadlinkat-with-size: New module.
* lib/areadlinkat-with-size.c (areadlinkat_with_size): New file.
* lib/areadlink.h (areadlinkat): Declare it.
* MODULES.html.sh (File system functions): Mention it.
* modules/areadlinkat-with-size-tests: New test.
* tests/test-areadlinkat-with-size.c: New file.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Wed, 07 Oct 2009 10:52:15 -0600 |
parents | 581348c45fcc |
children | 66ce99c59da1 |
files | ChangeLog MODULES.html.sh lib/areadlink.h lib/areadlinkat-with-size.c modules/areadlinkat-with-size modules/areadlinkat-with-size-tests tests/test-areadlinkat-with-size.c |
diffstat | 7 files changed, 284 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2009-10-07 Eric Blake <ebb9@byu.net> + areadlinkat-with-size: new module + * modules/areadlinkat-with-size: New module. + * lib/areadlinkat-with-size.c (areadlinkat_with_size): New file. + * lib/areadlink.h (areadlinkat): Declare it. + * MODULES.html.sh (File system functions): Mention it. + * modules/areadlinkat-with-size-tests: New test. + * tests/test-areadlinkat-with-size.c: New file. + xreadlinkat: new module * modules/xreadlinkat: New module. * lib/xreadlinkat.c (xreadlinkat): New file.
--- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -2449,6 +2449,7 @@ func_module areadlink func_module areadlink-with-size func_module areadlinkat + func_module areadlinkat-with-size func_module backupfile func_module canonicalize func_module canonicalize-lgpl
--- a/lib/areadlink.h +++ b/lib/areadlink.h @@ -26,3 +26,8 @@ #if GNULIB_AREADLINKAT extern char *areadlinkat (int fd, char const *filename); #endif + +#if GNULIB_AREADLINKAT_WITH_SIZE +extern char *areadlinkat_with_size (int fd, char const *filename, + size_t size_hint); +#endif
new file mode 100644 --- /dev/null +++ b/lib/areadlinkat-with-size.c @@ -0,0 +1,133 @@ +/* readlinkat wrapper to return the link name in malloc'd storage. + Unlike xreadlinkat, only call exit on failure to change directory. + + Copyright (C) 2001, 2003-2007, 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 Jim Meyering <jim@meyering.net> + and Eric Blake <ebb9@byu.net>. */ + +#include <config.h> + +#include "areadlink.h" + +#include <stdio.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> + +#if HAVE_READLINKAT + +# ifndef SSIZE_MAX +# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2)) +# endif + +/* SYMLINK_MAX is used only for an initial memory-allocation sanity + check, so it's OK to guess too small on hosts where there is no + arbitrary limit to symbolic link length. */ +# ifndef SYMLINK_MAX +# define SYMLINK_MAX 1024 +# endif + +# define MAXSIZE (SIZE_MAX < SSIZE_MAX ? SIZE_MAX : SSIZE_MAX) + +/* Call readlinkat to get the symbolic link value of FILE, relative to FD. + SIZE is a hint as to how long the link is expected to be; + typically it is taken from st_size. It need not be correct. + Return a pointer to that NUL-terminated string in malloc'd storage. + If readlinkat fails, malloc fails, or if the link value is longer + than SSIZE_MAX, return NULL (caller may use errno to diagnose). + However, failure to change directory during readlinkat will issue + a diagnostic and exit. */ + +char * +areadlinkat_with_size (int fd, char const *file, size_t size) +{ + /* Some buggy file systems report garbage in st_size. Defend + against them by ignoring outlandish st_size values in the initial + memory allocation. */ + size_t symlink_max = SYMLINK_MAX; + size_t INITIAL_LIMIT_BOUND = 8 * 1024; + size_t initial_limit = (symlink_max < INITIAL_LIMIT_BOUND + ? symlink_max + 1 + : INITIAL_LIMIT_BOUND); + + /* The initial buffer size for the link value. */ + size_t buf_size = size < initial_limit ? size + 1 : initial_limit; + + while (1) + { + ssize_t r; + size_t link_length; + char *buffer = malloc (buf_size); + + if (buffer == NULL) + return NULL; + r = readlinkat (fd, file, buffer, buf_size); + link_length = r; + + /* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1 + with errno == ERANGE if the buffer is too small. */ + if (r < 0 && errno != ERANGE) + { + int saved_errno = errno; + free (buffer); + errno = saved_errno; + return NULL; + } + + if (link_length < buf_size) + { + buffer[link_length] = 0; + return buffer; + } + + free (buffer); + if (buf_size <= MAXSIZE / 2) + buf_size *= 2; + else if (buf_size < MAXSIZE) + buf_size = MAXSIZE; + else + { + errno = ENOMEM; + return NULL; + } + } +} + +#else /* !HAVE_READLINKAT */ + + +/* It is more efficient to change directories only once and call + areadlink_with_size, rather than repeatedly call the replacement + readlinkat. */ + +# define AT_FUNC_NAME areadlinkat_with_size +# define AT_FUNC_F1 areadlink_with_size +# define AT_FUNC_POST_FILE_PARAM_DECLS , size_t size +# define AT_FUNC_POST_FILE_ARGS , size +# define AT_FUNC_RESULT char * +# define AT_FUNC_FAIL NULL +# include "at-func.c" +# undef AT_FUNC_NAME +# undef AT_FUNC_F1 +# undef AT_FUNC_POST_FILE_PARAM_DECLS +# undef AT_FUNC_POST_FILE_ARGS +# undef AT_FUNC_RESULT +# undef AT_FUNC_FAIL + +#endif /* !HAVE_READLINKAT */
new file mode 100644 --- /dev/null +++ b/modules/areadlinkat-with-size @@ -0,0 +1,26 @@ +Description: +Read a symbolic link, without size limitations, relative to fd. + +Files: +lib/areadlink.h +lib/areadlinkat-with-size.c + +Depends-on: +areadlink-with-size +stdint +symlinkat + +configure.ac: +gl_MODULE_INDICATOR([areadlinkat-with-size]) + +Makefile.am: +lib_SOURCES += areadlinkat-with-size.c + +Include: +"areadlink.h" + +License: +GPL + +Maintainer: +Jim Meyering, Eric Blake
new file mode 100644 --- /dev/null +++ b/modules/areadlinkat-with-size-tests @@ -0,0 +1,13 @@ +Files: +tests/test-areadlink.h +tests/test-areadlinkat-with-size.c + +Depends-on: +stdbool + +configure.ac: + +Makefile.am: +TESTS += test-areadlinkat-with-size +check_PROGRAMS += test-areadlinkat-with-size +test_areadlinkat_with_size_LDADD = $(LDADD) @LIBINTL@
new file mode 100644 --- /dev/null +++ b/tests/test-areadlinkat-with-size.c @@ -0,0 +1,98 @@ +/* Tests of areadlinkat_with_size. + 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 "areadlink.h" + +#include <fcntl.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +#define BASE "test-areadlinkat-with-size.t" + +#include "test-areadlink.h" + +static int dfd = AT_FDCWD; + +/* Wrapper for testing areadlinkat_with_size. */ +static char * +do_areadlinkat_with_size (char const *name, size_t size) +{ + return areadlinkat_with_size (dfd, name, size); +} + +int +main () +{ + int result; + + /* Remove any leftovers from a previous partial run. */ + ASSERT (system ("rm -rf " BASE "*") == 0); + + /* Basic tests. */ + result = test_areadlink (do_areadlinkat_with_size, false); + dfd = open (".", O_RDONLY); + ASSERT (0 <= dfd); + ASSERT (test_areadlink (do_areadlinkat_with_size, false) == result); + + /* Relative tests. */ + if (result == 77) + fputs ("skipping test: symlinks not supported on this filesystem\n", + stderr); + else + { + char *buf; + ASSERT (symlink ("nowhere", BASE "link") == 0); + ASSERT (mkdir (BASE "dir", 0700) == 0); + ASSERT (chdir (BASE "dir") == 0); + buf = areadlinkat_with_size (dfd, BASE "link", strlen (BASE "link")); + ASSERT (buf); + ASSERT (strcmp (buf, "nowhere") == 0); + free (buf); + errno = 0; + ASSERT (areadlinkat_with_size (-1, BASE "link", 1) == NULL); + ASSERT (errno == EBADF); + errno = 0; + ASSERT (areadlinkat_with_size (AT_FDCWD, BASE "link", 1) == NULL); + ASSERT (errno == ENOENT); + ASSERT (chdir ("..") == 0); + ASSERT (rmdir (BASE "dir") == 0); + ASSERT (unlink (BASE "link") == 0); + } + + ASSERT (close (dfd) == 0); + return result; +}