00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 #include "postgres.h"
00040
00041 #include <fcntl.h>
00042 #include <sys/stat.h>
00043 #include <unistd.h>
00044
00045 #include "libpq/be-fsstubs.h"
00046 #include "libpq/libpq-fs.h"
00047 #include "miscadmin.h"
00048 #include "storage/fd.h"
00049 #include "storage/large_object.h"
00050 #include "utils/acl.h"
00051 #include "utils/builtins.h"
00052 #include "utils/memutils.h"
00053
00054
00055
00056
00057 bool lo_compat_privileges;
00058
00059
00060
00061
00062 #define BUFSIZE 8192
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072 static LargeObjectDesc **cookies = NULL;
00073 static int cookies_size = 0;
00074
00075 static MemoryContext fscxt = NULL;
00076
00077 #define CreateFSContext() \
00078 do { \
00079 if (fscxt == NULL) \
00080 fscxt = AllocSetContextCreate(TopMemoryContext, \
00081 "Filesystem", \
00082 ALLOCSET_DEFAULT_MINSIZE, \
00083 ALLOCSET_DEFAULT_INITSIZE, \
00084 ALLOCSET_DEFAULT_MAXSIZE); \
00085 } while (0)
00086
00087
00088 static int newLOfd(LargeObjectDesc *lobjCookie);
00089 static void deleteLOfd(int fd);
00090 static Oid lo_import_internal(text *filename, Oid lobjOid);
00091
00092
00093
00094
00095
00096
00097 Datum
00098 lo_open(PG_FUNCTION_ARGS)
00099 {
00100 Oid lobjId = PG_GETARG_OID(0);
00101 int32 mode = PG_GETARG_INT32(1);
00102 LargeObjectDesc *lobjDesc;
00103 int fd;
00104
00105 #if FSDB
00106 elog(DEBUG4, "lo_open(%u,%d)", lobjId, mode);
00107 #endif
00108
00109 CreateFSContext();
00110
00111 lobjDesc = inv_open(lobjId, mode, fscxt);
00112
00113 if (lobjDesc == NULL)
00114 {
00115 #if FSDB
00116 elog(DEBUG4, "could not open large object %u", lobjId);
00117 #endif
00118 PG_RETURN_INT32(-1);
00119 }
00120
00121 fd = newLOfd(lobjDesc);
00122
00123 PG_RETURN_INT32(fd);
00124 }
00125
00126 Datum
00127 lo_close(PG_FUNCTION_ARGS)
00128 {
00129 int32 fd = PG_GETARG_INT32(0);
00130
00131 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00132 ereport(ERROR,
00133 (errcode(ERRCODE_UNDEFINED_OBJECT),
00134 errmsg("invalid large-object descriptor: %d", fd)));
00135
00136 #if FSDB
00137 elog(DEBUG4, "lo_close(%d)", fd);
00138 #endif
00139
00140 inv_close(cookies[fd]);
00141
00142 deleteLOfd(fd);
00143
00144 PG_RETURN_INT32(0);
00145 }
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156 int
00157 lo_read(int fd, char *buf, int len)
00158 {
00159 int status;
00160 LargeObjectDesc *lobj;
00161
00162 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00163 ereport(ERROR,
00164 (errcode(ERRCODE_UNDEFINED_OBJECT),
00165 errmsg("invalid large-object descriptor: %d", fd)));
00166 lobj = cookies[fd];
00167
00168
00169
00170
00171 if ((lobj->flags & IFS_RD_PERM_OK) == 0)
00172 {
00173 if (!lo_compat_privileges &&
00174 pg_largeobject_aclcheck_snapshot(lobj->id,
00175 GetUserId(),
00176 ACL_SELECT,
00177 lobj->snapshot) != ACLCHECK_OK)
00178 ereport(ERROR,
00179 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00180 errmsg("permission denied for large object %u",
00181 lobj->id)));
00182 lobj->flags |= IFS_RD_PERM_OK;
00183 }
00184
00185 status = inv_read(lobj, buf, len);
00186
00187 return status;
00188 }
00189
00190 int
00191 lo_write(int fd, const char *buf, int len)
00192 {
00193 int status;
00194 LargeObjectDesc *lobj;
00195
00196 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00197 ereport(ERROR,
00198 (errcode(ERRCODE_UNDEFINED_OBJECT),
00199 errmsg("invalid large-object descriptor: %d", fd)));
00200 lobj = cookies[fd];
00201
00202 if ((lobj->flags & IFS_WRLOCK) == 0)
00203 ereport(ERROR,
00204 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00205 errmsg("large object descriptor %d was not opened for writing",
00206 fd)));
00207
00208
00209 if ((lobj->flags & IFS_WR_PERM_OK) == 0)
00210 {
00211 if (!lo_compat_privileges &&
00212 pg_largeobject_aclcheck_snapshot(lobj->id,
00213 GetUserId(),
00214 ACL_UPDATE,
00215 lobj->snapshot) != ACLCHECK_OK)
00216 ereport(ERROR,
00217 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00218 errmsg("permission denied for large object %u",
00219 lobj->id)));
00220 lobj->flags |= IFS_WR_PERM_OK;
00221 }
00222
00223 status = inv_write(lobj, buf, len);
00224
00225 return status;
00226 }
00227
00228 Datum
00229 lo_lseek(PG_FUNCTION_ARGS)
00230 {
00231 int32 fd = PG_GETARG_INT32(0);
00232 int32 offset = PG_GETARG_INT32(1);
00233 int32 whence = PG_GETARG_INT32(2);
00234 int64 status;
00235
00236 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00237 ereport(ERROR,
00238 (errcode(ERRCODE_UNDEFINED_OBJECT),
00239 errmsg("invalid large-object descriptor: %d", fd)));
00240
00241 status = inv_seek(cookies[fd], offset, whence);
00242
00243
00244 if (status != (int32) status)
00245 ereport(ERROR,
00246 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
00247 errmsg("lo_lseek result out of range for large-object descriptor %d",
00248 fd)));
00249
00250 PG_RETURN_INT32((int32) status);
00251 }
00252
00253 Datum
00254 lo_lseek64(PG_FUNCTION_ARGS)
00255 {
00256 int32 fd = PG_GETARG_INT32(0);
00257 int64 offset = PG_GETARG_INT64(1);
00258 int32 whence = PG_GETARG_INT32(2);
00259 int64 status;
00260
00261 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00262 ereport(ERROR,
00263 (errcode(ERRCODE_UNDEFINED_OBJECT),
00264 errmsg("invalid large-object descriptor: %d", fd)));
00265
00266 status = inv_seek(cookies[fd], offset, whence);
00267
00268 PG_RETURN_INT64(status);
00269 }
00270
00271 Datum
00272 lo_creat(PG_FUNCTION_ARGS)
00273 {
00274 Oid lobjId;
00275
00276
00277
00278
00279
00280 CreateFSContext();
00281
00282 lobjId = inv_create(InvalidOid);
00283
00284 PG_RETURN_OID(lobjId);
00285 }
00286
00287 Datum
00288 lo_create(PG_FUNCTION_ARGS)
00289 {
00290 Oid lobjId = PG_GETARG_OID(0);
00291
00292
00293
00294
00295
00296 CreateFSContext();
00297
00298 lobjId = inv_create(lobjId);
00299
00300 PG_RETURN_OID(lobjId);
00301 }
00302
00303 Datum
00304 lo_tell(PG_FUNCTION_ARGS)
00305 {
00306 int32 fd = PG_GETARG_INT32(0);
00307 int64 offset;
00308
00309 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00310 ereport(ERROR,
00311 (errcode(ERRCODE_UNDEFINED_OBJECT),
00312 errmsg("invalid large-object descriptor: %d", fd)));
00313
00314 offset = inv_tell(cookies[fd]);
00315
00316
00317 if (offset != (int32) offset)
00318 ereport(ERROR,
00319 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
00320 errmsg("lo_tell result out of range for large-object descriptor %d",
00321 fd)));
00322
00323 PG_RETURN_INT32((int32) offset);
00324 }
00325
00326 Datum
00327 lo_tell64(PG_FUNCTION_ARGS)
00328 {
00329 int32 fd = PG_GETARG_INT32(0);
00330 int64 offset;
00331
00332 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00333 ereport(ERROR,
00334 (errcode(ERRCODE_UNDEFINED_OBJECT),
00335 errmsg("invalid large-object descriptor: %d", fd)));
00336
00337 offset = inv_tell(cookies[fd]);
00338
00339 PG_RETURN_INT64(offset);
00340 }
00341
00342 Datum
00343 lo_unlink(PG_FUNCTION_ARGS)
00344 {
00345 Oid lobjId = PG_GETARG_OID(0);
00346
00347
00348 if (!lo_compat_privileges &&
00349 !pg_largeobject_ownercheck(lobjId, GetUserId()))
00350 ereport(ERROR,
00351 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00352 errmsg("must be owner of large object %u", lobjId)));
00353
00354
00355
00356
00357 if (fscxt != NULL)
00358 {
00359 int i;
00360
00361 for (i = 0; i < cookies_size; i++)
00362 {
00363 if (cookies[i] != NULL && cookies[i]->id == lobjId)
00364 {
00365 inv_close(cookies[i]);
00366 deleteLOfd(i);
00367 }
00368 }
00369 }
00370
00371
00372
00373
00374
00375 PG_RETURN_INT32(inv_drop(lobjId));
00376 }
00377
00378
00379
00380
00381
00382 Datum
00383 loread(PG_FUNCTION_ARGS)
00384 {
00385 int32 fd = PG_GETARG_INT32(0);
00386 int32 len = PG_GETARG_INT32(1);
00387 bytea *retval;
00388 int totalread;
00389
00390 if (len < 0)
00391 len = 0;
00392
00393 retval = (bytea *) palloc(VARHDRSZ + len);
00394 totalread = lo_read(fd, VARDATA(retval), len);
00395 SET_VARSIZE(retval, totalread + VARHDRSZ);
00396
00397 PG_RETURN_BYTEA_P(retval);
00398 }
00399
00400 Datum
00401 lowrite(PG_FUNCTION_ARGS)
00402 {
00403 int32 fd = PG_GETARG_INT32(0);
00404 bytea *wbuf = PG_GETARG_BYTEA_P(1);
00405 int bytestowrite;
00406 int totalwritten;
00407
00408 bytestowrite = VARSIZE(wbuf) - VARHDRSZ;
00409 totalwritten = lo_write(fd, VARDATA(wbuf), bytestowrite);
00410 PG_RETURN_INT32(totalwritten);
00411 }
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421 Datum
00422 lo_import(PG_FUNCTION_ARGS)
00423 {
00424 text *filename = PG_GETARG_TEXT_PP(0);
00425
00426 PG_RETURN_OID(lo_import_internal(filename, InvalidOid));
00427 }
00428
00429
00430
00431
00432
00433 Datum
00434 lo_import_with_oid(PG_FUNCTION_ARGS)
00435 {
00436 text *filename = PG_GETARG_TEXT_PP(0);
00437 Oid oid = PG_GETARG_OID(1);
00438
00439 PG_RETURN_OID(lo_import_internal(filename, oid));
00440 }
00441
00442 static Oid
00443 lo_import_internal(text *filename, Oid lobjOid)
00444 {
00445 int fd;
00446 int nbytes,
00447 tmp PG_USED_FOR_ASSERTS_ONLY;
00448 char buf[BUFSIZE];
00449 char fnamebuf[MAXPGPATH];
00450 LargeObjectDesc *lobj;
00451 Oid oid;
00452
00453 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
00454 if (!superuser())
00455 ereport(ERROR,
00456 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00457 errmsg("must be superuser to use server-side lo_import()"),
00458 errhint("Anyone can use the client-side lo_import() provided by libpq.")));
00459 #endif
00460
00461 CreateFSContext();
00462
00463
00464
00465
00466 text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
00467 fd = OpenTransientFile(fnamebuf, O_RDONLY | PG_BINARY, S_IRWXU);
00468 if (fd < 0)
00469 ereport(ERROR,
00470 (errcode_for_file_access(),
00471 errmsg("could not open server file \"%s\": %m",
00472 fnamebuf)));
00473
00474
00475
00476
00477 oid = inv_create(lobjOid);
00478
00479
00480
00481
00482 lobj = inv_open(oid, INV_WRITE, fscxt);
00483
00484 while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
00485 {
00486 tmp = inv_write(lobj, buf, nbytes);
00487 Assert(tmp == nbytes);
00488 }
00489
00490 if (nbytes < 0)
00491 ereport(ERROR,
00492 (errcode_for_file_access(),
00493 errmsg("could not read server file \"%s\": %m",
00494 fnamebuf)));
00495
00496 inv_close(lobj);
00497 CloseTransientFile(fd);
00498
00499 return oid;
00500 }
00501
00502
00503
00504
00505
00506 Datum
00507 lo_export(PG_FUNCTION_ARGS)
00508 {
00509 Oid lobjId = PG_GETARG_OID(0);
00510 text *filename = PG_GETARG_TEXT_PP(1);
00511 int fd;
00512 int nbytes,
00513 tmp;
00514 char buf[BUFSIZE];
00515 char fnamebuf[MAXPGPATH];
00516 LargeObjectDesc *lobj;
00517 mode_t oumask;
00518
00519 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
00520 if (!superuser())
00521 ereport(ERROR,
00522 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00523 errmsg("must be superuser to use server-side lo_export()"),
00524 errhint("Anyone can use the client-side lo_export() provided by libpq.")));
00525 #endif
00526
00527 CreateFSContext();
00528
00529
00530
00531
00532 lobj = inv_open(lobjId, INV_READ, fscxt);
00533
00534
00535
00536
00537
00538
00539
00540
00541 text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
00542 oumask = umask(S_IWGRP | S_IWOTH);
00543 fd = OpenTransientFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
00544 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
00545 umask(oumask);
00546 if (fd < 0)
00547 ereport(ERROR,
00548 (errcode_for_file_access(),
00549 errmsg("could not create server file \"%s\": %m",
00550 fnamebuf)));
00551
00552
00553
00554
00555 while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
00556 {
00557 tmp = write(fd, buf, nbytes);
00558 if (tmp != nbytes)
00559 ereport(ERROR,
00560 (errcode_for_file_access(),
00561 errmsg("could not write server file \"%s\": %m",
00562 fnamebuf)));
00563 }
00564
00565 CloseTransientFile(fd);
00566 inv_close(lobj);
00567
00568 PG_RETURN_INT32(1);
00569 }
00570
00571
00572
00573
00574
00575 static void
00576 lo_truncate_internal(int32 fd, int64 len)
00577 {
00578 LargeObjectDesc *lobj;
00579
00580 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
00581 ereport(ERROR,
00582 (errcode(ERRCODE_UNDEFINED_OBJECT),
00583 errmsg("invalid large-object descriptor: %d", fd)));
00584 lobj = cookies[fd];
00585
00586 if ((lobj->flags & IFS_WRLOCK) == 0)
00587 ereport(ERROR,
00588 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00589 errmsg("large object descriptor %d was not opened for writing",
00590 fd)));
00591
00592
00593 if ((lobj->flags & IFS_WR_PERM_OK) == 0)
00594 {
00595 if (!lo_compat_privileges &&
00596 pg_largeobject_aclcheck_snapshot(lobj->id,
00597 GetUserId(),
00598 ACL_UPDATE,
00599 lobj->snapshot) != ACLCHECK_OK)
00600 ereport(ERROR,
00601 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
00602 errmsg("permission denied for large object %u",
00603 lobj->id)));
00604 lobj->flags |= IFS_WR_PERM_OK;
00605 }
00606
00607 inv_truncate(lobj, len);
00608 }
00609
00610 Datum
00611 lo_truncate(PG_FUNCTION_ARGS)
00612 {
00613 int32 fd = PG_GETARG_INT32(0);
00614 int32 len = PG_GETARG_INT32(1);
00615
00616 lo_truncate_internal(fd, len);
00617 PG_RETURN_INT32(0);
00618 }
00619
00620 Datum
00621 lo_truncate64(PG_FUNCTION_ARGS)
00622 {
00623 int32 fd = PG_GETARG_INT32(0);
00624 int64 len = PG_GETARG_INT64(1);
00625
00626 lo_truncate_internal(fd, len);
00627 PG_RETURN_INT32(0);
00628 }
00629
00630
00631
00632
00633
00634 void
00635 AtEOXact_LargeObject(bool isCommit)
00636 {
00637 int i;
00638
00639 if (fscxt == NULL)
00640 return;
00641
00642
00643
00644
00645
00646 for (i = 0; i < cookies_size; i++)
00647 {
00648 if (cookies[i] != NULL)
00649 {
00650 if (isCommit)
00651 inv_close(cookies[i]);
00652 deleteLOfd(i);
00653 }
00654 }
00655
00656
00657 cookies = NULL;
00658 cookies_size = 0;
00659
00660
00661 MemoryContextDelete(fscxt);
00662 fscxt = NULL;
00663
00664
00665 close_lo_relation(isCommit);
00666 }
00667
00668
00669
00670
00671
00672
00673
00674
00675 void
00676 AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
00677 SubTransactionId parentSubid)
00678 {
00679 int i;
00680
00681 if (fscxt == NULL)
00682 return;
00683
00684 for (i = 0; i < cookies_size; i++)
00685 {
00686 LargeObjectDesc *lo = cookies[i];
00687
00688 if (lo != NULL && lo->subid == mySubid)
00689 {
00690 if (isCommit)
00691 lo->subid = parentSubid;
00692 else
00693 {
00694
00695
00696
00697
00698 deleteLOfd(i);
00699 inv_close(lo);
00700 }
00701 }
00702 }
00703 }
00704
00705
00706
00707
00708
00709 static int
00710 newLOfd(LargeObjectDesc *lobjCookie)
00711 {
00712 int i,
00713 newsize;
00714
00715
00716 for (i = 0; i < cookies_size; i++)
00717 {
00718 if (cookies[i] == NULL)
00719 {
00720 cookies[i] = lobjCookie;
00721 return i;
00722 }
00723 }
00724
00725
00726 if (cookies_size <= 0)
00727 {
00728
00729 i = 0;
00730 newsize = 64;
00731 cookies = (LargeObjectDesc **)
00732 MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
00733 cookies_size = newsize;
00734 }
00735 else
00736 {
00737
00738 i = cookies_size;
00739 newsize = cookies_size * 2;
00740 cookies = (LargeObjectDesc **)
00741 repalloc(cookies, newsize * sizeof(LargeObjectDesc *));
00742 MemSet(cookies + cookies_size, 0,
00743 (newsize - cookies_size) * sizeof(LargeObjectDesc *));
00744 cookies_size = newsize;
00745 }
00746
00747 Assert(cookies[i] == NULL);
00748 cookies[i] = lobjCookie;
00749 return i;
00750 }
00751
00752 static void
00753 deleteLOfd(int fd)
00754 {
00755 cookies[fd] = NULL;
00756 }