@@ -52,6 +52,10 @@ type naiveNotify struct {
5252 wrappedEvents chan FileEvent
5353 errors chan error
5454 numWatches int64
55+
56+ // addWatch registers a path with the watcher. A field so tests can inject
57+ // a permission error, which a process running as root cannot produce.
58+ addWatch func (path string ) error
5559}
5660
5761func (d * naiveNotify ) Start () error {
@@ -90,7 +94,7 @@ func (d *naiveNotify) Start() error {
9094 return fmt .Errorf ("notify.Add(%q): %w" , name , err )
9195 }
9296 } else {
93- err = d .add (filepath .Dir (name ))
97+ err = d .addWatch (filepath .Dir (name ))
9498 if err != nil {
9599 return fmt .Errorf ("notify.Add(%q): %w" , filepath .Dir (name ), err )
96100 }
@@ -104,46 +108,49 @@ func (d *naiveNotify) Start() error {
104108
105109func (d * naiveNotify ) watchRecursively (dir string ) error {
106110 if d .isWatcherRecursive {
107- err := d .add (dir )
111+ err := d .addWatch (dir )
108112 if err == nil || os .IsNotExist (err ) {
109113 return nil
110114 }
111115 return fmt .Errorf ("watcher.Add(%q): %w" , dir , err )
112116 }
113117
114- return filepath .WalkDir (dir , func (path string , info fs.DirEntry , err error ) error {
115- if err != nil {
116- // A directory we are not allowed to read is not a reason to abandon the
117- // whole watch: we simply cannot see inside it, so skip it and carry on.
118- if os .IsPermission (err ) {
119- logrus .Debugf ("Not watching %s: %v" , path , err )
120- return filepath .SkipDir
121- }
122- return err
118+ return filepath .WalkDir (dir , d .walkAndAdd )
119+ }
120+
121+ // walkAndAdd puts a watch on every directory of the tree being walked.
122+ func (d * naiveNotify ) walkAndAdd (path string , info fs.DirEntry , err error ) error {
123+ if err != nil {
124+ // A directory we are not allowed to read is not a reason to abandon the
125+ // whole watch: we simply cannot see inside it, so skip it and carry on.
126+ if os .IsPermission (err ) {
127+ logrus .Debugf ("Not watching %s: %v" , path , err )
128+ return filepath .SkipDir
123129 }
130+ return err
131+ }
132+
133+ if ! info .IsDir () {
134+ return nil
135+ }
136+
137+ if d .shouldSkipDir (path ) {
138+ logrus .Debugf ("Ignoring directory and its contents (recursively): %s" , path )
139+ return filepath .SkipDir
140+ }
124141
125- if ! info .IsDir () {
142+ err = d .addWatch (path )
143+ if err != nil {
144+ if os .IsNotExist (err ) {
126145 return nil
127146 }
128-
129- if d .shouldSkipDir (path ) {
130- logrus .Debugf ("Ignoring directory and its contents (recursively): %s" , path )
147+ if os .IsPermission (err ) {
148+ logrus .Debugf ("Not watching %s: %v" , path , err )
131149 return filepath .SkipDir
132150 }
133-
134- err = d .add (path )
135- if err != nil {
136- if os .IsNotExist (err ) {
137- return nil
138- }
139- if os .IsPermission (err ) {
140- logrus .Debugf ("Not watching %s: %v" , path , err )
141- return filepath .SkipDir
142- }
143- return fmt .Errorf ("watcher.Add(%q): %w" , path , err )
144- }
145- return nil
146- })
151+ return fmt .Errorf ("watcher.Add(%q): %w" , path , err )
152+ }
153+ return nil
147154}
148155
149156func (d * naiveNotify ) Close () error {
@@ -160,7 +167,7 @@ func (d *naiveNotify) Errors() chan error {
160167 return d .errors
161168}
162169
163- func (d * naiveNotify ) loop () { //nolint:gocyclo
170+ func (d * naiveNotify ) loop () {
164171 defer close (d .wrappedEvents )
165172 for e := range d .events {
166173 // The Windows fsnotify event stream sometimes gets events with empty names
@@ -188,47 +195,53 @@ func (d *naiveNotify) loop() { //nolint:gocyclo
188195 // because it's a bit more elegant that way.
189196 //
190197 // TODO(dbentley): if there's a delete should we call d.watcher.Remove to prevent leaking?
191- err := filepath .WalkDir (e .Name , func (path string , info fs.DirEntry , err error ) error {
192- if err != nil {
193- if os .IsPermission (err ) {
194- logrus .Debugf ("Not watching %s: %v" , path , err )
195- return filepath .SkipDir
196- }
197- return err
198- }
198+ err := filepath .WalkDir (e .Name , d .walkAndNotify (e .Name ))
199+ if err != nil && ! os .IsNotExist (err ) {
200+ logrus .Infof ("Error walking directory %s: %s" , e .Name , err )
201+ }
202+ }
203+ }
199204
200- if d .shouldNotify (path ) {
201- d .wrappedEvents <- FileEvent (path )
205+ // walkAndNotify fires an event for every path under name, watching the
206+ // directories it goes through.
207+ func (d * naiveNotify ) walkAndNotify (name string ) fs.WalkDirFunc {
208+ return func (path string , info fs.DirEntry , err error ) error {
209+ if err != nil {
210+ if os .IsPermission (err ) {
211+ logrus .Debugf ("Not watching %s: %v" , path , err )
212+ return filepath .SkipDir
202213 }
214+ return err
215+ }
203216
204- // TODO(dmiller): symlinks 😭
217+ if d .shouldNotify (path ) {
218+ d .wrappedEvents <- FileEvent (path )
219+ }
205220
206- shouldWatch := false
207- if info .IsDir () {
208- // watch directories unless we can skip them entirely
209- if d .shouldSkipDir (path ) {
210- return filepath .SkipDir
211- }
221+ // TODO(dmiller): symlinks 😭
222+
223+ shouldWatch := false
224+ if info .IsDir () {
225+ // watch directories unless we can skip them entirely
226+ if d .shouldSkipDir (path ) {
227+ return filepath .SkipDir
228+ }
212229
230+ shouldWatch = true
231+ } else {
232+ // watch files that are explicitly named, but don't watch others
233+ _ , ok := d .notifyList [path ]
234+ if ok {
213235 shouldWatch = true
214- } else {
215- // watch files that are explicitly named, but don't watch others
216- _ , ok := d .notifyList [path ]
217- if ok {
218- shouldWatch = true
219- }
220236 }
221- if shouldWatch {
222- err := d . add ( path )
223- if err != nil && ! os . IsNotExist ( err ) {
224- logrus . Infof ( "Error watching path %s: %s" , e . Name , err )
225- }
237+ }
238+ if shouldWatch {
239+ err := d . addWatch ( path )
240+ if err != nil && ! os . IsNotExist ( err ) {
241+ logrus . Infof ( "Error watching path %s: %s" , name , err )
226242 }
227- return nil
228- })
229- if err != nil && ! os .IsNotExist (err ) {
230- logrus .Infof ("Error walking directory %s: %s" , e .Name , err )
231243 }
244+ return nil
232245 }
233246}
234247
@@ -320,6 +333,7 @@ func newWatcher(paths []string) (Notify, error) {
320333 errors : fsw .Errors ,
321334 isWatcherRecursive : isWatcherRecursive ,
322335 }
336+ wmw .addWatch = wmw .add
323337
324338 return wmw , nil
325339}
0 commit comments