changeset 6527:6b31c8787689

Sync from coreutils. * doc/getdate.texi (General date syntax): Invalid dates are rejected. (Time of day items): Mention the possibility of leap seconds. Problem reported by Dr. David Alan Gilbert. * lib/chdir-long.c (cdb_free): Don't bother trying to open directory for write access: POSIX says that must fail. * lib/fts.c (diropen): Likewise. * lib/save-cwd.c (save_cwd): Likewise. * lib/chdir-long.c (cdb_free): Open with O_NOCTTY | O_NONBLOCK as well, for minor improvements on hosts that lack O_DIRECTORY. * lib/gettime.c (gettime) [!defined OK_TO_USE_1S_CLOCK]: Report an error at compile-time if only a 1-second nominal clock resolution is found. * lib/lchmod.h: New file. * lib/mkdir-p.c: Include lchmod.h, lchown.h. (make_dir_parents): Use lchown rather than chown, and lchmod rather than chmod. * lib/mountlist.c (ME_DUMMY): "none" and "proc" file systems are dummies too. Problem with "none" reported by Bob Proulx. Problem with "proc" reported by n0dalus. * lib/mountlist.c: Include <limits.h>. (dev_from_mount_options) [defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2]: New function. It no longer assumes "dev=" has the System V meaning on Linux (since it doesn't). It also parses "dev=" more carefully. (read_file_system_list) [defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2]: Use it. MOUNTED_GETMNTENT2 is new here; the code didn't used to look for dev= in that case. * lib/posixtm.h (PDS_PRE_2000): New macro. * lib/posixtm.c (year): Arg is now syntax_bits rather than allow_century. All usages changed. Reject dates outside the range 1969-1999 if PDS_PRE_2000 is used. * modules/mkdir-p (Files): Add chdir-safer.c, chdir-safer.h, lchmod.h, chdir-safer.m4, lchmod.m4. * modules/openat: Add mkdirat.c, openat-priv.h. * modules/lib-ignore: New file. * lib/version-etc.c (COPYRIGHT_YEAR): Update to 2006. Rewrite fts.c not to change the current working directory, by using openat, fstatat, fdopendir, etc.. * lib/fts.c [! _LIBC]: Include "openat.h", "unistd--.h", and "fcntl--.h". [_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. (fts_rft): Remove now-unused member. * lib/openat.c (fchownat): New function. * lib/openat.h (fchmodat, fchownat): Declare. (chmodat, lchmodat): Define convenience functions. (chownat, lchownat): Likewise. * lib/chdir-safer.h, chdir-safer.c: New files. * lib/modechange.c (mode_compile): Reject an invalid mode string that starts with an octal digit. From Andreas Gruenbacher. * lib/openat.c: Include "fcntl--.h" and "unistd--.h", to map open and dup to open_safer and dup_safer, respectively. (openat_permissive): Fix typo in comment. * lib/openat.c: Don't include <stdlib.h>, <unistd.h>, <fcntl.h>, "gettext.h"; either no longer needed or are guaranteed by openat.h. (_): Remove; no longer needed. (openat): Renamed from rpl_openat; no need for rpl_openat since openat.h renames openat for us. Replace most of the body with a call to openat_permissive, to avoid duplicate code. Port to (probably hypothetical) environments were mode_t is wider than int. (openat_permissive): Require mode arg, so that we can check types better. Put it just after flags. Change cwd failure indicator from pointer-to-bool to pointer-to-errno-value. All callers changed. Invoke openat_save_fail and/or openat_restore_fail if cwd_errno is null, so that openat can call us. (openat_permissive, fdopendir, fstatat, unlinkat): Simplify errno handling to avoid some duplicate code, as it's OK to set errno on success. * lib/openat.h: Revamp code so that function macros depend on __OPENAT_PREFIX only, not also on AT_FDCWD. (openat_ro): Remove. Caller changed to use openat_permissive. (openat_permissive): Now a macro, if not a function. (openat_restore_fail, openat_save_fail): Now always functions, since mkdirat needs them even if __OPENAT_PREFIX is defined. * lib/openat-priv.h: New file, defining macros used by mkdirat.c and openat.c. * lib/mkdirat.c: Include openat-priv.h. Remove definitions of macros defined therein. * lib/openat.c: Likewise. * lib/mkdirat.c (mkdirat): New file and function. * lib/openat.h (mkdirat): Declare. * lib/openat.c (fdopendir): Don't change errno when returning non-NULL. * lib/openat.h (openat_permissive): Declare. (openat_ro): Define. * lib/openat.c (EXPECTED_ERRNO): New macro. (openat_permissive): New function -- used in remove.c rewrite. (all functions): Set errno just before returning, only if there was an actual failure. Use EXPECTED_ERRNO rather than comparing against only ENOTDIR. Emulate openat-family functions using Linux's procfs, if possible. Idea and some code based on Ulrich Drepper's glibc changes. * lib/openat.c: (BUILD_PROC_NAME): New macro. Include <stdio.h>, <string.h>, "alloca.h" and "intprops.h". (rpl_openat): Emulate by trying to open /proc/self/fd/%d/%s, before falling back on save_cwd and restore_cwd. (fdopendir, fstatat, unlinkat): Likewise. * lib/openat.c (fstatat, unlinkat): Perform the syscall directly, skipping the save_cwd...restore_cwd overhead, if FILE is absolute. * lib/openat.c (rpl_openat): Use the promoted type (int), not mode_t, as second argument to va_arg. Otherwise, some versions of gcc warn that `if this code is reached, the program will abort'. Add POSIX ACL support * lib/acl.h (copy_acl, set_acl): Add declarations. * lib/acl.c (acl_entries): Add fallback implementation for POSIX ACL systems other than Linux. (chmod_or_fchmod): New function: use fchmod when possible, and chmod otherwise. (file_has_acl): Add a POSIX ACL implementation, with a Linux-specific subcase. (copy_acl): Add: copy an acl and S_ISUID, S_ISGID, and S_ISVTX from one file to another. Fall back to fchmod/chmod when acls are unsupported. (set_acl): Add: set a file's acl and S_ISUID, S_ISGID, and S_ISVTX to a defined value. Fall back to fchmod/chmod when acls are unsupported. * m4/lib-ignore.m4: New file. * m4/lchmod.m4: New file. * m4/chdir-safer.m4: New file. * m4/openat.m4 (gl_FUNC_OPENAT): Require and compile mkdirat.c. Require openat-priv.h. * m4/acl.m4 (AC_FUNC_ACL): Add POSIX ACL and Linux-specific acl tests.
author Paul Eggert <eggert@cs.ucla.edu>
date Mon, 09 Jan 2006 23:13:56 +0000
parents c01f6f225013
children 1b09ff4edc40
files ChangeLog doc/ChangeLog doc/getdate.texi lib/ChangeLog lib/acl.c lib/acl.h lib/chdir-long.c lib/chdir-safer.c lib/chdir-safer.h lib/chown.c lib/fts.c lib/gettime.c lib/lchmod.h lib/mkdir-p.c lib/mkdirat.c lib/modechange.c lib/mountlist.c lib/openat-priv.h lib/openat.c lib/openat.h lib/posixtm.c lib/posixtm.h lib/save-cwd.c lib/version-etc.c m4/ChangeLog m4/acl.m4 m4/lchmod.m4 m4/lib-ignore.m4 m4/openat.m4 modules/lib-ignore modules/mkdir-p modules/openat
diffstat 32 files changed, 1291 insertions(+), 198 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2006-01-09  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Sync from coreutils:
+	* modules/mkdir-p (Files): Add chdir-safer.c, chdir-safer.h, lchmod.h,
+	chdir-safer.m4, lchmod.m4.
+	* modules/openat: Add mkdirat.c, openat-priv.h.
+
 2006-01-09  Bruno Haible  <bruno@clisp.org>
 
 	* modules/strnlen (Include): Use strnlen.h.
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,3 +1,10 @@
+2006-01-09  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Sync from coreutils.
+	* getdate.texi (General date syntax): Invalid dates are rejected.
+	(Time of day items): Mention the possibility of leap seconds.
+	Problem reported by Dr. David Alan Gilbert.
+
 2005-09-19  Bruno Haible  <bruno@clisp.org>
 
 	* quote.texi: New file, extracted from gnulib.texi.
--- a/doc/getdate.texi
+++ b/doc/getdate.texi
@@ -1,7 +1,7 @@
 @c GNU date syntax documentation
 
 @c Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-@c 2003, 2004, 2005 Free Software Foundation, Inc.
+@c 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
 
 @c Permission is granted to copy, distribute and/or modify this document
 @c under the terms of the GNU Free Documentation License, Version 1.1 or
@@ -144,6 +144,11 @@
 nested.  Hyphens not followed by a digit are currently ignored.  Leading
 zeros on numbers are ignored.
 
+Invalid dates like @samp{2005-02-29} or times like @samp{24:00} are
+rejected.  In the typical case of a host that does not support leap
+seconds, a time like @samp{23:59:60} is rejected even if it
+corresponds to a valid leap second.
+
 
 @node Calendar date items
 @section Calendar date items
@@ -238,7 +243,8 @@
 @samp{.} or @samp{,} and a fraction containing one or more digits.
 Alternatively,
 @samp{:@var{second}} can be omitted, in which case it is taken to
-be zero.
+be zero.  On the rare hosts that support leap seconds, @var{second}
+may be 60.
 
 @findex am @r{in date strings}
 @findex pm @r{in date strings}
@@ -464,8 +470,8 @@
 of seconds with nanosecond subcounts, and can represent all the times
 in the known lifetime of the universe to a resolution of 1 nanosecond.
 
-On most systems, these counts ignore the presence of leap seconds.
-For example, on most systems @samp{@@915148799} represents 1998-12-31
+On most hosts, these counts ignore the presence of leap seconds.
+For example, on most hosts @samp{@@915148799} represents 1998-12-31
 23:59:59 @sc{utc}, @samp{@@915148800} represents 1999-01-01 00:00:00
 @sc{utc}, and there is no way to represent the intervening leap second
 1998-12-31 23:59:60 @sc{utc}.
--- a/lib/ChangeLog
+++ b/lib/ChangeLog
@@ -1,3 +1,166 @@
+2006-01-09  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Sync from coreutils.
+	* chdir-long.c (cdb_free): Don't bother trying to open directory
+	for write access: POSIX says that must fail.
+	* fts.c (diropen): Likewise.
+	* save-cwd.c (save_cwd): Likewise.
+	* chdir-long.c (cdb_free): Open with O_NOCTTY | O_NONBLOCK as
+	well, for minor improvements on hosts that lack O_DIRECTORY.
+
+	* gettime.c (gettime) [!defined OK_TO_USE_1S_CLOCK]:
+	Report an error at compile-time if only a 1-second nominal clock
+	resolution is found.
+
+	* lchmod.h: New file.
+	* mkdir-p.c: Include lchmod.h, lchown.h.
+	(make_dir_parents): Use lchown rather than chown, and
+	lchmod rather than chmod.
+
+	* mountlist.c (ME_DUMMY): "none" and "proc" file systems are dummies
+	too.  Problem with "none" reported by Bob Proulx.  Problem with
+	"proc" reported by n0dalus.
+
+	* mountlist.c: Include <limits.h>.
+	(dev_from_mount_options)
+	[defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2]:
+	New function.  It no longer assumes "dev=" has the System V meaning
+	on Linux (since it doesn't).  It also parses "dev=" more carefully.
+	(read_file_system_list)
+	[defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2]: Use it.
+	MOUNTED_GETMNTENT2 is new here; the code didn't used to look for
+	dev= in that case.
+
+	* posixtm.h (PDS_PRE_2000): New macro.
+	* posixtm.c (year): Arg is now syntax_bits rather than allow_century.
+	All usages changed.  Reject dates outside the range 1969-1999 if
+	PDS_PRE_2000 is used.
+
+2006-01-09  Jim Meyering  <jim@meyering.net>
+
+	Sync from coreutils.
+
+	* version-etc.c (COPYRIGHT_YEAR): Update to 2006.
+
+	Rewrite fts.c not to change the current working directory,
+	by using openat, fstatat, fdopendir, etc..
+
+	* fts.c [! _LIBC]: Include "openat.h", "unistd--.h", and "fcntl--.h".
+	[_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.
+	(fts_rft): Remove now-unused member.
+
+	* openat.c (fchownat): New function.
+	* openat.h (fchmodat, fchownat): Declare.
+	(chmodat, lchmodat): Define convenience functions.
+	(chownat, lchownat): Likewise.
+
+	* chdir-safer.h, chdir-safer.c: New files.
+
+	* modechange.c (mode_compile): Reject an invalid mode string
+	that starts with an octal digit.  From Andreas Gruenbacher.
+
+	* openat.c: Include "fcntl--.h" and "unistd--.h", to map open
+	and dup to open_safer and dup_safer, respectively.
+	(openat_permissive): Fix typo in comment.
+
+	* openat.c: Don't include <stdlib.h>, <unistd.h>, <fcntl.h>,
+	"gettext.h"; either no longer needed or are guaranteed by openat.h.
+	(_): Remove; no longer needed.
+	(openat): Renamed from rpl_openat; no need for rpl_openat
+	since openat.h renames openat for us.
+	Replace most of the body with a call to openat_permissive,
+	to avoid duplicate code.
+	Port to (probably hypothetical) environments were mode_t is
+	wider than int.
+	(openat_permissive): Require mode arg, so that we can check
+	types better.  Put it just after flags.  Change cwd failure
+	indicator from pointer-to-bool to pointer-to-errno-value.
+	All callers changed.
+	Invoke openat_save_fail and/or openat_restore_fail if
+	cwd_errno is null, so that openat can call us.
+	(openat_permissive, fdopendir, fstatat, unlinkat):
+	Simplify errno handling to avoid some duplicate code,
+	as it's OK to set errno on success.
+	* openat.h: Revamp code so that function macros depend on
+	__OPENAT_PREFIX only, not also on AT_FDCWD.
+	(openat_ro): Remove.  Caller changed to use openat_permissive.
+	(openat_permissive): Now a macro, if not a function.
+	(openat_restore_fail, openat_save_fail): Now always functions,
+	since mkdirat needs them even if __OPENAT_PREFIX is defined.
+
+	* openat-priv.h: New file, defining macros used by mkdirat.c
+	and openat.c.
+	* mkdirat.c: Include openat-priv.h.
+	Remove definitions of macros defined therein.
+	* openat.c: Likewise.
+
+	* mkdirat.c (mkdirat): New file and function.
+	* openat.h (mkdirat): Declare.
+
+	* openat.c (fdopendir): Don't change errno when returning non-NULL.
+
+	* openat.h (openat_permissive): Declare.
+	(openat_ro): Define.
+
+	* openat.c (EXPECTED_ERRNO): New macro.
+	(openat_permissive): New function -- used in remove.c rewrite.
+	(all functions): Set errno just before returning, only if there
+	was an actual failure.
+	Use EXPECTED_ERRNO rather than comparing against only ENOTDIR.
+
+	Emulate openat-family functions using Linux's procfs, if possible.
+	Idea and some code based on Ulrich Drepper's glibc changes.
+
+	* openat.c: (BUILD_PROC_NAME): New macro.
+	Include <stdio.h>, <string.h>, "alloca.h" and "intprops.h".
+	(rpl_openat): Emulate by trying to open /proc/self/fd/%d/%s,
+	before falling back on save_cwd and restore_cwd.
+	(fdopendir, fstatat, unlinkat): Likewise.
+
+	* openat.c (fstatat, unlinkat): Perform the syscall directly,
+	skipping the save_cwd...restore_cwd overhead, if FILE is absolute.
+
+	* openat.c (rpl_openat): Use the promoted type (int), not mode_t,
+	as second argument to va_arg.  Otherwise, some versions of gcc
+	warn that `if this code is reached, the program will abort'.
+
+2006-01-09  Andreas Gruenbacher  <agruen@suse.de>
+
+	Sync from coreutils.
+
+	Add POSIX ACL support
+	* acl.h (copy_acl, set_acl): Add declarations.
+	* acl.c (acl_entries): Add fallback implementation for POSIX ACL
+	systems other than Linux.
+	(chmod_or_fchmod): New function: use fchmod when possible,
+	and chmod otherwise.
+	(file_has_acl): Add a POSIX ACL implementation, with a
+	Linux-specific subcase.
+	(copy_acl): Add: copy an acl and S_ISUID, S_ISGID, and
+	S_ISVTX from one file to another.  Fall back to fchmod/chmod when
+	acls are unsupported.
+	(set_acl): Add: set a file's acl and S_ISUID, S_ISGID, and
+	S_ISVTX to a defined value.  Fall back to fchmod/chmod when acls
+	are unsupported.
+
 2006-01-09  Bruno Haible  <bruno@clisp.org>
 
 	* sysexit_.h (EX_OK): New macro.
--- a/lib/acl.c
+++ b/lib/acl.c
@@ -16,48 +16,394 @@
    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 Paul Eggert.  */
+   Written by Paul Eggert and Andreas Gruenbacher.  */
 
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
 
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #ifndef S_ISLNK
 # define S_ISLNK(Mode) 0
 #endif
 
+#ifdef HAVE_ACL_LIBACL_H
+# include <acl/libacl.h>
+#endif
+
 #include "acl.h"
+#include "error.h"
+#include "quote.h"
 
 #include <errno.h>
 #ifndef ENOSYS
 # define ENOSYS (-1)
 #endif
+#ifndef ENOTSUP
+# define ENOTSUP (-1)
+#endif
 
-#ifndef MIN_ACL_ENTRIES
-# define MIN_ACL_ENTRIES 4
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+#else
+# define _(Text) Text
+#endif
+
+#ifndef HAVE_FCHMOD
+# define HAVE_FCHMOD false
+# define fchmod(fd, mode) (-1)
+#endif
+
+/* POSIX 1003.1e (draft 17) */
+#ifndef HAVE_ACL_GET_FD
+# define HAVE_ACL_GET_FD false
+# define acl_get_fd(fd) (NULL)
+#endif
+
+/* POSIX 1003.1e (draft 17) */
+#ifndef HAVE_ACL_SET_FD
+# define HAVE_ACL_SET_FD false
+# define acl_set_fd(fd, acl) (-1)
+#endif
+
+/* Linux-specific */
+#ifndef HAVE_ACL_EXTENDED_FILE
+# define HAVE_ACL_EXTENDED_FILE false
+# define acl_extended_file(name) (-1)
 #endif
 
-/* Return 1 if FILE has a nontrivial access control list, 0 if not,
-   and -1 (setting errno) if an error is encountered.  */
+/* Linux-specific */
+#ifndef HAVE_ACL_FROM_MODE
+# define HAVE_ACL_FROM_MODE false
+# define acl_from_mode(mode) (NULL)
+#endif
+
+/* We detect presence of POSIX 1003.1e (draft 17 -- abandoned) support
+   by checking for HAVE_ACL_GET_FILE, HAVE_ACL_SET_FILE, and HAVE_ACL_FREE.
+   Systems that have acl_get_file, acl_set_file, and acl_free must also
+   have acl_to_text, acl_from_text, and acl_delete_def_file (all defined
+   in the draft); systems that don't would hit #error statements here.  */
+
+#if USE_ACL && HAVE_ACL_GET_FILE && !HAVE_ACL_ENTRIES
+# ifndef HAVE_ACL_TO_TEXT
+#  error Must have acl_to_text (see POSIX 1003.1e draft 17).
+# endif
+
+/* Return the number of entries in ACL. Linux implements acl_entries
+   as a more efficient extension than using this workaround.  */
+
+static int
+acl_entries (acl_t acl)
+{
+  char *text = acl_to_text (acl, NULL), *t;
+  int entries;
+  if (text == NULL)
+    return -1;
+  for (entries = 0, t = text; ; t++, entries++) {
+    t = strchr (t, '\n');
+    if (t == NULL)
+      break;
+  }
+  acl_free (text);
+  return entries;
+}
+#endif
+
+/* If DESC is a valid file descriptor use fchmod to change the
+   file's mode to MODE on systems that have fchown. On systems
+   that don't have fchown and if DESC is invalid, use chown on
+   NAME instead.  */
 
 int
-file_has_acl (char const *file, struct stat const *filestat)
+chmod_or_fchmod (const char *name, int desc, mode_t mode)
+{
+  if (HAVE_FCHMOD && desc != -1)
+    return fchmod (desc, mode);
+  else
+    return chmod (name, mode);
+}
+
+/* Return 1 if NAME has a nontrivial access control list, 0 if
+   NAME only has no or a base access control list, and -1 on
+   error.  SB must be set to the stat buffer of FILE.  */
+
+int
+file_has_acl (char const *name, struct stat const *sb)
 {
-  /* FIXME: This implementation should work on recent-enough versions
-     of HP-UX, Solaris, and Unixware, but it simply returns 0 with
-     POSIX 1003.1e (draft 17 -- abandoned), AIX, GNU/Linux, Irix, and
-     Tru64.  Please see Samba's source/lib/sysacls.c file for
-     fix-related ideas.  */
+#if USE_ACL && HAVE_ACL && defined GETACLCNT
+  /* This implementation should work on recent-enough versions of HP-UX,
+     Solaris, and Unixware.  */
+
+# ifndef MIN_ACL_ENTRIES
+#  define MIN_ACL_ENTRIES 4
+# endif
+
+  if (! S_ISLNK (sb->st_mode))
+    {
+      int n = acl (name, GETACLCNT, 0, NULL);
+      return n < 0 ? (errno == ENOSYS ? 0 : -1) : (MIN_ACL_ENTRIES < n);
+    }
+#elif USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_FREE
+  /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
+
+  if (! S_ISLNK (sb->st_mode))
+    {
+      int ret;
 
-#if HAVE_ACL && defined GETACLCNT
-  if (! S_ISLNK (filestat->st_mode))
-    {
-      int n = acl (file, GETACLCNT, 0, NULL);
-      return n < 0 ? (errno == ENOSYS ? 0 : -1) : (MIN_ACL_ENTRIES < n);
+      if (HAVE_ACL_EXTENDED_FILE)
+	ret = acl_extended_file (name);
+      else
+	{
+	  acl_t acl = acl_get_file (name, ACL_TYPE_ACCESS);
+	  if (acl)
+	    {
+	      ret = (3 < acl_entries (acl));
+	      acl_free (acl);
+	      if (ret == 0 && S_ISDIR (sb->st_mode))
+		{
+		  acl = acl_get_file (name, ACL_TYPE_DEFAULT);
+		  if (acl)
+		    {
+		      ret = (0 < acl_entries (acl));
+		      acl_free (acl);
+		    }
+		  else
+		    ret = -1;
+		}
+	    }
+	  else
+	    ret = -1;
+	}
+      if (ret < 0)
+	return (errno == ENOSYS || errno == ENOTSUP) ? 0 : -1;
+      return ret;
     }
 #endif
 
+  /* FIXME: Add support for AIX, Irix, and Tru64.  Please see Samba's
+     source/lib/sysacls.c file for fix-related ideas.  */
+
   return 0;
 }
+
+/* Copy access control lists from one file to another. If SOURCE_DESC is
+   a valid file descriptor, use file descriptor operations, else use
+   filename based operations on SRC_NAME. Likewise for DEST_DESC and
+   DEST_NAME.
+   If access control lists are not available, fchmod the target file to
+   MODE.  Also sets the non-permission bits of the destination file
+   (S_ISUID, S_ISGID, S_ISVTX) to those from MODE if any are set.
+   System call return value semantics.  */
+
+int
+copy_acl (const char *src_name, int source_desc, const char *dst_name,
+	  int dest_desc, mode_t mode)
+{
+  int ret;
+
+#if USE_ACL && HAVE_ACL_GET_FILE && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
+  /* POSIX 1003.1e (draft 17 -- abandoned) specific version.  */
+
+  acl_t acl;
+  if (HAVE_ACL_GET_FD && source_desc != -1)
+    acl = acl_get_fd (source_desc);
+  else
+    acl = acl_get_file (src_name, ACL_TYPE_ACCESS);
+  if (acl == NULL)
+    {
+      if (errno == ENOSYS || errno == ENOTSUP)
+	return set_acl (dst_name, dest_desc, mode);
+      else
+        {
+	  error (0, errno, "%s", quote (src_name));
+	  return -1;
+	}
+    }
+
+  if (HAVE_ACL_SET_FD && dest_desc != -1)
+    ret = acl_set_fd (dest_desc, acl);
+  else
+    ret = acl_set_file (dst_name, ACL_TYPE_ACCESS, acl);
+  if (ret != 0)
+    {
+      int saved_errno = errno;
+
+      if (errno == ENOSYS || errno == ENOTSUP)
+        {
+	  int n = acl_entries (acl);
+
+	  acl_free (acl);
+	  if (n == 3)
+	    {
+	      if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
+		saved_errno = errno;
+	      else
+		return 0;
+	    }
+	  else
+	    chmod_or_fchmod (dst_name, dest_desc, mode);
+	}
+      else
+	{
+	  acl_free (acl);
+	  chmod_or_fchmod (dst_name, dest_desc, mode);
+	}
+      error (0, saved_errno, _("preserving permissions for %s"),
+	     quote (dst_name));
+      return -1;
+    }
+  else
+    acl_free (acl);
+
+  if (mode & (S_ISUID | S_ISGID | S_ISVTX))
+    {
+      /* We did not call chmod so far, so the special bits have not yet
+         been set.  */
+
+      if (chmod_or_fchmod (dst_name, dest_desc, mode) != 0)
+	{
+	  error (0, errno, _("preserving permissions for %s"),
+		 quote (dst_name));
+	  return -1;
+	}
+    }
+
+  if (S_ISDIR (mode))
+    {
+      acl = acl_get_file (src_name, ACL_TYPE_DEFAULT);
+      if (acl == NULL)
+	{
+	  error (0, errno, "%s", quote (src_name));
+	  return -1;
+	}
+
+      if (acl_set_file (dst_name, ACL_TYPE_DEFAULT, acl))
+	{
+	  error (0, errno, _("preserving permissions for %s"),
+		 quote (dst_name));
+	  acl_free (acl);
+	  return -1;
+	}
+      else
+        acl_free (acl);
+    }
+  return 0;
+#else
+  ret = chmod_or_fchmod (dst_name, dest_desc, mode);
+  if (ret != 0)
+    error (0, errno, _("preserving permissions for %s"), quote (dst_name));
+  return ret;
+#endif
+}
+
+/* Set the access control lists of a file. If DESC is a valid file
+   descriptor, use file descriptor operations where available, else use
+   filename based operations on NAME.  If access control lists are not
+   available, fchmod the target file to MODE.  Also sets the
+   non-permission bits of the destination file (S_ISUID, S_ISGID, S_ISVTX)
+   to those from MODE if any are set.  System call return value
+   semantics.  */
+
+int
+set_acl (char const *name, int desc, mode_t mode)
+{
+#if USE_ACL && HAVE_ACL_SET_FILE && HAVE_ACL_FREE
+  /* POSIX 1003.1e draft 17 (abandoned) specific version.  */
+
+  /* We must also have have_acl_from_text and acl_delete_def_file.
+     (acl_delete_def_file could be emulated with acl_init followed
+      by acl_set_file, but acl_set_file with an empty acl is
+      unspecified.)  */
+
+# ifndef HAVE_ACL_FROM_TEXT
+#  error Must have acl_from_text (see POSIX 1003.1e draft 17).
+# endif
+# ifndef HAVE_ACL_DELETE_DEF_FILE
+#  error Must have acl_delete_def_file (see POSIX 1003.1e draft 17).
+# endif
+
+  acl_t acl;
+  int ret;
+
+  if (HAVE_ACL_FROM_MODE)
+    {
+      acl = acl_from_mode (mode);
+      if (!acl)
+	{
+	  error (0, errno, "%s", quote (name));
+	  return -1;
+	}
+    }
+  else
+    {
+      char acl_text[] = "u::---,g::---,o::---";
+
+      if (mode & S_IRUSR) acl_text[ 3] = 'r';
+      if (mode & S_IWUSR) acl_text[ 4] = 'w';
+      if (mode & S_IXUSR) acl_text[ 5] = 'x';
+      if (mode & S_IRGRP) acl_text[10] = 'r';
+      if (mode & S_IWGRP) acl_text[11] = 'w';
+      if (mode & S_IXGRP) acl_text[12] = 'x';
+      if (mode & S_IROTH) acl_text[17] = 'r';
+      if (mode & S_IWOTH) acl_text[18] = 'w';
+      if (mode & S_IXOTH) acl_text[19] = 'x';
+
+      acl = acl_from_text (acl_text);
+      if (!acl)
+	{
+	  error (0, errno, "%s", quote (name));
+	  return -1;
+	}
+    }
+  if (HAVE_ACL_SET_FD && desc != -1)
+    ret = acl_set_fd (desc, acl);
+  else
+    ret = acl_set_file (name, ACL_TYPE_ACCESS, acl);
+  if (ret != 0)
+    {
+      int saved_errno = errno;
+      acl_free (acl);
+
+      if (errno == ENOTSUP || errno == ENOSYS)
+	{
+	  if (chmod_or_fchmod (name, desc, mode) != 0)
+	    saved_errno = errno;
+	  else
+	    return 0;
+	}
+      error (0, saved_errno, _("setting permissions for %s"), quote (name));
+      return -1;
+    }
+  else
+    acl_free (acl);
+
+  if (S_ISDIR (mode) && acl_delete_def_file (name))
+    {
+      error (0, errno, _("setting permissions for %s"), quote (name));
+      return -1;
+    }
+
+  if (mode & (S_ISUID | S_ISGID | S_ISVTX))
+    {
+      /* We did not call chmod so far, so the special bits have not yet
+         been set.  */
+
+      if (chmod_or_fchmod (name, desc, mode))
+	{
+	  error (0, errno, _("preserving permissions for %s"), quote (name));
+	  return -1;
+	}
+    }
+  return 0;
+#else
+   int ret = chmod_or_fchmod (name, desc, mode);
+   if (ret)
+     error (0, errno, _("setting permissions for %s"), quote (name));
+   return ret;
+#endif
+}
--- a/lib/acl.h
+++ b/lib/acl.h
@@ -18,11 +18,14 @@
 
    Written by Paul Eggert.  */
 
-#if HAVE_SYS_ACL_H && HAVE_ACL
+#if HAVE_SYS_ACL_H
 # include <sys/acl.h>
 #endif
-#if ! defined GETACLCNT && defined ACL_CNT
+#if defined HAVE_ACL && ! defined GETACLCNT && defined ACL_CNT
 # define GETACLCNT ACL_CNT
 #endif
 
 int file_has_acl (char const *, struct stat const *);
+int copy_acl (char const *, int, char const *, int, mode_t);
+int set_acl (char const *, int, mode_t);
+int chmod_or_fchmod (char const *, int, mode_t);
--- a/lib/chdir-long.c
+++ b/lib/chdir-long.c
@@ -77,13 +77,10 @@
 static int
 cdb_advance_fd (struct cd_buf *cdb, char const *dir)
 {
-  int new_fd = openat (cdb->fd, dir, O_RDONLY | O_DIRECTORY);
+  int new_fd = openat (cdb->fd, dir,
+		       O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
   if (new_fd < 0)
-    {
-      new_fd = openat (cdb->fd, dir, O_WRONLY | O_DIRECTORY);
-      if (new_fd < 0)
-	return -1;
-    }
+    return -1;
 
   cdb_free (cdb);
   cdb->fd = new_fd;
new file mode 100644
--- /dev/null
+++ b/lib/chdir-safer.c
@@ -0,0 +1,87 @@
+/* much like chdir(2), but safer
+
+   Copyright (C) 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
+   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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "chdir-safer.h"
+
+#include <stdbool.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef O_DIRECTORY
+# define O_DIRECTORY 0
+#endif
+
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
+#define SAME_INODE(Stat_buf_1, Stat_buf_2) \
+  ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \
+   && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev)
+
+/* Like chdir, but fail if DIR is a symbolic link to a directory (or
+   similar funny business), or if DIR is not readable.  This avoids a
+   minor race condition between when a directory is created or statted
+   and when the process chdirs into it.  */
+int
+chdir_no_follow (char const *dir)
+{
+  int result = 0;
+  int saved_errno;
+  int fd = open (dir,
+		 O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK);
+  if (fd < 0)
+    return -1;
+
+  /* If open follows symlinks, lstat DIR and fstat FD to ensure that
+     they are the same file; if they are different files, set errno to
+     ELOOP (the same value that open uses for symlinks with
+     O_NOFOLLOW) so the caller can report a failure.  */
+  if (! O_NOFOLLOW)
+    {
+      struct stat sb1;
+      result = lstat (dir, &sb1);
+      if (result == 0)
+	{
+	  struct stat sb2;
+	  result = fstat (fd, &sb2);
+	  if (result == 0 && ! SAME_INODE (sb1, sb2))
+	    {
+	      errno = ELOOP;
+	      result = -1;
+	    }
+	}
+    }
+
+  if (result == 0)
+    result = fchdir (fd);
+
+  saved_errno = errno;
+  close (fd);
+  errno = saved_errno;
+  return result;
+}
new file mode 100644
--- /dev/null
+++ b/lib/chdir-safer.h
@@ -0,0 +1,21 @@
+/* much like chdir(2), but safer
+
+   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.  */
+
+/* Written by Jim Meyering.  */
+
+int chdir_no_follow (char const *file);
--- a/lib/chown.c
+++ b/lib/chown.c
@@ -27,12 +27,15 @@
    most systems.  */
 #undef chown
 
+#include <stdbool.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
 
+#include "stat-macros.h"
+
 /* Provide a more-closely POSIX-conforming version of chown on
    systems with one or both of the following problems:
    - chown doesn't treat an ID of -1 as meaning
@@ -66,20 +69,34 @@
        on the symlink itself.  To work around that, we open the
        file (but this can fail due to lack of read or write permission) and
        use fchown on the resulting descriptor.  */
-    int fd = open (file, O_RDONLY | O_NONBLOCK | O_NOCTTY);
-    if (fd < 0
-	&& (fd = open (file, O_WRONLY | O_NONBLOCK | O_NOCTTY)) < 0)
-      return -1;
-    if (fchown (fd, uid, gid))
+    int open_flags = O_NONBLOCK | O_NOCTTY;
+    int fd = open (file, O_RDONLY | open_flags);
+    if (0 <= fd
+	|| (errno == EACCES
+	    && 0 <= (fd = open (file, O_WRONLY | open_flags))))
       {
+	int result = fchown (fd, uid, gid);
 	int saved_errno = errno;
+
+	/* POSIX says fchown can fail with errno == EINVAL on sockets,
+	   so fall back on chown in that case.  */
+	struct stat sb;
+	bool fchown_socket_failure =
+	  (result != 0 && saved_errno == EINVAL
+	   && fstat (fd, &sb) == 0 && S_ISFIFO (sb.st_mode));
+
 	close (fd);
-	errno = saved_errno;
-	return -1;
+
+	if (! fchown_socket_failure)
+	  {
+	    errno = saved_errno;
+	    return result;
+	  }
       }
-    return close (fd);
+    else if (errno != EACCES)
+      return -1;
   }
-#else
+#endif
+
   return chown (file, uid, gid);
-#endif
 }
--- a/lib/fts.c
+++ b/lib/fts.c
@@ -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
@@ -203,10 +203,7 @@
 internal_function
 diropen (char const *dir)
 {
-  int fd = open (dir, O_RDONLY | O_DIRECTORY);
-  if (fd < 0)
-    fd = open (dir, O_WRONLY | O_DIRECTORY);
-  return fd;
+  return open (dir, O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
 }
 
 FTS *
@@ -244,7 +241,8 @@
 #ifndef MAXPATHLEN
 # define MAXPATHLEN 1024
 #endif
-	if (! fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
+	size_t maxarglen = fts_maxarglen(argv);
+	if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
 		goto mem1;
 
 	/* Allocate/initialize root's parent. */
--- a/lib/gettime.c
+++ b/lib/gettime.c
@@ -45,8 +45,14 @@
     ts->tv_nsec = tv.tv_usec * 1000;
   }
 # else
+
+#  ifndef OK_TO_USE_1S_CLOCK
+#   error "Only 1-second nominal clock resolution found.  Is that intended?" \
+          "If so, compile with the -DOK_TO_USE_1S_CLOCK option."
+#  endif
   ts->tv_sec = time (NULL);
   ts->tv_nsec = 0;
+
 # endif
 
 #endif
new file mode 100644
--- /dev/null
+++ b/lib/lchmod.h
@@ -0,0 +1,35 @@
+/* Provide a replacement for lchmod on hosts that lack it.
+
+   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.  */
+
+/* Written by Paul Eggert.  */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef HAVE_LCHMOD
+
+/* The lchmod replacement follows symbolic links.  Callers should take
+   this into account; lchmod should be applied only to arguments that
+   are known to not be symbolic links.  On hosts that lack lchmod,
+   this can lead to race conditions between the check and the
+   invocation of lchmod, but we know of no workarounds that are
+   reliable in general.  You might try requesting support for lchmod
+   from your operating system supplier.  */
+
+# define lchmod chmod
+#endif
--- a/lib/mkdir-p.c
+++ b/lib/mkdir-p.c
@@ -1,6 +1,6 @@
 /* mkdir-p.c -- Ensure that a directory and its parents exist.
 
-   Copyright (C) 1990, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005
+   Copyright (C) 1990, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
@@ -39,18 +39,15 @@
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-#include "save-cwd.h"
+#include "chdir-safer.h"
 #include "dirname.h"
 #include "error.h"
+#include "lchmod.h"
+#include "lchown.h"
 #include "quote.h"
+#include "save-cwd.h"
 #include "stat-macros.h"
 
-#ifndef ENOSYS
-# define ENOSYS EEXIST
-#endif
-
-#define WX_USR (S_IWUSR | S_IXUSR)
-
 /* Ensure that the directory ARG exists.
 
    Create any leading directories that don't already exist, with
@@ -127,15 +124,15 @@
       mode_t oldmask = umask (0);
 
       /* Make a copy of ARG that we can scribble NULs on.  */
-      dir = (char *) alloca (strlen (arg) + 1);
+      dir = alloca (strlen (arg) + 1);
       strcpy (dir, arg);
       strip_trailing_slashes (dir);
       full_dir = dir;
 
-      /* If leading directories shouldn't be writable or executable,
+      /* If leading directories shouldn't be readable, writable or executable,
 	 or should have set[ug]id or sticky bits set and we are setting
 	 their owners, we need to fix their permissions after making them.  */
-      if (((parent_mode & WX_USR) != WX_USR)
+      if (((parent_mode & S_IRWXU) != S_IRWXU)
 	  || ((owner != (uid_t) -1 || group != (gid_t) -1)
 	      && (parent_mode & (S_ISUID | S_ISGID | S_ISVTX)) != 0))
 	{
@@ -175,6 +172,9 @@
 
       while (true)
 	{
+	  bool dir_known_to_exist;
+	  int mkdir_errno;
+
 	  /* slash points to the leftmost unprocessed component of dir.  */
 	  basename_dir = slash;
 
@@ -188,13 +188,16 @@
 	    basename_dir = dir;
 
 	  *slash = '\0';
-	  if (mkdir (basename_dir, tmp_mode) == 0)
+	  dir_known_to_exist = (mkdir (basename_dir, tmp_mode) == 0);
+	  mkdir_errno = errno;
+
+	  if (dir_known_to_exist)
 	    {
 	      if (verbose_fmt_string)
 		error (0, 0, verbose_fmt_string, quote (dir));
 
 	      if ((owner != (uid_t) -1 || group != (gid_t) -1)
-		  && chown (basename_dir, owner, group)
+		  && lchown (basename_dir, owner, group)
 #if defined AFS && defined EPERM
 		  && errno != EPERM
 #endif
@@ -208,36 +211,43 @@
 
 	      if (re_protect)
 		{
-		  struct ptr_list *new = (struct ptr_list *)
-		    alloca (sizeof *new);
+		  struct ptr_list *new = alloca (sizeof *new);
 		  new->dirname_end = slash;
 		  new->next = leading_dirs;
 		  leading_dirs = new;
 		}
 	    }
-	  else if (errno == EEXIST || errno == ENOSYS)
-	    {
-	      /* A file is already there.  Perhaps it is a directory.
-		 If not, it will be diagnosed later.
-
-		 The ENOSYS is for Solaris 8 NFS clients, which can
-		 fail with errno == ENOSYS if mkdir is invoked on an
-		 NFS mount point.  */
-	    }
-	  else
-	    {
-	      error (0, errno, _("cannot create directory %s"), quote (dir));
-	      retval = false;
-	      break;
-	    }
 
 	  /* If we were able to save the initial working directory,
 	     then we can use chdir to change into each directory before
 	     creating an entry in that directory.  This avoids making
 	     mkdir process O(n^2) file name components.  */
-	  if (do_chdir && chdir (basename_dir) < 0)
+	  if (do_chdir)
 	    {
-	      error (0, errno, _("cannot chdir to directory %s"),
+	      /* If we know that basename_dir is a directory (because we've
+		 just created it), then ensure that when we change to it,
+		 that final component is not a symlink.  Otherwise, we must
+		 accept the possibility that basename_dir is a preexisting
+		 symlink-to-directory and chdir through the symlink.  */
+	      if ((dir_known_to_exist
+		   ? chdir_no_follow (basename_dir)
+		   : chdir (basename_dir)) == 0)
+		dir_known_to_exist = true;
+	      else if (dir_known_to_exist)
+		{
+		  error (0, errno, _("cannot chdir to directory %s"),
+			 quote (dir));
+		  retval = false;
+		  break;
+		}
+	    }
+	  else if (!dir_known_to_exist)
+	    dir_known_to_exist = (stat (basename_dir, &stats) == 0
+				  && S_ISDIR (stats.st_mode));
+
+	  if (!dir_known_to_exist)
+	    {
+	      error (0, mkdir_errno, _("cannot create directory %s"),
 		     quote (dir));
 	      retval = false;
 	      break;
@@ -261,9 +271,18 @@
 	 Create the final component of the file name.  */
       if (retval)
 	{
-	  if (mkdir (basename_dir, mode) != 0)
+	  bool dir_known_to_exist = (mkdir (basename_dir, mode) == 0);
+	  int mkdir_errno = errno;
+	  struct stat sbuf;
+
+	  if ( ! dir_known_to_exist)
+	    dir_known_to_exist = (stat (basename_dir, &sbuf) == 0
+				  && S_ISDIR (sbuf.st_mode));
+
+	  if ( ! dir_known_to_exist)
 	    {
-	      error (0, errno, _("cannot create directory %s"), quote (dir));
+	      error (0, mkdir_errno,
+		     _("cannot create directory %s"), quote (dir));
 	      retval = false;
 	    }
 	  else
@@ -285,7 +304,7 @@
 
       if (owner != (uid_t) -1 || group != (gid_t) -1)
 	{
-	  if (chown (fixup_permissions_dir, owner, group) != 0
+	  if (lchown (fixup_permissions_dir, owner, group) != 0
 #ifdef AFS
 	      && errno != EPERM
 #endif
@@ -302,7 +321,7 @@
 	 required to honor only the file permission bits.  In particular,
 	 it need not honor the `special' bits, so if MODE includes any
 	 special bits, set them here.  */
-      if ((mode & ~S_IRWXUGO) && chmod (fixup_permissions_dir, mode) != 0)
+      if ((mode & ~S_IRWXUGO) && lchmod (fixup_permissions_dir, mode) != 0)
 	{
 	  error (0, errno, _("cannot change permissions of %s"),
 		 quote (full_dir));
@@ -326,7 +345,7 @@
     {
       leading_dirs->dirname_end[0] = '\0';
       if ((cwd_problem && *full_dir != '/')
-	  || chmod (full_dir, parent_mode) != 0)
+	  || lchmod (full_dir, parent_mode) != 0)
 	{
 	  error (0, (cwd_problem ? 0 : errno),
 		 _("cannot change permissions of %s"), quote (full_dir));
new file mode 100644
--- /dev/null
+++ b/lib/mkdirat.c
@@ -0,0 +1,88 @@
+/* fd-relative mkdir
+   Copyright (C) 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
+   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 */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "openat.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#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.
+   Create a subdirectory, FILE, with mode MODE, in the directory
+   open on descriptor FD.  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.  */
+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;
+}
--- a/lib/modechange.c
+++ b/lib/modechange.c
@@ -124,6 +124,9 @@
 	}
       while ('0' <= *mode_string && *mode_string < '8');
 
+      if (*mode_string)
+	return NULL;
+
       /* Help the compiler optimize the usual case where mode_t uses
 	 the traditional octal representation.  */
       mode = ((S_ISUID == SUID && S_ISGID == SGID && S_ISVTX == SVTX
--- a/lib/mountlist.c
+++ b/lib/mountlist.c
@@ -23,6 +23,7 @@
 
 #include "mountlist.h"
 
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -143,6 +144,8 @@
 #ifndef ME_DUMMY
 # define ME_DUMMY(Fs_name, Fs_type)		\
     (strcmp (Fs_type, "autofs") == 0		\
+     || strcmp (Fs_type, "none") == 0		\
+     || strcmp (Fs_type, "proc") == 0		\
      || strcmp (Fs_type, "subfs") == 0		\
      /* for Irix 6.5 */				\
      || strcmp (Fs_type, "ignore") == 0)
@@ -283,6 +286,44 @@
 }
 #endif /* MOUNTED_VMOUNT */
 
+
+#if defined MOUNTED_GETMNTENT1 || defined MOUNTED_GETMNTENT2
+
+/* Return the device number from MOUNT_OPTIONS, if possible.
+   Otherwise return (dev_t) -1.  */
+
+static dev_t
+dev_from_mount_options (char const *mount_options)
+{
+  /* GNU/Linux allows file system implementations to define their own
+     meaning for "dev=" mount options, so don't trust the meaning
+     here.  */
+# ifndef __linux__
+
+  static char const dev_pattern[] = ",dev=";
+  char const *devopt = strstr (mount_options, dev_pattern);
+
+  if (devopt)
+    {
+      char const *optval = devopt + sizeof dev_pattern - 1;
+      char *optvalend;
+      unsigned long int dev;
+      errno = 0;
+      dev = strtoul (optval, &optvalend, 16);
+      if (optval != optvalend
+	  && (*optvalend == '\0' || *optvalend == ',')
+	  && ! (dev == ULONG_MAX && errno == ERANGE)
+	  && dev == (dev_t) dev)
+	return dev;
+    }
+
+# endif
+
+  return -1;
+}
+
+#endif
+
 /* Return a list of the currently mounted file systems, or NULL on error.
    Add each entry to the tail of the list so that they stay in order.
    If NEED_FS_TYPE is true, ensure that the file system type fields in
@@ -325,12 +366,11 @@
   }
 #endif
 
-#ifdef MOUNTED_GETMNTENT1	/* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
+#ifdef MOUNTED_GETMNTENT1 /* GNU/Linux, 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
   {
     struct mntent *mnt;
     char *table = MOUNTED;
     FILE *fp;
-    char *devopt;
 
     fp = setmntent (table, "r");
     if (fp == NULL)
@@ -345,11 +385,7 @@
 	me->me_type_malloced = 1;
 	me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
 	me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
-	devopt = strstr (mnt->mnt_opts, "dev=");
-	if (devopt)
-	  me->me_dev = strtoul (devopt + 4, NULL, 16);
-	else
-	  me->me_dev = (dev_t) -1;	/* Magic; means not known yet. */
+	me->me_dev = dev_from_mount_options (mnt->mnt_opts);
 
 	/* Add to the linked list. */
 	*mtail = me;
@@ -623,7 +659,7 @@
   }
 #endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP.  */
 
-#ifdef MOUNTED_GETMNTTBL	/* DolphinOS goes it's own way */
+#ifdef MOUNTED_GETMNTTBL	/* DolphinOS goes its own way.  */
   {
     struct mntent **mnttbl = getmnttbl (), **ent;
     for (ent=mnttbl;*ent;ent++)
@@ -697,7 +733,7 @@
 	    me->me_type_malloced = 1;
 	    me->me_dummy = MNT_IGNORE (&mnt) != 0;
 	    me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
-	    me->me_dev = (dev_t) -1;	/* Magic; means not known yet. */
+	    me->me_dev = dev_from_mount_options (mnt.mnt_mntopts);
 
 	    /* Add to the linked list. */
 	    *mtail = me;
new file mode 100644
--- /dev/null
+++ b/lib/openat-priv.h
@@ -0,0 +1,52 @@
+/* macros used by openat-like functions
+   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.  */
+
+/* written by Jim Meyering */
+
+#include <stdio.h>
+#include <string.h>
+#include "alloca.h"
+#include "intprops.h"
+
+/* Set PROC_FD_FILENAME to the expansion of "/proc/self/fd/%d/%s" in
+   alloca'd memory, using FD and FILE, respectively for %d and %s. */
+#define BUILD_PROC_NAME(Proc_fd_filename, Fd, File)			\
+  do									\
+    {									\
+      size_t filelen = strlen (File);					\
+      static const char procfd[] = "/proc/self/fd/%d/%s";		\
+      /* Buffer for the file 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.			\
+	 Subtract 4 to account for %d and %s.  */			\
+      size_t buflen = sizeof (procfd) - 4 + INT_STRLEN_BOUND (Fd) + filelen; \
+      (Proc_fd_filename) = alloca (buflen);				\
+      snprintf ((Proc_fd_filename), buflen, procfd, (Fd), (File));	\
+    }									\
+  while (0)
+
+/* 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
+   readable, or the particular .../fd/N directory is not present.  */
+#define EXPECTED_ERRNO(Errno)			\
+  ((Errno) == ENOTDIR || (Errno) == ENOENT	\
+   || (Errno) == EPERM || (Errno) == EACCES	\
+   || (Errno) == ENOSYS /* Solaris 8 */		\
+   || (Errno) == EOPNOTSUPP /* FreeBSD */)
--- a/lib/openat.c
+++ b/lib/openat.c
@@ -23,17 +23,15 @@
 
 #include "openat.h"
 
-#include <stdlib.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
+#include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
+#include "fcntl--.h"
+#include "openat-priv.h"
+#include "save-cwd.h"
+#include "unistd--.h"
 
-#include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
-#include "save-cwd.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
+#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>
@@ -44,11 +42,8 @@
    Otherwise, upon failure, set errno and return -1, as openat does.
    Upon successful completion, return a file descriptor.  */
 int
-rpl_openat (int fd, char const *file, int flags, ...)
+openat (int fd, char const *file, int flags, ...)
 {
-  struct saved_cwd saved_cwd;
-  int saved_errno;
-  int new_fd;
   mode_t mode = 0;
 
   if (flags & O_CREAT)
@@ -56,37 +51,80 @@
       va_list arg;
       va_start (arg, flags);
 
-      /* Assume that mode_t is passed compatibly with mode_t's type
-	 after argument promotion.  */
-      mode = va_arg (arg, mode_t);
+      /* If mode_t is narrower than int, use the promoted type (int),
+         not mode_t.  Use sizeof to guess whether mode_t is nerrower;
+         we don't know of any practical counterexamples.  */
+      if (sizeof (mode_t) < sizeof (int))
+	mode = va_arg (arg, int);
+      else
+	mode = va_arg (arg, mode_t);
 
       va_end (arg);
     }
 
+  return openat_permissive (fd, file, flags, mode, NULL);
+}
+
+/* Like openat (FD, FILE, FLAGS, MODE), but if CWD_ERRNO is
+   nonnull, set *CWD_ERRNO to an errno value if unable to save
+   or restore the initial working directory.  This is needed only
+   the first time remove.c's remove_dir opens a command-line
+   directory argument.
+
+   If a previous attempt to restore the current working directory
+   failed, then we must not even try to access a `.'-relative name.
+   It is the caller's responsibility not to call this function
+   in that case.  */
+
+int
+openat_permissive (int fd, char const *file, int flags, mode_t mode,
+		   int *cwd_errno)
+{
+  struct saved_cwd saved_cwd;
+  int saved_errno;
+  int err;
+  bool save_ok;
+
   if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
     return open (file, flags, mode);
 
-  if (save_cwd (&saved_cwd) != 0)
-    openat_save_fail (errno);
+  {
+    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;
+  }
 
-  if (fchdir (fd) != 0)
+  save_ok = (save_cwd (&saved_cwd) == 0);
+  if (! save_ok)
     {
-      saved_errno = errno;
-      free_cwd (&saved_cwd);
-      errno = saved_errno;
-      return -1;
+      if (! cwd_errno)
+	openat_save_fail (errno);
+      *cwd_errno = errno;
     }
 
-  new_fd = open (file, flags, mode);
+  err = fchdir (fd);
   saved_errno = errno;
 
-  if (restore_cwd (&saved_cwd) != 0)
-    openat_restore_fail (errno);
+  if (! err)
+    {
+      err = open (file, flags, mode);
+      saved_errno = errno;
+      if (save_ok && restore_cwd (&saved_cwd) != 0)
+	{
+	  if (! cwd_errno)
+	    openat_restore_fail (errno);
+	  *cwd_errno = errno;
+	}
+    }
 
   free_cwd (&saved_cwd);
-
   errno = saved_errno;
-  return new_fd;
+  return err;
 }
 
 #if !HAVE_FDOPENDIR
@@ -110,27 +148,37 @@
   int saved_errno;
   DIR *dir;
 
-  if (save_cwd (&saved_cwd) != 0)
-    openat_save_fail (errno);
+  char *proc_file;
+  BUILD_PROC_NAME (proc_file, fd, ".");
+  dir = opendir (proc_file);
+  saved_errno = errno;
 
-  if (fchdir (fd) != 0)
+  /* If the syscall fails with an expected errno value, resort to
+     save_cwd/restore_cwd.  */
+  if (! dir && EXPECTED_ERRNO (saved_errno))
     {
-      saved_errno = errno;
+      if (save_cwd (&saved_cwd) != 0)
+	openat_save_fail (errno);
+
+      if (fchdir (fd) != 0)
+	{
+	  dir = NULL;
+	  saved_errno = errno;
+	}
+      else
+	{
+	  dir = opendir (".");
+	  saved_errno = errno;
+
+	  if (restore_cwd (&saved_cwd) != 0)
+	    openat_restore_fail (errno);
+	}
+
       free_cwd (&saved_cwd);
-      errno = saved_errno;
-      return NULL;
     }
 
-  dir = opendir (".");
-  saved_errno = errno;
-
-  if (restore_cwd (&saved_cwd) != 0)
-    openat_restore_fail (errno);
-
-  free_cwd (&saved_cwd);
   if (dir)
     close (fd);
-
   errno = saved_errno;
   return dir;
 }
@@ -151,32 +199,42 @@
   int saved_errno;
   int err;
 
-  if (fd == AT_FDCWD)
+  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);
 
-  if (fchdir (fd) != 0)
+  err = fchdir (fd);
+  saved_errno = errno;
+
+  if (! err)
     {
+      err = (flag == AT_SYMLINK_NOFOLLOW
+	     ? lstat (file, st)
+	     : stat (file, st));
       saved_errno = errno;
-      free_cwd (&saved_cwd);
-      errno = saved_errno;
-      return -1;
+
+      if (restore_cwd (&saved_cwd) != 0)
+	openat_restore_fail (errno);
     }
 
-  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;
 }
@@ -195,28 +253,36 @@
   int saved_errno;
   int err;
 
-  if (fd == AT_FDCWD)
+  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);
 
-  if (fchdir (fd) != 0)
+  err = fchdir (fd);
+  saved_errno = errno;
+
+  if (! err)
     {
+      err = (flag == AT_REMOVEDIR ? rmdir (file) : unlink (file));
       saved_errno = errno;
-      free_cwd (&saved_cwd);
-      errno = saved_errno;
-      return -1;
+
+      if (restore_cwd (&saved_cwd) != 0)
+	openat_restore_fail (errno);
     }
 
-  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;
 }
--- a/lib/openat.h
+++ b/lib/openat.h
@@ -23,6 +23,7 @@
 #include <sys/stat.h>
 #include <dirent.h>
 #include <unistd.h>
+#include <stdbool.h>
 
 #ifndef __attribute__
 # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
@@ -35,30 +36,39 @@
 #endif
 
 #ifndef AT_FDCWD
-# define AT_FDCWD (-3041965)		/* same value as Solaris 9 */
-# define AT_SYMLINK_NOFOLLOW 4096	/* same value as Solaris 9 */
-# define AT_REMOVEDIR (0x1)		/* same value as Solaris 9 */
+/* Use the same values as Solaris 9.  This shouldn't matter, but
+   there's no real reason to differ.  */
+# define AT_FDCWD (-3041965)
+# define AT_SYMLINK_NOFOLLOW 4096
+# define AT_REMOVEDIR 1
+#endif
+
+#ifdef __OPENAT_PREFIX
 
-# ifdef __OPENAT_PREFIX
-#  undef openat
-#  define __OPENAT_CONCAT(x, y) x ## y
-#  define __OPENAT_XCONCAT(x, y) __OPENAT_CONCAT (x, y)
-#  define __OPENAT_ID(y) __OPENAT_XCONCAT (__OPENAT_PREFIX, y)
-#  define openat __OPENAT_ID (openat)
+# undef openat
+# define __OPENAT_CONCAT(x, y) x ## y
+# define __OPENAT_XCONCAT(x, y) __OPENAT_CONCAT (x, y)
+# define __OPENAT_ID(y) __OPENAT_XCONCAT (__OPENAT_PREFIX, y)
+# define openat __OPENAT_ID (openat)
 int openat (int fd, char const *file, int flags, /* mode_t mode */ ...);
-#  if ! HAVE_FDOPENDIR
-#   define fdopendir __OPENAT_ID (fdopendir)
-#  endif
+int openat_permissive (int fd, char const *file, int flags, mode_t mode,
+		       int *cwd_errno);
+# if ! HAVE_FDOPENDIR
+#  define fdopendir __OPENAT_ID (fdopendir)
+# endif
 DIR *fdopendir (int fd);
-#  define fstatat __OPENAT_ID (fstatat)
+# define fstatat __OPENAT_ID (fstatat)
 int fstatat (int fd, char const *file, struct stat *st, int flag);
-#  define unlinkat __OPENAT_ID (unlinkat)
+# define unlinkat __OPENAT_ID (unlinkat)
 int unlinkat (int fd, char const *file, int flag);
+
+#else
+
+# define openat_permissive(Fd, File, Flags, Mode, Cwd_errno) \
+    openat (Fd, File, Flags, Mode)
+
+#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;
-# else
-#  define openat_restore_fail(Errno) /* empty */
-#  define openat_save_fail(Errno) /* empty */
-# endif
-
-#endif
--- a/lib/posixtm.c
+++ b/lib/posixtm.c
@@ -62,8 +62,8 @@
     (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS)
 
   touch mmddhhmm[YY] FILE... (obsoleted by POSIX 1003.1-2001)
-    8 or 10 digits
-    (PDS_TRAILING_YEAR)
+    8 or 10 digits, YY (if present) must be in the range 69-99
+    (PDS_TRAILING_YEAR | PDS_PRE_2000)
 
   date mmddhhmm[[CC]YY]
     8, 10, or 12 digits
@@ -72,7 +72,7 @@
 */
 
 static int
-year (struct tm *tm, const int *digit_pair, size_t n, int allow_century)
+year (struct tm *tm, const int *digit_pair, size_t n, unsigned int syntax_bits)
 {
   switch (n)
     {
@@ -82,11 +82,15 @@
 	 POSIX requires that 00-68 be interpreted as 2000-2068,
 	 and that 69-99 be interpreted as 1969-1999.  */
       if (digit_pair[0] <= 68)
-	tm->tm_year += 100;
+	{
+	  if (syntax_bits & PDS_PRE_2000)
+	    return 1;
+	  tm->tm_year += 100;
+	}
       break;
 
     case 2:
-      if (!allow_century)
+      if (! (syntax_bits & PDS_CENTURY))
 	return 1;
       tm->tm_year = digit_pair[0] * 100 + digit_pair[1] - 1900;
       break;
@@ -148,7 +152,7 @@
   p = pair;
   if (syntax_bits & PDS_LEADING_YEAR)
     {
-      if (year (tm, p, len - 4, syntax_bits & PDS_CENTURY))
+      if (year (tm, p, len - 4, syntax_bits))
 	return 1;
       p += len - 4;
       len = 4;
@@ -164,7 +168,7 @@
   /* Handle any trailing year.  */
   if (syntax_bits & PDS_TRAILING_YEAR)
     {
-      if (year (tm, p, len, syntax_bits & PDS_CENTURY))
+      if (year (tm, p, len, syntax_bits))
 	return 1;
     }
 
--- a/lib/posixtm.h
+++ b/lib/posixtm.h
@@ -1,6 +1,6 @@
 /* Parse dates for touch and date.
 
-   Copyright (C) 1998, 2003 Free Software Foundation Inc.
+   Copyright (C) 1998, 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
@@ -27,6 +27,7 @@
 # define PDS_TRAILING_YEAR 2
 # define PDS_CENTURY 4
 # define PDS_SECONDS 8
+# define PDS_PRE_2000 16
 
 bool posixtime (time_t *p, const char *s, unsigned int syntax_bits);
 
--- a/lib/save-cwd.c
+++ b/lib/save-cwd.c
@@ -75,12 +75,8 @@
   cwd->desc = open (".", O_RDONLY);
   if (cwd->desc < 0)
     {
-      cwd->desc = open (".", O_WRONLY);
-      if (cwd->desc < 0)
-	{
-	  cwd->name = xgetcwd ();
-	  return cwd->name ? 0 : -1;
-	}
+      cwd->name = xgetcwd ();
+      return cwd->name ? 0 : -1;
     }
 
   return 0;
--- a/lib/version-etc.c
+++ b/lib/version-etc.c
@@ -1,5 +1,5 @@
 /* Utility to help print --version output in a consistent format.
-   Copyright (C) 1999-2005 Free Software Foundation, Inc.
+   Copyright (C) 1999-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
@@ -35,7 +35,7 @@
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
-enum { COPYRIGHT_YEAR = 2005 };
+enum { COPYRIGHT_YEAR = 2006 };
 
 /* Like version_etc, below, but with the NULL-terminated author list
    provided via a variable of type va_list.  */
--- a/m4/ChangeLog
+++ b/m4/ChangeLog
@@ -1,3 +1,21 @@
+2006-01-09  Paul Eggert  <eggert@cs.ucla.edu>
+
+	Sync from coreutils.
+	* lib-ignore.m4: New file.
+	* lchmod.m4: New file.
+
+2006-01-09  Jim Meyering  <jim@meyering.net>
+
+	Sync from coreutils.
+	* chdir-safer.m4: New file.
+	* openat.m4 (gl_FUNC_OPENAT): Require and compile mkdirat.c.
+	Require openat-priv.h.
+
+2006-01-09  Andreas Gruenbacher  <agruen@suse.de>
+
+	Sync from coreutils.
+	* acl.m4 (AC_FUNC_ACL): Add POSIX ACL and Linux-specific acl tests.
+
 2005-10-16  Bruno Haible  <bruno@clisp.org>
 
 	* stdint.m4 (gl_STDINT_H): Also test for <sys/bitypes.h>.
--- a/m4/acl.m4
+++ b/m4/acl.m4
@@ -14,5 +14,22 @@
 
   dnl Prerequisites of lib/acl.c.
   AC_CHECK_HEADERS(sys/acl.h)
+  if test "$ac_cv_header_sys_acl_h" = yes; then
+    use_acl=1
+  else
+    use_acl=0
+  fi
+  AC_DEFINE_UNQUOTED(USE_ACL, $use_acl,
+		     [Define if you want access control list support.])
   AC_CHECK_FUNCS(acl)
+  ac_save_LIBS="$LIBS"
+  AC_SEARCH_LIBS(acl_get_file, acl,
+		 [test "$ac_cv_search_acl_get_file" = "none required" ||
+		  LIB_ACL=$ac_cv_search_acl_get_file])
+  AC_SUBST(LIB_ACL)
+  AC_CHECK_HEADERS(acl/libacl.h)
+  AC_CHECK_FUNCS(acl_get_file acl_get_fd acl_set_file acl_set_fd \
+		 acl_free acl_from_mode acl_from_text acl_to_text \
+		 acl_delete_def_file acl_entries acl_extended_file)
+  LIBS="$ac_save_LIBS"
 ])
new file mode 100644
--- /dev/null
+++ b/m4/lchmod.m4
@@ -0,0 +1,15 @@
+#serial 1
+
+dnl Copyright (C) 2005 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+dnl Provide a replacement for lchmod on hosts that lack it.
+
+AC_DEFUN([gl_FUNC_LCHMOD],
+[
+  AC_LIBSOURCES([lchmod.h])
+  AC_CHECK_FUNCS_ONCE([lchmod])
+])
new file mode 100644
--- /dev/null
+++ b/m4/lib-ignore.m4
@@ -0,0 +1,43 @@
+# If possible, ignore libraries that are not depended on.
+
+dnl Copyright (C) 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,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Written by Paul Eggert.
+
+AC_DEFUN([gl_IGNORE_UNUSED_LIBRARIES],
+[
+  AC_CACHE_CHECK([for flag to ignore unused libraries],
+    [gl_cv_ignore_unused_libraries],
+    [gl_cv_ignore_unused_libraries=none
+     AC_LINK_IFELSE([AC_LANG_PROGRAM()],
+       [gl_ldd_output0=`(ldd conftest$ac_exeext) 2>/dev/null` ||
+	  gl_ldd_output0=])
+     if test "$gl_ldd_output0"; then
+       gl_saved_ldflags=$LDFLAGS
+       gl_saved_libs=$LIBS
+       LIBS="$LIBS -lm"
+       AC_LINK_IFELSE([AC_LANG_PROGRAM()],
+	 [gl_ldd_output1=`(ldd conftest$ac_exeext) 2>/dev/null` ||
+	    gl_ldd_output1=])
+       if test "$gl_ldd_output1" && test "$gl_ldd_output0" != "$gl_ldd_output1"
+       then
+	 for gl_flags in '-Xlinker -zignore' '-zignore'; do
+	   LDFLAGS="$gl_flags $LDFLAGS"
+	   AC_LINK_IFELSE([AC_LANG_PROGRAM()],
+	     [if gl_ldd_output2=`(ldd conftest$ac_exeext) 2>/dev/null` &&
+		 test "$gl_ldd_output0" = "$gl_ldd_output2"; then
+		gl_cv_ignore_unused_libraries=$gl_flags
+	      fi])
+	   LDFLAGS=$gl_saved_ldflags
+	   test "gl_cv_ignore_unused_libraries" != none && break
+	 done
+       fi
+       LIBS=$gl_saved_LIBS
+     fi])
+
+  test "$gl_cv_ignore_unused_libraries" != none &&
+    LDFLAGS="$LDFLAGS $gl_cv_ignore_unused_libraries"
+])
--- a/m4/openat.m4
+++ b/m4/openat.m4
@@ -1,4 +1,4 @@
-#serial 6
+#serial 7
 # See if we need to use our replacement for Solaris' openat function.
 
 dnl Copyright (C) 2004, 2005 Free Software Foundation, Inc.
@@ -10,7 +10,12 @@
 
 AC_DEFUN([gl_FUNC_OPENAT],
 [
-  AC_LIBSOURCES([openat.c, openat.h, openat-die.c])
+  AC_LIBSOURCES([openat.c, openat.h, openat-priv.h, openat-die.c])
+  AC_LIBSOURCES([mkdirat.c])
+
+  # No system provides a mkdirat function; compile it unconditionally.
+  AC_LIBOBJ([mkdirat])
+
   AC_LIBOBJ([openat-die])
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
   AC_CHECK_FUNCS_ONCE([fdopendir])
new file mode 100644
--- /dev/null
+++ b/modules/lib-ignore
@@ -0,0 +1,20 @@
+Description:
+If possible, ignore libraries that are not depended on.
+
+Files:
+m4/lib-ignore.m4
+
+Depends-on:
+
+configure.ac:
+gl_IGNORE_UNUSED_LIBRARIES
+
+Makefile.am:
+
+Include:
+
+License:
+GPL
+
+Maintainer:
+Paul Eggert and Jim Meyering
--- a/modules/mkdir-p
+++ b/modules/mkdir-p
@@ -2,9 +2,14 @@
 Ensure that a directory and its parents exist.
 
 Files:
-lib/mkdir-p.h
+lib/chdir-safer.c
+lib/chdir-safer.h
+lib/lchmod.h
 lib/mkdir-p.c
+lib/mkdir-p.h
 m4/afs.m4
+m4/chdir-safer.m4
+m4/lchmod.m4
 m4/mkdir-p.m4
 
 Depends-on:
--- a/modules/openat
+++ b/modules/openat
@@ -2,9 +2,11 @@
 Open a file at a directory.
 
 Files:
+lib/mkdirat.c
 lib/openat.c
 lib/openat.h
 lib/openat-die.c
+lib/openat-priv.h
 m4/openat.m4
 
 Depends-on: