📄 repos.c
字号:
#if DAV_DEBUG
if (src->hooks != dst->hooks) {
/*
** ### strictly speaking, this is a design error; we should not
** ### have reached this point.
*/
return dav_new_error(src->info->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
"DESIGN ERROR: a mix of repositories "
"was passed to copy_resource.");
}
#endif
if ((err = dav_fs_copymove_resource(0, src, dst, depth,
response)) == NULL) {
/* update state of destination resource to show it exists */
dst->exists = 1;
dst->collection = src->collection;
}
return err;
}
static dav_error * dav_fs_move_resource(
dav_resource *src,
dav_resource *dst,
dav_response **response)
{
dav_resource_private *srcinfo = src->info;
dav_resource_private *dstinfo = dst->info;
dav_error *err;
int can_rename = 0;
#if DAV_DEBUG
if (src->hooks != dst->hooks) {
/*
** ### strictly speaking, this is a design error; we should not
** ### have reached this point.
*/
return dav_new_error(src->info->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
"DESIGN ERROR: a mix of repositories "
"was passed to move_resource.");
}
#endif
/* determine whether a simple rename will work.
* Assume source exists, else we wouldn't get called.
*/
if (dstinfo->finfo.filetype != 0) {
if (dstinfo->finfo.device == srcinfo->finfo.device) {
/* target exists and is on the same device. */
can_rename = 1;
}
}
else {
const char *dirpath;
apr_finfo_t finfo;
apr_status_t rv;
/* destination does not exist, but the parent directory should,
* so try it
*/
dirpath = ap_make_dirstr_parent(dstinfo->pool, dstinfo->pathname);
/*
* XXX: If missing dev ... then what test?
* Really need a try and failover for those platforms.
*
*/
rv = apr_stat(&finfo, dirpath, APR_FINFO_DEV, dstinfo->pool);
if ((rv == APR_SUCCESS || rv == APR_INCOMPLETE)
&& (finfo.valid & srcinfo->finfo.valid & APR_FINFO_DEV)
&& (finfo.device == srcinfo->finfo.device)) {
can_rename = 1;
}
}
/* if we can't simply rename, then do it the hard way... */
if (!can_rename) {
if ((err = dav_fs_copymove_resource(1, src, dst, DAV_INFINITY,
response)) == NULL) {
/* update resource states */
dst->exists = 1;
dst->collection = src->collection;
src->exists = 0;
src->collection = 0;
}
return err;
}
/* a rename should work. do it, and move properties as well */
/* no multistatus response */
*response = NULL;
/* ### APR has no rename? */
if (apr_file_rename(srcinfo->pathname, dstinfo->pathname,
srcinfo->pool) != APR_SUCCESS) {
/* ### should have a better error than this. */
return dav_new_error(srcinfo->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
"Could not rename resource.");
}
/* update resource states */
dst->exists = 1;
dst->collection = src->collection;
src->exists = 0;
src->collection = 0;
if ((err = dav_fs_copymoveset(1, src->info->pool,
src, dst, NULL)) == NULL) {
/* no error. we're done. go ahead and return now. */
return NULL;
}
/* error occurred during properties move; try to put resource back */
if (apr_file_rename(dstinfo->pathname, srcinfo->pathname,
srcinfo->pool) != APR_SUCCESS) {
/* couldn't put it back! */
return dav_push_error(srcinfo->pool,
HTTP_INTERNAL_SERVER_ERROR, 0,
"The resource was moved, but a failure "
"occurred during the move of its "
"properties. The resource could not be "
"restored to its original location. The "
"server is now in an inconsistent state.",
err);
}
/* update resource states again */
src->exists = 1;
src->collection = dst->collection;
dst->exists = 0;
dst->collection = 0;
/* resource moved back, but properties may be inconsistent */
return dav_push_error(srcinfo->pool,
HTTP_INTERNAL_SERVER_ERROR, 0,
"The resource was moved, but a failure "
"occurred during the move of its properties. "
"The resource was moved back to its original "
"location, but its properties may have been "
"partially moved. The server may be in an "
"inconsistent state.",
err);
}
static dav_error * dav_fs_delete_walker(dav_walk_resource *wres, int calltype)
{
dav_resource_private *info = wres->resource->info;
/* do not attempt to remove a null resource,
* or a collection with children
*/
if (wres->resource->exists &&
(!wres->resource->collection || calltype == DAV_CALLTYPE_POSTFIX)) {
/* try to remove the resource */
apr_status_t result;
result = wres->resource->collection
? apr_dir_remove(info->pathname, wres->pool)
: apr_file_remove(info->pathname, wres->pool);
/*
** If an error occurred, then add it to multistatus response.
** Note that we add it for the root resource, too. It is quite
** possible to delete the whole darn tree, yet fail on the root.
**
** (also: remember we are deleting via a postfix traversal)
*/
if (result != APR_SUCCESS) {
/* ### assume there is a permissions problem */
/* ### use errno to generate DAV:responsedescription? */
dav_add_response(wres, HTTP_FORBIDDEN, NULL);
}
}
return NULL;
}
static dav_error * dav_fs_remove_resource(dav_resource *resource,
dav_response **response)
{
dav_resource_private *info = resource->info;
*response = NULL;
/* if a collection, recursively remove it and its children,
* including the state dirs
*/
if (resource->collection) {
dav_walk_params params = { 0 };
dav_error *err = NULL;
dav_response *multi_status;
params.walk_type = (DAV_WALKTYPE_NORMAL
| DAV_WALKTYPE_HIDDEN
| DAV_WALKTYPE_POSTFIX);
params.func = dav_fs_delete_walker;
params.pool = info->pool;
params.root = resource;
if ((err = dav_fs_walk(¶ms, DAV_INFINITY,
&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(info->pool, HTTP_MULTI_STATUS, 0,
"Error(s) occurred on some resources during "
"the deletion process.");
}
/* no errors... update resource state */
resource->exists = 0;
resource->collection = 0;
return NULL;
}
/* not a collection; remove the file and its properties */
if (apr_file_remove(info->pathname, info->pool) != APR_SUCCESS) {
/* ### put a description in here */
return dav_new_error(info->pool, HTTP_FORBIDDEN, 0, NULL);
}
/* update resource state */
resource->exists = 0;
resource->collection = 0;
/* remove properties and return its result */
return dav_fs_deleteset(info->pool, resource);
}
/* ### move this to dav_util? */
/* Walk recursively down through directories, *
* including lock-null resources as we go. */
static dav_error * dav_fs_walker(dav_fs_walker_context *fsctx, int depth)
{
const dav_walk_params *params = fsctx->params;
apr_pool_t *pool = params->pool;
dav_error *err = NULL;
int isdir = fsctx->res1.collection;
apr_finfo_t dirent;
apr_dir_t *dirp;
/* ensure the context is prepared properly, then call the func */
err = (*params->func)(&fsctx->wres,
isdir
? DAV_CALLTYPE_COLLECTION
: DAV_CALLTYPE_MEMBER);
if (err != NULL) {
return err;
}
if (depth == 0 || !isdir) {
return NULL;
}
/* put a trailing slash onto the directory, in preparation for appending
* files to it as we discovery them within the directory */
dav_check_bufsize(pool, &fsctx->path1, DAV_BUFFER_PAD);
fsctx->path1.buf[fsctx->path1.cur_len++] = '/';
fsctx->path1.buf[fsctx->path1.cur_len] = '\0'; /* in pad area */
/* if a secondary path is present, then do that, too */
if (fsctx->path2.buf != NULL) {
dav_check_bufsize(pool, &fsctx->path2, DAV_BUFFER_PAD);
fsctx->path2.buf[fsctx->path2.cur_len++] = '/';
fsctx->path2.buf[fsctx->path2.cur_len] = '\0'; /* in pad area */
}
/* Note: the URI should ALREADY have a trailing "/" */
/* for this first pass of files, all resources exist */
fsctx->res1.exists = 1;
/* a file is the default; we'll adjust if we hit a directory */
fsctx->res1.collection = 0;
fsctx->res2.collection = 0;
/* open and scan the directory */
if ((apr_dir_open(&dirp, fsctx->path1.buf, pool)) != APR_SUCCESS) {
/* ### need a better error */
return dav_new_error(pool, HTTP_NOT_FOUND, 0, NULL);
}
while ((apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp)) == APR_SUCCESS) {
apr_size_t len;
apr_status_t status;
len = strlen(dirent.name);
/* avoid recursing into our current, parent, or state directories */
if (dirent.name[0] == '.'
&& (len == 1 || (dirent.name[1] == '.' && len == 2))) {
continue;
}
if (params->walk_type & DAV_WALKTYPE_AUTH) {
/* ### need to authorize each file */
/* ### example: .htaccess is normally configured to fail auth */
/* stuff in the state directory is never authorized! */
if (!strcmp(dirent.name, DAV_FS_STATE_DIR)) {
continue;
}
}
/* skip the state dir unless a HIDDEN is performed */
if (!(params->walk_type & DAV_WALKTYPE_HIDDEN)
&& !strcmp(dirent.name, DAV_FS_STATE_DIR)) {
continue;
}
/* append this file onto the path buffer (copy null term) */
dav_buffer_place_mem(pool, &fsctx->path1, dirent.name, len + 1, 0);
/* ### Optimize me, dirent can give us what we need! */
status = apr_lstat(&fsctx->info1.finfo, fsctx->path1.buf,
APR_FINFO_NORM, pool);
if (status != APR_SUCCESS && status != APR_INCOMPLETE) {
/* woah! where'd it go? */
/* ### should have a better error here */
err = dav_new_error(pool, HTTP_NOT_FOUND, 0, NULL);
break;
}
/* copy the file to the URI, too. NOTE: we will pad an extra byte
for the trailing slash later. */
dav_buffer_place_mem(pool, &fsctx->uri_buf, dirent.name, len + 1, 1);
/* if there is a secondary path, then do that, too */
if (fsctx->path2.buf != NULL) {
dav_buffer_place_mem(pool, &fsctx->path2, dirent.name, len + 1, 0);
}
/* set up the (internal) pathnames for the two resources */
fsctx->info1.pathname = fsctx->path1.buf;
fsctx->info2.pathname = fsctx->path2.buf;
/* set up the URI for the current resource */
fsctx->res1.uri = fsctx->uri_buf.buf;
/* ### for now, only process regular files (e.g. skip symlinks) */
if (fsctx->info1.finfo.filetype == APR_REG) {
/* call the function for the specified dir + file */
if ((err = (*params->func)(&fsctx->wres,
DAV_CALLTYPE_MEMBER)) != NULL) {
/* ### maybe add a higher-level description? */
break;
}
}
else if (fsctx->info1.finfo.filetype == APR_DIR) {
apr_size_t save_path_len = fsctx->path1.cur_len;
apr_size_t save_uri_len = fsctx->uri_buf.cur_len;
apr_size_t save_path2_len = fsctx->path2.cur_len;
/* adjust length to incorporate the subdir name */
fsctx->path1.cur_len += len;
fsctx->path2.cur_len += len;
/* adjust URI length to incorporate subdir and a slash */
fsctx->uri_buf.cur_len += len + 1;
fsctx->uri_buf.buf[fsctx->uri_buf.cur_len - 1] = '/';
fsctx->uri_buf.buf[fsctx->uri_buf.cur_len] = '\0';
/* switch over to a collection */
fsctx->res1.collection = 1;
fsctx->res2.collection = 1;
/* recurse on the subdir */
/* ### don't always want to quit on error from single child */
if ((err = dav_fs_walker(fsctx, depth - 1)) != NULL) {
/* ### maybe add a higher-level description? */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -