tidying
[exim.git] / src / src / dbfn.c
index 3aca1832650440aeed61e6abf16e27f7c60fe791..389d1518ed8a0249d6a3f0f75f8a78c941e64426 100644 (file)
@@ -2,12 +2,19 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 
 #include "exim.h"
 
+/* We have buffers holding path names for database files.
+PATH_MAX could be used here, but would be wasting memory, as we deal
+with database files like $spooldirectory/db/<name> */
+#define PATHLEN 256
+
 
 /* Functions for accessing Exim's hints database, which consists of a number of
 different DBM files. This module does not contain code for reading DBM files
@@ -32,33 +39,6 @@ arrange to hold the locks for the bare minimum of time. */
 
 
 
-/*************************************************
-*         Berkeley DB error callback             *
-*************************************************/
-
-/* For Berkeley DB >= 2, we can define a function to be called in case of DB
-errors. This should help with debugging strange DB problems, e.g. getting "File
-exists" when you try to open a db file. The API for this function was changed
-at DB release 4.3. */
-
-#if defined(USE_DB) && defined(DB_VERSION_STRING)
-void
-#if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
-dbfn_bdb_error_callback(const DB_ENV *dbenv, const char *pfx, const char *msg)
-{
-dbenv = dbenv;
-#else
-dbfn_bdb_error_callback(const char *pfx, char *msg)
-{
-#endif
-pfx = pfx;
-log_write(0, LOG_MAIN, "Berkeley DB error: %s", msg);
-}
-#endif
-
-
-
-
 /*************************************************
 *          Open and lock a database file         *
 *************************************************/
@@ -90,9 +70,8 @@ dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof, BOOL panic)
 {
 int rc, save_errno;
 BOOL read_only = flags == O_RDONLY;
-BOOL created = FALSE;
 flock_t lock_data;
-uschar dirname[256], filename[256];
+uschar dirname[PATHLEN], filename[PATHLEN];
 
 DEBUG(D_hints_lookup) acl_level++;
 
@@ -112,17 +91,18 @@ exists, there is no error. */
 snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
 snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name);
 
+priv_drop_temp(exim_uid, exim_gid);
 if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0)
   {
-  created = TRUE;
   (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic);
   dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE);
   }
+priv_restore();
 
 if (dbblock->lockfd < 0)
   {
   log_write(0, LOG_MAIN, "%s",
-    string_open_failed(errno, "database lock file %s", filename));
+    string_open_failed("database lock file %s", filename));
   errno = 0;      /* Indicates locking failure */
   DEBUG(D_hints_lookup) acl_level--;
   return NULL;
@@ -166,60 +146,17 @@ it easy to pin this down, there are now debug statements on either side of the
 open call. */
 
 snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name);
-EXIM_DBOPEN(filename, dirname, flags, EXIMDB_MODE, &(dbblock->dbptr));
 
+priv_drop_temp(exim_uid, exim_gid);
+dbblock->dbptr = exim_dbopen(filename, dirname, flags, EXIMDB_MODE);
 if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR)
   {
   DEBUG(D_hints_lookup)
     debug_printf_indent("%s appears not to exist: trying to create\n", filename);
-  created = TRUE;
-  EXIM_DBOPEN(filename, dirname, flags|O_CREAT, EXIMDB_MODE, &(dbblock->dbptr));
+  dbblock->dbptr = exim_dbopen(filename, dirname, flags|O_CREAT, EXIMDB_MODE);
   }
-
 save_errno = errno;
-
-/* If we are running as root and this is the first access to the database, its
-files will be owned by root. We want them to be owned by exim. We detect this
-situation by noting above when we had to create the lock file or the database
-itself. Because the different dbm libraries use different extensions for their
-files, I don't know of any easier way of arranging this than scanning the
-directory for files with the appropriate base name. At least this deals with
-the lock file at the same time. Also, the directory will typically have only
-half a dozen files, so the scan will be quick.
-
-This code is placed here, before the test for successful opening, because there
-was a case when a file was created, but the DBM library still returned NULL
-because of some problem. It also sorts out the lock file if that was created
-but creation of the database file failed. */
-
-if (created && geteuid() == root_uid)
-  {
-  DIR * dd;
-  uschar *lastname = Ustrrchr(filename, '/') + 1;
-  int namelen = Ustrlen(name);
-
-  *lastname = 0;
-
-  if ((dd = exim_opendir(filename)))
-    for (struct dirent *ent; ent = readdir(dd); )
-      if (Ustrncmp(ent->d_name, name, namelen) == 0)
-       {
-       struct stat statbuf;
-       /* Filenames from readdir() are trusted,
-       so use a taint-nonchecking copy */
-       strcpy(CS lastname, CCS ent->d_name);
-       if (Ustat(filename, &statbuf) >= 0 && statbuf.st_uid != exim_uid)
-         {
-         DEBUG(D_hints_lookup)
-           debug_printf_indent("ensuring %s is owned by exim\n", filename);
-         if (exim_chown(filename, exim_uid, exim_gid))
-           DEBUG(D_hints_lookup)
-             debug_printf_indent("failed setting %s to owned by exim\n", filename);
-         }
-       }
-
-  closedir(dd);
-  }
+priv_restore();
 
 /* If the open has failed, return NULL, leaving errno set. If lof is TRUE,
 log the event - also for debugging - but debug only if the file just doesn't
@@ -227,12 +164,13 @@ exist. */
 
 if (!dbblock->dbptr)
   {
+  errno = save_errno;
   if (lof && save_errno != ENOENT)
-    log_write(0, LOG_MAIN, "%s", string_open_failed(save_errno, "DB file %s",
+    log_write(0, LOG_MAIN, "%s", string_open_failed("DB file %s",
         filename));
   else
     DEBUG(D_hints_lookup)
-      debug_printf_indent("%s\n", CS string_open_failed(save_errno, "DB file %s",
+      debug_printf_indent("%s\n", CS string_open_failed("DB file %s",
           filename));
   (void)close(dbblock->lockfd);
   errno = save_errno;
@@ -270,7 +208,7 @@ Returns:  nothing
 void
 dbfn_close(open_db *dbblock)
 {
-EXIM_DBCLOSE(dbblock->dbptr);
+exim_dbclose(dbblock->dbptr);
 (void)close(dbblock->lockfd);
 DEBUG(D_hints_lookup)
   { debug_printf_indent("closed hints database and lockfile\n"); acl_level--; }
@@ -301,36 +239,66 @@ Returns: a pointer to the retrieved record, or
 */
 
 void *
-dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length)
+dbfn_read_with_length(open_db * dbblock, const uschar * key, int * length)
 {
-void *yield;
+void * yield;
 EXIM_DATUM key_datum, result_datum;
 int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen, is_tainted(key));
+uschar * key_copy = store_get(klen, key);
+unsigned dlen;
 
 memcpy(key_copy, key, klen);
 
 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: key=%s\n", key);
 
-EXIM_DATUM_INIT(key_datum);         /* Some DBM libraries require the datum */
-EXIM_DATUM_INIT(result_datum);      /* to be cleared before use. */
-EXIM_DATUM_DATA(key_datum) = CS key_copy;
-EXIM_DATUM_SIZE(key_datum) = klen;
+exim_datum_init(&key_datum);         /* Some DBM libraries require the datum */
+exim_datum_init(&result_datum);      /* to be cleared before use. */
+exim_datum_data_set(&key_datum, key_copy);
+exim_datum_size_set(&key_datum, klen);
 
-if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL;
+if (!exim_dbget(dbblock->dbptr, &key_datum, &result_datum)) return NULL;
 
 /* Assume the data store could have been tainted.  Properly, we should
 store the taint status with the data. */
 
-yield = store_get(EXIM_DATUM_SIZE(result_datum), TRUE);
-memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum));
-if (length != NULL) *length = EXIM_DATUM_SIZE(result_datum);
+dlen = exim_datum_size_get(&result_datum);
+yield = store_get(dlen, GET_TAINTED);
+memcpy(yield, exim_datum_data_get(&result_datum), dlen);
+if (length) *length = dlen;
 
-EXIM_DATUM_FREE(result_datum);    /* Some DBM libs require freeing */
+exim_datum_free(&result_datum);    /* Some DBM libs require freeing */
 return yield;
 }
 
 
+/* Read a record.  If the length is not as expected then delete it, write
+an error log line, delete the record and return NULL.
+Use this for fixed-size records (so not retry or wait records).
+
+Arguments:
+  dbblock   a pointer to an open database block
+  key       the key of the record to be read
+  length    the expected record length
+
+Returns: a pointer to the retrieved record, or
+         NULL if the record is not found/bad
+*/
+
+void *
+dbfn_read_enforce_length(open_db * dbblock, const uschar * key, size_t length)
+{
+int rlen;
+void * yield = dbfn_read_with_length(dbblock, key, &rlen);
+
+if (yield)
+  {
+  if (rlen == length) return yield;
+  log_write(0, LOG_MAIN|LOG_PANIC, "Bad db record size for '%s'", key);
+  dbfn_delete(dbblock, key);
+  }
+return NULL;
+}
+
 
 /*************************************************
 *             Write to database file             *
@@ -353,20 +321,20 @@ dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length)
 EXIM_DATUM key_datum, value_datum;
 dbdata_generic *gptr = (dbdata_generic *)ptr;
 int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen, is_tainted(key));
+uschar * key_copy = store_get(klen, key);
 
 memcpy(key_copy, key, klen);
 gptr->time_stamp = time(NULL);
 
 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_write: key=%s\n", key);
 
-EXIM_DATUM_INIT(key_datum);         /* Some DBM libraries require the datum */
-EXIM_DATUM_INIT(value_datum);       /* to be cleared before use. */
-EXIM_DATUM_DATA(key_datum) = CS key_copy;
-EXIM_DATUM_SIZE(key_datum) = klen;
-EXIM_DATUM_DATA(value_datum) = CS ptr;
-EXIM_DATUM_SIZE(value_datum) = length;
-return EXIM_DBPUT(dbblock->dbptr, key_datum, value_datum);
+exim_datum_init(&key_datum);         /* Some DBM libraries require the datum */
+exim_datum_init(&value_datum);       /* to be cleared before use. */
+exim_datum_data_set(&key_datum, key_copy);
+exim_datum_size_set(&key_datum, klen);
+exim_datum_data_set(&value_datum, ptr);
+exim_datum_size_set(&value_datum, length);
+return exim_dbput(dbblock->dbptr, &key_datum, &value_datum);
 }
 
 
@@ -387,16 +355,16 @@ int
 dbfn_delete(open_db *dbblock, const uschar *key)
 {
 int klen = Ustrlen(key) + 1;
-uschar * key_copy = store_get(klen, is_tainted(key));
+uschar * key_copy = store_get(klen, key);
+EXIM_DATUM key_datum;
 
 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_delete: key=%s\n", key);
 
 memcpy(key_copy, key, klen);
-EXIM_DATUM key_datum;
-EXIM_DATUM_INIT(key_datum);         /* Some DBM libraries require clearing */
-EXIM_DATUM_DATA(key_datum) = CS key_copy;
-EXIM_DATUM_SIZE(key_datum) = klen;
-return EXIM_DBDEL(dbblock->dbptr, key_datum);
+exim_datum_init(&key_datum);         /* Some DBM libraries require clearing */
+exim_datum_data_set(&key_datum, key_copy);
+exim_datum_size_set(&key_datum, klen);
+return exim_dbdel(dbblock->dbptr, &key_datum);
 }
 
 
@@ -422,23 +390,22 @@ dbfn_scan(open_db *dbblock, BOOL start, EXIM_CURSOR **cursor)
 {
 EXIM_DATUM key_datum, value_datum;
 uschar *yield;
-value_datum = value_datum;    /* dummy; not all db libraries use this */
 
 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_scan\n");
 
 /* Some dbm require an initialization */
 
-if (start) EXIM_DBCREATE_CURSOR(dbblock->dbptr, cursor);
+if (start) *cursor = exim_dbcreate_cursor(dbblock->dbptr);
 
-EXIM_DATUM_INIT(key_datum);         /* Some DBM libraries require the datum */
-EXIM_DATUM_INIT(value_datum);       /* to be cleared before use. */
+exim_datum_init(&key_datum);         /* Some DBM libraries require the datum */
+exim_datum_init(&value_datum);       /* to be cleared before use. */
 
-yield = (EXIM_DBSCAN(dbblock->dbptr, key_datum, value_datum, start, *cursor))?
-  US EXIM_DATUM_DATA(key_datum) : NULL;
+yield = exim_dbscan(dbblock->dbptr, &key_datum, &value_datum, start, *cursor)
+  ? US exim_datum_data_get(&key_datum) : NULL;
 
 /* Some dbm require a termination */
 
-if (!yield) EXIM_DBDELETE_CURSOR(*cursor);
+if (!yield) exim_dbdelete_cursor(*cursor);
 return yield;
 }
 
@@ -523,7 +490,7 @@ while (Ufgets(buffer, 256, stdin) != NULL)
     {
     count = Uatoi(cmd);
     while (isdigit((uschar)*cmd)) cmd++;
-    while (isspace((uschar)*cmd)) cmd++;
+    Uskip_whitespace(&cmd);
     }
 
   if (Ustrncmp(cmd, "open", 4) == 0)
@@ -531,7 +498,7 @@ while (Ufgets(buffer, 256, stdin) != NULL)
     int i;
     open_db *odb;
     uschar *s = cmd + 4;
-    while (isspace((uschar)*s)) s++;
+    Uskip_whitespace(&s);
 
     for (i = 0; i < max_db; i++)
       if (dbblock[i].dbptr == NULL) break;
@@ -567,8 +534,7 @@ while (Ufgets(buffer, 256, stdin) != NULL)
   else if (Ustrncmp(cmd, "write", 5) == 0)
     {
     int rc = 0;
-    uschar *key = cmd + 5;
-    uschar *data;
+    uschar * key = cmd + 5, * data;
 
     if (current < 0)
       {
@@ -576,11 +542,11 @@ while (Ufgets(buffer, 256, stdin) != NULL)
       continue;
       }
 
-    while (isspace((uschar)*key)) key++;
+    Uskip_whitespace(&key);
     data = key;
-    while (*data != 0 && !isspace((uschar)*data)) data++;
-    *data++ = 0;
-    while (isspace((uschar)*data)) data++;
+    Uskip_nonwhite(&data);
+    *data++ = '\0';
+    Uskip_whitespace(&data);
 
     dbwait = (dbdata_wait *)(&structbuffer);
     Ustrcpy(dbwait->text, data);
@@ -595,13 +561,13 @@ while (Ufgets(buffer, 256, stdin) != NULL)
 
   else if (Ustrncmp(cmd, "read", 4) == 0)
     {
-    uschar *key = cmd + 4;
+    uschar * key = cmd + 4;
     if (current < 0)
       {
       printf("No current database\n");
       continue;
       }
-    while (isspace((uschar)*key)) key++;
+    Uskip_whitespace(&key);
     start = clock();
     while (count-- > 0)
       dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock+ current, key, NULL);
@@ -611,13 +577,13 @@ while (Ufgets(buffer, 256, stdin) != NULL)
 
   else if (Ustrncmp(cmd, "delete", 6) == 0)
     {
-    uschar *key = cmd + 6;
+    uschar * key = cmd + 6;
     if (current < 0)
       {
       printf("No current database\n");
       continue;
       }
-    while (isspace((uschar)*key)) key++;
+    Uskip_whitespace(&key);
     dbfn_delete(dbblock + current, key);
     }
 
@@ -647,8 +613,8 @@ while (Ufgets(buffer, 256, stdin) != NULL)
 
   else if (Ustrncmp(cmd, "close", 5) == 0)
     {
-    uschar *s = cmd + 5;
-    while (isspace((uschar)*s)) s++;
+    uschar * s = cmd + 5;
+    Uskip_whitespace(&s);
     i = Uatoi(s);
     if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n"); else
       {
@@ -662,8 +628,8 @@ while (Ufgets(buffer, 256, stdin) != NULL)
 
   else if (Ustrncmp(cmd, "file", 4) == 0)
     {
-    uschar *s = cmd + 4;
-    while (isspace((uschar)*s)) s++;
+    uschar * s = cmd + 4;
+    Uskip_whitespace(&s);
     i = Uatoi(s);
     if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n");
       else current = i;
@@ -715,3 +681,5 @@ return 0;
 #endif
 
 /* End of dbfn.c */
+/* vi: aw ai sw=2
+*/