* dont't require direntry->d_type; this should help the Solaris build.

also, some cleanups
This commit is contained in:
Dirk-Jan C. Binnema 2010-12-11 13:52:03 +02:00
parent a0069702ba
commit d14727c7a8
6 changed files with 201 additions and 128 deletions

View File

@ -25,6 +25,7 @@ AC_CONFIG_MACRO_DIR([m4])
# silent build # silent build
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
LT_INIT # don't use AC_PROG_LIBTOOL anymore LT_INIT # don't use AC_PROG_LIBTOOL anymore
AS_IF([test x$prefix = xNONE],[ AS_IF([test x$prefix = xNONE],[
@ -38,24 +39,33 @@ AC_PROG_CXX
AC_HEADER_STDC AC_HEADER_STDC
# currently, we don't support systems without d_type in their struct # we need some special tricks for filesystems that don't have d_type;
# dirent (Solaris 10); but we do support FSs for which d_type is always # e.g. Solaris. See mu-maildir.c. Explicitly disabling it is for
# DT_UNKNOWN (Like ReiserFS, XFS on Linux) # testing purposes only
# AC_ARG_ENABLE([dirent-d-type],
# note, we could work around this if there are many people for which AC_HELP_STRING([--disable-dirent-d-type],
# this breaks [Don't use dirent->d_type, even if you have it]),
AC_STRUCT_DIRENT_D_TYPE [], [AC_STRUCT_DIRENT_D_TYPE]
AS_IF([test "x$ac_cv_member_struct_dirent_d_type" != "xyes"],[ )
AC_MSG_ERROR([ AS_IF([test "x$ac_cv_member_struct_dirent_d_type" != "xyes"],
*** We need the d_type member in struct dirent, but it seems [use_dirent_d_type="no"], [use_dirent_d_type="yes"])
*** your system does not have it])
])
# support for d_ino in struct dirent is optional # support for d_ino (inode) in struct dirent is optional; if it's
AC_STRUCT_DIRENT_D_INO # available we can sort direntries by inode and access them in that
# order; this is much faster on some file systems (such as extfs3).
# Explicity disabling it is for testing purposes only.
AC_ARG_ENABLE([dirent-d-ino],
AC_HELP_STRING([--disable-dirent-d-ino],
[Don't use dirent->d_ino, even if you have it]),
[], [AC_STRUCT_DIRENT_D_INO]
)
AS_IF([test "x$ac_cv_member_struct_dirent_d_ino" != "xyes"],
[use_dirent_d_ino="no"], [use_dirent_d_ino="yes"])
# we need these # we need these
AC_CHECK_FUNCS([memset realpath setlocale strerror]) AC_CHECK_FUNCS([memset memcpy realpath setlocale strerror])
# require pkg-config # require pkg-config
AC_PATH_PROG([PKG_CONFIG], [pkg-config], [no]) AC_PATH_PROG([PKG_CONFIG], [pkg-config], [no])
@ -170,7 +180,7 @@ AS_IF([test "x$PMCCABE" = "xno"],[
if test "x$PMCCABE" = "xno"; then if test "x$PMCCABE" = "xno"; then
have_pmccabe="no" have_pmccabe="no"
else lselse
have_pmccabe="yes" have_pmccabe="yes"
fi fi
@ -198,10 +208,15 @@ if test -e ~/.mu/xapian-0.6; then
echo "remove the old <muhome>/xapian-0.6 directory to save some disk space" echo "remove the old <muhome>/xapian-0.6 directory to save some disk space"
fi fi
echo "Xapian version : $xapian_version" echo "Xapian version : $xapian_version"
echo "Build unit tests (glib >= 2.16) : $have_gtest" echo "Build unit tests (glib >= 2.16) : $have_gtest"
echo "Build 'mug' (requires GTK+) : $have_gtk" echo "Build 'mug' (requires GTK+) : $have_gtk"
echo "McCabe's Cyclomatic Complexity tool : $have_pmccabe" echo "McCabe's Cyclomatic Complexity tool : $have_pmccabe"
echo
echo "Use direntry->d_ino : $use_dirent_d_ino"
echo "Use direntry->d_type : $use_dirent_d_type"
echo echo
echo "type 'make' to build mu, or 'make check' to run the unit tests." echo "type 'make' to build mu, or 'make check' to run the unit tests."

View File

@ -25,7 +25,6 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
@ -33,22 +32,24 @@
#include "mu-util.h" #include "mu-util.h"
#include "mu-maildir.h" #include "mu-maildir.h"
#include "mu-str.h"
#define MU_MAILDIR_WALK_MAX_FILE_SIZE (32*1000*1000) #define MU_MAILDIR_WALK_MAX_FILE_SIZE (32*1000*1000)
#define MU_MAILDIR_NOINDEX_FILE ".noindex" #define MU_MAILDIR_NOINDEX_FILE ".noindex"
/* note: this function is *not* re-entrant, it returns a static buffer */ /* On Linux (and some BSD), we have entry->d_type, but some file
static const char* * systems (XFS, ReiserFS) do not support it, and set it DT_UNKNOWN.
fullpath_s (const char* path, const char* name) * On other OSs, notably Solaris, entry->d_type is not present at all.
{ * For these cases, we use lstat (in get_dtype) as a slower fallback,
static char buf[4096]; * and return it in the d_type parameter
*/
snprintf (buf, sizeof(buf), "%s%c%s", #ifdef HAVE_STRUCT_DIRENT_D_TYPE
path, G_DIR_SEPARATOR, #define GET_DTYPE(DE,FP) \
name ? name : ""); ((DE)->d_type == DT_UNKNOWN ? mu_util_get_dtype_with_lstat((FP)) : (DE)->d_type)
#else
return buf; #define GET_DTYPE(DE,FP) \
} mu_util_get_dtype_with_lstat((FP))
#endif /*HAVE_STRUCT_DIRENT_D_TYPE*/
static gboolean static gboolean
@ -71,7 +72,7 @@ create_maildir (const char *path, mode_t mode, GError **err)
int rv; int rv;
/* static buffer */ /* static buffer */
fullpath = fullpath_s (path, subdirs[i]); fullpath = mu_str_fullpath_s (path, subdirs[i]);
rv = g_mkdir_with_parents (fullpath, (int)mode); rv = g_mkdir_with_parents (fullpath, (int)mode);
if (rv != 0) { if (rv != 0) {
g_set_error (err, 0, MU_FILE_ERROR_CANNOT_MKDIR, g_set_error (err, 0, MU_FILE_ERROR_CANNOT_MKDIR,
@ -92,7 +93,7 @@ create_noindex (const char *path, GError **err)
const char *noindexpath; const char *noindexpath;
/* static buffer */ /* static buffer */
noindexpath = fullpath_s (path, MU_MAILDIR_NOINDEX_FILE); noindexpath = mu_str_fullpath_s (path, MU_MAILDIR_NOINDEX_FILE);
fd = creat (noindexpath, 0644); fd = creat (noindexpath, 0644);
@ -288,7 +289,7 @@ has_noindex_file (const char *path)
const char* noindexpath; const char* noindexpath;
/* static buffer */ /* static buffer */
noindexpath = fullpath_s (path, MU_MAILDIR_NOINDEX_FILE); noindexpath = mu_str_fullpath_s (path, MU_MAILDIR_NOINDEX_FILE);
if (access (noindexpath, F_OK) == 0) if (access (noindexpath, F_OK) == 0)
return TRUE; return TRUE;
@ -301,62 +302,15 @@ has_noindex_file (const char *path)
} }
/* do readdir, and of file systems that do not support ->_dtype, fill it
* using stat -- much slower, but it works.
*/
static struct dirent*
readdir_with_stat_fallback (DIR* dir, const char* path)
{
struct dirent *entry;
errno = 0;
entry = readdir (dir);
if (G_UNLIKELY(!entry)) {
if (errno != 0)
g_warning ("readdir failed in %s: %s",
path, strerror (errno));
return NULL;
}
/* XFS, ReiserFS and some other FSs don't support d_type, and
* always set it to NULL; we use (slow) lstat instead then */
if (G_UNLIKELY(entry->d_type == DT_UNKNOWN)) {
struct stat statbuf;
const char* fullpath;
/* note, fullpath_s returns a static buffer */
fullpath = fullpath_s (path, entry->d_name);
if G_UNLIKELY(lstat (fullpath, &statbuf) != 0) {
g_warning ("stat failed on %s: %s", fullpath, strerror(errno));
return FALSE;
}
/* we only care about dirs, regular files and links */
if (S_ISREG (statbuf.st_mode))
entry->d_type = DT_REG;
else if (S_ISDIR (statbuf.st_mode))
entry->d_type = DT_DIR;
else if (S_ISLNK (statbuf.st_mode))
entry->d_type = DT_LNK;
}
return entry;
}
static gboolean static gboolean
ignore_dir_entry (struct dirent *entry) ignore_dir_entry (struct dirent *entry, unsigned char d_type)
{ {
const char *name; const char *name;
/* if it's not a dir and not a file, ignore it. /* if it's not a dir and not a file, ignore it.
* note, this means also symlinks (DT_LNK) are ignored, * note, this means also symlinks (DT_LNK) are ignored,
* maybe make this optional. Also note that entry->d_type is * maybe make this optional */
* defined on Linux, BSDs is not part of POSIX; this needs a if (d_type != DT_REG && d_type != DT_DIR)
* configure check */
if (entry->d_type != DT_REG &&
entry->d_type != DT_DIR)
return TRUE; return TRUE;
name = entry->d_name; name = entry->d_name;
@ -399,22 +353,24 @@ process_dir_entry (const char* path, const char* mdir, struct dirent *entry,
{ {
const char *fp; const char *fp;
char* fullpath; char* fullpath;
unsigned char d_type;
/* ignore special dirs: */
if (ignore_dir_entry (entry))
return MU_OK;
/* we have to copy the buffer from fullpath_s, because it /* we have to copy the buffer from fullpath_s, because it
* returns a static buffer */ * returns a static buffer */
fp = fullpath_s (path, entry->d_name); fp = mu_str_fullpath_s (path, entry->d_name);
fullpath = g_newa (char, strlen(fp) + 1); fullpath = g_newa (char, strlen(fp) + 1);
strcpy (fullpath, fp); strcpy (fullpath, fp);
switch (entry->d_type) { d_type = GET_DTYPE(entry, fullpath);
case DT_REG:
/* we only want files in cur/ and new/ */ /* ignore special files/dirs */
if (ignore_dir_entry (entry, d_type))
return MU_OK;
switch (d_type) {
case DT_REG: /* we only want files in cur/ and new/ */
if (!is_maildir_new_or_cur (path)) if (!is_maildir_new_or_cur (path))
return MU_OK; return MU_OK;
return process_file (fullpath, mdir, cb_msg, data); return process_file (fullpath, mdir, cb_msg, data);
@ -425,6 +381,7 @@ process_dir_entry (const char* path, const char* mdir, struct dirent *entry,
my_mdir = get_mdir_for_path (mdir, entry->d_name); my_mdir = get_mdir_for_path (mdir, entry->d_name);
rv = process_dir (fullpath, my_mdir, cb_msg, cb_dir, data); rv = process_dir (fullpath, my_mdir, cb_msg, cb_dir, data);
g_free (my_mdir); g_free (my_mdir);
return rv; return rv;
} }
@ -437,13 +394,12 @@ process_dir_entry (const char* path, const char* mdir, struct dirent *entry,
static struct dirent* static struct dirent*
dirent_copy (struct dirent *entry) dirent_copy (struct dirent *entry)
{ {
struct dirent *d; struct dirent *d;
d = g_slice_new (struct dirent);
/* NOTE: simply memcpy'ing sizeof(struct dirent) bytes will /* NOTE: simply memcpy'ing sizeof(struct dirent) bytes will
* give memory errors. Also note, g_slice_new has been known to * give memory errors. */
* crash on FreeBSD */
d = g_slice_new (struct dirent);
return (struct dirent*) memcpy (d, entry, entry->d_reclen); return (struct dirent*) memcpy (d, entry, entry->d_reclen);
} }
@ -469,32 +425,28 @@ dirent_cmp (struct dirent *d1, struct dirent *d2)
} }
#endif /*HAVE_STRUCT_DIRENT_D_INO*/ #endif /*HAVE_STRUCT_DIRENT_D_INO*/
/* we sort the inodes if the FS's dirent has them. It makes
* file-access much faster on some filesystems, such as ext3,4.
*
* readdir_with_stat_fallback is a wrapper for readdir that falls back
* to (slow) lstats if the FS does not support entry->d_type
*/
static MuResult static MuResult
process_dir_entries_sorted (DIR *dir, const char* path, const char* mdir, process_dir_entries (DIR *dir, const char* path, const char* mdir,
MuMaildirWalkMsgCallback msg_cb, MuMaildirWalkMsgCallback msg_cb,
MuMaildirWalkDirCallback dir_cb, void *data) MuMaildirWalkDirCallback dir_cb, void *data)
{ {
MuResult result; MuResult result;
GList *lst, *c; GList *lst, *c;
struct dirent *entry; struct dirent *entry;
lst = NULL; lst = NULL;
while ((entry = readdir_with_stat_fallback (dir, path))) while ((entry = readdir (dir)))
lst = g_list_prepend (lst, dirent_copy(entry)); lst = g_list_prepend (lst, dirent_copy(entry));
/* we sort by inode; this makes things much faster on
* extfs2,3 */
#if HAVE_STRUCT_DIRENT_D_INO #if HAVE_STRUCT_DIRENT_D_INO
c = lst = g_list_sort (lst, (GCompareFunc)dirent_cmp); c = lst = g_list_sort (lst, (GCompareFunc)dirent_cmp);
#endif /*HAVE_STRUCT_DIRENT_D_INO*/ #endif /*HAVE_STRUCT_DIRENT_D_INO*/
for (c = lst, result = MU_OK; c && result == MU_OK; c = c->next) { for (c = lst, result = MU_OK; c && result == MU_OK; c = c->next) {
result = process_dir_entry (path, mdir, (struct dirent*)c->data, result = process_dir_entry (path, mdir,
(struct dirent*)c->data,
msg_cb, dir_cb, data); msg_cb, dir_cb, data);
/* hmmm, break on MU_ERROR as well? */ /* hmmm, break on MU_ERROR as well? */
if (result == MU_STOP) if (result == MU_STOP)
@ -538,8 +490,7 @@ process_dir (const char* path, const char* mdir,
} }
} }
result = process_dir_entries_sorted (dir, path, mdir, msg_cb, dir_cb, result = process_dir_entries (dir, path, mdir, msg_cb, dir_cb, data);
data);
closedir (dir); closedir (dir);
/* only run dir_cb if it exists and so far, things went ok */ /* only run dir_cb if it exists and so far, things went ok */
@ -567,8 +518,7 @@ mu_maildir_walk (const char *path, MuMaildirWalkMsgCallback cb_msg,
if (mypath[strlen(mypath)-1] == G_DIR_SEPARATOR) if (mypath[strlen(mypath)-1] == G_DIR_SEPARATOR)
mypath[strlen(mypath)-1] = '\0'; mypath[strlen(mypath)-1] = '\0';
rv = process_dir (mypath, NULL, cb_msg, rv = process_dir (mypath, NULL, cb_msg, cb_dir, data);
cb_dir, data);
g_free (mypath); g_free (mypath);
return rv; return rv;
@ -582,27 +532,31 @@ clear_links (const gchar* dirname, DIR *dir, GError **err)
gboolean rv; gboolean rv;
rv = TRUE; rv = TRUE;
while ((entry = readdir_with_stat_fallback (dir, dirname))) { errno = 0;
while ((entry = readdir (dir))) {
const char *fp; const char *fp;
char *fullpath; char *fullpath;
unsigned char d_type;
/* ignore empty, dot thingies */ /* ignore empty, dot thingies */
if (!entry->d_name || entry->d_name[0] == '.') if (!entry->d_name || entry->d_name[0] == '.')
continue; continue;
/* ignore non-links / non-dirs */
if (entry->d_type != DT_LNK && entry->d_type != DT_DIR)
continue;
/* we have to copy the buffer from fullpath_s, because /* we have to copy the buffer from fullpath_s, because
* it returns a static buffer and we are * it returns a static buffer and we are
* recursive*/ * recursive*/
fp = fullpath_s (dirname, entry->d_name); fp = mu_str_fullpath_s (dirname, entry->d_name);
fullpath = g_newa (char, strlen(fp) + 1); fullpath = g_newa (char, strlen(fp) + 1);
strcpy (fullpath, fp); strcpy (fullpath, fp);
d_type = GET_DTYPE (entry, fullpath);
/* ignore non-links / non-dirs */
if (d_type != DT_LNK && d_type != DT_DIR)
continue;
if (entry->d_type == DT_LNK) { if (d_type == DT_LNK) {
if (unlink (fullpath) != 0) { if (unlink (fullpath) != 0) {
/* don't use err */ /* don't use err */
g_warning ("error unlinking %s: %s", g_warning ("error unlinking %s: %s",
@ -612,8 +566,11 @@ clear_links (const gchar* dirname, DIR *dir, GError **err)
} else /* DT_DIR, see check before*/ } else /* DT_DIR, see check before*/
rv = mu_maildir_clear_links (fullpath, err); rv = mu_maildir_clear_links (fullpath, err);
} }
if (errno != 0)
g_set_error (err, 0, MU_ERROR_FILE,"file error: %s", strerror(errno));
return rv; return (rv == FALSE && errno == 0);
} }

View File

@ -27,6 +27,17 @@
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
/* hopefully, this should get us a sane PATH_MAX */
#include <limits.h>
/* not all systems provide PATH_MAX in limits.h */
#ifndef PATH_MAX
#include <sys/param.h>
#ifndef PATH_MAX
#define PATH_MAX MAXPATHLEN
#endif /*!PATH_MAX*/
#endif /*PATH_MAX*/
#include "mu-str.h" #include "mu-str.h"
#include "mu-msg-flags.h" #include "mu-msg-flags.h"
@ -342,3 +353,18 @@ mu_str_ascii_xapian_escape (const char *query)
return mu_str_ascii_xapian_escape_in_place (g_strdup(query)); return mu_str_ascii_xapian_escape_in_place (g_strdup(query));
} }
/* note: this function is *not* re-entrant, it returns a static buffer */
const char*
mu_str_fullpath_s (const char* path, const char* name)
{
static char buf[PATH_MAX + 1];
g_return_val_if_fail (path, NULL);
snprintf (buf, sizeof(buf), "%s%c%s", path, G_DIR_SEPARATOR,
name ? name : "");
return buf;
}

View File

@ -209,6 +209,17 @@ char* mu_str_ascii_xapian_escape (const char *query);
time_t mu_str_date_parse_hdwmy (const char* str); time_t mu_str_date_parse_hdwmy (const char* str);
/**
* create a full path from a path + a filename. function is _not_
* reentrant.
*
* @param path a path (!= NULL)
* @param name a name (may be NULL)
*
* @return the path as a statically allocated buffer. don't free.
*/
const char* mu_str_fullpath_s (const char* path, const char* name);
G_END_DECLS G_END_DECLS
#endif /*__MU_STR_H__*/ #endif /*__MU_STR_H__*/

View File

@ -27,7 +27,7 @@
#include <wordexp.h> /* for shell-style globbing */ #include <wordexp.h> /* for shell-style globbing */
#include <stdlib.h> #include <stdlib.h>
/* hopefully, the should get us a sane PATH_MAX */ /* hopefully, this should get us a sane PATH_MAX */
#include <limits.h> #include <limits.h>
/* not all systems provide PATH_MAX in limits.h */ /* not all systems provide PATH_MAX in limits.h */
#ifndef PATH_MAX #ifndef PATH_MAX
@ -274,4 +274,28 @@ mu_util_create_writeable_fd (const char* filename, const char* dir,
return fd; return fd;
} }
unsigned char
mu_util_get_dtype_with_lstat (const char *path)
{
struct stat statbuf;
g_return_val_if_fail (path, DT_UNKNOWN);
if (lstat (path, &statbuf) != 0) {
g_warning ("stat failed on %s: %s",
path, strerror(errno));
return DT_UNKNOWN;
}
/* we only care about dirs, regular files and links */
if (S_ISREG (statbuf.st_mode))
return DT_REG;
else if (S_ISDIR (statbuf.st_mode))
return DT_DIR;
else if (S_ISLNK (statbuf.st_mode))
return DT_LNK;
return DT_UNKNOWN;
}

View File

@ -21,12 +21,14 @@
#define __MU_UTIL_H__ #define __MU_UTIL_H__
#include <glib.h> #include <glib.h>
#include <dirent.h>
G_BEGIN_DECLS G_BEGIN_DECLS
/** /**
* do system-specific initialization. should be called before anything * do system-specific initialization. should be called before anything
* else. Initializes the locale and Gtype * else. Initializes the locale and Gtype. Note: this function is
* called by mu_runtime_init.
* *
* @return TRUE if is succeeds, FALSE otherwise * @return TRUE if is succeeds, FALSE otherwise
*/ */
@ -121,12 +123,50 @@ int mu_util_create_writeable_fd (const char* filename, const char* dir,
gchar* mu_util_str_from_strv (const gchar **params) gchar* mu_util_str_from_strv (const gchar **params)
G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
/*
* for OSs with out support for direntry->d_type, like Solaris
*/
#ifndef DT_UNKNOWN
enum {
DT_UNKNOWN = 0,
# define DT_UNKNOWN DT_UNKNOWN
DT_FIFO = 1,
# define DT_FIFO DT_FIFO
DT_CHR = 2,
# define DT_CHR DT_CHR
DT_DIR = 4,
# define DT_DIR DT_DIR
DT_BLK = 6,
# define DT_BLK DT_BLK
DT_REG = 8,
# define DT_REG DT_REG
DT_LNK = 10,
# define DT_LNK DT_LNK
DT_SOCK = 12,
# define DT_SOCK DT_SOCK
DT_WHT = 14
# define DT_WHT DT_WHT
};
#endif /*DT_UNKNOWN*/
/**
* get the d_type (as in direntry->d_type) for the file at path, using
* lstat(3)
*
* @param path full path
*
* @return DT_REG, DT_DIR, DT_LNK, or DT_UNKNOWN (other values are not
* supported currently )
*/
unsigned char mu_util_get_dtype_with_lstat (const char *path);
/** /**
* *
* don't repeat these catch blocks everywhere... * don't repeat these catch blocks everywhere...
* *
*/ */
#define MU_XAPIAN_CATCH_BLOCK \ #define MU_XAPIAN_CATCH_BLOCK \
catch (const Xapian::Error &xerr) { \ catch (const Xapian::Error &xerr) { \
g_critical ("%s: caught xapian exception '%s'", \ g_critical ("%s: caught xapian exception '%s'", \