From 905ae44c77a4b899100de99360823a586e095622 Mon Sep 17 00:00:00 2001 From: Adhemerval Zanella Date: Sat, 11 Apr 2020 17:07:11 -0300 Subject: linux: Move posix dir implementations to Linux This generic implementation already expects a getdents API which is Linux specific. It also allows simplify it by assuming _DIRENT_HAVE_D_RECLEN and _DIRENT_HAVE_D_OFF support. The readdir are also expanded on each required implementation, futher fixes and improvements will make parametrize the implementation more complex. Checked on x86_64-linux-gnu, i686-linux-gnu, and with a build for all affected ABIs. --- sysdeps/unix/sysv/linux/closedir.c | 54 ++++++++++ sysdeps/unix/sysv/linux/dirfd.c | 31 ++++++ sysdeps/unix/sysv/linux/dirstream.h | 55 ++++++++++ sysdeps/unix/sysv/linux/fdopendir.c | 52 +++++++++ sysdeps/unix/sysv/linux/opendir.c | 148 ++++++++++++++++++++++++++ sysdeps/unix/sysv/linux/readdir.c | 65 +++++++++++- sysdeps/unix/sysv/linux/readdir64.c | 131 +++++++++++++++++++++-- sysdeps/unix/sysv/linux/readdir64_r.c | 194 ++++++++++++++++++++++++++++++++-- sysdeps/unix/sysv/linux/readdir_r.c | 95 ++++++++++++++++- sysdeps/unix/sysv/linux/rewinddir.c | 41 +++++++ sysdeps/unix/sysv/linux/seekdir.c | 35 ++++++ sysdeps/unix/sysv/linux/telldir.c | 33 ++++++ 12 files changed, 911 insertions(+), 23 deletions(-) create mode 100644 sysdeps/unix/sysv/linux/closedir.c create mode 100644 sysdeps/unix/sysv/linux/dirfd.c create mode 100644 sysdeps/unix/sysv/linux/dirstream.h create mode 100644 sysdeps/unix/sysv/linux/fdopendir.c create mode 100644 sysdeps/unix/sysv/linux/opendir.c create mode 100644 sysdeps/unix/sysv/linux/rewinddir.c create mode 100644 sysdeps/unix/sysv/linux/seekdir.c create mode 100644 sysdeps/unix/sysv/linux/telldir.c (limited to 'sysdeps/unix/sysv') diff --git a/sysdeps/unix/sysv/linux/closedir.c b/sysdeps/unix/sysv/linux/closedir.c new file mode 100644 index 0000000000..ccc19eefcd --- /dev/null +++ b/sysdeps/unix/sysv/linux/closedir.c @@ -0,0 +1,54 @@ +/* Copyright (C) 1991-2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include + + +/* Close the directory stream DIRP. + Return 0 if successful, -1 if not. */ +int +__closedir (DIR *dirp) +{ + int fd; + + if (dirp == NULL) + { + __set_errno (EINVAL); + return -1; + } + + /* We do not try to synchronize access here. If some other thread + still uses this handle it is a big mistake and that thread + deserves all the bad data it gets. */ + + fd = dirp->fd; + +#if IS_IN (libc) + __libc_lock_fini (dirp->lock); +#endif + + free ((void *) dirp); + + return __close_nocancel (fd); +} +weak_alias (__closedir, closedir) diff --git a/sysdeps/unix/sysv/linux/dirfd.c b/sysdeps/unix/sysv/linux/dirfd.c new file mode 100644 index 0000000000..5d0928dca4 --- /dev/null +++ b/sysdeps/unix/sysv/linux/dirfd.c @@ -0,0 +1,31 @@ +/* Return the file descriptor used by a DIR stream. Unix version. + Copyright (C) 1995-2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +#undef dirfd + +int +__dirfd (DIR *dirp) +{ + return dirp->fd; +} + +weak_alias (__dirfd, dirfd) +libc_hidden_def (dirfd) diff --git a/sysdeps/unix/sysv/linux/dirstream.h b/sysdeps/unix/sysv/linux/dirstream.h new file mode 100644 index 0000000000..a3ea2b7197 --- /dev/null +++ b/sysdeps/unix/sysv/linux/dirstream.h @@ -0,0 +1,55 @@ +/* Copyright (C) 1993-2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef _DIRSTREAM_H +#define _DIRSTREAM_H 1 + +#include + +#include + +/* Directory stream type. + + The miscellaneous Unix `readdir' implementations read directory data + into a buffer and return `struct dirent *' pointers into it. */ + +struct __dirstream + { + int fd; /* File descriptor. */ + + __libc_lock_define (, lock) /* Mutex lock for this structure. */ + + size_t allocation; /* Space allocated for the block. */ + size_t size; /* Total valid data in the block. */ + size_t offset; /* Current offset into the block. */ + + off_t filepos; /* Position of next entry to read. */ + + int errcode; /* Delayed error code. */ + + /* Directory block. We must make sure that this block starts + at an address that is aligned adequately enough to store + dirent entries. Using the alignment of "void *" is not + sufficient because dirents on 32-bit platforms can require + 64-bit alignment. We use "long double" here to be consistent + with what malloc uses. */ + char data[0] __attribute__ ((aligned (__alignof__ (long double)))); + }; + +#define _DIR_dirfd(dirp) ((dirp)->fd) + +#endif /* dirstream.h */ diff --git a/sysdeps/unix/sysv/linux/fdopendir.c b/sysdeps/unix/sysv/linux/fdopendir.c new file mode 100644 index 0000000000..e424fbdaa2 --- /dev/null +++ b/sysdeps/unix/sysv/linux/fdopendir.c @@ -0,0 +1,52 @@ +/* Copyright (C) 2005-2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include + +#include + + +DIR * +__fdopendir (int fd) +{ + struct stat64 statbuf; + + if (__builtin_expect (__fstat64 (fd, &statbuf), 0) < 0) + return NULL; + if (__glibc_unlikely (! S_ISDIR (statbuf.st_mode))) + { + __set_errno (ENOTDIR); + return NULL; + } + + /* Make sure the descriptor allows for reading. */ + int flags = __fcntl64_nocancel (fd, F_GETFL); + if (__glibc_unlikely (flags == -1)) + return NULL; + if (__glibc_unlikely ((flags & O_ACCMODE) == O_WRONLY)) + { + __set_errno (EINVAL); + return NULL; + } + + return __alloc_dir (fd, false, flags, &statbuf); +} +weak_alias (__fdopendir, fdopendir) diff --git a/sysdeps/unix/sysv/linux/opendir.c b/sysdeps/unix/sysv/linux/opendir.c new file mode 100644 index 0000000000..e89e09bfc7 --- /dev/null +++ b/sysdeps/unix/sysv/linux/opendir.c @@ -0,0 +1,148 @@ +/* Copyright (C) 1991-2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include /* For BUFSIZ. */ +#include /* For MIN and MAX. */ + +#include + +/* The st_blksize value of the directory is used as a hint for the + size of the buffer which receives struct dirent values from the + kernel. st_blksize is limited to MAX_DIR_BUFFER_SIZE, in case the + file system provides a bogus value. */ +#define MAX_DIR_BUFFER_SIZE 1048576U + +enum { + opendir_oflags = O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC +}; + +static bool +invalid_name (const char *name) +{ + if (__glibc_unlikely (name[0] == '\0')) + { + /* POSIX.1-1990 says an empty name gets ENOENT; + but `open' might like it fine. */ + __set_errno (ENOENT); + return true; + } + return false; +} + +static DIR * +opendir_tail (int fd) +{ + if (__glibc_unlikely (fd < 0)) + return NULL; + + /* Now make sure this really is a directory and nothing changed since the + `stat' call. The S_ISDIR check is superfluous if O_DIRECTORY works, + but it's cheap and we need the stat call for st_blksize anyway. */ + struct stat64 statbuf; + if (__glibc_unlikely (__fstat64 (fd, &statbuf) < 0)) + goto lose; + if (__glibc_unlikely (! S_ISDIR (statbuf.st_mode))) + { + __set_errno (ENOTDIR); + lose: + __close_nocancel_nostatus (fd); + return NULL; + } + + return __alloc_dir (fd, true, 0, &statbuf); +} + + +#if IS_IN (libc) +DIR * +__opendirat (int dfd, const char *name) +{ + if (__glibc_unlikely (invalid_name (name))) + return NULL; + + return opendir_tail (__openat_nocancel (dfd, name, opendir_oflags)); +} +#endif + + +/* Open a directory stream on NAME. */ +DIR * +__opendir (const char *name) +{ + if (__glibc_unlikely (invalid_name (name))) + return NULL; + + return opendir_tail (__open_nocancel (name, opendir_oflags)); +} +weak_alias (__opendir, opendir) + +DIR * +__alloc_dir (int fd, bool close_fd, int flags, const struct stat64 *statp) +{ + /* We have to set the close-on-exit flag if the user provided the + file descriptor. */ + if (!close_fd + && __glibc_unlikely (__fcntl64_nocancel (fd, F_SETFD, FD_CLOEXEC) < 0)) + goto lose; + + const size_t default_allocation = (4 * BUFSIZ < sizeof (struct dirent64) + ? sizeof (struct dirent64) : 4 * BUFSIZ); + const size_t small_allocation = (BUFSIZ < sizeof (struct dirent64) + ? sizeof (struct dirent64) : BUFSIZ); + size_t allocation = default_allocation; +#ifdef _STATBUF_ST_BLKSIZE + /* Increase allocation if requested, but not if the value appears to + be bogus. */ + if (statp != NULL) + allocation = MIN (MAX ((size_t) statp->st_blksize, default_allocation), + MAX_DIR_BUFFER_SIZE); +#endif + + DIR *dirp = (DIR *) malloc (sizeof (DIR) + allocation); + if (dirp == NULL) + { + allocation = small_allocation; + dirp = (DIR *) malloc (sizeof (DIR) + allocation); + + if (dirp == NULL) + lose: + { + if (close_fd) + { + int save_errno = errno; + __close_nocancel_nostatus (fd); + __set_errno (save_errno); + } + return NULL; + } + } + + dirp->fd = fd; +#if IS_IN (libc) + __libc_lock_init (dirp->lock); +#endif + dirp->allocation = allocation; + dirp->size = 0; + dirp->offset = 0; + dirp->filepos = 0; + dirp->errcode = 0; + + return dirp; +} diff --git a/sysdeps/unix/sysv/linux/readdir.c b/sysdeps/unix/sysv/linux/readdir.c index df7a92aa78..2e03e66e69 100644 --- a/sysdeps/unix/sysv/linux/readdir.c +++ b/sysdeps/unix/sysv/linux/readdir.c @@ -19,5 +19,68 @@ #include #if !_DIRENT_MATCHES_DIRENT64 -# include +#include + +/* Read a directory entry from DIRP. */ +struct dirent * +__readdir (DIR *dirp) +{ + struct dirent *dp; + int saved_errno = errno; + +#if IS_IN (libc) + __libc_lock_lock (dirp->lock); +#endif + + 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 = __getdents (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 dirent *) &dirp->data[dirp->offset]; + + reclen = dp->d_reclen; + + dirp->offset += reclen; + + dirp->filepos = dp->d_off; + + /* Skip deleted files. */ + } while (dp->d_ino == 0); + +#if IS_IN (libc) + __libc_lock_unlock (dirp->lock); +#endif + + return dp; +} +weak_alias (__readdir, readdir) + #endif diff --git a/sysdeps/unix/sysv/linux/readdir64.c b/sysdeps/unix/sysv/linux/readdir64.c index 170a889c51..1aa6e2664f 100644 --- a/sysdeps/unix/sysv/linux/readdir64.c +++ b/sysdeps/unix/sysv/linux/readdir64.c @@ -23,17 +23,71 @@ #define readdir __no_readdir_decl #define __readdir __no___readdir_decl #include +#undef __readdir +#undef readdir -#define __READDIR __readdir64 -#define __GETDENTS __getdents64 -#define DIRENT_TYPE struct dirent64 +/* Read a directory entry from DIRP. */ +struct dirent64 * +__readdir64 (DIR *dirp) +{ + struct dirent64 *dp; + int saved_errno = errno; -#include +#if IS_IN (libc) + __libc_lock_lock (dirp->lock); +#endif -#undef __readdir -#undef readdir + 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 = __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 dirent64 *) &dirp->data[dirp->offset]; + + reclen = dp->d_reclen; + + dirp->offset += reclen; + + dirp->filepos = dp->d_off; + + /* Skip deleted files. */ + } while (dp->d_ino == 0); + +#if IS_IN (libc) + __libc_lock_unlock (dirp->lock); +#endif + + return dp; +} libc_hidden_def (__readdir64) + #if _DIRENT_MATCHES_DIRENT64 strong_alias (__readdir64, __readdir) weak_alias (__readdir64, readdir64) @@ -49,10 +103,67 @@ versioned_symbol (libc, __readdir64, readdir64, GLIBC_2_2); # endif # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) # include -# define __READDIR attribute_compat_text_section __old_readdir64 -# define __GETDENTS __old_getdents64 -# define DIRENT_TYPE struct __old_dirent64 -# include + +attribute_compat_text_section +struct __old_dirent64 * +__old_readdir64 (DIR *dirp) +{ + struct __old_dirent64 *dp; + int saved_errno = errno; + +#if IS_IN (libc) + __libc_lock_lock (dirp->lock); +#endif + + 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); + 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]; + + reclen = dp->d_reclen; + + dirp->offset += reclen; + + dirp->filepos = dp->d_off; + + /* Skip deleted files. */ + } while (dp->d_ino == 0); + +#if IS_IN (libc) + __libc_lock_unlock (dirp->lock); +#endif + + return dp; +} libc_hidden_def (__old_readdir64) compat_symbol (libc, __old_readdir64, readdir64, GLIBC_2_1); # endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) */ diff --git a/sysdeps/unix/sysv/linux/readdir64_r.c b/sysdeps/unix/sysv/linux/readdir64_r.c index 6d589f36f5..c587787417 100644 --- a/sysdeps/unix/sysv/linux/readdir64_r.c +++ b/sysdeps/unix/sysv/linux/readdir64_r.c @@ -23,15 +23,100 @@ #define readdir_r __no_readdir_r_decl #define __readdir_r __no___readdir_r_decl #include +#undef __readdir_r +#undef readdir_r -#define __READDIR_R __readdir64_r -#define __GETDENTS __getdents64 -#define DIRENT_TYPE struct dirent64 +/* Read a directory entry from DIRP. */ +int +__readdir64_r (DIR *dirp, struct dirent64 *entry, struct dirent64 **result) +{ + struct dirent64 *dp; + size_t reclen; + const int saved_errno = errno; + int ret; -#include + __libc_lock_lock (dirp->lock); + + do + { + if (dirp->offset >= dirp->size) + { + /* We've emptied out our buffer. Refill it. */ + + size_t maxread = dirp->allocation; + ssize_t bytes; + + maxread = dirp->allocation; + + bytes = __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; + __set_errno (saved_errno); + } + if (bytes < 0) + dirp->errcode = errno; + + dp = NULL; + break; + } + dirp->size = (size_t) bytes; + + /* Reset the offset into the buffer. */ + dirp->offset = 0; + } + + dp = (struct dirent64 *) &dirp->data[dirp->offset]; + + reclen = dp->d_reclen; + + dirp->offset += reclen; + + dirp->filepos = dp->d_off; + + if (reclen > offsetof (struct dirent64, d_name) + NAME_MAX + 1) + { + /* The record is very long. It could still fit into the + caller-supplied buffer if we can skip padding at the + end. */ + size_t namelen = _D_EXACT_NAMLEN (dp); + if (namelen <= NAME_MAX) + reclen = offsetof (struct dirent64, d_name) + namelen + 1; + else + { + /* The name is too long. Ignore this file. */ + dirp->errcode = ENAMETOOLONG; + dp->d_ino = 0; + continue; + } + } + + /* Skip deleted and ignored files. */ + } + while (dp->d_ino == 0); + + if (dp != NULL) + { + *result = memcpy (entry, dp, reclen); + entry->d_reclen = reclen; + ret = 0; + } + else + { + *result = NULL; + ret = dirp->errcode; + } + + __libc_lock_unlock (dirp->lock); + + return ret; +} -#undef __readdir_r -#undef readdir_r #if _DIRENT_MATCHES_DIRENT64 strong_alias (__readdir64_r, __readdir_r) @@ -44,10 +129,99 @@ weak_alias (__readdir64_r, readdir64_r) versioned_symbol (libc, __readdir64_r, readdir64_r, GLIBC_2_2); # if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) # include -# define __READDIR_R attribute_compat_text_section __old_readdir64_r -# define __GETDENTS __old_getdents64 -# define DIRENT_TYPE struct __old_dirent64 -# include + +int +attribute_compat_text_section +__old_readdir64_r (DIR *dirp, struct __old_dirent64 *entry, + struct __old_dirent64 **result) +{ + struct __old_dirent64 *dp; + size_t reclen; + const int saved_errno = errno; + int ret; + + __libc_lock_lock (dirp->lock); + + do + { + if (dirp->offset >= dirp->size) + { + /* We've emptied out our buffer. Refill it. */ + + size_t maxread = dirp->allocation; + ssize_t bytes; + + maxread = dirp->allocation; + + 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; + __set_errno (saved_errno); + } + if (bytes < 0) + dirp->errcode = 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]; + + reclen = dp->d_reclen; + + dirp->offset += reclen; + + dirp->filepos = dp->d_off; + + if (reclen > offsetof (struct __old_dirent64, d_name) + NAME_MAX + 1) + { + /* The record is very long. It could still fit into the + caller-supplied buffer if we can skip padding at the + end. */ + size_t namelen = _D_EXACT_NAMLEN (dp); + if (namelen <= NAME_MAX) + reclen = offsetof (struct __old_dirent64, d_name) + namelen + 1; + else + { + /* The name is too long. Ignore this file. */ + dirp->errcode = ENAMETOOLONG; + dp->d_ino = 0; + continue; + } + } + + /* Skip deleted and ignored files. */ + } + while (dp->d_ino == 0); + + if (dp != NULL) + { + *result = memcpy (entry, dp, reclen); + entry->d_reclen = reclen; + ret = 0; + } + else + { + *result = NULL; + ret = dirp->errcode; + } + + __libc_lock_unlock (dirp->lock); + + return ret; +} + compat_symbol (libc, __old_readdir64_r, readdir64_r, GLIBC_2_1); # endif /* SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) */ #endif /* _DIRENT_MATCHES_DIRENT64 */ diff --git a/sysdeps/unix/sysv/linux/readdir_r.c b/sysdeps/unix/sysv/linux/readdir_r.c index 30f237dbcc..0069041394 100644 --- a/sysdeps/unix/sysv/linux/readdir_r.c +++ b/sysdeps/unix/sysv/linux/readdir_r.c @@ -19,5 +19,96 @@ #include #if !_DIRENT_MATCHES_DIRENT64 -# include -#endif +/* Read a directory entry from DIRP. */ +int +__readdir_r (DIR *dirp, struct dirent *entry, struct dirent **result) +{ + struct dirent *dp; + size_t reclen; + const int saved_errno = errno; + int ret; + + __libc_lock_lock (dirp->lock); + + do + { + if (dirp->offset >= dirp->size) + { + /* We've emptied out our buffer. Refill it. */ + + size_t maxread = dirp->allocation; + ssize_t bytes; + + maxread = dirp->allocation; + + bytes = __getdents (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; + __set_errno (saved_errno); + } + if (bytes < 0) + dirp->errcode = errno; + + dp = NULL; + break; + } + dirp->size = (size_t) bytes; + + /* Reset the offset into the buffer. */ + dirp->offset = 0; + } + + dp = (struct dirent *) &dirp->data[dirp->offset]; + + reclen = dp->d_reclen; + + dirp->offset += reclen; + + dirp->filepos = dp->d_off; + + if (reclen > offsetof (struct dirent, d_name) + NAME_MAX + 1) + { + /* The record is very long. It could still fit into the + caller-supplied buffer if we can skip padding at the + end. */ + size_t namelen = _D_EXACT_NAMLEN (dp); + if (namelen <= NAME_MAX) + reclen = offsetof (struct dirent, d_name) + namelen + 1; + else + { + /* The name is too long. Ignore this file. */ + dirp->errcode = ENAMETOOLONG; + dp->d_ino = 0; + continue; + } + } + + /* Skip deleted and ignored files. */ + } + while (dp->d_ino == 0); + + if (dp != NULL) + { + *result = memcpy (entry, dp, reclen); + entry->d_reclen = reclen; + ret = 0; + } + else + { + *result = NULL; + ret = dirp->errcode; + } + + __libc_lock_unlock (dirp->lock); + + return ret; +} + +weak_alias (__readdir_r, readdir_r) +#endif /* _DIRENT_MATCHES_DIRENT64 */ diff --git a/sysdeps/unix/sysv/linux/rewinddir.c b/sysdeps/unix/sysv/linux/rewinddir.c new file mode 100644 index 0000000000..860bfda004 --- /dev/null +++ b/sysdeps/unix/sysv/linux/rewinddir.c @@ -0,0 +1,41 @@ +/* Copyright (C) 1991-2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include + +/* Rewind DIRP to the beginning of the directory. */ +void +__rewinddir (DIR *dirp) +{ +#if IS_IN (libc) + __libc_lock_lock (dirp->lock); +#endif + (void) __lseek (dirp->fd, (off_t) 0, SEEK_SET); + dirp->filepos = 0; + dirp->offset = 0; + dirp->size = 0; + dirp->errcode = 0; +#if IS_IN (libc) + __libc_lock_unlock (dirp->lock); +#endif +} +libc_hidden_def (__rewinddir) +weak_alias (__rewinddir, rewinddir) diff --git a/sysdeps/unix/sysv/linux/seekdir.c b/sysdeps/unix/sysv/linux/seekdir.c new file mode 100644 index 0000000000..3c30520928 --- /dev/null +++ b/sysdeps/unix/sysv/linux/seekdir.c @@ -0,0 +1,35 @@ +/* Copyright (C) 1991-2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include + +/* Seek to position POS in DIRP. */ +/* XXX should be __seekdir ? */ +void +seekdir (DIR *dirp, long int pos) +{ + __libc_lock_lock (dirp->lock); + (void) __lseek (dirp->fd, pos, SEEK_SET); + dirp->size = 0; + dirp->offset = 0; + dirp->filepos = pos; + __libc_lock_unlock (dirp->lock); +} diff --git a/sysdeps/unix/sysv/linux/telldir.c b/sysdeps/unix/sysv/linux/telldir.c new file mode 100644 index 0000000000..57d435ed21 --- /dev/null +++ b/sysdeps/unix/sysv/linux/telldir.c @@ -0,0 +1,33 @@ +/* Copyright (C) 1991-2020 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +#include + +/* Return the current position of DIRP. */ +long int +telldir (DIR *dirp) +{ + long int ret; + + __libc_lock_lock (dirp->lock); + ret = dirp->filepos; + __libc_lock_unlock (dirp->lock); + + return ret; +} -- cgit 1.4.1