mirror of
git://git.sv.gnu.org/coreutils
synced 2026-05-15 19:37:20 -04:00
du: don't abort when a subdir is renamed during traversal
* NEWS (Bug fixes): Mention it. * src/du.c (prev_level): Move declaration "up" to file-scope global. (du_files): Reset prev_level to 0 upon abnormal fts_read termination. Reported by Johathan Nieder in http://bugs.debian.org/609049 Also, improve a diagnostic. * tests/du/move-dir-while-traversing: Test for the above. * tests/Makefile.am (TESTS): Add it.
This commit is contained in:
@@ -2,6 +2,14 @@ GNU coreutils NEWS -*- outline -*-
|
||||
|
||||
* Noteworthy changes in release ?.? (????-??-??) [?]
|
||||
|
||||
** Bug fixes
|
||||
|
||||
du would abort with a failed assertion when two conditions are met:
|
||||
part of the hierarchy being traversed is moved to a higher level in the
|
||||
directory tree, and there is at least one more command line directory
|
||||
argument following the one containing the moved sub-tree.
|
||||
[bug introduced in coreutils-5.1.0]
|
||||
|
||||
|
||||
* Noteworthy changes in release 8.9 (2011-01-04) [stable]
|
||||
|
||||
|
||||
@@ -63,8 +63,11 @@ extern bool fts_debug;
|
||||
/* A set of dev/ino pairs. */
|
||||
static struct di_set *di_set;
|
||||
|
||||
/* Define a class for collecting directory information. */
|
||||
/* Keep track of the preceding "level" (depth in hierarchy)
|
||||
from one call of process_file to the next. */
|
||||
static size_t prev_level;
|
||||
|
||||
/* Define a class for collecting directory information. */
|
||||
struct duinfo
|
||||
{
|
||||
/* Size of files in directory. */
|
||||
@@ -399,7 +402,6 @@ process_file (FTS *fts, FTSENT *ent)
|
||||
struct duinfo dui;
|
||||
struct duinfo dui_to_print;
|
||||
size_t level;
|
||||
static size_t prev_level;
|
||||
static size_t n_alloc;
|
||||
/* First element of the structure contains:
|
||||
The sum of the st_size values of all entries in the single directory
|
||||
@@ -582,10 +584,15 @@ du_files (char **files, int bit_flags)
|
||||
{
|
||||
if (errno != 0)
|
||||
{
|
||||
/* FIXME: try to give a better message */
|
||||
error (0, errno, _("fts_read failed"));
|
||||
error (0, errno, _("fts_read failed: %s"),
|
||||
quotearg_colon (fts->fts_path));
|
||||
ok = false;
|
||||
}
|
||||
|
||||
/* When exiting this loop early, be careful to reset the
|
||||
global, prev_level, used in process_file. Otherwise, its
|
||||
(level == prev_level - 1) assertion could fail. */
|
||||
prev_level = 0;
|
||||
break;
|
||||
}
|
||||
FTS_CROSS_CHECK (fts);
|
||||
|
||||
@@ -370,6 +370,7 @@ TESTS = \
|
||||
du/long-from-unreadable \
|
||||
du/long-sloop \
|
||||
du/max-depth \
|
||||
du/move-dir-while-traversing \
|
||||
du/no-deref \
|
||||
du/no-x \
|
||||
du/one-file-system \
|
||||
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/bin/sh
|
||||
# Trigger a failed assertion in coreutils-8.9 and earlier.
|
||||
|
||||
# Copyright (C) 2011 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
print_ver_ du
|
||||
|
||||
# We use a python-inotify script, so...
|
||||
python -m pyinotify -h > /dev/null \
|
||||
|| skip_ 'python inotify package not installed'
|
||||
|
||||
# Move a directory "up" while du is processing its sub-directories.
|
||||
# While du is processing a hierarchy .../B/C/D/... this script
|
||||
# detects when du opens D/, and then moves C/ "up" one level
|
||||
# so that it is a sibling of B/.
|
||||
# Given the inherent race condition, we have to add enough "weight"
|
||||
# under D/ so that in most cases, the monitor performs the single
|
||||
# rename syscall before du finishes processing the subtree under D/.
|
||||
|
||||
cat <<'EOF' > inotify-watch-for-dir-access.py
|
||||
#!/usr/bin/env python
|
||||
import pyinotify as pn
|
||||
import os,sys
|
||||
|
||||
dir = sys.argv[1]
|
||||
dest_parent = os.path.dirname(os.path.dirname(dir))
|
||||
dest = os.path.join(dest_parent, os.path.basename(dir))
|
||||
|
||||
class ProcessDir(pn.ProcessEvent):
|
||||
|
||||
def process_IN_OPEN(self, event):
|
||||
os.rename(dir, dest)
|
||||
sys.exit(0)
|
||||
|
||||
def process_default(self, event):
|
||||
pass
|
||||
|
||||
wm = pn.WatchManager()
|
||||
notifier = pn.Notifier(wm)
|
||||
wm.watch_transient_file(dir, pn.IN_OPEN, ProcessDir)
|
||||
sys.stdout.write('started\n')
|
||||
sys.stdout.flush()
|
||||
notifier.loop()
|
||||
EOF
|
||||
chmod a+x inotify-watch-for-dir-access.py
|
||||
|
||||
long=d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z
|
||||
t=T/U
|
||||
mkdir -p $t/3/a/b/c/$long d2 || framework_failure
|
||||
timeout 6 ./inotify-watch-for-dir-access.py $t/3/a/b > start-msg &
|
||||
|
||||
# Wait for the watcher to start...
|
||||
nonempty() { test -s start-msg || { sleep $1; return 1; }; }
|
||||
retry_delay_ nonempty .1 5
|
||||
|
||||
# The above watches for an IN_OPEN event on $t/3/a/b,
|
||||
# and when it triggers, moves the parent, $t/3/a, up one level
|
||||
# so it's directly under $t.
|
||||
|
||||
du -a $t d2 2> err
|
||||
# Before coreutils-8.10, du would abort.
|
||||
test $? = 1 || fail=1
|
||||
|
||||
# check for the new diagnostic
|
||||
printf "du: fts_read failed: $t/3/a/b: No such file or directory\n" > exp \
|
||||
|| fail=1
|
||||
compare err exp || fail=1
|
||||
|
||||
Exit $fail
|
||||
Reference in New Issue
Block a user