12308: Use RWMutex for filehandle map.
[arvados.git] / lib / mount / fs.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 package mount
6
7 import (
8         "io"
9         "log"
10         "os"
11         "runtime/debug"
12         "sync"
13
14         "git.arvados.org/arvados.git/sdk/go/arvados"
15         "git.arvados.org/arvados.git/sdk/go/keepclient"
16         "github.com/arvados/cgofuse/fuse"
17 )
18
19 // sharedFile wraps arvados.File with a sync.Mutex, so fuse can safely
20 // use a single filehandle concurrently on behalf of multiple
21 // threads/processes.
22 type sharedFile struct {
23         arvados.File
24         sync.Mutex
25 }
26
27 // keepFS implements cgofuse's FileSystemInterface.
28 type keepFS struct {
29         fuse.FileSystemBase
30         Client     *arvados.Client
31         KeepClient *keepclient.KeepClient
32         ReadOnly   bool
33         Uid        int
34         Gid        int
35
36         root   arvados.CustomFileSystem
37         open   map[uint64]*sharedFile
38         lastFH uint64
39         sync.RWMutex
40
41         // If non-nil, this channel will be closed by Init() to notify
42         // other goroutines that the mount is ready.
43         ready chan struct{}
44 }
45
46 var (
47         invalidFH = ^uint64(0)
48 )
49
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 {
53         fs.Lock()
54         defer fs.Unlock()
55         if fs.open == nil {
56                 fs.open = make(map[uint64]*sharedFile)
57         }
58         fs.lastFH++
59         fh := fs.lastFH
60         fs.open[fh] = &sharedFile{File: f}
61         return fh
62 }
63
64 func (fs *keepFS) lookupFH(fh uint64) *sharedFile {
65         fs.RLock()
66         defer fs.RUnlock()
67         return fs.open[fh]
68 }
69
70 func (fs *keepFS) Init() {
71         defer fs.debugPanics()
72         fs.root = fs.Client.SiteFileSystem(fs.KeepClient)
73         fs.root.MountProject("home", "")
74         if fs.ready != nil {
75                 close(fs.ready)
76         }
77 }
78
79 func (fs *keepFS) Create(path string, flags int, mode uint32) (errc int, fh uint64) {
80         defer fs.debugPanics()
81         if fs.ReadOnly {
82                 return -fuse.EROFS, invalidFH
83         }
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
89         }
90         return 0, fs.newFH(f)
91 }
92
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
97         }
98         f, err := fs.root.OpenFile(path, flags, 0)
99         if err != nil {
100                 return -fuse.ENOENT, invalidFH
101         } else if fi, err := f.Stat(); err != nil {
102                 return -fuse.EIO, invalidFH
103         } else if fi.IsDir() {
104                 f.Close()
105                 return -fuse.EISDIR, invalidFH
106         }
107         return 0, fs.newFH(f)
108 }
109
110 func (fs *keepFS) Utimens(path string, tmsp []fuse.Timespec) int {
111         defer fs.debugPanics()
112         if fs.ReadOnly {
113                 return -fuse.EROFS
114         }
115         f, err := fs.root.OpenFile(path, 0, 0)
116         if err != nil {
117                 return fs.errCode(err)
118         }
119         f.Close()
120         return 0
121 }
122
123 func (fs *keepFS) errCode(err error) int {
124         if os.IsNotExist(err) {
125                 return -fuse.ENOENT
126         }
127         switch err {
128         case os.ErrExist:
129                 return -fuse.EEXIST
130         case arvados.ErrInvalidArgument:
131                 return -fuse.EINVAL
132         case arvados.ErrInvalidOperation:
133                 return -fuse.ENOSYS
134         case arvados.ErrDirectoryNotEmpty:
135                 return -fuse.ENOTEMPTY
136         case nil:
137                 return 0
138         default:
139                 return -fuse.EIO
140         }
141 }
142
143 func (fs *keepFS) Mkdir(path string, mode uint32) int {
144         defer fs.debugPanics()
145         if fs.ReadOnly {
146                 return -fuse.EROFS
147         }
148         f, err := fs.root.OpenFile(path, os.O_CREATE|os.O_EXCL, os.FileMode(mode)|os.ModeDir)
149         if err != nil {
150                 return fs.errCode(err)
151         }
152         f.Close()
153         return 0
154 }
155
156 func (fs *keepFS) Opendir(path string) (errc int, fh uint64) {
157         defer fs.debugPanics()
158         f, err := fs.root.OpenFile(path, 0, 0)
159         if err != nil {
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() {
164                 f.Close()
165                 return -fuse.ENOTDIR, invalidFH
166         }
167         return 0, fs.newFH(f)
168 }
169
170 func (fs *keepFS) Releasedir(path string, fh uint64) (errc int) {
171         defer fs.debugPanics()
172         return fs.Release(path, fh)
173 }
174
175 func (fs *keepFS) Rmdir(path string) int {
176         defer fs.debugPanics()
177         return fs.errCode(fs.root.Remove(path))
178 }
179
180 func (fs *keepFS) Release(path string, fh uint64) (errc int) {
181         defer fs.debugPanics()
182         fs.Lock()
183         defer fs.Unlock()
184         defer delete(fs.open, fh)
185         if f := fs.open[fh]; f != nil {
186                 err := f.Close()
187                 if err != nil {
188                         return -fuse.EIO
189                 }
190         }
191         return 0
192 }
193
194 func (fs *keepFS) Rename(oldname, newname string) (errc int) {
195         defer fs.debugPanics()
196         if fs.ReadOnly {
197                 return -fuse.EROFS
198         }
199         return fs.errCode(fs.root.Rename(oldname, newname))
200 }
201
202 func (fs *keepFS) Unlink(path string) (errc int) {
203         defer fs.debugPanics()
204         if fs.ReadOnly {
205                 return -fuse.EROFS
206         }
207         return fs.errCode(fs.root.Remove(path))
208 }
209
210 func (fs *keepFS) Truncate(path string, size int64, fh uint64) (errc int) {
211         defer fs.debugPanics()
212         if fs.ReadOnly {
213                 return -fuse.EROFS
214         }
215
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))
220         }
221
222         // Other times, fh is invalid and we need to lookup path.
223         f, err := fs.root.OpenFile(path, os.O_RDWR, 0)
224         if err != nil {
225                 return fs.errCode(err)
226         }
227         defer f.Close()
228         return fs.errCode(f.Truncate(size))
229 }
230
231 func (fs *keepFS) Getattr(path string, stat *fuse.Stat_t, fh uint64) (errc int) {
232         defer fs.debugPanics()
233         var fi os.FileInfo
234         var err error
235         if f := fs.lookupFH(fh); f != nil {
236                 // Valid filehandle -- ignore path.
237                 fi, err = f.Stat()
238         } else {
239                 // Invalid filehandle -- lookup path.
240                 fi, err = fs.root.Stat(path)
241         }
242         if err != nil {
243                 return fs.errCode(err)
244         }
245         fs.fillStat(stat, fi)
246         return 0
247 }
248
249 func (fs *keepFS) Chmod(path string, mode uint32) (errc int) {
250         if fs.ReadOnly {
251                 return -fuse.EROFS
252         }
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 || (fi.Mode()&os.ModeDir != 0) != (mode&fuse.S_IFDIR != 0) {
256                 return -fuse.ENOSYS
257         } else {
258                 return 0
259         }
260 }
261
262 func (fs *keepFS) fillStat(stat *fuse.Stat_t, fi os.FileInfo) {
263         defer fs.debugPanics()
264         var m uint32
265         if fi.IsDir() {
266                 m = m | fuse.S_IFDIR
267         } else {
268                 m = m | fuse.S_IFREG
269         }
270         m = m | uint32(fi.Mode()&os.ModePerm)
271         stat.Mode = m
272         stat.Nlink = 1
273         stat.Size = fi.Size()
274         t := fuse.NewTimespec(fi.ModTime())
275         stat.Mtim = t
276         stat.Ctim = t
277         stat.Atim = t
278         stat.Birthtim = t
279         stat.Blksize = 1024
280         stat.Blocks = (stat.Size + stat.Blksize - 1) / stat.Blksize
281         if fs.Uid > 0 && int64(fs.Uid) < 1<<31 {
282                 stat.Uid = uint32(fs.Uid)
283         }
284         if fs.Gid > 0 && int64(fs.Gid) < 1<<31 {
285                 stat.Gid = uint32(fs.Gid)
286         }
287 }
288
289 func (fs *keepFS) Write(path string, buf []byte, ofst int64, fh uint64) (n int) {
290         defer fs.debugPanics()
291         if fs.ReadOnly {
292                 return -fuse.EROFS
293         }
294         f := fs.lookupFH(fh)
295         if f == nil {
296                 return -fuse.EBADF
297         }
298         f.Lock()
299         defer f.Unlock()
300         if _, err := f.Seek(ofst, io.SeekStart); err != nil {
301                 return fs.errCode(err)
302         }
303         n, err := f.Write(buf)
304         if err != nil {
305                 log.Printf("error writing %q: %s", path, err)
306                 return fs.errCode(err)
307         }
308         return n
309 }
310
311 func (fs *keepFS) Read(path string, buf []byte, ofst int64, fh uint64) (n int) {
312         defer fs.debugPanics()
313         f := fs.lookupFH(fh)
314         if f == nil {
315                 return -fuse.EBADF
316         }
317         f.Lock()
318         defer f.Unlock()
319         if _, err := f.Seek(ofst, io.SeekStart); err != nil {
320                 return fs.errCode(err)
321         }
322         n, err := f.Read(buf)
323         for err == nil && n < len(buf) {
324                 // f is an io.Reader ("If some data is available but
325                 // not len(p) bytes, Read conventionally returns what
326                 // is available instead of waiting for more") -- but
327                 // our caller requires us to either fill buf or reach
328                 // EOF.
329                 done := n
330                 n, err = f.Read(buf[done:])
331                 n += done
332         }
333         if err != nil && err != io.EOF {
334                 log.Printf("error reading %q: %s", path, err)
335                 return fs.errCode(err)
336         }
337         return n
338 }
339
340 func (fs *keepFS) Readdir(path string,
341         fill func(name string, stat *fuse.Stat_t, ofst int64) bool,
342         ofst int64,
343         fh uint64) (errc int) {
344         defer fs.debugPanics()
345         f := fs.lookupFH(fh)
346         if f == nil {
347                 return -fuse.EBADF
348         }
349         fill(".", nil, 0)
350         fill("..", nil, 0)
351         var stat fuse.Stat_t
352         fis, err := f.Readdir(-1)
353         if err != nil {
354                 return fs.errCode(err)
355         }
356         for _, fi := range fis {
357                 fs.fillStat(&stat, fi)
358                 fill(fi.Name(), &stat, 0)
359         }
360         return 0
361 }
362
363 func (fs *keepFS) Fsync(path string, datasync bool, fh uint64) int {
364         defer fs.debugPanics()
365         f := fs.lookupFH(fh)
366         if f == nil {
367                 return -fuse.EBADF
368         }
369         return fs.errCode(f.Sync())
370 }
371
372 func (fs *keepFS) Fsyncdir(path string, datasync bool, fh uint64) int {
373         return fs.Fsync(path, datasync, fh)
374 }
375
376 // debugPanics (when deferred by keepFS handlers) prints an error and
377 // stack trace on stderr when a handler crashes. (Without this,
378 // cgofuse recovers from panics silently and returns EIO.)
379 func (fs *keepFS) debugPanics() {
380         if err := recover(); err != nil {
381                 log.Printf("(%T) %v", err, err)
382                 debug.PrintStack()
383                 panic(err)
384         }
385 }