Return-Path: X-Original-To: notmuch@notmuchmail.org Delivered-To: notmuch@notmuchmail.org Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id 460FC431FB6 for ; Mon, 7 May 2012 14:08:39 -0700 (PDT) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: -0.699 X-Spam-Level: X-Spam-Status: No, score=-0.699 tagged_above=-999 required=5 tests=[HTML_MESSAGE=0.001, RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled Received: from olra.theworths.org ([127.0.0.1]) by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id v9+Gi4YCfqE6 for ; Mon, 7 May 2012 14:08:37 -0700 (PDT) Received: from mail-pb0-f53.google.com (mail-pb0-f53.google.com [209.85.160.53]) (using TLSv1 with cipher RC4-SHA (128/128 bits)) (No client certificate requested) by olra.theworths.org (Postfix) with ESMTPS id AF375431FAE for ; Mon, 7 May 2012 14:08:37 -0700 (PDT) Received: by pbbrr13 with SMTP id rr13so8240660pbb.26 for ; Mon, 07 May 2012 14:08:35 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type:x-gm-message-state; bh=vmHcNGrU4I/7z3kpCwJhVirRk0Hap1yMXRTbQFRFyhE=; b=DPr1y4CbTOPTtEvpeHD99HPfPhoTveicyQjj6Bg4OkOBvUowBKdOQMBa02qjd4z07R R4mU/CesDgYezoLZgg9E5u0IEnC1GIqMX6b7L6mRzGLZrLtfyRixYs3F17RzlQtwoBNc 1V1eH7WMdOGHDjJq6GJoIjoGSpJETl6eoac5umml1eiWdswcWZvNkpDdmSVlZUChb5Zo /3pcTk00NO0NoqJLfeIOVU7agItvYeJ87AgAbWJShTP+q/Brz643VLNre3gPcYFn23CS rQqdzTH8fh7L/w+pgMRfZHmSgPGfLc/2C+rRv3DvUCT4mSZirmGaq0zD2MbXaZMJOphx eakQ== MIME-Version: 1.0 Received: by 10.68.236.5 with SMTP id uq5mr410171pbc.165.1336424915761; Mon, 07 May 2012 14:08:35 -0700 (PDT) Received: by 10.68.52.33 with HTTP; Mon, 7 May 2012 14:08:35 -0700 (PDT) Received: by 10.68.52.33 with HTTP; Mon, 7 May 2012 14:08:35 -0700 (PDT) In-Reply-To: <1336414186-15293-3-git-send-email-amdragon@mit.edu> References: <1336414186-15293-1-git-send-email-amdragon@mit.edu> <1336414186-15293-3-git-send-email-amdragon@mit.edu> Date: Tue, 8 May 2012 00:08:35 +0300 Message-ID: Subject: Re: [PATCH 2/2] new: Centralize file type stat-ing logic From: Jani Nikula To: Austin Clements Content-Type: multipart/alternative; boundary=047d7b33c9c65f7b8204bf78ac10 X-Gm-Message-State: ALoCoQmhKRKJHFxNKNd4Q3OgSi6Wk0CGmXs1VXfUKMYwCjTunscnVZ4zVse3K3U19uflXQ4VgCwf Cc: Notmuch Mail , Vladimir Marek X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 07 May 2012 21:08:39 -0000 --047d7b33c9c65f7b8204bf78ac10 Content-Type: text/plain; charset=UTF-8 On May 7, 2012 9:10 PM, "Austin Clements" wrote: > > This moves our logic to get a file's type into one function. This has > several benefits: we can support OSes and file systems that do not > provide dirent.d_type or always return DT_UNKNOWN, complex > symlink-handling logic has been replaced by a simple stat fall-through > in one place, and the error message for un-stat-able file is more > accurate (previously, the error always mentioned directories, even > though a broken symlink is not a directory). Please find some quick drive-by-review below. J. > --- > notmuch-new.c | 99 ++++++++++++++++++++++++++++++++++----------------------- > test/new | 2 +- > 2 files changed, 60 insertions(+), 41 deletions(-) > > diff --git a/notmuch-new.c b/notmuch-new.c > index cb720cc..cf2580e 100644 > --- a/notmuch-new.c > +++ b/notmuch-new.c > @@ -154,6 +154,44 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) > return strcmp ((*a)->d_name, (*b)->d_name); > } > > +/* Return the type of a directory entry relative to path as a stat(2) > + * mode. Like stat, this follows symlinks. Returns -1 and sets errno > + * if the file's type cannot be determined (which includes dangling > + * symlinks). > + */ > +static int > +dirent_type (const char *path, const struct dirent *entry) > +{ > + struct stat statbuf; > + char *abspath; > + int err; > + > +#ifdef _DIRENT_HAVE_D_TYPE > + /* Mapping from d_type to stat mode_t. We omit DT_LNK so that > + * we'll fall through to stat and get the real file type. */ > + static const mode_t modes[] = { > + [DT_BLK] = S_IFBLK, > + [DT_CHR] = S_IFCHR, > + [DT_DIR] = S_IFDIR, > + [DT_FIFO] = S_IFIFO, > + [DT_REG] = S_IFREG, > + [DT_SOCK] = S_IFSOCK > + }; > + if (entry->d_type < sizeof(modes)/sizeof(modes[0]) && ARRAY_SIZE() > + modes[entry->d_type]) > + return modes[entry->d_type]; > +#endif > + > + abspath = talloc_asprintf (NULL, "%s/%s", path, entry->d_name); > + if (!abspath) > + return -1; Does talloc set errno in this case? I suspect not. > + err = stat(abspath, &statbuf); > + talloc_free (abspath); This likely breaks your promise about errno. You can't trust talloc_free() not calling some function that sets errno. > + if (err < 0) > + return -1; > + return statbuf.st_mode & S_IFMT; > +} > + > /* Test if the directory looks like a Maildir directory. > * > * Search through the array of directory entries to see if we can find all > @@ -162,12 +200,12 @@ dirent_sort_strcmp_name (const struct dirent **a, const struct dirent **b) > * Return 1 if the directory looks like a Maildir and 0 otherwise. > */ > static int > -_entries_resemble_maildir (struct dirent **entries, int count) > +_entries_resemble_maildir (const char *path, struct dirent **entries, int count) > { > int i, found = 0; > > for (i = 0; i < count; i++) { > - if (entries[i]->d_type != DT_DIR && entries[i]->d_type != DT_UNKNOWN) > + if (dirent_type (path, entries[i]) != S_IFDIR) > continue; > > if (strcmp(entries[i]->d_name, "new") == 0 || > @@ -250,7 +288,7 @@ add_files_recursive (notmuch_database_t *notmuch, > notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS; > notmuch_message_t *message = NULL; > struct dirent **fs_entries = NULL; > - int i, num_fs_entries; > + int i, num_fs_entries, entry_type; > notmuch_directory_t *directory; > notmuch_filenames_t *db_files = NULL; > notmuch_filenames_t *db_subdirs = NULL; > @@ -317,7 +355,7 @@ add_files_recursive (notmuch_database_t *notmuch, > } > > /* Pass 1: Recurse into all sub-directories. */ > - is_maildir = _entries_resemble_maildir (fs_entries, num_fs_entries); > + is_maildir = _entries_resemble_maildir (path, fs_entries, num_fs_entries); > > for (i = 0; i < num_fs_entries; i++) { > if (interrupted) > @@ -325,17 +363,16 @@ add_files_recursive (notmuch_database_t *notmuch, > > entry = fs_entries[i]; > > - /* We only want to descend into directories. > - * But symlinks can be to directories too, of course. > - * > - * And if the filesystem doesn't tell us the file type in the > - * scandir results, then it might be a directory (and if not, > - * then we'll stat and return immediately in the next level of > - * recursion). */ > - if (entry->d_type != DT_DIR && > - entry->d_type != DT_LNK && > - entry->d_type != DT_UNKNOWN) > - { > + /* We only want to descend into directories (and symlinks to > + * directories). */ > + entry_type = dirent_type (path, entry); > + if (entry_type == -1) { > + /* Be pessimistic, e.g. so we don't loose lots of mail s/loose/lose/ ? > + * just because a user broke a symlink. */ > + fprintf (stderr, "Error reading file %s/%s: %s\n", > + path, entry->d_name, strerror (errno)); You can't trust errno here, as explained above. > + return NOTMUCH_STATUS_FILE_ERROR; > + } else if (entry_type != S_IFDIR) { > continue; > } > > @@ -425,31 +462,13 @@ add_files_recursive (notmuch_database_t *notmuch, > notmuch_filenames_move_to_next (db_subdirs); > } > > - /* If we're looking at a symlink, we only want to add it if it > - * links to a regular file, (and not to a directory, say). > - * > - * Similarly, if the file is of unknown type (due to filesystem > - * limitations), then we also need to look closer. > - * > - * In either case, a stat does the trick. > - */ > - if (entry->d_type == DT_LNK || entry->d_type == DT_UNKNOWN) { > - int err; > - > - next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name); > - err = stat (next, &st); > - talloc_free (next); > - next = NULL; > - > - /* Don't emit an error for a link pointing nowhere, since > - * the directory-traversal pass will have already done > - * that. */ > - if (err) > - continue; > - > - if (! S_ISREG (st.st_mode)) > - continue; > - } else if (entry->d_type != DT_REG) { > + /* Only add regular files (and symlinks to regular files). */ > + entry_type = dirent_type (path, entry); > + if (entry_type == -1) { > + fprintf (stderr, "Error reading file %s/%s: %s\n", > + path, entry->d_name, strerror (errno)); Ditto. > + return NOTMUCH_STATUS_FILE_ERROR; > + } else if (entry_type != S_IFREG) { > continue; > } > > diff --git a/test/new b/test/new > index 26253db..e3900f5 100755 > --- a/test/new > +++ b/test/new > @@ -140,7 +140,7 @@ test_begin_subtest "Broken symlink aborts" > ln -s does-not-exist "${MAIL_DIR}/broken" > output=$(NOTMUCH_NEW 2>&1) > test_expect_equal "$output" \ > -"Error reading directory /run/shm/nm/tmp.new/mail/broken: No such file or directory > +"Error reading file /run/shm/nm/tmp.new/mail/broken: No such file or directory > Note: A fatal error was encountered: Something went wrong trying to read or write a file > No new mail." > rm "${MAIL_DIR}/broken" > -- > 1.7.10 > > _______________________________________________ > notmuch mailing list > notmuch@notmuchmail.org > http://notmuchmail.org/mailman/listinfo/notmuch --047d7b33c9c65f7b8204bf78ac10 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable


On May 7, 2012 9:10 PM, "Austin Clements" <amdragon@mit.edu> wrote:
>
> This moves our logic to get a file's type into one function. =C2= =A0This has
> several benefits: we can support OSes and file systems that do not
> provide dirent.d_type or always return DT_UNKNOWN, complex
> symlink-handling logic has been replaced by a simple stat fall-through=
> in one place, and the error message for un-stat-able file is more
> accurate (previously, the error always mentioned directories, even
> though a broken symlink is not a directory).

Please find some quick drive-by-review below.

J.

> ---
> =C2=A0notmuch-new.c | =C2=A0 99 ++++++++++++++++++++++++++++++++++----= -------------------
> =C2=A0test/new =C2=A0 =C2=A0 =C2=A0| =C2=A0 =C2=A02 +-
> =C2=A02 files changed, 60 insertions(+), 41 deletions(-)
>
> diff --git a/notmuch-new.c b/notmuch-new.c
> index cb720cc..cf2580e 100644
> --- a/notmuch-new.c
> +++ b/notmuch-new.c
> @@ -154,6 +154,44 @@ dirent_sort_strcmp_name (const struct dirent **a,= const struct dirent **b)
> =C2=A0 =C2=A0 return strcmp ((*a)->d_name, (*b)->d_name);
> =C2=A0}
>
> +/* Return the type of a directory entry relative to path as a stat(2)=
> + * mode. =C2=A0Like stat, this follows symlinks. =C2=A0Returns -1 and= sets errno
> + * if the file's type cannot be determined (which includes dangli= ng
> + * symlinks).
> + */
> +static int
> +dirent_type (const char *path, const struct dirent *entry)
> +{
> + =C2=A0 =C2=A0struct stat statbuf;
> + =C2=A0 =C2=A0char *abspath;
> + =C2=A0 =C2=A0int err;
> +
> +#ifdef _DIRENT_HAVE_D_TYPE
> + =C2=A0 =C2=A0/* Mapping from d_type to stat mode_t. =C2=A0We omit DT= _LNK so that
> + =C2=A0 =C2=A0 * we'll fall through to stat and get the real file= type. */
> + =C2=A0 =C2=A0static const mode_t modes[] =3D {
> + =C2=A0 =C2=A0 =C2=A0 [DT_BLK] =C2=A0=3D S_IFBLK,
> + =C2=A0 =C2=A0 =C2=A0 [DT_CHR] =C2=A0=3D S_IFCHR,
> + =C2=A0 =C2=A0 =C2=A0 [DT_DIR] =C2=A0=3D S_IFDIR,
> + =C2=A0 =C2=A0 =C2=A0 [DT_FIFO] =3D S_IFIFO,
> + =C2=A0 =C2=A0 =C2=A0 [DT_REG] =C2=A0=3D S_IFREG,
> + =C2=A0 =C2=A0 =C2=A0 [DT_SOCK] =3D S_IFSOCK
> + =C2=A0 =C2=A0};
> + =C2=A0 =C2=A0if (entry->d_type < sizeof(modes)/sizeof(modes[0]= ) &&

ARRAY_SIZE()

> + =C2=A0 =C2=A0 =C2=A0 modes[entry->d_type])
> + =C2=A0 =C2=A0 =C2=A0 return modes[entry->d_type];
> +#endif
> +
> + =C2=A0 =C2=A0abspath =3D talloc_asprintf (NULL, "%s/%s", p= ath, entry->d_name);
> + =C2=A0 =C2=A0if (!abspath)
> + =C2=A0 =C2=A0 =C2=A0 return -1;

Does talloc set errno in this case? I suspect not.

> + =C2=A0 =C2=A0err =3D stat(abspath, &statbuf);
> + =C2=A0 =C2=A0talloc_free (abspath);

This likely breaks your promise about errno. You can't trust talloc_= free() not calling some function that sets errno.

> + =C2=A0 =C2=A0if (err < 0)
> + =C2=A0 =C2=A0 =C2=A0 return -1;
> + =C2=A0 =C2=A0return statbuf.st_mode & S_IFMT;
> +}
> +
> =C2=A0/* Test if the directory looks like a Maildir directory.
> =C2=A0*
> =C2=A0* Search through the array of directory entries to see if we can= find all
> @@ -162,12 +200,12 @@ dirent_sort_strcmp_name (const struct dirent **a= , const struct dirent **b)
> =C2=A0* Return 1 if the directory looks like a Maildir and 0 otherwise= .
> =C2=A0*/
> =C2=A0static int
> -_entries_resemble_maildir (struct dirent **entries, int count)
> +_entries_resemble_maildir (const char *path, struct dirent **entries,= int count)
> =C2=A0{
> =C2=A0 =C2=A0 int i, found =3D 0;
>
> =C2=A0 =C2=A0 for (i =3D 0; i < count; i++) {
> - =C2=A0 =C2=A0 =C2=A0 if (entries[i]->d_type !=3D DT_DIR &&= ; entries[i]->d_type !=3D DT_UNKNOWN)
> + =C2=A0 =C2=A0 =C2=A0 if (dirent_type (path, entries[i]) !=3D S_IFDIR= )
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0continue;
>
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (strcmp(entries[i]->d_name, "new= ") =3D=3D 0 ||
> @@ -250,7 +288,7 @@ add_files_recursive (notmuch_database_t *notmuch,<= br> > =C2=A0 =C2=A0 notmuch_status_t status, ret =3D NOTMUCH_STATUS_SUCCESS;=
> =C2=A0 =C2=A0 notmuch_message_t *message =3D NULL;
> =C2=A0 =C2=A0 struct dirent **fs_entries =3D NULL;
> - =C2=A0 =C2=A0int i, num_fs_entries;
> + =C2=A0 =C2=A0int i, num_fs_entries, entry_type;
> =C2=A0 =C2=A0 notmuch_directory_t *directory;
> =C2=A0 =C2=A0 notmuch_filenames_t *db_files =3D NULL;
> =C2=A0 =C2=A0 notmuch_filenames_t *db_subdirs =3D NULL;
> @@ -317,7 +355,7 @@ add_files_recursive (notmuch_database_t *notmuch,<= br> > =C2=A0 =C2=A0 }
>
> =C2=A0 =C2=A0 /* Pass 1: Recurse into all sub-directories. */
> - =C2=A0 =C2=A0is_maildir =3D _entries_resemble_maildir (fs_entries, n= um_fs_entries);
> + =C2=A0 =C2=A0is_maildir =3D _entries_resemble_maildir (path, fs_entr= ies, num_fs_entries);
>
> =C2=A0 =C2=A0 for (i =3D 0; i < num_fs_entries; i++) {
> =C2=A0 =C2=A0 =C2=A0 =C2=A0if (interrupted)
> @@ -325,17 +363,16 @@ add_files_recursive (notmuch_database_t *notmuch= ,
>
> =C2=A0 =C2=A0 =C2=A0 =C2=A0entry =3D fs_entries[i];
>
> - =C2=A0 =C2=A0 =C2=A0 /* We only want to descend into directories. > - =C2=A0 =C2=A0 =C2=A0 =C2=A0* But symlinks can be to directories too,= of course.
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0*
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0* And if the filesystem doesn't tell = us the file type in the
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0* scandir results, then it might be a dir= ectory (and if not,
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0* then we'll stat and return immediat= ely in the next level of
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0* recursion). */
> - =C2=A0 =C2=A0 =C2=A0 if (entry->d_type !=3D DT_DIR &&
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 entry->d_type !=3D DT_LNK &= ;&
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 entry->d_type !=3D DT_UNKNOWN)=
> - =C2=A0 =C2=A0 =C2=A0 {
> + =C2=A0 =C2=A0 =C2=A0 /* We only want to descend into directories (an= d symlinks to
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0* directories). */
> + =C2=A0 =C2=A0 =C2=A0 entry_type =3D dirent_type (path, entry);
> + =C2=A0 =C2=A0 =C2=A0 if (entry_type =3D=3D -1) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Be pessimistic, e.g. so we don= 't loose lots of mail

s/loose/lose/ ?

> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* just because a user br= oke a symlink. */
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fprintf (stderr, "Error read= ing file %s/%s: %s\n",
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0path, entry->d_name, strerror (errno));

You can't trust errno here, as explained above.

> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return NOTMUCH_STATUS_FILE_ERR= OR;
> + =C2=A0 =C2=A0 =C2=A0 } else if (entry_type !=3D S_IFDIR) {
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0continue;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0}
>
> @@ -425,31 +462,13 @@ add_files_recursive (notmuch_database_t *notmuch= ,
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0notmuch_filenames_move_to_nex= t (db_subdirs);
> =C2=A0 =C2=A0 =C2=A0 =C2=A0}
>
> - =C2=A0 =C2=A0 =C2=A0 /* If we're looking at a symlink, we only w= ant to add it if it
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0* links to a regular file, (and not to a = directory, say).
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0*
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0* Similarly, if the file is of unknown ty= pe (due to filesystem
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0* limitations), then we also need to look= closer.
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0*
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0* In either case, a stat does the trick.<= br> > - =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
> - =C2=A0 =C2=A0 =C2=A0 if (entry->d_type =3D=3D DT_LNK || entry->= ;d_type =3D=3D DT_UNKNOWN) {
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 int err;
> -
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 next =3D talloc_asprintf (notmuch= , "%s/%s", path, entry->d_name);
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 err =3D stat (next, &st);
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 talloc_free (next);
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 next =3D NULL;
> -
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 /* Don't emit an error for a = link pointing nowhere, since
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* the directory-traversal p= ass will have already done
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0* that. */
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (err)
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 continue;
> -
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (! S_ISREG (st.st_mode))
> - =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 continue;
> - =C2=A0 =C2=A0 =C2=A0 } else if (entry->d_type !=3D DT_REG) {
> + =C2=A0 =C2=A0 =C2=A0 /* Only add regular files (and symlinks to regu= lar files). */
> + =C2=A0 =C2=A0 =C2=A0 entry_type =3D dirent_type (path, entry);
> + =C2=A0 =C2=A0 =C2=A0 if (entry_type =3D=3D -1) {
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fprintf (stderr, "Error read= ing file %s/%s: %s\n",
> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0path, entry->d_name, strerror (errno));

Ditto.

> + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return NOTMUCH_STATUS_FILE_ERR= OR;
> + =C2=A0 =C2=A0 =C2=A0 } else if (entry_type !=3D S_IFREG) {
> =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0continue;
> =C2=A0 =C2=A0 =C2=A0 =C2=A0}
>
> diff --git a/test/new b/test/new
> index 26253db..e3900f5 100755
> --- a/test/new
> +++ b/test/new
> @@ -140,7 +140,7 @@ test_begin_subtest "Broken symlink aborts&quo= t;
> =C2=A0ln -s does-not-exist "${MAIL_DIR}/broken"
> =C2=A0output=3D$(NOTMUCH_NEW 2>&1)
> =C2=A0test_expect_equal "$output" \
> -"Error reading directory /run/shm/nm/tmp.new/mail/broken: No suc= h file or directory
> +"Error reading file /run/shm/nm/tmp.new/mail/broken: No such fil= e or directory
> =C2=A0Note: A fatal error was encountered: Something went wrong trying= to read or write a file
> =C2=A0No new mail."
> =C2=A0rm "${MAIL_DIR}/broken"
> --
> 1.7.10
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org=
> http://not= muchmail.org/mailman/listinfo/notmuch

--047d7b33c9c65f7b8204bf78ac10--