1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2011-2021 Free Software Foundation, Inc.
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 3 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General
15    Public License along with this library.  If not, see
16    <http://www.gnu.org/licenses/>. */
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <stdlib.h>
22 #include <string.h>
23 #include <mailutils/types.h>
24 #include <mailutils/dbm.h>
25 #include <mailutils/util.h>
26 #include <mailutils/errno.h>
27 #include <mailutils/error.h>
28 #include <mailutils/stream.h>
29 #include "mudbm.h"
30 
31 #if defined(WITH_GDBM)
32 #include <gdbm.h>
33 
34 struct gdbm_descr
35 {
36   GDBM_FILE file;           /* GDBM file */
37   datum prev;               /* Previous key for sequential access */
38 };
39 
40 static int
_gdbm_file_safety(mu_dbm_file_t db,int mode,uid_t owner)41 _gdbm_file_safety (mu_dbm_file_t db, int mode, uid_t owner)
42 {
43   return mu_file_safety_check (db->db_name, mode, owner, NULL);
44 }
45 
46 int
_gdbm_get_fd(mu_dbm_file_t db,int * pag,int * dir)47 _gdbm_get_fd (mu_dbm_file_t db, int *pag, int *dir)
48 {
49   struct gdbm_descr *gd = db->db_descr;
50   *pag = gdbm_fdesc (gd->file);
51   if (dir)
52     *dir = *pag;
53   return 0;
54 }
55 
56 static int
_gdbm_open(mu_dbm_file_t db,int flags,int mode)57 _gdbm_open (mu_dbm_file_t db, int flags, int mode)
58 {
59   int f;
60   struct gdbm_descr *gd;
61   GDBM_FILE file;
62 
63   switch (flags)
64     {
65     case MU_STREAM_CREAT:
66       f = GDBM_NEWDB;
67       break;
68 
69     case MU_STREAM_READ:
70       f = GDBM_READER;
71       break;
72 
73     case MU_STREAM_RDWR:
74       f = GDBM_WRCREAT;
75       break;
76 
77     default:
78       return EINVAL;
79     }
80   file = gdbm_open (db->db_name, 512, f, mode, NULL);
81   if (!file)
82     return MU_ERR_FAILURE;
83   gd = calloc (1, sizeof (*gd));
84   gd->file = file;
85   db->db_descr = gd;
86   return 0;
87 }
88 
89 static int
_gdbm_close(mu_dbm_file_t db)90 _gdbm_close (mu_dbm_file_t db)
91 {
92   if (db->db_descr)
93     {
94       struct gdbm_descr *gd = db->db_descr;
95       gdbm_close (gd->file);
96       free (gd);
97       db->db_descr = NULL;
98     }
99   return 0;
100 }
101 
102 static int
_gdbm_conv_datum(mu_dbm_file_t db,struct mu_dbm_datum * ret,datum content,int copy)103 _gdbm_conv_datum (mu_dbm_file_t db, struct mu_dbm_datum *ret, datum content,
104 		  int copy)
105 {
106   if (copy)
107     {
108       ret->mu_dptr = malloc (content.dsize);
109       if (!ret->mu_dptr)
110 	return errno;
111       memcpy (ret->mu_dptr, content.dptr, content.dsize);
112     }
113   else
114     {
115       ret->mu_dptr = content.dptr;
116     }
117   ret->mu_dsize = content.dsize;
118   ret->mu_sys = db->db_sys;
119   return 0;
120 }
121 
122 static int
_gdbm_fetch(mu_dbm_file_t db,struct mu_dbm_datum const * key,struct mu_dbm_datum * ret)123 _gdbm_fetch (mu_dbm_file_t db, struct mu_dbm_datum const *key,
124 	     struct mu_dbm_datum *ret)
125 {
126   struct gdbm_descr *gd = db->db_descr;
127   int rc;
128   datum keydat, content;
129 
130   keydat.dptr = key->mu_dptr;
131   keydat.dsize = key->mu_dsize;
132   gdbm_errno = 0;
133   content = gdbm_fetch (gd->file, keydat);
134   if (content.dptr == NULL)
135     {
136       if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
137 	return MU_ERR_NOENT;
138       else
139 	{
140 	  db->db_errno.n = gdbm_errno;
141 	  return MU_ERR_FAILURE;
142 	}
143     }
144   mu_dbm_datum_free (ret);
145   rc = _gdbm_conv_datum (db, ret, content, 1);
146   if (rc)
147     {
148       free (content.dptr);
149       return rc;
150     }
151   return 0;
152 }
153 
154 static int
_gdbm_store(mu_dbm_file_t db,struct mu_dbm_datum const * key,struct mu_dbm_datum const * contents,int replace)155 _gdbm_store (mu_dbm_file_t db,
156 	     struct mu_dbm_datum const *key,
157 	     struct mu_dbm_datum const *contents,
158 	     int replace)
159 {
160   struct gdbm_descr *gd = db->db_descr;
161   datum keydat, condat;
162 
163   keydat.dptr = key->mu_dptr;
164   keydat.dsize = key->mu_dsize;
165   condat.dptr = contents->mu_dptr;
166   condat.dsize = contents->mu_dsize;
167   switch (gdbm_store (gd->file, keydat, condat, replace))
168     {
169     case 0:
170       break;
171 
172     case 1:
173       return MU_ERR_EXISTS;
174 
175     case -1:
176       db->db_errno.n = gdbm_errno;
177       return MU_ERR_FAILURE;
178     }
179   return 0;
180 }
181 
182 static int
_gdbm_delete(mu_dbm_file_t db,struct mu_dbm_datum const * key)183 _gdbm_delete (mu_dbm_file_t db, struct mu_dbm_datum const *key)
184 {
185   struct gdbm_descr *gd = db->db_descr;
186   datum keydat;
187 
188   keydat.dptr = key->mu_dptr;
189   keydat.dsize = key->mu_dsize;
190   gdbm_errno = 0;
191   if (gdbm_delete (gd->file, keydat))
192     {
193       if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
194 	return MU_ERR_NOENT;
195       else
196 	{
197 	  db->db_errno.n = gdbm_errno;
198 	  return MU_ERR_FAILURE;
199 	}
200     }
201   return 0;
202 }
203 
204 static int
_gdbm_firstkey(mu_dbm_file_t db,struct mu_dbm_datum * ret)205 _gdbm_firstkey (mu_dbm_file_t db, struct mu_dbm_datum *ret)
206 {
207   struct gdbm_descr *gd = db->db_descr;
208   int rc;
209   datum key = gdbm_firstkey (gd->file);
210   if (key.dptr == NULL)
211     {
212       if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
213 	return MU_ERR_NOENT;
214       else
215 	{
216 	  db->db_errno.n = gdbm_errno;
217 	  return MU_ERR_FAILURE;
218 	}
219     }
220   mu_dbm_datum_free (ret);
221   rc = _gdbm_conv_datum (db, ret, key, 0);
222   if (rc)
223     {
224       free (key.dptr);
225       return rc;
226     }
227   free (gd->prev.dptr);
228   gd->prev = key;
229   return 0;
230 }
231 
232 static int
_gdbm_nextkey(mu_dbm_file_t db,struct mu_dbm_datum * ret)233 _gdbm_nextkey (mu_dbm_file_t db, struct mu_dbm_datum *ret)
234 {
235   struct gdbm_descr *gd = db->db_descr;
236   int rc;
237   datum key;
238 
239   if (!gd->prev.dptr)
240     return MU_ERR_NOENT;
241   key = gdbm_nextkey (gd->file, gd->prev);
242   if (key.dptr == NULL)
243     {
244       if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
245 	return MU_ERR_NOENT;
246       else
247 	{
248 	  db->db_errno.n = gdbm_errno;
249 	  return MU_ERR_FAILURE;
250 	}
251     }
252   mu_dbm_datum_free (ret);
253   rc = _gdbm_conv_datum (db, ret, key, 0);
254   if (rc)
255     {
256       free (key.dptr);
257       return rc;
258     }
259   free (gd->prev.dptr);
260   gd->prev = key;
261   return 0;
262 }
263 
264 static void
_gdbm_datum_free(struct mu_dbm_datum * datum)265 _gdbm_datum_free (struct mu_dbm_datum *datum)
266 {
267   free (datum->mu_dptr);
268 }
269 
270 static char const *
_gdbm_strerror(mu_dbm_file_t db)271 _gdbm_strerror (mu_dbm_file_t db)
272 {
273   return gdbm_strerror (db->db_errno.n);
274 }
275 
276 struct mu_dbm_impl _mu_dbm_gdbm = {
277   "gdbm",
278   _gdbm_file_safety,
279   _gdbm_get_fd,
280   _gdbm_open,
281   _gdbm_close,
282   _gdbm_fetch,
283   _gdbm_store,
284   _gdbm_delete,
285   _gdbm_firstkey,
286   _gdbm_nextkey,
287   _gdbm_datum_free,
288   _gdbm_strerror
289 };
290 #endif
291