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