/* * DeleteAllExportedSnapshotFiles * Clean up any files that have been left behind by a crashed backend * that had exported snapshots before it died. * * This should be called during database startup or crash recovery. */ void DeleteAllExportedSnapshotFiles(void) { char buf[MAXPGPATH]; DIR *s_dir; struct dirent *s_de; if (!(s_dir = AllocateDir(SNAPSHOT_EXPORT_DIR))) { /* * We really should have that directory in a sane cluster setup. But * then again if we don't, it's not fatal enough to make it FATAL. * Since we're running in the postmaster, LOG is our best bet. */ elog(LOG, "could not open directory \"%s\": %m", SNAPSHOT_EXPORT_DIR); return; } while ((s_de = ReadDir(s_dir, SNAPSHOT_EXPORT_DIR)) != NULL) { if (strcmp(s_de->d_name, ".") == 0 || strcmp(s_de->d_name, "..") == 0) continue; snprintf(buf, MAXPGPATH, SNAPSHOT_EXPORT_DIR "/%s", s_de->d_name); /* Again, unlink failure is not worthy of FATAL */ if (unlink(buf)) elog(LOG, "could not unlink file \"%s\": %m", buf); } FreeDir(s_dir); }
/* * pg_start_backup: set up for taking an on-line backup dump * * Essentially what this does is to create a backup label file in $PGDATA, * where it will be archived as part of the backup dump. The label file * contains the user-supplied label string (typically this would be used * to tell where the backup dump will be stored) and the starting time and * starting WAL location for the dump. */ Datum pg_start_backup(PG_FUNCTION_ARGS) { text *backupid = PG_GETARG_TEXT_P(0); bool fast = PG_GETARG_BOOL(1); char *backupidstr; XLogRecPtr startpoint; DIR *dir; backupidstr = text_to_cstring(backupid); if (!superuser() && !has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to run a backup"))); /* Make sure we can open the directory with tablespaces in it */ dir = AllocateDir("pg_tblspc"); if (!dir) ereport(ERROR, (errmsg("could not open directory \"%s\": %m", "pg_tblspc"))); startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL, dir, NULL, NULL, false, true); FreeDir(dir); PG_RETURN_LSN(startpoint); }
/* Return physical size of directory contents, or 0 if dir doesn't exist */ static int64 db_dir_size(const char *path) { int64 dirsize = 0; struct dirent *direntry; DIR *dirdesc; char filename[MAXPGPATH]; dirdesc = AllocateDir(path); if (!dirdesc) return 0; while ((direntry = ReadDir(dirdesc, path)) != NULL) { struct stat fst; if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) continue; snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name); if (stat(filename, &fst) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", filename))); dirsize += fst.st_size; } FreeDir(dirdesc); return dirsize; }
/* * Scan the SimpleLRU directory and apply a callback to each file found in it. * * If the callback returns true, the scan is stopped. The last return value * from the callback is returned. * * Note that the ordering in which the directory is scanned is not guaranteed. * * Note that no locking is applied. */ bool SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data) { bool retval = false; DIR *cldir; struct dirent *clde; int segno; int segpage; cldir = AllocateDir(ctl->Dir); while ((clde = ReadDir(cldir, ctl->Dir)) != NULL) { if (strlen(clde->d_name) == 4 && strspn(clde->d_name, "0123456789ABCDEF") == 4) { segno = (int) strtol(clde->d_name, NULL, 16); segpage = segno * SLRU_PAGES_PER_SEGMENT; elog(DEBUG2, "SlruScanDirectory invoking callback on %s/%s", ctl->Dir, clde->d_name); retval = callback(ctl, clde->d_name, segpage, data); if (retval) break; } } FreeDir(cldir); return retval; }
Datum database_size(PG_FUNCTION_ARGS) { Name dbname = PG_GETARG_NAME(0); Oid dbid; char *dbpath; DIR *dirdesc; struct dirent *direntry; int64 totalsize; dbid = get_database_oid(NameStr(*dbname)); if (!OidIsValid(dbid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", NameStr(*dbname)))); dbpath = GetDatabasePath(dbid); dirdesc = AllocateDir(dbpath); if (!dirdesc) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", dbpath))); totalsize = 0; for (;;) { char *fullname; struct stat statbuf; errno = 0; direntry = readdir(dirdesc); if (!direntry) { if (errno) ereport(ERROR, (errcode_for_file_access(), errmsg("error reading directory: %m"))); else break; } fullname = psnprintf(strlen(dbpath) + 1 + strlen(direntry->d_name) + 1, "%s/%s", dbpath, direntry->d_name); if (stat(fullname, &statbuf) == -1) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat \"%s\": %m", fullname))); totalsize += statbuf.st_size; pfree(fullname); } FreeDir(dirdesc); PG_RETURN_INT64(totalsize); }
/* * List a directory (returns the filenames only) */ Datum pg_ls_dir(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; struct dirent *de; directory_fctx *fctx; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to get directory listings")))); if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); fctx = palloc(sizeof(directory_fctx)); fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0)); fctx->dirdesc = AllocateDir(fctx->location); if (!fctx->dirdesc) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", fctx->location))); funcctx->user_fctx = fctx; MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); fctx = (directory_fctx *) funcctx->user_fctx; while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL) { int len = strlen(de->d_name); text *result; if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; result = palloc(len + VARHDRSZ); SET_VARSIZE(result, len + VARHDRSZ); memcpy(VARDATA(result), de->d_name, len); SRF_RETURN_NEXT(funcctx, PointerGetDatum(result)); } FreeDir(fctx->dirdesc); SRF_RETURN_DONE(funcctx); }
/* * calculate total size of tablespace */ static int64 calculate_tablespace_size(Oid tblspcOid) { char tblspcPath[MAXPGPATH]; char pathname[MAXPGPATH]; int64 totalsize = 0; DIR *dirdesc; struct dirent *direntry; if (tblspcOid == DEFAULTTABLESPACE_OID) snprintf(tblspcPath, MAXPGPATH, "base"); else if (tblspcOid == GLOBALTABLESPACE_OID) snprintf(tblspcPath, MAXPGPATH, "global"); else snprintf(tblspcPath, MAXPGPATH, "pg_tblspc/%u", tblspcOid); dirdesc = AllocateDir(tblspcPath); if (!dirdesc) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open tablespace directory \"%s\": %m", tblspcPath))); while ((direntry = ReadDir(dirdesc, tblspcPath)) != NULL) { struct stat fst; CHECK_FOR_INTERRUPTS(); if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) continue; snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name); if (stat(pathname, &fst) < 0) { if (errno == ENOENT) continue; else ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", pathname))); } if (S_ISDIR(fst.st_mode)) totalsize += db_dir_size(pathname); totalsize += fst.st_size; } FreeDir(dirdesc); return totalsize; }
/* * CopyTaskFilesFromDirectory finds all files in the given directory, except for * those having an attempt suffix. The function then copies these files into the * database table identified by the given schema and table name. */ static void CopyTaskFilesFromDirectory(StringInfo schemaName, StringInfo relationName, StringInfo sourceDirectoryName) { const char *directoryName = sourceDirectoryName->data; struct dirent *directoryEntry = NULL; uint64 copiedRowTotal = 0; DIR *directory = AllocateDir(directoryName); if (directory == NULL) { ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", directoryName))); } directoryEntry = ReadDir(directory, directoryName); for (; directoryEntry != NULL; directoryEntry = ReadDir(directory, directoryName)) { const char *baseFilename = directoryEntry->d_name; const char *queryString = NULL; StringInfo fullFilename = NULL; RangeVar *relation = NULL; CopyStmt *copyStatement = NULL; uint64 copiedRowCount = 0; /* if system file or lingering task file, skip it */ if (strncmp(baseFilename, ".", MAXPGPATH) == 0 || strncmp(baseFilename, "..", MAXPGPATH) == 0 || strstr(baseFilename, ATTEMPT_FILE_SUFFIX) != NULL) { continue; } fullFilename = makeStringInfo(); appendStringInfo(fullFilename, "%s/%s", directoryName, baseFilename); /* build relation object and copy statement */ relation = makeRangeVar(schemaName->data, relationName->data, -1); copyStatement = CopyStatement(relation, fullFilename->data); if (BinaryWorkerCopyFormat) { DefElem *copyOption = makeDefElem("format", (Node *) makeString("binary")); copyStatement->options = list_make1(copyOption); } DoCopy(copyStatement, queryString, &copiedRowCount); copiedRowTotal += copiedRowCount; CommandCounterIncrement(); } ereport(DEBUG2, (errmsg("copied " UINT64_FORMAT " rows into table: \"%s.%s\"", copiedRowTotal, schemaName->data, relationName->data))); FreeDir(directory); }
/* * calculate size of database in all tablespaces */ static int64 calculate_database_size(Oid dbOid) { int64 totalsize; DIR *dirdesc; struct dirent *direntry; char dirpath[MAXPGPATH]; char pathname[MAXPGPATH + 12 + sizeof(TABLESPACE_VERSION_DIRECTORY)]; AclResult aclresult; /* * User must have connect privilege for target database * or be a member of pg_read_all_stats */ aclresult = pg_database_aclcheck(dbOid, GetUserId(), ACL_CONNECT); if (aclresult != ACLCHECK_OK && !is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_ALL_STATS)) { aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(dbOid)); } /* Shared storage in pg_global is not counted */ /* Include pg_default storage */ snprintf(pathname, sizeof(pathname), "base/%u", dbOid); totalsize = db_dir_size(pathname); /* Scan the non-default tablespaces */ snprintf(dirpath, MAXPGPATH, "pg_tblspc"); dirdesc = AllocateDir(dirpath); if (!dirdesc) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open tablespace directory \"%s\": %m", dirpath))); while ((direntry = ReadDir(dirdesc, dirpath)) != NULL) { CHECK_FOR_INTERRUPTS(); if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) continue; snprintf(pathname, sizeof(pathname), "pg_tblspc/%s/%s/%u", direntry->d_name, TABLESPACE_VERSION_DIRECTORY, dbOid); totalsize += db_dir_size(pathname); } FreeDir(dirdesc); return totalsize; }
/* * pgarch_readyXlog * * Return name of the oldest xlog file that has not yet been archived. * No notification is set that file archiving is now in progress, so * this would need to be extended if multiple concurrent archival * tasks were created. If a failure occurs, we will completely * re-copy the file at the next available opportunity. * * It is important that we return the oldest, so that we archive xlogs * in order that they were written, for two reasons: * 1) to maintain the sequential chain of xlogs required for recovery * 2) because the oldest ones will sooner become candidates for * recycling at time of checkpoint * * NOTE: the "oldest" comparison will presently consider all segments of * a timeline with a smaller ID to be older than all segments of a timeline * with a larger ID; the net result being that past timelines are given * higher priority for archiving. This seems okay, or at least not * obviously worth changing. */ static bool pgarch_readyXlog(char *xlog) { /* * open xlog status directory and read through list of xlogs that have the * .ready suffix, looking for earliest file. It is possible to optimise * this code, though only a single file is expected on the vast majority * of calls, so.... */ char XLogArchiveStatusDir[MAXPGPATH]; char newxlog[MAX_XFN_CHARS + 6 + 1]; DIR *rldir; struct dirent *rlde; bool found = false; snprintf(XLogArchiveStatusDir, MAXPGPATH, XLOGDIR "/archive_status"); rldir = AllocateDir(XLogArchiveStatusDir); if (rldir == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open archive status directory \"%s\": %m", XLogArchiveStatusDir))); while ((rlde = ReadDir(rldir, XLogArchiveStatusDir)) != NULL) { int basenamelen = (int) strlen(rlde->d_name) - 6; if (basenamelen >= MIN_XFN_CHARS && basenamelen <= MAX_XFN_CHARS && strspn(rlde->d_name, VALID_XFN_CHARS) >= basenamelen && strcmp(rlde->d_name + basenamelen, ".ready") == 0) { if (!found) { strcpy(newxlog, rlde->d_name); found = true; } else { if (strcmp(rlde->d_name, newxlog) < 0) strcpy(newxlog, rlde->d_name); } } } FreeDir(rldir); if (found) { /* truncate off the .ready */ newxlog[strlen(newxlog) - 6] = '\0'; strcpy(xlog, newxlog); } return found; }
/* * calculate size of database in all tablespaces */ static int64 calculate_database_size(Oid dbOid) { int64 totalsize; DIR *dirdesc; struct dirent *direntry; char dirpath[MAXPGPATH]; char pathname[MAXPGPATH]; AclResult aclresult; /* User must have connect privilege for target database */ aclresult = pg_database_aclcheck(dbOid, GetUserId(), ACL_CONNECT); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(dbOid)); /* Shared storage in pg_global is not counted */ /* Include pg_default storage */ snprintf(pathname, MAXPGPATH, "base/%u", dbOid); totalsize = db_dir_size(pathname); /* Scan the non-default tablespaces */ snprintf(dirpath, MAXPGPATH, "pg_tblspc"); dirdesc = AllocateDir(dirpath); if (!dirdesc) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open tablespace directory \"%s\": %m", dirpath))); while ((direntry = ReadDir(dirdesc, dirpath)) != NULL) { CHECK_FOR_INTERRUPTS(); if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) continue; snprintf(pathname, MAXPGPATH, "pg_tblspc/%s/%s/%u", direntry->d_name, TABLESPACE_VERSION_DIRECTORY, dbOid); totalsize += db_dir_size(pathname); } FreeDir(dirdesc); /* Complain if we found no trace of the DB at all */ if (!totalsize) ereport(ERROR, (ERRCODE_UNDEFINED_DATABASE, errmsg("database with OID %u does not exist", dbOid))); return totalsize; }
/* * Reset unlogged relations from before the last restart. * * If op includes UNLOGGED_RELATION_CLEANUP, we remove all forks of any * relation with an "init" fork, except for the "init" fork itself. * * If op includes UNLOGGED_RELATION_INIT, we copy the "init" fork to the main * fork. */ void ResetUnloggedRelations(int op) { char temp_path[MAXPGPATH]; DIR *spc_dir; struct dirent *spc_de; MemoryContext tmpctx, oldctx; /* Log it. */ elog(DEBUG1, "resetting unlogged relations: cleanup %d init %d", (op & UNLOGGED_RELATION_CLEANUP) != 0, (op & UNLOGGED_RELATION_INIT) != 0); /* * Just to be sure we don't leak any memory, let's create a temporary * memory context for this operation. */ tmpctx = AllocSetContextCreate(CurrentMemoryContext, "ResetUnloggedRelations", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldctx = MemoryContextSwitchTo(tmpctx); /* * First process unlogged files in pg_default ($PGDATA/base) */ ResetUnloggedRelationsInTablespaceDir("base", op); /* * Cycle through directories for all non-default tablespaces. */ spc_dir = AllocateDir("pg_tblspc"); while ((spc_de = ReadDir(spc_dir, "pg_tblspc")) != NULL) { if (strcmp(spc_de->d_name, ".") == 0 || strcmp(spc_de->d_name, "..") == 0) continue; snprintf(temp_path, sizeof(temp_path), "pg_tblspc/%s/%s", spc_de->d_name, TABLESPACE_VERSION_DIRECTORY); ResetUnloggedRelationsInTablespaceDir(temp_path, op); } FreeDir(spc_dir); /* * Restore memory context. */ MemoryContextSwitchTo(oldctx); MemoryContextDelete(tmpctx); }
/* * Load all replication slots from disk into memory at server startup. This * needs to be run before we start crash recovery. */ void StartupReplicationSlots(XLogRecPtr checkPointRedo) { DIR *replication_dir; struct dirent *replication_de; ereport(DEBUG1, (errmsg("starting up replication slots"))); /* restore all slots by iterating over all on-disk entries */ replication_dir = AllocateDir("pg_replslot"); while ((replication_de = ReadDir(replication_dir, "pg_replslot")) != NULL) { struct stat statbuf; char path[MAXPGPATH]; if (strcmp(replication_de->d_name, ".") == 0 || strcmp(replication_de->d_name, "..") == 0) continue; snprintf(path, MAXPGPATH, "pg_replslot/%s", replication_de->d_name); /* we're only creating directories here, skip if it's not our's */ if (lstat(path, &statbuf) == 0 && !S_ISDIR(statbuf.st_mode)) continue; /* we crashed while a slot was being setup or deleted, clean up */ if (string_endswith(replication_de->d_name, ".tmp")) { if (!rmtree(path, true)) { ereport(WARNING, (errcode_for_file_access(), errmsg("could not remove directory \"%s\"", path))); continue; } fsync_fname("pg_replslot", true); continue; } /* looks like a slot in a normal state, restore */ RestoreSlotFromDisk(replication_de->d_name); } FreeDir(replication_dir); /* currently no slots exist, we're done. */ if (max_replication_slots <= 0) return; /* Now that we have recovered all the data, compute replication xmin */ ReplicationSlotsComputeRequiredXmin(); ReplicationSlotsComputeRequiredLSN(); }
/* * pg_start_backup: set up for taking an on-line backup dump * * Essentially what this does is to create a backup label file in $PGDATA, * where it will be archived as part of the backup dump. The label file * contains the user-supplied label string (typically this would be used * to tell where the backup dump will be stored) and the starting time and * starting WAL location for the dump. * * Permission checking for this function is managed through the normal * GRANT system. */ Datum pg_start_backup(PG_FUNCTION_ARGS) { text *backupid = PG_GETARG_TEXT_P(0); bool fast = PG_GETARG_BOOL(1); bool exclusive = PG_GETARG_BOOL(2); char *backupidstr; XLogRecPtr startpoint; DIR *dir; backupidstr = text_to_cstring(backupid); if (exclusive_backup_running || nonexclusive_backup_running) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("a backup is already in progress in this session"))); /* Make sure we can open the directory with tablespaces in it */ dir = AllocateDir("pg_tblspc"); if (!dir) ereport(ERROR, (errmsg("could not open directory \"%s\": %m", "pg_tblspc"))); if (exclusive) { startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL, dir, NULL, NULL, false, true); exclusive_backup_running = true; } else { MemoryContext oldcontext; /* * Label file and tablespace map file need to be long-lived, since they * are read in pg_stop_backup. */ oldcontext = MemoryContextSwitchTo(TopMemoryContext); label_file = makeStringInfo(); tblspc_map_file = makeStringInfo(); MemoryContextSwitchTo(oldcontext); startpoint = do_pg_start_backup(backupidstr, fast, NULL, label_file, dir, NULL, tblspc_map_file, false, true); nonexclusive_backup_running = true; before_shmem_exit(nonexclusive_base_backup_cleanup, (Datum) 0); } FreeDir(dir); PG_RETURN_LSN(startpoint); }
/* * copydir: copy a directory * * If recurse is false, subdirectories are ignored. Anything that's not * a directory or a regular file is ignored. */ void copydir(char *fromdir, char *todir, bool recurse) { DIR *xldir; struct dirent *xlde; char fromfile[MAXPGPATH]; char tofile[MAXPGPATH]; if (mkdir(todir, S_IRUSR | S_IWUSR | S_IXUSR) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", todir))); xldir = AllocateDir(fromdir); if (xldir == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", fromdir))); while ((xlde = ReadDir(xldir, fromdir)) != NULL) { struct stat fst; /* If we got a cancel signal during the copy of the directory, quit */ CHECK_FOR_INTERRUPTS(); if (strcmp(xlde->d_name, ".") == 0 || strcmp(xlde->d_name, "..") == 0) continue; snprintf(fromfile, MAXPGPATH, "%s/%s", fromdir, xlde->d_name); snprintf(tofile, MAXPGPATH, "%s/%s", todir, xlde->d_name); if (lstat(fromfile, &fst) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", fromfile))); if (S_ISDIR(fst.st_mode)) { /* recurse to handle subdirectories */ if (recurse) copydir(fromfile, tofile, true); } else if (S_ISREG(fst.st_mode)) copy_file(fromfile, tofile); } FreeDir(xldir); }
static int SlruRecoverMirrorDir(char *slru_dir) { DIR *cldir; struct dirent *clde; int retval = 0; char *dir = NULL; char *mirrorDir = NULL; int counter = 0; if (isTxnDir(slru_dir)) { dir = makeRelativeToTxnFilespace(slru_dir); mirrorDir = makeRelativeToPeerTxnFilespace(slru_dir); } else { dir = (char*)palloc(MAXPGPATH); strncpy(dir, slru_dir, MAXPGPATH); mirrorDir = (char*)palloc(MAXPGPATH); strncpy(mirrorDir, slru_dir, MAXPGPATH); } cldir = AllocateDir(dir); while ((clde = ReadDir(cldir, dir)) != NULL) { if (strlen(clde->d_name) == 4 && strspn(clde->d_name, "0123456789ABCDEF") == 4) { retval = MirrorFlatFile(slru_dir, clde->d_name); if (retval != 0) break; } counter++; if (counter % log_count_recovered_files_batch == 0) { elog(LOG, "completed recovering %d files", counter); } } elog(LOG, "completed recovering %d files", counter); FreeDir(cldir); pfree(dir); pfree(mirrorDir); return retval; }
/* * calculate total size of tablespace */ Datum pg_tablespace_size(PG_FUNCTION_ARGS) { Oid tblspcOid = PG_GETARG_OID(0); char tblspcPath[MAXPGPATH]; char pathname[MAXPGPATH]; int64 totalsize=0; DIR *dirdesc; struct dirent *direntry; if (tblspcOid == DEFAULTTABLESPACE_OID) snprintf(tblspcPath, MAXPGPATH, "%s/base", DataDir); else if (tblspcOid == GLOBALTABLESPACE_OID) snprintf(tblspcPath, MAXPGPATH, "%s/global", DataDir); else snprintf(tblspcPath, MAXPGPATH, "%s/pg_tblspc/%u", DataDir, tblspcOid); dirdesc = AllocateDir(tblspcPath); if (!dirdesc) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open tablespace directory \"%s\": %m", tblspcPath))); while ((direntry = readdir(dirdesc)) != NULL) { struct stat fst; if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) continue; snprintf(pathname, MAXPGPATH, "%s/%s", tblspcPath, direntry->d_name); if (stat(pathname, &fst) < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat \"%s\": %m", pathname))); totalsize += fst.st_size; if (fst.st_mode & S_IFDIR) totalsize += db_dir_size(pathname); } FreeDir(dirdesc); PG_RETURN_INT64(totalsize); }
pg_tzenum * pg_tzenumerate_start(void) { pg_tzenum *ret = (pg_tzenum *) palloc0(sizeof(pg_tzenum)); char *startdir = pstrdup(pg_TZDIR()); ret->baselen = strlen(startdir) + 1; ret->depth = 0; ret->dirname[0] = startdir; ret->dirdesc[0] = AllocateDir(startdir); if (!ret->dirdesc[0]) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", startdir))); return ret; }
/* * calculate size of database in all tablespaces */ static int64 calculate_database_size(Oid dbOid) { int64 totalsize; DIR *dirdesc; struct dirent *direntry; char dirpath[MAXPGPATH]; char pathname[MAXPGPATH]; /* Shared storage in pg_global is not counted */ /* Include pg_default storage */ snprintf(pathname, MAXPGPATH, "base/%u", dbOid); totalsize = db_dir_size(pathname); /* Scan the non-default tablespaces */ snprintf(dirpath, MAXPGPATH, "pg_tblspc"); dirdesc = AllocateDir(dirpath); if (!dirdesc) ereport(ERROR, (errcode_for_file_access(), errmsg("could not open tablespace directory \"%s\": %m", dirpath))); while ((direntry = ReadDir(dirdesc, dirpath)) != NULL) { CHECK_FOR_INTERRUPTS(); if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) continue; snprintf(pathname, MAXPGPATH, "pg_tblspc/%s/%u", direntry->d_name, dbOid); totalsize += db_dir_size(pathname); } FreeDir(dirdesc); /* Complain if we found no trace of the DB at all */ if (!totalsize) ereport(ERROR, (ERRCODE_UNDEFINED_DATABASE, errmsg("database with OID %u does not exist", dbOid))); return totalsize; }
/* Process one pgsql_tmp directory for RemovePgTempFiles */ static void RemovePgTempFilesInDir(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 (HasTempFilePrefix(temp_de->d_name)) { /* * It can be a file or a directory, so try to delete both ways * We ignore errors. */ unlink(rm_path); rmtree(rm_path,true); } else { elog(LOG, "unexpected file found in temporary-files directory: \"%s\"", rm_path); } } FreeDir(temp_dir); }
/* Return physical size of directory contents, or 0 if dir doesn't exist */ int64 db_dir_size(const char *path) { int64 dirsize = 0; struct dirent *direntry; char filename[MAXPGPATH]; /* Deal with remote shared storage */ if (!IsLocalPath(path)) { dirsize += HdfsPathSize(path); } /* Local storage */ else { DIR *dirdesc = AllocateDir(path); if (!dirdesc) return 0; while ((direntry = ReadDir(dirdesc, path)) != NULL) { struct stat fst; if (strcmp(direntry->d_name, ".") == 0 || strcmp(direntry->d_name, "..") == 0) continue; snprintf(filename, MAXPGPATH, "%s/%s", path, direntry->d_name); if (stat(filename, &fst) < 0) { if (errno == ENOENT) continue; else ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", filename))); } dirsize += fst.st_size; } FreeDir(dirdesc); } return dirsize; }
/* * Process one tablespace directory for ResetUnloggedRelations */ static void ResetUnloggedRelationsInTablespaceDir(const char *tsdirname, int op) { DIR *ts_dir; struct dirent *de; char dbspace_path[MAXPGPATH * 2]; ts_dir = AllocateDir(tsdirname); /* * If we get ENOENT on a tablespace directory, log it and return. This * can happen if a previous DROP TABLESPACE crashed between removing the * tablespace directory and removing the symlink in pg_tblspc. We don't * really want to prevent database startup in that scenario, so let it * pass instead. Any other type of error will be reported by ReadDir * (causing a startup failure). */ if (ts_dir == NULL && errno == ENOENT) { ereport(LOG, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", tsdirname))); return; } while ((de = ReadDir(ts_dir, tsdirname)) != NULL) { /* * We're only interested in the per-database directories, which have * numeric names. Note that this code will also (properly) ignore "." * and "..". */ if (strspn(de->d_name, "0123456789") != strlen(de->d_name)) continue; snprintf(dbspace_path, sizeof(dbspace_path), "%s/%s", tsdirname, de->d_name); ResetUnloggedRelationsInDbspaceDir(dbspace_path, op); } FreeDir(ts_dir); }
/* * SimpleLruTruncate subroutine: scan directory for removable segments. * Actually remove them iff doDeletions is true. Return TRUE iff any * removable segments were found. Note: no locking is needed. * * This can be called directly from clog.c, for reasons explained there. */ bool SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions) { bool found = false; DIR *cldir; struct dirent *clde; int segno; int segpage; char path[MAXPGPATH]; /* * The cutoff point is the start of the segment containing cutoffPage. * (This is redundant when called from SimpleLruTruncate, but not when * called directly from clog.c.) */ cutoffPage -= cutoffPage % SLRU_PAGES_PER_SEGMENT; cldir = AllocateDir(ctl->Dir); while ((clde = ReadDir(cldir, ctl->Dir)) != NULL) { if (strlen(clde->d_name) == 4 && strspn(clde->d_name, "0123456789ABCDEF") == 4) { segno = (int) strtol(clde->d_name, NULL, 16); segpage = segno * SLRU_PAGES_PER_SEGMENT; if (ctl->PagePrecedes(segpage, cutoffPage)) { found = true; if (doDeletions) { snprintf(path, MAXPGPATH, "%s/%s", ctl->Dir, clde->d_name); ereport(DEBUG2, (errmsg("removing file \"%s\"", path))); unlink(path); } } } } FreeDir(cldir); return found; }
/* * Check if a directory is empty. * * This probably belongs somewhere else, but not sure where... */ bool directory_is_empty(const char *path) { DIR *dirdesc; struct dirent *de; dirdesc = AllocateDir(path); while ((de = ReadDir(dirdesc, path)) != NULL) { if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; FreeDir(dirdesc); return false; } FreeDir(dirdesc); return true; }
/* * copydir: copy a directory (we only need to go one level deep) * * Return 0 on success, nonzero on failure. * * NB: do not elog(ERROR) on failure. Return to caller so it can try to * clean up. */ int copydir(char *fromdir, char *todir) { DIR *xldir; struct dirent *xlde; char fromfl[MAXPGPATH]; char tofl[MAXPGPATH]; if (mkdir(todir) != 0) { ereport(WARNING, (errcode_for_file_access(), errmsg("could not create directory \"%s\": %m", todir))); return -1; } xldir = AllocateDir(fromdir); if (xldir == NULL) { ereport(WARNING, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", fromdir))); return -1; } while ((xlde = readdir(xldir)) != NULL) { snprintf(fromfl, MAXPGPATH, "%s/%s", fromdir, xlde->d_name); snprintf(tofl, MAXPGPATH, "%s/%s", todir, xlde->d_name); if (CopyFile(fromfl, tofl, TRUE) < 0) { ereport(WARNING, (errcode_for_file_access(), errmsg("could not copy file \"%s\": %m", fromfl))); FreeDir(xldir); return -1; } } FreeDir(xldir); return 0; }
/* * Scan specified directory for a case-insensitive match to fname * (of length fnamelen --- fname may not be null terminated!). If found, * copy the actual filename into canonname and return true. */ static bool scan_directory_ci(const char *dirname, const char *fname, int fnamelen, char *canonname, int canonnamelen) { bool found = false; DIR *dirdesc; struct dirent *direntry; dirdesc = AllocateDir(dirname); if (!dirdesc) { ereport(LOG, (errcode_for_file_access(), errmsg("could not open directory \"%s\": %m", dirname))); return false; } while ((direntry = ReadDir(dirdesc, dirname)) != NULL) { /* * Ignore . and .., plus any other "hidden" files. This is a security * measure to prevent access to files outside the timezone directory. */ if (direntry->d_name[0] == '.') continue; if (strlen(direntry->d_name) == fnamelen && pg_strncasecmp(direntry->d_name, fname, fnamelen) == 0) { /* Found our match */ strlcpy(canonname, direntry->d_name, canonnamelen); found = true; break; } } FreeDir(dirdesc); return found; }
/* * 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. */ void RemovePgTempFiles(void) { char temp_path[MAXPGPATH]; DIR *spc_dir; struct dirent *spc_de; /* * First process temp files in pg_default ($PGDATA/base) */ snprintf(temp_path, sizeof(temp_path), "base/%s", PG_TEMP_FILES_DIR); RemovePgTempFilesInDir(temp_path); /* * Cycle through temp directories for all non-default tablespaces. */ spc_dir = AllocateDir("pg_tblspc"); while ((spc_de = ReadDir(spc_dir, "pg_tblspc")) != NULL) { if (strcmp(spc_de->d_name, ".") == 0 || strcmp(spc_de->d_name, "..") == 0) continue; snprintf(temp_path, sizeof(temp_path), "pg_tblspc/%s/%s", spc_de->d_name, PG_TEMP_FILES_DIR); RemovePgTempFilesInDir(temp_path); } FreeDir(spc_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 void RemovePgTempFilesInDir(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); }
/* Process one tablespace directory for ResetUnloggedRelations */ static void ResetUnloggedRelationsInTablespaceDir(const char *tsdirname, int op) { DIR *ts_dir; struct dirent *de; char dbspace_path[MAXPGPATH]; ts_dir = AllocateDir(tsdirname); if (ts_dir == NULL) { /* anything except ENOENT is fishy */ if (errno != ENOENT) elog(LOG, "could not open tablespace directory \"%s\": %m", tsdirname); return; } while ((de = ReadDir(ts_dir, tsdirname)) != NULL) { int i = 0; /* * We're only interested in the per-database directories, which have * numeric names. Note that this code will also (properly) ignore "." * and "..". */ while (isdigit((unsigned char) de->d_name[i])) ++i; if (de->d_name[i] != '\0' || i == 0) continue; snprintf(dbspace_path, sizeof(dbspace_path), "%s/%s", tsdirname, de->d_name); ResetUnloggedRelationsInDbspaceDir(dbspace_path, op); } FreeDir(ts_dir); }
static bool cfs_gc_directory(int worker_id, char const* path) { DIR* dir = AllocateDir(path); bool success = true; if (dir != NULL) { struct dirent* entry; char file_path[MAXPGPATH]; int len; while ((entry = ReadDir(dir, path)) != NULL && !cfs_stop) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } len = snprintf(file_path, sizeof(file_path), "%s/%s", path, entry->d_name); if (len > 4 && strcmp(file_path + len - 4, ".map") == 0) { if (entry->d_ino % cfs_state->n_workers == worker_id && !cfs_gc_file(file_path)) { success = false; break; } } else { if (!cfs_gc_directory(worker_id, file_path)) { success = false; break; } } } FreeDir(dir); } return success; }