]>
Commit | Line | Data |
---|---|---|
21746aa3 DB |
1 | /* |
2 | * Parse and rearrange a svnadmin dump. | |
3 | * Create the dump with: | |
4 | * svnadmin dump --incremental -r<startrev>:<endrev> <repository> >outfile | |
5 | * | |
6 | * Licensed under a two-clause BSD-style license. | |
7 | * See LICENSE for details. | |
8 | */ | |
9 | ||
10 | #include "cache.h" | |
11 | #include "repo_tree.h" | |
12 | #include "fast_export.h" | |
13 | #include "line_buffer.h" | |
14 | #include "obj_pool.h" | |
15 | #include "string_pool.h" | |
16 | ||
17 | #define NODEACT_REPLACE 4 | |
18 | #define NODEACT_DELETE 3 | |
19 | #define NODEACT_ADD 2 | |
20 | #define NODEACT_CHANGE 1 | |
21 | #define NODEACT_UNKNOWN 0 | |
22 | ||
23 | #define DUMP_CTX 0 | |
24 | #define REV_CTX 1 | |
25 | #define NODE_CTX 2 | |
26 | ||
27 | #define LENGTH_UNKNOWN (~0) | |
28 | #define DATE_RFC2822_LEN 31 | |
29 | ||
30 | /* Create memory pool for log messages */ | |
31 | obj_pool_gen(log, char, 4096) | |
32 | ||
33 | static char* log_copy(uint32_t length, char *log) | |
34 | { | |
35 | char *buffer; | |
36 | log_free(log_pool.size); | |
37 | buffer = log_pointer(log_alloc(length)); | |
38 | strncpy(buffer, log, length); | |
39 | return buffer; | |
40 | } | |
41 | ||
42 | static struct { | |
43 | uint32_t action, propLength, textLength, srcRev, srcMode, mark, type; | |
44 | uint32_t src[REPO_MAX_PATH_DEPTH], dst[REPO_MAX_PATH_DEPTH]; | |
45 | } node_ctx; | |
46 | ||
47 | static struct { | |
48 | uint32_t revision, author; | |
49 | unsigned long timestamp; | |
50 | char *log; | |
51 | } rev_ctx; | |
52 | ||
53 | static struct { | |
b3e5bce1 | 54 | uint32_t version, uuid, url; |
21746aa3 DB |
55 | } dump_ctx; |
56 | ||
57 | static struct { | |
58 | uint32_t svn_log, svn_author, svn_date, svn_executable, svn_special, uuid, | |
59 | revision_number, node_path, node_kind, node_action, | |
60 | node_copyfrom_path, node_copyfrom_rev, text_content_length, | |
b3e5bce1 | 61 | prop_content_length, content_length, svn_fs_dump_format_version; |
21746aa3 DB |
62 | } keys; |
63 | ||
64 | static void reset_node_ctx(char *fname) | |
65 | { | |
66 | node_ctx.type = 0; | |
67 | node_ctx.action = NODEACT_UNKNOWN; | |
68 | node_ctx.propLength = LENGTH_UNKNOWN; | |
69 | node_ctx.textLength = LENGTH_UNKNOWN; | |
70 | node_ctx.src[0] = ~0; | |
71 | node_ctx.srcRev = 0; | |
72 | node_ctx.srcMode = 0; | |
73 | pool_tok_seq(REPO_MAX_PATH_DEPTH, node_ctx.dst, "/", fname); | |
74 | node_ctx.mark = 0; | |
75 | } | |
76 | ||
77 | static void reset_rev_ctx(uint32_t revision) | |
78 | { | |
79 | rev_ctx.revision = revision; | |
80 | rev_ctx.timestamp = 0; | |
81 | rev_ctx.log = NULL; | |
82 | rev_ctx.author = ~0; | |
83 | } | |
84 | ||
85 | static void reset_dump_ctx(uint32_t url) | |
86 | { | |
87 | dump_ctx.url = url; | |
b3e5bce1 | 88 | dump_ctx.version = 1; |
21746aa3 DB |
89 | dump_ctx.uuid = ~0; |
90 | } | |
91 | ||
92 | static void init_keys(void) | |
93 | { | |
94 | keys.svn_log = pool_intern("svn:log"); | |
95 | keys.svn_author = pool_intern("svn:author"); | |
96 | keys.svn_date = pool_intern("svn:date"); | |
97 | keys.svn_executable = pool_intern("svn:executable"); | |
98 | keys.svn_special = pool_intern("svn:special"); | |
99 | keys.uuid = pool_intern("UUID"); | |
100 | keys.revision_number = pool_intern("Revision-number"); | |
101 | keys.node_path = pool_intern("Node-path"); | |
102 | keys.node_kind = pool_intern("Node-kind"); | |
103 | keys.node_action = pool_intern("Node-action"); | |
104 | keys.node_copyfrom_path = pool_intern("Node-copyfrom-path"); | |
105 | keys.node_copyfrom_rev = pool_intern("Node-copyfrom-rev"); | |
106 | keys.text_content_length = pool_intern("Text-content-length"); | |
107 | keys.prop_content_length = pool_intern("Prop-content-length"); | |
108 | keys.content_length = pool_intern("Content-length"); | |
b3e5bce1 | 109 | keys.svn_fs_dump_format_version = pool_intern("SVN-fs-dump-format-version"); |
21746aa3 DB |
110 | } |
111 | ||
112 | static void read_props(void) | |
113 | { | |
114 | uint32_t len; | |
115 | uint32_t key = ~0; | |
116 | char *val = NULL; | |
117 | char *t; | |
118 | while ((t = buffer_read_line()) && strcmp(t, "PROPS-END")) { | |
119 | if (!strncmp(t, "K ", 2)) { | |
120 | len = atoi(&t[2]); | |
121 | key = pool_intern(buffer_read_string(len)); | |
122 | buffer_read_line(); | |
123 | } else if (!strncmp(t, "V ", 2)) { | |
124 | len = atoi(&t[2]); | |
125 | val = buffer_read_string(len); | |
126 | if (key == keys.svn_log) { | |
127 | /* Value length excludes terminating nul. */ | |
128 | rev_ctx.log = log_copy(len + 1, val); | |
129 | } else if (key == keys.svn_author) { | |
130 | rev_ctx.author = pool_intern(val); | |
131 | } else if (key == keys.svn_date) { | |
132 | if (parse_date_basic(val, &rev_ctx.timestamp, NULL)) | |
133 | fprintf(stderr, "Invalid timestamp: %s\n", val); | |
134 | } else if (key == keys.svn_executable) { | |
135 | node_ctx.type = REPO_MODE_EXE; | |
136 | } else if (key == keys.svn_special) { | |
137 | node_ctx.type = REPO_MODE_LNK; | |
138 | } | |
139 | key = ~0; | |
140 | buffer_read_line(); | |
141 | } | |
142 | } | |
143 | } | |
144 | ||
145 | static void handle_node(void) | |
146 | { | |
147 | if (node_ctx.propLength != LENGTH_UNKNOWN && node_ctx.propLength) | |
148 | read_props(); | |
149 | ||
150 | if (node_ctx.srcRev) | |
151 | node_ctx.srcMode = repo_copy(node_ctx.srcRev, node_ctx.src, node_ctx.dst); | |
152 | ||
153 | if (node_ctx.textLength != LENGTH_UNKNOWN && | |
154 | node_ctx.type != REPO_MODE_DIR) | |
155 | node_ctx.mark = next_blob_mark(); | |
156 | ||
157 | if (node_ctx.action == NODEACT_DELETE) { | |
158 | repo_delete(node_ctx.dst); | |
159 | } else if (node_ctx.action == NODEACT_CHANGE || | |
160 | node_ctx.action == NODEACT_REPLACE) { | |
161 | if (node_ctx.action == NODEACT_REPLACE && | |
162 | node_ctx.type == REPO_MODE_DIR) | |
163 | repo_replace(node_ctx.dst, node_ctx.mark); | |
164 | else if (node_ctx.propLength != LENGTH_UNKNOWN) | |
165 | repo_modify(node_ctx.dst, node_ctx.type, node_ctx.mark); | |
166 | else if (node_ctx.textLength != LENGTH_UNKNOWN) | |
167 | node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark); | |
168 | } else if (node_ctx.action == NODEACT_ADD) { | |
169 | if (node_ctx.srcRev && node_ctx.propLength != LENGTH_UNKNOWN) | |
170 | repo_modify(node_ctx.dst, node_ctx.type, node_ctx.mark); | |
171 | else if (node_ctx.srcRev && node_ctx.textLength != LENGTH_UNKNOWN) | |
172 | node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark); | |
173 | else if ((node_ctx.type == REPO_MODE_DIR && !node_ctx.srcRev) || | |
174 | node_ctx.textLength != LENGTH_UNKNOWN) | |
175 | repo_add(node_ctx.dst, node_ctx.type, node_ctx.mark); | |
176 | } | |
177 | ||
178 | if (node_ctx.propLength == LENGTH_UNKNOWN && node_ctx.srcMode) | |
179 | node_ctx.type = node_ctx.srcMode; | |
180 | ||
181 | if (node_ctx.mark) | |
182 | fast_export_blob(node_ctx.type, node_ctx.mark, node_ctx.textLength); | |
183 | else if (node_ctx.textLength != LENGTH_UNKNOWN) | |
184 | buffer_skip_bytes(node_ctx.textLength); | |
185 | } | |
186 | ||
187 | static void handle_revision(void) | |
188 | { | |
189 | if (rev_ctx.revision) | |
190 | repo_commit(rev_ctx.revision, rev_ctx.author, rev_ctx.log, | |
191 | dump_ctx.uuid, dump_ctx.url, rev_ctx.timestamp); | |
192 | } | |
193 | ||
194 | void svndump_read(const char *url) | |
195 | { | |
196 | char *val; | |
197 | char *t; | |
198 | uint32_t active_ctx = DUMP_CTX; | |
199 | uint32_t len; | |
200 | uint32_t key; | |
201 | ||
202 | reset_dump_ctx(pool_intern(url)); | |
203 | while ((t = buffer_read_line())) { | |
204 | val = strstr(t, ": "); | |
205 | if (!val) | |
206 | continue; | |
207 | *val++ = '\0'; | |
208 | *val++ = '\0'; | |
209 | key = pool_intern(t); | |
210 | ||
b3e5bce1 JN |
211 | if (key == keys.svn_fs_dump_format_version) { |
212 | dump_ctx.version = atoi(val); | |
213 | if (dump_ctx.version > 2) | |
5ee5f5a6 | 214 | die("expected svn dump format version <= 2, found %"PRIu32, |
b3e5bce1 JN |
215 | dump_ctx.version); |
216 | } else if (key == keys.uuid) { | |
21746aa3 DB |
217 | dump_ctx.uuid = pool_intern(val); |
218 | } else if (key == keys.revision_number) { | |
219 | if (active_ctx == NODE_CTX) | |
220 | handle_node(); | |
221 | if (active_ctx != DUMP_CTX) | |
222 | handle_revision(); | |
223 | active_ctx = REV_CTX; | |
224 | reset_rev_ctx(atoi(val)); | |
225 | } else if (key == keys.node_path) { | |
226 | if (active_ctx == NODE_CTX) | |
227 | handle_node(); | |
228 | active_ctx = NODE_CTX; | |
229 | reset_node_ctx(val); | |
230 | } else if (key == keys.node_kind) { | |
231 | if (!strcmp(val, "dir")) | |
232 | node_ctx.type = REPO_MODE_DIR; | |
233 | else if (!strcmp(val, "file")) | |
234 | node_ctx.type = REPO_MODE_BLB; | |
235 | else | |
236 | fprintf(stderr, "Unknown node-kind: %s\n", val); | |
237 | } else if (key == keys.node_action) { | |
238 | if (!strcmp(val, "delete")) { | |
239 | node_ctx.action = NODEACT_DELETE; | |
240 | } else if (!strcmp(val, "add")) { | |
241 | node_ctx.action = NODEACT_ADD; | |
242 | } else if (!strcmp(val, "change")) { | |
243 | node_ctx.action = NODEACT_CHANGE; | |
244 | } else if (!strcmp(val, "replace")) { | |
245 | node_ctx.action = NODEACT_REPLACE; | |
246 | } else { | |
247 | fprintf(stderr, "Unknown node-action: %s\n", val); | |
248 | node_ctx.action = NODEACT_UNKNOWN; | |
249 | } | |
250 | } else if (key == keys.node_copyfrom_path) { | |
251 | pool_tok_seq(REPO_MAX_PATH_DEPTH, node_ctx.src, "/", val); | |
252 | } else if (key == keys.node_copyfrom_rev) { | |
253 | node_ctx.srcRev = atoi(val); | |
254 | } else if (key == keys.text_content_length) { | |
255 | node_ctx.textLength = atoi(val); | |
256 | } else if (key == keys.prop_content_length) { | |
257 | node_ctx.propLength = atoi(val); | |
258 | } else if (key == keys.content_length) { | |
259 | len = atoi(val); | |
260 | buffer_read_line(); | |
261 | if (active_ctx == REV_CTX) { | |
262 | read_props(); | |
263 | } else if (active_ctx == NODE_CTX) { | |
264 | handle_node(); | |
265 | active_ctx = REV_CTX; | |
266 | } else { | |
5418d96d | 267 | fprintf(stderr, "Unexpected content length header: %"PRIu32"\n", len); |
21746aa3 DB |
268 | buffer_skip_bytes(len); |
269 | } | |
270 | } | |
271 | } | |
272 | if (active_ctx == NODE_CTX) | |
273 | handle_node(); | |
274 | if (active_ctx != DUMP_CTX) | |
275 | handle_revision(); | |
276 | } | |
277 | ||
278 | void svndump_init(const char *filename) | |
279 | { | |
280 | buffer_init(filename); | |
281 | repo_init(); | |
282 | reset_dump_ctx(~0); | |
283 | reset_rev_ctx(0); | |
284 | reset_node_ctx(NULL); | |
285 | init_keys(); | |
286 | } | |
287 | ||
288 | void svndump_deinit(void) | |
289 | { | |
290 | log_reset(); | |
291 | repo_reset(); | |
292 | reset_dump_ctx(~0); | |
293 | reset_rev_ctx(0); | |
294 | reset_node_ctx(NULL); | |
295 | if (buffer_deinit()) | |
296 | fprintf(stderr, "Input error\n"); | |
297 | if (ferror(stdout)) | |
298 | fprintf(stderr, "Output error\n"); | |
299 | } | |
300 | ||
301 | void svndump_reset(void) | |
302 | { | |
303 | log_reset(); | |
304 | buffer_reset(); | |
305 | repo_reset(); | |
306 | reset_dump_ctx(~0); | |
307 | reset_rev_ctx(0); | |
308 | reset_node_ctx(NULL); | |
309 | } |