]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_fs_fs/rep-cache.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_fs_fs / rep-cache.c
1 /* rep-sharing.c --- the rep-sharing cache for fsfs
2  *
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  */
22
23 #include "svn_pools.h"
24
25 #include "svn_private_config.h"
26
27 #include "fs_fs.h"
28 #include "fs.h"
29 #include "rep-cache.h"
30 #include "../libsvn_fs/fs-loader.h"
31
32 #include "svn_path.h"
33
34 #include "private/svn_sqlite.h"
35
36 #include "rep-cache-db.h"
37
38 /* A few magic values */
39 #define REP_CACHE_SCHEMA_FORMAT   1
40
41 REP_CACHE_DB_SQL_DECLARE_STATEMENTS(statements);
42
43
44 \f
45 /** Helper functions. **/
46 static APR_INLINE const char *
47 path_rep_cache_db(const char *fs_path,
48                   apr_pool_t *result_pool)
49 {
50   return svn_dirent_join(fs_path, REP_CACHE_DB_NAME, result_pool);
51 }
52
53 /* Check that REP refers to a revision that exists in FS. */
54 static svn_error_t *
55 rep_has_been_born(representation_t *rep,
56                   svn_fs_t *fs,
57                   apr_pool_t *pool)
58 {
59   SVN_ERR_ASSERT(rep);
60
61   SVN_ERR(svn_fs_fs__revision_exists(rep->revision, fs, pool));
62
63   return SVN_NO_ERROR;
64 }
65
66
67 \f
68 /** Library-private API's. **/
69
70 /* Body of svn_fs_fs__open_rep_cache().
71    Implements svn_atomic__init_once().init_func.
72  */
73 static svn_error_t *
74 open_rep_cache(void *baton,
75                apr_pool_t *pool)
76 {
77   svn_fs_t *fs = baton;
78   fs_fs_data_t *ffd = fs->fsap_data;
79   svn_sqlite__db_t *sdb;
80   const char *db_path;
81   int version;
82
83   /* Open (or create) the sqlite database.  It will be automatically
84      closed when fs->pool is destoyed.  */
85   db_path = path_rep_cache_db(fs->path, pool);
86 #ifndef WIN32
87   {
88     /* We want to extend the permissions that apply to the repository
89        as a whole when creating a new rep cache and not simply default
90        to umask. */
91     svn_boolean_t exists;
92
93     SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, fs, pool));
94     if (!exists)
95       {
96         const char *current = svn_fs_fs__path_current(fs, pool);
97         svn_error_t *err = svn_io_file_create(db_path, "", pool);
98
99         if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
100           /* A real error. */
101           return svn_error_trace(err);
102         else if (err)
103           /* Some other thread/process created the file. */
104           svn_error_clear(err);
105         else
106           /* We created the file. */
107           SVN_ERR(svn_io_copy_perms(current, db_path, pool));
108       }
109   }
110 #endif
111   SVN_ERR(svn_sqlite__open(&sdb, db_path,
112                            svn_sqlite__mode_rwcreate, statements,
113                            0, NULL,
114                            fs->pool, pool));
115
116   SVN_ERR(svn_sqlite__read_schema_version(&version, sdb, pool));
117   if (version < REP_CACHE_SCHEMA_FORMAT)
118     {
119       /* Must be 0 -- an uninitialized (no schema) database. Create
120          the schema. Results in schema version of 1.  */
121       SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_SCHEMA));
122     }
123
124   /* This is used as a flag that the database is available so don't
125      set it earlier. */
126   ffd->rep_cache_db = sdb;
127
128   return SVN_NO_ERROR;
129 }
130
131 svn_error_t *
132 svn_fs_fs__open_rep_cache(svn_fs_t *fs,
133                           apr_pool_t *pool)
134 {
135   fs_fs_data_t *ffd = fs->fsap_data;
136   svn_error_t *err = svn_atomic__init_once(&ffd->rep_cache_db_opened,
137                                            open_rep_cache, fs, pool);
138   return svn_error_quick_wrap(err, _("Couldn't open rep-cache database"));
139 }
140
141 svn_error_t *
142 svn_fs_fs__exists_rep_cache(svn_boolean_t *exists,
143                             svn_fs_t *fs, apr_pool_t *pool)
144 {
145   svn_node_kind_t kind;
146
147   SVN_ERR(svn_io_check_path(path_rep_cache_db(fs->path, pool),
148                             &kind, pool));
149
150   *exists = (kind != svn_node_none);
151   return SVN_NO_ERROR;
152 }
153
154 svn_error_t *
155 svn_fs_fs__walk_rep_reference(svn_fs_t *fs,
156                               svn_revnum_t start,
157                               svn_revnum_t end,
158                               svn_error_t *(*walker)(representation_t *,
159                                                      void *,
160                                                      svn_fs_t *,
161                                                      apr_pool_t *),
162                               void *walker_baton,
163                               svn_cancel_func_t cancel_func,
164                               void *cancel_baton,
165                               apr_pool_t *pool)
166 {
167   fs_fs_data_t *ffd = fs->fsap_data;
168   svn_sqlite__stmt_t *stmt;
169   svn_boolean_t have_row;
170   int iterations = 0;
171
172   apr_pool_t *iterpool = svn_pool_create(pool);
173
174   /* Don't check ffd->rep_sharing_allowed. */
175   SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT);
176
177   if (! ffd->rep_cache_db)
178     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
179
180   /* Check global invariants. */
181   if (start == 0)
182     {
183       svn_revnum_t max;
184
185       SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db,
186                                         STMT_GET_MAX_REV));
187       SVN_ERR(svn_sqlite__step(&have_row, stmt));
188       max = svn_sqlite__column_revnum(stmt, 0);
189       SVN_ERR(svn_sqlite__reset(stmt));
190       if (SVN_IS_VALID_REVNUM(max))  /* The rep-cache could be empty. */
191         SVN_ERR(svn_fs_fs__revision_exists(max, fs, iterpool));
192     }
193
194   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db,
195                                     STMT_GET_REPS_FOR_RANGE));
196   SVN_ERR(svn_sqlite__bindf(stmt, "rr",
197                             start, end));
198
199   /* Walk the cache entries. */
200   SVN_ERR(svn_sqlite__step(&have_row, stmt));
201   while (have_row)
202     {
203       representation_t *rep;
204       const char *sha1_digest;
205       svn_error_t *err;
206
207       /* Clear ITERPOOL occasionally. */
208       if (iterations++ % 16 == 0)
209         svn_pool_clear(iterpool);
210
211       /* Check for cancellation. */
212       if (cancel_func)
213         {
214           err = cancel_func(cancel_baton);
215           if (err)
216             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
217         }
218
219       /* Construct a representation_t. */
220       rep = apr_pcalloc(iterpool, sizeof(*rep));
221       sha1_digest = svn_sqlite__column_text(stmt, 0, iterpool);
222       err = svn_checksum_parse_hex(&rep->sha1_checksum,
223                                    svn_checksum_sha1, sha1_digest,
224                                    iterpool);
225       if (err)
226         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
227       rep->revision = svn_sqlite__column_revnum(stmt, 1);
228       rep->offset = svn_sqlite__column_int64(stmt, 2);
229       rep->size = svn_sqlite__column_int64(stmt, 3);
230       rep->expanded_size = svn_sqlite__column_int64(stmt, 4);
231
232       /* Walk. */
233       err = walker(rep, walker_baton, fs, iterpool);
234       if (err)
235         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
236
237       SVN_ERR(svn_sqlite__step(&have_row, stmt));
238     }
239
240   SVN_ERR(svn_sqlite__reset(stmt));
241   svn_pool_destroy(iterpool);
242
243   return SVN_NO_ERROR;
244 }
245
246
247 /* This function's caller ignores most errors it returns.
248    If you extend this function, check the callsite to see if you have
249    to make it not-ignore additional error codes.  */
250 svn_error_t *
251 svn_fs_fs__get_rep_reference(representation_t **rep,
252                              svn_fs_t *fs,
253                              svn_checksum_t *checksum,
254                              apr_pool_t *pool)
255 {
256   fs_fs_data_t *ffd = fs->fsap_data;
257   svn_sqlite__stmt_t *stmt;
258   svn_boolean_t have_row;
259
260   SVN_ERR_ASSERT(ffd->rep_sharing_allowed);
261   if (! ffd->rep_cache_db)
262     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
263
264   /* We only allow SHA1 checksums in this table. */
265   if (checksum->kind != svn_checksum_sha1)
266     return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
267                             _("Only SHA1 checksums can be used as keys in the "
268                               "rep_cache table.\n"));
269
270   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, STMT_GET_REP));
271   SVN_ERR(svn_sqlite__bindf(stmt, "s",
272                             svn_checksum_to_cstring(checksum, pool)));
273
274   SVN_ERR(svn_sqlite__step(&have_row, stmt));
275   if (have_row)
276     {
277       *rep = apr_pcalloc(pool, sizeof(**rep));
278       (*rep)->sha1_checksum = svn_checksum_dup(checksum, pool);
279       (*rep)->revision = svn_sqlite__column_revnum(stmt, 0);
280       (*rep)->offset = svn_sqlite__column_int64(stmt, 1);
281       (*rep)->size = svn_sqlite__column_int64(stmt, 2);
282       (*rep)->expanded_size = svn_sqlite__column_int64(stmt, 3);
283     }
284   else
285     *rep = NULL;
286
287   SVN_ERR(svn_sqlite__reset(stmt));
288
289   if (*rep)
290     SVN_ERR(rep_has_been_born(*rep, fs, pool));
291
292   return SVN_NO_ERROR;
293 }
294
295 svn_error_t *
296 svn_fs_fs__set_rep_reference(svn_fs_t *fs,
297                              representation_t *rep,
298                              svn_boolean_t reject_dup,
299                              apr_pool_t *pool)
300 {
301   fs_fs_data_t *ffd = fs->fsap_data;
302   svn_sqlite__stmt_t *stmt;
303   svn_error_t *err;
304
305   SVN_ERR_ASSERT(ffd->rep_sharing_allowed);
306   if (! ffd->rep_cache_db)
307     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
308
309   /* We only allow SHA1 checksums in this table. */
310   if (rep->sha1_checksum == NULL)
311     return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
312                             _("Only SHA1 checksums can be used as keys in the "
313                               "rep_cache table.\n"));
314
315   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, STMT_SET_REP));
316   SVN_ERR(svn_sqlite__bindf(stmt, "siiii",
317                             svn_checksum_to_cstring(rep->sha1_checksum, pool),
318                             (apr_int64_t) rep->revision,
319                             (apr_int64_t) rep->offset,
320                             (apr_int64_t) rep->size,
321                             (apr_int64_t) rep->expanded_size));
322
323   err = svn_sqlite__insert(NULL, stmt);
324   if (err)
325     {
326       representation_t *old_rep;
327
328       if (err->apr_err != SVN_ERR_SQLITE_CONSTRAINT)
329         return svn_error_trace(err);
330
331       svn_error_clear(err);
332
333       /* Constraint failed so the mapping for SHA1_CHECKSUM->REP
334          should exist.  If so, and the value is the same one we were
335          about to write, that's cool -- just do nothing.  If, however,
336          the value is *different*, that's a red flag!  */
337       SVN_ERR(svn_fs_fs__get_rep_reference(&old_rep, fs, rep->sha1_checksum,
338                                            pool));
339
340       if (old_rep)
341         {
342           if (reject_dup && ((old_rep->revision != rep->revision)
343                              || (old_rep->offset != rep->offset)
344                              || (old_rep->size != rep->size)
345                              || (old_rep->expanded_size != rep->expanded_size)))
346             return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
347                                      apr_psprintf(pool,
348                               _("Representation key for checksum '%%s' exists "
349                                 "in filesystem '%%s' with a different value "
350                                 "(%%ld,%%%s,%%%s,%%%s) than what we were about "
351                                 "to store (%%ld,%%%s,%%%s,%%%s)"),
352                               APR_OFF_T_FMT, SVN_FILESIZE_T_FMT,
353                               SVN_FILESIZE_T_FMT, APR_OFF_T_FMT,
354                               SVN_FILESIZE_T_FMT, SVN_FILESIZE_T_FMT),
355                  svn_checksum_to_cstring_display(rep->sha1_checksum, pool),
356                  fs->path, old_rep->revision, old_rep->offset, old_rep->size,
357                  old_rep->expanded_size, rep->revision, rep->offset, rep->size,
358                  rep->expanded_size);
359           else
360             return SVN_NO_ERROR;
361         }
362       else
363         {
364           /* Something really odd at this point, we failed to insert the
365              checksum AND failed to read an existing checksum.  Do we need
366              to flag this? */
367         }
368     }
369
370   return SVN_NO_ERROR;
371 }
372
373
374 svn_error_t *
375 svn_fs_fs__del_rep_reference(svn_fs_t *fs,
376                              svn_revnum_t youngest,
377                              apr_pool_t *pool)
378 {
379   fs_fs_data_t *ffd = fs->fsap_data;
380   svn_sqlite__stmt_t *stmt;
381
382   SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT);
383   if (! ffd->rep_cache_db)
384     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
385
386   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db,
387                                     STMT_DEL_REPS_YOUNGER_THAN_REV));
388   SVN_ERR(svn_sqlite__bindf(stmt, "r", youngest));
389   SVN_ERR(svn_sqlite__step_done(stmt));
390
391   return SVN_NO_ERROR;
392 }
393
394 svn_error_t *
395 svn_fs_fs__lock_rep_cache(svn_fs_t *fs,
396                           apr_pool_t *pool)
397 {
398   fs_fs_data_t *ffd = fs->fsap_data;
399
400   if (! ffd->rep_cache_db)
401     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
402
403   SVN_ERR(svn_sqlite__exec_statements(ffd->rep_cache_db, STMT_LOCK_REP));
404
405   return SVN_NO_ERROR;
406 }