From 9159eca507577b05c2b6124ecbc51d4720e64c96 Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Mon, 14 Apr 2025 17:14:14 -0700 Subject: Make error handling more robust --- trash.go | 150 ++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 72 insertions(+), 78 deletions(-) (limited to 'trash.go') diff --git a/trash.go b/trash.go index 126d3fe..37a48ab 100644 --- a/trash.go +++ b/trash.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "io" + "io/fs" "net/url" "os" "path/filepath" @@ -11,104 +12,81 @@ import ( "time" ) -func cp(srcRoot string, dstRoot string) (err error) { - defer func() { +func cpEntry(srcDirEntry os.DirEntry, src, dst string) error { + switch srcDirEntry.Type() { + case 0: // regular file + srcF, err := os.Open(src) if err != nil { - os.RemoveAll(dstRoot) + return err } - }() - - type roDir struct { - path string - mode os.FileMode - } - var roDirs []roDir - - srcRoot = filepath.Clean(srcRoot) - if err := filepath.WalkDir(srcRoot, func(src string, d os.DirEntry, err error) error { + defer srcF.Close() + stat, err := srcF.Stat() if err != nil { return err } - dst := filepath.Join(dstRoot, strings.TrimPrefix(src, srcRoot)) - switch d.Type() { - case 0: // regular file - srcF, err := os.Open(src) - if err != nil { - return err - } - defer srcF.Close() - stat, err := srcF.Stat() - if err != nil { - return err - } - dstF, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 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 - case os.ModeSymlink: - linkTarget, err := os.Readlink(src) - if err != nil { - return err - } - return os.Symlink(linkTarget, dst) - case os.ModeDir: - stat, err := d.Info() - if err != nil { - return err - } - perm := stat.Mode().Perm() - if perm&0300 != 0300 { - roDirs = append(roDirs, roDir{dst, perm}) - // Make sure we can create directory contents. - perm |= 0300 - } - return os.MkdirAll(dst, perm) - default: - return fmt.Errorf("unknown file type %s", d.Type()) + dstF, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, stat.Mode().Perm()) + if err != nil { + return err } - }); err != nil { - return err - } - // Iterate backwards to process directory contents before the parent directory. - for i := len(roDirs) - 1; i >= 0; i-- { - dir := roDirs[i] - if err := os.Chmod(dir.path, dir.mode); err != nil { + defer dstF.Close() + if _, err := io.Copy(dstF, srcF); err != nil { return err } + return dstF.Close() + case os.ModeSymlink: + linkTarget, err := os.Readlink(src) + if err != nil { + return err + } + return os.Symlink(linkTarget, dst) + case os.ModeDir: + stat, err := srcDirEntry.Info() + if err != nil { + return err + } + entries, err := os.ReadDir(src) + if err != nil { + return err + } + if err := os.Mkdir(dst, 0700); err != nil { + return err + } + for _, entry := range entries { + name := entry.Name() + if err := cpEntry(entry, filepath.Join(src, name), filepath.Join(dst, name)); err != nil { + return err + } + } + return os.Chmod(dst, stat.Mode().Perm()) + default: + return fmt.Errorf("unknown file type %s", srcDirEntry.Type()) } - return nil } -func mv(src, dst string) error { - if err := os.Rename(src, dst); err == nil { - return nil +func cp(src, dst string) error { + stat, err := os.Lstat(src) + if err != nil { + return err } - if err := cp(src, dst); err != nil { + if err := cpEntry(fs.FileInfoToDirEntry(stat), src, dst); err != nil { + os.RemoveAll(dst) return err } - return os.RemoveAll(src) + return nil } -func trashFile(fileName, trash, now string) (err error) { +func writeTrashInfo(fileName, trash, now string) (_ string, err error) { absPath, err := filepath.Abs(fileName) if err != nil { - return fmt.Errorf("%s: find absolute path: %s", fileName, err) + return "", fmt.Errorf("find absolute path: %s", err) } info, err := os.CreateTemp(filepath.Join(trash, "info"), filepath.Base(fileName)+"."+now+".*.trashinfo") if err != nil { - return fmt.Errorf("%s: create trashinfo: %s", fileName, err) + return "", fmt.Errorf("create trashinfo: %s", err) } defer func() { - info.Close() if err != nil { + info.Close() os.Remove(info.Name()) } }() @@ -120,15 +98,31 @@ func trashFile(fileName, trash, now string) (err error) { Path=%s DeletionDate=%s `, - filepath.Join(escapedPath...), + strings.Join(escapedPath, string(filepath.Separator)), now) if err != nil { - return fmt.Errorf("%s: write trashinfo: %s", fileName, err) + return "", fmt.Errorf("write trashinfo: %s", err) } if err := info.Close(); err != nil { - return fmt.Errorf("%s: write trashinfo: %s", fileName, err) + return "", fmt.Errorf("write trashinfo: %s", err) + } + return info.Name(), err +} + +func trashFile(fileName, trash, now string) error { + trashInfo, err := writeTrashInfo(fileName, trash, now) + if err != nil { + return fmt.Errorf("%s: %s", fileName, err) + } + dst := filepath.Join(trash, "files", strings.TrimSuffix(filepath.Base(trashInfo), ".trashinfo")) + if err := os.Rename(fileName, dst); err == nil { + return nil + } + if err := cp(fileName, dst); err != nil { + os.Remove(trashInfo) + return fmt.Errorf("%s: %s", fileName, err) } - if err := mv(fileName, filepath.Join(trash, "files", strings.TrimSuffix(filepath.Base(info.Name()), ".trashinfo"))); err != nil { + if err := os.RemoveAll(fileName); err != nil { return fmt.Errorf("%s: %s", fileName, err) } return nil -- cgit v1.3.1