diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2025-03-12 17:29:32 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2025-03-12 17:29:32 -0700 |
| commit | 1aeeff401fc58de06daaf5fde6c520031ce22fed (patch) | |
| tree | 983c82309fe0c8e45f4721bcd169d2f1ff227e14 | |
| parent | c2dde01d2ac53baec761e64d636167e17db22bb0 (diff) | |
| download | trash-1aeeff401fc58de06daaf5fde6c520031ce22fed.tar.zst | |
Add a fallback path in case os.Rename fails
For example to trash files on a different filesystem (but would you
really want to do that?)
| -rw-r--r-- | trash.go | 60 |
1 files changed, 59 insertions, 1 deletions
@@ -3,6 +3,8 @@ package main import ( "flag" "fmt" + "io" + "io/fs" "net/url" "os" "path/filepath" @@ -10,6 +12,62 @@ import ( "time" ) +func mv(src, dst string) error { + if err := os.Rename(src, dst); err == nil { + return nil + } + src = filepath.Clean(src) + if err := filepath.WalkDir(src, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + relPath := strings.TrimPrefix(path, src) + dstPath := filepath.Join(dst, relPath) + switch d.Type() { + case fs.ModeDir: + stat, err := d.Info() + if err != nil { + return err + } + return os.Mkdir(dstPath, stat.Mode().Perm()) + case fs.ModeSymlink: + linkTarget, err := os.Readlink(path) + if err != nil { + return err + } + return os.Symlink(linkTarget, dstPath) + case 0: + srcF, err := os.Open(path) + if err != nil { + return err + } + defer srcF.Close() + stat, err := srcF.Stat() + if err != nil { + return err + } + dstF, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE, stat.Mode().Perm()) + if err != nil { + return err + } + defer dstF.Close() + if _, err := io.Copy(dstF, srcF); err != nil { + return err + } + if err := dstF.Close(); err != nil { + return err + } + return nil + default: + return fmt.Errorf("unknown file type %s", d.Type()) + } + }); err != nil { + os.RemoveAll(dst) + return err + } + return os.RemoveAll(src) +} + func trashFile(fileName, trash, now string) error { absPath, err := filepath.Abs(fileName) if err != nil { @@ -38,7 +96,7 @@ DeletionDate=%s os.Remove(info.Name()) return fmt.Errorf("%s: write trashinfo: %s", fileName, err) } - if err := os.Rename(fileName, trash+"/files/"+strings.TrimSuffix(filepath.Base(info.Name()), ".trashinfo")); err != nil { + if err := mv(fileName, trash+"/files/"+strings.TrimSuffix(filepath.Base(info.Name()), ".trashinfo")); err != nil { os.Remove(info.Name()) return fmt.Errorf("%s: %s", fileName, err) } |
