fcstat.c revision 18bd4a06
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" 25ca08ab68Smrg#include <dirent.h> 26ca08ab68Smrg#include <limits.h> 27ca08ab68Smrg#include <sys/types.h> 28ca08ab68Smrg#include <sys/stat.h> 29ca08ab68Smrg#include <fcntl.h> 30ca08ab68Smrg#ifdef HAVE_SYS_VFS_H 31ca08ab68Smrg#include <sys/vfs.h> 32ca08ab68Smrg#endif 33b2a52337Smrg#ifdef HAVE_SYS_STATVFS_H 34b2a52337Smrg#include <sys/statvfs.h> 35b2a52337Smrg#endif 36ca08ab68Smrg#ifdef HAVE_SYS_STATFS_H 37ca08ab68Smrg#include <sys/statfs.h> 38ca08ab68Smrg#endif 39ca08ab68Smrg#ifdef HAVE_SYS_PARAM_H 40ca08ab68Smrg#include <sys/param.h> 41ca08ab68Smrg#endif 42ca08ab68Smrg#ifdef HAVE_SYS_MOUNT_H 43ca08ab68Smrg#include <sys/mount.h> 44ca08ab68Smrg#endif 4518bd4a06Smrg#include <errno.h> 46ca08ab68Smrg 47ca08ab68Smrg#ifdef _WIN32 48ca08ab68Smrg#ifdef __GNUC__ 49ca08ab68Smrgtypedef long long INT64; 50ca08ab68Smrg#define EPOCH_OFFSET 11644473600ll 51ca08ab68Smrg#else 52ca08ab68Smrg#define EPOCH_OFFSET 11644473600i64 53ca08ab68Smrgtypedef __int64 INT64; 54ca08ab68Smrg#endif 55ca08ab68Smrg 56ca08ab68Smrg/* Workaround for problems in the stat() in the Microsoft C library: 57ca08ab68Smrg * 58ca08ab68Smrg * 1) stat() uses FindFirstFile() to get the file 59ca08ab68Smrg * attributes. Unfortunately this API doesn't return correct values 60ca08ab68Smrg * for modification time of a directory until some time after a file 61ca08ab68Smrg * or subdirectory has been added to the directory. (This causes 62ca08ab68Smrg * run-test.sh to fail, for instance.) GetFileAttributesEx() is 63ca08ab68Smrg * better, it returns the updated timestamp right away. 64ca08ab68Smrg * 65ca08ab68Smrg * 2) stat() does some strange things related to backward 66ca08ab68Smrg * compatibility with the local time timestamps on FAT volumes and 67ca08ab68Smrg * daylight saving time. This causes problems after the switches 68ca08ab68Smrg * to/from daylight saving time. See 69ca08ab68Smrg * http://bugzilla.gnome.org/show_bug.cgi?id=154968 , especially 70ca08ab68Smrg * comment #30, and http://www.codeproject.com/datetime/dstbugs.asp . 71ca08ab68Smrg * We don't need any of that, FAT and Win9x are as good as dead. So 72ca08ab68Smrg * just use the UTC timestamps from NTFS, converted to the Unix epoch. 73ca08ab68Smrg */ 74ca08ab68Smrg 75ca08ab68Smrgint 76ca08ab68SmrgFcStat (const FcChar8 *file, struct stat *statb) 77ca08ab68Smrg{ 78ca08ab68Smrg WIN32_FILE_ATTRIBUTE_DATA wfad; 79ca08ab68Smrg char full_path_name[MAX_PATH]; 80ca08ab68Smrg char *basename; 81ca08ab68Smrg DWORD rc; 82ca08ab68Smrg 83ca08ab68Smrg if (!GetFileAttributesEx ((LPCSTR) file, GetFileExInfoStandard, &wfad)) 84ca08ab68Smrg return -1; 85ca08ab68Smrg 86ca08ab68Smrg statb->st_dev = 0; 87ca08ab68Smrg 88ca08ab68Smrg /* Calculate a pseudo inode number as a hash of the full path name. 89ca08ab68Smrg * Call GetLongPathName() to get the spelling of the path name as it 90ca08ab68Smrg * is on disk. 91ca08ab68Smrg */ 92ca08ab68Smrg rc = GetFullPathName ((LPCSTR) file, sizeof (full_path_name), full_path_name, &basename); 93ca08ab68Smrg if (rc == 0 || rc > sizeof (full_path_name)) 94ca08ab68Smrg return -1; 95ca08ab68Smrg 96ca08ab68Smrg rc = GetLongPathName (full_path_name, full_path_name, sizeof (full_path_name)); 97ca08ab68Smrg statb->st_ino = FcStringHash ((const FcChar8 *) full_path_name); 98ca08ab68Smrg 99ca08ab68Smrg statb->st_mode = _S_IREAD | _S_IWRITE; 100ca08ab68Smrg statb->st_mode |= (statb->st_mode >> 3) | (statb->st_mode >> 6); 101ca08ab68Smrg 102ca08ab68Smrg if (wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 103ca08ab68Smrg statb->st_mode |= _S_IFDIR; 104ca08ab68Smrg else 105ca08ab68Smrg statb->st_mode |= _S_IFREG; 106ca08ab68Smrg 107ca08ab68Smrg statb->st_nlink = 1; 108ca08ab68Smrg statb->st_uid = statb->st_gid = 0; 109ca08ab68Smrg statb->st_rdev = 0; 110ca08ab68Smrg 111ca08ab68Smrg if (wfad.nFileSizeHigh > 0) 112ca08ab68Smrg return -1; 113ca08ab68Smrg statb->st_size = wfad.nFileSizeLow; 114ca08ab68Smrg 115ca08ab68Smrg statb->st_atime = (*(INT64 *)&wfad.ftLastAccessTime)/10000000 - EPOCH_OFFSET; 116ca08ab68Smrg statb->st_mtime = (*(INT64 *)&wfad.ftLastWriteTime)/10000000 - EPOCH_OFFSET; 117ca08ab68Smrg statb->st_ctime = statb->st_mtime; 118ca08ab68Smrg 119ca08ab68Smrg return 0; 120ca08ab68Smrg} 121ca08ab68Smrg 122ca08ab68Smrg#else 123ca08ab68Smrg 124ca08ab68Smrgint 125ca08ab68SmrgFcStat (const FcChar8 *file, struct stat *statb) 126ca08ab68Smrg{ 127ca08ab68Smrg return stat ((char *) file, statb); 128ca08ab68Smrg} 129ca08ab68Smrg 130ca08ab68Smrg/* Adler-32 checksum implementation */ 131ca08ab68Smrgstruct Adler32 { 132ca08ab68Smrg int a; 133ca08ab68Smrg int b; 134ca08ab68Smrg}; 135ca08ab68Smrg 136ca08ab68Smrgstatic void 137ca08ab68SmrgAdler32Init (struct Adler32 *ctx) 138ca08ab68Smrg{ 139ca08ab68Smrg ctx->a = 1; 140ca08ab68Smrg ctx->b = 0; 141ca08ab68Smrg} 142ca08ab68Smrg 143ca08ab68Smrgstatic void 144ca08ab68SmrgAdler32Update (struct Adler32 *ctx, const char *data, int data_len) 145ca08ab68Smrg{ 146ca08ab68Smrg while (data_len--) 147ca08ab68Smrg { 148ca08ab68Smrg ctx->a = (ctx->a + *data++) % 65521; 149ca08ab68Smrg ctx->b = (ctx->b + ctx->a) % 65521; 150ca08ab68Smrg } 151ca08ab68Smrg} 152ca08ab68Smrg 153ca08ab68Smrgstatic int 154ca08ab68SmrgAdler32Finish (struct Adler32 *ctx) 155ca08ab68Smrg{ 156ca08ab68Smrg return ctx->a + (ctx->b << 16); 157ca08ab68Smrg} 158ca08ab68Smrg 159ca08ab68Smrg#ifdef HAVE_STRUCT_DIRENT_D_TYPE 160ca08ab68Smrg/* dirent.d_type can be relied upon on FAT filesystem */ 161ca08ab68Smrgstatic FcBool 162ca08ab68SmrgFcDirChecksumScandirFilter(const struct dirent *entry) 163ca08ab68Smrg{ 164ca08ab68Smrg return entry->d_type != DT_DIR; 165ca08ab68Smrg} 166ca08ab68Smrg#endif 167ca08ab68Smrg 168ca08ab68Smrgstatic int 169ea107527SmartinFcDirChecksumScandirSorter(const struct dirent **lhs, const struct dirent **rhs) 170ca08ab68Smrg{ 171ca08ab68Smrg return strcmp((*lhs)->d_name, (*rhs)->d_name); 172ca08ab68Smrg} 17318bd4a06Smrg 17418bd4a06Smrgstatic void 17518bd4a06Smrgfree_dirent (struct dirent **p) 176d91dd368Smrg{ 17718bd4a06Smrg struct dirent **x; 17818bd4a06Smrg 17918bd4a06Smrg for (x = p; *x != NULL; x++) 18018bd4a06Smrg free (*x); 181d91dd368Smrg 18218bd4a06Smrg free (p); 18318bd4a06Smrg} 18418bd4a06Smrg 18518bd4a06Smrgint 18618bd4a06SmrgFcScandir (const char *dirp, 18718bd4a06Smrg struct dirent ***namelist, 18818bd4a06Smrg int (*filter) (const struct dirent *), 18918bd4a06Smrg int (*compar) (const struct dirent **, const struct dirent **)); 19018bd4a06Smrg 19118bd4a06Smrgint 19218bd4a06SmrgFcScandir (const char *dirp, 19318bd4a06Smrg struct dirent ***namelist, 19418bd4a06Smrg int (*filter) (const struct dirent *), 19518bd4a06Smrg int (*compar) (const struct dirent **, const struct dirent **)) 19618bd4a06Smrg{ 19718bd4a06Smrg DIR *d; 19818bd4a06Smrg struct dirent *dent, *p, **dlist, **dlp; 19918bd4a06Smrg size_t lsize = 128, n = 0; 20018bd4a06Smrg 20118bd4a06Smrg d = opendir (dirp); 20218bd4a06Smrg if (!d) 20318bd4a06Smrg return -1; 20418bd4a06Smrg 20518bd4a06Smrg dlist = (struct dirent **) malloc (sizeof (struct dirent *) * lsize); 20618bd4a06Smrg if (!dlist) 20718bd4a06Smrg { 20818bd4a06Smrg closedir (d); 20918bd4a06Smrg errno = ENOMEM; 21018bd4a06Smrg 21118bd4a06Smrg return -1; 21218bd4a06Smrg } 21318bd4a06Smrg *dlist = NULL; 21418bd4a06Smrg while ((dent = readdir (d))) 21518bd4a06Smrg { 21618bd4a06Smrg if (!filter || (filter) (dent)) 21718bd4a06Smrg { 21818bd4a06Smrg size_t dentlen = FcPtrToOffset (dent, dent->d_name) + strlen (dent->d_name) + 1; 21918bd4a06Smrg dentlen = ((dentlen + ALIGNOF_VOID_P - 1) & ~(ALIGNOF_VOID_P - 1)); 22018bd4a06Smrg p = (struct dirent *) malloc (dentlen); 22118bd4a06Smrg memcpy (p, dent, dentlen); 22218bd4a06Smrg if ((n + 1) >= lsize) 22318bd4a06Smrg { 22418bd4a06Smrg lsize += 128; 22518bd4a06Smrg dlp = (struct dirent **) realloc (dlist, sizeof (struct dirent *) * lsize); 22618bd4a06Smrg if (!dlp) 22718bd4a06Smrg { 22818bd4a06Smrg free_dirent (dlist); 22918bd4a06Smrg closedir (d); 23018bd4a06Smrg errno = ENOMEM; 23118bd4a06Smrg 23218bd4a06Smrg return -1; 23318bd4a06Smrg } 23418bd4a06Smrg dlist = dlp; 23518bd4a06Smrg } 23618bd4a06Smrg dlist[n++] = p; 23718bd4a06Smrg dlist[n] = NULL; 23818bd4a06Smrg } 23918bd4a06Smrg } 24018bd4a06Smrg closedir (d); 24118bd4a06Smrg 24218bd4a06Smrg qsort (dlist, n, sizeof (struct dirent *), (int (*) (const void *, const void *))compar); 24318bd4a06Smrg 24418bd4a06Smrg *namelist = dlist; 24518bd4a06Smrg 24618bd4a06Smrg return n; 247d91dd368Smrg} 248ca08ab68Smrg 249ca08ab68Smrgstatic int 250ca08ab68SmrgFcDirChecksum (const FcChar8 *dir, time_t *checksum) 251ca08ab68Smrg{ 252ca08ab68Smrg struct Adler32 ctx; 253ca08ab68Smrg struct dirent **files; 2545e61939bSmrg int n; 2555e61939bSmrg int ret = 0; 256ca08ab68Smrg size_t len = strlen ((const char *)dir); 257ca08ab68Smrg 258ca08ab68Smrg Adler32Init (&ctx); 259ca08ab68Smrg 26018bd4a06Smrg n = FcScandir ((const char *)dir, &files, 261ca08ab68Smrg#ifdef HAVE_STRUCT_DIRENT_D_TYPE 262ca08ab68Smrg &FcDirChecksumScandirFilter, 263ca08ab68Smrg#else 264ca08ab68Smrg NULL, 265ca08ab68Smrg#endif 266ca08ab68Smrg &FcDirChecksumScandirSorter); 267ca08ab68Smrg if (n == -1) 268ca08ab68Smrg return -1; 269ca08ab68Smrg 270ca08ab68Smrg while (n--) 271ca08ab68Smrg { 272ca08ab68Smrg size_t dlen = strlen (files[n]->d_name); 273ca08ab68Smrg int dtype; 274ca08ab68Smrg 275ca08ab68Smrg#ifdef HAVE_STRUCT_DIRENT_D_TYPE 276ca08ab68Smrg dtype = files[n]->d_type; 277b2a52337Smrg if (dtype == DT_UNKNOWN) 278b2a52337Smrg { 279b2a52337Smrg#endif 280ca08ab68Smrg struct stat statb; 28118bd4a06Smrg char *f = malloc (len + 1 + dlen + 1); 282ca08ab68Smrg 28318bd4a06Smrg if (!f) 28418bd4a06Smrg { 28518bd4a06Smrg ret = -1; 28618bd4a06Smrg goto bail; 28718bd4a06Smrg } 288ca08ab68Smrg memcpy (f, dir, len); 289ca08ab68Smrg f[len] = FC_DIR_SEPARATOR; 290ca08ab68Smrg memcpy (&f[len + 1], files[n]->d_name, dlen); 291ca08ab68Smrg f[len + 1 + dlen] = 0; 292ca08ab68Smrg if (lstat (f, &statb) < 0) 293ca08ab68Smrg { 294ca08ab68Smrg ret = -1; 29518bd4a06Smrg free (f); 296ca08ab68Smrg goto bail; 297ca08ab68Smrg } 298ca08ab68Smrg if (S_ISDIR (statb.st_mode)) 29918bd4a06Smrg { 30018bd4a06Smrg free (f); 301ca08ab68Smrg goto bail; 30218bd4a06Smrg } 303ca08ab68Smrg 30418bd4a06Smrg free (f); 305ca08ab68Smrg dtype = statb.st_mode; 306b2a52337Smrg#ifdef HAVE_STRUCT_DIRENT_D_TYPE 307b2a52337Smrg } 308ca08ab68Smrg#endif 309ca08ab68Smrg Adler32Update (&ctx, files[n]->d_name, dlen + 1); 310ca08ab68Smrg Adler32Update (&ctx, (char *)&dtype, sizeof (int)); 311ca08ab68Smrg 312ca08ab68Smrg bail: 313ca08ab68Smrg free (files[n]); 314ca08ab68Smrg } 315ca08ab68Smrg free (files); 316ca08ab68Smrg if (ret == -1) 317ca08ab68Smrg return -1; 318ca08ab68Smrg 319ca08ab68Smrg *checksum = Adler32Finish (&ctx); 320ca08ab68Smrg 321ca08ab68Smrg return 0; 322ca08ab68Smrg} 323ca08ab68Smrg#endif /* _WIN32 */ 324ca08ab68Smrg 325ca08ab68Smrgint 326ca08ab68SmrgFcStatChecksum (const FcChar8 *file, struct stat *statb) 327ca08ab68Smrg{ 328ca08ab68Smrg if (FcStat (file, statb) == -1) 329ca08ab68Smrg return -1; 330ca08ab68Smrg 331ca08ab68Smrg#ifndef _WIN32 332ca08ab68Smrg /* We have a workaround of the broken stat() in FcStat() for Win32. 333ca08ab68Smrg * No need to do something further more. 334ca08ab68Smrg */ 335ca08ab68Smrg if (FcIsFsMtimeBroken (file)) 336ca08ab68Smrg { 337ca08ab68Smrg if (FcDirChecksum (file, &statb->st_mtime) == -1) 338ca08ab68Smrg return -1; 339ca08ab68Smrg } 340ca08ab68Smrg#endif 341ca08ab68Smrg 342ca08ab68Smrg return 0; 343ca08ab68Smrg} 344ca08ab68Smrg 345ca08ab68Smrgstatic int 346ca08ab68SmrgFcFStatFs (int fd, FcStatFS *statb) 347ca08ab68Smrg{ 348ca08ab68Smrg const char *p = NULL; 349ca08ab68Smrg int ret = -1; 350ca08ab68Smrg FcBool flag = FcFalse; 351ca08ab68Smrg 352ca08ab68Smrg#if defined(HAVE_FSTATVFS) && (defined(HAVE_STRUCT_STATVFS_F_BASETYPE) || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)) 353ca08ab68Smrg struct statvfs buf; 354ca08ab68Smrg 3555e61939bSmrg memset (statb, 0, sizeof (FcStatFS)); 3565e61939bSmrg 357ca08ab68Smrg if ((ret = fstatvfs (fd, &buf)) == 0) 358ca08ab68Smrg { 359ca08ab68Smrg# if defined(HAVE_STRUCT_STATVFS_F_BASETYPE) 360ca08ab68Smrg p = buf.f_basetype; 361ca08ab68Smrg# elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) 362ca08ab68Smrg p = buf.f_fstypename; 363ca08ab68Smrg# endif 364ca08ab68Smrg } 365ca08ab68Smrg#elif defined(HAVE_FSTATFS) && (defined(HAVE_STRUCT_STATFS_F_FLAGS) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) || defined(__linux__)) 366ca08ab68Smrg struct statfs buf; 367ca08ab68Smrg 3685e61939bSmrg memset (statb, 0, sizeof (FcStatFS)); 3695e61939bSmrg 370ca08ab68Smrg if ((ret = fstatfs (fd, &buf)) == 0) 371ca08ab68Smrg { 372ca08ab68Smrg# if defined(HAVE_STRUCT_STATFS_F_FLAGS) && defined(MNT_LOCAL) 373ca08ab68Smrg statb->is_remote_fs = !(buf.f_flags & MNT_LOCAL); 374ca08ab68Smrg flag = FcTrue; 375ca08ab68Smrg# endif 376ca08ab68Smrg# if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) 377ca08ab68Smrg p = buf.f_fstypename; 378ca08ab68Smrg# elif defined(__linux__) 379ca08ab68Smrg switch (buf.f_type) 380ca08ab68Smrg { 381ca08ab68Smrg case 0x6969: /* nfs */ 382ca08ab68Smrg statb->is_remote_fs = FcTrue; 383ca08ab68Smrg break; 384ca08ab68Smrg case 0x4d44: /* fat */ 385ca08ab68Smrg statb->is_mtime_broken = FcTrue; 386ca08ab68Smrg break; 387ca08ab68Smrg default: 388ca08ab68Smrg break; 389ca08ab68Smrg } 390ca08ab68Smrg 391ca08ab68Smrg return ret; 392ca08ab68Smrg# else 393ca08ab68Smrg# error "BUG: No way to figure out with fstatfs()" 394ca08ab68Smrg# endif 395ca08ab68Smrg } 396ca08ab68Smrg#endif 397ca08ab68Smrg if (p) 398ca08ab68Smrg { 399ca08ab68Smrg if (!flag && strcmp (p, "nfs") == 0) 400ca08ab68Smrg statb->is_remote_fs = FcTrue; 401ca08ab68Smrg if (strcmp (p, "msdosfs") == 0 || 402ca08ab68Smrg strcmp (p, "pcfs") == 0) 403ca08ab68Smrg statb->is_mtime_broken = FcTrue; 404ca08ab68Smrg } 405ca08ab68Smrg 406ca08ab68Smrg return ret; 407ca08ab68Smrg} 408ca08ab68Smrg 409ca08ab68SmrgFcBool 410ca08ab68SmrgFcIsFsMmapSafe (int fd) 411ca08ab68Smrg{ 412ca08ab68Smrg FcStatFS statb; 413ca08ab68Smrg 414ca08ab68Smrg if (FcFStatFs (fd, &statb) < 0) 415ca08ab68Smrg return FcTrue; 416ca08ab68Smrg 417ca08ab68Smrg return !statb.is_remote_fs; 418ca08ab68Smrg} 419ca08ab68Smrg 420ca08ab68SmrgFcBool 421ca08ab68SmrgFcIsFsMtimeBroken (const FcChar8 *dir) 422ca08ab68Smrg{ 4235e61939bSmrg int fd = FcOpen ((const char *) dir, O_RDONLY); 424ca08ab68Smrg 425ca08ab68Smrg if (fd != -1) 426ca08ab68Smrg { 427ca08ab68Smrg FcStatFS statb; 428ca08ab68Smrg int ret = FcFStatFs (fd, &statb); 429ca08ab68Smrg 430ca08ab68Smrg close (fd); 431ca08ab68Smrg if (ret < 0) 432ca08ab68Smrg return FcFalse; 433ca08ab68Smrg 434ca08ab68Smrg return statb.is_mtime_broken; 435ca08ab68Smrg } 436ca08ab68Smrg 437ca08ab68Smrg return FcFalse; 438ca08ab68Smrg} 43918bd4a06Smrg 44018bd4a06Smrg#define __fcstat__ 44118bd4a06Smrg#include "fcaliastail.h" 44218bd4a06Smrg#undef __fcstat__ 443