webdav: stop using os errors in FileSystem interface

Use NewHTTPError instead.

Closes: https://github.com/emersion/go-webdav/issues/20
This commit is contained in:
Simon Ser 2024-02-06 15:23:30 +01:00
parent eaac65215b
commit 80d77a977a
2 changed files with 39 additions and 33 deletions

View File

@ -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) { func (fs LocalFileSystem) Stat(ctx context.Context, name string) (*FileInfo, error) {
p, err := fs.localPath(name) p, err := fs.localPath(name)
if err != nil { if err != nil {
@ -70,7 +82,7 @@ func (fs LocalFileSystem) Stat(ctx context.Context, name string) (*FileInfo, err
} }
fi, err := os.Stat(p) fi, err := os.Stat(p)
if err != nil { if err != nil {
return nil, err return nil, errFromOS(err)
} }
return fileInfoFromOS(name, fi), nil return fileInfoFromOS(name, fi), nil
} }
@ -99,7 +111,7 @@ func (fs LocalFileSystem) ReadDir(ctx context.Context, name string, recursive bo
} }
return nil return nil
}) })
return l, err return l, errFromOS(err)
} }
func (fs LocalFileSystem) Create(ctx context.Context, name string) (io.WriteCloser, error) { 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 { if err != nil {
return nil, err 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 { 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 // 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. // case the resource doesn't exist. We need to Stat before RemoveAll.
if _, err = os.Stat(p); err != nil { 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 { 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 { if err != nil {
return err return err
} }
return os.Mkdir(p, 0755) return errFromOS(os.Mkdir(p, 0755))
} }
func copyRegularFile(src, dst string, perm os.FileMode) error { func copyRegularFile(src, dst string, perm os.FileMode) error {
srcFile, err := os.Open(src) srcFile, err := os.Open(src)
if err != nil { if err != nil {
return err return errFromOS(err)
} }
defer srcFile.Close() defer srcFile.Close()
dstFile, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm) dstFile, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm)
if err != nil { if os.IsNotExist(err) {
// TODO: send http.StatusConflict on os.IsNotExist return NewHTTPError(http.StatusConflict, err)
return err } else if err != nil {
return errFromOS(err)
} }
defer dstFile.Close() defer dstFile.Close()
@ -169,21 +183,21 @@ func (fs LocalFileSystem) Copy(ctx context.Context, src, dst string, options *Co
srcInfo, err := os.Stat(srcPath) srcInfo, err := os.Stat(srcPath)
if err != nil { if err != nil {
return false, err return false, errFromOS(err)
} }
srcPerm := srcInfo.Mode() & os.ModePerm srcPerm := srcInfo.Mode() & os.ModePerm
if _, err := os.Stat(dstPath); err != nil { if _, err := os.Stat(dstPath); err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return false, err return false, errFromOS(err)
} }
created = true created = true
} else { } else {
if options.NoOverwrite { if options.NoOverwrite {
return false, os.ErrExist return false, NewHTTPError(http.StatusPreconditionFailed, os.ErrExist)
} }
if err := os.RemoveAll(dstPath); err != nil { 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 fi.IsDir() {
if err := os.Mkdir(dstPath, srcPerm); err != nil { if err := os.Mkdir(dstPath, srcPerm); err != nil {
return err return errFromOS(err)
} }
} else { } else {
if err := copyRegularFile(srcPath, dstPath, srcPerm); err != nil { 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 return nil
}) })
if err != nil { if err != nil {
return false, err return false, errFromOS(err)
} }
return created, nil 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 _, err := os.Stat(dstPath); err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return false, err return false, errFromOS(err)
} }
created = true created = true
} else { } else {
if options.NoOverwrite { if options.NoOverwrite {
return false, os.ErrExist return false, NewHTTPError(http.StatusPreconditionFailed, os.ErrExist)
} }
if err := os.RemoveAll(dstPath); err != nil { if err := os.RemoveAll(dstPath); err != nil {
return false, err return false, errFromOS(err)
} }
} }
if err := os.Rename(srcPath, dstPath); err != nil { if err := os.Rename(srcPath, dstPath); err != nil {
return false, err return false, errFromOS(err)
} }
return created, nil return created, nil

View File

@ -57,7 +57,7 @@ type backend struct {
func (b *backend) Options(r *http.Request) (caps []string, allow []string, err error) { func (b *backend) Options(r *http.Request) (caps []string, allow []string, err error) {
fi, err := b.FileSystem.Stat(r.Context(), r.URL.Path) 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 return nil, []string{http.MethodOptions, http.MethodPut, "MKCOL"}, nil
} else if err != nil { } else if err != nil {
return nil, nil, err 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 { func (b *backend) HeadGet(w http.ResponseWriter, r *http.Request) error {
fi, err := b.FileSystem.Stat(r.Context(), r.URL.Path) fi, err := b.FileSystem.Stat(r.Context(), r.URL.Path)
if os.IsNotExist(err) { if err != nil {
return &internal.HTTPError{Code: http.StatusNotFound, Err: err}
} else if err != nil {
return err return err
} }
if fi.IsDir { 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 // TODO: use partial error Response on error
fi, err := b.FileSystem.Stat(r.Context(), r.URL.Path) fi, err := b.FileSystem.Stat(r.Context(), r.URL.Path)
if os.IsNotExist(err) { if err != nil {
return nil, &internal.HTTPError{Code: http.StatusNotFound, Err: err}
} else if err != nil {
return nil, err 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 { func (b *backend) Delete(r *http.Request) error {
err := b.FileSystem.RemoveAll(r.Context(), r.URL.Path) return b.FileSystem.RemoveAll(r.Context(), r.URL.Path)
if os.IsNotExist(err) {
return &internal.HTTPError{Code: http.StatusNotFound, Err: err}
}
return err
} }
func (b *backend) Mkcol(r *http.Request) error { 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") return internal.HTTPErrorf(http.StatusUnsupportedMediaType, "webdav: request body not supported in MKCOL request")
} }
err := b.FileSystem.Mkdir(r.Context(), r.URL.Path) 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 &internal.HTTPError{Code: http.StatusConflict, Err: err}
} }
return err return err