The FTS_NOSTAT option is an optimisation that reduces the number
of stat(2) calls by keeping an eye of the number of links a directory has. It assumes that each subdirectory will have a hard link to its parent, to represent the ".." node, and stops calling stat(2) when all links are accounted for in a given directory. This assumption is really only valid for UNIX-like filesystems: A concrete example is NTFS. The NTFS "i-node" does contain a link count, but most/all directories have a link count between 0 and 2 inclusive. The end result is that find on an NTFS volume won't actually traverse the entire hierarchy of the directories passed to it. (Those with a link count of two are not traversed at all) The fix checks the "UFSness" of the filesystem before enabling the optimisation. Reviewed By: Tim Kientzle (kientzle@)
This commit is contained in:
parent
66a79ac95d
commit
99ca5b8804
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=129052
@ -37,6 +37,8 @@
|
|||||||
#ifndef _FTS_H_
|
#ifndef _FTS_H_
|
||||||
#define _FTS_H_
|
#define _FTS_H_
|
||||||
|
|
||||||
|
struct _fts_private; /* implementation data */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct _ftsent *fts_cur; /* current node */
|
struct _ftsent *fts_cur; /* current node */
|
||||||
struct _ftsent *fts_child; /* linked list of children */
|
struct _ftsent *fts_child; /* linked list of children */
|
||||||
@ -63,6 +65,7 @@ typedef struct {
|
|||||||
#define FTS_STOP 0x200 /* (private) unrecoverable error */
|
#define FTS_STOP 0x200 /* (private) unrecoverable error */
|
||||||
int fts_options; /* fts_open options, global flags */
|
int fts_options; /* fts_open options, global flags */
|
||||||
void *fts_clientptr; /* thunk for sort function */
|
void *fts_clientptr; /* thunk for sort function */
|
||||||
|
struct _fts_private *fts_priv; /* Implementation data */
|
||||||
} FTS;
|
} FTS;
|
||||||
|
|
||||||
typedef struct _ftsent {
|
typedef struct _ftsent {
|
||||||
|
@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -63,6 +64,7 @@ static int fts_palloc(FTS *, size_t);
|
|||||||
static FTSENT *fts_sort(FTS *, FTSENT *, int);
|
static FTSENT *fts_sort(FTS *, FTSENT *, int);
|
||||||
static u_short fts_stat(FTS *, FTSENT *, int);
|
static u_short fts_stat(FTS *, FTSENT *, int);
|
||||||
static int fts_safe_changedir(FTS *, FTSENT *, int, char *);
|
static int fts_safe_changedir(FTS *, FTSENT *, int, char *);
|
||||||
|
static int fts_ufslinks(FTS *sp, const FTSENT *ent);
|
||||||
|
|
||||||
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
|
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
|
||||||
|
|
||||||
@ -77,12 +79,43 @@ static int fts_safe_changedir(FTS *, FTSENT *, int, char *);
|
|||||||
#define BNAMES 2 /* fts_children, names only */
|
#define BNAMES 2 /* fts_children, names only */
|
||||||
#define BREAD 3 /* fts_read */
|
#define BREAD 3 /* fts_read */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal representation of FTS, including extra implementation details.
|
||||||
|
* The FTS returned from fts_open is ftsp_fts from this structure, and it's
|
||||||
|
* fts_priv in turn points back to this internal version. i.e. for a given
|
||||||
|
* fts_private *priv: &priv->fts_fts == (FTS *)f == priv->fts_fts.fts_priv
|
||||||
|
*/
|
||||||
|
struct _fts_private {
|
||||||
|
FTS ftsp_fts;
|
||||||
|
struct statfs ftsp_statfs;
|
||||||
|
dev_t ftsp_dev;
|
||||||
|
int ftsp_linksreliable;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The "FTS_NOSTAT" option can avoid a lot of calls to stat(2) if it knows
|
||||||
|
* that a directory could not possibly have subdirectories. This is decided
|
||||||
|
* by looking at the link count: A subdirectory would increment its parent's
|
||||||
|
* link count by virtue of its own ".." entry.
|
||||||
|
* This assumption only holds for UFS-like filesystems that implement links
|
||||||
|
* and directories this way, so we must punt for others.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char *ufslike_filesystems[] = {
|
||||||
|
"ufs",
|
||||||
|
"nfs",
|
||||||
|
"nfs4",
|
||||||
|
"ext2fs",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
FTS *
|
FTS *
|
||||||
fts_open(argv, options, compar)
|
fts_open(argv, options, compar)
|
||||||
char * const *argv;
|
char * const *argv;
|
||||||
int options;
|
int options;
|
||||||
int (*compar)(const FTSENT * const *, const FTSENT * const *);
|
int (*compar)(const FTSENT * const *, const FTSENT * const *);
|
||||||
{
|
{
|
||||||
|
struct _fts_private *priv;
|
||||||
FTS *sp;
|
FTS *sp;
|
||||||
FTSENT *p, *root;
|
FTSENT *p, *root;
|
||||||
int nitems;
|
int nitems;
|
||||||
@ -96,11 +129,13 @@ fts_open(argv, options, compar)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate/initialize the stream */
|
/* Allocate/initialize the stream */
|
||||||
if ((sp = malloc(sizeof(FTS))) == NULL)
|
if ((priv = malloc(sizeof(struct _fts_private))) == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
memset(sp, 0, sizeof(FTS));
|
memset(priv, 0, sizeof(struct _fts_private));
|
||||||
|
sp = &priv->ftsp_fts;
|
||||||
sp->fts_compar = compar;
|
sp->fts_compar = compar;
|
||||||
sp->fts_options = options;
|
sp->fts_options = options;
|
||||||
|
sp->fts_priv = priv;
|
||||||
|
|
||||||
/* Shush, GCC. */
|
/* Shush, GCC. */
|
||||||
tmp = NULL;
|
tmp = NULL;
|
||||||
@ -637,7 +672,10 @@ fts_build(sp, type)
|
|||||||
/* Be quiet about nostat, GCC. */
|
/* Be quiet about nostat, GCC. */
|
||||||
nostat = 0;
|
nostat = 0;
|
||||||
} else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
|
} else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
|
||||||
nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
|
if (fts_ufslinks(sp, cur))
|
||||||
|
nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
|
||||||
|
else
|
||||||
|
nlinks = -1;
|
||||||
nostat = 1;
|
nostat = 1;
|
||||||
} else {
|
} else {
|
||||||
nlinks = -1;
|
nlinks = -1;
|
||||||
@ -1154,3 +1192,37 @@ fts_safe_changedir(sp, p, fd, path)
|
|||||||
errno = oerrno;
|
errno = oerrno;
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the filesystem for "ent" has UFS-style links.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
fts_ufslinks(FTS *sp, const FTSENT *ent)
|
||||||
|
{
|
||||||
|
struct _fts_private *priv;
|
||||||
|
const char **cpp;
|
||||||
|
|
||||||
|
priv = sp->fts_priv;
|
||||||
|
/*
|
||||||
|
* If this node's device is different from the previous, grab
|
||||||
|
* the filesystem information, and decide on the reliability
|
||||||
|
* of the link information from this filesystem for stat(2)
|
||||||
|
* avoidance.
|
||||||
|
*/
|
||||||
|
if (priv->ftsp_dev != ent->fts_dev) {
|
||||||
|
if (statfs(ent->fts_path, &priv->ftsp_statfs) != -1) {
|
||||||
|
priv->ftsp_dev = ent->fts_dev;
|
||||||
|
priv->ftsp_linksreliable = 0;
|
||||||
|
for (cpp = ufslike_filesystems; *cpp; cpp++) {
|
||||||
|
if (strcmp(priv->ftsp_statfs.f_fstypename,
|
||||||
|
*cpp) == 0) {
|
||||||
|
priv->ftsp_linksreliable = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
priv->ftsp_linksreliable = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return priv->ftsp_linksreliable;
|
||||||
|
}
|
||||||
|
@ -37,6 +37,8 @@
|
|||||||
#ifndef _FTS_H_
|
#ifndef _FTS_H_
|
||||||
#define _FTS_H_
|
#define _FTS_H_
|
||||||
|
|
||||||
|
struct _fts_private; /* implementation data */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct _ftsent *fts_cur; /* current node */
|
struct _ftsent *fts_cur; /* current node */
|
||||||
struct _ftsent *fts_child; /* linked list of children */
|
struct _ftsent *fts_child; /* linked list of children */
|
||||||
@ -63,6 +65,7 @@ typedef struct {
|
|||||||
#define FTS_STOP 0x200 /* (private) unrecoverable error */
|
#define FTS_STOP 0x200 /* (private) unrecoverable error */
|
||||||
int fts_options; /* fts_open options, global flags */
|
int fts_options; /* fts_open options, global flags */
|
||||||
void *fts_clientptr; /* thunk for sort function */
|
void *fts_clientptr; /* thunk for sort function */
|
||||||
|
struct _fts_private *fts_priv; /* Implementation data */
|
||||||
} FTS;
|
} FTS;
|
||||||
|
|
||||||
typedef struct _ftsent {
|
typedef struct _ftsent {
|
||||||
|
@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@ -63,6 +64,7 @@ static int fts_palloc(FTS *, size_t);
|
|||||||
static FTSENT *fts_sort(FTS *, FTSENT *, int);
|
static FTSENT *fts_sort(FTS *, FTSENT *, int);
|
||||||
static u_short fts_stat(FTS *, FTSENT *, int);
|
static u_short fts_stat(FTS *, FTSENT *, int);
|
||||||
static int fts_safe_changedir(FTS *, FTSENT *, int, char *);
|
static int fts_safe_changedir(FTS *, FTSENT *, int, char *);
|
||||||
|
static int fts_ufslinks(FTS *sp, const FTSENT *ent);
|
||||||
|
|
||||||
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
|
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
|
||||||
|
|
||||||
@ -77,12 +79,43 @@ static int fts_safe_changedir(FTS *, FTSENT *, int, char *);
|
|||||||
#define BNAMES 2 /* fts_children, names only */
|
#define BNAMES 2 /* fts_children, names only */
|
||||||
#define BREAD 3 /* fts_read */
|
#define BREAD 3 /* fts_read */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal representation of FTS, including extra implementation details.
|
||||||
|
* The FTS returned from fts_open is ftsp_fts from this structure, and it's
|
||||||
|
* fts_priv in turn points back to this internal version. i.e. for a given
|
||||||
|
* fts_private *priv: &priv->fts_fts == (FTS *)f == priv->fts_fts.fts_priv
|
||||||
|
*/
|
||||||
|
struct _fts_private {
|
||||||
|
FTS ftsp_fts;
|
||||||
|
struct statfs ftsp_statfs;
|
||||||
|
dev_t ftsp_dev;
|
||||||
|
int ftsp_linksreliable;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The "FTS_NOSTAT" option can avoid a lot of calls to stat(2) if it knows
|
||||||
|
* that a directory could not possibly have subdirectories. This is decided
|
||||||
|
* by looking at the link count: A subdirectory would increment its parent's
|
||||||
|
* link count by virtue of its own ".." entry.
|
||||||
|
* This assumption only holds for UFS-like filesystems that implement links
|
||||||
|
* and directories this way, so we must punt for others.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char *ufslike_filesystems[] = {
|
||||||
|
"ufs",
|
||||||
|
"nfs",
|
||||||
|
"nfs4",
|
||||||
|
"ext2fs",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
FTS *
|
FTS *
|
||||||
fts_open(argv, options, compar)
|
fts_open(argv, options, compar)
|
||||||
char * const *argv;
|
char * const *argv;
|
||||||
int options;
|
int options;
|
||||||
int (*compar)(const FTSENT * const *, const FTSENT * const *);
|
int (*compar)(const FTSENT * const *, const FTSENT * const *);
|
||||||
{
|
{
|
||||||
|
struct _fts_private *priv;
|
||||||
FTS *sp;
|
FTS *sp;
|
||||||
FTSENT *p, *root;
|
FTSENT *p, *root;
|
||||||
int nitems;
|
int nitems;
|
||||||
@ -96,11 +129,13 @@ fts_open(argv, options, compar)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate/initialize the stream */
|
/* Allocate/initialize the stream */
|
||||||
if ((sp = malloc(sizeof(FTS))) == NULL)
|
if ((priv = malloc(sizeof(struct _fts_private))) == NULL)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
memset(sp, 0, sizeof(FTS));
|
memset(priv, 0, sizeof(struct _fts_private));
|
||||||
|
sp = &priv->ftsp_fts;
|
||||||
sp->fts_compar = compar;
|
sp->fts_compar = compar;
|
||||||
sp->fts_options = options;
|
sp->fts_options = options;
|
||||||
|
sp->fts_priv = priv;
|
||||||
|
|
||||||
/* Shush, GCC. */
|
/* Shush, GCC. */
|
||||||
tmp = NULL;
|
tmp = NULL;
|
||||||
@ -637,7 +672,10 @@ fts_build(sp, type)
|
|||||||
/* Be quiet about nostat, GCC. */
|
/* Be quiet about nostat, GCC. */
|
||||||
nostat = 0;
|
nostat = 0;
|
||||||
} else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
|
} else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
|
||||||
nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
|
if (fts_ufslinks(sp, cur))
|
||||||
|
nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
|
||||||
|
else
|
||||||
|
nlinks = -1;
|
||||||
nostat = 1;
|
nostat = 1;
|
||||||
} else {
|
} else {
|
||||||
nlinks = -1;
|
nlinks = -1;
|
||||||
@ -1154,3 +1192,37 @@ fts_safe_changedir(sp, p, fd, path)
|
|||||||
errno = oerrno;
|
errno = oerrno;
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if the filesystem for "ent" has UFS-style links.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
fts_ufslinks(FTS *sp, const FTSENT *ent)
|
||||||
|
{
|
||||||
|
struct _fts_private *priv;
|
||||||
|
const char **cpp;
|
||||||
|
|
||||||
|
priv = sp->fts_priv;
|
||||||
|
/*
|
||||||
|
* If this node's device is different from the previous, grab
|
||||||
|
* the filesystem information, and decide on the reliability
|
||||||
|
* of the link information from this filesystem for stat(2)
|
||||||
|
* avoidance.
|
||||||
|
*/
|
||||||
|
if (priv->ftsp_dev != ent->fts_dev) {
|
||||||
|
if (statfs(ent->fts_path, &priv->ftsp_statfs) != -1) {
|
||||||
|
priv->ftsp_dev = ent->fts_dev;
|
||||||
|
priv->ftsp_linksreliable = 0;
|
||||||
|
for (cpp = ufslike_filesystems; *cpp; cpp++) {
|
||||||
|
if (strcmp(priv->ftsp_statfs.f_fstypename,
|
||||||
|
*cpp) == 0) {
|
||||||
|
priv->ftsp_linksreliable = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
priv->ftsp_linksreliable = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return priv->ftsp_linksreliable;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user