# The 2.6 asm/stat.h for ARM has some rather unusual transmogrifications # for big-endian running. This patch adds ARM specific code in xstatconv.c # which deals with the 2.4->2.6 ABI change. --- uClibc-0.9.27/libc/sysdeps/linux/common/xstatconv.c 2005-01-11 23:59:21.000000000 -0800 +++ uClibc-0.9.27/libc/sysdeps/linux/common/xstatconv.c 2005-06-05 11:03:56.742587966 -0700 @@ -18,7 +18,14 @@ 02111-1307 USA. Modified for uClibc by Erik Andersen + Further modified for ARMBE by John Bowler */ +/* This is a copy of common/xstatconv.c with a fixup for the ABI + * (structure layout) change in ARM Linux 2.6 - this shifts the + * st_dev and st_rdev information from the start of the 8 byte + * space to the end on big-endian ARM (only). The code is unchanged + * on little endian. + */ #define _GNU_SOURCE #define _LARGEFILE64_SOURCE @@ -32,6 +39,84 @@ #include #include "xstatconv.h" +/* Only for ARMEB and LFS. */ +#if defined(__ARMEB__) && defined(__UCLIBC_HAS_LFS__) +/* stat64 (renamed) from 2.6.11.11. What happened here is that after + * Linux 2.4 the 2.4 unsigned short st_rdev and st_dev fields were + * lengthened to unsigned long long - causing the inclusion of at least + * some of the 0 padding bytes which followed them. On little endian + * this is fine because 2.4 did zero the pad bytes (I think) and the + * position of the data did not change. On big endian the change + * shifted the data to the end of the field. Someone noticed for the + * struct stat, and the armeb (big endian) case preserved the + * unsigned short (yuck), but no so for stat64 (maybe this was deliberate, + * but there is no evidence in the code of this.) Consequently a + * fixup is necessary for the stat64 case. The fixup here is to + * use the new structure when the change is detected. See below. + */ +struct __kernel_stat64_armeb { + /* This definition changes the layout on big-endian from that + * used in 2.4.31 - ABI change! Likewise for st_rdev. + */ + unsigned long long st_dev; + unsigned char __pad0[4]; + unsigned long __st_ino; + unsigned int st_mode; + unsigned int st_nlink; + unsigned long st_uid; + unsigned long st_gid; + unsigned long long st_rdev; + unsigned char __pad3[4]; + long long st_size; + unsigned long st_blksize; + unsigned long __pad4; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long st_atime_nsec; + unsigned long st_mtime; + unsigned long st_mtime_nsec; + unsigned long st_ctime; + unsigned long st_ctime_nsec; + unsigned long long st_ino; +}; + +/* This fixup only works so long as the old struct stat64 is no + * smaller than the new one - the caller of xstatconv uses the + * *old* struct, but the kernel writes the new one. CASSERT + * detects this at compile time. + */ +#define CASSERT(c) do switch (0) { case 0:; case (c):; } while (0) + +void __xstat64_conv_new(struct __kernel_stat64_armeb *kbuf, struct stat64 *buf) +{ + CASSERT(sizeof *kbuf <= sizeof (struct kernel_stat64)); + + /* Convert from new kernel version of `struct stat64'. */ + buf->st_dev = kbuf->st_dev; + buf->st_ino = kbuf->st_ino; +#ifdef _HAVE_STAT64___ST_INO + buf->__st_ino = kbuf->__st_ino; +#endif + buf->st_mode = kbuf->st_mode; + buf->st_nlink = kbuf->st_nlink; + buf->st_uid = kbuf->st_uid; + buf->st_gid = kbuf->st_gid; + buf->st_rdev = kbuf->st_rdev; + buf->st_size = kbuf->st_size; + buf->st_blksize = kbuf->st_blksize; + buf->st_blocks = kbuf->st_blocks; + buf->st_atime = kbuf->st_atime; + buf->st_mtime = kbuf->st_mtime; + buf->st_ctime = kbuf->st_ctime; +} +#define _MAY_HAVE_NEW_STAT64 1 +#else +#define _MAY_HAVE_NEW_STAT64 0 +#endif + +/* The following is taken verbatim from xstatconv.c apart from + * the addition of the _MAY_HAVE_NEW_STAT64 code. + */ void __xstat_conv(struct kernel_stat *kbuf, struct stat *buf) { /* Convert to current kernel version of `struct stat'. */ @@ -53,6 +138,19 @@ #if defined __UCLIBC_HAS_LFS__ void __xstat64_conv(struct kernel_stat64 *kbuf, struct stat64 *buf) { +# if _MAY_HAVE_NEW_STAT64 + /* This relies on any device (0,0) not being mountable - i.e. + * it fails on Linux 2.4 if dev(0,0) is a mountable block file + * system and itself contains it's own device. That doesn't + * happen on Linux 2.4 so far as I can see, but even if it + * does the API only fails (even then) if 2.4 didn't set all + * of the pad bytes to 0 (and it does set them to zero.) + */ + if (kbuf->st_dev == 0 && kbuf->st_rdev == 0) { + __xstat64_conv_new((struct __kernel_stat64_armeb*)kbuf, buf); + return; + } +# endif /* Convert to current kernel version of `struct stat64'. */ buf->st_dev = kbuf->st_dev; buf->st_ino = kbuf->st_ino;