Restarted some stuff so I can do it better.
Made a reader that can reade across data blocks if necessary Still can't get things to read right
This commit is contained in:
@@ -0,0 +1,913 @@
|
||||
// // Package squashfs implements writing SquashFS file system images using zlib
|
||||
// // compression for data blocks (inodes and directory entries are written
|
||||
// // uncompressed for simplicity).
|
||||
// //
|
||||
// // Note that SquashFS requires directory entries to be sorted, i.e. files and
|
||||
// // directories need to be added in the correct order.
|
||||
// //
|
||||
// // This package intentionally only implements a subset of SquashFS. Notably,
|
||||
// // block devices, character devices, FIFOs, sockets and xattrs are not
|
||||
// // supported.
|
||||
package squashfs
|
||||
|
||||
// import (
|
||||
// "bytes"
|
||||
// "compress/zlib"
|
||||
// "encoding/binary"
|
||||
// "io"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "strings"
|
||||
// "time"
|
||||
|
||||
// "golang.org/x/sys/unix"
|
||||
// )
|
||||
|
||||
// // inode contains a block number + offset within that block.
|
||||
// type Inode int64
|
||||
|
||||
// const (
|
||||
// zlibCompression = 1 + iota
|
||||
// lzmaCompression
|
||||
// lzoCompression
|
||||
// xzCompression
|
||||
// lz4Compression
|
||||
// zstdCompression
|
||||
// )
|
||||
|
||||
// const (
|
||||
// invalidFragment = 0xFFFFFFFF
|
||||
// invalidXattr = 0xFFFFFFFF
|
||||
// )
|
||||
|
||||
// const (
|
||||
// dirType = 1 + iota
|
||||
// fileType
|
||||
// symlinkType
|
||||
// blkdevType
|
||||
// chrdevType
|
||||
// fifoType
|
||||
// socketType
|
||||
// // The larger types are used for e.g. sparse files, xattrs, etc.
|
||||
// ldirType
|
||||
// lregType
|
||||
// lsymlinkType
|
||||
// lblkdevType
|
||||
// lchrdevType
|
||||
// lfifoType
|
||||
// lsocketType
|
||||
// )
|
||||
|
||||
// type inodeHeader struct {
|
||||
// InodeType uint16
|
||||
// Mode uint16
|
||||
// Uid uint16
|
||||
// Gid uint16
|
||||
// Mtime int32
|
||||
// InodeNumber uint32
|
||||
// }
|
||||
|
||||
// // fileType
|
||||
// type regInodeHeader struct {
|
||||
// inodeHeader
|
||||
|
||||
// // full byte offset from the start of the file system, e.g. 96 for first
|
||||
// // file contents. Not using fragments limits us to 2^32-1-96 (≈ 4GiB) bytes
|
||||
// // of file contents.
|
||||
// StartBlock uint32
|
||||
// Fragment uint32
|
||||
// Offset uint32
|
||||
// FileSize uint32
|
||||
|
||||
// // Followed by a uint32 array of compressed block sizes.
|
||||
// }
|
||||
|
||||
// // lregType
|
||||
// type lregInodeHeader struct {
|
||||
// inodeHeader
|
||||
|
||||
// // full byte offset from the start of the file system, e.g. 96 for first
|
||||
// // file contents. Not using fragments limits us to 2^32-1-96 (≈ 4GiB) bytes
|
||||
// // of file contents.
|
||||
// StartBlock uint64
|
||||
// FileSize uint64
|
||||
// Sparse uint64
|
||||
// Nlink uint32
|
||||
// Fragment uint32
|
||||
// Offset uint32
|
||||
// Xattr uint32
|
||||
|
||||
// // Followed by a uint32 array of compressed block sizes.
|
||||
// }
|
||||
|
||||
// // symlinkType
|
||||
// type symlinkInodeHeader struct {
|
||||
// inodeHeader
|
||||
|
||||
// Nlink uint32
|
||||
// SymlinkSize uint32
|
||||
|
||||
// // Followed by a byte array of SymlinkSize bytes.
|
||||
// }
|
||||
|
||||
// // chrdevType and blkdevType
|
||||
// type devInodeHeader struct {
|
||||
// inodeHeader
|
||||
|
||||
// Nlink uint32
|
||||
// Rdev uint32
|
||||
// }
|
||||
|
||||
// // fifoType and socketType
|
||||
// type ipcInodeHeader struct {
|
||||
// inodeHeader
|
||||
|
||||
// Nlink uint32
|
||||
// }
|
||||
|
||||
// // dirType
|
||||
// type dirInodeHeader struct {
|
||||
// inodeHeader
|
||||
|
||||
// StartBlock uint32
|
||||
// Nlink uint32
|
||||
// FileSize uint16
|
||||
// Offset uint16
|
||||
// ParentInode uint32
|
||||
// }
|
||||
|
||||
// // ldirType
|
||||
// type ldirInodeHeader struct {
|
||||
// inodeHeader
|
||||
|
||||
// Nlink uint32
|
||||
// FileSize uint32
|
||||
// StartBlock uint32
|
||||
// ParentInode uint32
|
||||
// Icount uint16
|
||||
// Offset uint16
|
||||
// Xattr uint32
|
||||
// }
|
||||
|
||||
// type dirHeader struct {
|
||||
// Count uint32
|
||||
// StartBlock uint32
|
||||
// InodeOffset uint32
|
||||
// }
|
||||
|
||||
// func (d *dirHeader) Unmarshal(b []byte) {
|
||||
// _ = b[11]
|
||||
// e := binary.LittleEndian
|
||||
// d.Count = e.Uint32(b)
|
||||
// d.StartBlock = e.Uint32(b[4:])
|
||||
// d.InodeOffset = e.Uint32(b[8:])
|
||||
// }
|
||||
|
||||
// type dirEntry struct {
|
||||
// Offset uint16
|
||||
// InodeNumber int16
|
||||
// EntryType uint16
|
||||
// Size uint16
|
||||
|
||||
// // Followed by a byte array of Size bytes.
|
||||
// }
|
||||
|
||||
// func (d *dirEntry) Unmarshal(b []byte) {
|
||||
// _ = b[7]
|
||||
// e := binary.LittleEndian
|
||||
// d.Offset = e.Uint16(b)
|
||||
// d.InodeNumber = int16(e.Uint16(b[2:]))
|
||||
// d.EntryType = e.Uint16(b[4:])
|
||||
// d.Size = e.Uint16(b[6:])
|
||||
// }
|
||||
|
||||
// // xattr types
|
||||
// const (
|
||||
// XattrTypeUser = iota
|
||||
// XattrTypeTrusted
|
||||
// XattrTypeSecurity
|
||||
// )
|
||||
|
||||
// var xattrPrefix = map[int]string{
|
||||
// XattrTypeUser: "user.",
|
||||
// XattrTypeTrusted: "trusted.",
|
||||
// XattrTypeSecurity: "security.",
|
||||
// }
|
||||
|
||||
// type Xattr struct {
|
||||
// Type uint16
|
||||
// FullName string
|
||||
// Value []byte
|
||||
// }
|
||||
|
||||
// func XattrFromAttr(attr string, val []byte) Xattr {
|
||||
// for typ, prefix := range xattrPrefix {
|
||||
// if !strings.HasPrefix(attr, prefix) {
|
||||
// continue
|
||||
// }
|
||||
// return Xattr{
|
||||
// Type: uint16(typ),
|
||||
// FullName: strings.TrimPrefix(attr, prefix),
|
||||
// Value: val,
|
||||
// }
|
||||
// }
|
||||
// return Xattr{}
|
||||
// }
|
||||
|
||||
// type xattrId struct {
|
||||
// Xattr uint64
|
||||
// Count uint32
|
||||
// Size uint32
|
||||
// }
|
||||
|
||||
// func writeIdTable(w io.WriteSeeker, ids []uint32) (start int64, err error) {
|
||||
// metaOff, err := w.Seek(0, io.SeekCurrent)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// var buf bytes.Buffer
|
||||
// if err := binary.Write(&buf, binary.LittleEndian, ids); err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
|
||||
// if err := binary.Write(w, binary.LittleEndian, uint16(buf.Len())|0x8000); err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// if _, err := io.Copy(w, &buf); err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// off, err := w.Seek(0, io.SeekCurrent)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// return off, binary.Write(w, binary.LittleEndian, metaOff)
|
||||
// }
|
||||
|
||||
// type fullDirEntry struct {
|
||||
// startBlock uint32
|
||||
// offset uint16
|
||||
// inodeNumber uint32
|
||||
// entryType uint16
|
||||
// name string
|
||||
// }
|
||||
|
||||
// const (
|
||||
// magic = 0x73717368
|
||||
// dataBlockSize = 131072
|
||||
// metadataBlockSize = 8192
|
||||
// majorVersion = 4
|
||||
// minorVersion = 0
|
||||
// )
|
||||
|
||||
// type Writer struct {
|
||||
// // Root represents the file system root. Like all directories, Flush must be
|
||||
// // called precisely once.
|
||||
// Root *Directory
|
||||
|
||||
// xattrs []Xattr
|
||||
// xattrIds []xattrId
|
||||
|
||||
// w io.WriteSeeker
|
||||
|
||||
// sb superblock
|
||||
// inodeBuf bytes.Buffer
|
||||
// dirBuf bytes.Buffer
|
||||
|
||||
// writeInodeNumTo map[string][]int64
|
||||
// }
|
||||
|
||||
// // TODO: document what this is doing and what it is used for
|
||||
// func slog(block uint32) uint16 {
|
||||
// for i := uint16(12); i <= 20; i++ {
|
||||
// if block == (1 << i) {
|
||||
// return i
|
||||
// }
|
||||
// }
|
||||
// return 0
|
||||
// }
|
||||
|
||||
// // filesystemFlags returns flags for a SquashFS file system created by this
|
||||
// // package (disabling most features for now).
|
||||
// func filesystemFlags() uint16 {
|
||||
// const (
|
||||
// noI = 1 << iota // uncompressed metadata
|
||||
// noD // uncompressed data
|
||||
// _
|
||||
// noF // uncompressed fragments
|
||||
// noFrag // never use fragments
|
||||
// alwaysFrag // always use fragments
|
||||
// duplicateChecking // de-duplication
|
||||
// exportable // exportable via NFS
|
||||
// noX // uncompressed xattrs
|
||||
// noXattr // no xattrs
|
||||
// compopt // compressor-specific options present?
|
||||
// )
|
||||
// return noI | noF | noFrag | noX | noXattr
|
||||
// }
|
||||
|
||||
// // NewWriter returns a Writer which will write a SquashFS file system image to w
|
||||
// // once Flush is called.
|
||||
// //
|
||||
// // Create new files and directories with the corresponding methods on the Root
|
||||
// // directory of the Writer.
|
||||
// //
|
||||
// // File data is written to w even before Flush is called.
|
||||
// func NewWriter(w io.WriteSeeker, mkfsTime time.Time) (*Writer, error) {
|
||||
// // Skip over superblock to the data area, we come back to the superblock
|
||||
// // when flushing.
|
||||
// if _, err := w.Seek(96, io.SeekStart); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// wr := &Writer{
|
||||
// w: w,
|
||||
// sb: superblock{
|
||||
// Magic: magic,
|
||||
// MkfsTime: int32(mkfsTime.Unix()),
|
||||
// BlockSize: dataBlockSize,
|
||||
// Fragments: 0,
|
||||
// Compression: zlibCompression,
|
||||
// BlockLog: slog(dataBlockSize),
|
||||
// Flags: filesystemFlags(),
|
||||
// NoIds: 1, // just one uid/gid mapping (for root)
|
||||
// Major: majorVersion,
|
||||
// Minor: minorVersion,
|
||||
// XattrIdTableStart: -1, // not present
|
||||
// LookupTableStart: -1, // not present
|
||||
// },
|
||||
// writeInodeNumTo: make(map[string][]int64),
|
||||
// }
|
||||
// wr.Root = &Directory{
|
||||
// w: wr,
|
||||
// name: "", // root
|
||||
// modTime: mkfsTime,
|
||||
// }
|
||||
// return wr, nil
|
||||
// }
|
||||
|
||||
// // Directory represents a SquashFS directory.
|
||||
// type Directory struct {
|
||||
// w *Writer
|
||||
// name string
|
||||
// modTime time.Time
|
||||
// dirEntries []fullDirEntry
|
||||
// parent *Directory
|
||||
// }
|
||||
|
||||
// func (d *Directory) path() string {
|
||||
// if d.parent == nil {
|
||||
// return d.name
|
||||
// }
|
||||
// return filepath.Join(d.parent.path(), d.name)
|
||||
// }
|
||||
|
||||
// type file struct {
|
||||
// w *Writer
|
||||
// d *Directory
|
||||
// off int64
|
||||
// size uint32
|
||||
// name string
|
||||
// modTime time.Time
|
||||
// mode uint16
|
||||
|
||||
// // buf accumulates at least dataBlockSize bytes, at which point a new block
|
||||
// // is being written.
|
||||
// buf bytes.Buffer
|
||||
|
||||
// // blocksizes stores, for each block of dataBlockSize bytes (uncompressed),
|
||||
// // the number of bytes the block compressed down to.
|
||||
// blocksizes []uint32
|
||||
|
||||
// // compBuf is used for holding a block during compression to avoid memory
|
||||
// // allocations.
|
||||
// compBuf *bytes.Buffer
|
||||
// // zlibWriter is re-used for each compressed block
|
||||
// zlibWriter *zlib.Writer
|
||||
|
||||
// xattrRef uint32
|
||||
// }
|
||||
|
||||
// // Directory creates a new directory with the specified name and modTime.
|
||||
// func (d *Directory) Directory(name string, modTime time.Time) *Directory {
|
||||
// return &Directory{
|
||||
// w: d.w,
|
||||
// name: name,
|
||||
// modTime: modTime,
|
||||
// parent: d,
|
||||
// }
|
||||
// }
|
||||
|
||||
// // File creates a file with the specified name, modTime and mode. The returned
|
||||
// // io.WriterCloser must be closed after writing the file.
|
||||
// func (d *Directory) File(name string, modTime time.Time, mode uint16, xattrs []Xattr) (io.WriteCloser, error) {
|
||||
// off, err := d.w.w.Seek(0, io.SeekCurrent)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// // zlib.BestSpeed results in only a 2x slow-down over no compression
|
||||
// // (compared to >4x slow-down with DefaultCompression), but generates
|
||||
// // results which are in the same ball park (10% larger).
|
||||
// zw, err := zlib.NewWriterLevel(nil, zlib.BestSpeed)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
// xattrRef := uint32(invalidXattr)
|
||||
// if len(xattrs) > 0 {
|
||||
// xattrRef = uint32(len(d.w.xattrs))
|
||||
// d.w.xattrs = append(d.w.xattrs, xattrs[0]) // TODO: support multiple
|
||||
// size := len(xattrs[0].FullName) + len(xattrs[0].Value)
|
||||
// d.w.xattrIds = append(d.w.xattrIds, xattrId{
|
||||
// // Xattr is populated in writeXattrTables
|
||||
// Count: 1, // TODO: support multiple
|
||||
// Size: uint32(size),
|
||||
// })
|
||||
// }
|
||||
// return &file{
|
||||
// w: d.w,
|
||||
// d: d,
|
||||
// off: off,
|
||||
// name: name,
|
||||
// modTime: modTime,
|
||||
// mode: mode,
|
||||
// compBuf: bytes.NewBuffer(make([]byte, dataBlockSize)),
|
||||
// zlibWriter: zw,
|
||||
// xattrRef: xattrRef,
|
||||
// }, nil
|
||||
// }
|
||||
|
||||
// // Symlink creates a symbolic link from newname to oldname with the specified
|
||||
// // modTime and mode.
|
||||
// func (d *Directory) Symlink(oldname, newname string, modTime time.Time, mode os.FileMode) error {
|
||||
// startBlock := d.w.inodeBuf.Len() / metadataBlockSize
|
||||
// offset := d.w.inodeBuf.Len() - startBlock*metadataBlockSize
|
||||
|
||||
// if err := binary.Write(&d.w.inodeBuf, binary.LittleEndian, symlinkInodeHeader{
|
||||
// inodeHeader: inodeHeader{
|
||||
// InodeType: symlinkType,
|
||||
// Mode: uint16(mode),
|
||||
// Uid: 0,
|
||||
// Gid: 0,
|
||||
// Mtime: int32(modTime.Unix()),
|
||||
// InodeNumber: d.w.sb.Inodes + 1,
|
||||
// },
|
||||
// Nlink: 1, // TODO(later): when is this not 1?
|
||||
// SymlinkSize: uint32(len(oldname)),
|
||||
// }); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, err := d.w.inodeBuf.Write([]byte(oldname)); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// d.dirEntries = append(d.dirEntries, fullDirEntry{
|
||||
// startBlock: uint32(startBlock),
|
||||
// offset: uint16(offset),
|
||||
// inodeNumber: d.w.sb.Inodes + 1,
|
||||
// entryType: symlinkType,
|
||||
// name: newname,
|
||||
// })
|
||||
|
||||
// d.w.sb.Inodes++
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // Flush writes directory entries and creates inodes for the directory.
|
||||
// func (d *Directory) Flush() error {
|
||||
// countByStartBlock := make(map[uint32]uint32)
|
||||
// for _, de := range d.dirEntries {
|
||||
// countByStartBlock[de.startBlock]++
|
||||
// }
|
||||
|
||||
// dirBufStartBlock := d.w.dirBuf.Len() / metadataBlockSize
|
||||
// dirBufOffset := d.w.dirBuf.Len()
|
||||
|
||||
// currentBlock := int64(-1)
|
||||
// currentInodeOffset := int64(-1)
|
||||
// var subdirs int
|
||||
// for _, de := range d.dirEntries {
|
||||
// if de.entryType == dirType {
|
||||
// subdirs++
|
||||
// }
|
||||
// if int64(de.startBlock) != currentBlock {
|
||||
// dh := dirHeader{
|
||||
// Count: countByStartBlock[de.startBlock] - 1,
|
||||
// StartBlock: de.startBlock * (metadataBlockSize + 2),
|
||||
// InodeOffset: de.inodeNumber,
|
||||
// }
|
||||
// if err := binary.Write(&d.w.dirBuf, binary.LittleEndian, &dh); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// currentBlock = int64(de.startBlock)
|
||||
// currentInodeOffset = int64(de.inodeNumber)
|
||||
// }
|
||||
// if err := binary.Write(&d.w.dirBuf, binary.LittleEndian, &dirEntry{
|
||||
// Offset: de.offset,
|
||||
// InodeNumber: int16(de.inodeNumber - uint32(currentInodeOffset)),
|
||||
// EntryType: de.entryType,
|
||||
// Size: uint16(len(de.name) - 1),
|
||||
// }); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, err := d.w.dirBuf.Write([]byte(de.name)); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
// startBlock := d.w.inodeBuf.Len() / metadataBlockSize
|
||||
// offset := d.w.inodeBuf.Len() - startBlock*metadataBlockSize
|
||||
// inodeBufOffset := d.w.inodeBuf.Len()
|
||||
|
||||
// // parentInodeOffset is the offset (in bytes) of the ParentInode field
|
||||
// // within a dirInodeHeader or ldirInodeHeader
|
||||
// var parentInodeOffset int64
|
||||
|
||||
// if len(d.dirEntries) > 256 ||
|
||||
// d.w.dirBuf.Len()-dirBufOffset > metadataBlockSize {
|
||||
// parentInodeOffset = (2 + 2 + 2 + 2 + 4 + 4) + 4 + 4 + 4
|
||||
// if err := binary.Write(&d.w.inodeBuf, binary.LittleEndian, ldirInodeHeader{
|
||||
// inodeHeader: inodeHeader{
|
||||
// InodeType: ldirType,
|
||||
// Mode: unix.S_IRUSR | unix.S_IWUSR | unix.S_IXUSR |
|
||||
// unix.S_IRGRP | unix.S_IXGRP |
|
||||
// unix.S_IROTH | unix.S_IXOTH,
|
||||
// Uid: 0,
|
||||
// Gid: 0,
|
||||
// Mtime: int32(d.modTime.Unix()),
|
||||
// InodeNumber: d.w.sb.Inodes + 1,
|
||||
// },
|
||||
|
||||
// Nlink: uint32(subdirs + 2 - 1), // + 2 for . and ..
|
||||
// FileSize: uint32(d.w.dirBuf.Len()-dirBufOffset) + 3,
|
||||
// StartBlock: uint32(dirBufStartBlock * (metadataBlockSize + 2)),
|
||||
// ParentInode: d.w.sb.Inodes + 2, // invalid
|
||||
// Icount: 0, // no directory index
|
||||
// Offset: uint16(dirBufOffset - dirBufStartBlock*metadataBlockSize),
|
||||
// Xattr: invalidXattr,
|
||||
// }); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// } else {
|
||||
// parentInodeOffset = (2 + 2 + 2 + 2 + 4 + 4) + 4 + 4 + 2 + 2
|
||||
// if err := binary.Write(&d.w.inodeBuf, binary.LittleEndian, dirInodeHeader{
|
||||
// inodeHeader: inodeHeader{
|
||||
// InodeType: dirType,
|
||||
// Mode: unix.S_IRUSR | unix.S_IWUSR | unix.S_IXUSR |
|
||||
// unix.S_IRGRP | unix.S_IXGRP |
|
||||
// unix.S_IROTH | unix.S_IXOTH,
|
||||
// Uid: 0,
|
||||
// Gid: 0,
|
||||
// Mtime: int32(d.modTime.Unix()),
|
||||
// InodeNumber: d.w.sb.Inodes + 1,
|
||||
// },
|
||||
// StartBlock: uint32(dirBufStartBlock * (metadataBlockSize + 2)),
|
||||
// Nlink: uint32(subdirs + 2 - 1), // + 2 for . and ..
|
||||
// FileSize: uint16(d.w.dirBuf.Len()-dirBufOffset) + 3,
|
||||
// Offset: uint16(dirBufOffset - dirBufStartBlock*metadataBlockSize),
|
||||
// ParentInode: d.w.sb.Inodes + 2, // invalid
|
||||
// }); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
// path := d.path()
|
||||
// for _, offset := range d.w.writeInodeNumTo[path] {
|
||||
// // Directly manipulating unread data in bytes.Buffer via Bytes(), as per
|
||||
// // https://groups.google.com/d/msg/golang-nuts/1ON9XVQ1jXE/8j9RaeSYxuEJ
|
||||
// b := d.w.inodeBuf.Bytes()
|
||||
// binary.LittleEndian.PutUint32(b[offset:offset+4], d.w.sb.Inodes+1)
|
||||
// }
|
||||
|
||||
// if d.parent != nil {
|
||||
// parentPath := filepath.Dir(d.path())
|
||||
// if parentPath == "." {
|
||||
// parentPath = ""
|
||||
// }
|
||||
// d.w.writeInodeNumTo[parentPath] = append(d.w.writeInodeNumTo[parentPath], int64(inodeBufOffset)+parentInodeOffset)
|
||||
// d.parent.dirEntries = append(d.parent.dirEntries, fullDirEntry{
|
||||
// startBlock: uint32(startBlock),
|
||||
// offset: uint16(offset),
|
||||
// inodeNumber: d.w.sb.Inodes + 1,
|
||||
// entryType: dirType,
|
||||
// name: d.name,
|
||||
// })
|
||||
// } else { // root
|
||||
// d.w.sb.RootInode = Inode((startBlock*(metadataBlockSize+2))<<16 | offset)
|
||||
// }
|
||||
|
||||
// d.w.sb.Inodes++
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // Write implements io.Writer
|
||||
// func (f *file) Write(p []byte) (n int, err error) {
|
||||
// n, err = f.buf.Write(p)
|
||||
// if n > 0 {
|
||||
// // Keep track of the uncompressed file size.
|
||||
// f.size += uint32(n)
|
||||
// for f.buf.Len() >= dataBlockSize {
|
||||
// if err := f.writeBlock(); err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return n, err
|
||||
// }
|
||||
|
||||
// func (f *file) writeBlock() error {
|
||||
// n := f.buf.Len()
|
||||
// if n > dataBlockSize {
|
||||
// n = dataBlockSize
|
||||
// }
|
||||
// // Feed dataBlockSize bytes to the compressor
|
||||
// b := f.buf.Bytes()
|
||||
// block := b[:n]
|
||||
// rest := b[n:]
|
||||
// /*
|
||||
// f.compBuf.Reset()
|
||||
// f.zlibWriter.Reset(f.compBuf)
|
||||
// if _, err := f.zlibWriter.Write(block); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := f.zlibWriter.Close(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// size := f.compBuf.Len()
|
||||
// if size > len(block) {
|
||||
// // Copy uncompressed data: Linux returns i/o errors when it encounters a
|
||||
// // compressed block which is larger than the uncompressed data:
|
||||
// // https://github.com/torvalds/linux/blob/3ca24ce9ff764bc27bceb9b2fd8ece74846c3fd3/fs/squashfs/block.c#L150
|
||||
// size = len(block) | (1 << 24) // SQUASHFS_COMPRESSED_BIT_BLOCK
|
||||
// if _, err := f.w.w.Write(block); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// } else {
|
||||
// if _, err := io.Copy(f.w.w, f.compBuf); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
// */
|
||||
// // Copy uncompressed data: Linux returns i/o errors when it encounters a
|
||||
// // compressed block which is larger than the uncompressed data:
|
||||
// // https://github.com/torvalds/linux/blob/3ca24ce9ff764bc27bceb9b2fd8ece74846c3fd3/fs/squashfs/block.c#L150
|
||||
// size := len(block) | (1 << 24) // SQUASHFS_COMPRESSED_BIT_BLOCK
|
||||
// if _, err := f.w.w.Write(block); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// f.blocksizes = append(f.blocksizes, uint32(size))
|
||||
|
||||
// // Keep the rest in f.buf for the next write
|
||||
// copy(b, rest)
|
||||
// f.buf.Truncate(len(rest))
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// // Close implements io.Closer
|
||||
// func (f *file) Close() error {
|
||||
// for f.buf.Len() > 0 {
|
||||
// if err := f.writeBlock(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
// startBlock := f.w.inodeBuf.Len() / metadataBlockSize
|
||||
// offset := f.w.inodeBuf.Len() - startBlock*metadataBlockSize
|
||||
|
||||
// if err := binary.Write(&f.w.inodeBuf, binary.LittleEndian, lregInodeHeader{
|
||||
// inodeHeader: inodeHeader{
|
||||
// InodeType: lregType,
|
||||
// Mode: f.mode,
|
||||
// Uid: 0,
|
||||
// Gid: 0,
|
||||
// Mtime: int32(f.modTime.Unix()),
|
||||
// InodeNumber: f.w.sb.Inodes + 1,
|
||||
// },
|
||||
// StartBlock: uint64(f.off),
|
||||
// FileSize: uint64(f.size),
|
||||
// Nlink: 1,
|
||||
// Fragment: invalidFragment,
|
||||
// Offset: 0,
|
||||
// Xattr: f.xattrRef,
|
||||
// }); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if err := binary.Write(&f.w.inodeBuf, binary.LittleEndian, f.blocksizes); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// f.d.dirEntries = append(f.d.dirEntries, fullDirEntry{
|
||||
// startBlock: uint32(startBlock),
|
||||
// offset: uint16(offset),
|
||||
// inodeNumber: f.w.sb.Inodes + 1,
|
||||
// entryType: fileType,
|
||||
// name: f.name,
|
||||
// })
|
||||
|
||||
// f.w.sb.Inodes++
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func writeXattr(w io.Writer, xattrs []Xattr) error {
|
||||
// for _, attr := range xattrs {
|
||||
// if err := binary.Write(w, binary.LittleEndian, struct {
|
||||
// Type uint16
|
||||
// NameSize uint16
|
||||
// }{
|
||||
// Type: attr.Type,
|
||||
// NameSize: uint16(len(attr.FullName)),
|
||||
// }); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, err := w.Write([]byte(attr.FullName)); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if err := binary.Write(w, binary.LittleEndian, struct {
|
||||
// ValSize uint32
|
||||
// }{
|
||||
// ValSize: uint32(len(attr.Value)),
|
||||
// }); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// if _, err := w.Write(attr.Value); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// type xattrTableHeader struct {
|
||||
// XattrTableStart uint64
|
||||
// XattrIds uint32
|
||||
// Unused uint32
|
||||
// }
|
||||
|
||||
// func (w *Writer) writeXattrTables() (int64, error) {
|
||||
// if len(w.xattrs) == 0 {
|
||||
// return -1, nil
|
||||
// }
|
||||
// off, err := w.w.Seek(0, io.SeekCurrent)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// xattrTableStart := uint64(off)
|
||||
|
||||
// var xattrBuf bytes.Buffer
|
||||
// if err := writeXattr(&xattrBuf, w.xattrs); err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// xattrBlocks := (xattrBuf.Len() + (metadataBlockSize - 1)) / metadataBlockSize
|
||||
|
||||
// if err := w.writeMetadataChunks(&xattrBuf); err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
|
||||
// // write xattr id table
|
||||
// off, err = w.w.Seek(0, io.SeekCurrent)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// idTableOff := uint64(off)
|
||||
// var xattrIdBuf bytes.Buffer
|
||||
// size := uint64(0)
|
||||
// for _, id := range w.xattrIds {
|
||||
// id.Xattr = uint64(size)
|
||||
// size += uint64(id.Size) + 8 /* sizeof(Type+NameSize+ValSize) */
|
||||
// if err := binary.Write(&xattrIdBuf, binary.LittleEndian, id); err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// }
|
||||
// if err := w.writeMetadataChunks(&xattrIdBuf); err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
|
||||
// // xattr table header
|
||||
// off, err = w.w.Seek(0, io.SeekCurrent)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// if err := binary.Write(w.w, binary.LittleEndian, xattrTableHeader{
|
||||
// XattrTableStart: xattrTableStart,
|
||||
// XattrIds: uint32(len(w.xattrs)),
|
||||
// }); err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// // write block index
|
||||
// for i := 0; i < xattrBlocks; i++ {
|
||||
// if err := binary.Write(w.w, binary.LittleEndian, struct {
|
||||
// BlockOffset uint64
|
||||
// }{
|
||||
// BlockOffset: idTableOff + (uint64(i) * (8192 + 2 /* sizeof(uint16) */)),
|
||||
// }); err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// }
|
||||
// return off, nil
|
||||
// }
|
||||
|
||||
// // writeMetadataChunks copies from r to w in blocks of metadataBlockSize bytes
|
||||
// // each, prefixing each block with a uint16 length header, setting the
|
||||
// // uncompressed bit.
|
||||
// func (w *Writer) writeMetadataChunks(r io.Reader) error {
|
||||
// buf := make([]byte, metadataBlockSize)
|
||||
// for {
|
||||
// buf = buf[:metadataBlockSize]
|
||||
// n, err := r.Read(buf)
|
||||
// if err != nil {
|
||||
// if err == io.EOF { // done
|
||||
// return nil
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
// buf = buf[:n]
|
||||
// if err := binary.Write(w.w, binary.LittleEndian, uint16(len(buf))|0x8000); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, err := w.w.Write(buf); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Flush writes the SquashFS file system. The Writer must not be used after
|
||||
// // calling Flush.
|
||||
// func (w *Writer) Flush() error {
|
||||
// // (1) superblock will be written later
|
||||
|
||||
// // (2) compressor-specific options omitted
|
||||
|
||||
// // (3) data has already been written
|
||||
|
||||
// // (4) write inode table
|
||||
// off, err := w.w.Seek(0, io.SeekCurrent)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// w.sb.InodeTableStart = off
|
||||
|
||||
// if err := w.writeMetadataChunks(&w.inodeBuf); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// // (5) write directory table
|
||||
// off, err = w.w.Seek(0, io.SeekCurrent)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// w.sb.DirectoryTableStart = off
|
||||
|
||||
// if err := w.writeMetadataChunks(&w.dirBuf); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// // (6) fragment table omitted
|
||||
// off, err = w.w.Seek(0, io.SeekCurrent)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// w.sb.FragmentTableStart = off
|
||||
|
||||
// // (7) export table omitted
|
||||
|
||||
// // (8) write uid/gid lookup table
|
||||
// idTableStart, err := writeIdTable(w.w, []uint32{0})
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// w.sb.IdTableStart = idTableStart
|
||||
|
||||
// // (9) xattr table
|
||||
// off, err = w.writeXattrTables()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// w.sb.XattrIdTableStart = off
|
||||
|
||||
// off, err = w.w.Seek(0, io.SeekCurrent)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// w.sb.BytesUsed = off
|
||||
|
||||
// // Pad to 4096, required for the kernel to be able to access all pages
|
||||
// if pad := off % 4096; pad > 0 {
|
||||
// padding := make([]byte, 4096-pad)
|
||||
// if _, err := w.w.Write(padding); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
// // (1) Write superblock
|
||||
// if _, err := w.w.Seek(0, io.SeekStart); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// return binary.Write(w.w, binary.LittleEndian, &w.sb)
|
||||
// }
|
||||
Reference in New Issue
Block a user