From d4268f4ab181d6bb9bf8d14be9e9c2d5e8abebba Mon Sep 17 00:00:00 2001 From: dukai Date: Fri, 23 May 2025 16:08:47 +0800 Subject: [PATCH] feat: add remove directory --- api_directory.go | 20 ++++++-- engine/filesystem_manager.go | 96 ++++++++++++++++++++++++++++++++++++ utils/operation.go | 16 ++++++ 3 files changed, 129 insertions(+), 3 deletions(-) diff --git a/api_directory.go b/api_directory.go index 4c291b8..890f407 100644 --- a/api_directory.go +++ b/api_directory.go @@ -45,7 +45,7 @@ func (s *Service) HandleMkdir( err := s.FileSystemManager.MakeDirectory(req.Context(), utils.FullPath(newPath)) if err != nil { - logger.Error("makeDirectory %s: %v", path, err) + logger.Error("mkdir %s: %v", path, err) return resp.InternalServerError("mkdir failed, " + err.Error()) } @@ -70,8 +70,8 @@ func (s *Service) HandleListDirectory( limit, ) if err != nil { - logger.Error("listDirectory %s %s %d: %v", path, startFileName, limit, err) - return resp.InternalServerError("listDirectory failed, " + err.Error()) + logger.Error("list dir %s %s %d: %v", path, startFileName, limit, err) + return resp.InternalServerError("list directory failed, " + err.Error()) } result := ListResult{ @@ -102,6 +102,20 @@ func (s *Service) HandleDeleteDirectory( req *httpserver.Request, resp *httpserver.Response, ) *httpserver.Response { + params := req.Binded.(*DeleteParams) + path := params.Path + isDir := params.IsDir + newPath := utils.NormalizePath(path) + + if !isDir { + resp.BadRequest("is dir param must be true") + } + + if err := s.FileSystemManager.DeleteDirectory(req.Context(), utils.FullPath(newPath), isDir, false); err != nil { + logger.Error("del dir %s: %v", path, err) + return resp.InternalServerError("delete directory is failed, " + err.Error()) + } + return resp.NoContent() } diff --git a/engine/filesystem_manager.go b/engine/filesystem_manager.go index 0b75163..dd3ca0b 100644 --- a/engine/filesystem_manager.go +++ b/engine/filesystem_manager.go @@ -83,6 +83,102 @@ func (f *FileSystemManager) MakeDirectory(ctx context.Context, path utils.FullPa return f.meta.InsertEntry(ctx, entry) } +func (f *FileSystemManager) DeleteDirectory(ctx context.Context, path utils.FullPath, isRecursive, ignoreRecursiveError bool) error { + f.Lock() + defer f.Unlock() + + if string(path) == "/" { + return nil + } + + entry, err := f.FindEntry(ctx, path) + if err != nil { + return err + } + + if entry.IsDir { + err = f.doBatchDeleteFolderMetaAndData(ctx, entry, isRecursive, ignoreRecursiveError) + if err != nil { + return err + } + } + + // Delete the directory entry itself + err = f.doDeleteEntryMetaAndData(ctx, entry) + if err != nil { + return err + } + + if err := utils.Remove(f.root, string(path), isRecursive); err != nil { + return err + } + + return nil +} + +func (f *FileSystemManager) doBatchDeleteFolderMetaAndData(ctx context.Context, entry *utils.Entry, isRecursive, ignoreRecursiveError bool) error { + lastFileName := "" + includeLastFile := false + + for { + lastFileName, err := f.doListDirectoryEntries(ctx, entry.FullPath, lastFileName, includeLastFile, 1000, func(sub *utils.Entry) bool { + if sub.IsDir { + // Recursively delete subdirectory + err := f.doBatchDeleteFolderMetaAndData(ctx, sub, isRecursive, ignoreRecursiveError) + if err == nil { + err = f.doDeleteEntryMetaAndData(ctx, sub) + } + if err != nil && !ignoreRecursiveError { + return false + } + } else { + // Delete file entry + err := f.doDeleteEntryMetaAndData(ctx, sub) + if err != nil && !ignoreRecursiveError { + return false + } + } + return true + }) + if err != nil { + return fmt.Errorf("list folder %s: %v", entry.FullPath, err) + } + + if lastFileName == "" && !isRecursive { + // Check if there are any entries + hasEntries := false + _, err := f.doListDirectoryEntries(ctx, entry.FullPath, "", false, 1, func(_ *utils.Entry) bool { + hasEntries = true + return false + }) + if err != nil { + return fmt.Errorf("check directory empty: %v", err) + } + if hasEntries { + return fmt.Errorf("directory not empty: %s", entry.FullPath) + } + } + + if lastFileName == "" { + break + } + } + + if storeDeletionErr := f.meta.DeleteFolderChildren(ctx, entry.FullPath); storeDeletionErr != nil { + return fmt.Errorf("delete folder children metadata: %v", storeDeletionErr) + } + + return nil +} + +func (f *FileSystemManager) doDeleteEntryMetaAndData(ctx context.Context, entry *utils.Entry) error { + if storeDeletionErr := f.meta.DeleteEntry(ctx, entry.FullPath); storeDeletionErr != nil { + return fmt.Errorf("delete entry metadata: %v", storeDeletionErr) + } + + return nil +} + func (f *FileSystemManager) ListDirectoryEntries(ctx context.Context, p utils.FullPath, startFileName string, inclusive bool, limit int64) (entries []*utils.Entry, hasMore bool, err error) { f.RLock() defer f.RUnlock() diff --git a/utils/operation.go b/utils/operation.go index 3cdf512..62dd470 100644 --- a/utils/operation.go +++ b/utils/operation.go @@ -21,5 +21,21 @@ func Move(root, src, dst string) error { if err != nil { return fmt.Errorf("move failed: %v, output: %s", err, string(output)) } + + return nil +} + +func Remove(root, path string, isDir bool) error { + var cmd *exec.Cmd + if isDir { + cmd = exec.Command("rm", "-r", root+path) + } else { + cmd = exec.Command("rm", root+path) + } + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("remove failed: %v, output: %s", err, string(output)) + } + return nil }