mirror of
https://github.com/postgres/postgres.git
synced 2025-05-25 00:04:05 -04:00
Skip unnecessary stat() calls in walkdir().
Some kernels can tell us the type of a "dirent", so we can avoid a call to stat() or lstat() in many cases. Define a new function get_dirent_type() to contain that logic, for use by the backend and frontend versions of walkdir(), and perhaps other callers in future. Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Juan José Santamaría Flecha <juanjo.santamaria@gmail.com> Discussion: https://postgr.es/m/CA%2BhUKG%2BFzxupGGN4GpUdbzZN%2Btn6FQPHo8w0Q%2BAPH5Wz8RG%2Bww%40mail.gmail.com
This commit is contained in:
parent
f0942b1327
commit
861c6e7c8e
@ -89,6 +89,7 @@
|
|||||||
#include "access/xlog.h"
|
#include "access/xlog.h"
|
||||||
#include "catalog/pg_tablespace.h"
|
#include "catalog/pg_tablespace.h"
|
||||||
#include "common/file_perm.h"
|
#include "common/file_perm.h"
|
||||||
|
#include "common/file_utils.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "pgstat.h"
|
#include "pgstat.h"
|
||||||
#include "portability/mem.h"
|
#include "portability/mem.h"
|
||||||
@ -3340,8 +3341,6 @@ walkdir(const char *path,
|
|||||||
while ((de = ReadDirExtended(dir, path, elevel)) != NULL)
|
while ((de = ReadDirExtended(dir, path, elevel)) != NULL)
|
||||||
{
|
{
|
||||||
char subpath[MAXPGPATH * 2];
|
char subpath[MAXPGPATH * 2];
|
||||||
struct stat fst;
|
|
||||||
int sret;
|
|
||||||
|
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
@ -3351,23 +3350,23 @@ walkdir(const char *path,
|
|||||||
|
|
||||||
snprintf(subpath, sizeof(subpath), "%s/%s", path, de->d_name);
|
snprintf(subpath, sizeof(subpath), "%s/%s", path, de->d_name);
|
||||||
|
|
||||||
if (process_symlinks)
|
switch (get_dirent_type(subpath, de, process_symlinks, elevel))
|
||||||
sret = stat(subpath, &fst);
|
|
||||||
else
|
|
||||||
sret = lstat(subpath, &fst);
|
|
||||||
|
|
||||||
if (sret < 0)
|
|
||||||
{
|
{
|
||||||
ereport(elevel,
|
case PGFILETYPE_REG:
|
||||||
(errcode_for_file_access(),
|
(*action) (subpath, false, elevel);
|
||||||
errmsg("could not stat file \"%s\": %m", subpath)));
|
break;
|
||||||
continue;
|
case PGFILETYPE_DIR:
|
||||||
}
|
walkdir(subpath, action, false, elevel);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
if (S_ISREG(fst.st_mode))
|
/*
|
||||||
(*action) (subpath, false, elevel);
|
* Errors are already reported directly by get_dirent_type(),
|
||||||
else if (S_ISDIR(fst.st_mode))
|
* and any remaining symlinks and unknown file types are
|
||||||
walkdir(subpath, action, false, elevel);
|
* ignored.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeDir(dir); /* we ignore any error here */
|
FreeDir(dir); /* we ignore any error here */
|
||||||
|
@ -56,6 +56,7 @@ OBJS_COMMON = \
|
|||||||
exec.o \
|
exec.o \
|
||||||
f2s.o \
|
f2s.o \
|
||||||
file_perm.o \
|
file_perm.o \
|
||||||
|
file_utils.o \
|
||||||
hashfn.o \
|
hashfn.o \
|
||||||
ip.o \
|
ip.o \
|
||||||
jsonapi.o \
|
jsonapi.o \
|
||||||
@ -91,7 +92,6 @@ endif
|
|||||||
OBJS_FRONTEND = \
|
OBJS_FRONTEND = \
|
||||||
$(OBJS_COMMON) \
|
$(OBJS_COMMON) \
|
||||||
fe_memutils.o \
|
fe_memutils.o \
|
||||||
file_utils.o \
|
|
||||||
logging.o \
|
logging.o \
|
||||||
restricted_token.o \
|
restricted_token.o \
|
||||||
sprompt.o
|
sprompt.o
|
||||||
|
@ -14,10 +14,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FRONTEND
|
#ifndef FRONTEND
|
||||||
#error "This file is not expected to be compiled for backend code"
|
#include "postgres.h"
|
||||||
#endif
|
#else
|
||||||
|
|
||||||
#include "postgres_fe.h"
|
#include "postgres_fe.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@ -25,8 +25,11 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "common/file_utils.h"
|
#include "common/file_utils.h"
|
||||||
|
#ifdef FRONTEND
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FRONTEND
|
||||||
|
|
||||||
/* Define PG_FLUSH_DATA_WORKS if we have an implementation for pg_flush_data */
|
/* Define PG_FLUSH_DATA_WORKS if we have an implementation for pg_flush_data */
|
||||||
#if defined(HAVE_SYNC_FILE_RANGE)
|
#if defined(HAVE_SYNC_FILE_RANGE)
|
||||||
@ -167,8 +170,6 @@ walkdir(const char *path,
|
|||||||
while (errno = 0, (de = readdir(dir)) != NULL)
|
while (errno = 0, (de = readdir(dir)) != NULL)
|
||||||
{
|
{
|
||||||
char subpath[MAXPGPATH * 2];
|
char subpath[MAXPGPATH * 2];
|
||||||
struct stat fst;
|
|
||||||
int sret;
|
|
||||||
|
|
||||||
if (strcmp(de->d_name, ".") == 0 ||
|
if (strcmp(de->d_name, ".") == 0 ||
|
||||||
strcmp(de->d_name, "..") == 0)
|
strcmp(de->d_name, "..") == 0)
|
||||||
@ -176,21 +177,23 @@ walkdir(const char *path,
|
|||||||
|
|
||||||
snprintf(subpath, sizeof(subpath), "%s/%s", path, de->d_name);
|
snprintf(subpath, sizeof(subpath), "%s/%s", path, de->d_name);
|
||||||
|
|
||||||
if (process_symlinks)
|
switch (get_dirent_type(subpath, de, process_symlinks, PG_LOG_ERROR))
|
||||||
sret = stat(subpath, &fst);
|
|
||||||
else
|
|
||||||
sret = lstat(subpath, &fst);
|
|
||||||
|
|
||||||
if (sret < 0)
|
|
||||||
{
|
{
|
||||||
pg_log_error("could not stat file \"%s\": %m", subpath);
|
case PGFILETYPE_REG:
|
||||||
continue;
|
(*action) (subpath, false);
|
||||||
}
|
break;
|
||||||
|
case PGFILETYPE_DIR:
|
||||||
|
walkdir(subpath, action, false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
|
||||||
if (S_ISREG(fst.st_mode))
|
/*
|
||||||
(*action) (subpath, false);
|
* Errors are already reported directly by get_dirent_type(),
|
||||||
else if (S_ISDIR(fst.st_mode))
|
* and any remaining symlinks and unknown file types are
|
||||||
walkdir(subpath, action, false);
|
* ignored.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errno)
|
if (errno)
|
||||||
@ -394,3 +397,73 @@ durable_rename(const char *oldfile, const char *newfile)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* FRONTEND */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the type of a directory entry.
|
||||||
|
*
|
||||||
|
* In frontend code, elevel should be a level from logging.h; in backend code
|
||||||
|
* it should be a level from elog.h.
|
||||||
|
*/
|
||||||
|
PGFileType
|
||||||
|
get_dirent_type(const char *path,
|
||||||
|
const struct dirent *de,
|
||||||
|
bool look_through_symlinks,
|
||||||
|
int elevel)
|
||||||
|
{
|
||||||
|
PGFileType result;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some systems tell us the type directly in the dirent struct, but that's
|
||||||
|
* a BSD and Linux extension not required by POSIX. Even when the
|
||||||
|
* interface is present, sometimes the type is unknown, depending on the
|
||||||
|
* filesystem.
|
||||||
|
*/
|
||||||
|
#if defined(DT_REG) && defined(DT_DIR) && defined(DT_LNK)
|
||||||
|
if (de->d_type == DT_REG)
|
||||||
|
result = PGFILETYPE_REG;
|
||||||
|
else if (de->d_type == DT_DIR)
|
||||||
|
result = PGFILETYPE_DIR;
|
||||||
|
else if (de->d_type == DT_LNK && !look_through_symlinks)
|
||||||
|
result = PGFILETYPE_LNK;
|
||||||
|
else
|
||||||
|
result = PGFILETYPE_UNKNOWN;
|
||||||
|
#else
|
||||||
|
result = PGFILETYPE_UNKNOWN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (result == PGFILETYPE_UNKNOWN)
|
||||||
|
{
|
||||||
|
struct stat fst;
|
||||||
|
int sret;
|
||||||
|
|
||||||
|
|
||||||
|
if (look_through_symlinks)
|
||||||
|
sret = stat(path, &fst);
|
||||||
|
else
|
||||||
|
sret = lstat(path, &fst);
|
||||||
|
|
||||||
|
if (sret < 0)
|
||||||
|
{
|
||||||
|
result = PGFILETYPE_ERROR;
|
||||||
|
#ifdef FRONTEND
|
||||||
|
pg_log_generic(elevel, "could not stat file \"%s\": %m", path);
|
||||||
|
#else
|
||||||
|
ereport(elevel,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not stat file \"%s\": %m", path)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (S_ISREG(fst.st_mode))
|
||||||
|
result = PGFILETYPE_REG;
|
||||||
|
else if (S_ISDIR(fst.st_mode))
|
||||||
|
result = PGFILETYPE_DIR;
|
||||||
|
#ifdef S_ISLNK
|
||||||
|
else if (S_ISLNK(fst.st_mode))
|
||||||
|
result = PGFILETYPE_LNK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
/*-------------------------------------------------------------------------
|
/*-------------------------------------------------------------------------
|
||||||
*
|
|
||||||
* File-processing utility routines for frontend code
|
|
||||||
*
|
*
|
||||||
* Assorted utility functions to work on files.
|
* Assorted utility functions to work on files.
|
||||||
*
|
*
|
||||||
@ -15,10 +13,28 @@
|
|||||||
#ifndef FILE_UTILS_H
|
#ifndef FILE_UTILS_H
|
||||||
#define FILE_UTILS_H
|
#define FILE_UTILS_H
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
typedef enum PGFileType
|
||||||
|
{
|
||||||
|
PGFILETYPE_ERROR,
|
||||||
|
PGFILETYPE_UNKNOWN,
|
||||||
|
PGFILETYPE_REG,
|
||||||
|
PGFILETYPE_DIR,
|
||||||
|
PGFILETYPE_LNK
|
||||||
|
} PGFileType;
|
||||||
|
|
||||||
|
#ifdef FRONTEND
|
||||||
extern int fsync_fname(const char *fname, bool isdir);
|
extern int fsync_fname(const char *fname, bool isdir);
|
||||||
extern void fsync_pgdata(const char *pg_data, int serverVersion);
|
extern void fsync_pgdata(const char *pg_data, int serverVersion);
|
||||||
extern void fsync_dir_recurse(const char *dir);
|
extern void fsync_dir_recurse(const char *dir);
|
||||||
extern int durable_rename(const char *oldfile, const char *newfile);
|
extern int durable_rename(const char *oldfile, const char *newfile);
|
||||||
extern int fsync_parent_path(const char *fname);
|
extern int fsync_parent_path(const char *fname);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern PGFileType get_dirent_type(const char *path,
|
||||||
|
const struct dirent *de,
|
||||||
|
bool look_through_symlinks,
|
||||||
|
int elevel);
|
||||||
|
|
||||||
#endif /* FILE_UTILS_H */
|
#endif /* FILE_UTILS_H */
|
||||||
|
@ -121,7 +121,7 @@ sub mkvcbuild
|
|||||||
our @pgcommonallfiles = qw(
|
our @pgcommonallfiles = qw(
|
||||||
archive.c base64.c checksum_helper.c
|
archive.c base64.c checksum_helper.c
|
||||||
config_info.c controldata_utils.c d2s.c encnames.c exec.c
|
config_info.c controldata_utils.c d2s.c encnames.c exec.c
|
||||||
f2s.c file_perm.c hashfn.c ip.c jsonapi.c
|
f2s.c file_perm.c file_utils.c hashfn.c ip.c jsonapi.c
|
||||||
keywords.c kwlookup.c link-canary.c md5.c
|
keywords.c kwlookup.c link-canary.c md5.c
|
||||||
pg_get_line.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
|
pg_get_line.c pg_lzcompress.c pgfnames.c psprintf.c relpath.c rmtree.c
|
||||||
saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
|
saslprep.c scram-common.c string.c stringinfo.c unicode_norm.c username.c
|
||||||
@ -138,7 +138,7 @@ sub mkvcbuild
|
|||||||
}
|
}
|
||||||
|
|
||||||
our @pgcommonfrontendfiles = (
|
our @pgcommonfrontendfiles = (
|
||||||
@pgcommonallfiles, qw(fe_memutils.c file_utils.c
|
@pgcommonallfiles, qw(fe_memutils.c
|
||||||
logging.c restricted_token.c sprompt.c));
|
logging.c restricted_token.c sprompt.c));
|
||||||
|
|
||||||
our @pgcommonbkndfiles = @pgcommonallfiles;
|
our @pgcommonbkndfiles = @pgcommonallfiles;
|
||||||
|
@ -1515,6 +1515,7 @@ PGEventResultCopy
|
|||||||
PGEventResultCreate
|
PGEventResultCreate
|
||||||
PGEventResultDestroy
|
PGEventResultDestroy
|
||||||
PGFInfoFunction
|
PGFInfoFunction
|
||||||
|
PGFileType
|
||||||
PGFunction
|
PGFunction
|
||||||
PGLZ_HistEntry
|
PGLZ_HistEntry
|
||||||
PGLZ_Strategy
|
PGLZ_Strategy
|
||||||
|
Loading…
x
Reference in New Issue
Block a user