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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.