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