1ca08ab68Smrg/*
2ca08ab68Smrg * Copyright © 2000 Keith Packard
3ca08ab68Smrg * Copyright © 2005 Patrick Lam
4ca08ab68Smrg *
5ca08ab68Smrg * Permission to use, copy, modify, distribute, and sell this software and its
6ca08ab68Smrg * documentation for any purpose is hereby granted without fee, provided that
7ca08ab68Smrg * the above copyright notice appear in all copies and that both that
8ca08ab68Smrg * copyright notice and this permission notice appear in supporting
9ca08ab68Smrg * documentation, and that the name of the author(s) not be used in
10ca08ab68Smrg * advertising or publicity pertaining to distribution of the software without
11ca08ab68Smrg * specific, written prior permission.  The authors make no
12ca08ab68Smrg * representations about the suitability of this software for any purpose.  It
13ca08ab68Smrg * is provided "as is" without express or implied warranty.
14ca08ab68Smrg *
15ca08ab68Smrg * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16ca08ab68Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17ca08ab68Smrg * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18ca08ab68Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19ca08ab68Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20ca08ab68Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21ca08ab68Smrg * PERFORMANCE OF THIS SOFTWARE.
22ca08ab68Smrg */
23ca08ab68Smrg#include "fcint.h"
24ca08ab68Smrg#include "fcarch.h"
257872e0a1Smrg#ifdef HAVE_DIRENT_H
26ca08ab68Smrg#include <dirent.h>
277872e0a1Smrg#endif
28ca08ab68Smrg#include <limits.h>
29ca08ab68Smrg#include <sys/types.h>
30ca08ab68Smrg#include <sys/stat.h>
31ca08ab68Smrg#include <fcntl.h>
32ca08ab68Smrg#ifdef HAVE_SYS_VFS_H
33ca08ab68Smrg#include <sys/vfs.h>
34ca08ab68Smrg#endif
35b2a52337Smrg#ifdef HAVE_SYS_STATVFS_H
36b2a52337Smrg#include <sys/statvfs.h>
37b2a52337Smrg#endif
38ca08ab68Smrg#ifdef HAVE_SYS_STATFS_H
39ca08ab68Smrg#include <sys/statfs.h>
40ca08ab68Smrg#endif
41ca08ab68Smrg#ifdef HAVE_SYS_PARAM_H
42ca08ab68Smrg#include <sys/param.h>
43ca08ab68Smrg#endif
44ca08ab68Smrg#ifdef HAVE_SYS_MOUNT_H
45ca08ab68Smrg#include <sys/mount.h>
46ca08ab68Smrg#endif
4718bd4a06Smrg#include <errno.h>
48ca08ab68Smrg
49ca08ab68Smrg#ifdef _WIN32
50ca08ab68Smrg#ifdef __GNUC__
51ca08ab68Smrgtypedef long long INT64;
52ca08ab68Smrg#define EPOCH_OFFSET 11644473600ll
53ca08ab68Smrg#else
54ca08ab68Smrg#define EPOCH_OFFSET 11644473600i64
55ca08ab68Smrgtypedef __int64 INT64;
56ca08ab68Smrg#endif
57ca08ab68Smrg
58ca08ab68Smrg/* Workaround for problems in the stat() in the Microsoft C library:
59ca08ab68Smrg *
60ca08ab68Smrg * 1) stat() uses FindFirstFile() to get the file
61ca08ab68Smrg * attributes. Unfortunately this API doesn't return correct values
62ca08ab68Smrg * for modification time of a directory until some time after a file
63ca08ab68Smrg * or subdirectory has been added to the directory. (This causes
64ca08ab68Smrg * run-test.sh to fail, for instance.) GetFileAttributesEx() is
65ca08ab68Smrg * better, it returns the updated timestamp right away.
66ca08ab68Smrg *
67ca08ab68Smrg * 2) stat() does some strange things related to backward
68ca08ab68Smrg * compatibility with the local time timestamps on FAT volumes and
69ca08ab68Smrg * daylight saving time. This causes problems after the switches
70ca08ab68Smrg * to/from daylight saving time. See
71ca08ab68Smrg * http://bugzilla.gnome.org/show_bug.cgi?id=154968 , especially
72ca08ab68Smrg * comment #30, and http://www.codeproject.com/datetime/dstbugs.asp .
73ca08ab68Smrg * We don't need any of that, FAT and Win9x are as good as dead. So
74ca08ab68Smrg * just use the UTC timestamps from NTFS, converted to the Unix epoch.
75ca08ab68Smrg */
76ca08ab68Smrg
77ca08ab68Smrgint
78ca08ab68SmrgFcStat (const FcChar8 *file, struct stat *statb)
79ca08ab68Smrg{
80ca08ab68Smrg    WIN32_FILE_ATTRIBUTE_DATA wfad;
81ca08ab68Smrg    char full_path_name[MAX_PATH];
82ca08ab68Smrg    char *basename;
83ca08ab68Smrg    DWORD rc;
84ca08ab68Smrg
85ca08ab68Smrg    if (!GetFileAttributesEx ((LPCSTR) file, GetFileExInfoStandard, &wfad))
86ca08ab68Smrg	return -1;
87ca08ab68Smrg
88ca08ab68Smrg    statb->st_dev = 0;
89ca08ab68Smrg
90ca08ab68Smrg    /* Calculate a pseudo inode number as a hash of the full path name.
91ca08ab68Smrg     * Call GetLongPathName() to get the spelling of the path name as it
92ca08ab68Smrg     * is on disk.
93ca08ab68Smrg     */
94ca08ab68Smrg    rc = GetFullPathName ((LPCSTR) file, sizeof (full_path_name), full_path_name, &basename);
95ca08ab68Smrg    if (rc == 0 || rc > sizeof (full_path_name))
96ca08ab68Smrg	return -1;
97ca08ab68Smrg
98ca08ab68Smrg    rc = GetLongPathName (full_path_name, full_path_name, sizeof (full_path_name));
99ca08ab68Smrg    statb->st_ino = FcStringHash ((const FcChar8 *) full_path_name);
100ca08ab68Smrg
101ca08ab68Smrg    statb->st_mode = _S_IREAD | _S_IWRITE;
102ca08ab68Smrg    statb->st_mode |= (statb->st_mode >> 3) | (statb->st_mode >> 6);
103ca08ab68Smrg
104ca08ab68Smrg    if (wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
105ca08ab68Smrg	statb->st_mode |= _S_IFDIR;
106ca08ab68Smrg    else
107ca08ab68Smrg	statb->st_mode |= _S_IFREG;
108ca08ab68Smrg
109ca08ab68Smrg    statb->st_nlink = 1;
110ca08ab68Smrg    statb->st_uid = statb->st_gid = 0;
111ca08ab68Smrg    statb->st_rdev = 0;
112ca08ab68Smrg
113ca08ab68Smrg    if (wfad.nFileSizeHigh > 0)
114ca08ab68Smrg	return -1;
115ca08ab68Smrg    statb->st_size = wfad.nFileSizeLow;
116ca08ab68Smrg
117ca08ab68Smrg    statb->st_atime = (*(INT64 *)&wfad.ftLastAccessTime)/10000000 - EPOCH_OFFSET;
118ca08ab68Smrg    statb->st_mtime = (*(INT64 *)&wfad.ftLastWriteTime)/10000000 - EPOCH_OFFSET;
119ca08ab68Smrg    statb->st_ctime = statb->st_mtime;
120ca08ab68Smrg
121ca08ab68Smrg    return 0;
122ca08ab68Smrg}
123ca08ab68Smrg
124ca08ab68Smrg#else
125ca08ab68Smrg
126ca08ab68Smrgint
127ca08ab68SmrgFcStat (const FcChar8 *file, struct stat *statb)
128ca08ab68Smrg{
129ca08ab68Smrg  return stat ((char *) file, statb);
130ca08ab68Smrg}
131ca08ab68Smrg
132ca08ab68Smrg/* Adler-32 checksum implementation */
133ca08ab68Smrgstruct Adler32 {
134ca08ab68Smrg    int a;
135ca08ab68Smrg    int b;
136ca08ab68Smrg};
137ca08ab68Smrg
138ca08ab68Smrgstatic void
139ca08ab68SmrgAdler32Init (struct Adler32 *ctx)
140ca08ab68Smrg{
141ca08ab68Smrg    ctx->a = 1;
142ca08ab68Smrg    ctx->b = 0;
143ca08ab68Smrg}
144ca08ab68Smrg
145ca08ab68Smrgstatic void
146ca08ab68SmrgAdler32Update (struct Adler32 *ctx, const char *data, int data_len)
147ca08ab68Smrg{
148ca08ab68Smrg    while (data_len--)
149ca08ab68Smrg    {
150ca08ab68Smrg	ctx->a = (ctx->a + *data++) % 65521;
151ca08ab68Smrg	ctx->b = (ctx->b + ctx->a) % 65521;
152ca08ab68Smrg    }
153ca08ab68Smrg}
154ca08ab68Smrg
155ca08ab68Smrgstatic int
156ca08ab68SmrgAdler32Finish (struct Adler32 *ctx)
157ca08ab68Smrg{
158ca08ab68Smrg    return ctx->a + (ctx->b << 16);
159ca08ab68Smrg}
160ca08ab68Smrg
161ca08ab68Smrg#ifdef HAVE_STRUCT_DIRENT_D_TYPE
162ca08ab68Smrg/* dirent.d_type can be relied upon on FAT filesystem */
163ca08ab68Smrgstatic FcBool
164ca08ab68SmrgFcDirChecksumScandirFilter(const struct dirent *entry)
165ca08ab68Smrg{
166ca08ab68Smrg    return entry->d_type != DT_DIR;
167ca08ab68Smrg}
168ca08ab68Smrg#endif
169ca08ab68Smrg
170ca08ab68Smrgstatic int
171ea107527SmartinFcDirChecksumScandirSorter(const struct dirent **lhs, const struct dirent **rhs)
172ca08ab68Smrg{
173ca08ab68Smrg    return strcmp((*lhs)->d_name, (*rhs)->d_name);
174ca08ab68Smrg}
17518bd4a06Smrg
17618bd4a06Smrgstatic void
17718bd4a06Smrgfree_dirent (struct dirent **p)
178d91dd368Smrg{
17918bd4a06Smrg    struct dirent **x;
18018bd4a06Smrg
18118bd4a06Smrg    for (x = p; *x != NULL; x++)
18218bd4a06Smrg	free (*x);
183d91dd368Smrg
18418bd4a06Smrg    free (p);
18518bd4a06Smrg}
18618bd4a06Smrg
18718bd4a06Smrgint
18818bd4a06SmrgFcScandir (const char		*dirp,
18918bd4a06Smrg	   struct dirent	***namelist,
19018bd4a06Smrg	   int (*filter) (const struct dirent *),
19118bd4a06Smrg	   int (*compar) (const struct dirent **, const struct dirent **));
19218bd4a06Smrg
19318bd4a06Smrgint
19418bd4a06SmrgFcScandir (const char		*dirp,
19518bd4a06Smrg	   struct dirent	***namelist,
19618bd4a06Smrg	   int (*filter) (const struct dirent *),
19718bd4a06Smrg	   int (*compar) (const struct dirent **, const struct dirent **))
19818bd4a06Smrg{
19918bd4a06Smrg    DIR *d;
20018bd4a06Smrg    struct dirent *dent, *p, **dlist, **dlp;
20118bd4a06Smrg    size_t lsize = 128, n = 0;
20218bd4a06Smrg
20318bd4a06Smrg    d = opendir (dirp);
20418bd4a06Smrg    if (!d)
20518bd4a06Smrg	return -1;
20618bd4a06Smrg
20729c28367Schristos    dlist = malloc (sizeof (struct dirent *) * lsize);
20818bd4a06Smrg    if (!dlist)
20918bd4a06Smrg    {
21018bd4a06Smrg	closedir (d);
21118bd4a06Smrg	errno = ENOMEM;
21218bd4a06Smrg
21318bd4a06Smrg	return -1;
21418bd4a06Smrg    }
21518bd4a06Smrg    *dlist = NULL;
21618bd4a06Smrg    while ((dent = readdir (d)))
21718bd4a06Smrg    {
21818bd4a06Smrg	if (!filter || (filter) (dent))
21918bd4a06Smrg	{
22018bd4a06Smrg	    size_t dentlen = FcPtrToOffset (dent, dent->d_name) + strlen (dent->d_name) + 1;
22118bd4a06Smrg	    dentlen = ((dentlen + ALIGNOF_VOID_P - 1) & ~(ALIGNOF_VOID_P - 1));
2221887081fSmrg	    p = (struct dirent *) malloc (dentlen);
2231887081fSmrg	    if (!p)
2241887081fSmrg	    {
2251887081fSmrg		free_dirent (dlist);
2261887081fSmrg		closedir (d);
2271887081fSmrg		errno = ENOMEM;
2281887081fSmrg
2291887081fSmrg		return -1;
2301887081fSmrg	    }
23118bd4a06Smrg	    memcpy (p, dent, dentlen);
23218bd4a06Smrg	    if ((n + 1) >= lsize)
23318bd4a06Smrg	    {
23418bd4a06Smrg		lsize += 128;
2351887081fSmrg		dlp = (struct dirent **) realloc (dlist, sizeof (struct dirent *) * lsize);
23618bd4a06Smrg		if (!dlp)
23718bd4a06Smrg		{
2381887081fSmrg		    free (p);
23918bd4a06Smrg		    free_dirent (dlist);
24018bd4a06Smrg		    closedir (d);
24118bd4a06Smrg		    errno = ENOMEM;
24218bd4a06Smrg
24318bd4a06Smrg		    return -1;
24418bd4a06Smrg		}
24518bd4a06Smrg		dlist = dlp;
24618bd4a06Smrg	    }
24718bd4a06Smrg	    dlist[n++] = p;
24818bd4a06Smrg	    dlist[n] = NULL;
24918bd4a06Smrg	}
25018bd4a06Smrg    }
25118bd4a06Smrg    closedir (d);
25218bd4a06Smrg
25318bd4a06Smrg    qsort (dlist, n, sizeof (struct dirent *), (int (*) (const void *, const void *))compar);
25418bd4a06Smrg
25518bd4a06Smrg    *namelist = dlist;
25618bd4a06Smrg
25718bd4a06Smrg    return n;
258d91dd368Smrg}
259ca08ab68Smrg
260ca08ab68Smrgstatic int
261ca08ab68SmrgFcDirChecksum (const FcChar8 *dir, time_t *checksum)
262ca08ab68Smrg{
263ca08ab68Smrg    struct Adler32 ctx;
264ca08ab68Smrg    struct dirent **files;
2655e61939bSmrg    int n;
2665e61939bSmrg    int ret = 0;
267ca08ab68Smrg    size_t len = strlen ((const char *)dir);
268ca08ab68Smrg
269ca08ab68Smrg    Adler32Init (&ctx);
270ca08ab68Smrg
27118bd4a06Smrg    n = FcScandir ((const char *)dir, &files,
272ca08ab68Smrg#ifdef HAVE_STRUCT_DIRENT_D_TYPE
273ca08ab68Smrg		 &FcDirChecksumScandirFilter,
274ca08ab68Smrg#else
275ca08ab68Smrg		 NULL,
276ca08ab68Smrg#endif
277ca08ab68Smrg		 &FcDirChecksumScandirSorter);
278ca08ab68Smrg    if (n == -1)
279ca08ab68Smrg	return -1;
280ca08ab68Smrg
281ca08ab68Smrg    while (n--)
282ca08ab68Smrg    {
283ca08ab68Smrg	size_t dlen = strlen (files[n]->d_name);
284ca08ab68Smrg	int dtype;
285ca08ab68Smrg
286ca08ab68Smrg#ifdef HAVE_STRUCT_DIRENT_D_TYPE
287ca08ab68Smrg	dtype = files[n]->d_type;
288b2a52337Smrg	if (dtype == DT_UNKNOWN)
289b2a52337Smrg	{
290b2a52337Smrg#endif
291ca08ab68Smrg	struct stat statb;
29218bd4a06Smrg	char *f = malloc (len + 1 + dlen + 1);
293ca08ab68Smrg
29418bd4a06Smrg	if (!f)
29518bd4a06Smrg	{
29618bd4a06Smrg	    ret = -1;
29718bd4a06Smrg	    goto bail;
29818bd4a06Smrg	}
299ca08ab68Smrg	memcpy (f, dir, len);
300ca08ab68Smrg	f[len] = FC_DIR_SEPARATOR;
301ca08ab68Smrg	memcpy (&f[len + 1], files[n]->d_name, dlen);
302ca08ab68Smrg	f[len + 1 + dlen] = 0;
303ca08ab68Smrg	if (lstat (f, &statb) < 0)
304ca08ab68Smrg	{
305ca08ab68Smrg	    ret = -1;
30618bd4a06Smrg	    free (f);
307ca08ab68Smrg	    goto bail;
308ca08ab68Smrg	}
309ca08ab68Smrg	if (S_ISDIR (statb.st_mode))
31018bd4a06Smrg	{
31118bd4a06Smrg	    free (f);
312ca08ab68Smrg	    goto bail;
31318bd4a06Smrg	}
314ca08ab68Smrg
31518bd4a06Smrg	free (f);
316ca08ab68Smrg	dtype = statb.st_mode;
317b2a52337Smrg#ifdef HAVE_STRUCT_DIRENT_D_TYPE
318b2a52337Smrg	}
319ca08ab68Smrg#endif
320ca08ab68Smrg	Adler32Update (&ctx, files[n]->d_name, dlen + 1);
321ca08ab68Smrg	Adler32Update (&ctx, (char *)&dtype, sizeof (int));
322ca08ab68Smrg
323ca08ab68Smrg      bail:
324ca08ab68Smrg	free (files[n]);
325ca08ab68Smrg    }
326ca08ab68Smrg    free (files);
327ca08ab68Smrg    if (ret == -1)
328ca08ab68Smrg	return -1;
329ca08ab68Smrg
330ca08ab68Smrg    *checksum = Adler32Finish (&ctx);
331ca08ab68Smrg
332ca08ab68Smrg    return 0;
333ca08ab68Smrg}
334ca08ab68Smrg#endif /* _WIN32 */
335ca08ab68Smrg
336ca08ab68Smrgint
337ca08ab68SmrgFcStatChecksum (const FcChar8 *file, struct stat *statb)
338ca08ab68Smrg{
339ca08ab68Smrg    if (FcStat (file, statb) == -1)
340ca08ab68Smrg        return -1;
341ca08ab68Smrg
342ca08ab68Smrg#ifndef _WIN32
343ca08ab68Smrg    /* We have a workaround of the broken stat() in FcStat() for Win32.
344ca08ab68Smrg     * No need to do something further more.
345ca08ab68Smrg     */
346ca08ab68Smrg    if (FcIsFsMtimeBroken (file))
347ca08ab68Smrg    {
348ca08ab68Smrg        if (FcDirChecksum (file, &statb->st_mtime) == -1)
349ca08ab68Smrg            return -1;
350ca08ab68Smrg    }
351ca08ab68Smrg#endif
352ca08ab68Smrg
353ca08ab68Smrg    return 0;
354ca08ab68Smrg}
355ca08ab68Smrg
356ca08ab68Smrgstatic int
357ca08ab68SmrgFcFStatFs (int fd, FcStatFS *statb)
358ca08ab68Smrg{
359ca08ab68Smrg    const char *p = NULL;
360ca08ab68Smrg    int ret = -1;
361ca08ab68Smrg    FcBool flag = FcFalse;
362ca08ab68Smrg
363ca08ab68Smrg#if defined(HAVE_FSTATVFS) && (defined(HAVE_STRUCT_STATVFS_F_BASETYPE) || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME))
364ca08ab68Smrg    struct statvfs buf;
365ca08ab68Smrg
3665e61939bSmrg    memset (statb, 0, sizeof (FcStatFS));
3675e61939bSmrg
368ca08ab68Smrg    if ((ret = fstatvfs (fd, &buf)) == 0)
369ca08ab68Smrg    {
370ca08ab68Smrg#  if defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
371ca08ab68Smrg	p = buf.f_basetype;
372ca08ab68Smrg#  elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
373ca08ab68Smrg	p = buf.f_fstypename;
374ca08ab68Smrg#  endif
375ca08ab68Smrg    }
376ca08ab68Smrg#elif defined(HAVE_FSTATFS) && (defined(HAVE_STRUCT_STATFS_F_FLAGS) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) || defined(__linux__))
377ca08ab68Smrg    struct statfs buf;
378ca08ab68Smrg
3795e61939bSmrg    memset (statb, 0, sizeof (FcStatFS));
3805e61939bSmrg
381ca08ab68Smrg    if ((ret = fstatfs (fd, &buf)) == 0)
382ca08ab68Smrg    {
383ca08ab68Smrg#  if defined(HAVE_STRUCT_STATFS_F_FLAGS) && defined(MNT_LOCAL)
384ca08ab68Smrg	statb->is_remote_fs = !(buf.f_flags & MNT_LOCAL);
385ca08ab68Smrg	flag = FcTrue;
386ca08ab68Smrg#  endif
387ca08ab68Smrg#  if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
388ca08ab68Smrg	p = buf.f_fstypename;
3897872e0a1Smrg#  elif defined(__linux__) || defined (__EMSCRIPTEN__)
390ca08ab68Smrg	switch (buf.f_type)
391ca08ab68Smrg	{
392ca08ab68Smrg	case 0x6969: /* nfs */
393ca08ab68Smrg	    statb->is_remote_fs = FcTrue;
394ca08ab68Smrg	    break;
395ca08ab68Smrg	case 0x4d44: /* fat */
396ca08ab68Smrg	    statb->is_mtime_broken = FcTrue;
397ca08ab68Smrg	    break;
398ca08ab68Smrg	default:
399ca08ab68Smrg	    break;
400ca08ab68Smrg	}
401ca08ab68Smrg
402ca08ab68Smrg	return ret;
403ca08ab68Smrg#  else
404ca08ab68Smrg#    error "BUG: No way to figure out with fstatfs()"
405ca08ab68Smrg#  endif
406ca08ab68Smrg    }
407ca08ab68Smrg#endif
408ca08ab68Smrg    if (p)
409ca08ab68Smrg    {
410ca08ab68Smrg	if (!flag && strcmp (p, "nfs") == 0)
411ca08ab68Smrg	    statb->is_remote_fs = FcTrue;
412ca08ab68Smrg	if (strcmp (p, "msdosfs") == 0 ||
413ca08ab68Smrg	    strcmp (p, "pcfs") == 0)
414ca08ab68Smrg	    statb->is_mtime_broken = FcTrue;
415ca08ab68Smrg    }
416ca08ab68Smrg
417ca08ab68Smrg    return ret;
418ca08ab68Smrg}
419ca08ab68Smrg
420ca08ab68SmrgFcBool
421ca08ab68SmrgFcIsFsMmapSafe (int fd)
422ca08ab68Smrg{
423ca08ab68Smrg    FcStatFS statb;
424ca08ab68Smrg
425ca08ab68Smrg    if (FcFStatFs (fd, &statb) < 0)
426ca08ab68Smrg	return FcTrue;
427ca08ab68Smrg
428ca08ab68Smrg    return !statb.is_remote_fs;
429ca08ab68Smrg}
430ca08ab68Smrg
431ca08ab68SmrgFcBool
432ca08ab68SmrgFcIsFsMtimeBroken (const FcChar8 *dir)
433ca08ab68Smrg{
4345e61939bSmrg    int fd = FcOpen ((const char *) dir, O_RDONLY);
435ca08ab68Smrg
436ca08ab68Smrg    if (fd != -1)
437ca08ab68Smrg    {
438ca08ab68Smrg	FcStatFS statb;
439ca08ab68Smrg	int ret = FcFStatFs (fd, &statb);
440ca08ab68Smrg
441ca08ab68Smrg	close (fd);
442ca08ab68Smrg	if (ret < 0)
443ca08ab68Smrg	    return FcFalse;
444ca08ab68Smrg
445ca08ab68Smrg	return statb.is_mtime_broken;
446ca08ab68Smrg    }
447ca08ab68Smrg
448ca08ab68Smrg    return FcFalse;
449ca08ab68Smrg}
45018bd4a06Smrg
45118bd4a06Smrg#define __fcstat__
45218bd4a06Smrg#include "fcaliastail.h"
45318bd4a06Smrg#undef __fcstat__
454