/* * Copyright © 2000 Keith Packard * Copyright © 2005 Patrick Lam * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of the author(s) not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. The authors make no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "fcint.h" #include "fcarch.h" #include #include #include #include #include #ifdef HAVE_SYS_VFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #include #ifdef _WIN32 #ifdef __GNUC__ typedef long long INT64; #define EPOCH_OFFSET 11644473600ll #else #define EPOCH_OFFSET 11644473600i64 typedef __int64 INT64; #endif /* Workaround for problems in the stat() in the Microsoft C library: * * 1) stat() uses FindFirstFile() to get the file * attributes. Unfortunately this API doesn't return correct values * for modification time of a directory until some time after a file * or subdirectory has been added to the directory. (This causes * run-test.sh to fail, for instance.) GetFileAttributesEx() is * better, it returns the updated timestamp right away. * * 2) stat() does some strange things related to backward * compatibility with the local time timestamps on FAT volumes and * daylight saving time. This causes problems after the switches * to/from daylight saving time. See * http://bugzilla.gnome.org/show_bug.cgi?id=154968 , especially * comment #30, and http://www.codeproject.com/datetime/dstbugs.asp . * We don't need any of that, FAT and Win9x are as good as dead. So * just use the UTC timestamps from NTFS, converted to the Unix epoch. */ int FcStat (const FcChar8 *file, struct stat *statb) { WIN32_FILE_ATTRIBUTE_DATA wfad; char full_path_name[MAX_PATH]; char *basename; DWORD rc; if (!GetFileAttributesEx ((LPCSTR) file, GetFileExInfoStandard, &wfad)) return -1; statb->st_dev = 0; /* Calculate a pseudo inode number as a hash of the full path name. * Call GetLongPathName() to get the spelling of the path name as it * is on disk. */ rc = GetFullPathName ((LPCSTR) file, sizeof (full_path_name), full_path_name, &basename); if (rc == 0 || rc > sizeof (full_path_name)) return -1; rc = GetLongPathName (full_path_name, full_path_name, sizeof (full_path_name)); statb->st_ino = FcStringHash ((const FcChar8 *) full_path_name); statb->st_mode = _S_IREAD | _S_IWRITE; statb->st_mode |= (statb->st_mode >> 3) | (statb->st_mode >> 6); if (wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) statb->st_mode |= _S_IFDIR; else statb->st_mode |= _S_IFREG; statb->st_nlink = 1; statb->st_uid = statb->st_gid = 0; statb->st_rdev = 0; if (wfad.nFileSizeHigh > 0) return -1; statb->st_size = wfad.nFileSizeLow; statb->st_atime = (*(INT64 *)&wfad.ftLastAccessTime)/10000000 - EPOCH_OFFSET; statb->st_mtime = (*(INT64 *)&wfad.ftLastWriteTime)/10000000 - EPOCH_OFFSET; statb->st_ctime = statb->st_mtime; return 0; } #else int FcStat (const FcChar8 *file, struct stat *statb) { return stat ((char *) file, statb); } /* Adler-32 checksum implementation */ struct Adler32 { int a; int b; }; static void Adler32Init (struct Adler32 *ctx) { ctx->a = 1; ctx->b = 0; } static void Adler32Update (struct Adler32 *ctx, const char *data, int data_len) { while (data_len--) { ctx->a = (ctx->a + *data++) % 65521; ctx->b = (ctx->b + ctx->a) % 65521; } } static int Adler32Finish (struct Adler32 *ctx) { return ctx->a + (ctx->b << 16); } #ifdef HAVE_STRUCT_DIRENT_D_TYPE /* dirent.d_type can be relied upon on FAT filesystem */ static FcBool FcDirChecksumScandirFilter(const struct dirent *entry) { return entry->d_type != DT_DIR; } #endif static int FcDirChecksumScandirSorter(const struct dirent **lhs, const struct dirent **rhs) { return strcmp((*lhs)->d_name, (*rhs)->d_name); } static void free_dirent (struct dirent **p) { struct dirent **x; for (x = p; *x != NULL; x++) free (*x); free (p); } int FcScandir (const char *dirp, struct dirent ***namelist, int (*filter) (const struct dirent *), int (*compar) (const struct dirent **, const struct dirent **)); int FcScandir (const char *dirp, struct dirent ***namelist, int (*filter) (const struct dirent *), int (*compar) (const struct dirent **, const struct dirent **)) { DIR *d; struct dirent *dent, *p, **dlist, **dlp; size_t lsize = 128, n = 0; d = opendir (dirp); if (!d) return -1; dlist = (struct dirent **) malloc (sizeof (struct dirent *) * lsize); if (!dlist) { closedir (d); errno = ENOMEM; return -1; } *dlist = NULL; while ((dent = readdir (d))) { if (!filter || (filter) (dent)) { size_t dentlen = FcPtrToOffset (dent, dent->d_name) + strlen (dent->d_name) + 1; dentlen = ((dentlen + ALIGNOF_VOID_P - 1) & ~(ALIGNOF_VOID_P - 1)); p = (struct dirent *) malloc (dentlen); if (!p) { free_dirent (dlist); closedir (d); errno = ENOMEM; return -1; } memcpy (p, dent, dentlen); if ((n + 1) >= lsize) { lsize += 128; dlp = (struct dirent **) realloc (dlist, sizeof (struct dirent *) * lsize); if (!dlp) { free (p); free_dirent (dlist); closedir (d); errno = ENOMEM; return -1; } dlist = dlp; } dlist[n++] = p; dlist[n] = NULL; } } closedir (d); qsort (dlist, n, sizeof (struct dirent *), (int (*) (const void *, const void *))compar); *namelist = dlist; return n; } static int FcDirChecksum (const FcChar8 *dir, time_t *checksum) { struct Adler32 ctx; struct dirent **files; int n; int ret = 0; size_t len = strlen ((const char *)dir); Adler32Init (&ctx); n = FcScandir ((const char *)dir, &files, #ifdef HAVE_STRUCT_DIRENT_D_TYPE &FcDirChecksumScandirFilter, #else NULL, #endif &FcDirChecksumScandirSorter); if (n == -1) return -1; while (n--) { size_t dlen = strlen (files[n]->d_name); int dtype; #ifdef HAVE_STRUCT_DIRENT_D_TYPE dtype = files[n]->d_type; if (dtype == DT_UNKNOWN) { #endif struct stat statb; char *f = malloc (len + 1 + dlen + 1); if (!f) { ret = -1; goto bail; } memcpy (f, dir, len); f[len] = FC_DIR_SEPARATOR; memcpy (&f[len + 1], files[n]->d_name, dlen); f[len + 1 + dlen] = 0; if (lstat (f, &statb) < 0) { ret = -1; free (f); goto bail; } if (S_ISDIR (statb.st_mode)) { free (f); goto bail; } free (f); dtype = statb.st_mode; #ifdef HAVE_STRUCT_DIRENT_D_TYPE } #endif Adler32Update (&ctx, files[n]->d_name, dlen + 1); Adler32Update (&ctx, (char *)&dtype, sizeof (int)); bail: free (files[n]); } free (files); if (ret == -1) return -1; *checksum = Adler32Finish (&ctx); return 0; } #endif /* _WIN32 */ int FcStatChecksum (const FcChar8 *file, struct stat *statb) { if (FcStat (file, statb) == -1) return -1; #ifndef _WIN32 /* We have a workaround of the broken stat() in FcStat() for Win32. * No need to do something further more. */ if (FcIsFsMtimeBroken (file)) { if (FcDirChecksum (file, &statb->st_mtime) == -1) return -1; } #endif return 0; } static int FcFStatFs (int fd, FcStatFS *statb) { const char *p = NULL; int ret = -1; FcBool flag = FcFalse; #if defined(HAVE_FSTATVFS) && (defined(HAVE_STRUCT_STATVFS_F_BASETYPE) || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)) struct statvfs buf; memset (statb, 0, sizeof (FcStatFS)); if ((ret = fstatvfs (fd, &buf)) == 0) { # if defined(HAVE_STRUCT_STATVFS_F_BASETYPE) p = buf.f_basetype; # elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) p = buf.f_fstypename; # endif } #elif defined(HAVE_FSTATFS) && (defined(HAVE_STRUCT_STATFS_F_FLAGS) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) || defined(__linux__)) struct statfs buf; memset (statb, 0, sizeof (FcStatFS)); if ((ret = fstatfs (fd, &buf)) == 0) { # if defined(HAVE_STRUCT_STATFS_F_FLAGS) && defined(MNT_LOCAL) statb->is_remote_fs = !(buf.f_flags & MNT_LOCAL); flag = FcTrue; # endif # if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) p = buf.f_fstypename; # elif defined(__linux__) switch (buf.f_type) { case 0x6969: /* nfs */ statb->is_remote_fs = FcTrue; break; case 0x4d44: /* fat */ statb->is_mtime_broken = FcTrue; break; default: break; } return ret; # else # error "BUG: No way to figure out with fstatfs()" # endif } #endif if (p) { if (!flag && strcmp (p, "nfs") == 0) statb->is_remote_fs = FcTrue; if (strcmp (p, "msdosfs") == 0 || strcmp (p, "pcfs") == 0) statb->is_mtime_broken = FcTrue; } return ret; } FcBool FcIsFsMmapSafe (int fd) { FcStatFS statb; if (FcFStatFs (fd, &statb) < 0) return FcTrue; return !statb.is_remote_fs; } FcBool FcIsFsMtimeBroken (const FcChar8 *dir) { int fd = FcOpen ((const char *) dir, O_RDONLY); if (fd != -1) { FcStatFS statb; int ret = FcFStatFs (fd, &statb); close (fd); if (ret < 0) return FcFalse; return statb.is_mtime_broken; } return FcFalse; } #define __fcstat__ #include "fcaliastail.h" #undef __fcstat__