1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-07-29 11:41:21 +03:00

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.
This commit is contained in:
Adhemerval Zanella
2020-04-11 17:07:11 -03:00
parent f1ed4d4c2c
commit 905ae44c77
14 changed files with 464 additions and 311 deletions

View File

@ -23,16 +23,101 @@
#define readdir_r __no_readdir_r_decl
#define __readdir_r __no___readdir_r_decl
#include <dirent.h>
#define __READDIR_R __readdir64_r
#define __GETDENTS __getdents64
#define DIRENT_TYPE struct dirent64
#include <sysdeps/posix/readdir_r.c>
#undef __readdir_r
#undef readdir_r
/* 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;
__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;
}
#if _DIRENT_MATCHES_DIRENT64
strong_alias (__readdir64_r, __readdir_r)
weak_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 <olddirent.h>
# define __READDIR_R attribute_compat_text_section __old_readdir64_r
# define __GETDENTS __old_getdents64
# define DIRENT_TYPE struct __old_dirent64
# include <sysdeps/posix/readdir_r.c>
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 */