github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/fsutil/inode.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 fsutil 16 17 import ( 18 "github.com/SagerNet/gvisor/pkg/abi/linux" 19 "github.com/SagerNet/gvisor/pkg/context" 20 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 21 "github.com/SagerNet/gvisor/pkg/sentry/fs" 22 ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time" 23 "github.com/SagerNet/gvisor/pkg/sentry/memmap" 24 "github.com/SagerNet/gvisor/pkg/sentry/socket/unix/transport" 25 "github.com/SagerNet/gvisor/pkg/sync" 26 "github.com/SagerNet/gvisor/pkg/syserror" 27 "github.com/SagerNet/gvisor/pkg/waiter" 28 ) 29 30 // SimpleFileInode is a simple implementation of InodeOperations. 31 // 32 // +stateify savable 33 type SimpleFileInode struct { 34 InodeGenericChecker `state:"nosave"` 35 InodeNoExtendedAttributes `state:"nosave"` 36 InodeNoopRelease `state:"nosave"` 37 InodeNoopWriteOut `state:"nosave"` 38 InodeNotAllocatable `state:"nosave"` 39 InodeNotDirectory `state:"nosave"` 40 InodeNotMappable `state:"nosave"` 41 InodeNotOpenable `state:"nosave"` 42 InodeNotSocket `state:"nosave"` 43 InodeNotSymlink `state:"nosave"` 44 InodeNotTruncatable `state:"nosave"` 45 InodeNotVirtual `state:"nosave"` 46 47 InodeSimpleAttributes 48 } 49 50 // NewSimpleFileInode returns a new SimpleFileInode. 51 func NewSimpleFileInode(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions, typ uint64) *SimpleFileInode { 52 return &SimpleFileInode{ 53 InodeSimpleAttributes: NewInodeSimpleAttributes(ctx, owner, perms, typ), 54 } 55 } 56 57 // NoReadWriteFileInode is an implementation of InodeOperations that supports 58 // opening files that are not readable or writeable. 59 // 60 // +stateify savable 61 type NoReadWriteFileInode struct { 62 InodeGenericChecker `state:"nosave"` 63 InodeNoExtendedAttributes `state:"nosave"` 64 InodeNoopRelease `state:"nosave"` 65 InodeNoopWriteOut `state:"nosave"` 66 InodeNotAllocatable `state:"nosave"` 67 InodeNotDirectory `state:"nosave"` 68 InodeNotMappable `state:"nosave"` 69 InodeNotSocket `state:"nosave"` 70 InodeNotSymlink `state:"nosave"` 71 InodeNotTruncatable `state:"nosave"` 72 InodeNotVirtual `state:"nosave"` 73 74 InodeSimpleAttributes 75 } 76 77 // NewNoReadWriteFileInode returns a new NoReadWriteFileInode. 78 func NewNoReadWriteFileInode(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions, typ uint64) *NoReadWriteFileInode { 79 return &NoReadWriteFileInode{ 80 InodeSimpleAttributes: NewInodeSimpleAttributes(ctx, owner, perms, typ), 81 } 82 } 83 84 // GetFile implements fs.InodeOperations.GetFile. 85 func (*NoReadWriteFileInode) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 86 return fs.NewFile(ctx, dirent, flags, &NoReadWriteFile{}), nil 87 } 88 89 // InodeSimpleAttributes implements methods for updating in-memory unstable 90 // attributes. 91 // 92 // +stateify savable 93 type InodeSimpleAttributes struct { 94 // fsType is the immutable filesystem type that will be returned by 95 // StatFS. 96 fsType uint64 97 98 // mu protects unstable. 99 mu sync.RWMutex `state:"nosave"` 100 unstable fs.UnstableAttr 101 } 102 103 // NewInodeSimpleAttributes returns a new InodeSimpleAttributes with the given 104 // owner and permissions, and all timestamps set to the current time. 105 func NewInodeSimpleAttributes(ctx context.Context, owner fs.FileOwner, perms fs.FilePermissions, typ uint64) InodeSimpleAttributes { 106 return NewInodeSimpleAttributesWithUnstable(fs.WithCurrentTime(ctx, fs.UnstableAttr{ 107 Owner: owner, 108 Perms: perms, 109 }), typ) 110 } 111 112 // NewInodeSimpleAttributesWithUnstable returns a new InodeSimpleAttributes 113 // with the given unstable attributes. 114 func NewInodeSimpleAttributesWithUnstable(uattr fs.UnstableAttr, typ uint64) InodeSimpleAttributes { 115 return InodeSimpleAttributes{ 116 fsType: typ, 117 unstable: uattr, 118 } 119 } 120 121 // UnstableAttr implements fs.InodeOperations.UnstableAttr. 122 func (i *InodeSimpleAttributes) UnstableAttr(ctx context.Context, _ *fs.Inode) (fs.UnstableAttr, error) { 123 i.mu.RLock() 124 u := i.unstable 125 i.mu.RUnlock() 126 return u, nil 127 } 128 129 // SetPermissions implements fs.InodeOperations.SetPermissions. 130 func (i *InodeSimpleAttributes) SetPermissions(ctx context.Context, _ *fs.Inode, p fs.FilePermissions) bool { 131 i.mu.Lock() 132 i.unstable.SetPermissions(ctx, p) 133 i.mu.Unlock() 134 return true 135 } 136 137 // SetOwner implements fs.InodeOperations.SetOwner. 138 func (i *InodeSimpleAttributes) SetOwner(ctx context.Context, _ *fs.Inode, owner fs.FileOwner) error { 139 i.mu.Lock() 140 i.unstable.SetOwner(ctx, owner) 141 i.mu.Unlock() 142 return nil 143 } 144 145 // SetTimestamps implements fs.InodeOperations.SetTimestamps. 146 func (i *InodeSimpleAttributes) SetTimestamps(ctx context.Context, _ *fs.Inode, ts fs.TimeSpec) error { 147 i.mu.Lock() 148 i.unstable.SetTimestamps(ctx, ts) 149 i.mu.Unlock() 150 return nil 151 } 152 153 // AddLink implements fs.InodeOperations.AddLink. 154 func (i *InodeSimpleAttributes) AddLink() { 155 i.mu.Lock() 156 i.unstable.Links++ 157 i.mu.Unlock() 158 } 159 160 // DropLink implements fs.InodeOperations.DropLink. 161 func (i *InodeSimpleAttributes) DropLink() { 162 i.mu.Lock() 163 i.unstable.Links-- 164 i.mu.Unlock() 165 } 166 167 // StatFS implements fs.InodeOperations.StatFS. 168 func (i *InodeSimpleAttributes) StatFS(context.Context) (fs.Info, error) { 169 if i.fsType == 0 { 170 return fs.Info{}, syserror.ENOSYS 171 } 172 return fs.Info{Type: i.fsType}, nil 173 } 174 175 // NotifyAccess updates the access time. 176 func (i *InodeSimpleAttributes) NotifyAccess(ctx context.Context) { 177 i.mu.Lock() 178 i.unstable.AccessTime = ktime.NowFromContext(ctx) 179 i.mu.Unlock() 180 } 181 182 // NotifyModification updates the modification time. 183 func (i *InodeSimpleAttributes) NotifyModification(ctx context.Context) { 184 i.mu.Lock() 185 i.unstable.ModificationTime = ktime.NowFromContext(ctx) 186 i.mu.Unlock() 187 } 188 189 // NotifyStatusChange updates the status change time. 190 func (i *InodeSimpleAttributes) NotifyStatusChange(ctx context.Context) { 191 i.mu.Lock() 192 i.unstable.StatusChangeTime = ktime.NowFromContext(ctx) 193 i.mu.Unlock() 194 } 195 196 // NotifyModificationAndStatusChange updates the modification and status change 197 // times. 198 func (i *InodeSimpleAttributes) NotifyModificationAndStatusChange(ctx context.Context) { 199 i.mu.Lock() 200 now := ktime.NowFromContext(ctx) 201 i.unstable.ModificationTime = now 202 i.unstable.StatusChangeTime = now 203 i.mu.Unlock() 204 } 205 206 // InodeSimpleExtendedAttributes implements 207 // fs.InodeOperations.{Get,Set,List}Xattr. 208 // 209 // +stateify savable 210 type InodeSimpleExtendedAttributes struct { 211 // mu protects xattrs. 212 mu sync.RWMutex `state:"nosave"` 213 xattrs map[string]string 214 } 215 216 // GetXattr implements fs.InodeOperations.GetXattr. 217 func (i *InodeSimpleExtendedAttributes) GetXattr(_ context.Context, _ *fs.Inode, name string, _ uint64) (string, error) { 218 i.mu.RLock() 219 value, ok := i.xattrs[name] 220 i.mu.RUnlock() 221 if !ok { 222 return "", linuxerr.ENOATTR 223 } 224 return value, nil 225 } 226 227 // SetXattr implements fs.InodeOperations.SetXattr. 228 func (i *InodeSimpleExtendedAttributes) SetXattr(_ context.Context, _ *fs.Inode, name, value string, flags uint32) error { 229 i.mu.Lock() 230 defer i.mu.Unlock() 231 if i.xattrs == nil { 232 if flags&linux.XATTR_REPLACE != 0 { 233 return linuxerr.ENODATA 234 } 235 i.xattrs = make(map[string]string) 236 } 237 238 _, ok := i.xattrs[name] 239 if ok && flags&linux.XATTR_CREATE != 0 { 240 return syserror.EEXIST 241 } 242 if !ok && flags&linux.XATTR_REPLACE != 0 { 243 return linuxerr.ENODATA 244 } 245 246 i.xattrs[name] = value 247 return nil 248 } 249 250 // ListXattr implements fs.InodeOperations.ListXattr. 251 func (i *InodeSimpleExtendedAttributes) ListXattr(context.Context, *fs.Inode, uint64) (map[string]struct{}, error) { 252 i.mu.RLock() 253 names := make(map[string]struct{}, len(i.xattrs)) 254 for name := range i.xattrs { 255 names[name] = struct{}{} 256 } 257 i.mu.RUnlock() 258 return names, nil 259 } 260 261 // RemoveXattr implements fs.InodeOperations.RemoveXattr. 262 func (i *InodeSimpleExtendedAttributes) RemoveXattr(_ context.Context, _ *fs.Inode, name string) error { 263 i.mu.Lock() 264 defer i.mu.Unlock() 265 if _, ok := i.xattrs[name]; ok { 266 delete(i.xattrs, name) 267 return nil 268 } 269 return linuxerr.ENOATTR 270 } 271 272 // staticFile is a file with static contents. It is returned by 273 // InodeStaticFileGetter.GetFile. 274 // 275 // +stateify savable 276 type staticFile struct { 277 FileGenericSeek `state:"nosave"` 278 FileNoIoctl `state:"nosave"` 279 FileNoMMap `state:"nosave"` 280 FileNoSplice `state:"nosave"` 281 FileNoopFsync `state:"nosave"` 282 FileNoopFlush `state:"nosave"` 283 FileNoopRelease `state:"nosave"` 284 FileNoopWrite `state:"nosave"` 285 FileNotDirReaddir `state:"nosave"` 286 FileUseInodeUnstableAttr `state:"nosave"` 287 waiter.AlwaysReady `state:"nosave"` 288 289 FileStaticContentReader 290 } 291 292 // InodeNoStatFS implement StatFS by retuning ENOSYS. 293 type InodeNoStatFS struct{} 294 295 // StatFS implements fs.InodeOperations.StatFS. 296 func (InodeNoStatFS) StatFS(context.Context) (fs.Info, error) { 297 return fs.Info{}, syserror.ENOSYS 298 } 299 300 // InodeStaticFileGetter implements GetFile for a file with static contents. 301 // 302 // +stateify savable 303 type InodeStaticFileGetter struct { 304 Contents []byte 305 } 306 307 // GetFile implements fs.InodeOperations.GetFile. 308 func (i *InodeStaticFileGetter) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 309 return fs.NewFile(ctx, dirent, flags, &staticFile{ 310 FileStaticContentReader: NewFileStaticContentReader(i.Contents), 311 }), nil 312 } 313 314 // InodeNotMappable returns a nil memmap.Mappable. 315 type InodeNotMappable struct{} 316 317 // Mappable implements fs.InodeOperations.Mappable. 318 func (InodeNotMappable) Mappable(*fs.Inode) memmap.Mappable { 319 return nil 320 } 321 322 // InodeNoopWriteOut is a no-op implementation of fs.InodeOperations.WriteOut. 323 type InodeNoopWriteOut struct{} 324 325 // WriteOut is a no-op. 326 func (InodeNoopWriteOut) WriteOut(context.Context, *fs.Inode) error { 327 return nil 328 } 329 330 // InodeNotDirectory can be used by Inodes that are not directories. 331 type InodeNotDirectory struct{} 332 333 // Lookup implements fs.InodeOperations.Lookup. 334 func (InodeNotDirectory) Lookup(context.Context, *fs.Inode, string) (*fs.Dirent, error) { 335 return nil, syserror.ENOTDIR 336 } 337 338 // Create implements fs.InodeOperations.Create. 339 func (InodeNotDirectory) Create(context.Context, *fs.Inode, string, fs.FileFlags, fs.FilePermissions) (*fs.File, error) { 340 return nil, syserror.ENOTDIR 341 } 342 343 // CreateLink implements fs.InodeOperations.CreateLink. 344 func (InodeNotDirectory) CreateLink(context.Context, *fs.Inode, string, string) error { 345 return syserror.ENOTDIR 346 } 347 348 // CreateHardLink implements fs.InodeOperations.CreateHardLink. 349 func (InodeNotDirectory) CreateHardLink(context.Context, *fs.Inode, *fs.Inode, string) error { 350 return syserror.ENOTDIR 351 } 352 353 // CreateDirectory implements fs.InodeOperations.CreateDirectory. 354 func (InodeNotDirectory) CreateDirectory(context.Context, *fs.Inode, string, fs.FilePermissions) error { 355 return syserror.ENOTDIR 356 } 357 358 // Bind implements fs.InodeOperations.Bind. 359 func (InodeNotDirectory) Bind(context.Context, *fs.Inode, string, transport.BoundEndpoint, fs.FilePermissions) (*fs.Dirent, error) { 360 return nil, syserror.ENOTDIR 361 } 362 363 // CreateFifo implements fs.InodeOperations.CreateFifo. 364 func (InodeNotDirectory) CreateFifo(context.Context, *fs.Inode, string, fs.FilePermissions) error { 365 return syserror.ENOTDIR 366 } 367 368 // Remove implements fs.InodeOperations.Remove. 369 func (InodeNotDirectory) Remove(context.Context, *fs.Inode, string) error { 370 return syserror.ENOTDIR 371 } 372 373 // RemoveDirectory implements fs.InodeOperations.RemoveDirectory. 374 func (InodeNotDirectory) RemoveDirectory(context.Context, *fs.Inode, string) error { 375 return syserror.ENOTDIR 376 } 377 378 // Rename implements fs.FileOperations.Rename. 379 func (InodeNotDirectory) Rename(context.Context, *fs.Inode, *fs.Inode, string, *fs.Inode, string, bool) error { 380 return linuxerr.EINVAL 381 } 382 383 // InodeNotSocket can be used by Inodes that are not sockets. 384 type InodeNotSocket struct{} 385 386 // BoundEndpoint implements fs.InodeOperations.BoundEndpoint. 387 func (InodeNotSocket) BoundEndpoint(*fs.Inode, string) transport.BoundEndpoint { 388 return nil 389 } 390 391 // InodeNotTruncatable can be used by Inodes that cannot be truncated. 392 type InodeNotTruncatable struct{} 393 394 // Truncate implements fs.InodeOperations.Truncate. 395 func (InodeNotTruncatable) Truncate(context.Context, *fs.Inode, int64) error { 396 return linuxerr.EINVAL 397 } 398 399 // InodeIsDirTruncate implements fs.InodeOperations.Truncate for directories. 400 type InodeIsDirTruncate struct{} 401 402 // Truncate implements fs.InodeOperations.Truncate. 403 func (InodeIsDirTruncate) Truncate(context.Context, *fs.Inode, int64) error { 404 return syserror.EISDIR 405 } 406 407 // InodeNoopTruncate implements fs.InodeOperations.Truncate as a noop. 408 type InodeNoopTruncate struct{} 409 410 // Truncate implements fs.InodeOperations.Truncate. 411 func (InodeNoopTruncate) Truncate(context.Context, *fs.Inode, int64) error { 412 return nil 413 } 414 415 // InodeNotRenameable can be used by Inodes that cannot be truncated. 416 type InodeNotRenameable struct{} 417 418 // Rename implements fs.InodeOperations.Rename. 419 func (InodeNotRenameable) Rename(context.Context, *fs.Inode, *fs.Inode, string, *fs.Inode, string, bool) error { 420 return linuxerr.EINVAL 421 } 422 423 // InodeNotOpenable can be used by Inodes that cannot be opened. 424 type InodeNotOpenable struct{} 425 426 // GetFile implements fs.InodeOperations.GetFile. 427 func (InodeNotOpenable) GetFile(context.Context, *fs.Dirent, fs.FileFlags) (*fs.File, error) { 428 return nil, syserror.EIO 429 } 430 431 // InodeNotVirtual can be used by Inodes that are not virtual. 432 type InodeNotVirtual struct{} 433 434 // IsVirtual implements fs.InodeOperations.IsVirtual. 435 func (InodeNotVirtual) IsVirtual() bool { 436 return false 437 } 438 439 // InodeVirtual can be used by Inodes that are virtual. 440 type InodeVirtual struct{} 441 442 // IsVirtual implements fs.InodeOperations.IsVirtual. 443 func (InodeVirtual) IsVirtual() bool { 444 return true 445 } 446 447 // InodeNotSymlink can be used by Inodes that are not symlinks. 448 type InodeNotSymlink struct{} 449 450 // Readlink implements fs.InodeOperations.Readlink. 451 func (InodeNotSymlink) Readlink(context.Context, *fs.Inode) (string, error) { 452 return "", linuxerr.ENOLINK 453 } 454 455 // Getlink implements fs.InodeOperations.Getlink. 456 func (InodeNotSymlink) Getlink(context.Context, *fs.Inode) (*fs.Dirent, error) { 457 return nil, linuxerr.ENOLINK 458 } 459 460 // InodeNoExtendedAttributes can be used by Inodes that do not support 461 // extended attributes. 462 type InodeNoExtendedAttributes struct{} 463 464 // GetXattr implements fs.InodeOperations.GetXattr. 465 func (InodeNoExtendedAttributes) GetXattr(context.Context, *fs.Inode, string, uint64) (string, error) { 466 return "", syserror.EOPNOTSUPP 467 } 468 469 // SetXattr implements fs.InodeOperations.SetXattr. 470 func (InodeNoExtendedAttributes) SetXattr(context.Context, *fs.Inode, string, string, uint32) error { 471 return syserror.EOPNOTSUPP 472 } 473 474 // ListXattr implements fs.InodeOperations.ListXattr. 475 func (InodeNoExtendedAttributes) ListXattr(context.Context, *fs.Inode, uint64) (map[string]struct{}, error) { 476 return nil, syserror.EOPNOTSUPP 477 } 478 479 // RemoveXattr implements fs.InodeOperations.RemoveXattr. 480 func (InodeNoExtendedAttributes) RemoveXattr(context.Context, *fs.Inode, string) error { 481 return syserror.EOPNOTSUPP 482 } 483 484 // InodeNoopRelease implements fs.InodeOperations.Release as a noop. 485 type InodeNoopRelease struct{} 486 487 // Release implements fs.InodeOperations.Release. 488 func (InodeNoopRelease) Release(context.Context) {} 489 490 // InodeGenericChecker implements fs.InodeOperations.Check with a generic 491 // implementation. 492 type InodeGenericChecker struct{} 493 494 // Check implements fs.InodeOperations.Check. 495 func (InodeGenericChecker) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool { 496 return fs.ContextCanAccessFile(ctx, inode, p) 497 } 498 499 // InodeDenyWriteChecker implements fs.InodeOperations.Check which denies all 500 // write operations. 501 type InodeDenyWriteChecker struct{} 502 503 // Check implements fs.InodeOperations.Check. 504 func (InodeDenyWriteChecker) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool { 505 if p.Write { 506 return false 507 } 508 return fs.ContextCanAccessFile(ctx, inode, p) 509 } 510 511 //InodeNotAllocatable can be used by Inodes that do not support Allocate(). 512 type InodeNotAllocatable struct{} 513 514 // Allocate implements fs.InodeOperations.Allocate. 515 func (InodeNotAllocatable) Allocate(_ context.Context, _ *fs.Inode, _, _ int64) error { 516 return syserror.EOPNOTSUPP 517 } 518 519 // InodeNoopAllocate implements fs.InodeOperations.Allocate as a noop. 520 type InodeNoopAllocate struct{} 521 522 // Allocate implements fs.InodeOperations.Allocate. 523 func (InodeNoopAllocate) Allocate(_ context.Context, _ *fs.Inode, _, _ int64) error { 524 return nil 525 } 526 527 // InodeIsDirAllocate implements fs.InodeOperations.Allocate for directories. 528 type InodeIsDirAllocate struct{} 529 530 // Allocate implements fs.InodeOperations.Allocate. 531 func (InodeIsDirAllocate) Allocate(_ context.Context, _ *fs.Inode, _, _ int64) error { 532 return syserror.EISDIR 533 }