diff --git a/fuse2.go b/fuse2.go index 8338610..4140704 100644 --- a/fuse2.go +++ b/fuse2.go @@ -1,4 +1,4 @@ -package squashfs +package squashfuse import ( "bytes" @@ -6,98 +6,133 @@ import ( "errors" "io" - "github.com/CalebQ42/squashfs/internal/inode" + "github.com/CalebQ42/squashfs" + squashfslow "github.com/CalebQ42/squashfs/low" + "github.com/CalebQ42/squashfs/low/inode" "github.com/seaweedfs/fuse" "github.com/seaweedfs/fuse/fs" ) -// Mounts the archive to the given mountpoint using fuse2. Non-blocking. +type Fuse2Mount struct { + r *squashfslow.Reader + con *fuse.Conn + mountDone chan struct{} +} + +func NewFuse2Mount(r *squashfs.Reader) *Mount { + return NewMountFromLow(r.Low) +} + +func NewFuse2MountFromLow(r *squashfslow.Reader) *Mount { + return &Mount{r: r} +} + +// Mounts the archive to the given mountpoint using fuse3. Non-blocking. // If Unmount does not get called, the mount point must be unmounted using umount before the directory can be used again. -func (r *Reader) MountFuse2(mountpoint string) (err error) { - if r.con != nil { +func (m *Fuse2Mount) Mount(mountpoint string) (err error) { + if m.con != nil { return errors.New("squashfs archive already mounted") } - r.con2, err = fuse.Mount(mountpoint, fuse.ReadOnly()) + m.con, err = fuse.Mount(mountpoint, fuse.ReadOnly()) if err != nil { return } - <-r.con2.Ready - r.mount2Done = make(chan struct{}) + <-m.con.Ready + m.mountDone = make(chan struct{}) go func() { - fs.Serve(r.con2, squashFuse2{r: r}) - close(r.mount2Done) + fs.Serve(m.con, squashFuse2{r: m.r}) + close(m.mountDone) }() return } // Blocks until the mount ends. -// Fuse2 version. -func (r *Reader) MountWaitFuse2() { - if r.mount2Done != nil { - <-r.mount2Done +func (m *Fuse2Mount) MountWait() { + if m.mountDone != nil { + <-m.mountDone } } // Unmounts the archive. -// Fuse2 version. -func (r *Reader) UnmountFuse2() error { - if r.con != nil { - defer func() { r.con = nil }() - return r.con.Close() +func (m *Fuse2Mount) Unmount() error { + if m.con != nil { + defer func() { m.con = nil }() + return m.con.Close() } return errors.New("squashfs archive is not mounted") } type squashFuse2 struct { - r *Reader + r *squashfslow.Reader } func (s squashFuse2) Root() (fs.Node, error) { - return fileNode2{File: s.r.FS.File}, nil + return fileNode2{ + FileBase: &s.r.Root.FileBase, + r: s.r, + }, nil } type fileNode2 struct { - *File + *squashfslow.FileBase + r *squashfslow.Reader } func (f fileNode2) Attr(ctx context.Context, attr *fuse.Attr) error { - attr.Blocks = f.r.s.Size / 512 - if f.r.s.Size%512 > 0 { + var err error + attr.Gid, err = f.r.Id(f.Inode.GidInd) + if err != nil { + return err + } + attr.Uid, err = f.r.Id(f.Inode.UidInd) + if err != nil { + return err + } + attr.Size = f.Inode.Size() + attr.Blocks = f.Inode.Size() / 512 + if f.Inode.Size()%512 > 0 { attr.Blocks++ } - attr.Gid = f.r.ids[f.i.GidInd] - attr.Inode = uint64(f.i.Num) - attr.Mode = f.i.Mode() - attr.Nlink = f.i.LinkCount() - attr.Size = f.i.Size() - attr.Uid = f.r.ids[f.i.UidInd] + attr.Inode = uint64(f.Inode.Num) + attr.Mode = f.Inode.Mode() + attr.Nlink = f.Inode.LinkCount() return nil } func (f fileNode2) Id() uint64 { - return uint64(f.i.Num) + return uint64(f.Inode.Num) } func (f fileNode2) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) { - return f.SymlinkPath(), nil + switch f.Inode.Type { + case inode.Sym: + return string(f.Inode.Data.(inode.Symlink).Target), nil + case inode.ESym: + return string(f.Inode.Data.(inode.ESymlink).Target), nil + } + return "", nil } func (f fileNode2) Lookup(ctx context.Context, name string) (fs.Node, error) { - asFS, err := f.FS() + asFS, err := f.ToDir(f.r) if err != nil { return nil, fuse.ENOTDIR } - ret, err := asFS.OpenFile(name) + ret, err := asFS.Open(f.r, name) if err != nil { return nil, fuse.ENOENT } - return fileNode2{File: ret}, nil + return fileNode2{FileBase: ret}, nil } func (f fileNode2) ReadAll(ctx context.Context) ([]byte, error) { if f.IsRegular() { + rdr, err := f.GetFullReader(f.r) + if err != nil { + return nil, err + } var buf bytes.Buffer - _, err := f.WriteTo(&buf) + _, err = rdr.WriteTo(&buf) return buf.Bytes(), err } return nil, ENODATA @@ -105,8 +140,13 @@ func (f fileNode2) ReadAll(ctx context.Context) ([]byte, error) { func (f fileNode2) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { if f.IsRegular() { + rdr, err := f.GetReader(f.r) + if err != nil { + return err + } buf := make([]byte, req.Size) - n, err := f.File.ReadAt(buf, req.Offset) + rdr.Read(make([]byte, req.Offset)) + n, err := rdr.Read(buf) if err == io.EOF { resp.Data = buf[:n] } @@ -116,13 +156,13 @@ func (f fileNode2) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.R } func (f fileNode2) ReadDirAll(ctx context.Context) (out []fuse.Dirent, err error) { - asFS, err := f.FS() + asFS, err := f.ToDir(f.r) if err != nil { return nil, fuse.ENOTDIR } var t fuse.DirentType - for i := range asFS.e { - switch asFS.e[i].Type { + for i := range asFS.Entries { + switch asFS.Entries[i].InodeType { case inode.Fil: t = fuse.DT_File case inode.Dir: @@ -141,9 +181,9 @@ func (f fileNode2) ReadDirAll(ctx context.Context) (out []fuse.Dirent, err error t = fuse.DT_Unknown } out = append(out, fuse.Dirent{ - Inode: uint64(asFS.e[i].Num), + Inode: uint64(asFS.Entries[i].Num), Type: t, - Name: asFS.e[i].Name, + Name: asFS.Entries[i].Name, }) } return diff --git a/fuse3.go b/fuse3.go index e0b8b28..3977d59 100644 --- a/fuse3.go +++ b/fuse3.go @@ -1,4 +1,4 @@ -package squashfs +package squashfuse import ( "bytes" @@ -8,94 +8,131 @@ import ( "github.com/CalebQ42/fuse" "github.com/CalebQ42/fuse/fs" - "github.com/CalebQ42/squashfs/internal/inode" + "github.com/CalebQ42/squashfs" + squashfslow "github.com/CalebQ42/squashfs/low" + "github.com/CalebQ42/squashfs/low/inode" ) +type Mount struct { + r *squashfslow.Reader + con *fuse.Conn + mountDone chan struct{} +} + +func NewMount(r *squashfs.Reader) *Mount { + return NewMountFromLow(r.Low) +} + +func NewMountFromLow(r *squashfslow.Reader) *Mount { + return &Mount{r: r} +} + // Mounts the archive to the given mountpoint using fuse3. Non-blocking. // If Unmount does not get called, the mount point must be unmounted using umount before the directory can be used again. -func (r *Reader) Mount(mountpoint string) (err error) { - if r.con != nil { +func (m *Mount) Mount(mountpoint string) (err error) { + if m.con != nil { return errors.New("squashfs archive already mounted") } - r.con, err = fuse.Mount(mountpoint, fuse.ReadOnly()) + m.con, err = fuse.Mount(mountpoint, fuse.ReadOnly()) if err != nil { return } - <-r.con.Ready - r.mountDone = make(chan struct{}) + <-m.con.Ready + m.mountDone = make(chan struct{}) go func() { - fs.Serve(r.con, squashFuse{r: r}) - close(r.mountDone) + fs.Serve(m.con, squashFuse{r: m.r}) + close(m.mountDone) }() return } // Blocks until the mount ends. -func (r *Reader) MountWait() { - if r.mountDone != nil { - <-r.mountDone +func (m *Mount) MountWait() { + if m.mountDone != nil { + <-m.mountDone } } // Unmounts the archive. -func (r *Reader) Unmount() error { - if r.con != nil { - defer func() { r.con = nil }() - return r.con.Close() +func (m *Mount) Unmount() error { + if m.con != nil { + defer func() { m.con = nil }() + return m.con.Close() } return errors.New("squashfs archive is not mounted") } type squashFuse struct { - r *Reader + r *squashfslow.Reader } func (s squashFuse) Root() (fs.Node, error) { - return fileNode{File: s.r.FS.File}, nil + return fileNode{ + FileBase: &s.r.Root.FileBase, + r: s.r, + }, nil } type fileNode struct { - *File + *squashfslow.FileBase + r *squashfslow.Reader } func (f fileNode) Attr(ctx context.Context, attr *fuse.Attr) error { - attr.Blocks = f.r.s.Size / 512 - if f.r.s.Size%512 > 0 { + var err error + attr.Gid, err = f.r.Id(f.Inode.GidInd) + if err != nil { + return err + } + attr.Uid, err = f.r.Id(f.Inode.UidInd) + if err != nil { + return err + } + attr.Size = f.Inode.Size() + attr.Blocks = f.Inode.Size() / 512 + if f.Inode.Size()%512 > 0 { attr.Blocks++ } - attr.Gid = f.r.ids[f.i.GidInd] - attr.Inode = uint64(f.i.Num) - attr.Mode = f.i.Mode() - attr.Nlink = f.i.LinkCount() - attr.Size = f.i.Size() - attr.Uid = f.r.ids[f.i.UidInd] + attr.Inode = uint64(f.Inode.Num) + attr.Mode = f.Inode.Mode() + attr.Nlink = f.Inode.LinkCount() return nil } func (f fileNode) Id() uint64 { - return uint64(f.i.Num) + return uint64(f.Inode.Num) } func (f fileNode) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) { - return f.SymlinkPath(), nil + switch f.Inode.Type { + case inode.Sym: + return string(f.Inode.Data.(inode.Symlink).Target), nil + case inode.ESym: + return string(f.Inode.Data.(inode.ESymlink).Target), nil + } + return "", nil } func (f fileNode) Lookup(ctx context.Context, name string) (fs.Node, error) { - asFS, err := f.FS() + asFS, err := f.ToDir(f.r) if err != nil { return nil, fuse.ENOTDIR } - ret, err := asFS.OpenFile(name) + ret, err := asFS.Open(f.r, name) if err != nil { return nil, fuse.ENOENT } - return fileNode{File: ret}, nil + return fileNode{FileBase: ret}, nil } func (f fileNode) ReadAll(ctx context.Context) ([]byte, error) { if f.IsRegular() { + rdr, err := f.GetFullReader(f.r) + if err != nil { + return nil, err + } var buf bytes.Buffer - _, err := f.WriteTo(&buf) + _, err = rdr.WriteTo(&buf) return buf.Bytes(), err } return nil, ENODATA @@ -103,8 +140,13 @@ func (f fileNode) ReadAll(ctx context.Context) ([]byte, error) { func (f fileNode) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { if f.IsRegular() { + rdr, err := f.GetReader(f.r) + if err != nil { + return err + } buf := make([]byte, req.Size) - n, err := f.File.ReadAt(buf, req.Offset) + rdr.Read(make([]byte, req.Offset)) + n, err := rdr.Read(buf) if err == io.EOF { resp.Data = buf[:n] } @@ -114,13 +156,13 @@ func (f fileNode) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.Re } func (f fileNode) ReadDirAll(ctx context.Context) (out []fuse.Dirent, err error) { - asFS, err := f.FS() + asFS, err := f.ToDir(f.r) if err != nil { return nil, fuse.ENOTDIR } var t fuse.DirentType - for i := range asFS.e { - switch asFS.e[i].Type { + for i := range asFS.Entries { + switch asFS.Entries[i].InodeType { case inode.Fil: t = fuse.DT_File case inode.Dir: @@ -139,9 +181,9 @@ func (f fileNode) ReadDirAll(ctx context.Context) (out []fuse.Dirent, err error) t = fuse.DT_Unknown } out = append(out, fuse.Dirent{ - Inode: uint64(asFS.e[i].Num), + Inode: uint64(asFS.Entries[i].Num), Type: t, - Name: asFS.e[i].Name, + Name: asFS.Entries[i].Name, }) } return diff --git a/fuse_darwin.go b/fuse_darwin.go index 353aee1..e2b97fe 100644 --- a/fuse_darwin.go +++ b/fuse_darwin.go @@ -1,4 +1,4 @@ -package squashfs +package squashfuse import ( "golang.org/x/sys/unix" diff --git a/fuse_linux.go b/fuse_linux.go index 816560a..7112edd 100644 --- a/fuse_linux.go +++ b/fuse_linux.go @@ -1,4 +1,4 @@ -package squashfs +package squashfuse import "github.com/CalebQ42/fuse" diff --git a/fuse_windows.go b/fuse_windows.go index c50a696..af59099 100644 --- a/fuse_windows.go +++ b/fuse_windows.go @@ -1,3 +1,3 @@ -package squashfs +package squashfuse var ENODATA = windows.Errno(windows.ENODATA) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..95818f4 --- /dev/null +++ b/go.mod @@ -0,0 +1,20 @@ +module github.com/CalebQ42/squashfuse + +go 1.21.5 + +require ( + github.com/CalebQ42/fuse v0.1.0 + github.com/CalebQ42/squashfs v0.8.4 + github.com/seaweedfs/fuse v1.2.3 + golang.org/x/sys v0.15.0 +) + +require ( + github.com/klauspost/compress v1.17.4 // indirect + github.com/pierrec/lz4/v4 v4.1.19 // indirect + github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e // indirect + github.com/therootcompany/xz v1.0.1 // indirect + github.com/ulikunitz/xz v0.5.11 // indirect +) + +replace github.com/CalebQ42/squashfs => ../squashfs diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3a6fc98 --- /dev/null +++ b/go.sum @@ -0,0 +1,22 @@ +github.com/CalebQ42/fuse v0.1.0 h1:KLCNjun7zcd2kBNVFfH+SWJyhuwJdE0nhw5/q8K8HGQ= +github.com/CalebQ42/fuse v0.1.0/go.mod h1:pJpoKG03HJKVhsp8o0YQYqmfbFsr3Eowt90yQGQVO+4= +github.com/CalebQ42/squashfs v0.8.4 h1:HnthgRKuLliiMwYsPTSE/ln2zECt7UelYcbsUc5p+PA= +github.com/CalebQ42/squashfs v0.8.4/go.mod h1:CmGHRknB7BlYJ49qSTGpW8wnFcGFdZW0l6+qHOvFr5c= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= +github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4= +github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e h1:dCWirM5F3wMY+cmRda/B1BiPsFtmzXqV9b0hLWtVBMs= +github.com/rasky/go-lzo v0.0.0-20200203143853-96a758eda86e/go.mod h1:9leZcVcItj6m9/CfHY5Em/iBrCz7js8LcRQGTKEEv2M= +github.com/seaweedfs/fuse v1.2.3 h1:VH4VF9D3yvuQBILqDbNttz7Whjgo3JBLfpZeecmYfm0= +github.com/seaweedfs/fuse v1.2.3/go.mod h1:iwbDQv5BZACY54r6AO/6xsLNuMaYcBKSkLTZVfmK594= +github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= +github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= +github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= +github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=