internal/fs/wrappers/error_mapping.go (306 lines of code) (raw):

// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package wrappers import ( "context" "errors" "net/http" "strings" "syscall" "cloud.google.com/go/storage" "github.com/googlecloudplatform/gcsfuse/v2/internal/fs/gcsfuse_errors" "github.com/googlecloudplatform/gcsfuse/v2/internal/logger" "github.com/jacobsa/fuse/fuseops" "github.com/jacobsa/fuse/fuseutil" "google.golang.org/api/googleapi" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) var ( DefaultFSError = syscall.EIO ) func errno(err error, preconditionErrCfg bool) error { if err == nil { return nil } // Use existing em errno var errno syscall.Errno if errors.As(err, &errno) { return errno } // The fuse op is interrupted if errors.Is(err, context.Canceled) { return syscall.EINTR } // The object is modified or deleted by a concurrent process. var clobberedErr *gcsfuse_errors.FileClobberedError if errors.As(err, &clobberedErr) { if preconditionErrCfg { return syscall.ESTALE } return nil } if errors.Is(err, storage.ErrObjectNotExist) { return syscall.ENOENT } // The HTTP request is canceled if strings.Contains(err.Error(), "net/http: request canceled") { return syscall.ECANCELED } // Cannot authenticate or Permission denied. if strings.Contains(err.Error(), "oauth2: cannot fetch token") { return syscall.EACCES } if grpcStatus, ok := status.FromError(err); ok { switch grpcStatus.Code() { case codes.Canceled: return syscall.EINTR case codes.AlreadyExists: return syscall.EEXIST case codes.PermissionDenied, codes.Unauthenticated: return syscall.EACCES case codes.NotFound: return syscall.ENOENT } } // Translate API errors into an em errno var googleApiErr *googleapi.Error if errors.As(err, &googleApiErr) { switch googleApiErr.Code { case http.StatusForbidden, http.StatusUnauthorized: return syscall.EACCES case http.StatusNotFound: return syscall.ENOENT } } return DefaultFSError } // WithErrorMapping wraps a FileSystem, processing the returned errors, and // mapping them into syscall.Errno that can be understood by FUSE. func WithErrorMapping(wrapped fuseutil.FileSystem, preconditionErrCfg bool) fuseutil.FileSystem { return &errorMapping{ wrapped: wrapped, preconditionErrCfg: preconditionErrCfg, } } type errorMapping struct { wrapped fuseutil.FileSystem preconditionErrCfg bool } func (em *errorMapping) handlePanic() { // detect if panic occurred or not a := recover() if a != nil { logger.Fatal("Panic: %v", a) } } func (em *errorMapping) mapError(op string, err error) error { fsErr := errno(err, em.preconditionErrCfg) if err != nil && fsErr != nil && err != fsErr { logger.Errorf("%s: %v, %v", op, fsErr, err) } return fsErr } func (em *errorMapping) Destroy() { defer em.handlePanic() em.wrapped.Destroy() } func (em *errorMapping) StatFS( ctx context.Context, op *fuseops.StatFSOp) error { defer em.handlePanic() err := em.wrapped.StatFS(ctx, op) return em.mapError("StatFS", err) } func (em *errorMapping) LookUpInode( ctx context.Context, op *fuseops.LookUpInodeOp) error { defer em.handlePanic() err := em.wrapped.LookUpInode(ctx, op) return em.mapError("LookUpInode", err) } func (em *errorMapping) GetInodeAttributes( ctx context.Context, op *fuseops.GetInodeAttributesOp) error { defer em.handlePanic() err := em.wrapped.GetInodeAttributes(ctx, op) return em.mapError("GetInodeAttributes", err) } func (em *errorMapping) SetInodeAttributes( ctx context.Context, op *fuseops.SetInodeAttributesOp) error { defer em.handlePanic() err := em.wrapped.SetInodeAttributes(ctx, op) return em.mapError("SetInodeAttributes", err) } func (em *errorMapping) ForgetInode( ctx context.Context, op *fuseops.ForgetInodeOp) error { defer em.handlePanic() err := em.wrapped.ForgetInode(ctx, op) return em.mapError("ForgetInode", err) } func (em *errorMapping) BatchForget( ctx context.Context, op *fuseops.BatchForgetOp) error { defer em.handlePanic() err := em.wrapped.BatchForget(ctx, op) return em.mapError("BatchForget", err) } func (em *errorMapping) MkDir( ctx context.Context, op *fuseops.MkDirOp) error { defer em.handlePanic() err := em.wrapped.MkDir(ctx, op) return em.mapError("MkDir", err) } func (em *errorMapping) MkNode( ctx context.Context, op *fuseops.MkNodeOp) error { defer em.handlePanic() err := em.wrapped.MkNode(ctx, op) return em.mapError("MkNode", err) } func (em *errorMapping) CreateFile( ctx context.Context, op *fuseops.CreateFileOp) error { defer em.handlePanic() err := em.wrapped.CreateFile(ctx, op) return em.mapError("CreateFile", err) } func (em *errorMapping) CreateLink( ctx context.Context, op *fuseops.CreateLinkOp) error { defer em.handlePanic() err := em.wrapped.CreateLink(ctx, op) return em.mapError("CreateLink", err) } func (em *errorMapping) CreateSymlink( ctx context.Context, op *fuseops.CreateSymlinkOp) error { defer em.handlePanic() err := em.wrapped.CreateSymlink(ctx, op) return em.mapError("CreateSymlink", err) } func (em *errorMapping) Rename( ctx context.Context, op *fuseops.RenameOp) error { defer em.handlePanic() err := em.wrapped.Rename(ctx, op) return em.mapError("Rename", err) } func (em *errorMapping) RmDir( ctx context.Context, op *fuseops.RmDirOp) error { defer em.handlePanic() err := em.wrapped.RmDir(ctx, op) return em.mapError("RmDir", err) } func (em *errorMapping) Unlink( ctx context.Context, op *fuseops.UnlinkOp) error { defer em.handlePanic() err := em.wrapped.Unlink(ctx, op) return em.mapError("Unlink", err) } func (em *errorMapping) OpenDir( ctx context.Context, op *fuseops.OpenDirOp) error { defer em.handlePanic() err := em.wrapped.OpenDir(ctx, op) return em.mapError("OpenDir", err) } func (em *errorMapping) ReadDir( ctx context.Context, op *fuseops.ReadDirOp) error { defer em.handlePanic() err := em.wrapped.ReadDir(ctx, op) return em.mapError("ReadDir", err) } func (em *errorMapping) ReleaseDirHandle( ctx context.Context, op *fuseops.ReleaseDirHandleOp) error { defer em.handlePanic() err := em.wrapped.ReleaseDirHandle(ctx, op) return em.mapError("ReleaseDirHandle", err) } func (em *errorMapping) OpenFile( ctx context.Context, op *fuseops.OpenFileOp) error { defer em.handlePanic() err := em.wrapped.OpenFile(ctx, op) return em.mapError("OpenFile", err) } func (em *errorMapping) ReadFile( ctx context.Context, op *fuseops.ReadFileOp) error { defer em.handlePanic() err := em.wrapped.ReadFile(ctx, op) return em.mapError("ReadFile", err) } func (em *errorMapping) WriteFile( ctx context.Context, op *fuseops.WriteFileOp) error { defer em.handlePanic() err := em.wrapped.WriteFile(ctx, op) return em.mapError("WriteFile", err) } func (em *errorMapping) SyncFile( ctx context.Context, op *fuseops.SyncFileOp) error { defer em.handlePanic() err := em.wrapped.SyncFile(ctx, op) return em.mapError("SyncFile", err) } func (em *errorMapping) FlushFile( ctx context.Context, op *fuseops.FlushFileOp) error { defer em.handlePanic() err := em.wrapped.FlushFile(ctx, op) return em.mapError("FlushFile", err) } func (em *errorMapping) ReleaseFileHandle( ctx context.Context, op *fuseops.ReleaseFileHandleOp) error { defer em.handlePanic() err := em.wrapped.ReleaseFileHandle(ctx, op) return em.mapError("ReleaseFileHandle", err) } func (em *errorMapping) ReadSymlink( ctx context.Context, op *fuseops.ReadSymlinkOp) error { defer em.handlePanic() err := em.wrapped.ReadSymlink(ctx, op) return em.mapError("ReadSymlink", err) } func (em *errorMapping) RemoveXattr( ctx context.Context, op *fuseops.RemoveXattrOp) error { defer em.handlePanic() err := em.wrapped.RemoveXattr(ctx, op) return em.mapError("RemoveXattr", err) } func (em *errorMapping) GetXattr( ctx context.Context, op *fuseops.GetXattrOp) error { defer em.handlePanic() err := em.wrapped.GetXattr(ctx, op) return em.mapError("GetXattr", err) } func (em *errorMapping) ListXattr( ctx context.Context, op *fuseops.ListXattrOp) error { defer em.handlePanic() err := em.wrapped.ListXattr(ctx, op) return em.mapError("ListXattr", err) } func (em *errorMapping) SetXattr( ctx context.Context, op *fuseops.SetXattrOp) error { defer em.handlePanic() err := em.wrapped.SetXattr(ctx, op) return em.mapError("SetXattr", err) } func (em *errorMapping) Fallocate( ctx context.Context, op *fuseops.FallocateOp) error { defer em.handlePanic() err := em.wrapped.Fallocate(ctx, op) return em.mapError("Fallocate", err) } func (em *errorMapping) SyncFS( ctx context.Context, op *fuseops.SyncFSOp) error { defer em.handlePanic() err := em.wrapped.SyncFS(ctx, op) return em.mapError("SyncFS", err) }