diff --git a/cmd/stgenfiles/main.go b/cmd/stgenfiles/main.go new file mode 100644 index 000000000..3ce491a0f --- /dev/null +++ b/cmd/stgenfiles/main.go @@ -0,0 +1,124 @@ +// Copyright (C) 2016 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +package main + +import ( + "flag" + "fmt" + "io" + "log" + "math/rand" + "os" + "path/filepath" + "time" +) + +func main() { + dir := flag.String("dir", "~/files", "Directory to generate into") + files := flag.Int("files", 1000, "Number of files to create") + maxExp := flag.Int("maxexp", 20, "Max size exponent") + src := flag.String("src", "/dev/urandom", "Source of file data") + flag.Parse() + if err := generateFiles(*dir, *files, *maxExp, *src); err != nil { + log.Println(err) + } +} + +func generateFiles(dir string, files, maxexp int, srcname string) error { + fd, err := os.Open(srcname) + if err != nil { + return err + } + + for i := 0; i < files; i++ { + n := randomName() + + if rand.Float64() < 0.05 { + // Some files and directories are dotfiles + n = "." + n + } + + p0 := filepath.Join(dir, string(n[0]), n[0:2]) + err = os.MkdirAll(p0, 0755) + if err != nil { + log.Fatal(err) + } + + p1 := filepath.Join(p0, n) + + s := int64(1 << uint(rand.Intn(maxexp))) + a := int64(128 * 1024) + if a > s { + a = s + } + s += rand.Int63n(a) + + if err := generateOneFile(fd, p1, s); err != nil { + return err + } + } + + return nil +} + +func generateOneFile(fd io.ReadSeeker, p1 string, s int64) error { + src := io.LimitReader(&inifiteReader{fd}, int64(s)) + dst, err := os.Create(p1) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if err != nil { + return err + } + + err = dst.Close() + if err != nil { + return err + } + + _ = os.Chmod(p1, os.FileMode(rand.Intn(0777)|0400)) + + t := time.Now().Add(-time.Duration(rand.Intn(30*86400)) * time.Second) + err = os.Chtimes(p1, t, t) + if err != nil { + return err + } + + return nil +} + +func randomName() string { + var b [16]byte + readRand(b[:]) + return fmt.Sprintf("%x", b[:]) +} + +func readRand(bs []byte) (int, error) { + var r uint32 + for i := range bs { + if i%4 == 0 { + r = uint32(rand.Int63()) + } + bs[i] = byte(r >> uint((i%4)*8)) + } + return len(bs), nil +} + +type inifiteReader struct { + rd io.ReadSeeker +} + +func (i *inifiteReader) Read(bs []byte) (int, error) { + n, err := i.rd.Read(bs) + if err == io.EOF { + err = nil + i.rd.Seek(0, 0) + } + return n, err +}