8 "github.com/gorilla/mux"
15 const DEFAULT_PORT = 25107
16 const BLOCKSIZE = 64 * 1024 * 1024
18 var PROC_MOUNTS = "/proc/mounts"
20 var KeepVolumes []string
23 // Look for local keep volumes.
24 KeepVolumes = FindKeepVolumes()
25 if len(KeepVolumes) == 0 {
26 log.Fatal("could not find any keep volumes")
28 for _, v := range KeepVolumes {
29 log.Println("keep volume:", v)
32 // Set up REST handlers.
34 // Start with a router that will route each URL path to an
35 // appropriate handler.
37 rest := mux.NewRouter()
38 rest.HandleFunc("/{hash:[0-9a-f]{32}}", GetBlockHandler).Methods("GET")
40 // Tell the built-in HTTP server to direct all requests to the REST
42 http.Handle("/", rest)
44 // Start listening for requests.
45 port := fmt.Sprintf(":%d", DEFAULT_PORT)
46 http.ListenAndServe(port, nil)
50 // Returns a list of Keep volumes mounted on this system.
52 // A Keep volume is a normal or tmpfs volume with a /keep
53 // directory at the top level of the mount point.
55 func FindKeepVolumes() []string {
56 vols := make([]string, 0)
58 if f, err := os.Open(PROC_MOUNTS); err != nil {
59 log.Fatalf("opening %s: %s\n", PROC_MOUNTS, err)
61 scanner := bufio.NewScanner(f)
63 args := strings.Fields(scanner.Text())
64 dev, mount := args[0], args[1]
65 if (dev == "tmpfs" || strings.HasPrefix(dev, "/dev/")) && mount != "/" {
66 keep := mount + "/keep"
67 if st, err := os.Stat(keep); err == nil && st.IsDir() {
68 vols = append(vols, keep)
72 if err := scanner.Err(); err != nil {
79 func GetBlockHandler(w http.ResponseWriter, req *http.Request) {
80 hash := mux.Vars(req)["hash"]
82 block, err := GetBlock(hash)
84 http.Error(w, err.Error(), 404)
88 _, err = w.Write(block)
90 log.Printf("GetBlockHandler: writing response: %s", err)
96 func GetBlock(hash string) ([]byte, error) {
97 var buf = make([]byte, BLOCKSIZE)
99 // Attempt to read the requested hash from a keep volume.
100 for _, vol := range KeepVolumes {
105 path := fmt.Sprintf("%s/%s/%s", vol, hash[0:3], hash)
107 f, err = os.Open(path)
109 log.Printf("%s: opening %s: %s\n", vol, path, err)
113 nread, err = f.Read(buf)
115 log.Printf("%s: reading %s: %s\n", vol, path, err)
119 // Double check the file checksum.
121 filehash := fmt.Sprintf("%x", md5.Sum(buf[:nread]))
122 if filehash != hash {
123 // TODO(twp): this condition probably represents a bad disk and
124 // should raise major alarm bells for an administrator: e.g.
125 // they should be sent directly to an event manager at high
126 // priority or logged as urgent problems.
128 log.Printf("%s: checksum mismatch: %s (actual hash %s)\n",
134 return buf[:nread], nil
137 log.Printf("%s: not found on any volumes, giving up\n", hash)
138 return buf, errors.New("not found: " + hash)