📄 fd.c
字号:
{ switch (whence) { case SEEK_SET: if (offset < 0) elog(ERROR, "invalid seek offset: %ld", offset); VfdCache[file].seekPos = offset; break; case SEEK_CUR: VfdCache[file].seekPos += offset; break; case SEEK_END: returnCode = FileAccess(file); if (returnCode < 0) return returnCode; VfdCache[file].seekPos = lseek(VfdCache[file].fd, offset, whence); break; default: elog(ERROR, "invalid whence: %d", whence); break; } } else { switch (whence) { case SEEK_SET: if (offset < 0) elog(ERROR, "invalid seek offset: %ld", offset); if (VfdCache[file].seekPos != offset) VfdCache[file].seekPos = lseek(VfdCache[file].fd, offset, whence); break; case SEEK_CUR: if (offset != 0 || VfdCache[file].seekPos == FileUnknownPos) VfdCache[file].seekPos = lseek(VfdCache[file].fd, offset, whence); break; case SEEK_END: VfdCache[file].seekPos = lseek(VfdCache[file].fd, offset, whence); break; default: elog(ERROR, "invalid whence: %d", whence); break; } } return VfdCache[file].seekPos;}/* * XXX not actually used but here for completeness */#ifdef NOT_USEDlongFileTell(File file){ Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileTell %d (%s)", file, VfdCache[file].fileName)); return VfdCache[file].seekPos;}#endifintFileTruncate(File file, long offset){ int returnCode; Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileTruncate %d (%s)", file, VfdCache[file].fileName)); returnCode = FileAccess(file); if (returnCode < 0) return returnCode; returnCode = ftruncate(VfdCache[file].fd, (size_t) offset); return returnCode;}/* * Routines that want to use stdio (ie, FILE*) should use AllocateFile * rather than plain fopen(). This lets fd.c deal with freeing FDs if * necessary to open the file. When done, call FreeFile rather than fclose. * * Note that files that will be open for any significant length of time * should NOT be handled this way, since they cannot share kernel file * descriptors with other files; there is grave risk of running out of FDs * if anyone locks down too many FDs. Most callers of this routine are * simply reading a config file that they will read and close immediately. * * 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 ereport(ERROR). * * Ideally this should be the *only* direct call of fopen() in the backend. */FILE *AllocateFile(char *name, char *mode){ FILE *file; DO_DB(elog(LOG, "AllocateFile: Allocated %d (%s)", numAllocatedDescs, name)); /* * The test against MAX_ALLOCATED_DESCS prevents us from overflowing * allocatedFiles[]; the test against max_safe_fds prevents AllocateFile * from hogging every one of the available FDs, which'd lead to infinite * looping. */ if (numAllocatedDescs >= MAX_ALLOCATED_DESCS || numAllocatedDescs >= max_safe_fds - 1) elog(ERROR, "too many private files demanded");TryAgain: if ((file = fopen(name, mode)) != NULL) { AllocateDesc *desc = &allocatedDescs[numAllocatedDescs]; desc->kind = AllocateDescFile; desc->desc.file = file; desc->create_subid = GetCurrentSubTransactionId(); numAllocatedDescs++; return desc->desc.file; } if (errno == EMFILE || errno == ENFILE) { int save_errno = errno; ereport(LOG, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("out of file descriptors: %m; release and retry"))); errno = 0; if (ReleaseLruFile()) goto TryAgain; errno = save_errno; } return NULL;}/* * Free an AllocateDesc of either type. * * The argument *must* point into the allocatedDescs[] array. */static intFreeDesc(AllocateDesc *desc){ int result; /* Close the underlying object */ switch (desc->kind) { case AllocateDescFile: result = fclose(desc->desc.file); break; case AllocateDescDir: result = closedir(desc->desc.dir); break; default: elog(ERROR, "AllocateDesc kind not recognized"); result = 0; /* keep compiler quiet */ break; } /* Compact storage in the allocatedDescs array */ numAllocatedDescs--; *desc = allocatedDescs[numAllocatedDescs]; return result;}/* * Close a file returned by AllocateFile. * * Note we do not check fclose's return value --- it is up to the caller * to handle close errors. */intFreeFile(FILE *file){ int i; DO_DB(elog(LOG, "FreeFile: Allocated %d", numAllocatedDescs)); /* Remove file from list of allocated files, if it's present */ for (i = numAllocatedDescs; --i >= 0;) { AllocateDesc *desc = &allocatedDescs[i]; if (desc->kind == AllocateDescFile && desc->desc.file == file) return FreeDesc(desc); } /* Only get here if someone passes us a file not in allocatedDescs */ elog(WARNING, "file passed to FreeFile was not obtained from AllocateFile"); return fclose(file);}/* * Routines that want to use <dirent.h> (ie, DIR*) should use AllocateDir * rather than plain opendir(). This lets fd.c deal with freeing FDs if * necessary to open the directory, and with closing it after an elog. * When done, call FreeDir rather than closedir. * * Ideally this should be the *only* direct call of opendir() in the backend. */DIR *AllocateDir(const char *dirname){ DIR *dir; DO_DB(elog(LOG, "AllocateDir: Allocated %d (%s)", numAllocatedDescs, dirname)); /* * The test against MAX_ALLOCATED_DESCS prevents us from overflowing * allocatedDescs[]; the test against max_safe_fds prevents AllocateDir * from hogging every one of the available FDs, which'd lead to infinite * looping. */ if (numAllocatedDescs >= MAX_ALLOCATED_DESCS || numAllocatedDescs >= max_safe_fds - 1) elog(ERROR, "too many private dirs demanded");TryAgain: if ((dir = opendir(dirname)) != NULL) { AllocateDesc *desc = &allocatedDescs[numAllocatedDescs]; desc->kind = AllocateDescDir; desc->desc.dir = dir; desc->create_subid = GetCurrentSubTransactionId(); numAllocatedDescs++; return desc->desc.dir; } if (errno == EMFILE || errno == ENFILE) { int save_errno = errno; ereport(LOG, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("out of file descriptors: %m; release and retry"))); errno = 0; if (ReleaseLruFile()) goto TryAgain; errno = save_errno; } return NULL;}/* * Read a directory opened with AllocateDir, ereport'ing any error. * * This is easier to use than raw readdir() since it takes care of some * otherwise rather tedious and error-prone manipulation of errno. Also, * if you are happy with a generic error message for AllocateDir failure, * you can just do * * dir = AllocateDir(path); * while ((dirent = ReadDir(dir, path)) != NULL) * process dirent; * FreeDir(path); * * since a NULL dir parameter is taken as indicating AllocateDir failed. * (Make sure errno hasn't been changed since AllocateDir if you use this * shortcut.) * * The pathname passed to AllocateDir must be passed to this routine too, * but it is only used for error reporting. */struct dirent *ReadDir(DIR *dir, const char *dirname){ struct dirent *dent; /* Give a generic message for AllocateDir failure, if caller didn't */ if (dir == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", dirname))); errno = 0; if ((dent = readdir(dir)) != NULL) return dent;#ifdef WIN32 /* * This fix is in mingw cvs (runtime/mingwex/dirent.c rev 1.4), but not in * released version */ if (GetLastError() == ERROR_NO_MORE_FILES) errno = 0;#endif if (errno) ereport(ERROR, (errcode_for_file_access(), errmsg("could not read directory \"%s\": %m", dirname))); return NULL;}/* * Close a directory opened with AllocateDir. * * Note we do not check closedir's return value --- it is up to the caller * to handle close errors. */intFreeDir(DIR *dir){ int i; DO_DB(elog(LOG, "FreeDir: Allocated %d", numAllocatedDescs)); /* Remove dir from list of allocated dirs, if it's present */ for (i = numAllocatedDescs; --i >= 0;) { AllocateDesc *desc = &allocatedDescs[i]; if (desc->kind == AllocateDescDir && desc->desc.dir == dir) return FreeDesc(desc); } /* Only get here if someone passes us a dir not in allocatedDescs */ elog(WARNING, "dir passed to FreeDir was not obtained from AllocateDir"); return closedir(dir);}/* * closeAllVfds * * Force all VFDs into the physically-closed state, so that the fewest * possible number of kernel file descriptors are in use. There is no * change in the logical state of the VFDs. */voidcloseAllVfds(void){ Index i; if (SizeVfdCache > 0) { Assert(FileIsNotOpen(0)); /* Make sure ring not corrupted */ for (i = 1; i < SizeVfdCache; i++) { if (!FileIsNotOpen(i)) LruDelete(i); } }}/* * AtEOSubXact_Files * * Take care of subtransaction commit/abort. At abort, we close temp files * that the subtransaction may have opened. At commit, we reassign the * files that were opened to the parent subtransaction. */voidAtEOSubXact_Files(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid){ Index i; if (SizeVfdCache > 0) { Assert(FileIsNotOpen(0)); /* Make sure ring not corrupted */ for (i = 1; i < SizeVfdCache; i++) { unsigned short fdstate = VfdCache[i].fdstate; if ((fdstate & FD_XACT_TEMPORARY) && VfdCache[i].create_subid == mySubid) { if (isCommit) VfdCache[i].create_subid = parentSubid; else if (VfdCache[i].fileName != NULL) FileClose(i); } } } for (i = 0; i < numAllocatedDescs; i++) { if (allocatedDescs[i].create_subid == mySubid) { if (isCommit) allocatedDescs[i].create_subid = parentSubid; else { /* have to recheck the item after FreeDesc (ugly) */ FreeDesc(&allocatedDescs[i--]); } } }}/* * AtEOXact_Files * * This routine is called during transaction commit or abort (it doesn't * particularly care which). All still-open per-transaction temporary file * VFDs are closed, which also causes the underlying files to be * deleted. Furthermore, all "allocated" stdio files are closed. */voidAtEOXact_Files(void){ CleanupTempFiles(false);}/* * AtProcExit_Files * * on_proc_exit hook to clean up temp files during backend shutdown. * Here, we want to clean up *all* temp files including interXact ones. */static voidAtProcExit_Files(int code, Datum arg){ CleanupTempFiles(true);}/* * Close temporary files and delete their underlying files. * * isProcExit: if true, this is being called as the backend process is * exiting. If that's the case, we should remove all temporary files; if * that's not the case, we are being called for transaction commit/abort * and should only remove transaction-local temp files. In either case, * also clean up "allocated" stdio files and dirs. */static voidCleanupTempFiles(bool isProcExit){ Index i; if (SizeVfdCache > 0) { Assert(FileIsNotOpen(0)); /* Make sure ring not corrupted */ for (i = 1; i < SizeVfdCache; i++) { unsigned short fdstate = VfdCache[i].fdstate; if ((fdstate & FD_TEMPORARY) && VfdCache[i].fileName != NULL) { /* * If we're in the process of exiting a backend process, close * all temporary files. Otherwise, only close temporary files * local to the current transaction. */ if (isProcExit || (fdstate & FD_XACT_TEMPORARY)) FileClose(i); } } } while (numAllocatedDescs > 0) FreeDesc(&allocatedDescs[0]);}/* * Remove temporary files left over from a prior postmaster session * * This should be called during postmaster startup. It will forcibly * remove any leftover files created by OpenTemporaryFile. * * NOTE: we could, but don't, call this during a post-backend-crash restart * cycle. The argument for not doing it is that someone might want to examine * the temp files for debugging purposes. This does however mean that * OpenTemporaryFile had better allow for collision with an existing temp * file name. */voidRemovePgTempFiles(void){ char temp_path[MAXPGPATH]; DIR *db_dir; struct dirent *db_de; /* * Cycle through pgsql_tmp directories for all databases and remove old * temp files. */ db_dir = AllocateDir("base"); while ((db_de = ReadDir(db_dir, "base")) != NULL) { if (strcmp(db_de->d_name, ".") == 0 || strcmp(db_de->d_name, "..") == 0) continue; snprintf(temp_path, sizeof(temp_path), "base/%s/%s", db_de->d_name, PG_TEMP_FILES_DIR); RemovePgTempFilesInDir(temp_path); } FreeDir(db_dir); /* * In EXEC_BACKEND case there is a pgsql_tmp directory at the top level of * DataDir as well. */#ifdef EXEC_BACKEND RemovePgTempFilesInDir(PG_TEMP_FILES_DIR);#endif}/* Process one pgsql_tmp directory for RemovePgTempFiles */static voidRemovePgTempFilesInDir(const char *tmpdirname){ DIR *temp_dir; struct dirent *temp_de; char rm_path[MAXPGPATH]; temp_dir = AllocateDir(tmpdirname); if (temp_dir == NULL) { /* anything except ENOENT is fishy */ if (errno != ENOENT) elog(LOG, "could not open temporary-files directory \"%s\": %m", tmpdirname); return; } while ((temp_de = ReadDir(temp_dir, tmpdirname)) != NULL) { if (strcmp(temp_de->d_name, ".") == 0 || strcmp(temp_de->d_name, "..") == 0) continue; snprintf(rm_path, sizeof(rm_path), "%s/%s", tmpdirname, temp_de->d_name); if (strncmp(temp_de->d_name, PG_TEMP_FILE_PREFIX, strlen(PG_TEMP_FILE_PREFIX)) == 0) unlink(rm_path); /* note we ignore any error */ else elog(LOG, "unexpected file found in temporary-files directory: \"%s\"", rm_path); } FreeDir(temp_dir);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -