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.
JLink
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
(*
c:\ {canExecute,canRead,canWrite,exists,isDirectory,isHidden}
d:\ {}
c:\temp {canExecute,canRead,canWrite,exists,isDirectory}
c:\pagefile.sys {exists,isFile}
c:\bootsect.bak {canExecute,canRead,exists,isFile,isHidden}
*)
NETLink
On Windows, or a system running Mono, we can perform a similar operation using the .NET method File.GetFileAttributes:
Needs["NETLink`"]
InstallNET[];
LoadNETType["System.IO.File", AllowShortContext -> False];
LoadNETType["System.IO.FileAttributes", AllowShortContext -> False];
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@System`IO`File`GetAttributes[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]}
c:\bootsect.bak {ARCHIVE,HIDDEN,READONLY,SYSTEM}
*)
/home
, a directory. It doesn't matter whether the hard disk is there or not. $\endgroup$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 usingFileNameSplit
?), then (2) callingSetDirectory
orFileExistsQ
to see if you can actually go there. $\endgroup$