Mercurial > hg > octave-lojdl > gnulib-hg
changeset 12099:01c66cf797b5
renameat: new module
Passes on Linux and cygwin 1.7 native renameat, and on systems
lacking renameat, but fails on Solaris 9 and 10 for now.
* modules/renameat: New file.
* lib/renameat.c (renameat): Likewise.
* m4/renameat.m4 (gl_FUNC_RENAMEAT): Likewise.
* m4/stdio_h.m4 (gl_STDIO_H_DEFAULTS): Add witnesses.
* modules/stdio (Makefile.am): Substitute them.
* lib/stdio.in.h (renameat): Declare it.
* MODULES.html.sh (systems lacking POSIX:2008): Mention module.
* doc/posix-functions/renameat.texi (renameat): Likewise.
* modules/renameat-tests: New test.
* tests/test-renameat.c: Likewise.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Mon, 07 Sep 2009 06:45:59 -0600 |
parents | 3d66373d8171 |
children | 8e81b30aa8b7 |
files | ChangeLog MODULES.html.sh doc/posix-functions/renameat.texi lib/renameat.c lib/stdio.in.h m4/renameat.m4 m4/stdio_h.m4 modules/renameat modules/renameat-tests modules/stdio tests/test-renameat.c |
diffstat | 11 files changed, 345 insertions(+), 5 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,17 @@ 2009-10-02 Eric Blake <ebb9@byu.net> + renameat: new module + * modules/renameat: New file. + * lib/renameat.c (renameat): Likewise. + * m4/renameat.m4 (gl_FUNC_RENAMEAT): Likewise. + * m4/stdio_h.m4 (gl_STDIO_H_DEFAULTS): Add witnesses. + * modules/stdio (Makefile.am): Substitute them. + * lib/stdio.in.h (renameat): Declare it. + * MODULES.html.sh (systems lacking POSIX:2008): Mention module. + * doc/posix-functions/renameat.texi (renameat): Likewise. + * modules/renameat-tests: New test. + * tests/test-renameat.c: Likewise. + rename: fix mingw bugs * lib/rename.c (rpl_rename) [W32]: Fix trailing slash and directory overwrite bugs.
--- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -2346,6 +2346,7 @@ func_module nanosleep func_module regex func_module rename + func_module renameat func_module rmdir func_module search func_module sigaction
--- a/doc/posix-functions/renameat.texi +++ b/doc/posix-functions/renameat.texi @@ -4,16 +4,37 @@ POSIX specification: @url{http://www.opengroup.org/onlinepubs/9699919799/functions/renameat.html} -Gnulib module: --- +Gnulib module: renameat Portability problems fixed by Gnulib: @itemize +@item +This function does not reject trailing slashes on non-directories on +some platforms, as in @code{renameat(fd,"file",fd,"new/")}: +Solaris 10. +@item +This function ignores trailing slashes on symlinks on some platforms, +such that @code{renameat(fd,"link/",fd,"new")} corrupts @file{link}: +Solaris 9. +@item +This function is missing on some platforms: +glibc 2.3.6, MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX +5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 8, Cygwin 1.5.x, mingw, +Interix 3.5, BeOS. @end itemize Portability problems not fixed by Gnulib: @itemize @item -This function is missing on some platforms: -glibc 2.3.6, MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX -5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Cygwin 1.5.x, mingw, Interix 3.5, BeOS. +POSIX requires that @code{renameat(fd,"symlink-to-dir/",fd,"dir2")} rename +@file{dir} and leave @file{symlink-to-dir} dangling; likewise, it +requires that @code{renameat(fd,"dir",fd,"dangling/")} rename @file{dir} so +that @file{dangling} is no longer a dangling symlink. This behavior +is counter-intuitive, so on some systems, @code{renameat} fails with +@code{ENOTDIR} if either argument is a symlink with a trailing slash: +glibc, OpenBSD, Cygwin 1.7. +@item +This function will not rename a source that is currently opened +by any process: +mingw. @end itemize
new file mode 100644 --- /dev/null +++ b/lib/renameat.c @@ -0,0 +1,35 @@ +/* Rename a file relative to open directories. + 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 */ + +#include <config.h> + +#include <stdio.h> + +#include "openat-priv.h" + +/* Rename FILE1, in the directory open on descriptor FD1, to FILE2, in + the directory open on descriptor FD2. If possible, do it without + changing the working directory. Otherwise, resort to using + save_cwd/fchdir, then rename/restore_cwd. If either the save_cwd or + the restore_cwd fails, then give a diagnostic and exit nonzero. */ + +int +renameat (int fd1, char const *file1, int fd2, char const *file2) +{ + return at_func2 (fd1, file1, fd2, file2, rename); +}
--- a/lib/stdio.in.h +++ b/lib/stdio.in.h @@ -444,6 +444,18 @@ rename (o, n)) #endif +#if @GNULIB_RENAMEAT@ +# if !@HAVE_RENAMEAT@ +extern int renameat (int fd1, char const *file1, int fd2, char const *file2); +# endif +#elif defined GNULIB_POSIXCHECK +# undef renameat +# define renameat(d1,f1,d2,f2) \ + (GL_LINK_WARNING ("renameat is not portable - " \ + "use gnulib module renameat for portability"), \ + renameat (d1, f1, d2, f2)) +#endif + #if @GNULIB_SNPRINTF@ # if @REPLACE_SNPRINTF@ # define snprintf rpl_snprintf
new file mode 100644 --- /dev/null +++ b/m4/renameat.m4 @@ -0,0 +1,23 @@ +# serial 1 +# See if we need to provide renameat replacement. + +dnl Copyright (C) 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, +dnl with or without modifications, as long as this notice is preserved. + +# Written by Eric Blake. + +AC_DEFUN([gl_FUNC_RENAMEAT], +[ + AC_REQUIRE([gl_FUNC_OPENAT]) + AC_REQUIRE([gl_FUNC_RENAME]) + AC_REQUIRE([gl_STDIO_H_DEFAULTS]) + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_CHECK_FUNCS_ONCE([renameat]) + if test $ac_cv_func_renameat = no; then + HAVE_RENAMEAT=0 + AC_LIBOBJ([renameat]) + AC_LIBOBJ([at-func2]) + fi +])
--- a/m4/stdio_h.m4 +++ b/m4/stdio_h.m4 @@ -1,4 +1,4 @@ -# stdio_h.m4 serial 19 +# stdio_h.m4 serial 20 dnl Copyright (C) 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, @@ -69,6 +69,7 @@ GNULIB_PUTS=0; AC_SUBST([GNULIB_PUTS]) GNULIB_REMOVE=0; AC_SUBST([GNULIB_REMOVE]) GNULIB_RENAME=0; AC_SUBST([GNULIB_RENAME]) + GNULIB_RENAMEAT=0; AC_SUBST([GNULIB_RENAMEAT]) GNULIB_SNPRINTF=0; AC_SUBST([GNULIB_SNPRINTF]) GNULIB_SPRINTF_POSIX=0; AC_SUBST([GNULIB_SPRINTF_POSIX]) GNULIB_STDIO_H_SIGPIPE=0; AC_SUBST([GNULIB_STDIO_H_SIGPIPE]) @@ -90,6 +91,7 @@ HAVE_DPRINTF=1; AC_SUBST([HAVE_DPRINTF]) HAVE_FSEEKO=1; AC_SUBST([HAVE_FSEEKO]) HAVE_FTELLO=1; AC_SUBST([HAVE_FTELLO]) + HAVE_RENAMEAT=1; AC_SUBST([HAVE_RENAMEAT]) HAVE_VASPRINTF=1; AC_SUBST([HAVE_VASPRINTF]) HAVE_VDPRINTF=1; AC_SUBST([HAVE_VDPRINTF]) REPLACE_DPRINTF=0; AC_SUBST([REPLACE_DPRINTF])
new file mode 100644 --- /dev/null +++ b/modules/renameat @@ -0,0 +1,33 @@ +Description: +renameat(): rename a file, relative to two directories + +Files: +lib/at-func2.c +lib/renameat.c +m4/renameat.m4 + +Depends-on: +extensions +fcntl-h +filenamecat +openat +rename +same-inode +stdio +stpcpy + +configure.ac: +gl_FUNC_RENAMEAT +gl_STDIO_MODULE_INDICATOR([renameat]) + +Makefile.am: + +Include: +<fcntl.h> +<stdio.h> + +License: +GPL + +Maintainer: +Jim Meyering, Eric Blake
new file mode 100644 --- /dev/null +++ b/modules/renameat-tests @@ -0,0 +1,14 @@ +Files: +tests/test-rename.h +tests/test-renameat.c + +Depends-on: +progname +xgetcwd + +configure.ac: + +Makefile.am: +TESTS += test-renameat +check_PROGRAMS += test-renameat +test_renameat_LDADD = $(LDADD) @LIBINTL@
--- a/modules/stdio +++ b/modules/stdio @@ -54,6 +54,7 @@ -e 's|@''GNULIB_PUTS''@|$(GNULIB_PUTS)|g' \ -e 's|@''GNULIB_REMOVE''@|$(GNULIB_REMOVE)|g' \ -e 's|@''GNULIB_RENAME''@|$(GNULIB_RENAME)|g' \ + -e 's|@''GNULIB_RENAMEAT''@|$(GNULIB_RENAMEAT)|g' \ -e 's|@''GNULIB_SNPRINTF''@|$(GNULIB_SNPRINTF)|g' \ -e 's|@''GNULIB_SPRINTF_POSIX''@|$(GNULIB_SPRINTF_POSIX)|g' \ -e 's|@''GNULIB_STDIO_H_SIGPIPE''@|$(GNULIB_STDIO_H_SIGPIPE)|g' \ @@ -72,6 +73,7 @@ -e 's|@''HAVE_DECL_SNPRINTF''@|$(HAVE_DECL_SNPRINTF)|g' \ -e 's|@''HAVE_DECL_VSNPRINTF''@|$(HAVE_DECL_VSNPRINTF)|g' \ -e 's|@''HAVE_DPRINTF''@|$(HAVE_DPRINTF)|g' \ + -e 's|@''HAVE_RENAMEAT''@|$(HAVE_RENAMEAT)|g' \ -e 's|@''HAVE_VASPRINTF''@|$(HAVE_VASPRINTF)|g' \ -e 's|@''HAVE_VDPRINTF''@|$(HAVE_VDPRINTF)|g' \ -e 's|@''REPLACE_DPRINTF''@|$(REPLACE_DPRINTF)|g' \
new file mode 100644 --- /dev/null +++ b/tests/test-renameat.c @@ -0,0 +1,185 @@ +/* Tests of renameat. + 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 <stdio.h> + +#include <fcntl.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "filenamecat.h" +#include "xgetcwd.h" + +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +#define BASE "test-renameat.t" + +#include "test-rename.h" + +static int dfd1 = AT_FDCWD; +static int dfd2 = AT_FDCWD; + +/* Wrapper to test renameat like rename. */ +static int +do_rename (char const *name1, char const *name2) +{ + return renameat (dfd1, name1, dfd2, name2); +} + +int +main () +{ + int i; + int dfd; + char *cwd; + int result; + + /* Clean up any trash from prior testsuite runs. */ + ASSERT (system ("rm -rf " BASE "*") == 0); + + /* Test basic rename functionality, using current directory. */ + result = test_rename (do_rename, false); + dfd1 = open (".", O_RDONLY); + ASSERT (0 <= dfd1); + ASSERT (test_rename (do_rename, false) == result); + dfd2 = dfd1; + ASSERT (test_rename (do_rename, false) == result); + dfd1 = AT_FDCWD; + ASSERT (test_rename (do_rename, false) == result); + ASSERT (close (dfd2) == 0); + + /* Create locations to manipulate. */ + ASSERT (mkdir (BASE "sub1", 0700) == 0); + ASSERT (mkdir (BASE "sub2", 0700) == 0); + dfd = creat (BASE "00", 0600); + ASSERT (0 <= dfd); + ASSERT (close (dfd) == 0); + cwd = xgetcwd (); + + dfd = open (BASE "sub1", O_RDONLY); + ASSERT (0 <= dfd); + ASSERT (chdir (BASE "sub2") == 0); + + /* There are 16 possible scenarios, based on whether an fd is + AT_FDCWD or real, and whether a file is absolute or relative. + + To ensure that we test all of the code paths (rather than + triggering early normalization optimizations), we use a loop to + repeatedly rename a file in the parent directory, use an fd open + on subdirectory 1, all while executing in subdirectory 2; all + relative names are thus given with a leading "../". Finally, the + last scenario (two relative paths given, neither one AT_FDCWD) + has two paths, based on whether the two fds are equivalent, so we + do the other variant after the loop. */ + for (i = 0; i < 16; i++) + { + int fd1 = (i & 8) ? dfd : AT_FDCWD; + char *file1 = file_name_concat ((i & 4) ? ".." : cwd, BASE "xx", NULL); + int fd2 = (i & 2) ? dfd : AT_FDCWD; + char *file2 = file_name_concat ((i & 1) ? ".." : cwd, BASE "xx", NULL); + + ASSERT (sprintf (strchr (file1, '\0') - 2, "%02d", i) == 2); + ASSERT (sprintf (strchr (file2, '\0') - 2, "%02d", i + 1) == 2); + ASSERT (renameat (fd1, file1, fd2, file2) == 0); + free (file1); + free (file2); + } + dfd2 = open ("..", O_RDONLY); + ASSERT (0 <= dfd2); + ASSERT (renameat (dfd, "../" BASE "16", dfd2, BASE "17") == 0); + ASSERT (close (dfd2) == 0); + + /* Now we change back to the parent directory, and set dfd to "."; + using dfd in remaining tests will expose any bugs if emulation + via /proc/self/fd doesn't check for empty names. */ + ASSERT (chdir ("..") == 0); + ASSERT (close (dfd) == 0); + dfd = open (".", O_RDONLY); + ASSERT (0 <= dfd); + + ASSERT (close (creat (BASE "sub2/file", 0600)) == 0); + errno = 0; + ASSERT (renameat (dfd, BASE "sub1", dfd, BASE "sub2") == -1); + ASSERT (errno == EEXIST || errno == ENOTEMPTY); + ASSERT (unlink (BASE "sub2/file") == 0); + errno = 0; + ASSERT (renameat (dfd, BASE "sub2", dfd, BASE "sub1/.") == -1); + ASSERT (errno == EINVAL || errno == EISDIR || errno == EBUSY); + errno = 0; + ASSERT (renameat (dfd, BASE "sub2/.", dfd, BASE "sub1") == -1); + ASSERT (errno == EINVAL || errno == EBUSY); + errno = 0; + ASSERT (renameat (dfd, BASE "17", dfd, BASE "sub1") == -1); + ASSERT (errno == EISDIR); + errno = 0; + ASSERT (renameat (dfd, BASE "nosuch", dfd, BASE "18") == -1); + ASSERT (errno == ENOENT); + errno = 0; + ASSERT (renameat (dfd, "", dfd, BASE "17") == -1); + ASSERT (errno == ENOENT); + errno = 0; + ASSERT (renameat (dfd, BASE "17", dfd, "") == -1); + ASSERT (errno == ENOENT); + errno = 0; + ASSERT (renameat (dfd, BASE "sub2", dfd, BASE "17") == -1); + ASSERT (errno == ENOTDIR); + errno = 0; + ASSERT (renameat (dfd, BASE "17/", dfd, BASE "18") == -1); + ASSERT (errno == ENOTDIR); + errno = 0; + ASSERT (renameat (dfd, BASE "17", dfd, BASE "18/") == -1); + ASSERT (errno == ENOTDIR || errno == ENOENT); + + /* Finally, make sure we can overwrite existing files. */ + ASSERT (close (creat (BASE "sub2/file", 0600)) == 0); + errno = 0; + ASSERT (renameat (dfd, BASE "sub2", dfd, BASE "sub1") == 0); + ASSERT (renameat (dfd, BASE "sub1/file", dfd, BASE "17") == 0); + + /* Cleanup. */ + ASSERT (close (dfd) == 0); + errno = 0; + ASSERT (unlink (BASE "sub1/file") == -1); + ASSERT (errno == ENOENT); + ASSERT (unlink (BASE "17") == 0); + ASSERT (rmdir (BASE "sub1") == 0); + errno = 0; + ASSERT (rmdir (BASE "sub2") == -1); + ASSERT (errno == ENOENT); + free (cwd); + + if (result) + fputs ("skipping test: symlinks not supported on this filesystem\n", + stderr); + return result; +}