Subject: [bug] "tail -f" with inotify fails to follow a file after a rename()
From: Stephane Chazelas <[email-masked]>
To: [email-masked]
Date: Tue, 3 Feb 2015 22:04:11 +0000
List-Id: <debbugs-submit.debbugs.gnu.org>

Hello,

On Linux, when inotify is used,

   tail -f file

follows a file only until it's renamed. After it is renamed, the
inotify watch is removed, which means tail sits there doing
nothing and any further modifications to the file are ignored.

To reproduce:

    echo 1 > file
    tail -f file &
    exec 3>> file
    echo 2 >&3
    sleep 1
    mv file file2
    sleep 1
    echo 3 >&3
    sleep 1
    : > file2

"3" is not displayed. No message about the file being truncated
either.

Work arounds:

   tail ---disable-inotify -f file
   tail -f < file # effectively disables inotify

   or rename the file with a link() followed by an unlink()
   ln file newfile && rm -f file

Note that the IN_DELETED_SELF event is not reached in
follow-descriptor mode because tail has the file open preventing
it from being deleted even after it's unlinked from the last
directory.

Path attached (on the current git head).

I don't think IN_DELETED_SELF is useful in follow-name mode
either, but I've not removed it.

-- 
Stephane
From f97355fa31f5e13f95667a50908506388d62df64 Mon Sep 17 00:00:00 2001
From: Stephane Chazelas <[email-masked]>
Date: Tue, 3 Feb 2015 21:22:06 +0000
Subject: [PATCH] tail: fix rename in follow-descriptor mode

In follow-descriptor mode, even when using inotify, we don't care if the
file has been renamed or deleted.  We still want to watch it.
---
 src/tail.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/tail.c b/src/tail.c
index b512c2a..864c71f 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -159,13 +159,6 @@ struct File_spec
   uintmax_t n_unchanged_stats;
 };
 
-#if HAVE_INOTIFY
-/* The events mask used with inotify on files.  This mask is not used on
-   directories.  */
-static const uint32_t inotify_wd_mask = (IN_MODIFY | IN_ATTRIB
-                                         | IN_DELETE_SELF | IN_MOVE_SELF);
-#endif
-
 /* Keep trying to open a file even if it is inaccessible when tail starts
    or if it becomes inaccessible later -- useful only with -f.  */
 static bool reopen_inaccessible_files;
@@ -1385,11 +1378,22 @@ tail_forever_inotify (int wd, struct File_spec *f, size_t n_files,
   char *evbuf;
   size_t evbuf_off = 0;
   size_t len = 0;
+  uint32_t inotify_wd_mask;
 
   wd_to_name = hash_initialize (n_files, NULL, wd_hasher, wd_comparator, NULL);
   if (! wd_to_name)
     xalloc_die ();
 
+  /* The events mask used with inotify on files.  This mask is not used on
+     directories.  */
+  if (follow_mode == Follow_name)
+    inotify_wd_mask = (IN_MODIFY | IN_ATTRIB | IN_DELETE_SELF | IN_MOVE_SELF);
+  else
+    /* if following by descriptor, we're watching the same file even if
+       unlinked or renamed.  */
+    inotify_wd_mask = IN_MODIFY;
+
+
   /* Add an inotify watch for each watched file.  If -F is specified then watch
      its parent directory too, in this way when they re-appear we can add them
      again to the watch list.  */
-- 
1.9.1