Coverage Report

Created: 2026-11-12 06:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/statinfo.c
Line
Count
Source
1
#define USE_THE_REPOSITORY_VARIABLE
2
3
#include "git-compat-util.h"
4
#include "environment.h"
5
#include "statinfo.h"
6
#include "repository.h"
7
8
/*
9
 * Munge st_size into an unsigned int.
10
 */
11
0
static unsigned int munge_st_size(off_t st_size) {
12
0
  unsigned int sd_size = st_size;
13
14
  /*
15
   * If the file is an exact multiple of 4 GiB, modify the value so it
16
   * doesn't get marked as racily clean (zero).
17
   */
18
0
  if (!sd_size && st_size)
19
0
    return 0x80000000;
20
0
  else
21
0
    return sd_size;
22
0
}
23
24
void fill_stat_data(struct stat_data *sd, struct stat *st)
25
0
{
26
0
  sd->sd_ctime.sec = (unsigned int)st->st_ctime;
27
0
  sd->sd_mtime.sec = (unsigned int)st->st_mtime;
28
0
  sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
29
0
  sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
30
0
  sd->sd_dev = st->st_dev;
31
0
  sd->sd_ino = st->st_ino;
32
0
  sd->sd_uid = st->st_uid;
33
0
  sd->sd_gid = st->st_gid;
34
0
  sd->sd_size = munge_st_size(st->st_size);
35
0
}
36
37
static void set_times(struct stat *st, const struct stat_data *sd)
38
0
{
39
0
  st->st_ctime = sd->sd_ctime.sec;
40
0
  st->st_mtime = sd->sd_mtime.sec;
41
#ifdef NO_NSEC
42
  ; /* nothing */
43
#else
44
#ifdef USE_ST_TIMESPEC
45
  st->st_ctimespec.tv_nsec = sd->sd_ctime.nsec;
46
  st->st_mtimespec.tv_nsec = sd->sd_mtime.nsec;
47
#else
48
0
  st->st_ctim.tv_nsec = sd->sd_ctime.nsec;
49
0
  st->st_mtim.tv_nsec = sd->sd_mtime.nsec;
50
0
#endif
51
0
#endif
52
0
}
53
54
void fake_lstat_data(const struct stat_data *sd, struct stat *st)
55
0
{
56
0
  set_times(st, sd);
57
0
  st->st_dev = sd->sd_dev;
58
0
  st->st_ino = sd->sd_ino;
59
0
  st->st_uid = sd->sd_uid;
60
0
  st->st_gid = sd->sd_gid;
61
0
  st->st_size = sd->sd_size;
62
0
}
63
64
int match_stat_data(const struct stat_data *sd, struct stat *st)
65
0
{
66
0
  int changed = 0;
67
0
  struct repo_config_values *cfg = repo_config_values(the_repository);
68
69
0
  if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
70
0
    changed |= MTIME_CHANGED;
71
0
  if (cfg->trust_ctime && cfg->check_stat &&
72
0
      sd->sd_ctime.sec != (unsigned int)st->st_ctime)
73
0
    changed |= CTIME_CHANGED;
74
75
0
  if (cfg->use_nanosec) {
76
0
    if (cfg->check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
77
0
      changed |= MTIME_CHANGED;
78
0
    if (cfg->trust_ctime && cfg->check_stat &&
79
0
        sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
80
0
      changed |= CTIME_CHANGED;
81
0
  }
82
83
0
  if (cfg->check_stat) {
84
0
    if (sd->sd_uid != (unsigned int) st->st_uid ||
85
0
      sd->sd_gid != (unsigned int) st->st_gid)
86
0
      changed |= OWNER_CHANGED;
87
0
    if (sd->sd_ino != (unsigned int) st->st_ino)
88
0
      changed |= INODE_CHANGED;
89
0
  }
90
91
#ifdef USE_STDEV
92
  /*
93
   * st_dev breaks on network filesystems where different
94
   * clients will have different views of what "device"
95
   * the filesystem is on
96
   */
97
  if (cfg->check_stat && sd->sd_dev != (unsigned int) st->st_dev)
98
      changed |= INODE_CHANGED;
99
#endif
100
101
0
  if (sd->sd_size != munge_st_size(st->st_size))
102
0
    changed |= DATA_CHANGED;
103
104
0
  return changed;
105
0
}
106
107
void stat_validity_clear(struct stat_validity *sv)
108
0
{
109
0
  FREE_AND_NULL(sv->sd);
110
0
}
111
112
int stat_validity_check(struct stat_validity *sv, const char *path)
113
0
{
114
0
  struct stat st;
115
116
0
  if (stat(path, &st) < 0)
117
0
    return sv->sd == NULL;
118
0
  if (!sv->sd)
119
0
    return 0;
120
0
  return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
121
0
}
122
123
void stat_validity_update(struct stat_validity *sv, int fd)
124
0
{
125
0
  struct stat st;
126
127
0
  if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
128
0
    stat_validity_clear(sv);
129
0
  else {
130
0
    if (!sv->sd)
131
0
      CALLOC_ARRAY(sv->sd, 1);
132
0
    fill_stat_data(sv->sd, &st);
133
0
  }
134
0
}