xref: /PHP-8.4/ext/dba/dba_db4.c (revision 11accb5c)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Marcus Boerger <helly@php.net>                              |
14    |          Sascha Schumann <sascha@schumann.cx>                        |
15    +----------------------------------------------------------------------+
16  */
17 
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21 
22 #include "php.h"
23 
24 #ifdef DBA_DB4
25 #include "php_db4.h"
26 #include <sys/stat.h>
27 
28 #include <string.h>
29 #ifdef DB4_INCLUDE_FILE
30 #include DB4_INCLUDE_FILE
31 #else
32 #include <db.h>
33 #endif
34 
php_dba_db4_errcall_fcn(const DB_ENV * dbenv,const char * errpfx,const char * msg)35 static void php_dba_db4_errcall_fcn(
36 #if (DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3))
37 	const DB_ENV *dbenv,
38 #endif
39 	const char *errpfx, const char *msg)
40 {
41 
42 #if (DB_VERSION_MAJOR == 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR == 8))
43 /* Bug 51086, Berkeley DB 4.8.26 */
44 /* This code suppresses a BDB 4.8+ error message, thus keeping PHP test compatibility */
45 	{
46 		const char *function = get_active_function_name();
47 		if (function && (!strcmp(function,"dba_popen") || !strcmp(function,"dba_open"))
48 			&& (!strncmp(msg, "fop_read_meta", sizeof("fop_read_meta")-1)
49 				|| !strncmp(msg, "BDB0004 fop_read_meta", sizeof("BDB0004 fop_read_meta")-1))) {
50 			return;
51 		}
52 	}
53 #endif
54 
55 	php_error_docref(NULL, E_NOTICE, "%s%s", errpfx?errpfx:"", msg);
56 }
57 
58 typedef struct {
59 	DB *dbp;
60 	DBC *cursor;
61 } dba_db4_data;
62 
DBA_OPEN_FUNC(db4)63 DBA_OPEN_FUNC(db4)
64 {
65 	DB *dbp = NULL;
66 	DBTYPE type;
67 	int gmode = 0, err;
68 	int filemode = info->file_permission;
69 	struct stat check_stat;
70 	int s = VCWD_STAT(ZSTR_VAL(info->path), &check_stat);
71 
72 #if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR <= 7)  /* Bug 51086 */
73 	if (!s && !check_stat.st_size) {
74 		info->mode = DBA_TRUNC; /* force truncate */
75 	}
76 
77 	type = info->mode == DBA_READER ? DB_UNKNOWN :
78 		info->mode == DBA_TRUNC ? DB_BTREE :
79 		s ? DB_BTREE : DB_UNKNOWN;
80 
81 	gmode = info->mode == DBA_READER ? DB_RDONLY :
82 		(info->mode == DBA_CREAT && s) ? DB_CREATE :
83 		(info->mode == DBA_CREAT && !s) ? 0 :
84 		info->mode == DBA_WRITER ? 0         :
85 		info->mode == DBA_TRUNC ? DB_CREATE | DB_TRUNCATE : -1;
86 #else
87 	if (!s && !check_stat.st_size) {
88 		info->mode = DBA_CREAT; /* force creation */
89 	}
90 
91 	type = info->mode == DBA_READER ? DB_UNKNOWN :
92 		(info->mode == DBA_TRUNC || info->mode == DBA_CREAT) ? DB_BTREE :
93 		s ? DB_BTREE : DB_UNKNOWN;
94 
95 	gmode = info->mode == DBA_READER ? DB_RDONLY :
96 		info->mode == DBA_CREAT ? DB_CREATE :
97 		info->mode == DBA_WRITER ? 0         :
98 		info->mode == DBA_TRUNC ? DB_CREATE | DB_TRUNCATE : -1;
99 #endif
100 
101 	if (gmode == -1) {
102 		return FAILURE; /* not possible */
103 	}
104 
105 	if (info->flags & DBA_PERSISTENT) {
106 		gmode |= DB_THREAD;
107 	}
108 
109 	if ((err=db_create(&dbp, NULL, 0)) == 0) {
110 	    dbp->set_errcall(dbp, php_dba_db4_errcall_fcn);
111 	    if (
112 #if (DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1))
113 			(err=dbp->open(dbp, 0, ZSTR_VAL(info->path), NULL, type, gmode, filemode)) == 0) {
114 #else
115 			(err=dbp->open(dbp, ZSTR_VAL(info->path), NULL, type, gmode, filemode)) == 0) {
116 #endif
117 			dba_db4_data *data;
118 
119 			data = pemalloc(sizeof(*data), info->flags&DBA_PERSISTENT);
120 			data->dbp = dbp;
121 			data->cursor = NULL;
122 			info->dbf = data;
123 
124 			return SUCCESS;
125 		} else {
126 			dbp->close(dbp, 0);
127 			*error = db_strerror(err);
128 		}
129 	} else {
130 		*error = db_strerror(err);
131 	}
132 
133 	return FAILURE;
134 }
135 
136 DBA_CLOSE_FUNC(db4)
137 {
138 	dba_db4_data *dba = info->dbf;
139 
140 	if (dba->cursor) dba->cursor->c_close(dba->cursor);
141 	dba->dbp->close(dba->dbp, 0);
142 	pefree(dba, info->flags&DBA_PERSISTENT);
143 }
144 
145 DBA_FETCH_FUNC(db4)
146 {
147 	dba_db4_data *dba = info->dbf;
148 	DBT gval;
149 	DBT gkey;
150 	zend_string *fetched_value = NULL;
151 
152 	memset(&gkey, 0, sizeof(gkey));
153 	gkey.data = ZSTR_VAL(key);
154 	gkey.size = ZSTR_LEN(key);
155 
156 	memset(&gval, 0, sizeof(gval));
157 	if (info->flags & DBA_PERSISTENT) {
158 		gval.flags |= DB_DBT_MALLOC;
159 	}
160 	if (!dba->dbp->get(dba->dbp, NULL, &gkey, &gval, 0)) {
161 		fetched_value = zend_string_init(gval.data, gval.size, /* persistent */ false);
162 		if (info->flags & DBA_PERSISTENT) {
163 			free(gval.data);
164 		}
165 	}
166 	return fetched_value;
167 }
168 
169 DBA_UPDATE_FUNC(db4)
170 {
171 	dba_db4_data *dba = info->dbf;
172 	DBT gval;
173 	DBT gkey;
174 
175 	memset(&gkey, 0, sizeof(gkey));
176 	gkey.data = ZSTR_VAL(key);
177 	gkey.size = ZSTR_LEN(key);
178 
179 	memset(&gval, 0, sizeof(gval));
180 	gval.data = ZSTR_VAL(val);
181 	gval.size = ZSTR_LEN(val);
182 
183 	if (!dba->dbp->put(dba->dbp, NULL, &gkey, &gval,
184 				mode == 1 ? DB_NOOVERWRITE : 0)) {
185 		return SUCCESS;
186 	}
187 	return FAILURE;
188 }
189 
190 DBA_EXISTS_FUNC(db4)
191 {
192 	dba_db4_data *dba = info->dbf;
193 	DBT gval;
194 	DBT gkey;
195 
196 	memset(&gkey, 0, sizeof(gkey));
197 	gkey.data = ZSTR_VAL(key);
198 	gkey.size = ZSTR_LEN(key);
199 
200 	memset(&gval, 0, sizeof(gval));
201 
202 	if (info->flags & DBA_PERSISTENT) {
203 		gval.flags |= DB_DBT_MALLOC;
204 	}
205 
206 	if (!dba->dbp->get(dba->dbp, NULL, &gkey, &gval, 0)) {
207 		if (info->flags & DBA_PERSISTENT) {
208 			free(gval.data);
209 		}
210 		return SUCCESS;
211 	}
212 	return FAILURE;
213 }
214 
215 DBA_DELETE_FUNC(db4)
216 {
217 	dba_db4_data *dba = info->dbf;
218 	DBT gkey;
219 
220 	memset(&gkey, 0, sizeof(gkey));
221 	gkey.data = ZSTR_VAL(key);
222 	gkey.size = ZSTR_LEN(key);
223 
224 	return dba->dbp->del(dba->dbp, NULL, &gkey, 0) ? FAILURE : SUCCESS;
225 }
226 
227 DBA_FIRSTKEY_FUNC(db4)
228 {
229 	dba_db4_data *dba = info->dbf;
230 
231 	if (dba->cursor) {
232 		dba->cursor->c_close(dba->cursor);
233 	}
234 
235 	dba->cursor = NULL;
236 	if (dba->dbp->cursor(dba->dbp, NULL, &dba->cursor, 0) != 0) {
237 		return NULL;
238 	}
239 
240 	return dba_nextkey_db4(info);
241 }
242 
243 DBA_NEXTKEY_FUNC(db4)
244 {
245 	dba_db4_data *dba = info->dbf;
246 	DBT gkey, gval;
247 	zend_string *key = NULL;
248 
249 	memset(&gkey, 0, sizeof(gkey));
250 	memset(&gval, 0, sizeof(gval));
251 
252 	if (info->flags & DBA_PERSISTENT) {
253 		gkey.flags |= DB_DBT_MALLOC;
254 		gval.flags |= DB_DBT_MALLOC;
255 	}
256 	if (dba->cursor && dba->cursor->c_get(dba->cursor, &gkey, &gval, DB_NEXT) == 0) {
257 		if (gkey.data) {
258 			key = zend_string_init(gkey.data, gkey.size, /* persistent */ false);
259 		}
260 		if (info->flags & DBA_PERSISTENT) {
261 			if (gkey.data) {
262 				free(gkey.data);
263 			}
264 			if (gval.data) {
265 				free(gval.data);
266 			}
267 		}
268 	}
269 
270 	return key;
271 }
272 
273 DBA_OPTIMIZE_FUNC(db4)
274 {
275 	return SUCCESS;
276 }
277 
278 DBA_SYNC_FUNC(db4)
279 {
280 	dba_db4_data *dba = info->dbf;
281 
282 	return dba->dbp->sync(dba->dbp, 0) ? FAILURE : SUCCESS;
283 }
284 
285 DBA_INFO_FUNC(db4)
286 {
287 	return estrdup(DB_VERSION_STRING);
288 }
289 
290 #endif
291