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 
37 typedef struct {
38     PyTypeObject *dbm_type;
39     PyObject *dbm_error;
40 } _dbm_state;
41 
42 static inline _dbm_state*
get_dbm_state(PyObject * module)43 get_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]
51 module _dbm
52 class _dbm.dbm "dbmobject *" "&Dbmtype"
53 [clinic start generated code]*/
54 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=9b1aa8756d16150e]*/
55 
56 typedef 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 
71 static PyObject *
newdbmobject(_dbm_state * state,const char * file,int flags,int mode)72 newdbmobject(_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 */
92 static int
dbm_traverse(dbmobject * dp,visitproc visit,void * arg)93 dbm_traverse(dbmobject *dp, visitproc visit, void *arg)
94 {
95     Py_VISIT(Py_TYPE(dp));
96     return 0;
97 }
98 
99 static void
dbm_dealloc(dbmobject * dp)100 dbm_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 
111 static Py_ssize_t
dbm_length(dbmobject * dp)112 dbm_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 
133 static PyObject *
dbm_subscript(dbmobject * dp,PyObject * key)134 dbm_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 
159 static int
dbm_ass_sub(dbmobject * dp,PyObject * v,PyObject * w)160 dbm_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 
216 Close the database.
217 [clinic start generated code]*/
218 
219 static PyObject *
_dbm_dbm_close_impl(dbmobject * self)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 
235 Return a list of all keys in the database.
236 [clinic start generated code]*/
237 
238 static PyObject *
_dbm_dbm_keys_impl(dbmobject * self,PyTypeObject * cls)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 
270 static int
dbm_contains(PyObject * self,PyObject * arg)271 dbm_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 
311 Return the value for key if present, otherwise default.
312 [clinic start generated code]*/
313 
314 static PyObject *
_dbm_dbm_get_impl(dbmobject * self,PyTypeObject * cls,const char * key,Py_ssize_t key_length,PyObject * default_value)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 
341 Return the value for key if present, otherwise default.
342 
343 If key is not in the database, it is inserted with default as the value.
344 [clinic start generated code]*/
345 
346 static PyObject *
_dbm_dbm_setdefault_impl(dbmobject * self,PyTypeObject * cls,const char * key,Py_ssize_t key_length,PyObject * default_value)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 
388 static PyObject *
dbm__enter__(PyObject * self,PyObject * args)389 dbm__enter__(PyObject *self, PyObject *args)
390 {
391     Py_INCREF(self);
392     return self;
393 }
394 
395 static PyObject *
dbm__exit__(PyObject * self,PyObject * args)396 dbm__exit__(PyObject *self, PyObject *args)
397 {
398     return _dbm_dbm_close_impl((dbmobject *)self);
399 }
400 
401 static 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 
411 static 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 
423 static 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 
453 Return a database object.
454 
455 [clinic start generated code]*/
456 
457 static PyObject *
dbmopen_impl(PyObject * module,PyObject * filename,const char * flags,int mode)458 dbmopen_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 
503 static PyMethodDef dbmmodule_methods[] = {
504     DBMOPEN_METHODDEF
505     { 0, 0 },
506 };
507 
508 static int
_dbm_exec(PyObject * module)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 
530 static int
_dbm_module_traverse(PyObject * module,visitproc visit,void * arg)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 
539 static int
_dbm_module_clear(PyObject * module)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 
548 static void
_dbm_module_free(void * module)549 _dbm_module_free(void *module)
550 {
551     _dbm_module_clear((PyObject *)module);
552 }
553 
554 static PyModuleDef_Slot _dbmmodule_slots[] = {
555     {Py_mod_exec, _dbm_exec},
556     {0, NULL}
557 };
558 
559 static 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 
570 PyMODINIT_FUNC
PyInit__dbm(void)571 PyInit__dbm(void)
572 {
573     return PyModuleDef_Init(&_dbmmodule);
574 }
575