Rework filter process code to make the interface simpler.
[cumulus.git] / store.h
1 /* Cumulus: Efficient Filesystem Backup to the Cloud
2  * Copyright (C) 2006-2008 The Cumulus Developers
3  * See the AUTHORS file for a list of contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 /* Backup data is stored in a collection of objects, which are grouped together
21  * into segments for storage purposes.  This implementation of the object store
22  * represents segments as TAR files and objects as files within them. */
23
24 #ifndef _LBS_STORE_H
25 #define _LBS_STORE_H
26
27 #include <stdint.h>
28
29 #include <list>
30 #include <map>
31 #include <set>
32 #include <string>
33 #include <iostream>
34 #include <sstream>
35
36 #include "localdb.h"
37 #include "remote.h"
38 #include "ref.h"
39 #include "third_party/sha1.h"
40
41 class LbsObject;
42
43 /* In memory datatype to represent key/value pairs of information, such as file
44  * metadata.  Currently implemented as map<string, string>. */
45 typedef std::map<std::string, std::string> dictionary;
46
47 /* Simplified TAR header--we only need to store regular files, don't need to
48  * handle long filenames, etc. */
49 static const int TAR_BLOCK_SIZE = 512;
50
51 struct tar_header
52 {
53     char name[100];
54     char mode[8];
55     char uid[8];
56     char gid[8];
57     char size[12];
58     char mtime[12];
59     char chksum[8];
60     char typeflag;
61     char linkname[100];
62     char magic[8];
63     char uname[32];
64     char gname[32];
65     char devmajor[8];
66     char devminor[8];
67     char prefix[155];
68     char padding[12];
69 };
70
71 class FileFilter {
72 public:
73     // It is valid for program to be NULL or empty; if so, no filtering is
74     // done.
75     static FileFilter *New(int fd, const char *program);
76
77     // Wait for the filter process to terminate.
78     int wait();
79
80     // Accessors for the file descriptors.
81     int get_raw_fd() const { return fd_raw; }
82     int get_wrapped_fd() const { return fd_wrapped; }
83
84 private:
85     FileFilter(int raw, int wrapped, pid_t pid);
86
87     // Launch a process to filter data written to a file descriptor.  fd_out is
88     // the file descriptor where the filtered data should be written.  program
89     // is the filter program to execute (a single string which will be
90     // interpreted by /bin/sh).  The return value is a file descriptor to which
91     // the data to be filtered should be written.  The process ID of the filter
92     // process is stored at address filter_pid if non-NULL.
93     static int spawn_filter(int fd_out, const char *program, pid_t *filter_pid);
94
95     // The original file descriptor passed when creating the FileFilter object.
96     int fd_raw;
97
98     // The wrapped file descriptor: writes here are piped through the filter
99     // program.
100     int fd_wrapped;
101
102     // The filter process if one was launched, or -1 if there is no filter
103     // program.
104     pid_t pid;
105 };
106
107 /* A simple wrapper around a single TAR file to represent a segment.  Objects
108  * may only be written out all at once, since the tar header must be written
109  * first; incremental writing is not supported. */
110 class Tarfile {
111 public:
112     Tarfile(RemoteFile *file, const std::string &segment);
113     ~Tarfile();
114
115     void write_object(int id, const char *data, size_t len);
116
117     // Return an estimate of the size of the file.
118     size_t size_estimate();
119
120 private:
121     size_t size;
122     std::string segment_name;
123
124     RemoteFile *file;
125     FileFilter *filter;
126
127     // Write data to the tar file
128     void tar_write(const char *data, size_t size);
129 };
130
131 class TarSegmentStore {
132 public:
133     // New segments will be stored in the given directory.
134     TarSegmentStore(RemoteStore *remote,
135                     LocalDb *db = NULL)
136         { this->remote = remote; this->db = db; }
137     ~TarSegmentStore() { sync(); }
138
139     // Writes an object to segment in the store, and returns the name
140     // (segment/object) to refer to it.  The optional parameter group can be
141     // used to control object placement; objects with different group
142     // parameters are kept in separate segments.
143     ObjectReference write_object(const char *data, size_t len,
144                                  const std::string &group = "",
145                                  const std::string &checksum = "",
146                                  double age = 0.0);
147
148     // Ensure all segments have been fully written.
149     void sync();
150
151     // Dump statistics to stdout about how much data has been written
152     void dump_stats();
153
154 private:
155     struct segment_info {
156         Tarfile *file;
157         std::string group;
158         std::string name;           // UUID
159         int count;                  // Objects written to this segment
160         int data_size;              // Combined size of objects written
161         std::string basename;       // Name of segment without directory
162         RemoteFile *rf;
163     };
164
165     RemoteStore *remote;
166     std::map<std::string, struct segment_info *> segments;
167     LocalDb *db;
168
169     // Ensure that all segments in the given group have been fully written.
170     void close_segment(const std::string &group);
171
172     // Parse an object reference string and return just the segment name
173     // portion.
174     std::string object_reference_to_segment(const std::string &object);
175 };
176
177 /* An in-memory representation of an object, which can be incrementally built
178  * before it is written out to a segment. */
179 class LbsObject {
180 public:
181     LbsObject();
182     ~LbsObject();
183
184     // If an object is placed in a group, it will be written out to segments
185     // only containing other objects in the same group.  A group name is simply
186     // a string.
187     //std::string get_group() const { return group; }
188     void set_group(const std::string &g) { group = g; }
189
190     // Data in an object must be written all at once, and cannot be generated
191     // incrementally.  Data can be an arbitrary block of binary data of any
192     // size.  The pointer to the data need only remain valid until write() is
193     // called.  If checksum is non-NULL then it is assumed to contain a hash
194     // value for the data; this provides an optimization in case the caller has
195     // already checksummed the data.  Otherwise the set_data will compute a
196     // hash of the data itself.
197     void set_data(const char *d, size_t len, const char *checksum);
198
199     // Explicitly sets the age of the data, for later garbage-collection or
200     // repacking purposes.  If not set, the age defaults to the current time.
201     // The age is stored in the database as a floating point value, expressing
202     // the time in Julian days.
203     void set_age(double age) { this->age = age; }
204
205     // Write an object to a segment, thus making it permanent.  This function
206     // can be called at most once.
207     void write(TarSegmentStore *store);
208
209     // An object is assigned a permanent name once it has been written to a
210     // segment.  Until that time, its name cannot be determined.
211     ObjectReference get_ref() { return ref; }
212
213 private:
214     std::string group;
215     double age;
216     const char *data;
217     size_t data_len;
218     std::string checksum;
219
220     bool written;
221     ObjectReference ref;
222 };
223
224 /* Program through which segment data is piped before being written to file. */
225 extern const char *filter_program;
226
227 /* Extension which should be appended to segments written out (.tar is already
228  * included; this adds to it) */
229 extern const char *filter_extension;
230
231 #endif // _LBS_STORE_H