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..529469db --- /dev/null +++ b/examples/osfs/osfs.go @@ -0,0 +1,233 @@ +/* +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" + "syscall" + "time" + "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() + 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 +} + +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 + } + } + if request.Valid.Size() { + if err := os.Truncate(n.path, int64(request.Size)); err != nil { + 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 +} + +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) + 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) { + path := filepath.Join(d.path, request.Name) + if err := os.Mkdir(path, request.Mode); err != nil { + return nil, err + } + return &dir{node{path}}, 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_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()), + 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() +}