From: Junio C Hamano <gitster@pobox.com>
To: git@vger.kernel.org
Cc: Josip Sokcevic <sokcevic@google.com>
Subject: [PATCH] cache: add fake_lstat()
Date: Thu, 14 Sep 2023 14:46:46 -0700 [thread overview]
Message-ID: <xmqqcyykig1l.fsf@gitster.g> (raw)
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@pobox.com>
---
* 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
next reply other threads:[~2023-09-14 21:46 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-14 21:46 Junio C Hamano [this message]
2023-09-14 22:00 ` [PATCH] cache: add fake_lstat() Junio C Hamano
2023-09-15 0:17 ` Josip Sokcevic
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=xmqqcyykig1l.fsf@gitster.g \
--to=gitster@pobox.com \
--cc=git@vger.kernel.org \
--cc=sokcevic@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).