+ /* Initialize the remote storage layer. If using an upload script, create
+ * a temporary directory for staging files. Otherwise, write backups
+ * directly to the destination directory. */
+ if (backup_script != "") {
+ tmp_dir = tmp_dir + "/lbs." + generate_uuid();
+ if (mkdir(tmp_dir.c_str(), 0700) < 0) {
+ fprintf(stderr, "Cannot create temporary directory %s: %m\n",
+ tmp_dir.c_str());
+ return 1;
+ }
+ remote = new RemoteStore(tmp_dir);
+ remote->set_script(backup_script);
+ } else {
+ remote = new RemoteStore(backup_dest);
+ }
+
+ /* Store the time when the backup started, so it can be included in the
+ * snapshot name. */
+ time_t now;
+ struct tm time_buf;
+ char desc_buf[256];
+ time(&now);
+ localtime_r(&now, &time_buf);
+ strftime(desc_buf, sizeof(desc_buf), "%Y%m%dT%H%M%S", &time_buf);
+
+ /* Open the local database which tracks all objects that are stored
+ * remotely, for efficient incrementals. Provide it with the name of this
+ * snapshot. */
+ string database_path = localdb_dir + "/localdb.sqlite";
+ db = new LocalDb;
+ db->Open(database_path.c_str(), desc_buf, backup_scheme.c_str(),
+ snapshot_intent);
+
+ tss = new TarSegmentStore(remote, db);
+
+ /* Initialize the stat cache, for skipping over unchanged files. */
+ metawriter = new MetadataWriter(tss, localdb_dir.c_str(), desc_buf,
+ backup_scheme.c_str());
+
+ scanfile(".", false);
+
+ ObjectReference root_ref = metawriter->close();
+ add_segment(root_ref.get_segment());
+ string backup_root = root_ref.to_string();
+
+ delete metawriter;
+
+ tss->sync();
+ tss->dump_stats();
+ delete tss;
+
+ /* Write out a checksums file which lists the checksums for all the
+ * segments included in this snapshot. The format is designed so that it
+ * may be easily verified using the sha1sums command. */
+ const char csum_type[] = "sha1";
+ string checksum_filename = "snapshot-";
+ if (backup_scheme.size() > 0)
+ checksum_filename += backup_scheme + "-";
+ checksum_filename = checksum_filename + desc_buf + "." + csum_type + "sums";
+ RemoteFile *checksum_file = remote->alloc_file(checksum_filename,
+ "checksums");
+ FILE *checksums = fdopen(checksum_file->get_fd(), "w");
+
+ for (std::set<string>::iterator i = segment_list.begin();
+ i != segment_list.end(); ++i) {
+ string seg_path, seg_csum;
+ if (db->GetSegmentChecksum(*i, &seg_path, &seg_csum)) {
+ const char *raw_checksum = NULL;
+ if (strncmp(seg_csum.c_str(), csum_type,
+ strlen(csum_type)) == 0) {
+ raw_checksum = seg_csum.c_str() + strlen(csum_type);
+ if (*raw_checksum == '=')
+ raw_checksum++;
+ else
+ raw_checksum = NULL;
+ }
+
+ if (raw_checksum != NULL)
+ fprintf(checksums, "%s *%s\n",
+ raw_checksum, seg_path.c_str());
+ }
+ }
+ fclose(checksums);
+ checksum_file->send();
+
+ db->Close();
+
+ /* All other files should be flushed to remote storage before writing the
+ * backup descriptor below, so that it is not possible to have a backup
+ * descriptor written out depending on non-existent (not yet written)
+ * files. */
+ remote->sync();
+
+ /* Write a backup descriptor file, which says which segments are needed and
+ * where to start to restore this snapshot. The filename is based on the
+ * current time. If a signature filter program was specified, filter the
+ * data through that to give a chance to sign the descriptor contents. */
+ string desc_filename = "snapshot-";
+ if (backup_scheme.size() > 0)
+ desc_filename += backup_scheme + "-";
+ desc_filename = desc_filename + desc_buf + ".lbs";
+
+ RemoteFile *descriptor_file = remote->alloc_file(desc_filename,
+ "snapshots");
+ int descriptor_fd = descriptor_file->get_fd();
+ if (descriptor_fd < 0) {
+ fprintf(stderr, "Unable to open descriptor output file: %m\n");
+ return 1;
+ }
+ pid_t signature_pid = 0;
+ if (signature_filter.size() > 0) {
+ int new_fd = spawn_filter(descriptor_fd, signature_filter.c_str(),
+ &signature_pid);
+ close(descriptor_fd);
+ descriptor_fd = new_fd;
+ }
+ FILE *descriptor = fdopen(descriptor_fd, "w");
+
+ fprintf(descriptor, "Format: LBS Snapshot v0.6\n");
+ fprintf(descriptor, "Producer: LBS %s\n", lbs_version);
+ strftime(desc_buf, sizeof(desc_buf), "%Y-%m-%d %H:%M:%S %z", &time_buf);
+ fprintf(descriptor, "Date: %s\n", desc_buf);
+ if (backup_scheme.size() > 0)
+ fprintf(descriptor, "Scheme: %s\n", backup_scheme.c_str());
+ fprintf(descriptor, "Backup-Intent: %g\n", snapshot_intent);
+ fprintf(descriptor, "Root: %s\n", backup_root.c_str());
+
+ SHA1Checksum checksum_csum;
+ if (checksum_csum.process_file(checksum_filename.c_str())) {
+ string csum = checksum_csum.checksum_str();
+ fprintf(descriptor, "Checksums: %s\n", csum.c_str());
+ }
+
+ fprintf(descriptor, "Segments:\n");
+ for (std::set<string>::iterator i = segment_list.begin();
+ i != segment_list.end(); ++i) {
+ fprintf(descriptor, " %s\n", i->c_str());
+ }