📄 fd.c
字号:
/* returns 0 on success, -1 on re-open failure (with errno set) */static intLruInsert(File file){ Vfd *vfdP; Assert(file != 0); DO_DB(elog(LOG, "LruInsert %d (%s)", file, VfdCache[file].fileName)); vfdP = &VfdCache[file]; if (FileIsNotOpen(file)) { while (nfile + numAllocatedDescs >= max_safe_fds) { if (!ReleaseLruFile()) break; } /* * The open could still fail for lack of file descriptors, eg due to * overall system file table being full. So, be prepared to release * another FD if necessary... */ vfdP->fd = BasicOpenFile(vfdP->fileName, vfdP->fileFlags, vfdP->fileMode); if (vfdP->fd < 0) { DO_DB(elog(LOG, "RE_OPEN FAILED: %d", errno)); return vfdP->fd; } else { DO_DB(elog(LOG, "RE_OPEN SUCCESS")); ++nfile; } /* seek to the right position */ if (vfdP->seekPos != 0L) { long returnValue; returnValue = (long) lseek(vfdP->fd, vfdP->seekPos, SEEK_SET); Assert(returnValue != -1L); } } /* * put it at the head of the Lru ring */ Insert(file); return 0;}static boolReleaseLruFile(void){ DO_DB(elog(LOG, "ReleaseLruFile. Opened %d", nfile)); if (nfile > 0) { /* * There are opened files and so there should be at least one used vfd * in the ring. */ Assert(VfdCache[0].lruMoreRecently != 0); LruDelete(VfdCache[0].lruMoreRecently); return true; /* freed a file */ } return false; /* no files available to free */}static FileAllocateVfd(void){ Index i; File file; DO_DB(elog(LOG, "AllocateVfd. Size %d", SizeVfdCache)); Assert(SizeVfdCache > 0); /* InitFileAccess not called? */ if (VfdCache[0].nextFree == 0) { /* * The free list is empty so it is time to increase the size of the * array. We choose to double it each time this happens. However, * there's not much point in starting *real* small. */ Size newCacheSize = SizeVfdCache * 2; Vfd *newVfdCache; if (newCacheSize < 32) newCacheSize = 32; /* * Be careful not to clobber VfdCache ptr if realloc fails. */ newVfdCache = (Vfd *) realloc(VfdCache, sizeof(Vfd) * newCacheSize); if (newVfdCache == NULL) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); VfdCache = newVfdCache; /* * Initialize the new entries and link them into the free list. */ for (i = SizeVfdCache; i < newCacheSize; i++) { MemSet((char *) &(VfdCache[i]), 0, sizeof(Vfd)); VfdCache[i].nextFree = i + 1; VfdCache[i].fd = VFD_CLOSED; } VfdCache[newCacheSize - 1].nextFree = 0; VfdCache[0].nextFree = SizeVfdCache; /* * Record the new size */ SizeVfdCache = newCacheSize; } file = VfdCache[0].nextFree; VfdCache[0].nextFree = VfdCache[file].nextFree; return file;}static voidFreeVfd(File file){ Vfd *vfdP = &VfdCache[file]; DO_DB(elog(LOG, "FreeVfd: %d (%s)", file, vfdP->fileName ? vfdP->fileName : "")); if (vfdP->fileName != NULL) { free(vfdP->fileName); vfdP->fileName = NULL; } vfdP->fdstate = 0x0; vfdP->nextFree = VfdCache[0].nextFree; VfdCache[0].nextFree = file;}/* * make_database_relative() * Prepend DatabasePath to the given file name. * * Result is a palloc'd string. */static char *make_database_relative(const char *filename){ char *buf; Assert(!is_absolute_path(filename)); buf = (char *) palloc(strlen(DatabasePath) + strlen(filename) + 2); sprintf(buf, "%s/%s", DatabasePath, filename); return buf;}/* returns 0 on success, -1 on re-open failure (with errno set) */static intFileAccess(File file){ int returnValue; DO_DB(elog(LOG, "FileAccess %d (%s)", file, VfdCache[file].fileName)); /* * Is the file open? If not, open it and put it at the head of the LRU * ring (possibly closing the least recently used file to get an FD). */ if (FileIsNotOpen(file)) { returnValue = LruInsert(file); if (returnValue != 0) return returnValue; } else if (VfdCache[0].lruLessRecently != file) { /* * We now know that the file is open and that it is not the last one * accessed, so we need to move it to the head of the Lru ring. */ Delete(file); Insert(file); } return 0;}/* * Called when we get a shared invalidation message on some relation. */#ifdef NOT_USEDvoidFileInvalidate(File file){ Assert(FileIsValid(file)); if (!FileIsNotOpen(file)) LruDelete(file);}#endif/* * open a file in an arbitrary directory * * NB: if the passed pathname is relative (which it usually is), * it will be interpreted relative to the process' working directory * (which should always be $PGDATA when this code is running). */FilePathNameOpenFile(FileName fileName, int fileFlags, int fileMode){ char *fnamecopy; File file; Vfd *vfdP; DO_DB(elog(LOG, "PathNameOpenFile: %s %x %o", fileName, fileFlags, fileMode)); /* * We need a malloc'd copy of the file name; fail cleanly if no room. */ fnamecopy = strdup(fileName); if (fnamecopy == NULL) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"))); file = AllocateVfd(); vfdP = &VfdCache[file]; while (nfile + numAllocatedDescs >= max_safe_fds) { if (!ReleaseLruFile()) break; } vfdP->fd = BasicOpenFile(fileName, fileFlags, fileMode); if (vfdP->fd < 0) { FreeVfd(file); free(fnamecopy); return -1; } ++nfile; DO_DB(elog(LOG, "PathNameOpenFile: success %d", vfdP->fd)); Insert(file); vfdP->fileName = fnamecopy; /* Saved flags are adjusted to be OK for re-opening file */ vfdP->fileFlags = fileFlags & ~(O_CREAT | O_TRUNC | O_EXCL); vfdP->fileMode = fileMode; vfdP->seekPos = 0; vfdP->fdstate = 0x0; return file;}/* * open a file in the database directory ($PGDATA/base/DIROID/) * * The passed name MUST be a relative path. Effectively, this * prepends DatabasePath to it and then acts like PathNameOpenFile. */FileFileNameOpenFile(FileName fileName, int fileFlags, int fileMode){ File fd; char *fname; fname = make_database_relative(fileName); fd = PathNameOpenFile(fname, fileFlags, fileMode); pfree(fname); return fd;}/* * Open a temporary file that will disappear when we close it. * * This routine takes care of generating an appropriate tempfile name. * There's no need to pass in fileFlags or fileMode either, since only * one setting makes any sense for a temp file. * * interXact: if true, don't close the file at end-of-transaction. In * most cases, you don't want temporary files to outlive the transaction * that created them, so this should be false -- but if you need * "somewhat" temporary storage, this might be useful. In either case, * the file is removed when the File is explicitly closed. */FileOpenTemporaryFile(bool interXact){ char tempfilepath[MAXPGPATH]; File file; /* * Generate a tempfile name that should be unique within the current * database instance. */ snprintf(tempfilepath, sizeof(tempfilepath), "%s/%s%d.%ld", PG_TEMP_FILES_DIR, PG_TEMP_FILE_PREFIX, MyProcPid, tempFileCounter++); /* * Open the file. Note: we don't use O_EXCL, in case there is an orphaned * temp file that can be reused. */ file = FileNameOpenFile(tempfilepath, O_RDWR | O_CREAT | O_TRUNC | PG_BINARY, 0600); if (file <= 0) { char *dirpath; /* * We might need to create the pg_tempfiles subdirectory, if no one * has yet done so. * * Don't check for error from mkdir; it could fail if someone else * just did the same thing. If it doesn't work then we'll bomb out on * the second create attempt, instead. */ dirpath = make_database_relative(PG_TEMP_FILES_DIR); mkdir(dirpath, S_IRWXU); pfree(dirpath); file = FileNameOpenFile(tempfilepath, O_RDWR | O_CREAT | O_TRUNC | PG_BINARY, 0600); if (file <= 0) elog(ERROR, "could not create temporary file \"%s\": %m", tempfilepath); } /* Mark it for deletion at close */ VfdCache[file].fdstate |= FD_TEMPORARY; /* Mark it for deletion at EOXact */ if (!interXact) { VfdCache[file].fdstate |= FD_XACT_TEMPORARY; VfdCache[file].create_subid = GetCurrentSubTransactionId(); } return file;}/* * close a file when done with it */voidFileClose(File file){ Vfd *vfdP; Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileClose: %d (%s)", file, VfdCache[file].fileName)); vfdP = &VfdCache[file]; if (!FileIsNotOpen(file)) { /* remove the file from the lru ring */ Delete(file); /* close the file */ if (close(vfdP->fd)) elog(ERROR, "failed to close \"%s\": %m", vfdP->fileName); --nfile; vfdP->fd = VFD_CLOSED; } /* * Delete the file if it was temporary */ if (vfdP->fdstate & FD_TEMPORARY) { /* reset flag so that die() interrupt won't cause problems */ vfdP->fdstate &= ~FD_TEMPORARY; if (unlink(vfdP->fileName)) elog(LOG, "failed to unlink \"%s\": %m", vfdP->fileName); } /* * Return the Vfd slot to the free list */ FreeVfd(file);}/* * close a file and forcibly delete the underlying Unix file */voidFileUnlink(File file){ Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileUnlink: %d (%s)", file, VfdCache[file].fileName)); /* force FileClose to delete it */ VfdCache[file].fdstate |= FD_TEMPORARY; FileClose(file);}intFileRead(File file, char *buffer, int amount){ int returnCode; Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileRead: %d (%s) %ld %d %p", file, VfdCache[file].fileName, VfdCache[file].seekPos, amount, buffer)); returnCode = FileAccess(file); if (returnCode < 0) return returnCode;retry: returnCode = read(VfdCache[file].fd, buffer, amount); if (returnCode >= 0) VfdCache[file].seekPos += returnCode; else { /* * Windows may run out of kernel buffers and return "Insufficient * system resources" error. Wait a bit and retry to solve it. * * It is rumored that EINTR is also possible on some Unix filesystems, * in which case immediate retry is indicated. */#ifdef WIN32 DWORD error = GetLastError(); switch (error) { case ERROR_NO_SYSTEM_RESOURCES: pg_usleep(1000L); errno = EINTR; break; default: _dosmaperr(error); break; }#endif /* OK to retry if interrupted */ if (errno == EINTR) goto retry; /* Trouble, so assume we don't know the file position anymore */ VfdCache[file].seekPos = FileUnknownPos; } return returnCode;}intFileWrite(File file, char *buffer, int amount){ int returnCode; Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileWrite: %d (%s) %ld %d %p", file, VfdCache[file].fileName, VfdCache[file].seekPos, amount, buffer)); returnCode = FileAccess(file); if (returnCode < 0) return returnCode;retry: errno = 0; returnCode = write(VfdCache[file].fd, buffer, amount); /* if write didn't set errno, assume problem is no disk space */ if (returnCode != amount && errno == 0) errno = ENOSPC; if (returnCode >= 0) VfdCache[file].seekPos += returnCode; else { /* * See comments in FileRead() */#ifdef WIN32 DWORD error = GetLastError(); switch (error) { case ERROR_NO_SYSTEM_RESOURCES: pg_usleep(1000L); errno = EINTR; break; default: _dosmaperr(error); break; }#endif /* OK to retry if interrupted */ if (errno == EINTR) goto retry; /* Trouble, so assume we don't know the file position anymore */ VfdCache[file].seekPos = FileUnknownPos; } return returnCode;}intFileSync(File file){ int returnCode; Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileSync: %d (%s)", file, VfdCache[file].fileName)); returnCode = FileAccess(file); if (returnCode < 0) return returnCode; return pg_fsync(VfdCache[file].fd);}longFileSeek(File file, long offset, int whence){ int returnCode; Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileSeek: %d (%s) %ld %ld %d", file, VfdCache[file].fileName, VfdCache[file].seekPos, offset, whence)); if (FileIsNotOpen(file))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -