summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/dentry.go
blob: 29fb155a471ad3756cec421906990fc8c58bbec3 (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// Copyright 2018 Google LLC
//
// 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 (
	"sort"

	"gvisor.googlesource.com/gvisor/pkg/sentry/device"
)

// DentAttr is the metadata of a directory entry. It is a subset of StableAttr.
//
// +stateify savable
type DentAttr struct {
	// Type is the InodeType of an Inode.
	Type InodeType

	// InodeID uniquely identifies an Inode on a device.
	InodeID uint64
}

// GenericDentAttr returns a generic DentAttr where:
//
// Type == nt
// InodeID == the inode id of a new inode on device.
func GenericDentAttr(nt InodeType, device *device.Device) DentAttr {
	return DentAttr{
		Type:    nt,
		InodeID: device.NextIno(),
	}
}

// DentrySerializer serializes a directory entry.
type DentrySerializer interface {
	// CopyOut serializes a directory entry based on its name and attributes.
	CopyOut(name string, attributes DentAttr) error

	// Written returns the number of bytes written.
	Written() int
}

// CollectEntriesSerializer copies DentAttrs to Entries. The order in
// which entries are encountered is preserved in Order.
type CollectEntriesSerializer struct {
	Entries map[string]DentAttr
	Order   []string
}

// CopyOut implements DentrySerializer.CopyOut.
func (c *CollectEntriesSerializer) CopyOut(name string, attr DentAttr) error {
	if c.Entries == nil {
		c.Entries = make(map[string]DentAttr)
	}
	c.Entries[name] = attr
	c.Order = append(c.Order, name)
	return nil
}

// Written implements DentrySerializer.Written.
func (c *CollectEntriesSerializer) Written() int {
	return len(c.Entries)
}

// DirCtx is used by node.Readdir to emit directory entries.  It is not
// thread-safe.
type DirCtx struct {
	// Serializer is used to serialize the node attributes.
	Serializer DentrySerializer

	// attrs are DentAttrs
	attrs map[string]DentAttr

	// DirCursor is the directory cursor.
	// TODO(b/67778717): Once Handles are removed this can just live in the
	// respective FileOperations implementations and not need to get
	// plumbed everywhere.
	DirCursor *string
}

// DirEmit is called for each directory entry.
func (c *DirCtx) DirEmit(name string, attr DentAttr) error {
	if c.Serializer != nil {
		if err := c.Serializer.CopyOut(name, attr); err != nil {
			return err
		}
	}
	if c.attrs == nil {
		c.attrs = make(map[string]DentAttr)
	}
	c.attrs[name] = attr
	return nil
}

// DentAttrs returns a map of DentAttrs corresponding to the emitted directory
// entries.
func (c *DirCtx) DentAttrs() map[string]DentAttr {
	if c.attrs == nil {
		c.attrs = make(map[string]DentAttr)
	}
	return c.attrs
}

// GenericReaddir serializes DentAttrs based on a SortedDentryMap that must
// contain _all_ up-to-date DentAttrs under a directory. If ctx.DirCursor is
// not nil, it is updated to the name of the last DentAttr that was
// successfully serialized.
//
// Returns the number of entries serialized.
func GenericReaddir(ctx *DirCtx, s *SortedDentryMap) (int, error) {
	// Retrieve the next directory entries.
	var names []string
	var entries map[string]DentAttr
	if ctx.DirCursor != nil {
		names, entries = s.GetNext(*ctx.DirCursor)
	} else {
		names, entries = s.GetAll()
	}

	// Try to serialize each entry.
	var serialized int
	for _, name := range names {
		// Skip "" per POSIX. Skip "." and ".." which will be added by Dirent.Readdir.
		if name == "" || name == "." || name == ".." {
			continue
		}

		// Emit the directory entry.
		if err := ctx.DirEmit(name, entries[name]); err != nil {
			// Return potentially a partial serialized count.
			return serialized, err
		}

		// We successfully serialized this entry.
		serialized++

		// Update the cursor with the name of the entry last serialized.
		if ctx.DirCursor != nil {
			*ctx.DirCursor = name
		}
	}

	// Everything was serialized.
	return serialized, nil
}

// SortedDentryMap is a sorted map of names and fs.DentAttr entries.
//
// +stateify savable
type SortedDentryMap struct {
	// names is always kept in sorted-order.
	names []string

	// entries maps names to fs.DentAttrs.
	entries map[string]DentAttr
}

// NewSortedDentryMap maintains entries in name sorted order.
func NewSortedDentryMap(entries map[string]DentAttr) *SortedDentryMap {
	s := &SortedDentryMap{
		names:   make([]string, 0, len(entries)),
		entries: entries,
	}
	// Don't allow s.entries to be nil, because nil maps arn't Saveable.
	if s.entries == nil {
		s.entries = make(map[string]DentAttr)
	}

	// Collect names from entries and sort them.
	for name := range s.entries {
		s.names = append(s.names, name)
	}
	sort.Strings(s.names)
	return s
}

// GetAll returns all names and entries in s. Callers should not modify the
// returned values.
func (s *SortedDentryMap) GetAll() ([]string, map[string]DentAttr) {
	return s.names, s.entries
}

// GetNext returns names after cursor in s and all entries.
func (s *SortedDentryMap) GetNext(cursor string) ([]string, map[string]DentAttr) {
	i := sort.SearchStrings(s.names, cursor)
	if i == len(s.names) {
		return nil, s.entries
	}

	// Return everything strictly after the cursor.
	if s.names[i] == cursor {
		i++
	}
	return s.names[i:], s.entries
}

// Add adds an entry with the given name to the map, preserving sort order.  If
// name already exists in the map, its entry will be overwritten.
func (s *SortedDentryMap) Add(name string, entry DentAttr) {
	if _, ok := s.entries[name]; !ok {
		// Map does not yet contain an entry with this name.  We must
		// insert it in s.names at the appropriate spot.
		i := sort.SearchStrings(s.names, name)
		s.names = append(s.names, "")
		copy(s.names[i+1:], s.names[i:])
		s.names[i] = name
	}
	s.entries[name] = entry
}

// Remove removes an entry with the given name from the map, preserving sort order.
func (s *SortedDentryMap) Remove(name string) {
	if _, ok := s.entries[name]; !ok {
		return
	}
	i := sort.SearchStrings(s.names, name)
	copy(s.names[i:], s.names[i+1:])
	s.names = s.names[:len(s.names)-1]
	delete(s.entries, name)
}

// Contains reports whether the map contains an entry with the given name.
func (s *SortedDentryMap) Contains(name string) bool {
	_, ok := s.entries[name]
	return ok
}