mirror of
https://https.git.savannah.gnu.org/git/gnulib.git
synced 2026-09-01 02:34:55 +00:00
renameat2: new module
Although the Linux syscall renameat2 is not in glibc (yet?), it is useful to have access to its RENAME_NOREPLACE flag. * MODULES.html.sh (func_all_modules): Add renameat2. * lib/renameat2.c, lib/renameat2.h, modules/renameat2: * modules/renameat2-tests, tests/test-renameat2.c: New files. * lib/renameat.c (renameat): Move most of the implementation to renameat2, and just call renameat2. * modules/renameat (Files): Remove lib/at-func2.c. (Depends-on): Depend only on renameat2. (Include): Remove <fcntl.h>. * modules/renameat-tests (test_renameat_LDADD): Add $(LIB_EACCESS), since renameat (via renameat2) might use faccessat.
This commit is contained in:
@@ -1,3 +1,19 @@
|
||||
2017-07-27 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
renameat2: new module
|
||||
Although the Linux syscall renameat2 is not in glibc (yet?), it is
|
||||
useful to have access to its RENAME_NOREPLACE flag.
|
||||
* MODULES.html.sh (func_all_modules): Add renameat2.
|
||||
* lib/renameat2.c, lib/renameat2.h, modules/renameat2:
|
||||
* modules/renameat2-tests, tests/test-renameat2.c: New files.
|
||||
* lib/renameat.c (renameat): Move most of the implementation
|
||||
to renameat2, and just call renameat2.
|
||||
* modules/renameat (Files): Remove lib/at-func2.c.
|
||||
(Depends-on): Depend only on renameat2.
|
||||
(Include): Remove <fcntl.h>.
|
||||
* modules/renameat-tests (test_renameat_LDADD): Add $(LIB_EACCESS),
|
||||
since renameat (via renameat2) might use faccessat.
|
||||
|
||||
2017-07-27 Erik Skultety <eskultet@redhat.com> (tiny change)
|
||||
|
||||
vc-list-files: Adjust the script to support git worktrees
|
||||
|
||||
@@ -2677,6 +2677,7 @@ func_all_modules ()
|
||||
func_module qset-acl
|
||||
func_module read-file
|
||||
func_module readlinkat
|
||||
func_module renameat2
|
||||
func_module same
|
||||
func_module save-cwd
|
||||
func_module savedir
|
||||
|
||||
+4
-136
@@ -1,5 +1,5 @@
|
||||
/* Rename a file relative to open directories.
|
||||
Copyright (C) 2009-2017 Free Software Foundation, Inc.
|
||||
Copyright 2017 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
|
||||
@@ -14,144 +14,12 @@
|
||||
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>
|
||||
|
||||
#if HAVE_RENAMEAT
|
||||
|
||||
# include <errno.h>
|
||||
# include <stdbool.h>
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
# include <sys/stat.h>
|
||||
|
||||
# include "dirname.h"
|
||||
# include "openat.h"
|
||||
|
||||
# undef renameat
|
||||
|
||||
/* renameat does not honor trailing / on Solaris 10. Solve it in a
|
||||
similar manner to rename. No need to worry about bugs not present
|
||||
on Solaris, since all other systems either lack renameat or honor
|
||||
trailing slash correctly. */
|
||||
#include "renameat2.h"
|
||||
|
||||
int
|
||||
rpl_renameat (int fd1, char const *src, int fd2, char const *dst)
|
||||
renameat (int fd1, char const *src, int fd2, char const *dst)
|
||||
{
|
||||
size_t src_len = strlen (src);
|
||||
size_t dst_len = strlen (dst);
|
||||
char *src_temp = (char *) src;
|
||||
char *dst_temp = (char *) dst;
|
||||
bool src_slash;
|
||||
bool dst_slash;
|
||||
int ret_val = -1;
|
||||
int rename_errno = ENOTDIR;
|
||||
struct stat src_st;
|
||||
struct stat dst_st;
|
||||
|
||||
/* Let strace see any ENOENT failure. */
|
||||
if (!src_len || !dst_len)
|
||||
return renameat (fd1, src, fd2, dst);
|
||||
|
||||
src_slash = src[src_len - 1] == '/';
|
||||
dst_slash = dst[dst_len - 1] == '/';
|
||||
if (!src_slash && !dst_slash)
|
||||
return renameat (fd1, src, fd2, dst);
|
||||
|
||||
/* Presence of a trailing slash requires directory semantics. If
|
||||
the source does not exist, or if the destination cannot be turned
|
||||
into a directory, give up now. Otherwise, strip trailing slashes
|
||||
before calling rename. */
|
||||
if (lstatat (fd1, src, &src_st))
|
||||
return -1;
|
||||
if (lstatat (fd2, dst, &dst_st))
|
||||
{
|
||||
if (errno != ENOENT || !S_ISDIR (src_st.st_mode))
|
||||
return -1;
|
||||
}
|
||||
else if (!S_ISDIR (dst_st.st_mode))
|
||||
{
|
||||
errno = ENOTDIR;
|
||||
return -1;
|
||||
}
|
||||
else if (!S_ISDIR (src_st.st_mode))
|
||||
{
|
||||
errno = EISDIR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
# if RENAME_TRAILING_SLASH_SOURCE_BUG
|
||||
/* See the lengthy comment in rename.c why Solaris 9 is forced to
|
||||
GNU behavior, while Solaris 10 is left with POSIX behavior,
|
||||
regarding symlinks with trailing slash. */
|
||||
if (src_slash)
|
||||
{
|
||||
src_temp = strdup (src);
|
||||
if (!src_temp)
|
||||
{
|
||||
/* Rather than rely on strdup-posix, we set errno ourselves. */
|
||||
rename_errno = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
strip_trailing_slashes (src_temp);
|
||||
if (lstatat (fd1, src_temp, &src_st))
|
||||
{
|
||||
rename_errno = errno;
|
||||
goto out;
|
||||
}
|
||||
if (S_ISLNK (src_st.st_mode))
|
||||
goto out;
|
||||
}
|
||||
if (dst_slash)
|
||||
{
|
||||
dst_temp = strdup (dst);
|
||||
if (!dst_temp)
|
||||
{
|
||||
rename_errno = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
strip_trailing_slashes (dst_temp);
|
||||
if (lstatat (fd2, dst_temp, &dst_st))
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
rename_errno = errno;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if (S_ISLNK (dst_st.st_mode))
|
||||
goto out;
|
||||
}
|
||||
# endif /* RENAME_TRAILING_SLASH_SOURCE_BUG */
|
||||
|
||||
ret_val = renameat (fd1, src_temp, fd2, dst_temp);
|
||||
rename_errno = errno;
|
||||
out:
|
||||
if (src_temp != src)
|
||||
free (src_temp);
|
||||
if (dst_temp != dst)
|
||||
free (dst_temp);
|
||||
errno = rename_errno;
|
||||
return ret_val;
|
||||
return renameat2 (fd1, src, fd2, dst, 0);
|
||||
}
|
||||
|
||||
#else /* !HAVE_RENAMEAT */
|
||||
|
||||
# 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);
|
||||
}
|
||||
|
||||
#endif /* !HAVE_RENAMEAT */
|
||||
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
/* Rename a file relative to open directories.
|
||||
Copyright (C) 2009-2017 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 and Paul Eggert */
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "renameat2.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __linux__
|
||||
# include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
static int
|
||||
errno_fail (int e)
|
||||
{
|
||||
errno = e;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if HAVE_RENAMEAT
|
||||
|
||||
# include <stdbool.h>
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
# include <sys/stat.h>
|
||||
|
||||
# include "dirname.h"
|
||||
# include "openat.h"
|
||||
|
||||
#else
|
||||
# include "openat-priv.h"
|
||||
|
||||
static int
|
||||
rename_noreplace (char const *src, char const *dst)
|
||||
{
|
||||
/* This has a race between the call to faccessat and the call to rename. */
|
||||
int r = faccessat (AT_FDCWD, dst, F_OK, AT_EACCESS | AT_SYMLINK_NOFOLLOW);
|
||||
reurn (r == 0 ? errno_fail (EEXIST)
|
||||
: errno == ENOENT ? rename (src, dst)
|
||||
: r);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* 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.
|
||||
|
||||
Obey FLAGS when doing the renaming. If FLAGS is zero, this
|
||||
function is equivalent to renameat (FD1, SRC, FD2, DST). */
|
||||
|
||||
int
|
||||
renameat2 (int fd1, char const *src, int fd2, char const *dst,
|
||||
unsigned int flags)
|
||||
{
|
||||
#ifdef SYS_renameat2
|
||||
int r = syscall (SYS_renameat2, fd1, src, fd2, dst, flags);
|
||||
if (! (r < 0 && errno == ENOSYS))
|
||||
return r;
|
||||
#endif
|
||||
|
||||
#if HAVE_RENAMEAT
|
||||
{
|
||||
size_t src_len;
|
||||
size_t dst_len;
|
||||
char *src_temp = (char *) src;
|
||||
char *dst_temp = (char *) dst;
|
||||
bool src_slash;
|
||||
bool dst_slash;
|
||||
int ret_val;
|
||||
int rename_errno = ENOTDIR;
|
||||
struct stat src_st;
|
||||
struct stat dst_st;
|
||||
|
||||
if (flags != 0)
|
||||
{
|
||||
int r;
|
||||
/* RENAME_NOREPLACE is the only flag currently supported. */
|
||||
if (flags & ~RENAME_NOREPLACE)
|
||||
return errno_fail (ENOTSUP);
|
||||
/* This has a race between the call to faccessat and the
|
||||
call to renameat. */
|
||||
r = faccessat (fd2, dst, F_OK, AT_EACCESS | AT_SYMLINK_NOFOLLOW);
|
||||
if (r == 0)
|
||||
return errno_fail (EEXIST);
|
||||
if (errno != ENOENT)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Let strace see any ENOENT failure. */
|
||||
src_len = strlen (src);
|
||||
dst_len = strlen (dst);
|
||||
if (!src_len || !dst_len)
|
||||
return renameat (fd1, src, fd2, dst);
|
||||
|
||||
src_slash = src[src_len - 1] == '/';
|
||||
dst_slash = dst[dst_len - 1] == '/';
|
||||
if (!src_slash && !dst_slash)
|
||||
return renameat (fd1, src, fd2, dst);
|
||||
|
||||
/* Presence of a trailing slash requires directory semantics. If
|
||||
the source does not exist, or if the destination cannot be turned
|
||||
into a directory, give up now. Otherwise, strip trailing slashes
|
||||
before calling rename. */
|
||||
if (lstatat (fd1, src, &src_st))
|
||||
return -1;
|
||||
if (lstatat (fd2, dst, &dst_st))
|
||||
{
|
||||
if (errno != ENOENT || !S_ISDIR (src_st.st_mode))
|
||||
return -1;
|
||||
}
|
||||
else if (!S_ISDIR (dst_st.st_mode))
|
||||
return errno_fail (ENOTDIR);
|
||||
else if (!S_ISDIR (src_st.st_mode))
|
||||
return errno_fail (EISDIR);
|
||||
|
||||
# if RENAME_TRAILING_SLASH_SOURCE_BUG
|
||||
/* See the lengthy comment in rename.c why Solaris 9 is forced to
|
||||
GNU behavior, while Solaris 10 is left with POSIX behavior,
|
||||
regarding symlinks with trailing slash. */
|
||||
if (src_slash)
|
||||
{
|
||||
src_temp = strdup (src);
|
||||
if (!src_temp)
|
||||
{
|
||||
/* Rather than rely on strdup-posix, we set errno ourselves. */
|
||||
rename_errno = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
strip_trailing_slashes (src_temp);
|
||||
if (lstatat (fd1, src_temp, &src_st))
|
||||
{
|
||||
rename_errno = errno;
|
||||
goto out;
|
||||
}
|
||||
if (S_ISLNK (src_st.st_mode))
|
||||
goto out;
|
||||
}
|
||||
if (dst_slash)
|
||||
{
|
||||
dst_temp = strdup (dst);
|
||||
if (!dst_temp)
|
||||
{
|
||||
rename_errno = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
strip_trailing_slashes (dst_temp);
|
||||
if (lstatat (fd2, dst_temp, &dst_st))
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
{
|
||||
rename_errno = errno;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else if (S_ISLNK (dst_st.st_mode))
|
||||
goto out;
|
||||
}
|
||||
# endif /* RENAME_TRAILING_SLASH_SOURCE_BUG */
|
||||
|
||||
/* renameat does not honor trailing / on Solaris 10. Solve it in a
|
||||
similar manner to rename. No need to worry about bugs not present
|
||||
on Solaris, since all other systems either lack renameat or honor
|
||||
trailing slash correctly. */
|
||||
|
||||
ret_val = renameat (fd1, src_temp, fd2, dst_temp);
|
||||
rename_errno = errno;
|
||||
out:
|
||||
if (src_temp != src)
|
||||
free (src_temp);
|
||||
if (dst_temp != dst)
|
||||
free (dst_temp);
|
||||
errno = rename_errno;
|
||||
return ret_val;
|
||||
}
|
||||
#else /* !HAVE_RENAMEAT */
|
||||
|
||||
/* RENAME_NOREPLACE is the only flag currently supported. */
|
||||
if (flags & ~RENAME_NOREPLACE)
|
||||
return errno_fail (ENOTSUP);
|
||||
return at_func2 (fd1, file1, fd2, file2,
|
||||
flags ? rename_noreplace : rename);
|
||||
|
||||
#endif /* !HAVE_RENAMEAT */
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/* Rename a file relative to open directories.
|
||||
Copyright 2017 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 Paul Eggert */
|
||||
|
||||
/* Get RENAME_* macros from linux/fs.h if present, otherwise supply
|
||||
the traditional Linux values. */
|
||||
#ifdef __linux__
|
||||
# include <linux/fs.h>
|
||||
#endif
|
||||
#ifndef RENAME_NOREPLACE
|
||||
# define RENAME_NOREPLACE (1 << 0)
|
||||
# define RENAME_EXCHANGE (1 << 1)
|
||||
# define RENAME_WHITEOUT (1 << 2)
|
||||
#endif
|
||||
|
||||
extern int renameat2 (int, char const *, int, char const *, unsigned int);
|
||||
+1
-16
@@ -2,25 +2,11 @@ Description:
|
||||
renameat() function: rename a file, relative to two directories
|
||||
|
||||
Files:
|
||||
lib/at-func2.c
|
||||
lib/renameat.c
|
||||
m4/renameat.m4
|
||||
|
||||
Depends-on:
|
||||
stdio
|
||||
extensions
|
||||
fcntl-h
|
||||
filenamecat-lgpl [test $HAVE_RENAMEAT = 0 || test $REPLACE_RENAMEAT = 1]
|
||||
openat-h [test $HAVE_RENAMEAT = 0 || test $REPLACE_RENAMEAT = 1]
|
||||
statat [test $REPLACE_RENAMEAT = 1]
|
||||
stdbool [test $REPLACE_RENAMEAT = 1]
|
||||
at-internal [test $HAVE_RENAMEAT = 0]
|
||||
dosname [test $HAVE_RENAMEAT = 0]
|
||||
getcwd-lgpl [test $HAVE_RENAMEAT = 0]
|
||||
openat-die [test $HAVE_RENAMEAT = 0]
|
||||
rename [test $HAVE_RENAMEAT = 0]
|
||||
same-inode [test $HAVE_RENAMEAT = 0]
|
||||
save-cwd [test $HAVE_RENAMEAT = 0]
|
||||
renameat2 [test $HAVE_RENAMEAT = 0 || test $REPLACE_RENAMEAT = 1]
|
||||
|
||||
configure.ac:
|
||||
gl_FUNC_RENAMEAT
|
||||
@@ -35,7 +21,6 @@ gl_STDIO_MODULE_INDICATOR([renameat])
|
||||
Makefile.am:
|
||||
|
||||
Include:
|
||||
<fcntl.h>
|
||||
<stdio.h>
|
||||
|
||||
License:
|
||||
|
||||
@@ -17,4 +17,4 @@ configure.ac:
|
||||
Makefile.am:
|
||||
TESTS += test-renameat
|
||||
check_PROGRAMS += test-renameat
|
||||
test_renameat_LDADD = $(LDADD) @LIBINTL@
|
||||
test_renameat_LDADD = $(LDADD) $(LIB_EACCESS) @LIBINTL@
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
Description:
|
||||
renameat2() function: rename a file, relative to two directories
|
||||
|
||||
Files:
|
||||
lib/at-func2.c
|
||||
lib/renameat2.c
|
||||
lib/renameat2.h
|
||||
m4/renameat.m4
|
||||
|
||||
Depends-on:
|
||||
stdio
|
||||
extensions
|
||||
faccessat
|
||||
fcntl-h
|
||||
filenamecat-lgpl [test $HAVE_RENAMEAT = 0 || test $REPLACE_RENAMEAT = 1]
|
||||
openat-h [test $HAVE_RENAMEAT = 0 || test $REPLACE_RENAMEAT = 1]
|
||||
statat [test $REPLACE_RENAMEAT = 1]
|
||||
stdbool [test $REPLACE_RENAMEAT = 1]
|
||||
at-internal [test $HAVE_RENAMEAT = 0]
|
||||
dosname [test $HAVE_RENAMEAT = 0]
|
||||
getcwd-lgpl [test $HAVE_RENAMEAT = 0]
|
||||
openat-die [test $HAVE_RENAMEAT = 0]
|
||||
rename [test $HAVE_RENAMEAT = 0]
|
||||
same-inode [test $HAVE_RENAMEAT = 0]
|
||||
save-cwd [test $HAVE_RENAMEAT = 0]
|
||||
|
||||
configure.ac:
|
||||
gl_FUNC_RENAMEAT
|
||||
if test $HAVE_RENAMEAT = 0; then
|
||||
AC_LIBOBJ([at-func2])
|
||||
fi
|
||||
|
||||
Makefile.am:
|
||||
lib_SOURCES += renameat2.c
|
||||
|
||||
Include:
|
||||
"renameat2.h"
|
||||
|
||||
License:
|
||||
GPL
|
||||
|
||||
Maintainer:
|
||||
Jim Meyering, Eric Blake
|
||||
@@ -0,0 +1,20 @@
|
||||
Files:
|
||||
tests/test-rename.h
|
||||
tests/test-renameat2.c
|
||||
tests/signature.h
|
||||
tests/macros.h
|
||||
|
||||
Depends-on:
|
||||
ignore-value
|
||||
filenamecat
|
||||
getcwd-lgpl
|
||||
opendir
|
||||
readdir
|
||||
closedir
|
||||
|
||||
configure.ac:
|
||||
|
||||
Makefile.am:
|
||||
TESTS += test-renameat2
|
||||
check_PROGRAMS += test-renameat2
|
||||
test_renameat2_LDADD = $(LDADD) $(LIB_EACCESS) @LIBINTL@
|
||||
@@ -0,0 +1,209 @@
|
||||
/* Test renameat2.
|
||||
Copyright (C) 2009-2017 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 <renameat2.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "signature.h"
|
||||
SIGNATURE_CHECK (renameat2, int,
|
||||
(int, char const *, int, char const *, unsigned int));
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "filenamecat.h"
|
||||
#include "ignore-value.h"
|
||||
#include "macros.h"
|
||||
|
||||
#define BASE "test-renameat2.t"
|
||||
|
||||
#include "test-rename.h"
|
||||
|
||||
static int dfd1 = AT_FDCWD;
|
||||
static int dfd2 = AT_FDCWD;
|
||||
|
||||
/* Wrapper to test renameat2 like rename. */
|
||||
static int
|
||||
do_rename (char const *name1, char const *name2)
|
||||
{
|
||||
return renameat2 (dfd1, name1, dfd2, name2, 0);
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
int i;
|
||||
int dfd;
|
||||
char *cwd;
|
||||
int result;
|
||||
|
||||
/* Clean up any trash from prior testsuite runs. */
|
||||
ignore_value (system ("rm -rf " BASE "*"));
|
||||
|
||||
/* Test behaviour for invalid file descriptors. */
|
||||
{
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (-1, "foo", AT_FDCWD, "bar", 0) == -1);
|
||||
ASSERT (errno == EBADF);
|
||||
}
|
||||
{
|
||||
close (99);
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (99, "foo", AT_FDCWD, "bar", 0) == -1);
|
||||
ASSERT (errno == EBADF);
|
||||
}
|
||||
ASSERT (close (creat (BASE "oo", 0600)) == 0);
|
||||
{
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (AT_FDCWD, BASE "oo", -1, "bar", 0) == -1);
|
||||
ASSERT (errno == EBADF);
|
||||
}
|
||||
{
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (AT_FDCWD, BASE "oo", 99, "bar", 0) == -1);
|
||||
ASSERT (errno == EBADF);
|
||||
}
|
||||
ASSERT (unlink (BASE "oo") == 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 = getcwd (NULL, 0);
|
||||
ASSERT (cwd);
|
||||
|
||||
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 (renameat2 (fd1, file1, fd2, file2, 0) == 0);
|
||||
free (file1);
|
||||
free (file2);
|
||||
}
|
||||
dfd2 = open ("..", O_RDONLY);
|
||||
ASSERT (0 <= dfd2);
|
||||
ASSERT (renameat2 (dfd, "../" BASE "16", dfd2, BASE "17", 0) == 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 (renameat2 (dfd, BASE "sub1", dfd, BASE "sub2", 0) == -1);
|
||||
ASSERT (errno == EEXIST || errno == ENOTEMPTY);
|
||||
ASSERT (unlink (BASE "sub2/file") == 0);
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (dfd, BASE "sub2", dfd, BASE "sub1/.", 0) == -1);
|
||||
ASSERT (errno == EINVAL || errno == EISDIR || errno == EBUSY
|
||||
|| errno == ENOTEMPTY || errno == EEXIST);
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (dfd, BASE "sub2/.", dfd, BASE "sub1", 0) == -1);
|
||||
ASSERT (errno == EINVAL || errno == EBUSY || errno == EEXIST);
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (dfd, BASE "17", dfd, BASE "sub1", 0) == -1);
|
||||
ASSERT (errno == EISDIR);
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (dfd, BASE "nosuch", dfd, BASE "18", 0) == -1);
|
||||
ASSERT (errno == ENOENT);
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (dfd, "", dfd, BASE "17", 0) == -1);
|
||||
ASSERT (errno == ENOENT);
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (dfd, BASE "17", dfd, "", 0) == -1);
|
||||
ASSERT (errno == ENOENT);
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (dfd, BASE "sub2", dfd, BASE "17", 0) == -1);
|
||||
ASSERT (errno == ENOTDIR);
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (dfd, BASE "17/", dfd, BASE "18", 0) == -1);
|
||||
ASSERT (errno == ENOTDIR);
|
||||
errno = 0;
|
||||
ASSERT (renameat2 (dfd, BASE "17", dfd, BASE "18/", 0) == -1);
|
||||
ASSERT (errno == ENOTDIR || errno == ENOENT);
|
||||
|
||||
/* Finally, make sure we cannot overwrite existing files. */
|
||||
ASSERT (close (creat (BASE "sub2/file", 0600)) == 0);
|
||||
errno = 0;
|
||||
ASSERT ((renameat2 (dfd, BASE "sub2", dfd, BASE "sub1", RENAME_NOREPLACE)
|
||||
== -1)
|
||||
&& errno == EEXIST);
|
||||
ASSERT ((renameat2 (dfd, BASE "sub2/file", dfd, BASE "17", RENAME_NOREPLACE)
|
||||
== -1)
|
||||
&& errno == EEXIST);
|
||||
|
||||
/* Cleanup. */
|
||||
ASSERT (close (dfd) == 0);
|
||||
ASSERT (unlink (BASE "sub2/file") == 0);
|
||||
ASSERT (unlink (BASE "17") == 0);
|
||||
ASSERT (rmdir (BASE "sub1") == 0);
|
||||
ASSERT (rmdir (BASE "sub2") == 0);
|
||||
free (cwd);
|
||||
|
||||
if (result)
|
||||
fputs ("skipping test: symlinks not supported on this file system\n",
|
||||
stderr);
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user