diff options
Diffstat (limited to 'pkg/sentry/fs/attr.go')
-rw-r--r-- | pkg/sentry/fs/attr.go | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/pkg/sentry/fs/attr.go b/pkg/sentry/fs/attr.go new file mode 100644 index 000000000..591e35e6a --- /dev/null +++ b/pkg/sentry/fs/attr.go @@ -0,0 +1,422 @@ +// 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, +} |