1 /* LBS: An LFS-inspired filesystem backup system
2 * Copyright (C) 2007 Michael Vrable
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. */
11 #include <sys/types.h>
16 #include <uuid/uuid.h>
29 list<string> TarSegmentStore::norefs;
31 Tarfile::Tarfile(const string &path, const string &segment)
35 if (tar_open(&t, (char *)path.c_str(), NULL, O_WRONLY | O_CREAT, 0600,
36 TAR_VERBOSE | TAR_GNU) == -1)
37 throw IOException("Error opening Tarfile");
42 string checksum_list = checksums.str();
43 internal_write_object(segment_name + "/checksums",
44 checksum_list.data(), checksum_list.size());
47 if (tar_close(t) != 0)
48 throw IOException("Error closing Tarfile");
51 void Tarfile::write_object(int id, const char *data, size_t len)
54 sprintf(buf, "%08x", id);
55 string path = segment_name + "/" + buf;
57 internal_write_object(path, data, len);
59 // Compute a checksum for the data block, which will be stored at the end
62 hash.process(data, len);
63 sprintf(buf, "%08x", id);
64 checksums << buf << " " << hash.checksum_str() << "\n";
67 void Tarfile::internal_write_object(const string &path,
68 const char *data, size_t len)
70 memset(&t->th_buf, 0, sizeof(struct tar_header));
72 th_set_type(t, S_IFREG | 0600);
77 th_set_mtime(t, time(NULL));
78 th_set_path(t, const_cast<char *>(path.c_str()));
82 throw IOException("Error writing tar header");
89 size_t blocks = (len + T_BLOCKSIZE - 1) / T_BLOCKSIZE;
90 size_t padding = blocks * T_BLOCKSIZE - len;
92 for (size_t i = 0; i < blocks - 1; i++) {
93 if (tar_block_write(t, &data[i * T_BLOCKSIZE]) == -1)
94 throw IOException("Error writing tar block");
97 char block[T_BLOCKSIZE];
98 memset(block, 0, sizeof(block));
99 memcpy(block, &data[T_BLOCKSIZE * (blocks - 1)], T_BLOCKSIZE - padding);
100 if (tar_block_write(t, block) == -1)
101 throw IOException("Error writing final tar block");
103 size += blocks * T_BLOCKSIZE;
106 static const size_t SEGMENT_SIZE = 4 * 1024 * 1024;
108 string TarSegmentStore::write_object(const char *data, size_t len, const
110 const std::list<std::string> &refs)
112 struct segment_info *segment;
114 // Find the segment into which the object should be written, looking up by
115 // group. If no segment exists yet, create one.
116 if (segments.find(group) == segments.end()) {
117 segment = new segment_info;
122 uuid_unparse_lower(uuid, uuid_buf);
123 segment->name = uuid_buf;
125 string filename = path + "/" + segment->name + ".tar";
126 segment->file = new Tarfile(filename, segment->name);
130 segments[group] = segment;
132 segment = segments[group];
135 int id = segment->count;
137 sprintf(id_buf, "%08x", id);
139 segment->file->write_object(id, data, len);
142 string full_name = segment->name + "/" + id_buf;
144 // Store any dependencies this object has on other segments, so they can be
145 // written when the segment is closed.
146 for (list<string>::const_iterator i = refs.begin(); i != refs.end(); ++i) {
147 segment->refs.insert(*i);
150 // If this segment meets or exceeds the size target, close it so that
151 // future objects will go into a new segment.
152 if (segment->file->size_estimate() >= SEGMENT_SIZE)
153 close_segment(group);
158 void TarSegmentStore::sync()
160 while (!segments.empty())
161 close_segment(segments.begin()->first);
164 void TarSegmentStore::close_segment(const string &group)
166 struct segment_info *segment = segments[group];
167 fprintf(stderr, "Closing segment group %s (%s)\n",
168 group.c_str(), segment->name.c_str());
171 for (set<string>::iterator i = segment->refs.begin();
172 i != segment->refs.end(); ++i) {
173 reflist += *i + "\n";
175 segment->file->internal_write_object(segment->name + "/references",
176 reflist.data(), reflist.size());
178 delete segment->file;
179 segments.erase(segments.find(group));
183 string TarSegmentStore::object_reference_to_segment(const string &object)
188 LbsObject::LbsObject()
189 : group(""), data(NULL), data_len(0), written(false)
193 LbsObject::~LbsObject()
197 void LbsObject::add_reference(const LbsObject *o)
199 refs.insert(o->get_name());
202 void LbsObject::write(TarSegmentStore *store)
204 assert(data != NULL);
207 list<string> reflist;
208 for (set<string>::iterator i = refs.begin(); i != refs.end(); ++i) {
209 reflist.push_back(*i);
212 name = store->write_object(data, data_len, group, reflist);