From d14727c7a85b36e914c834ca8fbe5250e687f20e Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sat, 11 Dec 2010 13:52:03 +0200 Subject: [PATCH] * dont't require direntry->d_type; this should help the Solaris build. also, some cleanups --- configure.ac | 49 +++++++++----- src/mu-maildir.c | 173 ++++++++++++++++++----------------------------- src/mu-str.c | 26 +++++++ src/mu-str.h | 11 +++ src/mu-util.c | 26 ++++++- src/mu-util.h | 44 +++++++++++- 6 files changed, 201 insertions(+), 128 deletions(-) diff --git a/configure.ac b/configure.ac index 02adea72..d991cdfa 100644 --- a/configure.ac +++ b/configure.ac @@ -25,6 +25,7 @@ AC_CONFIG_MACRO_DIR([m4]) # silent build AM_SILENT_RULES([yes]) + LT_INIT # don't use AC_PROG_LIBTOOL anymore AS_IF([test x$prefix = xNONE],[ @@ -38,24 +39,33 @@ AC_PROG_CXX AC_HEADER_STDC -# currently, we don't support systems without d_type in their struct -# dirent (Solaris 10); but we do support FSs for which d_type is always -# DT_UNKNOWN (Like ReiserFS, XFS on Linux) -# -# note, we could work around this if there are many people for which -# this breaks -AC_STRUCT_DIRENT_D_TYPE -AS_IF([test "x$ac_cv_member_struct_dirent_d_type" != "xyes"],[ - AC_MSG_ERROR([ - *** We need the d_type member in struct dirent, but it seems - *** your system does not have it]) - ]) -# support for d_ino in struct dirent is optional -AC_STRUCT_DIRENT_D_INO +# we need some special tricks for filesystems that don't have d_type; +# e.g. Solaris. See mu-maildir.c. Explicitly disabling it is for +# testing purposes only +AC_ARG_ENABLE([dirent-d-type], + AC_HELP_STRING([--disable-dirent-d-type], + [Don't use dirent->d_type, even if you have it]), + [], [AC_STRUCT_DIRENT_D_TYPE] +) +AS_IF([test "x$ac_cv_member_struct_dirent_d_type" != "xyes"], + [use_dirent_d_type="no"], [use_dirent_d_type="yes"]) + + +# support for d_ino (inode) in struct dirent is optional; if it's +# 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 -AC_CHECK_FUNCS([memset realpath setlocale strerror]) - +AC_CHECK_FUNCS([memset memcpy realpath setlocale strerror]) # require pkg-config AC_PATH_PROG([PKG_CONFIG], [pkg-config], [no]) @@ -170,7 +180,7 @@ AS_IF([test "x$PMCCABE" = "xno"],[ if test "x$PMCCABE" = "xno"; then have_pmccabe="no" -else +lselse have_pmccabe="yes" fi @@ -198,10 +208,15 @@ if test -e ~/.mu/xapian-0.6; then echo "remove the old /xapian-0.6 directory to save some disk space" fi + echo "Xapian version : $xapian_version" echo "Build unit tests (glib >= 2.16) : $have_gtest" echo "Build 'mug' (requires GTK+) : $have_gtk" 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 "type 'make' to build mu, or 'make check' to run the unit tests." diff --git a/src/mu-maildir.c b/src/mu-maildir.c index 251f632d..9db87217 100644 --- a/src/mu-maildir.c +++ b/src/mu-maildir.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -33,22 +32,24 @@ #include "mu-util.h" #include "mu-maildir.h" +#include "mu-str.h" #define MU_MAILDIR_WALK_MAX_FILE_SIZE (32*1000*1000) #define MU_MAILDIR_NOINDEX_FILE ".noindex" -/* note: this function is *not* re-entrant, it returns a static buffer */ -static const char* -fullpath_s (const char* path, const char* name) -{ - static char buf[4096]; - - snprintf (buf, sizeof(buf), "%s%c%s", - path, G_DIR_SEPARATOR, - name ? name : ""); - - return buf; -} +/* On Linux (and some BSD), we have entry->d_type, but some file + * systems (XFS, ReiserFS) do not support it, and set it DT_UNKNOWN. + * 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, + * and return it in the d_type parameter + */ +#ifdef HAVE_STRUCT_DIRENT_D_TYPE +#define GET_DTYPE(DE,FP) \ + ((DE)->d_type == DT_UNKNOWN ? mu_util_get_dtype_with_lstat((FP)) : (DE)->d_type) +#else +#define GET_DTYPE(DE,FP) \ + mu_util_get_dtype_with_lstat((FP)) +#endif /*HAVE_STRUCT_DIRENT_D_TYPE*/ static gboolean @@ -71,7 +72,7 @@ create_maildir (const char *path, mode_t mode, GError **err) int rv; /* static buffer */ - fullpath = fullpath_s (path, subdirs[i]); + fullpath = mu_str_fullpath_s (path, subdirs[i]); rv = g_mkdir_with_parents (fullpath, (int)mode); if (rv != 0) { g_set_error (err, 0, MU_FILE_ERROR_CANNOT_MKDIR, @@ -92,7 +93,7 @@ create_noindex (const char *path, GError **err) const char *noindexpath; /* static buffer */ - noindexpath = fullpath_s (path, MU_MAILDIR_NOINDEX_FILE); + noindexpath = mu_str_fullpath_s (path, MU_MAILDIR_NOINDEX_FILE); fd = creat (noindexpath, 0644); @@ -288,7 +289,7 @@ has_noindex_file (const char *path) const char* noindexpath; /* 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) 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 -ignore_dir_entry (struct dirent *entry) +ignore_dir_entry (struct dirent *entry, unsigned char d_type) { const char *name; - + /* if it's not a dir and not a file, ignore it. * note, this means also symlinks (DT_LNK) are ignored, - * maybe make this optional. Also note that entry->d_type is - * defined on Linux, BSDs is not part of POSIX; this needs a - * configure check */ - if (entry->d_type != DT_REG && - entry->d_type != DT_DIR) + * maybe make this optional */ + if (d_type != DT_REG && d_type != DT_DIR) return TRUE; name = entry->d_name; @@ -399,22 +353,24 @@ process_dir_entry (const char* path, const char* mdir, struct dirent *entry, { const char *fp; char* fullpath; - - /* ignore special dirs: */ - if (ignore_dir_entry (entry)) - return MU_OK; + unsigned char d_type; /* we have to copy the buffer from fullpath_s, because it * 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); strcpy (fullpath, fp); - switch (entry->d_type) { - case DT_REG: - /* we only want files in cur/ and new/ */ + d_type = GET_DTYPE(entry, fullpath); + + /* 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)) - return MU_OK; + return MU_OK; 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); rv = process_dir (fullpath, my_mdir, cb_msg, cb_dir, data); g_free (my_mdir); + return rv; } @@ -437,13 +394,12 @@ process_dir_entry (const char* path, const char* mdir, struct dirent *entry, static struct dirent* 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 - * give memory errors. Also note, g_slice_new has been known to - * crash on FreeBSD */ - d = g_slice_new (struct dirent); - + * give memory errors. */ 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*/ - -/* 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 -process_dir_entries_sorted (DIR *dir, const char* path, const char* mdir, - MuMaildirWalkMsgCallback msg_cb, - MuMaildirWalkDirCallback dir_cb, void *data) +process_dir_entries (DIR *dir, const char* path, const char* mdir, + MuMaildirWalkMsgCallback msg_cb, + MuMaildirWalkDirCallback dir_cb, void *data) { MuResult result; GList *lst, *c; struct dirent *entry; lst = NULL; - while ((entry = readdir_with_stat_fallback (dir, path))) + while ((entry = readdir (dir))) 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 c = lst = g_list_sort (lst, (GCompareFunc)dirent_cmp); #endif /*HAVE_STRUCT_DIRENT_D_INO*/ 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); /* hmmm, break on MU_ERROR as well? */ 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, - data); + result = process_dir_entries (dir, path, mdir, msg_cb, dir_cb, data); closedir (dir); /* 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) mypath[strlen(mypath)-1] = '\0'; - rv = process_dir (mypath, NULL, cb_msg, - cb_dir, data); + rv = process_dir (mypath, NULL, cb_msg, cb_dir, data); g_free (mypath); return rv; @@ -582,27 +532,31 @@ clear_links (const gchar* dirname, DIR *dir, GError **err) gboolean rv; rv = TRUE; - while ((entry = readdir_with_stat_fallback (dir, dirname))) { + errno = 0; + while ((entry = readdir (dir))) { const char *fp; char *fullpath; - + unsigned char d_type; + /* ignore empty, dot thingies */ if (!entry->d_name || entry->d_name[0] == '.') 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 - * it returns a static buffer and we are - * recursive*/ - fp = fullpath_s (dirname, entry->d_name); + * it returns a static buffer and we are + * recursive*/ + fp = mu_str_fullpath_s (dirname, entry->d_name); fullpath = g_newa (char, strlen(fp) + 1); 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) { /* don't use err */ 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*/ 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); } diff --git a/src/mu-str.c b/src/mu-str.c index e110b8e3..ba3d0d93 100644 --- a/src/mu-str.c +++ b/src/mu-str.c @@ -27,6 +27,17 @@ #include #include #include +#include + +/* hopefully, this should get us a sane PATH_MAX */ +#include +/* not all systems provide PATH_MAX in limits.h */ +#ifndef PATH_MAX +#include +#ifndef PATH_MAX +#define PATH_MAX MAXPATHLEN +#endif /*!PATH_MAX*/ +#endif /*PATH_MAX*/ #include "mu-str.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)); } + + +/* 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; +} diff --git a/src/mu-str.h b/src/mu-str.h index 009b386c..f63d81d4 100644 --- a/src/mu-str.h +++ b/src/mu-str.h @@ -209,6 +209,17 @@ char* mu_str_ascii_xapian_escape (const char *query); 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 #endif /*__MU_STR_H__*/ diff --git a/src/mu-util.c b/src/mu-util.c index 5c854bd6..9c677e79 100644 --- a/src/mu-util.c +++ b/src/mu-util.c @@ -27,7 +27,7 @@ #include /* for shell-style globbing */ #include -/* hopefully, the should get us a sane PATH_MAX */ +/* hopefully, this should get us a sane PATH_MAX */ #include /* not all systems provide PATH_MAX in limits.h */ #ifndef PATH_MAX @@ -274,4 +274,28 @@ mu_util_create_writeable_fd (const char* filename, const char* dir, 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; +} + diff --git a/src/mu-util.h b/src/mu-util.h index 068f7644..c7834e1c 100644 --- a/src/mu-util.h +++ b/src/mu-util.h @@ -21,12 +21,14 @@ #define __MU_UTIL_H__ #include +#include G_BEGIN_DECLS /** * 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 */ @@ -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) 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... * */ - #define MU_XAPIAN_CATCH_BLOCK \ catch (const Xapian::Error &xerr) { \ g_critical ("%s: caught xapian exception '%s'", \