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