/* provide a replacement openat function
- Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/* written by Jim Meyering */
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include <config.h>
#include "openat.h"
+#include <stdarg.h>
+#include <stddef.h>
+
#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 <stdarg.h>
-#include <stddef.h>
-#include <errno.h>
/* Replacement for Solaris' openat function.
<http://www.google.com/search?q=openat+site:docs.sun.com>
- 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. */
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);
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.
<http://www.google.com/search?q=fdopendir+site:docs.sun.com>
- 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.
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. */
if (dir)
close (fd);
+ if (proc_file != buf)
+ free (proc_file);
errno = saved_errno;
return dir;
}
/* Replacement for Solaris' function by the same name.
<http://www.google.com/search?q=fstatat+site:docs.sun.com>
- 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.
<http://www.google.com/search?q=unlinkat+site:docs.sun.com>
- 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));
+#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
- {
- 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;
-}
+/* Replacement for Solaris' function by the same name.
+ Invoke chown or lchown on file, FILE, using OWNER and GROUP, in the
+ directory open on descriptor FD. If FLAG is AT_SYMLINK_NOFOLLOW, then
+ use lchown, otherwise, use chown. If possible, do it without changing
+ the working directory. Otherwise, resort to using save_cwd/fchdir,
+ then mkdir/restore_cwd. If either the save_cwd or the restore_cwd
+ fails, then give a diagnostic and exit nonzero. */
+
+#define AT_FUNC_NAME fchownat
+#define AT_FUNC_F1 lchown
+#define AT_FUNC_F2 chown
+#define AT_FUNC_USE_F1_COND flag == AT_SYMLINK_NOFOLLOW
+#define AT_FUNC_POST_FILE_PARAM_DECLS , uid_t owner, gid_t group, int flag
+#define AT_FUNC_POST_FILE_ARGS , owner, group
+#include "at-func.c"