fix(trash): retain metadata when permanent deletion fails

This commit is contained in:
eezd committed 2026-10-25 13:20:53 +08:00
1 parent 56145dba72
commit 9aaaf15278
3 files changed
+119 -5

No files matched your search

+32 -3
View File
@@ -1,12 +1,41 @@
package vault
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"time"
)
// 调用方持有写锁并已读取验证元数据。先删除内容,失败时保留条目的管理依据。
func (v *Vault) purgeTrashItemLocked(metadata trashMetadata) error {
dir := filepath.Join(v.trash, metadata.ID)
entries, err := os.ReadDir(dir)
if err != nil {
return err
}
for _, entry := range entries {
if entry.Name() == "meta.json" {
continue
}
if err := os.RemoveAll(filepath.Join(dir, entry.Name())); err != nil {
return err
}
}
metaPath := filepath.Join(dir, "meta.json")
if err := os.Remove(metaPath); err != nil {
return err
}
if err := os.Remove(dir); err != nil {
// 父目录不可写等原因仍可能阻止最后的 rmdir,恢复元数据以便继续清理。
data, _ := json.Marshal(metadata)
return errors.Join(err, atomicWrite(metaPath, data))
}
return nil
}
type TrashFailure struct {
ID string `json:"id"`
Error string `json:"error"`
@@ -35,12 +64,12 @@ func (v *Vault) PurgeTrash(days int64, now time.Time) (TrashPurgeResult, error)
if !trashIDPattern.MatchString(id) {
continue // 不删除非本程序管理的文件。
}
item, err := v.readTrashItem(id)
if err == nil && days > 0 && (item.DeletedAt > now.UnixMilli() || (now.UnixMilli()-item.DeletedAt)/int64(24*time.Hour/time.Millisecond) < days) {
metadata, err := v.readTrashMetadata(id)
if err == nil && days > 0 && (metadata.DeletedAt > now.UnixMilli() || (now.UnixMilli()-metadata.DeletedAt)/int64(24*time.Hour/time.Millisecond) < days) {
continue
}
if err == nil {
err = os.RemoveAll(filepath.Join(v.trash, id))
err = v.purgeTrashItemLocked(metadata)
}
if err != nil {
result.Failed = append(result.Failed, TrashFailure{ID: id, Error: err.Error()})