2 * config_file.c : efficiently read config files from disk or repo
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
27 #include "svn_checksum.h"
29 #include "svn_pools.h"
31 #include "private/svn_subr_private.h"
32 #include "private/svn_repos_private.h"
33 #include "private/svn_config_private.h"
35 #include "config_file.h"
37 #include "svn_private_config.h"
41 struct config_access_t
43 /* The last repository that we found the requested URL in. May be NULL. */
46 /* Owning pool of this structure and is private to this structure.
47 * All objects with the lifetime of this access object will be allocated
54 /* A stream object that gives access to a representation's content but
55 * delays accessing the repository data until the stream is first used.
56 * IOW, the stream object is cheap as long as it is not accessed.
58 typedef struct presentation_stream_baton_t
64 } presentation_stream_baton_t;
67 auto_open_inner_stream(presentation_stream_baton_t *b)
71 svn_filesize_t length;
73 svn_stringbuf_t *contents;
75 SVN_ERR(svn_fs_file_length(&length, b->root, b->fs_path, b->pool));
76 SVN_ERR(svn_fs_file_contents(&stream, b->root, b->fs_path, b->pool));
77 SVN_ERR(svn_stringbuf_from_stream(&contents, stream,
78 (apr_size_t)length, b->pool));
79 b->inner = svn_stream_from_stringbuf(contents, b->pool);
86 read_handler_rep(void *baton, char *buffer, apr_size_t *len)
88 presentation_stream_baton_t *b = baton;
89 SVN_ERR(auto_open_inner_stream(b));
91 return svn_error_trace(svn_stream_read2(b->inner, buffer, len));
95 mark_handler_rep(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool)
97 presentation_stream_baton_t *b = baton;
98 SVN_ERR(auto_open_inner_stream(b));
100 return svn_error_trace(svn_stream_mark(b->inner, mark, pool));
104 seek_handler_rep(void *baton, const svn_stream_mark_t *mark)
106 presentation_stream_baton_t *b = baton;
107 SVN_ERR(auto_open_inner_stream(b));
109 return svn_error_trace(svn_stream_seek(b->inner, mark));
113 skip_handler_rep(void *baton, apr_size_t len)
115 presentation_stream_baton_t *b = baton;
116 SVN_ERR(auto_open_inner_stream(b));
118 return svn_error_trace(svn_stream_skip(b->inner, len));
122 data_available_handler_rep(void *baton, svn_boolean_t *data_available)
124 presentation_stream_baton_t *b = baton;
125 SVN_ERR(auto_open_inner_stream(b));
127 return svn_error_trace(svn_stream_data_available(b->inner, data_available));
131 readline_handler_rep(void *baton,
132 svn_stringbuf_t **stringbuf,
137 presentation_stream_baton_t *b = baton;
138 SVN_ERR(auto_open_inner_stream(b));
140 return svn_error_trace(svn_stream_readline(b->inner, stringbuf, eol, eof,
144 /* Return a lazy access stream for FS_PATH under ROOT, allocated in POOL. */
145 static svn_stream_t *
146 representation_stream(svn_fs_root_t *root,
150 svn_stream_t *stream;
151 presentation_stream_baton_t *baton;
153 baton = apr_pcalloc(pool, sizeof(*baton));
155 baton->fs_path = fs_path;
158 stream = svn_stream_create(baton, pool);
159 svn_stream_set_read2(stream, read_handler_rep, read_handler_rep);
160 svn_stream_set_mark(stream, mark_handler_rep);
161 svn_stream_set_seek(stream, seek_handler_rep);
162 svn_stream_set_skip(stream, skip_handler_rep);
163 svn_stream_set_data_available(stream, data_available_handler_rep);
164 svn_stream_set_readline(stream, readline_handler_rep);
168 /* Handle the case of a file PATH / url pointing to anything that is either
169 * not a file or does not exist at all. The case is given by NODE_KIND.
171 * If MUST_EXIST is not set and the file does not exist at all, return a
172 * default *STREAM and *CHECKSUM allocated in the context of ACCESS, or an
176 handle_missing_file(svn_stream_t **stream,
177 svn_checksum_t **checksum,
178 config_access_t *access,
180 svn_boolean_t must_exist,
181 svn_node_kind_t node_kind)
183 if (node_kind == svn_node_none && !must_exist)
185 *stream = svn_stream_empty(access->pool);
186 SVN_ERR(svn_checksum(checksum, svn_checksum_md5, "", 0, access->pool));
188 else if (node_kind != svn_node_file)
190 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
191 "'%s' is not a file", path);
197 /* Open the in-repository file at URL, return its content checksum in
198 * *CHECKSUM and the content itself through *STREAM. Allocate those with
199 * the lifetime of ACCESS and use SCRATCH_POOL for temporaries.
201 * Error out when the file does not exist but MUST_EXIST is set.
204 get_repos_config(svn_stream_t **stream,
205 svn_checksum_t **checksum,
206 config_access_t *access,
208 svn_boolean_t must_exist,
209 apr_pool_t *scratch_pool)
213 svn_revnum_t youngest_rev;
214 svn_node_kind_t node_kind;
217 const char *repos_root_dirent;
219 SVN_ERR(svn_uri_get_dirent_from_file_url(&dirent, url, access->pool));
221 /* Maybe we can use the repos hint instance instead of creating a
225 repos_root_dirent = svn_repos_path(access->repos, scratch_pool);
226 if (!svn_dirent_is_absolute(repos_root_dirent))
227 SVN_ERR(svn_dirent_get_absolute(&repos_root_dirent,
231 if (!svn_dirent_is_ancestor(repos_root_dirent, dirent))
232 access->repos = NULL;
235 /* Open repos if no suitable repos is available. */
238 /* Search for a repository in the full path. */
239 repos_root_dirent = svn_repos_find_root_path(dirent, scratch_pool);
241 /* Attempt to open a repository at repos_root_dirent. */
242 SVN_ERR(svn_repos_open3(&access->repos, repos_root_dirent, NULL,
243 access->pool, scratch_pool));
246 fs_path = &dirent[strlen(repos_root_dirent)];
248 /* Get the filesystem. */
249 fs = svn_repos_fs(access->repos);
251 /* Find HEAD and the revision root */
252 SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, scratch_pool));
253 SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, access->pool));
255 /* Special case: non-existent paths may be handled as "empty" contents. */
256 SVN_ERR(svn_fs_check_path(&node_kind, root, fs_path, scratch_pool));
257 if (node_kind != svn_node_file)
258 return svn_error_trace(handle_missing_file(stream, checksum, access,
259 url, must_exist, node_kind));
261 /* Fetch checksum and see whether we already have a matching config */
262 SVN_ERR(svn_fs_file_checksum(checksum, svn_checksum_md5, root, fs_path,
263 TRUE, access->pool));
265 *stream = representation_stream(root, fs_path, access->pool);
270 /* Open the file at PATH, return its content checksum in CHECKSUM and the
271 * content itself through *STREAM. Allocate those with the lifetime of
275 get_file_config(svn_stream_t **stream,
276 svn_checksum_t **checksum,
277 config_access_t *access,
279 svn_boolean_t must_exist,
280 apr_pool_t *scratch_pool)
282 svn_stringbuf_t *contents;
283 svn_node_kind_t node_kind;
285 /* Special case: non-existent paths may be handled as "empty" contents. */
286 SVN_ERR(svn_io_check_path(path, &node_kind, scratch_pool));
287 if (node_kind != svn_node_file)
288 return svn_error_trace(handle_missing_file(stream, checksum, access,
289 path, must_exist, node_kind));
291 /* Now, we should be able to read the file. */
292 SVN_ERR(svn_stringbuf_from_file2(&contents, path, access->pool));
294 /* calculate MD5 over the whole file contents */
295 SVN_ERR(svn_checksum(checksum, svn_checksum_md5,
296 contents->data, contents->len, access->pool));
297 *stream = svn_stream_from_stringbuf(contents, access->pool);
302 /* Read the configuration from path, URL or registry sub-tree PATH, return
303 * its content checksum in CHECKSUM and the content itself through *STREAM.
304 * Allocate those with the lifetime of ACCESS.
307 get_generic_config(svn_stream_t **stream,
308 svn_checksum_t **checksum,
309 config_access_t *access,
311 svn_boolean_t must_exist,
312 apr_pool_t *scratch_pool)
314 svn_stringbuf_t *contents = svn_stringbuf_create_empty(access->pool);
315 svn_config_t *config;
317 /* Read the configuration and serialize it into CONTENTS.
318 * That copy can then be processed by the authz parser etc. */
319 SVN_ERR(svn_config_read3(&config, path, must_exist, TRUE, TRUE,
321 SVN_ERR(svn_config__write(svn_stream_from_stringbuf(contents, scratch_pool),
322 config, scratch_pool));
324 /* calculate MD5 over the whole file contents */
325 SVN_ERR(svn_checksum(checksum, svn_checksum_md5,
326 contents->data, contents->len, access->pool));
327 *stream = svn_stream_from_stringbuf(contents, access->pool);
333 svn_repos__create_config_access(svn_repos_t *repos_hint,
334 apr_pool_t *result_pool)
336 apr_pool_t *pool = svn_pool_create(result_pool);
337 config_access_t *result = apr_pcalloc(pool, sizeof(*result));
339 result->repos = repos_hint;
346 svn_repos__destroy_config_access(config_access_t *access)
348 svn_pool_destroy(access->pool);
352 svn_repos__get_config(svn_stream_t **stream,
353 svn_checksum_t **checksum,
354 config_access_t *access,
356 svn_boolean_t must_exist,
357 apr_pool_t *scratch_pool)
360 /* Directly access the config data. */
361 if (svn_path_is_url(path))
362 err = get_repos_config(stream, checksum, access, path, must_exist,
365 err = get_file_config(stream, checksum, access, path, must_exist,
368 /* Fallback to indirect access using the generic config file parser.
369 * This is mainly used for registry support under Win32. */
372 svn_error_t *err2 = get_generic_config(stream, checksum, access, path,
373 must_exist, scratch_pool);
376 svn_error_clear(err2);
380 svn_error_clear(err);
385 return svn_error_trace(err);