📄 repos.c
字号:
if (strlen(uri) > 1 && uri[strlen(uri) - 1] == '/')
uri[strlen(uri) - 1] = '\0';
parent_resource->uri = uri;
}
rv = apr_stat(&parent_ctx->finfo, parent_ctx->pathname,
APR_FINFO_NORM, ctx->pool);
if (rv == APR_SUCCESS || rv == APR_INCOMPLETE) {
parent_resource->exists = 1;
}
*result_parent = parent_resource;
return NULL;
}
static int dav_fs_is_same_resource(
const dav_resource *res1,
const dav_resource *res2)
{
dav_resource_private *ctx1 = res1->info;
dav_resource_private *ctx2 = res2->info;
if (res1->hooks != res2->hooks)
return 0;
if ((ctx1->finfo.filetype != 0) && (ctx2->finfo.filetype != 0)
&& (ctx1->finfo.valid & ctx2->finfo.valid & APR_FINFO_INODE)) {
return ctx1->finfo.inode == ctx2->finfo.inode;
}
else {
return strcmp(ctx1->pathname, ctx2->pathname) == 0;
}
}
static int dav_fs_is_parent_resource(
const dav_resource *res1,
const dav_resource *res2)
{
dav_resource_private *ctx1 = res1->info;
dav_resource_private *ctx2 = res2->info;
apr_size_t len1 = strlen(ctx1->pathname);
apr_size_t len2;
if (res1->hooks != res2->hooks)
return 0;
/* it is safe to use ctx2 now */
len2 = strlen(ctx2->pathname);
return (len2 > len1
&& memcmp(ctx1->pathname, ctx2->pathname, len1) == 0
&& ctx2->pathname[len1] == '/');
}
static dav_error * dav_fs_open_stream(const dav_resource *resource,
dav_stream_mode mode,
dav_stream **stream)
{
apr_pool_t *p = resource->info->pool;
dav_stream *ds = apr_pcalloc(p, sizeof(*ds));
apr_int32_t flags;
apr_status_t rv;
switch (mode) {
default:
flags = APR_READ | APR_BINARY;
break;
case DAV_MODE_WRITE_TRUNC:
flags = APR_WRITE | APR_CREATE | APR_TRUNCATE | APR_BINARY;
break;
case DAV_MODE_WRITE_SEEKABLE:
flags = APR_WRITE | APR_CREATE | APR_BINARY;
break;
}
ds->p = p;
ds->pathname = resource->info->pathname;
rv = apr_file_open(&ds->f, ds->pathname, flags, APR_OS_DEFAULT, ds->p);
if (rv != APR_SUCCESS) {
return dav_new_error(p, MAP_IO2HTTP(rv), 0,
"An error occurred while opening a resource.");
}
/* (APR registers cleanups for the fd with the pool) */
*stream = ds;
return NULL;
}
static dav_error * dav_fs_close_stream(dav_stream *stream, int commit)
{
apr_file_close(stream->f);
if (!commit) {
if (apr_file_remove(stream->pathname, stream->p) != APR_SUCCESS) {
/* ### use a better description? */
return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
"There was a problem removing (rolling "
"back) the resource "
"when it was being closed.");
}
}
return NULL;
}
static dav_error * dav_fs_write_stream(dav_stream *stream,
const void *buf, apr_size_t bufsize)
{
apr_status_t status;
status = apr_file_write_full(stream->f, buf, bufsize, NULL);
if (APR_STATUS_IS_ENOSPC(status)) {
return dav_new_error(stream->p, HTTP_INSUFFICIENT_STORAGE, 0,
"There is not enough storage to write to "
"this resource.");
}
else if (status != APR_SUCCESS) {
/* ### use something besides 500? */
return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
"An error occurred while writing to a "
"resource.");
}
return NULL;
}
static dav_error * dav_fs_seek_stream(dav_stream *stream, apr_off_t abs_pos)
{
if (apr_file_seek(stream->f, APR_SET, &abs_pos) != APR_SUCCESS) {
/* ### should check whether apr_file_seek set abs_pos was set to the
* correct position? */
/* ### use something besides 500? */
return dav_new_error(stream->p, HTTP_INTERNAL_SERVER_ERROR, 0,
"Could not seek to specified position in the "
"resource.");
}
return NULL;
}
#if DEBUG_GET_HANDLER
/* only define set_headers() and deliver() for debug purposes */
static dav_error * dav_fs_set_headers(request_rec *r,
const dav_resource *resource)
{
/* ### this function isn't really used since we have a get_pathname */
if (!resource->exists)
return NULL;
/* make sure the proper mtime is in the request record */
ap_update_mtime(r, resource->info->finfo.mtime);
/* ### note that these use r->filename rather than <resource> */
ap_set_last_modified(r);
ap_set_etag(r);
/* we accept byte-ranges */
apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
/* set up the Content-Length header */
ap_set_content_length(r, resource->info->finfo.size);
/* ### how to set the content type? */
/* ### until this is resolved, the Content-Type header is busted */
return NULL;
}
static dav_error * dav_fs_deliver(const dav_resource *resource,
ap_filter_t *output)
{
apr_pool_t *pool = resource->pool;
apr_bucket_brigade *bb;
apr_file_t *fd;
apr_status_t status;
apr_bucket *bkt;
/* Check resource type */
if (resource->type != DAV_RESOURCE_TYPE_REGULAR
&& resource->type != DAV_RESOURCE_TYPE_VERSION
&& resource->type != DAV_RESOURCE_TYPE_WORKING) {
return dav_new_error(pool, HTTP_CONFLICT, 0,
"Cannot GET this type of resource.");
}
if (resource->collection) {
return dav_new_error(pool, HTTP_CONFLICT, 0,
"There is no default response to GET for a "
"collection.");
}
if ((status = apr_file_open(&fd, resource->info->pathname,
APR_READ | APR_BINARY, 0,
pool)) != APR_SUCCESS) {
return dav_new_error(pool, HTTP_FORBIDDEN, 0,
"File permissions deny server access.");
}
bb = apr_brigade_create(pool, output->c->bucket_alloc);
/* ### this does not handle large files. but this is test code anyway */
bkt = apr_bucket_file_create(fd, 0,
(apr_size_t)resource->info->finfo.size,
pool, output->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, bkt);
bkt = apr_bucket_eos_create(output->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, bkt);
if ((status = ap_pass_brigade(output, bb)) != APR_SUCCESS) {
return dav_new_error(pool, HTTP_FORBIDDEN, 0,
"Could not write contents to filter.");
}
return NULL;
}
#endif /* DEBUG_GET_HANDLER */
static dav_error * dav_fs_create_collection(dav_resource *resource)
{
dav_resource_private *ctx = resource->info;
apr_status_t status;
status = apr_dir_make(ctx->pathname, APR_OS_DEFAULT, ctx->pool);
if (APR_STATUS_IS_ENOSPC(status)) {
return dav_new_error(ctx->pool, HTTP_INSUFFICIENT_STORAGE, 0,
"There is not enough storage to create "
"this collection.");
}
else if (APR_STATUS_IS_ENOENT(status)) {
return dav_new_error(ctx->pool, HTTP_CONFLICT, 0,
"Cannot create collection; intermediate "
"collection does not exist.");
}
else if (status != APR_SUCCESS) {
/* ### refine this error message? */
return dav_new_error(ctx->pool, HTTP_FORBIDDEN, 0,
"Unable to create collection.");
}
/* update resource state to show it exists as a collection */
resource->exists = 1;
resource->collection = 1;
return NULL;
}
static dav_error * dav_fs_copymove_walker(dav_walk_resource *wres,
int calltype)
{
dav_fs_copymove_walk_ctx *ctx = wres->walk_ctx;
dav_resource_private *srcinfo = wres->resource->info;
dav_resource_private *dstinfo = ctx->res_dst->info;
dav_error *err = NULL;
if (wres->resource->collection) {
if (calltype == DAV_CALLTYPE_POSTFIX) {
/* Postfix call for MOVE. delete the source dir.
* Note: when copying, we do not enable the postfix-traversal.
*/
/* ### we are ignoring any error here; what should we do? */
(void) apr_dir_remove(srcinfo->pathname, ctx->pool);
}
else {
/* copy/move of a collection. Create the new, target collection */
if (apr_dir_make(dstinfo->pathname, APR_OS_DEFAULT,
ctx->pool) != APR_SUCCESS) {
/* ### assume it was a permissions problem */
/* ### need a description here */
err = dav_new_error(ctx->pool, HTTP_FORBIDDEN, 0, NULL);
}
}
}
else {
err = dav_fs_copymove_file(ctx->is_move, ctx->pool,
srcinfo->pathname, dstinfo->pathname,
&ctx->work_buf);
/* ### push a higher-level description? */
}
/*
** If we have a "not so bad" error, then it might need to go into a
** multistatus response.
**
** For a MOVE, it will always go into the multistatus. It could be
** that everything has been moved *except* for the root. Using a
** multistatus (with no errors for the other resources) will signify
** this condition.
**
** For a COPY, we are traversing in a prefix fashion. If the root fails,
** then we can just bail out now.
*/
if (err != NULL
&& !ap_is_HTTP_SERVER_ERROR(err->status)
&& (ctx->is_move
|| !dav_fs_is_same_resource(wres->resource, ctx->root))) {
/* ### use errno to generate DAV:responsedescription? */
dav_add_response(wres, err->status, NULL);
/* the error is in the multistatus now. do not stop the traversal. */
return NULL;
}
return err;
}
static dav_error *dav_fs_copymove_resource(
int is_move,
const dav_resource *src,
const dav_resource *dst,
int depth,
dav_response **response)
{
dav_error *err = NULL;
dav_buffer work_buf = { 0 };
*response = NULL;
/* if a collection, recursively copy/move it and its children,
* including the state dirs
*/
if (src->collection) {
dav_walk_params params = { 0 };
dav_response *multi_status;
params.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_HIDDEN;
params.func = dav_fs_copymove_walker;
params.pool = src->info->pool;
params.root = src;
/* params.walk_ctx is managed by dav_fs_internal_walk() */
/* postfix is needed for MOVE to delete source dirs */
if (is_move)
params.walk_type |= DAV_WALKTYPE_POSTFIX;
/* note that we return the error OR the multistatus. never both */
if ((err = dav_fs_internal_walk(¶ms, depth, is_move, dst,
&multi_status)) != NULL) {
/* on a "real" error, then just punt. nothing else to do. */
return err;
}
if ((*response = multi_status) != NULL) {
/* some multistatus responses exist. wrap them in a 207 */
return dav_new_error(src->info->pool, HTTP_MULTI_STATUS, 0,
"Error(s) occurred on some resources during "
"the COPY/MOVE process.");
}
return NULL;
}
/* not a collection */
if ((err = dav_fs_copymove_file(is_move, src->info->pool,
src->info->pathname, dst->info->pathname,
&work_buf)) != NULL) {
/* ### push a higher-level description? */
return err;
}
/* copy/move properties as well */
return dav_fs_copymoveset(is_move, src->info->pool, src, dst, &work_buf);
}
static dav_error * dav_fs_copy_resource(
const dav_resource *src,
dav_resource *dst,
int depth,
dav_response **response)
{
dav_error *err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -