(root)/
Python-3.12.0/
Modules/
_dbmmodule.c
       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*
      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 *
      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
      93  dbm_traverse(dbmobject *dp, visitproc visit, void *arg)
      94  {
      95      Py_VISIT(Py_TYPE(dp));
      96      return 0;
      97  }
      98  
      99  static void
     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
     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 int
     134  dbm_bool(dbmobject *dp)
     135  {
     136      _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
     137      assert(state != NULL);
     138  
     139      if (dp->di_dbm == NULL) {
     140          PyErr_SetString(state->dbm_error, "DBM object has already been closed");
     141          return -1;
     142      }
     143  
     144      if (dp->di_size > 0) {
     145          /* Known non-zero size. */
     146          return 1;
     147      }
     148      if (dp->di_size == 0) {
     149          /* Known zero size. */
     150          return 0;
     151      }
     152  
     153      /* Unknown size.  Ensure DBM object has an entry. */
     154      datum key = dbm_firstkey(dp->di_dbm);
     155      if (key.dptr == NULL) {
     156          /* Empty. Cache this fact. */
     157          dp->di_size = 0;
     158          return 0;
     159      }
     160      /* Non-empty. Don't cache the length since we don't know. */
     161      return 1;
     162  }
     163  
     164  static PyObject *
     165  dbm_subscript(dbmobject *dp, PyObject *key)
     166  {
     167      datum drec, krec;
     168      Py_ssize_t tmp_size;
     169      _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
     170      assert(state != NULL);
     171      if (!PyArg_Parse(key, "s#", &krec.dptr, &tmp_size)) {
     172          return NULL;
     173      }
     174  
     175      krec.dsize = tmp_size;
     176      check_dbmobject_open(dp, state->dbm_error);
     177      drec = dbm_fetch(dp->di_dbm, krec);
     178      if ( drec.dptr == 0 ) {
     179          PyErr_SetObject(PyExc_KeyError, key);
     180          return NULL;
     181      }
     182      if ( dbm_error(dp->di_dbm) ) {
     183          dbm_clearerr(dp->di_dbm);
     184          PyErr_SetString(state->dbm_error, "");
     185          return NULL;
     186      }
     187      return PyBytes_FromStringAndSize(drec.dptr, drec.dsize);
     188  }
     189  
     190  static int
     191  dbm_ass_sub(dbmobject *dp, PyObject *v, PyObject *w)
     192  {
     193      datum krec, drec;
     194      Py_ssize_t tmp_size;
     195  
     196      if ( !PyArg_Parse(v, "s#", &krec.dptr, &tmp_size) ) {
     197          PyErr_SetString(PyExc_TypeError,
     198                          "dbm mappings have bytes or string keys only");
     199          return -1;
     200      }
     201      _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
     202      assert(state != NULL);
     203      krec.dsize = tmp_size;
     204      if (dp->di_dbm == NULL) {
     205               PyErr_SetString(state->dbm_error, "DBM object has already been closed");
     206               return -1;
     207      }
     208      dp->di_size = -1;
     209      if (w == NULL) {
     210          if ( dbm_delete(dp->di_dbm, krec) < 0 ) {
     211              dbm_clearerr(dp->di_dbm);
     212              /* we might get a failure for reasons like file corrupted,
     213                 but we are not able to distinguish it */
     214              if (dp->flags & O_RDWR) {
     215                  PyErr_SetObject(PyExc_KeyError, v);
     216              }
     217              else {
     218                  PyErr_SetString(state->dbm_error, "cannot delete item from database");
     219              }
     220              return -1;
     221          }
     222      } else {
     223          if ( !PyArg_Parse(w, "s#", &drec.dptr, &tmp_size) ) {
     224              PyErr_SetString(PyExc_TypeError,
     225                   "dbm mappings have bytes or string elements only");
     226              return -1;
     227          }
     228          drec.dsize = tmp_size;
     229          if ( dbm_store(dp->di_dbm, krec, drec, DBM_REPLACE) < 0 ) {
     230              dbm_clearerr(dp->di_dbm);
     231              PyErr_SetString(state->dbm_error,
     232                              "cannot add item to database");
     233              return -1;
     234          }
     235      }
     236      if ( dbm_error(dp->di_dbm) ) {
     237          dbm_clearerr(dp->di_dbm);
     238          PyErr_SetString(state->dbm_error, "");
     239          return -1;
     240      }
     241      return 0;
     242  }
     243  
     244  /*[clinic input]
     245  _dbm.dbm.close
     246  
     247  Close the database.
     248  [clinic start generated code]*/
     249  
     250  static PyObject *
     251  _dbm_dbm_close_impl(dbmobject *self)
     252  /*[clinic end generated code: output=c8dc5b6709600b86 input=046db72377d51be8]*/
     253  {
     254      if (self->di_dbm) {
     255          dbm_close(self->di_dbm);
     256      }
     257      self->di_dbm = NULL;
     258      Py_RETURN_NONE;
     259  }
     260  
     261  /*[clinic input]
     262  _dbm.dbm.keys
     263  
     264      cls: defining_class
     265  
     266  Return a list of all keys in the database.
     267  [clinic start generated code]*/
     268  
     269  static PyObject *
     270  _dbm_dbm_keys_impl(dbmobject *self, PyTypeObject *cls)
     271  /*[clinic end generated code: output=f2a593b3038e5996 input=d3706a28fc051097]*/
     272  {
     273      PyObject *v, *item;
     274      datum key;
     275      int err;
     276  
     277      _dbm_state *state = PyType_GetModuleState(cls);
     278      assert(state != NULL);
     279      check_dbmobject_open(self, state->dbm_error);
     280      v = PyList_New(0);
     281      if (v == NULL) {
     282          return NULL;
     283      }
     284      for (key = dbm_firstkey(self->di_dbm); key.dptr;
     285           key = dbm_nextkey(self->di_dbm)) {
     286          item = PyBytes_FromStringAndSize(key.dptr, key.dsize);
     287          if (item == NULL) {
     288              Py_DECREF(v);
     289              return NULL;
     290          }
     291          err = PyList_Append(v, item);
     292          Py_DECREF(item);
     293          if (err != 0) {
     294              Py_DECREF(v);
     295              return NULL;
     296          }
     297      }
     298      return v;
     299  }
     300  
     301  static int
     302  dbm_contains(PyObject *self, PyObject *arg)
     303  {
     304      dbmobject *dp = (dbmobject *)self;
     305      datum key, val;
     306      Py_ssize_t size;
     307  
     308      _dbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
     309      assert(state != NULL);
     310      if ((dp)->di_dbm == NULL) {
     311          PyErr_SetString(state->dbm_error,
     312                          "DBM object has already been closed");
     313           return -1;
     314      }
     315      if (PyUnicode_Check(arg)) {
     316          key.dptr = (char *)PyUnicode_AsUTF8AndSize(arg, &size);
     317          key.dsize = size;
     318          if (key.dptr == NULL)
     319              return -1;
     320      }
     321      else if (!PyBytes_Check(arg)) {
     322          PyErr_Format(PyExc_TypeError,
     323                       "dbm key must be bytes or string, not %.100s",
     324                       Py_TYPE(arg)->tp_name);
     325          return -1;
     326      }
     327      else {
     328          key.dptr = PyBytes_AS_STRING(arg);
     329          key.dsize = PyBytes_GET_SIZE(arg);
     330      }
     331      val = dbm_fetch(dp->di_dbm, key);
     332      return val.dptr != NULL;
     333  }
     334  
     335  /*[clinic input]
     336  _dbm.dbm.get
     337      cls: defining_class
     338      key: str(accept={str, robuffer}, zeroes=True)
     339      default: object = None
     340      /
     341  
     342  Return the value for key if present, otherwise default.
     343  [clinic start generated code]*/
     344  
     345  static PyObject *
     346  _dbm_dbm_get_impl(dbmobject *self, PyTypeObject *cls, const char *key,
     347                    Py_ssize_t key_length, PyObject *default_value)
     348  /*[clinic end generated code: output=b4e55f8b6d482bc4 input=66b993b8349fa8c1]*/
     349  {
     350      datum dbm_key, val;
     351      _dbm_state *state = PyType_GetModuleState(cls);
     352      assert(state != NULL);
     353      dbm_key.dptr = (char *)key;
     354      dbm_key.dsize = key_length;
     355      check_dbmobject_open(self, state->dbm_error);
     356      val = dbm_fetch(self->di_dbm, dbm_key);
     357      if (val.dptr != NULL) {
     358          return PyBytes_FromStringAndSize(val.dptr, val.dsize);
     359      }
     360  
     361      return Py_NewRef(default_value);
     362  }
     363  
     364  /*[clinic input]
     365  _dbm.dbm.setdefault
     366      cls: defining_class
     367      key: str(accept={str, robuffer}, zeroes=True)
     368      default: object(c_default="NULL") = b''
     369      /
     370  
     371  Return the value for key if present, otherwise default.
     372  
     373  If key is not in the database, it is inserted with default as the value.
     374  [clinic start generated code]*/
     375  
     376  static PyObject *
     377  _dbm_dbm_setdefault_impl(dbmobject *self, PyTypeObject *cls, const char *key,
     378                           Py_ssize_t key_length, PyObject *default_value)
     379  /*[clinic end generated code: output=9c2f6ea6d0fb576c input=126a3ff15c5f8232]*/
     380  {
     381      datum dbm_key, val;
     382      Py_ssize_t tmp_size;
     383      _dbm_state *state = PyType_GetModuleState(cls);
     384      assert(state != NULL);
     385      dbm_key.dptr = (char *)key;
     386      dbm_key.dsize = key_length;
     387      check_dbmobject_open(self, state->dbm_error);
     388      val = dbm_fetch(self->di_dbm, dbm_key);
     389      if (val.dptr != NULL) {
     390          return PyBytes_FromStringAndSize(val.dptr, val.dsize);
     391      }
     392      if (default_value == NULL) {
     393          default_value = PyBytes_FromStringAndSize(NULL, 0);
     394          if (default_value == NULL) {
     395              return NULL;
     396          }
     397          val.dptr = NULL;
     398          val.dsize = 0;
     399      }
     400      else {
     401          if ( !PyArg_Parse(default_value, "s#", &val.dptr, &tmp_size) ) {
     402              PyErr_SetString(PyExc_TypeError,
     403                  "dbm mappings have bytes or string elements only");
     404              return NULL;
     405          }
     406          val.dsize = tmp_size;
     407          Py_INCREF(default_value);
     408      }
     409      if (dbm_store(self->di_dbm, dbm_key, val, DBM_INSERT) < 0) {
     410          dbm_clearerr(self->di_dbm);
     411          PyErr_SetString(state->dbm_error, "cannot add item to database");
     412          Py_DECREF(default_value);
     413          return NULL;
     414      }
     415      return default_value;
     416  }
     417  
     418  static PyObject *
     419  dbm__enter__(PyObject *self, PyObject *args)
     420  {
     421      return Py_NewRef(self);
     422  }
     423  
     424  static PyObject *
     425  dbm__exit__(PyObject *self, PyObject *args)
     426  {
     427      return _dbm_dbm_close_impl((dbmobject *)self);
     428  }
     429  
     430  static PyMethodDef dbm_methods[] = {
     431      _DBM_DBM_CLOSE_METHODDEF
     432      _DBM_DBM_KEYS_METHODDEF
     433      _DBM_DBM_GET_METHODDEF
     434      _DBM_DBM_SETDEFAULT_METHODDEF
     435      {"__enter__", dbm__enter__, METH_NOARGS, NULL},
     436      {"__exit__",  dbm__exit__, METH_VARARGS, NULL},
     437      {NULL,  NULL}           /* sentinel */
     438  };
     439  
     440  static PyType_Slot dbmtype_spec_slots[] = {
     441      {Py_tp_dealloc, dbm_dealloc},
     442      {Py_tp_traverse, dbm_traverse},
     443      {Py_tp_methods, dbm_methods},
     444      {Py_sq_contains, dbm_contains},
     445      {Py_mp_length, dbm_length},
     446      {Py_mp_subscript, dbm_subscript},
     447      {Py_mp_ass_subscript, dbm_ass_sub},
     448      {Py_nb_bool, dbm_bool},
     449      {0, 0}
     450  };
     451  
     452  
     453  static PyType_Spec dbmtype_spec = {
     454      .name = "_dbm.dbm",
     455      .basicsize = sizeof(dbmobject),
     456      // Calling PyType_GetModuleState() on a subclass is not safe.
     457      // dbmtype_spec does not have Py_TPFLAGS_BASETYPE flag
     458      // which prevents to create a subclass.
     459      // So calling PyType_GetModuleState() in this file is always safe.
     460      .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION |
     461                Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
     462      .slots = dbmtype_spec_slots,
     463  };
     464  
     465  /* ----------------------------------------------------------------- */
     466  
     467  /*[clinic input]
     468  
     469  _dbm.open as dbmopen
     470  
     471      filename: object
     472          The filename to open.
     473  
     474      flags: str="r"
     475          How to open the file.  "r" for reading, "w" for writing, etc.
     476  
     477      mode: int(py_default="0o666") = 0o666
     478          If creating a new file, the mode bits for the new file
     479          (e.g. os.O_RDWR).
     480  
     481      /
     482  
     483  Return a database object.
     484  
     485  [clinic start generated code]*/
     486  
     487  static PyObject *
     488  dbmopen_impl(PyObject *module, PyObject *filename, const char *flags,
     489               int mode)
     490  /*[clinic end generated code: output=9527750f5df90764 input=d8cf50a9f81218c8]*/
     491  {
     492      int iflags;
     493      _dbm_state *state =  get_dbm_state(module);
     494      assert(state != NULL);
     495      if (strcmp(flags, "r") == 0) {
     496          iflags = O_RDONLY;
     497      }
     498      else if (strcmp(flags, "w") == 0) {
     499          iflags = O_RDWR;
     500      }
     501      else if (strcmp(flags, "rw") == 0) {
     502          /* Backward compatibility */
     503          iflags = O_RDWR|O_CREAT;
     504      }
     505      else if (strcmp(flags, "c") == 0) {
     506          iflags = O_RDWR|O_CREAT;
     507      }
     508      else if (strcmp(flags, "n") == 0) {
     509          iflags = O_RDWR|O_CREAT|O_TRUNC;
     510      }
     511      else {
     512          PyErr_SetString(state->dbm_error,
     513                          "arg 2 to open should be 'r', 'w', 'c', or 'n'");
     514          return NULL;
     515      }
     516  
     517      PyObject *filenamebytes;
     518      if (!PyUnicode_FSConverter(filename, &filenamebytes)) {
     519          return NULL;
     520      }
     521  
     522      const char *name = PyBytes_AS_STRING(filenamebytes);
     523      if (strlen(name) != (size_t)PyBytes_GET_SIZE(filenamebytes)) {
     524          Py_DECREF(filenamebytes);
     525          PyErr_SetString(PyExc_ValueError, "embedded null character");
     526          return NULL;
     527      }
     528      PyObject *self = newdbmobject(state, name, iflags, mode);
     529      Py_DECREF(filenamebytes);
     530      return self;
     531  }
     532  
     533  static PyMethodDef dbmmodule_methods[] = {
     534      DBMOPEN_METHODDEF
     535      { 0, 0 },
     536  };
     537  
     538  static int
     539  _dbm_exec(PyObject *module)
     540  {
     541      _dbm_state *state = get_dbm_state(module);
     542      state->dbm_type = (PyTypeObject *)PyType_FromModuleAndSpec(module,
     543                                                          &dbmtype_spec, NULL);
     544      if (state->dbm_type == NULL) {
     545          return -1;
     546      }
     547      state->dbm_error = PyErr_NewException("_dbm.error", PyExc_OSError, NULL);
     548      if (state->dbm_error == NULL) {
     549          return -1;
     550      }
     551      if (PyModule_AddStringConstant(module, "library", which_dbm) < 0) {
     552          return -1;
     553      }
     554      if (PyModule_AddType(module, (PyTypeObject *)state->dbm_error) < 0) {
     555          return -1;
     556      }
     557      return 0;
     558  }
     559  
     560  static int
     561  _dbm_module_traverse(PyObject *module, visitproc visit, void *arg)
     562  {
     563      _dbm_state *state = get_dbm_state(module);
     564      Py_VISIT(state->dbm_error);
     565      Py_VISIT(state->dbm_type);
     566      return 0;
     567  }
     568  
     569  static int
     570  _dbm_module_clear(PyObject *module)
     571  {
     572      _dbm_state *state = get_dbm_state(module);
     573      Py_CLEAR(state->dbm_error);
     574      Py_CLEAR(state->dbm_type);
     575      return 0;
     576  }
     577  
     578  static void
     579  _dbm_module_free(void *module)
     580  {
     581      _dbm_module_clear((PyObject *)module);
     582  }
     583  
     584  static PyModuleDef_Slot _dbmmodule_slots[] = {
     585      {Py_mod_exec, _dbm_exec},
     586      {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
     587      {0, NULL}
     588  };
     589  
     590  static struct PyModuleDef _dbmmodule = {
     591      PyModuleDef_HEAD_INIT,
     592      .m_name = "_dbm",
     593      .m_size = sizeof(_dbm_state),
     594      .m_methods = dbmmodule_methods,
     595      .m_slots = _dbmmodule_slots,
     596      .m_traverse = _dbm_module_traverse,
     597      .m_clear = _dbm_module_clear,
     598      .m_free = _dbm_module_free,
     599  };
     600  
     601  PyMODINIT_FUNC
     602  PyInit__dbm(void)
     603  {
     604      return PyModuleDef_Init(&_dbmmodule);
     605  }