vendor/github.com/fsnotify/fsnotify/inotify.go
changeset 242 2a9ec03fe5a1
child 260 445e01aede7e
equal deleted inserted replaced
241:e77dad242f4c 242:2a9ec03fe5a1
       
     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 
       
     7 package fsnotify
       
     8 
       
     9 import (
       
    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.
       
    23 type 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.
       
    36 func 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 
       
    63 func (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.
       
    73 func (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).
       
    91 func (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).
       
   126 func (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 
       
   165 type 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
       
   172 func (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.
       
   300 func (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.
       
   319 func 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 }