1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17 18 19 20
21 package local
22
23 import (
24 "bytes"
25 "crypto/rand"
26 "fmt"
27 "io"
28 "os"
29 "path/filepath"
30 "time"
31 )
32
33 type Generationer struct {
34 root string
35 }
36
37
38 func NewGenerationer(rootDir string) *Generationer {
39 return &Generationer{rootDir}
40 }
41
42 func (g Generationer) generationFile() string {
43 return filepath.Join(g.root, "GENERATION.dat")
44 }
45
46
47
48
49 func (g Generationer) StorageGeneration() (initTime time.Time, random string, err error) {
50 f, err := os.Open(g.generationFile())
51 if os.IsNotExist(err) {
52 if err = g.ResetStorageGeneration(); err != nil {
53 return
54 }
55 f, err = os.Open(g.generationFile())
56 }
57 if err != nil {
58 return
59 }
60 defer f.Close()
61 bs, err := io.ReadAll(f)
62 if err != nil {
63 return
64 }
65 if i := bytes.IndexByte(bs, '\n'); i != -1 {
66 bs = bs[:i]
67 }
68 if fi, err := f.Stat(); err == nil {
69 initTime = fi.ModTime()
70 }
71 random = string(bs)
72 return
73 }
74
75
76
77 func (g Generationer) ResetStorageGeneration() error {
78 var buf bytes.Buffer
79 if _, err := io.CopyN(&buf, rand.Reader, 20); err != nil {
80 return err
81 }
82 hex := fmt.Sprintf("%x", buf.Bytes())
83 buf.Reset()
84 buf.WriteString(hex)
85 buf.WriteString(`
86
87 This file's random string on the first line is an optimization and
88 paranoia facility for clients.
89
90 If the client sees the same random string in multiple upload sessions,
91 it assumes that the blobserver still has all the same blobs, and also
92 it's the same server. This mechanism is not fundamental to
93 Perkeep's operation: the client could also check each blob before
94 uploading, or enumerate all blobs from the server too. This is purely
95 an optimization so clients can mix this value into their "is this file
96 uploaded?" local cache keys.
97
98 If you deleted any blobs (or suspect any were corrupted), it's best to
99 delete this file so clients can safely re-upload them.
100
101 `)
102
103 return os.WriteFile(g.generationFile(), buf.Bytes(), 0644)
104 }