1 /*
2 * node_tree.c: an editor for tracking repository deltas changes
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 /* ==================================================================== */
25
26
27
28
29 #include <stdio.h>
30
31 #define APR_WANT_STRFUNC
32 #include <apr_want.h>
33 #include <apr_pools.h>
34
35 #include "svn_types.h"
36 #include "svn_error.h"
37 #include "svn_dirent_uri.h"
38 #include "svn_path.h"
39 #include "svn_delta.h"
40 #include "svn_fs.h"
41 #include "svn_repos.h"
42 #include "repos.h"
43 #include "svn_private_config.h"
44 #include "private/svn_fspath.h"
45
46 /*** NOTE: This editor is unique in that it currently is hard-coded to
47 be anchored at the root directory of the filesystem. This
48 affords us the ability to use the same paths for filesystem
49 locations and editor paths. ***/
50
51
52
53 /*** Node creation and assembly structures and routines. ***/
54 static svn_repos_node_t *
create_node(const char * name,svn_repos_node_t * parent,apr_pool_t * pool)55 create_node(const char *name,
56 svn_repos_node_t *parent,
57 apr_pool_t *pool)
58 {
59 svn_repos_node_t *node = apr_pcalloc(pool, sizeof(*node));
60 node->action = 'R';
61 node->kind = svn_node_unknown;
62 node->name = apr_pstrdup(pool, name);
63 node->parent = parent;
64 return node;
65 }
66
67
68 static svn_repos_node_t *
create_sibling_node(svn_repos_node_t * elder,const char * name,apr_pool_t * pool)69 create_sibling_node(svn_repos_node_t *elder,
70 const char *name,
71 apr_pool_t *pool)
72 {
73 svn_repos_node_t *tmp_node;
74
75 /* No ELDER sibling? That's just not gonna work out. */
76 if (! elder)
77 return NULL;
78
79 /* Run to the end of the list of siblings of ELDER. */
80 tmp_node = elder;
81 while (tmp_node->sibling)
82 tmp_node = tmp_node->sibling;
83
84 /* Create a new youngest sibling and return that. */
85 return (tmp_node->sibling = create_node(name, elder->parent, pool));
86 }
87
88
89 static svn_repos_node_t *
create_child_node(svn_repos_node_t * parent,const char * name,apr_pool_t * pool)90 create_child_node(svn_repos_node_t *parent,
91 const char *name,
92 apr_pool_t *pool)
93 {
94 /* No PARENT node? That's just not gonna work out. */
95 if (! parent)
96 return NULL;
97
98 /* If PARENT has no children, create its first one and return that. */
99 if (! parent->child)
100 return (parent->child = create_node(name, parent, pool));
101
102 /* If PARENT already has a child, create a new sibling for its first
103 child and return that. */
104 return create_sibling_node(parent->child, name, pool);
105 }
106
107
108 static svn_repos_node_t *
find_child_by_name(svn_repos_node_t * parent,const char * name)109 find_child_by_name(svn_repos_node_t *parent,
110 const char *name)
111 {
112 svn_repos_node_t *tmp_node;
113
114 /* No PARENT node, or a barren PARENT? Nothing to find. */
115 if ((! parent) || (! parent->child))
116 return NULL;
117
118 /* Look through the children for a node with a matching name. */
119 tmp_node = parent->child;
120 while (1)
121 {
122 if (! strcmp(tmp_node->name, name))
123 {
124 return tmp_node;
125 }
126 else
127 {
128 if (tmp_node->sibling)
129 tmp_node = tmp_node->sibling;
130 else
131 break;
132 }
133 }
134
135 return NULL;
136 }
137
138
139 static void
find_real_base_location(const char ** path_p,svn_revnum_t * rev_p,svn_repos_node_t * node,apr_pool_t * pool)140 find_real_base_location(const char **path_p,
141 svn_revnum_t *rev_p,
142 svn_repos_node_t *node,
143 apr_pool_t *pool)
144 {
145 /* If NODE is an add-with-history, then its real base location is
146 the copy source. */
147 if ((node->action == 'A')
148 && node->copyfrom_path
149 && SVN_IS_VALID_REVNUM(node->copyfrom_rev))
150 {
151 *path_p = node->copyfrom_path;
152 *rev_p = node->copyfrom_rev;
153 return;
154 }
155
156 /* Otherwise, if NODE has a parent, we'll recurse, and add NODE's
157 name to whatever the parent's real base path turns out to be (and
158 pass the base revision on through). */
159 if (node->parent)
160 {
161 const char *path;
162 svn_revnum_t rev;
163
164 find_real_base_location(&path, &rev, node->parent, pool);
165 *path_p = svn_fspath__join(path, node->name, pool);
166 *rev_p = rev;
167 return;
168 }
169
170 /* Finally, if the node has no parent, then its name is "/", and it
171 has no interesting base revision. */
172 *path_p = "/";
173 *rev_p = SVN_INVALID_REVNUM;
174 return;
175 }
176
177
178
179
180 /*** Editor functions and batons. ***/
181
182 struct edit_baton
183 {
184 svn_fs_t *fs;
185 svn_fs_root_t *root;
186 svn_fs_root_t *base_root;
187 apr_pool_t *node_pool;
188 svn_repos_node_t *node;
189 };
190
191
192 struct node_baton
193 {
194 struct edit_baton *edit_baton;
195 struct node_baton *parent_baton;
196 svn_repos_node_t *node;
197 };
198
199
200 static svn_error_t *
delete_entry(const char * path,svn_revnum_t revision,void * parent_baton,apr_pool_t * pool)201 delete_entry(const char *path,
202 svn_revnum_t revision,
203 void *parent_baton,
204 apr_pool_t *pool)
205 {
206 struct node_baton *d = parent_baton;
207 struct edit_baton *eb = d->edit_baton;
208 svn_repos_node_t *node;
209 const char *name;
210 const char *base_path;
211 svn_revnum_t base_rev;
212 svn_fs_root_t *base_root;
213 svn_node_kind_t kind;
214
215 /* Get (or create) the change node and update it. */
216 name = svn_relpath_basename(path, pool);
217 node = find_child_by_name(d->node, name);
218 if (! node)
219 node = create_child_node(d->node, name, eb->node_pool);
220 node->action = 'D';
221
222 /* We need to look up this node's parents to see what its original
223 path in the filesystem was. Why? Because if this deletion
224 occurred underneath a copied path, the thing that was deleted
225 probably lived at a different location (relative to the copy
226 source). */
227 find_real_base_location(&base_path, &base_rev, node, pool);
228 if (! SVN_IS_VALID_REVNUM(base_rev))
229 {
230 /* No interesting base revision? We'll just look for the path
231 in our base root. */
232 base_root = eb->base_root;
233 }
234 else
235 {
236 /* Oh. Perhaps some copy goodness happened somewhere? */
237 SVN_ERR(svn_fs_revision_root(&base_root, eb->fs, base_rev, pool));
238 }
239
240 /* Now figure out if this thing was a file or a dir. */
241 SVN_ERR(svn_fs_check_path(&kind, base_root, base_path, pool));
242 if (kind == svn_node_none)
243 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
244 _("'%s' not found in filesystem"), path);
245 node->kind = kind;
246
247 return SVN_NO_ERROR;
248 }
249
250
251
252 static svn_error_t *
add_open_helper(const char * path,char action,svn_node_kind_t kind,void * parent_baton,const char * copyfrom_path,svn_revnum_t copyfrom_rev,apr_pool_t * pool,void ** child_baton)253 add_open_helper(const char *path,
254 char action,
255 svn_node_kind_t kind,
256 void *parent_baton,
257 const char *copyfrom_path,
258 svn_revnum_t copyfrom_rev,
259 apr_pool_t *pool,
260 void **child_baton)
261 {
262 struct node_baton *pb = parent_baton;
263 struct edit_baton *eb = pb->edit_baton;
264 struct node_baton *nb = apr_pcalloc(pool, sizeof(*nb));
265
266 SVN_ERR_ASSERT(parent_baton && path);
267
268 nb->edit_baton = eb;
269 nb->parent_baton = pb;
270
271 /* Create and populate the node. */
272 nb->node = create_child_node(pb->node, svn_relpath_basename(path, NULL),
273 eb->node_pool);
274 nb->node->kind = kind;
275 nb->node->action = action;
276 nb->node->copyfrom_rev = copyfrom_rev;
277 nb->node->copyfrom_path =
278 copyfrom_path ? apr_pstrdup(eb->node_pool, copyfrom_path) : NULL;
279
280 *child_baton = nb;
281 return SVN_NO_ERROR;
282 }
283
284
285 static svn_error_t *
open_root(void * edit_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** root_baton)286 open_root(void *edit_baton,
287 svn_revnum_t base_revision,
288 apr_pool_t *pool,
289 void **root_baton)
290 {
291 struct edit_baton *eb = edit_baton;
292 struct node_baton *d = apr_pcalloc(pool, sizeof(*d));
293
294 d->edit_baton = eb;
295 d->parent_baton = NULL;
296 d->node = (eb->node = create_node("", NULL, eb->node_pool));
297 d->node->kind = svn_node_dir;
298 d->node->action = 'R';
299 *root_baton = d;
300
301 return SVN_NO_ERROR;
302 }
303
304
305 static svn_error_t *
open_directory(const char * path,void * parent_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** child_baton)306 open_directory(const char *path,
307 void *parent_baton,
308 svn_revnum_t base_revision,
309 apr_pool_t *pool,
310 void **child_baton)
311 {
312 return add_open_helper(path, 'R', svn_node_dir, parent_baton,
313 NULL, SVN_INVALID_REVNUM,
314 pool, child_baton);
315 }
316
317
318 static svn_error_t *
add_directory(const char * path,void * parent_baton,const char * copyfrom_path,svn_revnum_t copyfrom_revision,apr_pool_t * pool,void ** child_baton)319 add_directory(const char *path,
320 void *parent_baton,
321 const char *copyfrom_path,
322 svn_revnum_t copyfrom_revision,
323 apr_pool_t *pool,
324 void **child_baton)
325 {
326 return add_open_helper(path, 'A', svn_node_dir, parent_baton,
327 copyfrom_path, copyfrom_revision,
328 pool, child_baton);
329 }
330
331
332 static svn_error_t *
open_file(const char * path,void * parent_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** file_baton)333 open_file(const char *path,
334 void *parent_baton,
335 svn_revnum_t base_revision,
336 apr_pool_t *pool,
337 void **file_baton)
338 {
339 return add_open_helper(path, 'R', svn_node_file, parent_baton,
340 NULL, SVN_INVALID_REVNUM,
341 pool, file_baton);
342 }
343
344
345 static svn_error_t *
add_file(const char * path,void * parent_baton,const char * copyfrom_path,svn_revnum_t copyfrom_revision,apr_pool_t * pool,void ** file_baton)346 add_file(const char *path,
347 void *parent_baton,
348 const char *copyfrom_path,
349 svn_revnum_t copyfrom_revision,
350 apr_pool_t *pool,
351 void **file_baton)
352 {
353 return add_open_helper(path, 'A', svn_node_file, parent_baton,
354 copyfrom_path, copyfrom_revision,
355 pool, file_baton);
356 }
357
358
359 static svn_error_t *
apply_textdelta(void * file_baton,const char * base_checksum,apr_pool_t * pool,svn_txdelta_window_handler_t * handler,void ** handler_baton)360 apply_textdelta(void *file_baton,
361 const char *base_checksum,
362 apr_pool_t *pool,
363 svn_txdelta_window_handler_t *handler,
364 void **handler_baton)
365 {
366 struct node_baton *fb = file_baton;
367 fb->node->text_mod = TRUE;
368 *handler = svn_delta_noop_window_handler;
369 *handler_baton = NULL;
370 return SVN_NO_ERROR;
371 }
372
373
374
375 static svn_error_t *
change_node_prop(void * node_baton,const char * name,const svn_string_t * value,apr_pool_t * pool)376 change_node_prop(void *node_baton,
377 const char *name,
378 const svn_string_t *value,
379 apr_pool_t *pool)
380 {
381 struct node_baton *nb = node_baton;
382 nb->node->prop_mod = TRUE;
383 return SVN_NO_ERROR;
384 }
385
386
387 svn_error_t *
svn_repos_node_editor(const svn_delta_editor_t ** editor,void ** edit_baton,svn_repos_t * repos,svn_fs_root_t * base_root,svn_fs_root_t * root,apr_pool_t * node_pool,apr_pool_t * pool)388 svn_repos_node_editor(const svn_delta_editor_t **editor,
389 void **edit_baton,
390 svn_repos_t *repos,
391 svn_fs_root_t *base_root,
392 svn_fs_root_t *root,
393 apr_pool_t *node_pool,
394 apr_pool_t *pool)
395 {
396 svn_delta_editor_t *my_editor;
397 struct edit_baton *my_edit_baton;
398
399 /* Set up the editor. */
400 my_editor = svn_delta_default_editor(pool);
401 my_editor->open_root = open_root;
402 my_editor->delete_entry = delete_entry;
403 my_editor->add_directory = add_directory;
404 my_editor->open_directory = open_directory;
405 my_editor->add_file = add_file;
406 my_editor->open_file = open_file;
407 my_editor->apply_textdelta = apply_textdelta;
408 my_editor->change_file_prop = change_node_prop;
409 my_editor->change_dir_prop = change_node_prop;
410
411 /* Set up the edit baton. */
412 my_edit_baton = apr_pcalloc(pool, sizeof(*my_edit_baton));
413 my_edit_baton->node_pool = node_pool;
414 my_edit_baton->fs = repos->fs;
415 my_edit_baton->root = root;
416 my_edit_baton->base_root = base_root;
417
418 *editor = my_editor;
419 *edit_baton = my_edit_baton;
420
421 return SVN_NO_ERROR;
422 }
423
424
425
426 svn_repos_node_t *
svn_repos_node_from_baton(void * edit_baton)427 svn_repos_node_from_baton(void *edit_baton)
428 {
429 struct edit_baton *eb = edit_baton;
430 return eb->node;
431 }
432