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