diff mbox series

[09/12] fsmonitor: refactor non-directory callback

Message ID a0cc4c8274c108d231816ac981781d9e3cf8fded.1707857541.git.gitgitgadget@gmail.com (mailing list archive)
State Superseded
Headers show
Series FSMonitor edge cases on case-insensitive file systems | expand

Commit Message

Jeff Hostetler Feb. 13, 2024, 8:52 p.m. UTC
From: Jeff Hostetler <jeffhostetler@github.com>

Refactor the fsmonitor_refresh_callback_unqualified() code
to try to use the _callback_slash() code and avoid having
a custom filter in the child cache-entry scanner.

On platforms that DO NOT annotate FS events with a trailing
slash, if we fail to find an exact match for the pathname
in the index, we do not know if the pathname represents a
directory or simply an untracked file.  Pretend that the pathname
is a directory and try again before assuming it is an untracked
file.

Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
---
 fsmonitor.c | 59 +++++++++++++++++++++++++++++++----------------------
 1 file changed, 35 insertions(+), 24 deletions(-)

Comments

Patrick Steinhardt Feb. 15, 2024, 9:32 a.m. UTC | #1
On Tue, Feb 13, 2024 at 08:52:18PM +0000, Jeff Hostetler via GitGitGadget wrote:
> From: Jeff Hostetler <jeffhostetler@github.com>
> 
> Refactor the fsmonitor_refresh_callback_unqualified() code
> to try to use the _callback_slash() code and avoid having
> a custom filter in the child cache-entry scanner.
> 
> On platforms that DO NOT annotate FS events with a trailing
> slash, if we fail to find an exact match for the pathname
> in the index, we do not know if the pathname represents a
> directory or simply an untracked file.  Pretend that the pathname
> is a directory and try again before assuming it is an untracked
> file.
> 
> Signed-off-by: Jeff Hostetler <jeffhostetler@github.com>
> ---
>  fsmonitor.c | 59 +++++++++++++++++++++++++++++++----------------------
>  1 file changed, 35 insertions(+), 24 deletions(-)
> 
> diff --git a/fsmonitor.c b/fsmonitor.c
> index 73e6ac82af7..cb27bae8aa8 100644
> --- a/fsmonitor.c
> +++ b/fsmonitor.c
> @@ -287,41 +287,52 @@ static int my_callback_dir_name_hash(
>  	return nr_in_cone;
>  }
>  
> -static void fsmonitor_refresh_callback_unqualified(
> +/*
> + * The daemon sent an observed pathname without a trailing slash.
> + * (This is the normal case.)  We do not know if it is a tracked or
> + * untracked file, a sparse-directory, or a populated directory (on a
> + * platform such as Windows where FSEvents are not qualified).
> + *
> + * The pathname contains the observed case reported by the FS. We
> + * do not know it is case-correct or -incorrect.
> + *
> + * Assume it is case-correct and try an exact match.
> + *
> + * Return the number of cache-entries that we invalidated.
> + */
> +static int fsmonitor_refresh_callback_unqualified(
>  	struct index_state *istate, const char *name, int len, int pos)
>  {
> -	int i;
> -
>  	my_invalidate_untracked_cache(istate, name, len);
>  
>  	if (pos >= 0) {
>  		/*
> -		 * We have an exact match for this path and can just
> -		 * invalidate it.
> +		 * An exact match on a tracked file. We assume that we
> +		 * do not need to scan forward for a sparse-directory
> +		 * cache-entry with the same pathname, nor for a cone
> +		 * at that directory. (That is, assume no D/F conflicts.)
>  		 */
>  		istate->cache[pos]->ce_flags &= ~CE_FSMONITOR_VALID;
> +		return 1;
>  	} else {
> +		int nr_in_cone;
> +		struct strbuf work_path = STRBUF_INIT;
> +
>  		/*
> -		 * The path is not a tracked file -or- it is a
> -		 * directory event on a platform that cannot
> -		 * distinguish between file and directory events in
> -		 * the event handler, such as Windows.
> -		 *
> -		 * Scan as if it is a directory and invalidate the
> -		 * cone under it.  (But remember to ignore items
> -		 * between "name" and "name/", such as "name-" and
> -		 * "name.".
> +		 * The negative "pos" gives us the suggested insertion
> +		 * point for the pathname (without the trailing slash).
> +		 * We need to see if there is a directory with that
> +		 * prefix, but there can be lots of pathnames between
> +		 * "foo" and "foo/" like "foo-" or "foo-bar", so we
> +		 * don't want to do our own scan.
>  		 */
> -		pos = -pos - 1;
> -
> -		for (i = pos; i < istate->cache_nr; i++) {
> -			if (!starts_with(istate->cache[i]->name, name))
> -				break;
> -			if ((unsigned char)istate->cache[i]->name[len] > '/')
> -				break;
> -			if (istate->cache[i]->name[len] == '/')
> -				istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
> -		}
> +		strbuf_add(&work_path, name, len);
> +		strbuf_addch(&work_path, '/');
> +		pos = index_name_pos(istate, work_path.buf, work_path.len);
> +		nr_in_cone = fsmonitor_refresh_callback_slash(
> +			istate, work_path.buf, work_path.len, pos);
> +		strbuf_release(&work_path);
> +		return nr_in_cone;

I didn't spot any users of this return value, and Junio also mentioned
this in a preceding patch for a different function. Would it make sense
to introduce this return value as-needed in a later patch so that the
reader isn't left wondering why it's changed now?

Patrick

>  	}
>  }
>  
> -- 
> gitgitgadget
> 
>
diff mbox series

Patch

diff --git a/fsmonitor.c b/fsmonitor.c
index 73e6ac82af7..cb27bae8aa8 100644
--- a/fsmonitor.c
+++ b/fsmonitor.c
@@ -287,41 +287,52 @@  static int my_callback_dir_name_hash(
 	return nr_in_cone;
 }
 
-static void fsmonitor_refresh_callback_unqualified(
+/*
+ * The daemon sent an observed pathname without a trailing slash.
+ * (This is the normal case.)  We do not know if it is a tracked or
+ * untracked file, a sparse-directory, or a populated directory (on a
+ * platform such as Windows where FSEvents are not qualified).
+ *
+ * The pathname contains the observed case reported by the FS. We
+ * do not know it is case-correct or -incorrect.
+ *
+ * Assume it is case-correct and try an exact match.
+ *
+ * Return the number of cache-entries that we invalidated.
+ */
+static int fsmonitor_refresh_callback_unqualified(
 	struct index_state *istate, const char *name, int len, int pos)
 {
-	int i;
-
 	my_invalidate_untracked_cache(istate, name, len);
 
 	if (pos >= 0) {
 		/*
-		 * We have an exact match for this path and can just
-		 * invalidate it.
+		 * An exact match on a tracked file. We assume that we
+		 * do not need to scan forward for a sparse-directory
+		 * cache-entry with the same pathname, nor for a cone
+		 * at that directory. (That is, assume no D/F conflicts.)
 		 */
 		istate->cache[pos]->ce_flags &= ~CE_FSMONITOR_VALID;
+		return 1;
 	} else {
+		int nr_in_cone;
+		struct strbuf work_path = STRBUF_INIT;
+
 		/*
-		 * The path is not a tracked file -or- it is a
-		 * directory event on a platform that cannot
-		 * distinguish between file and directory events in
-		 * the event handler, such as Windows.
-		 *
-		 * Scan as if it is a directory and invalidate the
-		 * cone under it.  (But remember to ignore items
-		 * between "name" and "name/", such as "name-" and
-		 * "name.".
+		 * The negative "pos" gives us the suggested insertion
+		 * point for the pathname (without the trailing slash).
+		 * We need to see if there is a directory with that
+		 * prefix, but there can be lots of pathnames between
+		 * "foo" and "foo/" like "foo-" or "foo-bar", so we
+		 * don't want to do our own scan.
 		 */
-		pos = -pos - 1;
-
-		for (i = pos; i < istate->cache_nr; i++) {
-			if (!starts_with(istate->cache[i]->name, name))
-				break;
-			if ((unsigned char)istate->cache[i]->name[len] > '/')
-				break;
-			if (istate->cache[i]->name[len] == '/')
-				istate->cache[i]->ce_flags &= ~CE_FSMONITOR_VALID;
-		}
+		strbuf_add(&work_path, name, len);
+		strbuf_addch(&work_path, '/');
+		pos = index_name_pos(istate, work_path.buf, work_path.len);
+		nr_in_cone = fsmonitor_refresh_callback_slash(
+			istate, work_path.buf, work_path.len, pos);
+		strbuf_release(&work_path);
+		return nr_in_cone;
 	}
 }