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