gvisor/pkg/sentry/fs/attr.go

423 lines
11 KiB
Go

// 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"
"syscall"
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/p9"
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
ktime "gvisor.googlesource.com/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"
}
}
// 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
}
// 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 (syscall.S_IXOTH, etc.) for these permissions
// in the "other" bits.
func (p PermMask) Mode() (mode os.FileMode) {
if p.Read {
mode |= syscall.S_IROTH
}
if p.Write {
mode |= syscall.S_IWOTH
}
if p.Execute {
mode |= syscall.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
}
// 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,
}