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