About a month ago, I received an upstream
bugreport that the nbd-server
wouldn't build on Solaris and its derivatives. This was because
nbd-server uses the d_type
field of struct dirent
, which is widely
implemented (in Linux and FreeBSD, at least), but not part of POSIX and
therefore not implemented on Solaris (which tends to be more
conservative about implementing new features).
The bug reporter pointed towards a blog
post
by a Solaris user who had written something he calls "adirent", meant to
work around the issue by implementing something that would wrap
readdir()
so that it would inject a stat()
call when needed. While
that approach works, it seems a bit strange to add a function which
wraps readdir
to become portable. After all, readdir()
does not
always return the file type in d_type
, not even on systems that do
implement it. One example in which this is true is XFS; if one runs
readdir()
on a directory on an XFS filesystem, then everything will
have DT_UNKNOWN
as its filetype, indicating that you need to run
stat()
after all.
As such, I think a better approach is to use that fact so that things
will just work on systems where d_type
isn't available. The GNU
autotools even have a test for it (AC_STRUCT_DIRENT_D_TYPE
), which
makes things easier. In the case of NBD, I've added that to
configure.ac, and then added a touch of preprocessor magic to reuse the
infrastructure for dealing with DT_UNKNOWN which is already there:
#ifdef HAVE_STRUCT_DIRENT_D_TYPE
#define NBD_D_TYPE de->d_type
#else
#define NBD_D_TYPE 0
#define DT_UKNOWN 0
#define DT_REG 1
#endif
(...opendir()
, readdir()
, ...)
switch(NBD_D_TYPE) {
case DT_UNKNOWN:
(...call stat()
, figure out if it is a file...)
case DT_REG:
(...we know it is a file...)
default:
(...we know it is not a file...)
this seems cleaner to me than using a wrapper, and has the additional
advantage that the DT_UNKNOWN
code path could receive some more
testing.