[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: how to check for dirent.d_type support from shell, python or perl?
From: |
Jim Meyering |
Subject: |
Re: how to check for dirent.d_type support from shell, python or perl? |
Date: |
Fri, 13 May 2011 14:06:26 +0200 |
Pádraig Brady wrote:
> On 12/05/11 17:41, Jim Meyering wrote:
>> I'd like to skip my new strace-vs-ls --color test on any
>> system (OS and/or file system) that lacks dirent.d_type support.
>> Ideally, it'd be a tiny bourne shell, python or perl script
>> to tell me if calling readdir on a given directory (let's say ".")
>> would provide struct dirent data including usable d_type values.
>> [For reference, some file system types do not support that, e.g.,
>> reiser3 and xfs, while others do, like ext4 and tmpfs. ]
>>
>> Unfortunately, so far it appears there is no way to get to
>> the d_type member from any of those languages.
>>
>> Does anyone know a way that does not require compiling
>> and running a C program?
>>
>> I though strace -v might help, but e.g., strace -v ls empty-dir shows
>> only the getdents syscalls. No details about readdir-returned data.
>
> Something based on this might work?
>
>
> #!/usr/bin/python
>
> # Return status indicates if d_type returned
>
> import ctypes
> import sys
>
> (DT_UNKNOWN, DT_DIR,) = (0, 4,)
>
> class dirent(ctypes.Structure):
> _fields_ = [
> ("d_ino", ctypes.c_long),
> ("d_off", ctypes.c_long),
> ("d_reclen", ctypes.c_ushort),
> ("d_type", ctypes.c_ubyte),
> ("d_name", ctypes.c_char*256)]
>
> direntp = ctypes.POINTER(dirent)
>
> libc = ctypes.cdll.LoadLibrary("libc.so.6")
> libc.readdir.restype = direntp
>
> dirp = libc.opendir(".")
> if dirp:
> ep = libc.readdir(dirp)
> else:
> sys.exit(1)
>
> sys.exit(ep.contents.d_type != DT_DIR)
Very nice. Thank you!
That appears to work fine on Linux/glibc,
but fails on a solaris 10 system which lacks ctypes:
Traceback (most recent call last):
File "./d_type-check", line 5, in ?
import ctypes
ImportError: No module named ctypes
and fails on solaris 5.11 and freebsd 8.x due to the absence of libc.so.6
(one of those has libc.so and libc.so.1):
Traceback (most recent call last):
File "./d_type-check", line 20, in <module>
libc = ctypes.cdll.LoadLibrary("libc.so.6")
File "/usr/lib/python2.6/ctypes/__init__.py", line 431, in LoadLibrary
return self._dlltype(name)
File "/usr/lib/python2.6/ctypes/__init__.py", line 353, in __init__
self._handle = _dlopen(self._name, mode)
And when I change the name, it does appear to work, in that
it always exits nonzero indicating that there is no d_type information.
However, I'm a little nervous about testing the nominal d_type member
on systems like that where it doesn't exist. In that case, we're
comparing with the first byte of the d_name file name, which happens
to be "." in my test, but could conceivably be \004 (evoking a false-
positive successful exit) in an arbitrary directory on a system where
"." and ".." are not returned first.
Hmm... actually, I don't mind at all if it fails on non-Linux/GNU.
The point for me was to determine reliably (on systems I test regularly)
whether d_type is available for a given directory.
The test
However, there *is* one real problem. What if we're using a
d_type-enabled file system with no "." or ".." entry, or for
which readdir does not return one of those entries first?
Then the first entry may well be a non-directory, which would
cause a false-negative.
To avoid that, I think we'd have to know the name and type
of at least one entry in the directory we're reading, and then
call readdir until it hits the matching name.
Otherwise, just requiring that "." be the first entry
should be enough for nearly all uses.
sys.exit(ep.contents.d_name != "." or ep.contents.d_type != DT_DIR)