github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/cmn/archive/fast.go (about) 1 // Package archive: write, read, copy, append, list primitives 2 // across all supported formats 3 /* 4 * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved. 5 */ 6 package archive 7 8 import ( 9 "archive/tar" 10 "fmt" 11 "io" 12 "os" 13 14 "github.com/NVIDIA/aistore/cmn/cos" 15 "github.com/NVIDIA/aistore/cmn/debug" 16 ) 17 18 // fast.go provides "fast append" 19 20 // Opens TAR and uses its reader's Next() to skip to the position 21 // right _after_ the last file in the TAR (padding bytes including). 22 // 23 // Background: 24 // TAR file is padded with one or more 512-byte blocks of zero bytes. 25 // The blocks must be overwritten, otherwise newly added files won't be 26 // accessible. Different TAR formats (such as `ustar`, `pax` and `GNU`) 27 // write different number of zero blocks. 28 func OpenTarSeekEnd(cname, workFQN string) (rwfh *os.File, tarFormat tar.Format, err error) { 29 if rwfh, err = os.OpenFile(workFQN, os.O_RDWR, cos.PermRWR); err != nil { 30 return 31 } 32 if tarFormat, err = _seekTarEnd(cname, rwfh); err != nil { 33 rwfh.Close() // always close on err 34 } 35 return 36 } 37 38 func _seekTarEnd(cname string, fh *os.File) (tarFormat tar.Format, _ error) { 39 var ( 40 twr = tar.NewReader(fh) 41 size int64 42 pos = int64(-1) 43 unknown bool 44 ) 45 for { 46 hdr, err := twr.Next() 47 if err != nil { 48 if err != io.EOF { 49 return tarFormat, err // invalid TAR format 50 } 51 // EOF 52 if pos < 0 { 53 return tarFormat, ErrTarIsEmpty 54 } 55 break 56 } 57 if pos < 0 { 58 tarFormat = hdr.Format 59 } else if !unknown { // once unknown remains unknown 60 if tarFormat != hdr.Format { 61 tarFormat = tar.FormatUnknown 62 unknown = true 63 } 64 } 65 pos, err = fh.Seek(0, io.SeekCurrent) 66 if err != nil { 67 debug.AssertNoErr(err) // unlikely 68 return tarFormat, err 69 } 70 size = hdr.Size 71 } 72 if pos == 0 { 73 return tarFormat, fmt.Errorf("failed to seek end of the TAR %s", cname) 74 } 75 padded := cos.CeilAlignInt64(size, TarBlockSize) 76 _, err := fh.Seek(pos+padded, io.SeekStart) 77 return tarFormat, err 78 }