github.com/hanwen/go-fuse@v1.0.0/fuse/pathfs/loopback.go (about)

     1  // Copyright 2016 the Go-FUSE Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pathfs
     6  
     7  import (
     8  	"io"
     9  	"log"
    10  	"os"
    11  	"path/filepath"
    12  	"syscall"
    13  
    14  	"github.com/hanwen/go-fuse/fuse"
    15  	"github.com/hanwen/go-fuse/fuse/nodefs"
    16  )
    17  
    18  type loopbackFileSystem struct {
    19  	// TODO - this should need default fill in.
    20  	FileSystem
    21  	Root string
    22  }
    23  
    24  // A FUSE filesystem that shunts all request to an underlying file
    25  // system.  Its main purpose is to provide test coverage without
    26  // having to build a synthetic filesystem.
    27  func NewLoopbackFileSystem(root string) FileSystem {
    28  	// Make sure the Root path is absolute to avoid problems when the
    29  	// application changes working directory.
    30  	root, err := filepath.Abs(root)
    31  	if err != nil {
    32  		panic(err)
    33  	}
    34  	return &loopbackFileSystem{
    35  		FileSystem: NewDefaultFileSystem(),
    36  		Root:       root,
    37  	}
    38  }
    39  
    40  func (fs *loopbackFileSystem) StatFs(name string) *fuse.StatfsOut {
    41  	s := syscall.Statfs_t{}
    42  	err := syscall.Statfs(fs.GetPath(name), &s)
    43  	if err == nil {
    44  		out := &fuse.StatfsOut{}
    45  		out.FromStatfsT(&s)
    46  		return out
    47  	}
    48  	return nil
    49  }
    50  
    51  func (fs *loopbackFileSystem) OnMount(nodeFs *PathNodeFs) {
    52  }
    53  
    54  func (fs *loopbackFileSystem) OnUnmount() {}
    55  
    56  func (fs *loopbackFileSystem) GetPath(relPath string) string {
    57  	return filepath.Join(fs.Root, relPath)
    58  }
    59  
    60  func (fs *loopbackFileSystem) GetAttr(name string, context *fuse.Context) (a *fuse.Attr, code fuse.Status) {
    61  	fullPath := fs.GetPath(name)
    62  	var err error = nil
    63  	st := syscall.Stat_t{}
    64  	if name == "" {
    65  		// When GetAttr is called for the toplevel directory, we always want
    66  		// to look through symlinks.
    67  		err = syscall.Stat(fullPath, &st)
    68  	} else {
    69  		err = syscall.Lstat(fullPath, &st)
    70  	}
    71  	if err != nil {
    72  		return nil, fuse.ToStatus(err)
    73  	}
    74  	a = &fuse.Attr{}
    75  	a.FromStat(&st)
    76  	return a, fuse.OK
    77  }
    78  
    79  func (fs *loopbackFileSystem) OpenDir(name string, context *fuse.Context) (stream []fuse.DirEntry, status fuse.Status) {
    80  	// What other ways beyond O_RDONLY are there to open
    81  	// directories?
    82  	f, err := os.Open(fs.GetPath(name))
    83  	if err != nil {
    84  		return nil, fuse.ToStatus(err)
    85  	}
    86  	want := 500
    87  	output := make([]fuse.DirEntry, 0, want)
    88  	for {
    89  		infos, err := f.Readdir(want)
    90  		for i := range infos {
    91  			// workaround for https://code.google.com/p/go/issues/detail?id=5960
    92  			if infos[i] == nil {
    93  				continue
    94  			}
    95  			n := infos[i].Name()
    96  			d := fuse.DirEntry{
    97  				Name: n,
    98  			}
    99  			if s := fuse.ToStatT(infos[i]); s != nil {
   100  				d.Mode = uint32(s.Mode)
   101  				d.Ino = s.Ino
   102  			} else {
   103  				log.Printf("ReadDir entry %q for %q has no stat info", n, name)
   104  			}
   105  			output = append(output, d)
   106  		}
   107  		if len(infos) < want || err == io.EOF {
   108  			break
   109  		}
   110  		if err != nil {
   111  			log.Println("Readdir() returned err:", err)
   112  			break
   113  		}
   114  	}
   115  	f.Close()
   116  
   117  	return output, fuse.OK
   118  }
   119  
   120  func (fs *loopbackFileSystem) Open(name string, flags uint32, context *fuse.Context) (fuseFile nodefs.File, status fuse.Status) {
   121  	f, err := os.OpenFile(fs.GetPath(name), int(flags), 0)
   122  	if err != nil {
   123  		return nil, fuse.ToStatus(err)
   124  	}
   125  	return nodefs.NewLoopbackFile(f), fuse.OK
   126  }
   127  
   128  func (fs *loopbackFileSystem) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
   129  	err := os.Chmod(fs.GetPath(path), os.FileMode(mode))
   130  	return fuse.ToStatus(err)
   131  }
   132  
   133  func (fs *loopbackFileSystem) Chown(path string, uid uint32, gid uint32, context *fuse.Context) (code fuse.Status) {
   134  	return fuse.ToStatus(os.Chown(fs.GetPath(path), int(uid), int(gid)))
   135  }
   136  
   137  func (fs *loopbackFileSystem) Truncate(path string, offset uint64, context *fuse.Context) (code fuse.Status) {
   138  	return fuse.ToStatus(os.Truncate(fs.GetPath(path), int64(offset)))
   139  }
   140  
   141  func (fs *loopbackFileSystem) Readlink(name string, context *fuse.Context) (out string, code fuse.Status) {
   142  	f, err := os.Readlink(fs.GetPath(name))
   143  	return f, fuse.ToStatus(err)
   144  }
   145  
   146  func (fs *loopbackFileSystem) Mknod(name string, mode uint32, dev uint32, context *fuse.Context) (code fuse.Status) {
   147  	return fuse.ToStatus(syscall.Mknod(fs.GetPath(name), mode, int(dev)))
   148  }
   149  
   150  func (fs *loopbackFileSystem) Mkdir(path string, mode uint32, context *fuse.Context) (code fuse.Status) {
   151  	return fuse.ToStatus(os.Mkdir(fs.GetPath(path), os.FileMode(mode)))
   152  }
   153  
   154  // Don't use os.Remove, it removes twice (unlink followed by rmdir).
   155  func (fs *loopbackFileSystem) Unlink(name string, context *fuse.Context) (code fuse.Status) {
   156  	return fuse.ToStatus(syscall.Unlink(fs.GetPath(name)))
   157  }
   158  
   159  func (fs *loopbackFileSystem) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
   160  	return fuse.ToStatus(syscall.Rmdir(fs.GetPath(name)))
   161  }
   162  
   163  func (fs *loopbackFileSystem) Symlink(pointedTo string, linkName string, context *fuse.Context) (code fuse.Status) {
   164  	return fuse.ToStatus(os.Symlink(pointedTo, fs.GetPath(linkName)))
   165  }
   166  
   167  func (fs *loopbackFileSystem) Rename(oldPath string, newPath string, context *fuse.Context) (codee fuse.Status) {
   168  	oldPath = fs.GetPath(oldPath)
   169  	newPath = fs.GetPath(newPath)
   170  	err := os.Rename(oldPath, newPath)
   171  	return fuse.ToStatus(err)
   172  }
   173  
   174  func (fs *loopbackFileSystem) Link(orig string, newName string, context *fuse.Context) (code fuse.Status) {
   175  	return fuse.ToStatus(os.Link(fs.GetPath(orig), fs.GetPath(newName)))
   176  }
   177  
   178  func (fs *loopbackFileSystem) Access(name string, mode uint32, context *fuse.Context) (code fuse.Status) {
   179  	return fuse.ToStatus(syscall.Access(fs.GetPath(name), mode))
   180  }
   181  
   182  func (fs *loopbackFileSystem) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) {
   183  	f, err := os.OpenFile(fs.GetPath(path), int(flags)|os.O_CREATE, os.FileMode(mode))
   184  	return nodefs.NewLoopbackFile(f), fuse.ToStatus(err)
   185  }