diff --git a/coreutils-8.27-df-stat.patch b/coreutils-8.27-df-stat.patch new file mode 100644 index 0000000..fbfa283 --- /dev/null +++ b/coreutils-8.27-df-stat.patch @@ -0,0 +1,82 @@ +From 9c4641f42bbecf63ec0a0e05caacbccd5332b831 Mon Sep 17 00:00:00 2001 +From: Philipp Thomas +Date: Sun, 26 Mar 2017 22:34:00 -0700 +Subject: [PATCH 1/2] df: avoid querying excluded file systems + +* src/df.c (filter_mount_list): Avoid stat() on +explicitly excluded file systems, which is especially +significant in cases like `-x nfs` which may hang. +* NEWS: Mention the bug fix. + +Upstream-commit: 7c228bc55ed3fd6d56a6ad135438066de2f54a30 +Signed-off-by: Kamil Dudka +--- + src/df.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/df.c b/src/df.c +index 5b9e8fd..e0ebed7 100644 +--- a/src/df.c ++++ b/src/df.c +@@ -682,9 +682,11 @@ filter_mount_list (bool devices_only) + On Linux we probably have me_dev populated from /proc/self/mountinfo, + however we still stat() in case another device was mounted later. */ + if ((me->me_remote && show_local_fs) ++ || (!selected_fstype (me->me_type) || excluded_fstype (me->me_type)) + || -1 == stat (me->me_mountdir, &buf)) + { +- /* If remote, and showing just local, add ME for filtering later. ++ /* If remote, and showing just local, or FS type is excluded, ++ add ME for filtering later. + If stat failed; add ME to be able to complain about it later. */ + buf.st_dev = me->me_dev; + } +-- +2.13.6 + + +From 605ec5a50d341fb2d3eea6ad68316e229c7b8382 Mon Sep 17 00:00:00 2001 +From: Josef Cejka +Date: Tue, 1 Aug 2017 01:50:34 +0200 +Subject: [PATCH 2/2] df: avoid stat() for dummy file systems with -l + +When systemd is configured to automount a remote file system - see +'man systemd.automount(5)', then the mount point is initially +mounted by systemd with the file system type "autofs". +When the resource is used later on, then the wanted file system is +mounted over that mount point on demand. +'df -l' triggered systemd to mount the file system because it called +stat() on the mount point. +Instead of single-casing "autofs" targets, we can avoid stat()ing +all dummy file systems (which includes "autofs"), because those are +skipped later on in get_dev() anyway. + +*src/df.c (filter_mount_list): Also skip dummy file systems unless +the -a option or a specific target are given. +* NEWS: Mention the fix. + +Co-authored-by: Bernhard Voelker + +Fixes http://bugzilla.suse.com/show_bug.cgi?id=1043059 + +Upstream-commit: a19ff5d8179a7de38109fc78278229fd96f3941a +Signed-off-by: Kamil Dudka +--- + src/df.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/df.c b/src/df.c +index e0ebed7..11b5a22 100644 +--- a/src/df.c ++++ b/src/df.c +@@ -682,6 +682,7 @@ filter_mount_list (bool devices_only) + On Linux we probably have me_dev populated from /proc/self/mountinfo, + however we still stat() in case another device was mounted later. */ + if ((me->me_remote && show_local_fs) ++ || (me->me_dummy && !show_all_fs && !show_listed_fs) + || (!selected_fstype (me->me_type) || excluded_fstype (me->me_type)) + || -1 == stat (me->me_mountdir, &buf)) + { +-- +2.13.6 + diff --git a/coreutils-8.27-fts-update.patch b/coreutils-8.27-fts-update.patch new file mode 100644 index 0000000..b74c614 --- /dev/null +++ b/coreutils-8.27-fts-update.patch @@ -0,0 +1,640 @@ +From 138ef167ed028bf739143c03c7b4ffba08ce3d27 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Mon, 12 Feb 2018 12:45:36 +0100 +Subject: [PATCH] import gnulib's FTS module from coreutils-8.29 + +--- + lib/fts.c | 382 +++++++++++++++++++++++++++++++------------------------------ + lib/fts_.h | 8 +- + 2 files changed, 198 insertions(+), 192 deletions(-) + +diff --git a/lib/fts.c b/lib/fts.c +index fabaa97..8f2595d 100644 +--- a/lib/fts.c ++++ b/lib/fts.c +@@ -13,7 +13,7 @@ + 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, see . */ ++ along with this program. If not, see . */ + + /*- + * Copyright (c) 1990, 1993, 1994 +@@ -71,11 +71,6 @@ static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94"; + + #if ! _LIBC + # include "fcntl--.h" +-# include "dirent--.h" +-# include "unistd--.h" +-/* FIXME - use fcntl(F_DUPFD_CLOEXEC)/openat(O_CLOEXEC) once they are +- supported. */ +-# include "cloexec.h" + # include "flexmember.h" + # include "openat.h" + # include "same-inode.h" +@@ -203,6 +198,14 @@ enum Fts_stat + while (false) + #endif + ++#ifndef FALLTHROUGH ++# if __GNUC__ < 7 ++# define FALLTHROUGH ((void) 0) ++# else ++# define FALLTHROUGH __attribute__ ((__fallthrough__)) ++# endif ++#endif ++ + static FTSENT *fts_alloc (FTS *, const char *, size_t) internal_function; + static FTSENT *fts_build (FTS *, int) internal_function; + static void fts_lfree (FTSENT *) internal_function; +@@ -297,14 +300,13 @@ static DIR * + internal_function + opendirat (int fd, char const *dir, int extra_flags, int *pdir_fd) + { +- int new_fd = openat (fd, dir, +- (O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK +- | extra_flags)); ++ int open_flags = (O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOCTTY ++ | O_NONBLOCK | extra_flags); ++ int new_fd = openat (fd, dir, open_flags); + DIR *dirp; + + if (new_fd < 0) + return NULL; +- set_cloexec_flag (new_fd, true); + dirp = fdopendir (new_fd); + if (dirp) + *pdir_fd = new_fd; +@@ -367,15 +369,13 @@ static int + internal_function + diropen (FTS const *sp, char const *dir) + { +- int open_flags = (O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK ++ int open_flags = (O_SEARCH | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK + | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0) + | (ISSET (FTS_NOATIME) ? O_NOATIME : 0)); + + int fd = (ISSET (FTS_CWDFD) + ? openat (sp->fts_cwd_fd, dir, open_flags) + : open (dir, open_flags)); +- if (0 <= fd) +- set_cloexec_flag (fd, true); + return fd; + } + +@@ -471,6 +471,7 @@ fts_open (char * const *argv, + if ((parent = fts_alloc(sp, "", 0)) == NULL) + goto mem2; + parent->fts_level = FTS_ROOTPARENTLEVEL; ++ parent->fts_n_dirs_remaining = -1; + } + + /* The classic fts implementation would call fts_stat with +@@ -657,39 +658,139 @@ fts_close (FTS *sp) + return (0); + } + ++/* Minimum link count of a traditional Unix directory. When leaf ++ optimization is OK and MIN_DIR_NLINK <= st_nlink, then st_nlink is ++ an upper bound on the number of subdirectories (counting "." and ++ ".."). */ ++enum { MIN_DIR_NLINK = 2 }; ++ ++/* Whether leaf optimization is OK for a directory. */ ++enum leaf_optimization ++ { ++ /* st_nlink is not reliable for this directory's subdirectories. */ ++ NO_LEAF_OPTIMIZATION, ++ ++ /* Leaf optimization is OK, but is not useful for avoiding stat calls. */ ++ OK_LEAF_OPTIMIZATION, ++ ++ /* Leaf optimization is not only OK: it is useful for avoiding ++ stat calls, because dirent.d_type does not work. */ ++ NOSTAT_LEAF_OPTIMIZATION ++ }; ++ + #if defined __linux__ \ + && HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE + + # include + + /* Linux-specific constants from coreutils' src/fs.h */ +-# define S_MAGIC_TMPFS 0x1021994 ++# define S_MAGIC_AFS 0x5346414F + # define S_MAGIC_NFS 0x6969 ++# define S_MAGIC_PROC 0x9FA0 + # define S_MAGIC_REISERFS 0x52654973 ++# define S_MAGIC_TMPFS 0x1021994 + # define S_MAGIC_XFS 0x58465342 +-# define S_MAGIC_PROC 0x9FA0 + +-/* Return false if it is easy to determine the file system type of +- the directory on which DIR_FD is open, and sorting dirents on +- inode numbers is known not to improve traversal performance with +- that type of file system. Otherwise, return true. */ ++# ifdef HAVE___FSWORD_T ++typedef __fsword_t fsword; ++# else ++typedef long int fsword; ++# endif ++ ++/* Map a stat.st_dev number to a file system type number f_ftype. */ ++struct dev_type ++{ ++ dev_t st_dev; ++ fsword f_type; ++}; ++ ++/* Use a tiny initial size. If a traversal encounters more than ++ a few devices, the cost of growing/rehashing this table will be ++ rendered negligible by the number of inodes processed. */ ++enum { DEV_TYPE_HT_INITIAL_SIZE = 13 }; ++ ++static size_t ++dev_type_hash (void const *x, size_t table_size) ++{ ++ struct dev_type const *ax = x; ++ uintmax_t dev = ax->st_dev; ++ return dev % table_size; ++} ++ + static bool +-dirent_inode_sort_may_be_useful (int dir_fd) ++dev_type_compare (void const *x, void const *y) ++{ ++ struct dev_type const *ax = x; ++ struct dev_type const *ay = y; ++ return ax->st_dev == ay->st_dev; ++} ++ ++/* Return the file system type of P, or 0 if not known. ++ Try to cache known values. */ ++ ++static fsword ++filesystem_type (FTSENT const *p) ++{ ++ FTS *sp = p->fts_fts; ++ Hash_table *h = sp->fts_leaf_optimization_works_ht; ++ struct dev_type *ent; ++ struct statfs fs_buf; ++ ++ /* If we're not in CWDFD mode, don't bother with this optimization, ++ since the caller is not serious about performance. */ ++ if (!ISSET (FTS_CWDFD)) ++ return 0; ++ ++ if (! h) ++ h = sp->fts_leaf_optimization_works_ht ++ = hash_initialize (DEV_TYPE_HT_INITIAL_SIZE, NULL, dev_type_hash, ++ dev_type_compare, free); ++ if (h) ++ { ++ struct dev_type tmp; ++ tmp.st_dev = p->fts_statp->st_dev; ++ ent = hash_lookup (h, &tmp); ++ if (ent) ++ return ent->f_type; ++ } ++ ++ /* Look-up failed. Query directly and cache the result. */ ++ if (fstatfs (p->fts_fts->fts_cwd_fd, &fs_buf) != 0) ++ return 0; ++ ++ if (h) ++ { ++ struct dev_type *t2 = malloc (sizeof *t2); ++ if (t2) ++ { ++ t2->st_dev = p->fts_statp->st_dev; ++ t2->f_type = fs_buf.f_type; ++ ++ ent = hash_insert (h, t2); ++ if (ent) ++ fts_assert (ent == t2); ++ else ++ free (t2); ++ } ++ } ++ ++ return fs_buf.f_type; ++} ++ ++/* Return false if it is easy to determine the file system type of the ++ directory P, and sorting dirents on inode numbers is known not to ++ improve traversal performance with that type of file system. ++ Otherwise, return true. */ ++static bool ++dirent_inode_sort_may_be_useful (FTSENT const *p) + { + /* Skip the sort only if we can determine efficiently + that skipping it is the right thing to do. + The cost of performing an unnecessary sort is negligible, + while the cost of *not* performing it can be O(N^2) with + a very large constant. */ +- struct statfs fs_buf; +- +- /* If fstatfs fails, assume sorting would be useful. */ +- if (fstatfs (dir_fd, &fs_buf) != 0) +- return true; + +- /* FIXME: what about when f_type is not an integral type? +- deal with that if/when it's encountered. */ +- switch (fs_buf.f_type) ++ switch (filesystem_type (p)) + { + case S_MAGIC_TMPFS: + case S_MAGIC_NFS: +@@ -702,134 +803,58 @@ dirent_inode_sort_may_be_useful (int dir_fd) + } + } + +-/* Given a file descriptor DIR_FD open on a directory D, +- return true if it is valid to apply the leaf-optimization +- technique of counting directories in D via stat.st_nlink. */ +-static bool +-leaf_optimization_applies (int dir_fd) ++/* Given an FTS entry P for a directory D, ++ return true if it is both useful and valid to apply leaf optimization. ++ The optimization is useful only for file systems that lack usable ++ dirent.d_type info. The optimization is valid if an st_nlink value ++ of at least MIN_DIR_NLINK is an upper bound on the number of ++ subdirectories of D, counting "." and ".." as subdirectories. */ ++static enum leaf_optimization ++leaf_optimization (FTSENT const *p) + { +- struct statfs fs_buf; +- +- /* If fstatfs fails, assume we can't use the optimization. */ +- if (fstatfs (dir_fd, &fs_buf) != 0) +- return false; +- +- /* FIXME: do we need to detect AFS mount points? I doubt it, +- unless fstatfs can report S_MAGIC_REISERFS for such a directory. */ +- +- switch (fs_buf.f_type) ++ switch (filesystem_type (p)) + { +- /* List here the file system types that lack usable dirent.d_type ++ /* List here the file system types that may lack usable dirent.d_type + info, yet for which the optimization does apply. */ + case S_MAGIC_REISERFS: +- case S_MAGIC_XFS: +- return true; +- +- /* Explicitly list here any other file system type for which the +- optimization is not applicable, but need documentation. */ ++ case S_MAGIC_XFS: /* XFS lacked it until 2013-08-22 commit. */ ++ return NOSTAT_LEAF_OPTIMIZATION; ++ ++ case 0: ++ /* Leaf optimization is unsafe if the file system type is unknown. */ ++ FALLTHROUGH; ++ case S_MAGIC_AFS: ++ /* Although AFS mount points are not counted in st_nlink, they ++ act like directories. See . */ ++ FALLTHROUGH; + case S_MAGIC_NFS: + /* NFS provides usable dirent.d_type but not necessarily for all entries + of large directories, so as per + NFS should return true. However st_nlink values are not accurate on + all implementations as per . */ +- /* fall through */ ++ FALLTHROUGH; + case S_MAGIC_PROC: +- /* Per /proc may have +- bogus stat.st_nlink values. */ +- /* fall through */ ++ /* Per /proc ++ may have bogus stat.st_nlink values. */ ++ return NO_LEAF_OPTIMIZATION; ++ + default: +- return false; ++ return OK_LEAF_OPTIMIZATION; + } + } + + #else + static bool +-dirent_inode_sort_may_be_useful (int dir_fd _GL_UNUSED) { return true; } +-static bool +-leaf_optimization_applies (int dir_fd _GL_UNUSED) { return false; } +-#endif +- +-/* link-count-optimization entry: +- map a stat.st_dev number to a boolean: leaf_optimization_works */ +-struct LCO_ent ++dirent_inode_sort_may_be_useful (FTSENT const *p _GL_UNUSED) + { +- dev_t st_dev; +- bool opt_ok; +-}; +- +-/* Use a tiny initial size. If a traversal encounters more than +- a few devices, the cost of growing/rehashing this table will be +- rendered negligible by the number of inodes processed. */ +-enum { LCO_HT_INITIAL_SIZE = 13 }; +- +-static size_t +-LCO_hash (void const *x, size_t table_size) +-{ +- struct LCO_ent const *ax = x; +- return (uintmax_t) ax->st_dev % table_size; ++ return true; + } +- +-static bool +-LCO_compare (void const *x, void const *y) ++static enum leaf_optimization ++leaf_optimization (FTSENT const *p _GL_UNUSED) + { +- struct LCO_ent const *ax = x; +- struct LCO_ent const *ay = y; +- return ax->st_dev == ay->st_dev; +-} +- +-/* Ask the same question as leaf_optimization_applies, but query +- the cache first (FTS.fts_leaf_optimization_works_ht), and if necessary, +- update that cache. */ +-static bool +-link_count_optimize_ok (FTSENT const *p) +-{ +- FTS *sp = p->fts_fts; +- Hash_table *h = sp->fts_leaf_optimization_works_ht; +- struct LCO_ent tmp; +- struct LCO_ent *ent; +- bool opt_ok; +- struct LCO_ent *t2; +- +- /* If we're not in CWDFD mode, don't bother with this optimization, +- since the caller is not serious about performance. */ +- if (!ISSET(FTS_CWDFD)) +- return false; +- +- /* map st_dev to the boolean, leaf_optimization_works */ +- if (h == NULL) +- { +- h = sp->fts_leaf_optimization_works_ht +- = hash_initialize (LCO_HT_INITIAL_SIZE, NULL, LCO_hash, +- LCO_compare, free); +- if (h == NULL) +- return false; +- } +- tmp.st_dev = p->fts_statp->st_dev; +- ent = hash_lookup (h, &tmp); +- if (ent) +- return ent->opt_ok; +- +- /* Look-up failed. Query directly and cache the result. */ +- t2 = malloc (sizeof *t2); +- if (t2 == NULL) +- return false; +- +- /* Is it ok to perform the optimization in the dir, FTS_CWD_FD? */ +- opt_ok = leaf_optimization_applies (sp->fts_cwd_fd); +- t2->opt_ok = opt_ok; +- t2->st_dev = p->fts_statp->st_dev; +- +- ent = hash_insert (h, t2); +- if (ent == NULL) +- { +- /* insertion failed */ +- free (t2); +- return false; +- } +- fts_assert (ent == t2); +- +- return opt_ok; ++ return NO_LEAF_OPTIMIZATION; + } ++#endif + + /* + * Special case of "/" at the end of the file name so that slashes aren't +@@ -1016,13 +1041,11 @@ check_for_dir: + if (p->fts_statp->st_size == FTS_STAT_REQUIRED) + { + FTSENT *parent = p->fts_parent; +- if (FTS_ROOTLEVEL < p->fts_level +- /* ->fts_n_dirs_remaining is not valid +- for command-line-specified names. */ +- && parent->fts_n_dirs_remaining == 0 ++ if (parent->fts_n_dirs_remaining == 0 + && ISSET(FTS_NOSTAT) + && ISSET(FTS_PHYSICAL) +- && link_count_optimize_ok (parent)) ++ && (leaf_optimization (parent) ++ == NOSTAT_LEAF_OPTIMIZATION)) + { + /* nothing more needed */ + } +@@ -1031,7 +1054,8 @@ check_for_dir: + p->fts_info = fts_stat(sp, p, false); + if (S_ISDIR(p->fts_statp->st_mode) + && p->fts_level != FTS_ROOTLEVEL +- && parent->fts_n_dirs_remaining) ++ && 0 < parent->fts_n_dirs_remaining ++ && parent->fts_n_dirs_remaining != (nlink_t) -1) + parent->fts_n_dirs_remaining--; + } + } +@@ -1300,8 +1324,6 @@ fts_build (register FTS *sp, int type) + bool descend; + bool doadjust; + ptrdiff_t level; +- nlink_t nlinks; +- bool nostat; + size_t len, maxlen, new_len; + char *cp; + int dir_fd; +@@ -1372,24 +1394,6 @@ fts_build (register FTS *sp, int type) + max_entries = sp->fts_compar ? SIZE_MAX : FTS_MAX_READDIR_ENTRIES; + + /* +- * Nlinks is the number of possible entries of type directory in the +- * directory if we're cheating on stat calls, 0 if we're not doing +- * any stat calls at all, (nlink_t) -1 if we're statting everything. +- */ +- if (type == BNAMES) { +- nlinks = 0; +- /* Be quiet about nostat, GCC. */ +- nostat = false; +- } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) { +- nlinks = (cur->fts_statp->st_nlink +- - (ISSET(FTS_SEEDOT) ? 0 : 2)); +- nostat = true; +- } else { +- nlinks = -1; +- nostat = false; +- } +- +- /* + * If we're going to need to stat anything or we want to descend + * and stay in the directory, chdir. If this fails we keep going, + * but set a flag so we don't chdir after the post-order visit. +@@ -1410,15 +1414,22 @@ fts_build (register FTS *sp, int type) + the required dirp and dir_fd. */ + descend = true; + } +- else if (nlinks || type == BREAD) { ++ else ++ { ++ /* Try to descend unless it is a names-only fts_children, ++ or the directory is known to lack subdirectories. */ ++ descend = (type != BNAMES ++ && ! (ISSET (FTS_NOSTAT) && ISSET (FTS_PHYSICAL) ++ && ! ISSET (FTS_SEEDOT) ++ && cur->fts_statp->st_nlink == MIN_DIR_NLINK ++ && (leaf_optimization (cur) ++ != NO_LEAF_OPTIMIZATION))); ++ if (descend || type == BREAD) ++ { + if (ISSET(FTS_CWDFD)) +- { +- dir_fd = dup (dir_fd); +- if (0 <= dir_fd) +- set_cloexec_flag (dir_fd, true); +- } ++ dir_fd = fcntl (dir_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1); + if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) { +- if (nlinks && type == BREAD) ++ if (descend && type == BREAD) + cur->fts_errno = errno; + cur->fts_flags |= FTS_DONTCHDIR; + descend = false; +@@ -1428,8 +1439,8 @@ fts_build (register FTS *sp, int type) + cur->fts_dirp = NULL; + } else + descend = true; +- } else +- descend = false; ++ } ++ } + + /* + * Figure out the max file name length that can be stored in the +@@ -1460,7 +1471,6 @@ fts_build (register FTS *sp, int type) + tail = NULL; + nitems = 0; + while (cur->fts_dirp) { +- bool is_dir; + size_t d_namelen; + __set_errno (0); + struct dirent *dp = readdir(cur->fts_dirp); +@@ -1561,19 +1571,10 @@ mem1: saved_errno = errno; + to caller, when possible. */ + set_stat_type (p->fts_statp, D_TYPE (dp)); + fts_set_stat_required(p, !skip_stat); +- is_dir = (ISSET(FTS_PHYSICAL) +- && DT_MUST_BE(dp, DT_DIR)); + } else { + p->fts_info = fts_stat(sp, p, false); +- is_dir = (p->fts_info == FTS_D +- || p->fts_info == FTS_DC +- || p->fts_info == FTS_DOT); + } + +- /* Decrement link count if applicable. */ +- if (nlinks > 0 && is_dir) +- nlinks -= nostat; +- + /* We walk in directory order so "ls -f" doesn't get upset. */ + p->fts_link = NULL; + if (head == NULL) +@@ -1645,8 +1646,7 @@ mem1: saved_errno = errno; + inode numbers. */ + if (nitems > _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD + && !sp->fts_compar +- && ISSET (FTS_CWDFD) +- && dirent_inode_sort_may_be_useful (sp->fts_cwd_fd)) { ++ && dirent_inode_sort_may_be_useful (cur)) { + sp->fts_compar = fts_compare_ino; + head = fts_sort (sp, head, nitems); + sp->fts_compar = NULL; +@@ -1769,7 +1769,7 @@ fd_ring_check (FTS const *sp) + I_ring fd_w = sp->fts_fd_ring; + + int cwd_fd = sp->fts_cwd_fd; +- cwd_fd = dup (cwd_fd); ++ cwd_fd = fcntl (cwd_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1); + char *dot = getcwdat (cwd_fd, NULL, 0); + error (0, 0, "===== check ===== cwd: %s", dot); + free (dot); +@@ -1778,7 +1778,8 @@ fd_ring_check (FTS const *sp) + int fd = i_ring_pop (&fd_w); + if (0 <= fd) + { +- int parent_fd = openat (cwd_fd, "..", O_SEARCH | O_NOATIME); ++ int open_flags = O_SEARCH | O_CLOEXEC | O_NOATIME; ++ int parent_fd = openat (cwd_fd, "..", open_flags); + if (parent_fd < 0) + { + // Warn? +@@ -1807,7 +1808,6 @@ internal_function + fts_stat(FTS *sp, register FTSENT *p, bool follow) + { + struct stat *sbp = p->fts_statp; +- int saved_errno; + + if (p->fts_level == FTS_ROOTLEVEL && ISSET(FTS_COMFOLLOW)) + follow = true; +@@ -1819,13 +1819,12 @@ fts_stat(FTS *sp, register FTSENT *p, bool follow) + */ + if (ISSET(FTS_LOGICAL) || follow) { + if (stat(p->fts_accpath, sbp)) { +- saved_errno = errno; + if (errno == ENOENT + && lstat(p->fts_accpath, sbp) == 0) { + __set_errno (0); + return (FTS_SLNONE); + } +- p->fts_errno = saved_errno; ++ p->fts_errno = errno; + goto err; + } + } else if (fstatat(sp->fts_cwd_fd, p->fts_accpath, sbp, +@@ -1836,8 +1835,11 @@ err: memset(sbp, 0, sizeof(struct stat)); + } + + if (S_ISDIR(sbp->st_mode)) { +- p->fts_n_dirs_remaining = (sbp->st_nlink +- - (ISSET(FTS_SEEDOT) ? 0 : 2)); ++ p->fts_n_dirs_remaining ++ = ((sbp->st_nlink < MIN_DIR_NLINK ++ || p->fts_level <= FTS_ROOTLEVEL) ++ ? -1 ++ : sbp->st_nlink - (ISSET (FTS_SEEDOT) ? 0 : MIN_DIR_NLINK)); + if (ISDOT(p->fts_name)) { + /* Command-line "." and ".." are real directories. */ + return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT); +diff --git a/lib/fts_.h b/lib/fts_.h +index 46fd0df..f84e45d 100644 +--- a/lib/fts_.h ++++ b/lib/fts_.h +@@ -13,7 +13,7 @@ + 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, see . */ ++ along with this program. If not, see . */ + + /* + * Copyright (c) 1989, 1993 +@@ -220,7 +220,11 @@ typedef struct _ftsent { + ptrdiff_t fts_level; /* depth (-1 to N) */ + + size_t fts_namelen; /* strlen(fts_name) */ +- nlink_t fts_n_dirs_remaining; /* count down from st_nlink */ ++ ++ /* If not (nlink_t) -1, an upper bound on the number of ++ remaining subdirectories of interest. If this becomes ++ zero, some work can be avoided. */ ++ nlink_t fts_n_dirs_remaining; + + # define FTS_D 1 /* preorder directory */ + # define FTS_DC 2 /* directory that causes cycles */ +-- +2.13.6 + diff --git a/coreutils-8.29-CVE-2017-18018.patch b/coreutils-8.29-CVE-2017-18018.patch new file mode 100644 index 0000000..577c90b --- /dev/null +++ b/coreutils-8.29-CVE-2017-18018.patch @@ -0,0 +1,124 @@ +From 0aa9b0a92cb61af76b75b57abfd6ea1a7c627367 Mon Sep 17 00:00:00 2001 +From: Michael Orlitzky +Date: Thu, 28 Dec 2017 15:52:42 -0500 +Subject: [PATCH 1/2] doc: clarify chown/chgrp --dereference defaults + +* doc/coreutils.texi: the documentation for the --dereference + flag of chown/chgrp states that it is the default mode of + operation. Document that this is only the case when operating + non-recursively. + +Upstream-commit: 7597cfa482e42a00a69fb9577ee523762980a9a2 +Signed-off-by: Kamil Dudka +--- + doc/coreutils.texi | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/doc/coreutils.texi b/doc/coreutils.texi +index de1f2eb..de06c0f 100644 +--- a/doc/coreutils.texi ++++ b/doc/coreutils.texi +@@ -10989,7 +10989,7 @@ chown -h -R --from=OLDUSER NEWUSER / + @cindex symbolic links, changing owner + @findex lchown + Do not act on symbolic links themselves but rather on what they point to. +-This is the default. ++This is the default when not operating recursively. + + @item -h + @itemx --no-dereference +@@ -11119,7 +11119,7 @@ changed. + @cindex symbolic links, changing owner + @findex lchown + Do not act on symbolic links themselves but rather on what they point to. +-This is the default. ++This is the default when not operating recursively. + + @item -h + @itemx --no-dereference +-- +2.13.6 + + +From 3fb331864c718e065804049001b573ff94810772 Mon Sep 17 00:00:00 2001 +From: Michael Orlitzky +Date: Thu, 4 Jan 2018 11:38:21 -0500 +Subject: [PATCH 2/2] doc: warn about following symlinks recursively in + chown/chgrp + +In both chown and chgrp (which shares its code with chown), operating +on symlinks recursively has a window of vulnerability where the +destination user or group can change the target of the operation. +Warn about combining the --dereference, --recursive, and -L flags. + +* doc/coreutils.texi (warnOptDerefWithRec): Add macro. +(node chown invocation): Add it to --dereference and -L. +(node chgrp invocation): Likewise. + +See also: CVE-2017-18018 + +Upstream-commit: bc2fd9796403e03bb757b064d44c22fab92e6842 +Signed-off-by: Kamil Dudka +--- + doc/coreutils.texi | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/doc/coreutils.texi b/doc/coreutils.texi +index de06c0f..24cc85b 100644 +--- a/doc/coreutils.texi ++++ b/doc/coreutils.texi +@@ -1428,6 +1428,19 @@ a command line argument is a symbolic link to a directory, traverse it. + In a recursive traversal, traverse every symbolic link to a directory + that is encountered. + @end macro ++ ++@c Append the following warning to -L where appropriate (e.g. chown). ++@macro warnOptDerefWithRec ++ ++Combining this dereferencing option with the @option{--recursive} option ++may create a security risk: ++During the traversal of the directory tree, an attacker may be able to ++introduce a symlink to an arbitrary target; when the tool reaches that, ++the operation will be performed on the target of that symlink, ++possibly allowing the attacker to escalate privileges. ++ ++@end macro ++ + @choptL + + @macro choptP +@@ -10990,6 +11003,7 @@ chown -h -R --from=OLDUSER NEWUSER / + @findex lchown + Do not act on symbolic links themselves but rather on what they point to. + This is the default when not operating recursively. ++@warnOptDerefWithRec + + @item -h + @itemx --no-dereference +@@ -11046,6 +11060,7 @@ Recursively change ownership of directories and their contents. + @xref{Traversing symlinks}. + + @choptL ++@warnOptDerefWithRec + @xref{Traversing symlinks}. + + @choptP +@@ -11120,6 +11135,7 @@ changed. + @findex lchown + Do not act on symbolic links themselves but rather on what they point to. + This is the default when not operating recursively. ++@warnOptDerefWithRec + + @item -h + @itemx --no-dereference +@@ -11175,6 +11191,7 @@ Recursively change the group ownership of directories and their contents. + @xref{Traversing symlinks}. + + @choptL ++@warnOptDerefWithRec + @xref{Traversing symlinks}. + + @choptP +-- +2.13.6 + diff --git a/coreutils-8.29-fts-leaf-opt.patch b/coreutils-8.29-fts-leaf-opt.patch new file mode 100644 index 0000000..926e5de --- /dev/null +++ b/coreutils-8.29-fts-leaf-opt.patch @@ -0,0 +1,228 @@ +From 42b0e609390e62a900c0d73de60282c8b0f15121 Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Thu, 5 Apr 2018 08:48:01 -0700 +Subject: [PATCH 1/2] fts: treat CIFS like NFS + +Problem reported by Kamil Dudka in: +https://lists.gnu.org/r/bug-gnulib/2018-04/msg00015.html +* lib/fts.c (S_MAGIC_CIFS): New macro. +(dirent_inode_sort_may_be_useful, leaf_optimization): +Treat CIFS like NFS. + +Upstream-commit: 2e53df541a30d438859087ed4b5a396e04697b9b +Signed-off-by: Kamil Dudka +--- + lib/fts.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/lib/fts.c b/lib/fts.c +index 8f2595d..0689da6 100644 +--- a/lib/fts.c ++++ b/lib/fts.c +@@ -685,6 +685,7 @@ enum leaf_optimization + + /* Linux-specific constants from coreutils' src/fs.h */ + # define S_MAGIC_AFS 0x5346414F ++# define S_MAGIC_CIFS 0xFF534D42 + # define S_MAGIC_NFS 0x6969 + # define S_MAGIC_PROC 0x9FA0 + # define S_MAGIC_REISERFS 0x52654973 +@@ -792,8 +793,9 @@ dirent_inode_sort_may_be_useful (FTSENT const *p) + + switch (filesystem_type (p)) + { +- case S_MAGIC_TMPFS: ++ case S_MAGIC_CIFS: + case S_MAGIC_NFS: ++ case S_MAGIC_TMPFS: + /* On a file system of any of these types, sorting + is unnecessary, and hence wasteful. */ + return false; +@@ -827,6 +829,10 @@ leaf_optimization (FTSENT const *p) + /* Although AFS mount points are not counted in st_nlink, they + act like directories. See . */ + FALLTHROUGH; ++ case S_MAGIC_CIFS: ++ /* Leaf optimization causes 'find' to abort. See ++ . */ ++ FALLTHROUGH; + case S_MAGIC_NFS: + /* NFS provides usable dirent.d_type but not necessarily for all entries + of large directories, so as per +-- +2.14.3 + + +From bf96f62507931eb296c5b16d7e46c141ad505a1f Mon Sep 17 00:00:00 2001 +From: Paul Eggert +Date: Wed, 11 Apr 2018 12:50:35 -0700 +Subject: [PATCH 2/2] fts: fix bug in find across filesystems + +This fixes a bug I introduced last summer. +Problem reported by Kamil Dudka in: +https://lists.gnu.org/r/bug-gnulib/2018-04/msg00033.html +* lib/fts.c (filesystem_type, dirent_inode_sort_may_be_useful) +(leaf_optimization): +New arg for file descriptor. All callers changed. +(fts_build): Check for whether inodes should be sorted +before closing the directory. + +Upstream-commit: 81b8c0d3be98f5a77403599de3d06329b3e7673e +Signed-off-by: Kamil Dudka +--- + lib/fts.c | 55 +++++++++++++++++++++++++++++++------------------------ + 1 file changed, 31 insertions(+), 24 deletions(-) + +diff --git a/lib/fts.c b/lib/fts.c +index 0689da6..6420ba1 100644 +--- a/lib/fts.c ++++ b/lib/fts.c +@@ -726,11 +726,12 @@ dev_type_compare (void const *x, void const *y) + return ax->st_dev == ay->st_dev; + } + +-/* Return the file system type of P, or 0 if not known. ++/* Return the file system type of P with file descriptor FD, or 0 if not known. ++ If FD is negative, P's file descriptor is unavailable. + Try to cache known values. */ + + static fsword +-filesystem_type (FTSENT const *p) ++filesystem_type (FTSENT const *p, int fd) + { + FTS *sp = p->fts_fts; + Hash_table *h = sp->fts_leaf_optimization_works_ht; +@@ -756,7 +757,7 @@ filesystem_type (FTSENT const *p) + } + + /* Look-up failed. Query directly and cache the result. */ +- if (fstatfs (p->fts_fts->fts_cwd_fd, &fs_buf) != 0) ++ if (fd < 0 || fstatfs (fd, &fs_buf) != 0) + return 0; + + if (h) +@@ -778,12 +779,12 @@ filesystem_type (FTSENT const *p) + return fs_buf.f_type; + } + +-/* Return false if it is easy to determine the file system type of the +- directory P, and sorting dirents on inode numbers is known not to +- improve traversal performance with that type of file system. +- Otherwise, return true. */ ++/* Return true if sorting dirents on inode numbers is known to improve ++ traversal performance for the directory P with descriptor DIR_FD. ++ Return false otherwise. When in doubt, return true. ++ DIR_FD is negative if unavailable. */ + static bool +-dirent_inode_sort_may_be_useful (FTSENT const *p) ++dirent_inode_sort_may_be_useful (FTSENT const *p, int dir_fd) + { + /* Skip the sort only if we can determine efficiently + that skipping it is the right thing to do. +@@ -791,7 +792,7 @@ dirent_inode_sort_may_be_useful (FTSENT const *p) + while the cost of *not* performing it can be O(N^2) with + a very large constant. */ + +- switch (filesystem_type (p)) ++ switch (filesystem_type (p, dir_fd)) + { + case S_MAGIC_CIFS: + case S_MAGIC_NFS: +@@ -805,16 +806,17 @@ dirent_inode_sort_may_be_useful (FTSENT const *p) + } + } + +-/* Given an FTS entry P for a directory D, ++/* Given an FTS entry P for a directory with descriptor DIR_FD, + return true if it is both useful and valid to apply leaf optimization. + The optimization is useful only for file systems that lack usable + dirent.d_type info. The optimization is valid if an st_nlink value + of at least MIN_DIR_NLINK is an upper bound on the number of +- subdirectories of D, counting "." and ".." as subdirectories. */ ++ subdirectories of D, counting "." and ".." as subdirectories. ++ DIR_FD is negative if unavailable. */ + static enum leaf_optimization +-leaf_optimization (FTSENT const *p) ++leaf_optimization (FTSENT const *p, int dir_fd) + { +- switch (filesystem_type (p)) ++ switch (filesystem_type (p, dir_fd)) + { + /* List here the file system types that may lack usable dirent.d_type + info, yet for which the optimization does apply. */ +@@ -851,12 +853,13 @@ leaf_optimization (FTSENT const *p) + + #else + static bool +-dirent_inode_sort_may_be_useful (FTSENT const *p _GL_UNUSED) ++dirent_inode_sort_may_be_useful (FTSENT const *p _GL_UNUSED, ++ int dir_fd _GL_UNUSED) + { + return true; + } + static enum leaf_optimization +-leaf_optimization (FTSENT const *p _GL_UNUSED) ++leaf_optimization (FTSENT const *p _GL_UNUSED, int dir_fd _GL_UNUSED) + { + return NO_LEAF_OPTIMIZATION; + } +@@ -1050,7 +1053,7 @@ check_for_dir: + if (parent->fts_n_dirs_remaining == 0 + && ISSET(FTS_NOSTAT) + && ISSET(FTS_PHYSICAL) +- && (leaf_optimization (parent) ++ && (leaf_optimization (parent, sp->fts_cwd_fd) + == NOSTAT_LEAF_OPTIMIZATION)) + { + /* nothing more needed */ +@@ -1335,6 +1338,7 @@ fts_build (register FTS *sp, int type) + int dir_fd; + FTSENT *cur = sp->fts_cur; + bool continue_readdir = !!cur->fts_dirp; ++ bool sort_by_inode = false; + size_t max_entries; + + /* When cur->fts_dirp is non-NULL, that means we should +@@ -1428,7 +1432,7 @@ fts_build (register FTS *sp, int type) + && ! (ISSET (FTS_NOSTAT) && ISSET (FTS_PHYSICAL) + && ! ISSET (FTS_SEEDOT) + && cur->fts_statp->st_nlink == MIN_DIR_NLINK +- && (leaf_optimization (cur) ++ && (leaf_optimization (cur, dir_fd) + != NO_LEAF_OPTIMIZATION))); + if (descend || type == BREAD) + { +@@ -1589,6 +1593,15 @@ mem1: saved_errno = errno; + tail->fts_link = p; + tail = p; + } ++ ++ /* If there are many entries, no sorting function has been ++ specified, and this file system is of a type that may be ++ slow with a large number of entries, arrange to sort the ++ directory entries on increasing inode numbers. */ ++ if (nitems == _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD ++ && !sp->fts_compar) ++ sort_by_inode = dirent_inode_sort_may_be_useful (cur, dir_fd); ++ + ++nitems; + if (max_entries <= nitems) { + /* When there are too many dir entries, leave +@@ -1646,13 +1659,7 @@ mem1: saved_errno = errno; + return (NULL); + } + +- /* If there are many entries, no sorting function has been specified, +- and this file system is of a type that may be slow with a large +- number of entries, then sort the directory entries on increasing +- inode numbers. */ +- if (nitems > _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD +- && !sp->fts_compar +- && dirent_inode_sort_may_be_useful (cur)) { ++ if (sort_by_inode) { + sp->fts_compar = fts_compare_ino; + head = fts_sort (sp, head, nitems); + sp->fts_compar = NULL; +-- +2.14.3 + diff --git a/coreutils-8.29-mv-n-noreplace.patch b/coreutils-8.29-mv-n-noreplace.patch new file mode 100644 index 0000000..6c31b6b --- /dev/null +++ b/coreutils-8.29-mv-n-noreplace.patch @@ -0,0 +1,1017 @@ +From 76df06ff8fa39ae0cb0d167b7f622139778dc7d7 Mon Sep 17 00:00:00 2001 +From: Kamil Dudka +Date: Thu, 4 Jan 2018 09:42:10 +0100 +Subject: [PATCH] mv -n: do not overwrite the destination + +... if it is created by another process after mv has checked its +non-existence. + +* src/copy.c (copy_internal): Use renameat2 (..., RENAME_NOREPLACE) +if called by mv -n. If it fails with EEXIST in that case, pretend +successful rename as if the existing destination file was detected +by the preceding lstat call. + +Fixes https://bugs.gnu.org/29961 +--- + aclocal.m4 | 1 + + bootstrap.conf | 2 + + gnulib-tests/gnulib.mk | 18 ++++ + gnulib-tests/test-renameat.c | 206 ++++++++++++++++++++++++++++++++++++++ + gnulib-tests/test-renameat2.c | 209 ++++++++++++++++++++++++++++++++++++++ + lib/gnulib.mk | 21 +++- + lib/renameat.c | 25 +++++ + lib/renameat2.c | 227 ++++++++++++++++++++++++++++++++++++++++++ + lib/renameat2.h | 30 ++++++ + m4/gnulib-comp.m4 | 22 ++++ + m4/renameat.m4 | 25 +++++ + src/copy.c | 27 ++++- + 12 files changed, 808 insertions(+), 5 deletions(-) + create mode 100644 gnulib-tests/test-renameat.c + create mode 100644 gnulib-tests/test-renameat2.c + create mode 100644 lib/renameat.c + create mode 100644 lib/renameat2.c + create mode 100644 lib/renameat2.h + create mode 100644 m4/renameat.m4 + +diff --git a/aclocal.m4 b/aclocal.m4 +index 9c5a2b0..c678967 100644 +--- a/aclocal.m4 ++++ b/aclocal.m4 +@@ -1476,6 +1476,7 @@ m4_include([m4/realloc.m4]) + m4_include([m4/regex.m4]) + m4_include([m4/remove.m4]) + m4_include([m4/rename.m4]) ++m4_include([m4/renameat.m4]) + m4_include([m4/rewinddir.m4]) + m4_include([m4/rmdir.m4]) + m4_include([m4/rpmatch.m4]) +diff --git a/bootstrap.conf b/bootstrap.conf +index 7def1f9..9b7c913 100644 +--- a/bootstrap.conf ++++ b/bootstrap.conf +@@ -202,6 +202,8 @@ gnulib_modules=" + regex + remove + rename ++ renameat ++ renameat2 + rmdir + root-dev-ino + rpmatch +diff --git a/gnulib-tests/gnulib.mk b/gnulib-tests/gnulib.mk +index b2da030..38d439c 100644 +--- a/gnulib-tests/gnulib.mk ++++ b/gnulib-tests/gnulib.mk +@@ -1630,6 +1630,24 @@ EXTRA_DIST += test-rename.h test-rename.c signature.h macros.h + + ## end gnulib module rename-tests + ++## begin gnulib module renameat-tests ++ ++TESTS += test-renameat ++check_PROGRAMS += test-renameat ++test_renameat_LDADD = $(LDADD) @LIBINTL@ ++EXTRA_DIST += test-rename.h test-renameat.c signature.h macros.h ++ ++## end gnulib module renameat-tests ++ ++## begin gnulib module renameat2-tests ++ ++TESTS += test-renameat2 ++check_PROGRAMS += test-renameat2 ++test_renameat2_LDADD = $(LDADD) @LIBINTL@ ++EXTRA_DIST += test-rename.h test-renameat2.c signature.h macros.h ++ ++## end gnulib module renameat2-tests ++ + ## begin gnulib module rmdir-tests + + TESTS += test-rmdir +diff --git a/gnulib-tests/test-renameat.c b/gnulib-tests/test-renameat.c +new file mode 100644 +index 0000000..ac96d88 +--- /dev/null ++++ b/gnulib-tests/test-renameat.c +@@ -0,0 +1,206 @@ ++/* Tests of renameat. ++ Copyright (C) 2009-2017 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 3 of the License, 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, see . */ ++ ++/* Written by Eric Blake , 2009. */ ++ ++#include ++ ++#include ++ ++#include "signature.h" ++SIGNATURE_CHECK (renameat, int, (int, char const *, int, char const *)); ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "filenamecat.h" ++#include "ignore-value.h" ++#include "macros.h" ++ ++#define BASE "test-renameat.t" ++ ++#include "test-rename.h" ++ ++static int dfd1 = AT_FDCWD; ++static int dfd2 = AT_FDCWD; ++ ++/* Wrapper to test renameat like rename. */ ++static int ++do_rename (char const *name1, char const *name2) ++{ ++ return renameat (dfd1, name1, dfd2, name2); ++} ++ ++int ++main (void) ++{ ++ int i; ++ int dfd; ++ char *cwd; ++ int result; ++ ++ /* Clean up any trash from prior testsuite runs. */ ++ ignore_value (system ("rm -rf " BASE "*")); ++ ++ /* Test behaviour for invalid file descriptors. */ ++ { ++ errno = 0; ++ ASSERT (renameat (-1, "foo", AT_FDCWD, "bar") == -1); ++ ASSERT (errno == EBADF); ++ } ++ { ++ close (99); ++ errno = 0; ++ ASSERT (renameat (99, "foo", AT_FDCWD, "bar") == -1); ++ ASSERT (errno == EBADF); ++ } ++ ASSERT (close (creat (BASE "oo", 0600)) == 0); ++ { ++ errno = 0; ++ ASSERT (renameat (AT_FDCWD, BASE "oo", -1, "bar") == -1); ++ ASSERT (errno == EBADF); ++ } ++ { ++ errno = 0; ++ ASSERT (renameat (AT_FDCWD, BASE "oo", 99, "bar") == -1); ++ ASSERT (errno == EBADF); ++ } ++ ASSERT (unlink (BASE "oo") == 0); ++ ++ /* Test basic rename functionality, using current directory. */ ++ result = test_rename (do_rename, false); ++ dfd1 = open (".", O_RDONLY); ++ ASSERT (0 <= dfd1); ++ ASSERT (test_rename (do_rename, false) == result); ++ dfd2 = dfd1; ++ ASSERT (test_rename (do_rename, false) == result); ++ dfd1 = AT_FDCWD; ++ ASSERT (test_rename (do_rename, false) == result); ++ ASSERT (close (dfd2) == 0); ++ ++ /* Create locations to manipulate. */ ++ ASSERT (mkdir (BASE "sub1", 0700) == 0); ++ ASSERT (mkdir (BASE "sub2", 0700) == 0); ++ dfd = creat (BASE "00", 0600); ++ ASSERT (0 <= dfd); ++ ASSERT (close (dfd) == 0); ++ cwd = getcwd (NULL, 0); ++ ASSERT (cwd); ++ ++ dfd = open (BASE "sub1", O_RDONLY); ++ ASSERT (0 <= dfd); ++ ASSERT (chdir (BASE "sub2") == 0); ++ ++ /* There are 16 possible scenarios, based on whether an fd is ++ AT_FDCWD or real, and whether a file is absolute or relative. ++ ++ To ensure that we test all of the code paths (rather than ++ triggering early normalization optimizations), we use a loop to ++ repeatedly rename a file in the parent directory, use an fd open ++ on subdirectory 1, all while executing in subdirectory 2; all ++ relative names are thus given with a leading "../". Finally, the ++ last scenario (two relative paths given, neither one AT_FDCWD) ++ has two paths, based on whether the two fds are equivalent, so we ++ do the other variant after the loop. */ ++ for (i = 0; i < 16; i++) ++ { ++ int fd1 = (i & 8) ? dfd : AT_FDCWD; ++ char *file1 = file_name_concat ((i & 4) ? ".." : cwd, BASE "xx", NULL); ++ int fd2 = (i & 2) ? dfd : AT_FDCWD; ++ char *file2 = file_name_concat ((i & 1) ? ".." : cwd, BASE "xx", NULL); ++ ++ ASSERT (sprintf (strchr (file1, '\0') - 2, "%02d", i) == 2); ++ ASSERT (sprintf (strchr (file2, '\0') - 2, "%02d", i + 1) == 2); ++ ASSERT (renameat (fd1, file1, fd2, file2) == 0); ++ free (file1); ++ free (file2); ++ } ++ dfd2 = open ("..", O_RDONLY); ++ ASSERT (0 <= dfd2); ++ ASSERT (renameat (dfd, "../" BASE "16", dfd2, BASE "17") == 0); ++ ASSERT (close (dfd2) == 0); ++ ++ /* Now we change back to the parent directory, and set dfd to "."; ++ using dfd in remaining tests will expose any bugs if emulation ++ via /proc/self/fd doesn't check for empty names. */ ++ ASSERT (chdir ("..") == 0); ++ ASSERT (close (dfd) == 0); ++ dfd = open (".", O_RDONLY); ++ ASSERT (0 <= dfd); ++ ++ ASSERT (close (creat (BASE "sub2/file", 0600)) == 0); ++ errno = 0; ++ ASSERT (renameat (dfd, BASE "sub1", dfd, BASE "sub2") == -1); ++ ASSERT (errno == EEXIST || errno == ENOTEMPTY); ++ ASSERT (unlink (BASE "sub2/file") == 0); ++ errno = 0; ++ ASSERT (renameat (dfd, BASE "sub2", dfd, BASE "sub1/.") == -1); ++ ASSERT (errno == EINVAL || errno == EISDIR || errno == EBUSY ++ || errno == ENOTEMPTY || errno == EEXIST); ++ errno = 0; ++ ASSERT (renameat (dfd, BASE "sub2/.", dfd, BASE "sub1") == -1); ++ ASSERT (errno == EINVAL || errno == EBUSY || errno == EEXIST); ++ errno = 0; ++ ASSERT (renameat (dfd, BASE "17", dfd, BASE "sub1") == -1); ++ ASSERT (errno == EISDIR); ++ errno = 0; ++ ASSERT (renameat (dfd, BASE "nosuch", dfd, BASE "18") == -1); ++ ASSERT (errno == ENOENT); ++ errno = 0; ++ ASSERT (renameat (dfd, "", dfd, BASE "17") == -1); ++ ASSERT (errno == ENOENT); ++ errno = 0; ++ ASSERT (renameat (dfd, BASE "17", dfd, "") == -1); ++ ASSERT (errno == ENOENT); ++ errno = 0; ++ ASSERT (renameat (dfd, BASE "sub2", dfd, BASE "17") == -1); ++ ASSERT (errno == ENOTDIR); ++ errno = 0; ++ ASSERT (renameat (dfd, BASE "17/", dfd, BASE "18") == -1); ++ ASSERT (errno == ENOTDIR); ++ errno = 0; ++ ASSERT (renameat (dfd, BASE "17", dfd, BASE "18/") == -1); ++ ASSERT (errno == ENOTDIR || errno == ENOENT); ++ ++ /* Finally, make sure we can overwrite existing files. */ ++ ASSERT (close (creat (BASE "sub2/file", 0600)) == 0); ++ errno = 0; ++ ASSERT (renameat (dfd, BASE "sub2", dfd, BASE "sub1") == 0); ++ ASSERT (renameat (dfd, BASE "sub1/file", dfd, BASE "17") == 0); ++ ++ /* Cleanup. */ ++ ASSERT (close (dfd) == 0); ++ errno = 0; ++ ASSERT (unlink (BASE "sub1/file") == -1); ++ ASSERT (errno == ENOENT); ++ ASSERT (unlink (BASE "17") == 0); ++ ASSERT (rmdir (BASE "sub1") == 0); ++ errno = 0; ++ ASSERT (rmdir (BASE "sub2") == -1); ++ ASSERT (errno == ENOENT); ++ free (cwd); ++ ++ if (result) ++ fputs ("skipping test: symlinks not supported on this file system\n", ++ stderr); ++ return result; ++} +diff --git a/gnulib-tests/test-renameat2.c b/gnulib-tests/test-renameat2.c +new file mode 100644 +index 0000000..7c250ea +--- /dev/null ++++ b/gnulib-tests/test-renameat2.c +@@ -0,0 +1,209 @@ ++/* Test renameat2. ++ Copyright (C) 2009-2017 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 3 of the License, 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, see . */ ++ ++/* Written by Eric Blake , 2009. */ ++ ++#include ++ ++#include ++ ++#include ++ ++#include "signature.h" ++SIGNATURE_CHECK (renameat2, int, ++ (int, char const *, int, char const *, unsigned int)); ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "filenamecat.h" ++#include "ignore-value.h" ++#include "macros.h" ++ ++#define BASE "test-renameat2.t" ++ ++#include "test-rename.h" ++ ++static int dfd1 = AT_FDCWD; ++static int dfd2 = AT_FDCWD; ++ ++/* Wrapper to test renameat2 like rename. */ ++static int ++do_rename (char const *name1, char const *name2) ++{ ++ return renameat2 (dfd1, name1, dfd2, name2, 0); ++} ++ ++int ++main (void) ++{ ++ int i; ++ int dfd; ++ char *cwd; ++ int result; ++ ++ /* Clean up any trash from prior testsuite runs. */ ++ ignore_value (system ("rm -rf " BASE "*")); ++ ++ /* Test behaviour for invalid file descriptors. */ ++ { ++ errno = 0; ++ ASSERT (renameat2 (-1, "foo", AT_FDCWD, "bar", 0) == -1); ++ ASSERT (errno == EBADF); ++ } ++ { ++ close (99); ++ errno = 0; ++ ASSERT (renameat2 (99, "foo", AT_FDCWD, "bar", 0) == -1); ++ ASSERT (errno == EBADF); ++ } ++ ASSERT (close (creat (BASE "oo", 0600)) == 0); ++ { ++ errno = 0; ++ ASSERT (renameat2 (AT_FDCWD, BASE "oo", -1, "bar", 0) == -1); ++ ASSERT (errno == EBADF); ++ } ++ { ++ errno = 0; ++ ASSERT (renameat2 (AT_FDCWD, BASE "oo", 99, "bar", 0) == -1); ++ ASSERT (errno == EBADF); ++ } ++ ASSERT (unlink (BASE "oo") == 0); ++ ++ /* Test basic rename functionality, using current directory. */ ++ result = test_rename (do_rename, false); ++ dfd1 = open (".", O_RDONLY); ++ ASSERT (0 <= dfd1); ++ ASSERT (test_rename (do_rename, false) == result); ++ dfd2 = dfd1; ++ ASSERT (test_rename (do_rename, false) == result); ++ dfd1 = AT_FDCWD; ++ ASSERT (test_rename (do_rename, false) == result); ++ ASSERT (close (dfd2) == 0); ++ ++ /* Create locations to manipulate. */ ++ ASSERT (mkdir (BASE "sub1", 0700) == 0); ++ ASSERT (mkdir (BASE "sub2", 0700) == 0); ++ dfd = creat (BASE "00", 0600); ++ ASSERT (0 <= dfd); ++ ASSERT (close (dfd) == 0); ++ cwd = getcwd (NULL, 0); ++ ASSERT (cwd); ++ ++ dfd = open (BASE "sub1", O_RDONLY); ++ ASSERT (0 <= dfd); ++ ASSERT (chdir (BASE "sub2") == 0); ++ ++ /* There are 16 possible scenarios, based on whether an fd is ++ AT_FDCWD or real, and whether a file is absolute or relative. ++ ++ To ensure that we test all of the code paths (rather than ++ triggering early normalization optimizations), we use a loop to ++ repeatedly rename a file in the parent directory, use an fd open ++ on subdirectory 1, all while executing in subdirectory 2; all ++ relative names are thus given with a leading "../". Finally, the ++ last scenario (two relative paths given, neither one AT_FDCWD) ++ has two paths, based on whether the two fds are equivalent, so we ++ do the other variant after the loop. */ ++ for (i = 0; i < 16; i++) ++ { ++ int fd1 = (i & 8) ? dfd : AT_FDCWD; ++ char *file1 = file_name_concat ((i & 4) ? ".." : cwd, BASE "xx", NULL); ++ int fd2 = (i & 2) ? dfd : AT_FDCWD; ++ char *file2 = file_name_concat ((i & 1) ? ".." : cwd, BASE "xx", NULL); ++ ++ ASSERT (sprintf (strchr (file1, '\0') - 2, "%02d", i) == 2); ++ ASSERT (sprintf (strchr (file2, '\0') - 2, "%02d", i + 1) == 2); ++ ASSERT (renameat2 (fd1, file1, fd2, file2, 0) == 0); ++ free (file1); ++ free (file2); ++ } ++ dfd2 = open ("..", O_RDONLY); ++ ASSERT (0 <= dfd2); ++ ASSERT (renameat2 (dfd, "../" BASE "16", dfd2, BASE "17", 0) == 0); ++ ASSERT (close (dfd2) == 0); ++ ++ /* Now we change back to the parent directory, and set dfd to "."; ++ using dfd in remaining tests will expose any bugs if emulation ++ via /proc/self/fd doesn't check for empty names. */ ++ ASSERT (chdir ("..") == 0); ++ ASSERT (close (dfd) == 0); ++ dfd = open (".", O_RDONLY); ++ ASSERT (0 <= dfd); ++ ++ ASSERT (close (creat (BASE "sub2/file", 0600)) == 0); ++ errno = 0; ++ ASSERT (renameat2 (dfd, BASE "sub1", dfd, BASE "sub2", 0) == -1); ++ ASSERT (errno == EEXIST || errno == ENOTEMPTY); ++ ASSERT (unlink (BASE "sub2/file") == 0); ++ errno = 0; ++ ASSERT (renameat2 (dfd, BASE "sub2", dfd, BASE "sub1/.", 0) == -1); ++ ASSERT (errno == EINVAL || errno == EISDIR || errno == EBUSY ++ || errno == ENOTEMPTY || errno == EEXIST); ++ errno = 0; ++ ASSERT (renameat2 (dfd, BASE "sub2/.", dfd, BASE "sub1", 0) == -1); ++ ASSERT (errno == EINVAL || errno == EBUSY || errno == EEXIST); ++ errno = 0; ++ ASSERT (renameat2 (dfd, BASE "17", dfd, BASE "sub1", 0) == -1); ++ ASSERT (errno == EISDIR); ++ errno = 0; ++ ASSERT (renameat2 (dfd, BASE "nosuch", dfd, BASE "18", 0) == -1); ++ ASSERT (errno == ENOENT); ++ errno = 0; ++ ASSERT (renameat2 (dfd, "", dfd, BASE "17", 0) == -1); ++ ASSERT (errno == ENOENT); ++ errno = 0; ++ ASSERT (renameat2 (dfd, BASE "17", dfd, "", 0) == -1); ++ ASSERT (errno == ENOENT); ++ errno = 0; ++ ASSERT (renameat2 (dfd, BASE "sub2", dfd, BASE "17", 0) == -1); ++ ASSERT (errno == ENOTDIR); ++ errno = 0; ++ ASSERT (renameat2 (dfd, BASE "17/", dfd, BASE "18", 0) == -1); ++ ASSERT (errno == ENOTDIR); ++ errno = 0; ++ ASSERT (renameat2 (dfd, BASE "17", dfd, BASE "18/", 0) == -1); ++ ASSERT (errno == ENOTDIR || errno == ENOENT); ++ ++ /* Finally, make sure we cannot overwrite existing files. */ ++ ASSERT (close (creat (BASE "sub2/file", 0600)) == 0); ++ errno = 0; ++ ASSERT ((renameat2 (dfd, BASE "sub2", dfd, BASE "sub1", RENAME_NOREPLACE) ++ == -1) ++ && errno == EEXIST); ++ ASSERT ((renameat2 (dfd, BASE "sub2/file", dfd, BASE "17", RENAME_NOREPLACE) ++ == -1) ++ && errno == EEXIST); ++ ++ /* Cleanup. */ ++ ASSERT (close (dfd) == 0); ++ ASSERT (unlink (BASE "sub2/file") == 0); ++ ASSERT (unlink (BASE "17") == 0); ++ ASSERT (rmdir (BASE "sub1") == 0); ++ ASSERT (rmdir (BASE "sub2") == 0); ++ free (cwd); ++ ++ if (result) ++ fputs ("skipping test: symlinks not supported on this file system\n", ++ stderr); ++ return result; ++} +diff --git a/lib/gnulib.mk b/lib/gnulib.mk +index 844791b..76729b0 100644 +--- a/lib/gnulib.mk ++++ b/lib/gnulib.mk +@@ -21,7 +21,7 @@ + # the same distribution terms as the rest of that program. + # + # Generated by gnulib-tool. +-# Reproduce by: gnulib-tool --import --local-dir=gl --lib=libcoreutils --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=gnulib-tests --aux-dir=build-aux --with-tests --avoid=canonicalize-lgpl --avoid=dummy --makefile-name=gnulib.mk --no-conditional-dependencies --no-libtool --macro-prefix=gl acl alignof alloca announce-gen areadlink-with-size argmatch argv-iter assert autobuild backupfile base32 base64 buffer-lcm c-strcase c-strtod c-strtold calloc-gnu canon-host canonicalize chown cloexec closein closeout config-h configmake crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 cycle-check d-ino d-type di-set diacrit dirfd dirname do-release-commit-and-tag dtoastr dup2 environ error euidaccess exclude exitfail faccessat fadvise fchdir fchmodat fchownat fclose fcntl fcntl-safer fd-reopen fdatasync fdl fdopen fdutimensat file-has-acl file-type fileblocks filemode filenamecat filevercmp flexmember fnmatch-gnu fopen-safer fprintftime freopen freopen-safer fseeko fstatat fsusage fsync ftello ftoastr ftruncate fts full-read full-write getgroups gethrxtime getline getloadavg getlogin getndelim2 getopt-gnu getpagesize getpass-gnu gettext-h gettime gettimeofday getugroups getusershell git-version-gen gitlog-to-changelog gnu-make gnu-web-doc-update gnumakefile gnupload group-member hard-locale hash hash-pjw heap host-os human idcache ignore-value inttostr inttypes isapipe isatty isblank largefile lchmod lchown ldtoastr lib-ignore linebuffer link link-follow linkat long-options lstat maintainer-makefile malloc-gnu manywarnings mbrlen mbrtowc mbsalign mbswidth memcasecmp memchr memcmp2 mempcpy memrchr mgetgroups mkancesdirs mkdir mkdir-p mkfifo mknod mkstemp mktime modechange mountlist mpsort netinet_in non-recursive-gnulib-prefix-hack nproc obstack parse-datetime pathmax perl physmem pipe-posix posix-shell posixtm posixver priv-set progname propername pthread putenv quote quotearg randint randperm read-file readlink readtokens readtokens0 readutmp realloc-gnu regex remove rename rmdir root-dev-ino rpmatch safe-read same save-cwd savedir savewd selinux-at setenv settime sig2str sigaction smack ssize_t stat-macros stat-size stat-time statat stdbool stdlib-safer stpcpy stpncpy strdup-posix strftime strncat strnumcmp strpbrk strsignal strtod strtoimax strtoumax symlinkat sys_ioctl sys_resource sys_stat sys_wait tempname termios time_rz timer-time timespec tzset uname unicodeio unistd-safer unlink-busy unlinkat unlocked-io unsetenv update-copyright uptime useless-if-before-free userspec utimecmp utimens vasprintf-posix vc-list-files verify verror version-etc-fsf wcswidth wcwidth winsz-ioctl winsz-termios write-any-file xalloc xbinary-io xdectoint xfts xgetcwd xgetgroups xgethostname xmemcoll xnanosleep xprintf xprintf-posix xreadlink xstrtod xstrtoimax xstrtol xstrtold xstrtoumax yesno ++# Reproduce by: gnulib-tool --import --local-dir=gl --lib=libcoreutils --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=gnulib-tests --aux-dir=build-aux --with-tests --avoid=canonicalize-lgpl --avoid=dummy --makefile-name=gnulib.mk --no-conditional-dependencies --no-libtool --macro-prefix=gl acl alignof alloca announce-gen areadlink-with-size argmatch argv-iter assert autobuild backupfile base32 base64 buffer-lcm c-strcase c-strtod c-strtold calloc-gnu canon-host canonicalize chown cloexec closein closeout config-h configmake crypto/md5 crypto/sha1 crypto/sha256 crypto/sha512 cycle-check d-ino d-type di-set diacrit dirfd dirname do-release-commit-and-tag dtoastr dup2 environ error euidaccess exclude exitfail faccessat fadvise fchdir fchmodat fchownat fclose fcntl fcntl-safer fd-reopen fdatasync fdl fdopen fdutimensat file-has-acl file-type fileblocks filemode filenamecat filevercmp flexmember fnmatch-gnu fopen-safer fprintftime freopen freopen-safer fseeko fstatat fsusage fsync ftello ftoastr ftruncate fts full-read full-write getgroups gethrxtime getline getloadavg getlogin getndelim2 getopt-gnu getpagesize getpass-gnu gettext-h gettime gettimeofday getugroups getusershell git-version-gen gitlog-to-changelog gnu-make gnu-web-doc-update gnumakefile gnupload group-member hard-locale hash hash-pjw heap host-os human idcache ignore-value inttostr inttypes isapipe isatty isblank largefile lchmod lchown ldtoastr lib-ignore linebuffer link link-follow linkat long-options lstat maintainer-makefile malloc-gnu manywarnings mbrlen mbrtowc mbsalign mbswidth memcasecmp memchr memcmp2 mempcpy memrchr mgetgroups mkancesdirs mkdir mkdir-p mkfifo mknod mkstemp mktime modechange mountlist mpsort netinet_in non-recursive-gnulib-prefix-hack nproc obstack parse-datetime pathmax perl physmem pipe-posix posix-shell posixtm posixver priv-set progname propername pthread putenv quote quotearg randint randperm read-file readlink readtokens readtokens0 readutmp realloc-gnu regex remove rename renameat renameat2 rmdir root-dev-ino rpmatch safe-read same save-cwd savedir savewd selinux-at setenv settime sig2str sigaction smack ssize_t stat-macros stat-size stat-time statat stdbool stdlib-safer stpcpy stpncpy strdup-posix strftime strncat strnumcmp strpbrk strsignal strtod strtoimax strtoumax symlinkat sys_ioctl sys_resource sys_stat sys_wait tempname termios time_rz timer-time timespec tzset uname unicodeio unistd-safer unlink-busy unlinkat unlocked-io unsetenv update-copyright uptime useless-if-before-free userspec utimecmp utimens vasprintf-posix vc-list-files verify verror version-etc-fsf wcswidth wcwidth winsz-ioctl winsz-termios write-any-file xalloc xbinary-io xdectoint xfts xgetcwd xgetgroups xgethostname xmemcoll xnanosleep xprintf xprintf-posix xreadlink xstrtod xstrtoimax xstrtol xstrtold xstrtoumax yesno + + + MOSTLYCLEANFILES += lib/core lib/*.stackdump +@@ -3177,6 +3177,25 @@ EXTRA_lib_libcoreutils_a_SOURCES += lib/rename.c + + ## end gnulib module rename + ++## begin gnulib module renameat ++ ++ ++EXTRA_DIST += lib/renameat.c ++ ++EXTRA_lib_libcoreutils_a_SOURCES += lib/renameat.c ++ ++## end gnulib module renameat ++ ++## begin gnulib module renameat2 ++ ++lib_libcoreutils_a_SOURCES += lib/renameat2.c ++ ++EXTRA_DIST += lib/at-func2.c lib/renameat2.h ++ ++EXTRA_lib_libcoreutils_a_SOURCES += lib/at-func2.c ++ ++## end gnulib module renameat2 ++ + ## begin gnulib module rewinddir + + +diff --git a/lib/renameat.c b/lib/renameat.c +new file mode 100644 +index 0000000..48cee4b +--- /dev/null ++++ b/lib/renameat.c +@@ -0,0 +1,25 @@ ++/* Rename a file relative to open directories. ++ Copyright 2017 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 3 of the License, 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, see . */ ++ ++#include ++#include ++#include "renameat2.h" ++ ++int ++renameat (int fd1, char const *src, int fd2, char const *dst) ++{ ++ return renameat2 (fd1, src, fd2, dst, 0); ++} +diff --git a/lib/renameat2.c b/lib/renameat2.c +new file mode 100644 +index 0000000..26cde86 +--- /dev/null ++++ b/lib/renameat2.c +@@ -0,0 +1,227 @@ ++/* Rename a file relative to open directories. ++ Copyright (C) 2009-2017 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 3 of the License, 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, see . */ ++ ++/* written by Eric Blake and Paul Eggert */ ++ ++#include ++ ++#include "renameat2.h" ++ ++#include ++#include ++#include ++#include ++ ++#ifdef __linux__ ++# include ++#endif ++ ++static int ++errno_fail (int e) ++{ ++ errno = e; ++ return -1; ++} ++ ++#if HAVE_RENAMEAT ++ ++# include ++# include ++# include ++ ++# include "dirname.h" ++# include "openat.h" ++ ++#else ++# include "openat-priv.h" ++ ++static int ++rename_noreplace (char const *src, char const *dst) ++{ ++ /* This has a race between the call to lstat and the call to rename. */ ++ struct stat st; ++ return (lstat (dst, &st) == 0 || errno == EOVERFLOW ? errno_fail (EEXIST) ++ : errno == ENOENT ? rename (src, dst) ++ : -1); ++} ++#endif ++ ++#undef renameat ++ ++/* Rename FILE1, in the directory open on descriptor FD1, to FILE2, in ++ the directory open on descriptor FD2. If possible, do it without ++ changing the working directory. Otherwise, resort to using ++ save_cwd/fchdir, then rename/restore_cwd. If either the save_cwd or ++ the restore_cwd fails, then give a diagnostic and exit nonzero. ++ ++ Obey FLAGS when doing the renaming. If FLAGS is zero, this ++ function is equivalent to renameat (FD1, SRC, FD2, DST). */ ++ ++int ++renameat2 (int fd1, char const *src, int fd2, char const *dst, ++ unsigned int flags) ++{ ++ int ret_val = -1; ++ int err = EINVAL; ++ ++#ifdef SYS_renameat2 ++ ret_val = syscall (SYS_renameat2, fd1, src, fd2, dst, flags); ++ err = errno; ++#elif defined RENAME_EXCL ++ if (! (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE))) ++ { ++ ret_val = renameatx_np (fd1, src, fd2, dst, ++ ((flags & RENAME_EXCHANGE ? RENAME_SWAP : 0) ++ | (flags & RENAME_NOREPLACE ? RENAME_EXCL : 0))); ++ err = errno; ++ } ++#endif ++ ++ if (! (ret_val < 0 && (err == EINVAL || err == ENOSYS || err == ENOTSUP))) ++ return ret_val; ++ ++#if HAVE_RENAMEAT ++ { ++ size_t src_len; ++ size_t dst_len; ++ char *src_temp = (char *) src; ++ char *dst_temp = (char *) dst; ++ bool src_slash; ++ bool dst_slash; ++ int rename_errno = ENOTDIR; ++ struct stat src_st; ++ struct stat dst_st; ++ bool dst_found_nonexistent = false; ++ ++ if (flags != 0) ++ { ++ /* RENAME_NOREPLACE is the only flag currently supported. */ ++ if (flags & ~RENAME_NOREPLACE) ++ return errno_fail (ENOTSUP); ++ else ++ { ++ /* This has a race between the call to lstatat and the calls to ++ renameat below. */ ++ if (lstatat (fd2, dst, &dst_st) == 0 || errno == EOVERFLOW) ++ return errno_fail (EEXIST); ++ if (errno != ENOENT) ++ return -1; ++ dst_found_nonexistent = true; ++ } ++ } ++ ++ /* Let strace see any ENOENT failure. */ ++ src_len = strlen (src); ++ dst_len = strlen (dst); ++ if (!src_len || !dst_len) ++ return renameat (fd1, src, fd2, dst); ++ ++ src_slash = src[src_len - 1] == '/'; ++ dst_slash = dst[dst_len - 1] == '/'; ++ if (!src_slash && !dst_slash) ++ return renameat (fd1, src, fd2, dst); ++ ++ /* Presence of a trailing slash requires directory semantics. If ++ the source does not exist, or if the destination cannot be turned ++ into a directory, give up now. Otherwise, strip trailing slashes ++ before calling rename. */ ++ if (lstatat (fd1, src, &src_st)) ++ return -1; ++ if (dst_found_nonexistent) ++ { ++ if (!S_ISDIR (src_st.st_mode)) ++ return errno_fail (ENOENT); ++ } ++ else if (lstatat (fd2, dst, &dst_st)) ++ { ++ if (errno != ENOENT || !S_ISDIR (src_st.st_mode)) ++ return -1; ++ } ++ else if (!S_ISDIR (dst_st.st_mode)) ++ return errno_fail (ENOTDIR); ++ else if (!S_ISDIR (src_st.st_mode)) ++ return errno_fail (EISDIR); ++ ++# if RENAME_TRAILING_SLASH_SOURCE_BUG ++ /* See the lengthy comment in rename.c why Solaris 9 is forced to ++ GNU behavior, while Solaris 10 is left with POSIX behavior, ++ regarding symlinks with trailing slash. */ ++ ret_val = -1; ++ if (src_slash) ++ { ++ src_temp = strdup (src); ++ if (!src_temp) ++ { ++ /* Rather than rely on strdup-posix, we set errno ourselves. */ ++ rename_errno = ENOMEM; ++ goto out; ++ } ++ strip_trailing_slashes (src_temp); ++ if (lstatat (fd1, src_temp, &src_st)) ++ { ++ rename_errno = errno; ++ goto out; ++ } ++ if (S_ISLNK (src_st.st_mode)) ++ goto out; ++ } ++ if (dst_slash) ++ { ++ dst_temp = strdup (dst); ++ if (!dst_temp) ++ { ++ rename_errno = ENOMEM; ++ goto out; ++ } ++ strip_trailing_slashes (dst_temp); ++ if (lstatat (fd2, dst_temp, &dst_st)) ++ { ++ if (errno != ENOENT) ++ { ++ rename_errno = errno; ++ goto out; ++ } ++ } ++ else if (S_ISLNK (dst_st.st_mode)) ++ goto out; ++ } ++# endif /* RENAME_TRAILING_SLASH_SOURCE_BUG */ ++ ++ /* renameat does not honor trailing / on Solaris 10. Solve it in a ++ similar manner to rename. No need to worry about bugs not present ++ on Solaris, since all other systems either lack renameat or honor ++ trailing slash correctly. */ ++ ++ ret_val = renameat (fd1, src_temp, fd2, dst_temp); ++ rename_errno = errno; ++ goto out; ++ out: ++ if (src_temp != src) ++ free (src_temp); ++ if (dst_temp != dst) ++ free (dst_temp); ++ errno = rename_errno; ++ return ret_val; ++ } ++#else /* !HAVE_RENAMEAT */ ++ ++ /* RENAME_NOREPLACE is the only flag currently supported. */ ++ if (flags & ~RENAME_NOREPLACE) ++ return errno_fail (ENOTSUP); ++ return at_func2 (fd1, src, fd2, dst, flags ? rename_noreplace : rename); ++ ++#endif /* !HAVE_RENAMEAT */ ++} +diff --git a/lib/renameat2.h b/lib/renameat2.h +new file mode 100644 +index 0000000..179210f +--- /dev/null ++++ b/lib/renameat2.h +@@ -0,0 +1,30 @@ ++/* Rename a file relative to open directories. ++ Copyright 2017 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 3 of the License, 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, see . */ ++ ++/* written by Paul Eggert */ ++ ++/* Get RENAME_* macros from linux/fs.h if present, otherwise supply ++ the traditional Linux values. */ ++#if HAVE_LINUX_FS_H ++# include ++#endif ++#ifndef RENAME_NOREPLACE ++# define RENAME_NOREPLACE (1 << 0) ++# define RENAME_EXCHANGE (1 << 1) ++# define RENAME_WHITEOUT (1 << 2) ++#endif ++ ++extern int renameat2 (int, char const *, int, char const *, unsigned int); +diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 +index 4ef3c43..309e308 100644 +--- a/m4/gnulib-comp.m4 ++++ b/m4/gnulib-comp.m4 +@@ -544,6 +544,10 @@ AC_DEFUN([gl_EARLY], + # Code from module remove-tests: + # Code from module rename: + # Code from module rename-tests: ++ # Code from module renameat: ++ # Code from module renameat-tests: ++ # Code from module renameat2: ++ # Code from module renameat2-tests: + # Code from module rewinddir: + # Code from module rmdir: + # Code from module rmdir-tests: +@@ -1649,6 +1653,18 @@ AC_DEFUN([gl_INIT], + AC_LIBOBJ([rename]) + fi + gl_STDIO_MODULE_INDICATOR([rename]) ++ gl_FUNC_RENAMEAT ++ if test $HAVE_RENAMEAT = 0 || test $REPLACE_RENAMEAT = 1; then ++ AC_LIBOBJ([renameat]) ++ fi ++ if test $HAVE_RENAMEAT = 0; then ++ AC_LIBOBJ([at-func2]) ++ fi ++ gl_STDIO_MODULE_INDICATOR([renameat]) ++ gl_FUNC_RENAMEAT ++ if test $HAVE_RENAMEAT = 0; then ++ AC_LIBOBJ([at-func2]) ++ fi + gl_FUNC_REWINDDIR + if test $HAVE_REWINDDIR = 0; then + AC_LIBOBJ([rewinddir]) +@@ -2811,6 +2827,9 @@ AC_DEFUN([gl_FILE_LIST], [ + lib/regexec.c + lib/remove.c + lib/rename.c ++ lib/renameat.c ++ lib/renameat2.c ++ lib/renameat2.h + lib/rewinddir.c + lib/rmdir.c + lib/root-dev-ino.c +@@ -3296,6 +3315,7 @@ AC_DEFUN([gl_FILE_LIST], [ + m4/regex.m4 + m4/remove.m4 + m4/rename.m4 ++ m4/renameat.m4 + m4/rewinddir.m4 + m4/rmdir.m4 + m4/rpmatch.m4 +@@ -3711,6 +3731,8 @@ AC_DEFUN([gl_FILE_LIST], [ + tests/test-remove.c + tests/test-rename.c + tests/test-rename.h ++ tests/test-renameat.c ++ tests/test-renameat2.c + tests/test-rmdir.c + tests/test-rmdir.h + tests/test-rwlock1.c +diff --git a/m4/renameat.m4 b/m4/renameat.m4 +new file mode 100644 +index 0000000..1b97774 +--- /dev/null ++++ b/m4/renameat.m4 +@@ -0,0 +1,25 @@ ++# serial 3 ++# See if we need to provide renameat replacement. ++ ++dnl Copyright (C) 2009-2017 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. ++ ++# Written by Eric Blake. ++ ++AC_DEFUN([gl_FUNC_RENAMEAT], ++[ ++ AC_REQUIRE([gl_FUNC_OPENAT]) ++ AC_REQUIRE([gl_FUNC_RENAME]) ++ AC_REQUIRE([gl_STDIO_H_DEFAULTS]) ++ AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) ++ AC_CHECK_HEADERS([linux/fs.h]) ++ AC_CHECK_FUNCS_ONCE([renameat]) ++ if test $ac_cv_func_renameat = no; then ++ HAVE_RENAMEAT=0 ++ elif test $REPLACE_RENAME = 1; then ++ dnl Solaris 9 and 10 have the same bugs in renameat as in rename. ++ REPLACE_RENAMEAT=1 ++ fi ++]) +diff --git a/src/copy.c b/src/copy.c +index 2a804945e..be4e357a8 100644 +--- a/src/copy.c ++++ b/src/copy.c +@@ -53,6 +53,7 @@ + #include "ignore-value.h" + #include "ioblksize.h" + #include "quote.h" ++#include "renameat2.h" + #include "root-uid.h" + #include "same.h" + #include "savedir.h" +@@ -2192,8 +2193,9 @@ copy_internal (char const *src_name, char const *dst_name, + + /* If the source is a directory, we don't always create the destination + directory. So --verbose should not announce anything until we're +- sure we'll create a directory. */ +- if (x->verbose && !S_ISDIR (src_mode)) ++ sure we'll create a directory. In move mode we delay the diagnostic ++ message until we know whether renameat2() has actually succeeded. */ ++ if (x->verbose && !S_ISDIR (src_mode) && !x->move_mode) + emit_verbose (src_name, dst_name, backup_succeeded ? dst_backup : NULL); + + /* Associate the destination file name with the source device and inode +@@ -2312,9 +2314,14 @@ copy_internal (char const *src_name, char const *dst_name, + + if (x->move_mode) + { +- if (rename (src_name, dst_name) == 0) ++ int flags = 0; ++ if (x->interactive == I_ALWAYS_NO) ++ /* do not replace DST_NAME if it was created since our last check */ ++ flags = RENAME_NOREPLACE; ++ ++ if (renameat2 (AT_FDCWD, src_name, AT_FDCWD, dst_name, flags) == 0) + { +- if (x->verbose && S_ISDIR (src_mode)) ++ if (x->verbose) + emit_verbose (src_name, dst_name, + backup_succeeded ? dst_backup : NULL); + +@@ -2342,6 +2349,15 @@ copy_internal (char const *src_name, char const *dst_name, + return true; + } + ++ if ((flags & RENAME_NOREPLACE) && (errno == EEXIST)) ++ { ++ /* Pretend the rename succeeded, so the caller (mv) ++ doesn't end up removing the source file. */ ++ if (rename_succeeded) ++ *rename_succeeded = true; ++ return true; ++ } ++ + /* FIXME: someday, consider what to do when moving a directory into + itself but when source and destination are on different devices. */ + +@@ -2417,6 +2433,9 @@ copy_internal (char const *src_name, char const *dst_name, + return false; + } + ++ if (x->verbose && !S_ISDIR (src_mode)) ++ emit_verbose (src_name, dst_name, backup_succeeded ? dst_backup : NULL); ++ + new_dst = true; + } + +-- +2.13.6 + diff --git a/coreutils.spec b/coreutils.spec index 9265112..c49d836 100644 --- a/coreutils.spec +++ b/coreutils.spec @@ -1,7 +1,7 @@ Summary: A set of basic GNU tools commonly used in shell scripts Name: coreutils Version: 8.27 -Release: 16%{?dist} +Release: 21%{?dist} License: GPLv3+ Group: System Environment/Base Url: https://www.gnu.org/software/coreutils/ @@ -28,6 +28,22 @@ Patch4: coreutils-8.27-runcon-doc.patch # ptx: fix a possible crash caused by integer overflow (#1482445) Patch5: coreutils-8.27-ptx-int-overflow.patch +# df: do not call stat() on file system unless it is necessary (#1511951) +Patch6: coreutils-8.27-df-stat.patch + +# mv -n: do not overwrite the destination, superseded by +# http://git.savannah.gnu.org/cgit/coreutils.git/commit/?id=v8.29-9-g29baf25aa +Patch7: coreutils-8.29-mv-n-noreplace.patch + +# doc: warn about following symlinks recursively in chown/chgrp (CVE-2017-18018) +Patch8: coreutils-8.29-CVE-2017-18018.patch + +# import gnulib's FTS module from coreutils-8.29 (#1544392) +Patch9: coreutils-8.27-fts-update.patch + +# fix crash caused by mistakenly enabled leaf optimization (#1558249) +Patch10: coreutils-8.29-fts-leaf-opt.patch + # disable the test-lock gnulib test prone to deadlock Patch100: coreutils-8.26-test-lock.patch @@ -296,6 +312,22 @@ fi %license COPYING %changelog +* Fri Apr 20 2018 Kamil Dudka - 8.27-21 +- fix crash caused by mistakenly enabled leaf optimization (#1558249) + +* Mon Feb 12 2018 Kamil Dudka - 8.27-20 +- import gnulib's FTS module from coreutils-8.29 (#1544392) + +* Thu Jan 25 2018 Kamil Dudka - 8.27-19 +- mv -n: provide more reliable diagnostic messages + +* Tue Jan 23 2018 Kamil Dudka - 8.27-18 +- doc: warn about following symlinks recursively in chown/chgrp (CVE-2017-18018) +- mv -n: do not overwrite the destination + +* Fri Nov 10 2017 Kamil Dudka - 8.27-17 +- df: do not call stat() on file system unless it is necessary (#1511951) + * Tue Aug 22 2017 Ville Skyttä - 8.27-16 - Own the %%{_libexecdir}/coreutils dir