1// Copyright 2010 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build linux
6
7package fsnotify
8
9import (
10	"errors"
11	"fmt"
12	"io"
13	"os"
14	"path/filepath"
15	"strings"
16	"sync"
17	"unsafe"
18
19	"golang.org/x/sys/unix"
20)
21
22// Watcher watches a set of files, delivering events to a channel.
23type Watcher struct {
24	Events   chan Event
25	Errors   chan error
26	mu       sync.Mutex // Map access
27	fd       int
28	poller   *fdPoller
29	watches  map[string]*watch // Map of inotify watches (key: path)
30	paths    map[int]string    // Map of watched paths (key: watch descriptor)
31	done     chan struct{}     // Channel for sending a "quit message" to the reader goroutine
32	doneResp chan struct{}     // Channel to respond to Close
33}
34
35// NewWatcher establishes a new watcher with the underlying OS and begins waiting for events.
36func NewWatcher() (*Watcher, error) {
37	// Create inotify fd
38	fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC)
39	if fd == -1 {
40		return nil, errno
41	}
42	// Create epoll
43	poller, err := newFdPoller(fd)
44	if err != nil {
45		unix.Close(fd)
46		return nil, err
47	}
48	w := &Watcher{
49		fd:       fd,
50		poller:   poller,
51		watches:  make(map[string]*watch),
52		paths:    make(map[int]string),
53		Events:   make(chan Event),
54		Errors:   make(chan error),
55		done:     make(chan struct{}),
56		doneResp: make(chan struct{}),
57	}
58
59	go w.readEvents()
60	return w, nil
61}
62
63func (w *Watcher) isClosed() bool {
64	select {
65	case <-w.done:
66		return true
67	default:
68		return false
69	}
70}
71
72// Close removes all watches and closes the events channel.
73func (w *Watcher) Close() error {
74	if w.isClosed() {
75		return nil
76	}
77
78	// Send 'close' signal to goroutine, and set the Watcher to closed.
79	close(w.done)
80
81	// Wake up goroutine
82	w.poller.wake()
83
84	// Wait for goroutine to close
85	<-w.doneResp
86
87	return nil
88}
89
90// Add starts watching the named file or directory (non-recursively).
91func (w *Watcher) Add(name string) error {
92	name = filepath.Clean(name)
93	if w.isClosed() {
94		return errors.New("inotify instance already closed")
95	}
96
97	const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
98		unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
99		unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF
100
101	var flags uint32 = agnosticEvents
102
103	w.mu.Lock()
104	defer w.mu.Unlock()
105	watchEntry := w.watches[name]
106	if watchEntry != nil {
107		flags |= watchEntry.flags | unix.IN_MASK_ADD
108	}
109	wd, errno := unix.InotifyAddWatch(w.fd, name, flags)
110	if wd == -1 {
111		return errno
112	}
113
114	if watchEntry == nil {
115		w.watches[name] = &watch{wd: uint32(wd), flags: flags}
116		w.paths[wd] = name
117	} else {
118		watchEntry.wd = uint32(wd)
119		watchEntry.flags = flags
120	}
121
122	return nil
123}
124
125// Remove stops watching the named file or directory (non-recursively).
126func (w *Watcher) Remove(name string) error {
127	name = filepath.Clean(name)
128
129	// Fetch the watch.
130	w.mu.Lock()
131	defer w.mu.Unlock()
132	watch, ok := w.watches[name]
133
134	// Remove it from inotify.
135	if !ok {
136		return fmt.Errorf("can't remove non-existent inotify watch for: %s", name)
137	}
138
139	// We successfully removed the watch if InotifyRmWatch doesn't return an
140	// error, we need to clean up our internal state to ensure it matches
141	// inotify's kernel state.
142	delete(w.paths, int(watch.wd))
143	delete(w.watches, name)
144
145	// inotify_rm_watch will return EINVAL if the file has been deleted;
146	// the inotify will already have been removed.
147	// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously
148	// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE
149	// so that EINVAL means that the wd is being rm_watch()ed or its file removed
150	// by another thread and we have not received IN_IGNORE event.
151	success, errno := unix.InotifyRmWatch(w.fd, watch.wd)
152	if success == -1 {
153		// TODO: Perhaps it's not helpful to return an error here in every case.
154		// the only two possible errors are:
155		// EBADF, which happens when w.fd is not a valid file descriptor of any kind.
156		// EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor.
157		// Watch descriptors are invalidated when they are removed explicitly or implicitly;
158		// explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted.
159		return errno
160	}
161
162	return nil
163}
164
165type watch struct {
166	wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
167	flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
168}
169
170// readEvents reads from the inotify file descriptor, converts the
171// received events into Event objects and sends them via the Events channel
172func (w *Watcher) readEvents() {
173	var (
174		buf   [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
175		n     int                                  // Number of bytes read with read()
176		errno error                                // Syscall errno
177		ok    bool                                 // For poller.wait
178	)
179
180	defer close(w.doneResp)
181	defer close(w.Errors)
182	defer close(w.Events)
183	defer unix.Close(w.fd)
184	defer w.poller.close()
185
186	for {
187		// See if we have been closed.
188		if w.isClosed() {
189			return
190		}
191
192		ok, errno = w.poller.wait()
193		if errno != nil {
194			select {
195			case w.Errors <- errno:
196			case <-w.done:
197				return
198			}
199			continue
200		}
201
202		if !ok {
203			continue
204		}
205
206		n, errno = unix.Read(w.fd, buf[:])
207		// If a signal interrupted execution, see if we've been asked to close, and try again.
208		// http://man7.org/linux/man-pages/man7/signal.7.html :
209		// "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable"
210		if errno == unix.EINTR {
211			continue
212		}
213
214		// unix.Read might have been woken up by Close. If so, we're done.
215		if w.isClosed() {
216			return
217		}
218
219		if n < unix.SizeofInotifyEvent {
220			var err error
221			if n == 0 {
222				// If EOF is received. This should really never happen.
223				err = io.EOF
224			} else if n < 0 {
225				// If an error occurred while reading.
226				err = errno
227			} else {
228				// Read was too short.
229				err = errors.New("notify: short read in readEvents()")
230			}
231			select {
232			case w.Errors <- err:
233			case <-w.done:
234				return
235			}
236			continue
237		}
238
239		var offset uint32
240		// We don't know how many events we just read into the buffer
241		// While the offset points to at least one whole event...
242		for offset <= uint32(n-unix.SizeofInotifyEvent) {
243			// Point "raw" to the event in the buffer
244			raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
245
246			mask := uint32(raw.Mask)
247			nameLen := uint32(raw.Len)
248
249			if mask&unix.IN_Q_OVERFLOW != 0 {
250				select {
251				case w.Errors <- ErrEventOverflow:
252				case <-w.done:
253					return
254				}
255			}
256
257			// If the event happened to the watched directory or the watched file, the kernel
258			// doesn't append the filename to the event, but we would like to always fill the
259			// the "Name" field with a valid filename. We retrieve the path of the watch from
260			// the "paths" map.
261			w.mu.Lock()
262			name, ok := w.paths[int(raw.Wd)]
263			// IN_DELETE_SELF occurs when the file/directory being watched is removed.
264			// This is a sign to clean up the maps, otherwise we are no longer in sync
265			// with the inotify kernel state which has already deleted the watch
266			// automatically.
267			if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
268				delete(w.paths, int(raw.Wd))
269				delete(w.watches, name)
270			}
271			w.mu.Unlock()
272
273			if nameLen > 0 {
274				// Point "bytes" at the first byte of the filename
275				bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))
276				// The filename is padded with NULL bytes. TrimRight() gets rid of those.
277				name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
278			}
279
280			event := newEvent(name, mask)
281
282			// Send the events that are not ignored on the events channel
283			if !event.ignoreLinux(mask) {
284				select {
285				case w.Events <- event:
286				case <-w.done:
287					return
288				}
289			}
290
291			// Move to the next event in the buffer
292			offset += unix.SizeofInotifyEvent + nameLen
293		}
294	}
295}
296
297// Certain types of events can be "ignored" and not sent over the Events
298// channel. Such as events marked ignore by the kernel, or MODIFY events
299// against files that do not exist.
300func (e *Event) ignoreLinux(mask uint32) bool {
301	// Ignore anything the inotify API says to ignore
302	if mask&unix.IN_IGNORED == unix.IN_IGNORED {
303		return true
304	}
305
306	// If the event is not a DELETE or RENAME, the file must exist.
307	// Otherwise the event is ignored.
308	// *Note*: this was put in place because it was seen that a MODIFY
309	// event was sent after the DELETE. This ignores that MODIFY and
310	// assumes a DELETE will come or has come if the file doesn't exist.
311	if !(e.Op&Remove == Remove || e.Op&Rename == Rename) {
312		_, statErr := os.Lstat(e.Name)
313		return os.IsNotExist(statErr)
314	}
315	return false
316}
317
318// newEvent returns an platform-independent Event based on an inotify mask.
319func newEvent(name string, mask uint32) Event {
320	e := Event{Name: name}
321	if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
322		e.Op |= Create
323	}
324	if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE {
325		e.Op |= Remove
326	}
327	if mask&unix.IN_MODIFY == unix.IN_MODIFY {
328		e.Op |= Write
329	}
330	if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
331		e.Op |= Rename
332	}
333	if mask&unix.IN_ATTRIB == unix.IN_ATTRIB {
334		e.Op |= Chmod
335	}
336	return e
337}
338