]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_wc/adm_files.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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                               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 svn_error_t *
299 svn_wc__open_writable_base(svn_stream_t **stream,
300                            const char **temp_base_abspath,
301                            svn_checksum_t **md5_checksum,
302                            svn_checksum_t **sha1_checksum,
303                            svn_wc__db_t *db,
304                            const char *wri_abspath,
305                            apr_pool_t *result_pool,
306                            apr_pool_t *scratch_pool)
307 {
308   const char *temp_dir_abspath;
309   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
310
311   SVN_ERR(svn_wc__db_pristine_get_tempdir(&temp_dir_abspath, db, wri_abspath,
312                                           scratch_pool, scratch_pool));
313   SVN_ERR(svn_stream_open_unique(stream,
314                                  temp_base_abspath,
315                                  temp_dir_abspath,
316                                  svn_io_file_del_none,
317                                  result_pool, scratch_pool));
318   if (md5_checksum)
319     *stream = svn_stream_checksummed2(*stream, NULL, md5_checksum,
320                                       svn_checksum_md5, FALSE, result_pool);
321   if (sha1_checksum)
322     *stream = svn_stream_checksummed2(*stream, NULL, sha1_checksum,
323                                       svn_checksum_sha1, FALSE, result_pool);
324
325   return SVN_NO_ERROR;
326 }
327
328
329 \f
330 /*** Checking for and creating administrative subdirs. ***/
331
332
333 /* */
334 static svn_error_t *
335 init_adm_tmp_area(const char *path, apr_pool_t *pool)
336 {
337   /* SVN_WC__ADM_TMP */
338   SVN_ERR(make_adm_subdir(path, SVN_WC__ADM_TMP, pool));
339
340   return SVN_NO_ERROR;
341 }
342
343
344 /* Set up a new adm area for PATH, with REPOS_* as the repos info, and
345    INITIAL_REV as the starting revision.  The entries file starts out
346    marked as 'incomplete.  The adm area starts out locked; remember to
347    unlock it when done. */
348 static svn_error_t *
349 init_adm(svn_wc__db_t *db,
350          const char *local_abspath,
351          const char *repos_relpath,
352          const char *repos_root_url,
353          const char *repos_uuid,
354          svn_revnum_t initial_rev,
355          svn_depth_t depth,
356          apr_pool_t *pool)
357 {
358   /* First, make an empty administrative area. */
359   SVN_ERR(svn_io_dir_make_hidden(svn_wc__adm_child(local_abspath, NULL, pool),
360                                  APR_OS_DEFAULT, pool));
361
362   /** Make subdirectories. ***/
363
364   /* SVN_WC__ADM_PRISTINE */
365   SVN_ERR(make_adm_subdir(local_abspath, SVN_WC__ADM_PRISTINE, pool));
366
367   /* ### want to add another directory? do a format bump to ensure that
368      ### all existing working copies get the new directories. or maybe
369      ### create-on-demand (more expensive)  */
370
371   /** Init the tmp area. ***/
372   SVN_ERR(init_adm_tmp_area(local_abspath, pool));
373
374   /* Create the SDB. */
375   SVN_ERR(svn_wc__db_init(db, local_abspath,
376                           repos_relpath, repos_root_url, repos_uuid,
377                           initial_rev, depth,
378                           pool));
379
380   /* Stamp ENTRIES and FORMAT files for old clients.  */
381   SVN_ERR(svn_io_file_create(svn_wc__adm_child(local_abspath,
382                                                SVN_WC__ADM_ENTRIES,
383                                                pool),
384                              SVN_WC__NON_ENTRIES_STRING,
385                              pool));
386   SVN_ERR(svn_io_file_create(svn_wc__adm_child(local_abspath,
387                                                SVN_WC__ADM_FORMAT,
388                                                pool),
389                              SVN_WC__NON_ENTRIES_STRING,
390                              pool));
391
392   return SVN_NO_ERROR;
393 }
394
395 svn_error_t *
396 svn_wc__internal_ensure_adm(svn_wc__db_t *db,
397                             const char *local_abspath,
398                             const char *url,
399                             const char *repos_root_url,
400                             const char *repos_uuid,
401                             svn_revnum_t revision,
402                             svn_depth_t depth,
403                             apr_pool_t *scratch_pool)
404 {
405   int format;
406   const char *original_repos_relpath;
407   const char *original_root_url;
408   svn_boolean_t is_op_root;
409   const char *repos_relpath = svn_uri_skip_ancestor(repos_root_url, url,
410                                                     scratch_pool);
411   svn_wc__db_status_t status;
412   const char *db_repos_relpath, *db_repos_root_url, *db_repos_uuid;
413   svn_revnum_t db_revision;
414
415   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
416   SVN_ERR_ASSERT(url != NULL);
417   SVN_ERR_ASSERT(repos_root_url != NULL);
418   SVN_ERR_ASSERT(repos_uuid != NULL);
419   SVN_ERR_ASSERT(repos_relpath != NULL);
420
421   SVN_ERR(svn_wc__internal_check_wc(&format, db, local_abspath, TRUE,
422                                     scratch_pool));
423
424   /* Early out: we know we're not dealing with an existing wc, so
425      just create one. */
426   if (format == 0)
427     return svn_error_trace(init_adm(db, local_abspath,
428                                     repos_relpath, repos_root_url, repos_uuid,
429                                     revision, depth, scratch_pool));
430
431   SVN_ERR(svn_wc__db_read_info(&status, NULL,
432                                &db_revision, &db_repos_relpath,
433                                &db_repos_root_url, &db_repos_uuid,
434                                NULL, NULL, NULL, NULL, NULL, NULL,
435                                &original_repos_relpath, &original_root_url,
436                                NULL, NULL, NULL, NULL, NULL, NULL,
437                                NULL, &is_op_root, NULL, NULL,
438                                NULL, NULL, NULL,
439                                db, local_abspath, scratch_pool, scratch_pool));
440
441   /* When the directory exists and is scheduled for deletion or is not-present
442    * do not check the revision or the URL.  The revision can be any
443    * arbitrary revision and the URL may differ if the add is
444    * being driven from a merge which will have a different URL. */
445   if (status != svn_wc__db_status_deleted
446       && status != svn_wc__db_status_not_present)
447     {
448       /* ### Should we match copyfrom_revision? */
449       if (db_revision != revision)
450         return
451           svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
452                             _("Revision %ld doesn't match existing "
453                               "revision %ld in '%s'"),
454                             revision, db_revision, local_abspath);
455
456       if (!db_repos_root_url)
457         {
458           if (status == svn_wc__db_status_added)
459             SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
460                                              &db_repos_relpath,
461                                              &db_repos_root_url,
462                                              &db_repos_uuid,
463                                              NULL, NULL, NULL, NULL,
464                                              db, local_abspath,
465                                              scratch_pool, scratch_pool));
466           else
467             SVN_ERR(svn_wc__db_scan_base_repos(&db_repos_relpath,
468                                                &db_repos_root_url,
469                                                &db_repos_uuid,
470                                                db, local_abspath,
471                                                scratch_pool, scratch_pool));
472         }
473
474       /* The caller gives us a URL which should match the entry. However,
475          some callers compensate for an old problem in entry->url and pass
476          the copyfrom_url instead. See ^/notes/api-errata/1.7/wc002.txt. As
477          a result, we allow the passed URL to match copyfrom_url if it
478          does not match the entry's primary URL.  */
479       if (strcmp(db_repos_uuid, repos_uuid)
480           || strcmp(db_repos_root_url, repos_root_url)
481           || !svn_relpath_skip_ancestor(db_repos_relpath, repos_relpath))
482         {
483           if (!is_op_root /* copy_from was set on op-roots only */
484               || original_root_url == NULL
485               || strcmp(original_root_url, repos_root_url)
486               || strcmp(original_repos_relpath, repos_relpath))
487             return
488               svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
489                                 _("URL '%s' (uuid: '%s') doesn't match existing "
490                                   "URL '%s' (uuid: '%s') in '%s'"),
491                                 url,
492                                 db_repos_uuid,
493                                 svn_path_url_add_component2(db_repos_root_url,
494                                                             db_repos_relpath,
495                                                             scratch_pool),
496                                 repos_uuid,
497                                 local_abspath);
498         }
499     }
500
501   return SVN_NO_ERROR;
502 }
503
504 svn_error_t *
505 svn_wc_ensure_adm4(svn_wc_context_t *wc_ctx,
506                    const char *local_abspath,
507                    const char *url,
508                    const char *repos_root_url,
509                    const char *repos_uuid,
510                    svn_revnum_t revision,
511                    svn_depth_t depth,
512                    apr_pool_t *scratch_pool)
513 {
514   return svn_error_trace(
515     svn_wc__internal_ensure_adm(wc_ctx->db, local_abspath, url, repos_root_url,
516                                 repos_uuid, revision, depth, scratch_pool));
517 }
518
519 svn_error_t *
520 svn_wc__adm_destroy(svn_wc__db_t *db,
521                     const char *dir_abspath,
522                     svn_cancel_func_t cancel_func,
523                     void *cancel_baton,
524                     apr_pool_t *scratch_pool)
525 {
526   svn_boolean_t is_wcroot;
527
528   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
529
530   SVN_ERR(svn_wc__write_check(db, dir_abspath, scratch_pool));
531
532   SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, dir_abspath, scratch_pool));
533
534   /* Well, the coast is clear for blowing away the administrative
535      directory, which also removes remaining locks */
536
537   /* Now close the DB, and we can delete the working copy */
538   if (is_wcroot)
539     {
540       SVN_ERR(svn_wc__db_drop_root(db, dir_abspath, scratch_pool));
541       SVN_ERR(svn_io_remove_dir2(svn_wc__adm_child(dir_abspath, NULL,
542                                                    scratch_pool),
543                                  FALSE,
544                                  cancel_func, cancel_baton,
545                                  scratch_pool));
546     }
547
548   return SVN_NO_ERROR;
549 }
550
551
552 svn_error_t *
553 svn_wc__adm_cleanup_tmp_area(svn_wc__db_t *db,
554                              const char *adm_abspath,
555                              apr_pool_t *scratch_pool)
556 {
557   const char *tmp_path;
558
559   SVN_ERR_ASSERT(svn_dirent_is_absolute(adm_abspath));
560
561   SVN_ERR(svn_wc__write_check(db, adm_abspath, scratch_pool));
562
563   /* Get the path to the tmp area, and blow it away. */
564   tmp_path = svn_wc__adm_child(adm_abspath, SVN_WC__ADM_TMP, scratch_pool);
565
566   SVN_ERR(svn_io_remove_dir2(tmp_path, TRUE, NULL, NULL, scratch_pool));
567
568   /* Now, rebuild the tmp area. */
569   return svn_error_trace(init_adm_tmp_area(adm_abspath, scratch_pool));
570 }
571
572
573 svn_error_t *
574 svn_wc__get_tmpdir(const char **tmpdir_abspath,
575                    svn_wc_context_t *wc_ctx,
576                    const char *wri_abspath,
577                    apr_pool_t *result_pool,
578                    apr_pool_t *scratch_pool)
579 {
580   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(tmpdir_abspath,
581                                          wc_ctx->db, wri_abspath,
582                                          result_pool, scratch_pool));
583   return SVN_NO_ERROR;
584 }