diff --git a/drivers/alias/driver.go b/drivers/alias/driver.go index 208708aa9..32047b01b 100644 --- a/drivers/alias/driver.go +++ b/drivers/alias/driver.go @@ -234,16 +234,8 @@ func (d *Alias) Other(ctx context.Context, args model.OtherArgs) (interface{}, e if err != nil { continue } - other, ok := storage.(driver.Other) - if !ok { - continue - } - obj, err := op.GetUnwrap(ctx, storage, actualPath) - if err != nil { - continue - } - return other.Other(ctx, model.OtherArgs{ - Obj: obj, + return op.Other(ctx, storage, model.FsOtherArgs{ + Path: actualPath, Method: args.Method, Data: args.Data, }) @@ -535,6 +527,9 @@ func (d *Alias) ResolveLinkCacheMode(path string) driver.LinkCacheMode { if err != nil { continue } + if storage.Config().CheckStatus && storage.GetStorage().Status != op.WORK { + continue + } mode := storage.Config().LinkCacheMode if mode == -1 { return storage.(driver.LinkCacheModeResolver).ResolveLinkCacheMode(actualPath) diff --git a/drivers/crypt/driver.go b/drivers/crypt/driver.go index 1398ff1cb..7b14894c9 100644 --- a/drivers/crypt/driver.go +++ b/drivers/crypt/driver.go @@ -29,8 +29,7 @@ import ( type Crypt struct { model.Storage Addition - cipher *rcCrypt.Cipher - remoteStorage driver.Driver + cipher *rcCrypt.Cipher } const obfuscatedPrefix = "___Obfuscated___" @@ -60,15 +59,7 @@ func (d *Crypt) Init(ctx context.Context) error { } d.FileNameEncoding = utils.GetNoneEmpty(d.FileNameEncoding, "base64") d.EncryptedSuffix = utils.GetNoneEmpty(d.EncryptedSuffix, ".bin") - - op.MustSaveDriverStorage(d) - - // need remote storage exist - storage, err := fs.GetStorage(d.RemotePath, &fs.GetStoragesArgs{}) - if err != nil { - return fmt.Errorf("can't find remote storage: %w", err) - } - d.remoteStorage = storage + d.RemotePath = utils.FixAndCleanPath(d.RemotePath) p, _ := strings.CutPrefix(d.Password, obfuscatedPrefix) p2, _ := strings.CutPrefix(d.Salt, obfuscatedPrefix) @@ -108,21 +99,19 @@ func (d *Crypt) Drop(ctx context.Context) error { } func (d *Crypt) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { - path := dir.GetPath() - // return d.list(ctx, d.RemotePath, path) - // remoteFull - - objs, err := fs.List(ctx, d.getPathForRemote(path, true), &fs.ListArgs{NoLog: true, Refresh: args.Refresh}) + remoteFullPath := dir.GetPath() + objs, err := fs.List(ctx, remoteFullPath, &fs.ListArgs{NoLog: true, Refresh: args.Refresh}) // the obj must implement the model.SetPath interface // return objs, err if err != nil { return nil, err } - var result []model.Obj + result := make([]model.Obj, 0, len(objs)) for _, obj := range objs { + rawName := model.UnwrapObj(obj).GetName() if obj.IsDir() { - name, err := d.cipher.DecryptDirName(obj.GetName()) + name, err := d.cipher.DecryptDirName(rawName) if err != nil { // filter illegal files continue @@ -130,57 +119,55 @@ func (d *Crypt) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([ if !d.ShowHidden && strings.HasPrefix(name, ".") { continue } - objRes := model.Object{ + result = append(result, &model.Object{ + Path: stdpath.Join(remoteFullPath, rawName), Name: name, Size: 0, Modified: obj.ModTime(), IsFolder: obj.IsDir(), Ctime: obj.CreateTime(), // discarding hash as it's encrypted - } - result = append(result, &objRes) - } else { - thumb, ok := model.GetThumb(obj) - size, err := d.cipher.DecryptedSize(obj.GetSize()) - if err != nil { - // filter illegal files - continue - } - name, err := d.cipher.DecryptFileName(obj.GetName()) - if err != nil { - // filter illegal files - continue - } - if !d.ShowHidden && strings.HasPrefix(name, ".") { - continue - } - objRes := model.Object{ - Name: name, - Size: size, - Modified: obj.ModTime(), - IsFolder: obj.IsDir(), - Ctime: obj.CreateTime(), - // discarding hash as it's encrypted - } - if d.Thumbnail && thumb == "" { - thumbPath := stdpath.Join(args.ReqPath, ".thumbnails", name+".webp") - thumb = fmt.Sprintf("%s/d%s?sign=%s", - common.GetApiUrl(ctx), - utils.EncodePath(thumbPath, true), - sign.Sign(thumbPath)) - } - if !ok && !d.Thumbnail { - result = append(result, &objRes) - } else { - objWithThumb := model.ObjThumb{ - Object: objRes, - Thumbnail: model.Thumbnail{ - Thumbnail: thumb, - }, - } - result = append(result, &objWithThumb) - } + }) + continue + } + + size, err := d.cipher.DecryptedSize(obj.GetSize()) + if err != nil { + // filter illegal files + continue + } + name, err := d.cipher.DecryptFileName(rawName) + if err != nil { + // filter illegal files + continue + } + if !d.ShowHidden && strings.HasPrefix(name, ".") { + continue } + objRes := &model.Object{ + Path: stdpath.Join(remoteFullPath, rawName), + Name: name, + Size: size, + Modified: obj.ModTime(), + IsFolder: obj.IsDir(), + Ctime: obj.CreateTime(), + // discarding hash as it's encrypted + } + if !d.Thumbnail || !strings.HasPrefix(args.ReqPath, "/") { + result = append(result, objRes) + continue + } + thumbPath := stdpath.Join(args.ReqPath, ".thumbnails", name+".webp") + thumb := fmt.Sprintf("%s/d%s?sign=%s", + common.GetApiUrl(ctx), + utils.EncodePath(thumbPath, true), + sign.Sign(thumbPath)) + result = append(result, &model.ObjThumb{ + Object: *objRes, + Thumbnail: model.Thumbnail{ + Thumbnail: thumb, + }, + }) } return result, nil @@ -191,67 +178,62 @@ func (d *Crypt) Get(ctx context.Context, path string) (model.Obj, error) { return &model.Object{ Name: "Root", IsFolder: true, - Path: "/", + Path: d.RemotePath, }, nil } - remoteFullPath := "" - var remoteObj model.Obj - var err, err2 error + firstTryIsFolder, secondTry := guessPath(path) - remoteFullPath = d.getPathForRemote(path, firstTryIsFolder) - remoteObj, err = fs.Get(ctx, remoteFullPath, &fs.GetArgs{NoLog: true}) + remoteFullPath := stdpath.Join(d.RemotePath, d.encryptPath(path, firstTryIsFolder)) + remoteObj, err := fs.Get(ctx, remoteFullPath, &fs.GetArgs{NoLog: true}) if err != nil { - if errs.IsObjectNotFound(err) && secondTry { + if secondTry && errs.IsObjectNotFound(err) { // try the opposite - remoteFullPath = d.getPathForRemote(path, !firstTryIsFolder) - remoteObj, err2 = fs.Get(ctx, remoteFullPath, &fs.GetArgs{NoLog: true}) - if err2 != nil { - return nil, err2 + remoteFullPath = stdpath.Join(d.RemotePath, d.encryptPath(path, !firstTryIsFolder)) + remoteObj, err = fs.Get(ctx, remoteFullPath, &fs.GetArgs{NoLog: true}) + if err != nil { + return nil, err } } else { return nil, err } } - var size int64 = 0 - name := "" + + size := remoteObj.GetSize() + name := model.UnwrapObj(remoteObj).GetName() if !remoteObj.IsDir() { - size, err = d.cipher.DecryptedSize(remoteObj.GetSize()) + size, err = d.cipher.DecryptedSize(size) if err != nil { log.Warnf("DecryptedSize failed for %s ,will use original size, err:%s", path, err) - size = remoteObj.GetSize() } - name, err = d.cipher.DecryptFileName(remoteObj.GetName()) + name, err = d.cipher.DecryptFileName(name) if err != nil { log.Warnf("DecryptFileName failed for %s ,will use original name, err:%s", path, err) - name = remoteObj.GetName() } } else { - name, err = d.cipher.DecryptDirName(remoteObj.GetName()) + name, err = d.cipher.DecryptDirName(name) if err != nil { log.Warnf("DecryptDirName failed for %s ,will use original name, err:%s", path, err) - name = remoteObj.GetName() } } obj := &model.Object{ - Path: path, + Path: remoteFullPath, Name: name, Size: size, Modified: remoteObj.ModTime(), IsFolder: remoteObj.IsDir(), } return obj, nil - // return nil, errs.ObjectNotFound } // https://github.com/rclone/rclone/blob/v1.67.0/backend/crypt/cipher.go#L37 const fileHeaderSize = 32 -func (d *Crypt) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { - dstDirActualPath, err := d.getActualPathForRemote(file.GetPath(), false) +func (d *Crypt) Link(ctx context.Context, file model.Obj, _ model.LinkArgs) (*model.Link, error) { + remoteStorage, remoteActualPath, err := op.GetStorageAndActualPath(file.GetPath()) if err != nil { - return nil, fmt.Errorf("failed to convert path to remote path: %w", err) + return nil, err } - remoteLink, remoteFile, err := op.Link(ctx, d.remoteStorage, dstDirActualPath, args) + remoteLink, remoteFile, err := op.Link(ctx, remoteStorage, remoteActualPath, model.LinkArgs{}) if err != nil { return nil, err } @@ -323,30 +305,23 @@ func (d *Crypt) Link(ctx context.Context, file model.Obj, args model.LinkArgs) ( } func (d *Crypt) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error { - dstDirActualPath, err := d.getActualPathForRemote(parentDir.GetPath(), true) + remoteStorage, remoteActualPath, err := op.GetStorageAndActualPath(parentDir.GetPath()) if err != nil { - return fmt.Errorf("failed to convert path to remote path: %w", err) + return err } - dir := d.cipher.EncryptDirName(dirName) - return op.MakeDir(ctx, d.remoteStorage, stdpath.Join(dstDirActualPath, dir)) + encryptedName := d.cipher.EncryptDirName(dirName) + return op.MakeDir(ctx, remoteStorage, stdpath.Join(remoteActualPath, encryptedName)) } func (d *Crypt) Move(ctx context.Context, srcObj, dstDir model.Obj) error { - srcRemoteActualPath, err := d.getActualPathForRemote(srcObj.GetPath(), srcObj.IsDir()) - if err != nil { - return fmt.Errorf("failed to convert path to remote path: %w", err) - } - dstRemoteActualPath, err := d.getActualPathForRemote(dstDir.GetPath(), dstDir.IsDir()) - if err != nil { - return fmt.Errorf("failed to convert path to remote path: %w", err) - } - return op.Move(ctx, d.remoteStorage, srcRemoteActualPath, dstRemoteActualPath) + _, err := fs.Move(ctx, srcObj.GetPath(), dstDir.GetPath()) + return err } func (d *Crypt) Rename(ctx context.Context, srcObj model.Obj, newName string) error { - remoteActualPath, err := d.getActualPathForRemote(srcObj.GetPath(), srcObj.IsDir()) + remoteStorage, remoteActualPath, err := op.GetStorageAndActualPath(srcObj.GetPath()) if err != nil { - return fmt.Errorf("failed to convert path to remote path: %w", err) + return err } var newEncryptedName string if srcObj.IsDir() { @@ -354,33 +329,26 @@ func (d *Crypt) Rename(ctx context.Context, srcObj model.Obj, newName string) er } else { newEncryptedName = d.cipher.EncryptFileName(newName) } - return op.Rename(ctx, d.remoteStorage, remoteActualPath, newEncryptedName) + return op.Rename(ctx, remoteStorage, remoteActualPath, newEncryptedName) } func (d *Crypt) Copy(ctx context.Context, srcObj, dstDir model.Obj) error { - srcRemoteActualPath, err := d.getActualPathForRemote(srcObj.GetPath(), srcObj.IsDir()) - if err != nil { - return fmt.Errorf("failed to convert path to remote path: %w", err) - } - dstRemoteActualPath, err := d.getActualPathForRemote(dstDir.GetPath(), dstDir.IsDir()) - if err != nil { - return fmt.Errorf("failed to convert path to remote path: %w", err) - } - return op.Copy(ctx, d.remoteStorage, srcRemoteActualPath, dstRemoteActualPath) + _, err := fs.Copy(ctx, srcObj.GetPath(), dstDir.GetPath()) + return err } func (d *Crypt) Remove(ctx context.Context, obj model.Obj) error { - remoteActualPath, err := d.getActualPathForRemote(obj.GetPath(), obj.IsDir()) + remoteStorage, remoteActualPath, err := op.GetStorageAndActualPath(obj.GetPath()) if err != nil { - return fmt.Errorf("failed to convert path to remote path: %w", err) + return err } - return op.Remove(ctx, d.remoteStorage, remoteActualPath) + return op.Remove(ctx, remoteStorage, remoteActualPath) } func (d *Crypt) Put(ctx context.Context, dstDir model.Obj, streamer model.FileStreamer, up driver.UpdateProgress) error { - dstDirActualPath, err := d.getActualPathForRemote(dstDir.GetPath(), true) + remoteStorage, remoteActualPath, err := op.GetStorageAndActualPath(dstDir.GetPath()) if err != nil { - return fmt.Errorf("failed to convert path to remote path: %w", err) + return err } // Encrypt the data into wrappedIn @@ -404,15 +372,15 @@ func (d *Crypt) Put(ctx context.Context, dstDir model.Obj, streamer model.FileSt ForceStreamUpload: true, Exist: streamer.GetExist(), } - err = op.Put(ctx, d.remoteStorage, dstDirActualPath, streamOut, up, false) - if err != nil { - return err - } - return nil + return op.Put(ctx, remoteStorage, remoteActualPath, streamOut, up) } func (d *Crypt) GetDetails(ctx context.Context) (*model.StorageDetails, error) { - remoteDetails, err := op.GetStorageDetails(ctx, d.remoteStorage) + remoteStorage, _, err := op.GetStorageAndActualPath(d.RemotePath) + if err != nil { + return nil, errs.NotImplement + } + remoteDetails, err := op.GetStorageDetails(ctx, remoteStorage) if err != nil { return nil, err } @@ -421,8 +389,4 @@ func (d *Crypt) GetDetails(ctx context.Context) (*model.StorageDetails, error) { }, nil } -//func (d *Safe) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { -// return nil, errs.NotSupport -//} - var _ driver.Driver = (*Crypt)(nil) diff --git a/drivers/crypt/meta.go b/drivers/crypt/meta.go index b2d009ba3..6a4659910 100644 --- a/drivers/crypt/meta.go +++ b/drivers/crypt/meta.go @@ -6,11 +6,6 @@ import ( ) type Addition struct { - // Usually one of two - //driver.RootPath - //driver.RootID - // define other - FileNameEnc string `json:"filename_encryption" type:"select" required:"true" options:"off,standard,obfuscate" default:"off"` DirNameEnc string `json:"directory_name_encryption" type:"select" required:"true" options:"false,true" default:"false"` RemotePath string `json:"remote_path" required:"true" help:"This is where the encrypted data stores"` @@ -32,6 +27,7 @@ var config = driver.Config{ NoCache: true, DefaultRoot: "/", NoLinkURL: true, + CheckStatus: true, } func init() { diff --git a/drivers/crypt/util.go b/drivers/crypt/util.go index 417059d38..e0f2bef74 100644 --- a/drivers/crypt/util.go +++ b/drivers/crypt/util.go @@ -4,8 +4,6 @@ import ( stdpath "path" "path/filepath" "strings" - - "github.com/OpenListTeam/OpenList/v4/internal/op" ) // will give the best guessing based on the path @@ -15,30 +13,17 @@ func guessPath(path string) (isFolder, secondTry bool) { return true, false } lastSlash := strings.LastIndex(path, "/") - if strings.Index(path[lastSlash:], ".") < 0 { + if !strings.Contains(path[lastSlash:], ".") { //no dot, try folder then try file return true, true } return false, true } -func (d *Crypt) getPathForRemote(path string, isFolder bool) (remoteFullPath string) { - if isFolder && !strings.HasSuffix(path, "/") { - path = path + "/" +func (d *Crypt) encryptPath(path string, isFolder bool) string { + if isFolder { + return d.cipher.EncryptDirName(path) } dir, fileName := filepath.Split(path) - - remoteDir := d.cipher.EncryptDirName(dir) - remoteFileName := "" - if len(strings.TrimSpace(fileName)) > 0 { - remoteFileName = d.cipher.EncryptFileName(fileName) - } - return stdpath.Join(d.RemotePath, remoteDir, remoteFileName) - -} - -// actual path is used for internal only. any link for user should come from remoteFullPath -func (d *Crypt) getActualPathForRemote(path string, isFolder bool) (string, error) { - _, remoteActualPath, err := op.GetStorageAndActualPath(d.getPathForRemote(path, isFolder)) - return remoteActualPath, err + return stdpath.Join(d.cipher.EncryptDirName(dir), d.cipher.EncryptFileName(fileName)) } diff --git a/internal/op/fs.go b/internal/op/fs.go index 087857f5d..e88287b88 100644 --- a/internal/op/fs.go +++ b/internal/op/fs.go @@ -227,20 +227,23 @@ func Link(ctx context.Context, storage driver.Driver, path string, args model.Li } // Other api -func Other(ctx context.Context, storage driver.Driver, args model.FsOtherArgs) (interface{}, error) { +func Other(ctx context.Context, storage driver.Driver, args model.FsOtherArgs) (any, error) { + if storage.Config().CheckStatus && storage.GetStorage().Status != WORK { + return nil, errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status) + } + o, ok := storage.(driver.Other) + if !ok { + return nil, errs.NotImplement + } obj, err := GetUnwrap(ctx, storage, args.Path) if err != nil { return nil, errors.WithMessagef(err, "failed to get obj") } - if o, ok := storage.(driver.Other); ok { - return o.Other(ctx, model.OtherArgs{ - Obj: obj, - Method: args.Method, - Data: args.Data, - }) - } else { - return nil, errs.NotImplement - } + return o.Other(ctx, model.OtherArgs{ + Obj: obj, + Method: args.Method, + Data: args.Data, + }) } var mkdirG singleflight.Group[any] @@ -309,6 +312,9 @@ func Move(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string return errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status) } srcPath = utils.FixAndCleanPath(srcPath) + if utils.PathEqual(srcPath, "/") { + return errors.New("move root folder is not allowed") + } srcDirPath := stdpath.Dir(srcPath) dstDirPath = utils.FixAndCleanPath(dstDirPath) if dstDirPath == srcDirPath { @@ -371,6 +377,9 @@ func Rename(ctx context.Context, storage driver.Driver, srcPath, dstName string, return errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status) } srcPath = utils.FixAndCleanPath(srcPath) + if utils.PathEqual(srcPath, "/") { + return errors.New("rename root folder is not allowed") + } srcRawObj, err := Get(ctx, storage, srcPath) if err != nil { return errors.WithMessage(err, "failed to get src object") @@ -471,10 +480,10 @@ func Remove(ctx context.Context, storage driver.Driver, path string) error { if storage.Config().CheckStatus && storage.GetStorage().Status != WORK { return errors.WithMessagef(errs.StorageNotInit, "storage status: %s", storage.GetStorage().Status) } + path = utils.FixAndCleanPath(path) if utils.PathEqual(path, "/") { - return errors.New("delete root folder is not allowed, please goto the manage page to delete the storage instead") + return errors.New("delete root folder is not allowed") } - path = utils.FixAndCleanPath(path) rawObj, err := Get(ctx, storage, path) if err != nil { // if object not found, it's ok