int64 FileSeek(File file, int64 offset, int whence) { int returnCode; Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileSeek: %d (%s) " INT64_FORMAT " " INT64_FORMAT " %d", file, VfdCache[file].fileName, VfdCache[file].seekPos, offset, whence)); if (FileIsNotOpen(file)) { switch (whence) { case SEEK_SET: Assert(offset >= INT64CONST(0)); 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 = pg_lseek64(VfdCache[file].fd, offset, whence); break; default: Assert(!"invalid whence"); break; } } else { switch (whence) { case SEEK_SET: Assert(offset >= INT64CONST(0)); if (VfdCache[file].seekPos != offset) VfdCache[file].seekPos = pg_lseek64(VfdCache[file].fd, offset, whence); break; case SEEK_CUR: if (offset != 0 || VfdCache[file].seekPos == FileUnknownPos) VfdCache[file].seekPos = pg_lseek64(VfdCache[file].fd, offset, whence); break; case SEEK_END: VfdCache[file].seekPos = pg_lseek64(VfdCache[file].fd, offset, whence); break; default: Assert(!"invalid whence"); break; } } return VfdCache[file].seekPos; }
/* * XXX not actually used but here for completeness */ long FileTell(File file) { DO_DB(printf("DEBUG: FileTell %d (%s)\n", file, VfdCache[file].fileName)); return VfdCache[file].seekPos; }
/* returns 0 on success, -1 on re-open failure (with errno set) */ static int FileAccess(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; }
long FileSeek(File file, long offset, int whence) { int returnCode; DO_DB(printf("DEBUG: FileSeek: %d (%s) %d %d\n", file, VfdCache[file].fileName, offset, whence)); if (FileIsNotOpen(file)) { switch(whence) { case SEEK_SET: VfdCache[file].seekPos = offset; return offset; case SEEK_CUR: VfdCache[file].seekPos = VfdCache[file].seekPos +offset; return VfdCache[file].seekPos; case SEEK_END: FileAccess(file); returnCode = VfdCache[file].seekPos = lseek(VfdCache[file].fd, offset, whence); return returnCode; default: elog(WARN, "FileSeek: invalid whence: %d", whence); break; } } else { returnCode = VfdCache[file].seekPos = lseek(VfdCache[file].fd, offset, whence); return returnCode; } /*NOTREACHED*/ return(-1L); }
static void LruDelete(File file) { Vfd *vfdP; Assert(file != 0); DO_DB(elog(LOG, "LruDelete %d (%s)", file, VfdCache[file].fileName)); vfdP = &VfdCache[file]; /* delete the vfd record from the LRU ring */ Delete(file); /* save the seek position */ vfdP->seekPos = (long) lseek(vfdP->fd, 0L, SEEK_CUR); Assert(vfdP->seekPos != -1L); /* close the file */ if (close(vfdP->fd)) elog(ERROR, "could not close file \"%s\": %m", vfdP->fileName); --nfile; vfdP->fd = VFD_CLOSED; }
void AllocateFile() { int fd; int fdleft; while ((fd = open(Nulldev,O_WRONLY,0)) < 0) { if (errno == EMFILE) { errno = 0; FreeFd = 0; AssertLruRoom(); } else { elog(WARN,"Open: %s in %s line %d\n", Nulldev, __FILE__, __LINE__); } } close(fd); ++allocatedFiles; fdleft = MAXFILES - allocatedFiles; if (fdleft < 6) { elog(DEBUG,"warning: few usable file descriptors left (%d)", fdleft); } DO_DB(printf("DEBUG: AllocatedFile. FreeFd = %d\n", FreeFd)); }
/* * close a file when done with it */ void FileClose(File file) { Vfd *vfdP; struct stat filestats; 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, "could not close file \"%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 (log_temp_files >= 0) { if (stat(vfdP->fileName, &filestats) == 0) { if (filestats.st_size >= log_temp_files) ereport(LOG, (errmsg("temporary file: path \"%s\", size %lu", vfdP->fileName, (unsigned long) filestats.st_size))); } else elog(LOG, "could not stat file \"%s\": %m", vfdP->fileName); } if (unlink(vfdP->fileName)) elog(LOG, "could not unlink file \"%s\": %m", vfdP->fileName); } /* Unregister it from the resource owner */ if (vfdP->resowner) ResourceOwnerForgetFile(vfdP->resowner, file); /* * Return the Vfd slot to the free list */ FreeVfd(file); }
int64 FileTell(File file) { Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileTell %d (%s)", file, VfdCache[file].fileName)); return VfdCache[file].seekPos; }
int FileRead(File file, char *buffer, int amount) { int returnCode; Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileRead: %d (%s) " INT64_FORMAT " %d %p", file, VfdCache[file].fileName, VfdCache[file].seekPos, amount, buffer)); if (Debug_filerep_print) (elog(LOG, "FileRead: %d (%s) " INT64_FORMAT " %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; }
static void FreeVfd(File file) { DO_DB(printf("DB: FreeVfd: %d (%s)\n", file, VfdCache[file].fileName)); VfdCache[file].nextFree = VfdCache[0].nextFree; VfdCache[0].nextFree = file; }
static void Insert(File file) { Vfd *vfdP; DO_DB(printf("DEBUG: Insert %d (%s)\n", file, VfdCache[file].fileName)); DO_DB(_dump_lru()); vfdP = &VfdCache[file]; vfdP->lruMoreRecently = 0; vfdP->lruLessRecently = VfdCache[0].lruLessRecently; VfdCache[0].lruLessRecently = file; VfdCache[vfdP->lruLessRecently].lruMoreRecently = file; DO_DB(_dump_lru()); }
static void Delete(File file) { Vfd *vfdP; Assert(file != 0); DO_DB(elog(LOG, "Delete %d (%s)", file, VfdCache[file].fileName)); DO_DB(_dump_lru()); vfdP = &VfdCache[file]; VfdCache[vfdP->lruLessRecently].lruMoreRecently = vfdP->lruMoreRecently; VfdCache[vfdP->lruMoreRecently].lruLessRecently = vfdP->lruLessRecently; DO_DB(_dump_lru()); }
static File AllocateVfd(void) { Index i; File file; DO_DB(elog(LOG, "AllocateVfd. Size %lu", 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 void AssertLruRoom() { DO_DB(printf("DEBUG: AssertLruRoom (FreeFd = %d)\n", FreeFd)); if (FreeFd <= 0 || nfile >= MAXFILES) { LruDelete(VfdCache[0].lruMoreRecently); } }
/* * XXX What happens if FreeFile() is called without a previous * AllocateFile()? */ void FreeFile() { DO_DB(printf("DEBUG: FreeFile. FreeFd now %d\n", FreeFd)); FreeFd++; nfile++; /* dangerous */ Assert(allocatedFiles > 0); --allocatedFiles; }
static void Delete(File file) { Vfd *fileP; DO_DB(printf("DEBUG: Delete %d (%s)\n", file, VfdCache[file].fileName)); DO_DB(_dump_lru()); Assert(file != 0); fileP = &VfdCache[file]; VfdCache[fileP->lruLessRecently].lruMoreRecently = VfdCache[file].lruMoreRecently; VfdCache[fileP->lruMoreRecently].lruLessRecently = VfdCache[file].lruLessRecently; DO_DB(_dump_lru()); }
int FileWrite(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; }
static void Insert(File file) { Vfd *vfdP; Assert(file != 0); DO_DB(elog(LOG, "Insert %d (%s)", file, VfdCache[file].fileName)); DO_DB(_dump_lru()); vfdP = &VfdCache[file]; vfdP->lruMoreRecently = 0; vfdP->lruLessRecently = VfdCache[0].lruLessRecently; VfdCache[0].lruLessRecently = file; VfdCache[vfdP->lruLessRecently].lruMoreRecently = file; DO_DB(_dump_lru()); }
int FileTruncate(File file, int offset) { int returnCode; DO_DB(printf("DEBUG: FileTruncate %d (%s)\n", file, VfdCache[file].fileName)); (void) FileSync(file); (void) FileAccess(file); returnCode = ftruncate(VfdCache[file].fd, offset); return(returnCode); }
/* * close a file and forcibly delete the underlying Unix file */ void FileUnlink(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); }
int FileRead(File file, char *buffer, int amount) { int returnCode; DO_DB(printf("DEBUG: FileRead: %d (%s) %d 0x%x\n", file, VfdCache[file].fileName, amount, buffer)); FileAccess(file); returnCode = read(VfdCache[file].fd, buffer, amount); if (returnCode > 0) { VfdCache[file].seekPos += returnCode; } return returnCode; }
int FileSync(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); }
int FileTruncate(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; }
static bool ReleaseLruFile(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 */ }
int64 FileNonVirtualCurSeek(File file) { int returnCode; Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileNonVirtualCurSeek: %d (%s) virtual position" INT64_FORMAT, file, VfdCache[file].fileName, VfdCache[file].seekPos)); returnCode = FileAccess(file); if (returnCode < 0) return returnCode; return pg_lseek64(VfdCache[file].fd, 0, SEEK_CUR); }
int FileSync(File file) { int returnCode; FileRepGpmonRecord_s gpmonRecord; FileRepGpmonStatType_e whichStat; if (fileRepRole == FileRepPrimaryRole) { whichStat = FileRepGpmonStatType_PrimaryFsyncSyscall; FileRepGpmonStat_OpenRecord(whichStat, &gpmonRecord); } else { whichStat = FileRepGpmonStatType_MirrorFsyncSyscall; FileRepGpmonStat_OpenRecord(whichStat, &gpmonRecord); } Assert(FileIsValid(file)); DO_DB(elog(LOG, "FileSync: %d (%s)", file, VfdCache[file].fileName)); returnCode = FileAccess(file); if (returnCode < 0) return returnCode; #ifdef FAULT_INJECTOR FaultInjector_InjectFaultIfSet( FileRepFlush, DDLNotSpecified, "", //databaseName ""); // tableName #endif returnCode = pg_fsync(VfdCache[file].fd); if (returnCode >= 0) { //only include stats if successful if ((fileRepRole == FileRepPrimaryRole) || (fileRepRole == FileRepMirrorRole)) { FileRepGpmonStat_CloseRecord(whichStat, &gpmonRecord); } } 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(const char *name, const 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; }
/* * 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, "could not allocate directory: out of file handles"); 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 handles: %m; release and retry"))); errno = 0; if (ReleaseLruFile()) goto TryAgain; errno = save_errno; } return NULL; }
static void FreeVfd(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; }
/* * close a file when done with it */ void FileClose(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 (gp_retry_close(vfdP->fd)) elog(ERROR, "could not close file \"%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(DEBUG1, "failed to unlink \"%s\": %m", vfdP->fileName); } /* * Return the Vfd slot to the free list */ FreeVfd(file); }