github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/inode_inotify.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fs 16 17 import ( 18 "fmt" 19 20 "github.com/SagerNet/gvisor/pkg/context" 21 "github.com/SagerNet/gvisor/pkg/sync" 22 ) 23 24 // Watches is the collection of inotify watches on an inode. 25 // 26 // +stateify savable 27 type Watches struct { 28 // mu protects the fields below. 29 mu sync.RWMutex `state:"nosave"` 30 31 // ws is the map of active watches in this collection, keyed by the inotify 32 // instance id of the owner. 33 ws map[uint64]*Watch 34 35 // unlinked indicates whether the target inode was ever unlinked. This is a 36 // hack to figure out if we should queue a IN_DELETE_SELF event when this 37 // watches collection is being destroyed, since otherwise we have no way of 38 // knowing if the target inode is going down due to a deletion or 39 // revalidation. 40 unlinked bool 41 } 42 43 func newWatches() *Watches { 44 return &Watches{} 45 } 46 47 // MarkUnlinked indicates the target for this set of watches to be unlinked. 48 // This has implications for the IN_EXCL_UNLINK flag. 49 func (w *Watches) MarkUnlinked() { 50 w.mu.Lock() 51 defer w.mu.Unlock() 52 w.unlinked = true 53 } 54 55 // Lookup returns a matching watch with the given id. Returns nil if no such 56 // watch exists. Note that the result returned by this method only remains valid 57 // if the inotify instance owning the watch is locked, preventing modification 58 // of the returned watch and preventing the replacement of the watch by another 59 // one from the same instance (since there may be at most one watch per 60 // instance, per target). 61 func (w *Watches) Lookup(id uint64) *Watch { 62 w.mu.Lock() 63 defer w.mu.Unlock() 64 return w.ws[id] 65 } 66 67 // Add adds watch into this set of watches. The watch being added must be unique 68 // - its ID() should not collide with any existing watches. 69 func (w *Watches) Add(watch *Watch) { 70 w.mu.Lock() 71 defer w.mu.Unlock() 72 73 // Sanity check, the new watch shouldn't collide with an existing 74 // watch. Silently replacing an existing watch would result in a ref leak on 75 // this inode. We could handle this collision by calling Unpin() on the 76 // existing watch, but then we end up leaking watch descriptor ids at the 77 // inotify level. 78 if _, exists := w.ws[watch.ID()]; exists { 79 panic(fmt.Sprintf("Watch collision with ID %+v", watch.ID())) 80 } 81 if w.ws == nil { 82 w.ws = make(map[uint64]*Watch) 83 } 84 w.ws[watch.ID()] = watch 85 } 86 87 // Remove removes a watch with the given id from this set of watches. The caller 88 // is responsible for generating any watch removal event, as appropriate. The 89 // provided id must match an existing watch in this collection. 90 func (w *Watches) Remove(id uint64) { 91 w.mu.Lock() 92 defer w.mu.Unlock() 93 94 if w.ws == nil { 95 // This watch set is being destroyed. The thread executing the 96 // destructor is already in the process of deleting all our watches. We 97 // got here with no refs on the inode because we raced with the 98 // destructor notifying all the watch owners of the inode's destruction. 99 // See the comment in Watches.TargetDestroyed for why this race exists. 100 return 101 } 102 103 watch, ok := w.ws[id] 104 if !ok { 105 // While there's technically no problem with silently ignoring a missing 106 // watch, this is almost certainly a bug. 107 panic(fmt.Sprintf("Attempt to remove a watch, but no watch found with provided id %+v.", id)) 108 } 109 delete(w.ws, watch.ID()) 110 } 111 112 // Notify queues a new event with all watches in this set. 113 func (w *Watches) Notify(name string, events, cookie uint32) { 114 // N.B. We don't defer the unlocks because Notify is in the hot path of 115 // all IO operations, and the defer costs too much for small IO 116 // operations. 117 w.mu.RLock() 118 for _, watch := range w.ws { 119 if name != "" && w.unlinked && !watch.NotifyParentAfterUnlink() { 120 // IN_EXCL_UNLINK - By default, when watching events on the children 121 // of a directory, events are generated for children even after they 122 // have been unlinked from the directory. This can result in large 123 // numbers of uninteresting events for some applications (e.g., if 124 // watching /tmp, in which many applications create temporary files 125 // whose names are immediately unlinked). Specifying IN_EXCL_UNLINK 126 // changes the default behavior, so that events are not generated 127 // for children after they have been unlinked from the watched 128 // directory. -- inotify(7) 129 // 130 // We know we're dealing with events for a parent when the name 131 // isn't empty. 132 continue 133 } 134 watch.Notify(name, events, cookie) 135 } 136 w.mu.RUnlock() 137 } 138 139 // Unpin unpins dirent from all watches in this set. 140 func (w *Watches) Unpin(ctx context.Context, d *Dirent) { 141 w.mu.RLock() 142 defer w.mu.RUnlock() 143 for _, watch := range w.ws { 144 watch.Unpin(ctx, d) 145 } 146 } 147 148 // targetDestroyed is called by the inode destructor to notify the watch owners 149 // of the impending destruction of the watch target. 150 func (w *Watches) targetDestroyed() { 151 var ws map[uint64]*Watch 152 153 // We can't hold w.mu while calling watch.TargetDestroyed to preserve lock 154 // ordering w.r.t to the owner inotify instances. Instead, atomically move 155 // the watches map into a local variable so we can iterate over it safely. 156 // 157 // Because of this however, it is possible for the watches' owners to reach 158 // this inode while the inode has no refs. This is still safe because the 159 // owners can only reach the inode until this function finishes calling 160 // watch.TargetDestroyed() below and the inode is guaranteed to exist in the 161 // meanwhile. But we still have to be very careful not to rely on inode 162 // state that may have been already destroyed. 163 w.mu.Lock() 164 ws = w.ws 165 w.ws = nil 166 w.mu.Unlock() 167 168 for _, watch := range ws { 169 watch.TargetDestroyed() 170 } 171 }