Not using adirent

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.

Posted