all: sync with master

This commit is contained in:
Ainar Garipov
2024-03-12 17:45:11 +03:00
parent fbc0d981ba
commit 6f7bfd6c9c
93 changed files with 2828 additions and 1270 deletions

View File

@@ -8,6 +8,8 @@ import (
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/osutil"
"github.com/AdguardTeam/golibs/stringutil"
"github.com/fsnotify/fsnotify"
)
@@ -18,31 +20,38 @@ type event = struct{}
// FSWatcher tracks all the fyle system events and notifies about those.
//
// TODO(e.burkov, a.garipov): Move into another package like aghfs.
//
// TODO(e.burkov): Add tests.
type FSWatcher interface {
// Start starts watching the added files.
Start() (err error)
// Close stops watching the files and closes an update channel.
io.Closer
// Events should return a read-only channel which notifies about events.
// Events returns the channel to notify about the file system events.
Events() (e <-chan event)
// Add should check if the file named name is accessible and starts tracking
// it.
// Add starts tracking the file. It returns an error if the file can't be
// tracked. It must not be called after Start.
Add(name string) (err error)
}
// osWatcher tracks the file system provided by the OS.
type osWatcher struct {
// w is the actual notifier that is handled by osWatcher.
w *fsnotify.Watcher
// watcher is the actual notifier that is handled by osWatcher.
watcher *fsnotify.Watcher
// events is the channel to notify.
events chan event
// files is the set of tracked files.
files *stringutil.Set
}
const (
// osWatcherPref is a prefix for logging and wrapping errors in osWathcer's
// methods.
osWatcherPref = "os watcher"
)
// osWatcherPref is a prefix for logging and wrapping errors in osWathcer's
// methods.
const osWatcherPref = "os watcher"
// NewOSWritesWatcher creates FSWatcher that tracks the real file system of the
// OS and notifies only about writing events.
@@ -55,25 +64,27 @@ func NewOSWritesWatcher() (w FSWatcher, err error) {
return nil, fmt.Errorf("creating watcher: %w", err)
}
fsw := &osWatcher{
w: watcher,
events: make(chan event, 1),
}
go fsw.handleErrors()
go fsw.handleEvents()
return fsw, nil
return &osWatcher{
watcher: watcher,
events: make(chan event, 1),
files: stringutil.NewSet(),
}, nil
}
// handleErrors handles accompanying errors. It used to be called in a separate
// goroutine.
func (w *osWatcher) handleErrors() {
defer log.OnPanic(fmt.Sprintf("%s: handling errors", osWatcherPref))
// type check
var _ FSWatcher = (*osWatcher)(nil)
for err := range w.w.Errors {
log.Error("%s: %s", osWatcherPref, err)
}
// Start implements the FSWatcher interface for *osWatcher.
func (w *osWatcher) Start() (err error) {
go w.handleErrors()
go w.handleEvents()
return nil
}
// Close implements the FSWatcher interface for *osWatcher.
func (w *osWatcher) Close() (err error) {
return w.watcher.Close()
}
// Events implements the FSWatcher interface for *osWatcher.
@@ -81,34 +92,42 @@ func (w *osWatcher) Events() (e <-chan event) {
return w.events
}
// Add implements the FSWatcher interface for *osWatcher.
// Add implements the [FSWatcher] interface for *osWatcher.
//
// TODO(e.burkov): Make it accept non-existing files to detect it's creating.
func (w *osWatcher) Add(name string) (err error) {
defer func() { err = errors.Annotate(err, "%s: %w", osWatcherPref) }()
if _, err = fs.Stat(RootDirFS(), name); err != nil {
fi, err := fs.Stat(osutil.RootDirFS(), name)
if err != nil {
return fmt.Errorf("checking file %q: %w", name, err)
}
return w.w.Add(filepath.Join("/", name))
}
name = filepath.Join("/", name)
w.files.Add(name)
// Close implements the FSWatcher interface for *osWatcher.
func (w *osWatcher) Close() (err error) {
return w.w.Close()
// Watch the directory and filter the events by the file name, since the
// common recomendation to the fsnotify package is to watch the directory
// instead of the file itself.
//
// See https://pkg.go.dev/github.com/fsnotify/fsnotify@v1.7.0#readme-watching-a-file-doesn-t-work-well.
if !fi.IsDir() {
name = filepath.Dir(name)
}
return w.watcher.Add(name)
}
// handleEvents notifies about the received file system's event if needed. It
// used to be called in a separate goroutine.
// is intended to be used as a goroutine.
func (w *osWatcher) handleEvents() {
defer log.OnPanic(fmt.Sprintf("%s: handling events", osWatcherPref))
defer close(w.events)
ch := w.w.Events
ch := w.watcher.Events
for e := range ch {
if e.Op&fsnotify.Write == 0 {
if e.Op&fsnotify.Write == 0 || !w.files.Has(e.Name) {
continue
}
@@ -131,3 +150,13 @@ func (w *osWatcher) handleEvents() {
}
}
}
// handleErrors handles accompanying errors. It used to be called in a separate
// goroutine.
func (w *osWatcher) handleErrors() {
defer log.OnPanic(fmt.Sprintf("%s: handling errors", osWatcherPref))
for err := range w.watcher.Errors {
log.Error("%s: %s", osWatcherPref, err)
}
}