3
* ====================================================================
4
* Copyright (c) 2003-2004 CollabNet. All rights reserved.
6
* This software is licensed as described in the file COPYING, which
7
* you should have received as part of this distribution. The terms
8
* are also available at http://subversion.tigris.org/license-1.html.
9
* If newer versions of this license are posted there, you may use a
10
* newer version instead, at your option.
12
* This software consists of voluntary contributions made by many
13
* individuals. For exact contribution history, see the revision
14
* history and logs, available at http://subversion.tigris.org/.
15
* ====================================================================
19
* @brief Implementation of the class SVNAdmin
23
#include "SVNClient.h"
25
#include "svn_repos.h"
26
#include "svn_config.h"
27
#include "svn_pools.h"
30
#include "svn_private_config.h"
31
//////////////////////////////////////////////////////////////////////
32
// Construction/Destruction
33
//////////////////////////////////////////////////////////////////////
44
jlong SVNAdmin::getCppAddr()
46
return reinterpret_cast<jlong>(this);
48
SVNAdmin * SVNAdmin::getCppObject(jobject jthis)
50
static jfieldID fid = 0;
51
JNIEnv *env = JNIUtil::getEnv();
54
jclass clazz = env->FindClass(JAVA_PACKAGE"/SVNAdmin");
55
if(JNIUtil::isJavaExceptionThrown())
59
fid = env->GetFieldID(clazz, "cppAddr", "J");
60
if(JNIUtil::isJavaExceptionThrown())
66
jlong cppAddr = env->GetLongField(jthis, fid);
67
if(JNIUtil::isJavaExceptionThrown())
71
return reinterpret_cast<SVNAdmin*>(cppAddr);
75
void SVNAdmin::dispose(jobject jthis)
78
static jfieldID fid = 0;
79
JNIEnv *env = JNIUtil::getEnv();
82
jclass clazz = env->FindClass(JAVA_PACKAGE"/SVNAdmin");
83
if(JNIUtil::isJavaExceptionThrown())
87
fid = env->GetFieldID(clazz, "cppAddr", "J");
88
if(JNIUtil::isJavaExceptionThrown())
94
env->SetLongField(jthis, fid, 0);
95
if(JNIUtil::isJavaExceptionThrown())
101
void SVNAdmin::finalize()
103
JNIUtil::putFinalizedClient(this);
106
void SVNAdmin::create(const char *path, bool disableFsyncCommits,
107
bool keepLogs, const char *configPath,
113
JNIUtil::throwNullPointerException("path");
116
path = svn_path_internal_style(path, requestPool.pool());
117
if(configPath != NULL)
118
configPath = svn_path_internal_style(configPath, requestPool.pool());
121
apr_hash_t *fs_config = apr_hash_make (requestPool.pool());;
123
apr_hash_set (fs_config, SVN_FS_CONFIG_BDB_TXN_NOSYNC,
125
(disableFsyncCommits? "1" : "0"));
127
apr_hash_set (fs_config, SVN_FS_CONFIG_BDB_LOG_AUTOREMOVE,
129
(keepLogs ? "0" : "1"));
130
apr_hash_set (fs_config, SVN_FS_CONFIG_FS_TYPE,
135
svn_config_get_config (&config, configPath, requestPool.pool());
136
if(err != SVN_NO_ERROR)
138
JNIUtil::handleSVNError(err);
141
err = svn_repos_create (&repos, path,
143
config, fs_config, requestPool.pool());
145
if(err != SVN_NO_ERROR)
147
JNIUtil::handleSVNError(err);
152
void SVNAdmin::deltify(const char *path, Revision &revStart, Revision &revEnd)
157
JNIUtil::throwNullPointerException("path");
160
path = svn_path_internal_style(path, requestPool.pool());
163
svn_revnum_t start = SVN_INVALID_REVNUM, end = SVN_INVALID_REVNUM;
164
svn_revnum_t youngest, revision;
165
apr_pool_t *revisionPool = svn_pool_create (requestPool.pool());
167
svn_error_t *err = svn_repos_open (&repos, path, requestPool.pool());
168
if(err != SVN_NO_ERROR)
170
JNIUtil::handleSVNError(err);
173
fs = svn_repos_fs (repos);
174
err = svn_fs_youngest_rev (&youngest, fs, requestPool.pool());
175
if(err != SVN_NO_ERROR)
177
JNIUtil::handleSVNError(err);
181
if(revStart.revision()->kind == svn_opt_revision_number)
182
/* ### We only handle revision numbers right now, not dates. */
183
start = revStart.revision()->value.number;
184
else if (revStart.revision()->kind == svn_opt_revision_head)
187
start = SVN_INVALID_REVNUM;
189
if (revEnd.revision()->kind == svn_opt_revision_number)
190
end = revEnd.revision()->value.number;
191
else if (revEnd.revision()->kind == svn_opt_revision_head)
194
end = SVN_INVALID_REVNUM;
196
/* Fill in implied revisions if necessary. */
197
if (start == SVN_INVALID_REVNUM)
199
if (end == SVN_INVALID_REVNUM)
204
JNIUtil::handleSVNError( svn_error_create
205
(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
206
_("First revision cannot be higher than second")));
209
if ((start > youngest) || (end > youngest))
211
JNIUtil::handleSVNError(svn_error_createf
212
(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
213
_("Revisions must not be greater than the youngest revision (%"
214
SVN_REVNUM_T_FMT ")"), youngest));
218
/* Loop over the requested revision range, performing the
219
predecessor deltification on paths changed in each. */
220
for (revision = start; revision <= end; revision++)
222
svn_pool_clear (revisionPool);
223
err = svn_fs_deltify_revision (fs, revision, revisionPool);
224
if(err != SVN_NO_ERROR)
226
JNIUtil::handleSVNError(err);
230
svn_pool_destroy (revisionPool);
235
void SVNAdmin::dump(const char *path, Outputer &dataOut, Outputer &messageOut,
236
Revision &revsionStart, Revision &revisionEnd,
242
JNIUtil::throwNullPointerException("path");
245
path = svn_path_internal_style(path, requestPool.pool());
248
svn_revnum_t lower = SVN_INVALID_REVNUM, upper = SVN_INVALID_REVNUM;
249
svn_revnum_t youngest;
251
svn_error_t *err = svn_repos_open (&repos, path, requestPool.pool());
252
if(err != SVN_NO_ERROR)
254
JNIUtil::handleSVNError(err);
257
fs = svn_repos_fs (repos);
258
err = svn_fs_youngest_rev (&youngest, fs, requestPool.pool());
259
if(err != SVN_NO_ERROR)
261
JNIUtil::handleSVNError(err);
265
/* ### We only handle revision numbers right now, not dates. */
266
if (revsionStart.revision()->kind == svn_opt_revision_number)
267
lower = revsionStart.revision()->value.number;
268
else if (revsionStart.revision()->kind == svn_opt_revision_head)
271
lower = SVN_INVALID_REVNUM;
273
if (revisionEnd.revision()->kind == svn_opt_revision_number)
274
upper = revisionEnd.revision()->value.number;
275
else if (revisionEnd.revision()->kind == svn_opt_revision_head)
278
upper = SVN_INVALID_REVNUM;
280
/* Fill in implied revisions if necessary. */
281
if (lower == SVN_INVALID_REVNUM)
286
else if (upper == SVN_INVALID_REVNUM)
293
JNIUtil::handleSVNError(svn_error_create
294
(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
295
_("First revision cannot be higher than second")));
298
if ((lower > youngest) || (upper > youngest))
300
JNIUtil::handleSVNError(svn_error_createf
301
(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
302
_("Revisions must not be greater than the youngest revision (%"
303
SVN_REVNUM_T_FMT ")"), youngest));
307
err= svn_repos_dump_fs (repos, dataOut.getStream(requestPool),
308
messageOut.getStream(requestPool),
309
lower, upper, incremental,
310
NULL, NULL, requestPool.pool());
311
if(err != SVN_NO_ERROR)
313
JNIUtil::handleSVNError(err);
319
void SVNAdmin::hotcopy(const char *path, const char *targetPath,
325
JNIUtil::throwNullPointerException("path");
328
if(targetPath == NULL)
330
JNIUtil::throwNullPointerException("targetPath");
333
path = svn_path_internal_style(path, requestPool.pool());
334
targetPath = svn_path_internal_style(targetPath, requestPool.pool());
335
svn_error_t *err = svn_repos_hotcopy (path,
339
if(err != SVN_NO_ERROR)
341
JNIUtil::handleSVNError(err);
347
list_dblogs (const char *path, MessageReceiver &receiver, bool only_unused)
352
JNIUtil::throwNullPointerException("path");
355
path = svn_path_internal_style(path, requestPool.pool());
356
apr_array_header_t *logfiles;
358
svn_error_t *err = svn_repos_db_logfiles (&logfiles,
362
if(err != SVN_NO_ERROR)
364
JNIUtil::handleSVNError(err);
368
/* Loop, printing log files. We append the log paths to the
369
repository path, making sure to return everything to the native
370
style and encoding before printing. */
371
for (i = 0; i < logfiles->nelts; i++)
373
const char *log_utf8;
374
log_utf8 = svn_path_join (path,
375
APR_ARRAY_IDX (logfiles, i, const char *),
377
log_utf8 = svn_path_local_style (log_utf8, requestPool.pool());
378
receiver.receiveMessage(log_utf8);
382
void SVNAdmin::listDBLogs(const char *path, MessageReceiver &messageReceiver)
384
list_dblogs(path, messageReceiver, false);
387
void SVNAdmin::listUnusedDBLogs(const char *path, MessageReceiver &messageReceiver)
389
list_dblogs(path, messageReceiver, true);
392
void SVNAdmin::load(const char *path, Inputer &dataIn, Outputer &messageOut, bool ignoreUUID, bool forceUUID, const char *relativePath)
397
JNIUtil::throwNullPointerException("path");
400
path = svn_path_internal_style(path, requestPool.pool());
402
enum svn_repos_load_uuid uuid_action;
404
uuid_action = svn_repos_load_uuid_ignore;
406
uuid_action = svn_repos_load_uuid_force;
407
svn_error_t *err = svn_repos_open (&repos, path, requestPool.pool());
408
if(err != SVN_NO_ERROR)
410
JNIUtil::handleSVNError(err);
414
err = svn_repos_load_fs (repos, dataIn.getStream(requestPool),
415
messageOut.getStream(requestPool),
416
uuid_action, relativePath,
417
NULL, NULL, requestPool.pool());
419
if(err != SVN_NO_ERROR)
421
JNIUtil::handleSVNError(err);
427
void SVNAdmin::lstxns(const char *path, MessageReceiver &messageReceiver)
432
JNIUtil::throwNullPointerException("path");
435
path = svn_path_internal_style(path, requestPool.pool());
438
apr_array_header_t *txns;
441
svn_error_t *err = svn_repos_open (&repos, path, requestPool.pool());
442
if(err != SVN_NO_ERROR)
444
JNIUtil::handleSVNError(err);
447
fs = svn_repos_fs (repos);
448
err = svn_fs_list_transactions (&txns, fs, requestPool.pool());
449
if(err != SVN_NO_ERROR)
451
JNIUtil::handleSVNError(err);
455
/* Loop, printing revisions. */
456
for (i = 0; i < txns->nelts; i++)
458
messageReceiver.receiveMessage(APR_ARRAY_IDX (txns, i, const char *));
464
jlong SVNAdmin::recover(const char *path)
469
JNIUtil::throwNullPointerException("path");
472
path = svn_path_internal_style(path, requestPool.pool());
473
svn_revnum_t youngest_rev;
476
svn_error_t *err = svn_repos_recover2 (path, FALSE, NULL, NULL,
478
if(err != SVN_NO_ERROR)
480
JNIUtil::handleSVNError(err);
484
/* Since db transactions may have been replayed, it's nice to tell
485
people what the latest revision is. It also proves that the
486
recovery actually worked. */
487
err = svn_repos_open (&repos, path, requestPool.pool());
488
if(err != SVN_NO_ERROR)
490
JNIUtil::handleSVNError(err);
493
err = svn_fs_youngest_rev (&youngest_rev, svn_repos_fs (repos),
495
if(err != SVN_NO_ERROR)
497
JNIUtil::handleSVNError(err);
503
void SVNAdmin::rmtxns(const char *path, Targets &transactions)
508
JNIUtil::throwNullPointerException("path");
511
path = svn_path_internal_style(path, requestPool.pool());
515
const apr_array_header_t *args;
517
apr_pool_t *transactionPool = svn_pool_create (requestPool.pool());
519
svn_error_t *err = svn_repos_open (&repos, path, requestPool.pool());
520
if(err != SVN_NO_ERROR)
522
JNIUtil::handleSVNError(err);
525
fs = svn_repos_fs (repos);
527
args = transactions.array(requestPool);
528
/* All the rest of the arguments are transaction names. */
529
for (i = 0; i < args->nelts; i++)
531
const char *txn_name = APR_ARRAY_IDX (args, i, const char *);
533
/* Try to open the txn. If that succeeds, try to abort it. */
534
err = svn_fs_open_txn (&txn, fs, txn_name, transactionPool);
536
err = svn_fs_abort_txn (txn, transactionPool);
538
/* If either the open or the abort of the txn fails because that
539
transaction is dead, just try to purge the thing. Else,
540
there was either an error worth reporting, or not error at
542
if (err && (err->apr_err == SVN_ERR_FS_TRANSACTION_DEAD))
544
svn_error_clear (err);
545
err = svn_fs_purge_txn (fs, txn_name, transactionPool);
548
/* If we had a real from the txn open, abort, or purge, we clear
549
that error and just report to the user that we had an issue
550
with this particular txn. */
553
JNIUtil::handleSVNError(err);
556
svn_pool_clear (transactionPool);
561
void SVNAdmin::setLog(const char *path, Revision &revision,
562
const char *message, bool bypassHooks)
567
JNIUtil::throwNullPointerException("path");
572
JNIUtil::throwNullPointerException("message");
575
path = svn_path_internal_style(path, requestPool.pool());
577
svn_string_t *log_contents = svn_string_create (message,
580
if (revision.revision()->kind != svn_opt_revision_number)
582
JNIUtil::handleSVNError(
583
svn_error_createf (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
584
_("Missing revision")));
587
else if (revision.revision()->kind != svn_opt_revision_unspecified)
589
JNIUtil::handleSVNError(
590
svn_error_createf (SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
591
_("Only one revision allowed")));
594
/* Open the filesystem */
595
svn_error_t *err = svn_repos_open (&repos, path, requestPool.pool());
596
if(err != SVN_NO_ERROR)
598
JNIUtil::handleSVNError(err);
602
/* If we are bypassing the hooks system, we just hit the filesystem
606
svn_fs_t *fs = svn_repos_fs (repos);
607
err = svn_fs_change_rev_prop
608
(fs, revision.revision()->value.number,
609
SVN_PROP_REVISION_LOG, log_contents, requestPool.pool());
613
err = svn_repos_fs_change_rev_prop
614
(repos, revision.revision()->value.number,
615
NULL, SVN_PROP_REVISION_LOG, log_contents, requestPool.pool());
617
if(err != SVN_NO_ERROR)
619
JNIUtil::handleSVNError(err);
624
void SVNAdmin::verify(const char *path, Outputer &messageOut,
625
Revision &revisionStart, Revision &revisionEnd)
630
JNIUtil::throwNullPointerException("path");
633
path = svn_path_internal_style(path, requestPool.pool());
635
svn_revnum_t youngest;
637
/* This whole process is basically just a dump of the repository
638
with no interest in the output. */
639
svn_error_t *err = svn_repos_open (&repos, path, requestPool.pool());
640
if(err != SVN_NO_ERROR)
642
JNIUtil::handleSVNError(err);
645
err = svn_fs_youngest_rev (&youngest, svn_repos_fs (repos),
647
if(err != SVN_NO_ERROR)
649
JNIUtil::handleSVNError(err);
652
err = svn_repos_dump_fs (repos, NULL, messageOut.getStream(requestPool),
653
0, youngest, FALSE, NULL, NULL,
655
if(err != SVN_NO_ERROR)
657
JNIUtil::handleSVNError(err);
662
jobjectArray SVNAdmin::lslocks(const char *path)
667
JNIUtil::throwNullPointerException("path");
670
path = svn_path_internal_style(path, requestPool.pool());
674
apr_hash_index_t *hi;
676
svn_error_t *err = svn_repos_open (&repos, path, requestPool.pool());
677
if(err != SVN_NO_ERROR)
679
JNIUtil::handleSVNError(err);
682
fs = svn_repos_fs (repos);
683
/* Fetch all locks on or below the root directory. */
684
err = svn_repos_fs_get_locks (&locks, repos, "/", NULL, NULL,
686
if(err != SVN_NO_ERROR)
688
JNIUtil::handleSVNError(err);
692
int count = apr_hash_count (locks);
694
JNIEnv *env = JNIUtil::getEnv();
695
jclass clazz = env->FindClass(JAVA_PACKAGE"/Lock");
696
if(JNIUtil::isJavaExceptionThrown())
700
jobjectArray ret = env->NewObjectArray(count, clazz, NULL);
701
if(JNIUtil::isJavaExceptionThrown())
705
env->DeleteLocalRef(clazz);
706
if(JNIUtil::isJavaExceptionThrown())
712
for (hi = apr_hash_first (requestPool.pool(), locks); hi;
713
hi = apr_hash_next (hi),i++)
717
apr_hash_this (hi, &key, NULL, &val);
718
svn_lock_t *lock = (svn_lock_t *)val;
719
jobject jLock = SVNClient::createJavaLock(lock);
720
env->SetObjectArrayElement(ret, i, jLock);
721
if(JNIUtil::isJavaExceptionThrown())
725
env->DeleteLocalRef(jLock);
726
if(JNIUtil::isJavaExceptionThrown())
734
void SVNAdmin::rmlocks(const char *path, Targets &locks)
737
apr_pool_t *pool = requestPool.pool();
740
JNIUtil::throwNullPointerException("path");
743
path = svn_path_internal_style(path, requestPool.pool());
746
svn_fs_access_t *access;
748
svn_error_t *err = svn_repos_open (&repos, path, requestPool.pool());
749
if(err != SVN_NO_ERROR)
751
JNIUtil::handleSVNError(err);
754
fs = svn_repos_fs (repos);
755
const char *username;
757
/* svn_fs_unlock() demands that some username be associated with the
758
filesystem, so just use the UID of the person running 'svnadmin'.*/
763
if (apr_uid_current (&uid, &gid, pool) == APR_SUCCESS &&
764
apr_uid_name_get (&un, uid, pool) == APR_SUCCESS)
766
err = svn_utf_cstring_to_utf8 (&username, un, pool);
767
svn_error_clear (err);
769
username = "administrator";
773
/* Create an access context describing the current user. */
774
err = svn_fs_create_access (&access, username, pool);
775
if(err != SVN_NO_ERROR)
777
JNIUtil::handleSVNError(err);
781
/* Attach the access context to the filesystem. */
782
err = svn_fs_set_access (fs, access);
783
if(err != SVN_NO_ERROR)
785
JNIUtil::handleSVNError(err);
789
apr_pool_t *subpool = svn_pool_create (pool);
790
const apr_array_header_t *args = locks.array(requestPool);
791
for (int i = 0; i < args->nelts; i++)
793
const char *lock_path = APR_ARRAY_IDX (args, i, const char *);
796
/* Fetch the path's svn_lock_t. */
797
err = svn_fs_get_lock (&lock, fs, lock_path, subpool);
805
/* Now forcibly destroy the lock. */
806
err = svn_fs_unlock (fs, lock_path,
807
lock->token, 1 /* force */, subpool);
814
svn_error_clear (err);
817
svn_pool_clear (subpool);