1 package squashfs 2 3 import ( 4 "errors" 5 "io" 6 "io/fs" 7 "sync" 8 9 "vimagination.zapto.org/byteio" 10 ) 11 12 type file struct { 13 file fileStat 14 15 mu sync.Mutex 16 squashfs *squashfs 17 reader io.ReadSeeker 18 pos int64 19 } 20 21 func (f *file) Read(p []byte) (int, error) { 22 f.mu.Lock() 23 defer f.mu.Unlock() 24 25 if f.squashfs == nil { 26 return 0, fs.ErrClosed 27 } 28 29 return f.read(p) 30 } 31 32 func (f *file) read(p []byte) (int, error) { 33 if err := f.prepareReader(); err != nil { 34 return 0, err 35 } 36 37 n, err := f.reader.Read(p) 38 39 f.pos += int64(n) 40 41 if errors.Is(err, io.EOF) && uint64(f.pos) < f.file.fileSize { 42 f.reader = nil 43 err = nil 44 } 45 46 if err == nil && n < len(p) { 47 var m int 48 49 m, err = f.read(p[n:]) 50 51 n += m 52 } 53 54 return n, err 55 } 56 57 func (f *file) prepareReader() error { 58 if f.reader != nil { 59 return nil 60 } 61 62 if uint64(f.pos) == f.file.fileSize { 63 return io.EOF 64 } 65 66 reader, err := f.getOffsetReader(f.pos) 67 if err != nil { 68 return err 69 } 70 71 f.reader = reader 72 73 return nil 74 } 75 76 func (f *file) getReader(block int) (io.ReadSeeker, error) { 77 if block < len(f.file.blockSizes) { 78 return f.getBlockReader(block) 79 } else if f.file.fragIndex != fieldDisabled { 80 return f.getFragmentReader() 81 } 82 83 return nil, io.EOF 84 } 85 86 func (f *file) getBlockOffset(pos int64) (int, int64) { 87 return int(pos / int64(f.squashfs.superblock.BlockSize)), pos % int64(f.squashfs.superblock.BlockSize) 88 } 89 90 func (f *file) getOffsetReader(pos int64) (io.ReadSeeker, error) { 91 if uint64(pos) >= f.file.fileSize { 92 return nil, io.ErrUnexpectedEOF 93 } 94 95 block, skipBytes := f.getBlockOffset(pos) 96 97 reader, err := f.getReader(block) 98 if err != nil { 99 return nil, err 100 } 101 102 if skipBytes > 0 { 103 if _, err = reader.Seek(skipBytes, io.SeekStart); err != nil { 104 return nil, err 105 } 106 } 107 108 return reader, nil 109 } 110 111 const ( 112 sizeMask = 0x00ffffff 113 compressionMask = 0xff000000 114 fragmentIndexShift = 4 115 ) 116 117 func (f *file) getBlockReader(block int) (io.ReadSeeker, error) { 118 start := int64(f.file.blocksStart) 119 120 for _, size := range f.file.blockSizes[:block] { 121 start += int64(size & sizeMask) 122 } 123 124 size := int64(f.file.blockSizes[block]) 125 if size&compressionMask == 0 { 126 return f.squashfs.blockCache.getBlock(start, io.NewSectionReader(f.squashfs.reader, start, size), f.squashfs.superblock.Compressor) 127 } 128 129 return io.NewSectionReader(f.squashfs.reader, start, size&sizeMask), nil 130 } 131 132 func (f *file) getFragmentDetails() (start uint64, size uint32, err error) { 133 ler := byteio.StickyLittleEndianReader{Reader: io.NewSectionReader(f.squashfs.reader, int64(f.squashfs.superblock.FragTable)+int64(f.file.fragIndex>>10), 8)} 134 135 mdPos := ler.ReadUint64() 136 137 if ler.Err != nil { 138 return 0, 0, ler.Err 139 } 140 141 ler.Reader, ler.Err = f.squashfs.readMetadata((uint64(f.file.fragIndex)<<fragmentIndexShift)%blockSize, mdPos) 142 143 start = ler.ReadUint64() 144 size = ler.ReadUint32() 145 146 if ler.ReadUint32() != 0 { 147 return 0, 0, fs.ErrInvalid 148 } 149 150 return start, size, ler.Err 151 } 152 153 func (f *file) getFragmentReader() (io.ReadSeeker, error) { 154 start, size, err := f.getFragmentDetails() 155 if err != nil { 156 return nil, err 157 } 158 159 fragmentSize := int64(f.file.fileSize) % int64(f.squashfs.superblock.BlockSize) 160 161 if size&compressionMask == 0 { 162 reader, err := f.squashfs.blockCache.getBlock(int64(start), io.NewSectionReader(f.squashfs.reader, int64(start), int64(size)), f.squashfs.superblock.Compressor) 163 if err != nil { 164 return nil, err 165 } 166 167 return io.NewSectionReader(reader, int64(f.file.blockOffset), fragmentSize), nil 168 } 169 170 return io.NewSectionReader(f.squashfs.reader, int64(start)+int64(f.file.blockOffset), fragmentSize), nil 171 } 172 173 func (f *file) Seek(offset int64, whence int) (int64, error) { 174 f.mu.Lock() 175 defer f.mu.Unlock() 176 177 if f.squashfs == nil { 178 return 0, fs.ErrClosed 179 } 180 181 var base int64 182 183 switch whence { 184 case io.SeekStart: 185 case io.SeekCurrent: 186 base = f.pos 187 case io.SeekEnd: 188 base = int64(f.file.fileSize) 189 default: 190 return f.pos, fs.ErrInvalid 191 } 192 193 base += offset 194 195 if base < 0 { 196 return f.pos, fs.ErrInvalid 197 } 198 199 return f.setPos(base) 200 } 201 202 func (f *file) setPos(base int64) (int64, error) { 203 if f.reader != nil { 204 cBlock, _ := f.getBlockOffset(f.pos) 205 bBlock, offset := f.getBlockOffset(base) 206 207 if cBlock != bBlock { 208 f.reader = nil 209 } else if _, err := f.reader.Seek(offset, io.SeekStart); err != nil { 210 return f.pos, err 211 } 212 } 213 214 f.pos = base 215 216 return base, nil 217 } 218 219 func (f *file) ReadAt(p []byte, offset int64) (int, error) { 220 f.mu.Lock() 221 sqfs := f.squashfs 222 f.mu.Unlock() 223 224 if sqfs == nil { 225 return 0, fs.ErrClosed 226 } 227 228 if uint64(offset) == f.file.fileSize { 229 return 0, io.EOF 230 } 231 232 g := file{ 233 file: f.file, 234 squashfs: sqfs, 235 pos: offset, 236 } 237 238 return g.read(p) 239 } 240 241 func (f *file) Stat() (fs.FileInfo, error) { 242 return f.file, nil 243 } 244 245 func (f *file) Close() error { 246 f.mu.Lock() 247 defer f.mu.Unlock() 248 249 if f.squashfs == nil { 250 return fs.ErrClosed 251 } 252 253 f.squashfs = nil 254 255 return nil 256 } 257