1 /*
2 * workqueue.c : manipulating work queue items
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 <apr_pools.h>
25
26 #include "svn_types.h"
27 #include "svn_pools.h"
28 #include "svn_dirent_uri.h"
29 #include "svn_subst.h"
30 #include "svn_hash.h"
31 #include "svn_io.h"
32
33 #include "wc.h"
34 #include "wc_db.h"
35 #include "workqueue.h"
36 #include "adm_files.h"
37 #include "conflicts.h"
38 #include "translate.h"
39
40 #include "svn_private_config.h"
41 #include "private/svn_skel.h"
42
43
44 /* Workqueue operation names. */
45 #define OP_FILE_COMMIT "file-commit"
46 #define OP_FILE_INSTALL "file-install"
47 #define OP_FILE_REMOVE "file-remove"
48 #define OP_FILE_MOVE "file-move"
49 #define OP_FILE_COPY_TRANSLATED "file-translate"
50 #define OP_SYNC_FILE_FLAGS "sync-file-flags"
51 #define OP_PREJ_INSTALL "prej-install"
52 #define OP_DIRECTORY_REMOVE "dir-remove"
53 #define OP_DIRECTORY_INSTALL "dir-install"
54
55 #define OP_POSTUPGRADE "postupgrade"
56
57 /* Legacy items */
58 #define OP_BASE_REMOVE "base-remove"
59 #define OP_RECORD_FILEINFO "record-fileinfo"
60 #define OP_TMP_SET_TEXT_CONFLICT_MARKERS "tmp-set-text-conflict-markers"
61 #define OP_TMP_SET_PROPERTY_CONFLICT_MARKER "tmp-set-property-conflict-marker"
62
63 /* For work queue debugging. Generates output about its operation. */
64 /* #define SVN_DEBUG_WORK_QUEUE */
65
66 typedef struct work_item_baton_t work_item_baton_t;
67
68 struct work_item_dispatch {
69 const char *name;
70 svn_error_t *(*func)(work_item_baton_t *wqb,
71 svn_wc__db_t *db,
72 const svn_skel_t *work_item,
73 const char *wri_abspath,
74 svn_cancel_func_t cancel_func,
75 void *cancel_baton,
76 apr_pool_t *scratch_pool);
77 };
78
79 /* Forward definition */
80 static svn_error_t *
81 get_and_record_fileinfo(work_item_baton_t *wqb,
82 const char *local_abspath,
83 svn_boolean_t ignore_enoent,
84 apr_pool_t *scratch_pool);
85
86 /* ------------------------------------------------------------------------ */
87 /* OP_REMOVE_BASE */
88
89 /* Removes a BASE_NODE and all it's data, leaving any adds and copies as is.
90 Do this as a depth first traversal to make sure than any parent still exists
91 on error conditions.
92 */
93
94 /* Process the OP_REMOVE_BASE work item WORK_ITEM.
95 * See svn_wc__wq_build_remove_base() which generates this work item.
96 * Implements (struct work_item_dispatch).func. */
97 static svn_error_t *
run_base_remove(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)98 run_base_remove(work_item_baton_t *wqb,
99 svn_wc__db_t *db,
100 const svn_skel_t *work_item,
101 const char *wri_abspath,
102 svn_cancel_func_t cancel_func,
103 void *cancel_baton,
104 apr_pool_t *scratch_pool)
105 {
106 const svn_skel_t *arg1 = work_item->children->next;
107 const char *local_relpath;
108 const char *local_abspath;
109 svn_revnum_t not_present_rev = SVN_INVALID_REVNUM;
110 apr_int64_t val;
111
112 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
113 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
114 local_relpath, scratch_pool, scratch_pool));
115 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
116
117 if (arg1->next->next)
118 {
119 not_present_rev = (svn_revnum_t)val;
120
121 SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
122 }
123 else
124 {
125 svn_boolean_t keep_not_present;
126
127 SVN_ERR_ASSERT(SVN_WC__VERSION <= 28); /* Case unused in later versions*/
128
129 keep_not_present = (val != 0);
130
131 if (keep_not_present)
132 {
133 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL,
134 ¬_present_rev, NULL,
135 NULL, NULL, NULL,
136 NULL, NULL, NULL, NULL, NULL, NULL,
137 NULL, NULL, NULL,
138 db, local_abspath,
139 scratch_pool, scratch_pool));
140 }
141 }
142
143 SVN_ERR(svn_wc__db_base_remove(db, local_abspath,
144 FALSE /* keep_as_working */,
145 TRUE /* queue_deletes */,
146 FALSE /* remove_locks */,
147 not_present_rev,
148 NULL, NULL, scratch_pool));
149
150 return SVN_NO_ERROR;
151 }
152
153 /* ------------------------------------------------------------------------ */
154
155 /* ------------------------------------------------------------------------ */
156
157 /* OP_FILE_COMMIT */
158
159
160 /* FILE_ABSPATH is the new text base of the newly-committed versioned file,
161 * in repository-normal form (aka "detranslated" form). Adjust the working
162 * file accordingly.
163 *
164 * If eol and/or keyword translation would cause the working file to
165 * change, then overwrite the working file with a translated copy of
166 * the new text base (but only if the translated copy differs from the
167 * current working file -- if they are the same, do nothing, to avoid
168 * clobbering timestamps unnecessarily).
169 *
170 * Set the working file's executability according to its svn:executable
171 * property.
172 *
173 * Set the working file's read-only attribute according to its properties
174 * and lock status (see svn_wc__maybe_set_read_only()).
175 *
176 * If the working file was re-translated or had its executability or
177 * read-only state changed,
178 * then set OVERWROTE_WORKING to TRUE. If the working file isn't
179 * touched at all, then set to FALSE.
180 *
181 * Use SCRATCH_POOL for any temporary allocation.
182 */
183 static svn_error_t *
install_committed_file(svn_boolean_t * overwrote_working,svn_wc__db_t * db,const char * file_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)184 install_committed_file(svn_boolean_t *overwrote_working,
185 svn_wc__db_t *db,
186 const char *file_abspath,
187 svn_cancel_func_t cancel_func,
188 void *cancel_baton,
189 apr_pool_t *scratch_pool)
190 {
191 svn_boolean_t same;
192 const char *tmp_wfile;
193 svn_boolean_t special;
194
195 /* start off assuming that the working file isn't touched. */
196 *overwrote_working = FALSE;
197
198 /* In the commit, newlines and keywords may have been
199 * canonicalized and/or contracted... Or they may not have
200 * been. It's kind of hard to know. Here's how we find out:
201 *
202 * 1. Make a translated tmp copy of the committed text base,
203 * translated according to the versioned file's properties.
204 * Or, if no committed text base exists (the commit must have
205 * been a propchange only), make a translated tmp copy of the
206 * working file.
207 * 2. Compare the translated tmpfile to the working file.
208 * 3. If different, copy the tmpfile over working file.
209 *
210 * This means we only rewrite the working file if we absolutely
211 * have to, which is good because it avoids changing the file's
212 * timestamp unless necessary, so editors aren't tempted to
213 * reread the file if they don't really need to.
214 */
215
216 /* Copy and translate the new base-to-be file (if found, else the working
217 * file) from repository-normal form to working form, writing a new
218 * temporary file if any translation was actually done. Set TMP_WFILE to
219 * the translated file's path, which may be the source file's path if no
220 * translation was done. Set SAME to indicate whether the new working
221 * text is the same as the old working text (or TRUE if it's a special
222 * file). */
223 {
224 const char *tmp = file_abspath;
225
226 /* Copy and translate, if necessary. The output file will be deleted at
227 * scratch_pool cleanup.
228 * ### That's not quite safe: we might rename the file and then maybe
229 * its path will get re-used for another temp file before pool clean-up.
230 * Instead, we should take responsibility for deleting it. */
231 SVN_ERR(svn_wc__internal_translated_file(&tmp_wfile, tmp, db,
232 file_abspath,
233 SVN_WC_TRANSLATE_FROM_NF,
234 cancel_func, cancel_baton,
235 scratch_pool, scratch_pool));
236
237 /* If the translation is a no-op, the text base and the working copy
238 * file contain the same content, because we use the same props here
239 * as were used to detranslate from working file to text base.
240 *
241 * In that case: don't replace the working file, but make sure
242 * it has the right executable and read_write attributes set.
243 */
244
245 SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
246 NULL,
247 &special,
248 db, file_abspath, NULL, FALSE,
249 scratch_pool, scratch_pool));
250 /* Translated file returns the exact pointer if not translated. */
251 if (! special && tmp != tmp_wfile)
252 SVN_ERR(svn_io_files_contents_same_p(&same, tmp_wfile,
253 file_abspath, scratch_pool));
254 else
255 same = TRUE;
256 }
257
258 if (! same)
259 {
260 SVN_ERR(svn_io_file_rename(tmp_wfile, file_abspath, scratch_pool));
261 *overwrote_working = TRUE;
262 }
263
264 /* ### should be using OP_SYNC_FILE_FLAGS, or an internal version of
265 ### that here. do we need to set *OVERWROTE_WORKING? */
266
267 /* ### Re: OVERWROTE_WORKING, the following function is rather liberal
268 ### with setting that flag, so we should probably decide if we really
269 ### care about it when syncing flags. */
270 SVN_ERR(svn_wc__sync_flags_with_props(overwrote_working, db, file_abspath,
271 scratch_pool));
272
273 return SVN_NO_ERROR;
274 }
275
276 static svn_error_t *
process_commit_file_install(svn_wc__db_t * db,const char * local_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)277 process_commit_file_install(svn_wc__db_t *db,
278 const char *local_abspath,
279 svn_cancel_func_t cancel_func,
280 void *cancel_baton,
281 apr_pool_t *scratch_pool)
282 {
283 svn_boolean_t overwrote_working;
284
285 /* Install the new file, which may involve expanding keywords.
286 A copy of this file should have been dropped into our `tmp/text-base'
287 directory during the commit process. Part of this process
288 involves recording the textual timestamp for this entry. We'd like
289 to just use the timestamp of the working file, but it is possible
290 that at some point during the commit, the real working file might
291 have changed again.
292 */
293
294 SVN_ERR(install_committed_file(&overwrote_working, db,
295 local_abspath,
296 cancel_func, cancel_baton,
297 scratch_pool));
298
299 /* We will compute and modify the size and timestamp */
300 if (overwrote_working)
301 {
302 apr_finfo_t finfo;
303
304 SVN_ERR(svn_io_stat(&finfo, local_abspath,
305 APR_FINFO_MIN | APR_FINFO_LINK, scratch_pool));
306 SVN_ERR(svn_wc__db_global_record_fileinfo(db, local_abspath,
307 finfo.size, finfo.mtime,
308 scratch_pool));
309 }
310 else
311 {
312 svn_boolean_t modified;
313
314 /* The working copy file hasn't been overwritten. We just
315 removed the recorded size and modification time from the nodes
316 record by calling svn_wc__db_global_commit().
317
318 Now we have some file in our working copy that might be what
319 we just committed, but we are not certain at this point.
320
321 We still have a write lock here, so we check if the file is
322 what we expect it to be and if it is the right file we update
323 the recorded information. (If it isn't we keep the null data).
324
325 Instead of reimplementing all this here, we just call a function
326 that already does implement this when it notices that we have the
327 right kind of lock (and we ignore the result)
328 */
329 SVN_ERR(svn_wc__internal_file_modified_p(&modified,
330 db, local_abspath, FALSE,
331 scratch_pool));
332 }
333 return SVN_NO_ERROR;
334 }
335
336
337 static svn_error_t *
run_file_commit(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)338 run_file_commit(work_item_baton_t *wqb,
339 svn_wc__db_t *db,
340 const svn_skel_t *work_item,
341 const char *wri_abspath,
342 svn_cancel_func_t cancel_func,
343 void *cancel_baton,
344 apr_pool_t *scratch_pool)
345 {
346 const svn_skel_t *arg1 = work_item->children->next;
347 const char *local_relpath;
348 const char *local_abspath;
349
350 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
351 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
352 local_relpath, scratch_pool, scratch_pool));
353
354 /* We don't both parsing the other two values in the skel. */
355
356 return svn_error_trace(
357 process_commit_file_install(db, local_abspath,
358 cancel_func, cancel_baton,
359 scratch_pool));
360 }
361
362 svn_error_t *
svn_wc__wq_build_file_commit(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t props_mod,apr_pool_t * result_pool,apr_pool_t * scratch_pool)363 svn_wc__wq_build_file_commit(svn_skel_t **work_item,
364 svn_wc__db_t *db,
365 const char *local_abspath,
366 svn_boolean_t props_mod,
367 apr_pool_t *result_pool,
368 apr_pool_t *scratch_pool)
369 {
370 const char *local_relpath;
371 *work_item = svn_skel__make_empty_list(result_pool);
372
373 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
374 local_abspath, result_pool, scratch_pool));
375
376 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
377
378 svn_skel__prepend_str(OP_FILE_COMMIT, *work_item, result_pool);
379
380 return SVN_NO_ERROR;
381 }
382
383 /* ------------------------------------------------------------------------ */
384 /* OP_POSTUPGRADE */
385
386 static svn_error_t *
run_postupgrade(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)387 run_postupgrade(work_item_baton_t *wqb,
388 svn_wc__db_t *db,
389 const svn_skel_t *work_item,
390 const char *wri_abspath,
391 svn_cancel_func_t cancel_func,
392 void *cancel_baton,
393 apr_pool_t *scratch_pool)
394 {
395 const char *entries_path;
396 const char *format_path;
397 const char *wcroot_abspath;
398 const char *adm_path;
399 const char *temp_path;
400 svn_error_t *err;
401
402 err = svn_wc__wipe_postupgrade(wri_abspath, FALSE,
403 cancel_func, cancel_baton, scratch_pool);
404 if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
405 /* No entry, this can happen when the wq item is rerun. */
406 svn_error_clear(err);
407 else
408 SVN_ERR(err);
409
410 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath,
411 scratch_pool, scratch_pool));
412
413 adm_path = svn_wc__adm_child(wcroot_abspath, NULL, scratch_pool);
414 entries_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_ENTRIES,
415 scratch_pool);
416 format_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_FORMAT,
417 scratch_pool);
418
419 /* Write the 'format' and 'entries' files.
420
421 ### The order may matter for some sufficiently old clients.. but
422 ### this code only runs during upgrade after the files had been
423 ### removed earlier during the upgrade. */
424 SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING,
425 sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
426 svn_io_file_del_none, scratch_pool));
427 SVN_ERR(svn_io_file_rename(temp_path, format_path, scratch_pool));
428
429 SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING,
430 sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
431 svn_io_file_del_none, scratch_pool));
432 SVN_ERR(svn_io_file_rename(temp_path, entries_path, scratch_pool));
433
434 return SVN_NO_ERROR;
435 }
436
437 svn_error_t *
svn_wc__wq_build_postupgrade(svn_skel_t ** work_item,apr_pool_t * result_pool)438 svn_wc__wq_build_postupgrade(svn_skel_t **work_item,
439 apr_pool_t *result_pool)
440 {
441 *work_item = svn_skel__make_empty_list(result_pool);
442
443 svn_skel__prepend_str(OP_POSTUPGRADE, *work_item, result_pool);
444
445 return SVN_NO_ERROR;
446 }
447
448 /* ------------------------------------------------------------------------ */
449
450 /* OP_FILE_INSTALL */
451
452 /* Process the OP_FILE_INSTALL work item WORK_ITEM.
453 * See svn_wc__wq_build_file_install() which generates this work item.
454 * Implements (struct work_item_dispatch).func. */
455 static svn_error_t *
run_file_install(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)456 run_file_install(work_item_baton_t *wqb,
457 svn_wc__db_t *db,
458 const svn_skel_t *work_item,
459 const char *wri_abspath,
460 svn_cancel_func_t cancel_func,
461 void *cancel_baton,
462 apr_pool_t *scratch_pool)
463 {
464 const svn_skel_t *arg1 = work_item->children->next;
465 const svn_skel_t *arg4 = arg1->next->next->next;
466 const char *local_relpath;
467 const char *local_abspath;
468 svn_boolean_t use_commit_times;
469 svn_boolean_t record_fileinfo;
470 svn_boolean_t special;
471 svn_stream_t *src_stream;
472 svn_subst_eol_style_t style;
473 const char *eol;
474 apr_hash_t *keywords;
475 const char *temp_dir_abspath;
476 svn_stream_t *dst_stream;
477 const char *dst_abspath;
478 apr_int64_t val;
479 const char *wcroot_abspath;
480 const char *source_abspath;
481 const svn_checksum_t *checksum;
482 apr_hash_t *props;
483 apr_time_t changed_date;
484
485 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
486 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
487 local_relpath, scratch_pool, scratch_pool));
488
489 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
490 use_commit_times = (val != 0);
491 SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
492 record_fileinfo = (val != 0);
493
494 SVN_ERR(svn_wc__db_read_node_install_info(&wcroot_abspath,
495 &checksum, &props,
496 &changed_date,
497 db, local_abspath, wri_abspath,
498 scratch_pool, scratch_pool));
499
500 if (arg4 != NULL)
501 {
502 /* Use the provided path for the source. */
503 local_relpath = apr_pstrmemdup(scratch_pool, arg4->data, arg4->len);
504 SVN_ERR(svn_wc__db_from_relpath(&source_abspath, db, wri_abspath,
505 local_relpath,
506 scratch_pool, scratch_pool));
507 }
508 else if (! checksum)
509 {
510 /* This error replaces a previous assertion. Reporting an error from here
511 leaves the workingqueue operation in place, so the working copy is
512 still broken!
513
514 But when we report this error the user at least knows what node has
515 this specific problem, so maybe we can find out why users see this
516 error */
517 return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
518 _("Can't install '%s' from pristine store, "
519 "because no checksum is recorded for this "
520 "file"),
521 svn_dirent_local_style(local_abspath,
522 scratch_pool));
523 }
524 else
525 {
526 SVN_ERR(svn_wc__db_pristine_get_future_path(&source_abspath,
527 wcroot_abspath,
528 checksum,
529 scratch_pool, scratch_pool));
530 }
531
532 SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath,
533 scratch_pool, scratch_pool));
534
535 /* Fetch all the translation bits. */
536 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
537 &keywords,
538 &special, db, local_abspath,
539 props, FALSE,
540 scratch_pool, scratch_pool));
541 if (special)
542 {
543 /* When this stream is closed, the resulting special file will
544 atomically be created/moved into place at LOCAL_ABSPATH. */
545 SVN_ERR(svn_subst_create_specialfile(&dst_stream, local_abspath,
546 scratch_pool, scratch_pool));
547
548 /* Copy the "repository normal" form of the special file into the
549 special stream. */
550 SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
551 cancel_func, cancel_baton,
552 scratch_pool));
553
554 /* No need to set exec or read-only flags on special files. */
555
556 /* ### Shouldn't this record a timestamp and size, etc.? */
557 return SVN_NO_ERROR;
558 }
559
560 if (svn_subst_translation_required(style, eol, keywords,
561 FALSE /* special */,
562 TRUE /* force_eol_check */))
563 {
564 /* Wrap it in a translating (expanding) stream. */
565 src_stream = svn_subst_stream_translated(src_stream, eol,
566 TRUE /* repair */,
567 keywords,
568 TRUE /* expand */,
569 scratch_pool);
570 }
571
572 /* Where is the Right Place to put a temp file in this working copy? */
573 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath,
574 db, wcroot_abspath,
575 scratch_pool, scratch_pool));
576
577 /* Translate to a temporary file. We don't want the user seeing a partial
578 file, nor let them muck with it while we translate. We may also need to
579 get its TRANSLATED_SIZE before the user can monkey it. */
580 SVN_ERR(svn_stream_open_unique(&dst_stream, &dst_abspath,
581 temp_dir_abspath,
582 svn_io_file_del_none,
583 scratch_pool, scratch_pool));
584
585 /* Copy from the source to the dest, translating as we go. This will also
586 close both streams. */
587 SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
588 cancel_func, cancel_baton,
589 scratch_pool));
590
591 /* All done. Move the file into place. */
592
593 {
594 svn_error_t *err;
595
596 err = svn_io_file_rename(dst_abspath, local_abspath, scratch_pool);
597
598 /* With a single db we might want to install files in a missing directory.
599 Simply trying this scenario on error won't do any harm and at least
600 one user reported this problem on IRC. */
601 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
602 {
603 svn_error_t *err2;
604
605 err2 = svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath,
606 scratch_pool),
607 scratch_pool);
608
609 if (err2)
610 /* Creating directory didn't work: Return all errors */
611 return svn_error_trace(svn_error_compose_create(err, err2));
612 else
613 /* We could create a directory: retry install */
614 svn_error_clear(err);
615
616 SVN_ERR(svn_io_file_rename(dst_abspath, local_abspath, scratch_pool));
617 }
618 else
619 SVN_ERR(err);
620 }
621
622 /* Tweak the on-disk file according to its properties. */
623 #ifndef WIN32
624 if (props && svn_hash_gets(props, SVN_PROP_EXECUTABLE))
625 SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE,
626 scratch_pool));
627 #endif
628
629 /* Note that this explicitly checks the pristine properties, to make sure
630 that when the lock is locally set (=modification) it is not read only */
631 if (props && svn_hash_gets(props, SVN_PROP_NEEDS_LOCK))
632 {
633 svn_wc__db_status_t status;
634 svn_wc__db_lock_t *lock;
635 SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
636 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
637 NULL, NULL, &lock, NULL, NULL, NULL, NULL,
638 NULL, NULL, NULL, NULL, NULL, NULL,
639 db, local_abspath,
640 scratch_pool, scratch_pool));
641
642 if (!lock && status != svn_wc__db_status_added)
643 SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
644 }
645
646 if (use_commit_times)
647 {
648 if (changed_date)
649 SVN_ERR(svn_io_set_file_affected_time(changed_date,
650 local_abspath,
651 scratch_pool));
652 }
653
654 /* ### this should happen before we rename the file into place. */
655 if (record_fileinfo)
656 {
657 SVN_ERR(get_and_record_fileinfo(wqb, local_abspath,
658 FALSE /* ignore_enoent */,
659 scratch_pool));
660 }
661
662 return SVN_NO_ERROR;
663 }
664
665
666 svn_error_t *
svn_wc__wq_build_file_install(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,const char * source_abspath,svn_boolean_t use_commit_times,svn_boolean_t record_fileinfo,apr_pool_t * result_pool,apr_pool_t * scratch_pool)667 svn_wc__wq_build_file_install(svn_skel_t **work_item,
668 svn_wc__db_t *db,
669 const char *local_abspath,
670 const char *source_abspath,
671 svn_boolean_t use_commit_times,
672 svn_boolean_t record_fileinfo,
673 apr_pool_t *result_pool,
674 apr_pool_t *scratch_pool)
675 {
676 const char *local_relpath;
677 const char *wri_abspath;
678 *work_item = svn_skel__make_empty_list(result_pool);
679
680 /* Use the directory of the file to install as wri_abspath to avoid
681 filestats on just obtaining the wc-root */
682 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
683
684 /* If a SOURCE_ABSPATH was provided, then put it into the skel. If this
685 value is not provided, then the file's pristine contents will be used. */
686 if (source_abspath != NULL)
687 {
688 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
689 source_abspath,
690 result_pool, scratch_pool));
691
692 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
693 }
694
695 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
696 local_abspath, result_pool, scratch_pool));
697
698 svn_skel__prepend_int(record_fileinfo, *work_item, result_pool);
699 svn_skel__prepend_int(use_commit_times, *work_item, result_pool);
700 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
701 svn_skel__prepend_str(OP_FILE_INSTALL, *work_item, result_pool);
702
703 return SVN_NO_ERROR;
704 }
705
706
707 /* ------------------------------------------------------------------------ */
708
709 /* OP_FILE_REMOVE */
710
711 /* Process the OP_FILE_REMOVE work item WORK_ITEM.
712 * See svn_wc__wq_build_file_remove() which generates this work item.
713 * Implements (struct work_item_dispatch).func. */
714 static svn_error_t *
run_file_remove(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)715 run_file_remove(work_item_baton_t *wqb,
716 svn_wc__db_t *db,
717 const svn_skel_t *work_item,
718 const char *wri_abspath,
719 svn_cancel_func_t cancel_func,
720 void *cancel_baton,
721 apr_pool_t *scratch_pool)
722 {
723 const svn_skel_t *arg1 = work_item->children->next;
724 const char *local_relpath;
725 const char *local_abspath;
726
727 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
728 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
729 local_relpath, scratch_pool, scratch_pool));
730
731 /* Remove the path, no worrying if it isn't there. */
732 return svn_error_trace(svn_io_remove_file2(local_abspath, TRUE,
733 scratch_pool));
734 }
735
736
737 svn_error_t *
svn_wc__wq_build_file_remove(svn_skel_t ** work_item,svn_wc__db_t * db,const char * wri_abspath,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)738 svn_wc__wq_build_file_remove(svn_skel_t **work_item,
739 svn_wc__db_t *db,
740 const char *wri_abspath,
741 const char *local_abspath,
742 apr_pool_t *result_pool,
743 apr_pool_t *scratch_pool)
744 {
745 const char *local_relpath;
746 *work_item = svn_skel__make_empty_list(result_pool);
747
748 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
749 local_abspath, result_pool, scratch_pool));
750
751 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
752 svn_skel__prepend_str(OP_FILE_REMOVE, *work_item, result_pool);
753
754 return SVN_NO_ERROR;
755 }
756
757 /* ------------------------------------------------------------------------ */
758
759 /* OP_DIRECTORY_REMOVE */
760
761 /* Process the OP_FILE_REMOVE work item WORK_ITEM.
762 * See svn_wc__wq_build_file_remove() which generates this work item.
763 * Implements (struct work_item_dispatch).func. */
764 static svn_error_t *
run_dir_remove(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)765 run_dir_remove(work_item_baton_t *wqb,
766 svn_wc__db_t *db,
767 const svn_skel_t *work_item,
768 const char *wri_abspath,
769 svn_cancel_func_t cancel_func,
770 void *cancel_baton,
771 apr_pool_t *scratch_pool)
772 {
773 const svn_skel_t *arg1 = work_item->children->next;
774 const char *local_relpath;
775 const char *local_abspath;
776 svn_boolean_t recursive;
777
778 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
779 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
780 local_relpath, scratch_pool, scratch_pool));
781
782 recursive = FALSE;
783 if (arg1->next)
784 {
785 apr_int64_t val;
786 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
787
788 recursive = (val != 0);
789 }
790
791 /* Remove the path, no worrying if it isn't there. */
792 if (recursive)
793 return svn_error_trace(
794 svn_io_remove_dir2(local_abspath, TRUE,
795 cancel_func, cancel_baton,
796 scratch_pool));
797 else
798 {
799 svn_error_t *err;
800
801 err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool);
802
803 if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
804 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)
805 || APR_STATUS_IS_ENOTEMPTY(err->apr_err)))
806 {
807 svn_error_clear(err);
808 err = NULL;
809 }
810
811 return svn_error_trace(err);
812 }
813 }
814
815 svn_error_t *
svn_wc__wq_build_dir_remove(svn_skel_t ** work_item,svn_wc__db_t * db,const char * wri_abspath,const char * local_abspath,svn_boolean_t recursive,apr_pool_t * result_pool,apr_pool_t * scratch_pool)816 svn_wc__wq_build_dir_remove(svn_skel_t **work_item,
817 svn_wc__db_t *db,
818 const char *wri_abspath,
819 const char *local_abspath,
820 svn_boolean_t recursive,
821 apr_pool_t *result_pool,
822 apr_pool_t *scratch_pool)
823 {
824 const char *local_relpath;
825 *work_item = svn_skel__make_empty_list(result_pool);
826
827 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
828 local_abspath, result_pool, scratch_pool));
829
830 if (recursive)
831 svn_skel__prepend_int(TRUE, *work_item, result_pool);
832
833 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
834 svn_skel__prepend_str(OP_DIRECTORY_REMOVE, *work_item, result_pool);
835
836 return SVN_NO_ERROR;
837 }
838
839 /* ------------------------------------------------------------------------ */
840
841 /* OP_FILE_MOVE */
842
843 /* Process the OP_FILE_MOVE work item WORK_ITEM.
844 * See svn_wc__wq_build_file_move() which generates this work item.
845 * Implements (struct work_item_dispatch).func. */
846 static svn_error_t *
run_file_move(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)847 run_file_move(work_item_baton_t *wqb,
848 svn_wc__db_t *db,
849 const svn_skel_t *work_item,
850 const char *wri_abspath,
851 svn_cancel_func_t cancel_func,
852 void *cancel_baton,
853 apr_pool_t *scratch_pool)
854 {
855 const svn_skel_t *arg1 = work_item->children->next;
856 const char *src_abspath, *dst_abspath;
857 const char *local_relpath;
858 svn_error_t *err;
859
860 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
861 SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath, local_relpath,
862 scratch_pool, scratch_pool));
863 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
864 arg1->next->len);
865 SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath, local_relpath,
866 scratch_pool, scratch_pool));
867
868 /* Use svn_io_file_move() instead of svn_io_file_rename() to allow cross
869 device copies. We should not fail in the workqueue. */
870
871 err = svn_io_file_move(src_abspath, dst_abspath, scratch_pool);
872
873 /* If the source is not found, we assume the wq op is already handled */
874 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
875 svn_error_clear(err);
876 else
877 SVN_ERR(err);
878
879 return SVN_NO_ERROR;
880 }
881
882
883 svn_error_t *
svn_wc__wq_build_file_move(svn_skel_t ** work_item,svn_wc__db_t * db,const char * wri_abspath,const char * src_abspath,const char * dst_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)884 svn_wc__wq_build_file_move(svn_skel_t **work_item,
885 svn_wc__db_t *db,
886 const char *wri_abspath,
887 const char *src_abspath,
888 const char *dst_abspath,
889 apr_pool_t *result_pool,
890 apr_pool_t *scratch_pool)
891 {
892 svn_node_kind_t kind;
893 const char *local_relpath;
894 *work_item = svn_skel__make_empty_list(result_pool);
895
896 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
897 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
898 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
899
900 /* File must exist */
901 SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
902
903 if (kind == svn_node_none)
904 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
905 _("'%s' not found"),
906 svn_dirent_local_style(src_abspath,
907 scratch_pool));
908
909 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, dst_abspath,
910 result_pool, scratch_pool));
911 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
912
913 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, src_abspath,
914 result_pool, scratch_pool));
915 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
916
917 svn_skel__prepend_str(OP_FILE_MOVE, *work_item, result_pool);
918
919 return SVN_NO_ERROR;
920 }
921
922 /* ------------------------------------------------------------------------ */
923
924 /* OP_FILE_COPY_TRANSLATED */
925
926 /* Process the OP_FILE_COPY_TRANSLATED work item WORK_ITEM.
927 * See run_file_copy_translated() which generates this work item.
928 * Implements (struct work_item_dispatch).func. */
929 static svn_error_t *
run_file_copy_translated(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)930 run_file_copy_translated(work_item_baton_t *wqb,
931 svn_wc__db_t *db,
932 const svn_skel_t *work_item,
933 const char *wri_abspath,
934 svn_cancel_func_t cancel_func,
935 void *cancel_baton,
936 apr_pool_t *scratch_pool)
937 {
938 const svn_skel_t *arg1 = work_item->children->next;
939 const char *local_abspath, *src_abspath, *dst_abspath;
940 const char *local_relpath;
941 svn_subst_eol_style_t style;
942 const char *eol;
943 apr_hash_t *keywords;
944 svn_boolean_t special;
945
946 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
947 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
948 local_relpath, scratch_pool, scratch_pool));
949
950 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
951 arg1->next->len);
952 SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath,
953 local_relpath, scratch_pool, scratch_pool));
954
955 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->next->data,
956 arg1->next->next->len);
957 SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath,
958 local_relpath, scratch_pool, scratch_pool));
959
960 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
961 &keywords,
962 &special,
963 db, local_abspath, NULL, FALSE,
964 scratch_pool, scratch_pool));
965
966 SVN_ERR(svn_subst_copy_and_translate4(src_abspath, dst_abspath,
967 eol, TRUE /* repair */,
968 keywords, TRUE /* expand */,
969 special,
970 cancel_func, cancel_baton,
971 scratch_pool));
972 return SVN_NO_ERROR;
973 }
974
975
976 svn_error_t *
svn_wc__wq_build_file_copy_translated(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,const char * src_abspath,const char * dst_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)977 svn_wc__wq_build_file_copy_translated(svn_skel_t **work_item,
978 svn_wc__db_t *db,
979 const char *local_abspath,
980 const char *src_abspath,
981 const char *dst_abspath,
982 apr_pool_t *result_pool,
983 apr_pool_t *scratch_pool)
984 {
985 svn_node_kind_t kind;
986 const char *local_relpath;
987
988 *work_item = svn_skel__make_empty_list(result_pool);
989
990 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
991 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
992 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
993
994 /* File must exist */
995 SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
996
997 if (kind == svn_node_none)
998 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
999 _("'%s' not found"),
1000 svn_dirent_local_style(src_abspath,
1001 scratch_pool));
1002
1003 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, dst_abspath,
1004 result_pool, scratch_pool));
1005 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1006
1007 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, src_abspath,
1008 result_pool, scratch_pool));
1009 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1010
1011 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1012 local_abspath, result_pool, scratch_pool));
1013 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1014
1015 svn_skel__prepend_str(OP_FILE_COPY_TRANSLATED, *work_item, result_pool);
1016
1017 return SVN_NO_ERROR;
1018 }
1019
1020 /* ------------------------------------------------------------------------ */
1021
1022 /* OP_DIRECTORY_INSTALL */
1023
1024 static svn_error_t *
run_dir_install(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1025 run_dir_install(work_item_baton_t *wqb,
1026 svn_wc__db_t *db,
1027 const svn_skel_t *work_item,
1028 const char *wri_abspath,
1029 svn_cancel_func_t cancel_func,
1030 void *cancel_baton,
1031 apr_pool_t *scratch_pool)
1032 {
1033 const svn_skel_t *arg1 = work_item->children->next;
1034 const char *local_relpath;
1035 const char *local_abspath;
1036
1037 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1038 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1039 local_relpath, scratch_pool, scratch_pool));
1040
1041 SVN_ERR(svn_wc__ensure_directory(local_abspath, scratch_pool));
1042
1043 return SVN_NO_ERROR;
1044 }
1045
1046 svn_error_t *
svn_wc__wq_build_dir_install(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool,apr_pool_t * result_pool)1047 svn_wc__wq_build_dir_install(svn_skel_t **work_item,
1048 svn_wc__db_t *db,
1049 const char *local_abspath,
1050 apr_pool_t *scratch_pool,
1051 apr_pool_t *result_pool)
1052 {
1053 const char *local_relpath;
1054
1055 *work_item = svn_skel__make_empty_list(result_pool);
1056
1057 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1058 local_abspath, result_pool, scratch_pool));
1059 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1060
1061 svn_skel__prepend_str(OP_DIRECTORY_INSTALL, *work_item, result_pool);
1062
1063 return SVN_NO_ERROR;
1064 }
1065
1066
1067 /* ------------------------------------------------------------------------ */
1068
1069 /* OP_SYNC_FILE_FLAGS */
1070
1071 /* Process the OP_SYNC_FILE_FLAGS work item WORK_ITEM.
1072 * See svn_wc__wq_build_sync_file_flags() which generates this work item.
1073 * Implements (struct work_item_dispatch).func. */
1074 static svn_error_t *
run_sync_file_flags(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1075 run_sync_file_flags(work_item_baton_t *wqb,
1076 svn_wc__db_t *db,
1077 const svn_skel_t *work_item,
1078 const char *wri_abspath,
1079 svn_cancel_func_t cancel_func,
1080 void *cancel_baton,
1081 apr_pool_t *scratch_pool)
1082 {
1083 const svn_skel_t *arg1 = work_item->children->next;
1084 const char *local_relpath;
1085 const char *local_abspath;
1086
1087 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1088 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1089 local_relpath, scratch_pool, scratch_pool));
1090
1091 return svn_error_trace(svn_wc__sync_flags_with_props(NULL, db,
1092 local_abspath, scratch_pool));
1093 }
1094
1095
1096 svn_error_t *
svn_wc__wq_build_sync_file_flags(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1097 svn_wc__wq_build_sync_file_flags(svn_skel_t **work_item,
1098 svn_wc__db_t *db,
1099 const char *local_abspath,
1100 apr_pool_t *result_pool,
1101 apr_pool_t *scratch_pool)
1102 {
1103 const char *local_relpath;
1104 *work_item = svn_skel__make_empty_list(result_pool);
1105
1106 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1107 local_abspath, result_pool, scratch_pool));
1108
1109 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1110 svn_skel__prepend_str(OP_SYNC_FILE_FLAGS, *work_item, result_pool);
1111
1112 return SVN_NO_ERROR;
1113 }
1114
1115
1116 /* ------------------------------------------------------------------------ */
1117
1118 /* OP_PREJ_INSTALL */
1119
1120 static svn_error_t *
run_prej_install(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1121 run_prej_install(work_item_baton_t *wqb,
1122 svn_wc__db_t *db,
1123 const svn_skel_t *work_item,
1124 const char *wri_abspath,
1125 svn_cancel_func_t cancel_func,
1126 void *cancel_baton,
1127 apr_pool_t *scratch_pool)
1128 {
1129 const svn_skel_t *arg1 = work_item->children->next;
1130 const char *local_relpath;
1131 const char *local_abspath;
1132 svn_skel_t *conflicts;
1133 const svn_skel_t *prop_conflict_skel;
1134 const char *tmp_prejfile_abspath;
1135 const char *prejfile_abspath;
1136
1137 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1138 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1139 local_relpath, scratch_pool, scratch_pool));
1140
1141 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
1142 scratch_pool, scratch_pool));
1143
1144 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prejfile_abspath,
1145 NULL, NULL, NULL, NULL,
1146 db, local_abspath, conflicts,
1147 scratch_pool, scratch_pool));
1148
1149 if (arg1->next != NULL)
1150 prop_conflict_skel = arg1->next;
1151 else
1152 SVN_ERR_MALFUNCTION(); /* ### wc_db can't provide it ... yet. */
1153
1154 /* Construct a property reject file in the temporary area. */
1155 SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath,
1156 db, local_abspath,
1157 prop_conflict_skel,
1158 scratch_pool, scratch_pool));
1159
1160 /* ... and atomically move it into place. */
1161 SVN_ERR(svn_io_file_rename(tmp_prejfile_abspath,
1162 prejfile_abspath,
1163 scratch_pool));
1164
1165 return SVN_NO_ERROR;
1166 }
1167
1168
1169 svn_error_t *
svn_wc__wq_build_prej_install(svn_skel_t ** work_item,svn_wc__db_t * db,const char * local_abspath,svn_skel_t * conflict_skel,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1170 svn_wc__wq_build_prej_install(svn_skel_t **work_item,
1171 svn_wc__db_t *db,
1172 const char *local_abspath,
1173 svn_skel_t *conflict_skel,
1174 apr_pool_t *result_pool,
1175 apr_pool_t *scratch_pool)
1176 {
1177 const char *local_relpath;
1178 *work_item = svn_skel__make_empty_list(result_pool);
1179
1180 /* ### gotta have this, today */
1181 SVN_ERR_ASSERT(conflict_skel != NULL);
1182
1183 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1184 local_abspath, result_pool, scratch_pool));
1185
1186 if (conflict_skel != NULL)
1187 svn_skel__prepend(conflict_skel, *work_item);
1188 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1189 svn_skel__prepend_str(OP_PREJ_INSTALL, *work_item, result_pool);
1190
1191 return SVN_NO_ERROR;
1192 }
1193
1194
1195 /* ------------------------------------------------------------------------ */
1196
1197 /* OP_RECORD_FILEINFO */
1198
1199
1200 static svn_error_t *
run_record_fileinfo(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1201 run_record_fileinfo(work_item_baton_t *wqb,
1202 svn_wc__db_t *db,
1203 const svn_skel_t *work_item,
1204 const char *wri_abspath,
1205 svn_cancel_func_t cancel_func,
1206 void *cancel_baton,
1207 apr_pool_t *scratch_pool)
1208 {
1209 const svn_skel_t *arg1 = work_item->children->next;
1210 const char *local_relpath;
1211 const char *local_abspath;
1212 apr_time_t set_time = 0;
1213
1214 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1215
1216 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1217 local_relpath, scratch_pool, scratch_pool));
1218
1219 if (arg1->next)
1220 {
1221 apr_int64_t val;
1222
1223 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
1224 set_time = (apr_time_t)val;
1225 }
1226
1227 if (set_time != 0)
1228 {
1229 svn_node_kind_t kind;
1230 svn_boolean_t is_special;
1231
1232 /* Do not set the timestamp on special files. */
1233 SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
1234 scratch_pool));
1235
1236 /* Don't set affected time when local_abspath does not exist or is
1237 a special file */
1238 if (kind == svn_node_file && !is_special)
1239 SVN_ERR(svn_io_set_file_affected_time(set_time, local_abspath,
1240 scratch_pool));
1241
1242 /* Note that we can't use the value we get here for recording as the
1243 filesystem might have a different timestamp granularity */
1244 }
1245
1246
1247 return svn_error_trace(get_and_record_fileinfo(wqb, local_abspath,
1248 TRUE /* ignore_enoent */,
1249 scratch_pool));
1250 }
1251
1252 /* ------------------------------------------------------------------------ */
1253
1254 /* OP_TMP_SET_TEXT_CONFLICT_MARKERS */
1255
1256
1257 static svn_error_t *
run_set_text_conflict_markers(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1258 run_set_text_conflict_markers(work_item_baton_t *wqb,
1259 svn_wc__db_t *db,
1260 const svn_skel_t *work_item,
1261 const char *wri_abspath,
1262 svn_cancel_func_t cancel_func,
1263 void *cancel_baton,
1264 apr_pool_t *scratch_pool)
1265 {
1266 const svn_skel_t *arg = work_item->children->next;
1267 const char *local_relpath;
1268 const char *local_abspath;
1269 const char *old_abspath = NULL;
1270 const char *new_abspath = NULL;
1271 const char *wrk_abspath = NULL;
1272
1273 local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1274 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1275 local_relpath, scratch_pool, scratch_pool));
1276
1277 arg = arg->next;
1278 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1279 : NULL;
1280
1281 if (local_relpath)
1282 {
1283 SVN_ERR(svn_wc__db_from_relpath(&old_abspath, db, wri_abspath,
1284 local_relpath,
1285 scratch_pool, scratch_pool));
1286 }
1287
1288 arg = arg->next;
1289 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1290 : NULL;
1291 if (local_relpath)
1292 {
1293 SVN_ERR(svn_wc__db_from_relpath(&new_abspath, db, wri_abspath,
1294 local_relpath,
1295 scratch_pool, scratch_pool));
1296 }
1297
1298 arg = arg->next;
1299 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1300 : NULL;
1301
1302 if (local_relpath)
1303 {
1304 SVN_ERR(svn_wc__db_from_relpath(&wrk_abspath, db, wri_abspath,
1305 local_relpath,
1306 scratch_pool, scratch_pool));
1307 }
1308
1309 /* Upgrade scenario: We have a workqueue item that describes how to install a
1310 non skel conflict. Fetch all the information we can to create a new style
1311 conflict. */
1312 /* ### Before format 30 this is/was a common code path as we didn't install
1313 ### the conflict directly in the db. It just calls the wc_db code
1314 ### to set the right fields. */
1315
1316 {
1317 /* Check if we should combine with a property conflict... */
1318 svn_skel_t *conflicts;
1319
1320 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
1321 scratch_pool, scratch_pool));
1322
1323 if (! conflicts)
1324 {
1325 /* No conflict exists, create a basic skel */
1326 conflicts = svn_wc__conflict_skel_create(scratch_pool);
1327
1328 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1329 scratch_pool,
1330 scratch_pool));
1331 }
1332
1333 /* Add the text conflict to the existing onflict */
1334 SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflicts, db,
1335 local_abspath,
1336 wrk_abspath,
1337 old_abspath,
1338 new_abspath,
1339 scratch_pool,
1340 scratch_pool));
1341
1342 SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1343 NULL, scratch_pool));
1344 }
1345 return SVN_NO_ERROR;
1346 }
1347
1348 /* ------------------------------------------------------------------------ */
1349
1350 /* OP_TMP_SET_PROPERTY_CONFLICT_MARKER */
1351
1352 static svn_error_t *
run_set_property_conflict_marker(work_item_baton_t * wqb,svn_wc__db_t * db,const svn_skel_t * work_item,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1353 run_set_property_conflict_marker(work_item_baton_t *wqb,
1354 svn_wc__db_t *db,
1355 const svn_skel_t *work_item,
1356 const char *wri_abspath,
1357 svn_cancel_func_t cancel_func,
1358 void *cancel_baton,
1359 apr_pool_t *scratch_pool)
1360 {
1361 const svn_skel_t *arg = work_item->children->next;
1362 const char *local_relpath;
1363 const char *local_abspath;
1364 const char *prej_abspath = NULL;
1365
1366 local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1367
1368 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1369 local_relpath, scratch_pool, scratch_pool));
1370
1371
1372 arg = arg->next;
1373 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1374 : NULL;
1375
1376 if (local_relpath)
1377 SVN_ERR(svn_wc__db_from_relpath(&prej_abspath, db, wri_abspath,
1378 local_relpath,
1379 scratch_pool, scratch_pool));
1380
1381 {
1382 /* Check if we should combine with a text conflict... */
1383 svn_skel_t *conflicts;
1384 apr_hash_t *prop_names;
1385
1386 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
1387 scratch_pool, scratch_pool));
1388
1389 if (! conflicts)
1390 {
1391 /* No conflict exists, create a basic skel */
1392 conflicts = svn_wc__conflict_skel_create(scratch_pool);
1393
1394 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1395 scratch_pool,
1396 scratch_pool));
1397 }
1398
1399 prop_names = apr_hash_make(scratch_pool);
1400 SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflicts, db,
1401 local_abspath,
1402 prej_abspath,
1403 NULL, NULL, NULL,
1404 prop_names,
1405 scratch_pool,
1406 scratch_pool));
1407
1408 SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1409 NULL, scratch_pool));
1410 }
1411 return SVN_NO_ERROR;
1412 }
1413
1414 /* ------------------------------------------------------------------------ */
1415
1416 static const struct work_item_dispatch dispatch_table[] = {
1417 { OP_FILE_COMMIT, run_file_commit },
1418 { OP_FILE_INSTALL, run_file_install },
1419 { OP_FILE_REMOVE, run_file_remove },
1420 { OP_FILE_MOVE, run_file_move },
1421 { OP_FILE_COPY_TRANSLATED, run_file_copy_translated },
1422 { OP_SYNC_FILE_FLAGS, run_sync_file_flags },
1423 { OP_PREJ_INSTALL, run_prej_install },
1424 { OP_DIRECTORY_REMOVE, run_dir_remove },
1425 { OP_DIRECTORY_INSTALL, run_dir_install },
1426
1427 /* Upgrade steps */
1428 { OP_POSTUPGRADE, run_postupgrade },
1429
1430 /* Legacy workqueue items. No longer created */
1431 { OP_BASE_REMOVE, run_base_remove },
1432 { OP_RECORD_FILEINFO, run_record_fileinfo },
1433 { OP_TMP_SET_TEXT_CONFLICT_MARKERS, run_set_text_conflict_markers },
1434 { OP_TMP_SET_PROPERTY_CONFLICT_MARKER, run_set_property_conflict_marker },
1435
1436 /* Sentinel. */
1437 { NULL }
1438 };
1439
1440 struct work_item_baton_t
1441 {
1442 apr_pool_t *result_pool; /* Pool to allocate result in */
1443
1444 svn_boolean_t used; /* needs reset */
1445
1446 apr_hash_t *record_map; /* const char * -> svn_io_dirent2_t map */
1447 };
1448
1449
1450 static svn_error_t *
dispatch_work_item(work_item_baton_t * wqb,svn_wc__db_t * db,const char * wri_abspath,const svn_skel_t * work_item,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1451 dispatch_work_item(work_item_baton_t *wqb,
1452 svn_wc__db_t *db,
1453 const char *wri_abspath,
1454 const svn_skel_t *work_item,
1455 svn_cancel_func_t cancel_func,
1456 void *cancel_baton,
1457 apr_pool_t *scratch_pool)
1458 {
1459 const struct work_item_dispatch *scan;
1460
1461 /* Scan the dispatch table for a function to handle this work item. */
1462 for (scan = &dispatch_table[0]; scan->name != NULL; ++scan)
1463 {
1464 if (svn_skel__matches_atom(work_item->children, scan->name))
1465 {
1466
1467 #ifdef SVN_DEBUG_WORK_QUEUE
1468 SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1469 #endif
1470 SVN_ERR((*scan->func)(wqb, db, work_item, wri_abspath,
1471 cancel_func, cancel_baton,
1472 scratch_pool));
1473
1474 #ifdef SVN_RUN_WORK_QUEUE_TWICE
1475 #ifdef SVN_DEBUG_WORK_QUEUE
1476 SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1477 #endif
1478 /* Being able to run every workqueue item twice is one
1479 requirement for workqueues to be restartable. */
1480 SVN_ERR((*scan->func)(db, work_item, wri_abspath,
1481 cancel_func, cancel_baton,
1482 scratch_pool));
1483 #endif
1484
1485 break;
1486 }
1487 }
1488
1489 if (scan->name == NULL)
1490 {
1491 /* We should know about ALL possible work items here. If we do not,
1492 then something is wrong. Most likely, some kind of format/code
1493 skew. There is nothing more we can do. Erasing or ignoring this
1494 work item could leave the WC in an even more broken state.
1495
1496 Contrary to issue #1581, we cannot simply remove work items and
1497 continue, so bail out with an error. */
1498 return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, NULL,
1499 _("Unrecognized work item in the queue"));
1500 }
1501
1502 return SVN_NO_ERROR;
1503 }
1504
1505
1506 svn_error_t *
svn_wc__wq_run(svn_wc__db_t * db,const char * wri_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)1507 svn_wc__wq_run(svn_wc__db_t *db,
1508 const char *wri_abspath,
1509 svn_cancel_func_t cancel_func,
1510 void *cancel_baton,
1511 apr_pool_t *scratch_pool)
1512 {
1513 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1514 apr_uint64_t last_id = 0;
1515 work_item_baton_t wib = { 0 };
1516 wib.result_pool = svn_pool_create(scratch_pool);
1517
1518 #ifdef SVN_DEBUG_WORK_QUEUE
1519 SVN_DBG(("wq_run: wri='%s'\n", wri_abspath));
1520 {
1521 static int count = 0;
1522 const char *count_env_var = getenv("SVN_DEBUG_WORK_QUEUE");
1523
1524 if (count_env_var && ++count == atoi(count_env_var))
1525 return svn_error_create(SVN_ERR_CANCELLED, NULL, "fake cancel");
1526 }
1527 #endif
1528
1529 while (TRUE)
1530 {
1531 apr_uint64_t id;
1532 svn_skel_t *work_item;
1533 svn_error_t *err;
1534
1535 svn_pool_clear(iterpool);
1536
1537 if (! wib.used)
1538 {
1539 /* Make sure to do this *early* in the loop iteration. There may
1540 be a LAST_ID that needs to be marked as completed, *before* we
1541 start worrying about anything else. */
1542 SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, db, wri_abspath,
1543 last_id, iterpool, iterpool));
1544 }
1545 else
1546 {
1547 /* Make sure to do this *early* in the loop iteration. There may
1548 be a LAST_ID that needs to be marked as completed, *before* we
1549 start worrying about anything else. */
1550 SVN_ERR(svn_wc__db_wq_record_and_fetch_next(&id, &work_item,
1551 db, wri_abspath,
1552 last_id, wib.record_map,
1553 iterpool,
1554 wib.result_pool));
1555
1556 svn_pool_clear(wib.result_pool);
1557 wib.record_map = NULL;
1558 wib.used = FALSE;
1559 }
1560
1561 /* Stop work queue processing, if requested. A future 'svn cleanup'
1562 should be able to continue the processing. Note that we may
1563 have WORK_ITEM, but we'll just skip its processing for now. */
1564 if (cancel_func)
1565 SVN_ERR(cancel_func(cancel_baton));
1566
1567 /* If we have a WORK_ITEM, then process the sucker. Otherwise,
1568 we're done. */
1569 if (work_item == NULL)
1570 break;
1571
1572 err = dispatch_work_item(&wib, db, wri_abspath, work_item,
1573 cancel_func, cancel_baton, iterpool);
1574 if (err)
1575 {
1576 const char *skel = svn_skel__unparse(work_item, scratch_pool)->data;
1577
1578 return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, err,
1579 _("Failed to run the WC DB work queue "
1580 "associated with '%s', work item %d %s"),
1581 svn_dirent_local_style(wri_abspath,
1582 scratch_pool),
1583 (int)id, skel);
1584 }
1585
1586 /* The work item finished without error. Mark it completed
1587 in the next loop. */
1588 last_id = id;
1589 }
1590
1591 svn_pool_destroy(iterpool);
1592 return SVN_NO_ERROR;
1593 }
1594
1595
1596 svn_skel_t *
svn_wc__wq_merge(svn_skel_t * work_item1,svn_skel_t * work_item2,apr_pool_t * result_pool)1597 svn_wc__wq_merge(svn_skel_t *work_item1,
1598 svn_skel_t *work_item2,
1599 apr_pool_t *result_pool)
1600 {
1601 /* If either argument is NULL, then just return the other. */
1602 if (work_item1 == NULL)
1603 return work_item2;
1604 if (work_item2 == NULL)
1605 return work_item1;
1606
1607 /* We have two items. Figure out how to join them. */
1608 if (SVN_WC__SINGLE_WORK_ITEM(work_item1))
1609 {
1610 if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1611 {
1612 /* Both are singular work items. Construct a list, then put
1613 both work items into it (in the proper order). */
1614
1615 svn_skel_t *result = svn_skel__make_empty_list(result_pool);
1616
1617 svn_skel__prepend(work_item2, result);
1618 svn_skel__prepend(work_item1, result);
1619 return result;
1620 }
1621
1622 /* WORK_ITEM2 is a list of work items. We can simply shove WORK_ITEM1
1623 in the front to keep the ordering. */
1624 svn_skel__prepend(work_item1, work_item2);
1625 return work_item2;
1626 }
1627 /* WORK_ITEM1 is a list of work items. */
1628
1629 if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1630 {
1631 /* Put WORK_ITEM2 onto the end of the WORK_ITEM1 list. */
1632 svn_skel__append(work_item1, work_item2);
1633 return work_item1;
1634 }
1635
1636 /* We have two lists of work items. We need to chain all of the work
1637 items into one big list. We will leave behind the WORK_ITEM2 skel,
1638 as we only want its children. */
1639 svn_skel__append(work_item1, work_item2->children);
1640 return work_item1;
1641 }
1642
1643
1644 static svn_error_t *
get_and_record_fileinfo(work_item_baton_t * wqb,const char * local_abspath,svn_boolean_t ignore_enoent,apr_pool_t * scratch_pool)1645 get_and_record_fileinfo(work_item_baton_t *wqb,
1646 const char *local_abspath,
1647 svn_boolean_t ignore_enoent,
1648 apr_pool_t *scratch_pool)
1649 {
1650 const svn_io_dirent2_t *dirent;
1651
1652 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, ignore_enoent,
1653 wqb->result_pool, scratch_pool));
1654
1655 if (dirent->kind != svn_node_file)
1656 return SVN_NO_ERROR;
1657
1658 wqb->used = TRUE;
1659
1660 if (! wqb->record_map)
1661 wqb->record_map = apr_hash_make(wqb->result_pool);
1662
1663 svn_hash_sets(wqb->record_map, apr_pstrdup(wqb->result_pool, local_abspath),
1664 dirent);
1665
1666 return SVN_NO_ERROR;
1667 }
1668