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