Add contrib/atomicrename
$ ./contrib/atomicrename/atomicrename -h atomicrename creates 100 "src" files in the current directory, renames them in random order over a single "dst" file while reading the "dst" file concurrently in a loop. Progress and errors are reported as they occour in addition to a summary printed at the end. cifs and fuse filesystems are known to fail, local filesystems and nfs seem ok. See https://github.com/hanwen/go-fuse/issues/398 for background info.
This commit is contained in:
parent
015cd066e1
commit
3a1f009c1a
@ -94,7 +94,7 @@ fi
|
||||
# Actual "go build" call for gocryptfs
|
||||
go build "-ldflags=$GO_LDFLAGS" "$@"
|
||||
# Additional binaries
|
||||
for d in gocryptfs-xray contrib/statfs contrib/findholes ; do
|
||||
for d in gocryptfs-xray contrib/statfs contrib/findholes contrib/atomicrename ; do
|
||||
(cd "$d"; go build "-ldflags=$GO_LDFLAGS" "$@")
|
||||
done
|
||||
|
||||
|
1
contrib/atomicrename/.gitignore
vendored
Normal file
1
contrib/atomicrename/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/atomicrename
|
101
contrib/atomicrename/main.go
Normal file
101
contrib/atomicrename/main.go
Normal file
@ -0,0 +1,101 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const fileCount = 100
|
||||
|
||||
type stats struct {
|
||||
renameOk int
|
||||
renameError int
|
||||
readOk int
|
||||
readError int
|
||||
readContentMismatch int
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Printf(`atomicrename creates %d "src" files in the current directory, renames
|
||||
them in random order over a single "dst" file while reading the "dst"
|
||||
file concurrently in a loop.
|
||||
|
||||
Progress and errors are reported as they occour in addition to a summary
|
||||
printed at the end. cifs and fuse filesystems are known to fail, local
|
||||
filesystems and nfs seem ok.
|
||||
|
||||
See https://github.com/hanwen/go-fuse/issues/398 for background info.
|
||||
`, fileCount)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
hello := []byte("hello world")
|
||||
srcFiles := make(map[string]struct{})
|
||||
|
||||
// prepare source files
|
||||
fmt.Print("creating files")
|
||||
for i := 0; i < fileCount; i++ {
|
||||
srcName := fmt.Sprintf("src.atomicrename.%d", i)
|
||||
srcFiles[srcName] = struct{}{}
|
||||
buf := bytes.Repeat([]byte("_"), i)
|
||||
buf = append(buf, hello...)
|
||||
if err := ioutil.WriteFile(srcName, buf, 0600); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Print(".")
|
||||
}
|
||||
fmt.Print("\n")
|
||||
|
||||
// prepare destination file
|
||||
const dstName = "dst.atomicrename"
|
||||
if err := ioutil.WriteFile(dstName, hello, 0600); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var running int32 = 1
|
||||
|
||||
stats := stats{}
|
||||
|
||||
// read thread
|
||||
go func() {
|
||||
for atomic.LoadInt32(&running) == 1 {
|
||||
have, err := ioutil.ReadFile(dstName)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
stats.readError++
|
||||
continue
|
||||
}
|
||||
if !strings.HasSuffix(string(have), string(hello)) {
|
||||
fmt.Printf("content mismatch: have %q\n", have)
|
||||
stats.readContentMismatch++
|
||||
continue
|
||||
}
|
||||
fmt.Printf("content ok len=%d\n", len(have))
|
||||
stats.readOk++
|
||||
}
|
||||
}()
|
||||
|
||||
// rename thread = main thread
|
||||
for srcName := range srcFiles {
|
||||
if err := os.Rename(srcName, dstName); err != nil {
|
||||
fmt.Println(err)
|
||||
stats.renameError++
|
||||
}
|
||||
stats.renameOk++
|
||||
}
|
||||
// Signal the Read goroutine to stop when loop is done
|
||||
atomic.StoreInt32(&running, 0)
|
||||
|
||||
syscall.Unlink(dstName)
|
||||
fmt.Printf("stats: %#v\n", stats)
|
||||
}
|
Loading…
Reference in New Issue
Block a user