1/* locks-table.c : operations on the `locks' 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_pools.h" 29#include "svn_path.h" 30#include "private/svn_skel.h" 31 32#include "dbt.h" 33#include "../err.h" 34#include "../fs.h" 35#include "../util/fs_skels.h" 36#include "../trail.h" 37#include "../../libsvn_fs/fs-loader.h" 38#include "bdb-err.h" 39#include "locks-table.h" 40#include "lock-tokens-table.h" 41 42#include "private/svn_fs_util.h" 43#include "private/svn_fspath.h" 44 45 46int 47svn_fs_bdb__open_locks_table(DB **locks_p, 48 DB_ENV *env, 49 svn_boolean_t create) 50{ 51 const u_int32_t open_flags = (create ? (DB_CREATE | DB_EXCL) : 0); 52 DB *locks; 53 int error; 54 55 BDB_ERR(svn_fs_bdb__check_version()); 56 BDB_ERR(db_create(&locks, env, 0)); 57 error = (locks->open)(SVN_BDB_OPEN_PARAMS(locks, NULL), 58 "locks", 0, DB_BTREE, 59 open_flags, 0666); 60 61 /* Create the table if it doesn't yet exist. This is a form of 62 automagical repository upgrading. */ 63 if (error == ENOENT && (! create)) 64 { 65 BDB_ERR(locks->close(locks, 0)); 66 return svn_fs_bdb__open_locks_table(locks_p, env, TRUE); 67 } 68 BDB_ERR(error); 69 70 *locks_p = locks; 71 return 0; 72} 73 74 75 76svn_error_t * 77svn_fs_bdb__lock_add(svn_fs_t *fs, 78 const char *lock_token, 79 svn_lock_t *lock, 80 trail_t *trail, 81 apr_pool_t *pool) 82{ 83 base_fs_data_t *bfd = fs->fsap_data; 84 svn_skel_t *lock_skel; 85 DBT key, value; 86 87 /* Convert native type to skel. */ 88 SVN_ERR(svn_fs_base__unparse_lock_skel(&lock_skel, lock, pool)); 89 90 svn_fs_base__str_to_dbt(&key, lock_token); 91 svn_fs_base__skel_to_dbt(&value, lock_skel, pool); 92 svn_fs_base__trail_debug(trail, "lock", "add"); 93 return BDB_WRAP(fs, N_("storing lock record"), 94 bfd->locks->put(bfd->locks, trail->db_txn, 95 &key, &value, 0)); 96} 97 98 99 100svn_error_t * 101svn_fs_bdb__lock_delete(svn_fs_t *fs, 102 const char *lock_token, 103 trail_t *trail, 104 apr_pool_t *pool) 105{ 106 base_fs_data_t *bfd = fs->fsap_data; 107 DBT key; 108 int db_err; 109 110 svn_fs_base__str_to_dbt(&key, lock_token); 111 svn_fs_base__trail_debug(trail, "locks", "del"); 112 db_err = bfd->locks->del(bfd->locks, trail->db_txn, &key, 0); 113 114 if (db_err == DB_NOTFOUND) 115 return svn_fs_base__err_bad_lock_token(fs, lock_token); 116 return BDB_WRAP(fs, N_("deleting lock from 'locks' table"), db_err); 117} 118 119 120 121svn_error_t * 122svn_fs_bdb__lock_get(svn_lock_t **lock_p, 123 svn_fs_t *fs, 124 const char *lock_token, 125 trail_t *trail, 126 apr_pool_t *pool) 127{ 128 base_fs_data_t *bfd = fs->fsap_data; 129 DBT key, value; 130 int db_err; 131 svn_skel_t *skel; 132 svn_lock_t *lock; 133 134 svn_fs_base__trail_debug(trail, "lock", "get"); 135 db_err = bfd->locks->get(bfd->locks, trail->db_txn, 136 svn_fs_base__str_to_dbt(&key, lock_token), 137 svn_fs_base__result_dbt(&value), 138 0); 139 svn_fs_base__track_dbt(&value, pool); 140 141 if (db_err == DB_NOTFOUND) 142 return svn_fs_base__err_bad_lock_token(fs, lock_token); 143 SVN_ERR(BDB_WRAP(fs, N_("reading lock"), db_err)); 144 145 /* Parse TRANSACTION skel */ 146 skel = svn_skel__parse(value.data, value.size, pool); 147 if (! skel) 148 return svn_fs_base__err_corrupt_lock(fs, lock_token); 149 150 /* Convert skel to native type. */ 151 SVN_ERR(svn_fs_base__parse_lock_skel(&lock, skel, pool)); 152 153 /* Possibly auto-expire the lock. */ 154 if (lock->expiration_date && (apr_time_now() > lock->expiration_date)) 155 { 156 SVN_ERR(svn_fs_bdb__lock_delete(fs, lock_token, trail, pool)); 157 return SVN_FS__ERR_LOCK_EXPIRED(fs, lock_token); 158 } 159 160 *lock_p = lock; 161 return SVN_NO_ERROR; 162} 163 164 165static svn_error_t * 166get_lock(svn_lock_t **lock_p, 167 svn_fs_t *fs, 168 const char *path, 169 const char *lock_token, 170 trail_t *trail, 171 apr_pool_t *pool) 172{ 173 svn_error_t *err = SVN_NO_ERROR; 174 *lock_p = NULL; 175 176 /* Make sure the token points to an existing, non-expired lock, by 177 doing a lookup in the `locks' table. Use 'pool'. */ 178 err = svn_fs_bdb__lock_get(lock_p, fs, lock_token, trail, pool); 179 if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED) 180 || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN))) 181 { 182 svn_error_clear(err); 183 184 /* If `locks' doesn't have the lock, then we should lose it 185 from `lock-tokens' table as well, then skip to the next 186 matching path-key. */ 187 err = svn_fs_bdb__lock_token_delete(fs, path, trail, pool); 188 } 189 return svn_error_trace(err); 190} 191 192 193svn_error_t * 194svn_fs_bdb__locks_get(svn_fs_t *fs, 195 const char *path, 196 svn_depth_t depth, 197 svn_fs_get_locks_callback_t get_locks_func, 198 void *get_locks_baton, 199 trail_t *trail, 200 apr_pool_t *pool) 201{ 202 base_fs_data_t *bfd = fs->fsap_data; 203 DBC *cursor; 204 DBT key, value; 205 int db_err, db_c_err; 206 apr_pool_t *subpool = svn_pool_create(pool); 207 const char *lock_token; 208 svn_lock_t *lock; 209 svn_error_t *err; 210 const char *lookup_path = path; 211 apr_size_t lookup_len; 212 213 /* First, try to lookup PATH itself. */ 214 err = svn_fs_bdb__lock_token_get(&lock_token, fs, path, trail, pool); 215 if (err && ((err->apr_err == SVN_ERR_FS_LOCK_EXPIRED) 216 || (err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN) 217 || (err->apr_err == SVN_ERR_FS_NO_SUCH_LOCK))) 218 { 219 svn_error_clear(err); 220 } 221 else if (err) 222 { 223 return svn_error_trace(err); 224 } 225 else 226 { 227 SVN_ERR(get_lock(&lock, fs, path, lock_token, trail, pool)); 228 if (lock && get_locks_func) 229 { 230 SVN_ERR(get_locks_func(get_locks_baton, lock, pool)); 231 232 /* Found a lock so PATH is a file and we can ignore depth */ 233 return SVN_NO_ERROR; 234 } 235 } 236 237 /* If we're only looking at PATH itself (depth = empty), stop here. */ 238 if (depth == svn_depth_empty) 239 return SVN_NO_ERROR; 240 241 /* Now go hunt for possible children of PATH. */ 242 243 svn_fs_base__trail_debug(trail, "lock-tokens", "cursor"); 244 db_err = bfd->lock_tokens->cursor(bfd->lock_tokens, trail->db_txn, 245 &cursor, 0); 246 SVN_ERR(BDB_WRAP(fs, N_("creating cursor for reading lock tokens"), 247 db_err)); 248 249 /* Since the key is going to be returned as well as the value make 250 sure BDB malloc's the returned key. */ 251 svn_fs_base__str_to_dbt(&key, lookup_path); 252 key.flags |= DB_DBT_MALLOC; 253 254 /* Get the first matching key that is either equal or greater than 255 the one passed in, by passing in the DB_RANGE_SET flag. */ 256 db_err = svn_bdb_dbc_get(cursor, &key, svn_fs_base__result_dbt(&value), 257 DB_SET_RANGE); 258 259 if (!svn_fspath__is_root(path, strlen(path))) 260 lookup_path = apr_pstrcat(pool, path, "/", (char *)NULL); 261 lookup_len = strlen(lookup_path); 262 263 /* As long as the prefix of the returned KEY matches LOOKUP_PATH we 264 know it is either LOOKUP_PATH or a decendant thereof. */ 265 while ((! db_err) 266 && lookup_len < key.size 267 && strncmp(lookup_path, key.data, lookup_len) == 0) 268 { 269 const char *child_path; 270 271 svn_pool_clear(subpool); 272 273 svn_fs_base__track_dbt(&key, subpool); 274 svn_fs_base__track_dbt(&value, subpool); 275 276 /* Create a usable path and token in temporary memory. */ 277 child_path = apr_pstrmemdup(subpool, key.data, key.size); 278 lock_token = apr_pstrmemdup(subpool, value.data, value.size); 279 280 if ((depth == svn_depth_files) || (depth == svn_depth_immediates)) 281 { 282 /* On the assumption that we only store locks for files, 283 depth=files and depth=immediates should boil down to the 284 same set of results. So just see if CHILD_PATH is an 285 immediate child of PATH. If not, we don't care about 286 this item. */ 287 const char *rel_path = svn_fspath__skip_ancestor(path, child_path); 288 if (!rel_path || (svn_path_component_count(rel_path) != 1)) 289 goto loop_it; 290 } 291 292 /* Get the lock for CHILD_PATH. */ 293 err = get_lock(&lock, fs, child_path, lock_token, trail, subpool); 294 if (err) 295 { 296 svn_bdb_dbc_close(cursor); 297 return svn_error_trace(err); 298 } 299 300 /* Lock is verified, hand it off to our callback. */ 301 if (lock && get_locks_func) 302 { 303 err = get_locks_func(get_locks_baton, lock, subpool); 304 if (err) 305 { 306 svn_bdb_dbc_close(cursor); 307 return svn_error_trace(err); 308 } 309 } 310 311 loop_it: 312 svn_fs_base__result_dbt(&key); 313 svn_fs_base__result_dbt(&value); 314 db_err = svn_bdb_dbc_get(cursor, &key, &value, DB_NEXT); 315 } 316 317 svn_pool_destroy(subpool); 318 db_c_err = svn_bdb_dbc_close(cursor); 319 320 if (db_err && (db_err != DB_NOTFOUND)) 321 SVN_ERR(BDB_WRAP(fs, N_("fetching lock tokens"), db_err)); 322 if (db_c_err) 323 SVN_ERR(BDB_WRAP(fs, N_("fetching lock tokens (closing cursor)"), 324 db_c_err)); 325 326 return SVN_NO_ERROR; 327} 328 329