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