00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "apr.h"
00022 #include "apr_file_io.h"
00023 #include "apr_strings.h"
00024 #include "apr_buckets.h"
00025
00026 #if APR_HAVE_STDIO_H
00027 #include <stdio.h>
00028 #endif
00029
00030 #include "httpd.h"
00031 #include "http_log.h"
00032 #include "http_protocol.h"
00033 #include "http_request.h"
00034
00035 #include "mod_dav.h"
00036 #include "repos.h"
00037
00038
00039
00040 #define DEBUG_GET_HANDLER 0
00041
00042 #define DAV_FS_COPY_BLOCKSIZE 16384
00043
00044
00045 struct dav_resource_private {
00046 apr_pool_t *pool;
00047 const char *pathname;
00048 apr_finfo_t finfo;
00049 };
00050
00051
00052 typedef struct {
00053
00054 const dav_walk_params *params;
00055
00056
00057 dav_walk_resource wres;
00058
00059 dav_resource res1;
00060 dav_resource_private info1;
00061 dav_buffer path1;
00062 dav_buffer uri_buf;
00063
00064
00065 dav_resource res2;
00066 dav_resource_private info2;
00067 dav_buffer path2;
00068
00069 dav_buffer locknull_buf;
00070
00071 } dav_fs_walker_context;
00072
00073 typedef struct {
00074 int is_move;
00075 dav_buffer work_buf;
00076
00077
00078 const dav_resource *res_dst;
00079
00080
00081 const dav_resource *root;
00082 apr_pool_t *pool;
00083
00084 } dav_fs_copymove_walk_ctx;
00085
00086
00087 #define DAV_WALKTYPE_HIDDEN 0x4000
00088
00089
00090 #define DAV_WALKTYPE_POSTFIX 0x8000
00091
00092 #define DAV_CALLTYPE_POSTFIX 1000
00093
00094
00095
00096 extern const dav_hooks_locks dav_hooks_locks_fs;
00097
00098
00099 static const dav_hooks_repository dav_hooks_repository_fs;
00100 static const dav_hooks_liveprop dav_hooks_liveprop_fs;
00101
00102
00103
00104
00105
00106 static const char * const dav_fs_namespace_uris[] =
00107 {
00108 "DAV:",
00109 "http://apache.org/dav/props/",
00110
00111 NULL
00112 };
00113 enum {
00114 DAV_FS_URI_DAV,
00115 DAV_FS_URI_MYPROPS
00116 };
00117
00118
00119
00120
00121
00122
00123 #ifndef WIN32
00124 #define DAV_FS_HAS_EXECUTABLE
00125 #endif
00126
00127
00128
00129
00130 #define DAV_PROPID_FS_executable 1
00131
00132 static const dav_liveprop_spec dav_fs_props[] =
00133 {
00134
00135 {
00136 DAV_FS_URI_DAV,
00137 "creationdate",
00138 DAV_PROPID_creationdate,
00139 0
00140 },
00141 {
00142 DAV_FS_URI_DAV,
00143 "getcontentlength",
00144 DAV_PROPID_getcontentlength,
00145 0
00146 },
00147 {
00148 DAV_FS_URI_DAV,
00149 "getetag",
00150 DAV_PROPID_getetag,
00151 0
00152 },
00153 {
00154 DAV_FS_URI_DAV,
00155 "getlastmodified",
00156 DAV_PROPID_getlastmodified,
00157 0
00158 },
00159
00160
00161 {
00162 DAV_FS_URI_MYPROPS,
00163 "executable",
00164 DAV_PROPID_FS_executable,
00165 0
00166 },
00167
00168 { 0 }
00169 };
00170
00171 static const dav_liveprop_group dav_fs_liveprop_group =
00172 {
00173 dav_fs_props,
00174 dav_fs_namespace_uris,
00175 &dav_hooks_liveprop_fs
00176 };
00177
00178
00179
00180 struct dav_stream {
00181 apr_pool_t *p;
00182 apr_file_t *f;
00183 const char *pathname;
00184 };
00185
00186
00187
00188 #define MAP_IO2HTTP(e) (APR_STATUS_IS_ENOSPC(e) ? HTTP_INSUFFICIENT_STORAGE : \
00189 HTTP_INTERNAL_SERVER_ERROR)
00190
00191
00192 static dav_error * dav_fs_walk(const dav_walk_params *params, int depth,
00193 dav_response **response);
00194 static dav_error * dav_fs_internal_walk(const dav_walk_params *params,
00195 int depth, int is_move,
00196 const dav_resource *root_dst,
00197 dav_response **response);
00198
00199
00200
00201
00202
00203 apr_pool_t *dav_fs_pool(const dav_resource *resource)
00204 {
00205 return resource->info->pool;
00206 }
00207
00208 const char *dav_fs_pathname(const dav_resource *resource)
00209 {
00210 return resource->info->pathname;
00211 }
00212
00213 dav_error * dav_fs_dir_file_name(
00214 const dav_resource *resource,
00215 const char **dirpath_p,
00216 const char **fname_p)
00217 {
00218 dav_resource_private *ctx = resource->info;
00219
00220 if (resource->collection) {
00221 *dirpath_p = ctx->pathname;
00222 if (fname_p != NULL)
00223 *fname_p = NULL;
00224 }
00225 else {
00226 const char *testpath, *rootpath;
00227 char *dirpath = ap_make_dirstr_parent(ctx->pool, ctx->pathname);
00228 apr_size_t dirlen = strlen(dirpath);
00229 apr_status_t rv = APR_SUCCESS;
00230
00231 testpath = dirpath;
00232 if (dirlen > 0) {
00233 rv = apr_filepath_root(&rootpath, &testpath, 0, ctx->pool);
00234 }
00235
00236
00237
00238 if ((rv == APR_SUCCESS && testpath && *testpath)
00239 || rv == APR_ERELATIVE) {
00240 if (dirpath[dirlen - 1] == '/') {
00241 dirpath[dirlen - 1] = '\0';
00242 }
00243 }
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255 if (rv == APR_SUCCESS || rv == APR_ERELATIVE) {
00256 *dirpath_p = dirpath;
00257 if (fname_p != NULL)
00258 *fname_p = ctx->pathname + dirlen;
00259 }
00260 else {
00261 return dav_new_error(ctx->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
00262 "An incomplete/bad path was found in "
00263 "dav_fs_dir_file_name.");
00264 }
00265 }
00266
00267 return NULL;
00268 }
00269
00270
00271
00272 static void dav_format_time(int style, apr_time_t sec, char *buf)
00273 {
00274 apr_time_exp_t tms;
00275
00276
00277 (void) apr_time_exp_gmt(&tms, sec);
00278
00279 if (style == DAV_STYLE_ISO8601) {
00280
00281
00282
00283 sprintf(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2dZ",
00284 tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday,
00285 tms.tm_hour, tms.tm_min, tms.tm_sec);
00286 return;
00287 }
00288
00289
00290
00291
00292 sprintf(buf,
00293 "%s, %.2d %s %d %.2d:%.2d:%.2d GMT",
00294 apr_day_snames[tms.tm_wday],
00295 tms.tm_mday, apr_month_snames[tms.tm_mon],
00296 tms.tm_year + 1900,
00297 tms.tm_hour, tms.tm_min, tms.tm_sec);
00298 }
00299
00300 static dav_error * dav_fs_copymove_file(
00301 int is_move,
00302 apr_pool_t * p,
00303 const char *src,
00304 const char *dst,
00305 dav_buffer *pbuf)
00306 {
00307 dav_buffer work_buf = { 0 };
00308 apr_file_t *inf = NULL;
00309 apr_file_t *outf = NULL;
00310 apr_status_t status;
00311
00312 if (pbuf == NULL)
00313 pbuf = &work_buf;
00314
00315 dav_set_bufsize(p, pbuf, DAV_FS_COPY_BLOCKSIZE);
00316
00317 if ((apr_file_open(&inf, src, APR_READ | APR_BINARY, APR_OS_DEFAULT, p))
00318 != APR_SUCCESS) {
00319
00320 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00321 "Could not open file for reading");
00322 }
00323
00324
00325 status = apr_file_open(&outf, dst, APR_WRITE | APR_CREATE | APR_TRUNCATE
00326 | APR_BINARY, APR_OS_DEFAULT, p);
00327 if (status != APR_SUCCESS) {
00328 apr_file_close(inf);
00329
00330 return dav_new_error(p, MAP_IO2HTTP(status), 0,
00331 "Could not open file for writing");
00332 }
00333
00334 while (1) {
00335 apr_size_t len = DAV_FS_COPY_BLOCKSIZE;
00336
00337 status = apr_file_read(inf, pbuf->buf, &len);
00338 if (status != APR_SUCCESS && status != APR_EOF) {
00339 apr_file_close(inf);
00340 apr_file_close(outf);
00341
00342 if (apr_file_remove(dst, p) != APR_SUCCESS) {
00343
00344
00345
00346 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00347 "Could not delete output after read "
00348 "failure. Server is now in an "
00349 "inconsistent state.");
00350 }
00351
00352
00353 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00354 "Could not read input file");
00355 }
00356
00357 if (status == APR_EOF)
00358 break;
00359
00360
00361 status = apr_file_write_full(outf, pbuf->buf, len, NULL);
00362 if (status != APR_SUCCESS) {
00363 apr_file_close(inf);
00364 apr_file_close(outf);
00365
00366 if (apr_file_remove(dst, p) != APR_SUCCESS) {
00367
00368
00369
00370 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00371 "Could not delete output after write "
00372 "failure. Server is now in an "
00373 "inconsistent state.");
00374 }
00375
00376 return dav_new_error(p, MAP_IO2HTTP(status), 0,
00377 "Could not write output file");
00378 }
00379 }
00380
00381 apr_file_close(inf);
00382 apr_file_close(outf);
00383
00384 if (is_move && apr_file_remove(src, p) != APR_SUCCESS) {
00385 dav_error *err;
00386 int save_errno = errno;
00387
00388 if (apr_file_remove(dst, p) != APR_SUCCESS) {
00389
00390
00391
00392
00393 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00394 "Could not remove source or destination "
00395 "file. Server is now in an inconsistent "
00396 "state.");
00397 }
00398
00399
00400 err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00401 "Could not remove source file after move. "
00402 "Destination was removed to ensure consistency.");
00403 err->save_errno = save_errno;
00404 return err;
00405 }
00406
00407 return NULL;
00408 }
00409
00410
00411
00412 static dav_error * dav_fs_copymove_state(
00413 int is_move,
00414 apr_pool_t * p,
00415 const char *src_dir, const char *src_file,
00416 const char *dst_dir, const char *dst_file,
00417 dav_buffer *pbuf)
00418 {
00419 apr_finfo_t src_finfo;
00420 apr_finfo_t dst_state_finfo;
00421 apr_status_t rv;
00422 const char *src;
00423 const char *dst;
00424
00425
00426 src = apr_pstrcat(p, src_dir, "/" DAV_FS_STATE_DIR "/", src_file, NULL);
00427
00428
00429 rv = apr_stat(&src_finfo, src, APR_FINFO_NORM, p);
00430 if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) {
00431 return NULL;
00432 }
00433
00434
00435 dst = apr_pstrcat(p, dst_dir, "/" DAV_FS_STATE_DIR, NULL);
00436
00437
00438
00439
00440 rv = apr_dir_make(dst, APR_OS_DEFAULT, p);
00441 if (rv != APR_SUCCESS) {
00442 if (!APR_STATUS_IS_EEXIST(rv)) {
00443
00444 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00445 "Could not create internal state directory");
00446 }
00447 }
00448
00449
00450 rv = apr_stat(&dst_state_finfo, dst, APR_FINFO_NORM, p);
00451 if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) {
00452
00453
00454 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00455 "State directory disappeared");
00456 }
00457
00458
00459 if (dst_state_finfo.filetype != APR_DIR) {
00460
00461
00462 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00463 "State directory is actually a file");
00464 }
00465
00466
00467 dst = apr_pstrcat(p, dst, "/", dst_file, NULL);
00468
00469
00470 if (is_move && src_finfo.device == dst_state_finfo.device) {
00471
00472 if (apr_file_rename(src, dst, p) != APR_SUCCESS) {
00473
00474 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00475 "Could not move state file.");
00476 }
00477 }
00478 else
00479 {
00480
00481 return dav_fs_copymove_file(is_move, p, src, dst, pbuf);
00482 }
00483
00484 return NULL;
00485 }
00486
00487 static dav_error *dav_fs_copymoveset(int is_move, apr_pool_t *p,
00488 const dav_resource *src,
00489 const dav_resource *dst,
00490 dav_buffer *pbuf)
00491 {
00492 const char *src_dir;
00493 const char *src_file;
00494 const char *src_state1;
00495 const char *src_state2;
00496 const char *dst_dir;
00497 const char *dst_file;
00498 const char *dst_state1;
00499 const char *dst_state2;
00500 dav_error *err;
00501
00502
00503
00504 (void) dav_fs_dir_file_name(src, &src_dir, &src_file);
00505 (void) dav_fs_dir_file_name(dst, &dst_dir, &dst_file);
00506
00507
00508 dav_dbm_get_statefiles(p, src_file, &src_state1, &src_state2);
00509 dav_dbm_get_statefiles(p, dst_file, &dst_state1, &dst_state2);
00510 #if DAV_DEBUG
00511 if ((src_state2 != NULL && dst_state2 == NULL) ||
00512 (src_state2 == NULL && dst_state2 != NULL)) {
00513 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00514 "DESIGN ERROR: dav_dbm_get_statefiles() "
00515 "returned inconsistent results.");
00516 }
00517 #endif
00518
00519 err = dav_fs_copymove_state(is_move, p,
00520 src_dir, src_state1,
00521 dst_dir, dst_state1,
00522 pbuf);
00523
00524 if (err == NULL && src_state2 != NULL) {
00525 err = dav_fs_copymove_state(is_move, p,
00526 src_dir, src_state2,
00527 dst_dir, dst_state2,
00528 pbuf);
00529
00530 if (err != NULL) {
00531
00532
00533
00534
00535
00536 err->status = HTTP_INTERNAL_SERVER_ERROR;
00537 err->desc =
00538 "Could not fully copy/move the properties. "
00539 "The server is now in an inconsistent state.";
00540 }
00541 }
00542
00543 return err;
00544 }
00545
00546 static dav_error *dav_fs_deleteset(apr_pool_t *p, const dav_resource *resource)
00547 {
00548 const char *dirpath;
00549 const char *fname;
00550 const char *state1;
00551 const char *state2;
00552 const char *pathname;
00553 apr_status_t status;
00554
00555
00556
00557 (void) dav_fs_dir_file_name(resource, &dirpath, &fname);
00558 dav_dbm_get_statefiles(p, fname, &state1, &state2);
00559
00560
00561 pathname = apr_pstrcat(p,
00562 dirpath,
00563 "/" DAV_FS_STATE_DIR "/",
00564 state1,
00565 NULL);
00566
00567
00568 if ((status = apr_file_remove(pathname, p)) != APR_SUCCESS
00569 && !APR_STATUS_IS_ENOENT(status)) {
00570 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00571 "Could not remove properties.");
00572 }
00573
00574 if (state2 != NULL) {
00575
00576 pathname = apr_pstrcat(p,
00577 dirpath,
00578 "/" DAV_FS_STATE_DIR "/",
00579 state2,
00580 NULL);
00581
00582 if ((status = apr_file_remove(pathname, p)) != APR_SUCCESS
00583 && !APR_STATUS_IS_ENOENT(status)) {
00584
00585 return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
00586 "Could not fully remove properties. "
00587 "The server is now in an inconsistent "
00588 "state.");
00589 }
00590 }
00591
00592 return NULL;
00593 }
00594
00595
00596
00597
00598
00599
00600 static dav_error * dav_fs_get_resource(
00601 request_rec *r,
00602 const char *root_dir,
00603 const char *label,
00604 int use_checked_in,
00605 dav_resource **result_resource)
00606 {
00607 dav_resource_private *ctx;
00608 dav_resource *resource;
00609 char *s;
00610 char *filename;
00611 apr_size_t len;
00612
00613
00614
00615
00616 ctx = apr_pcalloc(r->pool, sizeof(*ctx));
00617 ctx->finfo = r->finfo;
00618
00619
00620 ctx->pool = r->pool;
00621
00622
00623 #if 0
00624
00625 filename = r->case_preserved_filename;
00626 #else
00627 filename = r->filename;
00628 #endif
00629
00630
00631
00632
00633
00634
00635
00636 s = apr_pstrcat(r->pool, filename, r->path_info, NULL);
00637
00638
00639 len = strlen(s);
00640 if (len > 1 && s[len - 1] == '/') {
00641 s[len - 1] = '\0';
00642 }
00643 ctx->pathname = s;
00644
00645
00646 resource = apr_pcalloc(r->pool, sizeof(*resource));
00647 resource->type = DAV_RESOURCE_TYPE_REGULAR;
00648 resource->info = ctx;
00649 resource->hooks = &dav_hooks_repository_fs;
00650 resource->pool = r->pool;
00651
00652
00653 len = strlen(r->uri);
00654 if (len > 1 && r->uri[len - 1] == '/') {
00655 s = apr_pstrdup(r->pool, r->uri);
00656 s[len - 1] = '\0';
00657 resource->uri = s;
00658 }
00659 else {
00660 resource->uri = r->uri;
00661 }
00662
00663 if (r->finfo.filetype != 0) {
00664 resource->exists = 1;
00665 resource->collection = r->finfo.filetype == APR_DIR;
00666
00667
00668
00669 if (r->path_info != NULL && *r->path_info != '\0') {
00670 if (resource->collection) {
00671
00672 if (*r->path_info != '/' || r->path_info[1] != '\0') {
00673
00674
00675
00676
00677
00678 resource->exists = 0;
00679 resource->collection = 0;
00680 }
00681 }
00682 else
00683 {
00684
00685
00686
00687
00688
00689 return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0,
00690 "The URL contains extraneous path "
00691 "components. The resource could not "
00692 "be identified.");
00693 }
00694
00695
00696 if (!resource->exists) {
00697 ctx->finfo.filetype = 0;
00698 }
00699 }
00700 }
00701
00702 *result_resource = resource;
00703 return NULL;
00704 }
00705
00706 static dav_error * dav_fs_get_parent_resource(const dav_resource *resource,
00707 dav_resource **result_parent)
00708 {
00709 dav_resource_private *ctx = resource->info;
00710 dav_resource_private *parent_ctx;
00711 dav_resource *parent_resource;
00712 apr_status_t rv;
00713 char *dirpath;
00714 const char *testroot;
00715 const char *testpath;
00716
00717
00718 if (strcmp(resource->uri, "/") == 0) {
00719 *result_parent = NULL;
00720 return NULL;
00721 }
00722
00723
00724
00725
00726
00727
00728 testpath = ctx->pathname;
00729 rv = apr_filepath_root(&testroot, &testpath, 0, ctx->pool);
00730 if ((rv != APR_SUCCESS && rv != APR_ERELATIVE)
00731 || !testpath || !*testpath) {
00732 *result_parent = NULL;
00733 return NULL;
00734 }
00735
00736
00737
00738
00739 parent_ctx = apr_pcalloc(ctx->pool, sizeof(*parent_ctx));
00740
00741
00742 parent_ctx->pool = ctx->pool;
00743
00744 dirpath = ap_make_dirstr_parent(ctx->pool, ctx->pathname);
00745 if (strlen(dirpath) > 1 && dirpath[strlen(dirpath) - 1] == '/')
00746 dirpath[strlen(dirpath) - 1] = '\0';
00747 parent_ctx->pathname = dirpath;
00748
00749 parent_resource = apr_pcalloc(ctx->pool, sizeof(*parent_resource));
00750 parent_resource->info = parent_ctx;
00751 parent_resource->collection = 1;
00752 parent_resource->hooks = &dav_hooks_repository_fs;
00753 parent_resource->pool = resource->pool;
00754
00755 if (resource->uri != NULL) {
00756 char *uri = ap_make_dirstr_parent(ctx->pool, resource->uri);
00757 if (strlen(uri) > 1 && uri[strlen(uri) - 1] == '/')
00758 uri[strlen(uri) - 1] = '\0';
00759 parent_resource->uri = uri;
00760 }
00761
00762 rv = apr_stat(&parent_ctx->finfo, parent_ctx->pathname,
00763 APR_FINFO_NORM, ctx->pool);
00764 if (rv == APR_SUCCESS || rv == APR_INCOMPLETE) {
00765 parent_resource->exists = 1;
00766 }
00767
00768 *result_parent = parent_resource;
00769 return NULL;
00770 }
00771
00772 static int dav_fs_is_same_resource(
00773 const dav_resource *res1,
00774 const dav_resource *res2)
00775 {
00776 dav_resource_private *ctx1 = res1->info;
00777 dav_resource_private *ctx2 = res2->info;
00778
00779 if (res1->hooks != res2->hooks)
00780 return 0;
00781
00782 if ((ctx1->finfo.filetype != 0) && (ctx2->finfo.filetype != 0)
00783 && (ctx1->finfo.valid & ctx2->finfo.valid & APR_FINFO_INODE)) {
00784 return ctx1->finfo.inode == ctx2->finfo.inode;
00785 }
00786 else {
00787 return strcmp(ctx1->pathname, ctx2->pathname) == 0;
00788 }
00789 }
00790
00791 static int dav_fs_is_parent_resource(
00792 const dav_resource *res1,
00793 const dav_resource *res2)
00794 {
00795 dav_resource_private *ctx1 = res1->info;
00796 dav_resource_private *ctx2 = res2->info;
00797 apr_size_t len1 = strlen(ctx1->pathname);
00798 apr_size_t len2;
00799
00800 if (res1->hooks != res2->hooks)
00801 return 0;
00802
00803
00804 len2 = strlen(ctx2->pathname);
00805
00806 return (len2 > len1
00807 && memcmp(ctx1->pathname, ctx2->pathname, len1) == 0
00808 && ctx2->pathname[len1] == '/');
00809 }
00810
00811 static dav_error * dav_fs_open_stream(const dav_resource *resource,
00812 dav_stream_mode mode,
00813 dav_stream **stream)
00814 {
00815 apr_pool_t *p = resource->info->pool;
00816 dav_stream *ds = apr_pcalloc(p, sizeof(*ds));
00817 apr_int32_t flags;
00818 apr_status_t rv;
00819
00820 switch (mode) {
00821 default:
00822 flags = APR_READ | APR_BINARY;
00823 break;
00824
00825 case DAV_MODE_WRITE_TRUNC:
00826 flags = APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BINARY;
00827 break;
00828 case DAV_MODE_WRITE_SEEKABLE:
00829 flags = APR_WRITE | APR_CREATE | APR_BINARY;
00830 break;
00831 }
00832
00833 ds->p = p;
00834 ds->pathname = resource->info->pathname;
00835 rv = apr_file_open(&ds->f, ds->pathname, flags, APR_OS_DEFAULT, ds->p);
00836 if (rv != APR_SUCCESS) {
00837 return dav_new_error(p, MAP_IO2HTTP(rv), 0,
00838 "An error occurred while opening a resource.");
00839 }
00840
00841
00842
00843 *stream = ds;
00844 return NULL;
00845 }
00846
00847 static dav_error * dav_fs_close_stream(dav_stream *stream, int commit)
00848 {
00849 apr_file_close(stream->f);
00850
00851 if (!commit) {
00852 if (apr_file_remove(stream->pathname, stream->p) != APR_SUCCESS) {
00853
00854 return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
00855 "There was a problem removing (rolling "
00856 "back) the resource "
00857 "when it was being closed.");
00858 }
00859 }
00860
00861 return NULL;
00862 }
00863
00864 static dav_error * dav_fs_write_stream(dav_stream *stream,
00865 const void *buf, apr_size_t bufsize)
00866 {
00867 apr_status_t status;
00868
00869 status = apr_file_write_full(stream->f, buf, bufsize, NULL);
00870 if (APR_STATUS_IS_ENOSPC(status)) {
00871 return dav_new_error(stream->p, HTTP_INSUFFICIENT_STORAGE, 0,
00872 "There is not enough storage to write to "
00873 "this resource.");
00874 }
00875 else if (status != APR_SUCCESS) {
00876
00877 return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
00878 "An error occurred while writing to a "
00879 "resource.");
00880 }
00881 return NULL;
00882 }
00883
00884 static dav_error * dav_fs_seek_stream(dav_stream *stream, apr_off_t abs_pos)
00885 {
00886 if (apr_file_seek(stream->f, APR_SET, &abs_pos) != APR_SUCCESS) {
00887
00888
00889
00890 return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
00891 "Could not seek to specified position in the "
00892 "resource.");
00893 }
00894 return NULL;
00895 }
00896
00897
00898 #if DEBUG_GET_HANDLER
00899
00900
00901
00902
00903 static dav_error * dav_fs_set_headers(request_rec *r,
00904 const dav_resource *resource)
00905 {
00906
00907 if (!resource->exists)
00908 return NULL;
00909
00910
00911 ap_update_mtime(r, resource->info->finfo.mtime);
00912
00913
00914 ap_set_last_modified(r);
00915 ap_set_etag(r);
00916
00917
00918 apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
00919
00920
00921 ap_set_content_length(r, resource->info->finfo.size);
00922
00923
00924
00925
00926 return NULL;
00927 }
00928
00929 static dav_error * dav_fs_deliver(const dav_resource *resource,
00930 ap_filter_t *output)
00931 {
00932 apr_pool_t *pool = resource->pool;
00933 apr_bucket_brigade *bb;
00934 apr_file_t *fd;
00935 apr_status_t status;
00936 apr_bucket *bkt;
00937
00938
00939 if (resource->type != DAV_RESOURCE_TYPE_REGULAR
00940 && resource->type != DAV_RESOURCE_TYPE_VERSION
00941 && resource->type != DAV_RESOURCE_TYPE_WORKING) {
00942 return dav_new_error(pool, HTTP_CONFLICT, 0,
00943 "Cannot GET this type of resource.");
00944 }
00945 if (resource->collection) {
00946 return dav_new_error(pool, HTTP_CONFLICT, 0,
00947 "There is no default response to GET for a "
00948 "collection.");
00949 }
00950
00951 if ((status = apr_file_open(&fd, resource->info->pathname,
00952 APR_READ | APR_BINARY, 0,
00953 pool)) != APR_SUCCESS) {
00954 return dav_new_error(pool, HTTP_FORBIDDEN, 0,
00955 "File permissions deny server access.");
00956 }
00957
00958 bb = apr_brigade_create(pool, output->c->bucket_alloc);
00959
00960
00961 bkt = apr_bucket_file_create(fd, 0,
00962 (apr_size_t)resource->info->finfo.size,
00963 pool, output->c->bucket_alloc);
00964 APR_BRIGADE_INSERT_TAIL(bb, bkt);
00965
00966 bkt = apr_bucket_eos_create(output->c->bucket_alloc);
00967 APR_BRIGADE_INSERT_TAIL(bb, bkt);
00968
00969 if ((status = ap_pass_brigade(output, bb)) != APR_SUCCESS) {
00970 return dav_new_error(pool, HTTP_FORBIDDEN, 0,
00971 "Could not write contents to filter.");
00972 }
00973
00974 return NULL;
00975 }
00976
00977 #endif
00978
00979
00980 static dav_error * dav_fs_create_collection(dav_resource *resource)
00981 {
00982 dav_resource_private *ctx = resource->info;
00983 apr_status_t status;
00984
00985 status = apr_dir_make(ctx->pathname, APR_OS_DEFAULT, ctx->pool);
00986 if (APR_STATUS_IS_ENOSPC(status)) {
00987 return dav_new_error(ctx->pool, HTTP_INSUFFICIENT_STORAGE, 0,
00988 "There is not enough storage to create "
00989 "this collection.");
00990 }
00991 else if (APR_STATUS_IS_ENOENT(status)) {
00992 return dav_new_error(ctx->pool, HTTP_CONFLICT, 0,
00993 "Cannot create collection; intermediate "
00994 "collection does not exist.");
00995 }
00996 else if (status != APR_SUCCESS) {
00997
00998 return dav_new_error(ctx->pool, HTTP_FORBIDDEN, 0,
00999 "Unable to create collection.");
01000 }
01001
01002
01003 resource->exists = 1;
01004 resource->collection = 1;
01005
01006 return NULL;
01007 }
01008
01009 static dav_error * dav_fs_copymove_walker(dav_walk_resource *wres,
01010 int calltype)
01011 {
01012 dav_fs_copymove_walk_ctx *ctx = wres->walk_ctx;
01013 dav_resource_private *srcinfo = wres->resource->info;
01014 dav_resource_private *dstinfo = ctx->res_dst->info;
01015 dav_error *err = NULL;
01016
01017 if (wres->resource->collection) {
01018 if (calltype == DAV_CALLTYPE_POSTFIX) {
01019
01020
01021
01022
01023 (void) apr_dir_remove(srcinfo->pathname, ctx->pool);
01024 }
01025 else {
01026
01027 if (apr_dir_make(dstinfo->pathname, APR_OS_DEFAULT,
01028 ctx->pool) != APR_SUCCESS) {
01029
01030
01031 err = dav_new_error(ctx->pool, HTTP_FORBIDDEN, 0, NULL);
01032 }
01033 }
01034 }
01035 else {
01036 err = dav_fs_copymove_file(ctx->is_move, ctx->pool,
01037 srcinfo->pathname, dstinfo->pathname,
01038 &ctx->work_buf);
01039
01040 }
01041
01042
01043
01044
01045
01046
01047
01048
01049
01050
01051
01052
01053
01054 if (err != NULL
01055 && !ap_is_HTTP_SERVER_ERROR(err->status)
01056 && (ctx->is_move
01057 || !dav_fs_is_same_resource(wres->resource, ctx->root))) {
01058
01059 dav_add_response(wres, err->status, NULL);
01060
01061
01062 return NULL;
01063 }
01064
01065 return err;
01066 }
01067
01068 static dav_error *dav_fs_copymove_resource(
01069 int is_move,
01070 const dav_resource *src,
01071 const dav_resource *dst,
01072 int depth,
01073 dav_response **response)
01074 {
01075 dav_error *err = NULL;
01076 dav_buffer work_buf = { 0 };
01077
01078 *response = NULL;
01079
01080
01081
01082
01083 if (src->collection) {
01084 dav_walk_params params = { 0 };
01085 dav_response *multi_status;
01086
01087 params.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_HIDDEN;
01088 params.func = dav_fs_copymove_walker;
01089 params.pool = src->info->pool;
01090 params.root = src;
01091
01092
01093
01094
01095 if (is_move)
01096 params.walk_type |= DAV_WALKTYPE_POSTFIX;
01097
01098
01099
01100 if ((err = dav_fs_internal_walk(¶ms, depth, is_move, dst,
01101 &multi_status)) != NULL) {
01102
01103 return err;
01104 }
01105
01106 if ((*response = multi_status) != NULL) {
01107
01108 return dav_new_error(src->info->pool, HTTP_MULTI_STATUS, 0,
01109 "Error(s) occurred on some resources during "
01110 "the COPY/MOVE process.");
01111 }
01112
01113 return NULL;
01114 }
01115
01116
01117 if ((err = dav_fs_copymove_file(is_move, src->info->pool,
01118 src->info->pathname, dst->info->pathname,
01119 &work_buf)) != NULL) {
01120
01121 return err;
01122 }
01123
01124
01125 return dav_fs_copymoveset(is_move, src->info->pool, src, dst, &work_buf);
01126 }
01127
01128 static dav_error * dav_fs_copy_resource(
01129 const dav_resource *src,
01130 dav_resource *dst,
01131 int depth,
01132 dav_response **response)
01133 {
01134 dav_error *err;
01135
01136 #if DAV_DEBUG
01137 if (src->hooks != dst->hooks) {
01138
01139
01140
01141
01142 return dav_new_error(src->info->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
01143 "DESIGN ERROR: a mix of repositories "
01144 "was passed to copy_resource.");
01145 }
01146 #endif
01147
01148 if ((err = dav_fs_copymove_resource(0, src, dst, depth,
01149 response)) == NULL) {
01150
01151
01152 dst->exists = 1;
01153 dst->collection = src->collection;
01154 }
01155
01156 return err;
01157 }
01158
01159 static dav_error * dav_fs_move_resource(
01160 dav_resource *src,
01161 dav_resource *dst,
01162 dav_response **response)
01163 {
01164 dav_resource_private *srcinfo = src->info;
01165 dav_resource_private *dstinfo = dst->info;
01166 dav_error *err;
01167 int can_rename = 0;
01168
01169 #if DAV_DEBUG
01170 if (src->hooks != dst->hooks) {
01171
01172
01173
01174
01175 return dav_new_error(src->info->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
01176 "DESIGN ERROR: a mix of repositories "
01177 "was passed to move_resource.");
01178 }
01179 #endif
01180
01181
01182
01183
01184 if (dstinfo->finfo.filetype != 0) {
01185 if (dstinfo->finfo.device == srcinfo->finfo.device) {
01186
01187 can_rename = 1;
01188 }
01189 }
01190 else {
01191 const char *dirpath;
01192 apr_finfo_t finfo;
01193 apr_status_t rv;
01194
01195
01196
01197
01198 dirpath = ap_make_dirstr_parent(dstinfo->pool, dstinfo->pathname);
01199
01200
01201
01202
01203
01204 rv = apr_stat(&finfo, dirpath, APR_FINFO_DEV, dstinfo->pool);
01205 if ((rv == APR_SUCCESS || rv == APR_INCOMPLETE)
01206 && (finfo.valid & srcinfo->finfo.valid & APR_FINFO_DEV)
01207 && (finfo.device == srcinfo->finfo.device)) {
01208 can_rename = 1;
01209 }
01210 }
01211
01212
01213 if (!can_rename) {
01214 if ((err = dav_fs_copymove_resource(1, src, dst, DAV_INFINITY,
01215 response)) == NULL) {
01216
01217 dst->exists = 1;
01218 dst->collection = src->collection;
01219 src->exists = 0;
01220 src->collection = 0;
01221 }
01222
01223 return err;
01224 }
01225
01226
01227
01228
01229 *response = NULL;
01230
01231
01232 if (apr_file_rename(srcinfo->pathname, dstinfo->pathname,
01233 srcinfo->pool) != APR_SUCCESS) {
01234
01235 return dav_new_error(srcinfo->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
01236 "Could not rename resource.");
01237 }
01238
01239
01240 dst->exists = 1;
01241 dst->collection = src->collection;
01242 src->exists = 0;
01243 src->collection = 0;
01244
01245 if ((err = dav_fs_copymoveset(1, src->info->pool,
01246 src, dst, NULL)) == NULL) {
01247
01248 return NULL;
01249 }
01250
01251
01252 if (apr_file_rename(dstinfo->pathname, srcinfo->pathname,
01253 srcinfo->pool) != APR_SUCCESS) {
01254
01255 return dav_push_error(srcinfo->pool,
01256 HTTP_INTERNAL_SERVER_ERROR, 0,
01257 "The resource was moved, but a failure "
01258 "occurred during the move of its "
01259 "properties. The resource could not be "
01260 "restored to its original location. The "
01261 "server is now in an inconsistent state.",
01262 err);
01263 }
01264
01265
01266 src->exists = 1;
01267 src->collection = dst->collection;
01268 dst->exists = 0;
01269 dst->collection = 0;
01270
01271
01272 return dav_push_error(srcinfo->pool,
01273 HTTP_INTERNAL_SERVER_ERROR, 0,
01274 "The resource was moved, but a failure "
01275 "occurred during the move of its properties. "
01276 "The resource was moved back to its original "
01277 "location, but its properties may have been "
01278 "partially moved. The server may be in an "
01279 "inconsistent state.",
01280 err);
01281 }
01282
01283 static dav_error * dav_fs_delete_walker(dav_walk_resource *wres, int calltype)
01284 {
01285 dav_resource_private *info = wres->resource->info;
01286
01287
01288
01289
01290 if (wres->resource->exists &&
01291 (!wres->resource->collection || calltype == DAV_CALLTYPE_POSTFIX)) {
01292
01293 apr_status_t result;
01294
01295 result = wres->resource->collection
01296 ? apr_dir_remove(info->pathname, wres->pool)
01297 : apr_file_remove(info->pathname, wres->pool);
01298
01299
01300
01301
01302
01303
01304
01305
01306 if (result != APR_SUCCESS) {
01307
01308
01309
01310 dav_add_response(wres, HTTP_FORBIDDEN, NULL);
01311 }
01312 }
01313
01314 return NULL;
01315 }
01316
01317 static dav_error * dav_fs_remove_resource(dav_resource *resource,
01318 dav_response **response)
01319 {
01320 dav_resource_private *info = resource->info;
01321
01322 *response = NULL;
01323
01324
01325
01326
01327 if (resource->collection) {
01328 dav_walk_params params = { 0 };
01329 dav_error *err = NULL;
01330 dav_response *multi_status;
01331
01332 params.walk_type = (DAV_WALKTYPE_NORMAL
01333 | DAV_WALKTYPE_HIDDEN
01334 | DAV_WALKTYPE_POSTFIX);
01335 params.func = dav_fs_delete_walker;
01336 params.pool = info->pool;
01337 params.root = resource;
01338
01339 if ((err = dav_fs_walk(¶ms, DAV_INFINITY,
01340 &multi_status)) != NULL) {
01341
01342 return err;
01343 }
01344
01345 if ((*response = multi_status) != NULL) {
01346
01347 return dav_new_error(info->pool, HTTP_MULTI_STATUS, 0,
0