Make a backup file: Difference between revisions

Content added Content deleted
(Go solution)
(→‎{{header|Go}}: modify to require existing file)
Line 51: Line 51:
bx := ".backup"
bx := ".backup"
// see if it's a link
// see if it's a link
var err error
if tf, err := os.Readlink(fn); err == nil {
if tf, err := os.Readlink(fn); err == nil {
fn = tf
fn = tf
}
}
// stat to preserve permissions.
// default permissions, used if file doesn't exist
perm := os.FileMode(0666)
var fi os.FileInfo
if fi, err = os.Stat(fn); err != nil {
// now see if file exists.
if fi, err := os.Stat(fn); err == nil {
fmt.Println(err)
return
// it does. use existing permissions instead of default
}
// and rename the file, (losing any existing backup.)
// attemp rename
perm = fi.Mode().Perm()
if err := os.Rename(fn, fn+bx); err != nil {
if err = os.Rename(fn, fn+bx); err != nil {
fmt.Println(err)
// if rename of known existing file fails, we don't continue.
fmt.Println(err)
return
return
}
} else {
// stat failed. only allowable error is "no such file..."
// any other error is fatal.
pErr, ok := err.(*os.PathError)
if !ok || pErr.Err.Error() != "no such file or directory" {
fmt.Println(err)
return
}
}
}
// create new file
// create new file
err := ioutil.WriteFile(fn, []byte("you too!\n"), perm)
err = ioutil.WriteFile(fn, []byte("you too!\n"), fi.Mode().Perm())
if err != nil {
if err != nil {
fmt.Println(err)
fmt.Println(err)
Line 92: Line 83:


func main() {
func main() {
err := updateWithBackup("myth", ".backup", 0666,
err := updateWithBackup("myth", ".backup", func(f *os.File) (err error) {
func(f *os.File) (err error) {
if _, err = f.Seek(0, os.SEEK_SET); err != nil {
if _, err = f.Seek(0, os.SEEK_SET); err != nil {
return
}
if err = f.Truncate(0); err != nil {
return
}
_, err = f.WriteString("you too!\n")
return
return
})
}
if err = f.Truncate(0); err != nil {
return
}
_, err = f.WriteString("you too!\n")
return
})
if err != nil {
if err != nil {
fmt.Println(err)
fmt.Println(err)
Line 112: Line 102:
// update the file as needed and return any error, but should not close
// update the file as needed and return any error, but should not close
// the file. updateWithBackup will then close the file and return any error.
// the file. updateWithBackup will then close the file and return any error.
func updateWithBackup(fn, bx string, perm os.FileMode,
func updateWithBackup(fn, bx string, up func(*os.File) error) (err error) {
up func(*os.File) error) (err error) {
var f *os.File
var f *os.File
if f, err = openWithBackup(fn, bx, perm); err != nil {
if f, err = openWithBackup(fn, bx); err != nil {
return
return
}
}
Line 130: Line 119:
// destination file + bx. Any error encountered is returned. tf will be
// destination file + bx. Any error encountered is returned. tf will be
// an open file if and only if err == nil.
// an open file if and only if err == nil.
func openWithBackup(fn, bx string, perm os.FileMode) (tf *os.File, err error) {
func openWithBackup(fn, bx string) (tf *os.File, err error) {
// follow symlink.
// follow symlink.
if target, err := os.Readlink(fn); err == nil {
if target, err := os.Readlink(fn); err == nil {
Line 137: Line 126:
// open the target file for exclusive access.
// open the target file for exclusive access.
if tf, err = os.OpenFile(fn, os.O_RDWR, 0); err != nil {
if tf, err = os.OpenFile(fn, os.O_RDWR, 0); err != nil {
return
// oops, that didn't work. see if it's simply missing.
pErr, ok := err.(*os.PathError)
if ok && pErr.Err.Error() == "no such file or directory" {
// yep, that's all it was. return a new file.
return os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_EXCL, perm)
}
return // no, it was some other error. fail.
}
}
// at this point an existing target file has been opened.
// deferred function closes target file if an error happens
// deferred function closes target file if an error happens
// during the backup operation.
// during the backup operation.