1 /*-------------------------------------------------------------------------
4 * Builtin functions for open/close/read/write operations on large objects
6 * Portions Copyright (c) 1996-2012, 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"
55 * compatibility flag for permission checks
57 bool lo_compat_privileges;
63 * LO "FD"s are indexes into the cookies array.
65 * A non-null entry is a pointer to a LargeObjectDesc allocated in the
66 * LO private memory context "fscxt". The cookies array itself is also
67 * dynamically allocated in that context. Its current allocated size is
68 * cookies_len entries, of which any unused entries will be NULL.
70 static LargeObjectDesc **cookies = NULL;
71 static int cookies_size = 0;
73 static MemoryContext fscxt = NULL;
75 #define CreateFSContext() \
78 fscxt = AllocSetContextCreate(TopMemoryContext, \
80 ALLOCSET_DEFAULT_MINSIZE, \
81 ALLOCSET_DEFAULT_INITSIZE, \
82 ALLOCSET_DEFAULT_MAXSIZE); \
86 static int newLOfd(LargeObjectDesc *lobjCookie);
87 static void deleteLOfd(int fd);
88 static Oid lo_import_internal(text *filename, Oid lobjOid);
91 /*****************************************************************************
92 * File Interfaces for Large Objects
93 *****************************************************************************/
96 lo_open(PG_FUNCTION_ARGS)
98 Oid lobjId = PG_GETARG_OID(0);
99 int32 mode = PG_GETARG_INT32(1);
100 LargeObjectDesc *lobjDesc;
104 elog(DEBUG4, "lo_open(%u,%d)", lobjId, mode);
109 lobjDesc = inv_open(lobjId, mode, fscxt);
111 if (lobjDesc == NULL)
112 { /* lookup failed */
114 elog(DEBUG4, "could not open large object %u", lobjId);
119 fd = newLOfd(lobjDesc);
125 lo_close(PG_FUNCTION_ARGS)
127 int32 fd = PG_GETARG_INT32(0);
129 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
131 (errcode(ERRCODE_UNDEFINED_OBJECT),
132 errmsg("invalid large-object descriptor: %d", fd)));
135 elog(DEBUG4, "lo_close(%d)", fd);
138 inv_close(cookies[fd]);
146 /*****************************************************************************
147 * Bare Read/Write operations --- these are not fmgr-callable!
149 * We assume the large object supports byte oriented reads and seeks so
150 * that our work is easier.
152 *****************************************************************************/
155 lo_read(int fd, char *buf, int len)
159 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
161 (errcode(ERRCODE_UNDEFINED_OBJECT),
162 errmsg("invalid large-object descriptor: %d", fd)));
164 /* Permission checks */
165 if (!lo_compat_privileges &&
166 pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
169 cookies[fd]->snapshot) != ACLCHECK_OK)
171 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
172 errmsg("permission denied for large object %u",
175 status = inv_read(cookies[fd], buf, len);
181 lo_write(int fd, const char *buf, int len)
185 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
187 (errcode(ERRCODE_UNDEFINED_OBJECT),
188 errmsg("invalid large-object descriptor: %d", fd)));
190 if ((cookies[fd]->flags & IFS_WRLOCK) == 0)
192 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
193 errmsg("large object descriptor %d was not opened for writing",
196 /* Permission checks */
197 if (!lo_compat_privileges &&
198 pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
201 cookies[fd]->snapshot) != ACLCHECK_OK)
203 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
204 errmsg("permission denied for large object %u",
207 status = inv_write(cookies[fd], buf, len);
214 lo_lseek(PG_FUNCTION_ARGS)
216 int32 fd = PG_GETARG_INT32(0);
217 int32 offset = PG_GETARG_INT32(1);
218 int32 whence = PG_GETARG_INT32(2);
221 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
223 (errcode(ERRCODE_UNDEFINED_OBJECT),
224 errmsg("invalid large-object descriptor: %d", fd)));
226 status = inv_seek(cookies[fd], offset, whence);
228 PG_RETURN_INT32(status);
232 lo_creat(PG_FUNCTION_ARGS)
237 * We don't actually need to store into fscxt, but create it anyway to
238 * ensure that AtEOXact_LargeObject knows there is state to clean up
242 lobjId = inv_create(InvalidOid);
244 PG_RETURN_OID(lobjId);
248 lo_create(PG_FUNCTION_ARGS)
250 Oid lobjId = PG_GETARG_OID(0);
253 * We don't actually need to store into fscxt, but create it anyway to
254 * ensure that AtEOXact_LargeObject knows there is state to clean up
258 lobjId = inv_create(lobjId);
260 PG_RETURN_OID(lobjId);
264 lo_tell(PG_FUNCTION_ARGS)
266 int32 fd = PG_GETARG_INT32(0);
268 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
270 (errcode(ERRCODE_UNDEFINED_OBJECT),
271 errmsg("invalid large-object descriptor: %d", fd)));
273 PG_RETURN_INT32(inv_tell(cookies[fd]));
277 lo_unlink(PG_FUNCTION_ARGS)
279 Oid lobjId = PG_GETARG_OID(0);
281 /* Must be owner of the largeobject */
282 if (!lo_compat_privileges &&
283 !pg_largeobject_ownercheck(lobjId, GetUserId()))
285 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
286 errmsg("must be owner of large object %u", lobjId)));
289 * If there are any open LO FDs referencing that ID, close 'em.
295 for (i = 0; i < cookies_size; i++)
297 if (cookies[i] != NULL && cookies[i]->id == lobjId)
299 inv_close(cookies[i]);
306 * inv_drop does not create a need for end-of-transaction cleanup and
307 * hence we don't need to have created fscxt.
309 PG_RETURN_INT32(inv_drop(lobjId));
312 /*****************************************************************************
313 * Read/Write using bytea
314 *****************************************************************************/
317 loread(PG_FUNCTION_ARGS)
319 int32 fd = PG_GETARG_INT32(0);
320 int32 len = PG_GETARG_INT32(1);
327 retval = (bytea *) palloc(VARHDRSZ + len);
328 totalread = lo_read(fd, VARDATA(retval), len);
329 SET_VARSIZE(retval, totalread + VARHDRSZ);
331 PG_RETURN_BYTEA_P(retval);
335 lowrite(PG_FUNCTION_ARGS)
337 int32 fd = PG_GETARG_INT32(0);
338 bytea *wbuf = PG_GETARG_BYTEA_P(1);
342 bytestowrite = VARSIZE(wbuf) - VARHDRSZ;
343 totalwritten = lo_write(fd, VARDATA(wbuf), bytestowrite);
344 PG_RETURN_INT32(totalwritten);
347 /*****************************************************************************
348 * Import/Export of Large Object
349 *****************************************************************************/
353 * imports a file as an (inversion) large object.
356 lo_import(PG_FUNCTION_ARGS)
358 text *filename = PG_GETARG_TEXT_PP(0);
360 PG_RETURN_OID(lo_import_internal(filename, InvalidOid));
364 * lo_import_with_oid -
365 * imports a file as an (inversion) large object specifying oid.
368 lo_import_with_oid(PG_FUNCTION_ARGS)
370 text *filename = PG_GETARG_TEXT_PP(0);
371 Oid oid = PG_GETARG_OID(1);
373 PG_RETURN_OID(lo_import_internal(filename, oid));
377 lo_import_internal(text *filename, Oid lobjOid)
381 tmp PG_USED_FOR_ASSERTS_ONLY;
383 char fnamebuf[MAXPGPATH];
384 LargeObjectDesc *lobj;
387 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
390 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
391 errmsg("must be superuser to use server-side lo_import()"),
392 errhint("Anyone can use the client-side lo_import() provided by libpq.")));
398 * open the file to be read in
400 text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
401 fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, S_IRWXU);
404 (errcode_for_file_access(),
405 errmsg("could not open server file \"%s\": %m",
409 * create an inversion object
411 oid = inv_create(lobjOid);
414 * read in from the filesystem and write to the inversion object
416 lobj = inv_open(oid, INV_WRITE, fscxt);
418 while ((nbytes = FileRead(fd, buf, BUFSIZE)) > 0)
420 tmp = inv_write(lobj, buf, nbytes);
421 Assert(tmp == nbytes);
426 (errcode_for_file_access(),
427 errmsg("could not read server file \"%s\": %m",
438 * exports an (inversion) large object.
441 lo_export(PG_FUNCTION_ARGS)
443 Oid lobjId = PG_GETARG_OID(0);
444 text *filename = PG_GETARG_TEXT_PP(1);
449 char fnamebuf[MAXPGPATH];
450 LargeObjectDesc *lobj;
453 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
456 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
457 errmsg("must be superuser to use server-side lo_export()"),
458 errhint("Anyone can use the client-side lo_export() provided by libpq.")));
464 * open the inversion object (no need to test for failure)
466 lobj = inv_open(lobjId, INV_READ, fscxt);
469 * open the file to be written to
471 * Note: we reduce backend's normal 077 umask to the slightly friendlier
472 * 022. This code used to drop it all the way to 0, but creating
473 * world-writable export files doesn't seem wise.
475 text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
476 oumask = umask(S_IWGRP | S_IWOTH);
477 fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
478 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
482 (errcode_for_file_access(),
483 errmsg("could not create server file \"%s\": %m",
487 * read in from the inversion file and write to the filesystem
489 while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
491 tmp = FileWrite(fd, buf, nbytes);
494 (errcode_for_file_access(),
495 errmsg("could not write server file \"%s\": %m",
507 * truncate a large object to a specified length
510 lo_truncate(PG_FUNCTION_ARGS)
512 int32 fd = PG_GETARG_INT32(0);
513 int32 len = PG_GETARG_INT32(1);
515 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
517 (errcode(ERRCODE_UNDEFINED_OBJECT),
518 errmsg("invalid large-object descriptor: %d", fd)));
520 /* Permission checks */
521 if (!lo_compat_privileges &&
522 pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
525 cookies[fd]->snapshot) != ACLCHECK_OK)
527 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
528 errmsg("permission denied for large object %u",
531 inv_truncate(cookies[fd], len);
537 * AtEOXact_LargeObject -
538 * prepares large objects for transaction commit
541 AtEOXact_LargeObject(bool isCommit)
546 return; /* no LO operations in this xact */
549 * Close LO fds and clear cookies array so that LO fds are no longer good.
550 * On abort we skip the close step.
552 for (i = 0; i < cookies_size; i++)
554 if (cookies[i] != NULL)
557 inv_close(cookies[i]);
562 /* Needn't actually pfree since we're about to zap context */
566 /* Release the LO memory context to prevent permanent memory leaks. */
567 MemoryContextDelete(fscxt);
570 /* Give inv_api.c a chance to clean up, too */
571 close_lo_relation(isCommit);
575 * AtEOSubXact_LargeObject
576 * Take care of large objects at subtransaction commit/abort
578 * Reassign LOs created/opened during a committing subtransaction
579 * to the parent subtransaction. On abort, just close them.
582 AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
583 SubTransactionId parentSubid)
587 if (fscxt == NULL) /* no LO operations in this xact */
590 for (i = 0; i < cookies_size; i++)
592 LargeObjectDesc *lo = cookies[i];
594 if (lo != NULL && lo->subid == mySubid)
597 lo->subid = parentSubid;
601 * Make sure we do not call inv_close twice if it errors out
602 * for some reason. Better a leak than a crash.
611 /*****************************************************************************
612 * Support routines for this file
613 *****************************************************************************/
616 newLOfd(LargeObjectDesc *lobjCookie)
621 /* Try to find a free slot */
622 for (i = 0; i < cookies_size; i++)
624 if (cookies[i] == NULL)
626 cookies[i] = lobjCookie;
631 /* No free slot, so make the array bigger */
632 if (cookies_size <= 0)
634 /* First time through, arbitrarily make 64-element array */
637 cookies = (LargeObjectDesc **)
638 MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
639 cookies_size = newsize;
643 /* Double size of array */
645 newsize = cookies_size * 2;
646 cookies = (LargeObjectDesc **)
647 repalloc(cookies, newsize * sizeof(LargeObjectDesc *));
648 MemSet(cookies + cookies_size, 0,
649 (newsize - cookies_size) * sizeof(LargeObjectDesc *));
650 cookies_size = newsize;
653 Assert(cookies[i] == NULL);
654 cookies[i] = lobjCookie;