| 1 | /*
|
|---|
| 2 | Unix SMB/CIFS implementation.
|
|---|
| 3 |
|
|---|
| 4 | trivial database library
|
|---|
| 5 |
|
|---|
| 6 | Copyright (C) Andrew Tridgell 1999-2005
|
|---|
| 7 | Copyright (C) Paul `Rusty' Russell 2000
|
|---|
| 8 | Copyright (C) Jeremy Allison 2000-2003
|
|---|
| 9 |
|
|---|
| 10 | ** NOTE! The following LGPL license applies to the tdb
|
|---|
| 11 | ** library. This does NOT imply that all of Samba is released
|
|---|
| 12 | ** under the LGPL
|
|---|
| 13 |
|
|---|
| 14 | This library is free software; you can redistribute it and/or
|
|---|
| 15 | modify it under the terms of the GNU Lesser General Public
|
|---|
| 16 | License as published by the Free Software Foundation; either
|
|---|
| 17 | version 2 of the License, or (at your option) any later version.
|
|---|
| 18 |
|
|---|
| 19 | This library is distributed in the hope that it will be useful,
|
|---|
| 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|---|
| 22 | Lesser General Public License for more details.
|
|---|
| 23 |
|
|---|
| 24 | You should have received a copy of the GNU Lesser General Public
|
|---|
| 25 | License along with this library; if not, write to the Free Software
|
|---|
| 26 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|---|
| 27 | */
|
|---|
| 28 |
|
|---|
| 29 | #include "tdb_private.h"
|
|---|
| 30 |
|
|---|
| 31 | TDB_DATA tdb_null;
|
|---|
| 32 |
|
|---|
| 33 | /*
|
|---|
| 34 | increment the tdb sequence number if the tdb has been opened using
|
|---|
| 35 | the TDB_SEQNUM flag
|
|---|
| 36 | */
|
|---|
| 37 | static void tdb_increment_seqnum(struct tdb_context *tdb)
|
|---|
| 38 | {
|
|---|
| 39 | tdb_off_t seqnum=0;
|
|---|
| 40 |
|
|---|
| 41 | if (!(tdb->flags & TDB_SEQNUM)) {
|
|---|
| 42 | return;
|
|---|
| 43 | }
|
|---|
| 44 |
|
|---|
| 45 | if (tdb_brlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, F_SETLKW, 1, 1) != 0) {
|
|---|
| 46 | return;
|
|---|
| 47 | }
|
|---|
| 48 |
|
|---|
| 49 | /* we ignore errors from this, as we have no sane way of
|
|---|
| 50 | dealing with them.
|
|---|
| 51 | */
|
|---|
| 52 | tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum);
|
|---|
| 53 | seqnum++;
|
|---|
| 54 | tdb_ofs_write(tdb, TDB_SEQNUM_OFS, &seqnum);
|
|---|
| 55 |
|
|---|
| 56 | tdb_brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1, 1);
|
|---|
| 57 | }
|
|---|
| 58 |
|
|---|
| 59 | static int tdb_key_compare(TDB_DATA key, TDB_DATA data, void *private_data)
|
|---|
| 60 | {
|
|---|
| 61 | return memcmp(data.dptr, key.dptr, data.dsize);
|
|---|
| 62 | }
|
|---|
| 63 |
|
|---|
| 64 | /* Returns 0 on fail. On success, return offset of record, and fills
|
|---|
| 65 | in rec */
|
|---|
| 66 | static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, u32 hash,
|
|---|
| 67 | struct list_struct *r)
|
|---|
| 68 | {
|
|---|
| 69 | tdb_off_t rec_ptr;
|
|---|
| 70 |
|
|---|
| 71 | /* read in the hash top */
|
|---|
| 72 | if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1)
|
|---|
| 73 | return 0;
|
|---|
| 74 |
|
|---|
| 75 | /* keep looking until we find the right record */
|
|---|
| 76 | while (rec_ptr) {
|
|---|
| 77 | if (tdb_rec_read(tdb, rec_ptr, r) == -1)
|
|---|
| 78 | return 0;
|
|---|
| 79 |
|
|---|
| 80 | if (!TDB_DEAD(r) && hash==r->full_hash
|
|---|
| 81 | && key.dsize==r->key_len
|
|---|
| 82 | && tdb_parse_data(tdb, key, rec_ptr + sizeof(*r),
|
|---|
| 83 | r->key_len, tdb_key_compare,
|
|---|
| 84 | NULL) == 0) {
|
|---|
| 85 | return rec_ptr;
|
|---|
| 86 | }
|
|---|
| 87 | rec_ptr = r->next;
|
|---|
| 88 | }
|
|---|
| 89 | return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
|
|---|
| 90 | }
|
|---|
| 91 |
|
|---|
| 92 | /* As tdb_find, but if you succeed, keep the lock */
|
|---|
| 93 | tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype,
|
|---|
| 94 | struct list_struct *rec)
|
|---|
| 95 | {
|
|---|
| 96 | u32 rec_ptr;
|
|---|
| 97 |
|
|---|
| 98 | if (tdb_lock(tdb, BUCKET(hash), locktype) == -1)
|
|---|
| 99 | return 0;
|
|---|
| 100 | if (!(rec_ptr = tdb_find(tdb, key, hash, rec)))
|
|---|
| 101 | tdb_unlock(tdb, BUCKET(hash), locktype);
|
|---|
| 102 | return rec_ptr;
|
|---|
| 103 | }
|
|---|
| 104 |
|
|---|
| 105 |
|
|---|
| 106 | /* update an entry in place - this only works if the new data size
|
|---|
| 107 | is <= the old data size and the key exists.
|
|---|
| 108 | on failure return -1.
|
|---|
| 109 | */
|
|---|
| 110 | static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, TDB_DATA dbuf)
|
|---|
| 111 | {
|
|---|
| 112 | struct list_struct rec;
|
|---|
| 113 | tdb_off_t rec_ptr;
|
|---|
| 114 |
|
|---|
| 115 | /* find entry */
|
|---|
| 116 | if (!(rec_ptr = tdb_find(tdb, key, hash, &rec)))
|
|---|
| 117 | return -1;
|
|---|
| 118 |
|
|---|
| 119 | /* must be long enough key, data and tailer */
|
|---|
| 120 | if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off_t)) {
|
|---|
| 121 | tdb->ecode = TDB_SUCCESS; /* Not really an error */
|
|---|
| 122 | return -1;
|
|---|
| 123 | }
|
|---|
| 124 |
|
|---|
| 125 | if (tdb->methods->tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len,
|
|---|
| 126 | dbuf.dptr, dbuf.dsize) == -1)
|
|---|
| 127 | return -1;
|
|---|
| 128 |
|
|---|
| 129 | if (dbuf.dsize != rec.data_len) {
|
|---|
| 130 | /* update size */
|
|---|
| 131 | rec.data_len = dbuf.dsize;
|
|---|
| 132 | return tdb_rec_write(tdb, rec_ptr, &rec);
|
|---|
| 133 | }
|
|---|
| 134 |
|
|---|
| 135 | return 0;
|
|---|
| 136 | }
|
|---|
| 137 |
|
|---|
| 138 | /* find an entry in the database given a key */
|
|---|
| 139 | /* If an entry doesn't exist tdb_err will be set to
|
|---|
| 140 | * TDB_ERR_NOEXIST. If a key has no data attached
|
|---|
| 141 | * then the TDB_DATA will have zero length but
|
|---|
| 142 | * a non-zero pointer
|
|---|
| 143 | */
|
|---|
| 144 | TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key)
|
|---|
| 145 | {
|
|---|
| 146 | tdb_off_t rec_ptr;
|
|---|
| 147 | struct list_struct rec;
|
|---|
| 148 | TDB_DATA ret;
|
|---|
| 149 | u32 hash;
|
|---|
| 150 |
|
|---|
| 151 | /* find which hash bucket it is in */
|
|---|
| 152 | hash = tdb->hash_fn(&key);
|
|---|
| 153 | if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec)))
|
|---|
| 154 | return tdb_null;
|
|---|
| 155 |
|
|---|
| 156 | ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len,
|
|---|
| 157 | rec.data_len);
|
|---|
| 158 | ret.dsize = rec.data_len;
|
|---|
| 159 | tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
|
|---|
| 160 | return ret;
|
|---|
| 161 | }
|
|---|
| 162 |
|
|---|
| 163 | /*
|
|---|
| 164 | * Find an entry in the database and hand the record's data to a parsing
|
|---|
| 165 | * function. The parsing function is executed under the chain read lock, so it
|
|---|
| 166 | * should be fast and should not block on other syscalls.
|
|---|
| 167 | *
|
|---|
| 168 | * DONT CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS.
|
|---|
| 169 | *
|
|---|
| 170 | * For mmapped tdb's that do not have a transaction open it points the parsing
|
|---|
| 171 | * function directly at the mmap area, it avoids the malloc/memcpy in this
|
|---|
| 172 | * case. If a transaction is open or no mmap is available, it has to do
|
|---|
| 173 | * malloc/read/parse/free.
|
|---|
| 174 | *
|
|---|
| 175 | * This is interesting for all readers of potentially large data structures in
|
|---|
| 176 | * the tdb records, ldb indexes being one example.
|
|---|
| 177 | */
|
|---|
| 178 |
|
|---|
| 179 | int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key,
|
|---|
| 180 | int (*parser)(TDB_DATA key, TDB_DATA data,
|
|---|
| 181 | void *private_data),
|
|---|
| 182 | void *private_data)
|
|---|
| 183 | {
|
|---|
| 184 | tdb_off_t rec_ptr;
|
|---|
| 185 | struct list_struct rec;
|
|---|
| 186 | int ret;
|
|---|
| 187 | u32 hash;
|
|---|
| 188 |
|
|---|
| 189 | /* find which hash bucket it is in */
|
|---|
| 190 | hash = tdb->hash_fn(&key);
|
|---|
| 191 |
|
|---|
| 192 | if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) {
|
|---|
| 193 | return TDB_ERRCODE(TDB_ERR_NOEXIST, 0);
|
|---|
| 194 | }
|
|---|
| 195 |
|
|---|
| 196 | ret = tdb_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len,
|
|---|
| 197 | rec.data_len, parser, private_data);
|
|---|
| 198 |
|
|---|
| 199 | tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
|
|---|
| 200 |
|
|---|
| 201 | return ret;
|
|---|
| 202 | }
|
|---|
| 203 |
|
|---|
| 204 | /* check if an entry in the database exists
|
|---|
| 205 |
|
|---|
| 206 | note that 1 is returned if the key is found and 0 is returned if not found
|
|---|
| 207 | this doesn't match the conventions in the rest of this module, but is
|
|---|
| 208 | compatible with gdbm
|
|---|
| 209 | */
|
|---|
| 210 | static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash)
|
|---|
| 211 | {
|
|---|
| 212 | struct list_struct rec;
|
|---|
| 213 |
|
|---|
| 214 | if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0)
|
|---|
| 215 | return 0;
|
|---|
| 216 | tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK);
|
|---|
| 217 | return 1;
|
|---|
| 218 | }
|
|---|
| 219 |
|
|---|
| 220 | int tdb_exists(struct tdb_context *tdb, TDB_DATA key)
|
|---|
| 221 | {
|
|---|
| 222 | u32 hash = tdb->hash_fn(&key);
|
|---|
| 223 | return tdb_exists_hash(tdb, key, hash);
|
|---|
| 224 | }
|
|---|
| 225 |
|
|---|
| 226 | /* actually delete an entry in the database given the offset */
|
|---|
| 227 | int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct*rec)
|
|---|
| 228 | {
|
|---|
| 229 | tdb_off_t last_ptr, i;
|
|---|
| 230 | struct list_struct lastrec;
|
|---|
| 231 |
|
|---|
| 232 | if (tdb->read_only || tdb->traverse_read) return -1;
|
|---|
| 233 |
|
|---|
| 234 | if (tdb_write_lock_record(tdb, rec_ptr) == -1) {
|
|---|
| 235 | /* Someone traversing here: mark it as dead */
|
|---|
| 236 | rec->magic = TDB_DEAD_MAGIC;
|
|---|
| 237 | return tdb_rec_write(tdb, rec_ptr, rec);
|
|---|
| 238 | }
|
|---|
| 239 | if (tdb_write_unlock_record(tdb, rec_ptr) != 0)
|
|---|
|
|---|