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