package main import ( // "bufio" "fmt" "io" "os" ) type CopyOp struct { Bytes int64 Original, Backup *os.File } func copy(src, dst string) (CopyOp, error) { var co CopyOp sourceFileStat, err := os.Stat(src) if err != nil { return co, err } if !sourceFileStat.Mode().IsRegular() { return co, fmt.Errorf("%s is not a regular file", src) } source, err := os.Open(src) if err != nil { return co, err } co.Original = source defer source.Close() destination, err := os.Create(dst) if err != nil { return co, err } defer destination.Close() co.Backup = destination nBytes, err := io.Copy(destination, source) co.Bytes = nBytes return co, err } func restoreBackup(fn string) error { err := os.Remove(fn) if err != nil { // removing the original has failed. return fmt.Errorf("error removing modified file[%s] : %w", fn, err) } // copy the backup back to the original _, err = copy(fn+".bak", fn) if err != nil { return fmt.Errorf("error copying backup into original location[%s]: %w", fn, err) } // remove the backup err = os.Remove(fn + ".bak") if err != nil { // removing the backup has failed. return fmt.Errorf("error removing backup file[%s] : %w", fn+".bak", err) } return nil } // Update modifies the contents of a file in-place. func Update(fn string, newContents io.Reader) error { // create a backup copy copyOp, err := copy(fn, fn+".bak") if err != nil { return fmt.Errorf("error backing up file[%s]: %w", fn, err) } // truncate the original err = os.Truncate(fn, 0) if err != nil { err := restoreBackup(fn) if err != nil { return fmt.Errorf("error restoring backup: %w", err) } return fmt.Errorf("error truncating original file[%s]: %w", fn, err) } // copy new contents into original _, err = io.Copy(copyOp.Original, newContents) if err != nil { // the copy has failed. we need to remove the original err := restoreBackup(fn) if err != nil { return fmt.Errorf("error restoring backup: %w", err) } return fmt.Errorf("error writing new contents to file[%s] : %w", fn, err) } return nil }