From patchwork Wed Oct 21 14:15:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 40804 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 990113959E6D; Wed, 21 Oct 2020 14:16:06 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 990113959E6D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1603289766; bh=Afx4r27ZhBdDfqA5xdcHNZulmcYtfxVVB1wONBAKKSE=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=rkWXbCa2P19ynfUi/uZrxXQY6GZvZpzF9xu9yaAGGhgsQL6qhMQtIGGkZ1I4HYNj/ VI2qfmZvlYDo1CXscTCpEMiOeDJpI8NKOiiS5+kRj72YOnsAx6/M0y8FYKCnEX3xdn AKJIWJJcZsOrq/s4A3WnrYiqj0Dq/6VssNWgGu7Q= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-qt1-x842.google.com (mail-qt1-x842.google.com [IPv6:2607:f8b0:4864:20::842]) by sourceware.org (Postfix) with ESMTPS id BB2373959E59 for ; Wed, 21 Oct 2020 14:16:03 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org BB2373959E59 Received: by mail-qt1-x842.google.com with SMTP id r8so2145699qtp.13 for ; Wed, 21 Oct 2020 07:16:03 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Afx4r27ZhBdDfqA5xdcHNZulmcYtfxVVB1wONBAKKSE=; b=JglqZNtabUsY3/6+m1T1uDsbCGKaFcG7/fiQnr3hK927au23Wjzh1pnooKaDKlDx4Z hunLxRYfuyG0xeEJyDMnDS1UCKmDj2YL7EcnwVX2L1pee4bJwPQ3QPoC1m6ClfSmN3JL nFyDtirqYE7A4Kx9wbxfdY9n0dzyIDLtTtGup+NyXuY2nLOoUbqF73HK6T2J3deHcE2n BwCrv4HNgO/TUMgG5n+cXKR5tKX0Q5TawAr0/humBD/dTr5VF+499TAwkw4+CtcL0iiw h3cPZafSpnT5PzA0PucLZJpyygTXJLNVBUkWJjagbiLNnDuq2roy1xqC6Fiq765J+Ubw hQ+w== X-Gm-Message-State: AOAM5316ZIJqYUlqfBBeDNQ/EjIbxKq0+sCqtJkAk+DeFUUBtcx8f1wz Peo2hW1ug7zKshVMZBjz4Krui+yqhU6/2w== X-Google-Smtp-Source: ABdhPJw9Jb7IRJfyvxmxgq8jl70WY33WVs/mJjxxbVGEFSDtE/3oOVEe1hKoyVUzrns3l9zkYVtf/w== X-Received: by 2002:ac8:36ca:: with SMTP id b10mr3242825qtc.135.1603289762913; Wed, 21 Oct 2020 07:16:02 -0700 (PDT) Received: from localhost.localdomain ([177.194.48.209]) by smtp.googlemail.com with ESMTPSA id e17sm1187952qts.54.2020.10.21.07.16.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Oct 2020 07:16:02 -0700 (PDT) To: libc-alpha@sourceware.org, Florian Weimer Subject: [PATCH v3 6/7] linux: Use getdents64 on readdir64 compat implementation Date: Wed, 21 Oct 2020 11:15:41 -0300 Message-Id: <20201021141542.2003377-7-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20201021141542.2003377-1-adhemerval.zanella@linaro.org> References: <20201021141542.2003377-1-adhemerval.zanella@linaro.org> MIME-Version: 1.0 X-Spam-Status: No, score=-14.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Adhemerval Zanella via Libc-alpha From: Adhemerval Zanella Netto Reply-To: Adhemerval Zanella Cc: James Clarke , John Paul Adrian Glaubitz Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" It uses a similar strategy from the non-LFS readdir that also uses getdents64 internally and uses a translation buffer to return the compat readdir64 entry. It allows to remove __old_getdents64. Checked on i686-linux-gnu. --- sysdeps/unix/sysv/linux/getdents64.c | 93 ------------------------ sysdeps/unix/sysv/linux/olddirent.h | 2 - sysdeps/unix/sysv/linux/opendir.c | 15 +++- sysdeps/unix/sysv/linux/readdir64.c | 104 +++++++++++++++++---------- 4 files changed, 79 insertions(+), 135 deletions(-) diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c index 75892c2823..b8682c3472 100644 --- a/sysdeps/unix/sysv/linux/getdents64.c +++ b/sysdeps/unix/sysv/linux/getdents64.c @@ -36,97 +36,4 @@ weak_alias (__getdents64, getdents64) #if _DIRENT_MATCHES_DIRENT64 strong_alias (__getdents64, __getdents) -#else -# include - -# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) -# include -# include - -static ssize_t -handle_overflow (int fd, __off64_t offset, ssize_t count) -{ - /* If this is the first entry in the buffer, we can report the - error. */ - if (offset == 0) - { - __set_errno (EOVERFLOW); - return -1; - } - - /* Otherwise, seek to the overflowing entry, so that the next call - will report the error, and return the data read so far. */ - if (__lseek64 (fd, offset, SEEK_SET) != 0) - return -1; - return count; -} - -ssize_t -__old_getdents64 (int fd, char *buf, size_t nbytes) -{ - /* We do not move the individual directory entries. This is only - possible if the target type (struct __old_dirent64) is smaller - than the source type. */ - _Static_assert (offsetof (struct __old_dirent64, d_name) - <= offsetof (struct dirent64, d_name), - "__old_dirent64 is larger than dirent64"); - _Static_assert (__alignof__ (struct __old_dirent64) - <= __alignof__ (struct dirent64), - "alignment of __old_dirent64 is larger than dirent64"); - - ssize_t retval = INLINE_SYSCALL_CALL (getdents64, fd, buf, nbytes); - if (retval > 0) - { - /* This is the marker for the first entry. Offset 0 is reserved - for the first entry (see rewinddir). Here, we use it as a - marker for the first entry in the buffer. We never actually - seek to offset 0 because handle_overflow reports the error - directly, so it does not matter that the offset is incorrect - if entries have been read from the descriptor before (so that - the descriptor is not actually at offset 0). */ - __off64_t previous_offset = 0; - - char *p = buf; - char *end = buf + retval; - while (p < end) - { - struct dirent64 *source = (struct dirent64 *) p; - - /* Copy out the fixed-size data. */ - __ino_t ino = source->d_ino; - __off64_t offset = source->d_off; - unsigned int reclen = source->d_reclen; - unsigned char type = source->d_type; - - /* Check for ino_t overflow. */ - if (__glibc_unlikely (ino != source->d_ino)) - return handle_overflow (fd, previous_offset, p - buf); - - /* Convert to the target layout. Use a separate struct and - memcpy to side-step aliasing issues. */ - struct __old_dirent64 result; - result.d_ino = ino; - result.d_off = offset; - result.d_reclen = reclen; - result.d_type = type; - - /* Write the fixed-sized part of the result to the - buffer. */ - size_t result_name_offset = offsetof (struct __old_dirent64, d_name); - memcpy (p, &result, result_name_offset); - - /* Adjust the position of the name if necessary. Copy - everything until the end of the record, including the - terminating NUL byte. */ - if (result_name_offset != offsetof (struct dirent64, d_name)) - memmove (p + result_name_offset, source->d_name, - reclen - offsetof (struct dirent64, d_name)); - - p += reclen; - previous_offset = offset; - } - } - return retval; -} -# endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) */ #endif /* _DIRENT_MATCHES_DIRENT64 */ diff --git a/sysdeps/unix/sysv/linux/olddirent.h b/sysdeps/unix/sysv/linux/olddirent.h index b118b0ef96..63d8e97868 100644 --- a/sysdeps/unix/sysv/linux/olddirent.h +++ b/sysdeps/unix/sysv/linux/olddirent.h @@ -36,8 +36,6 @@ extern struct __old_dirent64 *__old_readdir64_unlocked (DIR *__dirp) attribute_hidden; extern int __old_readdir64_r (DIR *__dirp, struct __old_dirent64 *__entry, struct __old_dirent64 **__result); -extern __ssize_t __old_getdents64 (int __fd, char *__buf, size_t __nbytes) - attribute_hidden; int __old_scandir64 (const char * __dir, struct __old_dirent64 *** __namelist, int (*__selector) (const struct __old_dirent64 *), diff --git a/sysdeps/unix/sysv/linux/opendir.c b/sysdeps/unix/sysv/linux/opendir.c index 28fd55341e..3a08b76c0a 100644 --- a/sysdeps/unix/sysv/linux/opendir.c +++ b/sysdeps/unix/sysv/linux/opendir.c @@ -23,6 +23,11 @@ #include +#include +#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) +# include +#endif + enum { opendir_oflags = O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC }; @@ -127,7 +132,15 @@ __alloc_dir (int fd, bool close_fd, int flags, const struct stat64 *statp) expand the buffer if required. */ enum { - tbuffer_size = sizeof (struct dirent) + NAME_MAX + 1 + tbuffer_size = +# if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) + /* This is used on compat readdir64. */ + MAX (sizeof (struct dirent), + sizeof (struct __old_dirent64)) +# else + sizeof (struct dirent) +# endif + + NAME_MAX + 1 }; dirp->tbuffer = malloc (tbuffer_size); if (dirp->tbuffer == NULL) diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c index acd190733c..799708174a 100644 --- a/sysdeps/unix/sysv/linux/readdir64.c +++ b/sysdeps/unix/sysv/linux/readdir64.c @@ -99,57 +99,83 @@ versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2); # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) # include +/* Translate the DP64 entry to the old LFS one in the translation buffer + at dirstream DS. Return true is the translation was possible or + false if either an internal fields can be represented in the non-LFS + entry or if the translation can not be resized. */ +static bool +dirstream_old_entry (struct __dirstream *ds, const struct dirent64 *dp64) +{ + /* Check for overflow. */ + ino_t d_ino = dp64->d_ino; + if (d_ino != dp64->d_ino) + return false; + + /* Expand the translation buffer to hold the new namesize. */ + size_t d_reclen = sizeof (struct __old_dirent64) + + dp64->d_reclen - offsetof (struct dirent64, d_name); + if (d_reclen > ds->tbuffer_size) + { + char *newbuffer = realloc (ds->tbuffer, d_reclen); + if (newbuffer == NULL) + return false; + ds->tbuffer = newbuffer; + ds->tbuffer_size = d_reclen; + } + + struct __old_dirent64 *olddp64 = (struct __old_dirent64 *) ds->tbuffer; + + olddp64->d_off = dp64->d_off; + olddp64->d_ino = dp64->d_ino; + olddp64->d_reclen = dp64->d_reclen; + olddp64->d_type = dp64->d_type; + memcpy (olddp64->d_name, dp64->d_name, + dp64->d_reclen - offsetof (struct dirent64, d_name)); + + return true; +} + attribute_compat_text_section struct __old_dirent64 * __old_readdir64_unlocked (DIR *dirp) { - struct __old_dirent64 *dp; - int saved_errno = errno; + const int saved_errno = errno; - do + if (dirp->offset >= dirp->size) { - size_t reclen; - - if (dirp->offset >= dirp->size) + /* We've emptied out our buffer. Refill it. */ + ssize_t bytes = __getdents64 (dirp->fd, dirp->data, dirp->allocation); + if (bytes <= 0) { - /* We've emptied out our buffer. Refill it. */ - - size_t maxread = dirp->allocation; - ssize_t bytes; - - bytes = __old_getdents64 (dirp->fd, dirp->data, maxread); - if (bytes <= 0) - { - /* On some systems getdents fails with ENOENT when the - open directory has been rmdir'd already. POSIX.1 - requires that we treat this condition like normal EOF. */ - if (bytes < 0 && errno == ENOENT) - bytes = 0; - - /* Don't modifiy errno when reaching EOF. */ - if (bytes == 0) - __set_errno (saved_errno); - dp = NULL; - break; - } - dirp->size = (size_t) bytes; - - /* Reset the offset into the buffer. */ - dirp->offset = 0; - } - - dp = (struct __old_dirent64 *) &dirp->data[dirp->offset]; + /* On some systems getdents fails with ENOENT when the + open directory has been rmdir'd already. POSIX.1 + requires that we treat this condition like normal EOF. */ + if (bytes < 0 && errno == ENOENT) + bytes = 0; - reclen = dp->d_reclen; + /* Don't modifiy errno when reaching EOF. */ + if (bytes == 0) + __set_errno (saved_errno); + return NULL; + } + dirp->size = bytes; - dirp->offset += reclen; + /* Reset the offset into the buffer. */ + dirp->offset = 0; + } - dirp->filepos = dp->d_off; + struct dirent64 *dp64 = (struct dirent64 *) &dirp->data[dirp->offset]; + dirp->offset += dp64->d_reclen; - /* Skip deleted files. */ - } while (dp->d_ino == 0); + /* Skip entries which might overflow d_ino or for memory allocation failure + in case of large file names. */ + if (dirstream_old_entry (dirp, dp64)) + { + dirp->filepos = dp64->d_off; + return (struct __old_dirent64 *) dirp->tbuffer; + } - return dp; + return NULL; } attribute_compat_text_section