|
7 | 7 | * Portions Copyright (c) 1994, Regents of the University of California
|
8 | 8 | *
|
9 | 9 | * IDENTIFICATION
|
10 |
| - * $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.80 2001/06/06 17:07:46 tgl Exp $ |
| 10 | + * $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.81 2001/06/11 04:12:29 tgl Exp $ |
11 | 11 | *
|
12 | 12 | * NOTES:
|
13 | 13 | *
|
|
44 | 44 | #include <sys/file.h>
|
45 | 45 | #include <sys/param.h>
|
46 | 46 | #include <sys/stat.h>
|
| 47 | +#include <dirent.h> |
47 | 48 | #include <errno.h>
|
48 | 49 | #include <unistd.h>
|
49 | 50 | #include <fcntl.h>
|
50 | 51 |
|
51 | 52 | #include "miscadmin.h"
|
52 | 53 | #include "storage/fd.h"
|
53 | 54 |
|
| 55 | + |
| 56 | +/* Filename components for OpenTemporaryFile */ |
| 57 | +#define PG_TEMP_FILES_DIR "pg_tempfiles" |
| 58 | +#define PG_TEMP_FILE_PREFIX "pg_temp" |
| 59 | + |
| 60 | + |
54 | 61 | /*
|
55 | 62 | * Problem: Postgres does a system(ld...) to do dynamic loading.
|
56 | 63 | * This will open several extra files in addition to those used by
|
@@ -189,7 +196,7 @@ static void FreeVfd(File file);
|
189 | 196 |
|
190 | 197 | static int FileAccess(File file);
|
191 | 198 | static File fileNameOpenFile(FileName fileName, int fileFlags, int fileMode);
|
192 |
| -static char *filepath(char *filename); |
| 199 | +static char *filepath(const char *filename); |
193 | 200 | static long pg_nofile(void);
|
194 | 201 |
|
195 | 202 | /*
|
@@ -568,28 +575,27 @@ FreeVfd(File file)
|
568 | 575 | /* filepath()
|
569 | 576 | * Convert given pathname to absolute.
|
570 | 577 | *
|
| 578 | + * Result is a palloc'd string. |
| 579 | + * |
571 | 580 | * (Generally, this isn't actually necessary, considering that we
|
572 | 581 | * should be cd'd into the database directory. Presently it is only
|
573 | 582 | * necessary to do it in "bootstrap" mode. Maybe we should change
|
574 | 583 | * bootstrap mode to do the cd, and save a few cycles/bytes here.)
|
575 | 584 | */
|
576 | 585 | static char *
|
577 |
| -filepath(char *filename) |
| 586 | +filepath(const char *filename) |
578 | 587 | {
|
579 | 588 | char *buf;
|
580 |
| - int len; |
581 | 589 |
|
582 | 590 | /* Not an absolute path name? Then fill in with database path... */
|
583 | 591 | if (*filename != '/')
|
584 | 592 | {
|
585 |
| - len = strlen(DatabasePath) + strlen(filename) + 2; |
586 |
| - buf = (char *) palloc(len); |
| 593 | + buf = (char *) palloc(strlen(DatabasePath) + strlen(filename) + 2); |
587 | 594 | sprintf(buf, "%s/%s", DatabasePath, filename);
|
588 | 595 | }
|
589 | 596 | else
|
590 | 597 | {
|
591 |
| - buf = (char *) palloc(strlen(filename) + 1); |
592 |
| - strcpy(buf, filename); |
| 598 | + buf = pstrdup(filename); |
593 | 599 | }
|
594 | 600 |
|
595 | 601 | #ifdef FILEDEBUG
|
@@ -742,21 +748,46 @@ PathNameOpenFile(FileName fileName, int fileFlags, int fileMode)
|
742 | 748 | File
|
743 | 749 | OpenTemporaryFile(void)
|
744 | 750 | {
|
745 |
| - char tempfilename[64]; |
| 751 | + char tempfilepath[128]; |
746 | 752 | File file;
|
747 | 753 |
|
748 | 754 | /*
|
749 | 755 | * Generate a tempfile name that's unique within the current
|
750 |
| - * transaction |
| 756 | + * transaction and database instance. |
751 | 757 | */
|
752 |
| - snprintf(tempfilename, sizeof(tempfilename), |
753 |
| - "pg_sorttemp%d.%ld", MyProcPid, tempFileCounter++); |
| 758 | + snprintf(tempfilepath, sizeof(tempfilepath), |
| 759 | + "%s/%s%d.%ld", PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX, |
| 760 | + MyProcPid, tempFileCounter++); |
754 | 761 |
|
755 |
| - /* Open the file */ |
756 |
| - file = FileNameOpenFile(tempfilename, |
757 |
| - O_RDWR | O_CREAT | O_TRUNC | PG_BINARY, 0600); |
| 762 | + /* |
| 763 | + * Open the file. Note: we don't use O_EXCL, in case there is an |
| 764 | + * orphaned temp file that can be reused. |
| 765 | + */ |
| 766 | + file = FileNameOpenFile(tempfilepath, |
| 767 | + O_RDWR | O_CREAT | O_TRUNC | PG_BINARY, |
| 768 | + 0600); |
758 | 769 | if (file <= 0)
|
759 |
| - elog(ERROR, "Failed to create temporary file %s", tempfilename); |
| 770 | + { |
| 771 | + char *dirpath; |
| 772 | + |
| 773 | + /* |
| 774 | + * We might need to create the pg_tempfiles subdirectory, if |
| 775 | + * no one has yet done so. |
| 776 | + * |
| 777 | + * Don't check for error from mkdir; it could fail if someone else |
| 778 | + * just did the same thing. If it doesn't work then we'll bomb out |
| 779 | + * on the second create attempt, instead. |
| 780 | + */ |
| 781 | + dirpath = filepath(PG_TEMP_FILES_DIR); |
| 782 | + mkdir(dirpath, S_IRWXU); |
| 783 | + pfree(dirpath); |
| 784 | + |
| 785 | + file = FileNameOpenFile(tempfilepath, |
| 786 | + O_RDWR | O_CREAT | O_TRUNC | PG_BINARY, |
| 787 | + 0600); |
| 788 | + if (file <= 0) |
| 789 | + elog(ERROR, "Failed to create temporary file %s", tempfilepath); |
| 790 | + } |
760 | 791 |
|
761 | 792 | /* Mark it for deletion at close or EOXact */
|
762 | 793 | VfdCache[file].fdstate |= FD_TEMPORARY;
|
@@ -1203,3 +1234,76 @@ AtEOXact_Files(void)
|
1203 | 1234 | */
|
1204 | 1235 | tempFileCounter = 0;
|
1205 | 1236 | }
|
| 1237 | + |
| 1238 | + |
| 1239 | +/* |
| 1240 | + * Remove old temporary files |
| 1241 | + * |
| 1242 | + * This should be called during postmaster startup. It will forcibly |
| 1243 | + * remove any leftover files created by OpenTemporaryFile. |
| 1244 | + */ |
| 1245 | +void |
| 1246 | +RemovePgTempFiles(void) |
| 1247 | +{ |
| 1248 | + char db_path[MAXPGPATH]; |
| 1249 | + char temp_path[MAXPGPATH]; |
| 1250 | + char rm_path[MAXPGPATH]; |
| 1251 | + DIR *db_dir; |
| 1252 | + DIR *temp_dir; |
| 1253 | + struct dirent *db_de; |
| 1254 | + struct dirent *temp_de; |
| 1255 | + |
| 1256 | + /* |
| 1257 | + * Cycle through pg_tempfiles for all databases |
| 1258 | + * and remove old temp files. |
| 1259 | + */ |
| 1260 | + snprintf(db_path, sizeof(db_path), "%s/base", DataDir); |
| 1261 | + if ((db_dir = opendir(db_path)) != NULL) |
| 1262 | + { |
| 1263 | + while ((db_de = readdir(db_dir)) != NULL) |
| 1264 | + { |
| 1265 | + if (strcmp(db_de->d_name, ".") == 0 || |
| 1266 | + strcmp(db_de->d_name, "..") == 0) |
| 1267 | + continue; |
| 1268 | + |
| 1269 | + snprintf(temp_path, sizeof(temp_path), |
| 1270 | + "%s/%s/%s", |
| 1271 | + db_path, db_de->d_name, |
| 1272 | + PG_TEMP_FILES_DIR); |
| 1273 | + if ((temp_dir = opendir(temp_path)) != NULL) |
| 1274 | + { |
| 1275 | + while ((temp_de = readdir(temp_dir)) != NULL) |
| 1276 | + { |
| 1277 | + if (strcmp(temp_de->d_name, ".") == 0 || |
| 1278 | + strcmp(temp_de->d_name, "..") == 0) |
| 1279 | + continue; |
| 1280 | + |
| 1281 | + snprintf(rm_path, sizeof(temp_path), |
| 1282 | + "%s/%s/%s/%s", |
| 1283 | + db_path, db_de->d_name, |
| 1284 | + PG_TEMP_FILES_DIR, |
| 1285 | + temp_de->d_name); |
| 1286 | + |
| 1287 | + if (strncmp(temp_de->d_name, |
| 1288 | + PG_TEMP_FILE_PREFIX, |
| 1289 | + strlen(PG_TEMP_FILE_PREFIX)) == 0) |
| 1290 | + { |
| 1291 | + unlink(rm_path); |
| 1292 | + } |
| 1293 | + else |
| 1294 | + { |
| 1295 | + /* |
| 1296 | + * would prefer to use elog here, but it's not |
| 1297 | + * up and running during postmaster startup... |
| 1298 | + */ |
| 1299 | + fprintf(stderr, |
| 1300 | + "Unexpected file found in temporary-files directory: %s\n", |
| 1301 | + rm_path); |
| 1302 | + } |
| 1303 | + } |
| 1304 | + closedir(temp_dir); |
| 1305 | + } |
| 1306 | + } |
| 1307 | + closedir(db_dir); |
| 1308 | + } |
| 1309 | +} |
0 commit comments