Blame src/fcstat.c

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