00001
00002
00003
00004
00005
00006
00007
00008
00009
00014 #include <errno.h>
00015 #include <time.h>
00016 #include <unistd.h>
00017
00018 #include <subversion-1/svn_ra.h>
00019 #include <subversion-1/svn_auth.h>
00020 #include <subversion-1/svn_client.h>
00021 #include <subversion-1/svn_cmdline.h>
00022
00023
00024
00025 #include "global.h"
00026 #include "helper.h"
00027 #include "update.h"
00028 #include "est_ops.h"
00029 #include "checksum.h"
00030 #include "status.h"
00031 #include "cache.h"
00032 #include "url.h"
00033 #include "racallback.h"
00034
00035
00036 svn_error_t *cb__init(apr_pool_t *pool)
00037 {
00038 int status;
00039 svn_error_t *status_svn;
00040 apr_hash_t *cfg_hash;
00041 svn_config_t *cfg;
00042
00043
00044 STOPIF( hlp__get_svn_config(&cfg_hash), NULL);
00045
00046 cfg = apr_hash_get(cfg_hash, SVN_CONFIG_CATEGORY_CONFIG,
00047 APR_HASH_KEY_STRING);
00048
00049
00050
00051 STOPIF_SVNERR( svn_cmdline_setup_auth_baton,
00052 (&cb__cb_table.auth_baton,
00053 !(isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)),
00054 opt__get_int(OPT__AUTHOR) ?
00055 opt__get_string(OPT__AUTHOR) : NULL,
00056 NULL,
00057 opt__get_string(OPT__CONFIG_DIR),
00058 0,
00059 cfg,
00060 NULL,
00061 NULL,
00062 pool)
00063 );
00064
00065 BUG_ON(!cb__cb_table.auth_baton);
00066
00067 ex:
00068 RETURN_SVNERR(status);
00069 }
00070
00071
00072
00073
00074
00076
00078 svn_error_t *cb__open_tmp (apr_file_t **fp,
00079 void *callback_baton,
00080 apr_pool_t *pool)
00081 {
00082 int status;
00083
00084
00085 STOPIF( waa__get_tmp_name( NULL, NULL, fp, pool), NULL);
00086
00087 ex:
00088 RETURN_SVNERR(status);
00089 }
00090
00091
00092 struct svn_ra_callbacks_t cb__cb_table=
00093 {
00094 .open_tmp_file = cb__open_tmp,
00095 .auth_baton = NULL,
00096
00097 .get_wc_prop = NULL,
00098 .set_wc_prop=NULL,
00099 .push_wc_prop=NULL,
00100 .invalidate_wc_props=NULL,
00101 };
00102
00103
00104
00105
00106
00107
00108
00109
00110 svn_revnum_t cb___dest_rev;
00111
00113 svn_error_t *cb__txdelta_discard(svn_txdelta_window_t *window UNUSED,
00114 void *baton UNUSED)
00115 {
00116 return NULL;
00117 }
00118
00119
00130 int cb__add_entry(struct estat *dir,
00131 const char *utf8_path, char **loc_path,
00132 const char *utf8_copy_path,
00133 svn_revnum_t copy_rev,
00134 int mode,
00135 int *has_existed,
00136 int may_create,
00137 void **new)
00138 {
00139 int status;
00140 struct estat *sts;
00141 char *filename;
00142 char* path;
00143 char* copy_path;
00144 int overwrite;
00145 struct sstat_t st;
00146
00147
00148 overwrite=0;
00149 STOPIF( hlp__utf82local(utf8_path, &path, -1), NULL );
00150 if (loc_path) *loc_path=path;
00151 STOPIF( hlp__utf82local(utf8_copy_path, ©_path, -1), NULL );
00152
00153 STOPIF_CODE_ERR(copy_path, EINVAL,
00154 "don't know how to handle copy_path %s@%ld",
00155 copy_path, copy_rev);
00156
00157
00158
00159 filename = ops__get_filename(path);
00160 STOPIF( ops__find_entry_byname(dir, filename, &sts, 0),
00161 "cannot lookup entry %s", path);
00162 DEBUGP("entry %s, mode 0%03o; %sfound, may %screate", path, mode,
00163 sts ? "" : "not ",
00164 may_create ? "" : "not ");
00165
00166
00167 if (sts)
00168 {
00169 if (has_existed) *has_existed=EEXIST;
00170
00171 if (!url__current_has_precedence(sts->url))
00172 goto no_change;
00173
00174
00175
00176
00177
00178 if (sts->remote_status & FS_REMOVED)
00179 {
00180 STOPIF( ops__make_shadow_entry(sts, SHADOWED_BY_REMOTE), NULL);
00181 overwrite=1;
00182 }
00183 }
00184 else
00185 {
00186 STOPIF_CODE_ERR(!may_create, ENOENT, NULL);
00187
00188 STOPIF( ops__allocate(1, &sts, NULL), NULL);
00189 memset(sts, 0, sizeof(*sts));
00190
00191
00192
00193
00194
00195
00196 STOPIF( hlp__strdup(&sts->name, filename), NULL);
00197
00198 sts->remote_status = FS_NEW;
00199 overwrite=1;
00200
00201 STOPIF( ops__new_entries(dir, 1, &sts), NULL);
00202
00203
00204 dir->remote_status |= FS_CHANGED;
00205
00206 if (has_existed) *has_existed=0;
00207 }
00208
00209
00210 if (overwrite)
00211 {
00212 sts->parent=dir;
00213
00214
00215
00216
00217 sts->entry_count=0;
00218 sts->by_inode=NULL;
00219 sts->by_name=NULL;
00220 sts->strings=NULL;
00221
00222 sts->decoder=NULL;
00223 sts->has_orig_md5=0;
00224 memset(& sts->md5, 0, sizeof(sts->md5));
00225
00226 memset(& sts->st, 0, sizeof(sts->st));
00227
00228
00229
00230
00231 if (!(mode & 0777))
00232 mode |= S_ISDIR(mode) ? 0700 : 0600;
00233
00234 sts->st.mode = mode;
00235
00236
00237 time( & sts->st.mtim.tv_sec );
00238
00239
00240 sts->st.uid=getuid();
00241 sts->st.gid=getgid();
00242 }
00243
00244 sts->url=current_url;
00245 ops__mark_parent_cc(sts, remote_status);
00246
00247
00248 sts->new_rev_mode_packed = MODE_T_to_PACKED(mode);
00249
00250
00251 if (sts->local_mode_packed == S_IFUNDEF)
00252 {
00253
00254
00255 status=hlp__lstat(path, &st);
00256 if (status == ENOENT)
00257 {
00258
00259
00260 }
00261 else if (!status || status == -ENOENT)
00262 {
00263
00264 sts->local_mode_packed=MODE_T_to_PACKED(st.mode);
00265 }
00266 else
00267 {
00268
00269
00270
00271 }
00272
00273 status=0;
00274 }
00275
00276 DEBUGP("%s is locally a %s",
00277 path, st__type_string(PACKED_to_MODE_T(sts->local_mode_packed)));
00278
00279
00280 no_change:
00281
00282
00283 *new = sts;
00284
00285 ex:
00286 return status;
00287 }
00288
00289
00290 inline int cb___store_prop(struct estat *sts,
00291 const char *utf8_name, const svn_string_t *value,
00292 apr_pool_t *pool)
00293 {
00294 int status;
00295 int user_prop;
00296 apr_pool_t *u_p_pool;
00297 char *copy;
00298 #ifdef DEBUG
00299 static long u_p_count=0,
00300 u_p_bytes=0;
00301 #endif
00302
00303
00304 status=0;
00305 if (!url__current_has_precedence(sts->url)) goto ex;
00306
00307
00308 user_prop=0;
00309 STOPIF( up__parse_prop(sts, utf8_name, value, &user_prop, pool), NULL);
00310 ops__mark_parent_cc(sts, remote_status);
00311 DEBUGP("have name=%s; user? %d", utf8_name, user_prop);
00312
00313
00314 if (action->keep_user_prop && user_prop)
00315 {
00316 if (!sts->user_prop)
00317 {
00318
00319
00320 STOPIF( apr_pool_create(&u_p_pool, sts->url ?
00321 sts->url->pool : global_pool), NULL);
00322
00323 sts->user_prop=apr_hash_make(u_p_pool);
00324 apr_hash_set(sts->user_prop, "", 0, u_p_pool);
00325 }
00326 else
00327 u_p_pool=apr_hash_get(sts->user_prop, "", 0);
00328
00329
00330
00331
00332
00333 copy=apr_palloc(u_p_pool, strlen(utf8_name)+1);
00334 strcpy(copy, utf8_name);
00335 apr_hash_set(sts->user_prop, copy, APR_HASH_KEY_STRING,
00336 svn_string_dup(value, u_p_pool) );
00337
00338 #ifdef ENABLE_DEBUG
00339 u_p_count++;
00340 u_p_bytes += value->len + sizeof(char*) + sizeof(*value);
00341 DEBUGP("%lu user-props stored, with %lu bytes.",
00342 u_p_count, u_p_bytes);
00343 #endif
00344 }
00345
00346 ex:
00347 return status;
00348 }
00349
00350
00351 svn_error_t *cb___set_target_revision(void *edit_baton,
00352 svn_revnum_t rev,
00353 apr_pool_t *pool)
00354 {
00355 int status;
00356 struct estat *root UNUSED=edit_baton;
00357
00358 status=0;
00359 DEBUGP("setting revision to %llu", (t_ull)rev);
00360 cb___dest_rev=rev;
00361 RETURN_SVNERR(status);
00362 }
00363
00364
00365 svn_error_t *cb___open_root(void *edit_baton,
00366 svn_revnum_t base_revision,
00367 apr_pool_t *dir_pool UNUSED,
00368 void **root_baton)
00369 {
00370 struct estat *sts=edit_baton;
00371
00372 *root_baton=sts;
00373
00374 return SVN_NO_ERROR;
00375 }
00376
00377
00378 svn_error_t *cb___delete_entry(const char *utf8_path,
00379 svn_revnum_t revision UNUSED,
00380 void *parent_baton,
00381 apr_pool_t *pool)
00382 {
00383 int status;
00384 struct estat *dir=parent_baton;
00385 struct estat *sts;
00386 char* path;
00387
00388 STOPIF( hlp__utf82local(utf8_path, &path, -1), NULL );
00389
00390 STOPIF( ops__find_entry_byname(dir, path, &sts, 0), NULL);
00391
00392 if (sts)
00393 {
00394 DEBUGP("deleting entry %s", path);
00395
00396 ops__mark_parent_cc(sts, remote_status);
00397
00398 sts->remote_status = FS_REMOVED;
00399
00400 if (action->repos_feedback)
00401 STOPIF( action->repos_feedback(sts), NULL);
00402
00406 sts->url = urllist[urllist_count-1];
00407 }
00408 else
00409 {
00410 DEBUGP("entry %s not found!", path);
00412 }
00413
00414 ex:
00415 RETURN_SVNERR(status);
00416 }
00417
00418
00419 svn_error_t *cb___add_directory(const char *utf8_path,
00420 void *parent_baton,
00421 const char *utf8_copy_path,
00422 svn_revnum_t copy_rev,
00423 apr_pool_t *dir_pool,
00424 void **child_baton)
00425 {
00426 struct estat *dir=parent_baton;
00427 struct estat *sts;
00428 int status;
00429 int has_existed;
00430
00431 STOPIF( cb__add_entry(dir, utf8_path, NULL, utf8_copy_path,
00432 copy_rev, S_IFDIR, &has_existed, 1,
00433 child_baton), NULL );
00434 sts=*child_baton;
00435
00436 if (!has_existed)
00437 {
00438
00439
00440
00441 sts->entry_count=0;
00442 sts->by_inode = sts->by_name = NULL;
00443 sts->strings = NULL;
00444 sts->other_revs=sts->to_be_sorted=0;
00445 }
00446
00447 ex:
00448 RETURN_SVNERR(status);
00449 }
00450
00451
00452 svn_error_t *cb___open_directory(const char *utf8_path,
00453 void *parent_baton,
00454 svn_revnum_t base_revision UNUSED,
00455 apr_pool_t *dir_pool,
00456 void **child_baton)
00457 {
00458 struct estat *dir=parent_baton;
00459 int status;
00460
00462 STOPIF( cb__add_entry(dir, utf8_path, NULL, NULL, 0,
00463 S_IFDIR, NULL, 0, child_baton), NULL);
00464
00465 ex:
00466 RETURN_SVNERR(status);
00467 }
00468
00469
00470 svn_error_t *cb___change_dir_prop(void *dir_baton,
00471 const char *utf8_name,
00472 const svn_string_t *value,
00473 apr_pool_t *pool)
00474 {
00475 int status;
00476
00477
00478 STOPIF( cb___store_prop(dir_baton, utf8_name, value, pool), NULL);
00479
00480 ex:
00481 RETURN_SVNERR(status);
00482 }
00483
00484
00485 int cb___close(struct estat *sts)
00486 {
00487 int status;
00488
00489 status=0;
00490 sts->repos_rev = cb___dest_rev;
00491
00492 if (action->repos_feedback)
00493 STOPIF( action->repos_feedback(sts), NULL);
00494
00495 ex:
00496 return status;
00497 }
00498
00499
00500 svn_error_t *cb___close_directory(
00501 void *dir_baton,
00502 apr_pool_t *pool)
00503 {
00504 struct estat *sts=dir_baton;
00505 int status;
00506
00507
00508
00509 IF_FREE(sts->by_name);
00510
00511 STOPIF( cb___close(sts), NULL);
00512
00513 ex:
00514 RETURN_SVNERR(status);
00515 }
00516
00517
00519 svn_error_t *cb___absent_directory(const char *utf8_path,
00520 void *parent_baton,
00521 apr_pool_t *pool)
00522 {
00523 struct estat *dir UNUSED =parent_baton;
00524
00525 DEBUGP("in %s", __PRETTY_FUNCTION__);
00526
00527 return SVN_NO_ERROR;
00528 }
00529
00530
00531 svn_error_t *cb___add_file(const char *utf8_path,
00532 void *parent_baton,
00533 const char *utf8_copy_path,
00534 svn_revnum_t copy_rev,
00535 apr_pool_t *file_pool,
00536 void **file_baton)
00537 {
00538 struct estat *dir=parent_baton;
00539 struct estat *sts;
00540 int status;
00541
00542 STOPIF( cb__add_entry(dir, utf8_path, NULL, utf8_copy_path,
00543 copy_rev, S_IFREG, NULL, 1, file_baton),
00544 NULL);
00545 sts=*file_baton;
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555 ex:
00556 RETURN_SVNERR(status);
00557 }
00558
00559
00560 svn_error_t *cb___open_file(const char *utf8_path,
00561 void *parent_baton,
00562 svn_revnum_t base_revision,
00563 apr_pool_t *file_pool,
00564 void **file_baton)
00565 {
00566 struct estat *dir=parent_baton;
00567 struct estat *sts;
00568 int status;
00569 int was_there;
00570
00571 STOPIF( cb__add_entry(dir, utf8_path, NULL, NULL, 0,
00572 S_IFREG, &was_there, 0, file_baton), NULL);
00573 sts=(struct estat*)*file_baton;
00574
00575 if (was_there)
00576 STOPIF( up__fetch_decoder(sts), NULL);
00577
00578 sts->decoder_is_correct=1;
00579
00580 ex:
00581 RETURN_SVNERR(status);
00582 }
00583
00584
00585 svn_error_t *cb___apply_textdelta(void *file_baton,
00586 const char *base_checksum UNUSED,
00587 apr_pool_t *pool UNUSED,
00588 svn_txdelta_window_handler_t *handler,
00589 void **handler_baton)
00590 {
00591 struct estat *sts UNUSED=file_baton;
00592 int status;
00593
00594 status=0;
00595 if (url__current_has_precedence(sts->url))
00596 ops__mark_changed_parentcc(sts, remote_status);
00597
00598 *handler = cb__txdelta_discard;
00599 *handler_baton=sts;
00600
00601 RETURN_SVNERR(status);
00602 }
00603
00604
00605 svn_error_t *cb___change_file_prop(void *file_baton,
00606 const char *utf8_name,
00607 const svn_string_t *value,
00608 apr_pool_t *pool)
00609 {
00610 int status;
00611
00612
00613 STOPIF( cb___store_prop(file_baton, utf8_name, value, pool), NULL);
00614
00615 ex:
00616 RETURN_SVNERR(status);
00617 }
00618
00619
00620 svn_error_t *cb___close_file(void *file_baton,
00621 const char *text_checksum,
00622 apr_pool_t *pool)
00623 {
00624 struct estat *sts=file_baton;
00625 int status;
00626
00627
00628 STOPIF( cb___close(sts), NULL);
00629
00630 if (!S_ISDIR(sts->st.mode))
00631 {
00632 if (sts->has_orig_md5 || sts->decoder)
00633 DEBUGP("Has an original MD5, %s not used", text_checksum);
00634 else
00635 if (text_checksum)
00636 STOPIF( cs__char2md5(text_checksum, NULL, sts->md5 ), NULL);
00637 }
00638
00639 ex:
00640 RETURN_SVNERR(status);
00641 }
00642
00643
00645 svn_error_t *cb___absent_file(const char *utf8_path,
00646 void *parent_baton,
00647 apr_pool_t *pool)
00648 {
00649 struct estat *dir UNUSED=parent_baton;
00650
00651 DEBUGP("in %s", __PRETTY_FUNCTION__);
00652 return SVN_NO_ERROR;
00653 }
00654
00655
00656 svn_error_t *cb___close_edit(void *edit_baton,
00657 apr_pool_t *pool UNUSED)
00658 {
00659 int status;
00660 struct estat *root UNUSED=edit_baton;
00661
00662 status=0;
00663
00664
00665
00666 RETURN_SVNERR(status);
00667 }
00668
00669
00671 svn_error_t *cb___abort_edit(void *edit_baton,
00672 apr_pool_t *pool UNUSED)
00673 {
00674 struct estat *sts UNUSED=edit_baton;
00675
00676 return SVN_NO_ERROR;
00677 }
00678
00679
00680
00681
00682 const svn_delta_editor_t cb___change_recorder =
00683 {
00684 .set_target_revision = cb___set_target_revision,
00685
00686 .open_root = cb___open_root,
00687
00688 .delete_entry = cb___delete_entry,
00689 .add_directory = cb___add_directory,
00690 .open_directory = cb___open_directory,
00691 .change_dir_prop = cb___change_dir_prop,
00692 .close_directory = cb___close_directory,
00693 .absent_directory = cb___absent_directory,
00694
00695 .add_file = cb___add_file,
00696 .open_file = cb___open_file,
00697 .apply_textdelta = cb___apply_textdelta,
00698 .change_file_prop = cb___change_file_prop,
00699 .close_file = cb___close_file,
00700 .absent_file = cb___absent_file,
00701
00702 .close_edit = cb___close_edit,
00703 .abort_edit = cb___abort_edit,
00704 };
00705
00709 int cb___report_path_rev(struct estat *dir,
00710 const svn_ra_reporter2_t *reporter,
00711 void *report_baton,
00712 apr_pool_t *pool)
00713 {
00714 int status, i;
00715 struct estat *sts;
00716 svn_error_t *status_svn;
00717 char *fn;
00718
00719
00720 status=0;
00721 for(i=0; i<dir->entry_count; i++)
00722 {
00723 sts=dir->by_inode[i];
00724
00725 STOPIF( ops__build_path(&fn, sts), NULL );
00726
00727
00728
00729
00730
00731 if ( sts->repos_rev != sts->parent->repos_rev)
00732 {
00733 DEBUGP("reporting %s at %llu", fn, (t_ull)sts->repos_rev);
00734 STOPIF_SVNERR( reporter->set_path,
00735 (report_baton, fn+2, sts->repos_rev, 0, "", pool));
00736 }
00737
00738 if (S_ISDIR(sts->st.mode) && sts->other_revs)
00739 {
00740 STOPIF( cb___report_path_rev(sts, reporter, report_baton, pool),
00741 NULL);
00742 }
00743 }
00744
00745 ex:
00746 return status;
00747 }
00748
00749
00753 int cb__record_changes(struct estat *root,
00754 svn_revnum_t target,
00755 apr_pool_t *pool)
00756 {
00757 int status;
00758
00759 STOPIF( cb__record_changes_mixed(root, target,
00760 NULL, 0, pool), NULL);
00761 ex:
00762 return status;
00763 }
00764
00765
00799 int cb__record_changes_mixed(struct estat *root,
00800 svn_revnum_t target,
00801 char *other_paths[], svn_revnum_t other_revs,
00802 apr_pool_t *pool)
00803 {
00804 int status;
00805 svn_error_t *status_svn;
00806 void *report_baton;
00807 const svn_ra_reporter2_t *reporter;
00808 int sent_wcroot;
00809 char *cur, **op;
00810
00811
00812 status=0;
00813 cb___dest_rev=target;
00814 STOPIF_SVNERR( svn_ra_do_status,
00815 (current_url->session,
00816 &reporter,
00817 &report_baton,
00818 "",
00819 target,
00820 TRUE,
00821 &cb___change_recorder,
00822 root,
00823 pool) );
00824
00825 sent_wcroot=0;
00826 cur=NULL;
00827 op=NULL;
00828 if (other_paths)
00829 {
00830 op=other_paths;
00831 while ( (cur=*op) )
00832 {
00833 if (cur[0] == '.' && cur[1] == 0)
00834 break;
00835 op++;
00836 }
00837 }
00838
00839
00840
00841
00842 if (cur)
00843 STOPIF_SVNERR( reporter->set_path,
00844 (report_baton,
00845 "", other_revs,
00846 FALSE, NULL, pool));
00847 else if (current_url->current_rev == 0)
00848 STOPIF_SVNERR( reporter->set_path,
00849 (report_baton,
00850 "", target,
00851 TRUE, NULL, pool));
00852 else
00853 STOPIF_SVNERR( reporter->set_path,
00854 (report_baton,
00855 "", current_url->current_rev,
00856 FALSE, NULL, pool));
00857
00858 if (other_paths)
00859 {
00860
00861
00862
00863
00864
00865 while ( (cur=*other_paths) )
00866 {
00867
00868 if (op != other_paths)
00869 {
00870 DEBUGP("reporting %s@%llu", cur, (t_ull)other_revs);
00871
00872 if (cur[0] == '.' && cur[1] == PATH_SEPARATOR)
00873 cur+=2;
00874
00875 STOPIF_SVNERR( reporter->set_path,
00876 (report_baton, cur, other_revs, FALSE, NULL, pool));
00877 }
00878
00879 other_paths++;
00880 }
00881 }
00882
00883
00884 DEBUGP("Getting changes from %llu to %llu",
00885 (t_ull)current_url->current_rev,
00886 (t_ull)target);
00887 #if 0
00888 STOPIF( cb___report_path_rev( root, reporter, report_baton, pool), NULL);
00889 #endif
00890
00891 STOPIF_SVNERR( reporter->finish_report,
00892 (report_baton, global_pool));
00893
00894 current_url->current_rev=cb___dest_rev;
00895
00896 ex:
00897 return status;
00898 }
00899
00900
00904 int cb__does_path_exist(svn_ra_session_t *session,
00905 char *path, svn_revnum_t rev,
00906 int *exists,
00907 apr_pool_t *pool)
00908 {
00909 int status;
00910 svn_dirent_t *dirent;
00911 svn_error_t *status_svn;
00912
00913
00914 status=0;
00915
00916 STOPIF_SVNERR( svn_ra_stat,
00917 (session, path, rev, &dirent, pool));
00918 *exists = dirent != NULL;
00919
00920 ex:
00921 return status;
00922 }
00923