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 }