forked from CalebQ42/squashfs
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Inode parsing and directory decoding
- Loading branch information
Showing
7 changed files
with
342 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package data | ||
|
||
type FullReader struct{ | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package squashfs | ||
|
||
import ( | ||
"errors" | ||
"io" | ||
|
||
"github.com/CalebQ42/squashfs/internal/data" | ||
"github.com/CalebQ42/squashfs/internal/metadata" | ||
"github.com/CalebQ42/squashfs/internal/toreader" | ||
"github.com/CalebQ42/squashfs/squashfs/directory" | ||
"github.com/CalebQ42/squashfs/squashfs/inode" | ||
) | ||
|
||
type Base struct { | ||
Inode *inode.Inode | ||
Name string | ||
} | ||
|
||
func (r *Reader) baseFromInode(i *inode.Inode, name string) *Base { | ||
return &Base{Inode: i, Name: name} | ||
} | ||
|
||
func (r *Reader) baseFromEntry(e directory.Entry) (*Base, error) { | ||
rdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.sup.InodeTableStart)+int64(e.BlockStart)), r.d) | ||
rdr.Read(make([]byte, e.Offset)) | ||
in, err := inode.Read(rdr, r.sup.BlockSize) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &Base{Inode: in, Name: e.Name}, nil | ||
} | ||
|
||
func (b *Base) Uid(r *Reader) (uint32, error) { | ||
return r.id(b.Inode.UidInd) | ||
} | ||
|
||
func (b *Base) Gid(r *Reader) (uint32, error) { | ||
return r.id(b.Inode.GidInd) | ||
} | ||
|
||
func (b *Base) IsDir() bool { | ||
return b.Inode.Type == inode.Dir || b.Inode.Type == inode.EDir | ||
} | ||
|
||
func (b *Base) ToDir(r *Reader) (*Directory, error) { | ||
return r.directoryFromInode(b.Inode, b.Name) | ||
} | ||
|
||
func (b *Base) IsRegular() bool { | ||
return b.Inode.Type == inode.Fil || b.Inode.Type == inode.EFil | ||
} | ||
|
||
func (b *Base) GetRegFileReaders(r *Reader) (io.ReadCloser, error) { | ||
if !b.IsRegular() { | ||
return nil, errors.New("not a regular file") | ||
} | ||
var blockStart uint64 | ||
var fragIndex uint32 | ||
var fragOffset uint32 | ||
var sizes []uint32 | ||
if b.Inode.Type == inode.Fil { | ||
blockStart = uint64(b.Inode.Data.(inode.File).BlockStart) | ||
fragIndex = b.Inode.Data.(inode.File).FragInd | ||
fragOffset = b.Inode.Data.(inode.File).FragOffset | ||
sizes = b.Inode.Data.(inode.File).BlockSizes | ||
} else { | ||
blockStart = b.Inode.Data.(inode.EFile).BlockStart | ||
fragIndex = b.Inode.Data.(inode.EFile).FragInd | ||
fragOffset = b.Inode.Data.(inode.EFile).FragOffset | ||
sizes = b.Inode.Data.(inode.EFile).BlockSizes | ||
} | ||
var frag *data.Reader | ||
if fragIndex != 0xFFFFFFFF { | ||
ent, err := r.fragEntry(fragIndex) | ||
if err != nil { | ||
return nil, err | ||
} | ||
frag = data.NewReader(toreader.NewReader(r.r, int64(ent.start)), r.d, []uint32{ent.size}) | ||
frag.Read(make([]byte, fragOffset)) | ||
} | ||
out := data.NewReader(toreader.NewReader(r.r, int64(blockStart)), r.d, sizes) | ||
if frag != nil { | ||
out.AddFrag(out) | ||
} | ||
//TODO: implement and add full reader | ||
return out, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package squashfs | ||
|
||
import ( | ||
"errors" | ||
"io/fs" | ||
"path/filepath" | ||
"slices" | ||
"strings" | ||
|
||
"github.com/CalebQ42/squashfs/internal/metadata" | ||
"github.com/CalebQ42/squashfs/internal/toreader" | ||
"github.com/CalebQ42/squashfs/squashfs/directory" | ||
"github.com/CalebQ42/squashfs/squashfs/inode" | ||
) | ||
|
||
type Directory struct { | ||
Base | ||
Entries []directory.Entry | ||
} | ||
|
||
func (r *Reader) directoryFromInode(i *inode.Inode, name string) (*Directory, error) { | ||
var blockStart uint32 | ||
var size uint32 | ||
var offset uint16 | ||
switch i.Type { | ||
case inode.Dir: | ||
blockStart = i.Data.(inode.Directory).BlockStart | ||
size = uint32(i.Data.(inode.Directory).Size) | ||
offset = i.Data.(inode.Directory).Offset | ||
case inode.EDir: | ||
blockStart = i.Data.(inode.EDirectory).BlockStart | ||
size = i.Data.(inode.EDirectory).Size | ||
offset = i.Data.(inode.EDirectory).Offset | ||
default: | ||
return nil, errors.New("not a directory") | ||
} | ||
dirRdr := metadata.NewReader(toreader.NewReader(r.r, int64(r.sup.DirTableStart)+int64(blockStart)), r.d) | ||
_, err := dirRdr.Read(make([]byte, offset)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
entries, err := directory.ReadDirectory(dirRdr, size) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &Directory{ | ||
Base: *r.baseFromInode(i, name), | ||
Entries: entries, | ||
}, nil | ||
} | ||
|
||
func (r *Reader) directoryFromRef(ref uint64, name string) (*Directory, error) { | ||
in, err := r.inodeFromRef(ref) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return r.directoryFromInode(in, name) | ||
} | ||
|
||
func (d *Directory) Open(r *Reader, path string) (*Base, error) { | ||
path = filepath.Clean(path) | ||
if path == "." || path == "" { | ||
return &d.Base, nil | ||
} | ||
split := strings.Split(path, "/") | ||
i, found := slices.BinarySearchFunc(d.Entries, split[0], func(e directory.Entry, name string) int { | ||
return strings.Compare(e.Name, name) | ||
}) | ||
if !found { | ||
return nil, fs.ErrNotExist | ||
} | ||
b, err := r.baseFromEntry(d.Entries[i]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if len(split) == 1 { | ||
return b, nil | ||
} else if !b.IsDir() { | ||
return nil, fs.ErrNotExist | ||
} | ||
dir, err := b.ToDir(r) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return dir.Open(r, strings.Join(split[1:], "/")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package directory | ||
|
||
import ( | ||
"encoding/binary" | ||
"io" | ||
) | ||
|
||
type header struct { | ||
Count uint32 | ||
BlockStart uint32 | ||
Num uint32 | ||
} | ||
|
||
type decEntry struct { | ||
Offset uint16 | ||
NumOffset int16 | ||
InodeType uint16 | ||
NameSize uint16 | ||
// Name []byte (not decoded along with decEntry) | ||
} | ||
|
||
type Entry struct { | ||
Name string | ||
BlockStart uint32 | ||
Offset uint16 | ||
InodeType uint16 | ||
} | ||
|
||
func ReadDirectory(r io.Reader, size uint32) (out []Entry, err error) { | ||
size -= 3 | ||
var curRead uint32 | ||
var h header | ||
var de decEntry | ||
for curRead < size { | ||
err = binary.Read(r, binary.LittleEndian, &h) | ||
if err != nil { | ||
return | ||
} | ||
curRead += 12 | ||
for i := uint32(0); i < h.Count+1 && curRead < size; i++ { | ||
err = binary.Read(r, binary.LittleEndian, &de) | ||
if err != nil { | ||
return | ||
} | ||
nameTmp := make([]byte, de.NameSize+1) | ||
err = binary.Read(r, binary.LittleEndian, &nameTmp) | ||
if err != nil { | ||
return | ||
} | ||
curRead += 8 + uint32(de.NameSize) + 1 | ||
out = append(out, Entry{ | ||
BlockStart: h.BlockStart, | ||
Offset: de.Offset, | ||
Name: string(nameTmp), | ||
InodeType: de.InodeType, | ||
}) | ||
} | ||
} | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package squashfs | ||
|
||
import ( | ||
"io" | ||
"net/http" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/CalebQ42/squashfs/squashfs/inode" | ||
) | ||
|
||
const ( | ||
squashfsURL = "https://darkstorm.tech/files/LinuxPATest.sfs" | ||
squashfsName = "LinuxPATest.sfs" | ||
|
||
// filePath = "PortableApps/Notepad++Portable/App/DefaultData/Config/contextMenu.xml" | ||
) | ||
|
||
func preTest(dir string) (fil *os.File, err error) { | ||
fil, err = os.Open(filepath.Join(dir, squashfsName)) | ||
if err != nil { | ||
_, err = os.Open(dir) | ||
if os.IsNotExist(err) { | ||
err = os.Mkdir(dir, 0755) | ||
} | ||
if err != nil { | ||
return | ||
} | ||
os.Remove(filepath.Join(dir, squashfsName)) | ||
fil, err = os.Create(filepath.Join(dir, squashfsName)) | ||
if err != nil { | ||
return | ||
} | ||
var resp *http.Response | ||
resp, err = http.DefaultClient.Get(squashfsURL) | ||
if err != nil { | ||
return | ||
} | ||
_, err = io.Copy(fil, resp.Body) | ||
if err != nil { | ||
return | ||
} | ||
} | ||
_, err = exec.LookPath("unsquashfs") | ||
if err != nil { | ||
return | ||
} | ||
_, err = exec.LookPath("mksquashfs") | ||
return | ||
} | ||
|
||
func TestReader(t *testing.T) { | ||
tmpDir := "../testing" | ||
fil, err := preTest(tmpDir) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
defer fil.Close() | ||
rdr, err := NewReader(fil) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
err = checkDir(rdr, rdr.root) | ||
t.Fatal(err) | ||
} | ||
|
||
func checkDir(rdr *Reader, d *Directory) error { | ||
for _, e := range d.Entries { | ||
if e.InodeType == inode.Dir { | ||
b, err := d.Open(rdr, e.Name) | ||
if err != nil { | ||
return err | ||
} | ||
d, err := b.ToDir(rdr) | ||
if err != nil { | ||
return err | ||
} | ||
err = checkDir(rdr, d) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
return nil | ||
} |