package squashfs // import ( // "bytes" // "encoding/binary" // "fmt" // "io" // "io/ioutil" // "log" // "os" // "path/filepath" // "strings" // "sync" // "syscall" // "time" // "golang.org/x/xerrors" // ) // type Reader struct { // r io.ReaderAt // super superblock // } // func NewReader(r io.ReaderAt) (*Reader, error) { // var sb superblock // if err := binary.Read(io.NewSectionReader(r, 0, int64(binary.Size(sb))), binary.LittleEndian, &sb); err != nil { // return nil, fmt.Errorf("reading superblock: %v", err) // } // if got, want := sb.Magic, uint32(magic); got != want { // return nil, fmt.Errorf("invalid magic (not a SquashFS image?): got %x, want %x", got, want) // } // //log.Printf("superblock: %+v", sb) // return &Reader{ // r: r, // super: sb, // }, nil // } // // TODO: maybe mmap instead of seeking? // func (r *Reader) inode(i Inode) (blockoffset int64, offset int64) { // return int64(i >> 16), int64(i & 0xFFFF) // } // type blockReader struct { // r io.ReadSeeker // lenBuf [2]byte // buf []byte // i int64 // off int64 // TODO: remove this once using mmap // } // func (br *blockReader) Read(p []byte) (n int, err error) { // if br.i >= int64(len(br.buf)) { // br.i = 0 // if _, err := io.ReadFull(br.r, br.lenBuf[:]); err != nil { // return 0, err // } // l := binary.LittleEndian.Uint16(br.lenBuf[:]) // //uncompressed := l&0x8000 > 0 // l &= 0x7FFF // //log.Printf("block of len %d, uncompressed: %v", l, uncompressed) // if int(l) > cap(br.buf) { // br.buf = make([]byte, int(l)) // } // br.buf = br.buf[:l] // if _, err := io.ReadFull(br.r, br.buf); err != nil { // return 0, err // } // //log.Printf("(retry) n = %v, err = %v", n, err) // } // n = copy(p, br.buf[br.i:]) // br.i += int64(n) // return n, err // } // func (br *blockReader) Close() error { // blockReaderPool.Put(br) // return nil // } // var blockReaderPool = sync.Pool{ // New: func() interface{} { // return &blockReader{ // buf: make([]byte, 0, metadataBlockSize), // } // }, // } // func (r *Reader) blockReader(blockoffset, offset int64) (io.ReadCloser, error) { // //log.Printf("blockoffset %v (%x), offset %v (%x)", blockoffset, blockoffset, offset, offset) // br := blockReaderPool.Get().(*blockReader) // br.buf = br.buf[:0] // br.r = io.NewSectionReader(r.r, blockoffset, 5500*1024*1024) // TODO: correct limit? can we use IntMax // br.off = blockoffset // br.i = 0 // //log.Printf("discarding %d bytes", offset) // for n := int64(0); n < offset; { // remaining := offset - n // if remaining > metadataBlockSize { // remaining = metadataBlockSize // } // nn, err := br.Read(br.buf[:remaining]) // if err != nil { // return nil, err // } // n += int64(nn) // } // return br, nil // } // // TODO: define an inode type to use instead of interface{}? // func (r *Reader) readInode(i Inode) (interface{}, error) { // blockoffset, offset := r.inode(i) // br, err := r.blockReader(r.super.InodeTableStart+blockoffset, offset) // if err != nil { // fmt.Println("oops! ", err) // return nil, err // } // defer br.Close() // fmt.Println("Hello There") // // We need the inode type before we know which type to pass to binary.Read, // // so we need to read it twice: // var inodeType uint16 // typeBuf := bytes.NewBuffer(make([]byte, 0, binary.Size(inodeType))) // if err := binary.Read(io.TeeReader(br, typeBuf), binary.LittleEndian, &inodeType); err != nil { // return nil, err // } // br = ioutil.NopCloser(io.MultiReader(typeBuf, br)) // // var ih inodeHeader // // if err := binary.Read(br, binary.LittleEndian, &ih); err != nil { // // return err // // } // // //log.Printf("ih: %+v", ih) // //log.Printf("inode type: %v", inodeType) // switch inodeType { // case dirType: // var di dirInodeHeader // if err := binary.Read(br, binary.LittleEndian, &di); err != nil { // return nil, err // } // return di, nil // case fileType: // var ri regInodeHeader // if err := binary.Read(br, binary.LittleEndian, &ri); err != nil { // return nil, err // } // return ri, nil // case symlinkType: // var si symlinkInodeHeader // if err := binary.Read(br, binary.LittleEndian, &si); err != nil { // return nil, err // } // return si, nil // case ldirType: // var di ldirInodeHeader // if err := binary.Read(br, binary.LittleEndian, &di); err != nil { // return nil, err // } // return di, nil // case lregType: // var di lregInodeHeader // if err := binary.Read(br, binary.LittleEndian, &di); err != nil { // return nil, err // } // return di, nil // // TODO: // // blkdevType // // chrdevType // // fifoType // // socketType // // // The larger types are used for e.g. sparse files, xattrs, etc. // // ldirType // // lsymlinkType // // lblkdevType // // lchrdevType // // lfifoType // // lsocketType // } // return nil, fmt.Errorf("unknown inode type %d", inodeType) // } // func (r *Reader) RootInode() Inode { // return r.super.RootInode // } // func (r *Reader) Stat(name string, i Inode) (os.FileInfo, error) { // inode, err := r.readInode(i) // if err != nil { // return nil, err // } // //log.Printf("i %d, inode: %T, %+v", i, inode, inode) // switch x := inode.(type) { // case dirInodeHeader: // return &FileInfo{ // name: name, // size: int64(x.FileSize), // mode: os.ModeDir | os.FileMode(x.Mode), // modTime: time.Unix(int64(x.Mtime), 0), // Inode: i, // }, nil // case ldirInodeHeader: // return &FileInfo{ // name: name, // size: int64(x.FileSize), // mode: os.ModeDir | os.FileMode(x.Mode), // modTime: time.Unix(int64(x.Mtime), 0), // Inode: i, // }, nil // case regInodeHeader: // mode := os.FileMode(x.Mode & 0777) // if x.Mode&syscall.S_ISUID != 0 { // mode |= os.ModeSetuid // } // return &FileInfo{ // name: name, // size: int64(x.FileSize), // mode: mode, // modTime: time.Unix(int64(x.Mtime), 0), // Inode: i, // }, nil // case lregInodeHeader: // mode := os.FileMode(x.Mode & 0777) // if x.Mode&syscall.S_ISUID != 0 { // mode |= os.ModeSetuid // } // return &FileInfo{ // name: name, // size: int64(x.FileSize), // mode: mode, // modTime: time.Unix(int64(x.Mtime), 0), // Inode: i, // }, nil // case symlinkInodeHeader: // return &FileInfo{ // name: name, // size: int64(x.SymlinkSize), // mode: os.ModeSymlink | os.FileMode(x.Mode), // modTime: time.Unix(int64(x.Mtime), 0), // Inode: i, // }, nil // } // return nil, fmt.Errorf("unknown inode type %T", inode) // } // func (r *Reader) ReadLink(i Inode) (string, error) { // // TODO: reduce code duplication with readInode // blockoffset, offset := r.inode(i) // br, err := r.blockReader(r.super.InodeTableStart+blockoffset, offset) // if err != nil { // return "", err // } // defer br.Close() // // We need the inode type before we know which type to pass to binary.Read, // // so we need to read it twice: // var inodeType uint16 // typeBuf := bytes.NewBuffer(make([]byte, 0, binary.Size(inodeType))) // if err := binary.Read(io.TeeReader(br, typeBuf), binary.LittleEndian, &inodeType); err != nil { // return "", err // } // br = ioutil.NopCloser(io.MultiReader(typeBuf, br)) // if inodeType != symlinkType { // return "", fmt.Errorf("invalid inode type: got %d instead of symlink", inodeType) // } // var si symlinkInodeHeader // if err := binary.Read(br, binary.LittleEndian, &si); err != nil { // return "", err // } // // Assumption: r.r is positioned right after the inode // buf := make([]byte, si.SymlinkSize) // if _, err := io.ReadFull(br, buf); err != nil { // return "", err // } // return string(buf), nil // } // func (r *Reader) FileReader(inode Inode) (*io.SectionReader, error) { // //log.Printf("Readfile(%v)", inode) // i, err := r.readInode(inode) // if err != nil { // return nil, err // } // //log.Printf("i: %+v", i) // // TODO(compression): read the blocksizes to read compressed blocks // switch ri := i.(type) { // case regInodeHeader: // off := int64(ri.StartBlock) + int64(ri.Offset) // return io.NewSectionReader(r.r, off, int64(ri.FileSize)), nil // case lregInodeHeader: // off := int64(ri.StartBlock) + int64(ri.Offset) // return io.NewSectionReader(r.r, off, int64(ri.FileSize)), nil // default: // return nil, fmt.Errorf("BUG: non-file inode type") // } // } // type FileNotFoundError struct { // path string // } // func (e *FileNotFoundError) Error() string { // return fmt.Sprintf("%q not found", e.path) // } // func (r *Reader) lookupComponent(parent Inode, component string) (Inode, error) { // rfis, err := r.readdir(parent, false) // if err != nil { // return 0, err // } // for _, rfi := range rfis { // if rfi.Name() == component { // return rfi.Sys().(*FileInfo).Inode, nil // } // } // return 0, &FileNotFoundError{path: component} // } // func (r *Reader) lookupPath(path string, followSymlink bool) (Inode, error) { // inode := r.RootInode() // parts := strings.Split(path, "/") // for idx, part := range parts { // var err error // inode, err = r.lookupComponent(inode, part) // if err != nil { // if _, ok := err.(*FileNotFoundError); ok { // return 0, &FileNotFoundError{path: path} // } // return 0, err // } // if !followSymlink { // continue // } // i, err := r.readInode(inode) // if err != nil { // return 0, xerrors.Errorf("Stat(%d): %v", inode, err) // } // if _, ok := i.(symlinkInodeHeader); ok { // target, err := r.ReadLink(inode) // if err != nil { // return 0, err // } // //log.Printf("component %q (full: %q) resolved to %q", part, parts[:idx+1], target) // target = filepath.Clean(filepath.Join(append(parts[:idx] /* parent */, target)...)) // //log.Printf("-> %s", target) // i, err := r.LookupPath(target) // if err != nil { // return 0, err // } // inode = i // } // } // return inode, nil // } // func (r *Reader) LookupPath(path string) (Inode, error) { // return r.lookupPath(path, true) // } // // LlookupPath is like LookupPath, but does not follow symbolic links, i.e. will // // instead return the inode of the link itself. // func (r *Reader) LlookupPath(path string) (Inode, error) { // return r.lookupPath(path, false) // } // func (r *Reader) Readdir(dirInode Inode) ([]os.FileInfo, error) { // return r.readdir(dirInode, true) // } // // Like Readdir, but does not call Stat on each file. The returned FileInfo // // structs will still have a filled in Name, partly filled in Mode, and filled // // in Inode. // func (r *Reader) ReaddirNoStat(dirInode Inode) ([]os.FileInfo, error) { // return r.readdir(dirInode, false) // } // var nameBufPool = sync.Pool{ // New: func() interface{} { // return &bytes.Buffer{} // }, // } // func (r *Reader) readdir(dirInode Inode, stat bool) ([]os.FileInfo, error) { // //log.Printf("Readdir(%v (%x))", dirInode, dirInode) // i, err := r.readInode(dirInode) // fmt.Println("Yodle") // if err != nil { // return nil, err // } // var ( // startBlock int64 // fileSize int64 // offset int64 // ) // switch x := i.(type) { // case dirInodeHeader: // startBlock = int64(x.StartBlock) // fileSize = int64(x.FileSize) // offset = int64(x.Offset) // case ldirInodeHeader: // startBlock = int64(x.StartBlock) // fileSize = int64(x.FileSize) // offset = int64(x.Offset) // default: // return nil, fmt.Errorf("unknown directory inode type %T", i) // } // br, err := r.blockReader(r.super.DirectoryTableStart+startBlock, offset) // if err != nil { // return nil, err // } // defer br.Close() // // See also https://elixir.bootlin.com/linux/v4.18.9/source/fs/squashfs/dir.c#L63 // limit := fileSize - int64(len(".")) - int64(len("..")) // br = ioutil.NopCloser(io.LimitReader(br, limit)) // var fis []os.FileInfo // var dh dirHeader // var de dirEntry // var dhBuf [12]byte // var deBuf [8]byte // nameBuf := nameBufPool.Get().(*bytes.Buffer) // defer nameBufPool.Put(nameBuf) // for { // if _, err := io.ReadFull(br, dhBuf[:]); err != nil { // if err == io.EOF { // return fis, nil // } // return nil, err // } // dh.Unmarshal(dhBuf[:]) // dh.Count++ // SquashFS stores count-1 // //log.Printf("dh: %+v", dh) // for i := 0; i < int(dh.Count); i++ { // if _, err := io.ReadFull(br, deBuf[:]); err != nil { // return nil, err // } // de.Unmarshal(deBuf[:]) // de.Size++ // SquashFS stores size-1 // //log.Printf("de: %+v", de) // nameBuf.Reset() // nameBuf.Grow(int(de.Size)) // nb := nameBuf.Bytes()[:de.Size] // if _, err := io.ReadFull(br, nb); err != nil { // return nil, err // } // name := string(nb) // //log.Printf("name: %q", string(name)) // var fi os.FileInfo // if stat { // var err error // fi, err = r.Stat(name, Inode(int64(dh.StartBlock)<<16|int64(de.Offset))) // if err != nil { // return nil, err // } // } else { // ffi := &FileInfo{ // name: name, // Inode: Inode(int64(dh.StartBlock)<<16 | int64(de.Offset)), // } // switch de.EntryType { // case dirType, ldirType: // ffi.mode |= os.ModeDir // case symlinkType, lsymlinkType: // ffi.mode |= os.ModeSymlink // } // fi = ffi // } // fis = append(fis, fi) // } // } // return fis, nil // } // type FileInfo struct { // name string // size int64 // mode os.FileMode // modTime time.Time // Inode Inode // } // func (fi *FileInfo) Name() string { return fi.name } // func (fi *FileInfo) Size() int64 { return fi.size } // func (fi *FileInfo) Mode() os.FileMode { return fi.mode } // func (fi *FileInfo) IsDir() bool { return fi.mode.IsDir() } // func (fi *FileInfo) ModTime() time.Time { return fi.modTime } // func (fi *FileInfo) Sys() interface{} { return fi } // func (r *Reader) readXattr(tableHeader xattrTableHeader, id xattrId) (*Xattr, error) { // blockoffset, offset := r.inode(Inode(id.Xattr)) // br, err := r.blockReader(int64(tableHeader.XattrTableStart)+blockoffset, offset) // if err != nil { // return nil, err // } // defer br.Close() // var typ, nameSize uint16 // if err := binary.Read(br, binary.LittleEndian, &typ); err != nil { // return nil, err // } // if err := binary.Read(br, binary.LittleEndian, &nameSize); err != nil { // return nil, err // } // log.Printf("type = %v, nameSize = %v", typ, nameSize) // name := make([]byte, nameSize) // if _, err := io.ReadFull(br, name); err != nil { // return nil, err // } // log.Printf("name = %v", string(name)) // var valSize uint32 // if err := binary.Read(br, binary.LittleEndian, &valSize); err != nil { // return nil, err // } // val := make([]byte, valSize) // if _, err := io.ReadFull(br, val); err != nil { // return nil, err // } // log.Printf("val = %x", val) // return &Xattr{ // Type: typ, // FullName: xattrPrefix[int(typ)] + string(name), // Value: val, // }, nil // } // func (r *Reader) ReadXattrs(inode Inode) ([]Xattr, error) { // //log.Printf("Readdir(%v (%x))", dirInode, dirInode) // i, err := r.readInode(inode) // if err != nil { // return nil, err // } // var xid uint32 // switch x := i.(type) { // case regInodeHeader, // dirInodeHeader, // ldirInodeHeader, // symlinkInodeHeader: // return nil, nil // no extended attributes // case lregInodeHeader: // if x.Xattr == invalidXattr { // return nil, nil // file has no extended attributes // } // xid = x.Xattr // default: // return nil, fmt.Errorf("unknown inode type %T", i) // } // const idEntriesPerBlock = 512 // = 8192 / 16 /* sizeof(xattrId) */ // block := xid / idEntriesPerBlock // offset := (xid % idEntriesPerBlock) * 16 // log.Printf("xattr id %d, block %d, offset %d", xid, block, offset) // log.Printf("r.super.XattrIdTableStart = 0x%x, r.super.XattrIdTableStart = %v", r.super.XattrIdTableStart, r.super.XattrIdTableStart) // br := ioutil.NopCloser(io.NewSectionReader(r.r, r.super.XattrIdTableStart, int64(16 /* sizeof(xattrTableHeader) */ +(block+1)*4 /* sizeof(uint32) */))) // var tableHeader xattrTableHeader // if err := binary.Read(br, binary.LittleEndian, &tableHeader); err != nil { // return nil, err // } // // index starts here // if _, err := io.CopyN(ioutil.Discard, br, int64(block*4 /* sizeof(uint32) */)); err != nil { // return nil, err // } // var blockOffset uint32 // if err := binary.Read(br, binary.LittleEndian, &blockOffset); err != nil { // return nil, err // } // log.Printf("blockOffset = 0x%x (%d)", blockOffset, blockOffset) // br, err = r.blockReader(int64(blockOffset), int64(offset)) // if err != nil { // return nil, err // } // defer br.Close() // var id xattrId // if err := binary.Read(br, binary.LittleEndian, &id); err != nil { // return nil, err // } // log.Printf("id: %+v", id) // log.Printf("tableHeader: %+v (start 0x%x)", tableHeader, tableHeader.XattrTableStart) // var xattrs []Xattr // for i := 0; i < int(id.Count); i++ { // xattr, err := r.readXattr(tableHeader, id) // if err != nil { // return nil, err // } // xattrs = append(xattrs, *xattr) // } // return xattrs, nil // }