From d3f307af6c54cf013e71e9923b20786c4049c96c Mon Sep 17 00:00:00 2001 From: dukai Date: Mon, 26 May 2025 10:45:13 +0800 Subject: [PATCH] feat: add move directory --- api_directory.go | 12 ++++ api_file.go | 2 +- engine/filesystem_reanme.go | 134 ++++++++++++++++++++++++++++++++++++ router.go | 2 +- 4 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 engine/filesystem_reanme.go diff --git a/api_directory.go b/api_directory.go index 890f407..61678d9 100644 --- a/api_directory.go +++ b/api_directory.go @@ -123,6 +123,18 @@ func (s *Service) HandleRenameDirectory( req *httpserver.Request, resp *httpserver.Response, ) *httpserver.Response { + params := req.Binded.(*RenameParams) + srcPath := utils.NormalizePath(params.SrcPath) + dstPath := utils.NormalizePath(params.DstPath) + isDir := params.IsDir + + oldPath := utils.FullPath(srcPath) + newPath := utils.FullPath(dstPath) + if err := s.FileSystemManager.RenameDirectory(req.Context(), oldPath, newPath, isDir); err != nil { + logger.Error("rename dir %s to %s: %v", params.SrcPath, params.DstPath, err) + return resp.InternalServerError("rename dir is failed, " + err.Error()) + } + return resp.NoContent() } diff --git a/api_file.go b/api_file.go index d800a20..9394bdb 100644 --- a/api_file.go +++ b/api_file.go @@ -137,7 +137,7 @@ func (s *Service) HandleRenameFile( err := s.FileSystemManager.RenameFile(req.Context(), srcPath, dstPath, isDir) if err != nil { - logger.Error("rename %s to %s: %v", srcPath, dstPath, err) + logger.Error("rename file %s to %s: %v", srcPath, dstPath, err) return resp.InternalServerError("rename file failed, " + err.Error()) } diff --git a/engine/filesystem_reanme.go b/engine/filesystem_reanme.go new file mode 100644 index 0000000..7b7f43d --- /dev/null +++ b/engine/filesystem_reanme.go @@ -0,0 +1,134 @@ +package engine + +import ( + "context" + "fmt" + "gosvc/logger" + "strings" + "time" + + "robotfs/utils" +) + +func (f *FileSystemManager) RenameDirectory(ctx context.Context, oldPath, newPath utils.FullPath, isDir bool) error { + f.Lock() + defer f.Unlock() + + if err := f.canRename(oldPath, newPath, isDir); err != nil { + return err + } + + oldEntry, err := f.FindEntry(ctx, oldPath) + if err != nil { + return fmt.Errorf("%s not found: %v", oldPath, err) + } + + moveErr := f.moveEntry(ctx, oldPath, oldEntry, newPath) + if moveErr != nil { + return fmt.Errorf("%s move error: %v", oldPath, moveErr) + } + + if err := utils.Move(f.root, string(oldPath), string(newPath)); err != nil { + return fmt.Errorf("move file data failed: %v", err) + } + + return nil +} + +func (f *FileSystemManager) canRename(source, target utils.FullPath, isDir bool) error { + if string(source) == "/" { + return fmt.Errorf("mv: cannot move root directory") + } + + if isDir && strings.HasPrefix(string(target), string(source)) { + return fmt.Errorf("mv: can not move directory to a subdirectory if itself") + } + + return nil +} + +func (f *FileSystemManager) moveEntry(ctx context.Context, oldPath utils.FullPath, entry *utils.Entry, newPath utils.FullPath) error { + if err := f.moveSelfEntry(ctx, oldPath, entry, newPath, func() error { + if entry.IsDir { + if err := f.moveFolderSubEntries(ctx, oldPath, newPath); err != nil { + return err + } + } + return nil + }); err != nil { + return fmt.Errorf("fail to move %s => %s: %v", oldPath, newPath, err) + } + + return nil +} + +func (f *FileSystemManager) moveFolderSubEntries(ctx context.Context, oldPath utils.FullPath, newPath utils.FullPath) error { + logger.Info("moving folder %s => %s", oldPath, newPath) + + lastFileName := "" + includeLastFile := false + for { + entries := make([]*utils.Entry, 0, 1000) + lastFileName, err := f.doListDirectoryEntries(ctx, oldPath, lastFileName, includeLastFile, 1000, func(entry *utils.Entry) bool { + entries = append(entries, entry) + return true + }) + if err != nil { + return err + } + + for _, item := range entries { + itemOldPath := oldPath.Child(item.FullPath.Name()) + itemNewPath := newPath.Child(item.FullPath.Name()) + err := f.moveEntry(ctx, itemOldPath, item, itemNewPath) + if err != nil { + return err + } + } + + if lastFileName == "" { + break + } + } + + return nil +} + +func (f *FileSystemManager) moveSelfEntry(ctx context.Context, oldPath utils.FullPath, entry *utils.Entry, newPath utils.FullPath, moveFolderSubEntries func() error) error { + logger.Info("moving entry %s => %s", oldPath, newPath) + + if oldPath == newPath { + logger.Info("skip moving entry %s => %s", oldPath, newPath) + return nil + } + + newEntry := &utils.Entry{ + FullPath: newPath, + IsDir: entry.IsDir, + Size: entry.Size, + CreateTime: time.Now().Unix(), + S3Key: entry.S3Key, + ContentType: entry.ContentType, + Etag: entry.Etag, + VersionID: entry.VersionID, + LastModificationTime: time.Now().Unix(), + Extended: entry.Extended, + } + + if createErr := f.meta.InsertEntry(ctx, newEntry); createErr != nil { + return createErr + } + + if moveFolderSubEntries != nil { + if moveChildrenErr := moveFolderSubEntries(); moveChildrenErr != nil { + return moveChildrenErr + } + } + + deleteErr := f.meta.DeleteEntry(ctx, oldPath) + if deleteErr != nil { + return deleteErr + } + + return nil +} diff --git a/router.go b/router.go index 121fa66..234f89d 100644 --- a/router.go +++ b/router.go @@ -63,7 +63,7 @@ func (s *Service) RegisterRouteRules() { Handler: s.HandleRenameDirectory, Bind: &RenameParams{}, }, - // Todo: need juicefs clone + // Todo: juicefs clone { Path: "/dir/copy", Method: http.MethodPost,