Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/fontconfig/src/fcstat.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright © 2000 Keith Packard
3
 * Copyright © 2005 Patrick Lam
4
 *
5
 * Permission to use, copy, modify, distribute, and sell this software and its
6
 * documentation for any purpose is hereby granted without fee, provided that
7
 * the above copyright notice appear in all copies and that both that
8
 * copyright notice and this permission notice appear in supporting
9
 * documentation, and that the name of the author(s) not be used in
10
 * advertising or publicity pertaining to distribution of the software without
11
 * specific, written prior permission.  The authors make no
12
 * representations about the suitability of this software for any purpose.  It
13
 * is provided "as is" without express or implied warranty.
14
 *
15
 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17
 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21
 * PERFORMANCE OF THIS SOFTWARE.
22
 */
23
#include "fcint.h"
24
25
#include "fcarch.h"
26
#ifdef HAVE_DIRENT_H
27
#  include <dirent.h>
28
#endif
29
#include <fcntl.h>
30
#include <limits.h>
31
#include <sys/stat.h>
32
#include <sys/types.h>
33
#ifdef HAVE_SYS_VFS_H
34
#  include <sys/vfs.h>
35
#endif
36
#ifdef HAVE_SYS_STATVFS_H
37
#  include <sys/statvfs.h>
38
#endif
39
#ifdef HAVE_SYS_STATFS_H
40
#  include <sys/statfs.h>
41
#endif
42
#ifdef HAVE_SYS_PARAM_H
43
#  include <sys/param.h>
44
#endif
45
#ifdef HAVE_SYS_MOUNT_H
46
#  include <sys/mount.h>
47
#endif
48
#include <errno.h>
49
50
#ifdef _WIN32
51
#  ifdef __GNUC__
52
typedef long long INT64;
53
#    define EPOCH_OFFSET 11644473600ll
54
#  else
55
#    define EPOCH_OFFSET 11644473600i64
56
typedef __int64 INT64;
57
#  endif
58
59
/* Workaround for problems in the stat() in the Microsoft C library:
60
 *
61
 * 1) stat() uses FindFirstFile() to get the file
62
 * attributes. Unfortunately this API doesn't return correct values
63
 * for modification time of a directory until some time after a file
64
 * or subdirectory has been added to the directory. (This causes
65
 * run-test.sh to fail, for instance.) GetFileAttributesEx() is
66
 * better, it returns the updated timestamp right away.
67
 *
68
 * 2) stat() does some strange things related to backward
69
 * compatibility with the local time timestamps on FAT volumes and
70
 * daylight saving time. This causes problems after the switches
71
 * to/from daylight saving time. See
72
 * http://bugzilla.gnome.org/show_bug.cgi?id=154968 , especially
73
 * comment #30, and http://www.codeproject.com/datetime/dstbugs.asp .
74
 * We don't need any of that, FAT and Win9x are as good as dead. So
75
 * just use the UTC timestamps from NTFS, converted to the Unix epoch.
76
 */
77
78
int
79
FcStat (const FcChar8 *file, struct stat *statb)
80
{
81
    WIN32_FILE_ATTRIBUTE_DATA wfad;
82
    char                      full_path_name[MAX_PATH];
83
    char                     *basename;
84
    DWORD                     rc;
85
86
    if (!GetFileAttributesEx ((LPCSTR)file, GetFileExInfoStandard, &wfad))
87
  return -1;
88
89
    statb->st_dev = 0;
90
91
    /* Calculate a pseudo inode number as a hash of the full path name.
92
     * Call GetLongPathName() to get the spelling of the path name as it
93
     * is on disk.
94
     */
95
    rc = GetFullPathName ((LPCSTR)file, sizeof (full_path_name), full_path_name, &basename);
96
    if (rc == 0 || rc > sizeof (full_path_name))
97
  return -1;
98
99
    rc = GetLongPathName (full_path_name, full_path_name, sizeof (full_path_name));
100
    statb->st_ino = FcStringHash ((const FcChar8 *)full_path_name);
101
102
    statb->st_mode = _S_IREAD | _S_IWRITE;
103
    statb->st_mode |= (statb->st_mode >> 3) | (statb->st_mode >> 6);
104
105
    if (wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
106
  statb->st_mode |= _S_IFDIR;
107
    else
108
  statb->st_mode |= _S_IFREG;
109
110
    statb->st_nlink = 1;
111
    statb->st_uid = statb->st_gid = 0;
112
    statb->st_rdev = 0;
113
114
    if (wfad.nFileSizeHigh > 0)
115
  return -1;
116
    statb->st_size = wfad.nFileSizeLow;
117
118
    statb->st_atime = (*(INT64 *)&wfad.ftLastAccessTime) / 10000000 - EPOCH_OFFSET;
119
    statb->st_mtime = (*(INT64 *)&wfad.ftLastWriteTime) / 10000000 - EPOCH_OFFSET;
120
    statb->st_ctime = statb->st_mtime;
121
122
    return 0;
123
}
124
125
#else
126
127
int
128
FcStat (const FcChar8 *file, struct stat *statb)
129
863
{
130
863
    return stat ((char *)file, statb);
131
863
}
132
133
/* Adler-32 checksum implementation */
134
struct Adler32 {
135
    int a;
136
    int b;
137
};
138
139
static void
140
Adler32Init (struct Adler32 *ctx)
141
0
{
142
0
    ctx->a = 1;
143
0
    ctx->b = 0;
144
0
}
145
146
static void
147
Adler32Update (struct Adler32 *ctx, const char *data, int data_len)
148
0
{
149
0
    while (data_len--) {
150
0
  ctx->a = (ctx->a + *data++) % 65521;
151
0
  ctx->b = (ctx->b + ctx->a) % 65521;
152
0
    }
153
0
}
154
155
static int
156
Adler32Finish (struct Adler32 *ctx)
157
0
{
158
0
    return ctx->a + (ctx->b << 16);
159
0
}
160
161
#  ifdef HAVE_STRUCT_DIRENT_D_TYPE
162
/* dirent.d_type can be relied upon on FAT filesystem */
163
static FcBool
164
FcDirChecksumScandirFilter (const struct dirent *entry)
165
0
{
166
0
    return entry->d_type != DT_DIR;
167
0
}
168
#  endif
169
170
static int
171
FcDirChecksumScandirSorter (const struct dirent **lhs, const struct dirent **rhs)
172
0
{
173
0
    return strcmp ((*lhs)->d_name, (*rhs)->d_name);
174
0
}
175
176
static void
177
free_dirent (struct dirent **p)
178
0
{
179
0
    struct dirent **x;
180
181
0
    for (x = p; *x != NULL; x++)
182
0
  free (*x);
183
184
0
    free (p);
185
0
}
186
187
int
188
FcScandir (const char      *dirp,
189
           struct dirent ***namelist,
190
           int (*filter) (const struct dirent *),
191
           int (*compar) (const struct dirent **, const struct dirent **));
192
193
int
194
FcScandir (const char      *dirp,
195
           struct dirent ***namelist,
196
           int (*filter) (const struct dirent *),
197
           int (*compar) (const struct dirent **, const struct dirent **))
198
0
{
199
0
    DIR           *d;
200
0
    struct dirent *dent, *p, **dlist, **dlp;
201
0
    size_t         lsize = 128, n = 0;
202
203
0
    d = opendir (dirp);
204
0
    if (!d)
205
0
  return -1;
206
207
0
    dlist = (struct dirent **)malloc (sizeof (struct dirent *) * lsize);
208
0
    if (!dlist) {
209
0
  closedir (d);
210
0
  errno = ENOMEM;
211
212
0
  return -1;
213
0
    }
214
0
    *dlist = NULL;
215
0
    while ((dent = readdir (d))) {
216
0
  if (!filter || (filter)(dent)) {
217
0
      size_t dentlen = FcPtrToOffset (dent, dent->d_name) + strlen (dent->d_name) + 1;
218
0
      dentlen = ((dentlen + ALIGNOF_VOID_P - 1) & ~(ALIGNOF_VOID_P - 1));
219
0
      p = (struct dirent *)malloc (dentlen);
220
0
      if (!p) {
221
0
    free_dirent (dlist);
222
0
    closedir (d);
223
0
    errno = ENOMEM;
224
225
0
    return -1;
226
0
      }
227
0
      memcpy (p, dent, dentlen);
228
0
      if ((n + 1) >= lsize) {
229
0
    lsize += 128;
230
0
    dlp = (struct dirent **)realloc (dlist, sizeof (struct dirent *) * lsize);
231
0
    if (!dlp) {
232
0
        free (p);
233
0
        free_dirent (dlist);
234
0
        closedir (d);
235
0
        errno = ENOMEM;
236
237
0
        return -1;
238
0
    }
239
0
    dlist = dlp;
240
0
      }
241
0
      dlist[n++] = p;
242
0
      dlist[n] = NULL;
243
0
  }
244
0
    }
245
0
    closedir (d);
246
247
0
    qsort (dlist, n, sizeof (struct dirent *), (int (*) (const void *, const void *))compar);
248
249
0
    *namelist = dlist;
250
251
0
    return n;
252
0
}
253
254
static int
255
FcDirChecksum (const FcChar8 *dir, time_t *checksum)
256
0
{
257
0
    struct Adler32  ctx;
258
0
    struct dirent **files;
259
0
    int             n;
260
0
    int             ret = 0;
261
0
    size_t          len = strlen ((const char *)dir);
262
263
0
    Adler32Init (&ctx);
264
265
0
    n = FcScandir ((const char *)dir, &files,
266
0
#  ifdef HAVE_STRUCT_DIRENT_D_TYPE
267
0
                   &FcDirChecksumScandirFilter,
268
#  else
269
                   NULL,
270
#  endif
271
0
                   &FcDirChecksumScandirSorter);
272
0
    if (n == -1)
273
0
  return -1;
274
275
0
    while (n--) {
276
0
  size_t dlen = strlen (files[n]->d_name);
277
0
  int    dtype;
278
279
0
#  ifdef HAVE_STRUCT_DIRENT_D_TYPE
280
0
  dtype = files[n]->d_type;
281
0
  if (dtype == DT_UNKNOWN) {
282
0
#  endif
283
0
      struct stat statb;
284
0
      char       *f = malloc (len + 1 + dlen + 1);
285
286
0
      if (!f) {
287
0
    ret = -1;
288
0
    goto bail;
289
0
      }
290
0
      memcpy (f, dir, len);
291
0
      f[len] = FC_DIR_SEPARATOR;
292
0
      memcpy (&f[len + 1], files[n]->d_name, dlen);
293
0
      f[len + 1 + dlen] = 0;
294
0
      if (lstat (f, &statb) < 0) {
295
0
    ret = -1;
296
0
    free (f);
297
0
    goto bail;
298
0
      }
299
0
      if (S_ISDIR (statb.st_mode)) {
300
0
    free (f);
301
0
    goto bail;
302
0
      }
303
304
0
      free (f);
305
0
      dtype = statb.st_mode;
306
0
#  ifdef HAVE_STRUCT_DIRENT_D_TYPE
307
0
  }
308
0
#  endif
309
0
  Adler32Update (&ctx, files[n]->d_name, dlen + 1);
310
0
  Adler32Update (&ctx, (char *)&dtype, sizeof (int));
311
312
0
    bail:
313
0
  free (files[n]);
314
0
    }
315
0
    free (files);
316
0
    if (ret == -1)
317
0
  return -1;
318
319
0
    *checksum = Adler32Finish (&ctx);
320
321
0
    return 0;
322
0
}
323
#endif /* _WIN32 */
324
325
int
326
FcStatChecksum (const FcChar8 *file, struct stat *statb)
327
531
{
328
531
    if (FcStat (file, statb) == -1)
329
424
  return -1;
330
331
107
#ifndef _WIN32
332
    /* We have a workaround of the broken stat() in FcStat() for Win32.
333
     * No need to do something further more.
334
     */
335
107
    if (FcIsFsMtimeBroken (file)) {
336
0
  if (FcDirChecksum (file, &statb->st_mtime) == -1)
337
0
      return -1;
338
0
    }
339
107
#endif
340
341
107
    return 0;
342
107
}
343
344
static int
345
FcFStatFs (int fd, FcStatFS *statb)
346
212
{
347
212
    const char *p = NULL;
348
212
    int         ret = -1;
349
212
    FcBool      flag = FcFalse;
350
351
#if defined(HAVE_FSTATVFS) && (defined(HAVE_STRUCT_STATVFS_F_BASETYPE) || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME))
352
    struct statvfs buf;
353
354
    memset (statb, 0, sizeof (FcStatFS));
355
356
    if ((ret = fstatvfs (fd, &buf)) == 0) {
357
#  if defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
358
  p = buf.f_basetype;
359
#  elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
360
  p = buf.f_fstypename;
361
#  endif
362
    }
363
#elif defined(HAVE_FSTATFS) && (defined(HAVE_STRUCT_STATFS_F_FLAGS) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) || defined(__linux__))
364
    struct statfs buf;
365
366
212
    memset (statb, 0, sizeof (FcStatFS));
367
368
212
    if ((ret = fstatfs (fd, &buf)) == 0) {
369
#  if defined(HAVE_STRUCT_STATFS_F_FLAGS) && defined(MNT_LOCAL)
370
  statb->is_remote_fs = !(buf.f_flags & MNT_LOCAL);
371
  flag = FcTrue;
372
#  endif
373
#  if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
374
  p = buf.f_fstypename;
375
#  elif defined(__linux__) || defined(__EMSCRIPTEN__)
376
  switch (buf.f_type) {
377
0
  case 0x6969: /* nfs */
378
0
      statb->is_remote_fs = FcTrue;
379
0
      break;
380
0
  case 0x4d44: /* fat */
381
0
      statb->is_mtime_broken = FcTrue;
382
0
      break;
383
212
  default:
384
212
      break;
385
212
  }
386
387
212
  return ret;
388
#  else
389
#    error "BUG: No way to figure out with fstatfs()"
390
#  endif
391
212
    }
392
0
#endif
393
0
    if (p) {
394
0
  if (!flag && strcmp (p, "nfs") == 0)
395
0
      statb->is_remote_fs = FcTrue;
396
0
  if (strcmp (p, "msdosfs") == 0 ||
397
0
      strcmp (p, "pcfs") == 0)
398
0
      statb->is_mtime_broken = FcTrue;
399
0
    }
400
401
0
    return ret;
402
212
}
403
404
FcBool
405
FcIsFsMmapSafe (int fd)
406
105
{
407
105
    FcStatFS statb;
408
409
105
    if (FcFStatFs (fd, &statb) < 0)
410
0
  return FcTrue;
411
412
105
    return !statb.is_remote_fs;
413
105
}
414
415
FcBool
416
FcIsFsMtimeBroken (const FcChar8 *dir)
417
107
{
418
107
    int fd = FcOpen ((const char *)dir, O_RDONLY);
419
420
107
    if (fd != -1) {
421
107
  FcStatFS statb;
422
107
  int      ret = FcFStatFs (fd, &statb);
423
424
107
  close (fd);
425
107
  if (ret < 0)
426
0
      return FcFalse;
427
428
107
  return statb.is_mtime_broken;
429
107
    }
430
431
0
    return FcFalse;
432
107
}
433
434
#define __fcstat__
435
#include "fcaliastail.h"
436
#undef __fcstat__