github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/attr.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package fs
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  
    21  	"golang.org/x/sys/unix"
    22  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    23  	"github.com/SagerNet/gvisor/pkg/context"
    24  	"github.com/SagerNet/gvisor/pkg/p9"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    26  	ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time"
    27  )
    28  
    29  // InodeType enumerates types of Inodes.
    30  type InodeType int
    31  
    32  const (
    33  	// RegularFile is a regular file.
    34  	RegularFile InodeType = iota
    35  
    36  	// SpecialFile is a file that doesn't support SeekEnd. It is used for
    37  	// things like proc files.
    38  	SpecialFile
    39  
    40  	// Directory is a directory.
    41  	Directory
    42  
    43  	// SpecialDirectory is a directory that *does* support SeekEnd. It's
    44  	// the opposite of the SpecialFile scenario above. It similarly
    45  	// supports proc files.
    46  	SpecialDirectory
    47  
    48  	// Symlink is a symbolic link.
    49  	Symlink
    50  
    51  	// Pipe is a pipe (named or regular).
    52  	Pipe
    53  
    54  	// Socket is a socket.
    55  	Socket
    56  
    57  	// CharacterDevice is a character device.
    58  	CharacterDevice
    59  
    60  	// BlockDevice is a block device.
    61  	BlockDevice
    62  
    63  	// Anonymous is an anonymous type when none of the above apply.
    64  	// Epoll fds and event-driven fds fit this category.
    65  	Anonymous
    66  )
    67  
    68  // String returns a human-readable representation of the InodeType.
    69  func (n InodeType) String() string {
    70  	switch n {
    71  	case RegularFile, SpecialFile:
    72  		return "file"
    73  	case Directory, SpecialDirectory:
    74  		return "directory"
    75  	case Symlink:
    76  		return "symlink"
    77  	case Pipe:
    78  		return "pipe"
    79  	case Socket:
    80  		return "socket"
    81  	case CharacterDevice:
    82  		return "character-device"
    83  	case BlockDevice:
    84  		return "block-device"
    85  	case Anonymous:
    86  		return "anonymous"
    87  	default:
    88  		return "unknown"
    89  	}
    90  }
    91  
    92  // LinuxType returns the linux file type for this inode type.
    93  func (n InodeType) LinuxType() uint32 {
    94  	switch n {
    95  	case RegularFile, SpecialFile:
    96  		return linux.ModeRegular
    97  	case Directory, SpecialDirectory:
    98  		return linux.ModeDirectory
    99  	case Symlink:
   100  		return linux.ModeSymlink
   101  	case Pipe:
   102  		return linux.ModeNamedPipe
   103  	case CharacterDevice:
   104  		return linux.ModeCharacterDevice
   105  	case BlockDevice:
   106  		return linux.ModeBlockDevice
   107  	case Socket:
   108  		return linux.ModeSocket
   109  	default:
   110  		return 0
   111  	}
   112  }
   113  
   114  // ToDirentType converts an InodeType to a linux dirent type field.
   115  func ToDirentType(nodeType InodeType) uint8 {
   116  	switch nodeType {
   117  	case RegularFile, SpecialFile:
   118  		return linux.DT_REG
   119  	case Symlink:
   120  		return linux.DT_LNK
   121  	case Directory, SpecialDirectory:
   122  		return linux.DT_DIR
   123  	case Pipe:
   124  		return linux.DT_FIFO
   125  	case CharacterDevice:
   126  		return linux.DT_CHR
   127  	case BlockDevice:
   128  		return linux.DT_BLK
   129  	case Socket:
   130  		return linux.DT_SOCK
   131  	default:
   132  		return linux.DT_UNKNOWN
   133  	}
   134  }
   135  
   136  // ToInodeType coverts a linux file type to InodeType.
   137  func ToInodeType(linuxFileType linux.FileMode) InodeType {
   138  	switch linuxFileType {
   139  	case linux.ModeRegular:
   140  		return RegularFile
   141  	case linux.ModeDirectory:
   142  		return Directory
   143  	case linux.ModeSymlink:
   144  		return Symlink
   145  	case linux.ModeNamedPipe:
   146  		return Pipe
   147  	case linux.ModeCharacterDevice:
   148  		return CharacterDevice
   149  	case linux.ModeBlockDevice:
   150  		return BlockDevice
   151  	case linux.ModeSocket:
   152  		return Socket
   153  	default:
   154  		panic(fmt.Sprintf("unknown file mode: %d", linuxFileType))
   155  	}
   156  }
   157  
   158  // StableAttr contains Inode attributes that will be stable throughout the
   159  // lifetime of the Inode.
   160  //
   161  // +stateify savable
   162  type StableAttr struct {
   163  	// Type is the InodeType of a InodeOperations.
   164  	Type InodeType
   165  
   166  	// DeviceID is the device on which a InodeOperations resides.
   167  	DeviceID uint64
   168  
   169  	// InodeID uniquely identifies InodeOperations on its device.
   170  	InodeID uint64
   171  
   172  	// BlockSize is the block size of data backing this InodeOperations.
   173  	BlockSize int64
   174  
   175  	// DeviceFileMajor is the major device number of this Node, if it is a
   176  	// device file.
   177  	DeviceFileMajor uint16
   178  
   179  	// DeviceFileMinor is the minor device number of this Node, if it is a
   180  	// device file.
   181  	DeviceFileMinor uint32
   182  }
   183  
   184  // IsRegular returns true if StableAttr.Type matches a regular file.
   185  func IsRegular(s StableAttr) bool {
   186  	return s.Type == RegularFile
   187  }
   188  
   189  // IsFile returns true if StableAttr.Type matches any type of file.
   190  func IsFile(s StableAttr) bool {
   191  	return s.Type == RegularFile || s.Type == SpecialFile
   192  }
   193  
   194  // IsDir returns true if StableAttr.Type matches any type of directory.
   195  func IsDir(s StableAttr) bool {
   196  	return s.Type == Directory || s.Type == SpecialDirectory
   197  }
   198  
   199  // IsSymlink returns true if StableAttr.Type matches a symlink.
   200  func IsSymlink(s StableAttr) bool {
   201  	return s.Type == Symlink
   202  }
   203  
   204  // IsPipe returns true if StableAttr.Type matches any type of pipe.
   205  func IsPipe(s StableAttr) bool {
   206  	return s.Type == Pipe
   207  }
   208  
   209  // IsAnonymous returns true if StableAttr.Type matches any type of anonymous.
   210  func IsAnonymous(s StableAttr) bool {
   211  	return s.Type == Anonymous
   212  }
   213  
   214  // IsSocket returns true if StableAttr.Type matches any type of socket.
   215  func IsSocket(s StableAttr) bool {
   216  	return s.Type == Socket
   217  }
   218  
   219  // IsCharDevice returns true if StableAttr.Type matches a character device.
   220  func IsCharDevice(s StableAttr) bool {
   221  	return s.Type == CharacterDevice
   222  }
   223  
   224  // UnstableAttr contains Inode attributes that may change over the lifetime
   225  // of the Inode.
   226  //
   227  // +stateify savable
   228  type UnstableAttr struct {
   229  	// Size is the file size in bytes.
   230  	Size int64
   231  
   232  	// Usage is the actual data usage in bytes.
   233  	Usage int64
   234  
   235  	// Perms is the protection (read/write/execute for user/group/other).
   236  	Perms FilePermissions
   237  
   238  	// Owner describes the ownership of this file.
   239  	Owner FileOwner
   240  
   241  	// AccessTime is the time of last access
   242  	AccessTime ktime.Time
   243  
   244  	// ModificationTime is the time of last modification.
   245  	ModificationTime ktime.Time
   246  
   247  	// StatusChangeTime is the time of last attribute modification.
   248  	StatusChangeTime ktime.Time
   249  
   250  	// Links is the number of hard links.
   251  	Links uint64
   252  }
   253  
   254  // SetOwner sets the owner and group if they are valid.
   255  //
   256  // This method is NOT thread-safe. Callers must prevent concurrent calls.
   257  func (ua *UnstableAttr) SetOwner(ctx context.Context, owner FileOwner) {
   258  	if owner.UID.Ok() {
   259  		ua.Owner.UID = owner.UID
   260  	}
   261  	if owner.GID.Ok() {
   262  		ua.Owner.GID = owner.GID
   263  	}
   264  	ua.StatusChangeTime = ktime.NowFromContext(ctx)
   265  }
   266  
   267  // SetPermissions sets the permissions.
   268  //
   269  // This method is NOT thread-safe. Callers must prevent concurrent calls.
   270  func (ua *UnstableAttr) SetPermissions(ctx context.Context, p FilePermissions) {
   271  	ua.Perms = p
   272  	ua.StatusChangeTime = ktime.NowFromContext(ctx)
   273  }
   274  
   275  // SetTimestamps sets the timestamps according to the TimeSpec.
   276  //
   277  // This method is NOT thread-safe. Callers must prevent concurrent calls.
   278  func (ua *UnstableAttr) SetTimestamps(ctx context.Context, ts TimeSpec) {
   279  	if ts.ATimeOmit && ts.MTimeOmit {
   280  		return
   281  	}
   282  
   283  	now := ktime.NowFromContext(ctx)
   284  	if !ts.ATimeOmit {
   285  		if ts.ATimeSetSystemTime {
   286  			ua.AccessTime = now
   287  		} else {
   288  			ua.AccessTime = ts.ATime
   289  		}
   290  	}
   291  	if !ts.MTimeOmit {
   292  		if ts.MTimeSetSystemTime {
   293  			ua.ModificationTime = now
   294  		} else {
   295  			ua.ModificationTime = ts.MTime
   296  		}
   297  	}
   298  	ua.StatusChangeTime = now
   299  }
   300  
   301  // WithCurrentTime returns u with AccessTime == ModificationTime == current time.
   302  func WithCurrentTime(ctx context.Context, u UnstableAttr) UnstableAttr {
   303  	t := ktime.NowFromContext(ctx)
   304  	u.AccessTime = t
   305  	u.ModificationTime = t
   306  	u.StatusChangeTime = t
   307  	return u
   308  }
   309  
   310  // AttrMask contains fields to mask StableAttr and UnstableAttr.
   311  //
   312  // +stateify savable
   313  type AttrMask struct {
   314  	Type             bool
   315  	DeviceID         bool
   316  	InodeID          bool
   317  	BlockSize        bool
   318  	Size             bool
   319  	Usage            bool
   320  	Perms            bool
   321  	UID              bool
   322  	GID              bool
   323  	AccessTime       bool
   324  	ModificationTime bool
   325  	StatusChangeTime bool
   326  	Links            bool
   327  }
   328  
   329  // Empty returns true if all fields in AttrMask are false.
   330  func (a AttrMask) Empty() bool {
   331  	return a == AttrMask{}
   332  }
   333  
   334  // PermMask are file access permissions.
   335  //
   336  // +stateify savable
   337  type PermMask struct {
   338  	// Read indicates reading is permitted.
   339  	Read bool
   340  
   341  	// Write indicates writing is permitted.
   342  	Write bool
   343  
   344  	// Execute indicates execution is permitted.
   345  	Execute bool
   346  }
   347  
   348  // OnlyRead returns true when only the read bit is set.
   349  func (p PermMask) OnlyRead() bool {
   350  	return p.Read && !p.Write && !p.Execute
   351  }
   352  
   353  // String implements the fmt.Stringer interface for PermMask.
   354  func (p PermMask) String() string {
   355  	return fmt.Sprintf("PermMask{Read: %v, Write: %v, Execute: %v}", p.Read, p.Write, p.Execute)
   356  }
   357  
   358  // Mode returns the system mode (unix.S_IXOTH, etc.) for these permissions
   359  // in the "other" bits.
   360  func (p PermMask) Mode() (mode os.FileMode) {
   361  	if p.Read {
   362  		mode |= unix.S_IROTH
   363  	}
   364  	if p.Write {
   365  		mode |= unix.S_IWOTH
   366  	}
   367  	if p.Execute {
   368  		mode |= unix.S_IXOTH
   369  	}
   370  	return
   371  }
   372  
   373  // SupersetOf returns true iff the permissions in p are a superset of the
   374  // permissions in other.
   375  func (p PermMask) SupersetOf(other PermMask) bool {
   376  	if !p.Read && other.Read {
   377  		return false
   378  	}
   379  	if !p.Write && other.Write {
   380  		return false
   381  	}
   382  	if !p.Execute && other.Execute {
   383  		return false
   384  	}
   385  	return true
   386  }
   387  
   388  // FilePermissions represents the permissions of a file, with
   389  // Read/Write/Execute bits for user, group, and other.
   390  //
   391  // +stateify savable
   392  type FilePermissions struct {
   393  	User  PermMask
   394  	Group PermMask
   395  	Other PermMask
   396  
   397  	// Sticky, if set on directories, restricts renaming and deletion of
   398  	// files in those directories to the directory owner, file owner, or
   399  	// CAP_FOWNER. The sticky bit is ignored when set on other files.
   400  	Sticky bool
   401  
   402  	// SetUID executables can call UID-setting syscalls without CAP_SETUID.
   403  	SetUID bool
   404  
   405  	// SetGID executables can call GID-setting syscalls without CAP_SETGID.
   406  	SetGID bool
   407  }
   408  
   409  // PermsFromMode takes the Other permissions (last 3 bits) of a FileMode and
   410  // returns a set of PermMask.
   411  func PermsFromMode(mode linux.FileMode) (perms PermMask) {
   412  	perms.Read = mode&linux.ModeOtherRead != 0
   413  	perms.Write = mode&linux.ModeOtherWrite != 0
   414  	perms.Execute = mode&linux.ModeOtherExec != 0
   415  	return
   416  }
   417  
   418  // FilePermsFromP9 converts a p9.FileMode to a FilePermissions struct.
   419  func FilePermsFromP9(mode p9.FileMode) FilePermissions {
   420  	return FilePermsFromMode(linux.FileMode(mode))
   421  }
   422  
   423  // FilePermsFromMode converts a system file mode to a FilePermissions struct.
   424  func FilePermsFromMode(mode linux.FileMode) (fp FilePermissions) {
   425  	perm := mode.Permissions()
   426  	fp.Other = PermsFromMode(perm)
   427  	fp.Group = PermsFromMode(perm >> 3)
   428  	fp.User = PermsFromMode(perm >> 6)
   429  	fp.Sticky = mode&linux.ModeSticky == linux.ModeSticky
   430  	fp.SetUID = mode&linux.ModeSetUID == linux.ModeSetUID
   431  	fp.SetGID = mode&linux.ModeSetGID == linux.ModeSetGID
   432  	return
   433  }
   434  
   435  // LinuxMode returns the linux mode_t representation of these permissions.
   436  func (f FilePermissions) LinuxMode() linux.FileMode {
   437  	m := linux.FileMode(f.User.Mode()<<6 | f.Group.Mode()<<3 | f.Other.Mode())
   438  	if f.SetUID {
   439  		m |= linux.ModeSetUID
   440  	}
   441  	if f.SetGID {
   442  		m |= linux.ModeSetGID
   443  	}
   444  	if f.Sticky {
   445  		m |= linux.ModeSticky
   446  	}
   447  	return m
   448  }
   449  
   450  // OSMode returns the Go runtime's OS independent os.FileMode representation of
   451  // these permissions.
   452  func (f FilePermissions) OSMode() os.FileMode {
   453  	m := os.FileMode(f.User.Mode()<<6 | f.Group.Mode()<<3 | f.Other.Mode())
   454  	if f.SetUID {
   455  		m |= os.ModeSetuid
   456  	}
   457  	if f.SetGID {
   458  		m |= os.ModeSetgid
   459  	}
   460  	if f.Sticky {
   461  		m |= os.ModeSticky
   462  	}
   463  	return m
   464  }
   465  
   466  // AnyExecute returns true if any of U/G/O have the execute bit set.
   467  func (f FilePermissions) AnyExecute() bool {
   468  	return f.User.Execute || f.Group.Execute || f.Other.Execute
   469  }
   470  
   471  // AnyWrite returns true if any of U/G/O have the write bit set.
   472  func (f FilePermissions) AnyWrite() bool {
   473  	return f.User.Write || f.Group.Write || f.Other.Write
   474  }
   475  
   476  // AnyRead returns true if any of U/G/O have the read bit set.
   477  func (f FilePermissions) AnyRead() bool {
   478  	return f.User.Read || f.Group.Read || f.Other.Read
   479  }
   480  
   481  // HasSetUIDOrGID returns true if either the setuid or setgid bit is set.
   482  func (f FilePermissions) HasSetUIDOrGID() bool {
   483  	return f.SetUID || f.SetGID
   484  }
   485  
   486  // DropSetUIDAndMaybeGID turns off setuid, and turns off setgid if f allows
   487  // group execution.
   488  func (f *FilePermissions) DropSetUIDAndMaybeGID() {
   489  	f.SetUID = false
   490  	if f.Group.Execute {
   491  		f.SetGID = false
   492  	}
   493  }
   494  
   495  // FileOwner represents ownership of a file.
   496  //
   497  // +stateify savable
   498  type FileOwner struct {
   499  	UID auth.KUID
   500  	GID auth.KGID
   501  }
   502  
   503  // RootOwner corresponds to KUID/KGID 0/0.
   504  var RootOwner = FileOwner{
   505  	UID: auth.RootKUID,
   506  	GID: auth.RootKGID,
   507  }