From b1830c7601d264005016bc05928a30102de8b3d7 Mon Sep 17 00:00:00 2001 From: Sebastian Kisela Date: Fri, 28 Apr 2017 15:47:38 +0200 Subject: [PATCH] Subject: [PATCH] tail: revert to polling if a followed directory is replaced (#1283760) --- coreutils-tail-inotify-recreate.patch | 165 ++++++++++++++++++++++++++ coreutils.spec | 8 +- 2 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 coreutils-tail-inotify-recreate.patch diff --git a/coreutils-tail-inotify-recreate.patch b/coreutils-tail-inotify-recreate.patch new file mode 100644 index 0000000..1773a95 --- /dev/null +++ b/coreutils-tail-inotify-recreate.patch @@ -0,0 +1,165 @@ +From ba5fe2d4b8b6a8366f48b1ad1f97fe26c9089b53 Mon Sep 17 00:00:00 2001 +From: Sebastian Kisela +Date: Wed, 5 Apr 2017 09:40:41 +0200 +Subject: [PATCH] tail: revert to polling if a followed directory is replaced + +* src/tail.c (tail_forever_inotify): Add the IN_DELETE_SELF flag when +creating watch for the parent directory. After the parent directory +is removed, an event is caught and then we switch from inotify to +polling mode. Till now, inotify has always frozen because it waited for +an event from a watched dir, which has been already deleted and was not +added again. +* tests/tail-2/inotify-dir-recreate.sh: Add a test case. +* tests/local.mk: Reference the new test. +Fixes http://bugs.gnu.org/26363 +Reported at https://bugzilla.redhat.com/1283760 +--- + src/tail.c | 22 +++++++++- + tests/local.mk | 1 + + tests/tail-2/inotify-dir-recreate.sh | 82 ++++++++++++++++++++++++++++++++++++ + 4 files changed, 107 insertions(+), 1 deletion(-) + create mode 100755 tests/tail-2/inotify-dir-recreate.sh + +diff --git a/src/tail.c b/src/tail.c +index d1552d4..6328fe0 100644 +--- a/src/tail.c ++++ b/src/tail.c +@@ -1457,7 +1457,8 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files, + In that case the same watch descriptor is returned. */ + f[i].parent_wd = inotify_add_watch (wd, dirlen ? f[i].name : ".", + (IN_CREATE | IN_DELETE +- | IN_MOVED_TO | IN_ATTRIB)); ++ | IN_MOVED_TO | IN_ATTRIB ++ | IN_DELETE_SELF)); + + f[i].name[dirlen] = prev; + +@@ -1628,6 +1629,25 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files, + ev = void_ev; + evbuf_off += sizeof (*ev) + ev->len; + ++ /* If a directory is deleted, IN_DELETE_SELF is emitted ++ with ev->name of length 0. ++ We need to catch it, otherwise it would wait forever, ++ as wd for directory becomes inactive. Revert to polling now. */ ++ if ((ev->mask & IN_DELETE_SELF) && ! ev->len) ++ { ++ for (i = 0; i < n_files; i++) ++ { ++ if (ev->wd == f[i].parent_wd) ++ { ++ hash_free (wd_to_name); ++ error (0, 0, ++ _("directory containing watched file was removed")); ++ errno = 0; /* we've already diagnosed enough errno detail. */ ++ return true; ++ } ++ } ++ } ++ + if (ev->len) /* event on ev->name in watched directory. */ + { + size_t j; +diff --git a/tests/local.mk b/tests/local.mk +index 3fe9ba8..e890c9a 100644 +--- a/tests/local.mk ++++ b/tests/local.mk +@@ -176,6 +176,7 @@ all_tests = \ + tests/tail-2/descriptor-vs-rename.sh \ + tests/tail-2/inotify-rotate.sh \ + tests/tail-2/inotify-rotate-resources.sh \ ++ tests/tail-2/inotify-dir-recreate.sh \ + tests/chmod/no-x.sh \ + tests/chgrp/basic.sh \ + tests/rm/dangling-symlink.sh \ +diff --git a/tests/tail-2/inotify-dir-recreate.sh b/tests/tail-2/inotify-dir-recreate.sh +new file mode 100755 +index 0000000..eaa8422 +--- /dev/null ++++ b/tests/tail-2/inotify-dir-recreate.sh +@@ -0,0 +1,82 @@ ++#!/bin/sh ++# Makes sure, inotify will switch to polling mode if directory ++# of the watched file was removed and recreated. ++# (...instead of getting stuck forever) ++ ++# Copyright (C) 2006-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 . ++ ++. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src ++print_ver_ tail ++ ++ ++# Terminate any background tail process ++cleanup_() { kill $pid 2>/dev/null && wait $pid; } ++ ++cleanup_fail_ () ++{ ++ warn_ $1 ++ cleanup_ ++ fail=1 ++} ++ ++# $check_re - string to be found ++# $check_f - file to be searched ++check_tail_output_ () ++{ ++ local delay="$1" ++ grep $check_re $check_f > /dev/null || ++ { sleep $delay ; return 1; } ++} ++ ++grep_timeout_ () ++{ ++ check_re="$1" ++ check_f="$2" ++ retry_delay_ check_tail_output_ .1 5 ++} ++ ++# Prepare the file to be watched ++mkdir dir && echo 'inotify' > dir/file || framework_failure_ ++ ++#tail must print content of the file to stdout, verify ++timeout 60 tail -F dir/file &>out & pid=$! ++grep_timeout_ 'inotify' 'out' || ++{ cleanup_fail_ 'file to be tailed does not exist'; } ++ ++# Remove the directory, should get the message about the deletion ++rm -r dir || framework_failure_ ++grep_timeout_ 'polling' 'out' || ++{ cleanup_fail_ 'tail did not switch to polling mode'; } ++ ++# Recreate the dir, must get a message about recreation ++mkdir dir && touch dir/file || framework_failure_ ++grep_timeout_ 'appeared' 'out' || ++{ cleanup_fail_ 'previously removed file did not appear'; } ++ ++cleanup_ ++ ++# Expected result for the whole process ++cat <<\EOF > exp || framework_failure_ ++inotify ++tail: 'dir/file' has become inaccessible: No such file or directory ++tail: directory containing watched file was removed ++tail: inotify cannot be used, reverting to polling ++tail: 'dir/file' has appeared; following new file ++EOF ++ ++compare exp out || fail=1 ++ ++Exit $fail +-- +2.9.3 + diff --git a/coreutils.spec b/coreutils.spec index c81744b..5254603 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.25 -Release: 8%{?dist} +Release: 9%{?dist} License: GPLv3+ Group: System Environment/Base Url: http://www.gnu.org/software/coreutils/ @@ -30,6 +30,8 @@ Patch954: coreutils-8.25-ls-signal.patch Patch955: coreutils-8.25-sum-ignore-missing.patch # install,mkdir: fix handling of -DZ and -pZ, respectively (#1398913) Patch956: coreutils-8.25-mkdir-p-selinux.patch +# tail: revert to polling if a followed directory is replaced(#1283760) +Patch957: coreutils-tail-inotify-recreate.patch # Our patches #general patch to workaround koji build system issues @@ -215,6 +217,7 @@ including documentation and translations. %patch954 -p1 %patch955 -p1 %patch956 -p1 +%patch957 -p1 chmod a+x \ tests/df/direct.sh \ @@ -382,6 +385,9 @@ fi %license COPYING %changelog +* Fri Apr 28 2017 Sebastian Kisela - 8.25-9 +- tail: revert to polling if a followed directory is replaced(#1283760) + * Thu Mar 02 2017 Kamil Dudka - 8.25-8 - install,mkdir: fix handling of -DZ and -pZ, respectively (#1398913)