1 package squashfs 2 3 import ( 4 "io" 5 "io/fs" 6 "sync" 7 8 "vimagination.zapto.org/byteio" 9 ) 10 11 type dir struct { 12 dir dirStat 13 14 mu sync.Mutex 15 squashfs *squashfs 16 reader io.Reader 17 count uint32 18 start uint32 19 read int 20 } 21 22 const ( 23 dirFileSizeOffset = 3 24 dirLinkCountOffset = 2 25 dirHeaderSize = 12 26 dirBodySize = 8 27 ) 28 29 func (s *squashfs) newDir(dirStat dirStat) (*dir, error) { 30 r, err := s.readMetadata(uint64(dirStat.blockIndex)<<metadataPointerShift|uint64(dirStat.blockOffset), s.superblock.DirTable) 31 if err != nil { 32 return nil, err 33 } 34 35 return &dir{ 36 dir: dirStat, 37 squashfs: s, 38 reader: io.LimitReader(r, int64(dirStat.fileSize-dirFileSizeOffset)), 39 }, nil 40 } 41 42 func (*dir) Read(_ []byte) (int, error) { 43 return 0, fs.ErrInvalid 44 } 45 46 func (d *dir) ReadDir(n int) ([]fs.DirEntry, error) { 47 d.mu.Lock() 48 defer d.mu.Unlock() 49 50 if d.squashfs == nil { 51 return nil, fs.ErrClosed 52 } 53 54 if n == 0 { 55 n = -1 56 } 57 58 return d.readDir(n) 59 } 60 61 func (d *dir) readDir(n int) ([]fs.DirEntry, error) { 62 var entries []fs.DirEntry 63 64 ler := byteio.StickyLittleEndianReader{Reader: d.reader} 65 66 m := n 67 68 for m != 0 && d.read+dirFileSizeOffset < int(d.dir.fileSize) { 69 de := d.readDirEntry(&ler) 70 71 if de.typ == 0 { 72 return entries, ler.Err 73 } 74 75 entries = append(entries, de) 76 77 m-- 78 } 79 80 if len(entries) == 0 && n > 0 { 81 return nil, io.EOF 82 } 83 84 return entries, nil 85 } 86 87 func (d *dir) readDirEntry(ler *byteio.StickyLittleEndianReader) dirEntry { 88 if d.count == 0 { 89 d.count = ler.ReadUint32() + 1 90 d.start = ler.ReadUint32() 91 ler.ReadUint32() 92 93 d.read += dirHeaderSize 94 } else { 95 d.count-- 96 } 97 98 offset := uint64(ler.ReadUint16()) 99 ler.ReadInt16() // inode offset 100 101 de := dirEntry{ 102 squashfs: d.squashfs, 103 typ: ler.ReadUint16(), 104 name: ler.ReadString(int(ler.ReadUint16()) + 1), 105 ptr: uint64(d.start<<metadataPointerShift) | offset, 106 } 107 108 d.read += dirBodySize + len(de.name) 109 110 return de 111 } 112 113 func (d *dir) Stat() (fs.FileInfo, error) { 114 return d.dir, nil 115 } 116 117 func (d *dir) Close() error { 118 d.mu.Lock() 119 defer d.mu.Unlock() 120 121 if d.squashfs == nil { 122 return fs.ErrClosed 123 } 124 125 d.squashfs = nil 126 127 return nil 128 } 129 130 type dirEntry struct { 131 squashfs *squashfs 132 typ uint16 133 name string 134 ptr uint64 135 } 136 137 func (d dirEntry) Name() string { 138 return d.name 139 } 140 141 func (d dirEntry) IsDir() bool { 142 return d.typ == inodeBasicDir 143 } 144 145 func (d dirEntry) Type() fs.FileMode { 146 switch d.typ { 147 case inodeBasicDir: 148 return fs.ModeDir 149 case inodeBasicFile: 150 return 0 151 case inodeBasicSymlink: 152 return fs.ModeSymlink 153 case inodeBasicBlock: 154 return fs.ModeDevice 155 case inodeBasicChar: 156 return fs.ModeCharDevice 157 case inodeBasicPipe: 158 return fs.ModeNamedPipe 159 case inodeBasicSock: 160 return fs.ModeSocket 161 } 162 163 return fs.ModeIrregular 164 } 165 166 func (d dirEntry) Info() (fs.FileInfo, error) { 167 return d.squashfs.getEntry(d.ptr, d.name) 168 } 169