1 /* nodes-table.c : working with the `nodes' table
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 <string.h>
24 #include <assert.h>
25 
26 #include "bdb_compat.h"
27 
28 #include "svn_fs.h"
29 #include "private/svn_skel.h"
30 
31 #include "../fs.h"
32 #include "../err.h"
33 #include "dbt.h"
34 #include "../util/fs_skels.h"
35 #include "../trail.h"
36 #include "../key-gen.h"
37 #include "../id.h"
38 #include "../../libsvn_fs/fs-loader.h"
39 #include "bdb-err.h"
40 #include "nodes-table.h"
41 
42 #include "svn_private_config.h"
43 
44 
45 
46 /* Opening/creating the `nodes' table.  */
47 
48 
49 int
svn_fs_bdb__open_nodes_table(DB ** nodes_p,DB_ENV * env,svn_boolean_t create)50 svn_fs_bdb__open_nodes_table(DB **nodes_p,
51                              DB_ENV *env,
52                              svn_boolean_t create)
53 {
54   const u_int32_t open_flags = (create ? (DB_CREATE | DB_EXCL) : 0);
55   DB *nodes;
56 
57   BDB_ERR(svn_fs_bdb__check_version());
58   BDB_ERR(db_create(&nodes, env, 0));
59   BDB_ERR((nodes->open)(SVN_BDB_OPEN_PARAMS(nodes, NULL),
60                         "nodes", 0, DB_BTREE,
61                         open_flags, 0666));
62 
63   /* Create the `next-key' table entry (use '1' because '0' is
64      reserved for the root directory to use). */
65   if (create)
66   {
67     DBT key, value;
68 
69     BDB_ERR(nodes->put(nodes, 0,
70                        svn_fs_base__str_to_dbt(&key, NEXT_KEY_KEY),
71                        svn_fs_base__str_to_dbt(&value, "1"), 0));
72   }
73 
74   *nodes_p = nodes;
75   return 0;
76 }
77 
78 
79 
80 /* Choosing node revision ID's.  */
81 
82 svn_error_t *
svn_fs_bdb__new_node_id(svn_fs_id_t ** id_p,svn_fs_t * fs,const char * copy_id,const char * txn_id,trail_t * trail,apr_pool_t * pool)83 svn_fs_bdb__new_node_id(svn_fs_id_t **id_p,
84                         svn_fs_t *fs,
85                         const char *copy_id,
86                         const char *txn_id,
87                         trail_t *trail,
88                         apr_pool_t *pool)
89 {
90   base_fs_data_t *bfd = fs->fsap_data;
91   DBT query, result;
92   apr_size_t len;
93   char next_key[MAX_KEY_SIZE];
94   int db_err;
95   const char *next_node_id;
96 
97   SVN_ERR_ASSERT(txn_id);
98 
99   /* Get the current value associated with the `next-key' key in the table.  */
100   svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY);
101   svn_fs_base__trail_debug(trail, "nodes", "get");
102   SVN_ERR(BDB_WRAP(fs, N_("allocating new node ID (getting 'next-key')"),
103                    bfd->nodes->get(bfd->nodes, trail->db_txn,
104                                    &query,
105                                    svn_fs_base__result_dbt(&result),
106                                    0)));
107   svn_fs_base__track_dbt(&result, pool);
108 
109   /* Squirrel away our next node id value. */
110   next_node_id = apr_pstrmemdup(pool, result.data, result.size);
111 
112   /* Bump to future key. */
113   len = result.size;
114   svn_fs_base__next_key(result.data, &len, next_key);
115   svn_fs_base__trail_debug(trail, "nodes", "put");
116   db_err = bfd->nodes->put(bfd->nodes, trail->db_txn,
117                            svn_fs_base__str_to_dbt(&query, NEXT_KEY_KEY),
118                            svn_fs_base__str_to_dbt(&result, next_key),
119                            0);
120   SVN_ERR(BDB_WRAP(fs, N_("bumping next node ID key"), db_err));
121 
122   /* Create and return the new node id. */
123   *id_p = svn_fs_base__id_create(next_node_id, copy_id, txn_id, pool);
124   return SVN_NO_ERROR;
125 }
126 
127 
128 svn_error_t *
svn_fs_bdb__new_successor_id(svn_fs_id_t ** successor_p,svn_fs_t * fs,const svn_fs_id_t * id,const char * copy_id,const char * txn_id,trail_t * trail,apr_pool_t * pool)129 svn_fs_bdb__new_successor_id(svn_fs_id_t **successor_p,
130                              svn_fs_t *fs,
131                              const svn_fs_id_t *id,
132                              const char *copy_id,
133                              const char *txn_id,
134                              trail_t *trail,
135                              apr_pool_t *pool)
136 {
137   svn_fs_id_t *new_id;
138   svn_error_t *err;
139 
140   SVN_ERR_ASSERT(txn_id);
141 
142   /* Create and return the new successor ID.  */
143   new_id = svn_fs_base__id_create(svn_fs_base__id_node_id(id),
144                                   copy_id ? copy_id
145                                   : svn_fs_base__id_copy_id(id),
146                                   txn_id, pool);
147 
148   /* Now, make sure this NEW_ID doesn't already exist in FS. */
149   err = svn_fs_bdb__get_node_revision(NULL, fs, new_id, trail, trail->pool);
150   if ((! err) || (err->apr_err != SVN_ERR_FS_ID_NOT_FOUND))
151     {
152       svn_string_t *id_str = svn_fs_base__id_unparse(id, pool);
153       svn_string_t *new_id_str = svn_fs_base__id_unparse(new_id, pool);
154       return svn_error_createf
155         (SVN_ERR_FS_ALREADY_EXISTS, err,
156          _("Successor id '%s' (for '%s') already exists in filesystem '%s'"),
157          new_id_str->data, id_str->data, fs->path);
158     }
159 
160   /* err is SVN_ERR_FS_ID_NOT_FOUND, meaning the ID is available. But
161      we don't want this error. */
162   svn_error_clear(err);
163 
164   /* Return the new node revision ID. */
165   *successor_p = new_id;
166   return SVN_NO_ERROR;
167 }
168 
169 
170 
171 /* Removing node revisions.  */
172 svn_error_t *
svn_fs_bdb__delete_nodes_entry(svn_fs_t * fs,const svn_fs_id_t * id,trail_t * trail,apr_pool_t * pool)173 svn_fs_bdb__delete_nodes_entry(svn_fs_t *fs,
174                                const svn_fs_id_t *id,
175                                trail_t *trail,
176                                apr_pool_t *pool)
177 {
178   base_fs_data_t *bfd = fs->fsap_data;
179   DBT key;
180 
181   svn_fs_base__trail_debug(trail, "nodes", "del");
182   return BDB_WRAP(fs, N_("deleting entry from 'nodes' table"),
183                   bfd->nodes->del(bfd->nodes,
184                                   trail->db_txn,
185                                   svn_fs_base__id_to_dbt(&key, id, pool),
186                                   0));
187 }
188 
189 
190 
191 
192 /* Storing and retrieving NODE-REVISIONs.  */
193 
194 
195 svn_error_t *
svn_fs_bdb__get_node_revision(node_revision_t ** noderev_p,svn_fs_t * fs,const svn_fs_id_t * id,trail_t * trail,apr_pool_t * pool)196 svn_fs_bdb__get_node_revision(node_revision_t **noderev_p,
197                               svn_fs_t *fs,
198                               const svn_fs_id_t *id,
199                               trail_t *trail,
200                               apr_pool_t *pool)
201 {
202   base_fs_data_t *bfd = fs->fsap_data;
203   node_revision_t *noderev;
204   svn_skel_t *skel;
205   int db_err;
206   DBT key, value;
207 
208   svn_fs_base__trail_debug(trail, "nodes", "get");
209   db_err = bfd->nodes->get(bfd->nodes, trail->db_txn,
210                            svn_fs_base__id_to_dbt(&key, id, pool),
211                            svn_fs_base__result_dbt(&value),
212                            0);
213   svn_fs_base__track_dbt(&value, pool);
214 
215   /* If there's no such node, return an appropriately specific error.  */
216   if (db_err == DB_NOTFOUND)
217     return svn_fs_base__err_dangling_id(fs, id);
218 
219   /* Handle any other error conditions.  */
220   SVN_ERR(BDB_WRAP(fs, N_("reading node revision"), db_err));
221 
222   /* If our caller doesn't really care about the return value here,
223      just return successfully. */
224   if (! noderev_p)
225     return SVN_NO_ERROR;
226 
227   /* Parse and the NODE-REVISION skel.  */
228   skel = svn_skel__parse(value.data, value.size, pool);
229 
230   /* Convert to a native FS type. */
231   SVN_ERR(svn_fs_base__parse_node_revision_skel(&noderev, skel, pool));
232   *noderev_p = noderev;
233   return SVN_NO_ERROR;
234 }
235 
236 
237 svn_error_t *
svn_fs_bdb__put_node_revision(svn_fs_t * fs,const svn_fs_id_t * id,node_revision_t * noderev,trail_t * trail,apr_pool_t * pool)238 svn_fs_bdb__put_node_revision(svn_fs_t *fs,
239                               const svn_fs_id_t *id,
240                               node_revision_t *noderev,
241                               trail_t *trail,
242                               apr_pool_t *pool)
243 {
244   base_fs_data_t *bfd = fs->fsap_data;
245   DB_TXN *db_txn = trail->db_txn;
246   DBT key, value;
247   svn_skel_t *skel;
248 
249   /* Convert from native type into skel */
250   SVN_ERR(svn_fs_base__unparse_node_revision_skel(&skel, noderev,
251                                                   bfd->format, pool));
252   svn_fs_base__trail_debug(trail, "nodes", "put");
253   return BDB_WRAP(fs, N_("storing node revision"),
254                   bfd->nodes->put(bfd->nodes, db_txn,
255                                   svn_fs_base__id_to_dbt(&key, id, pool),
256                                   svn_fs_base__skel_to_dbt(&value, skel,
257                                                            pool),
258                                   0));
259 }
260