899
900
comb->res.uri = dav_svn__build_uri(comb->priv.repos,
900
901
DAV_SVN__BUILD_URI_BASELINE,
901
902
comb->priv.root.rev, NULL,
903
FALSE /* add_href */,
1034
1035
comb->res.exists = (kind != svn_node_none);
1035
1036
comb->res.collection = (kind == svn_node_dir);
1038
if (comb->res.exists
1039
&& comb->priv.r->method_number == M_MKCOL
1040
&& comb->priv.repos->is_svn_client)
1042
/* mod_dav will now continue returning a generic HTTP_METHOD_NOT_ALLOWED
1043
error, which doesn't produce nice output on SVN, nor gives any details
1044
on why the operation failed.
1046
Let's error out a bit earlier and produce an error message that is
1047
easier to understand for both clients and users. */
1049
/* It would be nice if we could error out a bit later (see issue #2295),
1050
like in create_collection(), but mod_dav outsmarts us by just
1051
returning the error when the node exists. */
1053
return dav_svn__convert_err(
1054
svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
1055
"Path already exists, path '%s'",
1056
comb->priv.repos_path),
1057
HTTP_METHOD_NOT_ALLOWED, NULL, pool);
1169
1192
if (base->info->repos->root_path[1])
1170
1193
comb->res.uri = apr_pstrcat(base->pool, base->info->repos->root_path,
1171
path->data, (char *)NULL);
1194
path->data, SVN_VA_NULL);
1173
1196
comb->res.uri = path->data;
1174
1197
comb->res.info = &comb->priv;
1210
1233
AP_MODULE_DECLARE(dav_error *)
1211
dav_svn_split_uri(request_rec *r,
1212
const char *uri_to_split,
1213
const char *root_path,
1214
const char **cleaned_uri,
1215
int *trailing_slash,
1216
const char **repos_basename,
1217
const char **relative_path,
1218
const char **repos_path)
1234
dav_svn_split_uri2(request_rec *r,
1235
const char *uri_to_split,
1236
const char *root_path,
1237
const char **cleaned_uri,
1238
int *trailing_slash,
1239
const char **repos_basename,
1240
const char **relative_path,
1241
const char **repos_path,
1220
1244
apr_size_t len1;
1231
1255
if ((fs_path == NULL) && (fs_parent_path == NULL))
1233
1257
/* ### are SVN_ERR_APMOD codes within the right numeric space? */
1234
return dav_svn__new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR,
1258
return dav_svn__new_error(pool, HTTP_INTERNAL_SERVER_ERROR,
1235
1259
SVN_ERR_APMOD_MISSING_PATH_TO_FS,
1236
1260
"The server is misconfigured: "
1237
1261
"either an SVNPath or SVNParentPath "
1242
1266
/* make a copy so that we can do some work on it */
1243
uri = apr_pstrdup(r->pool, uri_to_split);
1267
uri = apr_pstrdup(pool, uri_to_split);
1245
1269
/* remove duplicate slashes, and make sure URI has no trailing '/' */
1246
1270
ap_no2slash(uri);
1255
1279
*trailing_slash = FALSE;
1257
1281
/* return the first item. */
1258
*cleaned_uri = apr_pstrdup(r->pool, uri);
1282
*cleaned_uri = apr_pstrdup(pool, uri);
1260
1284
/* The URL space defined by the SVN provider is always a virtual
1261
1285
space. Construct the path relative to the configured Location
1296
1320
if (fs_path != NULL)
1298
1322
/* the repos_basename is the last component of root_path. */
1299
*repos_basename = svn_dirent_basename(root_path, r->pool);
1323
*repos_basename = svn_dirent_basename(root_path, pool);
1301
1325
/* 'relative' is already correct for SVNPath; the root_path
1302
1326
already contains the name of the repository, so relative is
1314
1338
if (relative[1] == '\0')
1316
1340
/* ### are SVN_ERR_APMOD codes within the right numeric space? */
1317
return dav_svn__new_error(r->pool, HTTP_FORBIDDEN,
1341
return dav_svn__new_error(pool, HTTP_FORBIDDEN,
1318
1342
SVN_ERR_APMOD_MALFORMED_URI,
1319
1343
"The URI does not contain the name "
1320
1344
"of a repository.");
1334
magic_component = apr_pstrndup(r->pool, relative + 1,
1358
magic_component = apr_pstrndup(pool, relative + 1,
1335
1359
magic_end - relative - 1);
1336
1360
relative = magic_end;
1343
1367
/* We can return 'relative' at this point too. */
1344
*relative_path = apr_pstrdup(r->pool, relative);
1368
*relative_path = apr_pstrdup(pool, relative);
1346
1370
/* Code to remove the !svn junk from the front of the relative path,
1347
1371
mainly stolen from parse_uri(). This code assumes that
1362
1386
if (ch == '\0')
1364
1388
/* relative is just "!svn", which is malformed. */
1365
return dav_svn__new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR,
1389
return dav_svn__new_error(pool, HTTP_NOT_FOUND,
1366
1390
SVN_ERR_APMOD_MALFORMED_URI,
1367
1391
"Nothing follows the svn special_uri.");
1389
1413
*repos_path = NULL;
1391
1415
return dav_svn__new_error(
1392
r->pool, HTTP_INTERNAL_SERVER_ERROR,
1416
pool, HTTP_NOT_FOUND,
1393
1417
SVN_ERR_APMOD_MALFORMED_URI,
1394
1418
"Missing info after special_uri.");
1413
1437
/* Did we break from the loop prematurely? */
1414
1438
if (j != (defn->numcomponents - 1))
1415
1439
return dav_svn__new_error(
1416
r->pool, HTTP_INTERNAL_SERVER_ERROR,
1440
pool, HTTP_NOT_FOUND,
1417
1441
SVN_ERR_APMOD_MALFORMED_URI,
1418
1442
"Not enough components after "
1419
1443
"special_uri.");
1429
1453
/* Found a slash after the special components. */
1430
*repos_path = apr_pstrdup(r->pool, start);
1454
*repos_path = apr_pstrdup(pool, start - 1);
1436
dav_svn__new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR,
1460
dav_svn__new_error(pool, HTTP_NOT_FOUND,
1437
1461
SVN_ERR_APMOD_MALFORMED_URI,
1438
1462
"Unknown data after special_uri.");
1445
1469
if (defn->name == NULL)
1447
dav_svn__new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR,
1471
dav_svn__new_error(pool, HTTP_NOT_FOUND,
1448
1472
SVN_ERR_APMOD_MALFORMED_URI,
1449
1473
"Couldn't match subdir after special_uri.");
1454
1478
/* There's no "!svn/" at all, so the relative path is already
1455
1479
a valid path within the repository. */
1456
*repos_path = apr_pstrdup(r->pool, relative);
1480
*repos_path = apr_pstrdup(pool, relative - 1);
1487
AP_MODULE_DECLARE(dav_error *)
1488
dav_svn_split_uri(request_rec *r,
1489
const char *uri_to_split,
1490
const char *root_path,
1491
const char **cleaned_uri,
1492
int *trailing_slash,
1493
const char **repos_basename,
1494
const char **relative_path,
1495
const char **repos_path)
1497
return dav_svn_split_uri2(r, uri_to_split, root_path, cleaned_uri,
1498
trailing_slash, repos_basename, relative_path,
1499
repos_path, r->pool);
1464
1502
/* Context for cleanup handler. */
1465
1503
struct cleanup_fs_access_baton
1530
1568
if (r->uri[len-1] != '/')
1532
1570
new_uri = apr_pstrcat(r->pool, ap_escape_uri(r->pool, r->uri),
1534
1572
apr_table_setn(r->headers_out, "Location",
1535
1573
ap_construct_url(r->pool, new_uri, r));
1536
1574
return dav_svn__new_error(r->pool, HTTP_MOVED_PERMANENTLY, 0,
1820
1858
"Attempting to modify out-of-date resource.",
1861
else if (comb->priv.version_name > created_rev)
1863
svn_revnum_t txn_base_rev;
1865
txn_base_rev = svn_fs_txn_base_revision(comb->res.info->root.txn);
1866
if (comb->priv.version_name > txn_base_rev)
1868
serr = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
1869
"No such revision %ld",
1870
comb->priv.version_name);
1872
return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
1873
"Unknown base revision",
1824
else if (SVN_IS_VALID_REVNUM(comb->priv.version_name)
1825
&& comb->res.collection)
1878
else if (comb->res.collection)
1827
1880
/* Issue #4480: With HTTPv2 we can receive the first change for a
1828
1881
directory after it has been made mutable, because one of its
1837
1890
properties changed since the BASE version.
1839
1892
### I think svn_fs_node_relation() checks for more changes than we
1840
should check for here. Needs further review. But it looks like\
1893
should check for here. Needs further review. But it looks like
1841
1894
this check matches the checks in the libsvn_fs commit editor.
1843
1896
For now I would say reporting out of date in a few too many
1846
1899
svn_revnum_t txn_base_rev;
1847
1900
svn_fs_root_t *txn_base_root;
1848
1901
svn_fs_root_t *rev_root;
1849
const svn_fs_id_t *txn_base_id;
1850
const svn_fs_id_t *rev_id;
1902
svn_fs_node_relation_t node_relation;
1852
1904
txn_base_rev = svn_fs_txn_base_revision(comb->res.info->root.txn);
1857
1909
serr = svn_fs_revision_root(&txn_base_root, comb->res.info->repos->fs,
1858
1910
txn_base_rev, r->pool);
1861
serr = svn_fs_node_id(&txn_base_id, txn_base_root,
1862
comb->priv.repos_path, r->pool);
1864
1912
if (serr != NULL)
1866
1914
return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
1867
"Could not open youngest revision root "
1915
"Could not open the transaction revision "
1868
1916
"for verification against the base "
1869
1917
"revision", r->pool);
1872
1920
serr = svn_fs_revision_root(&rev_root, comb->res.info->repos->fs,
1873
1921
comb->priv.version_name, r->pool);
1876
serr = svn_fs_node_id(&rev_id, rev_root,
1877
comb->priv.repos_path, r->pool);
1879
1923
if (serr != NULL)
1925
svn_fs_close_root(txn_base_root);
1881
1926
return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
1882
"Could not open the base revision"
1883
"for verification against the youngest "
1884
"revision", r->pool);
1927
"Could not open the base revision "
1928
"for verification against the "
1929
"transaction revision", r->pool);
1932
serr = svn_fs_node_relation(&node_relation, rev_root,
1933
comb->priv.repos_path,
1935
comb->priv.repos_path,
1887
1938
svn_fs_close_root(rev_root);
1888
1939
svn_fs_close_root(txn_base_root);
1890
if (0 != svn_fs_compare_ids(txn_base_id, rev_id))
1943
/* ### correct HTTP error? */
1944
return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
1945
"Unable to fetch the node revision id "
1946
"of the version resource within the "
1951
if (node_relation != svn_fs_node_unchanged)
1892
1953
serr = svn_error_createf(SVN_ERR_RA_OUT_OF_DATE, NULL,
1893
1954
"Directory '%s' is out of date",
2065
2126
xslt_uri = dav_svn__get_xslt_uri(r);
2066
2127
fs_parent_path = dav_svn__get_fs_parent_path(r);
2129
if (r->method_number == M_COPY)
2131
/* Workaround for issue #4531: Avoid a depth-infinity walk on
2132
the copy source by overriding the Depth header here.
2133
mod_dav defaults to infinite depth if this header is not set
2134
which makes copies O(size of source) rather than the desired O(1).
2135
### Should be fixed by an explicit provider API feature in mod_dav. */
2136
apr_table_setn(r->headers_in, "Depth", "0");
2068
2139
/* Special case: detect and build the SVNParentPath as a unique type
2069
2140
of private resource, iff the SVNListParentPath directive is 'on'. */
2070
2141
if (dav_svn__is_parentpath_list(r))
2263
2334
/* Retrieve/cache open repository */
2264
repos_key = apr_pstrcat(r->pool, "mod_dav_svn:", fs_path, (char *)NULL);
2335
repos_key = apr_pstrcat(r->pool, "mod_dav_svn:", fs_path, SVN_VA_NULL);
2265
2336
apr_pool_userdata_get(&userdata, repos_key, r->connection->pool);
2266
2337
repos->repos = userdata;
2267
2338
if (repos->repos == NULL)
2275
2346
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
2276
2347
dav_svn__get_fulltext_cache_flag(r) ? "1" :"0");
2277
2348
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS,
2278
dav_svn__get_revprop_cache_flag(r) ? "1" :"0");
2349
dav_svn__get_revprop_cache_flag(r) ? "2" :"0");
2350
svn_hash_sets(fs_config, SVN_FS_CONFIG_FSFS_BLOCK_READ,
2351
dav_svn__get_block_read_flag(r) ? "1" :"0");
2280
2353
/* Disallow BDB/event until issue 4157 is fixed. */
2281
2354
if (!strcmp(ap_show_mpm(), "event"))
2299
2372
/* open the FS */
2301
serr = svn_repos_open2(&(repos->repos), fs_path, fs_config,
2302
r->connection->pool);
2374
serr = svn_repos_open3(&(repos->repos), fs_path, fs_config,
2375
r->connection->pool, r->pool);
2303
2376
if (serr != NULL)
2305
2378
/* The error returned by svn_repos_open2 might contain the
2307
2380
leak that path back to the client, because that would be
2308
2381
a security risk, but we do want to log the real error on
2309
2382
the server side. */
2310
return dav_svn__sanitize_error(serr, "Could not open the requested "
2312
HTTP_INTERNAL_SERVER_ERROR, r);
2384
apr_status_t cause = svn_error_root_cause(serr)->apr_err;
2385
if (APR_STATUS_IS_ENOENT(cause) || APR_STATUS_IS_ENOTDIR(cause))
2386
return dav_svn__sanitize_error(
2387
serr, "Could not find the requested SVN filesystem",
2390
return dav_svn__sanitize_error(
2391
serr, "Could not open the requested SVN filesystem",
2392
HTTP_INTERNAL_SERVER_ERROR, r);
2315
2395
/* Cache the open repos for the next request on this connection */
2468
2548
r->args ? "?" : "",
2469
2549
r->args ? r->args : "",
2471
2551
apr_table_setn(r->headers_out, "Location",
2472
2552
ap_construct_url(r->pool, new_path, r));
2473
2553
return dav_svn__new_error(r->pool, HTTP_MOVED_PERMANENTLY, 0,
3471
3551
/* ### The xml output doesn't like to see a trailing slash on
3472
3552
### the visible portion, so avoid that. */
3474
href = apr_pstrcat(entry_pool, href, "/", (char *)NULL);
3554
href = apr_pstrcat(entry_pool, href, "/", SVN_VA_NULL);
3723
3803
resource->info->repos->base_url,
3724
3804
ap_escape_uri(resource->pool,
3725
3805
resource->info->r->uri),
3727
3807
str_root = apr_pstrcat(resource->pool,
3728
3808
resource->info->repos->base_url,
3729
3809
resource->info->repos->root_path,
3732
3812
serr = svn_subst_build_keywords3(&kw, keywords->data,
3733
3813
str_cmt_rev, str_uri, str_root,
3754
3834
apr_size_t bufsize = SVN__STREAM_CHUNK_SIZE;
3756
3836
/* read from the FS ... */
3757
serr = svn_stream_read(stream, block, &bufsize);
3837
serr = svn_stream_read_full(stream, block, &bufsize);
3758
3838
if (serr != NULL)
3760
3840
return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
3907
serr = svn_dirent_get_absolute(&src_repos_path,
3908
svn_repos_path(src->info->repos->repos,
3912
serr = svn_dirent_get_absolute(&dst_repos_path,
3913
svn_repos_path(dst->info->repos->repos,
3919
if (strcmp(src_repos_path, dst_repos_path) != 0)
3920
return dav_svn__new_error_tag
3921
(dst->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
3922
"Copy source and destination are in different repositories.",
3923
SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG);
3987
src_repos_path = svn_repos_path(src->info->repos->repos, src->pool);
3988
dst_repos_path = svn_repos_path(dst->info->repos->repos, dst->pool);
3990
if (strcmp(src_repos_path, dst_repos_path) != 0)
3992
/* Perhaps the source and dst repos use different path formats? */
3993
serr = svn_error_compose_create(
3994
svn_dirent_get_absolute(&src_repos_path, src_repos_path,
3996
svn_dirent_get_absolute(&dst_repos_path, dst_repos_path,
3999
if (!serr && (strcmp(src_repos_path, dst_repos_path) != 0))
4000
return dav_svn__new_error_svn(
4001
dst->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
4002
"Copy source and destination are in different repositories");
4005
serr = SVN_NO_ERROR;
3924
4009
serr = svn_fs_copy(src->info->root.root, /* root object of src rev*/
3925
4010
src->info->repos_path, /* relative path of src */
3926
4011
dst->info->root.root, /* root object of dst txn*/
4030
4115
if (resource->info->version_name < created_rev)
4032
4117
serr = svn_error_createf(SVN_ERR_RA_OUT_OF_DATE, NULL,
4033
"Item '%s' is out of date",
4118
resource->collection
4119
? "Directory '%s' is out of date"
4121
? "File '%s' is out of date"
4122
: "'%s' is out of date"),
4034
4123
resource->info->repos_path);
4035
4124
return dav_svn__convert_err(serr, HTTP_CONFLICT,
4036
4125
"Can't DELETE out-of-date resource",
4042
4131
incoming lock-tokens into the filesystem's access_t. Normally
4043
4132
they come in via 'If:' header, and get_resource()
4044
4133
automatically notices them and does this work for us. In the
4045
case of a directory deletion, however, svn clients are sending
4046
'child' lock-tokens in the DELETE request body. */
4134
case of a directory deletion, however, older subversion clients
4135
are sending 'child' lock-tokens in the non-standard DELETE
4048
4138
err = dav_svn__build_lock_hash(&locks, resource->info->r,
4049
4139
resource->info->repos_path, resource->pool);
4158
4248
} walker_ctx_t;
4250
/* Recursively walk a resource for walk(). When DEPTH != 0, recurse with
4251
DEPTH-1 on child nodes. WALK_ROOT should be TRUE for the root and will be
4252
FALSE for any descendants, to avoid unneeded work for every descendant
4161
4255
static dav_error *
4162
4256
do_walk(walker_ctx_t *ctx,
4258
svn_boolean_t walk_root,
4164
4259
apr_pool_t *scratch_pool)
4166
4261
const dav_walk_params *params = ctx->params;
4225
4320
uri_len = ctx->uri->len;
4226
4321
repos_len = ctx->repos_path->len;
4228
/* Tell our logging subsystem that we're listing a directory.
4325
/* Tell our logging subsystem that we're listing a directory.
4230
Note: if we cared, we could look at the 'User-Agent:' request
4231
header and distinguish an svn client ('svn ls') from a generic
4233
dav_svn__operational_log(&ctx->info,
4234
svn_log__get_dir(ctx->info.repos_path,
4236
TRUE, FALSE, SVN_DIRENT_ALL,
4327
Note: if we cared, we could look at the 'User-Agent:' request
4328
header and distinguish an svn client ('svn ls') from a generic
4330
dav_svn__operational_log(&ctx->info,
4331
svn_log__get_dir(ctx->info.repos_path,
4333
TRUE, FALSE, SVN_DIRENT_ALL,
4239
4337
/* fetch this collection's children */
4240
4338
serr = svn_fs_dir_entries(&children, ctx->info.root.root,
4299
4400
ctx->res.uri = ctx->uri->data;
4301
4402
/* recurse on this collection */
4302
err = do_walk(ctx, depth - 1, iterpool);
4403
err = do_walk(ctx, depth - 1, FALSE, iterpool);
4303
4404
if (err != NULL)
4406
svn_pool_destroy(iterpool);
4306
4410
/* restore the data */
4307
4411
ctx->res.collection = FALSE;
4381
4485
/* ### is the root already/always open? need to verify */
4383
4487
/* always return the error, and any/all multistatus responses */
4384
err = do_walk(&ctx, depth, params->pool);
4488
err = do_walk(&ctx, depth, TRUE, params->pool);
4385
4489
*response = ctx.wres.response;
4481
4585
/* if rev was specific, create baseline-collection URL */
4482
4586
path = dav_svn__build_uri(repos, DAV_SVN__BUILD_URI_BC,
4483
4587
priv->root.rev, priv->repos_path,
4588
FALSE /* add_href */, resource->pool);
4486
4590
path = svn_path_uri_encode(path, resource->pool);
4487
4591
priv->uri_path = svn_stringbuf_create(path, resource->pool);