diff --git a/fsnotify.go b/fsnotify.go index 9a48d84..e0db097 100644 --- a/fsnotify.go +++ b/fsnotify.go @@ -8,12 +8,13 @@ package fsnotify import "fmt" const ( - FSN_CREATE = 1 - FSN_MODIFY = 2 - FSN_DELETE = 4 - FSN_RENAME = 8 + FSN_CREATE = 1 << iota + FSN_MODIFY + FSN_DELETE + FSN_RENAME + FSN_CLOSE_WRITE - FSN_ALL = FSN_MODIFY | FSN_DELETE | FSN_RENAME | FSN_CREATE + FSN_ALL = FSN_MODIFY | FSN_DELETE | FSN_RENAME | FSN_CREATE | FSN_CLOSE_WRITE ) // Purge events from interal chan to external chan if passes filter @@ -40,6 +41,26 @@ func (w *Watcher) purgeEvents() { sendEvent = true } + // In some cases, we make sure a file change is finished before we read it. + // So how do you do that? Try using IN_CLOSE_WRITE instead. + // + // https://stackoverflow.com/questions/32377517/inotify-event-in-modify-occurring-twice-for-tftp-put/32424150 + // Q: What is the difference between IN_MODIFY and IN_CLOSE_WRITE? + // The IN_MODIFY event is emitted on a file content change (e.g. via the write() syscall) + // while IN_CLOSE_WRITE occurs on closing the changed file. + // It means each change operation causes one IN_MODIFY event (it may occur many times during manipulations with an open file) + // whereas IN_CLOSE_WRITE is emitted only once (on closing the file). + // + // Q: Is it better to use IN_MODIFY or IN_CLOSE_WRITE? + // It varies from case to case. Usually it is more suitable to use IN_CLOSE_WRITE + // because if emitted the all changes on the appropriate file are safely written inside the file. + // The IN_MODIFY event needn't mean that a file change is finished (data may remain in memory buffers in the application). + // On the other hand, many logs and similar files must be monitored using IN_MODIFY - + // in such cases where these files are permanently open and thus no IN_CLOSE_WRITE can be emitted. + if (fsnFlags&FSN_CLOSE_WRITE == FSN_CLOSE_WRITE) && ev.IsCloseWrite() { + sendEvent = true + } + if sendEvent { w.Event <- ev } @@ -103,6 +124,10 @@ func (e *FileEvent) String() string { events += "|" + "ATTRIB" } + if e.IsCloseWrite() { + events += "|" + "CLOSE_WRITE" + } + if len(events) > 0 { events = events[1:] } diff --git a/fsnotify_linux.go b/fsnotify_linux.go index 80ade87..89a0fdc 100644 --- a/fsnotify_linux.go +++ b/fsnotify_linux.go @@ -47,7 +47,7 @@ const ( sys_IN_MOVE_SELF uint32 = syscall.IN_MOVE_SELF sys_IN_OPEN uint32 = syscall.IN_OPEN - sys_AGNOSTIC_EVENTS = sys_IN_MOVED_TO | sys_IN_MOVED_FROM | sys_IN_CREATE | sys_IN_ATTRIB | sys_IN_MODIFY | sys_IN_MOVE_SELF | sys_IN_DELETE | sys_IN_DELETE_SELF + sys_AGNOSTIC_EVENTS = sys_IN_CLOSE_WRITE | sys_IN_MOVED_TO | sys_IN_MOVED_FROM | sys_IN_CREATE | sys_IN_ATTRIB | sys_IN_MODIFY | sys_IN_MOVE_SELF | sys_IN_DELETE | sys_IN_DELETE_SELF // Special events sys_IN_ISDIR uint32 = syscall.IN_ISDIR @@ -87,6 +87,11 @@ func (e *FileEvent) IsAttrib() bool { return (e.mask & sys_IN_ATTRIB) == sys_IN_ATTRIB } +// IsCloseWrite reports whether the FileEvent was triggered by closing the changed file. +func (e *FileEvent) IsCloseWrite() bool { + return (e.mask & sys_IN_CLOSE_WRITE) == sys_IN_CLOSE_WRITE +} + type watch struct { wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)