1 2/* DBM module using dictionary interface */ 3 4 5#define PY_SSIZE_T_CLEAN 6#include "Python.h" 7 8#include <sys/types.h> 9#include <sys/stat.h> 10#include <fcntl.h> 11 12/* Some Linux systems install gdbm/ndbm.h, but not ndbm.h. This supports 13 * whichever configure was able to locate. 14 */ 15#if defined(USE_GDBM_COMPAT) 16 #ifdef HAVE_GDBM_NDBM_H 17 #include <gdbm/ndbm.h> 18 #elif HAVE_GDBM_DASH_NDBM_H 19 #include <gdbm-ndbm.h> 20 #else 21 #error "No gdbm/ndbm.h or gdbm-ndbm.h available" 22 #endif 23 static const char which_dbm[] = "GNU gdbm"; 24#elif defined(USE_NDBM) 25 #include <ndbm.h> 26 static const char which_dbm[] = "GNU gdbm"; 27#elif defined(USE_BERKDB) 28 #ifndef DB_DBM_HSEARCH 29 #define DB_DBM_HSEARCH 1 30 #endif 31 #include <db.h> 32 static const char which_dbm[] = "Berkeley DB"; 33#else 34 #error "No ndbm.h available!" 35#endif 36 37typedef struct { 38 PyTypeObject *dbm_type; 39 PyObject *dbm_error; 40} _dbm_state; 41 42static inline _dbm_state* 43get_dbm_state(PyObject *module) 44{ 45 void *state = PyModule_GetState(module); 46 assert(state != NULL); 47 return (_dbm_state *)state; 48} 49 50/*[clinic input] 51module _dbm 52class _dbm.dbm "dbmobject *" "&Dbmtype" 53[clinic start generated code]*/ 54/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9b1aa8756d16150e]*/ 55 56typedef struct { 57 PyObject_HEAD 58 int flags; 59 int di_size; /* -1 means recompute */ 60 DBM *di_dbm; 61} dbmobject; 62 63#include "clinic/_dbmmodule.c.h" 64 65#define check_dbmobject_open(v, err) \ 66 if ((v)->di_dbm == NULL) { \ 67 PyErr_SetString(err, "DBM object has already been closed"); \ 68 return NULL; \ 69 } 70 71static PyObject * 72newdbmobject(_dbm_state *state, const char *file, int flags, int mode) 73{ 74 dbmobject *dp = PyObject_GC_New(dbmobject, state->dbm_type); 75 if (dp == NULL) { 76 return NULL; 77 } 78 dp->di_size = -1; 79 dp->flags = flags; 80 PyObject_GC_Track(dp); 81 82 /* See issue #19296 */ 83 if ( (dp->di_dbm = dbm_open((char *)file, flags, mode)) == 0 ) { 84 PyErr_SetFromErrnoWithFilename(state->dbm_error, file); 85 Py_DECREF(dp); 86 return NULL; 87 } 88 return (PyObject *)dp; 89} 90 91/* Methods */ 92static int 93dbm_traverse(dbmobject *dp, visitproc visit, void *arg) 94{ 95 Py_VISIT(Py_TYPE(dp)); 96 return 0; 97} 98 99static void 100dbm_dealloc(dbmobject *dp) 101{ 102 PyObject_GC_UnTrack(dp); 103 if (dp->di_dbm) { 104 dbm_close(dp->di_dbm); 105 } 106 PyTypeObject *tp = Py_TYPE(dp); 107 tp->tp_free(dp); 108 Py_DECREF(tp); 109} 110 111static Py_ssize_t 112dbm_length(dbmobject *dp) 113{ 114 _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); 115 assert(state != NULL); 116 if (dp->di_dbm == NULL) { 117 PyErr_SetString(state->dbm_error, "DBM object has already been closed"); 118 return -1; 119 } 120 if ( dp->di_size < 0 ) { 121 datum key; 122 int size; 123 124 size = 0; 125 for ( key=dbm_firstkey(dp->di_dbm); key.dptr; 126 key = dbm_nextkey(dp->di_dbm)) 127 size++; 128 dp->di_size = size; 129 } 130 return dp->di_size; 131} 132 133static PyObject * 134dbm_subscript(dbmobject *dp, PyObject *key) 135{ 136 datum drec, krec; 137 Py_ssize_t tmp_size; 138 _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); 139 assert(state != NULL); 140 if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size)) { 141 return NULL; 142 } 143 144 krec.dsize = tmp_size; 145 check_dbmobject_open(dp, state->dbm_error); 146 drec = dbm_fetch(dp->di_dbm, krec); 147 if ( drec.dptr == 0 ) { 148 PyErr_SetObject(PyExc_KeyError, key); 149 return NULL; 150 } 151 if ( dbm_error(dp->di_dbm) ) { 152 dbm_clearerr(dp->di_dbm); 153 PyErr_SetString(state->dbm_error, ""); 154 return NULL; 155 } 156 return PyBytes_FromStringAndSize(drec.dptr, drec.dsize); 157} 158 159static int 160dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w) 161{ 162 datum krec, drec; 163 Py_ssize_t tmp_size; 164 165 if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) { 166 PyErr_SetString(PyExc_TypeError, 167 "dbm mappings have bytes or string keys only"); 168 return -1; 169 } 170 _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); 171 assert(state != NULL); 172 krec.dsize = tmp_size; 173 if (dp->di_dbm == NULL) { 174 PyErr_SetString(state->dbm_error, "DBM object has already been closed"); 175 return -1; 176 } 177 dp->di_size = -1; 178 if (w == NULL) { 179 if ( dbm_delete(dp->di_dbm, krec) < 0 ) { 180 dbm_clearerr(dp->di_dbm); 181 /* we might get a failure for reasons like file corrupted, 182 but we are not able to distinguish it */ 183 if (dp->flags & O_RDWR) { 184 PyErr_SetObject(PyExc_KeyError, v); 185 } 186 else { 187 PyErr_SetString(state->dbm_error, "cannot delete item from database"); 188 } 189 return -1; 190 } 191 } else { 192 if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) { 193 PyErr_SetString(PyExc_TypeError, 194 "dbm mappings have bytes or string elements only"); 195 return -1; 196 } 197 drec.dsize = tmp_size; 198 if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) { 199 dbm_clearerr(dp->di_dbm); 200 PyErr_SetString(state->dbm_error, 201 "cannot add item to database"); 202 return -1; 203 } 204 } 205 if ( dbm_error(dp->di_dbm) ) { 206 dbm_clearerr(dp->di_dbm); 207 PyErr_SetString(state->dbm_error, ""); 208 return -1; 209 } 210 return 0; 211} 212 213/*[clinic input] 214_dbm.dbm.close 215 216Close the database. 217[clinic start generated code]*/ 218 219static PyObject * 220_dbm_dbm_close_impl(dbmobject *self) 221/*[clinic end generated code: output=c8dc5b6709600b86 input=046db72377d51be8]*/ 222{ 223 if (self->di_dbm) { 224 dbm_close(self->di_dbm); 225 } 226 self->di_dbm = NULL; 227 Py_RETURN_NONE; 228} 229 230/*[clinic input] 231_dbm.dbm.keys 232 233 cls: defining_class 234 235Return a list of all keys in the database. 236[clinic start generated code]*/ 237 238static PyObject * 239_dbm_dbm_keys_impl(dbmobject *self, PyTypeObject *cls) 240/*[clinic end generated code: output=f2a593b3038e5996 input=d3706a28fc051097]*/ 241{ 242 PyObject *v, *item; 243 datum key; 244 int err; 245 246 _dbm_state *state = PyType_GetModuleState(cls); 247 assert(state != NULL); 248 check_dbmobject_open(self, state->dbm_error); 249 v = PyList_New(0); 250 if (v == NULL) { 251 return NULL; 252 } 253 for (key = dbm_firstkey(self->di_dbm); key.dptr; 254 key = dbm_nextkey(self->di_dbm)) { 255 item = PyBytes_FromStringAndSize(key.dptr, key.dsize); 256 if (item == NULL) { 257 Py_DECREF(v); 258 return NULL; 259 } 260 err = PyList_Append(v, item); 261 Py_DECREF(item); 262 if (err != 0) { 263 Py_DECREF(v); 264 return NULL; 265 } 266 } 267 return v; 268} 269 270static int 271dbm_contains(PyObject *self, PyObject *arg) 272{ 273 dbmobject *dp = (dbmobject *)self; 274 datum key, val; 275 Py_ssize_t size; 276 277 _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); 278 assert(state != NULL); 279 if ((dp)->di_dbm == NULL) { 280 PyErr_SetString(state->dbm_error, 281 "DBM object has already been closed"); 282 return -1; 283 } 284 if (PyUnicode_Check(arg)) { 285 key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size); 286 key.dsize = size; 287 if (key.dptr == NULL) 288 return -1; 289 } 290 else if (!PyBytes_Check(arg)) { 291 PyErr_Format(PyExc_TypeError, 292 "dbm key must be bytes or string, not %.100s", 293 Py_TYPE(arg)->tp_name); 294 return -1; 295 } 296 else { 297 key.dptr = PyBytes_AS_STRING(arg); 298 key.dsize = PyBytes_GET_SIZE(arg); 299 } 300 val = dbm_fetch(dp->di_dbm, key); 301 return val.dptr != NULL; 302} 303 304/*[clinic input] 305_dbm.dbm.get 306 cls: defining_class 307 key: str(accept={str, robuffer}, zeroes=True) 308 default: object = None 309 / 310 311Return the value for key if present, otherwise default. 312[clinic start generated code]*/ 313 314static PyObject * 315_dbm_dbm_get_impl(dbmobject *self, PyTypeObject *cls, const char *key, 316 Py_ssize_t key_length, PyObject *default_value) 317/*[clinic end generated code: output=b4e55f8b6d482bc4 input=66b993b8349fa8c1]*/ 318{ 319 datum dbm_key, val; 320 _dbm_state *state = PyType_GetModuleState(cls); 321 assert(state != NULL); 322 dbm_key.dptr = (char *)key; 323 dbm_key.dsize = key_length; 324 check_dbmobject_open(self, state->dbm_error); 325 val = dbm_fetch(self->di_dbm, dbm_key); 326 if (val.dptr != NULL) { 327 return PyBytes_FromStringAndSize(val.dptr, val.dsize); 328 } 329 330 Py_INCREF(default_value); 331 return default_value; 332} 333 334/*[clinic input] 335_dbm.dbm.setdefault 336 cls: defining_class 337 key: str(accept={str, robuffer}, zeroes=True) 338 default: object(c_default="NULL") = b'' 339 / 340 341Return the value for key if present, otherwise default. 342 343If key is not in the database, it is inserted with default as the value. 344[clinic start generated code]*/ 345 346static PyObject * 347_dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key, 348 Py_ssize_t key_length, PyObject *default_value) 349/*[clinic end generated code: output=9c2f6ea6d0fb576c input=126a3ff15c5f8232]*/ 350{ 351 datum dbm_key, val; 352 Py_ssize_t tmp_size; 353 _dbm_state *state = PyType_GetModuleState(cls); 354 assert(state != NULL); 355 dbm_key.dptr = (char *)key; 356 dbm_key.dsize = key_length; 357 check_dbmobject_open(self, state->dbm_error); 358 val = dbm_fetch(self->di_dbm, dbm_key); 359 if (val.dptr != NULL) { 360 return PyBytes_FromStringAndSize(val.dptr, val.dsize); 361 } 362 if (default_value == NULL) { 363 default_value = PyBytes_FromStringAndSize(NULL, 0); 364 if (default_value == NULL) { 365 return NULL; 366 } 367 val.dptr = NULL; 368 val.dsize = 0; 369 } 370 else { 371 if ( !PyArg_Parse(default_value, "s#", &val.dptr, &tmp_size) ) { 372 PyErr_SetString(PyExc_TypeError, 373 "dbm mappings have bytes or string elements only"); 374 return NULL; 375 } 376 val.dsize = tmp_size; 377 Py_INCREF(default_value); 378 } 379 if (dbm_store(self->di_dbm, dbm_key, val, DBM_INSERT) < 0) { 380 dbm_clearerr(self->di_dbm); 381 PyErr_SetString(state->dbm_error, "cannot add item to database"); 382 Py_DECREF(default_value); 383 return NULL; 384 } 385 return default_value; 386} 387 388static PyObject * 389dbm__enter__(PyObject *self, PyObject *args) 390{ 391 Py_INCREF(self); 392 return self; 393} 394 395static PyObject * 396dbm__exit__(PyObject *self, PyObject *args) 397{ 398 return _dbm_dbm_close_impl((dbmobject *)self); 399} 400 401static PyMethodDef dbm_methods[] = { 402 _DBM_DBM_CLOSE_METHODDEF 403 _DBM_DBM_KEYS_METHODDEF 404 _DBM_DBM_GET_METHODDEF 405 _DBM_DBM_SETDEFAULT_METHODDEF 406 {"__enter__", dbm__enter__, METH_NOARGS, NULL}, 407 {"__exit__", dbm__exit__, METH_VARARGS, NULL}, 408 {NULL, NULL} /* sentinel */ 409}; 410 411static PyType_Slot dbmtype_spec_slots[] = { 412 {Py_tp_dealloc, dbm_dealloc}, 413 {Py_tp_traverse, dbm_traverse}, 414 {Py_tp_methods, dbm_methods}, 415 {Py_sq_contains, dbm_contains}, 416 {Py_mp_length, dbm_length}, 417 {Py_mp_subscript, dbm_subscript}, 418 {Py_mp_ass_subscript, dbm_ass_sub}, 419 {0, 0} 420}; 421 422 423static PyType_Spec dbmtype_spec = { 424 .name = "_dbm.dbm", 425 .basicsize = sizeof(dbmobject), 426 // Calling PyType_GetModuleState() on a subclass is not safe. 427 // dbmtype_spec does not have Py_TPFLAGS_BASETYPE flag 428 // which prevents to create a subclass. 429 // So calling PyType_GetModuleState() in this file is always safe. 430 .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION | 431 Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE), 432 .slots = dbmtype_spec_slots, 433}; 434 435/* ----------------------------------------------------------------- */ 436 437/*[clinic input] 438 439_dbm.open as dbmopen 440 441 filename: object 442 The filename to open. 443 444 flags: str="r" 445 How to open the file. "r" for reading, "w" for writing, etc. 446 447 mode: int(py_default="0o666") = 0o666 448 If creating a new file, the mode bits for the new file 449 (e.g. os.O_RDWR). 450 451 / 452 453Return a database object. 454 455[clinic start generated code]*/ 456 457static PyObject * 458dbmopen_impl(PyObject *module, PyObject *filename, const char *flags, 459 int mode) 460/*[clinic end generated code: output=9527750f5df90764 input=d8cf50a9f81218c8]*/ 461{ 462 int iflags; 463 _dbm_state *state = get_dbm_state(module); 464 assert(state != NULL); 465 if (strcmp(flags, "r") == 0) { 466 iflags = O_RDONLY; 467 } 468 else if (strcmp(flags, "w") == 0) { 469 iflags = O_RDWR; 470 } 471 else if (strcmp(flags, "rw") == 0) { 472 /* Backward compatibility */ 473 iflags = O_RDWR|O_CREAT; 474 } 475 else if (strcmp(flags, "c") == 0) { 476 iflags = O_RDWR|O_CREAT; 477 } 478 else if (strcmp(flags, "n") == 0) { 479 iflags = O_RDWR|O_CREAT|O_TRUNC; 480 } 481 else { 482 PyErr_SetString(state->dbm_error, 483 "arg 2 to open should be 'r', 'w', 'c', or 'n'"); 484 return NULL; 485 } 486 487 PyObject *filenamebytes; 488 if (!PyUnicode_FSConverter(filename, &filenamebytes)) { 489 return NULL; 490 } 491 492 const char *name = PyBytes_AS_STRING(filenamebytes); 493 if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) { 494 Py_DECREF(filenamebytes); 495 PyErr_SetString(PyExc_ValueError, "embedded null character"); 496 return NULL; 497 } 498 PyObject *self = newdbmobject(state, name, iflags, mode); 499 Py_DECREF(filenamebytes); 500 return self; 501} 502 503static PyMethodDef dbmmodule_methods[] = { 504 DBMOPEN_METHODDEF 505 { 0, 0 }, 506}; 507 508static int 509_dbm_exec(PyObject *module) 510{ 511 _dbm_state *state = get_dbm_state(module); 512 state->dbm_type = (PyTypeObject *)PyType_FromModuleAndSpec(module, 513 &dbmtype_spec, NULL); 514 if (state->dbm_type == NULL) { 515 return -1; 516 } 517 state->dbm_error = PyErr_NewException("_dbm.error", PyExc_OSError, NULL); 518 if (state->dbm_error == NULL) { 519 return -1; 520 } 521 if (PyModule_AddStringConstant(module, "library", which_dbm) < 0) { 522 return -1; 523 } 524 if (PyModule_AddType(module, (PyTypeObject *)state->dbm_error) < 0) { 525 return -1; 526 } 527 return 0; 528} 529 530static int 531_dbm_module_traverse(PyObject *module, visitproc visit, void *arg) 532{ 533 _dbm_state *state = get_dbm_state(module); 534 Py_VISIT(state->dbm_error); 535 Py_VISIT(state->dbm_type); 536 return 0; 537} 538 539static int 540_dbm_module_clear(PyObject *module) 541{ 542 _dbm_state *state = get_dbm_state(module); 543 Py_CLEAR(state->dbm_error); 544 Py_CLEAR(state->dbm_type); 545 return 0; 546} 547 548static void 549_dbm_module_free(void *module) 550{ 551 _dbm_module_clear((PyObject *)module); 552} 553 554static PyModuleDef_Slot _dbmmodule_slots[] = { 555 {Py_mod_exec, _dbm_exec}, 556 {0, NULL} 557}; 558 559static struct PyModuleDef _dbmmodule = { 560 PyModuleDef_HEAD_INIT, 561 .m_name = "_dbm", 562 .m_size = sizeof(_dbm_state), 563 .m_methods = dbmmodule_methods, 564 .m_slots = _dbmmodule_slots, 565 .m_traverse = _dbm_module_traverse, 566 .m_clear = _dbm_module_clear, 567 .m_free = _dbm_module_free, 568}; 569 570PyMODINIT_FUNC 571PyInit__dbm(void) 572{ 573 return PyModuleDef_Init(&_dbmmodule); 574} 575