OSDN Git Service

280fdd9410d31bf70fb95a0eb4e4117d0bb558bb
[pg-rex/syncrep.git] / src / backend / libpq / be-fsstubs.c
1 /*-------------------------------------------------------------------------
2  *
3  * be-fsstubs.c
4  *        support for filesystem operations on large objects
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.41 1999/07/17 20:17:01 momjian Exp $
11  *
12  * NOTES
13  *        This should be moved to a more appropriate place.  It is here
14  *        for lack of a better place.
15  *
16  *        Builtin functions for open/close/read/write operations on large objects.
17  *
18  *        These functions operate in a private GlobalMemoryContext, which means
19  *        that large object descriptors hang around until we destroy the context.
20  *        That happens in lo_commit().  It'd be possible to prolong the lifetime
21  *        of the context so that LO FDs are good across transactions (for example,
22  *        we could release the context only if we see that no FDs remain open).
23  *        But we'd need additional state in order to do the right thing at the
24  *        end of an aborted transaction.  FDs opened during an aborted xact would
25  *        still need to be closed, since they might not be pointing at valid
26  *        relations at all.  For now, we'll stick with the existing documented
27  *        semantics of LO FDs: they're only good within a transaction.
28  *
29  *-------------------------------------------------------------------------
30  */
31
32 #include <fcntl.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36
37 #include "postgres.h"
38
39 #include "catalog/pg_shadow.h"
40 #include "libpq/be-fsstubs.h"
41 #include "libpq/libpq-fs.h"
42 #include "storage/large_object.h"
43
44 /* [PA] is Pascal André <andre@via.ecp.fr> */
45
46 /*#define FSDB 1*/
47 #define MAX_LOBJ_FDS    256
48 #define BUFSIZE                 1024
49 #define FNAME_BUFSIZE   8192
50
51 static LargeObjectDesc *cookies[MAX_LOBJ_FDS];
52
53 static GlobalMemory fscxt = NULL;
54
55
56 static int      newLOfd(LargeObjectDesc *lobjCookie);
57 static void deleteLOfd(int fd);
58
59 /*****************************************************************************
60  *      File Interfaces for Large Objects
61  *****************************************************************************/
62
63 int
64 lo_open(Oid lobjId, int mode)
65 {
66         LargeObjectDesc *lobjDesc;
67         int                     fd;
68         MemoryContext currentContext;
69
70 #if FSDB
71         elog(NOTICE, "LOopen(%u,%d)", lobjId, mode);
72 #endif
73
74         if (fscxt == NULL)
75                 fscxt = CreateGlobalMemory("Filesystem");
76         currentContext = MemoryContextSwitchTo((MemoryContext) fscxt);
77
78         lobjDesc = inv_open(lobjId, mode);
79
80         if (lobjDesc == NULL)
81         {                                                       /* lookup failed */
82                 MemoryContextSwitchTo(currentContext);
83 #if FSDB
84                 elog(NOTICE, "cannot open large object %u", lobjId);
85 #endif
86                 return -1;
87         }
88
89         fd = newLOfd(lobjDesc);
90
91         /* switch context back to orig. */
92         MemoryContextSwitchTo(currentContext);
93
94 #if FSDB
95         if (fd < 0)                                     /* newLOfd couldn't find a slot */
96                 elog(NOTICE, "Out of space for large object FDs");
97 #endif
98
99         return fd;
100 }
101
102 int
103 lo_close(int fd)
104 {
105         MemoryContext currentContext;
106
107         if (fd < 0 || fd >= MAX_LOBJ_FDS)
108         {
109                 elog(ERROR, "lo_close: large obj descriptor (%d) out of range", fd);
110                 return -2;
111         }
112         if (cookies[fd] == NULL)
113         {
114                 elog(ERROR, "lo_close: invalid large obj descriptor (%d)", fd);
115                 return -3;
116         }
117 #if FSDB
118         elog(NOTICE, "LOclose(%d)", fd);
119 #endif
120
121         Assert(fscxt != NULL);
122         currentContext = MemoryContextSwitchTo((MemoryContext) fscxt);
123
124         inv_close(cookies[fd]);
125
126         MemoryContextSwitchTo(currentContext);
127
128         deleteLOfd(fd);
129         return 0;
130 }
131
132 /*
133  *      We assume the large object supports byte oriented reads and seeks so
134  *      that our work is easier.
135  */
136 int
137 lo_read(int fd, char *buf, int len)
138 {
139         MemoryContext currentContext;
140         int                     status;
141
142         if (fd < 0 || fd >= MAX_LOBJ_FDS)
143         {
144                 elog(ERROR, "lo_read: large obj descriptor (%d) out of range", fd);
145                 return -2;
146         }
147         if (cookies[fd] == NULL)
148         {
149                 elog(ERROR, "lo_read: invalid large obj descriptor (%d)", fd);
150                 return -3;
151         }
152
153         Assert(fscxt != NULL);
154         currentContext = MemoryContextSwitchTo((MemoryContext) fscxt);
155
156         status = inv_read(cookies[fd], buf, len);
157
158         MemoryContextSwitchTo(currentContext);
159         return (status);
160 }
161
162 int
163 lo_write(int fd, char *buf, int len)
164 {
165         MemoryContext currentContext;
166         int                     status;
167
168         if (fd < 0 || fd >= MAX_LOBJ_FDS)
169         {
170                 elog(ERROR, "lo_write: large obj descriptor (%d) out of range", fd);
171                 return -2;
172         }
173         if (cookies[fd] == NULL)
174         {
175                 elog(ERROR, "lo_write: invalid large obj descriptor (%d)", fd);
176                 return -3;
177         }
178
179         Assert(fscxt != NULL);
180         currentContext = MemoryContextSwitchTo((MemoryContext) fscxt);
181
182         status = inv_write(cookies[fd], buf, len);
183
184         MemoryContextSwitchTo(currentContext);
185         return (status);
186 }
187
188
189 int
190 lo_lseek(int fd, int offset, int whence)
191 {
192         MemoryContext currentContext;
193         int                     status;
194
195         if (fd < 0 || fd >= MAX_LOBJ_FDS)
196         {
197                 elog(ERROR, "lo_lseek: large obj descriptor (%d) out of range", fd);
198                 return -2;
199         }
200         if (cookies[fd] == NULL)
201         {
202                 elog(ERROR, "lo_lseek: invalid large obj descriptor (%d)", fd);
203                 return -3;
204         }
205
206         Assert(fscxt != NULL);
207         currentContext = MemoryContextSwitchTo((MemoryContext) fscxt);
208
209         status = inv_seek(cookies[fd], offset, whence);
210
211         MemoryContextSwitchTo(currentContext);
212
213         return status;
214 }
215
216 Oid
217 lo_creat(int mode)
218 {
219         LargeObjectDesc *lobjDesc;
220         MemoryContext currentContext;
221         Oid                     lobjId;
222
223         if (fscxt == NULL)
224                 fscxt = CreateGlobalMemory("Filesystem");
225
226         currentContext = MemoryContextSwitchTo((MemoryContext) fscxt);
227
228         lobjDesc = inv_create(mode);
229
230         if (lobjDesc == NULL)
231         {
232                 MemoryContextSwitchTo(currentContext);
233                 return InvalidOid;
234         }
235
236         lobjId = RelationGetRelid(lobjDesc->heap_r);
237
238         inv_close(lobjDesc);
239
240         /* switch context back to original memory context */
241         MemoryContextSwitchTo(currentContext);
242
243         return lobjId;
244 }
245
246 int
247 lo_tell(int fd)
248 {
249         if (fd < 0 || fd >= MAX_LOBJ_FDS)
250         {
251                 elog(ERROR, "lo_tell: large object descriptor (%d) out of range", fd);
252                 return -2;
253         }
254         if (cookies[fd] == NULL)
255         {
256                 elog(ERROR, "lo_tell: invalid large object descriptor (%d)", fd);
257                 return -3;
258         }
259
260         /*
261          * We assume we do not need to switch contexts for inv_tell.
262          * That is true for now, but is probably more than this module
263          * ought to assume...
264          */
265         return inv_tell(cookies[fd]);
266 }
267
268 int
269 lo_unlink(Oid lobjId)
270 {
271         /*
272          * inv_destroy does not need a context switch, indeed it doesn't
273          * touch any LO-specific data structures at all.  (Again, that's
274          * probably more than this module ought to be assuming.)
275          *
276          * XXX there ought to be some code to clean up any open LOs that
277          * reference the specified relation... as is, they remain "open".
278          */
279         return inv_destroy(lobjId);
280 }
281
282 /*****************************************************************************
283  *      Read/Write using varlena
284  *****************************************************************************/
285
286 struct varlena *
287 loread(int fd, int len)
288 {
289         struct varlena *retval;
290         int                     totalread = 0;
291
292         retval = (struct varlena *) palloc(VARHDRSZ + len);
293         totalread = lo_read(fd, VARDATA(retval), len);
294         VARSIZE(retval) = totalread + VARHDRSZ;
295
296         return retval;
297 }
298
299 int
300 lowrite(int fd, struct varlena * wbuf)
301 {
302         int                     totalwritten;
303         int                     bytestowrite;
304
305         bytestowrite = VARSIZE(wbuf) - VARHDRSZ;
306         totalwritten = lo_write(fd, VARDATA(wbuf), bytestowrite);
307         return totalwritten;
308 }
309
310 /*****************************************************************************
311  *       Import/Export of Large Object
312  *****************************************************************************/
313
314 /*
315  * lo_import -
316  *        imports a file as an (inversion) large object.
317  */
318 Oid
319 lo_import(text *filename)
320 {
321         File            fd;
322         int                     nbytes,
323                                 tmp;
324         char            buf[BUFSIZE];
325         char            fnamebuf[FNAME_BUFSIZE];
326         LargeObjectDesc *lobj;
327         Oid                     lobjOid;
328
329 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
330         if (!superuser())
331                 elog(ERROR, "You must have Postgres superuser privilege to use "
332                          "server-side lo_import().\n\tAnyone can use the "
333                          "client-side lo_import() provided by libpq.");
334 #endif
335
336         /*
337          * open the file to be read in
338          */
339         nbytes = VARSIZE(filename) - VARHDRSZ + 1;
340         if (nbytes > FNAME_BUFSIZE)
341                 nbytes = FNAME_BUFSIZE;
342         StrNCpy(fnamebuf, VARDATA(filename), nbytes);
343 #ifndef __CYGWIN32__
344         fd = PathNameOpenFile(fnamebuf, O_RDONLY, 0666);
345 #else
346         fd = PathNameOpenFile(fnamebuf, O_RDONLY | O_BINARY, 0666);
347 #endif
348         if (fd < 0)
349         {                                                       /* error */
350                 elog(ERROR, "lo_import: can't open unix file \"%s\": %m",
351                          fnamebuf);
352         }
353
354         /*
355          * create an inversion "object"
356          */
357         lobj = inv_create(INV_READ | INV_WRITE);
358         if (lobj == NULL)
359         {
360                 elog(ERROR, "lo_import: can't create inv object for \"%s\"",
361                          fnamebuf);
362         }
363
364         /*
365          * the oid for the large object is just the oid of the relation
366          * XInv??? which contains the data.
367          */
368         lobjOid = RelationGetRelid(lobj->heap_r);
369
370         /*
371          * read in from the Unix file and write to the inversion file
372          */
373         while ((nbytes = FileRead(fd, buf, BUFSIZE)) > 0)
374         {
375                 tmp = inv_write(lobj, buf, nbytes);
376                 if (tmp < nbytes)
377                         elog(ERROR, "lo_import: error while reading \"%s\"",
378                                  fnamebuf);
379         }
380
381         FileClose(fd);
382         inv_close(lobj);
383
384         return lobjOid;
385 }
386
387 /*
388  * lo_export -
389  *        exports an (inversion) large object.
390  */
391 int4
392 lo_export(Oid lobjId, text *filename)
393 {
394         File            fd;
395         int                     nbytes,
396                                 tmp;
397         char            buf[BUFSIZE];
398         char            fnamebuf[FNAME_BUFSIZE];
399         LargeObjectDesc *lobj;
400         mode_t          oumask;
401
402 #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
403         if (!superuser())
404                 elog(ERROR, "You must have Postgres superuser privilege to use "
405                          "server-side lo_export().\n\tAnyone can use the "
406                          "client-side lo_export() provided by libpq.");
407 #endif
408
409         /*
410          * open the inversion "object"
411          */
412         lobj = inv_open(lobjId, INV_READ);
413         if (lobj == NULL)
414                 elog(ERROR, "lo_export: can't open inv object %u", lobjId);
415
416         /*
417          * open the file to be written to
418          *
419          * Note: we reduce backend's normal 077 umask to the slightly
420          * friendlier 022.  This code used to drop it all the way to 0,
421          * but creating world-writable export files doesn't seem wise.
422          */
423         nbytes = VARSIZE(filename) - VARHDRSZ + 1;
424         if (nbytes > FNAME_BUFSIZE)
425                 nbytes = FNAME_BUFSIZE;
426         StrNCpy(fnamebuf, VARDATA(filename), nbytes);
427         oumask = umask((mode_t) 0022);
428 #ifndef __CYGWIN32__
429         fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC, 0666);
430 #else
431         fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666);
432 #endif
433         umask(oumask);
434         if (fd < 0)
435         {                                                       /* error */
436                 elog(ERROR, "lo_export: can't open unix file \"%s\": %m",
437                          fnamebuf);
438         }
439
440         /*
441          * read in from the Unix file and write to the inversion file
442          */
443         while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
444         {
445                 tmp = FileWrite(fd, buf, nbytes);
446                 if (tmp < nbytes)
447                         elog(ERROR, "lo_export: error while writing \"%s\"",
448                                  fnamebuf);
449         }
450
451         inv_close(lobj);
452         FileClose(fd);
453
454         return 1;
455 }
456
457 /*
458  * lo_commit -
459  *               prepares large objects for transaction commit [PA, 7/17/98]
460  */
461 void
462 lo_commit(bool isCommit)
463 {
464         int                     i;
465         MemoryContext currentContext;
466
467         if (fscxt == NULL)
468                 return;                                 /* no LO operations in this xact */
469
470         currentContext = MemoryContextSwitchTo((MemoryContext) fscxt);
471
472         /* Clean out still-open index scans (not necessary if aborting)
473          * and clear cookies array so that LO fds are no longer good.
474          */
475         for (i = 0; i < MAX_LOBJ_FDS; i++)
476         {
477                 if (cookies[i] != NULL)
478                 {
479                         if (isCommit)
480                                 inv_cleanindex(cookies[i]);
481                         cookies[i] = NULL;
482                 }
483         }
484
485         MemoryContextSwitchTo(currentContext);
486
487         /* Release the LO memory context to prevent permanent memory leaks. */
488         GlobalMemoryDestroy(fscxt);
489         fscxt = NULL;
490 }
491
492
493 /*****************************************************************************
494  *      Support routines for this file
495  *****************************************************************************/
496
497 static int
498 newLOfd(LargeObjectDesc *lobjCookie)
499 {
500         int                     i;
501
502         for (i = 0; i < MAX_LOBJ_FDS; i++)
503         {
504
505                 if (cookies[i] == NULL)
506                 {
507                         cookies[i] = lobjCookie;
508                         return i;
509                 }
510         }
511         return -1;
512 }
513
514 static void
515 deleteLOfd(int fd)
516 {
517         cookies[fd] = NULL;
518 }