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" 257872e0a1Smrg#ifdef HAVE_DIRENT_H 26ca08ab68Smrg#include <dirent.h> 277872e0a1Smrg#endif 28ca08ab68Smrg#include <limits.h> 29ca08ab68Smrg#include <sys/types.h> 30ca08ab68Smrg#include <sys/stat.h> 31ca08ab68Smrg#include <fcntl.h> 32ca08ab68Smrg#ifdef HAVE_SYS_VFS_H 33ca08ab68Smrg#include <sys/vfs.h> 34ca08ab68Smrg#endif 35b2a52337Smrg#ifdef HAVE_SYS_STATVFS_H 36b2a52337Smrg#include <sys/statvfs.h> 37b2a52337Smrg#endif 38ca08ab68Smrg#ifdef HAVE_SYS_STATFS_H 39ca08ab68Smrg#include <sys/statfs.h> 40ca08ab68Smrg#endif 41ca08ab68Smrg#ifdef HAVE_SYS_PARAM_H 42ca08ab68Smrg#include <sys/param.h> 43ca08ab68Smrg#endif 44ca08ab68Smrg#ifdef HAVE_SYS_MOUNT_H 45ca08ab68Smrg#include <sys/mount.h> 46ca08ab68Smrg#endif 4718bd4a06Smrg#include <errno.h> 48ca08ab68Smrg 49ca08ab68Smrg#ifdef _WIN32 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 171ea107527SmartinFcDirChecksumScandirSorter(const struct dirent **lhs, const struct dirent **rhs) 172ca08ab68Smrg{ 173ca08ab68Smrg return strcmp((*lhs)->d_name, (*rhs)->d_name); 174ca08ab68Smrg} 17518bd4a06Smrg 17618bd4a06Smrgstatic void 17718bd4a06Smrgfree_dirent (struct dirent **p) 178d91dd368Smrg{ 17918bd4a06Smrg struct dirent **x; 18018bd4a06Smrg 18118bd4a06Smrg for (x = p; *x != NULL; x++) 18218bd4a06Smrg free (*x); 183d91dd368Smrg 18418bd4a06Smrg free (p); 18518bd4a06Smrg} 18618bd4a06Smrg 18718bd4a06Smrgint 18818bd4a06SmrgFcScandir (const char *dirp, 18918bd4a06Smrg struct dirent ***namelist, 19018bd4a06Smrg int (*filter) (const struct dirent *), 19118bd4a06Smrg int (*compar) (const struct dirent **, const struct dirent **)); 19218bd4a06Smrg 19318bd4a06Smrgint 19418bd4a06SmrgFcScandir (const char *dirp, 19518bd4a06Smrg struct dirent ***namelist, 19618bd4a06Smrg int (*filter) (const struct dirent *), 19718bd4a06Smrg int (*compar) (const struct dirent **, const struct dirent **)) 19818bd4a06Smrg{ 19918bd4a06Smrg DIR *d; 20018bd4a06Smrg struct dirent *dent, *p, **dlist, **dlp; 20118bd4a06Smrg size_t lsize = 128, n = 0; 20218bd4a06Smrg 20318bd4a06Smrg d = opendir (dirp); 20418bd4a06Smrg if (!d) 20518bd4a06Smrg return -1; 20618bd4a06Smrg 20729c28367Schristos dlist = malloc (sizeof (struct dirent *) * lsize); 20818bd4a06Smrg if (!dlist) 20918bd4a06Smrg { 21018bd4a06Smrg closedir (d); 21118bd4a06Smrg errno = ENOMEM; 21218bd4a06Smrg 21318bd4a06Smrg return -1; 21418bd4a06Smrg } 21518bd4a06Smrg *dlist = NULL; 21618bd4a06Smrg while ((dent = readdir (d))) 21718bd4a06Smrg { 21818bd4a06Smrg if (!filter || (filter) (dent)) 21918bd4a06Smrg { 22018bd4a06Smrg size_t dentlen = FcPtrToOffset (dent, dent->d_name) + strlen (dent->d_name) + 1; 22118bd4a06Smrg dentlen = ((dentlen + ALIGNOF_VOID_P - 1) & ~(ALIGNOF_VOID_P - 1)); 2221887081fSmrg p = (struct dirent *) malloc (dentlen); 2231887081fSmrg if (!p) 2241887081fSmrg { 2251887081fSmrg free_dirent (dlist); 2261887081fSmrg closedir (d); 2271887081fSmrg errno = ENOMEM; 2281887081fSmrg 2291887081fSmrg return -1; 2301887081fSmrg } 23118bd4a06Smrg memcpy (p, dent, dentlen); 23218bd4a06Smrg if ((n + 1) >= lsize) 23318bd4a06Smrg { 23418bd4a06Smrg lsize += 128; 2351887081fSmrg dlp = (struct dirent **) realloc (dlist, sizeof (struct dirent *) * lsize); 23618bd4a06Smrg if (!dlp) 23718bd4a06Smrg { 2381887081fSmrg free (p); 23918bd4a06Smrg free_dirent (dlist); 24018bd4a06Smrg closedir (d); 24118bd4a06Smrg errno = ENOMEM; 24218bd4a06Smrg 24318bd4a06Smrg return -1; 24418bd4a06Smrg } 24518bd4a06Smrg dlist = dlp; 24618bd4a06Smrg } 24718bd4a06Smrg dlist[n++] = p; 24818bd4a06Smrg dlist[n] = NULL; 24918bd4a06Smrg } 25018bd4a06Smrg } 25118bd4a06Smrg closedir (d); 25218bd4a06Smrg 25318bd4a06Smrg qsort (dlist, n, sizeof (struct dirent *), (int (*) (const void *, const void *))compar); 25418bd4a06Smrg 25518bd4a06Smrg *namelist = dlist; 25618bd4a06Smrg 25718bd4a06Smrg return n; 258d91dd368Smrg} 259ca08ab68Smrg 260ca08ab68Smrgstatic int 261ca08ab68SmrgFcDirChecksum (const FcChar8 *dir, time_t *checksum) 262ca08ab68Smrg{ 263ca08ab68Smrg struct Adler32 ctx; 264ca08ab68Smrg struct dirent **files; 2655e61939bSmrg int n; 2665e61939bSmrg int ret = 0; 267ca08ab68Smrg size_t len = strlen ((const char *)dir); 268ca08ab68Smrg 269ca08ab68Smrg Adler32Init (&ctx); 270ca08ab68Smrg 27118bd4a06Smrg n = FcScandir ((const char *)dir, &files, 272ca08ab68Smrg#ifdef HAVE_STRUCT_DIRENT_D_TYPE 273ca08ab68Smrg &FcDirChecksumScandirFilter, 274ca08ab68Smrg#else 275ca08ab68Smrg NULL, 276ca08ab68Smrg#endif 277ca08ab68Smrg &FcDirChecksumScandirSorter); 278ca08ab68Smrg if (n == -1) 279ca08ab68Smrg return -1; 280ca08ab68Smrg 281ca08ab68Smrg while (n--) 282ca08ab68Smrg { 283ca08ab68Smrg size_t dlen = strlen (files[n]->d_name); 284ca08ab68Smrg int dtype; 285ca08ab68Smrg 286ca08ab68Smrg#ifdef HAVE_STRUCT_DIRENT_D_TYPE 287ca08ab68Smrg dtype = files[n]->d_type; 288b2a52337Smrg if (dtype == DT_UNKNOWN) 289b2a52337Smrg { 290b2a52337Smrg#endif 291ca08ab68Smrg struct stat statb; 29218bd4a06Smrg char *f = malloc (len + 1 + dlen + 1); 293ca08ab68Smrg 29418bd4a06Smrg if (!f) 29518bd4a06Smrg { 29618bd4a06Smrg ret = -1; 29718bd4a06Smrg goto bail; 29818bd4a06Smrg } 299ca08ab68Smrg memcpy (f, dir, len); 300ca08ab68Smrg f[len] = FC_DIR_SEPARATOR; 301ca08ab68Smrg memcpy (&f[len + 1], files[n]->d_name, dlen); 302ca08ab68Smrg f[len + 1 + dlen] = 0; 303ca08ab68Smrg if (lstat (f, &statb) < 0) 304ca08ab68Smrg { 305ca08ab68Smrg ret = -1; 30618bd4a06Smrg free (f); 307ca08ab68Smrg goto bail; 308ca08ab68Smrg } 309ca08ab68Smrg if (S_ISDIR (statb.st_mode)) 31018bd4a06Smrg { 31118bd4a06Smrg free (f); 312ca08ab68Smrg goto bail; 31318bd4a06Smrg } 314ca08ab68Smrg 31518bd4a06Smrg free (f); 316ca08ab68Smrg dtype = statb.st_mode; 317b2a52337Smrg#ifdef HAVE_STRUCT_DIRENT_D_TYPE 318b2a52337Smrg } 319ca08ab68Smrg#endif 320ca08ab68Smrg Adler32Update (&ctx, files[n]->d_name, dlen + 1); 321ca08ab68Smrg Adler32Update (&ctx, (char *)&dtype, sizeof (int)); 322ca08ab68Smrg 323ca08ab68Smrg bail: 324ca08ab68Smrg free (files[n]); 325ca08ab68Smrg } 326ca08ab68Smrg free (files); 327ca08ab68Smrg if (ret == -1) 328ca08ab68Smrg return -1; 329ca08ab68Smrg 330ca08ab68Smrg *checksum = Adler32Finish (&ctx); 331ca08ab68Smrg 332ca08ab68Smrg return 0; 333ca08ab68Smrg} 334ca08ab68Smrg#endif /* _WIN32 */ 335ca08ab68Smrg 336ca08ab68Smrgint 337ca08ab68SmrgFcStatChecksum (const FcChar8 *file, struct stat *statb) 338ca08ab68Smrg{ 339ca08ab68Smrg if (FcStat (file, statb) == -1) 340ca08ab68Smrg return -1; 341ca08ab68Smrg 342ca08ab68Smrg#ifndef _WIN32 343ca08ab68Smrg /* We have a workaround of the broken stat() in FcStat() for Win32. 344ca08ab68Smrg * No need to do something further more. 345ca08ab68Smrg */ 346ca08ab68Smrg if (FcIsFsMtimeBroken (file)) 347ca08ab68Smrg { 348ca08ab68Smrg if (FcDirChecksum (file, &statb->st_mtime) == -1) 349ca08ab68Smrg return -1; 350ca08ab68Smrg } 351ca08ab68Smrg#endif 352ca08ab68Smrg 353ca08ab68Smrg return 0; 354ca08ab68Smrg} 355ca08ab68Smrg 356ca08ab68Smrgstatic int 357ca08ab68SmrgFcFStatFs (int fd, FcStatFS *statb) 358ca08ab68Smrg{ 359ca08ab68Smrg const char *p = NULL; 360ca08ab68Smrg int ret = -1; 361ca08ab68Smrg FcBool flag = FcFalse; 362ca08ab68Smrg 363ca08ab68Smrg#if defined(HAVE_FSTATVFS) && (defined(HAVE_STRUCT_STATVFS_F_BASETYPE) || defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME)) 364ca08ab68Smrg struct statvfs buf; 365ca08ab68Smrg 3665e61939bSmrg memset (statb, 0, sizeof (FcStatFS)); 3675e61939bSmrg 368ca08ab68Smrg if ((ret = fstatvfs (fd, &buf)) == 0) 369ca08ab68Smrg { 370ca08ab68Smrg# if defined(HAVE_STRUCT_STATVFS_F_BASETYPE) 371ca08ab68Smrg p = buf.f_basetype; 372ca08ab68Smrg# elif defined(HAVE_STRUCT_STATVFS_F_FSTYPENAME) 373ca08ab68Smrg p = buf.f_fstypename; 374ca08ab68Smrg# endif 375ca08ab68Smrg } 376ca08ab68Smrg#elif defined(HAVE_FSTATFS) && (defined(HAVE_STRUCT_STATFS_F_FLAGS) || defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) || defined(__linux__)) 377ca08ab68Smrg struct statfs buf; 378ca08ab68Smrg 3795e61939bSmrg memset (statb, 0, sizeof (FcStatFS)); 3805e61939bSmrg 381ca08ab68Smrg if ((ret = fstatfs (fd, &buf)) == 0) 382ca08ab68Smrg { 383ca08ab68Smrg# if defined(HAVE_STRUCT_STATFS_F_FLAGS) && defined(MNT_LOCAL) 384ca08ab68Smrg statb->is_remote_fs = !(buf.f_flags & MNT_LOCAL); 385ca08ab68Smrg flag = FcTrue; 386ca08ab68Smrg# endif 387ca08ab68Smrg# if defined(HAVE_STRUCT_STATFS_F_FSTYPENAME) 388ca08ab68Smrg p = buf.f_fstypename; 3897872e0a1Smrg# elif defined(__linux__) || defined (__EMSCRIPTEN__) 390ca08ab68Smrg switch (buf.f_type) 391ca08ab68Smrg { 392ca08ab68Smrg case 0x6969: /* nfs */ 393ca08ab68Smrg statb->is_remote_fs = FcTrue; 394ca08ab68Smrg break; 395ca08ab68Smrg case 0x4d44: /* fat */ 396ca08ab68Smrg statb->is_mtime_broken = FcTrue; 397ca08ab68Smrg break; 398ca08ab68Smrg default: 399ca08ab68Smrg break; 400ca08ab68Smrg } 401ca08ab68Smrg 402ca08ab68Smrg return ret; 403ca08ab68Smrg# else 404ca08ab68Smrg# error "BUG: No way to figure out with fstatfs()" 405ca08ab68Smrg# endif 406ca08ab68Smrg } 407ca08ab68Smrg#endif 408ca08ab68Smrg if (p) 409ca08ab68Smrg { 410ca08ab68Smrg if (!flag && strcmp (p, "nfs") == 0) 411ca08ab68Smrg statb->is_remote_fs = FcTrue; 412ca08ab68Smrg if (strcmp (p, "msdosfs") == 0 || 413ca08ab68Smrg strcmp (p, "pcfs") == 0) 414ca08ab68Smrg statb->is_mtime_broken = FcTrue; 415ca08ab68Smrg } 416ca08ab68Smrg 417ca08ab68Smrg return ret; 418ca08ab68Smrg} 419ca08ab68Smrg 420ca08ab68SmrgFcBool 421ca08ab68SmrgFcIsFsMmapSafe (int fd) 422ca08ab68Smrg{ 423ca08ab68Smrg FcStatFS statb; 424ca08ab68Smrg 425ca08ab68Smrg if (FcFStatFs (fd, &statb) < 0) 426ca08ab68Smrg return FcTrue; 427ca08ab68Smrg 428ca08ab68Smrg return !statb.is_remote_fs; 429ca08ab68Smrg} 430ca08ab68Smrg 431ca08ab68SmrgFcBool 432ca08ab68SmrgFcIsFsMtimeBroken (const FcChar8 *dir) 433ca08ab68Smrg{ 4345e61939bSmrg int fd = FcOpen ((const char *) dir, O_RDONLY); 435ca08ab68Smrg 436ca08ab68Smrg if (fd != -1) 437ca08ab68Smrg { 438ca08ab68Smrg FcStatFS statb; 439ca08ab68Smrg int ret = FcFStatFs (fd, &statb); 440ca08ab68Smrg 441ca08ab68Smrg close (fd); 442ca08ab68Smrg if (ret < 0) 443ca08ab68Smrg return FcFalse; 444ca08ab68Smrg 445ca08ab68Smrg return statb.is_mtime_broken; 446ca08ab68Smrg } 447ca08ab68Smrg 448ca08ab68Smrg return FcFalse; 449ca08ab68Smrg} 45018bd4a06Smrg 45118bd4a06Smrg#define __fcstat__ 45218bd4a06Smrg#include "fcaliastail.h" 45318bd4a06Smrg#undef __fcstat__ 454