]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_fs_fs/rep-cache.c
Update from subversion 1.9.4 to 1.9.5.
[FreeBSD/FreeBSD.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 #define SVN_ERR_CLOSE(x, db) do                                       \
54 {                                                                     \
55   svn_error_t *svn__err = (x);                                        \
56   if (svn__err)                                                       \
57     return svn_error_compose_create(svn__err, svn_sqlite__close(db)); \
58 } while (0)
59
60 \f
61 /** Library-private API's. **/
62
63 /* Body of svn_fs_fs__open_rep_cache().
64    Implements svn_atomic__init_once().init_func.
65  */
66 static svn_error_t *
67 open_rep_cache(void *baton,
68                apr_pool_t *pool)
69 {
70   svn_fs_t *fs = baton;
71   fs_fs_data_t *ffd = fs->fsap_data;
72   svn_sqlite__db_t *sdb;
73   const char *db_path;
74   int version;
75
76   /* Open (or create) the sqlite database.  It will be automatically
77      closed when fs->pool is destroyed. */
78   db_path = path_rep_cache_db(fs->path, pool);
79 #ifndef WIN32
80   {
81     /* We want to extend the permissions that apply to the repository
82        as a whole when creating a new rep cache and not simply default
83        to umask. */
84     svn_boolean_t exists;
85
86     SVN_ERR(svn_fs_fs__exists_rep_cache(&exists, fs, pool));
87     if (!exists)
88       {
89         const char *current = svn_fs_fs__path_current(fs, pool);
90         svn_error_t *err = svn_io_file_create_empty(db_path, pool);
91
92         if (err && !APR_STATUS_IS_EEXIST(err->apr_err))
93           /* A real error. */
94           return svn_error_trace(err);
95         else if (err)
96           /* Some other thread/process created the file. */
97           svn_error_clear(err);
98         else
99           /* We created the file. */
100           SVN_ERR(svn_io_copy_perms(current, db_path, pool));
101       }
102   }
103 #endif
104   SVN_ERR(svn_sqlite__open(&sdb, db_path,
105                            svn_sqlite__mode_rwcreate, statements,
106                            0, NULL, 0,
107                            fs->pool, pool));
108
109   SVN_ERR_CLOSE(svn_sqlite__read_schema_version(&version, sdb, pool), sdb);
110   if (version < REP_CACHE_SCHEMA_FORMAT)
111     {
112       /* Must be 0 -- an uninitialized (no schema) database. Create
113          the schema. Results in schema version of 1.  */
114       SVN_ERR_CLOSE(svn_sqlite__exec_statements(sdb, STMT_CREATE_SCHEMA), sdb);
115     }
116
117   /* This is used as a flag that the database is available so don't
118      set it earlier. */
119   ffd->rep_cache_db = sdb;
120
121   return SVN_NO_ERROR;
122 }
123
124 svn_error_t *
125 svn_fs_fs__open_rep_cache(svn_fs_t *fs,
126                           apr_pool_t *pool)
127 {
128   fs_fs_data_t *ffd = fs->fsap_data;
129   svn_error_t *err = svn_atomic__init_once(&ffd->rep_cache_db_opened,
130                                            open_rep_cache, fs, pool);
131   return svn_error_quick_wrap(err, _("Couldn't open rep-cache database"));
132 }
133
134 svn_error_t *
135 svn_fs_fs__close_rep_cache(svn_fs_t *fs)
136 {
137   fs_fs_data_t *ffd = fs->fsap_data;
138
139   if (ffd->rep_cache_db)
140     {
141       SVN_ERR(svn_sqlite__close(ffd->rep_cache_db));
142       ffd->rep_cache_db = NULL;
143       ffd->rep_cache_db_opened = 0;
144     }
145
146   return SVN_NO_ERROR;
147 }
148
149 svn_error_t *
150 svn_fs_fs__exists_rep_cache(svn_boolean_t *exists,
151                             svn_fs_t *fs, apr_pool_t *pool)
152 {
153   svn_node_kind_t kind;
154
155   SVN_ERR(svn_io_check_path(path_rep_cache_db(fs->path, pool),
156                             &kind, pool));
157
158   *exists = (kind != svn_node_none);
159   return SVN_NO_ERROR;
160 }
161
162 svn_error_t *
163 svn_fs_fs__walk_rep_reference(svn_fs_t *fs,
164                               svn_revnum_t start,
165                               svn_revnum_t end,
166                               svn_error_t *(*walker)(representation_t *,
167                                                      void *,
168                                                      svn_fs_t *,
169                                                      apr_pool_t *),
170                               void *walker_baton,
171                               svn_cancel_func_t cancel_func,
172                               void *cancel_baton,
173                               apr_pool_t *pool)
174 {
175   fs_fs_data_t *ffd = fs->fsap_data;
176   svn_sqlite__stmt_t *stmt;
177   svn_boolean_t have_row;
178   int iterations = 0;
179
180   apr_pool_t *iterpool = svn_pool_create(pool);
181
182   /* Don't check ffd->rep_sharing_allowed. */
183   SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT);
184
185   if (! ffd->rep_cache_db)
186     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
187
188   /* Check global invariants. */
189   if (start == 0)
190     {
191       svn_revnum_t max;
192
193       SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db,
194                                         STMT_GET_MAX_REV));
195       SVN_ERR(svn_sqlite__step(&have_row, stmt));
196       max = svn_sqlite__column_revnum(stmt, 0);
197       SVN_ERR(svn_sqlite__reset(stmt));
198       if (SVN_IS_VALID_REVNUM(max))  /* The rep-cache could be empty. */
199         SVN_ERR(svn_fs_fs__ensure_revision_exists(max, fs, iterpool));
200     }
201
202   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db,
203                                     STMT_GET_REPS_FOR_RANGE));
204   SVN_ERR(svn_sqlite__bindf(stmt, "rr",
205                             start, end));
206
207   /* Walk the cache entries. */
208   SVN_ERR(svn_sqlite__step(&have_row, stmt));
209   while (have_row)
210     {
211       representation_t *rep;
212       const char *sha1_digest;
213       svn_error_t *err;
214       svn_checksum_t *checksum;
215
216       /* Clear ITERPOOL occasionally. */
217       if (iterations++ % 16 == 0)
218         svn_pool_clear(iterpool);
219
220       /* Check for cancellation. */
221       if (cancel_func)
222         {
223           err = cancel_func(cancel_baton);
224           if (err)
225             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
226         }
227
228       /* Construct a representation_t. */
229       rep = apr_pcalloc(iterpool, sizeof(*rep));
230       svn_fs_fs__id_txn_reset(&rep->txn_id);
231       sha1_digest = svn_sqlite__column_text(stmt, 0, iterpool);
232       err = svn_checksum_parse_hex(&checksum, svn_checksum_sha1,
233                                    sha1_digest, iterpool);
234       if (err)
235         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
236
237       rep->has_sha1 = TRUE;
238       memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest));
239       rep->revision = svn_sqlite__column_revnum(stmt, 1);
240       rep->item_index = svn_sqlite__column_int64(stmt, 2);
241       rep->size = svn_sqlite__column_int64(stmt, 3);
242       rep->expanded_size = svn_sqlite__column_int64(stmt, 4);
243
244       /* Walk. */
245       err = walker(rep, walker_baton, fs, iterpool);
246       if (err)
247         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
248
249       SVN_ERR(svn_sqlite__step(&have_row, stmt));
250     }
251
252   SVN_ERR(svn_sqlite__reset(stmt));
253   svn_pool_destroy(iterpool);
254
255   return SVN_NO_ERROR;
256 }
257
258
259 /* This function's caller ignores most errors it returns.
260    If you extend this function, check the callsite to see if you have
261    to make it not-ignore additional error codes.  */
262 svn_error_t *
263 svn_fs_fs__get_rep_reference(representation_t **rep,
264                              svn_fs_t *fs,
265                              svn_checksum_t *checksum,
266                              apr_pool_t *pool)
267 {
268   fs_fs_data_t *ffd = fs->fsap_data;
269   svn_sqlite__stmt_t *stmt;
270   svn_boolean_t have_row;
271
272   SVN_ERR_ASSERT(ffd->rep_sharing_allowed);
273   if (! ffd->rep_cache_db)
274     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
275
276   /* We only allow SHA1 checksums in this table. */
277   if (checksum->kind != svn_checksum_sha1)
278     return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
279                             _("Only SHA1 checksums can be used as keys in the "
280                               "rep_cache table.\n"));
281
282   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, STMT_GET_REP));
283   SVN_ERR(svn_sqlite__bindf(stmt, "s",
284                             svn_checksum_to_cstring(checksum, pool)));
285
286   SVN_ERR(svn_sqlite__step(&have_row, stmt));
287   if (have_row)
288     {
289       *rep = apr_pcalloc(pool, sizeof(**rep));
290       svn_fs_fs__id_txn_reset(&(*rep)->txn_id);
291       memcpy((*rep)->sha1_digest, checksum->digest,
292              sizeof((*rep)->sha1_digest));
293       (*rep)->has_sha1 = TRUE;
294       (*rep)->revision = svn_sqlite__column_revnum(stmt, 0);
295       (*rep)->item_index = svn_sqlite__column_int64(stmt, 1);
296       (*rep)->size = svn_sqlite__column_int64(stmt, 2);
297       (*rep)->expanded_size = svn_sqlite__column_int64(stmt, 3);
298     }
299   else
300     *rep = NULL;
301
302   SVN_ERR(svn_sqlite__reset(stmt));
303
304   if (*rep)
305     {
306       /* Check that REP refers to a revision that exists in FS. */
307       svn_error_t *err = svn_fs_fs__ensure_revision_exists((*rep)->revision,
308                                                            fs, pool);
309       if (err)
310         return svn_error_createf(SVN_ERR_FS_CORRUPT, err,
311                                  "Checksum '%s' in rep-cache is beyond HEAD",
312                                  svn_checksum_to_cstring_display(checksum,
313                                                                  pool));
314     }
315
316   return SVN_NO_ERROR;
317 }
318
319 svn_error_t *
320 svn_fs_fs__set_rep_reference(svn_fs_t *fs,
321                              representation_t *rep,
322                              apr_pool_t *pool)
323 {
324   fs_fs_data_t *ffd = fs->fsap_data;
325   svn_sqlite__stmt_t *stmt;
326   svn_error_t *err;
327   svn_checksum_t checksum;
328   checksum.kind = svn_checksum_sha1;
329   checksum.digest = rep->sha1_digest;
330
331   SVN_ERR_ASSERT(ffd->rep_sharing_allowed);
332   if (! ffd->rep_cache_db)
333     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
334
335   /* We only allow SHA1 checksums in this table. */
336   if (! rep->has_sha1)
337     return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
338                             _("Only SHA1 checksums can be used as keys in the "
339                               "rep_cache table.\n"));
340
341   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db, STMT_SET_REP));
342   SVN_ERR(svn_sqlite__bindf(stmt, "siiii",
343                             svn_checksum_to_cstring(&checksum, pool),
344                             (apr_int64_t) rep->revision,
345                             (apr_int64_t) rep->item_index,
346                             (apr_int64_t) rep->size,
347                             (apr_int64_t) rep->expanded_size));
348
349   err = svn_sqlite__insert(NULL, stmt);
350   if (err)
351     {
352       representation_t *old_rep;
353
354       if (err->apr_err != SVN_ERR_SQLITE_CONSTRAINT)
355         return svn_error_trace(err);
356
357       svn_error_clear(err);
358
359       /* Constraint failed so the mapping for SHA1_CHECKSUM->REP
360          should exist.  If so that's cool -- just do nothing.  If not,
361          that's a red flag!  */
362       SVN_ERR(svn_fs_fs__get_rep_reference(&old_rep, fs, &checksum, pool));
363
364       if (!old_rep)
365         {
366           /* Something really odd at this point, we failed to insert the
367              checksum AND failed to read an existing checksum.  Do we need
368              to flag this? */
369         }
370     }
371
372   return SVN_NO_ERROR;
373 }
374
375
376 svn_error_t *
377 svn_fs_fs__del_rep_reference(svn_fs_t *fs,
378                              svn_revnum_t youngest,
379                              apr_pool_t *pool)
380 {
381   fs_fs_data_t *ffd = fs->fsap_data;
382   svn_sqlite__stmt_t *stmt;
383
384   SVN_ERR_ASSERT(ffd->format >= SVN_FS_FS__MIN_REP_SHARING_FORMAT);
385   if (! ffd->rep_cache_db)
386     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
387
388   SVN_ERR(svn_sqlite__get_statement(&stmt, ffd->rep_cache_db,
389                                     STMT_DEL_REPS_YOUNGER_THAN_REV));
390   SVN_ERR(svn_sqlite__bindf(stmt, "r", youngest));
391   SVN_ERR(svn_sqlite__step_done(stmt));
392
393   return SVN_NO_ERROR;
394 }
395
396 /* Start a transaction to take an SQLite reserved lock that prevents
397    other writes.
398
399    See unlock_rep_cache(). */
400 static svn_error_t *
401 lock_rep_cache(svn_fs_t *fs,
402                apr_pool_t *pool)
403 {
404   fs_fs_data_t *ffd = fs->fsap_data;
405
406   if (! ffd->rep_cache_db)
407     SVN_ERR(svn_fs_fs__open_rep_cache(fs, pool));
408
409   SVN_ERR(svn_sqlite__exec_statements(ffd->rep_cache_db, STMT_LOCK_REP));
410
411   return SVN_NO_ERROR;
412 }
413
414 /* End the transaction started by lock_rep_cache(). */
415 static svn_error_t *
416 unlock_rep_cache(svn_fs_t *fs,
417                  apr_pool_t *pool)
418 {
419   fs_fs_data_t *ffd = fs->fsap_data;
420
421   SVN_ERR_ASSERT(ffd->rep_cache_db); /* was opened by lock_rep_cache() */
422
423   SVN_ERR(svn_sqlite__exec_statements(ffd->rep_cache_db, STMT_UNLOCK_REP));
424
425   return SVN_NO_ERROR;
426 }
427
428 svn_error_t *
429 svn_fs_fs__with_rep_cache_lock(svn_fs_t *fs,
430                                svn_error_t *(*body)(void *,
431                                                     apr_pool_t *),
432                                void *baton,
433                                apr_pool_t *pool)
434 {
435   svn_error_t *err;
436
437   SVN_ERR(lock_rep_cache(fs, pool));
438   err = body(baton, pool);
439   return svn_error_compose_create(err, unlock_rep_cache(fs, pool));
440 }