Commit d4903fc7 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

fs: split zipfs into a example file and a test file

Change-Id: I9d9e7a098c0b2de29aad78a6938e39867522f919
parent e119fd48
......@@ -161,8 +161,9 @@
// the entire tree from an OnAdd method. Then, that in-memory tree
// structure becomes the source of truth. This means you Go-FUSE must
// remember Inodes even if the kernel is no longer interested in
// them. This is done by instantiating "persistent" inodes. See
// zip_test.go for an example of how to do this.
// them. This is done by instantiating "persistent" inodes from the
// OnAdd method of the root node. See the ZipFS example for a
// runnable example of how to do this.
package fs
import (
......@@ -180,8 +181,7 @@ import (
//
// In general, if an InodeEmbedder does not implement specific
// filesystem methods, the filesystem will react as if it is a
// read-only filesystem with a predefined tree structure. See
// zipfs_test.go for an example. A example is in zip_test.go
// read-only filesystem with a predefined tree structure.
type InodeEmbedder interface {
// populateInode and inode are used internally to link Inode
// to a Node.
......
......@@ -11,13 +11,10 @@ import (
"io/ioutil"
"path/filepath"
"reflect"
"strings"
"sync"
"syscall"
"testing"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse"
)
var testData = map[string]string{
......@@ -134,91 +131,3 @@ func TestZipFSOnAdd(t *testing.T) {
t.Errorf("got %q, want %q", got, want)
}
}
// zipFile is a file read from a zip archive.
type zipFile struct {
fs.Inode
file *zip.File
mu sync.Mutex
data []byte
}
var _ = (fs.NodeOpener)((*zipFile)(nil))
var _ = (fs.NodeGetattrer)((*zipFile)(nil))
// Getattr sets the minimum, which is the size. A more full-featured
// FS would also set timestamps and permissions.
func (zf *zipFile) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Size = zf.file.UncompressedSize64
return 0
}
// Open lazily unpacks zip data
func (zf *zipFile) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
zf.mu.Lock()
defer zf.mu.Unlock()
if zf.data == nil {
rc, err := zf.file.Open()
if err != nil {
return nil, 0, syscall.EIO
}
content, err := ioutil.ReadAll(rc)
if err != nil {
return nil, 0, syscall.EIO
}
zf.data = content
}
// We don't return a filehandle since we don't really need
// one. The file content is immutable, so hint the kernel to
// cache the data.
return nil, fuse.FOPEN_KEEP_CACHE, fs.OK
}
// Read simply returns the data that was already unpacked in the Open call
func (zf *zipFile) Read(ctx context.Context, f fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
end := int(off) + len(dest)
if end > len(zf.data) {
end = len(zf.data)
}
return fuse.ReadResultData(zf.data[off:end]), fs.OK
}
// zipRoot is the root of the Zip filesystem. Its only functionality
// is populating the filesystem.
type zipRoot struct {
fs.Inode
zr *zip.Reader
}
var _ = (fs.NodeOnAdder)((*zipRoot)(nil))
func (zr *zipRoot) OnAdd(ctx context.Context) {
// OnAdd is called once we are attached to an Inode. We can
// then construct a tree. We construct the entire tree, and
// we don't want parts of the tree to disappear when the
// kernel is short on memory, so we use persistent inodes.
for _, f := range zr.zr.File {
dir, base := filepath.Split(f.Name)
p := &zr.Inode
for _, component := range strings.Split(dir, "/") {
if len(component) == 0 {
continue
}
ch := p.GetChild(component)
if ch == nil {
ch = p.NewPersistentInode(ctx, &fs.Inode{},
fs.StableAttr{Mode: fuse.S_IFDIR})
p.AddChild(component, ch, true)
}
p = ch
}
ch := p.NewPersistentInode(ctx, &zipFile{file: f}, fs.StableAttr{})
p.AddChild(base, ch, true)
}
}
// Copyright 2019 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fs_test
import (
"archive/zip"
"context"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"sync"
"syscall"
"github.com/hanwen/go-fuse/fs"
"github.com/hanwen/go-fuse/fuse"
)
// zipFile is a file read from a zip archive.
type zipFile struct {
fs.Inode
file *zip.File
mu sync.Mutex
data []byte
}
// We decompress the file on demand in Open
var _ = (fs.NodeOpener)((*zipFile)(nil))
// Getattr sets the minimum, which is the size. A more full-featured
// FS would also set timestamps and permissions.
var _ = (fs.NodeGetattrer)((*zipFile)(nil))
func (zf *zipFile) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
out.Size = zf.file.UncompressedSize64
return 0
}
// Open lazily unpacks zip data
func (zf *zipFile) Open(ctx context.Context, flags uint32) (fs.FileHandle, uint32, syscall.Errno) {
zf.mu.Lock()
defer zf.mu.Unlock()
if zf.data == nil {
rc, err := zf.file.Open()
if err != nil {
return nil, 0, syscall.EIO
}
content, err := ioutil.ReadAll(rc)
if err != nil {
return nil, 0, syscall.EIO
}
zf.data = content
}
// We don't return a filehandle since we don't really need
// one. The file content is immutable, so hint the kernel to
// cache the data.
return nil, fuse.FOPEN_KEEP_CACHE, fs.OK
}
// Read simply returns the data that was already unpacked in the Open call
func (zf *zipFile) Read(ctx context.Context, f fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
end := int(off) + len(dest)
if end > len(zf.data) {
end = len(zf.data)
}
return fuse.ReadResultData(zf.data[off:end]), fs.OK
}
// zipRoot is the root of the Zip filesystem. Its only functionality
// is populating the filesystem.
type zipRoot struct {
fs.Inode
zr *zip.Reader
}
// The root populates the tree in its OnAdd method
var _ = (fs.NodeOnAdder)((*zipRoot)(nil))
func (zr *zipRoot) OnAdd(ctx context.Context) {
// OnAdd is called once we are attached to an Inode. We can
// then construct a tree. We construct the entire tree, and
// we don't want parts of the tree to disappear when the
// kernel is short on memory, so we use persistent inodes.
for _, f := range zr.zr.File {
dir, base := filepath.Split(f.Name)
p := &zr.Inode
for _, component := range strings.Split(dir, "/") {
if len(component) == 0 {
continue
}
ch := p.GetChild(component)
if ch == nil {
ch = p.NewPersistentInode(ctx, &fs.Inode{},
fs.StableAttr{Mode: fuse.S_IFDIR})
p.AddChild(component, ch, true)
}
p = ch
}
ch := p.NewPersistentInode(ctx, &zipFile{file: f}, fs.StableAttr{})
p.AddChild(base, ch, true)
}
}
// ExampleZipFS shows an in-memory, static file system
func Example_zipFS() {
flag.Parse()
if len(flag.Args()) != 1 {
log.Fatal("usage: zipmount ZIP-FILE")
}
zfile, err := zip.OpenReader(flag.Arg(0))
if err != nil {
log.Fatal(err)
}
root := &zipRoot{zr: &zfile.Reader}
mnt := "/tmp/x"
os.Mkdir(mnt, 0755)
server, err := fs.Mount(mnt, root, nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("zip file mounted")
fmt.Printf("to unmount: fusermount -u %s\n", mnt)
server.Wait()
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment