# What kind of check does DirectoryQ exactly perform?

I'm using Mathematica 9.0.1 on Windows XP and my drive letters are assigned as follows: C: and D: are hard local disks, while E: is assigned to some removable media. Now, the situation is as follows

DirectoryQ[# <> ":\\"] & /@ {"c", "d", "e"}

returns {True,True,True}, even when an optical disk isn't in the drive.

To outflank the problem I use a directoryQ function, defined in the code below.

However, it obviously fails against a full hard disk or when I don't have write access. Therefore, I' m wondering about the kind of check DirectoryQ basically performs. Is there a better solution to fix the problem?

directoryQ[unit_]:=Module[
{root,file,ret},
ret=False;
root=unit<>":\\";
file = FileNameJoin[{root, "testFile.txt"}];
If[
DirectoryQ[root],
Quiet[Export[file,"test","Text"]];
If[
FileExistsQ[file],
DeleteFile[file];
ret=True
];
];
Return[ret];
];

directoryQ["c"]
directoryQ["e"]
-
I don't have windows, but I like this question. I did some slight edits on your text to make it more conform with the site. What I can tell you from Linux, Unix and Mac is that disks or USB drives are always mounted to an existing directory (usually, some subfolder is created). Even my second (built-in) hard drive has a fixed mountpoint which is /home, a directory. It doesn't matter whether the hard disk is there or not. –  halirutan Apr 10 '14 at 21:23
Is the problem just that DirectoryQ returns the right answer except in the one case that its argument is a root directory of a drive that isn't mounted? In that case, you could try verifying the result by (1) checking if the path is a root directory (maybe using FileNameSplit?), then (2) calling SetDirectory or FileExistsQ to see if you can actually go there. –  librik Apr 11 '14 at 5:46

Since Mathematica is written in the C programming language, it is likely that any file-system status operation is doing nothing more than calling C's built-in functions. In this case, that would be stat.

So let's test what stat does on Windows. According to the C language documentation, stat is given the name of something in the file system, and returns a C structure whose fields describe the properties of that thing. In particular, the st_mode field seems relevant:

st_mode: Bit mask for file-mode information. The _S_IFDIR bit is set if path specifies a directory; the _S_IFREG bit is set if path specifies an ordinary file or a device. User read/write bits are set according to the file's permission mode; user execute bits are set according to the filename extension.

I wrote a simple test program in Microsoft Visual C that tests the stat function. Drive C: is my Windows system drive; drive E: is my DVD drive, which currently doesn't have anything in it. Here's what I found:

stat("C:\\WINDOWS") returns success and sets _S_IFDIR
stat("C:\\WINDOWS\\SYSTEM32\\NOTEPAD.EXE") returns success and does not set _S_IFDIR
stat("C:\\") returns success and sets _S_IFDIR
stat("E:\\") returns success and sets _S_IFDIR
stat("W:\\") returns failure

So we can conclude from this that Windows, or at least the stat function on Windows, considers empty DVD drives to be "there." I also have a flash memory stick that I can plug into a USB slot, which I have mapped to drive W, but that "isn't there" when it's unplugged.

This is consistent with what Windows Explorer shows me when I click on "My Computer". I see drive C: with a name, and I see drive E:, even though I get an error message when I double-click on it. I don't see drive W: (my flash memory stick) after I pulled it out of the socket. So Windows does consider an empty DVD drive to be present, which is probably why stat says it's a valid directory.

I have no idea how to find out whether a DVD drive is full or empty in C, much less in Mathematica. But at least we can see the core of the problem.

-
I really like your detailed answers and extensive knowledge of low-level I/O. +1 –  halirutan Apr 11 '14 at 0:30
I fully agree with halirutan. I only wish you posted more often. :-) –  Mr.Wizard Apr 11 '14 at 1:03
Using strace, I can confirm that on Linux at least, DirectoryQ does simply stat the input. –  0xFE Apr 11 '14 at 4:04

This response shows how to obtain more detailed information about directories (and files) using JLink, NETLink and the Win32 API. Such information can be used to determine if a device is offline, non-writable, etc. The various methods differ in how much information they return.

The Java 6 File class can be used to retrieve many file system attributes on any operating system supported by Mathematica:

Needs["JLink"]
InstallJava[];

javaFileAttributes[path_String] :=
JavaBlock @ Module[{p, file = JavaNew["java.io.File", path]}
, Cases[{canExecute, canRead, canWrite, exists, isDirectory, isFile, isHidden}
, p_ /; file@p[]
]
]

Example use (d:\ is an offline drive):

Table[{p, javaFileAttributes@p}
, {p, {"c:\\", "d:\\", "c:\\temp", "c:\\pagefile.sys", "c:\\bootsect.bak"}}
] // Grid

(*
d:\             {}
c:\pagefile.sys {exists,isFile}
*)

On Windows, or a system running Mono, we can perform a similar operation using the .NET method File.GetFileAttributes:

InstallNET[];

dotNetException[] := dotNetException[GetNETException[]]
dotNetException[e_] := Module[{i = e@InnerException[]}, dotNetException[i] /; i =!= Null]
dotNetException[e_] := $Failed@#&[e@Message // StringTrim] dotNetFileAttributes[path_String] := NETBlock @ Module[{p, file = Quiet@SystemIOFileGetAttributes[path]} , file /. { f_?NETObjectQ :> StringSplit[f@ToString[], (Whitespace|",")..] ,$Failed :> dotNetException[]
}
]

Example use (d:\ is an offline drive):

Table[{p, dotNetFileAttributes@p}
, {p, {"c:\\", "d:\\", "c:\\temp", "c:\\pagefile.sys", "c:\\bootsect.bak"}}
] // Grid

(*
c:\             {Hidden,System,Directory}
d:\             $Failed[The device is not ready.] c:\temp {Directory} c:\pagefile.sys {Hidden,System,Archive} c:\bootsect.bak {ReadOnly,Hidden,System,Archive} *) Win32 API through NETLink On Windows, we can access the Windows API function GetFileAttributes through NETLink: Needs["NETLink"] InstallNET[] kernel32GetLastError = DefineDLLFunction["GetLastError", "kernel32.dll", "DWORD", {}]; kernel32GetFileAttributes = DefineDLLFunction["GetFileAttributes", "kernel32.dll", "DWORD", {"string"}]; windowsError[] := kernel32GetLastError[] /. { 2 -> "FILE_NOT_FOUND", 3 -> "PATH_NOT_FOUND", 5 -> "ACCESS_DENIED" , 15 -> "INVALID_DRIVE", 21 -> "NOT_READY", 32 -> "SHARING_VIOLATION" , n_ :> "Error code: "~~ToString[n] }$fileAttributes =
{ {"ARCHIVE", 32}, {"COMPRESSED", 2048}, {"DEVICE", 64}, {"DIRECTORY", 16}
, {"ENCRYPTED", 16384}, {"HIDDEN", 2}, {"INTEGRITY_STREAM", 32768}
, {"NORMAL", 128}, {"NOT_CONTENT_INDEXED", 8192}, {"NO_SCRUB_DATA", 131072}
, {"OFFLINE", 4096}, {"READONLY", 1}, {"REPARSE_POINT", 1024}, {"SPARSE_FILE", 512}
, {"SYSTEM", 4}, {"TEMPORARY", 256}, {"VIRTUAL", 65536}
};

windowsFileAttributes[path_String] :=
kernel32GetFileAttributes[path] /.
{ -1 :> { $Failed@#&@windowsError[] } , a_ :> Cases[$fileAttributes, {n_, v_} /; BitAnd[v, a] != 0 :> n]
}

Example use (d:\ is an offline drive):

Table[{p, windowsFileAttributes@p}
, {p, {"c:\\", "d:\\", "c:\\temp", "c:\\pagefile.sys", "c:\\bootsect.bak"}}
] // Grid

(*
c:\             {DIRECTORY,HIDDEN,SYSTEM}
d:\             {$Failed[NOT_READY]} c:\temp {DIRECTORY} c:\pagefile.sys {$Failed[SHARING_VIOLATION]}
*)
-

I'm extremely grateful to halirutan for repairing my message ! I will try to bother as less I can.

Following librik's guidance I wrote two functions:

directoryQversionA[unit_]:=Module[
{root,file,ret},
ret=True;
root=unit<>":\\";
Quiet[Check[SetDirectory[root],ret=False]];
Return[ret];
];

directoryQversionB[unit_]:=Module[
{root,file,ret},ret=False;
root=unit<>":\\";
ret=FileExistsQ[root];
Return[ret];
];

This works as expected

directoryQversionA/@{"c","z"}
directoryQversionB/@{"c","z"}

Regarding the removable media, using

directoryQversionA["e"]
directoryQversionB["e"]

I get True when a support is inside and False when it isn't.

As far as I can see,the problem who drove me to ask the question is solved on my operating system: I will check on more recent versions.

-