//go:build linux && !appengine
// +build linux,!appengine

// Note: the documentation on the Watcher type and methods is generated from
// mkdoc.zsh

package fsnotify

import (
	
	
	
	
	
	
	
	

	
)

// Watcher watches a set of paths, delivering events on a channel.
//
// A watcher should not be copied (e.g. pass it by pointer, rather than by
// value).
//
// # Linux notes
//
// When a file is removed a Remove event won't be emitted until all file
// descriptors are closed, and deletes will always emit a Chmod. For example:
//
//	fp := os.Open("file")
//	os.Remove("file")        // Triggers Chmod
//	fp.Close()               // Triggers Remove
//
// This is the event that inotify sends, so not much can be changed about this.
//
// The fs.inotify.max_user_watches sysctl variable specifies the upper limit
// for the number of watches per user, and fs.inotify.max_user_instances
// specifies the maximum number of inotify instances per user. Every Watcher you
// create is an "instance", and every path you add is a "watch".
//
// These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and
// /proc/sys/fs/inotify/max_user_instances
//
// To increase them you can use sysctl or write the value to the /proc file:
//
//	# Default values on Linux 5.18
//	sysctl fs.inotify.max_user_watches=124983
//	sysctl fs.inotify.max_user_instances=128
//
// To make the changes persist on reboot edit /etc/sysctl.conf or
// /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check
// your distro's documentation):
//
//	fs.inotify.max_user_watches=124983
//	fs.inotify.max_user_instances=128
//
// Reaching the limit will result in a "no space left on device" or "too many open
// files" error.
//
// # kqueue notes (macOS, BSD)
//
// kqueue requires opening a file descriptor for every file that's being watched;
// so if you're watching a directory with five files then that's six file
// descriptors. You will run in to your system's "max open files" limit faster on
// these platforms.
//
// The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to
// control the maximum number of open files, as well as /etc/login.conf on BSD
// systems.
//
// # Windows notes
//
// Paths can be added as "C:\path\to\dir", but forward slashes
// ("C:/path/to/dir") will also work.
//
// When a watched directory is removed it will always send an event for the
// directory itself, but may not send events for all files in that directory.
// Sometimes it will send events for all times, sometimes it will send no
// events, and often only for some files.
//
// The default ReadDirectoryChangesW() buffer size is 64K, which is the largest
// value that is guaranteed to work with SMB filesystems. If you have many
// events in quick succession this may not be enough, and you will have to use
// [WithBufferSize] to increase the value.
type Watcher struct {
	// Events sends the filesystem change events.
	//
	// fsnotify can send the following events; a "path" here can refer to a
	// file, directory, symbolic link, or special file like a FIFO.
	//
	//   fsnotify.Create    A new path was created; this may be followed by one
	//                      or more Write events if data also gets written to a
	//                      file.
	//
	//   fsnotify.Remove    A path was removed.
	//
	//   fsnotify.Rename    A path was renamed. A rename is always sent with the
	//                      old path as Event.Name, and a Create event will be
	//                      sent with the new name. Renames are only sent for
	//                      paths that are currently watched; e.g. moving an
	//                      unmonitored file into a monitored directory will
	//                      show up as just a Create. Similarly, renaming a file
	//                      to outside a monitored directory will show up as
	//                      only a Rename.
	//
	//   fsnotify.Write     A file or named pipe was written to. A Truncate will
	//                      also trigger a Write. A single "write action"
	//                      initiated by the user may show up as one or multiple
	//                      writes, depending on when the system syncs things to
	//                      disk. For example when compiling a large Go program
	//                      you may get hundreds of Write events, and you may
	//                      want to wait until you've stopped receiving them
	//                      (see the dedup example in cmd/fsnotify).
	//
	//                      Some systems may send Write event for directories
	//                      when the directory content changes.
	//
	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent
	//                      when a file is removed (or more accurately, when a
	//                      link to an inode is removed). On kqueue it's sent
	//                      when a file is truncated. On Windows it's never
	//                      sent.
	Events chan Event

	// Errors sends any errors.
	Errors chan error

	// Store fd here as os.File.Read() will no longer return on close after
	// calling Fd(). See: https://github.com/golang/go/issues/26439
	fd          int
	inotifyFile *os.File
	watches     *watches
	done        chan struct{} // Channel for sending a "quit message" to the reader goroutine
	closeMu     sync.Mutex
	doneResp    chan struct{} // Channel to respond to Close
}

type (
	watches struct {
		mu   sync.RWMutex
		wd   map[uint32]*watch // wd → watch
		path map[string]uint32 // pathname → wd
	}
	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)
		path  string // Watch path.
	}
)

func newWatches() *watches {
	return &watches{
		wd:   make(map[uint32]*watch),
		path: make(map[string]uint32),
	}
}

func ( *watches) () int {
	.mu.RLock()
	defer .mu.RUnlock()
	return len(.wd)
}

func ( *watches) ( *watch) {
	.mu.Lock()
	defer .mu.Unlock()
	.wd[.wd] = 
	.path[.path] = .wd
}

func ( *watches) ( uint32) {
	.mu.Lock()
	defer .mu.Unlock()
	delete(.path, .wd[].path)
	delete(.wd, )
}

func ( *watches) ( string) (uint32, bool) {
	.mu.Lock()
	defer .mu.Unlock()

	,  := .path[]
	if ! {
		return 0, false
	}

	delete(.path, )
	delete(.wd, )

	return , true
}

func ( *watches) ( string) *watch {
	.mu.RLock()
	defer .mu.RUnlock()
	return .wd[.path[]]
}

func ( *watches) ( uint32) *watch {
	.mu.RLock()
	defer .mu.RUnlock()
	return .wd[]
}

func ( *watches) ( string,  func(*watch) (*watch, error)) error {
	.mu.Lock()
	defer .mu.Unlock()

	var  *watch
	,  := .path[]
	if  {
		 = .wd[]
	}

	,  := ()
	if  != nil {
		return 
	}
	if  != nil {
		.wd[.wd] = 
		.path[.path] = .wd

		if .wd !=  {
			delete(.wd, )
		}
	}

	return nil
}

// NewWatcher creates a new Watcher.
func () (*Watcher, error) {
	return NewBufferedWatcher(0)
}

// NewBufferedWatcher creates a new Watcher with a buffered Watcher.Events
// channel.
//
// The main use case for this is situations with a very large number of events
// where the kernel buffer size can't be increased (e.g. due to lack of
// permissions). An unbuffered Watcher will perform better for almost all use
// cases, and whenever possible you will be better off increasing the kernel
// buffers instead of adding a large userspace buffer.
func ( uint) (*Watcher, error) {
	// Need to set nonblocking mode for SetDeadline to work, otherwise blocking
	// I/O operations won't terminate on close.
	,  := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK)
	if  == -1 {
		return nil, 
	}

	 := &Watcher{
		fd:          ,
		inotifyFile: os.NewFile(uintptr(), ""),
		watches:     newWatches(),
		Events:      make(chan Event, ),
		Errors:      make(chan error),
		done:        make(chan struct{}),
		doneResp:    make(chan struct{}),
	}

	go .readEvents()
	return , nil
}

// Returns true if the event was sent, or false if watcher is closed.
func ( *Watcher) ( Event) bool {
	select {
	case .Events <- :
		return true
	case <-.done:
		return false
	}
}

// Returns true if the error was sent, or false if watcher is closed.
func ( *Watcher) ( error) bool {
	select {
	case .Errors <- :
		return true
	case <-.done:
		return false
	}
}

func ( *Watcher) () bool {
	select {
	case <-.done:
		return true
	default:
		return false
	}
}

// Close removes all watches and closes the Events channel.
func ( *Watcher) () error {
	.closeMu.Lock()
	if .isClosed() {
		.closeMu.Unlock()
		return nil
	}
	close(.done)
	.closeMu.Unlock()

	// Causes any blocking reads to return with an error, provided the file
	// still supports deadline operations.
	 := .inotifyFile.Close()
	if  != nil {
		return 
	}

	// Wait for goroutine to close
	<-.doneResp

	return nil
}

// Add starts monitoring the path for changes.
//
// A path can only be watched once; watching it more than once is a no-op and will
// not return an error. Paths that do not yet exist on the filesystem cannot be
// watched.
//
// A watch will be automatically removed if the watched path is deleted or
// renamed. The exception is the Windows backend, which doesn't remove the
// watcher on renames.
//
// Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special
// filesystems (/proc, /sys, etc.) generally don't work.
//
// Returns [ErrClosed] if [Watcher.Close] was called.
//
// See [Watcher.AddWith] for a version that allows adding options.
//
// # Watching directories
//
// All files in a directory are monitored, including new files that are created
// after the watcher is started. Subdirectories are not watched (i.e. it's
// non-recursive).
//
// # Watching files
//
// Watching individual files (rather than directories) is generally not
// recommended as many programs (especially editors) update files atomically: it
// will write to a temporary file which is then moved to to destination,
// overwriting the original (or some variant thereof). The watcher on the
// original file is now lost, as that no longer exists.
//
// The upshot of this is that a power failure or crash won't leave a
// half-written file.
//
// Watch the parent directory and use Event.Name to filter out files you're not
// interested in. There is an example of this in cmd/fsnotify/file.go.
func ( *Watcher) ( string) error { return .AddWith() }

// AddWith is like [Watcher.Add], but allows adding options. When using Add()
// the defaults described below are used.
//
// Possible options are:
//
//   - [WithBufferSize] sets the buffer size for the Windows backend; no-op on
//     other platforms. The default is 64K (65536 bytes).
func ( *Watcher) ( string,  ...addOpt) error {
	if .isClosed() {
		return ErrClosed
	}

	 = filepath.Clean()
	_ = getOptions(...)

	var  uint32 = unix.IN_MOVED_TO | unix.IN_MOVED_FROM |
		unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY |
		unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF

	return .watches.updatePath(, func( *watch) (*watch, error) {
		if  != nil {
			 |= .flags | unix.IN_MASK_ADD
		}

		,  := unix.InotifyAddWatch(.fd, , )
		if  == -1 {
			return nil, 
		}

		if  == nil {
			return &watch{
				wd:    uint32(),
				path:  ,
				flags: ,
			}, nil
		}

		.wd = uint32()
		.flags = 
		return , nil
	})
}

// Remove stops monitoring the path for changes.
//
// Directories are always removed non-recursively. For example, if you added
// /tmp/dir and /tmp/dir/subdir then you will need to remove both.
//
// Removing a path that has not yet been added returns [ErrNonExistentWatch].
//
// Returns nil if [Watcher.Close] was called.
func ( *Watcher) ( string) error {
	if .isClosed() {
		return nil
	}
	return .remove(filepath.Clean())
}

func ( *Watcher) ( string) error {
	,  := .watches.removePath()
	if ! {
		return fmt.Errorf("%w: %s", ErrNonExistentWatch, )
	}

	,  := unix.InotifyRmWatch(.fd, )
	if  == -1 {
		// TODO: Perhaps it's not helpful to return an error here in every case;
		//       The only two possible errors are:
		//
		//       - EBADF, which happens when w.fd is not a valid file descriptor
		//         of any kind.
		//       - EINVAL, which is when fd is not an inotify descriptor or wd
		//         is not a valid watch descriptor. Watch descriptors are
		//         invalidated when they are removed explicitly or implicitly;
		//         explicitly by inotify_rm_watch, implicitly when the file they
		//         are watching is deleted.
		return 
	}
	return nil
}

// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
// yet removed).
//
// Returns nil if [Watcher.Close] was called.
func ( *Watcher) () []string {
	if .isClosed() {
		return nil
	}

	 := make([]string, 0, .watches.len())
	.watches.mu.RLock()
	for  := range .watches.path {
		 = append(, )
	}
	.watches.mu.RUnlock()

	return 
}

// readEvents reads from the inotify file descriptor, converts the
// received events into Event objects and sends them via the Events channel
func ( *Watcher) () {
	defer func() {
		close(.doneResp)
		close(.Errors)
		close(.Events)
	}()

	var (
		   [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
		 error                                // Syscall errno
	)
	for {
		// See if we have been closed.
		if .isClosed() {
			return
		}

		,  := .inotifyFile.Read([:])
		switch {
		case errors.Unwrap() == os.ErrClosed:
			return
		case  != nil:
			if !.sendError() {
				return
			}
			continue
		}

		if  < unix.SizeofInotifyEvent {
			var  error
			if  == 0 {
				 = io.EOF // If EOF is received. This should really never happen.
			} else if  < 0 {
				 =  // If an error occurred while reading.
			} else {
				 = errors.New("notify: short read in readEvents()") // Read was too short.
			}
			if !.sendError() {
				return
			}
			continue
		}

		var  uint32
		// We don't know how many events we just read into the buffer
		// While the offset points to at least one whole event...
		for  <= uint32(-unix.SizeofInotifyEvent) {
			var (
				// Point "raw" to the event in the buffer
				     = (*unix.InotifyEvent)(unsafe.Pointer(&[]))
				    = uint32(.Mask)
				 = uint32(.Len)
			)

			if &unix.IN_Q_OVERFLOW != 0 {
				if !.sendError(ErrEventOverflow) {
					return
				}
			}

			// If the event happened to the watched directory or the watched file, the kernel
			// doesn't append the filename to the event, but we would like to always fill the
			// the "Name" field with a valid filename. We retrieve the path of the watch from
			// the "paths" map.
			 := .watches.byWd(uint32(.Wd))

			// inotify will automatically remove the watch on deletes; just need
			// to clean our state here.
			if  != nil && &unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
				.watches.remove(.wd)
			}
			// We can't really update the state when a watched path is moved;
			// only IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove
			// the watch.
			if  != nil && &unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
				 := .remove(.path)
				if  != nil && !errors.Is(, ErrNonExistentWatch) {
					if !.sendError() {
						return
					}
				}
			}

			var  string
			if  != nil {
				 = .path
			}
			if  > 0 {
				// Point "bytes" at the first byte of the filename
				 := (*[unix.PathMax]byte)(unsafe.Pointer(&[+unix.SizeofInotifyEvent]))[::]
				// The filename is padded with NULL bytes. TrimRight() gets rid of those.
				 += "/" + strings.TrimRight(string([0:]), "\000")
			}

			 := .newEvent(, )

			// Send the events that are not ignored on the events channel
			if &unix.IN_IGNORED == 0 {
				if !.sendEvent() {
					return
				}
			}

			// Move to the next event in the buffer
			 += unix.SizeofInotifyEvent + 
		}
	}
}

// newEvent returns an platform-independent Event based on an inotify mask.
func ( *Watcher) ( string,  uint32) Event {
	 := Event{Name: }
	if &unix.IN_CREATE == unix.IN_CREATE || &unix.IN_MOVED_TO == unix.IN_MOVED_TO {
		.Op |= Create
	}
	if &unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || &unix.IN_DELETE == unix.IN_DELETE {
		.Op |= Remove
	}
	if &unix.IN_MODIFY == unix.IN_MODIFY {
		.Op |= Write
	}
	if &unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || &unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
		.Op |= Rename
	}
	if &unix.IN_ATTRIB == unix.IN_ATTRIB {
		.Op |= Chmod
	}
	return 
}