1 /*-
2  * Copyright (c) 1996, 2020 Oracle and/or its affiliates.  All rights reserved.
3  *
4  * See the file LICENSE for license information.
5  *
6  * $Id$
7  */
8 
9 #include "db_config.h"
10 
11 #include "db_int.h"
12 #include "dbinc/db_page.h"
13 #include "dbinc/db_am.h"
14 #include "dbinc/heap.h"
15 
16 /*
17  * __db_ret --
18  *	Build return DBT.
19  *
20  * PUBLIC: int __db_ret __P((DBC *,
21  * PUBLIC:    PAGE *, u_int32_t, DBT *, void **, u_int32_t *));
22  */
23 int
__db_ret(dbc,h,indx,dbt,memp,memsize)24 __db_ret(dbc, h, indx, dbt, memp, memsize)
25 	DBC *dbc;
26 	PAGE *h;
27 	u_int32_t indx;
28 	DBT *dbt;
29 	void **memp;
30 	u_int32_t *memsize;
31 {
32 	BBLOB bl;
33 	BKEYDATA *bk;
34 	BOVERFLOW *bo;
35 	DB *dbp;
36 	ENV *env;
37 	HBLOB hblob;
38 	HEAPBLOBHDR bhdr;
39 	HEAPHDR *hdr;
40 	db_seq_t blob_id;
41 	int ret;
42 	HOFFPAGE ho;
43 	off_t blob_size;
44 	u_int32_t len;
45 	u_int8_t *hk;
46 	void *data;
47 
48 	if (F_ISSET(dbt, DB_DBT_READONLY))
49 		return (0);
50 	ret = 0;
51 	dbp = dbc->dbp;
52 	env = dbp->env;
53 
54 	switch (TYPE(h)) {
55 	case P_HASH_UNSORTED:
56 	case P_HASH:
57 		hk = P_ENTRY(dbp, h, indx);
58 		if (HPAGE_PTYPE(hk) == H_OFFPAGE) {
59 			memcpy(&ho, hk, sizeof(HOFFPAGE));
60 			return (__db_goff(dbc, dbt,
61 			    ho.tlen, ho.pgno, memp, memsize));
62 		} else if (HPAGE_PTYPE(hk) == H_BLOB) {
63 			/* Get the record instead of the blob item. */
64 			if (F_ISSET(dbt, DB_DBT_BLOB_REC)) {
65 				data = P_ENTRY(dbp, h, indx);
66 				len = HBLOB_SIZE;
67 				break;
68 			}
69 			memcpy(&hblob, hk, HBLOB_SIZE);
70 			blob_id = (db_seq_t)hblob.id;
71 			GET_BLOB_SIZE(env, hblob, blob_size, ret);
72 			if (ret != 0)
73 				return (ret);
74 			return (__blob_get(
75 			    dbc, dbt, blob_id, blob_size, memp, memsize));
76 		}
77 		len = LEN_HKEYDATA(dbp, h, dbp->pgsize, indx);
78 		data = HKEYDATA_DATA(hk);
79 		break;
80 	case P_HEAP:
81 		hdr = (HEAPHDR *)P_ENTRY(dbp, h, indx);
82 		if (F_ISSET(hdr,(HEAP_RECSPLIT | HEAP_RECFIRST)))
83 			return (__heapc_gsplit(dbc, dbt, memp, memsize));
84 		else if (F_ISSET(hdr, HEAP_RECBLOB)) {
85 			/* Get the record instead of the blob item. */
86 			if (F_ISSET(dbt, DB_DBT_BLOB_REC)) {
87 				data = P_ENTRY(dbp, h, indx);
88 				len = HEAPBLOBREC_SIZE;
89 				break;
90 			}
91 			memcpy(&bhdr, hdr, HEAPBLOBREC_SIZE);
92 			blob_id = (db_seq_t)bhdr.id;
93 			GET_BLOB_SIZE(env, bhdr, blob_size, ret);
94 			if (ret != 0)
95 				return (ret);
96 			return (__blob_get(
97 			    dbc, dbt, blob_id, blob_size, memp, memsize));
98 		}
99 		len = hdr->size;
100 		data = (u_int8_t *)hdr + sizeof(HEAPHDR);
101 		break;
102 	case P_LBTREE:
103 	case P_LDUP:
104 	case P_LRECNO:
105 		bk = GET_BKEYDATA(dbp, h, indx);
106 		if (B_TYPE(bk->type) == B_OVERFLOW) {
107 			bo = (BOVERFLOW *)bk;
108 			return (__db_goff(dbc, dbt,
109 			    bo->tlen, bo->pgno, memp, memsize));
110 		} else if (B_TYPE(bk->type) == B_BLOB) {
111 			/* Get the record instead of the blob item. */
112 			if (F_ISSET(dbt, DB_DBT_BLOB_REC)) {
113 				data = P_ENTRY(dbp, h, indx);
114 				len = BBLOB_SIZE;
115 				break;
116 			}
117 			memcpy(&bl, bk, BBLOB_SIZE);
118 			blob_id = (db_seq_t)bl.id;
119 			GET_BLOB_SIZE(env, bl, blob_size, ret);
120 			if (ret != 0)
121 				return (ret);
122 			return (__blob_get(
123 			    dbc, dbt, blob_id, blob_size, memp, memsize));
124 		}
125 		len = bk->len;
126 		data = bk->data;
127 		break;
128 	default:
129 		return (__db_pgfmt(dbp->env, h->pgno));
130 	}
131 
132 	return (__db_retcopy(dbp->env, dbt, data, len, memp, memsize));
133 }
134 
135 /*
136  * __db_retcopy --
137  *	Copy the returned data into the user's DBT, handling special flags.
138  *
139  * PUBLIC: int __db_retcopy __P((ENV *, DBT *,
140  * PUBLIC:    void *, u_int32_t, void **, u_int32_t *));
141  */
142 int
__db_retcopy(env,dbt,data,len,memp,memsize)143 __db_retcopy(env, dbt, data, len, memp, memsize)
144 	ENV *env;
145 	DBT *dbt;
146 	void *data;
147 	u_int32_t len;
148 	void **memp;
149 	u_int32_t *memsize;
150 {
151 	int ret;
152 
153 	if (F_ISSET(dbt, DB_DBT_READONLY))
154 		return (0);
155 	ret = 0;
156 
157 	/* If returning a partial record, reset the length. */
158 	if (F_ISSET(dbt, DB_DBT_PARTIAL)) {
159 		data = (u_int8_t *)data + dbt->doff;
160 		if (len > dbt->doff) {
161 			len -= dbt->doff;
162 			if (len > dbt->dlen)
163 				len = dbt->dlen;
164 		} else
165 			len = 0;
166 	}
167 
168 	/*
169 	 * Allocate memory to be owned by the application: DB_DBT_MALLOC,
170 	 * DB_DBT_REALLOC.
171 	 *
172 	 * !!!
173 	 * We always allocate memory, even if we're copying out 0 bytes. This
174 	 * guarantees consistency, i.e., the application can always free memory
175 	 * without concern as to how many bytes of the record were requested.
176 	 *
177 	 * Use the memory specified by the application: DB_DBT_USERMEM.
178 	 *
179 	 * !!!
180 	 * If the length we're going to copy is 0, the application-supplied
181 	 * memory pointer is allowed to be NULL.
182 	 */
183 	if (F_ISSET(dbt, DB_DBT_USERCOPY)) {
184 		dbt->size = len;
185 		return (len == 0 ? 0 : env->dbt_usercopy(dbt, 0, data,
186 		    len, DB_USERCOPY_SETDATA));
187 
188 	} else if (F_ISSET(dbt, DB_DBT_MALLOC))
189 		ret = __os_umalloc(env, len, &dbt->data);
190 	else if (F_ISSET(dbt, DB_DBT_REALLOC)) {
191 		if (dbt->data == NULL || dbt->size == 0 || dbt->size < len)
192 			ret = __os_urealloc(env, len, &dbt->data);
193 	} else if (F_ISSET(dbt, DB_DBT_USERMEM)) {
194 		if (len != 0 && (dbt->data == NULL || dbt->ulen < len))
195 			ret = DB_BUFFER_SMALL;
196 	} else if (memp == NULL || memsize == NULL)
197 		ret = USR_ERR(env, EINVAL);
198 	else {
199 		if (len != 0 && (*memsize == 0 || *memsize < len)) {
200 			if ((ret = __os_realloc(env, len, memp)) == 0)
201 				*memsize = len;
202 			else
203 				*memsize = 0;
204 		}
205 		if (ret == 0)
206 			dbt->data = *memp;
207 	}
208 
209 	if (ret == 0 && len != 0)
210 		memcpy(dbt->data, data, len);
211 
212 	/*
213 	 * Return the length of the returned record in the DBT size field.
214 	 * This satisfies the requirement that if we're using user memory
215 	 * and insufficient memory was provided, return the amount necessary
216 	 * in the size field.
217 	 */
218 	dbt->size = len;
219 
220 	return (ret);
221 }
222 
223 /*
224  * __db_dbt_clone --
225  *	Clone a DBT from another DBT.
226  * The input dest DBT must be a zero initialized DBT that will be populated.
227  * The function does not allocate a dest DBT to allow for cloning into stack
228  * or locally allocated variables. It is the callers responsibility to free
229  * the memory allocated in dest->data.
230  *
231  * PUBLIC: int __db_dbt_clone __P((ENV *, DBT *, const DBT *));
232  */
233 int
__db_dbt_clone(env,dest,src)234 __db_dbt_clone(env, dest, src)
235 	ENV *env;
236 	DBT *dest;
237 	const DBT *src;
238 {
239 	u_int32_t err_flags;
240 	int ret;
241 
242 	DB_ASSERT(env, dest->data == NULL);
243 
244 	ret = 0;
245 
246 	/* The function does not support the following DBT flags. */
247 	err_flags = DB_DBT_MALLOC | DB_DBT_REALLOC |
248 	    DB_DBT_MULTIPLE | DB_DBT_PARTIAL;
249 	if (F_ISSET(src, err_flags)) {
250 		__db_errx(env, DB_STR("0758",
251 		    "Unsupported flags when cloning the DBT."));
252 		return (EINVAL);
253 	}
254 
255 	if ((ret = __os_malloc(env, src->size, &dest->data)) != 0)
256 		return (ret);
257 
258 	memcpy(dest->data, src->data, src->size);
259 	dest->ulen = src->size;
260 	dest->size = src->size;
261 	dest->flags = DB_DBT_USERMEM;
262 
263 	return (ret);
264 }
265 
266 /*
267  * __db_dbt_clone_free --
268  *	Free a DBT cloned by __db_dbt_clone
269  *
270  * PUBLIC: int __db_dbt_clone_free __P((ENV *, DBT *));
271  */
272 int
__db_dbt_clone_free(env,dbt)273 __db_dbt_clone_free(env, dbt)
274 	ENV *env;
275 	DBT *dbt;
276 {
277 	/* Currently only DB_DBT_USERMEM is supported. */
278 	if (dbt->flags != DB_DBT_USERMEM) {
279 		__db_errx(env, DB_STR("0759",
280 		    "Unsupported flags when freeing the cloned DBT."));
281 		return (EINVAL);
282 	}
283 
284 	if (dbt->data != NULL)
285 		__os_free(env, dbt->data);
286 	dbt->size = dbt->ulen = 0;
287 
288 	return (0);
289 }
290