(root)/
fontconfig-2.14.2/
src/
fcstat.c
       1  /*
       2   * Copyright © 2000 Keith Packard
       3   * Copyright © 2005 Patrick Lam
       4   *
       5   * Permission to use, copy, modify, distribute, and sell this software and its
       6   * documentation for any purpose is hereby granted without fee, provided that
       7   * the above copyright notice appear in all copies and that both that
       8   * copyright notice and this permission notice appear in supporting
       9   * documentation, and that the name of the author(s) not be used in
      10   * advertising or publicity pertaining to distribution of the software without
      11   * specific, written prior permission.  The authors make no
      12   * representations about the suitability of this software for any purpose.  It
      13   * is provided "as is" without express or implied warranty.
      14   *
      15   * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
      16   * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
      17   * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
      18   * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
      19   * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
      20   * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
      21   * PERFORMANCE OF THIS SOFTWARE.
      22   */
      23  #include "fcint.h"
      24  #include "fcarch.h"
      25  #ifdef HAVE_DIRENT_H
      26  #include <dirent.h>
      27  #endif
      28  #include <limits.h>
      29  #include <sys/types.h>
      30  #include <sys/stat.h>
      31  #include <fcntl.h>
      32  #ifdef HAVE_SYS_VFS_H
      33  #include <sys/vfs.h>
      34  #endif
      35  #ifdef HAVE_SYS_STATVFS_H
      36  #include <sys/statvfs.h>
      37  #endif
      38  #ifdef HAVE_SYS_STATFS_H
      39  #include <sys/statfs.h>
      40  #endif
      41  #ifdef HAVE_SYS_PARAM_H
      42  #include <sys/param.h>
      43  #endif
      44  #ifdef HAVE_SYS_MOUNT_H
      45  #include <sys/mount.h>
      46  #endif
      47  #include <errno.h>
      48  
      49  #ifdef _WIN32
      50  #ifdef __GNUC__
      51  typedef long long INT64;
      52  #define EPOCH_OFFSET 11644473600ll
      53  #else
      54  #define EPOCH_OFFSET 11644473600i64
      55  typedef __int64 INT64;
      56  #endif
      57  
      58  /* Workaround for problems in the stat() in the Microsoft C library:
      59   *
      60   * 1) stat() uses FindFirstFile() to get the file
      61   * attributes. Unfortunately this API doesn't return correct values
      62   * for modification time of a directory until some time after a file
      63   * or subdirectory has been added to the directory. (This causes
      64   * run-test.sh to fail, for instance.) GetFileAttributesEx() is
      65   * better, it returns the updated timestamp right away.
      66   *
      67   * 2) stat() does some strange things related to backward
      68   * compatibility with the local time timestamps on FAT volumes and
      69   * daylight saving time. This causes problems after the switches
      70   * to/from daylight saving time. See
      71   * http://bugzilla.gnome.org/show_bug.cgi?id=154968 , especially
      72   * comment #30, and http://www.codeproject.com/datetime/dstbugs.asp .
      73   * We don't need any of that, FAT and Win9x are as good as dead. So
      74   * just use the UTC timestamps from NTFS, converted to the Unix epoch.
      75   */
      76  
      77  int
      78  FcStat (const FcChar8 *file, struct stat *statb)
      79  {
      80      WIN32_FILE_ATTRIBUTE_DATA wfad;
      81      char full_path_name[MAX_PATH];
      82      char *basename;
      83      DWORD rc;
      84  
      85      if (!GetFileAttributesEx ((LPCSTR) file, GetFileExInfoStandard, &wfad))
      86  	return -1;
      87  
      88      statb->st_dev = 0;
      89  
      90      /* Calculate a pseudo inode number as a hash of the full path name.
      91       * Call GetLongPathName() to get the spelling of the path name as it
      92       * is on disk.
      93       */
      94      rc = GetFullPathName ((LPCSTR) file, sizeof (full_path_name), full_path_name, &basename);
      95      if (rc == 0 || rc > sizeof (full_path_name))
      96  	return -1;
      97  
      98      rc = GetLongPathName (full_path_name, full_path_name, sizeof (full_path_name));
      99      statb->st_ino = FcStringHash ((const FcChar8 *) full_path_name);
     100  
     101      statb->st_mode = _S_IREAD | _S_IWRITE;
     102      statb->st_mode |= (statb->st_mode >> 3) | (statb->st_mode >> 6);
     103  
     104      if (wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
     105  	statb->st_mode |= _S_IFDIR;
     106      else
     107  	statb->st_mode |= _S_IFREG;
     108  
     109      statb->st_nlink = 1;
     110      statb->st_uid = statb->st_gid = 0;
     111      statb->st_rdev = 0;
     112  
     113      if (wfad.nFileSizeHigh > 0)
     114  	return -1;
     115      statb->st_size = wfad.nFileSizeLow;
     116  
     117      statb->st_atime = (*(INT64 *)&wfad.ftLastAccessTime)/10000000 - EPOCH_OFFSET;
     118      statb->st_mtime = (*(INT64 *)&wfad.ftLastWriteTime)/10000000 - EPOCH_OFFSET;
     119      statb->st_ctime = statb->st_mtime;
     120  
     121      return 0;
     122  }
     123  
     124  #else
     125  
     126  int
     127  FcStat (const FcChar8 *file, struct stat *statb)
     128  {
     129    return stat ((char *) file, statb);
     130  }
     131  
     132  /* Adler-32 checksum implementation */
     133  struct Adler32 {
     134      int a;
     135      int b;
     136  };
     137  
     138  static void
     139  Adler32Init (struct Adler32 *ctx)
     140  {
     141      ctx->a = 1;
     142      ctx->b = 0;
     143  }
     144  
     145  static void
     146  Adler32Update (struct Adler32 *ctx, const char *data, int data_len)
     147  {
     148      while (data_len--)
     149      {
     150  	ctx->a = (ctx->a + *data++) % 65521;
     151  	ctx->b = (ctx->b + ctx->a) % 65521;
     152      }
     153  }
     154  
     155  static int
     156  Adler32Finish (struct Adler32 *ctx)
     157  {
     158      return ctx->a + (ctx->b << 16);
     159  }
     160  
     161  #ifdef HAVE_STRUCT_DIRENT_D_TYPE
     162  /* dirent.d_type can be relied upon on FAT filesystem */
     163  static FcBool
     164  FcDirChecksumScandirFilter(const struct dirent *entry)
     165  {
     166      return entry->d_type != DT_DIR;
     167  }
     168  #endif
     169  
     170  static int
     171  FcDirChecksumScandirSorter(const struct dirent **lhs, const struct dirent **rhs)
     172  {
     173      return strcmp((*lhs)->d_name, (*rhs)->d_name);
     174  }
     175  
     176  static void
     177  free_dirent (struct dirent **p)
     178  {
     179      struct dirent **x;
     180  
     181      for (x = p; *x != NULL; x++)
     182  	free (*x);
     183  
     184      free (p);
     185  }
     186  
     187  int
     188  FcScandir (const char		*dirp,
     189  	   struct dirent	***namelist,
     190  	   int (*filter) (const struct dirent *),
     191  	   int (*compar) (const struct dirent **, const struct dirent **));
     192  
     193  int
     194  FcScandir (const char		*dirp,
     195  	   struct dirent	***namelist,
     196  	   int (*filter) (const struct dirent *),
     197  	   int (*compar) (const struct dirent **, const struct dirent **))
     198  {
     199      DIR *d;
     200      struct dirent *dent, *p, **dlist, **dlp;
     201      size_t lsize = 128, n = 0;
     202  
     203      d = opendir (dirp);
     204      if (!d)
     205  	return -1;
     206  
     207      dlist = (struct dirent **) malloc (sizeof (struct dirent *) * lsize);
     208      if (!dlist)
     209      {
     210  	closedir (d);
     211  	errno = ENOMEM;
     212  
     213  	return -1;
     214      }
     215      *dlist = NULL;
     216      while ((dent = readdir (d)))
     217      {
     218  	if (!filter || (filter) (dent))
     219  	{
     220  	    size_t dentlen = FcPtrToOffset (dent, dent->d_name) + strlen (dent->d_name) + 1;
     221  	    dentlen = ((dentlen + ALIGNOF_VOID_P - 1) & ~(ALIGNOF_VOID_P - 1));
     222  	    p = (struct dirent *) malloc (dentlen);
     223  	    if (!p)
     224  	    {
     225  		free_dirent (dlist);
     226  		closedir (d);
     227  		errno = ENOMEM;
     228  
     229  		return -1;
     230  	    }
     231  	    memcpy (p, dent, dentlen);
     232  	    if ((n + 1) >= lsize)
     233  	    {
     234  		lsize += 128;
     235  		dlp = (struct dirent **) realloc (dlist, sizeof (struct dirent *) * lsize);
     236  		if (!dlp)
     237  		{
     238  		    free (p);
     239  		    free_dirent (dlist);
     240  		    closedir (d);
     241  		    errno = ENOMEM;
     242  
     243  		    return -1;
     244  		}
     245  		dlist = dlp;
     246  	    }
     247  	    dlist[n++] = p;
     248  	    dlist[n] = NULL;
     249  	}
     250      }
     251      closedir (d);
     252  
     253      qsort (dlist, n, sizeof (struct dirent *), (int (*) (const void *, const void *))compar);
     254  
     255      *namelist = dlist;
     256  
     257      return n;
     258  }
     259  
     260  static int
     261  FcDirChecksum (const FcChar8 *dir, time_t *checksum)
     262  {
     263      struct Adler32 ctx;
     264      struct dirent **files;
     265      int n;
     266      int ret = 0;
     267      size_t len = strlen ((const char *)dir);
     268  
     269      Adler32Init (&ctx);
     270  
     271      n = FcScandir ((const char *)dir, &files,
     272  #ifdef HAVE_STRUCT_DIRENT_D_TYPE
     273  		 &FcDirChecksumScandirFilter,
     274  #else
     275  		 NULL,
     276  #endif
     277  		 &FcDirChecksumScandirSorter);
     278      if (n == -1)
     279  	return -1;
     280  
     281      while (n--)
     282      {
     283  	size_t dlen = strlen (files[n]->d_name);
     284  	int dtype;
     285  
     286  #ifdef HAVE_STRUCT_DIRENT_D_TYPE
     287  	dtype = files[n]->d_type;
     288  	if (dtype == DT_UNKNOWN)
     289  	{
     290  #endif
     291  	struct stat statb;
     292  	char *f = malloc (len + 1 + dlen + 1);
     293  
     294  	if (!f)
     295  	{
     296  	    ret = -1;
     297  	    goto bail;
     298  	}
     299  	memcpy (f, dir, len);
     300  	f[len] = FC_DIR_SEPARATOR;
     301  	memcpy (&f[len + 1], files[n]->d_name, dlen);
     302  	f[len + 1 + dlen] = 0;
     303  	if (lstat (f, &statb) < 0)
     304  	{
     305  	    ret = -1;
     306  	    free (f);
     307  	    goto bail;
     308  	}
     309  	if (S_ISDIR (statb.st_mode))
     310  	{
     311  	    free (f);
     312  	    goto bail;
     313  	}
     314  
     315  	free (f);
     316  	dtype = statb.st_mode;
     317  #ifdef HAVE_STRUCT_DIRENT_D_TYPE
     318  	}
     319  #endif
     320  	Adler32Update (&ctx, files[n]->d_name, dlen + 1);
     321  	Adler32Update (&ctx, (char *)&dtype, sizeof (int));
     322  
     323        bail:
     324  	free (files[n]);
     325      }
     326      free (files);
     327      if (ret == -1)
     328  	return -1;
     329  
     330      *checksum = Adler32Finish (&ctx);
     331  
     332      return 0;
     333  }
     334  #endif /* _WIN32 */
     335  
     336  int
     337  FcStatChecksum (const FcChar8 *file, struct stat *statb)
     338  {
     339      if (FcStat (file, statb) == -1)
     340          return -1;
     341  
     342  #ifndef _WIN32
     343      /* We have a workaround of the broken stat() in FcStat() for Win32.
     344       * No need to do something further more.
     345       */
     346      if (FcIsFsMtimeBroken (file))
     347      {
     348          if (FcDirChecksum (file, &statb->st_mtime) == -1)
     349              return -1;
     350      }
     351  #endif
     352  
     353      return 0;
     354  }
     355  
     356  static int
     357  FcFStatFs (int fd, FcStatFS *statb)
     358  {
     359      const char *p = NULL;
     360      int ret = -1;
     361      FcBool flag = FcFalse;
     362  
     363  #if defined(HAVE_FSTATVFS) && (defined(HAVE_STRUCT_STATVFS_F_BASETYPE) || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME))
     364      struct statvfs buf;
     365  
     366      memset (statb, 0, sizeof (FcStatFS));
     367  
     368      if ((ret = fstatvfs (fd, &buf)) == 0)
     369      {
     370  #  if defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
     371  	p = buf.f_basetype;
     372  #  elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)
     373  	p = buf.f_fstypename;
     374  #  endif
     375      }
     376  #elif defined(HAVE_FSTATFS) && (defined(HAVE_STRUCT_STATFS_F_FLAGS) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) || defined(__linux__))
     377      struct statfs buf;
     378  
     379      memset (statb, 0, sizeof (FcStatFS));
     380  
     381      if ((ret = fstatfs (fd, &buf)) == 0)
     382      {
     383  #  if defined(HAVE_STRUCT_STATFS_F_FLAGS) && defined(MNT_LOCAL)
     384  	statb->is_remote_fs = !(buf.f_flags & MNT_LOCAL);
     385  	flag = FcTrue;
     386  #  endif
     387  #  if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME)
     388  	p = buf.f_fstypename;
     389  #  elif defined(__linux__) || defined (__EMSCRIPTEN__)
     390  	switch (buf.f_type)
     391  	{
     392  	case 0x6969: /* nfs */
     393  	    statb->is_remote_fs = FcTrue;
     394  	    break;
     395  	case 0x4d44: /* fat */
     396  	    statb->is_mtime_broken = FcTrue;
     397  	    break;
     398  	default:
     399  	    break;
     400  	}
     401  
     402  	return ret;
     403  #  else
     404  #    error "BUG: No way to figure out with fstatfs()"
     405  #  endif
     406      }
     407  #endif
     408      if (p)
     409      {
     410  	if (!flag && strcmp (p, "nfs") == 0)
     411  	    statb->is_remote_fs = FcTrue;
     412  	if (strcmp (p, "msdosfs") == 0 ||
     413  	    strcmp (p, "pcfs") == 0)
     414  	    statb->is_mtime_broken = FcTrue;
     415      }
     416  
     417      return ret;
     418  }
     419  
     420  FcBool
     421  FcIsFsMmapSafe (int fd)
     422  {
     423      FcStatFS statb;
     424  
     425      if (FcFStatFs (fd, &statb) < 0)
     426  	return FcTrue;
     427  
     428      return !statb.is_remote_fs;
     429  }
     430  
     431  FcBool
     432  FcIsFsMtimeBroken (const FcChar8 *dir)
     433  {
     434      int fd = FcOpen ((const char *) dir, O_RDONLY);
     435  
     436      if (fd != -1)
     437      {
     438  	FcStatFS statb;
     439  	int ret = FcFStatFs (fd, &statb);
     440  
     441  	close (fd);
     442  	if (ret < 0)
     443  	    return FcFalse;
     444  
     445  	return statb.is_mtime_broken;
     446      }
     447  
     448      return FcFalse;
     449  }
     450  
     451  #define __fcstat__
     452  #include "fcaliastail.h"
     453  #undef __fcstat__