summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/attr.go
blob: 4c99944e708901616745f43a7e9f19128ee47e74 (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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
// Copyright 2018 The gVisor Authors.
//
// 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 (
	"fmt"
	"os"

	"golang.org/x/sys/unix"
	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/context"
	"gvisor.dev/gvisor/pkg/p9"
	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
	ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
)

// InodeType enumerates types of Inodes.
type InodeType int

const (
	// RegularFile is a regular file.
	RegularFile InodeType = iota

	// SpecialFile is a file that doesn't support SeekEnd. It is used for
	// things like proc files.
	SpecialFile

	// Directory is a directory.
	Directory

	// SpecialDirectory is a directory that *does* support SeekEnd. It's
	// the opposite of the SpecialFile scenario above. It similarly
	// supports proc files.
	SpecialDirectory

	// Symlink is a symbolic link.
	Symlink

	// Pipe is a pipe (named or regular).
	Pipe

	// Socket is a socket.
	Socket

	// CharacterDevice is a character device.
	CharacterDevice

	// BlockDevice is a block device.
	BlockDevice

	// Anonymous is an anonymous type when none of the above apply.
	// Epoll fds and event-driven fds fit this category.
	Anonymous
)

// String returns a human-readable representation of the InodeType.
func (n InodeType) String() string {
	switch n {
	case RegularFile, SpecialFile:
		return "file"
	case Directory, SpecialDirectory:
		return "directory"
	case Symlink:
		return "symlink"
	case Pipe:
		return "pipe"
	case Socket:
		return "socket"
	case CharacterDevice:
		return "character-device"
	case BlockDevice:
		return "block-device"
	case Anonymous:
		return "anonymous"
	default:
		return "unknown"
	}
}

// LinuxType returns the linux file type for this inode type.
func (n InodeType) LinuxType() uint32 {
	switch n {
	case RegularFile, SpecialFile:
		return linux.ModeRegular
	case Directory, SpecialDirectory:
		return linux.ModeDirectory
	case Symlink:
		return linux.ModeSymlink
	case Pipe:
		return linux.ModeNamedPipe
	case CharacterDevice:
		return linux.ModeCharacterDevice
	case BlockDevice:
		return linux.ModeBlockDevice
	case Socket:
		return linux.ModeSocket
	default:
		return 0
	}
}

// ToDirentType converts an InodeType to a linux dirent type field.
func ToDirentType(nodeType InodeType) uint8 {
	switch nodeType {
	case RegularFile, SpecialFile:
		return linux.DT_REG
	case Symlink:
		return linux.DT_LNK
	case Directory, SpecialDirectory:
		return linux.DT_DIR
	case Pipe:
		return linux.DT_FIFO
	case CharacterDevice:
		return linux.DT_CHR
	case BlockDevice:
		return linux.DT_BLK
	case Socket:
		return linux.DT_SOCK
	default:
		return linux.DT_UNKNOWN
	}
}

// ToInodeType coverts a linux file type to InodeType.
func ToInodeType(linuxFileType linux.FileMode) InodeType {
	switch linuxFileType {
	case linux.ModeRegular:
		return RegularFile
	case linux.ModeDirectory:
		return Directory
	case linux.ModeSymlink:
		return Symlink
	case linux.ModeNamedPipe:
		return Pipe
	case linux.ModeCharacterDevice:
		return CharacterDevice
	case linux.ModeBlockDevice:
		return BlockDevice
	case linux.ModeSocket:
		return Socket
	default:
		panic(fmt.Sprintf("unknown file mode: %d", linuxFileType))
	}
}

// StableAttr contains Inode attributes that will be stable throughout the
// lifetime of the Inode.
//
// +stateify savable
type StableAttr struct {
	// Type is the InodeType of a InodeOperations.
	Type InodeType

	// DeviceID is the device on which a InodeOperations resides.
	DeviceID uint64

	// InodeID uniquely identifies InodeOperations on its device.
	InodeID uint64

	// BlockSize is the block size of data backing this InodeOperations.
	BlockSize int64

	// DeviceFileMajor is the major device number of this Node, if it is a
	// device file.
	DeviceFileMajor uint16

	// DeviceFileMinor is the minor device number of this Node, if it is a
	// device file.
	DeviceFileMinor uint32
}

// IsRegular returns true if StableAttr.Type matches a regular file.
func IsRegular(s StableAttr) bool {
	return s.Type == RegularFile
}

// IsFile returns true if StableAttr.Type matches any type of file.
func IsFile(s StableAttr) bool {
	return s.Type == RegularFile || s.Type == SpecialFile
}

// IsDir returns true if StableAttr.Type matches any type of directory.
func IsDir(s StableAttr) bool {
	return s.Type == Directory || s.Type == SpecialDirectory
}

// IsSymlink returns true if StableAttr.Type matches a symlink.
func IsSymlink(s StableAttr) bool {
	return s.Type == Symlink
}

// IsPipe returns true if StableAttr.Type matches any type of pipe.
func IsPipe(s StableAttr) bool {
	return s.Type == Pipe
}

// IsAnonymous returns true if StableAttr.Type matches any type of anonymous.
func IsAnonymous(s StableAttr) bool {
	return s.Type == Anonymous
}

// IsSocket returns true if StableAttr.Type matches any type of socket.
func IsSocket(s StableAttr) bool {
	return s.Type == Socket
}

// IsCharDevice returns true if StableAttr.Type matches a character device.
func IsCharDevice(s StableAttr) bool {
	return s.Type == CharacterDevice
}

// UnstableAttr contains Inode attributes that may change over the lifetime
// of the Inode.
//
// +stateify savable
type UnstableAttr struct {
	// Size is the file size in bytes.
	Size int64

	// Usage is the actual data usage in bytes.
	Usage int64

	// Perms is the protection (read/write/execute for user/group/other).
	Perms FilePermissions

	// Owner describes the ownership of this file.
	Owner FileOwner

	// AccessTime is the time of last access
	AccessTime ktime.Time

	// ModificationTime is the time of last modification.
	ModificationTime ktime.Time

	// StatusChangeTime is the time of last attribute modification.
	StatusChangeTime ktime.Time

	// Links is the number of hard links.
	Links uint64
}

// SetOwner sets the owner and group if they are valid.
//
// This method is NOT thread-safe. Callers must prevent concurrent calls.
func (ua *UnstableAttr) SetOwner(ctx context.Context, owner FileOwner) {
	if owner.UID.Ok() {
		ua.Owner.UID = owner.UID
	}
	if owner.GID.Ok() {
		ua.Owner.GID = owner.GID
	}
	ua.StatusChangeTime = ktime.NowFromContext(ctx)
}

// SetPermissions sets the permissions.
//
// This method is NOT thread-safe. Callers must prevent concurrent calls.
func (ua *UnstableAttr) SetPermissions(ctx context.Context, p FilePermissions) {
	ua.Perms = p
	ua.StatusChangeTime = ktime.NowFromContext(ctx)
}

// SetTimestamps sets the timestamps according to the TimeSpec.
//
// This method is NOT thread-safe. Callers must prevent concurrent calls.
func (ua *UnstableAttr) SetTimestamps(ctx context.Context, ts TimeSpec) {
	if ts.ATimeOmit && ts.MTimeOmit {
		return
	}

	now := ktime.NowFromContext(ctx)
	if !ts.ATimeOmit {
		if ts.ATimeSetSystemTime {
			ua.AccessTime = now
		} else {
			ua.AccessTime = ts.ATime
		}
	}
	if !ts.MTimeOmit {
		if ts.MTimeSetSystemTime {
			ua.ModificationTime = now
		} else {
			ua.ModificationTime = ts.MTime
		}
	}
	ua.StatusChangeTime = now
}

// WithCurrentTime returns u with AccessTime == ModificationTime == current time.
func WithCurrentTime(ctx context.Context, u UnstableAttr) UnstableAttr {
	t := ktime.NowFromContext(ctx)
	u.AccessTime = t
	u.ModificationTime = t
	u.StatusChangeTime = t
	return u
}

// AttrMask contains fields to mask StableAttr and UnstableAttr.
//
// +stateify savable
type AttrMask struct {
	Type             bool
	DeviceID         bool
	InodeID          bool
	BlockSize        bool
	Size             bool
	Usage            bool
	Perms            bool
	UID              bool
	GID              bool
	AccessTime       bool
	ModificationTime bool
	StatusChangeTime bool
	Links            bool
}

// Empty returns true if all fields in AttrMask are false.
func (a AttrMask) Empty() bool {
	return a == AttrMask{}
}

// PermMask are file access permissions.
//
// +stateify savable
type PermMask struct {
	// Read indicates reading is permitted.
	Read bool

	// Write indicates writing is permitted.
	Write bool

	// Execute indicates execution is permitted.
	Execute bool
}

// OnlyRead returns true when only the read bit is set.
func (p PermMask) OnlyRead() bool {
	return p.Read && !p.Write && !p.Execute
}

// String implements the fmt.Stringer interface for PermMask.
func (p PermMask) String() string {
	return fmt.Sprintf("PermMask{Read: %v, Write: %v, Execute: %v}", p.Read, p.Write, p.Execute)
}

// Mode returns the system mode (unix.S_IXOTH, etc.) for these permissions
// in the "other" bits.
func (p PermMask) Mode() (mode os.FileMode) {
	if p.Read {
		mode |= unix.S_IROTH
	}
	if p.Write {
		mode |= unix.S_IWOTH
	}
	if p.Execute {
		mode |= unix.S_IXOTH
	}
	return
}

// SupersetOf returns true iff the permissions in p are a superset of the
// permissions in other.
func (p PermMask) SupersetOf(other PermMask) bool {
	if !p.Read && other.Read {
		return false
	}
	if !p.Write && other.Write {
		return false
	}
	if !p.Execute && other.Execute {
		return false
	}
	return true
}

// FilePermissions represents the permissions of a file, with
// Read/Write/Execute bits for user, group, and other.
//
// +stateify savable
type FilePermissions struct {
	User  PermMask
	Group PermMask
	Other PermMask

	// Sticky, if set on directories, restricts renaming and deletion of
	// files in those directories to the directory owner, file owner, or
	// CAP_FOWNER. The sticky bit is ignored when set on other files.
	Sticky bool

	// SetUID executables can call UID-setting syscalls without CAP_SETUID.
	SetUID bool

	// SetGID executables can call GID-setting syscalls without CAP_SETGID.
	SetGID bool
}

// PermsFromMode takes the Other permissions (last 3 bits) of a FileMode and
// returns a set of PermMask.
func PermsFromMode(mode linux.FileMode) (perms PermMask) {
	perms.Read = mode&linux.ModeOtherRead != 0
	perms.Write = mode&linux.ModeOtherWrite != 0
	perms.Execute = mode&linux.ModeOtherExec != 0
	return
}

// FilePermsFromP9 converts a p9.FileMode to a FilePermissions struct.
func FilePermsFromP9(mode p9.FileMode) FilePermissions {
	return FilePermsFromMode(linux.FileMode(mode))
}

// FilePermsFromMode converts a system file mode to a FilePermissions struct.
func FilePermsFromMode(mode linux.FileMode) (fp FilePermissions) {
	perm := mode.Permissions()
	fp.Other = PermsFromMode(perm)
	fp.Group = PermsFromMode(perm >> 3)
	fp.User = PermsFromMode(perm >> 6)
	fp.Sticky = mode&linux.ModeSticky == linux.ModeSticky
	fp.SetUID = mode&linux.ModeSetUID == linux.ModeSetUID
	fp.SetGID = mode&linux.ModeSetGID == linux.ModeSetGID
	return
}

// LinuxMode returns the linux mode_t representation of these permissions.
func (f FilePermissions) LinuxMode() linux.FileMode {
	m := linux.FileMode(f.User.Mode()<<6 | f.Group.Mode()<<3 | f.Other.Mode())
	if f.SetUID {
		m |= linux.ModeSetUID
	}
	if f.SetGID {
		m |= linux.ModeSetGID
	}
	if f.Sticky {
		m |= linux.ModeSticky
	}
	return m
}

// OSMode returns the Go runtime's OS independent os.FileMode representation of
// these permissions.
func (f FilePermissions) OSMode() os.FileMode {
	m := os.FileMode(f.User.Mode()<<6 | f.Group.Mode()<<3 | f.Other.Mode())
	if f.SetUID {
		m |= os.ModeSetuid
	}
	if f.SetGID {
		m |= os.ModeSetgid
	}
	if f.Sticky {
		m |= os.ModeSticky
	}
	return m
}

// AnyExecute returns true if any of U/G/O have the execute bit set.
func (f FilePermissions) AnyExecute() bool {
	return f.User.Execute || f.Group.Execute || f.Other.Execute
}

// AnyWrite returns true if any of U/G/O have the write bit set.
func (f FilePermissions) AnyWrite() bool {
	return f.User.Write || f.Group.Write || f.Other.Write
}

// AnyRead returns true if any of U/G/O have the read bit set.
func (f FilePermissions) AnyRead() bool {
	return f.User.Read || f.Group.Read || f.Other.Read
}

// HasSetUIDOrGID returns true if either the setuid or setgid bit is set.
func (f FilePermissions) HasSetUIDOrGID() bool {
	return f.SetUID || f.SetGID
}

// DropSetUIDAndMaybeGID turns off setuid, and turns off setgid if f allows
// group execution.
func (f *FilePermissions) DropSetUIDAndMaybeGID() {
	f.SetUID = false
	if f.Group.Execute {
		f.SetGID = false
	}
}

// FileOwner represents ownership of a file.
//
// +stateify savable
type FileOwner struct {
	UID auth.KUID
	GID auth.KGID
}

// RootOwner corresponds to KUID/KGID 0/0.
var RootOwner = FileOwner{
	UID: auth.RootKUID,
	GID: auth.RootKGID,
}