robotfs/engine/filesystem_copy.go
2025-05-26 17:07:00 +08:00

136 lines
3.5 KiB
Go

package engine
import (
"context"
"fmt"
"gosvc/logger"
"strings"
"time"
"robotfs/utils"
)
func (f *FileSystemManager) CopyDirectory(ctx context.Context, oldPath, newPath utils.FullPath, isDir bool) error {
f.Lock()
defer f.Unlock()
if string(oldPath) == "/" {
return fmt.Errorf("cannot copy root directory")
}
if isDir && strings.HasPrefix(string(newPath), string(oldPath)) {
return fmt.Errorf("cannot copy directory to a subdirectory of itself")
}
oldEntry, err := f.FindEntry(ctx, oldPath)
if err != nil {
return fmt.Errorf("%s not found: %v", oldPath, err)
}
if newEntry, err := f.FindEntry(ctx, newPath); newEntry != nil && err == nil {
return fmt.Errorf("destination %s already exists", newPath)
}
parentDir, _ := newPath.DirAndName()
if parentDir != "/" {
if parentEntry, _ := f.FindEntry(ctx, utils.FullPath(parentDir)); parentEntry == nil {
return fmt.Errorf("parent directory %s does not exist", parentDir)
}
}
if err := f.copyEntry(ctx, oldPath, oldEntry, newPath); err != nil {
return fmt.Errorf("copy metadata failed: %v", err)
}
if err := utils.Clone(f.root, string(oldPath), string(newPath)); err != nil {
return fmt.Errorf("copy file data failed: %v", err)
}
return nil
}
func (f *FileSystemManager) copyEntry(ctx context.Context, oldPath utils.FullPath, entry *utils.Entry, newPath utils.FullPath) error {
if err := f.copySelfEntry(ctx, oldPath, entry, newPath, func() error {
if entry.IsDir {
if err := f.copyFolderSubEntries(ctx, oldPath, newPath); err != nil {
return err
}
}
return nil
}); err != nil {
return fmt.Errorf("fail to copy %s => %s: %v", oldPath, newPath, err)
}
return nil
}
func (f *FileSystemManager) copyFolderSubEntries(ctx context.Context, oldPath utils.FullPath, newPath utils.FullPath) error {
logger.Info("copying 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
}
if len(entries) == 0 {
break
}
for _, item := range entries {
itemOldPath := oldPath.Child(item.FullPath.Name())
itemNewPath := newPath.Child(item.FullPath.Name())
err := f.copyEntry(ctx, itemOldPath, item, itemNewPath)
if err != nil {
return err
}
}
if lastFileName == "" || len(entries) < 1000 {
break
}
}
return nil
}
func (f *FileSystemManager) copySelfEntry(ctx context.Context, oldPath utils.FullPath, entry *utils.Entry, newPath utils.FullPath, copyFolderSubEntries func() error) error {
logger.Info("copying entry %s => %s", oldPath, newPath)
if oldPath == newPath {
logger.Info("skip copying 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 copyFolderSubEntries != nil {
if copyChildrenErr := copyFolderSubEntries(); copyChildrenErr != nil {
return copyChildrenErr
}
}
return nil
}