158 lines
3.3 KiB
Go
158 lines
3.3 KiB
Go
package squashfs
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"io/fs"
|
|
|
|
"github.com/CalebQ42/squashfs/internal/directory"
|
|
"github.com/CalebQ42/squashfs/internal/inode"
|
|
)
|
|
|
|
type File struct {
|
|
i *inode.Inode
|
|
parent *FS
|
|
r *Reader
|
|
reader *fileReader
|
|
name string
|
|
dirsRead int
|
|
}
|
|
|
|
func (f FileInfo) File() (file *File, err error) {
|
|
file = &File{
|
|
name: f.name,
|
|
r: f.r,
|
|
parent: f.parent,
|
|
i: f.i,
|
|
}
|
|
file.reader, err = f.r.newFileReader(f.i)
|
|
return
|
|
}
|
|
|
|
func (r *Reader) newFileFromDirEntry(en *directory.Entry, parent *FS) (file *File, err error) {
|
|
file = &File{
|
|
name: en.Name,
|
|
r: r,
|
|
parent: parent,
|
|
}
|
|
file.i, err = r.getInodeFromEntry(en)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
file.reader, err = r.newFileReader(file.i)
|
|
return
|
|
}
|
|
|
|
func (f *File) Stat() (fs.FileInfo, error) {
|
|
return &FileInfo{
|
|
i: f.i,
|
|
name: f.name,
|
|
parent: f.parent,
|
|
r: f.r,
|
|
}, nil
|
|
}
|
|
|
|
func (f *File) Read(p []byte) (int, error) {
|
|
if f.i.Type == inode.FileType || f.i.Type == inode.ExtFileType {
|
|
if f.reader == nil {
|
|
return 0, fs.ErrClosed
|
|
}
|
|
return f.reader.Read(p)
|
|
}
|
|
return 0, errors.New("Can only read files")
|
|
}
|
|
|
|
func (f *File) WriteTo(w io.Writer) (int64, error) {
|
|
if f.i.Type == inode.FileType || f.i.Type == inode.ExtFileType {
|
|
if f.reader == nil {
|
|
return 0, fs.ErrClosed
|
|
}
|
|
return f.reader.WriteTo(w)
|
|
}
|
|
return 0, errors.New("Can only read files")
|
|
}
|
|
|
|
func (f *File) Close() error {
|
|
f.reader = nil
|
|
return nil
|
|
}
|
|
|
|
func (f *File) ReadDir(n int) ([]fs.DirEntry, error) {
|
|
if !f.IsDir() {
|
|
return nil, errors.New("File is not a directory")
|
|
}
|
|
ffs, err := f.FS()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var beg, end int
|
|
if n <= 0 {
|
|
beg, end = 0, len(ffs.entries)
|
|
} else {
|
|
beg, end = f.dirsRead, f.dirsRead+n
|
|
if end > len(ffs.entries) {
|
|
end = len(ffs.entries)
|
|
err = io.EOF
|
|
}
|
|
}
|
|
out := make([]fs.DirEntry, end-beg)
|
|
for i, ent := range ffs.entries[beg:end] {
|
|
out[i] = f.r.newDirEntry(ent, ffs)
|
|
}
|
|
return out, err
|
|
}
|
|
|
|
func (f File) FS() (*FS, error) {
|
|
if !f.IsDir() {
|
|
return nil, errors.New("File is not a directory")
|
|
}
|
|
ents, err := f.r.readDirFromInode(f.i)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &FS{
|
|
entries: ents,
|
|
parent: f.parent,
|
|
r: f.r,
|
|
}, nil
|
|
}
|
|
|
|
func (f File) IsDir() bool {
|
|
return f.i.Type == inode.DirType || f.i.Type == inode.ExtDirType
|
|
}
|
|
|
|
func (f File) Path()
|
|
|
|
//ReadDirFromInode returns a fully populated Directory from a given Inode.
|
|
//If the given inode is not a directory it returns an error.
|
|
func (r *Reader) readDirFromInode(i *inode.Inode) ([]*directory.Entry, error) {
|
|
var offset uint32
|
|
var metaOffset uint16
|
|
var size uint32
|
|
switch i.Type {
|
|
case inode.DirType:
|
|
offset = i.Info.(inode.Dir).DirectoryIndex
|
|
metaOffset = i.Info.(inode.Dir).DirectoryOffset
|
|
size = uint32(i.Info.(inode.Dir).DirectorySize)
|
|
case inode.ExtDirType:
|
|
offset = i.Info.(inode.ExtDir).DirectoryIndex
|
|
metaOffset = i.Info.(inode.ExtDir).DirectoryOffset
|
|
size = i.Info.(inode.ExtDir).DirectorySize
|
|
default:
|
|
return nil, errors.New("Not a directory inode")
|
|
}
|
|
br, err := r.newMetadataReader(int64(r.super.DirTableStart + uint64(offset)))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_, err = br.Seek(int64(metaOffset), io.SeekStart)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ents, err := directory.NewDirectory(br, size)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return ents, nil
|
|
}
|