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