1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996, 1997, 1998 5 * Sleepycat Software. All rights reserved. 6 */ 7 /* 8 * Copyright (c) 1990, 1993 9 * Margo Seltzer. All rights reserved. 10 */ 11 /* 12 * Copyright (c) 1990, 1993 13 * The Regents of the University of California. All rights reserved. 14 * 15 * This code is derived from software contributed to Berkeley by 16 * Margo Seltzer. 17 * 18 * Redistribution and use in source and binary forms, with or without 19 * modification, are permitted provided that the following conditions 20 * are met: 21 * 1. Redistributions of source code must retain the above copyright 22 * notice, this list of conditions and the following disclaimer. 23 * 2. Redistributions in binary form must reproduce the above copyright 24 * notice, this list of conditions and the following disclaimer in the 25 * documentation and/or other materials provided with the distribution. 26 * 3. All advertising materials mentioning features or use of this software 27 * must display the following acknowledgement: 28 * This product includes software developed by the University of 29 * California, Berkeley and its contributors. 30 * 4. Neither the name of the University nor the names of its contributors 31 * may be used to endorse or promote products derived from this software 32 * without specific prior written permission. 33 * 34 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 35 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 36 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 37 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 38 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 40 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 42 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 43 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 44 * SUCH DAMAGE. 45 */ 46 47 #include "config.h" 48 49 #ifndef lint 50 static const char sccsid[] = "@(#)dbm.c 10.23 (Sleepycat) 11/22/98"; 51 #endif /* not lint */ 52 53 #ifndef NO_SYSTEM_INCLUDES 54 #include <sys/types.h> 55 56 #include <errno.h> 57 #include <fcntl.h> 58 #include <string.h> 59 #endif 60 61 #define DB_DBM_HSEARCH 1 62 #include "db_int.h" 63 64 #include "db_page.h" 65 #include "hash.h" 66 67 /* 68 * 69 * This package provides dbm and ndbm compatible interfaces to DB. 70 * 71 * The DBM routines, which call the NDBM routines. 72 */ 73 static DBM *__cur_db; 74 75 static void __db_no_open __P((void)); 76 77 int 78 __db_dbm_init(file) 79 char *file; 80 { 81 if (__cur_db != NULL) 82 (void)dbm_close(__cur_db); 83 if ((__cur_db = 84 dbm_open(file, O_CREAT | O_RDWR, __db_omode("rw----"))) != NULL) 85 return (0); 86 if ((__cur_db = dbm_open(file, O_RDONLY, 0)) != NULL) 87 return (0); 88 return (-1); 89 } 90 91 int 92 __db_dbm_close() 93 { 94 if (__cur_db != NULL) { 95 dbm_close(__cur_db); 96 __cur_db = NULL; 97 } 98 return (0); 99 } 100 101 datum 102 __db_dbm_fetch(key) 103 datum key; 104 { 105 datum item; 106 107 if (__cur_db == NULL) { 108 __db_no_open(); 109 item.dptr = 0; 110 return (item); 111 } 112 return (dbm_fetch(__cur_db, key)); 113 } 114 115 datum 116 __db_dbm_firstkey() 117 { 118 datum item; 119 120 if (__cur_db == NULL) { 121 __db_no_open(); 122 item.dptr = 0; 123 return (item); 124 } 125 return (dbm_firstkey(__cur_db)); 126 } 127 128 datum 129 __db_dbm_nextkey(key) 130 datum key; 131 { 132 datum item; 133 134 COMPQUIET(key.dsize, 0); 135 136 if (__cur_db == NULL) { 137 __db_no_open(); 138 item.dptr = 0; 139 return (item); 140 } 141 return (dbm_nextkey(__cur_db)); 142 } 143 144 int 145 __db_dbm_delete(key) 146 datum key; 147 { 148 if (__cur_db == NULL) { 149 __db_no_open(); 150 return (-1); 151 } 152 return (dbm_delete(__cur_db, key)); 153 } 154 155 int 156 __db_dbm_store(key, dat) 157 datum key, dat; 158 { 159 if (__cur_db == NULL) { 160 __db_no_open(); 161 return (-1); 162 } 163 return (dbm_store(__cur_db, key, dat, DBM_REPLACE)); 164 } 165 166 static void 167 __db_no_open() 168 { 169 (void)fprintf(stderr, "dbm: no open database.\n"); 170 } 171 172 /* 173 * This package provides dbm and ndbm compatible interfaces to DB. 174 * 175 * The NDBM routines, which call the DB routines. 176 */ 177 /* 178 * Returns: 179 * *DBM on success 180 * NULL on failure 181 */ 182 DBM * 183 __db_ndbm_open(file, oflags, mode) 184 const char *file; 185 int oflags, mode; 186 { 187 DB *dbp; 188 DBC *dbc; 189 DB_INFO dbinfo; 190 int sv_errno; 191 char path[MAXPATHLEN]; 192 193 memset(&dbinfo, 0, sizeof(dbinfo)); 194 dbinfo.db_pagesize = 4096; 195 dbinfo.h_ffactor = 40; 196 dbinfo.h_nelem = 1; 197 198 /* 199 * XXX 200 * Don't use sprintf(3)/snprintf(3) -- the former is dangerous, and 201 * the latter isn't standard, and we're manipulating strings handed 202 * us by the application. 203 */ 204 if (strlen(file) + strlen(DBM_SUFFIX) + 1 > sizeof(path)) { 205 errno = ENAMETOOLONG; 206 return (NULL); 207 } 208 (void)strcpy(path, file); 209 (void)strcat(path, DBM_SUFFIX); 210 if ((errno = db_open(path, 211 DB_HASH, __db_oflags(oflags), mode, NULL, &dbinfo, &dbp)) != 0) 212 return (NULL); 213 214 if ((errno = dbp->cursor(dbp, NULL, &dbc, 0)) != 0) { 215 sv_errno = errno; 216 (void)dbp->close(dbp, 0); 217 errno = sv_errno; 218 return (NULL); 219 } 220 221 return ((DBM *)dbc); 222 } 223 224 /* 225 * Returns: 226 * Nothing. 227 */ 228 void 229 __db_ndbm_close(dbm) 230 DBM *dbm; 231 { 232 DBC *dbc; 233 234 dbc = (DBC *)dbm; 235 236 (void)dbc->dbp->close(dbc->dbp, 0); 237 } 238 239 /* 240 * Returns: 241 * DATUM on success 242 * NULL on failure 243 */ 244 datum 245 __db_ndbm_fetch(dbm, key) 246 DBM *dbm; 247 datum key; 248 { 249 DBC *dbc; 250 DBT _key, _data; 251 datum data; 252 int ret; 253 254 dbc = (DBC *)dbm; 255 256 memset(&_key, 0, sizeof(DBT)); 257 memset(&_data, 0, sizeof(DBT)); 258 _key.size = key.dsize; 259 _key.data = key.dptr; 260 261 /* 262 * Note that we can't simply use the dbc we have to do a c_get/SET, 263 * because that cursor is the one used for sequential iteration and 264 * it has to remain stable in the face of intervening gets and puts. 265 */ 266 if ((ret = dbc->dbp->get(dbc->dbp, NULL, &_key, &_data, 0)) == 0) { 267 data.dptr = _data.data; 268 data.dsize = _data.size; 269 } else { 270 data.dptr = NULL; 271 data.dsize = 0; 272 if (ret == DB_NOTFOUND) 273 errno = ENOENT; 274 else { 275 errno = ret; 276 F_SET(dbc->dbp, DB_DBM_ERROR); 277 } 278 } 279 return (data); 280 } 281 282 /* 283 * Returns: 284 * DATUM on success 285 * NULL on failure 286 */ 287 datum 288 __db_ndbm_firstkey(dbm) 289 DBM *dbm; 290 { 291 DBC *dbc; 292 DBT _key, _data; 293 datum key; 294 int ret; 295 296 dbc = (DBC *)dbm; 297 298 memset(&_key, 0, sizeof(DBT)); 299 memset(&_data, 0, sizeof(DBT)); 300 301 if ((ret = dbc->c_get(dbc, &_key, &_data, DB_FIRST)) == 0) { 302 key.dptr = _key.data; 303 key.dsize = _key.size; 304 } else { 305 key.dptr = NULL; 306 key.dsize = 0; 307 if (ret == DB_NOTFOUND) 308 errno = ENOENT; 309 else { 310 errno = ret; 311 F_SET(dbc->dbp, DB_DBM_ERROR); 312 } 313 } 314 return (key); 315 } 316 317 /* 318 * Returns: 319 * DATUM on success 320 * NULL on failure 321 */ 322 datum 323 __db_ndbm_nextkey(dbm) 324 DBM *dbm; 325 { 326 DBC *dbc; 327 DBT _key, _data; 328 datum key; 329 int ret; 330 331 dbc = (DBC *)dbm; 332 333 memset(&_key, 0, sizeof(DBT)); 334 memset(&_data, 0, sizeof(DBT)); 335 336 if ((ret = dbc->c_get(dbc, &_key, &_data, DB_NEXT)) == 0) { 337 key.dptr = _key.data; 338 key.dsize = _key.size; 339 } else { 340 key.dptr = NULL; 341 key.dsize = 0; 342 if (ret == DB_NOTFOUND) 343 errno = ENOENT; 344 else { 345 errno = ret; 346 F_SET(dbc->dbp, DB_DBM_ERROR); 347 } 348 } 349 return (key); 350 } 351 352 /* 353 * Returns: 354 * 0 on success 355 * <0 failure 356 */ 357 int 358 __db_ndbm_delete(dbm, key) 359 DBM *dbm; 360 datum key; 361 { 362 DBC *dbc; 363 DBT _key; 364 int ret; 365 366 dbc = (DBC *)dbm; 367 368 memset(&_key, 0, sizeof(DBT)); 369 _key.data = key.dptr; 370 _key.size = key.dsize; 371 372 if ((ret = dbc->dbp->del(dbc->dbp, NULL, &_key, 0)) == 0) 373 return (0); 374 375 if (ret == DB_NOTFOUND) 376 errno = ENOENT; 377 else { 378 errno = ret; 379 F_SET(dbc->dbp, DB_DBM_ERROR); 380 } 381 return (-1); 382 } 383 384 /* 385 * Returns: 386 * 0 on success 387 * <0 failure 388 * 1 if DBM_INSERT and entry exists 389 */ 390 int 391 __db_ndbm_store(dbm, key, data, flags) 392 DBM *dbm; 393 datum key, data; 394 int flags; 395 { 396 DBC *dbc; 397 DBT _key, _data; 398 int ret; 399 400 dbc = (DBC *)dbm; 401 402 memset(&_key, 0, sizeof(DBT)); 403 _key.data = key.dptr; 404 _key.size = key.dsize; 405 406 memset(&_data, 0, sizeof(DBT)); 407 _data.data = data.dptr; 408 _data.size = data.dsize; 409 410 if ((ret = dbc->dbp->put(dbc->dbp, NULL, 411 &_key, &_data, flags == DBM_INSERT ? DB_NOOVERWRITE : 0)) == 0) 412 return (0); 413 414 if (ret == DB_KEYEXIST) 415 return (1); 416 417 errno = ret; 418 F_SET(dbc->dbp, DB_DBM_ERROR); 419 return (-1); 420 } 421 422 int 423 __db_ndbm_error(dbm) 424 DBM *dbm; 425 { 426 DBC *dbc; 427 428 dbc = (DBC *)dbm; 429 430 return (F_ISSET(dbc->dbp, DB_DBM_ERROR)); 431 } 432 433 int 434 __db_ndbm_clearerr(dbm) 435 DBM *dbm; 436 { 437 DBC *dbc; 438 439 dbc = (DBC *)dbm; 440 441 F_CLR(dbc->dbp, DB_DBM_ERROR); 442 return (0); 443 } 444 445 /* 446 * Returns: 447 * 1 if read-only 448 * 0 if not read-only 449 */ 450 int 451 __db_ndbm_rdonly(dbm) 452 DBM *dbm; 453 { 454 DBC *dbc; 455 456 dbc = (DBC *)dbm; 457 458 return (F_ISSET(dbc->dbp, DB_AM_RDONLY) ? 1 : 0); 459 } 460 461 /* 462 * XXX 463 * We only have a single file descriptor that we can return, not two. Return 464 * the same one for both files. Hopefully, the user is using it for locking 465 * and picked one to use at random. 466 */ 467 int 468 __db_ndbm_dirfno(dbm) 469 DBM *dbm; 470 { 471 return (dbm_pagfno(dbm)); 472 } 473 474 int 475 __db_ndbm_pagfno(dbm) 476 DBM *dbm; 477 { 478 DBC *dbc; 479 int fd; 480 481 dbc = (DBC *)dbm; 482 483 (void)dbc->dbp->fd(dbc->dbp, &fd); 484 return (fd); 485 } 486