1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
15 "git.arvados.org/arvados.git/sdk/go/arvados"
16 "git.arvados.org/arvados.git/sdk/go/keepclient"
17 "github.com/arvados/cgofuse/fuse"
20 // sharedFile wraps arvados.File with a sync.Mutex, so fuse can safely
21 // use a single filehandle concurrently on behalf of multiple
23 type sharedFile struct {
28 // keepFS implements cgofuse's FileSystemInterface.
31 Client *arvados.Client
32 KeepClient *keepclient.KeepClient
37 root arvados.CustomFileSystem
38 open map[uint64]*sharedFile
42 // If non-nil, this channel will be closed by Init() to notify
43 // other goroutines that the mount is ready.
48 invalidFH = ^uint64(0)
51 // newFH wraps f in a sharedFile, adds it to fs's lookup table using a
52 // new handle number, and returns the handle number.
53 func (fs *keepFS) newFH(f arvados.File) uint64 {
57 fs.open = make(map[uint64]*sharedFile)
61 fs.open[fh] = &sharedFile{File: f}
65 func (fs *keepFS) lookupFH(fh uint64) *sharedFile {
71 func (fs *keepFS) Init() {
72 defer fs.debugPanics()
73 fs.root = fs.Client.SiteFileSystem(fs.KeepClient)
74 fs.root.MountProject("home", "")
80 func (fs *keepFS) Create(path string, flags int, mode uint32) (errc int, fh uint64) {
81 defer fs.debugPanics()
83 return -fuse.EROFS, invalidFH
85 f, err := fs.root.OpenFile(path, flags|os.O_CREATE, os.FileMode(mode))
86 if err == os.ErrExist {
87 return -fuse.EEXIST, invalidFH
88 } else if err != nil {
89 return -fuse.EINVAL, invalidFH
94 func (fs *keepFS) Open(path string, flags int) (errc int, fh uint64) {
95 defer fs.debugPanics()
96 if fs.ReadOnly && flags&(os.O_RDWR|os.O_WRONLY|os.O_CREATE) != 0 {
97 return -fuse.EROFS, invalidFH
99 f, err := fs.root.OpenFile(path, flags, 0)
101 return -fuse.ENOENT, invalidFH
102 } else if fi, err := f.Stat(); err != nil {
103 return -fuse.EIO, invalidFH
104 } else if fi.IsDir() {
106 return -fuse.EISDIR, invalidFH
108 return 0, fs.newFH(f)
111 func (fs *keepFS) Utimens(path string, tmsp []fuse.Timespec) int {
112 defer fs.debugPanics()
116 f, err := fs.root.OpenFile(path, 0, 0)
118 return fs.errCode(err)
124 func (fs *keepFS) errCode(err error) int {
128 if errors.Is(err, os.ErrNotExist) {
131 if errors.Is(err, os.ErrExist) {
134 if errors.Is(err, arvados.ErrInvalidArgument) {
137 if errors.Is(err, arvados.ErrInvalidOperation) {
140 if errors.Is(err, arvados.ErrDirectoryNotEmpty) {
141 return -fuse.ENOTEMPTY
146 func (fs *keepFS) Mkdir(path string, mode uint32) int {
147 defer fs.debugPanics()
151 f, err := fs.root.OpenFile(path, os.O_CREATE|os.O_EXCL, os.FileMode(mode)|os.ModeDir)
153 return fs.errCode(err)
159 func (fs *keepFS) Opendir(path string) (errc int, fh uint64) {
160 defer fs.debugPanics()
161 f, err := fs.root.OpenFile(path, 0, 0)
163 return fs.errCode(err), invalidFH
164 } else if fi, err := f.Stat(); err != nil {
165 return fs.errCode(err), invalidFH
166 } else if !fi.IsDir() {
168 return -fuse.ENOTDIR, invalidFH
170 return 0, fs.newFH(f)
173 func (fs *keepFS) Releasedir(path string, fh uint64) (errc int) {
174 defer fs.debugPanics()
175 return fs.Release(path, fh)
178 func (fs *keepFS) Rmdir(path string) int {
179 defer fs.debugPanics()
180 return fs.errCode(fs.root.Remove(path))
183 func (fs *keepFS) Release(path string, fh uint64) (errc int) {
184 defer fs.debugPanics()
187 defer delete(fs.open, fh)
188 if f := fs.open[fh]; f != nil {
197 func (fs *keepFS) Rename(oldname, newname string) (errc int) {
198 defer fs.debugPanics()
202 return fs.errCode(fs.root.Rename(oldname, newname))
205 func (fs *keepFS) Unlink(path string) (errc int) {
206 defer fs.debugPanics()
210 return fs.errCode(fs.root.Remove(path))
213 func (fs *keepFS) Truncate(path string, size int64, fh uint64) (errc int) {
214 defer fs.debugPanics()
219 // Sometimes fh is a valid filehandle and we don't need to
220 // waste a name lookup.
221 if f := fs.lookupFH(fh); f != nil {
222 return fs.errCode(f.Truncate(size))
225 // Other times, fh is invalid and we need to lookup path.
226 f, err := fs.root.OpenFile(path, os.O_RDWR, 0)
228 return fs.errCode(err)
231 return fs.errCode(f.Truncate(size))
234 func (fs *keepFS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
235 defer fs.debugPanics()
238 if f := fs.lookupFH(fh); f != nil {
239 // Valid filehandle -- ignore path.
242 // Invalid filehandle -- lookup path.
243 fi, err = fs.root.Stat(path)
246 return fs.errCode(err)
248 fs.fillStat(stat, fi)
252 func (fs *keepFS) Chmod(path string, mode uint32) (errc int) {
256 if fi, err := fs.root.Stat(path); err != nil {
257 return fs.errCode(err)
258 } else if mode & ^uint32(fuse.S_IFREG|fuse.S_IFDIR|0777) != 0 {
259 // Refuse to set mode bits other than
262 } else if (fi.Mode()&os.ModeDir != 0) != (mode&fuse.S_IFDIR != 0) {
263 // Refuse to transform a regular file to a dir, or
267 // As long as the change isn't nonsense, chmod is a no-op,
268 // because we don't save permission bits.
272 func (fs *keepFS) fillStat(stat *fuse.Stat_t, fi os.FileInfo) {
273 defer fs.debugPanics()
280 m = m | uint32(fi.Mode()&os.ModePerm)
283 stat.Size = fi.Size()
284 t := fuse.NewTimespec(fi.ModTime())
290 stat.Blocks = (stat.Size + stat.Blksize - 1) / stat.Blksize
291 if fs.Uid > 0 && int64(fs.Uid) < 1<<31 {
292 stat.Uid = uint32(fs.Uid)
294 if fs.Gid > 0 && int64(fs.Gid) < 1<<31 {
295 stat.Gid = uint32(fs.Gid)
299 func (fs *keepFS) Write(path string, buf []byte, ofst int64, fh uint64) (n int) {
300 defer fs.debugPanics()
310 if _, err := f.Seek(ofst, io.SeekStart); err != nil {
311 return fs.errCode(err)
313 n, err := f.Write(buf)
315 log.Printf("error writing %q: %s", path, err)
316 return fs.errCode(err)
321 func (fs *keepFS) Read(path string, buf []byte, ofst int64, fh uint64) (n int) {
322 defer fs.debugPanics()
329 if _, err := f.Seek(ofst, io.SeekStart); err != nil {
330 return fs.errCode(err)
332 n, err := f.Read(buf)
333 for err == nil && n < len(buf) {
334 // f is an io.Reader ("If some data is available but
335 // not len(p) bytes, Read conventionally returns what
336 // is available instead of waiting for more") -- but
337 // our caller requires us to either fill buf or reach
340 n, err = f.Read(buf[done:])
343 if err != nil && err != io.EOF {
344 log.Printf("error reading %q: %s", path, err)
345 return fs.errCode(err)
350 func (fs *keepFS) Readdir(path string,
351 fill func(name string, stat *fuse.Stat_t, ofst int64) bool,
353 fh uint64) (errc int) {
354 defer fs.debugPanics()
362 fis, err := f.Readdir(-1)
364 return fs.errCode(err)
366 for _, fi := range fis {
367 fs.fillStat(&stat, fi)
368 fill(fi.Name(), &stat, 0)
373 func (fs *keepFS) Fsync(path string, datasync bool, fh uint64) int {
374 defer fs.debugPanics()
379 return fs.errCode(f.Sync())
382 func (fs *keepFS) Fsyncdir(path string, datasync bool, fh uint64) int {
383 return fs.Fsync(path, datasync, fh)
386 // debugPanics (when deferred by keepFS handlers) prints an error and
387 // stack trace on stderr when a handler crashes. (Without this,
388 // cgofuse recovers from panics silently and returns EIO.)
389 func (fs *keepFS) debugPanics() {
390 if err := recover(); err != nil {
391 log.Printf("(%T) %v", err, err)