From c33fa871a5c89091cfc89fd7b6ef504d2d48bef2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 14 Sep 2023 14:46:46 -0700 Subject: [PATCH] cache: add fake_lstat() 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 --- read-cache-ll.h | 8 ++++++++ read-cache.c | 27 +++++++++++++++++++++++++++ statinfo.c | 27 +++++++++++++++++++++++++++ statinfo.h | 8 ++++++++ 4 files changed, 70 insertions(+) diff --git a/read-cache-ll.h b/read-cache-ll.h index 9a1a7edc5a22cf..2a50a784f0ec6a 100644 --- a/read-cache-ll.h +++ b/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 a/read-cache.c b/read-cache.c index 080bd39713bc17..eb750e2e49a370 100644 --- a/read-cache.c +++ b/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 a/statinfo.c b/statinfo.c index 17bb8966c33d6d..45156109de72e9 100644 --- a/statinfo.c +++ b/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 a/statinfo.h b/statinfo.h index 700f502ac0b3cb..5b21a30f90bfd2 100644 --- a/statinfo.h +++ b/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