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