1 /*
2 * element.c : editing trees of versioned resources
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 #include <assert.h>
25 #include <apr_pools.h>
26
27 #include "svn_types.h"
28 #include "svn_error.h"
29 #include "svn_string.h"
30 #include "svn_props.h"
31 #include "svn_dirent_uri.h"
32 #include "svn_iter.h"
33 #include "private/svn_sorts_private.h"
34
35 #include "private/svn_element.h"
36 #include "svn_private_config.h"
37
38
39 void *
svn_eid__hash_get(apr_hash_t * ht,int key)40 svn_eid__hash_get(apr_hash_t *ht,
41 int key)
42 {
43 return apr_hash_get(ht, &key, sizeof(key));
44 }
45
46 void
svn_eid__hash_set(apr_hash_t * ht,int key,const void * val)47 svn_eid__hash_set(apr_hash_t *ht,
48 int key,
49 const void *val)
50 {
51 int *id_p = apr_pmemdup(apr_hash_pool_get(ht), &key, sizeof(key));
52
53 apr_hash_set(ht, id_p, sizeof(key), val);
54 }
55
56 int
svn_eid__hash_this_key(apr_hash_index_t * hi)57 svn_eid__hash_this_key(apr_hash_index_t *hi)
58 {
59 return *(const int *)apr_hash_this_key(hi);
60 }
61
62 svn_eid__hash_iter_t *
svn_eid__hash_sorted_first(apr_pool_t * pool,apr_hash_t * ht,int (* comparison_func)(const svn_sort__item_t *,const svn_sort__item_t *))63 svn_eid__hash_sorted_first(apr_pool_t *pool,
64 apr_hash_t *ht,
65 int (*comparison_func)(const svn_sort__item_t *,
66 const svn_sort__item_t *))
67 {
68 svn_eid__hash_iter_t *hi = apr_palloc(pool, sizeof(*hi));
69
70 if (apr_hash_count(ht) == 0)
71 return NULL;
72
73 hi->array = svn_sort__hash(ht, comparison_func, pool);
74 hi->i = 0;
75 hi->eid = *(const int *)(APR_ARRAY_IDX(hi->array, hi->i,
76 svn_sort__item_t).key);
77 hi->val = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).value;
78 return hi;
79 }
80
81 svn_eid__hash_iter_t *
svn_eid__hash_sorted_next(svn_eid__hash_iter_t * hi)82 svn_eid__hash_sorted_next(svn_eid__hash_iter_t *hi)
83 {
84 hi->i++;
85 if (hi->i >= hi->array->nelts)
86 {
87 return NULL;
88 }
89 hi->eid = *(const int *)(APR_ARRAY_IDX(hi->array, hi->i,
90 svn_sort__item_t).key);
91 hi->val = APR_ARRAY_IDX(hi->array, hi->i, svn_sort__item_t).value;
92 return hi;
93 }
94
95 int
svn_eid__hash_sort_compare_items_by_eid(const svn_sort__item_t * a,const svn_sort__item_t * b)96 svn_eid__hash_sort_compare_items_by_eid(const svn_sort__item_t *a,
97 const svn_sort__item_t *b)
98 {
99 int eid_a = *(const int *)a->key;
100 int eid_b = *(const int *)b->key;
101
102 return eid_a - eid_b;
103 }
104
105
106 /*
107 * ===================================================================
108 * Element payload
109 * ===================================================================
110 */
111
112 svn_boolean_t
svn_element__payload_invariants(const svn_element__payload_t * payload)113 svn_element__payload_invariants(const svn_element__payload_t *payload)
114 {
115 if (payload->is_subbranch_root)
116 return TRUE;
117
118 /* If kind is unknown, it's a reference; otherwise it has content
119 specified and may also have a reference. */
120 if (payload->kind == svn_node_unknown)
121 if (SVN_IS_VALID_REVNUM(payload->branch_ref.rev)
122 && payload->branch_ref.branch_id
123 && payload->branch_ref.eid != -1)
124 return TRUE;
125 if ((payload->kind == svn_node_dir
126 || payload->kind == svn_node_file
127 || payload->kind == svn_node_symlink)
128 && (payload->props
129 && ((payload->kind == svn_node_file) == !!payload->text)
130 && ((payload->kind == svn_node_symlink) == !!payload->target)))
131 return TRUE;
132 return FALSE;
133 }
134
135 svn_element__payload_t *
svn_element__payload_dup(const svn_element__payload_t * old,apr_pool_t * result_pool)136 svn_element__payload_dup(const svn_element__payload_t *old,
137 apr_pool_t *result_pool)
138 {
139 svn_element__payload_t *new_payload;
140
141 assert(! old || svn_element__payload_invariants(old));
142
143 if (old == NULL)
144 return NULL;
145
146 new_payload = apr_pmemdup(result_pool, old, sizeof(*new_payload));
147 if (old->branch_ref.branch_id)
148 new_payload->branch_ref.branch_id
149 = apr_pstrdup(result_pool, old->branch_ref.branch_id);
150 if (old->props)
151 new_payload->props = svn_prop_hash_dup(old->props, result_pool);
152 if (old->kind == svn_node_file && old->text)
153 new_payload->text = svn_stringbuf_dup(old->text, result_pool);
154 if (old->kind == svn_node_symlink && old->target)
155 new_payload->target = apr_pstrdup(result_pool, old->target);
156 return new_payload;
157 }
158
159 svn_boolean_t
svn_element__payload_equal(const svn_element__payload_t * left,const svn_element__payload_t * right,apr_pool_t * scratch_pool)160 svn_element__payload_equal(const svn_element__payload_t *left,
161 const svn_element__payload_t *right,
162 apr_pool_t *scratch_pool)
163 {
164 apr_array_header_t *prop_diffs;
165
166 assert(svn_element__payload_invariants(left));
167 assert(svn_element__payload_invariants(right));
168
169 /* any two subbranch-root elements compare equal */
170 if (left->is_subbranch_root && right->is_subbranch_root)
171 {
172 return TRUE;
173 }
174 else if (left->is_subbranch_root || right->is_subbranch_root)
175 {
176 return FALSE;
177 }
178
179 /* content defined only by reference is not supported */
180 SVN_ERR_ASSERT_NO_RETURN(left->kind != svn_node_unknown
181 && right->kind != svn_node_unknown);
182
183 if (left->kind != right->kind)
184 {
185 return FALSE;
186 }
187
188 svn_error_clear(svn_prop_diffs(&prop_diffs,
189 left->props, right->props,
190 scratch_pool));
191
192 if (prop_diffs->nelts != 0)
193 {
194 return FALSE;
195 }
196 switch (left->kind)
197 {
198 case svn_node_dir:
199 break;
200 case svn_node_file:
201 if (! svn_stringbuf_compare(left->text, right->text))
202 {
203 return FALSE;
204 }
205 break;
206 case svn_node_symlink:
207 if (strcmp(left->target, right->target) != 0)
208 {
209 return FALSE;
210 }
211 break;
212 default:
213 break;
214 }
215
216 return TRUE;
217 }
218
219 svn_element__payload_t *
svn_element__payload_create_subbranch(apr_pool_t * result_pool)220 svn_element__payload_create_subbranch(apr_pool_t *result_pool)
221 {
222 svn_element__payload_t *new_payload
223 = apr_pcalloc(result_pool, sizeof(*new_payload));
224
225 new_payload->pool = result_pool;
226 new_payload->is_subbranch_root = TRUE;
227 assert(svn_element__payload_invariants(new_payload));
228 return new_payload;
229 }
230
231 svn_element__payload_t *
svn_element__payload_create_ref(svn_revnum_t rev,const char * branch_id,int eid,apr_pool_t * result_pool)232 svn_element__payload_create_ref(svn_revnum_t rev,
233 const char *branch_id,
234 int eid,
235 apr_pool_t *result_pool)
236 {
237 svn_element__payload_t *new_payload
238 = apr_pcalloc(result_pool, sizeof(*new_payload));
239
240 new_payload->pool = result_pool;
241 new_payload->kind = svn_node_unknown;
242 new_payload->branch_ref.rev = rev;
243 new_payload->branch_ref.branch_id = apr_pstrdup(result_pool, branch_id);
244 new_payload->branch_ref.eid = eid;
245 assert(svn_element__payload_invariants(new_payload));
246 return new_payload;
247 }
248
249 svn_element__payload_t *
svn_element__payload_create_dir(apr_hash_t * props,apr_pool_t * result_pool)250 svn_element__payload_create_dir(apr_hash_t *props,
251 apr_pool_t *result_pool)
252 {
253 svn_element__payload_t *new_payload
254 = apr_pcalloc(result_pool, sizeof(*new_payload));
255
256 new_payload->pool = result_pool;
257 new_payload->kind = svn_node_dir;
258 new_payload->props = props ? svn_prop_hash_dup(props, result_pool) : NULL;
259 assert(svn_element__payload_invariants(new_payload));
260 return new_payload;
261 }
262
263 svn_element__payload_t *
svn_element__payload_create_file(apr_hash_t * props,svn_stringbuf_t * text,apr_pool_t * result_pool)264 svn_element__payload_create_file(apr_hash_t *props,
265 svn_stringbuf_t *text,
266 apr_pool_t *result_pool)
267 {
268 svn_element__payload_t *new_payload
269 = apr_pcalloc(result_pool, sizeof(*new_payload));
270
271 SVN_ERR_ASSERT_NO_RETURN(text);
272
273 new_payload->pool = result_pool;
274 new_payload->kind = svn_node_file;
275 new_payload->props = props ? svn_prop_hash_dup(props, result_pool) : NULL;
276 new_payload->text = svn_stringbuf_dup(text, result_pool);
277 assert(svn_element__payload_invariants(new_payload));
278 return new_payload;
279 }
280
281 svn_element__payload_t *
svn_element__payload_create_symlink(apr_hash_t * props,const char * target,apr_pool_t * result_pool)282 svn_element__payload_create_symlink(apr_hash_t *props,
283 const char *target,
284 apr_pool_t *result_pool)
285 {
286 svn_element__payload_t *new_payload
287 = apr_pcalloc(result_pool, sizeof(*new_payload));
288
289 SVN_ERR_ASSERT_NO_RETURN(target);
290
291 new_payload->pool = result_pool;
292 new_payload->kind = svn_node_symlink;
293 new_payload->props = props ? svn_prop_hash_dup(props, result_pool) : NULL;
294 new_payload->target = apr_pstrdup(result_pool, target);
295 assert(svn_element__payload_invariants(new_payload));
296 return new_payload;
297 }
298
299 svn_element__content_t *
svn_element__content_create(int parent_eid,const char * name,const svn_element__payload_t * payload,apr_pool_t * result_pool)300 svn_element__content_create(int parent_eid,
301 const char *name,
302 const svn_element__payload_t *payload,
303 apr_pool_t *result_pool)
304 {
305 svn_element__content_t *content
306 = apr_palloc(result_pool, sizeof(*content));
307
308 content->parent_eid = parent_eid;
309 content->name = apr_pstrdup(result_pool, name);
310 content->payload = svn_element__payload_dup(payload, result_pool);
311 return content;
312 }
313
314 svn_element__content_t *
svn_element__content_dup(const svn_element__content_t * old,apr_pool_t * result_pool)315 svn_element__content_dup(const svn_element__content_t *old,
316 apr_pool_t *result_pool)
317 {
318 svn_element__content_t *content
319 = apr_pmemdup(result_pool, old, sizeof(*content));
320
321 content->name = apr_pstrdup(result_pool, old->name);
322 content->payload = svn_element__payload_dup(old->payload, result_pool);
323 return content;
324 }
325
326 svn_boolean_t
svn_element__content_equal(const svn_element__content_t * content_left,const svn_element__content_t * content_right,apr_pool_t * scratch_pool)327 svn_element__content_equal(const svn_element__content_t *content_left,
328 const svn_element__content_t *content_right,
329 apr_pool_t *scratch_pool)
330 {
331 if (!content_left && !content_right)
332 {
333 return TRUE;
334 }
335 else if (!content_left || !content_right)
336 {
337 return FALSE;
338 }
339
340 if (content_left->parent_eid != content_right->parent_eid)
341 {
342 return FALSE;
343 }
344 if (strcmp(content_left->name, content_right->name) != 0)
345 {
346 return FALSE;
347 }
348 if (! svn_element__payload_equal(content_left->payload, content_right->payload,
349 scratch_pool))
350 {
351 return FALSE;
352 }
353
354 return TRUE;
355 }
356
357 svn_element__tree_t *
svn_element__tree_create(apr_hash_t * e_map,int root_eid,apr_pool_t * result_pool)358 svn_element__tree_create(apr_hash_t *e_map,
359 int root_eid,
360 apr_pool_t *result_pool)
361 {
362 svn_element__tree_t *element_tree
363 = apr_pcalloc(result_pool, sizeof(*element_tree));
364
365 element_tree->e_map = e_map ? apr_hash_copy(result_pool, e_map)
366 : apr_hash_make(result_pool);
367 element_tree->root_eid = root_eid;
368 return element_tree;
369 }
370
371 svn_element__content_t *
svn_element__tree_get(const svn_element__tree_t * tree,int eid)372 svn_element__tree_get(const svn_element__tree_t *tree,
373 int eid)
374 {
375 return svn_eid__hash_get(tree->e_map, eid);
376 }
377
378 void
svn_element__tree_set(svn_element__tree_t * tree,int eid,const svn_element__content_t * element)379 svn_element__tree_set(svn_element__tree_t *tree,
380 int eid,
381 const svn_element__content_t *element)
382 {
383 svn_eid__hash_set(tree->e_map, eid, element);
384 }
385
386 void
svn_element__tree_purge_orphans(apr_hash_t * e_map,int root_eid,apr_pool_t * scratch_pool)387 svn_element__tree_purge_orphans(apr_hash_t *e_map,
388 int root_eid,
389 apr_pool_t *scratch_pool)
390 {
391 apr_hash_index_t *hi;
392 svn_boolean_t changed;
393
394 SVN_ERR_ASSERT_NO_RETURN(svn_eid__hash_get(e_map, root_eid));
395
396 do
397 {
398 changed = FALSE;
399
400 for (hi = apr_hash_first(scratch_pool, e_map);
401 hi; hi = apr_hash_next(hi))
402 {
403 int this_eid = svn_eid__hash_this_key(hi);
404 svn_element__content_t *this_element = apr_hash_this_val(hi);
405
406 if (this_eid != root_eid)
407 {
408 svn_element__content_t *parent_element
409 = svn_eid__hash_get(e_map, this_element->parent_eid);
410
411 /* Purge if parent is deleted */
412 if (! parent_element)
413 {
414 svn_eid__hash_set(e_map, this_eid, NULL);
415 changed = TRUE;
416 }
417 else
418 SVN_ERR_ASSERT_NO_RETURN(
419 ! parent_element->payload->is_subbranch_root);
420 }
421 }
422 }
423 while (changed);
424 }
425
426 const char *
svn_element__tree_get_path_by_eid(const svn_element__tree_t * tree,int eid,apr_pool_t * result_pool)427 svn_element__tree_get_path_by_eid(const svn_element__tree_t *tree,
428 int eid,
429 apr_pool_t *result_pool)
430 {
431 const char *path = "";
432 svn_element__content_t *element;
433
434 for (; eid != tree->root_eid; eid = element->parent_eid)
435 {
436 element = svn_element__tree_get(tree, eid);
437 if (! element)
438 return NULL;
439 path = svn_relpath_join(element->name, path, result_pool);
440 }
441 SVN_ERR_ASSERT_NO_RETURN(eid == tree->root_eid);
442 return path;
443 }
444
445 svn_element__tree_t *
svn_element__tree_get_subtree_at_eid(svn_element__tree_t * element_tree,int eid,apr_pool_t * result_pool)446 svn_element__tree_get_subtree_at_eid(svn_element__tree_t *element_tree,
447 int eid,
448 apr_pool_t *result_pool)
449 {
450 svn_element__tree_t *new_subtree;
451 svn_element__content_t *subtree_root_element;
452
453 new_subtree = svn_element__tree_create(element_tree->e_map, eid,
454 result_pool);
455
456 /* Purge orphans */
457 svn_element__tree_purge_orphans(new_subtree->e_map,
458 new_subtree->root_eid, result_pool);
459
460 /* Remove 'parent' and 'name' attributes from subtree root element */
461 subtree_root_element
462 = svn_element__tree_get(new_subtree, new_subtree->root_eid);
463 svn_element__tree_set(new_subtree, new_subtree->root_eid,
464 svn_element__content_create(
465 -1, "", subtree_root_element->payload, result_pool));
466
467 return new_subtree;
468 }
469
470