1 /*-------------------------------------------------------------------------
4 * Builtin functions for open/close/read/write operations on large objects
6 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/libpq/be-fsstubs.c
14 * This should be moved to a more appropriate place. It is here
15 * for lack of a better place.
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
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
36 *-------------------------------------------------------------------------
45 #include "libpq/be-fsstubs.h"
46 #include "libpq/libpq-fs.h"
47 #include "miscadmin.h"
48 #include "storage/fd.h"
49 #include "storage/large_object.h"
50 #include "utils/acl.h"
51 #include "utils/builtins.h"
52 #include "utils/memutils.h"
54 /* define this to enable debug logging */
56 /* chunk size for lo_import/lo_export transfers */
60 * LO "FD"s are indexes into the cookies array.
62 * A non-null entry is a pointer to a LargeObjectDesc allocated in the
63 * LO private memory context "fscxt". The cookies array itself is also
64 * dynamically allocated in that context. Its current allocated size is
65 * cookies_size entries, of which any unused entries will be NULL.
67 static LargeObjectDesc **cookies = NULL;
68 static int cookies_size = 0;
70 static MemoryContext fscxt = NULL;
72 #define CreateFSContext() \
75 fscxt = AllocSetContextCreate(TopMemoryContext, \
77 ALLOCSET_DEFAULT_SIZES); \
81 static int newLOfd(LargeObjectDesc *lobjCookie);
82 static void deleteLOfd(int fd);
83 static Oid lo_import_internal(text *filename, Oid lobjOid);
86 /*****************************************************************************
87 * File Interfaces for Large Objects
88 *****************************************************************************/
91 be_lo_open(PG_FUNCTION_ARGS)
93 Oid lobjId = PG_GETARG_OID(0);
94 int32 mode = PG_GETARG_INT32(1);
95 LargeObjectDesc *lobjDesc;
99 elog(DEBUG4, "lo_open(%u,%d)", lobjId, mode);
104 lobjDesc = inv_open(lobjId, mode, fscxt);
106 fd = newLOfd(lobjDesc);
112 be_lo_close(PG_FUNCTION_ARGS)
114 int32 fd = PG_GETARG_INT32(0);
116 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
118 (errcode(ERRCODE_UNDEFINED_OBJECT),
119 errmsg("invalid large-object descriptor: %d", fd)));
122 elog(DEBUG4, "lo_close(%d)", fd);
125 inv_close(cookies[fd]);
133 /*****************************************************************************
134 * Bare Read/Write operations --- these are not fmgr-callable!
136 * We assume the large object supports byte oriented reads and seeks so
137 * that our work is easier.
139 *****************************************************************************/
142 lo_read(int fd, char *buf, int len)
145 LargeObjectDesc *lobj;
147 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
149 (errcode(ERRCODE_UNDEFINED_OBJECT),
150 errmsg("invalid large-object descriptor: %d", fd)));
154 * Check state. inv_read() would throw an error anyway, but we want the
155 * error to be about the FD's state not the underlying privilege; it might
156 * be that the privilege exists but user forgot to ask for read mode.
158 if ((lobj->flags & IFS_RDLOCK) == 0)
160 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
161 errmsg("large object descriptor %d was not opened for reading",
164 status = inv_read(lobj, buf, len);
170 lo_write(int fd, const char *buf, int len)
173 LargeObjectDesc *lobj;
175 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
177 (errcode(ERRCODE_UNDEFINED_OBJECT),
178 errmsg("invalid large-object descriptor: %d", fd)));
181 /* see comment in lo_read() */
182 if ((lobj->flags & IFS_WRLOCK) == 0)
184 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
185 errmsg("large object descriptor %d was not opened for writing",
188 status = inv_write(lobj, buf, len);
194 be_lo_lseek(PG_FUNCTION_ARGS)
196 int32 fd = PG_GETARG_INT32(0);
197 int32 offset = PG_GETARG_INT32(1);
198 int32 whence = PG_GETARG_INT32(2);
201 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
203 (errcode(ERRCODE_UNDEFINED_OBJECT),
204 errmsg("invalid large-object descriptor: %d", fd)));
206 status = inv_seek(cookies[fd], offset, whence);
208 /* guard against result overflow */
209 if (status != (int32) status)
211 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
212 errmsg("lo_lseek result out of range for large-object descriptor %d",
215 PG_RETURN_INT32((int32) status);
219 be_lo_lseek64(PG_FUNCTION_ARGS)
221 int32 fd = PG_GETARG_INT32(0);
222 int64 offset = PG_GETARG_INT64(1);
223 int32 whence = PG_GETARG_INT32(2);
226 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
228 (errcode(ERRCODE_UNDEFINED_OBJECT),
229 errmsg("invalid large-object descriptor: %d", fd)));
231 status = inv_seek(cookies[fd], offset, whence);
233 PG_RETURN_INT64(status);
237 be_lo_creat(PG_FUNCTION_ARGS)
242 * We don't actually need to store into fscxt, but create it anyway to
243 * ensure that AtEOXact_LargeObject knows there is state to clean up
247 lobjId = inv_create(InvalidOid);
249 PG_RETURN_OID(lobjId);
253 be_lo_create(PG_FUNCTION_ARGS)
255 Oid lobjId = PG_GETARG_OID(0);
258 * We don't actually need to store into fscxt, but create it anyway to
259 * ensure that AtEOXact_LargeObject knows there is state to clean up
263 lobjId = inv_create(lobjId);
265 PG_RETURN_OID(lobjId);
269 be_lo_tell(PG_FUNCTION_ARGS)
271 int32 fd = PG_GETARG_INT32(0);
274 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
276 (errcode(ERRCODE_UNDEFINED_OBJECT),
277 errmsg("invalid large-object descriptor: %d", fd)));
279 offset = inv_tell(cookies[fd]);
281 /* guard against result overflow */
282 if (offset != (int32) offset)
284 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
285 errmsg("lo_tell result out of range for large-object descriptor %d",
288 PG_RETURN_INT32((int32) offset);
292 be_lo_tell64(PG_FUNCTION_ARGS)
294 int32 fd = PG_GETARG_INT32(0);
297 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
299 (errcode(ERRCODE_UNDEFINED_OBJECT),
300 errmsg("invalid large-object descriptor: %d", fd)));
302 offset = inv_tell(cookies[fd]);
304 PG_RETURN_INT64(offset);
308 be_lo_unlink(PG_FUNCTION_ARGS)
310 Oid lobjId = PG_GETARG_OID(0);
313 * Must be owner of the large object. It would be cleaner to check this
314 * in inv_drop(), but we want to throw the error before not after closing
317 if (!lo_compat_privileges &&
318 !pg_largeobject_ownercheck(lobjId, GetUserId()))
320 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
321 errmsg("must be owner of large object %u", lobjId)));
324 * If there are any open LO FDs referencing that ID, close 'em.
330 for (i = 0; i < cookies_size; i++)
332 if (cookies[i] != NULL && cookies[i]->id == lobjId)
334 inv_close(cookies[i]);
341 * inv_drop does not create a need for end-of-transaction cleanup and
342 * hence we don't need to have created fscxt.
344 PG_RETURN_INT32(inv_drop(lobjId));
347 /*****************************************************************************
348 * Read/Write using bytea
349 *****************************************************************************/
352 be_loread(PG_FUNCTION_ARGS)
354 int32 fd = PG_GETARG_INT32(0);
355 int32 len = PG_GETARG_INT32(1);
362 retval = (bytea *) palloc(VARHDRSZ + len);
363 totalread = lo_read(fd, VARDATA(retval), len);
364 SET_VARSIZE(retval, totalread + VARHDRSZ);
366 PG_RETURN_BYTEA_P(retval);
370 be_lowrite(PG_FUNCTION_ARGS)
372 int32 fd = PG_GETARG_INT32(0);
373 bytea *wbuf = PG_GETARG_BYTEA_PP(1);
377 bytestowrite = VARSIZE_ANY_EXHDR(wbuf);
378 totalwritten = lo_write(fd, VARDATA_ANY(wbuf), bytestowrite);
379 PG_RETURN_INT32(totalwritten);
382 /*****************************************************************************
383 * Import/Export of Large Object
384 *****************************************************************************/
388 * imports a file as an (inversion) large object.
391 be_lo_import(PG_FUNCTION_ARGS)
393 text *filename = PG_GETARG_TEXT_PP(0);
395 PG_RETURN_OID(lo_import_internal(filename, InvalidOid));
399 * lo_import_with_oid -
400 * imports a file as an (inversion) large object specifying oid.
403 be_lo_import_with_oid(PG_FUNCTION_ARGS)
405 text *filename = PG_GETARG_TEXT_PP(0);
406 Oid oid = PG_GETARG_OID(1);
408 PG_RETURN_OID(lo_import_internal(filename, oid));
412 lo_import_internal(text *filename, Oid lobjOid)
416 tmp PG_USED_FOR_ASSERTS_ONLY;
418 char fnamebuf[MAXPGPATH];
419 LargeObjectDesc *lobj;
425 * open the file to be read in
427 text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
428 fd = OpenTransientFile(fnamebuf, O_RDONLY | PG_BINARY);
431 (errcode_for_file_access(),
432 errmsg("could not open server file \"%s\": %m",
436 * create an inversion object
438 oid = inv_create(lobjOid);
441 * read in from the filesystem and write to the inversion object
443 lobj = inv_open(oid, INV_WRITE, fscxt);
445 while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
447 tmp = inv_write(lobj, buf, nbytes);
448 Assert(tmp == nbytes);
453 (errcode_for_file_access(),
454 errmsg("could not read server file \"%s\": %m",
459 if (CloseTransientFile(fd) != 0)
461 (errcode_for_file_access(),
462 errmsg("could not close file \"%s\": %m",
470 * exports an (inversion) large object.
473 be_lo_export(PG_FUNCTION_ARGS)
475 Oid lobjId = PG_GETARG_OID(0);
476 text *filename = PG_GETARG_TEXT_PP(1);
481 char fnamebuf[MAXPGPATH];
482 LargeObjectDesc *lobj;
488 * open the inversion object (no need to test for failure)
490 lobj = inv_open(lobjId, INV_READ, fscxt);
493 * open the file to be written to
495 * Note: we reduce backend's normal 077 umask to the slightly friendlier
496 * 022. This code used to drop it all the way to 0, but creating
497 * world-writable export files doesn't seem wise.
499 text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
500 oumask = umask(S_IWGRP | S_IWOTH);
503 fd = OpenTransientFilePerm(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
504 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
515 (errcode_for_file_access(),
516 errmsg("could not create server file \"%s\": %m",
520 * read in from the inversion file and write to the filesystem
522 while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
524 tmp = write(fd, buf, nbytes);
527 (errcode_for_file_access(),
528 errmsg("could not write server file \"%s\": %m",
532 if (CloseTransientFile(fd) != 0)
534 (errcode_for_file_access(),
535 errmsg("could not close file \"%s\": %m",
545 * truncate a large object to a specified length
548 lo_truncate_internal(int32 fd, int64 len)
550 LargeObjectDesc *lobj;
552 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
554 (errcode(ERRCODE_UNDEFINED_OBJECT),
555 errmsg("invalid large-object descriptor: %d", fd)));
558 /* see comment in lo_read() */
559 if ((lobj->flags & IFS_WRLOCK) == 0)
561 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
562 errmsg("large object descriptor %d was not opened for writing",
565 inv_truncate(lobj, len);
569 be_lo_truncate(PG_FUNCTION_ARGS)
571 int32 fd = PG_GETARG_INT32(0);
572 int32 len = PG_GETARG_INT32(1);
574 lo_truncate_internal(fd, len);
579 be_lo_truncate64(PG_FUNCTION_ARGS)
581 int32 fd = PG_GETARG_INT32(0);
582 int64 len = PG_GETARG_INT64(1);
584 lo_truncate_internal(fd, len);
589 * AtEOXact_LargeObject -
590 * prepares large objects for transaction commit
593 AtEOXact_LargeObject(bool isCommit)
598 return; /* no LO operations in this xact */
601 * Close LO fds and clear cookies array so that LO fds are no longer good.
602 * On abort we skip the close step.
604 for (i = 0; i < cookies_size; i++)
606 if (cookies[i] != NULL)
609 inv_close(cookies[i]);
614 /* Needn't actually pfree since we're about to zap context */
618 /* Release the LO memory context to prevent permanent memory leaks. */
619 MemoryContextDelete(fscxt);
622 /* Give inv_api.c a chance to clean up, too */
623 close_lo_relation(isCommit);
627 * AtEOSubXact_LargeObject
628 * Take care of large objects at subtransaction commit/abort
630 * Reassign LOs created/opened during a committing subtransaction
631 * to the parent subtransaction. On abort, just close them.
634 AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
635 SubTransactionId parentSubid)
639 if (fscxt == NULL) /* no LO operations in this xact */
642 for (i = 0; i < cookies_size; i++)
644 LargeObjectDesc *lo = cookies[i];
646 if (lo != NULL && lo->subid == mySubid)
649 lo->subid = parentSubid;
653 * Make sure we do not call inv_close twice if it errors out
654 * for some reason. Better a leak than a crash.
663 /*****************************************************************************
664 * Support routines for this file
665 *****************************************************************************/
668 newLOfd(LargeObjectDesc *lobjCookie)
673 /* Try to find a free slot */
674 for (i = 0; i < cookies_size; i++)
676 if (cookies[i] == NULL)
678 cookies[i] = lobjCookie;
683 /* No free slot, so make the array bigger */
684 if (cookies_size <= 0)
686 /* First time through, arbitrarily make 64-element array */
689 cookies = (LargeObjectDesc **)
690 MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
691 cookies_size = newsize;
695 /* Double size of array */
697 newsize = cookies_size * 2;
698 cookies = (LargeObjectDesc **)
699 repalloc(cookies, newsize * sizeof(LargeObjectDesc *));
700 MemSet(cookies + cookies_size, 0,
701 (newsize - cookies_size) * sizeof(LargeObjectDesc *));
702 cookies_size = newsize;
705 Assert(cookies[i] == NULL);
706 cookies[i] = lobjCookie;
716 /*****************************************************************************
717 * Wrappers oriented toward SQL callers
718 *****************************************************************************/
721 * Read [offset, offset+nbytes) within LO; when nbytes is -1, read to end.
724 lo_get_fragment_internal(Oid loOid, int64 offset, int32 nbytes)
726 LargeObjectDesc *loDesc;
729 int total_read PG_USED_FOR_ASSERTS_ONLY;
730 bytea *result = NULL;
733 * We don't actually need to store into fscxt, but create it anyway to
734 * ensure that AtEOXact_LargeObject knows there is state to clean up
738 loDesc = inv_open(loOid, INV_READ, fscxt);
741 * Compute number of bytes we'll actually read, accommodating nbytes == -1
742 * and reads beyond the end of the LO.
744 loSize = inv_seek(loDesc, 0, SEEK_END);
747 if (nbytes >= 0 && nbytes <= loSize - offset)
748 result_length = nbytes; /* request is wholly inside LO */
750 result_length = loSize - offset; /* adjust to end of LO */
753 result_length = 0; /* request is wholly outside LO */
756 * A result_length calculated from loSize may not fit in a size_t. Check
757 * that the size will satisfy this and subsequently-enforced size limits.
759 if (result_length > MaxAllocSize - VARHDRSZ)
761 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
762 errmsg("large object read request is too large")));
764 result = (bytea *) palloc(VARHDRSZ + result_length);
766 inv_seek(loDesc, offset, SEEK_SET);
767 total_read = inv_read(loDesc, VARDATA(result), result_length);
768 Assert(total_read == result_length);
769 SET_VARSIZE(result, result_length + VARHDRSZ);
780 be_lo_get(PG_FUNCTION_ARGS)
782 Oid loOid = PG_GETARG_OID(0);
785 result = lo_get_fragment_internal(loOid, 0, -1);
787 PG_RETURN_BYTEA_P(result);
791 * Read range within LO
794 be_lo_get_fragment(PG_FUNCTION_ARGS)
796 Oid loOid = PG_GETARG_OID(0);
797 int64 offset = PG_GETARG_INT64(1);
798 int32 nbytes = PG_GETARG_INT32(2);
803 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
804 errmsg("requested length cannot be negative")));
806 result = lo_get_fragment_internal(loOid, offset, nbytes);
808 PG_RETURN_BYTEA_P(result);
812 * Create LO with initial contents given by a bytea argument
815 be_lo_from_bytea(PG_FUNCTION_ARGS)
817 Oid loOid = PG_GETARG_OID(0);
818 bytea *str = PG_GETARG_BYTEA_PP(1);
819 LargeObjectDesc *loDesc;
820 int written PG_USED_FOR_ASSERTS_ONLY;
824 loOid = inv_create(loOid);
825 loDesc = inv_open(loOid, INV_WRITE, fscxt);
826 written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
827 Assert(written == VARSIZE_ANY_EXHDR(str));
830 PG_RETURN_OID(loOid);
834 * Update range within LO
837 be_lo_put(PG_FUNCTION_ARGS)
839 Oid loOid = PG_GETARG_OID(0);
840 int64 offset = PG_GETARG_INT64(1);
841 bytea *str = PG_GETARG_BYTEA_PP(2);
842 LargeObjectDesc *loDesc;
843 int written PG_USED_FOR_ASSERTS_ONLY;
847 loDesc = inv_open(loOid, INV_WRITE, fscxt);
849 /* Permission check */
850 if (!lo_compat_privileges &&
851 pg_largeobject_aclcheck_snapshot(loDesc->id,
854 loDesc->snapshot) != ACLCHECK_OK)
856 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
857 errmsg("permission denied for large object %u",
860 inv_seek(loDesc, offset, SEEK_SET);
861 written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
862 Assert(written == VARSIZE_ANY_EXHDR(str));