Disallow empty passwords in LDAP authentication, the same way
[PostgreSQL.git] / src / backend / libpq / be-fsstubs.c
blob0831071cca91eded7637d7551c7874daf093dc0b
1 /*-------------------------------------------------------------------------
3 * be-fsstubs.c
4 * Builtin functions for open/close/read/write operations on large objects
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * $PostgreSQL$
13 * NOTES
14 * This should be moved to a more appropriate place. It is here
15 * for lack of a better place.
17 * These functions store LargeObjectDesc structs in a private MemoryContext,
18 * which means that large object descriptors hang around until we destroy
19 * the context at transaction end. It'd be possible to prolong the lifetime
20 * of the context so that LO FDs are good across transactions (for example,
21 * we could release the context only if we see that no FDs remain open).
22 * But we'd need additional state in order to do the right thing at the
23 * end of an aborted transaction. FDs opened during an aborted xact would
24 * still need to be closed, since they might not be pointing at valid
25 * relations at all. Locking semantics are also an interesting problem
26 * if LOs stay open across transactions. For now, we'll stick with the
27 * existing documented semantics of LO FDs: they're only good within a
28 * transaction.
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.
36 *-------------------------------------------------------------------------
39 #include "postgres.h"
41 #include <fcntl.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
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/builtins.h"
51 #include "utils/memutils.h"
54 /*#define FSDB 1*/
55 #define BUFSIZE 8192
58 * LO "FD"s are indexes into the cookies array.
60 * A non-null entry is a pointer to a LargeObjectDesc allocated in the
61 * LO private memory context "fscxt". The cookies array itself is also
62 * dynamically allocated in that context. Its current allocated size is
63 * cookies_len entries, of which any unused entries will be NULL.
65 static LargeObjectDesc **cookies = NULL;
66 static int cookies_size = 0;
68 static MemoryContext fscxt = NULL;
70 #define CreateFSContext() \
71 do { \
72 if (fscxt == NULL) \
73 fscxt = AllocSetContextCreate(TopMemoryContext, \
74 "Filesystem", \
75 ALLOCSET_DEFAULT_MINSIZE, \
76 ALLOCSET_DEFAULT_INITSIZE, \
77 ALLOCSET_DEFAULT_MAXSIZE); \
78 } while (0)
81 static int newLOfd(LargeObjectDesc *lobjCookie);
82 static void deleteLOfd(int fd);
83 static Oid lo_import_internal(text *filename, Oid lobjOid);
86 /*****************************************************************************
87 * File Interfaces for Large Objects
88 *****************************************************************************/
90 Datum
91 lo_open(PG_FUNCTION_ARGS)
93 Oid lobjId = PG_GETARG_OID(0);
94 int32 mode = PG_GETARG_INT32(1);
95 LargeObjectDesc *lobjDesc;
96 int fd;
98 #if FSDB
99 elog(DEBUG4, "lo_open(%u,%d)", lobjId, mode);
100 #endif
102 CreateFSContext();
104 lobjDesc = inv_open(lobjId, mode, fscxt);
106 if (lobjDesc == NULL)
107 { /* lookup failed */
108 #if FSDB
109 elog(DEBUG4, "could not open large object %u", lobjId);
110 #endif
111 PG_RETURN_INT32(-1);
114 fd = newLOfd(lobjDesc);
116 PG_RETURN_INT32(fd);
119 Datum
120 lo_close(PG_FUNCTION_ARGS)
122 int32 fd = PG_GETARG_INT32(0);
124 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
125 ereport(ERROR,
126 (errcode(ERRCODE_UNDEFINED_OBJECT),
127 errmsg("invalid large-object descriptor: %d", fd)));
129 #if FSDB
130 elog(DEBUG4, "lo_close(%d)", fd);
131 #endif
133 inv_close(cookies[fd]);
135 deleteLOfd(fd);
137 PG_RETURN_INT32(0);
141 /*****************************************************************************
142 * Bare Read/Write operations --- these are not fmgr-callable!
144 * We assume the large object supports byte oriented reads and seeks so
145 * that our work is easier.
147 *****************************************************************************/
150 lo_read(int fd, char *buf, int len)
152 int status;
154 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
155 ereport(ERROR,
156 (errcode(ERRCODE_UNDEFINED_OBJECT),
157 errmsg("invalid large-object descriptor: %d", fd)));
159 status = inv_read(cookies[fd], buf, len);
161 return status;
165 lo_write(int fd, const char *buf, int len)
167 int status;
169 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
170 ereport(ERROR,
171 (errcode(ERRCODE_UNDEFINED_OBJECT),
172 errmsg("invalid large-object descriptor: %d", fd)));
174 if ((cookies[fd]->flags & IFS_WRLOCK) == 0)
175 ereport(ERROR,
176 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
177 errmsg("large object descriptor %d was not opened for writing",
178 fd)));
180 status = inv_write(cookies[fd], buf, len);
182 return status;
186 Datum
187 lo_lseek(PG_FUNCTION_ARGS)
189 int32 fd = PG_GETARG_INT32(0);
190 int32 offset = PG_GETARG_INT32(1);
191 int32 whence = PG_GETARG_INT32(2);
192 int status;
194 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
195 ereport(ERROR,
196 (errcode(ERRCODE_UNDEFINED_OBJECT),
197 errmsg("invalid large-object descriptor: %d", fd)));
199 status = inv_seek(cookies[fd], offset, whence);
201 PG_RETURN_INT32(status);
204 Datum
205 lo_creat(PG_FUNCTION_ARGS)
207 Oid lobjId;
210 * We don't actually need to store into fscxt, but create it anyway to
211 * ensure that AtEOXact_LargeObject knows there is state to clean up
213 CreateFSContext();
215 lobjId = inv_create(InvalidOid);
217 PG_RETURN_OID(lobjId);
220 Datum
221 lo_create(PG_FUNCTION_ARGS)
223 Oid lobjId = PG_GETARG_OID(0);
226 * We don't actually need to store into fscxt, but create it anyway to
227 * ensure that AtEOXact_LargeObject knows there is state to clean up
229 CreateFSContext();
231 lobjId = inv_create(lobjId);
233 PG_RETURN_OID(lobjId);
236 Datum
237 lo_tell(PG_FUNCTION_ARGS)
239 int32 fd = PG_GETARG_INT32(0);
241 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
242 ereport(ERROR,
243 (errcode(ERRCODE_UNDEFINED_OBJECT),
244 errmsg("invalid large-object descriptor: %d", fd)));
246 PG_RETURN_INT32(inv_tell(cookies[fd]));
249 Datum
250 lo_unlink(PG_FUNCTION_ARGS)
252 Oid lobjId = PG_GETARG_OID(0);
255 * If there are any open LO FDs referencing that ID, close 'em.
257 if (fscxt != NULL)
259 int i;
261 for (i = 0; i < cookies_size; i++)
263 if (cookies[i] != NULL && cookies[i]->id == lobjId)
265 inv_close(cookies[i]);
266 deleteLOfd(i);
272 * inv_drop does not create a need for end-of-transaction cleanup and
273 * hence we don't need to have created fscxt.
275 PG_RETURN_INT32(inv_drop(lobjId));
278 /*****************************************************************************
279 * Read/Write using bytea
280 *****************************************************************************/
282 Datum
283 loread(PG_FUNCTION_ARGS)
285 int32 fd = PG_GETARG_INT32(0);
286 int32 len = PG_GETARG_INT32(1);
287 bytea *retval;
288 int totalread;
290 if (len < 0)
291 len = 0;
293 retval = (bytea *) palloc(VARHDRSZ + len);
294 totalread = lo_read(fd, VARDATA(retval), len);
295 SET_VARSIZE(retval, totalread + VARHDRSZ);
297 PG_RETURN_BYTEA_P(retval);
300 Datum
301 lowrite(PG_FUNCTION_ARGS)
303 int32 fd = PG_GETARG_INT32(0);
304 bytea *wbuf = PG_GETARG_BYTEA_P(1);
305 int bytestowrite;
306 int totalwritten;
308 bytestowrite = VARSIZE(wbuf) - VARHDRSZ;
309 totalwritten = lo_write(fd, VARDATA(wbuf), bytestowrite);
310 PG_RETURN_INT32(totalwritten);
313 /*****************************************************************************
314 * Import/Export of Large Object
315 *****************************************************************************/
318 * lo_import -
319 * imports a file as an (inversion) large object.
321 Datum
322 lo_import(PG_FUNCTION_ARGS)
324 text *filename = PG_GETARG_TEXT_PP(0);
326 PG_RETURN_OID(lo_import_internal(filename, InvalidOid));
330 * lo_import_with_oid -
331 * imports a file as an (inversion) large object specifying oid.
333 Datum
334 lo_import_with_oid(PG_FUNCTION_ARGS)
336 text *filename = PG_GETARG_TEXT_PP(0);
337 Oid oid = PG_GETARG_OID(1);
339 PG_RETURN_OID(lo_import_internal(filename, oid));
342 static Oid
343 lo_import_internal(text *filename, Oid lobjOid)
345 File fd;
346 int nbytes,
347 tmp;
348 char buf[BUFSIZE];
349 char fnamebuf[MAXPGPATH];
350 LargeObjectDesc *lobj;
351 Oid oid;
353 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
354 if (!superuser())
355 ereport(ERROR,
356 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
357 errmsg("must be superuser to use server-side lo_import()"),
358 errhint("Anyone can use the client-side lo_import() provided by libpq.")));
359 #endif
361 CreateFSContext();
364 * open the file to be read in
366 text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
367 fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666);
368 if (fd < 0)
369 ereport(ERROR,
370 (errcode_for_file_access(),
371 errmsg("could not open server file \"%s\": %m",
372 fnamebuf)));
375 * create an inversion object
377 oid = inv_create(lobjOid);
380 * read in from the filesystem and write to the inversion object
382 lobj = inv_open(oid, INV_WRITE, fscxt);
384 while ((nbytes = FileRead(fd, buf, BUFSIZE)) > 0)
386 tmp = inv_write(lobj, buf, nbytes);
387 Assert(tmp == nbytes);
390 if (nbytes < 0)
391 ereport(ERROR,
392 (errcode_for_file_access(),
393 errmsg("could not read server file \"%s\": %m",
394 fnamebuf)));
396 inv_close(lobj);
397 FileClose(fd);
399 return oid;
403 * lo_export -
404 * exports an (inversion) large object.
406 Datum
407 lo_export(PG_FUNCTION_ARGS)
409 Oid lobjId = PG_GETARG_OID(0);
410 text *filename = PG_GETARG_TEXT_PP(1);
411 File fd;
412 int nbytes,
413 tmp;
414 char buf[BUFSIZE];
415 char fnamebuf[MAXPGPATH];
416 LargeObjectDesc *lobj;
417 mode_t oumask;
419 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
420 if (!superuser())
421 ereport(ERROR,
422 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
423 errmsg("must be superuser to use server-side lo_export()"),
424 errhint("Anyone can use the client-side lo_export() provided by libpq.")));
425 #endif
427 CreateFSContext();
430 * open the inversion object (no need to test for failure)
432 lobj = inv_open(lobjId, INV_READ, fscxt);
435 * open the file to be written to
437 * Note: we reduce backend's normal 077 umask to the slightly friendlier
438 * 022. This code used to drop it all the way to 0, but creating
439 * world-writable export files doesn't seem wise.
441 text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
442 oumask = umask((mode_t) 0022);
443 fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666);
444 umask(oumask);
445 if (fd < 0)
446 ereport(ERROR,
447 (errcode_for_file_access(),
448 errmsg("could not create server file \"%s\": %m",
449 fnamebuf)));
452 * read in from the inversion file and write to the filesystem
454 while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
456 tmp = FileWrite(fd, buf, nbytes);
457 if (tmp != nbytes)
458 ereport(ERROR,
459 (errcode_for_file_access(),
460 errmsg("could not write server file \"%s\": %m",
461 fnamebuf)));
464 FileClose(fd);
465 inv_close(lobj);
467 PG_RETURN_INT32(1);
471 * lo_truncate -
472 * truncate a large object to a specified length
474 Datum
475 lo_truncate(PG_FUNCTION_ARGS)
477 int32 fd = PG_GETARG_INT32(0);
478 int32 len = PG_GETARG_INT32(1);
480 if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
481 ereport(ERROR,
482 (errcode(ERRCODE_UNDEFINED_OBJECT),
483 errmsg("invalid large-object descriptor: %d", fd)));
485 inv_truncate(cookies[fd], len);
487 PG_RETURN_INT32(0);
491 * AtEOXact_LargeObject -
492 * prepares large objects for transaction commit
494 void
495 AtEOXact_LargeObject(bool isCommit)
497 int i;
499 if (fscxt == NULL)
500 return; /* no LO operations in this xact */
503 * Close LO fds and clear cookies array so that LO fds are no longer good.
504 * On abort we skip the close step.
506 for (i = 0; i < cookies_size; i++)
508 if (cookies[i] != NULL)
510 if (isCommit)
511 inv_close(cookies[i]);
512 deleteLOfd(i);
516 /* Needn't actually pfree since we're about to zap context */
517 cookies = NULL;
518 cookies_size = 0;
520 /* Release the LO memory context to prevent permanent memory leaks. */
521 MemoryContextDelete(fscxt);
522 fscxt = NULL;
524 /* Give inv_api.c a chance to clean up, too */
525 close_lo_relation(isCommit);
529 * AtEOSubXact_LargeObject
530 * Take care of large objects at subtransaction commit/abort
532 * Reassign LOs created/opened during a committing subtransaction
533 * to the parent subtransaction. On abort, just close them.
535 void
536 AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
537 SubTransactionId parentSubid)
539 int i;
541 if (fscxt == NULL) /* no LO operations in this xact */
542 return;
544 for (i = 0; i < cookies_size; i++)
546 LargeObjectDesc *lo = cookies[i];
548 if (lo != NULL && lo->subid == mySubid)
550 if (isCommit)
551 lo->subid = parentSubid;
552 else
555 * Make sure we do not call inv_close twice if it errors out
556 * for some reason. Better a leak than a crash.
558 deleteLOfd(i);
559 inv_close(lo);
565 /*****************************************************************************
566 * Support routines for this file
567 *****************************************************************************/
569 static int
570 newLOfd(LargeObjectDesc *lobjCookie)
572 int i,
573 newsize;
575 /* Try to find a free slot */
576 for (i = 0; i < cookies_size; i++)
578 if (cookies[i] == NULL)
580 cookies[i] = lobjCookie;
581 return i;
585 /* No free slot, so make the array bigger */
586 if (cookies_size <= 0)
588 /* First time through, arbitrarily make 64-element array */
589 i = 0;
590 newsize = 64;
591 cookies = (LargeObjectDesc **)
592 MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
593 cookies_size = newsize;
595 else
597 /* Double size of array */
598 i = cookies_size;
599 newsize = cookies_size * 2;
600 cookies = (LargeObjectDesc **)
601 repalloc(cookies, newsize * sizeof(LargeObjectDesc *));
602 MemSet(cookies + cookies_size, 0,
603 (newsize - cookies_size) * sizeof(LargeObjectDesc *));
604 cookies_size = newsize;
607 Assert(cookies[i] == NULL);
608 cookies[i] = lobjCookie;
609 return i;
612 static void
613 deleteLOfd(int fd)
615 cookies[fd] = NULL;