summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/inotify_watch.go
blob: ff6ec6e3e4a8504b786dabe53461f6252f85c59a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fs

import (
	"sync"

	"gvisor.googlesource.com/gvisor/pkg/abi/linux"
)

// Watch represent a particular inotify watch created by inotify_add_watch.
//
// While a watch is active, it ensures the target inode is pinned in memory by
// holding an extra ref on each dirent known (by inotify) to point to the
// inode. These are known as pins. For a full discussion, see
// fs/g3doc/inotify.md.
type Watch struct {
	// Inotify instance which owns this watch.
	owner *Inotify

	// Descriptor for this watch. This is unique across an inotify instance.
	wd int32

	// Events being monitored via this watch.
	mask uint32

	// The inode being watched. Note that we don't directly hold a reference on
	// this inode. Instead we hold a reference on the dirent(s) containing the
	// inode, which we record in pins.
	target *Inode

	// unpinned indicates whether we have a hard reference on target. This field
	// may only be modified through atomic ops.
	unpinned uint32

	// mu protects the fields below.
	mu sync.Mutex `state:"nosave"`

	// pins is the set of dirents this watch is currently pinning in memory by
	// holding a reference to them. See Pin()/Unpin().
	pins map[*Dirent]bool
}

// ID returns the id of the inotify instance that owns this watch.
func (w *Watch) ID() uint64 {
	return w.owner.id
}

// NotifyParentAfterUnlink indicates whether the parent of the watched object
// should continue to be be notified of events after the target has been
// unlinked.
func (w *Watch) NotifyParentAfterUnlink() bool {
	return w.mask&linux.IN_EXCL_UNLINK == 0
}

// isRenameEvent returns true if eventMask describes a rename event.
func isRenameEvent(eventMask uint32) bool {
	return eventMask&(linux.IN_MOVED_FROM|linux.IN_MOVED_TO|linux.IN_MOVE_SELF) != 0
}

// Notify queues a new event on this watch.
func (w *Watch) Notify(name string, events uint32, cookie uint32) {
	unmaskableBits := ^uint32(0) &^ linux.IN_ALL_EVENTS
	effectiveMask := unmaskableBits | w.mask
	matchedEvents := effectiveMask & events

	if matchedEvents == 0 {
		// We weren't watching for this event.
		return
	}

	w.owner.queueEvent(newEvent(w.wd, name, matchedEvents, cookie))
}

// Pin acquires a new ref on dirent, which pins the dirent in memory while
// the watch is active. Calling Pin for a second time on the same dirent for
// the same watch is a no-op.
func (w *Watch) Pin(d *Dirent) {
	w.mu.Lock()
	defer w.mu.Unlock()
	if !w.pins[d] {
		w.pins[d] = true
		d.IncRef()
	}
}

// Unpin drops any extra refs held on dirent due to a previous Pin
// call. Calling Unpin multiple times for the same dirent, or on a dirent
// without a corresponding Pin call is a no-op.
func (w *Watch) Unpin(d *Dirent) {
	w.mu.Lock()
	defer w.mu.Unlock()
	if w.pins[d] {
		delete(w.pins, d)
		d.DecRef()
	}
}

// TargetDestroyed notifies the owner of the watch that the watch target is
// gone. The owner should release its own references to the watcher upon
// receiving this notification.
func (w *Watch) TargetDestroyed() {
	w.owner.targetDestroyed(w)
}

// destroy prepares the watch for destruction. It unpins all dirents pinned by
// this watch. Destroy does not cause any new events to be generated. The caller
// is responsible for ensuring there are no outstanding references to this
// watch.
func (w *Watch) destroy() {
	w.mu.Lock()
	defer w.mu.Unlock()
	for d := range w.pins {
		d.DecRef()
	}
	w.pins = nil
}