1 /* Recursively descend the filesystem and visit each file. */
23 static SegmentStore *segment_store;
24 static OutputStream *info_dump = NULL;
26 static SegmentPartitioner *index_segment, *data_segment;
28 void scandir(const string& path);
30 /* Converts time to microseconds since the epoch. */
31 int64_t encode_time(time_t time)
33 return (int64_t)time * 1000000;
36 void dumpfile(int fd, dictionary &file_info)
44 if ((stat_buf.st_mode & S_IFMT) != S_IFREG) {
45 printf("file is no longer a regular file!\n");
51 ssize_t res = read(fd, buf, sizeof(buf));
55 printf("Error while reading: %m\n");
57 } else if (res == 0) {
60 hash.process(buf, res);
61 OutputStream *block = data_segment->new_object();
62 block->write(buf, res);
67 file_info["sha1"] = string((const char *)hash.checksum(),
68 hash.checksum_size());
71 void scanfile(const string& path)
79 // Set to true if the item is a directory and we should recursively scan
84 lstat(path.c_str(), &stat_buf);
86 printf("%s\n", path.c_str());
88 file_info["mode"] = encode_u16(stat_buf.st_mode & 07777);
89 file_info["atime"] = encode_u64(encode_time(stat_buf.st_atime));
90 file_info["ctime"] = encode_u64(encode_time(stat_buf.st_ctime));
91 file_info["mtime"] = encode_u64(encode_time(stat_buf.st_mtime));
92 file_info["user"] = encode_u32(stat_buf.st_uid);
93 file_info["group"] = encode_u32(stat_buf.st_gid);
97 switch (stat_buf.st_mode & S_IFMT) {
113 /* Use the reported file size to allocate a buffer large enough to read
114 * the symlink. Allocate slightly more space, so that we ask for more
115 * bytes than we expect and so check for truncation. */
116 buf = new char[stat_buf.st_size + 2];
117 len = readlink(path.c_str(), buf, stat_buf.st_size + 1);
119 printf("error reading symlink: %m\n");
120 } else if (len <= stat_buf.st_size) {
122 printf(" contents=%s\n", buf);
123 } else if (len > stat_buf.st_size) {
124 printf("error reading symlink: name truncated\n");
127 file_info["contents"] = buf;
134 /* Be paranoid when opening the file. We have no guarantee that the
135 * file was not replaced between the stat() call above and the open()
136 * call below, so we might not even be opening a regular file. That
137 * the file descriptor refers to a regular file is checked in
138 * dumpfile(). But we also supply flags to open to to guard against
139 * various conditions before we can perform that verification:
140 * - O_NOFOLLOW: in the event the file was replaced by a symlink
141 * - O_NONBLOCK: prevents open() from blocking if the file was
143 * We also add in O_NOATIME, since this may reduce disk writes (for
145 fd = open(path.c_str(), O_RDONLY|O_NOATIME|O_NOFOLLOW|O_NONBLOCK);
147 /* Drop the use of the O_NONBLOCK flag; we only wanted that for file
149 flags = fcntl(fd, F_GETFL);
150 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
152 file_info["size"] = encode_u64(stat_buf.st_size);
153 dumpfile(fd, file_info);
163 fprintf(stderr, "Unknown inode type: mode=%x\n", stat_buf.st_mode);
167 file_info["type"] = string(1, inode_type);
169 info_dump->write_string(path);
170 info_dump->write_dictionary(file_info);
172 // If we hit a directory, now that we've written the directory itself,
173 // recursively scan the directory.
178 void scandir(const string& path)
180 DIR *dir = opendir(path.c_str());
183 printf("Error: %m\n");
188 vector<string> contents;
189 while ((ent = readdir(dir)) != NULL) {
190 string filename(ent->d_name);
191 if (filename == "." || filename == "..")
193 contents.push_back(filename);
196 sort(contents.begin(), contents.end());
198 for (vector<string>::iterator i = contents.begin();
199 i != contents.end(); ++i) {
200 const string& filename = *i;
201 scanfile(path + "/" + filename);
207 int main(int argc, char *argv[])
209 segment_store = new SegmentStore(".");
210 SegmentWriter *sw = segment_store->new_segment();
211 info_dump = sw->new_object();
213 index_segment = new SegmentPartitioner(segment_store);
214 data_segment = new SegmentPartitioner(segment_store);
216 string uuid = SegmentWriter::format_uuid(sw->get_uuid());
217 printf("Backup UUID: %s\n", uuid.c_str());
221 } catch (IOException e) {
222 fprintf(stderr, "IOException: %s\n", e.getError().c_str());
225 delete index_segment;