From: Paul Eggert Date: Thu, 17 Aug 2006 20:34:21 +0000 (+0000) Subject: Update from coreutils. X-Git-Tag: cvs-readonly~2035 X-Git-Url: http://erislabs.net/gitweb/?a=commitdiff_plain;h=8b3f78cdd40155b5864692b63b6a368c7dabd050;p=gnulib.git Update from coreutils. * lib/__fpending.h: Add copyright notice. * lib/fprintftime.h: Likewise. * lib/savedir.c: Use (C) in copyright notice. * lib/savedir.h: Likewise. 2006-08-15 Jim Meyering * lib/at-func.c: New file, with the logic of all emulated at-functions. * lib/openat-priv.h: Include and define ENOSYS, in support of the EXPECTED_ERRNO macro. * lib/openat.c (fstatat, unlinkat, fchownat): Remove function definitions. Instead, define the appropriate symbols and include "at-func.c". * lib/mkdirat.c (mkdirat): Likewise. * lib/fchmodat.c (fchmodat): Likewise. (ENOSYS): Remove definition. * lib/openat.c: Don't include , now that "openat-priv.h" does it. Don't include "unistd--.h" -- it wasn't ever used. 2006-01-17 Jim Meyering Rewrite fts.c not to change the current working directory, by using openat, fstatat, fdopendir, etc.. * lib/fts.c [! _LIBC]: Include "openat.h" and "unistd--.h". (HAVE_OPENAT_SUPPORT): Define. [_LIBC] (fchdir): Don't undef or define; no longer used. (FCHDIR): Define in terms of cwd_advance_fd rather than fchdir. Now, this `function' always succeeds, and consumes its file descriptor parameter -- so callers must not close such FDs. Update callers. (diropen_fd, opendirat, cwd_advance_fd): New functions. (diropen): Add parameter, SP. Adjust all callers. Implement using diropen_fd, rather than open. (fts_open): Initialize new member, fts_cwd_fd. Remove fts_rft-setting code. (fts_close): Close fts_cwd_fd, if necessary. (__opendir2): Define in terms of opendir or opendirat, depending on whether the FST_NOCHDIR flag is set. (fts_build): Since fts_safe_changedir consumes its FD, and since this code must do `closedir(dirp)', dup the dirfd(dirp) argument, and close the dup'd file descriptor upon failure. (fts_stat): Use fstatat(...AT_SYMLINK_NOFOLLOW) in place of lstat. (fts_safe_changedir): Tweak semantics to reflect that this function now calls cwd_advance_fd and hence consumes its FD argument. * lib/fts_.h [struct FTS] (fts_cwd_fd): New member. [struct FTS] (fts_rft): Remove now-unused member. [struct FTS] (fts_cycle.state): Improve comment. * lib/openat.c (openat_needs_fchdir): New function. * lib/openat.h (openat_needs_fchdir): Declare it. 2006-08-15 Jim Meyering * m4/openat.m4 (gl_FUNC_OPENAT): Add at-func.c via AC_LIBSOURCES. 2006-01-17 Jim Meyering * m4/fts.m4 (gl_FUNC_FTS_CORE): Depend on gl_FUNC_OPENAT. 2006-01-11 Jim Meyering * m4/openat.m4 (gl_FUNC_OPENAT): Require and compile fchmodat.c. Check for the lchmod function. --- diff --git a/lib/ChangeLog b/lib/ChangeLog index d90fa9361..2b1f94dea 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,57 @@ +2006-08-17 Paul Eggert + + Update from coreutils. + + * __fpending.h: Add copyright notice. + * fprintftime.h: Likewise. + * savedir.c: Use (C) in copyright notice. + * savedir.h: Likewise. + + 2006-08-15 Jim Meyering + + * at-func.c: New file, with the logic of all emulated at-functions. + * openat-priv.h: Include and define ENOSYS, + in support of the EXPECTED_ERRNO macro. + * openat.c (fstatat, unlinkat, fchownat): Remove function definitions. + Instead, define the appropriate symbols and include "at-func.c". + * mkdirat.c (mkdirat): Likewise. + * fchmodat.c (fchmodat): Likewise. + (ENOSYS): Remove definition. + * openat.c: Don't include , now that "openat-priv.h" does it. + Don't include "unistd--.h" -- it wasn't ever used. + + 2006-01-17 Jim Meyering + + Rewrite fts.c not to change the current working directory, + by using openat, fstatat, fdopendir, etc.. + + * fts.c [! _LIBC]: Include "openat.h" and "unistd--.h". + (HAVE_OPENAT_SUPPORT): Define. + [_LIBC] (fchdir): Don't undef or define; no longer used. + (FCHDIR): Define in terms of cwd_advance_fd rather than fchdir. + Now, this `function' always succeeds, and consumes its file descriptor + parameter -- so callers must not close such FDs. Update callers. + (diropen_fd, opendirat, cwd_advance_fd): New functions. + (diropen): Add parameter, SP. Adjust all callers. + Implement using diropen_fd, rather than open. + (fts_open): Initialize new member, fts_cwd_fd. + Remove fts_rft-setting code. + (fts_close): Close fts_cwd_fd, if necessary. + (__opendir2): Define in terms of opendir or opendirat, + depending on whether the FST_NOCHDIR flag is set. + (fts_build): Since fts_safe_changedir consumes its FD, and since + this code must do `closedir(dirp)', dup the dirfd(dirp) argument, + and close the dup'd file descriptor upon failure. + (fts_stat): Use fstatat(...AT_SYMLINK_NOFOLLOW) in place of lstat. + (fts_safe_changedir): Tweak semantics to reflect that this function + now calls cwd_advance_fd and hence consumes its FD argument. + * fts_.h [struct FTS] (fts_cwd_fd): New member. + [struct FTS] (fts_rft): Remove now-unused member. + [struct FTS] (fts_cycle.state): Improve comment. + + * openat.c (openat_needs_fchdir): New function. + * openat.h (openat_needs_fchdir): Declare it. + 2006-08-16 Paul Eggert * memcoll.c (memcoll): Set errno = 0 in the shortcut case, too. diff --git a/lib/__fpending.h b/lib/__fpending.h index 36a842ed5..3c9be1e08 100644 --- a/lib/__fpending.h +++ b/lib/__fpending.h @@ -1,3 +1,23 @@ +/* Declare __fpending. + + Copyright (C) 2000, 2003, 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Written by Jim Meyering. */ + #include #include diff --git a/lib/at-func.c b/lib/at-func.c new file mode 100644 index 000000000..2bdea7b93 --- /dev/null +++ b/lib/at-func.c @@ -0,0 +1,75 @@ +/* Define an at-style functions like fstatat, unlinkat, fchownat, etc. + Copyright (C) 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 + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* written by Jim Meyering */ + +#define CALL_FUNC(F) \ + (AT_FUNC_USE_F1_COND \ + ? AT_FUNC_F1 (F AT_FUNC_POST_FILE_ARGS) \ + : AT_FUNC_F2 (F AT_FUNC_POST_FILE_ARGS)) + +/* Call AT_FUNC_F1 or AT_FUNC_F2 (testing AT_FUNC_USE_F1_COND to + determine which) to operate on FILE, which is in the directory + open on descriptor FD. If possible, do it without changing the + working directory. Otherwise, resort to using save_cwd/fchdir, + then AT_FUNC_F?/restore_cwd. If either the save_cwd or the restore_cwd + fails, then give a diagnostic and exit nonzero. */ +int +AT_FUNC_NAME (int fd, char const *file AT_FUNC_POST_FILE_PARAM_DECLS) +{ + struct saved_cwd saved_cwd; + int saved_errno; + int err; + + if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file)) + return CALL_FUNC (file); + + { + char *proc_file; + BUILD_PROC_NAME (proc_file, fd, file); + err = CALL_FUNC (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); + + if (fchdir (fd) != 0) + { + saved_errno = errno; + free_cwd (&saved_cwd); + errno = saved_errno; + return -1; + } + + err = CALL_FUNC (file); + saved_errno = (err < 0 ? errno : 0); + + if (restore_cwd (&saved_cwd) != 0) + openat_restore_fail (errno); + + free_cwd (&saved_cwd); + + if (saved_errno) + errno = saved_errno; + return err; +} +#undef CALL_FUNC diff --git a/lib/fprintftime.h b/lib/fprintftime.h index 38fef43f1..e0be5cded 100644 --- a/lib/fprintftime.h +++ b/lib/fprintftime.h @@ -1,3 +1,21 @@ +/* Generate time strings directly to the output. */ + +/* Copyright (C) 2005 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + #include #include diff --git a/lib/fts.c b/lib/fts.c index 73ee36031..ea33805ea 100644 --- a/lib/fts.c +++ b/lib/fts.c @@ -72,7 +72,10 @@ static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94"; #include #if ! _LIBC +# include "fcntl--.h" # include "lstat.h" +# include "openat.h" +# include "unistd--.h" #endif #include @@ -112,6 +115,17 @@ static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94"; # define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) #endif +#if !defined O_NOFOLLOW +# define O_NOFOLLOW 0 +#endif + +/* If this host provides the openat function, then we can avoid + attempting to open "." in some initialization code below. */ +#ifdef HAVE_OPENAT +# define HAVE_OPENAT_SUPPORT 1 +#else +# define HAVE_OPENAT_SUPPORT 0 +#endif static FTSENT *fts_alloc (FTS *, const char *, size_t) internal_function; static FTSENT *fts_build (FTS *, int) internal_function; @@ -148,12 +162,21 @@ static void free_dir (FTS *fts) {} #endif #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) +#define STREQ(a, b) (strcmp ((a), (b)) == 0) #define CLR(opt) (sp->fts_options &= ~(opt)) #define ISSET(opt) (sp->fts_options & (opt)) #define SET(opt) (sp->fts_options |= (opt)) -#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) +#define RESTORE_INITIAL_CWD(sp) FCHDIR (sp, (ISSET (FTS_CWDFD) \ + ? AT_FDCWD \ + : sp->fts_rfd)) + +#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) \ + && (ISSET(FTS_CWDFD) \ + ? (cwd_advance_fd (sp, fd), 0) \ + : fchdir(fd))) + /* fts_build flags */ #define BCHILD 1 /* fts_children */ @@ -178,15 +201,56 @@ bool fts_debug = false; } \ while (false) +/* file-descriptor-relative opendir. */ +/* FIXME: if others need this function, move it into lib/openat.c */ +static inline DIR * +internal_function +opendirat (int fd, char const *dir) +{ + int new_fd = openat (fd, dir, O_RDONLY); + DIR *dirp; + + if (new_fd < 0) + return NULL; + dirp = fdopendir (new_fd); + if (dirp == NULL) + { + int saved_errno = errno; + close (new_fd); + errno = saved_errno; + } + return dirp; +} + +/* Virtual fchdir. Advance SP's working directory + file descriptor, SP->fts_cwd_fd, to FD, and close + the previous one, ignoring any error. */ +static void +internal_function +cwd_advance_fd (FTS *sp, int fd) +{ + int old = sp->fts_cwd_fd; + if (old == fd && old != AT_FDCWD) + abort (); + sp->fts_cwd_fd = fd; + if (0 <= old) + close (old); /* ignore any close failure */ +} + /* Open the directory DIR if possible, and return a file descriptor. Return -1 and set errno on failure. It doesn't matter whether the file descriptor has read or write access. */ -static int +static inline int internal_function -diropen (char const *dir) +diropen (FTS const *sp, char const *dir) { - return open (dir, O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK); + int open_flags = (O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK + | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0)); + + return (ISSET (FTS_CWDFD) + ? openat (sp->fts_cwd_fd, dir, open_flags) + : open (dir, open_flags)); } FTS * @@ -197,7 +261,8 @@ fts_open (char * const *argv, register FTS *sp; register FTSENT *p, *root; register size_t nitems; - FTSENT *parent, *tmp = NULL; /* pacify gcc */ + FTSENT *parent = NULL; + FTSENT *tmp = NULL; /* pacify gcc */ size_t len; /* Options check. */ @@ -205,6 +270,14 @@ fts_open (char * const *argv, __set_errno (EINVAL); return (NULL); } + if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) { + __set_errno (EINVAL); + return (NULL); + } + if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) { + __set_errno (EINVAL); + return (NULL); + } /* Allocate/initialize the stream */ if ((sp = malloc(sizeof(FTS))) == NULL) @@ -214,8 +287,44 @@ fts_open (char * const *argv, sp->fts_options = options; /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ - if (ISSET(FTS_LOGICAL)) + if (ISSET(FTS_LOGICAL)) { SET(FTS_NOCHDIR); + CLR(FTS_CWDFD); + } + + /* Initialize fts_cwd_fd. */ + sp->fts_cwd_fd = AT_FDCWD; + if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT) + { + /* While it isn't technically necessary to open "." this + early, doing it here saves us the trouble of ensuring + later (where it'd be messier) that "." can in fact + be opened. If not, revert to FTS_NOCHDIR mode. */ + int fd = open (".", O_RDONLY); + if (fd < 0) + { + /* Even if `.' is unreadable, don't revert to FTS_NOCHDIR mode + on systems like Linux+PROC_FS, where our openat emulation + is good enough. Note: on a system that emulates + openat via /proc, this technique can still fail, but + only in extreme conditions, e.g., when the working + directory cannot be saved (i.e. save_cwd fails) -- + and that happens only on Linux only when "." is unreadable + and the CWD would be longer than PATH_MAX. + FIXME: once Linux kernel openat support is well established, + replace the above open call and this entire if/else block + with the body of the if-block below. */ + if ( openat_needs_fchdir ()) + { + SET(FTS_NOCHDIR); + CLR(FTS_CWDFD); + } + } + else + { + close (fd); + } + } /* * Start out with 1K of file name space, and enough, in any case, @@ -231,9 +340,11 @@ fts_open (char * const *argv, } /* Allocate/initialize root's parent. */ - if ((parent = fts_alloc(sp, "", 0)) == NULL) - goto mem2; - parent->fts_level = FTS_ROOTPARENTLEVEL; + if (*argv != NULL) { + if ((parent = fts_alloc(sp, "", 0)) == NULL) + goto mem2; + parent->fts_level = FTS_ROOTPARENTLEVEL; + } /* Allocate/initialize root(s). */ for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) { @@ -293,8 +404,8 @@ fts_open (char * const *argv, * and ".." are all fairly nasty problems. Note, if we can't get the * descriptor we run anyway, just more slowly. */ - if (!ISSET(FTS_NOCHDIR) - && (sp->fts_rfd = diropen (".")) < 0) + if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD) + && (sp->fts_rfd = diropen (sp, ".")) < 0) SET(FTS_NOCHDIR); return (sp); @@ -358,12 +469,18 @@ fts_close (FTS *sp) free(sp->fts_array); free(sp->fts_path); - /* Return to original directory, save errno if necessary. */ - if (!ISSET(FTS_NOCHDIR)) { - if (fchdir(sp->fts_rfd)) - saved_errno = errno; - (void)close(sp->fts_rfd); - } + if (ISSET(FTS_CWDFD)) + { + if (0 <= sp->fts_cwd_fd) + close (sp->fts_cwd_fd); + } + else if (!ISSET(FTS_NOCHDIR)) + { + /* Return to original directory, save errno if necessary. */ + if (fchdir(sp->fts_rfd)) + saved_errno = errno; + close(sp->fts_rfd); + } free_dir (sp); @@ -393,7 +510,6 @@ fts_read (register FTS *sp) register FTSENT *p, *tmp; register unsigned short int instr; register char *t; - int saved_errno; /* If finished or unrecoverable error, return NULL. */ if (sp->fts_cur == NULL || ISSET(FTS_STOP)) @@ -424,7 +540,7 @@ fts_read (register FTS *sp) (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { p->fts_info = fts_stat(sp, p, true); if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { - if ((p->fts_symfd = diropen (".")) < 0) { + if ((p->fts_symfd = diropen (sp, ".")) < 0) { p->fts_errno = errno; p->fts_info = FTS_ERR; } else @@ -485,7 +601,6 @@ fts_read (register FTS *sp) subdirectory, tell the caller. */ if (p->fts_errno) p->fts_info = FTS_ERR; - /* FIXME: see if this should be in an else block */ LEAVE_DIR (sp, p, "2"); return (p); } @@ -505,7 +620,7 @@ next: tmp = p; * root. */ if (p->fts_level == FTS_ROOTLEVEL) { - if (FCHDIR(sp, sp->fts_rfd)) { + if (RESTORE_INITIAL_CWD(sp)) { SET(FTS_STOP); sp->fts_cur = p; return (NULL); @@ -524,7 +639,7 @@ next: tmp = p; if (p->fts_instr == FTS_FOLLOW) { p->fts_info = fts_stat(sp, p, true); if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) { - if ((p->fts_symfd = diropen (".")) < 0) { + if ((p->fts_symfd = diropen (sp, ".")) < 0) { p->fts_errno = errno; p->fts_info = FTS_ERR; } else @@ -568,18 +683,19 @@ check_for_dir: sp->fts_path[p->fts_pathlen] = '\0'; /* - * Return to the parent directory. If at a root node or came through - * a symlink, go back through the file descriptor. Otherwise, cd up - * one directory. + * Return to the parent directory. If at a root node, restore + * the initial working directory. If we came through a symlink, + * go back through the file descriptor. Otherwise, move up + * one level, via "..". */ if (p->fts_level == FTS_ROOTLEVEL) { - if (FCHDIR(sp, sp->fts_rfd)) { + if (RESTORE_INITIAL_CWD(sp)) { p->fts_errno = errno; SET(FTS_STOP); } } else if (p->fts_flags & FTS_SYMFOLLOW) { if (FCHDIR(sp, p->fts_symfd)) { - saved_errno = errno; + int saved_errno = errno; (void)close(p->fts_symfd); __set_errno (saved_errno); p->fts_errno = errno; @@ -674,16 +790,24 @@ fts_children (register FTS *sp, int instr) ISSET(FTS_NOCHDIR)) return (sp->fts_child = fts_build(sp, instr)); - if ((fd = diropen (".")) < 0) + if ((fd = diropen (sp, ".")) < 0) return (sp->fts_child = NULL); sp->fts_child = fts_build(sp, instr); - if (fchdir(fd)) { + if (ISSET(FTS_CWDFD)) + { + cwd_advance_fd (sp, fd); + } + else + { + if (fchdir(fd)) + { int saved_errno = errno; - (void)close(fd); + close (fd); __set_errno (saved_errno); - return (NULL); - } - (void)close(fd); + return NULL; + } + close (fd); + } return (sp->fts_child); } @@ -711,7 +835,6 @@ fts_build (register FTS *sp, int type) FTSENT *cur, *tail; DIR *dirp; void *oldaddr; - int cderrno; int saved_errno; bool descend; bool doadjust; @@ -734,7 +857,10 @@ fts_build (register FTS *sp, int type) else oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND; #else -# define __opendir2(file, flag) opendir(file) +# define __opendir2(file, flag) \ + ( ! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD) \ + ? opendirat(sp->fts_cwd_fd, file) \ + : opendir(file)) #endif if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { if (type == BREAD) { @@ -777,15 +903,18 @@ fts_build (register FTS *sp, int type) * needed sorted entries or stat information, they had better be * checking FTS_NS on the returned nodes. */ - cderrno = 0; if (nlinks || type == BREAD) { - if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { + int dir_fd = dirfd(dirp); + if (ISSET(FTS_CWDFD) && 0 <= dir_fd) + dir_fd = dup (dir_fd); + if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) { if (nlinks && type == BREAD) cur->fts_errno = errno; cur->fts_flags |= FTS_DONTCHDIR; descend = false; - cderrno = errno; closedir(dirp); + if (ISSET(FTS_CWDFD) && 0 <= dir_fd) + close (dir_fd); dirp = NULL; } else descend = true; @@ -877,37 +1006,19 @@ mem1: saved_errno = errno; p->fts_flags |= FTS_ISW; #endif - if (cderrno) { - if (nlinks) { - p->fts_info = FTS_NS; - p->fts_errno = cderrno; - } else - p->fts_info = FTS_NSOK; - p->fts_accpath = cur->fts_accpath; - } else if (nlinks == 0 -#if HAVE_STRUCT_DIRENT_D_TYPE - || (nostat && - dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) -#endif - ) { - p->fts_accpath = - ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; - p->fts_info = FTS_NSOK; - } else { - /* Build a file name for fts_stat to stat. */ - if (ISSET(FTS_NOCHDIR)) { - p->fts_accpath = p->fts_path; - memmove(cp, p->fts_name, p->fts_namelen + 1); - } else - p->fts_accpath = p->fts_name; - /* Stat it. */ - p->fts_info = fts_stat(sp, p, false); - - /* Decrement link count if applicable. */ - if (nlinks > 0 && (p->fts_info == FTS_D || - p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) - nlinks -= nostat; - } + /* Build a file name for fts_stat to stat. */ + if (ISSET(FTS_NOCHDIR)) { + p->fts_accpath = p->fts_path; + memmove(cp, p->fts_name, p->fts_namelen + 1); + } else + p->fts_accpath = p->fts_name; + /* Stat it. */ + p->fts_info = fts_stat(sp, p, false); + + /* Decrement link count if applicable. */ + if (nlinks > 0 && (p->fts_info == FTS_D || + p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) + nlinks -= nostat; /* We walk in directory order so "ls -f" doesn't get upset. */ p->fts_link = NULL; @@ -947,11 +1058,12 @@ mem1: saved_errno = errno; * can't get back, we're done. */ if (descend && (type == BCHILD || !nitems) && - (cur->fts_level == FTS_ROOTLEVEL ? - FCHDIR(sp, sp->fts_rfd) : - fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { + (cur->fts_level == FTS_ROOTLEVEL + ? RESTORE_INITIAL_CWD(sp) + : fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { cur->fts_info = FTS_ERR; SET(FTS_STOP); + fts_lfree(head); return (NULL); } @@ -959,6 +1071,7 @@ mem1: saved_errno = errno; if (!nitems) { if (type == BREAD) cur->fts_info = FTS_DP; + fts_lfree(head); return (NULL); } @@ -1063,7 +1176,8 @@ fts_stat(FTS *sp, register FTSENT *p, bool follow) p->fts_errno = saved_errno; goto err; } - } else if (lstat(p->fts_accpath, sbp)) { + } else if (fstatat(sp->fts_cwd_fd, p->fts_accpath, sbp, + AT_SYMLINK_NOFOLLOW)) { p->fts_errno = errno; err: memset(sbp, 0, sizeof(struct stat)); return (FTS_NS); @@ -1291,34 +1405,64 @@ fts_maxarglen (char * const *argv) * Change to dir specified by fd or file name without getting * tricked by someone changing the world out from underneath us. * Assumes p->fts_statp->st_dev and p->fts_statp->st_ino are filled in. + * If FD is non-negative, expect it to be used after this function returns, + * and to be closed eventually. So don't pass e.g., `dirfd(dirp)' and then + * do closedir(dirp), because that would invalidate the saved FD. + * Upon failure, close FD immediately and return nonzero. */ static int internal_function fts_safe_changedir (FTS *sp, FTSENT *p, int fd, char const *dir) { - int ret, oerrno, newfd; - struct stat sb; + int ret; - newfd = fd; - if (ISSET(FTS_NOCHDIR)) + int newfd = fd; + if (ISSET(FTS_NOCHDIR)) { + if (ISSET(FTS_CWDFD) && 0 <= fd) + close (fd); return (0); - if (fd < 0 && (newfd = diropen (dir)) < 0) + } + if (fd < 0 && (newfd = diropen (sp, dir)) < 0) return (-1); - if (fstat(newfd, &sb)) { + + /* The following dev/inode check is necessary if we're doing + a `logical' traversal (through symlinks, a la chown -L), + if the system lacks O_NOFOLLOW support, or if we're changing + to "..". In the latter case, O_NOFOLLOW can't help. In + general (when the target is not ".."), diropen's use of + O_NOFOLLOW ensures we don't mistakenly follow a symlink, + so we can avoid the expense of this fstat. */ + if (ISSET(FTS_LOGICAL) || O_NOFOLLOW == 0 + || (dir && STREQ (dir, ".."))) + { + struct stat sb; + if (fstat(newfd, &sb)) + { ret = -1; goto bail; - } - if (p->fts_statp->st_dev != sb.st_dev - || p->fts_statp->st_ino != sb.st_ino) { + } + if (p->fts_statp->st_dev != sb.st_dev + || p->fts_statp->st_ino != sb.st_ino) + { __set_errno (ENOENT); /* disinformation */ ret = -1; goto bail; - } + } + } + + if (ISSET(FTS_CWDFD)) + { + cwd_advance_fd (sp, newfd); + return 0; + } + ret = fchdir(newfd); bail: - oerrno = errno; if (fd < 0) - (void)close(newfd); - __set_errno (oerrno); + { + int oerrno = errno; + (void)close(newfd); + __set_errno (oerrno); + } return (ret); } diff --git a/lib/fts_.h b/lib/fts_.h index 42e06c987..2843107ea 100644 --- a/lib/fts_.h +++ b/lib/fts_.h @@ -1,6 +1,6 @@ /* Traverse a file hierarchy. - 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 @@ -73,8 +73,10 @@ typedef struct { dev_t fts_dev; /* starting device # */ char *fts_path; /* file name for this descent */ int fts_rfd; /* fd for root */ + int fts_cwd_fd; /* the file descriptor on which the + virtual cwd is open, or AT_FDCWD */ size_t fts_pathlen; /* sizeof(path) */ - size_t fts_nitems; /* elements in the sort array */ + size_t fts_nitems; /* elements in the sort array */ int (*fts_compar) (struct _ftsent const **, struct _ftsent const **); /* compare fn */ @@ -112,7 +114,16 @@ typedef struct { mode of cycle detection: FTS_TIGHT_CYCLE_CHECK. */ # define FTS_TIGHT_CYCLE_CHECK 0x0100 -# define FTS_OPTIONMASK 0x01ff /* valid user option mask */ + /* Use this flag to enable semantics with which the parent + application may be made both more efficient and more robust. + Whereas the default is to visit each directory in a recursive + traversal (via chdir), using this flag makes it so the initial + working directory is never changed. Instead, these functions + perform the traversal via a virtual working directory, maintained + through the file descriptor member, fts_cwd_fd. */ +# define FTS_CWDFD 0x0200 + +# define FTS_OPTIONMASK 0x03ff /* valid user option mask */ # define FTS_NAMEONLY 0x1000 /* (private) child names only */ # define FTS_STOP 0x2000 /* (private) unrecoverable error */ @@ -134,9 +145,10 @@ typedef struct { of thousands. */ struct hash_table *ht; - /* This data structure uses lazy checking, as done by rm via - cycle-check.c. It's the default, but it's not appropriate - for programs like du. */ + /* FIXME: rename these two members to have the fts_ prefix */ + /* This data structure uses a lazy cycle-detection algorithm, + as done by rm via cycle-check.c. It's the default, + but it's not appropriate for programs like du. */ struct cycle_check_state *state; } fts_cycle; # endif diff --git a/lib/mkdirat.c b/lib/mkdirat.c index 6103c6f15..dc75eb12e 100644 --- a/lib/mkdirat.c +++ b/lib/mkdirat.c @@ -23,17 +23,10 @@ #include "openat.h" -#include #include -#include -#include #include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */ #include "save-cwd.h" - -#include "gettext.h" -#define _(msgid) gettext (msgid) - #include "openat-priv.h" /* Solaris 10 has no function like this. @@ -42,47 +35,11 @@ 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. */ -int -mkdirat (int fd, char const *file, mode_t mode) -{ - struct saved_cwd saved_cwd; - int saved_errno; - int err; - - if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file)) - return mkdir (file, mode); - - { - char *proc_file; - BUILD_PROC_NAME (proc_file, fd, file); - err = mkdir (proc_file, 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; - } - - if (save_cwd (&saved_cwd) != 0) - openat_save_fail (errno); - - if (fchdir (fd) != 0) - { - saved_errno = errno; - free_cwd (&saved_cwd); - errno = saved_errno; - return -1; - } - - err = mkdir (file, mode); - saved_errno = (err < 0 ? errno : 0); - - if (restore_cwd (&saved_cwd) != 0) - openat_restore_fail (errno); - - free_cwd (&saved_cwd); - if (saved_errno) - errno = saved_errno; - return err; -} +#define AT_FUNC_NAME mkdirat +#define AT_FUNC_F1 mkdir +#define AT_FUNC_F2 mkdir +#define AT_FUNC_USE_F1_COND 1 +#define AT_FUNC_POST_FILE_PARAM_DECLS , mode_t mode +#define AT_FUNC_POST_FILE_ARGS , mode +#include "at-func.c" diff --git a/lib/openat-priv.h b/lib/openat-priv.h index 2b4780eec..1f420c175 100644 --- a/lib/openat-priv.h +++ b/lib/openat-priv.h @@ -19,6 +19,7 @@ #include #include +#include #include "alloca.h" #include "intprops.h" @@ -41,6 +42,16 @@ } \ while (0) +/* Some systems don't have ENOSYS. */ +#ifndef ENOSYS +# ifdef ENOTSUP +# define ENOSYS ENOTSUP +# else +/* Some systems don't have ENOTSUP either. */ +# define ENOSYS EINVAL +# endif +#endif + /* Trying to access a BUILD_PROC_NAME file will fail on systems without /proc support, and even on systems *with* ProcFS support. Return nonzero if the failure may be legitimate, e.g., because /proc is not diff --git a/lib/openat.c b/lib/openat.c index 6eb5a2e73..ca71d658e 100644 --- a/lib/openat.c +++ b/lib/openat.c @@ -23,21 +23,20 @@ #include "openat.h" +#include +#include + #include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */ #include "fcntl--.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. */ @@ -126,13 +125,33 @@ 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) +{ + int fd2; + int fd = open ("/", O_RDONLY); + char *proc_file; + + if (fd < 0) + return true; + BUILD_PROC_NAME (proc_file, fd, "."); + fd2 = open (proc_file, O_RDONLY); + close (fd); + if (0 <= fd2) + close (fd2); + + return fd2 < 0; +} + #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. @@ -186,102 +205,60 @@ 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)); +#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" diff --git a/lib/openat.h b/lib/openat.h index fd1bca502..9eca973b5 100644 --- a/lib/openat.h +++ b/lib/openat.h @@ -75,14 +75,45 @@ DIR *fdopendir (int fd); int fstatat (int fd, char const *file, struct stat *st, int flag); # define unlinkat __OPENAT_ID (unlinkat) int unlinkat (int fd, char const *file, int flag); +bool openat_needs_fchdir (void); #else # define openat_permissive(Fd, File, Flags, Mode, Cwd_errno) \ openat (Fd, File, Flags, Mode) +# define openat_needs_fchdir() false #endif int mkdirat (int fd, char const *file, mode_t mode); void openat_restore_fail (int) ATTRIBUTE_NORETURN; void openat_save_fail (int) ATTRIBUTE_NORETURN; +int fchmodat (int fd, char const *file, mode_t mode, int flag); +int fchownat (int fd, char const *file, uid_t owner, gid_t group, int flag); + +/* Using these function names makes application code + slightly more readable than it would be with + fchownat (..., 0) or fchownat (..., AT_SYMLINK_NOFOLLOW). */ +static inline int +chownat (int fd, char const *file, uid_t owner, gid_t group) +{ + return fchownat (fd, file, owner, group, 0); +} + +static inline int +lchownat (int fd, char const *file, uid_t owner, gid_t group) +{ + return fchownat (fd, file, owner, group, AT_SYMLINK_NOFOLLOW); +} + +static inline int +chmodat (int fd, char const *file, mode_t mode) +{ + return fchmodat (fd, file, mode, 0); +} + +static inline int +lchmodat (int fd, char const *file, mode_t mode) +{ + return fchmodat (fd, file, mode, AT_SYMLINK_NOFOLLOW); +} diff --git a/lib/savedir.c b/lib/savedir.c index 4b0702480..3c442aa6e 100644 --- a/lib/savedir.c +++ b/lib/savedir.c @@ -1,7 +1,7 @@ /* savedir.c -- save the list of files in a directory in a string - Copyright 1990, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006 Free - Software Foundation, Inc. + Copyright (C) 1990, 1997, 1998, 1999, 2000, 2001, 2003, 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 diff --git a/lib/savedir.h b/lib/savedir.h index 7809d2392..5b7bef971 100644 --- a/lib/savedir.h +++ b/lib/savedir.h @@ -1,6 +1,6 @@ /* Save the list of files in a directory in a string. - Copyright 1997, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. + Copyright (C) 1997, 1999, 2001, 2003, 2005 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 diff --git a/m4/ChangeLog b/m4/ChangeLog index 1634c4f83..484415238 100644 --- a/m4/ChangeLog +++ b/m4/ChangeLog @@ -1,3 +1,20 @@ +2006-08-17 Paul Eggert + + Update from coreutils. + + 2006-08-15 Jim Meyering + + * openat.m4 (gl_FUNC_OPENAT): Add at-func.c via AC_LIBSOURCES. + + 2006-01-17 Jim Meyering + + * fts.m4 (gl_FUNC_FTS_CORE): Depend on gl_FUNC_OPENAT. + + 2006-01-11 Jim Meyering + + * openat.m4 (gl_FUNC_OPENAT): Require and compile fchmodat.c. + Check for the lchmod function. + 2006-08-11 Bruno Haible * wcwidth.m4 (gl_FUNC_WCWIDTH): Include and before diff --git a/m4/fts.m4 b/m4/fts.m4 index 2e56094c6..ebb249826 100644 --- a/m4/fts.m4 +++ b/m4/fts.m4 @@ -1,4 +1,4 @@ -#serial 9 +#serial 10 dnl Copyright (C) 2005, 2006 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -27,6 +27,7 @@ AC_DEFUN([gl_FUNC_FTS_CORE], AC_LIBOBJ([fts]) dnl Prerequisites of lib/fts.c. + AC_REQUIRE([gl_FUNC_OPENAT]) # Checks for header files. AC_CHECK_HEADERS_ONCE([sys/param.h])dnl diff --git a/m4/openat.m4 b/m4/openat.m4 index cc34fb0ba..0abb8c87d 100644 --- a/m4/openat.m4 +++ b/m4/openat.m4 @@ -1,5 +1,5 @@ -#serial 8 -# See if we need to use our replacement for Solaris' openat function. +#serial 9 +# See if we need to use our replacement for Solaris' openat et al functions. dnl Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation @@ -10,15 +10,18 @@ dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([gl_FUNC_OPENAT], [ - AC_LIBSOURCES([openat.c, openat.h, openat-priv.h, openat-die.c]) - AC_LIBSOURCES([mkdirat.c]) + AC_LIBSOURCES([openat.c, openat.h, openat-priv.h, openat-die.c, at-func.c]) AC_LIBSOURCES([intprops.h]) + AC_LIBSOURCES([mkdirat.c]) + AC_LIBSOURCES([fchmodat.c]) - # No system provides a mkdirat function; compile it unconditionally. + # No system provides these functions; compile them unconditionally. AC_LIBOBJ([mkdirat]) + AC_LIBOBJ([fchmodat]) AC_LIBOBJ([openat-die]) AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_CHECK_FUNCS_ONCE([lchmod]) AC_CHECK_FUNCS_ONCE([fdopendir]) AC_REPLACE_FUNCS(openat) case $ac_cv_func_openat in diff --git a/modules/fts b/modules/fts index ebd99ec25..d6c95f870 100644 --- a/modules/fts +++ b/modules/fts @@ -12,8 +12,10 @@ cycle-check dirfd hash lstat +openat stdbool fcntl-safer +unistd-safer configure.ac: gl_FUNC_FTS diff --git a/modules/openat b/modules/openat index 761264323..cbc23cea9 100644 --- a/modules/openat +++ b/modules/openat @@ -2,6 +2,7 @@ Description: Open a file at a directory. Files: +lib/at-func.c lib/mkdirat.c lib/openat.c lib/openat.h