github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/inotify_watch.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  	"sync/atomic"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    21  	"github.com/SagerNet/gvisor/pkg/context"
    22  	"github.com/SagerNet/gvisor/pkg/sync"
    23  )
    24  
    25  // Watch represent a particular inotify watch created by inotify_add_watch.
    26  //
    27  // While a watch is active, it ensures the target inode is pinned in memory by
    28  // holding an extra ref on each dirent known (by inotify) to point to the
    29  // inode. These are known as pins. For a full discussion, see
    30  // fs/g3doc/inotify.md.
    31  //
    32  // +stateify savable
    33  type Watch struct {
    34  	// Inotify instance which owns this watch.
    35  	owner *Inotify
    36  
    37  	// Descriptor for this watch. This is unique across an inotify instance.
    38  	wd int32
    39  
    40  	// The inode being watched. Note that we don't directly hold a reference on
    41  	// this inode. Instead we hold a reference on the dirent(s) containing the
    42  	// inode, which we record in pins.
    43  	target *Inode
    44  
    45  	// unpinned indicates whether we have a hard reference on target. This field
    46  	// may only be modified through atomic ops.
    47  	unpinned uint32
    48  
    49  	// mu protects the fields below.
    50  	mu sync.Mutex `state:"nosave"`
    51  
    52  	// Events being monitored via this watch. Must be accessed atomically,
    53  	// writes are protected by mu.
    54  	mask uint32
    55  
    56  	// pins is the set of dirents this watch is currently pinning in memory by
    57  	// holding a reference to them. See Pin()/Unpin().
    58  	pins map[*Dirent]bool
    59  }
    60  
    61  // ID returns the id of the inotify instance that owns this watch.
    62  func (w *Watch) ID() uint64 {
    63  	return w.owner.id
    64  }
    65  
    66  // NotifyParentAfterUnlink indicates whether the parent of the watched object
    67  // should continue to be be notified of events after the target has been
    68  // unlinked.
    69  func (w *Watch) NotifyParentAfterUnlink() bool {
    70  	return atomic.LoadUint32(&w.mask)&linux.IN_EXCL_UNLINK == 0
    71  }
    72  
    73  // isRenameEvent returns true if eventMask describes a rename event.
    74  func isRenameEvent(eventMask uint32) bool {
    75  	return eventMask&(linux.IN_MOVED_FROM|linux.IN_MOVED_TO|linux.IN_MOVE_SELF) != 0
    76  }
    77  
    78  // Notify queues a new event on this watch.
    79  func (w *Watch) Notify(name string, events uint32, cookie uint32) {
    80  	mask := atomic.LoadUint32(&w.mask)
    81  	if mask&events == 0 {
    82  		// We weren't watching for this event.
    83  		return
    84  	}
    85  
    86  	// Event mask should include bits matched from the watch plus all control
    87  	// event bits.
    88  	unmaskableBits := ^uint32(0) &^ linux.IN_ALL_EVENTS
    89  	effectiveMask := unmaskableBits | mask
    90  	matchedEvents := effectiveMask & events
    91  	w.owner.queueEvent(newEvent(w.wd, name, matchedEvents, cookie))
    92  }
    93  
    94  // Pin acquires a new ref on dirent, which pins the dirent in memory while
    95  // the watch is active. Calling Pin for a second time on the same dirent for
    96  // the same watch is a no-op.
    97  func (w *Watch) Pin(d *Dirent) {
    98  	w.mu.Lock()
    99  	defer w.mu.Unlock()
   100  	if !w.pins[d] {
   101  		w.pins[d] = true
   102  		d.IncRef()
   103  	}
   104  }
   105  
   106  // Unpin drops any extra refs held on dirent due to a previous Pin
   107  // call. Calling Unpin multiple times for the same dirent, or on a dirent
   108  // without a corresponding Pin call is a no-op.
   109  func (w *Watch) Unpin(ctx context.Context, d *Dirent) {
   110  	w.mu.Lock()
   111  	defer w.mu.Unlock()
   112  	if w.pins[d] {
   113  		delete(w.pins, d)
   114  		d.DecRef(ctx)
   115  	}
   116  }
   117  
   118  // TargetDestroyed notifies the owner of the watch that the watch target is
   119  // gone. The owner should release its own references to the watcher upon
   120  // receiving this notification.
   121  func (w *Watch) TargetDestroyed() {
   122  	w.owner.targetDestroyed(w)
   123  }
   124  
   125  // destroy prepares the watch for destruction. It unpins all dirents pinned by
   126  // this watch. Destroy does not cause any new events to be generated. The caller
   127  // is responsible for ensuring there are no outstanding references to this
   128  // watch.
   129  func (w *Watch) destroy(ctx context.Context) {
   130  	w.mu.Lock()
   131  	defer w.mu.Unlock()
   132  	for d := range w.pins {
   133  		d.DecRef(ctx)
   134  	}
   135  	w.pins = nil
   136  }