diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index b672cf2c7a..07a8392c4e 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.13 2000/04/12 17:14:53 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.14 2000/06/02 03:58:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -44,8 +44,6 @@ bool StopIfError = false; SPINLOCK ControlFileLockId; SPINLOCK XidGenLockId; -extern bool ReleaseDataFile(void); - extern VariableCache ShmemVariableCache; #define MinXLOGbuffers 4 @@ -738,22 +736,13 @@ XLogFileInit(uint32 log, uint32 seg) XLogFileName(path, log, seg); unlink(path); -tryAgain: #ifndef __CYGWIN__ - fd = open(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + fd = BasicOpenFile(path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); #else - fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR); + fd = BasicOpenFile(path, O_RDWR | O_CREAT | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR); #endif - if (fd < 0 && (errno == EMFILE || errno == ENFILE)) - { - fd = errno; - if (!ReleaseDataFile()) - elog(STOP, "Create(logfile %u seg %u) failed: %d (and no one data file can be closed)", - logId, logSeg, fd); - goto tryAgain; - } if (fd < 0) - elog(STOP, "Init(logfile %u seg %u) failed: %d", + elog(STOP, "Open(logfile %u seg %u) failed: %d", logId, logSeg, errno); if (lseek(fd, XLogSegSize - 1, SEEK_SET) != (off_t) (XLogSegSize - 1)) @@ -783,20 +772,11 @@ XLogFileOpen(uint32 log, uint32 seg, bool econt) XLogFileName(path, log, seg); -tryAgain: #ifndef __CYGWIN__ - fd = open(path, O_RDWR); + fd = BasicOpenFile(path, O_RDWR, S_IRUSR | S_IWUSR); #else - fd = open(path, O_RDWR | O_BINARY); + fd = BasicOpenFile(path, O_RDWR | O_BINARY, S_IRUSR | S_IWUSR); #endif - if (fd < 0 && (errno == EMFILE || errno == ENFILE)) - { - fd = errno; - if (!ReleaseDataFile()) - elog(STOP, "Open(logfile %u seg %u) failed: %d (and no one data file can be closed)", - logId, logSeg, fd); - goto tryAgain; - } if (fd < 0) { if (econt && errno == ENOENT) @@ -1102,20 +1082,11 @@ UpdateControlFile() { int fd; -tryAgain: #ifndef __CYGWIN__ - fd = open(ControlFilePath, O_RDWR); + fd = BasicOpenFile(ControlFilePath, O_RDWR, S_IRUSR | S_IWUSR); #else - fd = open(ControlFilePath, O_RDWR | O_BINARY); + fd = BasicOpenFile(ControlFilePath, O_RDWR | O_BINARY, S_IRUSR | S_IWUSR); #endif - if (fd < 0 && (errno == EMFILE || errno == ENFILE)) - { - fd = errno; - if (!ReleaseDataFile()) - elog(STOP, "Open(cntlfile) failed: %d (and no one data file can be closed)", - fd); - goto tryAgain; - } if (fd < 0) elog(STOP, "Open(cntlfile) failed: %d", errno); @@ -1174,9 +1145,9 @@ BootStrapXLOG() #endif #ifndef __CYGWIN__ - fd = open(ControlFilePath, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + fd = BasicOpenFile(ControlFilePath, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); #else - fd = open(ControlFilePath, O_RDWR | O_CREAT | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR); + fd = BasicOpenFile(ControlFilePath, O_RDWR | O_CREAT | O_EXCL | O_BINARY, S_IRUSR | S_IWUSR); #endif if (fd < 0) elog(STOP, "BootStrapXLOG failed to create control file (%s): %d", @@ -1288,20 +1259,11 @@ StartupXLOG() /* * Open/read Control file */ -tryAgain: #ifndef __CYGWIN__ - fd = open(ControlFilePath, O_RDWR); + fd = BasicOpenFile(ControlFilePath, O_RDWR, S_IRUSR | S_IWUSR); #else - fd = open(ControlFilePath, O_RDWR | O_BINARY); + fd = BasicOpenFile(ControlFilePath, O_RDWR | O_BINARY, S_IRUSR | S_IWUSR); #endif - if (fd < 0 && (errno == EMFILE || errno == ENFILE)) - { - fd = errno; - if (!ReleaseDataFile()) - elog(STOP, "Open(\"%s\") failed: %d (and no one data file can be closed)", - ControlFilePath, fd); - goto tryAgain; - } if (fd < 0) elog(STOP, "Open(\"%s\") failed: %d", ControlFilePath, errno); diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 160fa5353b..04303a54b0 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.56 2000/05/30 00:49:44 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.57 2000/06/02 03:58:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -146,13 +146,13 @@ write_password_file(Relation rel) /* * Create a flag file the postmaster will detect the next time it * tries to authenticate a user. The postmaster will know to reload - * the pg_pwd file contents. Note: we used to elog(ERROR) if the - * creat() call failed, but it's a little silly to abort the transaction + * the pg_pwd file contents. Note: we used to elog(ERROR) if the file + * creation failed, but it's a little silly to abort the transaction * at this point, so let's just make it a NOTICE. */ filename = crypt_getpwdreloadfilename(); - flagfd = creat(filename, S_IRUSR | S_IWUSR); - if (flagfd == -1) + flagfd = BasicOpenFile(filename, O_WRONLY | O_CREAT, 0600); + if (flagfd < 0) elog(NOTICE, "%s: %m", filename); else close(flagfd); diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c index 2a6c946636..2d0d733a7c 100644 --- a/src/backend/storage/file/fd.c +++ b/src/backend/storage/file/fd.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.57 2000/05/31 00:28:27 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/file/fd.c,v 1.58 2000/06/02 03:58:32 tgl Exp $ * * NOTES: * @@ -202,6 +202,45 @@ pg_fsync(int fd) return 0; } +/* + * BasicOpenFile --- same as open(2) except can free other FDs if needed + * + * This is exported for use by places that really want a plain kernel FD, + * but need to be proof against running out of FDs. Once an FD has been + * successfully returned, it is the caller's responsibility to ensure that + * it will not be leaked on elog()! Most users should *not* call this + * routine directly, but instead use the VFD abstraction level, which + * provides protection against descriptor leaks as well as management of + * files that need to be open for more than a short period of time. + * + * Ideally this should be the *only* direct call of open() in the backend. + * In practice, the postmaster calls open() directly, and there are some + * direct open() calls done early in backend startup. Those are OK since + * this module wouldn't have any open files to close at that point anyway. + */ +int +BasicOpenFile(FileName fileName, int fileFlags, int fileMode) +{ + int fd; + +tryAgain: + fd = open(fileName, fileFlags, fileMode); + + if (fd >= 0) + return fd; /* success! */ + + if ((errno == EMFILE || errno == ENFILE) && nfile > 0) + { + DO_DB(elog(DEBUG, "BasicOpenFile: not enough descs, retry, er= %d", + errno)); + errno = 0; + ReleaseLruFile(); + goto tryAgain; + } + + return -1; /* failure */ +} + /* * pg_nofile: determine number of filedescriptors that fd.c is allowed to use */ @@ -348,7 +387,6 @@ LruInsert(File file) if (FileIsNotOpen(file)) { - while (nfile + numAllocatedFiles >= pg_nofile()) ReleaseLruFile(); @@ -357,15 +395,8 @@ LruInsert(File file) * to overall system file table being full. So, be prepared to * release another FD if necessary... */ -tryAgain: - vfdP->fd = open(vfdP->fileName, vfdP->fileFlags, vfdP->fileMode); - if (vfdP->fd < 0 && (errno == EMFILE || errno == ENFILE)) - { - errno = 0; - ReleaseLruFile(); - goto tryAgain; - } - + vfdP->fd = BasicOpenFile(vfdP->fileName, vfdP->fileFlags, + vfdP->fileMode); if (vfdP->fd < 0) { DO_DB(elog(DEBUG, "RE_OPEN FAILED: %d", @@ -411,22 +442,6 @@ ReleaseLruFile() LruDelete(VfdCache[0].lruMoreRecently); } -/* - * Force one kernel file descriptor to be released (temporarily). - */ -bool -ReleaseDataFile() -{ - DO_DB(elog(DEBUG, "ReleaseDataFile. Opened %d", nfile)); - - if (nfile <= 0) - return (false); - Assert(VfdCache[0].lruMoreRecently != 0); - LruDelete(VfdCache[0].lruMoreRecently); - - return (true); -} - static File AllocateVfd() { @@ -617,16 +632,7 @@ fileNameOpenFile(FileName fileName, while (nfile + numAllocatedFiles >= pg_nofile()) ReleaseLruFile(); -tryAgain: - vfdP->fd = open(fileName, fileFlags, fileMode); - if (vfdP->fd < 0 && (errno == EMFILE || errno == ENFILE)) - { - DO_DB(elog(DEBUG, "fileNameOpenFile: not enough descs, retry, er= %d", - errno)); - errno = 0; - ReleaseLruFile(); - goto tryAgain; - } + vfdP->fd = BasicOpenFile(fileName, fileFlags, fileMode); if (vfdP->fd < 0) { @@ -990,6 +996,8 @@ FileMarkDirty(File file) * fd.c will automatically close all files opened with AllocateFile at * transaction commit or abort; this prevents FD leakage if a routine * that calls AllocateFile is terminated prematurely by elog(ERROR). + * + * Ideally this should be the *only* direct call of fopen() in the backend. */ FILE * @@ -1005,7 +1013,7 @@ AllocateFile(char *name, char *mode) TryAgain: if ((file = fopen(name, mode)) == NULL) { - if (errno == EMFILE || errno == ENFILE) + if ((errno == EMFILE || errno == ENFILE) && nfile > 0) { DO_DB(elog(DEBUG, "AllocateFile: not enough descs, retry, er= %d", errno)); diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c index e4fd3120d2..4c1cac5472 100644 --- a/src/backend/storage/smgr/md.c +++ b/src/backend/storage/smgr/md.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.68 2000/05/25 23:30:20 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.69 2000/06/02 03:58:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1129,10 +1129,11 @@ _mdfd_blind_getseg(char *dbname, char *relname, Oid dbid, Oid relid, } #endif + /* call fd.c to allow other FDs to be closed if needed */ #ifndef __CYGWIN32__ - fd = open(path, O_RDWR, 0600); + fd = BasicOpenFile(path, O_RDWR, 0600); #else - fd = open(path, O_RDWR | O_BINARY, 0600); + fd = BasicOpenFile(path, O_RDWR | O_BINARY, 0600); #endif if (fd < 0) diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h index fb45e20456..5456f18372 100644 --- a/src/include/storage/fd.h +++ b/src/include/storage/fd.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: fd.h,v 1.21 2000/04/12 17:16:51 momjian Exp $ + * $Id: fd.h,v 1.22 2000/06/02 03:58:31 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,7 +22,7 @@ * Use them for all file activity... * * File fd; - * fd = FilePathOpenFile("foo", O_RDONLY); + * fd = FilePathOpenFile("foo", O_RDONLY, 0600); * * AllocateFile(); * FreeFile(); @@ -64,8 +64,10 @@ extern void FileMarkDirty(File file); extern FILE *AllocateFile(char *name, char *mode); extern void FreeFile(FILE *); +/* If you've really really gotta have a plain kernel FD, use this */ +extern int BasicOpenFile(FileName fileName, int fileFlags, int fileMode); + /* Miscellaneous support routines */ -extern bool ReleaseDataFile(void); extern void closeAllVfds(void); extern void AtEOXact_Files(void); extern int pg_fsync(int fd);