From patchwork Mon Feb 26 01:06:26 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nix X-Patchwork-Id: 26053 Received: (qmail 103132 invoked by alias); 26 Feb 2018 01:06:35 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 103122 invoked by uid 89); 26 Feb 2018 01:06:34 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, UNPARSEABLE_RELAY autolearn=ham version=3.3.2 spammy=1947 X-HELO: aserp2120.oracle.com From: Nick Alcock To: libc-alpha@sourceware.org Subject: [PATCH] Fix 32-bit getcwd() on filesystems with 64-bit inodes. Date: Mon, 26 Feb 2018 01:06:26 +0000 Message-ID: <87h8q47e5p.fsf@esperi.org.uk> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (gnu/linux) MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=nai engine=5900 definitions=8815 signatures=668680 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=1 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1711220000 definitions=main-1802260012 From: Nick Alcock When compiling a 32-bit glibc on a filesystem with 64-bit inodes, we observe failures of io/tst-getcwd-abspath: tst-getcwd-abspath.c:42: numeric comparison failure left: 75 (0x4b); from: errno right: 2 (0x2); from: ENOENT tst-getcwd-abspath.c:47: numeric comparison failure left: 75 (0x4b); from: errno right: 2 (0x2); from: ENOENT error: 2 test failures Errno 75 is EOVERFLOW, which is most unexpected from getcwd()! Having had experience with this class of pain in zic before (a patch which I should perhaps resubmit or combine with this one), the cause is clear: the getcwd() implementation was calling readdir(), and in glibc that is the non-LFS implementation so it falls over with just that error if it sees a single 64-bit inode. getcwd() is used in the dynamic linker as part of $ORIGIN support, so the usual SHLIB_COMPAT dance is needed there to prevent versioned symbols getting into it and causing disaster. While we're at it, fix likely similar errors in ttyname() (determined by code inspection, since my /dev is a tmpfs with 32-bit indoes: but one could run glibc on a system with a 64-bit-inode filesystem and a static /dev, and then this would probably fail too, the same way.) * include/dirent.h: Make __readdir64 hidden in rtld too. * sysdeps/unix/sysv/linux/i386/readdir64.c: Mark SHLIB_COMPAT in libc only. * posix/getcwd.c [__GNU_LIBRARY__] (__readdir64): Define. (__getcwd): Use dirent64 and __readdir64. * sysdeps/posix/ttyname.c (getttyname): Likewise: also ino64_t rather than ino_t. (ttyname): Use stat64 and fstat64. * sysdeps/posix/ttyname_r.c (getttyname_r): Likewise. (__ttyname_r): Likewise. sysdeps/unix/sysv/linux/tst-ttyname.c (run_chroot_tests): Use readdir64. --- include/dirent.h | 4 +++- sysdeps/posix/getcwd.c | 5 +++-- sysdeps/posix/ttyname.c | 16 ++++++++-------- sysdeps/posix/ttyname_r.c | 14 +++++++------- sysdeps/unix/sysv/linux/i386/readdir64.c | 14 ++++++++------ sysdeps/unix/sysv/linux/tst-ttyname.c | 4 ++-- 6 files changed, 31 insertions(+), 26 deletions(-) (Sent from my home address because my work address is not subscribed.) Tested on 32-bit and 64-bit x86_64; no new failures on 2.27 or master, and tst-getcwd-abspath is fixed. Quite possibly tst-ttyname is fixed too, but as noted I haven't been able to verify it. The ChangeLog incantations around posix/getcwd.c's top-level #defines (and readdir64.c's, too) are probably wrong: I'm not sure what the convention is here. It is quite possible I'm being unidiomatic in include/dirent.h and sysdeps/unix/sysv/linux/i386/readdir64.c: I'd be happy to switch to the idiomatic approach if someone could tell me what it is. :) diff --git a/include/dirent.h b/include/dirent.h index caaeb0be85..c1d3c6b53f 100644 --- a/include/dirent.h +++ b/include/dirent.h @@ -21,7 +21,9 @@ extern DIR *__fdopendir (int __fd) attribute_hidden; extern int __closedir (DIR *__dirp) attribute_hidden; extern struct dirent *__readdir (DIR *__dirp) attribute_hidden; extern struct dirent64 *__readdir64 (DIR *__dirp); -libc_hidden_proto (__readdir64) +# if IS_IN (libc) || IS_IN (rtld) + hidden_proto (__readdir64) +# endif extern int __readdir_r (DIR *__dirp, struct dirent *__entry, struct dirent **__result); extern int __readdir64_r (DIR *__dirp, struct dirent64 *__entry, diff --git a/sysdeps/posix/getcwd.c b/sysdeps/posix/getcwd.c index b53433a2dc..f547cd6612 100644 --- a/sysdeps/posix/getcwd.c +++ b/sysdeps/posix/getcwd.c @@ -194,6 +194,7 @@ extern char *alloca (); #ifndef __GNU_LIBRARY__ # define __lstat64 stat64 +# define __readdir64 readdir64 #endif #ifndef _LIBC @@ -366,14 +367,14 @@ __getcwd (char *buf, size_t size) goto lose; fd_needs_closing = false; - struct dirent *d; + struct dirent64 *d; bool use_d_ino = true; while (1) { /* Clear errno to distinguish EOF from error if readdir returns NULL. */ __set_errno (0); - d = __readdir (dirstream); + d = __readdir64 (dirstream); if (d == NULL) { if (errno == 0) diff --git a/sysdeps/posix/ttyname.c b/sysdeps/posix/ttyname.c index 3dae5e8411..dab784c7c6 100644 --- a/sysdeps/posix/ttyname.c +++ b/sysdeps/posix/ttyname.c @@ -27,20 +27,20 @@ char *__ttyname; -static char *getttyname (int fd, dev_t mydev, ino_t myino, +static char *getttyname (int fd, dev_t mydev, ino64_t myino, int save, int *dostat); libc_freeres_ptr (static char *getttyname_name); static char * -getttyname (int fd, dev_t mydev, ino_t myino, int save, int *dostat) +getttyname (int fd, dev_t mydev, ino64_t myino, int save, int *dostat) { static const char dev[] = "/dev"; static size_t namelen; struct stat st; DIR *dirstream; - struct dirent *d; + struct dirent64 *d; dirstream = __opendir (dev); if (dirstream == NULL) @@ -49,8 +49,8 @@ getttyname (int fd, dev_t mydev, ino_t myino, int save, int *dostat) return NULL; } - while ((d = __readdir (dirstream)) != NULL) - if (((ino_t) d->d_fileno == myino || *dostat) + while ((d = __readdir64 (dirstream)) != NULL) + if (((ino64_t) d->d_fileno == myino || *dostat) && strcmp (d->d_name, "stdin") && strcmp (d->d_name, "stdout") && strcmp (d->d_name, "stderr")) @@ -76,7 +76,7 @@ getttyname (int fd, dev_t mydev, ino_t myino, int save, int *dostat) #ifdef _STATBUF_ST_RDEV && S_ISCHR (st.st_mode) && st.st_rdev == mydev #else - && (ino_t) d->d_fileno == myino && st.st_dev == mydev + && (ino64_t) d->d_fileno == myino && st.st_dev == mydev #endif ) { @@ -97,7 +97,7 @@ getttyname (int fd, dev_t mydev, ino_t myino, int save, int *dostat) char * ttyname (int fd) { - struct stat st; + struct stat64 st; int dostat = 0; char *name; int save = errno; @@ -105,7 +105,7 @@ ttyname (int fd) if (!__isatty (fd)) return NULL; - if (fstat (fd, &st) < 0) + if (fstat64 (fd, &st) < 0) return NULL; #ifdef _STATBUF_ST_RDEV diff --git a/sysdeps/posix/ttyname_r.c b/sysdeps/posix/ttyname_r.c index 725de7c4fb..4e9b63028a 100644 --- a/sysdeps/posix/ttyname_r.c +++ b/sysdeps/posix/ttyname_r.c @@ -32,16 +32,16 @@ static const char dev[] = "/dev"; static int getttyname_r (int fd, char *buf, size_t buflen, - dev_t mydev, ino_t myino, int save, + dev_t mydev, ino64_t myino, int save, int *dostat) __THROW; static int -getttyname_r (int fd, char *buf, size_t buflen, dev_t mydev, ino_t myino, +getttyname_r (int fd, char *buf, size_t buflen, dev_t mydev, ino64_t myino, int save, int *dostat) { struct stat st; DIR *dirstream; - struct dirent *d; + struct dirent64 *d; dirstream = __opendir (dev); if (dirstream == NULL) @@ -50,8 +50,8 @@ getttyname_r (int fd, char *buf, size_t buflen, dev_t mydev, ino_t myino, return errno; } - while ((d = __readdir (dirstream)) != NULL) - if (((ino_t) d->d_fileno == myino || *dostat) + while ((d = __readdir64 (dirstream)) != NULL) + if (((ino64_t) d->d_fileno == myino || *dostat) && strcmp (d->d_name, "stdin") && strcmp (d->d_name, "stdout") && strcmp (d->d_name, "stderr")) @@ -74,7 +74,7 @@ getttyname_r (int fd, char *buf, size_t buflen, dev_t mydev, ino_t myino, #ifdef _STATBUF_ST_RDEV && S_ISCHR (st.st_mode) && st.st_rdev == mydev #else - && (ino_t) d->d_fileno == myino && st.st_dev == mydev + && (ino64_t) d->d_fileno == myino && st.st_dev == mydev #endif ) { @@ -121,7 +121,7 @@ __ttyname_r (int fd, char *buf, size_t buflen) return ENOTTY; } - if (fstat (fd, &st) < 0) + if (fstat64 (fd, &st) < 0) return errno; /* Prepare the result buffer. */ diff --git a/sysdeps/unix/sysv/linux/i386/readdir64.c b/sysdeps/unix/sysv/linux/i386/readdir64.c index 42b73023e0..e2981c7f9c 100644 --- a/sysdeps/unix/sysv/linux/i386/readdir64.c +++ b/sysdeps/unix/sysv/linux/i386/readdir64.c @@ -28,19 +28,21 @@ #undef DIRENT_TYPE libc_hidden_def (__readdir64) +#if IS_IN (libc) versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2); -#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) +# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) -#include +# include -#define __READDIR attribute_compat_text_section __old_readdir64 -#define __GETDENTS __old_getdents64 -#define DIRENT_TYPE struct __old_dirent64 +# define __READDIR attribute_compat_text_section __old_readdir64 +# define __GETDENTS __old_getdents64 +# define DIRENT_TYPE struct __old_dirent64 -#include +# include libc_hidden_def (__old_readdir64) compat_symbol (libc, __old_readdir64, readdir64, GLIBC_2_1); #endif +#endif diff --git a/sysdeps/unix/sysv/linux/tst-ttyname.c b/sysdeps/unix/sysv/linux/tst-ttyname.c index 35450e1c62..8a402c685e 100644 --- a/sysdeps/unix/sysv/linux/tst-ttyname.c +++ b/sysdeps/unix/sysv/linux/tst-ttyname.c @@ -528,8 +528,8 @@ run_chroot_tests (const char *slavename, int slave) int ci = 0; DIR *dirstream = opendir ("/dev"); VERIFY (dirstream != NULL); - struct dirent *d; - while ((d = readdir (dirstream)) != NULL && ci < 3) + struct dirent64 *d; + while ((d = readdir64 (dirstream)) != NULL && ci < 3) { if (strcmp (d->d_name, "console1") && strcmp (d->d_name, "console2") &&