From 5b15efa7fb944540eb814f0dd04cd6756f3248e0 Mon Sep 17 00:00:00 2001 From: Peter Edge Date: Fri, 29 Jan 2016 18:47:56 +0100 Subject: [PATCH 1/5] start of osfs example Signed-off-by: Peter Edge --- examples/osfs/cmd/osfs/main.go | 60 ++++++++++++ examples/osfs/osfs.go | 164 +++++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 examples/osfs/cmd/osfs/main.go create mode 100644 examples/osfs/osfs.go diff --git a/examples/osfs/cmd/osfs/main.go b/examples/osfs/cmd/osfs/main.go new file mode 100644 index 00000000..295327dd --- /dev/null +++ b/examples/osfs/cmd/osfs/main.go @@ -0,0 +1,60 @@ +/* +Package main mounts an osfs on a directory. +*/ +package main + +import ( + "flag" + "fmt" + "os" + + "bazil.org/fuse" + "bazil.org/fuse/examples/osfs" + "bazil.org/fuse/fs" +) + +func usage() { + fmt.Fprintf(os.Stderr, "Usage of %s:\n %s BASEDIR MOUNTPOINT\n", os.Args[0], os.Args[0]) + flag.PrintDefaults() +} + +func main() { + flag.Usage = usage + flag.Parse() + if flag.NArg() != 2 { + usage() + os.Exit(2) + } + if err := do(os.Args[1], os.Args[2]); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + os.Exit(0) +} + +func do(baseDirPath string, mountpoint string) (retErr error) { + if err := os.MkdirAll(mountpoint, 0777); err != nil { + return err + } + conn, err := fuse.Mount( + mountpoint, + fuse.FSName("os"), + fuse.Subtype("osfs"), + fuse.VolumeName("osfs"), + fuse.LocalVolume(), + fuse.MaxReadahead(1<<32-1), + ) + if err != nil { + return err + } + defer func() { + if err := conn.Close(); err != nil && retErr == nil { + retErr = err + } + }() + if err := fs.Serve(conn, osfs.New(baseDirPath)); err != nil { + return err + } + <-conn.Ready + return conn.MountError +} diff --git a/examples/osfs/osfs.go b/examples/osfs/osfs.go new file mode 100644 index 00000000..8805f0d8 --- /dev/null +++ b/examples/osfs/osfs.go @@ -0,0 +1,164 @@ +/* +Package osfs implements a file system that functions as a pass-through +to the os file system. Not all features are supported. +*/ +package osfs // import "bazil.org/fuse/examples/osfs" + +import ( + "io/ioutil" + "os" + "path/filepath" + + "bazil.org/fuse" + "bazil.org/fuse/fs" + "golang.org/x/net/context" +) + +// New returns a new pass-through fs.FS. +// +// Not optimized at all. +func New(path string) fs.FS { + return &filesystem{path} +} + +type filesystem struct { + path string +} + +func (f *filesystem) Root() (fs.Node, error) { + return &dir{node{f.path}}, nil +} + +type node struct { + path string +} + +func (n *node) Attr(ctx context.Context, attr *fuse.Attr) (retErr error) { + stat, err := os.Stat(n.path) + if err != nil { + return err + } + attr.Size = uint64(stat.Size()) + attr.Mode = stat.Mode() + return nil +} + +func (n *node) Setattr(ctx context.Context, request *fuse.SetattrRequest, response *fuse.SetattrResponse) (retErr error) { + if request.Valid.Mode() { + if err := os.Chmod(n.path, request.Mode); err != nil { + return err + } + } + if request.Valid.Size() { + if err := os.Truncate(n.path, int64(request.Size)); err != nil { + return err + } + } + return nil +} + +type dir struct { + node +} + +func (d *dir) Create(ctx context.Context, request *fuse.CreateRequest, response *fuse.CreateResponse) (_ fs.Node, _ fs.Handle, retErr error) { + file := &file{node{filepath.Join(d.path, request.Name)}} + handle, err := file.openFile(int(request.Flags), request.Mode) + return file, handle, err +} + +func (d *dir) Lookup(ctx context.Context, name string) (_ fs.Node, retErr error) { + path := filepath.Join(d.path, name) + _, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return nil, fuse.ENOENT + } + return nil, err + } + return &file{node{path}}, nil +} + +func (d *dir) Mkdir(ctx context.Context, request *fuse.MkdirRequest) (_ fs.Node, retErr error) { + if err := os.Mkdir(request.Name, request.Mode); err != nil { + return nil, err + } + return &dir{node{filepath.Join(d.path, request.Name)}}, nil +} + +func (d *dir) ReadDirAll(ctx context.Context) (_ []fuse.Dirent, retErr error) { + stats, err := ioutil.ReadDir(d.path) + if err != nil { + return nil, err + } + dirents := make([]fuse.Dirent, len(stats)) + for i, stat := range stats { + t := fuse.DT_File + if stat.IsDir() { + t = fuse.DT_Dir + } + dirents[i] = fuse.Dirent{ + Name: filepath.Base(stat.Name()), + Type: t, + } + } + return dirents, nil +} + +func (d *dir) Remove(ctx context.Context, request *fuse.RemoveRequest) error { + return os.Remove(filepath.Join(d.path, request.Name)) +} + +type file struct { + node +} + +func (f *file) Open(ctx context.Context, request *fuse.OpenRequest, response *fuse.OpenResponse) (_ fs.Handle, retErr error) { + return f.openFile(int(request.Flags), 0666) +} + +func (f *file) openFile(flags int, mode os.FileMode) (fs.Handle, error) { + file, err := os.OpenFile(f.path, flags, mode) + if err != nil { + return nil, err + } + return &handle{f.node, file}, nil +} + +type handle struct { + node + f *os.File +} + +func (h *handle) Read(ctx context.Context, request *fuse.ReadRequest, response *fuse.ReadResponse) (retErr error) { + if _, err := h.f.Seek(request.Offset, 0); err != nil { + return err + } + buffer := make([]byte, request.Size) + n, err := h.f.Read(buffer) + response.Data = buffer[:n] + if err != nil { + return err + } + return nil +} + +func (h *handle) Write(ctx context.Context, request *fuse.WriteRequest, response *fuse.WriteResponse) (retErr error) { + if _, err := h.f.Seek(request.Offset, 0); err != nil { + return err + } + n, err := h.f.Write(request.Data) + response.Size = n + if err != nil { + return err + } + return nil +} + +func (h *handle) Fsync(ctx context.Context, request *fuse.FsyncRequest) error { + return nil +} + +func (h *handle) Release(ctx context.Context, request *fuse.ReleaseRequest) error { + return h.f.Close() +} From 1f41fe537b84cc3698219359f3c18a297e60b1aa Mon Sep 17 00:00:00 2001 From: Jille Timmermans Date: Tue, 28 Jun 2016 23:54:27 +0100 Subject: [PATCH 2/5] osfs: Two bugfixes for directory traversal --- examples/osfs/osfs.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/osfs/osfs.go b/examples/osfs/osfs.go index 8805f0d8..068ef835 100644 --- a/examples/osfs/osfs.go +++ b/examples/osfs/osfs.go @@ -69,21 +69,25 @@ func (d *dir) Create(ctx context.Context, request *fuse.CreateRequest, response func (d *dir) Lookup(ctx context.Context, name string) (_ fs.Node, retErr error) { path := filepath.Join(d.path, name) - _, err := os.Stat(path) + stat, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { return nil, fuse.ENOENT } return nil, err } + if stat.IsDir() { + return &dir{node{path}}, nil + } return &file{node{path}}, nil } func (d *dir) Mkdir(ctx context.Context, request *fuse.MkdirRequest) (_ fs.Node, retErr error) { - if err := os.Mkdir(request.Name, request.Mode); err != nil { + path := filepath.Join(d.path, request.Name) + if err := os.Mkdir(path, request.Mode); err != nil { return nil, err } - return &dir{node{filepath.Join(d.path, request.Name)}}, nil + return &dir{node{path}}, nil } func (d *dir) ReadDirAll(ctx context.Context) (_ []fuse.Dirent, retErr error) { From 07e779eb0f291714af4073af1ae94f18d888b0eb Mon Sep 17 00:00:00 2001 From: Jille Timmermans Date: Wed, 29 Jun 2016 01:10:19 +0100 Subject: [PATCH 3/5] osfs: Implement all other properties in Attr --- examples/osfs/osfs.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/examples/osfs/osfs.go b/examples/osfs/osfs.go index 068ef835..0cc43a29 100644 --- a/examples/osfs/osfs.go +++ b/examples/osfs/osfs.go @@ -7,6 +7,8 @@ package osfs // import "bazil.org/fuse/examples/osfs" import ( "io/ioutil" "os" + "syscall" + "time" "path/filepath" "bazil.org/fuse" @@ -40,6 +42,18 @@ func (n *node) Attr(ctx context.Context, attr *fuse.Attr) (retErr error) { } attr.Size = uint64(stat.Size()) attr.Mode = stat.Mode() + attr.Mtime = stat.ModTime() + if st, ok := stat.Sys().(*syscall.Stat_t); ok { + attr.Inode = st.Ino + attr.Blocks = uint64(st.Blocks) + attr.Atime = time.Unix(st.Atim.Unix()) + attr.Ctime = time.Unix(st.Ctim.Unix()) + attr.Nlink = uint32(st.Nlink) + attr.Uid = st.Uid + attr.Gid = st.Gid + attr.Rdev = uint32(st.Rdev) + attr.BlockSize = uint32(st.Blksize) + } return nil } From 285df43acc273598e2f2760041ad7442cfa631d8 Mon Sep 17 00:00:00 2001 From: Jille Timmermans Date: Wed, 29 Jun 2016 01:10:45 +0100 Subject: [PATCH 4/5] osfs: Properly handle types other than file and directory in ReadDirAll --- examples/osfs/osfs.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/examples/osfs/osfs.go b/examples/osfs/osfs.go index 0cc43a29..676bd43e 100644 --- a/examples/osfs/osfs.go +++ b/examples/osfs/osfs.go @@ -111,9 +111,24 @@ func (d *dir) ReadDirAll(ctx context.Context) (_ []fuse.Dirent, retErr error) { } dirents := make([]fuse.Dirent, len(stats)) for i, stat := range stats { - t := fuse.DT_File - if stat.IsDir() { + t := fuse.DT_Unknown + switch stat.Mode() & os.ModeType { + case os.ModeDir: t = fuse.DT_Dir + case os.ModeSymlink: + t = fuse.DT_Link + case os.ModeNamedPipe: + t = fuse.DT_FIFO + case os.ModeSocket: + t = fuse.DT_Socket + case os.ModeDevice: + if stat.Mode() & os.ModeCharDevice == 0 { + t = fuse.DT_Block + } else { + t = fuse.DT_Char + } + case 0: + t = fuse.DT_File } dirents[i] = fuse.Dirent{ Name: filepath.Base(stat.Name()), From 7b0541f1d812d3edffae623dbbd8b1161085fc6b Mon Sep 17 00:00:00 2001 From: Jille Timmermans Date: Wed, 29 Jun 2016 01:11:15 +0100 Subject: [PATCH 5/5] osfs: Implement all generally available properties in Setattr --- examples/osfs/osfs.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/examples/osfs/osfs.go b/examples/osfs/osfs.go index 676bd43e..529469db 100644 --- a/examples/osfs/osfs.go +++ b/examples/osfs/osfs.go @@ -58,6 +58,18 @@ func (n *node) Attr(ctx context.Context, attr *fuse.Attr) (retErr error) { } func (n *node) Setattr(ctx context.Context, request *fuse.SetattrRequest, response *fuse.SetattrResponse) (retErr error) { + if request.Valid.Uid() || request.Valid.Gid() { + uid, gid := -1, -1 + if request.Valid.Uid() { + uid = int(request.Uid) + } + if request.Valid.Gid() { + gid = int(request.Gid) + } + if err := os.Chown(n.path, uid, gid); err != nil { + return err + } + } if request.Valid.Mode() { if err := os.Chmod(n.path, request.Mode); err != nil { return err @@ -68,6 +80,30 @@ func (n *node) Setattr(ctx context.Context, request *fuse.SetattrRequest, respon return err } } + if request.Valid.Mtime() || request.Valid.Atime() { + var mtime, atime time.Time + if !request.Valid.Mtime() || !request.Valid.Atime() { + stat, err := os.Stat(n.path) + if err != nil { + return err + } + mtime = stat.ModTime() + if st, ok := stat.Sys().(*syscall.Stat_t); ok { + atime = time.Unix(st.Atim.Unix()) + } else { + atime = time.Now() + } + } + if request.Valid.Mtime() { + mtime = request.Mtime + } + if request.Valid.Atime() { + atime = request.Atime + } + if err := os.Chtimes(n.path, mtime, atime); err != nil { + return err + } + } return nil }