[PATCH] cache: add fake_lstat()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



At times, we may already know that a path represented by a
cache_entry ce has no changes via some out-of-line means, like
fsmonitor, and yet need the control to go through a codepath that
requires us to have "struct stat" obtained by lstat() on the path,
for various purposes (e.g. "ie_match_stat()" wants cached stat-info
is still current wrt "struct stat", "diff" wants to know st_mode).

The callers of lstat() on a tracked file, when its cache_entry knows
it is up-to-date, can instead call this helper to pretend that it
called lstat() by faking the "struct stat" information.

Signed-off-by: Junio C Hamano <gitster@xxxxxxxxx>
---

 * Just setting the .st_mode member in check_removed() relies on
   the assumption that other members of "struct stat" are never
   looked at a bit too much.  We could use something like this to
   bypass calling lstat() and instead populate from the data we have
   in cache_entry.

 builtin/ls-files.c |  5 ++++-
 read-cache-ll.h    |  8 ++++++++
 read-cache.c       | 27 +++++++++++++++++++++++++++
 statinfo.c         | 27 +++++++++++++++++++++++++++
 statinfo.h         |  8 ++++++++
 5 files changed, 74 insertions(+), 1 deletion(-)

diff --git c/builtin/ls-files.c w/builtin/ls-files.c
index a0229c3277..7031ffcb0a 100644
--- c/builtin/ls-files.c
+++ w/builtin/ls-files.c
@@ -450,7 +450,10 @@ static void show_files(struct repository *repo, struct dir_struct *dir)
 			continue;
 		if (ce_skip_worktree(ce))
 			continue;
-		stat_err = lstat(fullname.buf, &st);
+		if (!(ce->ce_flags & CE_FSMONITOR_VALID))
+			stat_err = lstat(fullname.buf, &st);
+		else
+			stat_err = fake_lstat(ce, &st);
 		if (stat_err && (errno != ENOENT && errno != ENOTDIR))
 			error_errno("cannot lstat '%s'", fullname.buf);
 		if (stat_err && show_deleted) {
diff --git c/read-cache-ll.h w/read-cache-ll.h
index 9a1a7edc5a..2a50a784f0 100644
--- c/read-cache-ll.h
+++ w/read-cache-ll.h
@@ -436,6 +436,14 @@ int match_stat_data_racy(const struct index_state *istate,
 
 void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, struct stat *st);
 
+/*
+ * Fill members of st by members of sd enough to convince match_stat()
+ * to consider that they match.  It should be usable as a replacement
+ * for lstat() for a tracked path that is known to be up-to-date via
+ * some out-of-line means (like fsmonitor).
+ */
+int fake_lstat(const struct cache_entry *ce, struct stat *st);
+
 #define REFRESH_REALLY                   (1 << 0) /* ignore_valid */
 #define REFRESH_UNMERGED                 (1 << 1) /* allow unmerged */
 #define REFRESH_QUIET                    (1 << 2) /* be quiet about it */
diff --git c/read-cache.c w/read-cache.c
index 080bd39713..eb750e2e49 100644
--- c/read-cache.c
+++ w/read-cache.c
@@ -197,6 +197,33 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
 	}
 }
 
+static unsigned int st_mode_from_ce(const struct cache_entry *ce)
+{
+	extern int trust_executable_bit, has_symlinks;
+
+	switch (ce->ce_mode & S_IFMT) {
+	case S_IFLNK:
+		return has_symlinks ? S_IFLNK : (S_IFREG | 0644);
+	case S_IFREG:
+		return (ce->ce_mode & (trust_executable_bit ? 0755 : 0644)) | S_IFREG;
+	case S_IFGITLINK:
+		return S_IFDIR | 0755;
+	case S_IFDIR:
+		return ce->ce_mode;
+	default:
+		BUG("unsupported ce_mode: %o", ce->ce_mode);
+	}
+}
+
+int fake_lstat(const struct cache_entry *ce, struct stat *st)
+{
+	fake_lstat_data(&ce->ce_stat_data, st);
+	st->st_mode = st_mode_from_ce(ce);
+
+	/* always succeed as lstat() replacement */
+	return 0;
+}
+
 static int ce_compare_data(struct index_state *istate,
 			   const struct cache_entry *ce,
 			   struct stat *st)
diff --git c/statinfo.c w/statinfo.c
index 17bb8966c3..45156109de 100644
--- c/statinfo.c
+++ w/statinfo.c
@@ -15,6 +15,33 @@ void fill_stat_data(struct stat_data *sd, struct stat *st)
 	sd->sd_size = st->st_size;
 }
 
+static void set_times(struct stat *st, const struct stat_data *sd)
+{
+	st->st_ctime = sd->sd_ctime.sec;
+	st->st_mtime = sd->sd_mtime.sec;
+#ifdef NO_NSEC
+	; /* nothing */
+#else
+#ifdef USE_ST_TIMESPEC
+	st->st_ctimespec.tv_nsec = sd->sd_ctime.nsec;
+	st->st_mtimespec.tv_nsec = sd->sd_mtime.nsec;
+#else
+	st->st_ctim.tv_nsec = sd->sd_ctime.nsec;
+	st->st_mtim.tv_nsec = sd->sd_mtime.nsec;
+#endif
+#endif
+}
+
+void fake_lstat_data(const struct stat_data *sd, struct stat *st)
+{
+	set_times(st, sd);
+	st->st_dev = sd->sd_dev;
+	st->st_ino = sd->sd_ino;
+	st->st_uid = sd->sd_uid;
+	st->st_gid = sd->sd_gid;
+	st->st_size = sd->sd_size;
+}
+
 int match_stat_data(const struct stat_data *sd, struct stat *st)
 {
 	int changed = 0;
diff --git c/statinfo.h w/statinfo.h
index 700f502ac0..5b21a30f90 100644
--- c/statinfo.h
+++ w/statinfo.h
@@ -46,6 +46,14 @@ struct stat_validity {
  */
 void fill_stat_data(struct stat_data *sd, struct stat *st);
 
+/*
+ * The inverse of the above.  When we know the cache_entry that
+ * contains sd is up-to-date, but still need to pretend we called
+ * lstat() to learn that fact, this function fills "st" enough to
+ * fool ie_match_stat().
+ */
+void fake_lstat_data(const struct stat_data *sd, struct stat *st);
+
 /*
  * Return 0 if st is consistent with a file not having been changed
  * since sd was filled.  If there are differences, return a



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux