このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
では、実際にリパースデータバッファを取得し、
マウントポイントのリンク先を取得してみよう。
========== 1st half of mountpoint.c ==========
#define UNICODE
#define STRICT
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <winioctl.h>
#include <string.h>
typedef struct _MOUNT_POINT_INFO {
unsigned long tag;
unsigned short dataLength;
unsigned short reserved;
unsigned short targetOffset;
unsigned short targetByteLength;
unsigned short descriptionOffset;
unsigned short descriptionByteLength;
unsigned char buffer[1]; /* BYTE */
} MOUNT_POINT_INFO;
========== end of 1st half of mountpoint.c ==========
さて、デバイスの制御をするためには、
<winioctl.h> をインクルードする必要がある。
また、Windows 2000 以降の NT 固有の機能を使うため、
_WIN32_WINNT を定義しておく。
肝心のリパースポイントバッファだが、
これは REPARSE_DATA_BUFFER という構造体である。
これは特定の版の <winnt.h> には定義されていたのだが、
最新のヘッダファイルには見つからず、
代わりに <ntifs.h> の方に移されている。
しかしこの <ntifs.h>、通常の SDK に含まれないため、
ここではマウントポイント専用の構造体を
MOUNT_POINT_INFO として捏造することにする。
この MOUNT_POINT_INFO 構造体だが、実は正確ではない。
というのも、リパースデータバッファは可変長であるため、
C 言語の構造体では正確に表現できないのだ。
なので、実際にデータを取得するためには、
別に十分な容量を確保したバッファを用意し、
そこに読み込む必要がある。
========== 2nd half of mountpoint.c ==========
HRESULT MountPointGetTarget(const wchar_t *mountPointPath,
wchar_t *target, unsigned int bufferSize) {
HANDLE handle; /* ディレクトリハンドル */
const wchar_t *targetPath;
DWORD targetPathLength;
DWORD dataSize;
char dataBuffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
MOUNT_POINT_INFO *info = (MOUNT_POINT_INFO *)dataBuffer;
ZeroMemory(dataBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
/* ディレクトリを開く */
handle = CreateFile(mountPointPath, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT
| FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (handle == INVALID_HANDLE_VALUE) return 0;
/* ドライバに制御コード FSCTL_GET_REPARSE_POINT を送る */
if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT,
NULL, 0, dataBuffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
&dataSize, NULL)) {
CloseHandle(handle);
return HRESULT_FROM_WIN32(GetLastError());
}
/* ディレクトリを閉じる */
CloseHandle(handle);
/* タグが別の種類 */
if (info->tag != IO_REPARSE_TAG_MOUNT_POINT)
return HRESULT_FROM_WIN32(ERROR_REPARSE_TAG_MISMATCH);
/* リンク先文字列 (NUL 終端) */
targetPath = (const wchar_t *)(info->buffer + info->targetOffset);
/* リンク先文字数 */
targetPathLength = info->targetByteLength / sizeof (wchar_t);
/* バッファ不足か? */
if (targetPathLength >= bufferSize) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
/* バッファにコピー */
wcscpy(target, targetPath);
return S_OK;
}
========== end of 2nd half of mountpoint.c ==========
最初にすることは、ディレクトリを開くことだ。
ディレクトリはファイルと同じように、
CreateFile で開くことができる。
ポイントは、FILE_FLAG_OPEN_REPARSE_POINT と、
FILE_FLAG_BACKUP_SEMANTICS を指定することだ。
前者は、リパースポイントを追跡しないことを示す。
これを指定しないと、ディレクトリ自身ではなく、
ディレクトリが指すボリュームを開いてしまう。
後者は、ディレクトリのハンドルを取得するために必要だ。
続いて、DeviceIoControl を呼び出し、
リパースデータバッファを取得する。
制御コードは FSCTL_GET_REPARSE_POINT である。
リパースデータバッファは可変長なので、
とり得る最大容量のバッファ dataBuffer を確保しておき、
DeviceIoControl でそこに読みこむ。
そして MOUNT_POINT_INFO にキャストして参照する。
最後に、タグの種類を調べ、
IO_REPARSE_TAG_MOUNT_POINT であれば、
リンク先文字列が格納されているポインタを取得し、
バッファにコピーして完了となる。
試しに、「D:\mnt\cdrom」に対して呼び出してみると、
「\??\Volume{f08e7d20-64f1-11db-b10d-001125868411}\」
という結果が得られた。
実は、ボリュームマウントポイントのリンク先は、
GetVolumeNameForVolumeMountPoint でも得られるが、
こちらの API には制約があるため、
今回は敢えて直接取得をためしてみた。