xref: /PHP-8.1/ext/dba/dba_db4.c (revision 01b3fc03)
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 #if 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 #define DB4_DATA dba_db4_data *dba = info->dbf
59 #define DB4_GKEY \
60 	DBT gkey; \
61 	memset(&gkey, 0, sizeof(gkey)); \
62 	gkey.data = (char *) key; gkey.size = keylen
63 
64 typedef struct {
65 	DB *dbp;
66 	DBC *cursor;
67 } dba_db4_data;
68 
DBA_OPEN_FUNC(db4)69 DBA_OPEN_FUNC(db4)
70 {
71 	DB *dbp = NULL;
72 	DBTYPE type;
73 	int gmode = 0, err;
74 	int filemode = 0644;
75 	struct stat check_stat;
76 	int s = VCWD_STAT(info->path, &check_stat);
77 
78 #if (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR <= 7)  /* Bug 51086 */
79 	if (!s && !check_stat.st_size) {
80 		info->mode = DBA_TRUNC; /* force truncate */
81 	}
82 
83 	type = info->mode == DBA_READER ? DB_UNKNOWN :
84 		info->mode == DBA_TRUNC ? DB_BTREE :
85 		s ? DB_BTREE : DB_UNKNOWN;
86 
87 	gmode = info->mode == DBA_READER ? DB_RDONLY :
88 		(info->mode == DBA_CREAT && s) ? DB_CREATE :
89 		(info->mode == DBA_CREAT && !s) ? 0 :
90 		info->mode == DBA_WRITER ? 0         :
91 		info->mode == DBA_TRUNC ? DB_CREATE | DB_TRUNCATE : -1;
92 #else
93 	if (!s && !check_stat.st_size) {
94 		info->mode = DBA_CREAT; /* force creation */
95 	}
96 
97 	type = info->mode == DBA_READER ? DB_UNKNOWN :
98 		(info->mode == DBA_TRUNC || info->mode == DBA_CREAT) ? DB_BTREE :
99 		s ? DB_BTREE : DB_UNKNOWN;
100 
101 	gmode = info->mode == DBA_READER ? DB_RDONLY :
102 		info->mode == DBA_CREAT ? DB_CREATE :
103 		info->mode == DBA_WRITER ? 0         :
104 		info->mode == DBA_TRUNC ? DB_CREATE | DB_TRUNCATE : -1;
105 #endif
106 
107 	if (gmode == -1) {
108 		return FAILURE; /* not possible */
109 	}
110 
111 	if (info->flags & DBA_PERSISTENT) {
112 		gmode |= DB_THREAD;
113 	}
114 
115 	if (info->argc > 0) {
116 		filemode = zval_get_long(&info->argv[0]);
117 	}
118 
119 	if ((err=db_create(&dbp, NULL, 0)) == 0) {
120 	    dbp->set_errcall(dbp, php_dba_db4_errcall_fcn);
121 	    if (
122 #if (DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 1))
123 			(err=dbp->open(dbp, 0, info->path, NULL, type, gmode, filemode)) == 0) {
124 #else
125 			(err=dbp->open(dbp, info->path, NULL, type, gmode, filemode)) == 0) {
126 #endif
127 			dba_db4_data *data;
128 
129 			data = pemalloc(sizeof(*data), info->flags&DBA_PERSISTENT);
130 			data->dbp = dbp;
131 			data->cursor = NULL;
132 			info->dbf = data;
133 
134 			return SUCCESS;
135 		} else {
136 			dbp->close(dbp, 0);
137 			*error = db_strerror(err);
138 		}
139 	} else {
140 		*error = db_strerror(err);
141 	}
142 
143 	return FAILURE;
144 }
145 
146 DBA_CLOSE_FUNC(db4)
147 {
148 	DB4_DATA;
149 
150 	if (dba->cursor) dba->cursor->c_close(dba->cursor);
151 	dba->dbp->close(dba->dbp, 0);
152 	pefree(dba, info->flags&DBA_PERSISTENT);
153 }
154 
155 DBA_FETCH_FUNC(db4)
156 {
157 	DBT gval;
158 	char *new = NULL;
159 	DB4_DATA;
160 	DB4_GKEY;
161 
162 	memset(&gval, 0, sizeof(gval));
163 	if (info->flags & DBA_PERSISTENT) {
164 		gval.flags |= DB_DBT_MALLOC;
165 	}
166 	if (!dba->dbp->get(dba->dbp, NULL, &gkey, &gval, 0)) {
167 		if (newlen) *newlen = gval.size;
168 		new = estrndup(gval.data, gval.size);
169 		if (info->flags & DBA_PERSISTENT) {
170 			free(gval.data);
171 		}
172 	}
173 	return new;
174 }
175 
176 DBA_UPDATE_FUNC(db4)
177 {
178 	DBT gval;
179 	DB4_DATA;
180 	DB4_GKEY;
181 
182 	memset(&gval, 0, sizeof(gval));
183 	gval.data = (char *) val;
184 	gval.size = vallen;
185 
186 	if (!dba->dbp->put(dba->dbp, NULL, &gkey, &gval,
187 				mode == 1 ? DB_NOOVERWRITE : 0)) {
188 		return SUCCESS;
189 	}
190 	return FAILURE;
191 }
192 
193 DBA_EXISTS_FUNC(db4)
194 {
195 	DBT gval;
196 	DB4_DATA;
197 	DB4_GKEY;
198 
199 	memset(&gval, 0, sizeof(gval));
200 
201 	if (info->flags & DBA_PERSISTENT) {
202 		gval.flags |= DB_DBT_MALLOC;
203 	}
204 
205 	if (!dba->dbp->get(dba->dbp, NULL, &gkey, &gval, 0)) {
206 		if (info->flags & DBA_PERSISTENT) {
207 			free(gval.data);
208 		}
209 		return SUCCESS;
210 	}
211 	return FAILURE;
212 }
213 
214 DBA_DELETE_FUNC(db4)
215 {
216 	DB4_DATA;
217 	DB4_GKEY;
218 
219 	return dba->dbp->del(dba->dbp, NULL, &gkey, 0) ? FAILURE : SUCCESS;
220 }
221 
222 DBA_FIRSTKEY_FUNC(db4)
223 {
224 	DB4_DATA;
225 
226 	if (dba->cursor) {
227 		dba->cursor->c_close(dba->cursor);
228 	}
229 
230 	dba->cursor = NULL;
231 	if (dba->dbp->cursor(dba->dbp, NULL, &dba->cursor, 0) != 0) {
232 		return NULL;
233 	}
234 
235 	/* we should introduce something like PARAM_PASSTHRU... */
236 	return dba_nextkey_db4(info, newlen);
237 }
238 
239 DBA_NEXTKEY_FUNC(db4)
240 {
241 	DB4_DATA;
242 	DBT gkey, gval;
243 	char *nkey = NULL;
244 
245 	memset(&gkey, 0, sizeof(gkey));
246 	memset(&gval, 0, sizeof(gval));
247 
248 	if (info->flags & DBA_PERSISTENT) {
249 		gkey.flags |= DB_DBT_MALLOC;
250 		gval.flags |= DB_DBT_MALLOC;
251 	}
252 	if (dba->cursor && dba->cursor->c_get(dba->cursor, &gkey, &gval, DB_NEXT) == 0) {
253 		if (gkey.data) {
254 			nkey = estrndup(gkey.data, gkey.size);
255 			if (newlen) *newlen = gkey.size;
256 		}
257 		if (info->flags & DBA_PERSISTENT) {
258 			if (gkey.data) {
259 				free(gkey.data);
260 			}
261 			if (gval.data) {
262 				free(gval.data);
263 			}
264 		}
265 	}
266 
267 	return nkey;
268 }
269 
270 DBA_OPTIMIZE_FUNC(db4)
271 {
272 	return SUCCESS;
273 }
274 
275 DBA_SYNC_FUNC(db4)
276 {
277 	DB4_DATA;
278 
279 	return dba->dbp->sync(dba->dbp, 0) ? FAILURE : SUCCESS;
280 }
281 
282 DBA_INFO_FUNC(db4)
283 {
284 	return estrdup(DB_VERSION_STRING);
285 }
286 
287 #endif
288