package fs import ( "context" "fmt" "gosvc/logger" "strings" "time" "robotfs/utils" ) func (fs *FileSystem) CopyDirectory(ctx context.Context, oldPath, newPath utils.FullPath, isDir bool) error { locker := fs.locker.AcquireOrderedLock(string(oldPath), string(newPath)) defer fs.locker.ReleaseLocks(locker) 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 := fs.FindEntry(ctx, oldPath) if err != nil { return fmt.Errorf("%s not found: %v", oldPath, err) } if newEntry, err := fs.FindEntry(ctx, newPath); newEntry != nil && err == nil { return fmt.Errorf("destination %s already exists", newPath) } parentDir, _ := newPath.DirAndName() if parentDir != "/" { if parentEntry, _ := fs.FindEntry(ctx, utils.FullPath(parentDir)); parentEntry == nil { return fmt.Errorf("parent directory %s does not exist", parentDir) } } if err := fs.copyEntry(ctx, oldPath, oldEntry, newPath); err != nil { return fmt.Errorf("copy metadata failed: %v", err) } if err := utils.Clone(fs.root, string(oldPath), string(newPath)); err != nil { return fmt.Errorf("copy file data failed: %v", err) } return nil } func (fs *FileSystem) copyEntry(ctx context.Context, oldPath utils.FullPath, entry *utils.Entry, newPath utils.FullPath) error { if err := fs.copySelfEntry(ctx, oldPath, entry, newPath, func() error { if entry.IsDir { if err := fs.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 (fs *FileSystem) 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 := fs.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 := fs.copyEntry(ctx, itemOldPath, item, itemNewPath) if err != nil { return err } } if lastFileName == "" || len(entries) < 1000 { break } } return nil } func (fs *FileSystem) 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: newPath.ToS3Key(), ContentType: entry.ContentType, Etag: entry.Etag, VersionID: entry.VersionID, LastModificationTime: time.Now().Unix(), Extended: entry.Extended, } if createErr := fs.meta.InsertEntry(ctx, newEntry); createErr != nil { return createErr } if copyFolderSubEntries != nil { if copyChildrenErr := copyFolderSubEntries(); copyChildrenErr != nil { return copyChildrenErr } } return nil }