Remove checksums and reference tracking; I think they are not needed.
[cumulus.git] / store.cc
1 /* LBS: An LFS-inspired filesystem backup system
2  * Copyright (C) 2007  Michael Vrable
3  *
4  * Backup data is stored in a collection of objects, which are grouped together
5  * into segments for storage purposes.  This implementation of the object store
6  * is built on top of libtar, and represents segments as TAR files and objects
7  * as files within them. */
8
9 #include <assert.h>
10 #include <stdio.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <time.h>
16
17 #include <list>
18 #include <set>
19 #include <string>
20 #include <iostream>
21
22 #include "store.h"
23 #include "ref.h"
24
25 using std::list;
26 using std::set;
27 using std::string;
28
29 Tarfile::Tarfile(const string &path, const string &segment)
30     : size(0),
31       segment_name(segment)
32 {
33     if (tar_open(&t, (char *)path.c_str(), NULL, O_WRONLY | O_CREAT, 0600,
34                  TAR_VERBOSE | TAR_GNU) == -1)
35         throw IOException("Error opening Tarfile");
36 }
37
38 Tarfile::~Tarfile()
39 {
40     tar_append_eof(t);
41
42     if (tar_close(t) != 0)
43         throw IOException("Error closing Tarfile");
44 }
45
46 void Tarfile::write_object(int id, const char *data, size_t len)
47 {
48     char buf[64];
49     sprintf(buf, "%08x", id);
50     string path = segment_name + "/" + buf;
51
52     internal_write_object(path, data, len);
53 }
54
55 void Tarfile::internal_write_object(const string &path,
56                                     const char *data, size_t len)
57 {
58     memset(&t->th_buf, 0, sizeof(struct tar_header));
59
60     th_set_type(t, S_IFREG | 0600);
61     th_set_user(t, 0);
62     th_set_group(t, 0);
63     th_set_mode(t, 0600);
64     th_set_size(t, len);
65     th_set_mtime(t, time(NULL));
66     th_set_path(t, const_cast<char *>(path.c_str()));
67     th_finish(t);
68
69     if (th_write(t) != 0)
70         throw IOException("Error writing tar header");
71
72     size += T_BLOCKSIZE;
73
74     if (len == 0)
75         return;
76
77     size_t blocks = (len + T_BLOCKSIZE - 1) / T_BLOCKSIZE;
78     size_t padding = blocks * T_BLOCKSIZE - len;
79
80     for (size_t i = 0; i < blocks - 1; i++) {
81         if (tar_block_write(t, &data[i * T_BLOCKSIZE]) == -1)
82             throw IOException("Error writing tar block");
83     }
84
85     char block[T_BLOCKSIZE];
86     memset(block, 0, sizeof(block));
87     memcpy(block, &data[T_BLOCKSIZE * (blocks - 1)], T_BLOCKSIZE - padding);
88     if (tar_block_write(t, block) == -1)
89         throw IOException("Error writing final tar block");
90
91     size += blocks * T_BLOCKSIZE;
92 }
93
94 static const size_t SEGMENT_SIZE = 4 * 1024 * 1024;
95
96 string TarSegmentStore::write_object(const char *data, size_t len, const
97                                      std::string &group)
98 {
99     struct segment_info *segment;
100
101     // Find the segment into which the object should be written, looking up by
102     // group.  If no segment exists yet, create one.
103     if (segments.find(group) == segments.end()) {
104         segment = new segment_info;
105
106         segment->name = generate_uuid();
107
108         string filename = path + "/" + segment->name + ".tar";
109         segment->file = new Tarfile(filename, segment->name);
110
111         segment->count = 0;
112
113         segments[group] = segment;
114     } else {
115         segment = segments[group];
116     }
117
118     int id = segment->count;
119     char id_buf[64];
120     sprintf(id_buf, "%08x", id);
121
122     segment->file->write_object(id, data, len);
123     segment->count++;
124
125     string full_name = segment->name + "/" + id_buf;
126
127     // If this segment meets or exceeds the size target, close it so that
128     // future objects will go into a new segment.
129     if (segment->file->size_estimate() >= SEGMENT_SIZE)
130         close_segment(group);
131
132     return full_name;
133 }
134
135 void TarSegmentStore::sync()
136 {
137     while (!segments.empty())
138         close_segment(segments.begin()->first);
139 }
140
141 void TarSegmentStore::close_segment(const string &group)
142 {
143     struct segment_info *segment = segments[group];
144     fprintf(stderr, "Closing segment group %s (%s)\n",
145             group.c_str(), segment->name.c_str());
146
147     delete segment->file;
148     segments.erase(segments.find(group));
149     delete segment;
150 }
151
152 string TarSegmentStore::object_reference_to_segment(const string &object)
153 {
154     return object;
155 }
156
157 LbsObject::LbsObject()
158     : group(""), data(NULL), data_len(0), written(false)
159 {
160 }
161
162 LbsObject::~LbsObject()
163 {
164 }
165
166 void LbsObject::write(TarSegmentStore *store)
167 {
168     assert(data != NULL);
169     assert(!written);
170
171     name = store->write_object(data, data_len, group);
172
173     written = true;
174     data = NULL;
175 }