zsh-workers
 help / color / mirror / Atom feed
* [PATCH] Allow globbing with unreadable parent directories
@ 2021-01-13  3:04 Devin Hussey
  2021-01-13 22:27 ` Bart Schaefer
  0 siblings, 1 reply; 16+ messages in thread
From: Devin Hussey @ 2021-01-13  3:04 UTC (permalink / raw)
  To: zsh-workers

POSIX specifies that when globbing, parent directories only have to be
searchable, not readable.

Previously, globbing using NO_CASE_GLOB would fail if any of the parent
directories in the path were not readable.

This was a major issue primarily affecting Termux, a Linux environment
for Android.

Termux's $HOME is "/data/data/com.termux/files/home", and the issue is
that "/data" and sometimes even "/" are not readable without system/root
permission.

This made every full path glob with NO_CASE_GLOB fail, breaking many
scripts such as the Prezto prompt.

Now, zsh will correctly glob even if the parent directory is not readable,
while respecting the searchable bit.

See:
 - https://github.com/sorin-ionescu/prezto/issues/1560
 - https://github.com/termux/termux-packages/issues/1894

Signed-off-by: Devin Hussey <husseydevin@gmail.com>
---
 Src/glob.c | 169 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 89 insertions(+), 80 deletions(-)

diff --git a/Src/glob.c b/Src/glob.c
index bee890caf..4f5c2cf8b 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -580,100 +580,109 @@ scanner(Complist q, int shortcircuit)
     } else {
 	/* Do pattern matching on current path section. */
 	char *fn = pathbuf[pathbufcwd] ? unmeta(pathbuf + pathbufcwd) : ".";
-	int dirs = !!q->next;
-	DIR *lock = opendir(fn);
 	char *subdirs = NULL;
 	int subdirlen = 0;

-	if (lock == NULL)
+	/* First check for search permission. */
+	if (access(fn, X_OK) != 0)
 	    return;
-	while ((fn = zreaddir(lock, 1)) && !errflag) {
-	    /* prefix and suffix are zle trickery */
-	    if (!dirs && !colonmod &&
-		((glob_pre && !strpfx(glob_pre, fn))
-		 || (glob_suf && !strsfx(glob_suf, fn))))
-		continue;
-	    errsfound = errssofar;
-	    if (pattry(p, fn)) {
-		/* if this name matches the pattern... */
-		if (pbcwdsav == pathbufcwd &&
-		    strlen(fn) + pathpos - pathbufcwd >= PATH_MAX) {
-		    int err;
-
-		    DPUTS(pathpos == pathbufcwd,
-			  "BUG: filename longer than PATH_MAX");
-		    err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0);
-		    if (err == -1)
-			break;
-		    if (err) {
-			zerr("current directory lost during glob");
-			break;
+
+	/* Then, if we have read permission, try to open the directory. */
+	if (access(fn, R_OK) == 0) {
+	    int dirs = !!q->next;
+	    DIR *lock = opendir(fn);
+
+	    if (lock == NULL)
+		return;
+
+	    while ((fn = zreaddir(lock, 1)) && !errflag) {
+		/* prefix and suffix are zle trickery */
+		if (!dirs && !colonmod &&
+		    ((glob_pre && !strpfx(glob_pre, fn))
+		     || (glob_suf && !strsfx(glob_suf, fn))))
+		    continue;
+		errsfound = errssofar;
+		if (pattry(p, fn)) {
+		    /* if this name matches the pattern... */
+		    if (pbcwdsav == pathbufcwd &&
+			strlen(fn) + pathpos - pathbufcwd >= PATH_MAX) {
+			int err;
+
+			DPUTS(pathpos == pathbufcwd,
+			      "BUG: filename longer than PATH_MAX");
+			err = lchdir(unmeta(pathbuf + pathbufcwd), &ds, 0);
+			if (err == -1)
+			    break;
+			if (err) {
+			    zerr("current directory lost during glob");
+			    break;
+			}
+			pathbufcwd = pathpos;
 		    }
-		    pathbufcwd = pathpos;
-		}
-		if (dirs) {
-		    int l;
+		    if (dirs) {
+			int l;

-		    /*
-		     * If not the last component in the path:
-		     *
-		     * If we made an approximation in the new path segment,
-		     * then it is possible we made too many errors.  For
-		     * example, (ab)#(cb)# will match the directory abcb
-		     * with one error if allowed to, even though it can
-		     * match with none.  This will stop later parts of the
-		     * path matching, so we need to check by reducing the
-		     * maximum number of errors and seeing if the directory
-		     * still matches.  Luckily, this is not a terribly
-		     * common case, since complex patterns typically occur
-		     * in the last part of the path which is not affected
-		     * by this problem.
-		     */
-		    if (errsfound > errssofar) {
-			forceerrs = errsfound - 1;
-			while (forceerrs >= errssofar) {
-			    errsfound = errssofar;
-			    if (!pattry(p, fn))
-				break;
+			/*
+			 * If not the last component in the path:
+			 *
+			 * If we made an approximation in the new path segment,
+			 * then it is possible we made too many errors.  For
+			 * example, (ab)#(cb)# will match the directory abcb
+			 * with one error if allowed to, even though it can
+			 * match with none.  This will stop later parts of the
+			 * path matching, so we need to check by reducing the
+			 * maximum number of errors and seeing if the directory
+			 * still matches.  Luckily, this is not a terribly
+			 * common case, since complex patterns typically occur
+			 * in the last part of the path which is not affected
+			 * by this problem.
+			 */
+			if (errsfound > errssofar) {
 			    forceerrs = errsfound - 1;
+			    while (forceerrs >= errssofar) {
+				errsfound = errssofar;
+				if (!pattry(p, fn))
+				    break;
+				forceerrs = errsfound - 1;
+			    }
+			    errsfound = forceerrs + 1;
+			    forceerrs = -1;
 			}
-			errsfound = forceerrs + 1;
-			forceerrs = -1;
-		    }
-		    if (closure) {
-			/* if matching multiple directories */
-			struct stat buf;
-
-			if (statfullpath(fn, &buf, !q->follow)) {
-			    if (errno != ENOENT && errno != EINTR &&
-				errno != ENOTDIR && !errflag) {
-				zwarn("%e: %s", errno, fn);
+			if (closure) {
+			    /* if matching multiple directories */
+			    struct stat buf;
+
+			    if (statfullpath(fn, &buf, !q->follow)) {
+				if (errno != ENOENT && errno != EINTR &&
+				    errno != ENOTDIR && !errflag) {
+				    zwarn("%e: %s", errno, fn);
+				}
+				continue;
 			    }
-			    continue;
+			    if (!S_ISDIR(buf.st_mode))
+				continue;
+			}
+			l = strlen(fn) + 1;
+			subdirs = hrealloc(subdirs, subdirlen, subdirlen + l
+					   + sizeof(int));
+			strcpy(subdirs + subdirlen, fn);
+			subdirlen += l;
+			/* store the count of errors made so far, too */
+			memcpy(subdirs + subdirlen, (char *)&errsfound,
+			       sizeof(int));
+			subdirlen += sizeof(int);
+		    } else {
+			/* if the last filename component, just add it */
+			insert(fn, 1);
+			if (shortcircuit && shortcircuit == matchct) {
+			    closedir(lock);
+			    return;
 			}
-			if (!S_ISDIR(buf.st_mode))
-			    continue;
-		    }
-		    l = strlen(fn) + 1;
-		    subdirs = hrealloc(subdirs, subdirlen, subdirlen + l
-				       + sizeof(int));
-		    strcpy(subdirs + subdirlen, fn);
-		    subdirlen += l;
-		    /* store the count of errors made so far, too */
-		    memcpy(subdirs + subdirlen, (char *)&errsfound,
-			   sizeof(int));
-		    subdirlen += sizeof(int);
-		} else {
-		    /* if the last filename component, just add it */
-		    insert(fn, 1);
-		    if (shortcircuit && shortcircuit == matchct) {
-			closedir(lock);
-			return;
 		    }
 		}
 	    }
+	    closedir(lock);
 	}
-	closedir(lock);
 	if (subdirs) {
 	    int oppos = pathpos;

-- 
2.30.0


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Allow globbing with unreadable parent directories
  2021-01-13  3:04 [PATCH] Allow globbing with unreadable parent directories Devin Hussey
@ 2021-01-13 22:27 ` Bart Schaefer
  2021-01-14  0:27   ` Devin Hussey
  0 siblings, 1 reply; 16+ messages in thread
From: Bart Schaefer @ 2021-01-13 22:27 UTC (permalink / raw)
  To: Devin Hussey; +Cc: zsh-workers

On Tue, Jan 12, 2021 at 7:04 PM Devin Hussey <husseydevin@gmail.com> wrote:
>
> POSIX specifies that when globbing, parent directories only have to be
> searchable, not readable.
>
> +       /* First check for search permission. */
> +       if (access(fn, X_OK) != 0)
>             return;

I don't think this is correct.  Even if it is strictly correct per
POSIX (which would seem strange to me), it should not be applied in
zsh native mode (see my previous email about setopts).

% mkdir /tmp/adirectory
% touch /tmp/adirectory/somefile
% chmod a-x /tmp/adirectory
% echo /tmp/adirectory/*
/tmp/adirectory/somefile
% echo /tmp/adirectory/*(.)
zsh: no match

Lack of search permission only means that you can't tell what kind of
file "somefile" is (you can't read its inode data, e.g., stat() it),
not that you can't see the name itself.

Compare "ls /tmp/adirectory" and "ls -l /tmp/adirectory" for a related example.

> +       /* Then, if we have read permission, try to open the directory. */
> +       if (access(fn, R_OK) == 0) {
> +           int dirs = !!q->next;
> +           DIR *lock = opendir(fn);

The access call here is redundant; opendir(fn) will fail if there is
not read permission.  Why do you believe the extra test is needed?

I think the rest is just re-indentation.  Did I miss an "else" clause
for the R_OK ?

Can you provide a specific test case that shows the difference in
behavior with and without this patch?  As far as I can tell, the patch
would only cause globbing to fail in more cases, not succeed where it
previously did not.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Allow globbing with unreadable parent directories
  2021-01-13 22:27 ` Bart Schaefer
@ 2021-01-14  0:27   ` Devin Hussey
  2021-01-14  1:32     ` Lawrence Velázquez
  2021-01-14  4:04     ` Bart Schaefer
  0 siblings, 2 replies; 16+ messages in thread
From: Devin Hussey @ 2021-01-14  0:27 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: zsh-workers

[-- Attachment #1: Type: text/plain, Size: 2592 bytes --]

On Wed, Jan 13, 2021, 5:28 PM Bart Schaefer <schaefer@brasslantern.com>
wrote:

> On Tue, Jan 12, 2021 at 7:04 PM Devin Hussey <husseydevin@gmail.com>
> wrote:
> >
> > POSIX specifies that when globbing, parent directories only have to be
> > searchable, not readable.
> >
> > +       /* First check for search permission. */
> > +       if (access(fn, X_OK) != 0)
> >             return;
>
> I don't think this is correct.  Even if it is strictly correct per
> POSIX (which would seem strange to me), it should not be applied in
> zsh native mode (see my previous email about setopts).
>
> % mkdir /tmp/adirectory
> % touch /tmp/adirectory/somefile
> % chmod a-x /tmp/adirectory
> % echo /tmp/adirectory/*
> /tmp/adirectory/somefile
> % echo /tmp/adirectory/*(.)
> zsh: no match
>
> Lack of search permission only means that you can't tell what kind of
> file "somefile" is (you can't read its inode data, e.g., stat() it),
> not that you can't see the name itself.
>

This matches the behavior of the "pure" globber, POSIX, and literally every
other shell.

If we made it accept it unconditionally like my original solution, we would
end up with the *opposite* issue, where CASE_GLOB would fail on files where
NO_CASE_GLOB succeeds.

Case insensitivity should not change the output due to file permissions.

> +       /* Then, if we have read permission, try to open the directory. */
> > +       if (access(fn, R_OK) == 0) {
> > +           int dirs = !!q->next;
> > +           DIR *lock = opendir(fn);
>
> The access call here is redundant; opendir(fn) will fail if there is
> not read permission.  Why do you believe the extra test is needed?


opendir(fn) will also fail if the "folder" is a file.

However, you may be right, if the path is a file, the next access() will
definitely fail, so I could remove that and only return if we can't
access(fn, X_OK).

I think the rest is just re-indentation.  Did I miss an "else" clause
> for the R_OK ?
>

Yes it is just reindentation.

There is no else clause, it goes to the next section of the path if the
current directory is unreadable.


> Can you provide a specific test case that shows the difference in
> behavior with and without this patch?


I think I can throw something together, yes.

As far as I can tell, the patch

would only cause globbing to fail in more cases, not succeed where it
> previously did not.
>

No, that is definitely not the case.

opendir() would fail if either R_OK or X_OK was false, causing unreadable
folders to be a false negative.

This is allowing certain combinations where opendir() would fail.

>

[-- Attachment #2: Type: text/html, Size: 4979 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Allow globbing with unreadable parent directories
  2021-01-14  0:27   ` Devin Hussey
@ 2021-01-14  1:32     ` Lawrence Velázquez
  2021-01-14  2:22       ` Devin Hussey
  2021-01-17 17:22       ` Daniel Shahaf
  2021-01-14  4:04     ` Bart Schaefer
  1 sibling, 2 replies; 16+ messages in thread
From: Lawrence Velázquez @ 2021-01-14  1:32 UTC (permalink / raw)
  To: Devin Hussey; +Cc: Bart Schaefer, zsh-workers

> On Jan 13, 2021, at 7:27 PM, Devin Hussey <husseydevin@gmail.com> wrote:
> 
>> On Wed, Jan 13, 2021, 5:28 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>> 
>> As far as I can tell, the patch
>> would only cause globbing to fail in more cases, not succeed where it
>> previously did not.
> 
> No, that is definitely not the case.

But your patch *does* cause globbing to fail in cases for which it
currently doesn't.

	% mkdir -p notsearchable/{dir,DIR}
	% touch notsearchable/{dir,DIR}/file
	% chmod 600 notsearchable
	% zsh -fc 'echo notsearchable/*'
	notsearchable/DIR notsearchable/dir
	% ./zsh-patched -fc 'echo notsearchable/*'
	zsh:1: no matches found: notsearchable/*

> opendir() would fail if either R_OK or X_OK was false, causing unreadable folders to be a false negative. 
> 
> This is allowing certain combinations where opendir() would fail.

If I'm understanding your intention correctly, you would like
"literal" segments (e.g., lacking special characters) of
case-insensitive glob patterns to match literally if the "parent"
lacks read permissions. This doesn't seem to work, though. Am I
missing something?

	% mkdir -p notreadable/dir
	% touch notreadable/dir/file
	% chmod 300 notreadable
	% zsh -f +o CASE_GLOB -c 'echo notreadable/dir/*'          
	zsh:1: no matches found: notreadable/dir/*
	% ./zsh-patched -f +o CASE_GLOB -c 'echo notreadable/dir/*'
	zsh:1: no matches found: notreadable/dir/*

vq

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Allow globbing with unreadable parent directories
  2021-01-14  1:32     ` Lawrence Velázquez
@ 2021-01-14  2:22       ` Devin Hussey
  2021-01-14  2:24         ` Devin Hussey
  2021-01-17 17:22       ` Daniel Shahaf
  1 sibling, 1 reply; 16+ messages in thread
From: Devin Hussey @ 2021-01-14  2:22 UTC (permalink / raw)
  To: Lawrence Velázquez; +Cc: Bart Schaefer, zsh-workers

[-- Attachment #1: Type: text/plain, Size: 2889 bytes --]

On Wed, Jan 13, 2021, 8:33 PM Lawrence Velázquez <vq@larryv.me> wrote:

> > On Jan 13, 2021, at 7:27 PM, Devin Hussey <husseydevin@gmail.com> wrote:
> >
> >> On Wed, Jan 13, 2021, 5:28 PM Bart Schaefer <schaefer@brasslantern.com>
> wrote:
> >>
> >> As far as I can tell, the patch
> >> would only cause globbing to fail in more cases, not succeed where it
> >> previously did not.
> >
> > No, that is definitely not the case.
>
> But your patch *does* cause globbing to fail in cases for which it
> currently doesn't.
>
>         % mkdir -p notsearchable/{dir,DIR}
>         % touch notsearchable/{dir,DIR}/file
>         % chmod 600 notsearchable
>         % zsh -fc 'echo notsearchable/*'
>         notsearchable/DIR notsearchable/dir
>         % ./zsh-patched -fc 'echo notsearchable/*'
>         zsh:1: no matches found: notsearchable/*
>

Huh. Yeah, that is a bug.

I don't yet understand enough of the logic.

> opendir() would fail if either R_OK or X_OK was false, causing unreadable
> folders to be a false negative.
> >
> > This is allowing certain combinations where opendir() would fail.
>
> If I'm understanding your intention correctly, you would like
> "literal" segments (e.g., lacking special characters) of
> case-insensitive glob patterns to match literally if the "parent"
> lacks read permissions. This doesn't seem to work, though. Am I
> missing something?
>

That is correct.

To reiterate the problem:

 - Case-sensitive (and POSIX) globs seemingly only fail if the parent
directory is unsearchable or if the target directory is unreadable, as it
only opens the globbed directory.

 - The case-insensitive ("impure") glob will fail if ANY parent is
unreadable or unsearchable. This is because unlike the normal glob, the
impure glob will try to recursively opendir() from "/".

 - If I ignore EACCES entirely like in my first patch, case-insensitive
globs would succeed if the parent is unsearchable, causing the opposite bug
where case-sensitive globs fail.

 - The current patch does not handle the target directory being readable
but not searchable.

I admit that I know very little about the Zsh source tree, so I have tunnel
vision when it comes to the program logic. It is likely I who is missing
something obvious.

The only reason that I found the cause of this bug was via strace.

        % mkdir -p notreadable/dir
>         % touch notreadable/dir/file
>         % chmod 300 notreadable
>         % zsh -f +o CASE_GLOB -c 'echo notreadable/dir/*'
>         zsh:1: no matches found: notreadable/dir/*
>         % ./zsh-patched -f +o CASE_GLOB -c 'echo notreadable/dir/*'
>         zsh:1: no matches found: notreadable/dir/*


I didn't do anything that would directly affect CASE_GLOB, as CASE_GLOB
uses the "pure" codepath. It is expected that the behavior would not change.

[-- Attachment #2: Type: text/html, Size: 4529 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Allow globbing with unreadable parent directories
  2021-01-14  2:22       ` Devin Hussey
@ 2021-01-14  2:24         ` Devin Hussey
  0 siblings, 0 replies; 16+ messages in thread
From: Devin Hussey @ 2021-01-14  2:24 UTC (permalink / raw)
  To: Lawrence Velázquez; +Cc: Bart Schaefer, zsh-workers

[-- Attachment #1: Type: text/plain, Size: 3172 bytes --]

Nevermind, I'm dumb on that last part, I forgot that the shell option
syntax is backwards.

On Wed, Jan 13, 2021, 9:22 PM Devin Hussey <husseydevin@gmail.com> wrote:

>
> On Wed, Jan 13, 2021, 8:33 PM Lawrence Velázquez <vq@larryv.me> wrote:
>
>> > On Jan 13, 2021, at 7:27 PM, Devin Hussey <husseydevin@gmail.com>
>> wrote:
>> >
>> >> On Wed, Jan 13, 2021, 5:28 PM Bart Schaefer <schaefer@brasslantern.com>
>> wrote:
>> >>
>> >> As far as I can tell, the patch
>> >> would only cause globbing to fail in more cases, not succeed where it
>> >> previously did not.
>> >
>> > No, that is definitely not the case.
>>
>> But your patch *does* cause globbing to fail in cases for which it
>> currently doesn't.
>>
>>         % mkdir -p notsearchable/{dir,DIR}
>>         % touch notsearchable/{dir,DIR}/file
>>         % chmod 600 notsearchable
>>         % zsh -fc 'echo notsearchable/*'
>>         notsearchable/DIR notsearchable/dir
>>         % ./zsh-patched -fc 'echo notsearchable/*'
>>         zsh:1: no matches found: notsearchable/*
>>
>
> Huh. Yeah, that is a bug.
>
> I don't yet understand enough of the logic.
>
> > opendir() would fail if either R_OK or X_OK was false, causing
>> unreadable folders to be a false negative.
>> >
>> > This is allowing certain combinations where opendir() would fail.
>>
>> If I'm understanding your intention correctly, you would like
>> "literal" segments (e.g., lacking special characters) of
>> case-insensitive glob patterns to match literally if the "parent"
>> lacks read permissions. This doesn't seem to work, though. Am I
>> missing something?
>>
>
> That is correct.
>
> To reiterate the problem:
>
>  - Case-sensitive (and POSIX) globs seemingly only fail if the parent
> directory is unsearchable or if the target directory is unreadable, as it
> only opens the globbed directory.
>
>  - The case-insensitive ("impure") glob will fail if ANY parent is
> unreadable or unsearchable. This is because unlike the normal glob, the
> impure glob will try to recursively opendir() from "/".
>
>  - If I ignore EACCES entirely like in my first patch, case-insensitive
> globs would succeed if the parent is unsearchable, causing the opposite bug
> where case-sensitive globs fail.
>
>  - The current patch does not handle the target directory being readable
> but not searchable.
>
> I admit that I know very little about the Zsh source tree, so I have
> tunnel vision when it comes to the program logic. It is likely I who is
> missing something obvious.
>
> The only reason that I found the cause of this bug was via strace.
>
>         % mkdir -p notreadable/dir
>>         % touch notreadable/dir/file
>>         % chmod 300 notreadable
>>         % zsh -f +o CASE_GLOB -c 'echo notreadable/dir/*'
>>         zsh:1: no matches found: notreadable/dir/*
>>         % ./zsh-patched -f +o CASE_GLOB -c 'echo notreadable/dir/*'
>>         zsh:1: no matches found: notreadable/dir/*
>
>
> I didn't do anything that would directly affect CASE_GLOB, as CASE_GLOB
> uses the "pure" codepath. It is expected that the behavior would not change.
>

[-- Attachment #2: Type: text/html, Size: 4996 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Allow globbing with unreadable parent directories
  2021-01-14  0:27   ` Devin Hussey
  2021-01-14  1:32     ` Lawrence Velázquez
@ 2021-01-14  4:04     ` Bart Schaefer
  2021-01-14  5:57       ` NO_CASE_GLOB and unreadable directories (Episode VI: A New Hope) Bart Schaefer
  1 sibling, 1 reply; 16+ messages in thread
From: Bart Schaefer @ 2021-01-14  4:04 UTC (permalink / raw)
  To: Devin Hussey; +Cc: zsh-workers

On Wed, Jan 13, 2021 at 4:28 PM Devin Hussey <husseydevin@gmail.com> wrote:
>
> On Wed, Jan 13, 2021, 5:28 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>>
>> On Tue, Jan 12, 2021 at 7:04 PM Devin Hussey <husseydevin@gmail.com> wrote:
>> >
>> > POSIX specifies that when globbing, parent directories only have to be
>> > searchable, not readable.
>>
>> Lack of search permission only means that you can't tell what kind of
>> file "somefile" is (you can't read its inode data, e.g., stat() it),
>> not that you can't see the name itself.
>
> This matches the behavior of the "pure" globber, POSIX, and literally every other shell.

I tried the same test cases with "bash" and it behaved like
(unpatched) zsh.  Again, can you show me a counter-example?

> Case insensitivity should not change the output due to file permissions.

If that were a property of the filesystem, as for example on MacOS,
then I agree with you.

It's less clear when the filesystem is case-sensitive.  Why should
(#i)file not be the same as [Ff][Ii][Ll][Ee] ?

> opendir(fn) will also fail if the "folder" is a file.

Sure, but so might access(R_OK) ... and access(X_OK) might succeed on a file.

>> As far as I can tell, the patch
>> would only cause globbing to fail in more cases, not succeed where it
>> previously did not.
>
> No, that is definitely not the case.
>
> opendir() would fail if either R_OK or X_OK was false, causing unreadable folders to be a false negative.

I don't think that's true.  opendir() is fine with only R_OK.  Again,
try "ls" on a directory having mode 444.

What you cannot do is "ls -l parent/child" when parent is not searchable.

The actual problem is that PAT_PURES is never true when NO_CASE_GLOB
is set.  That means scanner() always passes through the branch that
attempts zreaddir()+pattry(), which fails when there is an unreadable
directory.  I'm not immediately sure how to fix that without
introducing a bug/vulnerability where a filename could be constructed
such that it literally matches the internal representation of a glob
pattern.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* NO_CASE_GLOB and unreadable directories (Episode VI: A New Hope)
  2021-01-14  4:04     ` Bart Schaefer
@ 2021-01-14  5:57       ` Bart Schaefer
  2021-01-14 18:56         ` Bart Schaefer
  2021-01-25  0:52         ` Bart Schaefer
  0 siblings, 2 replies; 16+ messages in thread
From: Bart Schaefer @ 2021-01-14  5:57 UTC (permalink / raw)
  To: zsh-workers; +Cc: Devin Hussey

On Wed, Jan 13, 2021 at 8:04 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> On Wed, Jan 13, 2021 at 4:28 PM Devin Hussey <husseydevin@gmail.com> wrote:
> >
> > Case insensitivity should not change the output due to file permissions.
>
> [... unless (?)] the filesystem is case-sensitive.  Why should
> (#i)file not be the same as [Ff][Ii][Ll][Ee] ?

I'm still of this opinion, but I now think zsh is [conceptually]
applying (#i) in the wrong way.

> The actual problem is that PAT_PURES is never true when NO_CASE_GLOB
> is set.

Effectively, NO_CASE_GLOB turns the pure path
  /data/data/com.termux/files/home
(to stick with the original example) into the extendedglob pattern
  /(#i)data/(#i)data/(#i)com.termux/(#i)files/(#i)home
which means every component has to be compared with pattry() rather
than as a literal string.  That in turn means that every parent must
be readable.

It further means that
  anydir/*
becomes
  (#i)anydir/(#i)*
which could be construed as incorrect, because if there are two directories
  AnyDir/
  anydir/
that pattern will return everything in both of them, even though only
the lower-case version was specified.  (This actually mentioned in
another of the github threads to which Devin linked.)

What's wanted in Devin's case is for (#i) to apply exactly when there
are other pattern characters in the component, e.g.,
  (#I)anydir/(#i)*
which I believe is expressed by the following patch.

This is not yet suitable for folding into the master branch, because
it is a significant change to the current zsh behavior (and because
there's some #ifdef __CYGWIN__ in there that I can't test and am not
sure what to do with).  For example (in the zsh source tree):

unpatched% setopt nocaseglob
unpatched% echo src/module?
Src/Modules

Whereas:

patched% setopt nocaseglob
patched% echo src/module?
zsh: no matches found: src/module?
patched% echo Src/module?
Src/Modules

There are no tests of NO_CASE_GLOB in Test/E01options.ztst, but all
the tests of (#i) in D02glob still pass.

diff --git a/Src/pattern.c b/Src/pattern.c
index c7c2c8bea..9869db5ef 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -509,10 +509,7 @@ void
 patcompstart(void)
 {
     patcompcharsset();
-    if (isset(CASEGLOB))
-       patglobflags = 0;
-    else
-       patglobflags = GF_IGNCASE;
+    patglobflags = 0;
     if (isset(MULTIBYTE))
        patglobflags |= GF_MULTIBYTE;
 }
@@ -632,6 +629,12 @@ patcompile(char *exp, int inflags, char **endexp)
     p->patmlen = len;
     p->patnpar = patnpar-1;

+    if (!isset(CASEGLOB) && !(patflags & PAT_PURES)) {
+       /* Special __CYGWIN__ handling needed? */
+       p->globflags |= GF_IGNCASE;
+       p->globend |= GF_IGNCASE;
+    }
+
     if (!strp) {
        pscan = (Upat)(patout + startoff);


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: NO_CASE_GLOB and unreadable directories (Episode VI: A New Hope)
  2021-01-14  5:57       ` NO_CASE_GLOB and unreadable directories (Episode VI: A New Hope) Bart Schaefer
@ 2021-01-14 18:56         ` Bart Schaefer
  2021-01-25  0:52         ` Bart Schaefer
  1 sibling, 0 replies; 16+ messages in thread
From: Bart Schaefer @ 2021-01-14 18:56 UTC (permalink / raw)
  To: zsh-workers; +Cc: Devin Hussey

On Wed, Jan 13, 2021 at 9:57 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> This is not yet suitable for folding into the master branch, because
> it is a significant change to the current zsh behavior

Found another problem with it:  CASEGLOB was being applied to patterns
in other contexts.

Fixed patch below (gmail is almost certainly going to line-wrap this
one, but since so far it's for discussion only, I'm not terribly
concerned):

diff --git a/Src/pattern.c b/Src/pattern.c
index c7c2c8bea..3d282b533 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -509,10 +509,7 @@ void
 patcompstart(void)
 {
     patcompcharsset();
-    if (isset(CASEGLOB))
-       patglobflags = 0;
-    else
-       patglobflags = GF_IGNCASE;
+    patglobflags = 0;
     if (isset(MULTIBYTE))
        patglobflags |= GF_MULTIBYTE;
 }
@@ -632,6 +629,12 @@ patcompile(char *exp, int inflags, char **endexp)
     p->patmlen = len;
     p->patnpar = patnpar-1;

+    if ((patflags & PAT_FILE) && !isset(CASEGLOB) && !(patflags & PAT_PURES)) {
+       /* Special __CYGWIN__ handling needed? */
+       p->globflags |= GF_IGNCASE;
+       p->globend |= GF_IGNCASE;
+    }
+
     if (!strp) {
        pscan = (Upat)(patout + startoff);


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Allow globbing with unreadable parent directories
  2021-01-14  1:32     ` Lawrence Velázquez
  2021-01-14  2:22       ` Devin Hussey
@ 2021-01-17 17:22       ` Daniel Shahaf
  2021-01-17 18:02         ` Bart Schaefer
  1 sibling, 1 reply; 16+ messages in thread
From: Daniel Shahaf @ 2021-01-17 17:22 UTC (permalink / raw)
  To: Lawrence Velázquez; +Cc: Devin Hussey, zsh-workers

(Sorry I'm late to join the party, but:)

Lawrence Velázquez wrote on Wed, Jan 13, 2021 at 20:32:58 -0500:
> > On Jan 13, 2021, at 7:27 PM, Devin Hussey <husseydevin@gmail.com> wrote:
> > 
> >> On Wed, Jan 13, 2021, 5:28 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
> >> 
> >> As far as I can tell, the patch
> >> would only cause globbing to fail in more cases, not succeed where it
> >> previously did not.
> > 
> > No, that is definitely not the case.
> 
> But your patch *does* cause globbing to fail in cases for which it
> currently doesn't.
> 
> 	% mkdir -p notsearchable/{dir,DIR}
> 	% touch notsearchable/{dir,DIR}/file
> 	% chmod 600 notsearchable
> 	% zsh -fc 'echo notsearchable/*'
> 	notsearchable/DIR notsearchable/dir
> 	% ./zsh-patched -fc 'echo notsearchable/*'
> 	zsh:1: no matches found: notsearchable/*

Which unit test covers this?


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Allow globbing with unreadable parent directories
  2021-01-17 17:22       ` Daniel Shahaf
@ 2021-01-17 18:02         ` Bart Schaefer
  2021-01-17 18:23           ` Bart Schaefer
  0 siblings, 1 reply; 16+ messages in thread
From: Bart Schaefer @ 2021-01-17 18:02 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Lawrence Velázquez, Devin Hussey, zsh-workers

On Sun, Jan 17, 2021 at 9:22 AM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>
> Which unit test covers this?

There isn't one; there are no unit tests for "setopt nocaseglob" yet.
Devin was going to produce an example.

My latest take on this is workers/47832, which has received no comment so far.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Allow globbing with unreadable parent directories
  2021-01-17 18:02         ` Bart Schaefer
@ 2021-01-17 18:23           ` Bart Schaefer
  0 siblings, 0 replies; 16+ messages in thread
From: Bart Schaefer @ 2021-01-17 18:23 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Lawrence Velázquez, Devin Hussey, zsh-workers

On Sun, Jan 17, 2021 at 10:02 AM Bart Schaefer
<schaefer@brasslantern.com> wrote:
>
> On Sun, Jan 17, 2021 at 9:22 AM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> >
> > Which unit test covers this?
>
> There isn't one; there are no unit tests for "setopt nocaseglob" yet.

There is a test for unreadable directories in D02:

0:unreadable directories can be globbed (users/24619, users/24626)

But it doesn't test for additional directories below the unreadable ones.


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: NO_CASE_GLOB and unreadable directories (Episode VI: A New Hope)
  2021-01-14  5:57       ` NO_CASE_GLOB and unreadable directories (Episode VI: A New Hope) Bart Schaefer
  2021-01-14 18:56         ` Bart Schaefer
@ 2021-01-25  0:52         ` Bart Schaefer
  2021-01-25 14:05           ` Peter Stephenson
  1 sibling, 1 reply; 16+ messages in thread
From: Bart Schaefer @ 2021-01-25  0:52 UTC (permalink / raw)
  To: zsh-workers; +Cc: Devin Hussey

No comment on this at all?  Some additional remarks below.

On Wed, Jan 13, 2021 at 9:57 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> Effectively, NO_CASE_GLOB turns the pure path
>   /data/data/com.termux/files/home
> (to stick with the original example) into the extendedglob pattern
>   /(#i)data/(#i)data/(#i)com.termux/(#i)files/(#i)home
> which means every component has to be compared with pattry() rather
> than as a literal string.  That in turn means that every parent must
> be readable.

There's no practical workaround for this that I can see.  Without
reading the parent directory, on a case-sensitive filesystem we'd have
to attempt to chdir to every possible combination of upper/lower case
in the pathname.

> It further means that
>   anydir/*
> becomes
>   (#i)anydir/(#i)*
> which could be construed as incorrect, because if there are two directories
>   AnyDir/
>   anydir/
> that pattern will return everything in both of them

I think this is a real bug, but it can't be fixed without breaking the
case in which the current implementation finds AnyDir/ in the absence
of anydir/ (assuming the parent of AnyDir is readable).

> What's wanted in Devin's case is for (#i) to apply exactly when there
> are other pattern characters in the component, e.g.,
>   (#I)anydir/(#i)*

This is probably how CASEGLOB should have been implemented in the
first place.  Note that the documentation sort of implies this
interpretation:

     [nocaseglob] the presence of any character which is special to
     filename generation will cause case-insensitive matching.  For
     example, cvs(/) can match the directory CVS owing to the presence
     of the globbing flag (unless the option BARE_GLOB_QUAL is unset).

There's no mention in there of what happens when the path is more than
one directory deep.

I see these possibilities:
1) Do nothing; Devin's case is broken by design and termux needs to
come up with a solution that doesn't involve NO_CASE_GLOB.
2) Adopt (a CYGWIN-clean variant of) my patch from workers/47832, and
accept that some current usage of NO_CASE_GLOB will break.
3) Add a setopt (CASE_DIRS ?) for the current behavior, and merge that
with workers/47832.  I haven't yet worked out how to avoid having
"setopt nocasedirs" imply the effects of "setopt nocaseglob" ...
perhaps name it CASE_SEARCH (?) and simply leave the implication?


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: NO_CASE_GLOB and unreadable directories (Episode VI: A New Hope)
  2021-01-25  0:52         ` Bart Schaefer
@ 2021-01-25 14:05           ` Peter Stephenson
  2021-02-05  5:53             ` Bart Schaefer
  0 siblings, 1 reply; 16+ messages in thread
From: Peter Stephenson @ 2021-01-25 14:05 UTC (permalink / raw)
  To: zsh-workers

> On 25 January 2021 at 00:52 Bart Schaefer <schaefer@brasslantern.com> wrote:
> I see these possibilities:
> 1) Do nothing; Devin's case is broken by design and termux needs to
> come up with a solution that doesn't involve NO_CASE_GLOB.
> 2) Adopt (a CYGWIN-clean variant of) my patch from workers/47832, and
> accept that some current usage of NO_CASE_GLOB will break.
> 3) Add a setopt (CASE_DIRS ?) for the current behavior, and merge that
> with workers/47832.  I haven't yet worked out how to avoid having
> "setopt nocasedirs" imply the effects of "setopt nocaseglob" ...
> perhaps name it CASE_SEARCH (?) and simply leave the implication?

I don't think 2) is so unreasonable in that only patterns with some
form of case-insensitivity are involved.  This is something of a minefield
at the best of times and getting something that looks natural on systems
that are intrinsically case-insensitive, whether or not case-preserving,
is difficult.  But with a new option it seems to provide a definite path
forward in any case.  The only serious objection to that other that I
can see is that it makes the code more complicated; the reply to that is
if the code is simple there's a good chance it doesn't quite do what you
need it to.

(For the same reasons I'm not actually commenting on the details, since
I'm well aware how easy it is to miss some crucial aspect.)

pws


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: NO_CASE_GLOB and unreadable directories (Episode VI: A New Hope)
  2021-01-25 14:05           ` Peter Stephenson
@ 2021-02-05  5:53             ` Bart Schaefer
  2021-02-06 12:31               ` Daniel Shahaf
  0 siblings, 1 reply; 16+ messages in thread
From: Bart Schaefer @ 2021-02-05  5:53 UTC (permalink / raw)
  To: zsh-workers; +Cc: Devin Hussey

[-- Attachment #1: Type: text/plain, Size: 1982 bytes --]

On Mon, Jan 25, 2021 at 6:06 AM Peter Stephenson
<p.w.stephenson@ntlworld.com> wrote:
>
> > On 25 January 2021 at 00:52 Bart Schaefer <schaefer@brasslantern.com> wrote:
> > 2) Adopt (a CYGWIN-clean variant of) my patch from workers/47832, and
> > accept that some current usage of NO_CASE_GLOB will break.
> > 3) Add a setopt (CASE_DIRS ?) for the current behavior, and merge that
> > with workers/47832.
>
> I don't think 2) is so unreasonable in that only patterns with some
> form of case-insensitivity are involved.  This is something of a minefield
> at the best of times and getting something that looks natural on systems
> that are intrinsically case-insensitive, whether or not case-preserving,
> is difficult.  But with a new option it seems to provide a definite path
> forward in any case.

I interpret this as a stronger preference for #3 than for #2, so I've
taken a stab at it with the patch below.

> The only serious objection to that other that I
> can see is that it makes the code more complicated; the reply to that is
> if the code is simple there's a good chance it doesn't quite do what you
> need it to.

It's actually the documentation that gets more complicated, the code
is pretty simple.  I rewrote the paragraph I've added to options.yo
about seven times and I'm still not entirely happy with it.

What I elected was to keep the current behavior by default, and add an
option to get the behavior that termux wants.  I named it CASE_PATHS
for reasons I think the documentation paragraph make obvious.

Changing CASE_PATHS has no effect by itself; it only matters if
CASE_GLOB changes.

The default is CASE_GLOB + NO_CASE_PATHS, which gives you the normal
zsh behavior, but CASE_GLOB + CASE_PATHS is the same.

NO_CASE_GLOB + NO_CASE_PATHS gives the current behavior of NO_CASE_GLOB.

NO_CASE_GLOB + CASE_PATHS is the behavior required for descending
through protected directories, as needed by termux.

Attached again to avoid line wrap.  No tests yet.

[-- Attachment #2: casepaths-diff.txt --]
[-- Type: text/plain, Size: 2544 bytes --]

diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index b3bf11f5c..714e8a1a1 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -474,6 +474,22 @@ item(tt(CASE_MATCH) <D>)(
 Make regular expressions using the tt(zsh/regex) module (including
 matches with tt(=~)) sensitive to case.
 )
+pindex(CASE_PATHS)
+pindex(NO_CASE_PATHS)
+pindex(CASEPATHS)
+pindex(NOCASEPATHS)
+cindex(case-sensitive globbing, option)
+item(tt(CASE_PATHS))(
+If tt(CASE_PATHS) is not set (the default), tt(CASE_GLOB) affects the
+interpretation of em(every) path component, whenever a special
+character appears in em(any) component.  When tt(CASE_PATHS) is set,
+file path components that do em(not) contain special filename
+generation characters are always sensitive to case, thus restricting
+tt(NO_CASE_GLOB) to components that contain globbing characters.
+
+Note that if the filesystem itself is not sensitive to case, then
+tt(CASE_PATHS) has no effect.
+)
 pindex(CSH_NULL_GLOB)
 pindex(NO_CSH_NULL_GLOB)
 pindex(CSHNULLGLOB)
diff --git a/Src/options.c b/Src/options.c
index fba021e7d..6ea6290e5 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -105,6 +105,7 @@ static struct optname optns[] = {
 {{NULL, "bsdecho",	      OPT_EMULATE|OPT_SH},	 BSDECHO},
 {{NULL, "caseglob",	      OPT_ALL},			 CASEGLOB},
 {{NULL, "casematch",	      OPT_ALL},			 CASEMATCH},
+{{NULL, "casepaths",	      0},			 CASEPATHS},
 {{NULL, "cbases",	      0},			 CBASES},
 {{NULL, "cprecedences",	      OPT_EMULATE|OPT_NONZSH},	 CPRECEDENCES},
 {{NULL, "cdablevars",	      OPT_EMULATE},		 CDABLEVARS},
diff --git a/Src/pattern.c b/Src/pattern.c
index c7c2c8bea..053d2a627 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -509,7 +509,7 @@ void
 patcompstart(void)
 {
     patcompcharsset();
-    if (isset(CASEGLOB))
+    if (isset(CASEGLOB) || isset(CASEPATHS))
 	patglobflags = 0;
     else
 	patglobflags = GF_IGNCASE;
@@ -632,6 +632,13 @@ patcompile(char *exp, int inflags, char **endexp)
     p->patmlen = len;
     p->patnpar = patnpar-1;
 
+#ifndef __CYGWIN__  /* The filesystem itself is case-insensitive on Cygwin */
+    if ((patflags & PAT_FILE) && !isset(CASEGLOB) && !(patflags & PAT_PURES)) {
+	p->globflags |= GF_IGNCASE;
+	p->globend |= GF_IGNCASE;
+    }
+#endif
+
     if (!strp) {
 	pscan = (Upat)(patout + startoff);
 
diff --git a/Src/zsh.h b/Src/zsh.h
index 6cf1b4186..5acbddea4 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2387,6 +2387,7 @@ enum {
     BSDECHO,
     CASEGLOB,
     CASEMATCH,
+    CASEPATHS,
     CBASES,
     CDABLEVARS,
     CDSILENT,

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: NO_CASE_GLOB and unreadable directories (Episode VI: A New Hope)
  2021-02-05  5:53             ` Bart Schaefer
@ 2021-02-06 12:31               ` Daniel Shahaf
  0 siblings, 0 replies; 16+ messages in thread
From: Daniel Shahaf @ 2021-02-06 12:31 UTC (permalink / raw)
  To: zsh-workers; +Cc: Devin Hussey

Bart Schaefer wrote on Thu, Feb 04, 2021 at 21:53:12 -0800:
> It's actually the documentation that gets more complicated, the code
> is pretty simple.  I rewrote the paragraph I've added to options.yo
> about seven times and I'm still not entirely happy with it.

Took a stab at it, below.

> What I elected was to keep the current behavior by default, and add an
> option to get the behavior that termux wants.  I named it CASE_PATHS
> for reasons I think the documentation paragraph make obvious.

Hmm.  It may be a bikeshed, but might people guess an option named
CASE_PATHS has to do with $PATH?

> +++ b/Doc/Zsh/options.yo
> @@ -474,6 +474,22 @@ item(tt(CASE_MATCH) <D>)(
> +pindex(CASE_PATHS)
> +pindex(NO_CASE_PATHS)
> +pindex(CASEPATHS)
> +pindex(NOCASEPATHS)
> +cindex(case-sensitive globbing, option)
> +item(tt(CASE_PATHS))(
> +If tt(CASE_PATHS) is not set (the default), tt(CASE_GLOB) affects the
> +interpretation of em(every) path component, whenever a special
> +character appears in em(any) component.  When tt(CASE_PATHS) is set,
> +file path components that do em(not) contain special filename
> +generation characters are always sensitive to case, thus restricting
> +tt(NO_CASE_GLOB) to components that contain globbing characters.
> +
> +Note that if the filesystem itself is not sensitive to case, then
> +tt(CASE_PATHS) has no effect.

diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index b3bf11f5c..4aca04746 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -463,6 +463,12 @@ the presence of any character which is special to filename generation
 will cause case-insensitive matching.  For example, tt(cvs+LPAR()/+RPAR())
 can match the directory tt(CVS) owing to the presence of the globbing flag
 (unless the option tt(BARE_GLOB_QUAL) is unset).
+
+The case-insensitive matching is affected by the tt(CASE_PATHS) option,
+which see.
+
+Note that if the filesystem itself is not sensitive to case, then
+this option has no effect.
 )
 pindex(CASE_MATCH)
 pindex(NO_CASE_MATCH)
@@ -474,6 +480,23 @@ item(tt(CASE_MATCH) <D>)(
 Make regular expressions using the tt(zsh/regex) module (including
 matches with tt(=~)) sensitive to case.
 )
+pindex(CASE_PATHS)
+pindex(NO_CASE_PATHS)
+pindex(CASEPATHS)
+pindex(NOCASEPATHS)
+cindex(case-sensitive globbing, option)
+item(tt(CASE_PATHS))(
+This option only has an effect when the tt(CASE_GLOB) option is unset, which is
+not the default.
+
+This option governs the case-insensitive matching used when the tt(CASE_GLOB)
+option is unset.  When this option is unset (the default), whenever a special
+character appears in em(any) path component, em(all) components will be
+interpreted case-insensitively.  When this option is set, 
+file path components that do em(not) contain special filename
+generation characters are always sensitive to case, thus restricting
+tt(NO_CASE_GLOB) to components that contain globbing characters.
+)
 pindex(CSH_NULL_GLOB)
 pindex(NO_CSH_NULL_GLOB)
 pindex(CSHNULLGLOB)


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2021-02-06 12:32 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-13  3:04 [PATCH] Allow globbing with unreadable parent directories Devin Hussey
2021-01-13 22:27 ` Bart Schaefer
2021-01-14  0:27   ` Devin Hussey
2021-01-14  1:32     ` Lawrence Velázquez
2021-01-14  2:22       ` Devin Hussey
2021-01-14  2:24         ` Devin Hussey
2021-01-17 17:22       ` Daniel Shahaf
2021-01-17 18:02         ` Bart Schaefer
2021-01-17 18:23           ` Bart Schaefer
2021-01-14  4:04     ` Bart Schaefer
2021-01-14  5:57       ` NO_CASE_GLOB and unreadable directories (Episode VI: A New Hope) Bart Schaefer
2021-01-14 18:56         ` Bart Schaefer
2021-01-25  0:52         ` Bart Schaefer
2021-01-25 14:05           ` Peter Stephenson
2021-02-05  5:53             ` Bart Schaefer
2021-02-06 12:31               ` Daniel Shahaf

zsh-workers

This inbox may be cloned and mirrored by anyone:

	git clone --mirror http://inbox.vuxu.org/zsh-workers

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V1 zsh-workers zsh-workers/ http://inbox.vuxu.org/zsh-workers \
		zsh-workers@zsh.org
	public-inbox-index zsh-workers

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://inbox.vuxu.org/vuxu.archive.zsh.workers


code repositories for the project(s) associated with this inbox:

	https://git.vuxu.org/mirror/zsh/

AGPL code for this site: git clone https://public-inbox.org/public-inbox.git