]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_wc/adm_files.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_wc / adm_files.c
1 /*
2  * adm_files.c: helper routines for handling files & dirs in the
3  *              working copy administrative area (creating,
4  *              deleting, opening, and closing).  This is the only
5  *              code that actually knows where administrative
6  *              information is kept.
7  *
8  * ====================================================================
9  *    Licensed to the Apache Software Foundation (ASF) under one
10  *    or more contributor license agreements.  See the NOTICE file
11  *    distributed with this work for additional information
12  *    regarding copyright ownership.  The ASF licenses this file
13  *    to you under the Apache License, Version 2.0 (the
14  *    "License"); you may not use this file except in compliance
15  *    with the License.  You may obtain a copy of the License at
16  *
17  *      http://www.apache.org/licenses/LICENSE-2.0
18  *
19  *    Unless required by applicable law or agreed to in writing,
20  *    software distributed under the License is distributed on an
21  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22  *    KIND, either express or implied.  See the License for the
23  *    specific language governing permissions and limitations
24  *    under the License.
25  * ====================================================================
26  */
27
28
29 \f
30 #include <stdarg.h>
31 #include <apr_pools.h>
32 #include <apr_file_io.h>
33 #include <apr_strings.h>
34
35 #include "svn_types.h"
36 #include "svn_error.h"
37 #include "svn_io.h"
38 #include "svn_dirent_uri.h"
39 #include "svn_path.h"
40 #include "svn_hash.h"
41
42 #include "wc.h"
43 #include "adm_files.h"
44 #include "entries.h"
45 #include "lock.h"
46
47 #include "svn_private_config.h"
48 #include "private/svn_wc_private.h"
49
50 \f
51 /*** File names in the adm area. ***/
52
53 /* The default name of the WC admin directory. This name is always
54    checked by svn_wc_is_adm_dir. */
55 static const char default_adm_dir_name[] = ".svn";
56
57 /* The name that is actually used for the WC admin directory.  The
58    commonest case where this won't be the default is in Windows
59    ASP.NET development environments, which used to choke on ".svn". */
60 static const char *adm_dir_name = default_adm_dir_name;
61
62
63 svn_boolean_t
64 svn_wc_is_adm_dir(const char *name, apr_pool_t *pool)
65 {
66   return (0 == strcmp(name, adm_dir_name)
67           || 0 == strcmp(name, default_adm_dir_name));
68 }
69
70
71 const char *
72 svn_wc_get_adm_dir(apr_pool_t *pool)
73 {
74   return adm_dir_name;
75 }
76
77
78 svn_error_t *
79 svn_wc_set_adm_dir(const char *name, apr_pool_t *pool)
80 {
81   /* This is the canonical list of administrative directory names.
82
83      FIXME:
84      An identical list is used in
85        libsvn_subr/opt.c:svn_opt__args_to_target_array(),
86      but that function can't use this list, because that use would
87      create a circular dependency between libsvn_wc and libsvn_subr.
88      Make sure changes to the lists are always synchronized! */
89   static const char *valid_dir_names[] = {
90     default_adm_dir_name,
91     "_svn",
92     NULL
93   };
94
95   const char **dir_name;
96   for (dir_name = valid_dir_names; *dir_name; ++dir_name)
97     if (0 == strcmp(name, *dir_name))
98       {
99         /* Use the pointer to the statically allocated string
100            constant, to avoid potential pool lifetime issues. */
101         adm_dir_name = *dir_name;
102         return SVN_NO_ERROR;
103       }
104   return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
105                            _("'%s' is not a valid administrative "
106                              "directory name"),
107                            svn_dirent_local_style(name, pool));
108 }
109
110
111 const char *
112 svn_wc__adm_child(const char *path,
113                   const char *child,
114                   apr_pool_t *result_pool)
115 {
116   return svn_dirent_join_many(result_pool,
117                               path,
118                               adm_dir_name,
119                               child,
120                               SVN_VA_NULL);
121 }
122
123
124 svn_boolean_t
125 svn_wc__adm_area_exists(const char *adm_abspath,
126                         apr_pool_t *pool)
127 {
128   const char *path = svn_wc__adm_child(adm_abspath, NULL, pool);
129   svn_node_kind_t kind;
130   svn_error_t *err;
131
132   err = svn_io_check_path(path, &kind, pool);
133   if (err)
134     {
135       svn_error_clear(err);
136       /* Return early, since kind is undefined in this case. */
137       return FALSE;
138     }
139
140   return kind != svn_node_none;
141 }
142
143
144 \f
145 /*** Making and using files in the adm area. ***/
146
147
148 /* */
149 static svn_error_t *
150 make_adm_subdir(const char *path,
151                 const char *subdir,
152                 apr_pool_t *pool)
153 {
154   const char *fullpath;
155
156   fullpath = svn_wc__adm_child(path, subdir, pool);
157
158   return svn_io_dir_make(fullpath, APR_OS_DEFAULT, pool);
159 }
160
161
162 \f
163 /*** Syncing files in the adm area. ***/
164
165
166 svn_error_t *
167 svn_wc__text_base_path_to_read(const char **result_abspath,
168                                svn_wc__db_t *db,
169                                const char *local_abspath,
170                                apr_pool_t *result_pool,
171                                apr_pool_t *scratch_pool)
172 {
173   svn_wc__db_status_t status;
174   svn_node_kind_t kind;
175   const svn_checksum_t *checksum;
176
177   SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL, NULL,
178                                         &checksum, NULL, NULL, NULL,
179                                         db, local_abspath,
180                                         scratch_pool, scratch_pool));
181
182   /* Sanity */
183   if (kind != svn_node_file)
184     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
185                              _("Can only get the pristine contents of files; "
186                                "'%s' is not a file"),
187                              svn_dirent_local_style(local_abspath,
188                                                     scratch_pool));
189
190   if (status == svn_wc__db_status_not_present)
191     /* We know that the delete of this node has been committed.
192        This should be the same as if called on an unknown path. */
193     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
194                              _("Cannot get the pristine contents of '%s' "
195                                "because its delete is already committed"),
196                              svn_dirent_local_style(local_abspath,
197                                                     scratch_pool));
198   else if (status == svn_wc__db_status_server_excluded
199       || status == svn_wc__db_status_excluded
200       || status == svn_wc__db_status_incomplete)
201     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
202                              _("Cannot get the pristine contents of '%s' "
203                                "because it has an unexpected status"),
204                              svn_dirent_local_style(local_abspath,
205                                                     scratch_pool));
206
207   if (checksum == NULL)
208     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
209                              _("Node '%s' has no pristine text"),
210                              svn_dirent_local_style(local_abspath,
211                                                     scratch_pool));
212   SVN_ERR(svn_wc__db_pristine_get_path(result_abspath, db, local_abspath,
213                                        checksum,
214                                        result_pool, scratch_pool));
215   return SVN_NO_ERROR;
216 }
217
218 svn_error_t *
219 svn_wc__get_pristine_contents(svn_stream_t **contents,
220                               svn_filesize_t *size,
221                               svn_wc__db_t *db,
222                               const char *local_abspath,
223                               apr_pool_t *result_pool,
224                               apr_pool_t *scratch_pool)
225 {
226   svn_wc__db_status_t status;
227   svn_node_kind_t kind;
228   const svn_checksum_t *sha1_checksum;
229
230   if (size)
231     *size = SVN_INVALID_FILESIZE;
232
233   SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL, NULL,
234                                         &sha1_checksum, NULL, NULL, NULL,
235                                         db, local_abspath,
236                                         scratch_pool, scratch_pool));
237
238   /* Sanity */
239   if (kind != svn_node_file)
240     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
241                              _("Can only get the pristine contents of files; "
242                                "'%s' is not a file"),
243                              svn_dirent_local_style(local_abspath,
244                                                     scratch_pool));
245
246   if (status == svn_wc__db_status_added && !sha1_checksum)
247     {
248       /* Simply added. The pristine base does not exist. */
249       *contents = NULL;
250       return SVN_NO_ERROR;
251     }
252   else if (status == svn_wc__db_status_not_present)
253     /* We know that the delete of this node has been committed.
254        This should be the same as if called on an unknown path. */
255     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
256                              _("Cannot get the pristine contents of '%s' "
257                                "because its delete is already committed"),
258                              svn_dirent_local_style(local_abspath,
259                                                     scratch_pool));
260   else if (status == svn_wc__db_status_server_excluded
261       || status == svn_wc__db_status_excluded
262       || status == svn_wc__db_status_incomplete)
263     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
264                              _("Cannot get the pristine contents of '%s' "
265                                "because it has an unexpected status"),
266                              svn_dirent_local_style(local_abspath,
267                                                     scratch_pool));
268   if (sha1_checksum)
269     SVN_ERR(svn_wc__db_pristine_read(contents, size, db, local_abspath,
270                                      sha1_checksum,
271                                      result_pool, scratch_pool));
272   else
273     *contents = NULL;
274
275   return SVN_NO_ERROR;
276 }
277
278
279 /*** Opening and closing files in the adm area. ***/
280
281 svn_error_t *
282 svn_wc__open_adm_stream(svn_stream_t **stream,
283                         const char *dir_abspath,
284                         const char *fname,
285                         apr_pool_t *result_pool,
286                         apr_pool_t *scratch_pool)
287 {
288   const char *local_abspath;
289
290   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
291
292   local_abspath = svn_wc__adm_child(dir_abspath, fname, scratch_pool);
293   return svn_error_trace(svn_stream_open_readonly(stream, local_abspath,
294                                                   result_pool, scratch_pool));
295 }
296
297
298 /*** Checking for and creating administrative subdirs. ***/
299
300
301 /* */
302 static svn_error_t *
303 init_adm_tmp_area(const char *path, apr_pool_t *pool)
304 {
305   /* SVN_WC__ADM_TMP */
306   SVN_ERR(make_adm_subdir(path, SVN_WC__ADM_TMP, pool));
307
308   return SVN_NO_ERROR;
309 }
310
311
312 /* Set up a new adm area for PATH, with REPOS_* as the repos info, and
313    INITIAL_REV as the starting revision.  The entries file starts out
314    marked as 'incomplete.  The adm area starts out locked; remember to
315    unlock it when done. */
316 static svn_error_t *
317 init_adm(svn_wc__db_t *db,
318          const char *local_abspath,
319          const char *repos_relpath,
320          const char *repos_root_url,
321          const char *repos_uuid,
322          svn_revnum_t initial_rev,
323          svn_depth_t depth,
324          apr_pool_t *pool)
325 {
326   /* First, make an empty administrative area. */
327   SVN_ERR(svn_io_dir_make_hidden(svn_wc__adm_child(local_abspath, NULL, pool),
328                                  APR_OS_DEFAULT, pool));
329
330   /** Make subdirectories. ***/
331
332   /* SVN_WC__ADM_PRISTINE */
333   SVN_ERR(make_adm_subdir(local_abspath, SVN_WC__ADM_PRISTINE, pool));
334
335   /* ### want to add another directory? do a format bump to ensure that
336      ### all existing working copies get the new directories. or maybe
337      ### create-on-demand (more expensive)  */
338
339   /** Init the tmp area. ***/
340   SVN_ERR(init_adm_tmp_area(local_abspath, pool));
341
342   /* Create the SDB. */
343   SVN_ERR(svn_wc__db_init(db, local_abspath,
344                           repos_relpath, repos_root_url, repos_uuid,
345                           initial_rev, depth,
346                           pool));
347
348   /* Stamp ENTRIES and FORMAT files for old clients.  */
349   SVN_ERR(svn_io_file_create(svn_wc__adm_child(local_abspath,
350                                                SVN_WC__ADM_ENTRIES,
351                                                pool),
352                              SVN_WC__NON_ENTRIES_STRING,
353                              pool));
354   SVN_ERR(svn_io_file_create(svn_wc__adm_child(local_abspath,
355                                                SVN_WC__ADM_FORMAT,
356                                                pool),
357                              SVN_WC__NON_ENTRIES_STRING,
358                              pool));
359
360   return SVN_NO_ERROR;
361 }
362
363 svn_error_t *
364 svn_wc__internal_ensure_adm(svn_wc__db_t *db,
365                             const char *local_abspath,
366                             const char *url,
367                             const char *repos_root_url,
368                             const char *repos_uuid,
369                             svn_revnum_t revision,
370                             svn_depth_t depth,
371                             apr_pool_t *scratch_pool)
372 {
373   int format;
374   const char *original_repos_relpath;
375   const char *original_root_url;
376   svn_boolean_t is_op_root;
377   const char *repos_relpath = svn_uri_skip_ancestor(repos_root_url, url,
378                                                     scratch_pool);
379   svn_wc__db_status_t status;
380   const char *db_repos_relpath, *db_repos_root_url, *db_repos_uuid;
381   svn_revnum_t db_revision;
382
383   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
384   SVN_ERR_ASSERT(url != NULL);
385   SVN_ERR_ASSERT(repos_root_url != NULL);
386   SVN_ERR_ASSERT(repos_uuid != NULL);
387   SVN_ERR_ASSERT(repos_relpath != NULL);
388
389   SVN_ERR(svn_wc__internal_check_wc(&format, db, local_abspath, TRUE,
390                                     scratch_pool));
391
392   /* Early out: we know we're not dealing with an existing wc, so
393      just create one. */
394   if (format == 0)
395     return svn_error_trace(init_adm(db, local_abspath,
396                                     repos_relpath, repos_root_url, repos_uuid,
397                                     revision, depth, scratch_pool));
398
399   SVN_ERR(svn_wc__db_read_info(&status, NULL,
400                                &db_revision, &db_repos_relpath,
401                                &db_repos_root_url, &db_repos_uuid,
402                                NULL, NULL, NULL, NULL, NULL, NULL,
403                                &original_repos_relpath, &original_root_url,
404                                NULL, NULL, NULL, NULL, NULL, NULL,
405                                NULL, &is_op_root, NULL, NULL,
406                                NULL, NULL, NULL,
407                                db, local_abspath, scratch_pool, scratch_pool));
408
409   /* When the directory exists and is scheduled for deletion or is not-present
410    * do not check the revision or the URL.  The revision can be any
411    * arbitrary revision and the URL may differ if the add is
412    * being driven from a merge which will have a different URL. */
413   if (status != svn_wc__db_status_deleted
414       && status != svn_wc__db_status_not_present)
415     {
416       /* ### Should we match copyfrom_revision? */
417       if (db_revision != revision)
418         return
419           svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
420                             _("Revision %ld doesn't match existing "
421                               "revision %ld in '%s'"),
422                             revision, db_revision, local_abspath);
423
424       if (!db_repos_root_url)
425         {
426           if (status == svn_wc__db_status_added)
427             SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
428                                              &db_repos_relpath,
429                                              &db_repos_root_url,
430                                              &db_repos_uuid,
431                                              NULL, NULL, NULL, NULL,
432                                              db, local_abspath,
433                                              scratch_pool, scratch_pool));
434           else
435             SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL,
436                                              &db_repos_relpath,
437                                              &db_repos_root_url,
438                                              &db_repos_uuid, NULL, NULL, NULL,
439                                              NULL, NULL, NULL, NULL, NULL,
440                                              NULL, NULL,
441                                              db, local_abspath,
442                                              scratch_pool, scratch_pool));
443         }
444
445       /* The caller gives us a URL which should match the entry. However,
446          some callers compensate for an old problem in entry->url and pass
447          the copyfrom_url instead. See ^/notes/api-errata/1.7/wc002.txt. As
448          a result, we allow the passed URL to match copyfrom_url if it
449          does not match the entry's primary URL.  */
450       if (strcmp(db_repos_uuid, repos_uuid)
451           || strcmp(db_repos_root_url, repos_root_url)
452           || !svn_relpath_skip_ancestor(db_repos_relpath, repos_relpath))
453         {
454           if (!is_op_root /* copy_from was set on op-roots only */
455               || original_root_url == NULL
456               || strcmp(original_root_url, repos_root_url)
457               || strcmp(original_repos_relpath, repos_relpath))
458             return
459               svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
460                                 _("URL '%s' (uuid: '%s') doesn't match existing "
461                                   "URL '%s' (uuid: '%s') in '%s'"),
462                                 url,
463                                 db_repos_uuid,
464                                 svn_path_url_add_component2(db_repos_root_url,
465                                                             db_repos_relpath,
466                                                             scratch_pool),
467                                 repos_uuid,
468                                 local_abspath);
469         }
470     }
471
472   return SVN_NO_ERROR;
473 }
474
475 svn_error_t *
476 svn_wc_ensure_adm4(svn_wc_context_t *wc_ctx,
477                    const char *local_abspath,
478                    const char *url,
479                    const char *repos_root_url,
480                    const char *repos_uuid,
481                    svn_revnum_t revision,
482                    svn_depth_t depth,
483                    apr_pool_t *scratch_pool)
484 {
485   return svn_error_trace(
486     svn_wc__internal_ensure_adm(wc_ctx->db, local_abspath, url, repos_root_url,
487                                 repos_uuid, revision, depth, scratch_pool));
488 }
489
490 svn_error_t *
491 svn_wc__adm_destroy(svn_wc__db_t *db,
492                     const char *dir_abspath,
493                     svn_cancel_func_t cancel_func,
494                     void *cancel_baton,
495                     apr_pool_t *scratch_pool)
496 {
497   svn_boolean_t is_wcroot;
498
499   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
500
501   SVN_ERR(svn_wc__write_check(db, dir_abspath, scratch_pool));
502
503   SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, dir_abspath, scratch_pool));
504
505   /* Well, the coast is clear for blowing away the administrative
506      directory, which also removes remaining locks */
507
508   /* Now close the DB, and we can delete the working copy */
509   if (is_wcroot)
510     {
511       SVN_ERR(svn_wc__db_drop_root(db, dir_abspath, scratch_pool));
512       SVN_ERR(svn_io_remove_dir2(svn_wc__adm_child(dir_abspath, NULL,
513                                                    scratch_pool),
514                                  FALSE,
515                                  cancel_func, cancel_baton,
516                                  scratch_pool));
517     }
518
519   return SVN_NO_ERROR;
520 }
521
522
523 svn_error_t *
524 svn_wc__adm_cleanup_tmp_area(svn_wc__db_t *db,
525                              const char *adm_abspath,
526                              apr_pool_t *scratch_pool)
527 {
528   const char *tmp_path;
529
530   SVN_ERR_ASSERT(svn_dirent_is_absolute(adm_abspath));
531
532   SVN_ERR(svn_wc__write_check(db, adm_abspath, scratch_pool));
533
534   /* Get the path to the tmp area, and blow it away. */
535   tmp_path = svn_wc__adm_child(adm_abspath, SVN_WC__ADM_TMP, scratch_pool);
536
537   SVN_ERR(svn_io_remove_dir2(tmp_path, TRUE, NULL, NULL, scratch_pool));
538
539   /* Now, rebuild the tmp area. */
540   return svn_error_trace(init_adm_tmp_area(adm_abspath, scratch_pool));
541 }
542
543
544 svn_error_t *
545 svn_wc__get_tmpdir(const char **tmpdir_abspath,
546                    svn_wc_context_t *wc_ctx,
547                    const char *wri_abspath,
548                    apr_pool_t *result_pool,
549                    apr_pool_t *scratch_pool)
550 {
551   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(tmpdir_abspath,
552                                          wc_ctx->db, wri_abspath,
553                                          result_pool, scratch_pool));
554   return SVN_NO_ERROR;
555 }