]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_fs_fs/rep-cache.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.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   SVN_ERR(svn_sqlite__open(&sdb, db_path,
87                            svn_sqlite__mode_rwcreate, statements,
88                            0, NULL,
89                            fs->pool, pool));
90
91   SVN_ERR(svn_sqlite__read_schema_version(&version, sdb, pool));
92   if (version < REP_CACHE_SCHEMA_FORMAT)
93     {
94       /* Must be 0 -- an uninitialized (no schema) database. Create
95          the schema. Results in schema version of 1.  */
96       SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_SCHEMA));
97     }
98
99   /* This is used as a flag that the database is available so don't
100      set it earlier. */
101   ffd->rep_cache_db = sdb;
102
103   return SVN_NO_ERROR;
104 }
105
106 svn_error_t *
107 svn_fs_fs__open_rep_cache(svn_fs_t *fs,
108                           apr_pool_t *pool)
109 {
110   fs_fs_data_t *ffd = fs->fsap_data;
111   svn_error_t *err = svn_atomic__init_once(&ffd->rep_cache_db_opened,
112                                            open_rep_cache, fs, pool);
113   return svn_error_quick_wrap(err, _("Couldn't open rep-cache database"));
114 }
115
116 svn_error_t *
117 svn_fs_fs__exists_rep_cache(svn_boolean_t *exists,
118                             svn_fs_t *fs, apr_pool_t *pool)
119 {
120   svn_node_kind_t kind;
121
122   SVN_ERR(svn_io_check_path(path_rep_cache_db(fs->path, pool),
123                             &kind, pool));
124
125   *exists = (kind != svn_node_none);
126   return SVN_NO_ERROR;
127 }
128
129 svn_error_t *
130 svn_fs_fs__walk_rep_reference(svn_fs_t *fs,
131                               svn_revnum_t start,
132                               svn_revnum_t end,
133                               svn_error_t *(*walker)(representation_t *,
134                                                      void *,
135                                                      svn_fs_t *,
136                                                      apr_pool_t *),
137                               void *walker_baton,
138                               svn_cancel_func_t cancel_func,
139                               void *cancel_baton,
140                               apr_pool_t *pool)
141 {
142   fs_fs_data_t *ffd = fs->fsap_data;
143   svn_sqlite__stmt_t *stmt;
144   svn_boolean_t have_row;
145   int iterations = 0;
146
147   apr_pool_t *iterpool = svn_pool_create(pool);
148
149   /* Don't check ffd->rep_sharing_allowed. */
150   SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT);
151
152   if (! ffd->rep_cache_db)
153     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
154
155   /* Check global invariants. */
156   if (start == 0)
157     {
158       svn_revnum_t max;
159
160       SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db,
161                                         STMT_GET_MAX_REV));
162       SVN_ERR(svn_sqlite__step(&have_row, stmt));
163       max = svn_sqlite__column_revnum(stmt, 0);
164       SVN_ERR(svn_sqlite__reset(stmt));
165       if (SVN_IS_VALID_REVNUM(max))  /* The rep-cache could be empty. */
166         SVN_ERR(svn_fs_fs__revision_exists(max, fs, iterpool));
167     }
168
169   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db,
170                                     STMT_GET_REPS_FOR_RANGE));
171   SVN_ERR(svn_sqlite__bindf(stmt, "rr",
172                             start, end));
173
174   /* Walk the cache entries. */
175   SVN_ERR(svn_sqlite__step(&have_row, stmt));
176   while (have_row)
177     {
178       representation_t *rep;
179       const char *sha1_digest;
180       svn_error_t *err;
181
182       /* Clear ITERPOOL occasionally. */
183       if (iterations++ % 16 == 0)
184         svn_pool_clear(iterpool);
185
186       /* Check for cancellation. */
187       if (cancel_func)
188         {
189           err = cancel_func(cancel_baton);
190           if (err)
191             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
192         }
193
194       /* Construct a representation_t. */
195       rep = apr_pcalloc(iterpool, sizeof(*rep));
196       sha1_digest = svn_sqlite__column_text(stmt, 0, iterpool);
197       err = svn_checksum_parse_hex(&rep->sha1_checksum,
198                                    svn_checksum_sha1, sha1_digest,
199                                    iterpool);
200       if (err)
201         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
202       rep->revision = svn_sqlite__column_revnum(stmt, 1);
203       rep->offset = svn_sqlite__column_int64(stmt, 2);
204       rep->size = svn_sqlite__column_int64(stmt, 3);
205       rep->expanded_size = svn_sqlite__column_int64(stmt, 4);
206
207       /* Walk. */
208       err = walker(rep, walker_baton, fs, iterpool);
209       if (err)
210         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
211
212       SVN_ERR(svn_sqlite__step(&have_row, stmt));
213     }
214
215   SVN_ERR(svn_sqlite__reset(stmt));
216   svn_pool_destroy(iterpool);
217
218   return SVN_NO_ERROR;
219 }
220
221
222 /* This function's caller ignores most errors it returns.
223    If you extend this function, check the callsite to see if you have
224    to make it not-ignore additional error codes.  */
225 svn_error_t *
226 svn_fs_fs__get_rep_reference(representation_t **rep,
227                              svn_fs_t *fs,
228                              svn_checksum_t *checksum,
229                              apr_pool_t *pool)
230 {
231   fs_fs_data_t *ffd = fs->fsap_data;
232   svn_sqlite__stmt_t *stmt;
233   svn_boolean_t have_row;
234
235   SVN_ERR_ASSERT(ffd->rep_sharing_allowed);
236   if (! ffd->rep_cache_db)
237     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
238
239   /* We only allow SHA1 checksums in this table. */
240   if (checksum->kind != svn_checksum_sha1)
241     return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
242                             _("Only SHA1 checksums can be used as keys in the "
243                               "rep_cache table.\n"));
244
245   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, STMT_GET_REP));
246   SVN_ERR(svn_sqlite__bindf(stmt, "s",
247                             svn_checksum_to_cstring(checksum, pool)));
248
249   SVN_ERR(svn_sqlite__step(&have_row, stmt));
250   if (have_row)
251     {
252       *rep = apr_pcalloc(pool, sizeof(**rep));
253       (*rep)->sha1_checksum = svn_checksum_dup(checksum, pool);
254       (*rep)->revision = svn_sqlite__column_revnum(stmt, 0);
255       (*rep)->offset = svn_sqlite__column_int64(stmt, 1);
256       (*rep)->size = svn_sqlite__column_int64(stmt, 2);
257       (*rep)->expanded_size = svn_sqlite__column_int64(stmt, 3);
258     }
259   else
260     *rep = NULL;
261
262   SVN_ERR(svn_sqlite__reset(stmt));
263
264   if (*rep)
265     SVN_ERR(rep_has_been_born(*rep, fs, pool));
266
267   return SVN_NO_ERROR;
268 }
269
270 svn_error_t *
271 svn_fs_fs__set_rep_reference(svn_fs_t *fs,
272                              representation_t *rep,
273                              svn_boolean_t reject_dup,
274                              apr_pool_t *pool)
275 {
276   fs_fs_data_t *ffd = fs->fsap_data;
277   svn_sqlite__stmt_t *stmt;
278   svn_error_t *err;
279
280   SVN_ERR_ASSERT(ffd->rep_sharing_allowed);
281   if (! ffd->rep_cache_db)
282     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
283
284   /* We only allow SHA1 checksums in this table. */
285   if (rep->sha1_checksum == NULL)
286     return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
287                             _("Only SHA1 checksums can be used as keys in the "
288                               "rep_cache table.\n"));
289
290   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, STMT_SET_REP));
291   SVN_ERR(svn_sqlite__bindf(stmt, "siiii",
292                             svn_checksum_to_cstring(rep->sha1_checksum, pool),
293                             (apr_int64_t) rep->revision,
294                             (apr_int64_t) rep->offset,
295                             (apr_int64_t) rep->size,
296                             (apr_int64_t) rep->expanded_size));
297
298   err = svn_sqlite__insert(NULL, stmt);
299   if (err)
300     {
301       representation_t *old_rep;
302
303       if (err->apr_err != SVN_ERR_SQLITE_CONSTRAINT)
304         return svn_error_trace(err);
305
306       svn_error_clear(err);
307
308       /* Constraint failed so the mapping for SHA1_CHECKSUM->REP
309          should exist.  If so, and the value is the same one we were
310          about to write, that's cool -- just do nothing.  If, however,
311          the value is *different*, that's a red flag!  */
312       SVN_ERR(svn_fs_fs__get_rep_reference(&old_rep, fs, rep->sha1_checksum,
313                                            pool));
314
315       if (old_rep)
316         {
317           if (reject_dup && ((old_rep->revision != rep->revision)
318                              || (old_rep->offset != rep->offset)
319                              || (old_rep->size != rep->size)
320                              || (old_rep->expanded_size != rep->expanded_size)))
321             return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
322                                      apr_psprintf(pool,
323                               _("Representation key for checksum '%%s' exists "
324                                 "in filesystem '%%s' with a different value "
325                                 "(%%ld,%%%s,%%%s,%%%s) than what we were about "
326                                 "to store (%%ld,%%%s,%%%s,%%%s)"),
327                               APR_OFF_T_FMT, SVN_FILESIZE_T_FMT,
328                               SVN_FILESIZE_T_FMT, APR_OFF_T_FMT,
329                               SVN_FILESIZE_T_FMT, SVN_FILESIZE_T_FMT),
330                  svn_checksum_to_cstring_display(rep->sha1_checksum, pool),
331                  fs->path, old_rep->revision, old_rep->offset, old_rep->size,
332                  old_rep->expanded_size, rep->revision, rep->offset, rep->size,
333                  rep->expanded_size);
334           else
335             return SVN_NO_ERROR;
336         }
337       else
338         {
339           /* Something really odd at this point, we failed to insert the
340              checksum AND failed to read an existing checksum.  Do we need
341              to flag this? */
342         }
343     }
344
345   return SVN_NO_ERROR;
346 }
347
348
349 svn_error_t *
350 svn_fs_fs__del_rep_reference(svn_fs_t *fs,
351                              svn_revnum_t youngest,
352                              apr_pool_t *pool)
353 {
354   fs_fs_data_t *ffd = fs->fsap_data;
355   svn_sqlite__stmt_t *stmt;
356
357   SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT);
358   if (! ffd->rep_cache_db)
359     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
360
361   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db,
362                                     STMT_DEL_REPS_YOUNGER_THAN_REV));
363   SVN_ERR(svn_sqlite__bindf(stmt, "r", youngest));
364   SVN_ERR(svn_sqlite__step_done(stmt));
365
366   return SVN_NO_ERROR;
367 }
368
369 svn_error_t *
370 svn_fs_fs__lock_rep_cache(svn_fs_t *fs,
371                           apr_pool_t *pool)
372 {
373   fs_fs_data_t *ffd = fs->fsap_data;
374
375   if (! ffd->rep_cache_db)
376     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
377
378   SVN_ERR(svn_sqlite__exec_statements(ffd->rep_cache_db, STMT_LOCK_REP));
379
380   return SVN_NO_ERROR;
381 }