glob: Simplify the interface for the GLOB_ALTDIRFUNC callback gl_readdir

Previously, application code had to set up the d_namlen member if
the target supported it, involving conditional compilation.  After
this change, glob will use the length of the string in d_name instead
of d_namlen to determine the file name length.  All glibc targets
provide the d_type and d_ino members, and setting them as needed for
gl_readdir is straightforward.

Changing the behavior with regards to d_ino is left to a future
cleanup.
This commit is contained in:
Florian Weimer 2016-04-29 09:33:07 +02:00
parent a7657f3012
commit 137fe72eca
6 changed files with 100 additions and 24 deletions

View File

@ -1,3 +1,18 @@
2016-04-29 Florian Weimer <fweimer@redhat.com>
glob: Simplify and document the interface for the GLOB_ALTDIRFUNC
callback function gl_readdir.
* posix/glob.c (NAMELEN, CONVERT_D_NAMLEN): Remove.
(CONVERT_DIRENT_DIRENT64): Use strcpy instead of memcpy.
(glob_in_dir): Remove len. Use strdup instead of malloc and
memcpy to copy the name.
* manual/pattern.texi (Calling Glob): Document requirements for
implementations of the gl_readdir callback function.
* manual/examples/mkdirent.c: New example.
* posix/bug-glob2.c (my_readdir): Set d_ino to 1 unconditionally,
per the manual guidance.
* posix/tst-gnuglob.c (my_readdir): Likewise.
2016-04-28 Joseph Myers <joseph@codesourcery.com>
[BZ #20014]

View File

@ -0,0 +1,42 @@
/* Example for creating a struct dirent object for use with glob.
Copyright (C) 2016 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, if not, see <http://www.gnu.org/licenses/>.
*/
#include <dirent.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
struct dirent *
mkdirent (const char *name)
{
size_t dirent_size = offsetof (struct dirent, d_name) + 1;
size_t name_length = strlen (name);
size_t total_size = dirent_size + name_length;
if (total_size < dirent_size)
{
errno = ENOMEM;
return NULL;
}
struct dirent *result = malloc (total_size);
if (result == NULL)
return NULL;
result->d_type = DT_UNKNOWN;
result->d_ino = 1; /* Do not skip this entry. */
memcpy (result->d_name, name, name_length + 1);
return result;
}

View File

@ -237,7 +237,44 @@ function used to read the contents of a directory. It is used if the
@code{GLOB_ALTDIRFUNC} bit is set in the flag parameter. The type of
this field is @w{@code{struct dirent *(*) (void *)}}.
This is a GNU extension.
An implementation of @code{gl_readdir} needs to initialize the following
members of the @code{struct dirent} object:
@table @code
@item d_type
This member should be set to the file type of the entry if it is known.
Otherwise, the value @code{DT_UNKNOWN} can be used. The @code{glob}
function may use the specified file type to avoid callbacks in cases
where the file type indicates that the data is not required.
@item d_ino
This member needs to be non-zero, otherwise @code{glob} may skip the
current entry and call the @code{gl_readdir} callback function again to
retrieve another entry.
@item d_name
This member must be set to the name of the entry. It must be
null-terminated.
@end table
The example below shows how to allocate a @code{struct dirent} object
containing a given name.
@smallexample
@include mkdirent.c.texi
@end smallexample
The @code{glob} function reads the @code{struct dirent} members listed
above and makes a copy of the file name in the @code{d_name} member
immediately after the @code{gl_readdir} callback function returns.
Future invocations of any of the callback functions may dealloacte or
reuse the buffer. It is the responsibility of the caller of the
@code{glob} function to allocate and deallocate the buffer, around the
call to @code{glob} or using the callback functions. For example, an
application could allocate the buffer in the @code{gl_readdir} callback
function, and deallocate it in the @code{gl_closedir} callback function.
The @code{gl_readdir} member is a GNU extension.
@item gl_opendir
The address of an alternative implementation of the @code{opendir}

View File

@ -193,7 +193,7 @@ my_readdir (void *gdir)
return NULL;
}
dir->d.d_ino = dir->idx;
dir->d.d_ino = 1; /* glob should not skip this entry. */
#ifdef _DIRENT_HAVE_D_TYPE
dir->d.d_type = filesystem[dir->idx].type;

View File

@ -57,10 +57,8 @@
#if defined HAVE_DIRENT_H || defined __GNU_LIBRARY__
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# ifdef HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
@ -76,12 +74,6 @@
#endif
/* In GNU systems, <dirent.h> defines this macro for us. */
#ifdef _D_NAMLEN
# undef NAMLEN
# define NAMLEN(d) _D_NAMLEN(d)
#endif
/* When used in the GNU libc the symbol _DIRENT_HAVE_D_TYPE is available
if the `d_type' member for `struct dirent' is available.
HAVE_STRUCT_DIRENT_D_TYPE plays the same role in GNULIB. */
@ -105,12 +97,6 @@
/* If the system has the `struct dirent64' type we use it internally. */
#if defined _LIBC && !defined COMPILE_GLOB64
# if defined HAVE_DIRENT_H || defined __GNU_LIBRARY__
# define CONVERT_D_NAMLEN(d64, d32)
# else
# define CONVERT_D_NAMLEN(d64, d32) \
(d64)->d_namlen = (d32)->d_namlen;
# endif
# if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__
# define CONVERT_D_INO(d64, d32)
@ -127,8 +113,7 @@
# endif
# define CONVERT_DIRENT_DIRENT64(d64, d32) \
memcpy ((d64)->d_name, (d32)->d_name, NAMLEN (d32) + 1); \
CONVERT_D_NAMLEN (d64, d32) \
strcpy ((d64)->d_name, (d32)->d_name); \
CONVERT_D_INO (d64, d32) \
CONVERT_D_TYPE (d64, d32)
#endif
@ -1554,7 +1539,6 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
while (1)
{
const char *name;
size_t len;
#if defined _LIBC && !defined COMPILE_GLOB64
struct dirent64 *d;
union
@ -1622,12 +1606,10 @@ glob_in_dir (const char *pattern, const char *directory, int flags,
names = newnames;
cur = 0;
}
len = NAMLEN (d);
names->name[cur] = (char *) malloc (len + 1);
names->name[cur] = strdup (d->d_name);
if (names->name[cur] == NULL)
goto memory_error;
*((char *) mempcpy (names->name[cur++], name, len))
= '\0';
++cur;
++nfound;
}
}

View File

@ -211,7 +211,7 @@ my_readdir (void *gdir)
return NULL;
}
dir->d.d_ino = dir->idx;
dir->d.d_ino = 1; /* glob should not skip this entry. */
#ifdef _DIRENT_HAVE_D_TYPE
dir->d.d_type = filesystem[dir->idx].type;