diff --git a/src/mu-maildir.c b/src/mu-maildir.c index e1b970dc..e1507f5c 100644 --- a/src/mu-maildir.c +++ b/src/mu-maildir.c @@ -37,6 +37,19 @@ #define MU_MAILDIR_NOINDEX_FILE ".noindex" #define MU_MAILDIR_CACHE_FILE ".mu.cache" +/* note: this function is *not* re-entrant, it returns a static buffer */ +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; +} + static gboolean create_maildir (const char *path, int mode) @@ -54,17 +67,16 @@ create_maildir (const char *path, int mode) for (i = 0; i != G_N_ELEMENTS(subdirs); ++i) { - gchar *fullpath; + const char *fullpath; int rv; + + /* static buffer */ + fullpath = fullpath_s (path, subdirs[i]); - fullpath = g_strdup_printf ("%s%c%s", path, - G_DIR_SEPARATOR, - subdirs[i]); rv = g_mkdir_with_parents (fullpath, mode); - g_free (fullpath); - if (rv != 0) { - g_warning ("%s", strerror (errno)); + g_warning ("g_mkdir_with_parents failed: %s", + strerror (errno)); return FALSE; } } @@ -77,14 +89,16 @@ create_noindex (const char *path) { /* create a noindex file if requested */ int fd; - gchar *noindexpath; - noindexpath = g_strdup_printf ("%s%c%s", path, - G_DIR_SEPARATOR, - MU_MAILDIR_NOINDEX_FILE); + const char *noindexpath; + + /* static buffer */ + noindexpath = fullpath_s (path, MU_MAILDIR_NOINDEX_FILE); + fd = creat (noindexpath, 0644); - g_free (noindexpath); + if (fd < 0 || close (fd) != 0) { - g_warning ("%s", strerror (errno)); + g_warning ("error in create_noindex: %s", + strerror (errno)); return FALSE; } @@ -193,7 +207,8 @@ mu_maildir_link (const char* src, const char *targetpath) } -static MuResult process_dir (const char* path, MuMaildirWalkMsgCallback msg_cb, +static MuResult process_dir (const char* path, + MuMaildirWalkMsgCallback msg_cb, MuMaildirWalkDirCallback dir_cb, void *data); static MuResult @@ -206,7 +221,8 @@ process_file (const char* fullpath, MuMaildirWalkMsgCallback cb, void *data) return MU_OK; if (G_UNLIKELY(access(fullpath, R_OK) != 0)) { - g_warning ("cannot access %s: %s", fullpath, strerror(errno)); + g_warning ("cannot access %s: %s", fullpath, + strerror(errno)); return MU_ERROR; } @@ -267,53 +283,67 @@ is_maildir_new_or_cur (const char *path) static gboolean has_noindex_file (const char *path) { - char *fname; + const char* noindexpath; - fname = g_newa (char, strlen(path) + 1 + - strlen(MU_MAILDIR_NOINDEX_FILE) + 1); - g_sprintf (fname, "%s%c%s", path, G_DIR_SEPARATOR, - MU_MAILDIR_NOINDEX_FILE); + /* static buffer */ + noindexpath = fullpath_s (path, MU_MAILDIR_NOINDEX_FILE); - if (access (fname, F_OK) == 0) + if (access (noindexpath, F_OK) == 0) return TRUE; + else if (errno != ENOENT) - g_warning ("error testing for noindex file: %s", - strerror(errno)); + g_warning ("error testing for noindex file %s: %s", + noindexpath, strerror(errno)); return FALSE; } -/* if the file system does not support entry->d_type, we add it ourselves - * this is slower (extra stat) but at least it works - */ -static gboolean -set_dtype (const char* path, struct dirent *entry) + +/* 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 stat statbuf; - char fullpath[4096]; - - snprintf (fullpath, sizeof(fullpath), "%s%c%s", - path, G_DIR_SEPARATOR, entry->d_name); + struct dirent *entry; - if (stat (fullpath, &statbuf) != 0) { - g_warning ("stat failed on %s: %s", fullpath, - strerror(errno)); - return FALSE; + errno = 0; + entry = readdir (dir); + + if (!entry) { + if (errno) + g_warning ("readdir failed in %s: %s", + path, strerror (errno)); + return NULL; } - - /* 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 TRUE; + /* XFS, ReiserFS and some other FSs don't support d_type, and + * always set it to NULL; we use (slow) stat 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 (stat (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) { @@ -353,16 +383,19 @@ process_dir_entry (const char* path, struct dirent *entry, MuMaildirWalkDirCallback cb_dir, void *data) { + const char *fp; char* fullpath; /* ignore special dirs: */ if (ignore_dir_entry (entry)) return MU_OK; - - fullpath = g_newa (char, strlen(path) + strlen(entry->d_name) + 1); - sprintf (fullpath, "%s%c%s", path, G_DIR_SEPARATOR, - entry->d_name); + /* we have to copy the buffer from fullpath_s, because it + * returns a static buffer */ + fp = 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/ */ @@ -421,8 +454,9 @@ process_dir (const char* path, MuMaildirWalkMsgCallback msg_cb, dir = opendir (path); if (G_UNLIKELY(!dir)) { - g_warning ("failed to open %s: %s", path, strerror(errno)); - return MU_ERROR; + g_warning ("%s: ignoring %s: %s", path, + __FUNCTION__, strerror(errno)); + return MU_OK; } if (dir_cb) { @@ -433,17 +467,16 @@ process_dir (const char* path, MuMaildirWalkMsgCallback msg_cb, } } - /* we sort the inodes, which makes file-access much faster on - some filesystems, such as ext3fs */ + /* 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) stats if the FS does not support + * entry->d_type + */ lst = NULL; - while ((entry = readdir (dir))) { - - /* handle FSs that don't support entry->d_type */ - if (entry->d_type == DT_UNKNOWN) - set_dtype (path, entry); - + while ((entry = readdir_with_stat_fallback (dir, path))) lst = g_list_prepend (lst, dirent_copy(entry)); - } + #if HAVE_STRUCT_DIRENT_D_INO c = lst = g_list_sort (lst, (GCompareFunc)dirent_cmp); #endif /*HAVE_STRUCT_DIRENT_D_INO*/ @@ -506,37 +539,35 @@ clear_links (const gchar* dirname, DIR *dir) gboolean rv; rv = TRUE; - while ((entry = readdir (dir)) && rv) { + while ((entry = readdir_with_stat_fallback (dir, dirname))) { - char fullpath[4096]; - size_t len; + const char *fp; + char *fullpath; - /* ignore dot thingies */ - if (entry->d_name && entry->d_name[0] == '.') + /* ignore empty, dot thingies */ + if (!entry->d_name || entry->d_name[0] == '.') continue; - - /* handle FSs that don't support entry->d_type */ - if (entry->d_type == DT_UNKNOWN) - set_dtype (dirname, entry); /* ignore non-links / non-dirs */ if (entry->d_type != DT_LNK && entry->d_type != DT_DIR) continue; - len = snprintf (fullpath, sizeof(fullpath), "%s%c%s", - dirname, G_DIR_SEPARATOR, entry->d_name); - if (len == sizeof(fullpath)) { - g_warning ("name is too long: %s", fullpath); - continue; - } - - if (entry->d_type == DT_LNK && unlink (fullpath) != 0) { - g_warning ("error unlinking %s: %s", - fullpath, strerror(errno)); - rv = FALSE; - } else /* == DT_DIR, see check before*/ + /* 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); + fullpath = g_newa (char, strlen(fp) + 1); + strcpy (fullpath, fp); + + if (entry->d_type == DT_LNK) { + if (unlink (fullpath) != 0) { + g_warning ("error unlinking %s: %s", + fullpath, strerror(errno)); + rv = FALSE; + } + } else /* DT_DIR, see check before*/ rv = mu_maildir_clear_links (fullpath); } + return rv; }