Adding a Volume interface and UnixVolume implementation.
[arvados.git] / services / keep / src / keep / volume.go
1 package main
2
3 import (
4         "crypto/md5"
5         "fmt"
6         "log"
7         "os"
8 )
9
10 // A Volume is an interface that represents a Keep back-end volume.
11
12 type Volume interface {
13         Read(locator string) ([]byte, error)
14         Write(locator string, block []byte) error
15 }
16
17 // A UnixVolume is a Volume that writes to a locally mounted disk.
18 type UnixVolume struct {
19         root string // path to this volume
20 }
21
22 func (v *UnixVolume) Read(locator string) ([]byte, error) {
23         var f *os.File
24         var err error
25         var nread int
26
27         blockFilename := fmt.Sprintf("%s/%s/%s", v.root, locator[0:3], locator)
28
29         f, err = os.Open(blockFilename)
30         if err != nil {
31                 return nil, err
32         }
33
34         var buf = make([]byte, BLOCKSIZE)
35         nread, err = f.Read(buf)
36         if err != nil {
37                 log.Printf("%s: reading %s: %s\n", v.root, blockFilename, err)
38                 return buf, err
39         }
40
41         // Double check the file checksum.
42         //
43         filehash := fmt.Sprintf("%x", md5.Sum(buf[:nread]))
44         if filehash != locator {
45                 // TODO(twp): this condition probably represents a bad disk and
46                 // should raise major alarm bells for an administrator: e.g.
47                 // they should be sent directly to an event manager at high
48                 // priority or logged as urgent problems.
49                 //
50                 log.Printf("%s: checksum mismatch: %s (actual locator %s)\n",
51                         v.root, blockFilename, filehash)
52                 return buf, CorruptError
53         }
54
55         // Success!
56         return buf[:nread], nil
57 }