From patchwork Fri Apr 17 13:22:08 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: 38828 Return-Path: X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-qk1-x744.google.com (mail-qk1-x744.google.com [IPv6:2607:f8b0:4864:20::744]) by sourceware.org (Postfix) with ESMTPS id 216E6385DC16 for ; Fri, 17 Apr 2020 13:22:27 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 216E6385DC16 Received: by mail-qk1-x744.google.com with SMTP id l78so2284300qke.7 for ; Fri, 17 Apr 2020 06:22:27 -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:subject:date:message-id:in-reply-to :references; bh=xvAWt/InoH4fPbVUh1NI/5rTttNNw4Q9wSYf93qAjdc=; b=hXFp4/v+OJru14gpaqp1CRUeDW/CT/tFUyYmFj+Hemy1IYAcG90BsfN8sET55LWdYO Rw7TeNVq16cdgDl9c2brG+HjA+M0Sfuw/neX8Uq46WexkWltAlIbtUpClFLutM2Jjbc5 IP+SLG0KbB731rRD8lNZF2BP7cosNaoE7FvaD45lgaWTUiHZd/LvoFtqz0RPvVVLqwfI xygI7HWhCJVRZjdPh63zg72TjigomTg2aGxjffdnLxr8d00PS8au+4/cYBy3ajrBLHbe cHlqZV5bqWcQ4XLuXKClwLnCGAdnmEAqspxpV+xXqhRzQVRUtT7td/KJbZvnir8uMJrG +NhQ== X-Gm-Message-State: AGi0PubV+/G3KMQfkAYTtK6TXS/2IBO8UjuC+Erc/YSCf1U0uxzyBcDQ Cl/DAkL4INfbFsBD2bJmYrk7YTiDGgV+QQ== X-Google-Smtp-Source: APiQypLzku3JewWKB4Xjxw9u0l3lnqoFgoh/VZSuy4sLFPbmuPQnvalt7a2b2+6W9ICG0k/Pk2q/Pg== X-Received: by 2002:a37:4708:: with SMTP id u8mr3192282qka.400.1587129746214; Fri, 17 Apr 2020 06:22:26 -0700 (PDT) Received: from localhost.localdomain ([177.194.48.209]) by smtp.googlemail.com with ESMTPSA id s14sm14737571qts.70.2020.04.17.06.22.25 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Apr 2020 06:22:25 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Subject: [PATCH 09/10] linux: Use getdents64 on readdir64 compat implementation Date: Fri, 17 Apr 2020 10:22:08 -0300 Message-Id: <20200417132209.22065-9-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200417132209.22065-1-adhemerval.zanella@linaro.org> References: <20200417132209.22065-1-adhemerval.zanella@linaro.org> X-Spam-Status: No, score=-25.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, 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-List-Received-Date: Fri, 17 Apr 2020 13:22:28 -0000 It uses a similar strategy from the non-LFS readdir that also uses getdents64 internally and reserves some space on the allocated internal DIR buffer to be used as a temporary buffer. The kernel obtained dirent64 data are copied to the temporary buffer for each readdir compat call. It allows to remove __old_getdents64. Checked on i686-linux-gnu. --- sysdeps/unix/sysv/linux/getdents64.c | 95 +--------------------------- sysdeps/unix/sysv/linux/olddirent.h | 2 - sysdeps/unix/sysv/linux/readdir.h | 47 +++++++++++++- sysdeps/unix/sysv/linux/readdir64.c | 27 ++++---- 4 files changed, 58 insertions(+), 113 deletions(-) diff --git a/sysdeps/unix/sysv/linux/getdents64.c b/sysdeps/unix/sysv/linux/getdents64.c index 75892c2823..f40dfacbfe 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 */ +#endif 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/readdir.h b/sysdeps/unix/sysv/linux/readdir.h index 7f6cf3783d..234a7f26e6 100644 --- a/sysdeps/unix/sysv/linux/readdir.h +++ b/sysdeps/unix/sysv/linux/readdir.h @@ -21,6 +21,7 @@ #if !_DIRENT_MATCHES_DIRENT64 # include +# include /* getdents64 is used internally for both LFS and non-LFS implementations. The non-LFS interface reserves part of the allocated buffer to return the @@ -106,9 +107,53 @@ dirstream_ret_entry (struct __dirstream *ds) return dp; } + +/* Return the allocated buffer used on LFS compat readdir call. */ +static inline struct __old_dirent64 * +dirstream_ret64_compat (struct __dirstream *ds) +{ + return (struct __old_dirent64 *) ds->data; +} + +static inline struct __old_dirent64 * +dirstream_ret_entry64_compat (struct __dirstream *ds) +{ + struct dirent64 *dp64 = dirstream_entry (ds); + struct __old_dirent64 *dp64_compat = dirstream_ret64_compat (ds); + + dp64_compat->d_ino = dp64->d_ino; + if (dp64_compat->d_ino != dp64->d_ino) + /* Overflow. */ + return NULL; + + dp64_compat->d_off = dp64->d_off; + + const size_t size_diff = (offsetof (struct dirent64, d_name) + - offsetof (struct __old_dirent64, d_name)); + const size_t alignment = _Alignof (struct __old_dirent64); + size_t new_reclen = (dp64->d_reclen - size_diff + alignment - 1) + & ~(alignment - 1); + if (new_reclen > return_buffer_size) + /* Overflow. */ + return NULL; + + /* The compat symbol report the kernel obtained d_reclen, even though + it has an incompatible dirent layout. */ + dp64_compat->d_reclen = dp64->d_reclen; + + dp64_compat->d_type = dp64->d_type; + + memcpy (dp64_compat->d_name, dp64->d_name, + dp64->d_reclen - offsetof (struct dirent64, d_name)); + + ds->offset += dp64->d_reclen; + ds->filepos = dp64->d_off; + + return dp64_compat; +} + #else /* No need to reserve an buffer space if dirent has already LFS support. */ enum { return_buffer_size = 0 }; #endif /* _DIRENT_MATCHES_DIRENT64 */ - #endif diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c index 4ce6024b38..4095b94a0f 100644 --- a/sysdeps/unix/sysv/linux/readdir64.c +++ b/sysdeps/unix/sysv/linux/readdir64.c @@ -108,27 +108,23 @@ weak_alias (__readdir64, readdir) # include versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2); # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) -# include +# include +# include 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 { - size_t reclen; - if (dirp->offset >= dirp->size) { /* We've emptied out our buffer. Refill it. */ - - size_t maxread = dirp->allocation; - ssize_t bytes; - - bytes = __old_getdents64 (dirp->fd, dirp->data, maxread); + ssize_t bytes = __getdents64 (dirp->fd, dirstream_data (dirp), + dirstream_alloc_size (dirp)); if (bytes <= 0) { /* On some systems getdents fails with ENOENT when the @@ -149,13 +145,12 @@ __old_readdir64_unlocked (DIR *dirp) dirp->offset = 0; } - dp = (struct __old_dirent64 *) &dirp->data[dirp->offset]; - - reclen = dp->d_reclen; - - dirp->offset += reclen; - - dirp->filepos = dp->d_off; + dp = dirstream_ret_entry64_compat (dirp); + if (dp == NULL) + { + __set_errno (EOVERFLOW); + break; + } /* Skip deleted files. */ } while (dp->d_ino == 0);