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