1 /*-
2 * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
3 *
4 * See the file LICENSE for license information.
5 */
6
7 #include "db_config.h"
8
9 #include "db_int.h"
10 #include "dbinc/db_page.h"
11 #include "dbinc/db_am.h"
12 #include "dbinc/fop.h"
13
14 static int __db_stream_close __P((DB_STREAM *, u_int32_t));
15 static int __db_stream_read
16 __P((DB_STREAM *, DBT *, db_off_t, u_int32_t, u_int32_t));
17 static int __db_stream_size __P((DB_STREAM *, db_off_t *, u_int32_t));
18 static int __db_stream_write __P((DB_STREAM *, DBT *, db_off_t, u_int32_t));
19
20 /*
21 * __db_stream_init
22 * DB_STREAM initializer.
23 *
24 * PUBLIC: int __db_stream_init __P((DBC *, DB_STREAM **, u_int32_t));
25 */
26 int
__db_stream_init(dbc,dbsp,flags)27 __db_stream_init(dbc, dbsp, flags)
28 DBC *dbc;
29 DB_STREAM **dbsp;
30 u_int32_t flags;
31 {
32 DB_STREAM *dbs;
33 DB_THREAD_INFO *ip;
34 ENV *env;
35 int ret;
36 off_t size;
37
38 dbs = NULL;
39 env = dbc->env;
40
41 if ((ret = __os_malloc(env, sizeof(DB_STREAM), &dbs)) != 0)
42 return (ret);
43 memset(dbs, 0, sizeof(DB_STREAM));
44
45 ENV_ENTER(env, ip);
46 dbc->thread_info = ip;
47 /* Should the copy be transient? */
48 if ((ret = __dbc_idup(dbc, &dbs->dbc, DB_POSITION)) != 0)
49 goto err;
50 dbs->flags = flags;
51
52 /*
53 * Make sure we have a write lock on the db record if writing
54 * to the blob.
55 */
56 if (F_ISSET(dbs, DB_FOP_WRITE))
57 F_SET(dbc, DBC_RMW);
58
59 if ((ret = __dbc_get_blob_id(dbs->dbc, &dbs->blob_id)) != 0) {
60 if (ret == EINVAL)
61 __db_errx(env, DB_STR("0211",
62 "Error, cursor does not point to an external file."));
63 goto err;
64 }
65
66 if ((ret = __dbc_get_blob_size(dbs->dbc, &size)) != 0)
67 goto err;
68 dbs->file_size = size;
69
70 if ((ret = __blob_file_open(
71 dbs->dbc->dbp, &dbs->fhp, dbs->blob_id, flags, 1)) != 0)
72 goto err;
73 ENV_LEAVE(env, ip);
74
75 dbs->close = __db_stream_close;
76 dbs->read = __db_stream_read;
77 dbs->size = __db_stream_size;
78 dbs->write = __db_stream_write;
79
80 *dbsp = dbs;
81 return (0);
82
83 err: if (dbs != NULL && dbs->dbc != NULL)
84 (void)__dbc_close(dbs->dbc);
85 ENV_LEAVE(env, ip);
86 if (dbs != NULL)
87 __os_free(env, dbs);
88 return (ret);
89 }
90
91 /*
92 * __db_stream_close --
93 *
94 * DB_STREAM->close
95 */
96 static int
__db_stream_close(dbs,flags)97 __db_stream_close(dbs, flags)
98 DB_STREAM *dbs;
99 u_int32_t flags;
100 {
101 DB_THREAD_INFO *ip;
102 ENV *env;
103 int ret;
104
105 env = dbs->dbc->env;
106
107 if ((ret = __db_fchk(env, "DB_STREAM->close", flags, 0)) != 0)
108 return (ret);
109
110 ENV_ENTER(env, ip);
111 dbs->dbc->thread_info = ip;
112
113 ret = __db_stream_close_int(dbs);
114
115 ENV_LEAVE(env, ip);
116
117 return (ret);
118 }
119
120 /*
121 * __db_stream_close_int --
122 * Close a DB_STREAM object.
123 *
124 * PUBLIC: int __db_stream_close_int __P ((DB_STREAM *));
125 */
126 int
__db_stream_close_int(dbs)127 __db_stream_close_int(dbs)
128 DB_STREAM *dbs;
129 {
130 DBC *dbc;
131 ENV *env;
132 int ret, t_ret;
133
134 dbc = dbs->dbc;
135 env = dbc->env;
136
137 ret = __blob_file_close(dbc, dbs->fhp, dbs->flags);
138
139 if ((t_ret = __dbc_close(dbs->dbc)) != 0 && ret == 0)
140 ret = t_ret;
141
142 __os_free(env, dbs);
143
144 return (ret);
145 }
146
147 /*
148 * __db_stream_read --
149 *
150 * DB_STREAM->read
151 */
152 static int
__db_stream_read(dbs,data,offset,size,flags)153 __db_stream_read(dbs, data, offset, size, flags)
154 DB_STREAM *dbs;
155 DBT *data;
156 db_off_t offset;
157 u_int32_t size;
158 u_int32_t flags;
159 {
160 DBC *dbc;
161 ENV *env;
162 int ret;
163 u_int32_t needed, start;
164
165 dbc = dbs->dbc;
166 env = dbc->dbp->env;
167 ret = 0;
168
169 if ((ret = __db_fchk(env, "DB_STREAM->read", flags, 0)) != 0)
170 return (ret);
171
172 if (F_ISSET(data, DB_DBT_PARTIAL)) {
173 ret = USR_ERR(env, EINVAL);
174 __db_errx(env, DB_STR("0212",
175 "Error, do not use DB_DBT_PARTIAL with DB_STREAM."));
176 goto err;
177 }
178
179 if (offset > dbs->file_size) {
180 data->size = 0;
181 goto err;
182 }
183
184 if ((ret = __db_alloc_dbt(
185 env, data, size, &needed, &start, NULL, NULL)) != 0)
186 goto err;
187 data->size = needed;
188
189 if (needed == 0)
190 goto err;
191
192 ret = __blob_file_read(env, dbs->fhp, data, offset, size);
193
194 err: return (ret);
195 }
196
197 /*
198 * __db_stream_size --
199 *
200 * DB_STREAM->size
201 */
202 static int
__db_stream_size(dbs,size,flags)203 __db_stream_size(dbs, size, flags)
204 DB_STREAM *dbs;
205 db_off_t *size;
206 u_int32_t flags;
207 {
208 int ret;
209
210 if ((ret = __db_fchk(dbs->dbc->env, "DB_STREAM->size", flags, 0)) != 0)
211 return (ret);
212
213 *size = dbs->file_size;
214
215 return (0);
216 }
217
218 /*
219 * __db_stream_write --
220 *
221 * DB_STREAM->write
222 */
223 static int
__db_stream_write(dbs,data,offset,flags)224 __db_stream_write(dbs, data, offset, flags)
225 DB_STREAM *dbs;
226 DBT *data;
227 db_off_t offset;
228 u_int32_t flags;
229 {
230 DB_THREAD_INFO *ip;
231 ENV *env;
232 int ret;
233 off_t file_size;
234 u_int32_t wflags;
235
236 env = dbs->dbc->env;
237
238 if ((ret = __db_fchk(
239 env, "DB_STREAM->write", flags, DB_STREAM_SYNC_WRITE)) != 0)
240 return (ret);
241
242 if (F_ISSET(dbs, DB_FOP_READONLY)) {
243 ret = USR_ERR(env, EINVAL);
244 __db_errx(env, DB_STR("0213",
245 "Error, external file is read only."));
246 return (ret);
247 }
248 if (F_ISSET(data, DB_DBT_PARTIAL)) {
249 ret = USR_ERR(env, EINVAL);
250 __db_errx(env, DB_STR("0212",
251 "Error, do not use DB_DBT_PARTIAL with DB_STREAM."));
252 return (ret);
253 }
254 if (offset < 0 ) {
255 ret = USR_ERR(env, EINVAL);
256 __db_errx(env, DB_STR_A("0215",
257 "Error, invalid offset value: %lld", "%lld"),
258 (long long)offset);
259 return (ret);
260 }
261 /*
262 * Catch offset overflow. After the above test, offset >= 0 is true, and
263 * DB_OFF_T_MAX is defined to be the maximum signed integer of the same
264 * size as (db_off_t), therefore (DB_OFF_T_MAX - offset) >= 0 must also
265 * be true and it cannot cause an overflow. data->size is an unsigned
266 * 32-bit integer, while offset is a signed 32-bit or 64-bit integer, so
267 * the only safe way to compare the two is to promote both to
268 * unsigned 64-bit integers.
269 */
270 if ((u_int64_t)data->size > (u_int64_t)(DB_OFF_T_MAX - offset)) {
271 ret = USR_ERR(env, EINVAL);
272 __db_errx(env, DB_STR_A("0216",
273 "Error, this write would exceed the maximum external file size: %lu %lld",
274 "%lu %lld"), (u_long)data->size, (long long)offset);
275 return (ret);
276 }
277
278 ENV_ENTER(env, ip);
279 dbs->dbc->thread_info = ip;
280 wflags = 0;
281 if (LF_ISSET(DB_STREAM_SYNC_WRITE) || F_ISSET(dbs, DB_FOP_SYNC_WRITE))
282 wflags |= DB_FOP_SYNC_WRITE;
283 file_size = dbs->file_size;
284 if ((ret = __blob_file_write(dbs->dbc, dbs->fhp,
285 data, offset, dbs->blob_id, &file_size, wflags)) != 0)
286 goto err;
287 if (file_size != dbs->file_size) {
288 dbs->file_size = file_size;
289 if ((ret = __dbc_set_blob_size(dbs->dbc, dbs->file_size)) != 0)
290 goto err;
291 }
292 err: ENV_LEAVE(env, ip);
293
294 return (ret);
295 }
296