]> granicus.if.org Git - postgresql/blob - src/backend/libpq/be-fsstubs.c
Reduce hash size for compute_array_stats, compute_tsvector_stats.
[postgresql] / src / backend / libpq / be-fsstubs.c
1 /*-------------------------------------------------------------------------
2  *
3  * be-fsstubs.c
4  *        Builtin functions for open/close/read/write operations on large objects
5  *
6  * Portions Copyright (c) 1996-2012, 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 FSDB 1*/
60 #define BUFSIZE                 8192
61
62 /*
63  * LO "FD"s are indexes into the cookies array.
64  *
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.
69  */
70 static LargeObjectDesc **cookies = NULL;
71 static int      cookies_size = 0;
72
73 static MemoryContext fscxt = NULL;
74
75 #define CreateFSContext() \
76         do { \
77                 if (fscxt == NULL) \
78                         fscxt = AllocSetContextCreate(TopMemoryContext, \
79                                                                                   "Filesystem", \
80                                                                                   ALLOCSET_DEFAULT_MINSIZE, \
81                                                                                   ALLOCSET_DEFAULT_INITSIZE, \
82                                                                                   ALLOCSET_DEFAULT_MAXSIZE); \
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 lo_open(PG_FUNCTION_ARGS)
97 {
98         Oid                     lobjId = PG_GETARG_OID(0);
99         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         CreateFSContext();
108
109         lobjDesc = inv_open(lobjId, mode, fscxt);
110
111         if (lobjDesc == NULL)
112         {                                                       /* lookup failed */
113 #if FSDB
114                 elog(DEBUG4, "could not open large object %u", lobjId);
115 #endif
116                 PG_RETURN_INT32(-1);
117         }
118
119         fd = newLOfd(lobjDesc);
120
121         PG_RETURN_INT32(fd);
122 }
123
124 Datum
125 lo_close(PG_FUNCTION_ARGS)
126 {
127         int32           fd = PG_GETARG_INT32(0);
128
129         if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
130                 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         inv_close(cookies[fd]);
139
140         deleteLOfd(fd);
141
142         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 lo_read(int fd, char *buf, int len)
156 {
157         int                     status;
158
159         if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
160                 ereport(ERROR,
161                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
162                                  errmsg("invalid large-object descriptor: %d", fd)));
163
164         /* Permission checks */
165         if (!lo_compat_privileges &&
166                 pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
167                                                                                  GetUserId(),
168                                                                                  ACL_SELECT,
169                                                                            cookies[fd]->snapshot) != ACLCHECK_OK)
170                 ereport(ERROR,
171                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
172                                  errmsg("permission denied for large object %u",
173                                                 cookies[fd]->id)));
174
175         status = inv_read(cookies[fd], buf, len);
176
177         return status;
178 }
179
180 int
181 lo_write(int fd, const char *buf, int len)
182 {
183         int                     status;
184
185         if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
186                 ereport(ERROR,
187                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
188                                  errmsg("invalid large-object descriptor: %d", fd)));
189
190         if ((cookies[fd]->flags & IFS_WRLOCK) == 0)
191                 ereport(ERROR,
192                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
193                           errmsg("large object descriptor %d was not opened for writing",
194                                          fd)));
195
196         /* Permission checks */
197         if (!lo_compat_privileges &&
198                 pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
199                                                                                  GetUserId(),
200                                                                                  ACL_UPDATE,
201                                                                            cookies[fd]->snapshot) != ACLCHECK_OK)
202                 ereport(ERROR,
203                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
204                                  errmsg("permission denied for large object %u",
205                                                 cookies[fd]->id)));
206
207         status = inv_write(cookies[fd], buf, len);
208
209         return status;
210 }
211
212
213 Datum
214 lo_lseek(PG_FUNCTION_ARGS)
215 {
216         int32           fd = PG_GETARG_INT32(0);
217         int32           offset = PG_GETARG_INT32(1);
218         int32           whence = PG_GETARG_INT32(2);
219         int                     status;
220
221         if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
222                 ereport(ERROR,
223                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
224                                  errmsg("invalid large-object descriptor: %d", fd)));
225
226         status = inv_seek(cookies[fd], offset, whence);
227
228         PG_RETURN_INT32(status);
229 }
230
231 Datum
232 lo_creat(PG_FUNCTION_ARGS)
233 {
234         Oid                     lobjId;
235
236         /*
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
239          */
240         CreateFSContext();
241
242         lobjId = inv_create(InvalidOid);
243
244         PG_RETURN_OID(lobjId);
245 }
246
247 Datum
248 lo_create(PG_FUNCTION_ARGS)
249 {
250         Oid                     lobjId = PG_GETARG_OID(0);
251
252         /*
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
255          */
256         CreateFSContext();
257
258         lobjId = inv_create(lobjId);
259
260         PG_RETURN_OID(lobjId);
261 }
262
263 Datum
264 lo_tell(PG_FUNCTION_ARGS)
265 {
266         int32           fd = PG_GETARG_INT32(0);
267
268         if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
269                 ereport(ERROR,
270                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
271                                  errmsg("invalid large-object descriptor: %d", fd)));
272
273         PG_RETURN_INT32(inv_tell(cookies[fd]));
274 }
275
276 Datum
277 lo_unlink(PG_FUNCTION_ARGS)
278 {
279         Oid                     lobjId = PG_GETARG_OID(0);
280
281         /* Must be owner of the largeobject */
282         if (!lo_compat_privileges &&
283                 !pg_largeobject_ownercheck(lobjId, GetUserId()))
284                 ereport(ERROR,
285                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
286                                  errmsg("must be owner of large object %u", lobjId)));
287
288         /*
289          * If there are any open LO FDs referencing that ID, close 'em.
290          */
291         if (fscxt != NULL)
292         {
293                 int                     i;
294
295                 for (i = 0; i < cookies_size; i++)
296                 {
297                         if (cookies[i] != NULL && cookies[i]->id == lobjId)
298                         {
299                                 inv_close(cookies[i]);
300                                 deleteLOfd(i);
301                         }
302                 }
303         }
304
305         /*
306          * inv_drop does not create a need for end-of-transaction cleanup and
307          * hence we don't need to have created fscxt.
308          */
309         PG_RETURN_INT32(inv_drop(lobjId));
310 }
311
312 /*****************************************************************************
313  *      Read/Write using bytea
314  *****************************************************************************/
315
316 Datum
317 loread(PG_FUNCTION_ARGS)
318 {
319         int32           fd = PG_GETARG_INT32(0);
320         int32           len = PG_GETARG_INT32(1);
321         bytea      *retval;
322         int                     totalread;
323
324         if (len < 0)
325                 len = 0;
326
327         retval = (bytea *) palloc(VARHDRSZ + len);
328         totalread = lo_read(fd, VARDATA(retval), len);
329         SET_VARSIZE(retval, totalread + VARHDRSZ);
330
331         PG_RETURN_BYTEA_P(retval);
332 }
333
334 Datum
335 lowrite(PG_FUNCTION_ARGS)
336 {
337         int32           fd = PG_GETARG_INT32(0);
338         bytea      *wbuf = PG_GETARG_BYTEA_P(1);
339         int                     bytestowrite;
340         int                     totalwritten;
341
342         bytestowrite = VARSIZE(wbuf) - VARHDRSZ;
343         totalwritten = lo_write(fd, VARDATA(wbuf), bytestowrite);
344         PG_RETURN_INT32(totalwritten);
345 }
346
347 /*****************************************************************************
348  *       Import/Export of Large Object
349  *****************************************************************************/
350
351 /*
352  * lo_import -
353  *        imports a file as an (inversion) large object.
354  */
355 Datum
356 lo_import(PG_FUNCTION_ARGS)
357 {
358         text       *filename = PG_GETARG_TEXT_PP(0);
359
360         PG_RETURN_OID(lo_import_internal(filename, InvalidOid));
361 }
362
363 /*
364  * lo_import_with_oid -
365  *        imports a file as an (inversion) large object specifying oid.
366  */
367 Datum
368 lo_import_with_oid(PG_FUNCTION_ARGS)
369 {
370         text       *filename = PG_GETARG_TEXT_PP(0);
371         Oid                     oid = PG_GETARG_OID(1);
372
373         PG_RETURN_OID(lo_import_internal(filename, oid));
374 }
375
376 static Oid
377 lo_import_internal(text *filename, Oid lobjOid)
378 {
379         File            fd;
380         int                     nbytes,
381                                 tmp PG_USED_FOR_ASSERTS_ONLY;
382         char            buf[BUFSIZE];
383         char            fnamebuf[MAXPGPATH];
384         LargeObjectDesc *lobj;
385         Oid                     oid;
386
387 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
388         if (!superuser())
389                 ereport(ERROR,
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.")));
393 #endif
394
395         CreateFSContext();
396
397         /*
398          * open the file to be read in
399          */
400         text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
401         fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, S_IRWXU);
402         if (fd < 0)
403                 ereport(ERROR,
404                                 (errcode_for_file_access(),
405                                  errmsg("could not open server file \"%s\": %m",
406                                                 fnamebuf)));
407
408         /*
409          * create an inversion object
410          */
411         oid = inv_create(lobjOid);
412
413         /*
414          * read in from the filesystem and write to the inversion object
415          */
416         lobj = inv_open(oid, INV_WRITE, fscxt);
417
418         while ((nbytes = FileRead(fd, buf, BUFSIZE)) > 0)
419         {
420                 tmp = inv_write(lobj, buf, nbytes);
421                 Assert(tmp == nbytes);
422         }
423
424         if (nbytes < 0)
425                 ereport(ERROR,
426                                 (errcode_for_file_access(),
427                                  errmsg("could not read server file \"%s\": %m",
428                                                 fnamebuf)));
429
430         inv_close(lobj);
431         FileClose(fd);
432
433         return oid;
434 }
435
436 /*
437  * lo_export -
438  *        exports an (inversion) large object.
439  */
440 Datum
441 lo_export(PG_FUNCTION_ARGS)
442 {
443         Oid                     lobjId = PG_GETARG_OID(0);
444         text       *filename = PG_GETARG_TEXT_PP(1);
445         File            fd;
446         int                     nbytes,
447                                 tmp;
448         char            buf[BUFSIZE];
449         char            fnamebuf[MAXPGPATH];
450         LargeObjectDesc *lobj;
451         mode_t          oumask;
452
453 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
454         if (!superuser())
455                 ereport(ERROR,
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.")));
459 #endif
460
461         CreateFSContext();
462
463         /*
464          * open the inversion object (no need to test for failure)
465          */
466         lobj = inv_open(lobjId, INV_READ, fscxt);
467
468         /*
469          * open the file to be written to
470          *
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.
474          */
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);
479         umask(oumask);
480         if (fd < 0)
481                 ereport(ERROR,
482                                 (errcode_for_file_access(),
483                                  errmsg("could not create server file \"%s\": %m",
484                                                 fnamebuf)));
485
486         /*
487          * read in from the inversion file and write to the filesystem
488          */
489         while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
490         {
491                 tmp = FileWrite(fd, buf, nbytes);
492                 if (tmp != nbytes)
493                         ereport(ERROR,
494                                         (errcode_for_file_access(),
495                                          errmsg("could not write server file \"%s\": %m",
496                                                         fnamebuf)));
497         }
498
499         FileClose(fd);
500         inv_close(lobj);
501
502         PG_RETURN_INT32(1);
503 }
504
505 /*
506  * lo_truncate -
507  *        truncate a large object to a specified length
508  */
509 Datum
510 lo_truncate(PG_FUNCTION_ARGS)
511 {
512         int32           fd = PG_GETARG_INT32(0);
513         int32           len = PG_GETARG_INT32(1);
514
515         if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
516                 ereport(ERROR,
517                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
518                                  errmsg("invalid large-object descriptor: %d", fd)));
519
520         /* Permission checks */
521         if (!lo_compat_privileges &&
522                 pg_largeobject_aclcheck_snapshot(cookies[fd]->id,
523                                                                                  GetUserId(),
524                                                                                  ACL_UPDATE,
525                                                                            cookies[fd]->snapshot) != ACLCHECK_OK)
526                 ereport(ERROR,
527                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
528                                  errmsg("permission denied for large object %u",
529                                                 cookies[fd]->id)));
530
531         inv_truncate(cookies[fd], len);
532
533         PG_RETURN_INT32(0);
534 }
535
536 /*
537  * AtEOXact_LargeObject -
538  *               prepares large objects for transaction commit
539  */
540 void
541 AtEOXact_LargeObject(bool isCommit)
542 {
543         int                     i;
544
545         if (fscxt == NULL)
546                 return;                                 /* no LO operations in this xact */
547
548         /*
549          * Close LO fds and clear cookies array so that LO fds are no longer good.
550          * On abort we skip the close step.
551          */
552         for (i = 0; i < cookies_size; i++)
553         {
554                 if (cookies[i] != NULL)
555                 {
556                         if (isCommit)
557                                 inv_close(cookies[i]);
558                         deleteLOfd(i);
559                 }
560         }
561
562         /* Needn't actually pfree since we're about to zap context */
563         cookies = NULL;
564         cookies_size = 0;
565
566         /* Release the LO memory context to prevent permanent memory leaks. */
567         MemoryContextDelete(fscxt);
568         fscxt = NULL;
569
570         /* Give inv_api.c a chance to clean up, too */
571         close_lo_relation(isCommit);
572 }
573
574 /*
575  * AtEOSubXact_LargeObject
576  *              Take care of large objects at subtransaction commit/abort
577  *
578  * Reassign LOs created/opened during a committing subtransaction
579  * to the parent subtransaction.  On abort, just close them.
580  */
581 void
582 AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
583                                                 SubTransactionId parentSubid)
584 {
585         int                     i;
586
587         if (fscxt == NULL)                      /* no LO operations in this xact */
588                 return;
589
590         for (i = 0; i < cookies_size; i++)
591         {
592                 LargeObjectDesc *lo = cookies[i];
593
594                 if (lo != NULL && lo->subid == mySubid)
595                 {
596                         if (isCommit)
597                                 lo->subid = parentSubid;
598                         else
599                         {
600                                 /*
601                                  * Make sure we do not call inv_close twice if it errors out
602                                  * for some reason.  Better a leak than a crash.
603                                  */
604                                 deleteLOfd(i);
605                                 inv_close(lo);
606                         }
607                 }
608         }
609 }
610
611 /*****************************************************************************
612  *      Support routines for this file
613  *****************************************************************************/
614
615 static int
616 newLOfd(LargeObjectDesc *lobjCookie)
617 {
618         int                     i,
619                                 newsize;
620
621         /* Try to find a free slot */
622         for (i = 0; i < cookies_size; i++)
623         {
624                 if (cookies[i] == NULL)
625                 {
626                         cookies[i] = lobjCookie;
627                         return i;
628                 }
629         }
630
631         /* No free slot, so make the array bigger */
632         if (cookies_size <= 0)
633         {
634                 /* First time through, arbitrarily make 64-element array */
635                 i = 0;
636                 newsize = 64;
637                 cookies = (LargeObjectDesc **)
638                         MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
639                 cookies_size = newsize;
640         }
641         else
642         {
643                 /* Double size of array */
644                 i = cookies_size;
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;
651         }
652
653         Assert(cookies[i] == NULL);
654         cookies[i] = lobjCookie;
655         return i;
656 }
657
658 static void
659 deleteLOfd(int fd)
660 {
661         cookies[fd] = NULL;
662 }