package fs import ( "context" "fmt" "gosvc/logger" "strings" "time" "robotfs/utils" ) func (fs *FileSystem) RenameDirectory(ctx context.Context, oldPath, newPath utils.FullPath, isDir bool) error { locker := fs.locker.AcquireOrderedLock(string(oldPath), string(newPath)) defer fs.locker.ReleaseLocks(locker) if err := fs.canRename(oldPath, newPath, isDir); err != nil { return err } oldEntry, err := fs.FindEntry(ctx, oldPath) if err != nil { return fmt.Errorf("%s not found: %v", oldPath, err) } moveErr := fs.moveEntry(ctx, oldPath, oldEntry, newPath) if moveErr != nil { return fmt.Errorf("%s move error: %v", oldPath, moveErr) } if err := utils.Move(fs.root, string(oldPath), string(newPath)); err != nil { return fmt.Errorf("move file data failed: %v", err) } return nil } func (fs *FileSystem) 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 (fs *FileSystem) moveEntry(ctx context.Context, oldPath utils.FullPath, entry *utils.Entry, newPath utils.FullPath) error { if err := fs.moveSelfEntry(ctx, oldPath, entry, newPath, func() error { if entry.IsDir { if err := fs.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 (fs *FileSystem) 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 := 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.moveEntry(ctx, itemOldPath, item, itemNewPath) if err != nil { return err } } if lastFileName == "" || len(entries) < 1000 { break } } return nil } func (fs *FileSystem) 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: newPath.ToS3Key(), ContentType: entry.ContentType, Etag: entry.Etag, VersionID: entry.VersionID, LastModificationTime: time.Now().Unix(), Extended: entry.Extended, } if updateErr := fs.meta.UpdateEntry(ctx, newEntry); updateErr != nil { return updateErr } if moveFolderSubEntries != nil { if moveChildrenErr := moveFolderSubEntries(); moveChildrenErr != nil { return moveChildrenErr } } deleteErr := fs.meta.DeleteEntry(ctx, oldPath) if deleteErr != nil { return deleteErr } return nil }