Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * be-fsstubs.c
4 : * Builtin functions for open/close/read/write operations on large objects
5 : *
6 : * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/libpq/be-fsstubs.c
12 : *
13 : * NOTES
14 : * This should be moved to a more appropriate place. It is here
15 : * for lack of a better place.
16 : *
17 : * These functions store LargeObjectDesc structs in a private MemoryContext,
18 : * which means that large object descriptors hang around until we destroy
19 : * the context at transaction end. It'd be possible to prolong the lifetime
20 : * of the context so that LO FDs are good across transactions (for example,
21 : * we could release the context only if we see that no FDs remain open).
22 : * But we'd need additional state in order to do the right thing at the
23 : * end of an aborted transaction. FDs opened during an aborted xact would
24 : * still need to be closed, since they might not be pointing at valid
25 : * relations at all. Locking semantics are also an interesting problem
26 : * if LOs stay open across transactions. For now, we'll stick with the
27 : * existing documented semantics of LO FDs: they're only good within a
28 : * transaction.
29 : *
30 : * As of PostgreSQL 8.0, much of the angst expressed above is no longer
31 : * relevant, and in fact it'd be pretty easy to allow LO FDs to stay
32 : * open across transactions. (Snapshot relevancy would still be an issue.)
33 : * However backwards compatibility suggests that we should stick to the
34 : * status quo.
35 : *
36 : *-------------------------------------------------------------------------
37 : */
38 :
39 : #include "postgres.h"
40 :
41 : #include <fcntl.h>
42 : #include <sys/stat.h>
43 : #include <unistd.h>
44 :
45 : #include "access/xact.h"
46 : #include "catalog/pg_largeobject_metadata.h"
47 : #include "libpq/be-fsstubs.h"
48 : #include "libpq/libpq-fs.h"
49 : #include "miscadmin.h"
50 : #include "storage/fd.h"
51 : #include "storage/large_object.h"
52 : #include "utils/acl.h"
53 : #include "utils/builtins.h"
54 : #include "utils/memutils.h"
55 : #include "utils/snapmgr.h"
56 : #include "varatt.h"
57 :
58 : /* define this to enable debug logging */
59 : /* #define FSDB 1 */
60 : /* chunk size for lo_import/lo_export transfers */
61 : #define BUFSIZE 8192
62 :
63 : /*
64 : * LO "FD"s are indexes into the cookies array.
65 : *
66 : * A non-null entry is a pointer to a LargeObjectDesc allocated in the
67 : * LO private memory context "fscxt". The cookies array itself is also
68 : * dynamically allocated in that context. Its current allocated size is
69 : * cookies_size entries, of which any unused entries will be NULL.
70 : */
71 : static LargeObjectDesc **cookies = NULL;
72 : static int cookies_size = 0;
73 :
74 : static bool lo_cleanup_needed = false;
75 : static MemoryContext fscxt = NULL;
76 :
77 : static int newLOfd(void);
78 : static void closeLOfd(int fd);
79 : static Oid lo_import_internal(text *filename, Oid lobjOid);
80 :
81 :
82 : /*****************************************************************************
83 : * File Interfaces for Large Objects
84 : *****************************************************************************/
85 :
86 : Datum
2294 peter_e 87 GIC 177 : be_lo_open(PG_FUNCTION_ARGS)
88 : {
8339 tgl 89 CBC 177 : Oid lobjId = PG_GETARG_OID(0);
8339 tgl 90 GIC 177 : int32 mode = PG_GETARG_INT32(1);
9345 bruce 91 ECB : LargeObjectDesc *lobjDesc;
9344 92 : int fd;
93 :
94 : #ifdef FSDB
95 : elog(DEBUG4, "lo_open(%u,%d)", lobjId, mode);
96 : #endif
97 :
279 michael 98 GNC 177 : if (mode & INV_WRITE)
99 58 : PreventCommandIfReadOnly("lo_open(INV_WRITE)");
100 :
101 : /*
102 : * Allocate a large object descriptor first. This will also create
522 heikki.linnakangas 103 ECB : * 'fscxt' if this is the first LO opened in this transaction.
104 : */
522 heikki.linnakangas 105 GIC 174 : fd = newLOfd();
106 :
6192 tgl 107 174 : lobjDesc = inv_open(lobjId, mode, fscxt);
522 heikki.linnakangas 108 150 : lobjDesc->subid = GetCurrentSubTransactionId();
109 :
522 heikki.linnakangas 110 ECB : /*
111 : * We must register the snapshot in TopTransaction's resowner so that it
112 : * stays alive until the LO is closed rather than until the current portal
113 : * shuts down.
114 : */
522 heikki.linnakangas 115 GIC 150 : if (lobjDesc->snapshot)
116 110 : lobjDesc->snapshot = RegisterSnapshotOnOwner(lobjDesc->snapshot,
117 : TopTransactionResourceOwner);
118 :
119 150 : Assert(cookies[fd] == NULL);
522 heikki.linnakangas 120 CBC 150 : cookies[fd] = lobjDesc;
9770 scrappy 121 ECB :
8339 tgl 122 GIC 150 : PG_RETURN_INT32(fd);
123 : }
9770 scrappy 124 ECB :
8339 tgl 125 : Datum
2294 peter_e 126 GIC 105 : be_lo_close(PG_FUNCTION_ARGS)
9770 scrappy 127 ECB : {
8339 tgl 128 GIC 105 : int32 fd = PG_GETARG_INT32(0);
129 :
8202 130 105 : if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
7201 tgl 131 LBC 0 : ereport(ERROR,
132 : (errcode(ERRCODE_UNDEFINED_OBJECT),
7201 tgl 133 ECB : errmsg("invalid large-object descriptor: %d", fd)));
134 :
1268 peter 135 : #ifdef FSDB
7257 bruce 136 EUB : elog(DEBUG4, "lo_close(%d)", fd);
137 : #endif
138 :
522 heikki.linnakangas 139 GIC 105 : closeLOfd(fd);
140 :
8339 tgl 141 105 : PG_RETURN_INT32(0);
142 : }
143 :
8339 tgl 144 ECB :
145 : /*****************************************************************************
146 : * Bare Read/Write operations --- these are not fmgr-callable!
147 : *
148 : * We assume the large object supports byte oriented reads and seeks so
149 : * that our work is easier.
150 : *
151 : *****************************************************************************/
152 :
153 : int
9770 scrappy 154 GIC 413 : lo_read(int fd, char *buf, int len)
155 : {
156 : int status;
157 : LargeObjectDesc *lobj;
158 :
8202 tgl 159 CBC 413 : if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
7201 tgl 160 UIC 0 : ereport(ERROR,
161 : (errcode(ERRCODE_UNDEFINED_OBJECT),
162 : errmsg("invalid large-object descriptor: %d", fd)));
3834 tgl 163 GIC 413 : lobj = cookies[fd];
8714 tgl 164 ECB :
1977 tgl 165 EUB : /*
166 : * Check state. inv_read() would throw an error anyway, but we want the
167 : * error to be about the FD's state not the underlying privilege; it might
1977 tgl 168 ECB : * be that the privilege exists but user forgot to ask for read mode.
169 : */
1977 tgl 170 GIC 413 : if ((lobj->flags & IFS_RDLOCK) == 0)
1977 tgl 171 UIC 0 : ereport(ERROR,
172 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
173 : errmsg("large object descriptor %d was not opened for reading",
174 : fd)));
4867 itagaki.takahiro 175 ECB :
3834 tgl 176 GBC 413 : status = inv_read(lobj, buf, len);
177 :
8339 tgl 178 GIC 413 : return status;
179 : }
180 :
9770 scrappy 181 ECB : int
6058 bruce 182 GIC 517 : lo_write(int fd, const char *buf, int len)
9770 scrappy 183 ECB : {
184 : int status;
185 : LargeObjectDesc *lobj;
186 :
8202 tgl 187 CBC 517 : if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
7201 tgl 188 UIC 0 : ereport(ERROR,
189 : (errcode(ERRCODE_UNDEFINED_OBJECT),
190 : errmsg("invalid large-object descriptor: %d", fd)));
3834 tgl 191 GIC 517 : lobj = cookies[fd];
8714 tgl 192 ECB :
1977 tgl 193 EUB : /* see comment in lo_read() */
3834 tgl 194 GIC 517 : if ((lobj->flags & IFS_WRLOCK) == 0)
6509 195 3 : ereport(ERROR,
6509 tgl 196 ECB : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
197 : errmsg("large object descriptor %d was not opened for writing",
198 : fd)));
199 :
3834 tgl 200 CBC 514 : status = inv_write(lobj, buf, len);
201 :
8339 tgl 202 GIC 514 : return status;
203 : }
204 :
8339 tgl 205 ECB : Datum
2294 peter_e 206 GIC 27 : be_lo_lseek(PG_FUNCTION_ARGS)
9770 scrappy 207 ECB : {
8339 tgl 208 GIC 27 : int32 fd = PG_GETARG_INT32(0);
209 27 : int32 offset = PG_GETARG_INT32(1);
210 27 : int32 whence = PG_GETARG_INT32(2);
3836 ishii 211 ECB : int64 status;
212 :
8202 tgl 213 CBC 27 : if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
7201 tgl 214 LBC 0 : ereport(ERROR,
7201 tgl 215 ECB : (errcode(ERRCODE_UNDEFINED_OBJECT),
216 : errmsg("invalid large-object descriptor: %d", fd)));
217 :
8714 tgl 218 CBC 27 : status = inv_seek(cookies[fd], offset, whence);
9518 scrappy 219 EUB :
220 : /* guard against result overflow */
3835 tgl 221 GIC 27 : if (status != (int32) status)
3836 ishii 222 UIC 0 : ereport(ERROR,
3835 tgl 223 ECB : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
224 : errmsg("lo_lseek result out of range for large-object descriptor %d",
225 : fd)));
3836 ishii 226 :
3835 tgl 227 GBC 27 : PG_RETURN_INT32((int32) status);
228 : }
229 :
230 : Datum
2294 peter_e 231 GIC 12 : be_lo_lseek64(PG_FUNCTION_ARGS)
3836 ishii 232 ECB : {
3836 ishii 233 GIC 12 : int32 fd = PG_GETARG_INT32(0);
234 12 : int64 offset = PG_GETARG_INT64(1);
235 12 : int32 whence = PG_GETARG_INT32(2);
3835 tgl 236 ECB : int64 status;
237 :
3836 ishii 238 CBC 12 : if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
3836 ishii 239 LBC 0 : ereport(ERROR,
3836 ishii 240 ECB : (errcode(ERRCODE_UNDEFINED_OBJECT),
241 : errmsg("invalid large-object descriptor: %d", fd)));
242 :
3836 ishii 243 CBC 12 : status = inv_seek(cookies[fd], offset, whence);
3836 ishii 244 EUB :
3836 ishii 245 GIC 12 : PG_RETURN_INT64(status);
246 : }
247 :
8339 tgl 248 ECB : Datum
2294 peter_e 249 GIC 13 : be_lo_creat(PG_FUNCTION_ARGS)
9770 scrappy 250 ECB : {
251 : Oid lobjId;
252 :
279 michael 253 GNC 13 : PreventCommandIfReadOnly("lo_creat()");
254 :
522 heikki.linnakangas 255 GIC 10 : lo_cleanup_needed = true;
6509 tgl 256 CBC 10 : lobjId = inv_create(InvalidOid);
257 :
6509 tgl 258 GIC 10 : PG_RETURN_OID(lobjId);
259 : }
6509 tgl 260 ECB :
261 : Datum
2294 peter_e 262 CBC 36 : be_lo_create(PG_FUNCTION_ARGS)
6509 tgl 263 ECB : {
6509 tgl 264 GIC 36 : Oid lobjId = PG_GETARG_OID(0);
9345 bruce 265 ECB :
279 michael 266 GNC 36 : PreventCommandIfReadOnly("lo_create()");
267 :
522 heikki.linnakangas 268 GIC 33 : lo_cleanup_needed = true;
6509 tgl 269 33 : lobjId = inv_create(lobjId);
270 :
8339 tgl 271 CBC 33 : PG_RETURN_OID(lobjId);
272 : }
9770 scrappy 273 ECB :
274 : Datum
2294 peter_e 275 CBC 12 : be_lo_tell(PG_FUNCTION_ARGS)
276 : {
8339 tgl 277 12 : int32 fd = PG_GETARG_INT32(0);
3835 tgl 278 ECB : int64 offset;
279 :
8202 tgl 280 CBC 12 : if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
7201 tgl 281 UIC 0 : ereport(ERROR,
282 : (errcode(ERRCODE_UNDEFINED_OBJECT),
283 : errmsg("invalid large-object descriptor: %d", fd)));
8714 tgl 284 ECB :
3836 ishii 285 GIC 12 : offset = inv_tell(cookies[fd]);
3836 ishii 286 ECB :
287 : /* guard against result overflow */
3835 tgl 288 GIC 12 : if (offset != (int32) offset)
3836 ishii 289 LBC 0 : ereport(ERROR,
3835 tgl 290 EUB : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
291 : errmsg("lo_tell result out of range for large-object descriptor %d",
292 : fd)));
293 :
3835 tgl 294 CBC 12 : PG_RETURN_INT32((int32) offset);
295 : }
296 :
3836 ishii 297 ECB : Datum
2294 peter_e 298 GBC 12 : be_lo_tell64(PG_FUNCTION_ARGS)
299 : {
3836 ishii 300 GIC 12 : int32 fd = PG_GETARG_INT32(0);
301 : int64 offset;
302 :
3836 ishii 303 CBC 12 : if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
3836 ishii 304 UIC 0 : ereport(ERROR,
305 : (errcode(ERRCODE_UNDEFINED_OBJECT),
306 : errmsg("invalid large-object descriptor: %d", fd)));
3836 ishii 307 ECB :
3835 tgl 308 GIC 12 : offset = inv_tell(cookies[fd]);
3835 tgl 309 ECB :
3835 tgl 310 GIC 12 : PG_RETURN_INT64(offset);
311 : }
9770 scrappy 312 ECB :
8339 tgl 313 EUB : Datum
2294 peter_e 314 GIC 50 : be_lo_unlink(PG_FUNCTION_ARGS)
315 : {
8339 tgl 316 50 : Oid lobjId = PG_GETARG_OID(0);
8397 bruce 317 ECB :
279 michael 318 GNC 50 : PreventCommandIfReadOnly("lo_unlink()");
319 :
320 : /*
1977 tgl 321 ECB : * Must be owner of the large object. It would be cleaner to check this
322 : * in inv_drop(), but we want to throw the error before not after closing
323 : * relevant FDs.
324 : */
4867 itagaki.takahiro 325 CBC 47 : if (!lo_compat_privileges &&
147 peter 326 GNC 44 : !object_ownercheck(LargeObjectMetadataRelationId, lobjId, GetUserId()))
4867 itagaki.takahiro 327 CBC 6 : ereport(ERROR,
328 : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
4867 itagaki.takahiro 329 ECB : errmsg("must be owner of large object %u", lobjId)));
330 :
331 : /*
332 : * If there are any open LO FDs referencing that ID, close 'em.
333 : */
8202 tgl 334 GIC 41 : if (fscxt != NULL)
335 : {
8202 tgl 336 ECB : int i;
337 :
8202 tgl 338 LBC 0 : for (i = 0; i < cookies_size; i++)
339 : {
8202 tgl 340 UIC 0 : if (cookies[i] != NULL && cookies[i]->id == lobjId)
522 heikki.linnakangas 341 0 : closeLOfd(i);
342 : }
343 : }
344 :
8714 tgl 345 ECB : /*
346 : * inv_drop does not create a need for end-of-transaction cleanup and
347 : * hence we don't need to set lo_cleanup_needed.
348 : */
8339 tgl 349 GBC 41 : PG_RETURN_INT32(inv_drop(lobjId));
350 : }
9770 scrappy 351 EUB :
352 : /*****************************************************************************
353 : * Read/Write using bytea
354 : *****************************************************************************/
355 :
356 : Datum
2294 peter_e 357 GIC 413 : be_loread(PG_FUNCTION_ARGS)
358 : {
8339 tgl 359 413 : int32 fd = PG_GETARG_INT32(0);
8339 tgl 360 CBC 413 : int32 len = PG_GETARG_INT32(1);
361 : bytea *retval;
362 : int totalread;
363 :
8339 tgl 364 GIC 413 : if (len < 0)
8339 tgl 365 UIC 0 : len = 0;
366 :
7532 tgl 367 GIC 413 : retval = (bytea *) palloc(VARHDRSZ + len);
9345 bruce 368 CBC 413 : totalread = lo_read(fd, VARDATA(retval), len);
5885 tgl 369 GIC 413 : SET_VARSIZE(retval, totalread + VARHDRSZ);
9345 bruce 370 ECB :
7532 tgl 371 CBC 413 : PG_RETURN_BYTEA_P(retval);
372 : }
373 :
374 : Datum
2294 peter_e 375 520 : be_lowrite(PG_FUNCTION_ARGS)
9770 scrappy 376 EUB : {
8053 bruce 377 GIC 520 : int32 fd = PG_GETARG_INT32(0);
2219 noah 378 CBC 520 : bytea *wbuf = PG_GETARG_BYTEA_PP(1);
8053 bruce 379 ECB : int bytestowrite;
380 : int totalwritten;
381 :
279 michael 382 GNC 520 : PreventCommandIfReadOnly("lowrite()");
383 :
2219 noah 384 CBC 517 : bytestowrite = VARSIZE_ANY_EXHDR(wbuf);
2219 noah 385 GIC 517 : totalwritten = lo_write(fd, VARDATA_ANY(wbuf), bytestowrite);
8339 tgl 386 514 : PG_RETURN_INT32(totalwritten);
387 : }
9770 scrappy 388 ECB :
389 : /*****************************************************************************
9345 bruce 390 : * Import/Export of Large Object
9770 scrappy 391 : *****************************************************************************/
392 :
393 : /*
394 : * lo_import -
9345 bruce 395 : * imports a file as an (inversion) large object.
396 : */
8339 tgl 397 : Datum
2294 peter_e 398 CBC 6 : be_lo_import(PG_FUNCTION_ARGS)
9770 scrappy 399 ECB : {
5493 tgl 400 GIC 6 : text *filename = PG_GETARG_TEXT_PP(0);
401 :
5496 ishii 402 6 : PG_RETURN_OID(lo_import_internal(filename, InvalidOid));
403 : }
404 :
405 : /*
406 : * lo_import_with_oid -
407 : * imports a file as an (inversion) large object specifying oid.
408 : */
409 : Datum
2294 peter_e 410 UIC 0 : be_lo_import_with_oid(PG_FUNCTION_ARGS)
5496 ishii 411 ECB : {
5493 tgl 412 UIC 0 : text *filename = PG_GETARG_TEXT_PP(0);
5050 bruce 413 LBC 0 : Oid oid = PG_GETARG_OID(1);
414 :
5496 ishii 415 0 : PG_RETURN_OID(lo_import_internal(filename, oid));
416 : }
417 :
418 : static Oid
5496 ishii 419 GIC 6 : lo_import_internal(text *filename, Oid lobjOid)
420 : {
421 : int fd;
422 : int nbytes,
4036 peter_e 423 EUB : tmp PG_USED_FOR_ASSERTS_ONLY;
424 : char buf[BUFSIZE];
8202 tgl 425 : char fnamebuf[MAXPGPATH];
9345 bruce 426 : LargeObjectDesc *lobj;
427 : Oid oid;
5050 428 :
279 michael 429 GNC 6 : PreventCommandIfReadOnly("lo_import()");
430 :
431 : /*
432 : * open the file to be read in
433 : */
5493 tgl 434 CBC 3 : text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
2024 peter_e 435 GIC 3 : fd = OpenTransientFile(fnamebuf, O_RDONLY | PG_BINARY);
9345 bruce 436 3 : if (fd < 0)
7201 tgl 437 UIC 0 : ereport(ERROR,
438 : (errcode_for_file_access(),
439 : errmsg("could not open server file \"%s\": %m",
440 : fnamebuf)));
441 :
442 : /*
443 : * create an inversion object
9345 bruce 444 ECB : */
522 heikki.linnakangas 445 GIC 3 : lo_cleanup_needed = true;
5496 ishii 446 3 : oid = inv_create(lobjOid);
447 :
448 : /*
6509 tgl 449 ECB : * read in from the filesystem and write to the inversion object
9345 bruce 450 : */
522 heikki.linnakangas 451 CBC 3 : lobj = inv_open(oid, INV_WRITE, CurrentMemoryContext);
6509 tgl 452 EUB :
3785 heikki.linnakangas 453 GIC 249 : while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
454 : {
9345 bruce 455 246 : tmp = inv_write(lobj, buf, nbytes);
7201 tgl 456 246 : Assert(tmp == nbytes);
457 : }
458 :
459 3 : if (nbytes < 0)
7201 tgl 460 LBC 0 : ereport(ERROR,
7201 tgl 461 ECB : (errcode_for_file_access(),
462 : errmsg("could not read server file \"%s\": %m",
463 : fnamebuf)));
464 :
9345 bruce 465 GIC 3 : inv_close(lobj);
1492 michael 466 ECB :
1373 peter 467 GIC 3 : if (CloseTransientFile(fd) != 0)
1492 michael 468 LBC 0 : ereport(ERROR,
469 : (errcode_for_file_access(),
1492 michael 470 ECB : errmsg("could not close file \"%s\": %m",
471 : fnamebuf)));
472 :
5496 ishii 473 GIC 3 : return oid;
9770 scrappy 474 ECB : }
9770 scrappy 475 EUB :
476 : /*
477 : * lo_export -
478 : * exports an (inversion) large object.
479 : */
8339 tgl 480 ECB : Datum
2294 peter_e 481 GIC 6 : be_lo_export(PG_FUNCTION_ARGS)
9770 scrappy 482 ECB : {
8339 tgl 483 GBC 6 : Oid lobjId = PG_GETARG_OID(0);
5493 tgl 484 GIC 6 : text *filename = PG_GETARG_TEXT_PP(1);
485 : int fd;
486 : int nbytes,
487 : tmp;
9344 bruce 488 ECB : char buf[BUFSIZE];
489 : char fnamebuf[MAXPGPATH];
490 : LargeObjectDesc *lobj;
491 : mode_t oumask;
492 :
493 : /*
494 : * open the inversion object (no need to test for failure)
495 : */
522 heikki.linnakangas 496 CBC 6 : lo_cleanup_needed = true;
522 heikki.linnakangas 497 GIC 6 : lobj = inv_open(lobjId, INV_READ, CurrentMemoryContext);
9345 bruce 498 ECB :
499 : /*
500 : * open the file to be written to
501 : *
502 : * Note: we reduce backend's normal 077 umask to the slightly friendlier
503 : * 022. This code used to drop it all the way to 0, but creating
504 : * world-writable export files doesn't seem wise.
505 : */
5493 tgl 506 GIC 6 : text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
4503 507 6 : oumask = umask(S_IWGRP | S_IWOTH);
2025 peter_e 508 6 : PG_TRY();
509 : {
2024 510 6 : fd = OpenTransientFilePerm(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
2024 peter_e 511 ECB : S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2025 512 : }
1255 peter 513 UIC 0 : PG_FINALLY();
514 : {
2025 peter_e 515 GIC 6 : umask(oumask);
516 : }
517 6 : PG_END_TRY();
9345 bruce 518 6 : if (fd < 0)
7201 tgl 519 3 : ereport(ERROR,
520 : (errcode_for_file_access(),
7201 tgl 521 ECB : errmsg("could not create server file \"%s\": %m",
522 : fnamebuf)));
9770 scrappy 523 :
524 : /*
6829 tgl 525 : * read in from the inversion file and write to the filesystem
526 : */
9345 bruce 527 GIC 249 : while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
9345 bruce 528 EUB : {
3785 heikki.linnakangas 529 GIC 246 : tmp = write(fd, buf, nbytes);
8202 tgl 530 CBC 246 : if (tmp != nbytes)
7201 tgl 531 UIC 0 : ereport(ERROR,
7201 tgl 532 ECB : (errcode_for_file_access(),
533 : errmsg("could not write server file \"%s\": %m",
534 : fnamebuf)));
535 : }
536 :
1373 peter 537 GIC 3 : if (CloseTransientFile(fd) != 0)
1492 michael 538 UIC 0 : ereport(ERROR,
539 : (errcode_for_file_access(),
540 : errmsg("could not close file \"%s\": %m",
541 : fnamebuf)));
1492 michael 542 ECB :
7201 tgl 543 GIC 3 : inv_close(lobj);
9770 scrappy 544 ECB :
8339 tgl 545 CBC 3 : PG_RETURN_INT32(1);
9770 scrappy 546 EUB : }
547 :
548 : /*
549 : * lo_truncate -
550 : * truncate a large object to a specified length
551 : */
3834 tgl 552 ECB : static void
3834 tgl 553 GBC 21 : lo_truncate_internal(int32 fd, int64 len)
554 : {
555 : LargeObjectDesc *lobj;
556 :
5881 bruce 557 GIC 21 : if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
5881 bruce 558 LBC 0 : ereport(ERROR,
559 : (errcode(ERRCODE_UNDEFINED_OBJECT),
5881 bruce 560 ECB : errmsg("invalid large-object descriptor: %d", fd)));
3834 tgl 561 GIC 21 : lobj = cookies[fd];
562 :
563 : /* see comment in lo_read() */
564 21 : if ((lobj->flags & IFS_WRLOCK) == 0)
3836 ishii 565 UIC 0 : ereport(ERROR,
566 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
567 : errmsg("large object descriptor %d was not opened for writing",
2118 tgl 568 ECB : fd)));
569 :
3834 tgl 570 GIC 21 : inv_truncate(lobj, len);
571 21 : }
3836 ishii 572 ECB :
3834 tgl 573 EUB : Datum
2294 peter_e 574 GIC 18 : be_lo_truncate(PG_FUNCTION_ARGS)
575 : {
3834 tgl 576 CBC 18 : int32 fd = PG_GETARG_INT32(0);
3834 tgl 577 GIC 18 : int32 len = PG_GETARG_INT32(1);
578 :
279 michael 579 GNC 18 : PreventCommandIfReadOnly("lo_truncate()");
580 :
3834 tgl 581 CBC 15 : lo_truncate_internal(fd, len);
3836 ishii 582 GBC 15 : PG_RETURN_INT32(0);
583 : }
584 :
585 : Datum
2294 peter_e 586 GIC 9 : be_lo_truncate64(PG_FUNCTION_ARGS)
3836 ishii 587 ECB : {
3836 ishii 588 CBC 9 : int32 fd = PG_GETARG_INT32(0);
3836 ishii 589 GIC 9 : int64 len = PG_GETARG_INT64(1);
590 :
279 michael 591 GNC 9 : PreventCommandIfReadOnly("lo_truncate64()");
592 :
3834 tgl 593 CBC 6 : lo_truncate_internal(fd, len);
5881 bruce 594 GIC 6 : PG_RETURN_INT32(0);
5881 bruce 595 ECB : }
596 :
597 : /*
6829 tgl 598 : * AtEOXact_LargeObject -
599 : * prepares large objects for transaction commit
9028 bruce 600 : */
8986 601 : void
6829 tgl 602 GIC 486069 : AtEOXact_LargeObject(bool isCommit)
603 : {
604 : int i;
9028 bruce 605 ECB :
522 heikki.linnakangas 606 GIC 486069 : if (!lo_cleanup_needed)
8714 tgl 607 CBC 485846 : return; /* no LO operations in this xact */
8986 bruce 608 ECB :
609 : /*
6385 610 : * Close LO fds and clear cookies array so that LO fds are no longer good.
611 : * The memory context and resource owner holding them are going away at
522 heikki.linnakangas 612 : * the end-of-transaction anyway, but on commit, we need to close them to
613 : * avoid warnings about leaked resources at commit. On abort we can skip
614 : * this step.
615 : */
522 heikki.linnakangas 616 GIC 223 : if (isCommit)
617 : {
618 4048 : for (i = 0; i < cookies_size; i++)
619 : {
620 3904 : if (cookies[i] != NULL)
522 heikki.linnakangas 621 CBC 36 : closeLOfd(i);
622 : }
623 : }
624 :
8202 tgl 625 ECB : /* Needn't actually pfree since we're about to zap context */
8202 tgl 626 CBC 223 : cookies = NULL;
8202 tgl 627 GIC 223 : cookies_size = 0;
628 :
629 : /* Release the LO memory context to prevent permanent memory leaks. */
522 heikki.linnakangas 630 223 : if (fscxt)
631 132 : MemoryContextDelete(fscxt);
8714 tgl 632 223 : fscxt = NULL;
633 :
634 : /* Give inv_api.c a chance to clean up, too */
6829 tgl 635 CBC 223 : close_lo_relation(isCommit);
636 :
522 heikki.linnakangas 637 223 : lo_cleanup_needed = false;
638 : }
9028 bruce 639 ECB :
6829 tgl 640 : /*
641 : * AtEOSubXact_LargeObject
642 : * Take care of large objects at subtransaction commit/abort
643 : *
644 : * Reassign LOs created/opened during a committing subtransaction
6779 645 : * to the parent subtransaction. On abort, just close them.
6829 646 : */
647 : void
6779 tgl 648 GIC 8786 : AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
6779 tgl 649 ECB : SubTransactionId parentSubid)
6829 650 : {
6797 bruce 651 : int i;
652 :
6829 tgl 653 GIC 8786 : if (fscxt == NULL) /* no LO operations in this xact */
6829 tgl 654 CBC 8786 : return;
655 :
6829 tgl 656 LBC 0 : for (i = 0; i < cookies_size; i++)
657 : {
6829 tgl 658 UIC 0 : LargeObjectDesc *lo = cookies[i];
659 :
6779 660 0 : if (lo != NULL && lo->subid == mySubid)
661 : {
6829 662 0 : if (isCommit)
6779 663 0 : lo->subid = parentSubid;
664 : else
522 heikki.linnakangas 665 0 : closeLOfd(i);
666 : }
6829 tgl 667 ECB : }
668 : }
669 :
670 : /*****************************************************************************
671 : * Support routines for this file
9770 scrappy 672 : *****************************************************************************/
673 :
674 : static int
522 heikki.linnakangas 675 GBC 174 : newLOfd(void)
676 : {
8202 tgl 677 EUB : int i,
678 : newsize;
9345 bruce 679 :
522 heikki.linnakangas 680 GIC 174 : lo_cleanup_needed = true;
522 heikki.linnakangas 681 GBC 174 : if (fscxt == NULL)
682 132 : fscxt = AllocSetContextCreate(TopMemoryContext,
683 : "Filesystem",
522 heikki.linnakangas 684 EUB : ALLOCSET_DEFAULT_SIZES);
685 :
686 : /* Try to find a free slot */
8202 tgl 687 GIC 174 : for (i = 0; i < cookies_size; i++)
688 : {
9345 bruce 689 42 : if (cookies[i] == NULL)
690 42 : return i;
691 : }
692 :
693 : /* No free slot, so make the array bigger */
8202 tgl 694 CBC 132 : if (cookies_size <= 0)
695 : {
696 : /* First time through, arbitrarily make 64-element array */
8202 tgl 697 GIC 132 : i = 0;
698 132 : newsize = 64;
8202 tgl 699 CBC 132 : cookies = (LargeObjectDesc **)
6192 700 132 : MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
701 : }
702 : else
703 : {
704 : /* Double size of array */
8202 tgl 705 LBC 0 : i = cookies_size;
8202 tgl 706 UIC 0 : newsize = cookies_size * 2;
148 peter 707 UNC 0 : cookies =
708 0 : repalloc0_array(cookies, LargeObjectDesc *, cookies_size, newsize);
8202 tgl 709 ECB : }
148 peter 710 GNC 132 : cookies_size = newsize;
711 :
8202 tgl 712 GIC 132 : return i;
9770 scrappy 713 ECB : }
714 :
9345 bruce 715 : static void
522 heikki.linnakangas 716 CBC 141 : closeLOfd(int fd)
717 : {
718 : LargeObjectDesc *lobj;
719 :
720 : /*
522 heikki.linnakangas 721 EUB : * Make sure we do not try to free twice if this errors out for some
722 : * reason. Better a leak than a crash.
723 : */
522 heikki.linnakangas 724 GBC 141 : lobj = cookies[fd];
9345 bruce 725 GIC 141 : cookies[fd] = NULL;
522 heikki.linnakangas 726 ECB :
522 heikki.linnakangas 727 GIC 141 : if (lobj->snapshot)
522 heikki.linnakangas 728 CBC 101 : UnregisterSnapshotFromOwner(lobj->snapshot,
729 : TopTransactionResourceOwner);
522 heikki.linnakangas 730 GIC 141 : inv_close(lobj);
9770 scrappy 731 141 : }
3451 noah 732 ECB :
733 : /*****************************************************************************
734 : * Wrappers oriented toward SQL callers
735 : *****************************************************************************/
736 :
737 : /*
738 : * Read [offset, offset+nbytes) within LO; when nbytes is -1, read to end.
739 : */
740 : static bytea *
3451 noah 741 CBC 36 : lo_get_fragment_internal(Oid loOid, int64 offset, int32 nbytes)
742 : {
3451 noah 743 ECB : LargeObjectDesc *loDesc;
744 : int64 loSize;
745 : int64 result_length;
2118 tgl 746 : int total_read PG_USED_FOR_ASSERTS_ONLY;
3451 noah 747 CBC 36 : bytea *result = NULL;
748 :
522 heikki.linnakangas 749 GIC 36 : lo_cleanup_needed = true;
750 36 : loDesc = inv_open(loOid, INV_READ, CurrentMemoryContext);
751 :
752 : /*
753 : * Compute number of bytes we'll actually read, accommodating nbytes == -1
754 : * and reads beyond the end of the LO.
755 : */
3451 noah 756 34 : loSize = inv_seek(loDesc, 0, SEEK_END);
3451 noah 757 CBC 34 : if (loSize > offset)
758 : {
3451 noah 759 GIC 30 : if (nbytes >= 0 && nbytes <= loSize - offset)
2118 tgl 760 9 : result_length = nbytes; /* request is wholly inside LO */
761 : else
3451 noah 762 21 : result_length = loSize - offset; /* adjust to end of LO */
3451 noah 763 ECB : }
764 : else
3451 noah 765 CBC 4 : result_length = 0; /* request is wholly outside LO */
3451 noah 766 ECB :
767 : /*
768 : * A result_length calculated from loSize may not fit in a size_t. Check
769 : * that the size will satisfy this and subsequently-enforced size limits.
770 : */
3451 noah 771 GIC 34 : if (result_length > MaxAllocSize - VARHDRSZ)
3451 noah 772 CBC 3 : ereport(ERROR,
3451 noah 773 ECB : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
774 : errmsg("large object read request is too large")));
775 :
3451 noah 776 CBC 31 : result = (bytea *) palloc(VARHDRSZ + result_length);
777 :
778 31 : inv_seek(loDesc, offset, SEEK_SET);
3451 noah 779 GIC 31 : total_read = inv_read(loDesc, VARDATA(result), result_length);
780 31 : Assert(total_read == result_length);
3451 noah 781 CBC 31 : SET_VARSIZE(result, result_length + VARHDRSZ);
782 :
3451 noah 783 GIC 31 : inv_close(loDesc);
784 :
785 31 : return result;
786 : }
3451 noah 787 ECB :
788 : /*
789 : * Read entire LO
790 : */
791 : Datum
2294 peter_e 792 CBC 24 : be_lo_get(PG_FUNCTION_ARGS)
793 : {
3451 noah 794 24 : Oid loOid = PG_GETARG_OID(0);
3451 noah 795 ECB : bytea *result;
796 :
3451 noah 797 CBC 24 : result = lo_get_fragment_internal(loOid, 0, -1);
798 :
799 19 : PG_RETURN_BYTEA_P(result);
800 : }
3451 noah 801 ECB :
802 : /*
803 : * Read range within LO
804 : */
805 : Datum
2294 peter_e 806 GIC 12 : be_lo_get_fragment(PG_FUNCTION_ARGS)
807 : {
3451 noah 808 CBC 12 : Oid loOid = PG_GETARG_OID(0);
3451 noah 809 GIC 12 : int64 offset = PG_GETARG_INT64(1);
3451 noah 810 CBC 12 : int32 nbytes = PG_GETARG_INT32(2);
811 : bytea *result;
812 :
813 12 : if (nbytes < 0)
3451 noah 814 UIC 0 : ereport(ERROR,
3451 noah 815 ECB : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
816 : errmsg("requested length cannot be negative")));
817 :
3451 noah 818 GIC 12 : result = lo_get_fragment_internal(loOid, offset, nbytes);
819 :
820 12 : PG_RETURN_BYTEA_P(result);
821 : }
3451 noah 822 ECB :
823 : /*
3223 tgl 824 : * Create LO with initial contents given by a bytea argument
3451 noah 825 : */
826 : Datum
2294 peter_e 827 GIC 13 : be_lo_from_bytea(PG_FUNCTION_ARGS)
828 : {
3451 noah 829 CBC 13 : Oid loOid = PG_GETARG_OID(0);
3451 noah 830 GBC 13 : bytea *str = PG_GETARG_BYTEA_PP(1);
831 : LargeObjectDesc *loDesc;
832 : int written PG_USED_FOR_ASSERTS_ONLY;
833 :
279 michael 834 GNC 13 : PreventCommandIfReadOnly("lo_from_bytea()");
835 :
522 heikki.linnakangas 836 CBC 10 : lo_cleanup_needed = true;
3451 noah 837 GIC 10 : loOid = inv_create(loOid);
522 heikki.linnakangas 838 CBC 10 : loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
3451 noah 839 GIC 10 : written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
840 10 : Assert(written == VARSIZE_ANY_EXHDR(str));
841 10 : inv_close(loDesc);
842 :
843 10 : PG_RETURN_OID(loOid);
844 : }
3451 noah 845 ECB :
846 : /*
847 : * Update range within LO
848 : */
849 : Datum
2294 peter_e 850 GIC 12 : be_lo_put(PG_FUNCTION_ARGS)
851 : {
3451 noah 852 CBC 12 : Oid loOid = PG_GETARG_OID(0);
3451 noah 853 GIC 12 : int64 offset = PG_GETARG_INT64(1);
3451 noah 854 CBC 12 : bytea *str = PG_GETARG_BYTEA_PP(2);
3451 noah 855 ECB : LargeObjectDesc *loDesc;
2118 tgl 856 : int written PG_USED_FOR_ASSERTS_ONLY;
3451 noah 857 :
279 michael 858 GNC 12 : PreventCommandIfReadOnly("lo_put()");
859 :
522 heikki.linnakangas 860 CBC 9 : lo_cleanup_needed = true;
861 9 : loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
862 :
2071 tgl 863 ECB : /* Permission check */
2071 tgl 864 GIC 12 : if (!lo_compat_privileges &&
865 6 : pg_largeobject_aclcheck_snapshot(loDesc->id,
866 : GetUserId(),
867 : ACL_UPDATE,
868 : loDesc->snapshot) != ACLCHECK_OK)
2071 tgl 869 UIC 0 : ereport(ERROR,
2071 tgl 870 ECB : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
871 : errmsg("permission denied for large object %u",
872 : loDesc->id)));
873 :
3451 noah 874 CBC 6 : inv_seek(loDesc, offset, SEEK_SET);
3451 noah 875 GIC 6 : written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
876 6 : Assert(written == VARSIZE_ANY_EXHDR(str));
877 6 : inv_close(loDesc);
3451 noah 878 ECB :
3451 noah 879 GIC 6 : PG_RETURN_VOID();
3451 noah 880 ECB : }
|