Bugfix for splitting the metadata log in the new metadata code.
[cumulus.git] / metadata.cc
1 /* LBS: An LFS-inspired filesystem backup system
2  * Copyright (C) 2007  Michael Vrable
3  *
4  * Handling of metadata written to backup snapshots.  This manages the writing
5  * of file metadata into new backup snapshots, including breaking the metadata
6  * log apart across separate objects.  Eventually this should include unified
7  * handling of the statcache, and re-use of metadata between snapshots.
8  */
9
10 #include <string>
11 #include <iostream>
12
13 #include "metadata.h"
14 #include "store.h"
15 #include "statcache.h"
16 #include "util.h"
17
18 using std::list;
19 using std::string;
20 using std::ostream;
21 using std::ostringstream;
22
23 static const size_t LBS_METADATA_BLOCK_SIZE = 65536;
24
25 /* TODO: Move to header file */
26 void add_segment(const string& segment);
27
28 MetadataWriter::MetadataWriter(TarSegmentStore *store,
29                                const char *path,
30                                const char *snapshot_name,
31                                const char *snapshot_scheme)
32 {
33     statcache_path = path;
34     statcache_path += "/statcache2";
35     if (snapshot_scheme != NULL)
36         statcache_path = statcache_path + "-" + snapshot_scheme;
37     statcache_tmp_path = statcache_path + "." + snapshot_name;
38
39     statcache_out = fopen(statcache_tmp_path.c_str(), "w");
40     if (statcache_out == NULL) {
41         fprintf(stderr, "Error opening statcache %s: %m\n",
42                 statcache_tmp_path.c_str());
43         throw IOException("Error opening statcache");
44     }
45
46     this->store = store;
47     chunk_size = 0;
48 }
49
50 /* Ensure contents of metadata are flushed to an object. */
51 void MetadataWriter::metadata_flush()
52 {
53     int offset = 0;
54
55     ostringstream metadata;
56     for (list<MetadataItem>::iterator i = items.begin();
57          i != items.end(); ++i) {
58         metadata << i->text;
59         i->offset = offset;
60         offset += i->text.size();
61     }
62     string m = metadata.str();
63     if (m.size() == 0)
64         return;
65
66     /* Write current metadata information to a new object. */
67     LbsObject *meta = new LbsObject;
68     meta->set_group("metadata");
69     meta->set_data(m.data(), m.size());
70     meta->write(store);
71     meta->checksum();
72
73     /* Write a reference to this block in the root. */
74     ObjectReference ref = meta->get_ref();
75     metadata_root << "@" << ref.to_string() << "\n";
76     add_segment(ref.get_segment());
77
78     delete meta;
79
80     /* Write these files out to the statcache, and include a reference to where
81      * the metadata lives (so we can re-use it if it has not changed). */
82     for (list<MetadataItem>::const_iterator i = items.begin();
83          i != items.end(); ++i) {
84         ObjectReference r = ref;
85         r.set_range(i->offset, i->text.size());
86
87         string refstr = r.to_string();
88         fprintf(statcache_out, "@@%s\n%s", refstr.c_str(), i->text.c_str());
89     }
90
91     chunk_size = 0;
92     items.clear();
93 }
94
95 void MetadataWriter::add(const string& path, dictionary info)
96 {
97     MetadataItem item;
98     item.offset = 0;
99     item.text = "path: " + uri_encode(path) + "\n";
100     item.text += encode_dict(info) + "\n";
101
102     items.push_back(item);
103     chunk_size += item.text.size();
104
105     if (chunk_size > LBS_METADATA_BLOCK_SIZE)
106         metadata_flush();
107 }
108
109 ObjectReference MetadataWriter::close()
110 {
111     metadata_flush();
112     const string root_data = metadata_root.str();
113
114     LbsObject *root = new LbsObject;
115     root->set_group("metadata");
116     root->set_data(root_data.data(), root_data.size());
117     root->write(store);
118     root->checksum();
119     add_segment(root->get_ref().get_segment());
120
121     ObjectReference ref = root->get_ref();
122     delete root;
123
124     fclose(statcache_out);
125     if (rename(statcache_tmp_path.c_str(), statcache_path.c_str()) < 0) {
126         fprintf(stderr, "Error renaming statcache from %s to %s: %m\n",
127                 statcache_tmp_path.c_str(), statcache_path.c_str());
128     }
129
130     return ref;
131 }