github.com/avfs/avfs@v0.33.1-0.20240303173310-c6ba67c33eb7/vfs/orefafs/orefafs_file.go (about) 1 // 2 // Copyright 2020 The AVFS authors 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 package orefafs 18 19 import ( 20 "io" 21 "io/fs" 22 "time" 23 24 "github.com/avfs/avfs" 25 ) 26 27 // Chdir changes the current working directory to the file, 28 // which must be a directory. 29 // If there is an error, it will be of type *PathError. 30 func (f *OrefaFile) Chdir() error { 31 const op = "chdir" 32 33 if f == nil { 34 return fs.ErrInvalid 35 } 36 37 f.mu.RLock() 38 defer f.mu.RUnlock() 39 40 if f.name == "" { 41 return fs.ErrInvalid 42 } 43 44 if f.nd == nil { 45 return &fs.PathError{Op: op, Path: f.name, Err: fs.ErrClosed} 46 } 47 48 if !f.nd.mode.IsDir() { 49 err := error(avfs.ErrNotADirectory) 50 if f.vfs.OSType() == avfs.OsWindows { 51 err = avfs.ErrWinDirNameInvalid 52 } 53 54 return &fs.PathError{Op: op, Path: f.name, Err: err} 55 } 56 57 _ = f.vfs.SetCurDir(f.name) 58 59 return nil 60 } 61 62 // Chmod changes the mode of the file to mode. 63 // If there is an error, it will be of type *PathError. 64 func (f *OrefaFile) Chmod(mode fs.FileMode) error { 65 const op = "chmod" 66 67 if f == nil { 68 return fs.ErrInvalid 69 } 70 71 f.mu.Lock() 72 defer f.mu.Unlock() 73 74 if f.name == "" { 75 return fs.ErrInvalid 76 } 77 78 if f.nd == nil { 79 return &fs.PathError{Op: op, Path: f.name, Err: fs.ErrClosed} 80 } 81 82 f.nd.setMode(mode) 83 84 return nil 85 } 86 87 // Chown changes the numeric uid and gid of the named file. 88 // If there is an error, it will be of type *PathError. 89 // 90 // On Windows, it always returns the syscall.EWINDOWS error, wrapped 91 // in *PathError. 92 func (f *OrefaFile) Chown(uid, gid int) error { 93 const op = "chown" 94 95 if f == nil { 96 return fs.ErrInvalid 97 } 98 99 f.mu.Lock() 100 defer f.mu.Unlock() 101 102 if f.name == "" { 103 return fs.ErrInvalid 104 } 105 106 if f.nd == nil { 107 return &fs.PathError{Op: op, Path: f.name, Err: fs.ErrClosed} 108 } 109 110 if f.vfs.OSType() == avfs.OsWindows { 111 return &fs.PathError{Op: op, Path: f.name, Err: avfs.ErrWinNotSupported} 112 } 113 114 f.nd.setOwner(uid, gid) 115 116 return nil 117 } 118 119 // Close closes the File, rendering it unusable for I/O. 120 // On files that support SetDeadline, any pending I/O operations will 121 // be canceled and return immediately with an error. 122 func (f *OrefaFile) Close() error { 123 const op = "close" 124 125 if f == nil { 126 return fs.ErrInvalid 127 } 128 129 f.mu.Lock() 130 defer f.mu.Unlock() 131 132 if f.nd == nil { 133 if f.name == "" { 134 return fs.ErrInvalid 135 } 136 137 return &fs.PathError{Op: op, Path: f.name, Err: fs.ErrClosed} 138 } 139 140 f.dirEntries = nil 141 f.dirNames = nil 142 f.nd = nil 143 144 return nil 145 } 146 147 // Fd returns the integer Unix file descriptor referencing the open file. 148 // The file descriptor is valid only until f.Close is called or f is garbage collected. 149 // On Unix systems this will cause the SetDeadline methods to stop working. 150 func (f *OrefaFile) Fd() uintptr { 151 return ^(uintptr(0)) 152 } 153 154 // Name returns the link of the file as presented to Open. 155 func (f *OrefaFile) Name() string { 156 return f.name 157 } 158 159 // Read reads up to len(b) bytes from the OrefaFile. 160 // It returns the number of bytes read and any error encountered. 161 // At end of file, Read returns 0, io.EOF. 162 func (f *OrefaFile) Read(b []byte) (n int, err error) { 163 const op = "read" 164 165 if f == nil { 166 return 0, fs.ErrInvalid 167 } 168 169 f.mu.RLock() 170 defer f.mu.RUnlock() 171 172 if f.name == "" { 173 return 0, fs.ErrInvalid 174 } 175 176 if f.nd == nil { 177 return 0, &fs.PathError{Op: op, Path: f.name, Err: fs.ErrClosed} 178 } 179 180 nd := f.nd 181 if nd.mode.IsDir() { 182 err = avfs.ErrIsADirectory 183 if f.vfs.OSType() == avfs.OsWindows { 184 err = avfs.ErrWinIncorrectFunc 185 } 186 187 return 0, &fs.PathError{Op: op, Path: f.name, Err: err} 188 } 189 190 if f.openMode&avfs.OpenRead == 0 { 191 return 0, &fs.PathError{Op: op, Path: f.name, Err: f.vfs.err.BadFileDesc} 192 } 193 194 nd.mu.RLock() 195 n = copy(b, nd.data[f.at:]) 196 nd.mu.RUnlock() 197 198 f.at += int64(n) 199 200 if n == 0 { 201 return 0, io.EOF 202 } 203 204 return n, nil 205 } 206 207 // ReadAt reads len(b) bytes from the File starting at byte offset off. 208 // It returns the number of bytes read and the error, if any. 209 // ReadAt always returns a non-nil error when n < len(b). 210 // At end of file, that error is io.EOF. 211 func (f *OrefaFile) ReadAt(b []byte, off int64) (n int, err error) { 212 const op = "read" 213 214 if f == nil { 215 return 0, fs.ErrInvalid 216 } 217 218 f.mu.RLock() 219 defer f.mu.RUnlock() 220 221 if f.name == "" { 222 return 0, fs.ErrInvalid 223 } 224 225 if f.nd == nil { 226 return 0, &fs.PathError{Op: op, Path: f.name, Err: fs.ErrClosed} 227 } 228 229 nd := f.nd 230 if nd.mode.IsDir() { 231 err = avfs.ErrIsADirectory 232 if f.vfs.OSType() == avfs.OsWindows { 233 err = avfs.ErrWinIncorrectFunc 234 } 235 236 return 0, &fs.PathError{Op: op, Path: f.name, Err: err} 237 } 238 239 if off < 0 { 240 return 0, &fs.PathError{Op: "readat", Path: f.name, Err: avfs.ErrNegativeOffset} 241 } 242 243 if f.openMode&avfs.OpenRead == 0 { 244 return 0, &fs.PathError{Op: op, Path: f.name, Err: f.vfs.err.BadFileDesc} 245 } 246 247 nd.mu.RLock() 248 defer nd.mu.RUnlock() 249 250 if int(off) > len(nd.data) { 251 return 0, io.EOF 252 } 253 254 n = copy(b, nd.data[off:]) 255 if n < len(b) { 256 return n, io.EOF 257 } 258 259 return n, nil 260 } 261 262 // ReadDir reads the contents of the directory associated with the file f 263 // and returns a slice of DirEntry values in directory order. 264 // Subsequent calls on the same file will yield later DirEntry records in the directory. 265 // 266 // If n > 0, ReadDir returns at most n DirEntry records. 267 // In this case, if ReadDir returns an empty slice, it will return an error explaining why. 268 // At the end of a directory, the error is io.EOF. 269 // 270 // If n <= 0, ReadDir returns all the DirEntry records remaining in the directory. 271 // When it succeeds, it returns a nil error (not io.EOF). 272 func (f *OrefaFile) ReadDir(n int) ([]fs.DirEntry, error) { 273 if f == nil { 274 return nil, fs.ErrInvalid 275 } 276 277 f.mu.RLock() 278 defer f.mu.RUnlock() 279 280 if f.name == "" { 281 return nil, fs.ErrInvalid 282 } 283 284 op := "readdirent" 285 if f.vfs.OSType() == avfs.OsWindows { 286 op = "readdir" 287 } 288 289 if f.nd == nil { 290 err := error(avfs.ErrFileClosing) 291 if f.vfs.OSType() == avfs.OsWindows { 292 err = avfs.ErrWinInvalidHandle 293 } 294 295 return nil, &fs.PathError{Op: op, Path: f.name, Err: err} 296 } 297 298 nd := f.nd 299 if !nd.mode.IsDir() { 300 return nil, &fs.PathError{Op: op, Path: f.name, Err: f.vfs.err.NotADirectory} 301 } 302 303 if n <= 0 || f.dirEntries == nil { 304 nd.mu.RLock() 305 de := nd.dirEntries() 306 nd.mu.RUnlock() 307 308 f.dirIndex = 0 309 310 if n <= 0 { 311 f.dirEntries = nil 312 313 return de, nil 314 } 315 316 f.dirEntries = de 317 } 318 319 start := f.dirIndex 320 if start >= len(f.dirEntries) { 321 f.dirIndex = 0 322 f.dirEntries = nil 323 324 return nil, io.EOF 325 } 326 327 end := start + n 328 if end > len(f.dirEntries) { 329 end = len(f.dirEntries) 330 } 331 332 f.dirIndex = end 333 334 return f.dirEntries[start:end], nil 335 } 336 337 // Readdirnames reads and returns a slice of names from the directory f. 338 // 339 // If n > 0, Readdirnames returns at most n names. In this case, if 340 // Readdirnames returns an empty slice, it will return a non-nil error 341 // explaining why. At the end of a directory, the error is io.EOF. 342 // 343 // If n <= 0, Readdirnames returns all the names from the directory in 344 // a single slice. In this case, if Readdirnames succeeds (reads all 345 // the way to the end of the directory), it returns the slice and a 346 // nil error. If it encounters an error before the end of the 347 // directory, Readdirnames returns the names read until that point and 348 // a non-nil error. 349 func (f *OrefaFile) Readdirnames(n int) (names []string, err error) { 350 if f == nil { 351 return nil, fs.ErrInvalid 352 } 353 354 f.mu.RLock() 355 defer f.mu.RUnlock() 356 357 if f.name == "" { 358 return nil, fs.ErrInvalid 359 } 360 361 op := "readdirent" 362 if f.vfs.OSType() == avfs.OsWindows { 363 op = "readdir" 364 } 365 366 if f.nd == nil { 367 err = avfs.ErrFileClosing 368 if f.vfs.OSType() == avfs.OsWindows { 369 err = avfs.ErrWinInvalidHandle 370 } 371 372 return nil, &fs.PathError{Op: op, Path: f.name, Err: err} 373 } 374 375 nd := f.nd 376 if !nd.mode.IsDir() { 377 return nil, &fs.PathError{Op: op, Path: f.name, Err: f.vfs.err.NotADirectory} 378 } 379 380 if n <= 0 || f.dirNames == nil { 381 nd.mu.RLock() 382 names = nd.dirNames() 383 nd.mu.RUnlock() 384 385 f.dirIndex = 0 386 387 if n <= 0 { 388 f.dirNames = nil 389 390 return names, nil 391 } 392 393 f.dirNames = names 394 } 395 396 start := f.dirIndex 397 if start >= len(f.dirNames) { 398 f.dirIndex = 0 399 f.dirNames = nil 400 401 return nil, io.EOF 402 } 403 404 end := start + n 405 if end > len(f.dirNames) { 406 end = len(f.dirNames) 407 } 408 409 f.dirIndex = end 410 411 return f.dirNames[start:end], nil 412 } 413 414 // Seek sets the offset for the next Read or Write on file to offset, interpreted 415 // according to whence: 0 means relative to the origin of the file, 1 means 416 // relative to the current offset, and 2 means relative to the end. 417 // It returns the new offset and an error, if any. 418 // The behavior of Seek on a file opened with O_APPEND is not specified. 419 func (f *OrefaFile) Seek(offset int64, whence int) (ret int64, err error) { 420 const op = "seek" 421 422 if f == nil { 423 return 0, fs.ErrInvalid 424 } 425 426 f.mu.Lock() 427 defer f.mu.Unlock() 428 429 if f.name == "" { 430 return 0, fs.ErrInvalid 431 } 432 433 if f.nd == nil { 434 return 0, &fs.PathError{Op: op, Path: f.name, Err: fs.ErrClosed} 435 } 436 437 nd := f.nd 438 if nd.mode.IsDir() { 439 return 0, nil 440 } 441 442 nd.mu.RLock() 443 size := int64(len(nd.data)) 444 nd.mu.RUnlock() 445 446 switch whence { 447 case io.SeekStart: 448 if offset < 0 { 449 return 0, &fs.PathError{Op: op, Path: f.name, Err: f.vfs.err.InvalidArgument} 450 } 451 452 f.at = offset 453 case io.SeekCurrent: 454 if f.at+offset < 0 { 455 return 0, &fs.PathError{Op: op, Path: f.name, Err: f.vfs.err.InvalidArgument} 456 } 457 458 f.at += offset 459 case io.SeekEnd: 460 if size+offset < 0 { 461 return 0, &fs.PathError{Op: op, Path: f.name, Err: f.vfs.err.InvalidArgument} 462 } 463 464 f.at = size + offset 465 default: 466 if f.vfs.OSType() != avfs.OsWindows { 467 return 0, &fs.PathError{Op: op, Path: f.name, Err: f.vfs.err.InvalidArgument} 468 } 469 470 return 0, nil 471 } 472 473 return f.at, nil 474 } 475 476 // Stat returns the FileInfo structure describing file. 477 // If there is an error, it will be of type *PathError. 478 func (f *OrefaFile) Stat() (info fs.FileInfo, err error) { 479 if f == nil { 480 return nil, fs.ErrInvalid 481 } 482 483 f.mu.RLock() 484 defer f.mu.RUnlock() 485 486 if f.name == "" { 487 return &OrefaInfo{}, fs.ErrInvalid 488 } 489 490 op := "stat" 491 if f.vfs.OSType() == avfs.OsWindows { 492 op = "GetFileType" 493 } 494 495 if f.nd == nil { 496 err = avfs.ErrFileClosing 497 if f.vfs.OSType() == avfs.OsWindows { 498 err = avfs.ErrWinInvalidHandle 499 } 500 501 return &OrefaInfo{}, &fs.PathError{Op: op, Path: f.name, Err: err} 502 } 503 504 _, name := avfs.SplitAbs(f.vfs, f.name) 505 info = f.nd.fillStatFrom(name) 506 507 return info, nil 508 } 509 510 // Sync commits the current contents of the file to stable storage. 511 // Typically, this means flushing the file system's in-memory copy 512 // of recently written data to disk. 513 func (f *OrefaFile) Sync() error { 514 const op = "sync" 515 516 if f == nil { 517 return fs.ErrInvalid 518 } 519 520 f.mu.RLock() 521 defer f.mu.RUnlock() 522 523 if f.name == "" { 524 return fs.ErrInvalid 525 } 526 527 if f.nd == nil { 528 return &fs.PathError{Op: op, Path: f.name, Err: fs.ErrClosed} 529 } 530 531 return nil 532 } 533 534 // Truncate changes the size of the file. 535 // It does not change the I/O offset. 536 // If there is an error, it will be of type *PathError. 537 func (f *OrefaFile) Truncate(size int64) error { 538 const op = "truncate" 539 540 if f == nil { 541 return fs.ErrInvalid 542 } 543 544 f.mu.RLock() 545 defer f.mu.RUnlock() 546 547 if f.name == "" { 548 return fs.ErrInvalid 549 } 550 551 if f.nd == nil { 552 return &fs.PathError{Op: op, Path: f.name, Err: fs.ErrClosed} 553 } 554 555 nd := f.nd 556 if nd.mode.IsDir() { 557 err := error(avfs.ErrInvalidArgument) 558 if f.vfs.OSType() == avfs.OsWindows { 559 err = avfs.ErrWinAccessDenied 560 } 561 562 return &fs.PathError{Op: op, Path: f.name, Err: err} 563 } 564 565 if f.openMode&avfs.OpenWrite == 0 { 566 err := error(avfs.ErrInvalidArgument) 567 if f.vfs.OSType() == avfs.OsWindows { 568 err = avfs.ErrWinAccessDenied 569 } 570 571 return &fs.PathError{Op: op, Path: f.name, Err: err} 572 } 573 574 if size < 0 { 575 return &fs.PathError{Op: op, Path: f.name, Err: f.vfs.err.InvalidArgument} 576 } 577 578 nd.mu.Lock() 579 580 nd.truncate(size) 581 nd.mtime = time.Now().UnixNano() 582 583 nd.mu.Unlock() 584 585 return nil 586 } 587 588 // Write writes len(b) bytes to the File. 589 // It returns the number of bytes written and an error, if any. 590 // Write returns a non-nil error when n != len(b). 591 func (f *OrefaFile) Write(b []byte) (n int, err error) { 592 const op = "write" 593 594 if f == nil { 595 return 0, fs.ErrInvalid 596 } 597 598 f.mu.RLock() 599 defer f.mu.RUnlock() 600 601 if f.name == "" { 602 return 0, fs.ErrInvalid 603 } 604 605 if f.nd == nil { 606 return 0, &fs.PathError{Op: op, Path: f.name, Err: fs.ErrClosed} 607 } 608 609 nd := f.nd 610 if nd.mode.IsDir() { 611 err = avfs.ErrBadFileDesc 612 if f.vfs.OSType() == avfs.OsWindows { 613 err = avfs.ErrWinAccessDenied 614 } 615 616 return 0, &fs.PathError{Op: op, Path: f.name, Err: err} 617 } 618 619 if f.openMode&avfs.OpenWrite == 0 { 620 err = avfs.ErrBadFileDesc 621 if f.vfs.OSType() == avfs.OsWindows { 622 err = avfs.ErrWinAccessDenied 623 } 624 625 return 0, &fs.PathError{Op: op, Path: f.name, Err: err} 626 } 627 628 nd.mu.Lock() 629 630 n = copy(nd.data[f.at:], b) 631 if n < len(b) { 632 nd.data = append(nd.data, b[n:]...) 633 n = len(b) 634 } 635 636 nd.mtime = time.Now().UnixNano() 637 638 nd.mu.Unlock() 639 640 f.at += int64(n) 641 642 return n, nil 643 } 644 645 // WriteAt writes len(b) bytes to the File starting at byte offset off. 646 // It returns the number of bytes written and an error, if any. 647 // WriteAt returns a non-nil error when n != len(b). 648 func (f *OrefaFile) WriteAt(b []byte, off int64) (n int, err error) { 649 const op = "write" 650 651 if f == nil { 652 return 0, fs.ErrInvalid 653 } 654 655 if off < 0 { 656 return 0, &fs.PathError{Op: "writeat", Path: f.name, Err: avfs.ErrNegativeOffset} 657 } 658 659 f.mu.RLock() 660 defer f.mu.RUnlock() 661 662 if f.name == "" { 663 return 0, fs.ErrInvalid 664 } 665 666 if f.nd == nil { 667 return 0, &fs.PathError{Op: op, Path: f.name, Err: fs.ErrClosed} 668 } 669 670 nd := f.nd 671 if nd.mode.IsDir() { 672 err = avfs.ErrBadFileDesc 673 if f.vfs.OSType() == avfs.OsWindows { 674 err = avfs.ErrWinAccessDenied 675 } 676 677 return 0, &fs.PathError{Op: op, Path: f.name, Err: err} 678 } 679 680 if f.openMode&avfs.OpenWrite == 0 { 681 err = avfs.ErrBadFileDesc 682 if f.vfs.OSType() == avfs.OsWindows { 683 err = avfs.ErrWinAccessDenied 684 } 685 686 return 0, &fs.PathError{Op: op, Path: f.name, Err: err} 687 } 688 689 nd.mu.Lock() 690 691 diff := off + int64(len(b)) - nd.size() 692 if diff > 0 { 693 nd.data = append(nd.data, make([]byte, diff)...) 694 } 695 696 n = copy(nd.data[off:], b) 697 698 nd.mtime = time.Now().UnixNano() 699 700 nd.mu.Unlock() 701 702 return n, nil 703 } 704 705 // WriteString is like Write, but writes the contents of string s rather than 706 // a slice of bytes. 707 func (f *OrefaFile) WriteString(s string) (n int, err error) { 708 return f.Write([]byte(s)) 709 } 710 711 // OrefaInfo is the implementation fs.DirEntry (returned by ReadDir) and fs.FileInfo (returned by Stat and Lstat). 712 713 // Info returns the FileInfo for the file or subdirectory described by the entry. 714 // The returned FileInfo may be from the time of the original directory read 715 // or from the time of the call to Info. If the file has been removed or renamed 716 // since the directory read, Info may return an error satisfying errors.Is(err, ErrNotExist). 717 // If the entry denotes a symbolic link, Info reports the information about the link itself, 718 // not the link's target. 719 func (info *OrefaInfo) Info() (fs.FileInfo, error) { 720 return info, nil 721 } 722 723 // IsDir is the abbreviation for Mode().IsDir(). 724 func (info *OrefaInfo) IsDir() bool { 725 return info.mode.IsDir() 726 } 727 728 // Mode returns the file mode bits. 729 func (info *OrefaInfo) Mode() fs.FileMode { 730 return info.mode 731 } 732 733 // ModTime returns the modification time. 734 func (info *OrefaInfo) ModTime() time.Time { 735 return time.Unix(0, info.mtime) 736 } 737 738 // Name returns the base name of the file. 739 func (info *OrefaInfo) Name() string { 740 return info.name 741 } 742 743 // Size returns the length in bytes for regular files; system-dependent for others. 744 func (info *OrefaInfo) Size() int64 { 745 return info.size 746 } 747 748 // Sys returns the underlying data source (can return nil). 749 func (info *OrefaInfo) Sys() any { 750 return info 751 } 752 753 // Type returns the type bits for the entry. 754 // The type bits are a subset of the usual FileMode bits, those returned by the FileMode.Type method. 755 func (info *OrefaInfo) Type() fs.FileMode { 756 return info.mode & fs.ModeType 757 } 758 759 // Gid returns the group id. 760 func (info *OrefaInfo) Gid() int { 761 return info.gid 762 } 763 764 // Uid returns the user id. 765 func (info *OrefaInfo) Uid() int { 766 return info.uid 767 } 768 769 // Nlink returns the number of hard links. 770 func (info *OrefaInfo) Nlink() uint64 { 771 return uint64(info.nlink) 772 }