1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
14 "git.arvados.org/arvados.git/sdk/go/arvados"
15 "git.arvados.org/arvados.git/sdk/go/keepclient"
16 "github.com/arvados/cgofuse/fuse"
19 // sharedFile wraps arvados.File with a sync.Mutex, so fuse can safely
20 // use a single filehandle concurrently on behalf of multiple
22 type sharedFile struct {
27 // keepFS implements cgofuse's FileSystemInterface.
30 Client *arvados.Client
31 KeepClient *keepclient.KeepClient
36 root arvados.CustomFileSystem
37 open map[uint64]*sharedFile
41 // If non-nil, this channel will be closed by Init() to notify
42 // other goroutines that the mount is ready.
47 invalidFH = ^uint64(0)
50 // newFH wraps f in a sharedFile, adds it to fs's lookup table using a
51 // new handle number, and returns the handle number.
52 func (fs *keepFS) newFH(f arvados.File) uint64 {
56 fs.open = make(map[uint64]*sharedFile)
60 fs.open[fh] = &sharedFile{File: f}
64 func (fs *keepFS) lookupFH(fh uint64) *sharedFile {
70 func (fs *keepFS) Init() {
71 defer fs.debugPanics()
72 fs.root = fs.Client.SiteFileSystem(fs.KeepClient)
73 fs.root.MountProject("home", "")
79 func (fs *keepFS) Create(path string, flags int, mode uint32) (errc int, fh uint64) {
80 defer fs.debugPanics()
82 return -fuse.EROFS, invalidFH
84 f, err := fs.root.OpenFile(path, flags|os.O_CREATE, os.FileMode(mode))
85 if err == os.ErrExist {
86 return -fuse.EEXIST, invalidFH
87 } else if err != nil {
88 return -fuse.EINVAL, invalidFH
93 func (fs *keepFS) Open(path string, flags int) (errc int, fh uint64) {
94 defer fs.debugPanics()
95 if fs.ReadOnly && flags&(os.O_RDWR|os.O_WRONLY|os.O_CREATE) != 0 {
96 return -fuse.EROFS, invalidFH
98 f, err := fs.root.OpenFile(path, flags, 0)
100 return -fuse.ENOENT, invalidFH
101 } else if fi, err := f.Stat(); err != nil {
102 return -fuse.EIO, invalidFH
103 } else if fi.IsDir() {
105 return -fuse.EISDIR, invalidFH
107 return 0, fs.newFH(f)
110 func (fs *keepFS) Utimens(path string, tmsp []fuse.Timespec) int {
111 defer fs.debugPanics()
115 f, err := fs.root.OpenFile(path, 0, 0)
117 return fs.errCode(err)
123 func (fs *keepFS) errCode(err error) int {
124 if os.IsNotExist(err) {
130 case arvados.ErrInvalidArgument:
132 case arvados.ErrInvalidOperation:
134 case arvados.ErrDirectoryNotEmpty:
135 return -fuse.ENOTEMPTY
143 func (fs *keepFS) Mkdir(path string, mode uint32) int {
144 defer fs.debugPanics()
148 f, err := fs.root.OpenFile(path, os.O_CREATE|os.O_EXCL, os.FileMode(mode)|os.ModeDir)
150 return fs.errCode(err)
156 func (fs *keepFS) Opendir(path string) (errc int, fh uint64) {
157 defer fs.debugPanics()
158 f, err := fs.root.OpenFile(path, 0, 0)
160 return fs.errCode(err), invalidFH
161 } else if fi, err := f.Stat(); err != nil {
162 return fs.errCode(err), invalidFH
163 } else if !fi.IsDir() {
165 return -fuse.ENOTDIR, invalidFH
167 return 0, fs.newFH(f)
170 func (fs *keepFS) Releasedir(path string, fh uint64) (errc int) {
171 defer fs.debugPanics()
172 return fs.Release(path, fh)
175 func (fs *keepFS) Rmdir(path string) int {
176 defer fs.debugPanics()
177 return fs.errCode(fs.root.Remove(path))
180 func (fs *keepFS) Release(path string, fh uint64) (errc int) {
181 defer fs.debugPanics()
184 defer delete(fs.open, fh)
185 if f := fs.open[fh]; f != nil {
194 func (fs *keepFS) Rename(oldname, newname string) (errc int) {
195 defer fs.debugPanics()
199 return fs.errCode(fs.root.Rename(oldname, newname))
202 func (fs *keepFS) Unlink(path string) (errc int) {
203 defer fs.debugPanics()
207 return fs.errCode(fs.root.Remove(path))
210 func (fs *keepFS) Truncate(path string, size int64, fh uint64) (errc int) {
211 defer fs.debugPanics()
216 // Sometimes fh is a valid filehandle and we don't need to
217 // waste a name lookup.
218 if f := fs.lookupFH(fh); f != nil {
219 return fs.errCode(f.Truncate(size))
222 // Other times, fh is invalid and we need to lookup path.
223 f, err := fs.root.OpenFile(path, os.O_RDWR, 0)
225 return fs.errCode(err)
228 return fs.errCode(f.Truncate(size))
231 func (fs *keepFS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
232 defer fs.debugPanics()
235 if f := fs.lookupFH(fh); f != nil {
236 // Valid filehandle -- ignore path.
239 // Invalid filehandle -- lookup path.
240 fi, err = fs.root.Stat(path)
243 return fs.errCode(err)
245 fs.fillStat(stat, fi)
249 func (fs *keepFS) Chmod(path string, mode uint32) (errc int) {
253 if fi, err := fs.root.Stat(path); err != nil {
254 return fs.errCode(err)
255 } else if mode & ^uint32(fuse.S_IFREG|fuse.S_IFDIR|0777) != 0 {
256 // Refuse to set mode bits other than
259 } else if (fi.Mode()&os.ModeDir != 0) != (mode&fuse.S_IFDIR != 0) {
260 // Refuse to transform a regular file to a dir, or
264 // As long as the change isn't nonsense, chmod is a no-op,
265 // because we don't save permission bits.
269 func (fs *keepFS) fillStat(stat *fuse.Stat_t, fi os.FileInfo) {
270 defer fs.debugPanics()
277 m = m | uint32(fi.Mode()&os.ModePerm)
280 stat.Size = fi.Size()
281 t := fuse.NewTimespec(fi.ModTime())
287 stat.Blocks = (stat.Size + stat.Blksize - 1) / stat.Blksize
288 if fs.Uid > 0 && int64(fs.Uid) < 1<<31 {
289 stat.Uid = uint32(fs.Uid)
291 if fs.Gid > 0 && int64(fs.Gid) < 1<<31 {
292 stat.Gid = uint32(fs.Gid)
296 func (fs *keepFS) Write(path string, buf []byte, ofst int64, fh uint64) (n int) {
297 defer fs.debugPanics()
307 if _, err := f.Seek(ofst, io.SeekStart); err != nil {
308 return fs.errCode(err)
310 n, err := f.Write(buf)
312 log.Printf("error writing %q: %s", path, err)
313 return fs.errCode(err)
318 func (fs *keepFS) Read(path string, buf []byte, ofst int64, fh uint64) (n int) {
319 defer fs.debugPanics()
326 if _, err := f.Seek(ofst, io.SeekStart); err != nil {
327 return fs.errCode(err)
329 n, err := f.Read(buf)
330 for err == nil && n < len(buf) {
331 // f is an io.Reader ("If some data is available but
332 // not len(p) bytes, Read conventionally returns what
333 // is available instead of waiting for more") -- but
334 // our caller requires us to either fill buf or reach
337 n, err = f.Read(buf[done:])
340 if err != nil && err != io.EOF {
341 log.Printf("error reading %q: %s", path, err)
342 return fs.errCode(err)
347 func (fs *keepFS) Readdir(path string,
348 fill func(name string, stat *fuse.Stat_t, ofst int64) bool,
350 fh uint64) (errc int) {
351 defer fs.debugPanics()
359 fis, err := f.Readdir(-1)
361 return fs.errCode(err)
363 for _, fi := range fis {
364 fs.fillStat(&stat, fi)
365 fill(fi.Name(), &stat, 0)
370 func (fs *keepFS) Fsync(path string, datasync bool, fh uint64) int {
371 defer fs.debugPanics()
376 return fs.errCode(f.Sync())
379 func (fs *keepFS) Fsyncdir(path string, datasync bool, fh uint64) int {
380 return fs.Fsync(path, datasync, fh)
383 // debugPanics (when deferred by keepFS handlers) prints an error and
384 // stack trace on stderr when a handler crashes. (Without this,
385 // cgofuse recovers from panics silently and returns EIO.)
386 func (fs *keepFS) debugPanics() {
387 if err := recover(); err != nil {
388 log.Printf("(%T) %v", err, err)