diff --git a/fs_local.go b/fs_local.go index 201ae93..7668612 100644 --- a/fs_local.go +++ b/fs_local.go @@ -63,6 +63,18 @@ func fileInfoFromOS(p string, fi os.FileInfo) *FileInfo { } } +func errFromOS(err error) error { + if os.IsNotExist(err) { + return NewHTTPError(http.StatusNotFound, err) + } else if os.IsPermission(err) { + return NewHTTPError(http.StatusForbidden, err) + } else if os.IsTimeout(err) { + return NewHTTPError(http.StatusServiceUnavailable, err) + } else { + return err + } +} + func (fs LocalFileSystem) Stat(ctx context.Context, name string) (*FileInfo, error) { p, err := fs.localPath(name) if err != nil { @@ -70,7 +82,7 @@ func (fs LocalFileSystem) Stat(ctx context.Context, name string) (*FileInfo, err } fi, err := os.Stat(p) if err != nil { - return nil, err + return nil, errFromOS(err) } return fileInfoFromOS(name, fi), nil } @@ -99,7 +111,7 @@ func (fs LocalFileSystem) ReadDir(ctx context.Context, name string, recursive bo } return nil }) - return l, err + return l, errFromOS(err) } func (fs LocalFileSystem) Create(ctx context.Context, name string) (io.WriteCloser, error) { @@ -107,7 +119,8 @@ func (fs LocalFileSystem) Create(ctx context.Context, name string) (io.WriteClos if err != nil { return nil, err } - return os.Create(p) + wc, err := os.Create(p) + return wc, errFromOS(err) } func (fs LocalFileSystem) RemoveAll(ctx context.Context, name string) error { @@ -119,10 +132,10 @@ func (fs LocalFileSystem) RemoveAll(ctx context.Context, name string) error { // WebDAV semantics are that it should return a "404 Not Found" error in // case the resource doesn't exist. We need to Stat before RemoveAll. if _, err = os.Stat(p); err != nil { - return err + return errFromOS(err) } - return os.RemoveAll(p) + return errFromOS(os.RemoveAll(p)) } func (fs LocalFileSystem) Mkdir(ctx context.Context, name string) error { @@ -130,20 +143,21 @@ func (fs LocalFileSystem) Mkdir(ctx context.Context, name string) error { if err != nil { return err } - return os.Mkdir(p, 0755) + return errFromOS(os.Mkdir(p, 0755)) } func copyRegularFile(src, dst string, perm os.FileMode) error { srcFile, err := os.Open(src) if err != nil { - return err + return errFromOS(err) } defer srcFile.Close() dstFile, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm) - if err != nil { - // TODO: send http.StatusConflict on os.IsNotExist - return err + if os.IsNotExist(err) { + return NewHTTPError(http.StatusConflict, err) + } else if err != nil { + return errFromOS(err) } defer dstFile.Close() @@ -169,21 +183,21 @@ func (fs LocalFileSystem) Copy(ctx context.Context, src, dst string, options *Co srcInfo, err := os.Stat(srcPath) if err != nil { - return false, err + return false, errFromOS(err) } srcPerm := srcInfo.Mode() & os.ModePerm if _, err := os.Stat(dstPath); err != nil { if !os.IsNotExist(err) { - return false, err + return false, errFromOS(err) } created = true } else { if options.NoOverwrite { - return false, os.ErrExist + return false, NewHTTPError(http.StatusPreconditionFailed, os.ErrExist) } if err := os.RemoveAll(dstPath); err != nil { - return false, err + return false, errFromOS(err) } } @@ -194,7 +208,7 @@ func (fs LocalFileSystem) Copy(ctx context.Context, src, dst string, options *Co if fi.IsDir() { if err := os.Mkdir(dstPath, srcPerm); err != nil { - return err + return errFromOS(err) } } else { if err := copyRegularFile(srcPath, dstPath, srcPerm); err != nil { @@ -208,7 +222,7 @@ func (fs LocalFileSystem) Copy(ctx context.Context, src, dst string, options *Co return nil }) if err != nil { - return false, err + return false, errFromOS(err) } return created, nil @@ -226,20 +240,20 @@ func (fs LocalFileSystem) Move(ctx context.Context, src, dst string, options *Mo if _, err := os.Stat(dstPath); err != nil { if !os.IsNotExist(err) { - return false, err + return false, errFromOS(err) } created = true } else { if options.NoOverwrite { - return false, os.ErrExist + return false, NewHTTPError(http.StatusPreconditionFailed, os.ErrExist) } if err := os.RemoveAll(dstPath); err != nil { - return false, err + return false, errFromOS(err) } } if err := os.Rename(srcPath, dstPath); err != nil { - return false, err + return false, errFromOS(err) } return created, nil diff --git a/server.go b/server.go index 984c763..bf3ec10 100644 --- a/server.go +++ b/server.go @@ -57,7 +57,7 @@ type backend struct { func (b *backend) Options(r *http.Request) (caps []string, allow []string, err error) { fi, err := b.FileSystem.Stat(r.Context(), r.URL.Path) - if os.IsNotExist(err) { + if internal.IsNotFound(err) { return nil, []string{http.MethodOptions, http.MethodPut, "MKCOL"}, nil } else if err != nil { return nil, nil, err @@ -80,9 +80,7 @@ func (b *backend) Options(r *http.Request) (caps []string, allow []string, err e func (b *backend) HeadGet(w http.ResponseWriter, r *http.Request) error { fi, err := b.FileSystem.Stat(r.Context(), r.URL.Path) - if os.IsNotExist(err) { - return &internal.HTTPError{Code: http.StatusNotFound, Err: err} - } else if err != nil { + if err != nil { return err } if fi.IsDir { @@ -121,9 +119,7 @@ func (b *backend) PropFind(r *http.Request, propfind *internal.PropFind, depth i // TODO: use partial error Response on error fi, err := b.FileSystem.Stat(r.Context(), r.URL.Path) - if os.IsNotExist(err) { - return nil, &internal.HTTPError{Code: http.StatusNotFound, Err: err} - } else if err != nil { + if err != nil { return nil, err } @@ -212,11 +208,7 @@ func (b *backend) Put(r *http.Request) (*internal.Href, error) { } func (b *backend) Delete(r *http.Request) error { - err := b.FileSystem.RemoveAll(r.Context(), r.URL.Path) - if os.IsNotExist(err) { - return &internal.HTTPError{Code: http.StatusNotFound, Err: err} - } - return err + return b.FileSystem.RemoveAll(r.Context(), r.URL.Path) } func (b *backend) Mkcol(r *http.Request) error { @@ -224,7 +216,7 @@ func (b *backend) Mkcol(r *http.Request) error { return internal.HTTPErrorf(http.StatusUnsupportedMediaType, "webdav: request body not supported in MKCOL request") } err := b.FileSystem.Mkdir(r.Context(), r.URL.Path) - if os.IsNotExist(err) { + if internal.IsNotFound(err) { return &internal.HTTPError{Code: http.StatusConflict, Err: err} } return err