diff --git a/ChangeLog b/ChangeLog index 8707a1b262..a4a15505ba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,70 @@ +2005-11-11 Ulrich Drepper + + * io/Makefile (routines): Add fstatat, fstatat64, fxstatat, fxstatat64, + fchownat, and unlinkat. + (static-only-routines): Add fstatat and fstatat64. + (tests): Add tst-unlinkat, tst-fstatat, tst-futimesat, tst-renameat, + and tst-fchownat. + * io/Versions [GLIBC_2.4]: Add fchownat, __fxstatat, __fxstatat64, + and unlinkat. + * io/fcntl.h: Define AT_FDCWD, AT_SYMLINK_NOFOLLOW, and AT_REMOVEDIR. + * io/fstatat.c: New file. + * io/fstatat64.c: New file. + * io/sys/stat.h: Declare fstatat, fstatat64, __fxstatat, __fxstatat64 + and define fstatat and fstatat64 inline functions. + * libio/stdio.h: Declare renameat. + * misc/Makefile (routines): Add futimesat. + * misc/Versions [GLIBC_2.4]: Add futimesat. + * posix/unistd.h: Declare fchownat and unlinkat. + * stdio-common/Makefile (routines): Add renameat. + * stdio-common/Versions [GLIBC_2.4]: Add renameat. + * sysdeps/generic/fchownat.c: New file. + * sysdeps/generic/futimesat.c: New file. + * sysdeps/generic/fxstatat.c: New file. + * sysdeps/generic/fxstatat64.c: New file. + * sysdeps/generic/renameat.c: New file. + * sysdeps/generic/unlinkat.c: New file. + * sysdeps/unix/sysv/linux/fchownat.c: New file. + * sysdeps/unix/sysv/linux/futimesat.c: New file. + * sysdeps/unix/sysv/linux/fxstatat.c: New file. + * sysdeps/unix/sysv/linux/fxstatat64.c: New file. + * sysdeps/unix/sysv/linux/renameat.c: New file. + * sysdeps/unix/sysv/linux/unlinkat.c: New file. + * sysdeps/unix/sysv/linux/alpha/fxstatat.c: New file. + * sysdeps/unix/sysv/linux/i386/fchownat.c: New file. + * sysdeps/unix/sysv/linux/i386/fxstatat.c: New file. + * sysdeps/unix/sysv/linux/m68k/fchownat.c: New file. + * sysdeps/unix/sysv/linux/m68k/fxstatat.c: New file. + * sysdeps/unix/sysv/linux/powerpc/fchownat.c: New file. + * sysdeps/unix/sysv/linux/powerpc/powerpc32/fxstatat.c: New file. + * sysdeps/unix/sysv/linux/s390/s390-32/fchownat.c: New file. + * sysdeps/unix/sysv/linux/s390/s390-32/fxstatat.c: New file. + * sysdeps/unix/sysv/linux/sh/fchownat.c: New file. + * sysdeps/unix/sysv/linux/sh/fxstatat.c: New file. + * sysdeps/unix/sysv/linux/sparc/sparc32/fchownat.c: New file. + * sysdeps/unix/sysv/linux/sparc/sparc32/fxstatat.c: New file. + * sysdeps/unix/sysv/linux/sparc/sparc64/fxstatat.c: New file. + * sysdeps/unix/sysv/linux/wordsize-64/fxstatat.c: New file. + * sysdeps/unix/sysv/linux/wordsize-64/fxstatat64.c: New file. + * time/sys/time.h: Declare futimesat. + * include/fcntl.h: Declare __atfct_seterrno. + * sysdeps/unix/sysv/linux/openat.c (__atfct_seterrno): New function. + Split out from openat code and called. + (do_test): Allow openat to fail with ENOSYS. Handle AT_FDCWD. + * sysdeps/generic/openat.c: Handle AT_FDCWD. + * sysdeps/generic/openat64.c: Likewise. + * io/tst-unlinkat.c: New file. + * io/tst-fstatat.c: New file. + * io/tst-futimesat.c: New file. + * io/tst-renameat.c: New file. + * io/tst-fchownat.c: New file. + * io/tst-openat.c: Don't fail if openat is not implemented. + + * sysdeps/unix/sysv/linux/powerpc/chown.c: Don't provide backward + compatibility code if new kernel is guaranteed. + + * time/sys/time.h: Add a few nonnull attributes. + 2005-11-09 Ulrich Drepper * io/fcntl.h: Declare openat and openat64. diff --git a/NEWS b/NEWS index e5beafd072..86c7b5e833 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -GNU C Library NEWS -- history of user-visible changes. 2005-11-09 +GNU C Library NEWS -- history of user-visible changes. 2005-11-11 Copyright (C) 1992-2002,2003,2004,2005 Free Software Foundation, Inc. See the end for copying conditions. @@ -27,7 +27,8 @@ Version 2.4 For a libc and libpthread that works well on Linux 2.4 kernels, we recommend using the stable 2.3 branch. -* New interfaces: fdopendir, openat. +* New interfaces: fdopendir, openat, fstatat, fchownat, futimesat, renameat, + unlinkat. Version 2.3.6 diff --git a/include/fcntl.h b/include/fcntl.h index 7764f1cd62..6080faba3e 100644 --- a/include/fcntl.h +++ b/include/fcntl.h @@ -18,4 +18,8 @@ libc_hidden_proto (__open) extern int __fcntl (int __fd, int __cmd, ...); libc_hidden_proto (__fcntl) +/* Helper functions for the various *at functions. For Linux. */ +extern void __atfct_seterrno (int errval, int fd, const char *buf) + attribute_hidden; + #endif diff --git a/io/Makefile b/io/Makefile index b1ca46814b..9339ffe699 100644 --- a/io/Makefile +++ b/io/Makefile @@ -26,40 +26,43 @@ headers := sys/stat.h bits/stat.h sys/statfs.h bits/statfs.h sys/vfs.h \ poll.h sys/poll.h bits/poll.h \ utime.h ftw.h fts.h sys/sendfile.h -routines := \ - utime \ - mkfifo \ - stat fstat lstat mknod stat64 fstat64 lstat64 \ - xstat fxstat lxstat xmknod xstat64 fxstat64 lxstat64 \ - statfs fstatfs statfs64 fstatfs64 \ - statvfs fstatvfs statvfs64 fstatvfs64 \ - umask chmod fchmod lchmod mkdir \ - open open64 openat openat64 close \ - read write lseek lseek64 access euidaccess \ - fcntl flock lockf lockf64 \ - dup dup2 pipe \ - creat creat64 \ - chdir fchdir \ - getcwd getwd getdirname \ - chown fchown lchown \ - ttyname ttyname_r isatty \ - link symlink readlink \ - unlink rmdir \ - ftw ftw64 fts poll \ - posix_fadvise posix_fadvise64 \ - posix_fallocate posix_fallocate64 \ +routines := \ + utime \ + mkfifo \ + stat fstat lstat mknod stat64 fstat64 lstat64 fstatat fstatat64 \ + xstat fxstat lxstat xmknod xstat64 fxstat64 lxstat64 \ + fxstatat fxstatat64 \ + statfs fstatfs statfs64 fstatfs64 \ + statvfs fstatvfs statvfs64 fstatvfs64 \ + umask chmod fchmod lchmod mkdir \ + open open64 openat openat64 close \ + read write lseek lseek64 access euidaccess \ + fcntl flock lockf lockf64 \ + dup dup2 pipe \ + creat creat64 \ + chdir fchdir \ + getcwd getwd getdirname \ + chown fchown lchown fchownat \ + ttyname ttyname_r isatty \ + link symlink readlink \ + unlink unlinkat rmdir \ + ftw ftw64 fts poll \ + posix_fadvise posix_fadvise64 \ + posix_fallocate posix_fallocate64 \ sendfile sendfile64 # These routines will be omitted from the libc shared object. # Instead the static object files will be included in a special archive # linked against when the shared library will be used. -static-only-routines = stat fstat lstat mknod stat64 fstat64 lstat64 +static-only-routines = stat fstat lstat mknod stat64 fstat64 lstat64 \ + fstatat fstatat64 others := pwd test-srcs := ftwtest tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \ tst-fcntl bug-ftw1 bug-ftw2 bug-ftw3 bug-ftw4 tst-statvfs \ - tst-openat + tst-openat tst-unlinkat tst-fstatat tst-futimesat \ + tst-renameat tst-fchownat distribute := ftwtest-sh diff --git a/io/Versions b/io/Versions index f5cd8d5148..65ee77182e 100644 --- a/io/Versions +++ b/io/Versions @@ -98,6 +98,9 @@ libc { nftw; nftw64; } GLIBC_2.4 { + fchownat; + __fxstatat; __fxstatat64; openat; openat64; + unlinkat; } } diff --git a/io/fcntl.h b/io/fcntl.h index 044a988872..8e13d33dcc 100644 --- a/io/fcntl.h +++ b/io/fcntl.h @@ -56,6 +56,15 @@ __BEGIN_DECLS # define SEEK_END 2 /* Seek from end of file. */ #endif /* XPG */ +#ifdef __USE_GNU +# define AT_FDCWD -100 /* Special value used to indicate + openat should use the current + working directory. */ +# define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */ +# define AT_REMOVEDIR 0x200 /* Remove directory instead of + unlinking file. */ +#endif + /* Do the file control operation described by CMD on FD. The remaining arguments are interpreted depending on CMD. diff --git a/io/fstatat.c b/io/fstatat.c new file mode 100644 index 0000000000..1ac80597a0 --- /dev/null +++ b/io/fstatat.c @@ -0,0 +1,58 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + In addition to the permissions in the GNU Lesser General Public + License, the Free Software Foundation gives you unlimited + permission to link the compiled version of this file with other + programs, and to distribute those programs without any restriction + coming from the use of this file. (The GNU Lesser General Public + License restrictions do apply in other respects; for example, they + cover modification of the file, and distribution when not linked + into another program.) + + Note that people who make modified versions of this file are not + obligated to grant this special exception for their modified + versions; it is their choice whether to do so. The GNU Lesser + General Public License gives permission to release a modified + version without this exception; this exception also makes it + possible to release a modified version which carries forward this + exception. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include + +/* This definition is only used if inlining fails for this function; see + the last page of . The real work is done by the `x' + function which is passed a version number argument. We arrange in the + makefile that when not inlined this function is always statically + linked; that way a dynamically-linked executable always encodes the + version number corresponding to the data structures it uses, so the `x' + functions in the shared library can adapt without needing to recompile + all callers. */ + +#undef fstatat +int +fstatat (int fd, const char *file, struct stat *buf, int flag) +{ + return __fxstatat (_STAT_VER, fd, file, buf, flag); +} + +/* Hide the symbol so that no definition but the one locally in the + executable or DSO is used. */ +#ifdef HAVE_DOT_HIDDEN +asm (".hidden\tfstatat"); +#endif diff --git a/io/fstatat64.c b/io/fstatat64.c new file mode 100644 index 0000000000..a14b42d42e --- /dev/null +++ b/io/fstatat64.c @@ -0,0 +1,58 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + In addition to the permissions in the GNU Lesser General Public + License, the Free Software Foundation gives you unlimited + permission to link the compiled version of this file with other + programs, and to distribute those programs without any restriction + coming from the use of this file. (The GNU Lesser General Public + License restrictions do apply in other respects; for example, they + cover modification of the file, and distribution when not linked + into another program.) + + Note that people who make modified versions of this file are not + obligated to grant this special exception for their modified + versions; it is their choice whether to do so. The GNU Lesser + General Public License gives permission to release a modified + version without this exception; this exception also makes it + possible to release a modified version which carries forward this + exception. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include + +/* This definition is only used if inlining fails for this function; see + the last page of . The real work is done by the `x' + function which is passed a version number argument. We arrange in the + makefile that when not inlined this function is always statically + linked; that way a dynamically-linked executable always encodes the + version number corresponding to the data structures it uses, so the `x' + functions in the shared library can adapt without needing to recompile + all callers. */ + +#undef fstatat64 +int +fstatat64 (int fd, const char *file, struct stat64 *buf, int flag) +{ + return __fxstatat64 (_STAT_VER, fd, file, buf, flag); +} + +/* Hide the symbol so that no definition but the one locally in the + executable or DSO is used. */ +#ifdef HAVE_DOT_HIDDEN +asm (".hidden\tfstatat64"); +#endif diff --git a/io/sys/stat.h b/io/sys/stat.h index 7075003922..0a82ef39e8 100644 --- a/io/sys/stat.h +++ b/io/sys/stat.h @@ -1,4 +1,4 @@ -/* Copyright (C) 1991,1992,1995-2002,2003,2004 Free Software Foundation, Inc. +/* Copyright (C) 1991,1992,1995-2004,2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -228,6 +228,23 @@ extern int stat64 (__const char *__restrict __file, extern int fstat64 (int __fd, struct stat64 *__buf) __THROW __nonnull ((2)); #endif +#ifdef __USE_GNU +/* Similar to stat, get the attributes for FILE and put them in BUF. + Relative path names are interpreted relative to FD unless FD is + AT_FDCWD. */ +# ifndef __USE_FILE_OFFSET64 +extern int fstatat (int __fd, const char *__file, struct stat *__buf, + int __flag) __THROW __nonnull ((2, 3)); +# else +extern int __REDIRECT_NTH (fstatat, (int __fd, const char *__file, + struct stat *__buf, int __flag), + fstatat64) __THROW __nonnull ((2, 3)); +# endif + +extern int fstatat64 (int __fd, const char *__file, struct stat64 *__buf, + int __flag) __THROW __nonnull ((2, 3)); +#endif + #if defined __USE_BSD || defined __USE_XOPEN_EXTENDED # ifndef __USE_FILE_OFFSET64 /* Get file attributes about FILE and put them in BUF. @@ -327,6 +344,9 @@ extern int __xstat (int __ver, __const char *__filename, struct stat *__stat_buf) __THROW __nonnull ((2, 3)); extern int __lxstat (int __ver, __const char *__filename, struct stat *__stat_buf) __THROW __nonnull ((2, 3)); +extern int __fxstatat (int __ver, int __fildes, __const char *__filename, + struct stat *__stat_buf, int __flag) + __THROW __nonnull ((3, 4)); #else # ifdef __REDIRECT_NTH extern int __REDIRECT_NTH (__fxstat, (int __ver, int __fildes, @@ -338,6 +358,10 @@ extern int __REDIRECT_NTH (__xstat, (int __ver, __const char *__filename, extern int __REDIRECT_NTH (__lxstat, (int __ver, __const char *__filename, struct stat *__stat_buf), __lxstat64) __nonnull ((2, 3)); +extern int __REDIRECT_NTH (__fxstatat, (int __ver, int __fildes, + __const char *__filename, + struct stat *__stat_buf, int __flag), + __fxstatat64) __nonnull ((3, 4)); # else # define __fxstat __fxstat64 @@ -353,6 +377,9 @@ extern int __xstat64 (int __ver, __const char *__filename, struct stat64 *__stat_buf) __THROW __nonnull ((2, 3)); extern int __lxstat64 (int __ver, __const char *__filename, struct stat64 *__stat_buf) __THROW __nonnull ((2, 3)); +extern int __fxstatat64 (int __ver, int __fildes, __const char *__filename, + struct stat64 *__stat_buf, int __flag) + __THROW __nonnull ((3, 4)); #endif extern int __xmknod (int __ver, __const char *__path, __mode_t __mode, __dev_t *__dev) __THROW __nonnull ((2, 4)); @@ -380,6 +407,15 @@ __NTH (fstat (int __fd, struct stat *__statbuf)) return __fxstat (_STAT_VER, __fd, __statbuf); } +# ifdef __USE_GNU +extern __inline__ int +__NTH (fstatat (int __fd, __const char *__filename, struct stat *__statbuf, + int __flag)) +{ + return __fxstatat (_STAT_VER, __fd, __filename, __statbuf, __flag); +} +# endif + # if defined __USE_MISC || defined __USE_BSD extern __inline__ int __NTH (mknod (__const char *__path, __mode_t __mode, __dev_t __dev)) @@ -412,6 +448,15 @@ __NTH (fstat64 (int __fd, struct stat64 *__statbuf)) } # endif +# ifdef __USE_GNU +extern __inline__ int +__NTH (fstatat64 (int __fd, __const char *__filename, struct stat64 *__statbuf, + int __flag)) +{ + return __fxstatat64 (_STAT_VER, __fd, __filename, __statbuf, __flag); +} +# endif + #endif __END_DECLS diff --git a/io/tst-fchownat.c b/io/tst-fchownat.c new file mode 100644 index 0000000000..0cbf78b2b0 --- /dev/null +++ b/io/tst-fchownat.c @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include + + +static void prepare (void); +#define PREPARE(argc, argv) prepare () + +static int do_test (void); +#define TEST_FUNCTION do_test () + +#include "../test-skeleton.c" + +static int dir_fd; + +static void +prepare (void) +{ +#if _POSIX_CHOWN_RESTRICTED > 0 + uid_t uid = getuid (); + if (uid != 0) + { + puts ("need root privileges"); + exit (0); + } +#endif + + size_t test_dir_len = strlen (test_dir); + static const char dir_name[] = "/tst-fchownat.XXXXXX"; + + size_t dirbuflen = test_dir_len + sizeof (dir_name); + char *dirbuf = malloc (dirbuflen); + if (dirbuf == NULL) + { + puts ("out of memory"); + exit (1); + } + + snprintf (dirbuf, dirbuflen, "%s%s", test_dir, dir_name); + if (mkdtemp (dirbuf) == NULL) + { + puts ("cannot create temporary directory"); + exit (1); + } + + add_temp_file (dirbuf); + + dir_fd = open (dirbuf, O_RDONLY | O_DIRECTORY); + if (dir_fd == -1) + { + puts ("cannot open directory"); + exit (1); + } +} + + +static int +do_test (void) +{ + /* fdopendir takes over the descriptor, make a copy. */ + int dupfd = dup (dir_fd); + if (dupfd == -1) + { + puts ("dup failed"); + return 1; + } + if (lseek (dupfd, 0, SEEK_SET) != 0) + { + puts ("1st lseek failed"); + return 1; + } + + /* The directory should be empty safe the . and .. files. */ + DIR *dir = fdopendir (dupfd); + if (dir == NULL) + { + puts ("fdopendir failed"); + return 1; + } + struct dirent64 *d; + while ((d = readdir64 (dir)) != NULL) + if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0) + { + printf ("temp directory contains file \"%s\"\n", d->d_name); + return 1; + } + closedir (dir); + + /* Try to create a file. */ + int fd = openat (dir_fd, "some-file", O_CREAT|O_RDWR|O_EXCL, 0666); + if (fd == -1) + { + if (errno == ENOSYS) + { + puts ("*at functions not supported"); + return 0; + } + + puts ("file creation failed"); + return 1; + } + write (fd, "hello", 5); + puts ("file created"); + + struct stat64 st1; + if (fstat64 (fd, &st1) != 0) + { + puts ("fstat64 failed"); + return 1; + } + + close (fd); + + if (fchownat (dir_fd, "some-file", st1.st_uid + 1, st1.st_gid + 1, 0) != 0) + { + puts ("fchownat failed"); + return 1; + } + + struct stat64 st2; + if (fstatat64 (dir_fd, "some-file", &st2, 0) != 0) + { + puts ("fstatat64 failed"); + return 1; + } + + if (st1.st_uid + 1 != st2.st_uid || st1.st_gid + 1 != st2.st_gid) + { + puts ("owner change failed"); + return 1; + } + + if (unlinkat (dir_fd, "some-file", 0) != 0) + { + puts ("unlinkat failed"); + return 1; + } + + close (dir_fd); + + return 0; +} diff --git a/io/tst-fstatat.c b/io/tst-fstatat.c new file mode 100644 index 0000000000..6ddf30d87b --- /dev/null +++ b/io/tst-fstatat.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include +#include +#include + + +static void prepare (void); +#define PREPARE(argc, argv) prepare () + +static int do_test (void); +#define TEST_FUNCTION do_test () + +#include "../test-skeleton.c" + +static int dir_fd; + +static void +prepare (void) +{ + size_t test_dir_len = strlen (test_dir); + static const char dir_name[] = "/tst-fstatat.XXXXXX"; + + size_t dirbuflen = test_dir_len + sizeof (dir_name); + char *dirbuf = malloc (dirbuflen); + if (dirbuf == NULL) + { + puts ("out of memory"); + exit (1); + } + + snprintf (dirbuf, dirbuflen, "%s%s", test_dir, dir_name); + if (mkdtemp (dirbuf) == NULL) + { + puts ("cannot create temporary directory"); + exit (1); + } + + add_temp_file (dirbuf); + + dir_fd = open (dirbuf, O_RDONLY | O_DIRECTORY); + if (dir_fd == -1) + { + puts ("cannot open directory"); + exit (1); + } +} + + +static int +do_test (void) +{ + /* fdopendir takes over the descriptor, make a copy. */ + int dupfd = dup (dir_fd); + if (dupfd == -1) + { + puts ("dup failed"); + return 1; + } + if (lseek (dupfd, 0, SEEK_SET) != 0) + { + puts ("1st lseek failed"); + return 1; + } + + /* The directory should be empty safe the . and .. files. */ + DIR *dir = fdopendir (dupfd); + if (dir == NULL) + { + puts ("fdopendir failed"); + return 1; + } + struct dirent64 *d; + while ((d = readdir64 (dir)) != NULL) + if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0) + { + printf ("temp directory contains file \"%s\"\n", d->d_name); + return 1; + } + closedir (dir); + + /* Try to create a file. */ + int fd = openat (dir_fd, "some-file", O_CREAT|O_RDWR|O_EXCL, 0666); + if (fd == -1) + { + if (errno == ENOSYS) + { + puts ("*at functions not supported"); + return 0; + } + + puts ("file creation failed"); + return 1; + } + write (fd, "hello", 5); + puts ("file created"); + + struct stat64 st1; + if (fstat64 (fd, &st1) != 0) + { + puts ("fstat64 failed"); + return 1; + } + + close (fd); + + struct stat64 st2; + if (fstatat64 (dir_fd, "some-file", &st2, 0) != 0) + { + puts ("fstatat64 failed"); + return 1; + } + + if (st1.st_dev != st2.st_dev + || st1.st_ino != st2.st_ino + || st1.st_size != st2.st_size) + { + puts ("stat results do not match"); + return 1; + } + + if (unlinkat (dir_fd, "some-file", 0) != 0) + { + puts ("unlinkat failed"); + return 1; + } + + if (fstatat64 (dir_fd, "some-file", &st2, 0) == 0) + { + puts ("second fstatat64 succeeded"); + return 1; + } + if (errno != ENOENT) + { + puts ("second fstatat64 did not fail with ENOENT"); + return 1; + } + + close (dir_fd); + + return 0; +} diff --git a/io/tst-futimesat.c b/io/tst-futimesat.c new file mode 100644 index 0000000000..c1e8d93f41 --- /dev/null +++ b/io/tst-futimesat.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include + + +static void prepare (void); +#define PREPARE(argc, argv) prepare () + +static int do_test (void); +#define TEST_FUNCTION do_test () + +#include "../test-skeleton.c" + +static int dir_fd; + +static void +prepare (void) +{ + size_t test_dir_len = strlen (test_dir); + static const char dir_name[] = "/tst-futimesat.XXXXXX"; + + size_t dirbuflen = test_dir_len + sizeof (dir_name); + char *dirbuf = malloc (dirbuflen); + if (dirbuf == NULL) + { + puts ("out of memory"); + exit (1); + } + + snprintf (dirbuf, dirbuflen, "%s%s", test_dir, dir_name); + if (mkdtemp (dirbuf) == NULL) + { + puts ("cannot create temporary directory"); + exit (1); + } + + add_temp_file (dirbuf); + + dir_fd = open (dirbuf, O_RDONLY | O_DIRECTORY); + if (dir_fd == -1) + { + puts ("cannot open directory"); + exit (1); + } +} + + +static int +do_test (void) +{ + /* fdopendir takes over the descriptor, make a copy. */ + int dupfd = dup (dir_fd); + if (dupfd == -1) + { + puts ("dup failed"); + return 1; + } + if (lseek (dupfd, 0, SEEK_SET) != 0) + { + puts ("1st lseek failed"); + return 1; + } + + /* The directory should be empty safe the . and .. files. */ + DIR *dir = fdopendir (dupfd); + if (dir == NULL) + { + puts ("fdopendir failed"); + return 1; + } + struct dirent64 *d; + while ((d = readdir64 (dir)) != NULL) + if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0) + { + printf ("temp directory contains file \"%s\"\n", d->d_name); + return 1; + } + closedir (dir); + + /* Try to create a file. */ + int fd = openat (dir_fd, "some-file", O_CREAT|O_RDWR|O_EXCL, 0666); + if (fd == -1) + { + if (errno == ENOSYS) + { + puts ("*at functions not supported"); + return 0; + } + + puts ("file creation failed"); + return 1; + } + write (fd, "hello", 5); + puts ("file created"); + + struct stat64 st1; + if (fstat64 (fd, &st1) != 0) + { + puts ("fstat64 failed"); + return 1; + } + + close (fd); + + struct timeval tv[2]; + tv[0].tv_sec = st1.st_atime + 1; + tv[0].tv_usec = 0; + tv[1].tv_sec = st1.st_mtime + 1; + tv[1].tv_usec = 0; + if (futimesat (dir_fd, "some-file", tv) != 0) + { + puts ("futimesat failed"); + return 1; + } + + struct stat64 st2; + if (fstatat64 (dir_fd, "some-file", &st2, 0) != 0) + { + puts ("fstatat64 failed"); + return 1; + } + + if (st2.st_mtime != tv[1].tv_sec +#ifdef _STATBUF_ST_NSEC + || st2.st_mtim.tv_nsec != 0 +#endif + ) + { + puts ("stat shows different mtime"); + return 1; + } + + + if (unlinkat (dir_fd, "some-file", 0) != 0) + { + puts ("unlinkat failed"); + return 1; + } + + close (dir_fd); + + return 0; +} diff --git a/io/tst-openat.c b/io/tst-openat.c index c20c95a10d..d10b654fa9 100644 --- a/io/tst-openat.c +++ b/io/tst-openat.c @@ -84,6 +84,12 @@ do_test (void) int fd = openat (dir_fd, "some-file", O_CREAT|O_RDWR|O_EXCL, 0666); if (fd == -1) { + if (errno == ENOSYS) + { + puts ("*at functions not supported"); + return 0; + } + puts ("file creation failed"); return 1; } diff --git a/io/tst-renameat.c b/io/tst-renameat.c new file mode 100644 index 0000000000..fb494594f5 --- /dev/null +++ b/io/tst-renameat.c @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include +#include + + +static void prepare (void); +#define PREPARE(argc, argv) prepare () + +static int do_test (void); +#define TEST_FUNCTION do_test () + +#include "../test-skeleton.c" + +static int dir_fd; + +static void +prepare (void) +{ + size_t test_dir_len = strlen (test_dir); + static const char dir_name[] = "/tst-renameat.XXXXXX"; + + size_t dirbuflen = test_dir_len + sizeof (dir_name); + char *dirbuf = malloc (dirbuflen); + if (dirbuf == NULL) + { + puts ("out of memory"); + exit (1); + } + + snprintf (dirbuf, dirbuflen, "%s%s", test_dir, dir_name); + if (mkdtemp (dirbuf) == NULL) + { + puts ("cannot create temporary directory"); + exit (1); + } + + add_temp_file (dirbuf); + + dir_fd = open (dirbuf, O_RDONLY | O_DIRECTORY); + if (dir_fd == -1) + { + puts ("cannot open directory"); + exit (1); + } +} + + +static int +do_test (void) +{ + /* fdopendir takes over the descriptor, make a copy. */ + int dupfd = dup (dir_fd); + if (dupfd == -1) + { + puts ("dup failed"); + return 1; + } + if (lseek (dupfd, 0, SEEK_SET) != 0) + { + puts ("1st lseek failed"); + return 1; + } + + /* The directory should be empty safe the . and .. files. */ + DIR *dir = fdopendir (dupfd); + if (dir == NULL) + { + puts ("fdopendir failed"); + return 1; + } + struct dirent64 *d; + while ((d = readdir64 (dir)) != NULL) + if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0) + { + printf ("temp directory contains file \"%s\"\n", d->d_name); + return 1; + } + closedir (dir); + + /* Try to create a file. */ + int fd = openat (dir_fd, "some-file", O_CREAT|O_RDWR|O_EXCL, 0666); + if (fd == -1) + { + if (errno == ENOSYS) + { + puts ("*at functions not supported"); + return 0; + } + + puts ("file creation failed"); + return 1; + } + write (fd, "hello", 5); + puts ("file created"); + + struct stat64 st1; + if (fstat64 (fd, &st1) != 0) + { + puts ("fstat64 failed"); + return 1; + } + + close (fd); + + if (renameat (dir_fd, "some-file", dir_fd, "another-file") != 0) + { + puts ("renameat failed"); + return 1; + } + + struct stat64 st2; + if (fstatat64 (dir_fd, "some-file", &st2, 0) == 0) + { + puts ("fstatat64 succeeded"); + return 1; + } + if (errno != ENOENT) + { + puts ("fstatat64 did not fail with ENOENT"); + return 1; + } + + if (fstatat64 (dir_fd, "another-file", &st2, 0) != 0) + { + puts ("2nd fstatat64 failed"); + return 1; + } + + if (st1.st_dev != st2.st_dev + || st1.st_ino != st2.st_ino + || st1.st_size != st2.st_size) + { + puts ("stat results do not match"); + return 1; + } + + if (unlinkat (dir_fd, "another-file", 0) != 0) + { + puts ("unlinkat failed"); + return 1; + } + + close (dir_fd); + + return 0; +} diff --git a/io/tst-unlinkat.c b/io/tst-unlinkat.c new file mode 100644 index 0000000000..c25443c27f --- /dev/null +++ b/io/tst-unlinkat.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include +#include +#include + + +static void prepare (void); +#define PREPARE(argc, argv) prepare () + +static int do_test (void); +#define TEST_FUNCTION do_test () + +#include "../test-skeleton.c" + +static int dir_fd; + +static void +prepare (void) +{ + size_t test_dir_len = strlen (test_dir); + static const char dir_name[] = "/tst-unlinkat.XXXXXX"; + + size_t dirbuflen = test_dir_len + sizeof (dir_name); + char *dirbuf = malloc (dirbuflen); + if (dirbuf == NULL) + { + puts ("out of memory"); + exit (1); + } + + snprintf (dirbuf, dirbuflen, "%s%s", test_dir, dir_name); + if (mkdtemp (dirbuf) == NULL) + { + puts ("cannot create temporary directory"); + exit (1); + } + + add_temp_file (dirbuf); + + dir_fd = open (dirbuf, O_RDONLY | O_DIRECTORY); + if (dir_fd == -1) + { + puts ("cannot open directory"); + exit (1); + } +} + + +static int +do_test (void) +{ + /* fdopendir takes over the descriptor, make a copy. */ + int dupfd = dup (dir_fd); + if (dupfd == -1) + { + puts ("dup failed"); + return 1; + } + if (lseek (dupfd, 0, SEEK_SET) != 0) + { + puts ("1st lseek failed"); + return 1; + } + + /* The directory should be empty safe the . and .. files. */ + DIR *dir = fdopendir (dupfd); + if (dir == NULL) + { + puts ("fdopendir failed"); + return 1; + } + struct dirent64 *d; + while ((d = readdir64 (dir)) != NULL) + if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0) + { + printf ("temp directory contains file \"%s\"\n", d->d_name); + return 1; + } + closedir (dir); + + /* Try to create a file. */ + int fd = openat (dir_fd, "some-file", O_CREAT|O_RDWR|O_EXCL, 0666); + if (fd == -1) + { + if (errno == ENOSYS) + { + puts ("*at functions not supported"); + return 0; + } + + puts ("file creation failed"); + return 1; + } + write (fd, "hello", 5); + close (fd); + puts ("file created"); + + /* fdopendir takes over the descriptor, make a copy. */ + dupfd = dup (dir_fd); + if (dupfd == -1) + { + puts ("2nd dup failed"); + return 1; + } + if (lseek (dupfd, 0, SEEK_SET) != 0) + { + puts ("2nd lseek failed"); + return 1; + } + + /* The directory should be empty safe the . and .. files. */ + dir = fdopendir (dupfd); + if (dir == NULL) + { + puts ("2nd fdopendir failed"); + return 1; + } + bool seen_file = false; + while ((d = readdir64 (dir)) != NULL) + if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0) + { + if (strcmp (d->d_name, "some-file") != 0) + { + printf ("temp directory contains file \"%s\"\n", d->d_name); + return 1; + } + + seen_file = true; + } + closedir (dir); + + if (!seen_file) + { + puts ("file not created in correct directory"); + return 1; + } + + /* Remove the file now. */ + if (unlinkat (dir_fd, "some-file", 0) != 0) + { + puts ("unlinkat failed"); + return 1; + } + + /* We won't need dir_fd anymore after this, so use it. */ + if (lseek (dir_fd, 0, SEEK_SET) != 0) + { + puts ("3rd lseek failed"); + return 1; + } + + /* The directory should be empty safe the . and .. files. */ + dir = fdopendir (dir_fd); + if (dir == NULL) + { + puts ("3rd fdopendir failed"); + return 1; + } + while ((d = readdir64 (dir)) != NULL) + if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0) + { + if (strcmp (d->d_name, "some-file") == 0) + { + puts ("some-file not removed"); + return 1; + } + else + { + printf ("temp directory contains file \"%s\"\n", d->d_name); + return 1; + } + } + closedir (dir); + + return 0; +} diff --git a/libio/stdio.h b/libio/stdio.h index f24a6ddd9c..0ea052b175 100644 --- a/libio/stdio.h +++ b/libio/stdio.h @@ -154,6 +154,11 @@ extern int remove (__const char *__filename) __THROW; extern int rename (__const char *__old, __const char *__new) __THROW; __END_NAMESPACE_STD +#ifdef __USE_GNU +/* Rename file OLD relative to OLDFD to NEW relative to NEWFD. */ +extern int renameat (int __oldfd, __const char *__old, int __newfd, + __const char *__new) __THROW; +#endif __BEGIN_NAMESPACE_STD /* Create a temporary file and open it read/write. diff --git a/misc/Makefile b/misc/Makefile index 7c0b64818d..5199b6b653 100644 --- a/misc/Makefile +++ b/misc/Makefile @@ -47,7 +47,7 @@ routines := brk sbrk sstk ioctl \ gtty stty \ ptrace \ fstab mntent mntent_r \ - utimes lutimes futimes \ + utimes lutimes futimes futimesat \ truncate ftruncate truncate64 ftruncate64 \ chflags fchflags \ insremque getttyent getusershell getpass ttyslot \ diff --git a/misc/Versions b/misc/Versions index 4c3aafad6e..fdf2d9d2af 100644 --- a/misc/Versions +++ b/misc/Versions @@ -131,6 +131,7 @@ libc { remap_file_pages; } GLIBC_2.4 { + futimesat; __syslog_chk; __vsyslog_chk; } } diff --git a/posix/unistd.h b/posix/unistd.h index b0fba33417..86e0e9e659 100644 --- a/posix/unistd.h +++ b/posix/unistd.h @@ -431,6 +431,14 @@ extern int lchown (__const char *__file, __uid_t __owner, __gid_t __group) #endif /* Use BSD || X/Open Unix. */ +#ifdef __USE_GNU +/* Change the owner and group of FILE relative to the directory FD is open + on. */ +extern int fchownat (int __fd, __const char *__file, __uid_t __owner, + __gid_t __group, int __flag) + __THROW __nonnull ((2)) __wur; +#endif /* Use GNU. */ + /* Change the process's working directory to PATH. */ extern int chdir (__const char *__path) __THROW __nonnull ((1)) __wur; @@ -749,6 +757,12 @@ extern int readlink (__const char *__restrict __path, char *__restrict __buf, /* Remove the link NAME. */ extern int unlink (__const char *__name) __THROW __nonnull ((1)); +#ifdef __USE_GNU +/* Remove the link NAME relative to FD. */ +extern int unlinkat (int __fd, __const char *__name, int __flag) + __THROW __nonnull ((2)); +#endif + /* Remove the directory PATH. */ extern int rmdir (__const char *__path) __THROW __nonnull ((1)); diff --git a/stdio-common/Makefile b/stdio-common/Makefile index 119323617c..a2cfbe720c 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -33,7 +33,7 @@ routines := \ perror psignal \ tmpfile tmpfile64 tmpnam tmpnam_r tempnam tempname \ getline getw putw \ - remove rename \ + remove rename renameat \ flockfile ftrylockfile funlockfile install-others = $(inst_includedir)/bits/stdio_lim.h diff --git a/stdio-common/Versions b/stdio-common/Versions index 1dbce1a124..2f64429346 100644 --- a/stdio-common/Versions +++ b/stdio-common/Versions @@ -37,7 +37,7 @@ libc { tempnam; tmpfile; tmpnam; tmpnam_r; # v* - vfprintf; vfscanf; vprintf; + vfprintf; vfscanf; vprintf; } GLIBC_2.1 { # p* @@ -46,6 +46,9 @@ libc { # t* tmpfile; tmpfile64; } + GLIBC_2.4 { + renameat; + } GLIBC_PRIVATE { # global variables _itoa_lower_digits; diff --git a/sysdeps/generic/fchownat.c b/sysdeps/generic/fchownat.c new file mode 100644 index 0000000000..f6921c9012 --- /dev/null +++ b/sysdeps/generic/fchownat.c @@ -0,0 +1,51 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include + +/* Change the owner and group of FILE. */ +int +fchownat (fd, file, owner, group, flag) + int fd; + const char *file; + uid_t owner; + gid_t group; + int flag; +{ + if (file == NULL || (flag & ~AT_SYMLINK_NOFOLLOW) != 0) + { + __set_errno (EINVAL); + return -1; + } + + if (fd < 0 && fd != AT_FDCWD) + { + __set_errno (EBADF); + return -1; + } + + __set_errno (ENOSYS); + return -1; +} +stub_warning (fchownat) + +#include diff --git a/sysdeps/generic/futimesat.c b/sysdeps/generic/futimesat.c new file mode 100644 index 0000000000..33d170862a --- /dev/null +++ b/sysdeps/generic/futimesat.c @@ -0,0 +1,52 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + + +/* Change the access time of FILE relative to FD to TVP[0] and + the modification time of FILE to TVP[1]. */ +int +futimesat (fd, file, tvp) + int fd; + const char *file; + const struct timeval tvp[2]; +{ + if (fd < 0 && fd != AT_FDCWD) + { + __set_errno (EBADF); + return -1; + } + + if (file == NULL) + { + __set_errno (EINVAL); + return -1; + } + + __set_errno (ENOSYS); + return -1; +} + +weak_alias (__utimes, utimes) + +stub_warning (utimes) +#include diff --git a/sysdeps/generic/fxstatat.c b/sysdeps/generic/fxstatat.c new file mode 100644 index 0000000000..62f7fe3efb --- /dev/null +++ b/sysdeps/generic/fxstatat.c @@ -0,0 +1,49 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + +/* Get information about the file descriptor FD in BUF. */ +int +__fxstatat (int vers, int fd, const char *filename, struct stat *buf, int flag) +{ + if (vers != _STAT_VER) + { + __set_errno (EINVAL); + return -1; + } + + if (fd < 0 && fd != AT_FDCWD) + { + __set_errno (EBADF); + return -1; + } + if (buf == NULL || (flag & ~AT_SYMLINK_NOFOLLOW) != 0) + { + __set_errno (EINVAL); + return -1; + } + + __set_errno (ENOSYS); + return -1; +} +stub_warning (fstatat) +#include diff --git a/sysdeps/generic/fxstatat64.c b/sysdeps/generic/fxstatat64.c new file mode 100644 index 0000000000..ba95b73e81 --- /dev/null +++ b/sysdeps/generic/fxstatat64.c @@ -0,0 +1,50 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + +/* Get information about the file descriptor FD in BUF. */ +int +__fxstatat64 (int vers, int fd, const char *filename, struct stat64 *buf, + int flag) +{ + if (vers != _STAT_VER) + { + __set_errno (EINVAL); + return -1; + } + + if (fd < 0 && fd != AT_FDCWD) + { + __set_errno (EBADF); + return -1; + } + if (buf == NULL || (flag & ~AT_SYMLINK_NOFOLLOW) != 0) + { + __set_errno (EINVAL); + return -1; + } + + __set_errno (ENOSYS); + return -1; +} +stub_warning (fstatat64) +#include diff --git a/sysdeps/generic/openat.c b/sysdeps/generic/openat.c index b058bcbe9f..f3f699ca23 100644 --- a/sysdeps/generic/openat.c +++ b/sysdeps/generic/openat.c @@ -39,7 +39,7 @@ openat (fd, file, oflag) return -1; } - if (file[0] != '/') + if (fd != AT_FDCWD && file[0] != '/') { /* Check FD is associated with a directory. */ struct stat64 st; diff --git a/sysdeps/generic/openat64.c b/sysdeps/generic/openat64.c index 82575df399..87952d38d6 100644 --- a/sysdeps/generic/openat64.c +++ b/sysdeps/generic/openat64.c @@ -39,7 +39,7 @@ openat64 (fd, file, oflag) return -1; } - if (file[0] != '/') + if (fd != AT_FDCWD && file[0] != '/') { /* Check FD is associated with a directory. */ struct stat64 st; diff --git a/sysdeps/generic/renameat.c b/sysdeps/generic/renameat.c new file mode 100644 index 0000000000..e8629098df --- /dev/null +++ b/sysdeps/generic/renameat.c @@ -0,0 +1,50 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include + + +/* Rename the file OLD relative to OLDFD to NEW relative to NEWFD. */ +int +renameat (oldfd, old, newfd, new) + int oldfd; + const char *old; + int newfd; + const char *new; +{ + if ((oldfd < 0 & oldfd !_ AT_FDCWD) || (newfd < 0 && newfd != AT_FDCWD)) + { + __set_errno (EBADF); + return -1; + } + + if (old == NULL || new == NULL) + { + __set_errno (EINVAL); + return -1; + } + + __set_errno (ENOSYS); + return -1; +} + + +stub_warning (renameat) +#include diff --git a/sysdeps/generic/unlinkat.c b/sysdeps/generic/unlinkat.c new file mode 100644 index 0000000000..f9a08b9903 --- /dev/null +++ b/sysdeps/generic/unlinkat.c @@ -0,0 +1,49 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + + +/* Remove the link named NAME. */ +int +unlinkat (fd, name, flag) + int fd; + const char *name; + int flag; +{ + if (name == NULL || (flag & AT_REMOVEDIR) != 0) + { + __set_errno (EINVAL); + return -1; + } + + if (fd < 0 && fd != AT_FDCWD) + { + __set_errno (EBADF); + return -1; + } + + __set_errno (ENOSYS); + return -1; +} +stub_warning (unlinkat) + +#include diff --git a/sysdeps/unix/sysv/linux/alpha/fxstatat.c b/sysdeps/unix/sysv/linux/alpha/fxstatat.c new file mode 100644 index 0000000000..65b7ad97a3 --- /dev/null +++ b/sysdeps/unix/sysv/linux/alpha/fxstatat.c @@ -0,0 +1,100 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#define __fxstatat64 __fxstatat64_disable + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef __fxstatat64 + + +/* Get information about the file NAME in BUF. */ +int +__fxstatat (int vers, int fd, const char *file, struct stat *st, int flag) +{ + if (flag & ~AT_SYMLINK_NOFOLLOW) + { + __set_errno (EINVAL); + return -1; + } + + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + + INTERNAL_SYSCALL_DECL (err); + int result, errno_out; + struct kernel_stat kst; + + if (vers == _STAT_VER_KERNEL64 && !__libc_missing_axp_stat64) + { + if (flags & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lstat64, err, 2, file, st); + else + result = INTERNAL_SYSCALL (stat64, err, 2, file, st); + + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return result; + errno_out = INTERNAL_SYSCALL_ERRNO (result, err); + if (errno_out != ENOSYS) + goto fail; + __libc_missing_axp_stat64 = 1; + } + + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lstat, err, 2, file, &kst); + else + result = INTERNAL_SYSCALL (stat, err, 2, file, &kst); + + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return __xstat_conv (vers, &kst, st); + errno_out = INTERNAL_SYSCALL_ERRNO (result, err); + + fail: + __atfct_seterrno (errno_out, fd, buf); + + return -1; +} +hidden_def (__xstat) +weak_alias (__xstat, _xstat); +strong_alias (__xstat, __xstat64); +hidden_ver (__xstat, __xstat64) diff --git a/sysdeps/unix/sysv/linux/fchownat.c b/sysdeps/unix/sysv/linux/fchownat.c new file mode 100644 index 0000000000..d3cb992a09 --- /dev/null +++ b/sysdeps/unix/sysv/linux/fchownat.c @@ -0,0 +1,73 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include +#include + +/* Change the owner and group of FILE. */ +int +fchownat (fd, file, owner, group, flag) + int fd; + const char *file; + uid_t owner; + gid_t group; + int flag; +{ + if (flag & ~AT_SYMLINK_NOFOLLOW) + { + __set_errno (EINVAL); + return -1; + } + + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + + int result; + INTERNAL_SYSCALL_DECL (err); + + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lchown, err, 3, file, owner, group); + else + result = INTERNAL_SYSCALL (chown, err, 3, file, owner, group); + + if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (result, err), 0)) + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (result, err), fd, buf); + + return result; +} diff --git a/sysdeps/unix/sysv/linux/futimesat.c b/sysdeps/unix/sysv/linux/futimesat.c new file mode 100644 index 0000000000..2fdedb0ff4 --- /dev/null +++ b/sysdeps/unix/sysv/linux/futimesat.c @@ -0,0 +1,97 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include "kernel-features.h" + + +/* Change the access time of FILE relative to FD to TVP[0] and + the modification time of FILE to TVP[1]. */ +int +futimesat (fd, file, tvp) + int fd; + const char *file; + const struct timeval tvp[2]; +{ + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + + int result; + INTERNAL_SYSCALL_DECL (err); + +#ifdef __NR_utimes + result = INTERNAL_SYSCALL (utimes, err, 2, file, tvp); + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return result; + +# ifndef __ASSUME_UTIMES + if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS) + goto fail; +# endif +#endif + + /* The utimes() syscall does not exist or is not available in the + used kernel. Use utime(). For this we have to convert to the + data format utime() expects. */ +#ifndef __ASSUME_UTIMES + struct utimbuf tmp; + struct utimbuf *times; + + if (tvp != NULL) + { + times = &tmp; + tmp.actime = tvp[0].tv_sec + tvp[0].tv_usec / 1000000; + tmp.modtime = tvp[1].tv_sec + tvp[1].tv_usec / 1000000; + } + else + times = NULL; + + result = INTERNAL_SYSCALL (utime, err, 2, file, times); + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return result; + + fail: +#endif + + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (result, err), fd, buf); + + return -1; +} diff --git a/sysdeps/unix/sysv/linux/fxstatat.c b/sysdeps/unix/sysv/linux/fxstatat.c new file mode 100644 index 0000000000..9bc3dbc459 --- /dev/null +++ b/sysdeps/unix/sysv/linux/fxstatat.c @@ -0,0 +1,106 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Ho hum, if fxstatat == fxstatat64 we must get rid of the prototype or gcc + will complain since they don't strictly match. */ +#define __fxstatat64 __fxstatat64_disable + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* Get information about the file NAME in BUF. */ +int +__fxstatat (int vers, int fd, const char *file, struct stat *st, int flag) +{ + if (flag & ~AT_SYMLINK_NOFOLLOW) + { + __set_errno (EINVAL); + return -1; + } + + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + + int result; + INTERNAL_SYSCALL_DECL (err); + + if (vers == _STAT_VER_KERNEL) + { + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lstat, err, 2, CHECK_STRING (file), + CHECK_1 ((struct kernel_stat *) st)); + else + result = INTERNAL_SYSCALL (stat, err, 2, CHECK_STRING (file), + CHECK_1 ((struct kernel_stat *) st)); + + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return result; + } + +#ifdef STAT_IS_KERNEL_STAT + __set_errno (EINVAL); + return -1; +#else + struct kernel_stat kst; + + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lstat, err, 2, CHECK_STRING (file), + __ptrvalue (&kst)); + else + result = INTERNAL_SYSCALL (stat, err, 2, CHECK_STRING (file), + __ptrvalue (&kst)); + + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return __xstat_conv (vers, &kst, st); +#endif + + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (result, err), fd, buf); + + return -1; +} +#ifdef XSTAT_IS_XSTAT64 +# undef __fxstatat64 +strong_alias (__fxstatat, __fxstatat64); +#endif diff --git a/sysdeps/unix/sysv/linux/fxstatat64.c b/sysdeps/unix/sysv/linux/fxstatat64.c new file mode 100644 index 0000000000..8c41db710c --- /dev/null +++ b/sysdeps/unix/sysv/linux/fxstatat64.c @@ -0,0 +1,135 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kernel-features.h" + +#if __ASSUME_STAT64_SYSCALL == 0 +# include +#endif + +#ifdef __NR_stat64 +# if __ASSUME_STAT64_SYSCALL == 0 +/* The variable is shared between all wrappers around *stat64 calls. + This is the definition. */ +extern int __have_no_stat64; +# endif +#endif + +/* Get information about the file NAME in BUF. */ + +int +__fxstatat64 (int vers, int fd, const char *file, struct stat64 *st, int flag) +{ + if (flag & ~AT_SYMLINK_NOFOLLOW) + { + __set_errno (EINVAL); + return -1; + } + + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + + int result; + INTERNAL_SYSCALL_DECL (err); + +#if __ASSUME_STAT64_SYSCALL > 0 + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lstat64, err, 2, CHECK_STRING (file), + CHECK_1 (st)); + else + result = INTERNAL_SYSCALL (stat64, err, 2, CHECK_STRING (file), + CHECK_1 (st)); + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + { +# if defined _HAVE_STAT64___ST_INO && __ASSUME_ST_INO_64_BIT == 0 + if (st->__st_ino != (__ino_t) st->st_ino) + st->st_ino = st->__st_ino; +# endif + return result; + } +#else + struct kernel_stat kst; +# if defined __NR_stat64 + if (! __have_no_stat64) + { + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lstat64, err, 2, CHECK_STRING (file), + CHECK_1 (st)); + else + result = INTERNAL_SYSCALL (stat64, err, 2, CHECK_STRING (file), + CHECK_1 (st)); + + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + { +# if defined _HAVE_STAT64___ST_INO && __ASSUME_ST_INO_64_BIT == 0 + if (st->__st_ino != (__ino_t) st->st_ino) + st->st_ino = st->__st_ino; +# endif + return result; + } + if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS) + goto fail; + + __have_no_stat64 = 1; + } +# endif + + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lstat, err, 2, CHECK_STRING (file), + __ptrvalue (&kst)); + else + result = INTERNAL_SYSCALL (stat, err, 2, CHECK_STRING (file), + __ptrvalue (&kst)); + + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return __xstat64_conv (vers, &kst, st); + + fail: + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (result, err), fd, buf); + + return -1; +#endif +} diff --git a/sysdeps/unix/sysv/linux/i386/fchownat.c b/sysdeps/unix/sysv/linux/i386/fchownat.c new file mode 100644 index 0000000000..331623f73d --- /dev/null +++ b/sysdeps/unix/sysv/linux/i386/fchownat.c @@ -0,0 +1,177 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "kernel-features.h" + +/* + In Linux 2.1.x the chown functions have been changed. A new function lchown + was introduced. The new chown now follows symlinks - the old chown and the + new lchown do not follow symlinks. + The new lchown function has the same number as the old chown had and the + new chown has a new number. When compiling with headers from Linux > 2.1.8x + it's impossible to run this libc with older kernels. In these cases libc + has therefore to route calls to chown to the old chown function. +*/ + +extern int __chown_is_lchown (const char *__file, uid_t __owner, + gid_t __group); +extern int __real_chown (const char *__file, uid_t __owner, gid_t __group); + + +#if defined __NR_lchown || __ASSUME_LCHOWN_SYSCALL > 0 +/* Running under Linux > 2.1.80. */ + +# ifdef __NR_chown32 +# if __ASSUME_32BITUIDS == 0 +/* This variable is shared with all files that need to check for 32bit + uids. */ +extern int __libc_missing_32bit_uids; +# endif +# endif /* __NR_chown32 */ +#endif + + +int +fchownat (int fd, const char *file, uid_t owner, gid_t group, int flag) +{ + if (flag & ~AT_SYMLINK_NOFOLLOW) + { + __set_errno (EINVAL); + return -1; + } + + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + + int result; + INTERNAL_SYSCALL_DECL (err); + +#if defined __NR_lchown || __ASSUME_LCHOWN_SYSCALL > 0 +# if __ASSUME_LCHOWN_SYSCALL == 0 + static int __libc_old_chown; + +# ifdef __NR_chown32 + if (__libc_missing_32bit_uids <= 0) + { + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lchown32, err, 3, CHECK_STRING (file), + owner, group); + else + result = INTERNAL_SYSCALL (chown32, err, 3, CHECK_STRING (file), + owner, group); + + if (!INTERNAL_SYSCALL_ERROR_P (result, err)) + return result; + if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS) + goto fail; + + __libc_missing_32bit_uids = 1; + } +# endif /* __NR_chown32 */ + + if (((owner + 1) > (uid_t) ((__kernel_uid_t) -1U)) + || ((group + 1) > (gid_t) ((__kernel_gid_t) -1U))) + { + __set_errno (EINVAL); + return -1; + } + + if (!__libc_old_chown && (flag & AT_SYMLINK_NOFOLLOW) == 0) + { + result = INTERNAL_SYSCALL (chown, err, 3, CHECK_STRING (file), owner, + group); + + if (!INTERNAL_SYSCALL_ERROR_P (result, err)) + return result; + if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS) + goto fail; + + __libc_old_chown = 1; + } + + result = INTERNAL_SYSCALL (lchown, err, 3, CHECK_STRING (file), owner, + group); +# elif __ASSUME_32BITUIDS + /* This implies __ASSUME_LCHOWN_SYSCALL. */ + result = INTERNAL_SYSCALL (chown32, err, 3, CHECK_STRING (file), owner, + group); +# else + /* !__ASSUME_32BITUIDS && ASSUME_LCHOWN_SYSCALL */ +# ifdef __NR_chown32 + if (__libc_missing_32bit_uids <= 0) + { + result = INTERNAL_SYSCALL (chown32, err, 3, CHECK_STRING (file), owner, + group); + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return result; + if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS) + goto fail; + + __libc_missing_32bit_uids = 1; + } +# endif /* __NR_chown32 */ + if (((owner + 1) > (uid_t) ((__kernel_uid_t) -1U)) + || ((group + 1) > (gid_t) ((__kernel_gid_t) -1U))) + { + __set_errno (EINVAL); + return -1; + } + + result = INTERNAL_SYSCALL (chown, err, 3, CHECK_STRING (file), owner, group); +# endif +#else + result = INTERNAL_SYSCALL (chown, err, 3, CHECK_STRING (file), owner, group); +#endif + + if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (result, err), 0)) + { + fail: + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (result, err), fd, buf); + result = -1; + } + + return result; +} diff --git a/sysdeps/unix/sysv/linux/i386/fxstatat.c b/sysdeps/unix/sysv/linux/i386/fxstatat.c new file mode 100644 index 0000000000..2fc89e69c4 --- /dev/null +++ b/sysdeps/unix/sysv/linux/i386/fxstatat.c @@ -0,0 +1,148 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Ho hum, if fxstatat == fxstatat64 we must get rid of the prototype or gcc + will complain since they don't strictly match. */ +#define __fxstatat64 __fxstatat64_disable + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kernel-features.h" + +#include + +#ifdef __NR_stat64 +# if __ASSUME_STAT64_SYSCALL == 0 +/* The variable is shared between all wrappers around *stat64 calls. */ +extern int __have_no_stat64; +# endif +#endif + + +/* Get information about the file NAME relative to FD in ST. */ +int +__fxstatat (int vers, int fd, const char *file, struct stat *st, int flag) +{ + if (flag & ~AT_SYMLINK_NOFOLLOW) + { + __set_errno (EINVAL); + return -1; + } + + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + +#if __ASSUME_STAT64_SYSCALL == 0 + struct kernel_stat kst; +#endif + int result; + INTERNAL_SYSCALL_DECL (err); + + if (vers == _STAT_VER_KERNEL) + { + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lstat, err, 2, CHECK_STRING (file), + CHECK_1 ((struct kernel_stat *) st)); + else + result = INTERNAL_SYSCALL (stat, err, 2, CHECK_STRING (file), + CHECK_1 ((struct kernel_stat *) st)); + goto out; + } + +#if __ASSUME_STAT64_SYSCALL > 0 + struct stat64 st64; + + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lstat64, err, 2, CHECK_STRING (file), + __ptrvalue (&st64)); + else + result = INTERNAL_SYSCALL (stat64, err, 2, CHECK_STRING (file), + __ptrvalue (&st64)); + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return __xstat32_conv (vers, &st64, st); +#else +# if defined __NR_stat64 + /* To support 32 bit UIDs, we have to use stat64. The normal stat + call only returns 16 bit UIDs. */ + if (! __have_no_stat64) + { + struct stat64 st64; + + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lstat64, err, 2, CHECK_STRING (file), + __ptrvalue (&st64)); + else + result = INTERNAL_SYSCALL (stat64, err, 2, CHECK_STRING (file), + __ptrvalue (&st64)); + + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + result = __xstat32_conv (vers, &st64, st); + + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1) + || INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS) + goto out; + + __have_no_stat64 = 1; + } +# endif + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lstat, err, 2, CHECK_STRING (file), + __ptrvalue (&kst)); + else + result = INTERNAL_SYSCALL (stat, err, 2, CHECK_STRING (file), + __ptrvalue (&kst)); + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return __xstat_conv (vers, &kst, st); +#endif /* __ASSUME_STAT64_SYSCALL */ + + out: + if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (result, err), 0)) + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (result, err), fd, buf); + + return result; +} +#ifdef XSTAT_IS_XSTAT64 +# undef __fxstatat64 +strong_alias (__fxstatat, __fxstatat64); +#endif diff --git a/sysdeps/unix/sysv/linux/m68k/fchownat.c b/sysdeps/unix/sysv/linux/m68k/fchownat.c new file mode 100644 index 0000000000..71df4feda7 --- /dev/null +++ b/sysdeps/unix/sysv/linux/m68k/fchownat.c @@ -0,0 +1,121 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "kernel-features.h" + +#ifdef __NR_chown32 +# if __ASSUME_32BITUIDS == 0 +/* This variable is shared with all files that need to check for 32bit + uids. */ +extern int __libc_missing_32bit_uids; +# endif +#endif /* __NR_chown32 */ + +int +fchownat (int fd, const char *file, uid_t owner, gid_t group, int flag) +{ + if (flag & ~AT_SYMLINK_NOFOLLOW) + { + __set_errno (EINVAL); + return -1; + } + + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + + int result; + INTERNAL_SYSCALL_DECL (err); + +#if __ASSUME_32BITUIDS > 0 + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lchown32, err, 3, CHECK_STRING (file), owner, + group); + else + result = INTERNAL_SYSCALL (chown32, err, 3, CHECK_STRING (file), owner, + group); +#else +# ifdef __NR_chown32 + if (__libc_missing_32bit_uids <= 0) + { + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lchown32, err, 3, CHECK_STRING (file), + owner, group); + else + result = INTERNAL_SYSCALL (chown32, err, 3, CHECK_STRING (file), owner, + group); + + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return result; + if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS) + goto fail; + + __libc_missing_32bit_uids = 1; + } +# endif /* __NR_chown32 */ + + if (((owner + 1) > (gid_t) ((__kernel_uid_t) -1U)) + || ((group + 1) > (gid_t) ((__kernel_gid_t) -1U))) + { + __set_errno (EINVAL); + return -1; + } + + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lchown, err, 3, CHECK_STRING (file), owner, + group); + else + result = INTERNAL_SYSCALL (chown, err, 3, CHECK_STRING (file), owner, + group); +#endif + + if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (result, err), 0)) + { + fail: + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (result, err), fd, buf); + result = -1; + } + + return result; +} diff --git a/sysdeps/unix/sysv/linux/m68k/fxstatat.c b/sysdeps/unix/sysv/linux/m68k/fxstatat.c new file mode 100644 index 0000000000..0f8b3135d8 --- /dev/null +++ b/sysdeps/unix/sysv/linux/m68k/fxstatat.c @@ -0,0 +1 @@ +#include diff --git a/sysdeps/unix/sysv/linux/openat.c b/sysdeps/unix/sysv/linux/openat.c index a89f172b62..d5d976cbc4 100644 --- a/sysdeps/unix/sysv/linux/openat.c +++ b/sysdeps/unix/sysv/linux/openat.c @@ -29,6 +29,30 @@ #ifndef OPENAT # define OPENAT openat # define MORE_OFLAGS 0 + + +void +attribute_hidden +__atfct_seterrno (int errval, int fd, const char *buf) +{ + if (buf != NULL && errval == ENOTDIR) + { + /* This can mean either the file descriptor is invalid or + /proc is not mounted. */ + struct stat64 st; + if (__fxstat64 (_STAT_VER, fd, &st) != 0) + /* errno is already set correctly. */ + return; + + /* If /proc is not mounted there is nothing we can do. */ + if (S_ISDIR (st.st_mode) + && (__xstat64 (_STAT_VER, "/proc/self/fd", &st) != 0 + || !S_ISDIR (st.st_mode))) + errval = ENOSYS; + } + + __set_errno (errval); +} #endif /* Open FILE with access OFLAG. Interpret relative paths relative to @@ -42,7 +66,7 @@ OPENAT (fd, file, oflag) { char *buf = NULL; - if (file[0] != '/') + if (fd != AT_FDCWD && file[0] != '/') { size_t filelen = strlen (file); static const char procfd[] = "/proc/self/fd/%d/%s"; @@ -85,26 +109,9 @@ OPENAT (fd, file, oflag) if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0)) { - int errval = INTERNAL_SYSCALL_ERRNO (res, err); - if (buf != NULL && errval == ENOTDIR) - { - /* This can mean either the file desriptor is invalid or - /proc is not mounted. */ - struct stat64 st; - if (__fxstat64 (_STAT_VER, fd, &st) != 0) - /* errno is already set correctly. */ - goto out; - - /* If /proc is not mounted there is nothing we can do. */ - if (S_ISDIR (st.st_mode) - && (__xstat64 (_STAT_VER, "/proc/self/fd", &st) != 0 - || !S_ISDIR (st.st_mode))) - errval = ENOSYS; - } - - __set_errno (errval); + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (res, err), fd, buf); + res = -1; } - out: return res; } diff --git a/sysdeps/unix/sysv/linux/powerpc/chown.c b/sysdeps/unix/sysv/linux/powerpc/chown.c index 4b283e720c..fdcbd3683a 100644 --- a/sysdeps/unix/sysv/linux/powerpc/chown.c +++ b/sysdeps/unix/sysv/linux/powerpc/chown.c @@ -1,5 +1,5 @@ /* chown() compatibility. - Copyright (C) 1998, 2000, 2002, 2003 Free Software Foundation, Inc. + Copyright (C) 1998, 2000, 2002, 2003, 2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -24,6 +24,8 @@ #include #include +#include + /* In Linux 2.1.x the chown functions have been changed. A new function lchown was introduced. The new chown now follows symlinks - the old chown and the @@ -34,58 +36,61 @@ int __chown (const char *file, uid_t owner, gid_t group) { - int err; - int old_errno; - char link[PATH_MAX+2]; - char path[2*PATH_MAX+4]; - int loopct; - size_t filelen; - static int libc_old_chown = 0 /* -1=old linux, 1=new linux, 0=unknown */; +#if __ASSUME_LCHOWN_SYSCALL + return INLINE_SYSCALL (chown, 3, file, owner, group); +#else + int err; + int old_errno; + char link[PATH_MAX + 2]; + char path[2 * PATH_MAX + 4]; + int loopct; + size_t filelen; + static int libc_old_chown = 0 /* -1=old linux, 1=new linux, 0=unknown */; - if (libc_old_chown == 1) - return INLINE_SYSCALL (chown, 3, __ptrvalue (file), owner, group); + if (libc_old_chown == 1) + return INLINE_SYSCALL (chown, 3, __ptrvalue (file), owner, group); - old_errno = errno; + old_errno = errno; -#ifdef __NR_lchown - if (libc_old_chown == 0) - { - err = INLINE_SYSCALL (chown, 3, __ptrvalue (file), owner, group); - if (err != -1 || errno != ENOSYS) - { - libc_old_chown = 1; - return err; - } - libc_old_chown = -1; - } -#endif +# ifdef __NR_lchown + if (libc_old_chown == 0) + { + err = INLINE_SYSCALL (chown, 3, __ptrvalue (file), owner, group); + if (err != -1 || errno != ENOSYS) + { + libc_old_chown = 1; + return err; + } + libc_old_chown = -1; + } +# endif - err = __readlink (file, link, PATH_MAX+1); - if (err == -1) - { - errno = old_errno; - return __lchown(file, owner, group); - } + err = __readlink (file, link, PATH_MAX + 1); + if (err == -1) + { + __set_errno (old_errno); + return __lchown (file, owner, group); + } - filelen = strlen (file) + 1; - if (filelen > sizeof(path)) - { - errno = ENAMETOOLONG; - return -1; - } - memcpy (path, file, filelen); + filelen = strlen (file) + 1; + if (filelen > sizeof (path)) + { + __set_errno (ENAMETOOLONG); + return -1; + } + memcpy (path, file, filelen); - /* 'The system has an arbitrary limit...' In practise, we'll hit - ENAMETOOLONG before this, usually. */ - for (loopct = 0; loopct < 128; loopct++) - { - size_t linklen; + /* 'The system has an arbitrary limit...' In practise, we'll hit + ENAMETOOLONG before this, usually. */ + for (loopct = 0; loopct < 128; ++loopct) + { + size_t linklen; - if (err >= PATH_MAX+1) - { - errno = ENAMETOOLONG; - return -1; - } + if (err >= PATH_MAX + 1) + { + __set_errno (ENAMETOOLONG); + return -1; + } link[err] = 0; /* Null-terminate string, just-in-case. */ @@ -97,28 +102,29 @@ __chown (const char *file, uid_t owner, gid_t group) { filelen = strlen (path); - while (filelen > 1 && path[filelen-1] == '/') - filelen--; - while (filelen > 0 && path[filelen-1] != '/') - filelen--; - if (filelen + linklen > sizeof(path)) + while (filelen > 1 && path[filelen - 1] == '/') + --filelen; + while (filelen > 0 && path[filelen - 1] != '/') + --filelen; + if (filelen + linklen > sizeof (path)) { errno = ENAMETOOLONG; return -1; } - memcpy (path+filelen, link, linklen); + memcpy (path + filelen, link, linklen); } - err = __readlink(path, link, PATH_MAX+1); + err = __readlink (path, link, PATH_MAX + 1); if (err == -1) - { - errno = old_errno; - return __lchown(path, owner, group); - } - } - errno = ELOOP; - return -1; + { + __set_errno (old_errno); + return __lchown (path, owner, group); + } + } + __set_errno (ELOOP); + return -1; +#endif } libc_hidden_def (__chown) diff --git a/sysdeps/unix/sysv/linux/powerpc/fchownat.c b/sysdeps/unix/sysv/linux/powerpc/fchownat.c new file mode 100644 index 0000000000..1fbae5c448 --- /dev/null +++ b/sysdeps/unix/sysv/linux/powerpc/fchownat.c @@ -0,0 +1,205 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + In Linux 2.1.x the chown functions have been changed. A new function lchown + was introduced. The new chown now follows symlinks - the old chown and the + new lchown do not follow symlinks. + This file emulates chown() under the old kernels. +*/ + +int +fchownat (int fd, const char *file, uid_t owner, gid_t group, int flag) +{ + if (flag & ~AT_SYMLINK_NOFOLLOW) + { + __set_errno (EINVAL); + return -1; + } + + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + + int result; + INTERNAL_SYSCALL_DECL (err); + +#if __ASSUME_LCHOWN_SYSCALL + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lchown, err, 3, file, owner, group); + else + result = INTERNAL_SYSCALL (chown, err, 3, file, owner, group); +#else + char link[PATH_MAX + 2]; + char path[2 * PATH_MAX + 4]; + int loopct; + size_t filelen; + static int libc_old_chown = 0 /* -1=old linux, 1=new linux, 0=unknown */; + + if (libc_old_chown == 1) + { + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lchown, err, 3, __ptrvalue (file), owner, + group); + else + result = INTERNAL_SYSCALL (chown, err, 3, __ptrvalue (file), owner, + group); + goto out; + } + +# ifdef __NR_lchown + if (flag & AT_SYMLINK_NOFOLLOW) + { + result = INTERNAL_SYSCALL (lchown, err, 3, __ptrvalue (file), owner, + group); + goto out; + } + + if (libc_old_chown == 0) + { + result = INTERNAL_SYSCALL (chown, err, 3, __ptrvalue (file), owner, + group); + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return result; + if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS) + { + libc_old_chown = 1; + goto fail; + } + libc_old_chown = -1; + } +# else + if (flag & AT_SYMLINK_NOFOLLOW) + { + result = INTERNAL_SYSCALL (chown, err, 3, __ptrvalue (file), owner, + group); + goto out; + } +# endif + + result = __readlink (file, link, PATH_MAX + 1); + if (result == -1) + { +# ifdef __NR_lchown + result = INTERNAL_SYSCALL (lchown, err, 3, __ptrvalue (file), owner, + group); +# else + result = INTERNAL_SYSCALL (chown, err, 3, __ptrvalue (file), owner, + group); +# endif + goto out; + } + + filelen = strlen (file) + 1; + if (filelen > sizeof (path)) + { + errno = ENAMETOOLONG; + return -1; + } + memcpy (path, file, filelen); + + /* 'The system has an arbitrary limit...' In practise, we'll hit + ENAMETOOLONG before this, usually. */ + for (loopct = 0; loopct < 128; ++loopct) + { + size_t linklen; + + if (result >= PATH_MAX + 1) + { + errno = ENAMETOOLONG; + return -1; + } + + link[result] = 0; /* Null-terminate string, just-in-case. */ + + linklen = strlen (link) + 1; + + if (link[0] == '/') + memcpy (path, link, linklen); + else + { + filelen = strlen (path); + + while (filelen > 1 && path[filelen - 1] == '/') + --filelen; + while (filelen > 0 && path[filelen - 1] != '/') + --filelen; + if (filelen + linklen > sizeof (path)) + { + errno = ENAMETOOLONG; + return -1; + } + memcpy (path + filelen, link, linklen); + } + + result = __readlink (path, link, PATH_MAX + 1); + + if (result == -1) + { +# ifdef __NR_lchown + result = INTERNAL_SYSCALL (lchown, err, 3, path, owner, group); +# else + result = INTERNAL_SYSCALL (chown, err, 3, path, owner, group); +# endif + goto out; + } + } + __set_errno (ELOOP); + return -1; + + out: +#endif + + if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (result, err), 0)) + { +#if !__ASSUME_LCHOWN_SYSCALL + fail: +#endif + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (result, err), fd, buf); + result = -1; + } + + return result; +} diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fxstatat.c b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fxstatat.c new file mode 100644 index 0000000000..0f8b3135d8 --- /dev/null +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fxstatat.c @@ -0,0 +1 @@ +#include diff --git a/sysdeps/unix/sysv/linux/renameat.c b/sysdeps/unix/sysv/linux/renameat.c new file mode 100644 index 0000000000..31662ea053 --- /dev/null +++ b/sysdeps/unix/sysv/linux/renameat.c @@ -0,0 +1,121 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + + +/* Rename the file OLD relative to OLDFD to NEW relative to NEWFD. */ +int +renameat (oldfd, old, newfd, new) + int oldfd; + const char *old; + int newfd; + const char *new; +{ + static const char procfd[] = "/proc/self/fd/%d/%s"; + char *bufold = NULL; + + if (oldfd != AT_FDCWD && old[0] != '/') + { + size_t filelen = strlen (old); + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + bufold = alloca (buflen); + + __snprintf (bufold, buflen, procfd, oldfd, old); + old = bufold; + } + + char *bufnew = NULL; + + if (newfd != AT_FDCWD && new[0] != '/') + { + size_t filelen = strlen (new); + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + bufnew = alloca (buflen); + + __snprintf (bufnew, buflen, procfd, newfd, new); + new = bufnew; + } + + INTERNAL_SYSCALL_DECL (err); + + int result = INTERNAL_SYSCALL (rename, err, 2, old, new); + + if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (result, err), 0)) + { + int errval = INTERNAL_SYSCALL_ERRNO (result, err); + if (errval == ENOTDIR && (bufnew != NULL || bufold != NULL)) + { + /* This can mean either the file descriptor is invalid or + /proc is not mounted. */ + struct stat64 st; + + if (bufnew != NULL) + { + if (__fxstat64 (_STAT_VER, newfd, &st) != 0) + /* errno is already set correctly. */ + return -1; + + /* If /proc is not mounted there is nothing we can do. */ + if (S_ISDIR (st.st_mode) + && (__xstat64 (_STAT_VER, "/proc/self/fd", &st) != 0 + || !S_ISDIR (st.st_mode))) + { + errval = ENOSYS; + goto out; + } + } + + if (bufold != NULL) + { + if (__fxstat64 (_STAT_VER, oldfd, &st) != 0) + /* errno is already set correctly. */ + return -1; + + /* If /proc is not mounted there is nothing we can do. */ + if (S_ISDIR (st.st_mode) + && (__xstat64 (_STAT_VER, "/proc/self/fd", &st) != 0 + || !S_ISDIR (st.st_mode))) + errval = ENOSYS; + } + } + + out: + __set_errno (errval); + result = -1; + } + + return result; +} diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/fchownat.c b/sysdeps/unix/sysv/linux/s390/s390-32/fchownat.c new file mode 100644 index 0000000000..b95665cad3 --- /dev/null +++ b/sysdeps/unix/sysv/linux/s390/s390-32/fchownat.c @@ -0,0 +1,140 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "kernel-features.h" + +/* + In Linux 2.1.x the chown functions have been changed. A new function lchown + was introduced. The new chown now follows symlinks - the old chown and the + new lchown do not follow symlinks. + The new lchown function has the same number as the old chown had and the + new chown has a new number. When compiling with headers from Linux > 2.1.8x + it's impossible to run this libc with older kernels. In these cases libc + has therefore to route calls to chown to the old chown function. +*/ + +/* Running under Linux > 2.1.80. */ + +#ifdef __NR_chown32 +# if __ASSUME_32BITUIDS == 0 +/* This variable is shared with all files that need to check for 32bit + uids. */ +extern int __libc_missing_32bit_uids; +# endif +#endif /* __NR_chown32 */ + +int +fchownat (int fd, const char *file, uid_t owner, gid_t group, int flag) +{ + if (flag & ~AT_SYMLINK_NOFOLLOW) + { + __set_errno (EINVAL); + return -1; + } + + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + + int result; + INTERNAL_SYSCALL_DECL (err); + +#if __ASSUME_32BITUIDS > 0 + result = INTERNAL_SYSCALL (chown32, err, 3, CHECK_STRING (file), owner, + group); +#else + static int __libc_old_chown; + +# ifdef __NR_chown32 + if (__libc_missing_32bit_uids <= 0) + { + if (flag & AT_SYMLINK_NOFOLLOW) + result = INTERNAL_SYSCALL (lchown32, err, 3, CHECK_STRING (file), + owner, group); + else + result = INTERNAL_SYSCALL (chown32, err, 3, CHECK_STRING (file), + owner, group); + + if (__builtin_expect (!INTERNAL_SYSCALL_ERROR_P (result, err), 1)) + return result; + if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS) + goto fail; + + __libc_missing_32bit_uids = 1; + } +# endif /* __NR_chown32 */ + if (((owner + 1) > (uid_t) ((__kernel_uid_t) -1U)) + || ((group + 1) > (gid_t) ((__kernel_gid_t) -1U))) + { + __set_errno (EINVAL); + return -1; + } + + if (!__libc_old_chown && (flag & AT_SYMLINK_NOFOLLOW) == 0) + { + result = INTERNAL_SYSCALL (chown, err, 3, CHECK_STRING (file), owner, + group); + + if (!INTERNAL_SYSCALL_ERROR_P (result, err)) + return result; + if (INTERNAL_SYSCALL_ERRNO (result, err) != ENOSYS) + goto fail; + + __libc_old_chown = 1; + } + + result = INTERNAL_SYSCALL (lchown, err, 3, CHECK_STRING (file), owner, + group); +#endif + + if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (result, err), 0)) + { + fail: + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (result, err), fd, buf); + result = -1; + } + + return result; +} diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/fxstatat.c b/sysdeps/unix/sysv/linux/s390/s390-32/fxstatat.c new file mode 100644 index 0000000000..0f8b3135d8 --- /dev/null +++ b/sysdeps/unix/sysv/linux/s390/s390-32/fxstatat.c @@ -0,0 +1 @@ +#include diff --git a/sysdeps/unix/sysv/linux/sh/fchownat.c b/sysdeps/unix/sysv/linux/sh/fchownat.c new file mode 100644 index 0000000000..7acf7918ee --- /dev/null +++ b/sysdeps/unix/sysv/linux/sh/fchownat.c @@ -0,0 +1 @@ +#include diff --git a/sysdeps/unix/sysv/linux/sh/fxstatat.c b/sysdeps/unix/sysv/linux/sh/fxstatat.c new file mode 100644 index 0000000000..0f8b3135d8 --- /dev/null +++ b/sysdeps/unix/sysv/linux/sh/fxstatat.c @@ -0,0 +1 @@ +#include diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/fchownat.c b/sysdeps/unix/sysv/linux/sparc/sparc32/fchownat.c new file mode 100644 index 0000000000..7acf7918ee --- /dev/null +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/fchownat.c @@ -0,0 +1 @@ +#include diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/fxstatat.c b/sysdeps/unix/sysv/linux/sparc/sparc32/fxstatat.c new file mode 100644 index 0000000000..0f8b3135d8 --- /dev/null +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/fxstatat.c @@ -0,0 +1 @@ +#include diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/fxstatat.c b/sysdeps/unix/sysv/linux/sparc/sparc64/fxstatat.c new file mode 100644 index 0000000000..c2610b2a06 --- /dev/null +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/fxstatat.c @@ -0,0 +1 @@ +#include "../../fxstatat.c" diff --git a/sysdeps/unix/sysv/linux/unlinkat.c b/sysdeps/unix/sysv/linux/unlinkat.c new file mode 100644 index 0000000000..c590e71ada --- /dev/null +++ b/sysdeps/unix/sysv/linux/unlinkat.c @@ -0,0 +1,74 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include +#include +#include +#include +#include + + +/* Remove the link named NAME. */ +int +unlinkat (fd, file, flag) + int fd; + const char *file; + int flag; +{ + if (flag & ~AT_REMOVEDIR) + { + __set_errno (EINVAL); + return -1; + } + + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + + int result; + INTERNAL_SYSCALL_DECL (err); + + if (flag & AT_REMOVEDIR) + result = INTERNAL_SYSCALL (rmdir, err, 1, file); + else + result = INTERNAL_SYSCALL (unlink, err, 1, file); + + if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (result, err), 0)) + { + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (result, err), fd, buf); + result = -1; + } + + return result; +} diff --git a/sysdeps/unix/sysv/linux/wordsize-64/fxstatat.c b/sysdeps/unix/sysv/linux/wordsize-64/fxstatat.c new file mode 100644 index 0000000000..70e73571f7 --- /dev/null +++ b/sysdeps/unix/sysv/linux/wordsize-64/fxstatat.c @@ -0,0 +1,78 @@ +/* Copyright (C) 2005 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Ho hum, since fxstatat == fxstatat64 we must get rid of the + prototype or gcc will complain since they don't strictly match. */ +#define __fxstatat64 __fxstatat64_disable + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Get information about the file NAME relative to FD in ST. */ +int +__fxstatat (int vers, int fd, const char *file, struct stat *st, int flag) +{ + if ((vers != _STAT_VER_KERNEL && vers != _STAT_VER_LINUX) + || (flag & ~AT_SYMLINK_NOFOLLOW) != 0) + { + __set_errno (EINVAL); + return -1; + } + + char *buf = NULL; + + if (fd != AT_FDCWD && file[0] != '/') + { + size_t filelen = strlen (file); + static const char procfd[] = "/proc/self/fd/%d/%s"; + /* Buffer for the path name we are going to use. It consists of + - the string /proc/self/fd/ + - the file descriptor number + - the file name provided. + The final NUL is included in the sizeof. A bit of overhead + due to the format elements compensates for possible negative + numbers. */ + size_t buflen = sizeof (procfd) + sizeof (int) * 3 + filelen; + buf = alloca (buflen); + + __snprintf (buf, buflen, procfd, fd, file); + file = buf; + } + + INTERNAL_SYSCALL_DECL (err); + int res; + + if (flag & AT_SYMLINK_NOFOLLOW) + res = INTERNAL_SYSCALL (lstat, err, 2, file, CHECK_1 (st)); + else + res = INTERNAL_SYSCALL (stat, err, 2, file, CHECK_1 (st)); + + if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0)) + __atfct_seterrno (INTERNAL_SYSCALL_ERRNO (res, err), fd, buf); + + return res; +} +#undef __fxstatat64 +strong_alias (__fxstatat, __fxstatat64); diff --git a/sysdeps/unix/sysv/linux/wordsize-64/fxstatat64.c b/sysdeps/unix/sysv/linux/wordsize-64/fxstatat64.c new file mode 100644 index 0000000000..05e7f413bb --- /dev/null +++ b/sysdeps/unix/sysv/linux/wordsize-64/fxstatat64.c @@ -0,0 +1 @@ +/* fxstatat64 is in fxstatat.c */ diff --git a/time/sys/time.h b/time/sys/time.h index 515ea7ec68..199125bff3 100644 --- a/time/sys/time.h +++ b/time/sys/time.h @@ -1,4 +1,4 @@ -/* Copyright (C) 1991-1994,1996-2002,2003 Free Software Foundation, Inc. +/* Copyright (C) 1991-1994,1996-2002,2003,2005 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -70,13 +70,14 @@ typedef void *__restrict __timezone_ptr_t; NOTE: This form of timezone information is obsolete. Use the functions and variables declared in instead. */ extern int gettimeofday (struct timeval *__restrict __tv, - __timezone_ptr_t __tz) __THROW; + __timezone_ptr_t __tz) __THROW __nonnull ((1)); #ifdef __USE_BSD /* Set the current time of day and timezone information. This call is restricted to the super-user. */ extern int settimeofday (__const struct timeval *__tv, - __const struct timezone *__tz) __THROW; + __const struct timezone *__tz) + __THROW __nonnull ((1)); /* Adjust the current time of day by the amount in DELTA. If OLDDELTA is not NULL, it is filled in with the amount @@ -136,17 +137,25 @@ extern int setitimer (__itimer_which_t __which, FILE to TVP[1]. If TVP is a null pointer, use the current time instead. Returns 0 on success, -1 on errors. */ extern int utimes (__const char *__file, __const struct timeval __tvp[2]) - __THROW; + __THROW __nonnull ((1)); #ifdef __USE_BSD /* Same as `utimes', but does not follow symbolic links. */ extern int lutimes (__const char *__file, __const struct timeval __tvp[2]) - __THROW; + __THROW __nonnull ((1)); /* Same as `utimes', but takes an open file descriptor instead of a name. */ extern int futimes (int __fd, __const struct timeval __tvp[2]) __THROW; #endif +#ifdef __USE_GNU +/* Change the access time of FILE relative to FD to TVP[0] and the + modification time of FILE to TVP[1]. If TVP is a null pointer, use + the current time instead. Returns 0 on success, -1 on errors. */ +extern int futimesat (int __fd, __const char *__file, + __const struct timeval __tvp[2]) __THROW __nonnull ((2)); +#endif + #ifdef __USE_BSD /* Convenience macros for operations on timevals.