X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fopenat.c;h=cd496540e7129d52e27c441aa96c95f6512ab1e1;hb=fa662f811f9a6dda54f64639b107b1637c3ba2e5;hp=6eb5a2e73d84a381537baf3577ab615f43f3080a;hpb=8eacc6e87ef59621f773289ca13733d05873a94f;p=gnulib.git diff --git a/lib/openat.c b/lib/openat.c index 6eb5a2e73..cd496540e 100644 --- a/lib/openat.c +++ b/lib/openat.c @@ -17,27 +17,25 @@ /* written by Jim Meyering */ -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include "openat.h" +#include +#include + #include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */ #include "fcntl--.h" +#include "lchown.h" +#include "lstat.h" #include "openat-priv.h" #include "save-cwd.h" -#include "unistd--.h" - -#include -#include -#include /* Replacement for Solaris' openat function. - Simulate it by doing save_cwd/fchdir/open/restore_cwd. - If either the save_cwd or the restore_cwd fails (relatively unlikely, - and usually indicative of a problem that deserves close attention), + First, try to simulate it via open ("/proc/self/fd/FD/FILE"). + Failing that, simulate it by doing save_cwd/fchdir/open/restore_cwd. + If either the save_cwd or the restore_cwd fails (relatively unlikely), then give a diagnostic and exit nonzero. Otherwise, upon failure, set errno and return -1, as openat does. Upon successful completion, return a file descriptor. */ @@ -88,14 +86,23 @@ openat_permissive (int fd, char const *file, int flags, mode_t mode, return open (file, flags, mode); { - char *proc_file; - BUILD_PROC_NAME (proc_file, fd, file); - err = open (proc_file, flags, mode); - /* If the syscall succeeds, or if it fails with an unexpected - errno value, then return right away. Otherwise, fall through - and resort to using save_cwd/restore_cwd. */ - if (0 <= err || ! EXPECTED_ERRNO (errno)) - return err; + char buf[OPENAT_BUFFER_SIZE]; + char *proc_file = openat_proc_name (buf, fd, file); + if (proc_file) + { + int open_result = open (proc_file, flags, mode); + int open_errno = errno; + if (proc_file != buf) + free (proc_file); + /* If the syscall succeeds, or if it fails with an unexpected + errno value, then return right away. Otherwise, fall through + and resort to using save_cwd/restore_cwd. */ + if (0 <= open_result || ! EXPECTED_ERRNO (open_errno)) + { + errno = open_errno; + return open_result; + } + } } save_ok = (save_cwd (&saved_cwd) == 0); @@ -126,13 +133,37 @@ openat_permissive (int fd, char const *file, int flags, mode_t mode, return err; } +/* Return true if our openat implementation must resort to + using save_cwd and restore_cwd. */ +bool +openat_needs_fchdir (void) +{ + bool needs_fchdir = true; + int fd = open ("/", O_RDONLY); + + if (0 <= fd) + { + char buf[OPENAT_BUFFER_SIZE]; + char *proc_file = openat_proc_name (buf, fd, "."); + if (proc_file) + { + needs_fchdir = false; + if (proc_file != buf) + free (proc_file); + } + close (fd); + } + + return needs_fchdir; +} + #if !HAVE_FDOPENDIR /* Replacement for Solaris' function by the same name. - Simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd. - If either the save_cwd or the restore_cwd fails (relatively unlikely, - and usually indicative of a problem that deserves close attention), + First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing + that, simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd. + If either the save_cwd or the restore_cwd fails (relatively unlikely), then give a diagnostic and exit nonzero. Otherwise, this function works just like Solaris' fdopendir. @@ -147,10 +178,18 @@ fdopendir (int fd) int saved_errno; DIR *dir; - char *proc_file; - BUILD_PROC_NAME (proc_file, fd, "."); - dir = opendir (proc_file); - saved_errno = errno; + char buf[OPENAT_BUFFER_SIZE]; + char *proc_file = openat_proc_name (buf, fd, "."); + if (proc_file) + { + dir = opendir (proc_file); + saved_errno = errno; + } + else + { + dir = NULL; + saved_errno = EOPNOTSUPP; + } /* If the syscall fails with an expected errno value, resort to save_cwd/restore_cwd. */ @@ -178,6 +217,8 @@ fdopendir (int fd) if (dir) close (fd); + if (proc_file != buf) + free (proc_file); errno = saved_errno; return dir; } @@ -186,102 +227,44 @@ fdopendir (int fd) /* Replacement for Solaris' function by the same name. - Simulate it by doing save_cwd/fchdir/(stat|lstat)/restore_cwd. - If either the save_cwd or the restore_cwd fails (relatively unlikely, - and usually indicative of a problem that deserves close attention), + First, try to simulate it via l?stat ("/proc/self/fd/FD/FILE"). + Failing that, simulate it via save_cwd/fchdir/(stat|lstat)/restore_cwd. + If either the save_cwd or the restore_cwd fails (relatively unlikely), then give a diagnostic and exit nonzero. Otherwise, this function works just like Solaris' fstatat. */ -int -fstatat (int fd, char const *file, struct stat *st, int flag) -{ - struct saved_cwd saved_cwd; - int saved_errno; - int err; - - if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file)) - return (flag == AT_SYMLINK_NOFOLLOW - ? lstat (file, st) - : stat (file, st)); - - { - char *proc_file; - BUILD_PROC_NAME (proc_file, fd, file); - err = (flag == AT_SYMLINK_NOFOLLOW - ? lstat (proc_file, st) - : stat (proc_file, st)); - /* If the syscall succeeds, or if it fails with an unexpected - errno value, then return right away. Otherwise, fall through - and resort to using save_cwd/restore_cwd. */ - if (0 <= err || ! EXPECTED_ERRNO (errno)) - return err; - } - - if (save_cwd (&saved_cwd) != 0) - openat_save_fail (errno); - - err = fchdir (fd); - saved_errno = errno; - - if (! err) - { - err = (flag == AT_SYMLINK_NOFOLLOW - ? lstat (file, st) - : stat (file, st)); - saved_errno = errno; - if (restore_cwd (&saved_cwd) != 0) - openat_restore_fail (errno); - } - - free_cwd (&saved_cwd); - errno = saved_errno; - return err; -} +#define AT_FUNC_NAME fstatat +#define AT_FUNC_F1 lstat +#define AT_FUNC_F2 stat +#define AT_FUNC_USE_F1_COND flag == AT_SYMLINK_NOFOLLOW +#define AT_FUNC_POST_FILE_PARAM_DECLS , struct stat *st, int flag +#define AT_FUNC_POST_FILE_ARGS , st +#include "at-func.c" +#undef AT_FUNC_NAME +#undef AT_FUNC_F1 +#undef AT_FUNC_F2 +#undef AT_FUNC_USE_F1_COND +#undef AT_FUNC_POST_FILE_PARAM_DECLS +#undef AT_FUNC_POST_FILE_ARGS /* Replacement for Solaris' function by the same name. - Simulate it by doing save_cwd/fchdir/(unlink|rmdir)/restore_cwd. - If either the save_cwd or the restore_cwd fails (relatively unlikely, - and usually indicative of a problem that deserves close attention), + First, try to simulate it via (unlink|rmdir) ("/proc/self/fd/FD/FILE"). + Failing that, simulate it via save_cwd/fchdir/(unlink|rmdir)/restore_cwd. + If either the save_cwd or the restore_cwd fails (relatively unlikely), then give a diagnostic and exit nonzero. Otherwise, this function works just like Solaris' unlinkat. */ -int -unlinkat (int fd, char const *file, int flag) -{ - struct saved_cwd saved_cwd; - int saved_errno; - int err; - - if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file)) - return (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file)); - { - char *proc_file; - BUILD_PROC_NAME (proc_file, fd, file); - err = (flag == AT_REMOVEDIR ? rmdir (proc_file) : unlink (proc_file)); - /* If the syscall succeeds, or if it fails with an unexpected - errno value, then return right away. Otherwise, fall through - and resort to using save_cwd/restore_cwd. */ - if (0 <= err || ! EXPECTED_ERRNO (errno)) - return err; - } - - if (save_cwd (&saved_cwd) != 0) - openat_save_fail (errno); - - err = fchdir (fd); - saved_errno = errno; - - if (! err) - { - err = (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file)); - saved_errno = errno; - - if (restore_cwd (&saved_cwd) != 0) - openat_restore_fail (errno); - } - - free_cwd (&saved_cwd); - errno = saved_errno; - return err; -} +#define AT_FUNC_NAME unlinkat +#define AT_FUNC_F1 rmdir +#define AT_FUNC_F2 unlink +#define AT_FUNC_USE_F1_COND flag == AT_REMOVEDIR +#define AT_FUNC_POST_FILE_PARAM_DECLS , int flag +#define AT_FUNC_POST_FILE_ARGS /* empty */ +#include "at-func.c" +#undef AT_FUNC_NAME +#undef AT_FUNC_F1 +#undef AT_FUNC_F2 +#undef AT_FUNC_USE_F1_COND +#undef AT_FUNC_POST_FILE_PARAM_DECLS +#undef AT_FUNC_POST_FILE_ARGS