12085: Counters support for status tracker.
[arvados.git] / services / keep-web / webdav.go
index 3e12f27e0d0bf2aaec5c847a0788ecb0dc7ba116..432c6af6d89847068cfbc154a413ade33c5585dc 100644 (file)
@@ -8,6 +8,7 @@ import (
        "crypto/rand"
        "errors"
        "fmt"
+       "io"
        prand "math/rand"
        "os"
        "path"
@@ -37,10 +38,16 @@ var (
 type webdavFS struct {
        collfs  arvados.CollectionFileSystem
        writing bool
+       // webdav PROPFIND reads the first few bytes of each file
+       // whose filename extension isn't recognized, which is
+       // prohibitively expensive: we end up fetching multiple 64MiB
+       // blocks. Avoid this by returning EOF on all reads when
+       // handling a PROPFIND.
+       alwaysReadEOF bool
 }
 
 func (fs *webdavFS) makeparents(name string) {
-       dir, name := path.Split(name)
+       dir, _ := path.Split(name)
        if dir == "" || dir == "/" {
                return
        }
@@ -61,12 +68,19 @@ func (fs *webdavFS) Mkdir(ctx context.Context, name string, perm os.FileMode) er
 func (fs *webdavFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (f webdav.File, err error) {
        writing := flag&(os.O_WRONLY|os.O_RDWR) != 0
        if writing {
-               if !fs.writing {
-                       return nil, errReadOnly
-               }
                fs.makeparents(name)
        }
        f, err = fs.collfs.OpenFile(name, flag, perm)
+       if !fs.writing {
+               // webdav module returns 404 on all OpenFile errors,
+               // but returns 405 Method Not Allowed if OpenFile()
+               // succeeds but Write() or Close() fails. We'd rather
+               // have 405.
+               f = writeFailer{File: f, err: errReadOnly}
+       }
+       if fs.alwaysReadEOF {
+               f = readEOF{File: f}
+       }
        return
 }
 
@@ -89,6 +103,27 @@ func (fs *webdavFS) Stat(ctx context.Context, name string) (os.FileInfo, error)
        return fs.collfs.Stat(name)
 }
 
+type writeFailer struct {
+       webdav.File
+       err error
+}
+
+func (wf writeFailer) Write([]byte) (int, error) {
+       return 0, wf.err
+}
+
+func (wf writeFailer) Close() error {
+       return wf.err
+}
+
+type readEOF struct {
+       webdav.File
+}
+
+func (readEOF) Read(p []byte) (int, error) {
+       return 0, io.EOF
+}
+
 // noLockSystem implements webdav.LockSystem by returning success for
 // every possible locking operation, even though it has no side
 // effects such as actually locking anything. This works for a