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:
Jakob Unterwurzacher 2021-06-04 22:16:41 +02:00
parent 015cd066e1
commit 3a1f009c1a
3 changed files with 103 additions and 1 deletions

View File

@ -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
View File

@ -0,0 +1 @@
/atomicrename

View 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)
}