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