📄 fd.c
字号:
{ DO_DB(elog(DEBUG, "fileNameOpenFile: not enough descs, retry, er= %d", errno)); errno = 0; ReleaseLruFile(); goto tryAgain; } if (vfdP->fd < 0) { FreeVfd(file); return -1; } ++nfile; DO_DB(elog(DEBUG, "fileNameOpenFile: success %d", vfdP->fd)); Insert(file); vfdP->fileName = malloc(strlen(fileName) + 1); strcpy(vfdP->fileName, fileName); vfdP->fileFlags = fileFlags & ~(O_TRUNC | O_EXCL); vfdP->fileMode = fileMode; vfdP->seekPos = 0; vfdP->fdstate = 0x0; return file;}/* * open a file in the database directory ($PGDATA/base/...) */FileFileNameOpenFile(FileName fileName, int fileFlags, int fileMode){ File fd; char *fname; fname = filepath(fileName); fd = fileNameOpenFile(fname, fileFlags, fileMode); pfree(fname); return fd;}/* * open a file in an arbitrary directory */FilePathNameOpenFile(FileName fileName, int fileFlags, int fileMode){ return fileNameOpenFile(fileName, fileFlags, fileMode);}/* * 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. */FileOpenTemporaryFile(void){ char tempfilename[64]; File file; /* * Generate a tempfile name that's unique within the current * transaction */ snprintf(tempfilename, sizeof(tempfilename), "pg_sorttemp%d.%ld", MyProcPid, tempFileCounter++); /* Open the file */#ifndef __CYGWIN32__ file = FileNameOpenFile(tempfilename, O_RDWR | O_CREAT | O_TRUNC, 0600);#else file = FileNameOpenFile(tempfilename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600);#endif if (file <= 0) elog(ERROR, "Failed to create temporary file %s", tempfilename); /* Mark it for deletion at close or EOXact */ VfdCache[file].fdstate |= FD_TEMPORARY; return file;}/* * close a file when done with it */voidFileClose(File file){ int returnValue; Assert(FileIsValid(file)); DO_DB(elog(DEBUG, "FileClose: %d (%s)", file, VfdCache[file].fileName)); if (!FileIsNotOpen(file)) { /* remove the file from the lru ring */ Delete(file); /* if we did any writes, sync the file before closing */ if (VfdCache[file].fdstate & FD_DIRTY) { returnValue = pg_fsync(VfdCache[file].fd); Assert(returnValue != -1); VfdCache[file].fdstate &= ~FD_DIRTY; } /* close the file */ returnValue = close(VfdCache[file].fd); Assert(returnValue != -1); --nfile; VfdCache[file].fd = VFD_CLOSED; } /* * Delete the file if it was temporary */ if (VfdCache[file].fdstate & FD_TEMPORARY) unlink(VfdCache[file].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(DEBUG, "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(DEBUG, "FileRead: %d (%s) %d %p", file, VfdCache[file].fileName, amount, buffer)); FileAccess(file); returnCode = read(VfdCache[file].fd, buffer, amount); if (returnCode > 0) VfdCache[file].seekPos += returnCode; return returnCode;}intFileWrite(File file, char *buffer, int amount){ int returnCode; Assert(FileIsValid(file)); DO_DB(elog(DEBUG, "FileWrite: %d (%s) %d %p", file, VfdCache[file].fileName, amount, buffer)); FileAccess(file); returnCode = write(VfdCache[file].fd, buffer, amount); if (returnCode > 0) VfdCache[file].seekPos += returnCode; /* record the write */ VfdCache[file].fdstate |= FD_DIRTY; return returnCode;}longFileSeek(File file, long offset, int whence){ Assert(FileIsValid(file)); DO_DB(elog(DEBUG, "FileSeek: %d (%s) %ld %d", file, VfdCache[file].fileName, offset, whence)); if (FileIsNotOpen(file)) { switch (whence) { case SEEK_SET: VfdCache[file].seekPos = offset; break; case SEEK_CUR: VfdCache[file].seekPos += offset; break; case SEEK_END: FileAccess(file); VfdCache[file].seekPos = lseek(VfdCache[file].fd, offset, whence); break; default: elog(ERROR, "FileSeek: invalid whence: %d", whence); break; } } else VfdCache[file].seekPos = lseek(VfdCache[file].fd, offset, whence); return VfdCache[file].seekPos;}/* * XXX not actually used but here for completeness */#ifdef NOT_USEDlongFileTell(File file){ Assert(FileIsValid(file)); DO_DB(elog(DEBUG, "FileTell %d (%s)", file, VfdCache[file].fileName)); return VfdCache[file].seekPos;}#endifintFileTruncate(File file, int offset){ int returnCode; Assert(FileIsValid(file)); DO_DB(elog(DEBUG, "FileTruncate %d (%s)", file, VfdCache[file].fileName)); FileSync(file); FileAccess(file); returnCode = ftruncate(VfdCache[file].fd, offset); return returnCode;}intFileSync(File file){ int returnCode; Assert(FileIsValid(file)); /* * If the file isn't open, then we don't need to sync it; we always * sync files when we close them. Also, if we haven't done any writes * that we haven't already synced, we can ignore the request. */ if (VfdCache[file].fd < 0 || !(VfdCache[file].fdstate & FD_DIRTY)) returnCode = 0; else { returnCode = pg_fsync(VfdCache[file].fd); VfdCache[file].fdstate &= ~FD_DIRTY; } return returnCode;}intFileNameUnlink(char *filename){ int retval; char *fname; fname = filepath(filename); retval = unlink(fname); pfree(fname); return retval;}/* * 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 elog(ERROR). */FILE *AllocateFile(char *name, char *mode){ FILE *file; DO_DB(elog(DEBUG, "AllocateFile: Allocated %d.", numAllocatedFiles)); if (numAllocatedFiles >= MAX_ALLOCATED_FILES) elog(ERROR, "AllocateFile: too many private FDs demanded");TryAgain: if ((file = fopen(name, mode)) == NULL) { if (errno == EMFILE || errno == ENFILE) { DO_DB(elog(DEBUG, "AllocateFile: not enough descs, retry, er= %d", errno)); errno = 0; ReleaseLruFile(); goto TryAgain; } } else allocatedFiles[numAllocatedFiles++] = file; return file;}voidFreeFile(FILE *file){ int i; DO_DB(elog(DEBUG, "FreeFile: Allocated %d.", numAllocatedFiles)); /* Remove file from list of allocated files, if it's present */ for (i = numAllocatedFiles; --i >= 0;) { if (allocatedFiles[i] == file) { allocatedFiles[i] = allocatedFiles[--numAllocatedFiles]; break; } } if (i < 0) elog(NOTICE, "FreeFile: file was not obtained from AllocateFile"); fclose(file);}/* * 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(){ Index i; if (SizeVfdCache > 0) { Assert(FileIsNotOpen(0)); /* Make sure ring not corrupted */ for (i = 1; i < SizeVfdCache; i++) { if (!FileIsNotOpen(i)) LruDelete(i); } }}/* * AtEOXact_Files * * This routine is called during transaction commit or abort (it doesn't * particularly care which). All still-open temporary-file VFDs are closed, * which also causes the underlying files to be deleted. Furthermore, * all "allocated" stdio files are closed. */voidAtEOXact_Files(void){ Index i; if (SizeVfdCache > 0) { Assert(FileIsNotOpen(0)); /* Make sure ring not corrupted */ for (i = 1; i < SizeVfdCache; i++) { if ((VfdCache[i].fdstate & FD_TEMPORARY) && VfdCache[i].fileName != NULL) FileClose(i); } } while (numAllocatedFiles > 0) FreeFile(allocatedFiles[0]); /* * Reset the tempfile name counter to 0; not really necessary, but * helps keep the names from growing unreasonably long. */ tempFileCounter = 0;}/* * Operations on BufFiles --- a very incomplete emulation of stdio * atop virtual Files. Currently, we only support the buffered-I/O * aspect of stdio: a read or write of the low-level File occurs only * when the buffer is filled or emptied. This is an even bigger win * for virtual Files than ordinary kernel files, since reducing the * frequency with which a virtual File is touched reduces "thrashing" * of opening/closing file descriptors. * * Note that BufFile structs are allocated with palloc(), and therefore * will go away automatically at transaction end. If the underlying * virtual File is made with OpenTemporaryFile, then all resources for * the file are certain to be cleaned up even if processing is aborted * by elog(ERROR). */struct BufFile{ File file; /* the underlying virtual File */ bool dirty; /* does buffer need to be written? */ int pos; /* next read/write position in buffer */ int nbytes; /* total # of valid bytes in buffer */ char buffer[BLCKSZ];};/* * Create a BufFile and attach it to an (already opened) virtual File. * * This is comparable to fdopen() in stdio. */BufFile *BufFileCreate(File file){ BufFile *bfile = (BufFile *) palloc(sizeof(BufFile)); bfile->file = file; bfile->dirty = false; bfile->pos = 0; bfile->nbytes = 0; return bfile;}/* * Close a BufFile * * Like fclose(), this also implicitly FileCloses the underlying File. */voidBufFileClose(BufFile *file){ /* flush any unwritten data */ BufFileFlush(file); /* close the underlying (with delete if it's a temp file) */ FileClose(file->file); /* release the buffer space */ pfree(file);}/* BufFileRead * * Like fread() except we assume 1-byte element size. */size_tBufFileRead(BufFile *file, void *ptr, size_t size){ size_t nread = 0; size_t nthistime; if (file->dirty) { elog(NOTICE, "BufFileRead: should have flushed after writing"); BufFileFlush(file); } while (size > 0) { if (file->pos >= file->nbytes) { /* Try to load more data into buffer */ file->pos = 0; file->nbytes = FileRead(file->file, file->buffer, sizeof(file->buffer)); if (file->nbytes < 0) file->nbytes = 0; if (file->nbytes <= 0) break; /* no more data available */ } nthistime = file->nbytes - file->pos; if (nthistime > size) nthistime = size; Assert(nthistime > 0); memcpy(ptr, file->buffer + file->pos, nthistime); file->pos += nthistime; ptr = (void *) ((char *) ptr + nthistime); size -= nthistime; nread += nthistime; } return nread;}/* BufFileWrite * * Like fwrite() except we assume 1-byte element size. */size_tBufFileWrite(BufFile *file, void *ptr, size_t size){ size_t nwritten = 0; size_t nthistime; while (size > 0) { if (file->pos >= BLCKSZ) { /* Buffer full, dump it out */ if (file->dirty) { if (FileWrite(file->file, file->buffer, file->nbytes) < 0) break; /* I/O error */ file->dirty = false; } file->pos = 0; file->nbytes = 0; } nthistime = BLCKSZ - file->pos; if (nthistime > size) nthistime = size; Assert(nthistime > 0); memcpy(file->buffer + file->pos, ptr, nthistime); file->dirty = true; file->pos += nthistime; if (file->nbytes < file->pos) file->nbytes = file->pos; ptr = (void *) ((char *) ptr + nthistime); size -= nthistime; nwritten += nthistime; } return nwritten;}/* BufFileFlush * * Like fflush() */static intBufFileFlush(BufFile *file){ if (file->dirty) { if (FileWrite(file->file, file->buffer, file->nbytes) < 0) return EOF; file->dirty = false; } return 0;}/* BufFileSeek * * Like fseek(), or really more like lseek() since the return value is * the new file offset (or -1 in case of error). */longBufFileSeek(BufFile *file, long offset, int whence){ if (BufFileFlush(file) < 0) return -1L; file->pos = 0; file->nbytes = 0; return FileSeek(file->file, offset, whence);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -