LCOV - differential code coverage report
Current view: top level - src/backend/libpq - be-fsstubs.c (source / functions) Coverage Total Hit UNC LBC UIC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 86.3 % 277 239 2 12 24 12 145 13 69 26 155 3
Current Date: 2023-04-08 15:15:32 Functions: 96.6 % 29 28 1 28 1 28
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a