📄 update.c
字号:
/* * update.c: handle the update-report request and response * * ==================================================================== * Copyright (c) 2000-2006 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#include <apr_pools.h>#include <apr_strings.h>#include <apr_xml.h>#include <apr_md5.h>#include <mod_dav.h>#include "svn_pools.h"#include "svn_repos.h"#include "svn_fs.h"#include "svn_md5.h"#include "svn_base64.h"#include "svn_xml.h"#include "svn_path.h"#include "svn_dav.h"#include "svn_props.h"#include "dav_svn.h"#include <http_request.h>#include <http_log.h>typedef struct { const dav_resource *resource; /* the revision we are updating to. used to generated IDs. */ svn_fs_root_t *rev_root; const char *anchor; const char *target; /* if doing a regular update, then dst_path == anchor. if this is a 'switch' operation, then this field is the fs path that is being switched to. This path needs to telescope in the update-editor just like 'anchor' above; it's used for retrieving CR's and vsn-url's during the edit. */ const char *dst_path; /* this buffers the output for a bit and is automatically flushed, at appropriate times, by the Apache filter system. */ apr_bucket_brigade *bb; /* where to deliver the output */ ap_filter_t *output; /* where do these editor paths *really* point to? */ apr_hash_t *pathmap; /* are we doing a resource walk? */ svn_boolean_t resource_walk; /* True iff we've already sent the open tag for the update. */ svn_boolean_t started_update; /* True iff client requested all data inline in the report. */ svn_boolean_t send_all; /* SVNDIFF version to send to client. */ int svndiff_version;} update_ctx_t;typedef struct item_baton_t { apr_pool_t *pool; update_ctx_t *uc; struct item_baton_t *parent; /* the parent of this item. */ const char *name; /* the single-component name of this item */ const char *path; /* a telescoping extension of uc->anchor */ const char *path2; /* a telescoping extension of uc->dst_path */ const char *path3; /* a telescoping extension of uc->dst_path without dst_path as prefix. */ const char *base_checksum; /* base_checksum (from apply_textdelta) */ const char *text_checksum; /* text_checksum (from close_file) */ svn_boolean_t text_changed; /* Did the file's contents change? */ svn_boolean_t added; /* File added? (Implies text_changed.) */ apr_array_header_t *changed_props; /* array of const char * prop names */ apr_array_header_t *removed_props; /* array of const char * prop names */ /* "entry props" */ const char *committed_rev; const char *committed_date; const char *last_author;} item_baton_t;#define DIR_OR_FILE(is_dir) ((is_dir) ? "directory" : "file")svn_error_t *dav_svn_authz_read(svn_boolean_t *allowed, svn_fs_root_t *root, const char *path, void *baton, apr_pool_t *pool){ dav_svn_authz_read_baton *arb = baton; request_rec *subreq = NULL; const char *uri; svn_revnum_t rev = SVN_INVALID_REVNUM; const char *revpath = NULL; /* Easy out: if the admin has explicitly set 'SVNPathAuthz Off', then this whole callback does nothing. */ if (! dav_svn_get_pathauthz_flag(arb->r)) { *allowed = TRUE; return SVN_NO_ERROR; } /* Our ultimate goal here is to create a Version Resource (VR) url, which is a url that represents a path within a revision. We then send a subrequest to apache, so that any installed authz modules can allow/disallow the path. ### That means that we're assuming that any installed authz module is *only* paying attention to revision-paths, not paths in uncommitted transactions. Someday we need to widen our horizons. */ if (svn_fs_is_txn_root(root)) { /* This means svn_repos_dir_delta is comparing two txn trees, rather than a txn and revision. It's probably updating a working copy that contains 'disjoint urls'. Because the 2nd transaction is likely to have all sorts of paths linked in from random places, we need to find the original (rev,path) of each txn path. That's what needs authorization. */ svn_stringbuf_t *path_s = svn_stringbuf_create(path, pool); const char *lopped_path = ""; /* The path might be copied implicitly, because it's down in a copied tree. So we start at path and walk up its parents asking if anyone was copied, and if so where from. */ while (! (svn_path_is_empty(path_s->data) || ((path_s->len == 1) && (path_s->data[0] == '/')))) { SVN_ERR(svn_fs_copied_from(&rev, &revpath, root, path_s->data, pool)); if (SVN_IS_VALID_REVNUM(rev) && revpath) { revpath = svn_path_join(revpath, lopped_path, pool); break; } /* Lop off the basename and try again. */ lopped_path = svn_path_join(svn_path_basename (path_s->data, pool), lopped_path, pool); svn_path_remove_component(path_s); } /* If no copy produced this path, its path in the original revision is the same as its path in this txn. */ if ((rev == SVN_INVALID_REVNUM) && (revpath == NULL)) { const char *txn_name; svn_fs_txn_t *txn; txn_name = svn_fs_txn_root_name(root, pool); SVN_ERR(svn_fs_open_txn(&txn, svn_fs_root_fs(root), txn_name, pool)); rev = svn_fs_txn_base_revision(txn); revpath = path; } } else /* revision root */ { rev = svn_fs_revision_root_revision(root); revpath = path; } /* We have a (rev, path) pair to check authorization on. */ /* Build a Version Resource uri representing (rev, path). */ uri = dav_svn_build_uri(arb->repos, DAV_SVN_BUILD_URI_VERSION, rev, revpath, FALSE, pool); /* Check if GET would work against this uri. */ subreq = ap_sub_req_method_uri("GET", uri, arb->r, arb->r->output_filters); if (subreq && (subreq->status == HTTP_OK)) *allowed = TRUE; else *allowed = FALSE; if (subreq) ap_destroy_sub_req(subreq); return SVN_NO_ERROR;}svn_repos_authz_func_t dav_svn_authz_read_func(dav_svn_authz_read_baton *baton){ /* Easy out: If the admin has explicitly set 'SVNPathAuthz Off', then we don't need to do any authorization checks. */ if (! dav_svn_get_pathauthz_flag(baton->r)) return NULL; return dav_svn_authz_read; }/* add PATH to the pathmap HASH with a repository path of LINKPATH. if LINKPATH is NULL, PATH will map to itself. */static void add_to_path_map(apr_hash_t *hash, const char *path, const char *linkpath){ /* normalize 'root paths' to have a slash */ const char *norm_path = strcmp(path, "") ? path : "/"; /* if there is an actual linkpath given, it is the repos path, else our path maps to itself. */ const char *repos_path = linkpath ? linkpath : norm_path; /* now, geez, put the path in the map already! */ apr_hash_set(hash, path, APR_HASH_KEY_STRING, repos_path);}/* return the actual repository path referred to by the editor's PATH, allocated in POOL, determined by examining the pathmap HASH. */static const char *get_from_path_map(apr_hash_t *hash, const char *path, apr_pool_t *pool){ const char *repos_path; svn_stringbuf_t *my_path; /* no hash means no map. that's easy enough. */ if (! hash) return apr_pstrdup(pool, path); if ((repos_path = apr_hash_get(hash, path, APR_HASH_KEY_STRING))) { /* what luck! this path is a hash key! if there is a linkpath, use that, else return the path itself. */ return apr_pstrdup(pool, repos_path); } /* bummer. PATH wasn't a key in path map, so we get to start hacking off components and looking for a parent from which to derive a repos_path. use a stringbuf for convenience. */ my_path = svn_stringbuf_create(path, pool); do { svn_path_remove_component(my_path); if ((repos_path = apr_hash_get(hash, my_path->data, my_path->len))) { /* we found a mapping ... but of one of PATH's parents. soooo, we get to re-append the chunks of PATH that we broke off to the REPOS_PATH we found. */ return apr_pstrcat(pool, repos_path, "/", path + my_path->len + 1, NULL); } } while (! svn_path_is_empty(my_path->data) && strcmp(my_path->data, "/") != 0); /* well, we simply never found anything worth mentioning the map. PATH is its own default finding, then. */ return apr_pstrdup(pool, path);}static item_baton_t *make_child_baton(item_baton_t *parent, const char *path, apr_pool_t *pool){ item_baton_t *baton; baton = apr_pcalloc(pool, sizeof(*baton)); baton->pool = pool; baton->uc = parent->uc; baton->name = svn_path_basename(path, pool); baton->parent = parent; /* Telescope the path based on uc->anchor. */ baton->path = svn_path_join(parent->path, baton->name, pool); /* Telescope the path based on uc->dst_path in the exact same way. */ baton->path2 = svn_path_join(parent->path2, baton->name, pool); /* Telescope the third path: it's relative, not absolute, to dst_path. Now, we gotta be careful here, because if this operation had a target, and we're it, then we have to use the basename of our source reflection instead of our own. */ if ((*baton->uc->target) && (! parent->parent)) baton->path3 = svn_path_join(parent->path3, baton->uc->target, pool); else baton->path3 = svn_path_join(parent->path3, baton->name, pool); return baton;}struct brigade_write_baton{ apr_bucket_brigade *bb; ap_filter_t *output;};/* This implements 'svn_write_fn_t'. */static svn_error_t * brigade_write_fn(void *baton, const char *data, apr_size_t *len){ struct brigade_write_baton *wb = baton; apr_status_t apr_err; apr_err = apr_brigade_write(wb->bb, ap_filter_flush, wb->output, data, *len); if (apr_err != APR_SUCCESS) return svn_error_wrap_apr(apr_err, "Error writing base64 data"); return SVN_NO_ERROR;}svn_stream_t * dav_svn_make_base64_output_stream(apr_bucket_brigade *bb, ap_filter_t *output, apr_pool_t *pool){ struct brigade_write_baton *wb = apr_palloc(pool, sizeof(*wb)); svn_stream_t *stream = svn_stream_create(wb, pool); wb->bb = bb; wb->output = output; svn_stream_set_write(stream, brigade_write_fn); return svn_base64_encode(stream, pool);}/* Get the real filesystem PATH for BATON, and return the value allocated from POOL. This function juggles the craziness of updates, switches, and updates of switched things. */static const char *get_real_fs_path(item_baton_t *baton, apr_pool_t *pool){ const char *path = get_from_path_map(baton->uc->pathmap, baton->path, pool); return strcmp(path, baton->path) ? path : baton->path2;}static svn_error_t * send_vsn_url(item_baton_t *baton, apr_pool_t *pool){ const char *href; const char *path; svn_revnum_t revision; /* Try to use the CR, assuming the path exists in CR. */ path = get_real_fs_path(baton, pool); revision = dav_svn_get_safe_cr(baton->uc->rev_root, path, pool); href = dav_svn_build_uri(baton->uc->resource->info->repos, DAV_SVN_BUILD_URI_VERSION, revision, path, 0 /* add_href */, pool); return dav_svn__send_xml(baton->uc->bb, baton->uc->output, "<D:checked-in><D:href>%s</D:href></D:checked-in>" DEBUG_CR, apr_xml_quote_string(pool, href, 1));}static svn_error_t * absent_helper(svn_boolean_t is_dir, const char *path, item_baton_t *parent, apr_pool_t *pool){ update_ctx_t *uc = parent->uc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -