Inside Mountvol.exe
以普通用户的身份以编程方式挂载卷
slxMountVol Mounting_Folder_Name Folder_To_Mount_Name The mounting folder MUST be empty The folder to mount can take following forms : C:\TEMP\DOSSIER\CIBLE or Volume{GUIG} or /D to suppress the mount EX: slxMountVol c:\mnt\temp c:\temp slxMountVol c:\mnt\cdrom Volume{346bfa41-38fb-11db-bc21-806d6172696f} slxMountVol c:\mnt\temp /D
引言
自 Windows 2000 以来,我们可以将目录或卷挂载到 NTFS 目录中。 存在一些命令行工具,例如“LINKD.EXE”或“MONTVOL.EXE”,它们允许挂载。
从编程的角度来看,在“文件管理功能”和“卷管理功能”中出现了 API,这些 API 允许管理链接、列出、挂载和卸载卷。 我在名为“SDK API for directories”和“SDK API for mount points”的段落中列出了声明。
在这一点上,我编写了一个挂载卷的程序。 该程序必须在 W2K 和 Vista 中作为普通用户运行。 我使用了 SDK 函数“SetVolumeMountPoint”。 它在 W2K 上有效,但在 XP 或 Vista 上无效。 起初,我没有注意到这个麻烦,只是认为这是一个权限问题。 当我深入研究,试图给予 SE_MANAGE_VOLUME_NAME 时,我意识到我无法作为普通用户挂载卷。
在 MSDN 中查找时,我发现了一篇文章,解释了挂载点和链接建立在“Reparse point”和“Directory junction”技术之上。“我对重新解析点的理解”段落给出了一些使用重新解析点进行挂载和链接的方法。
然后,我可以作为普通用户挂载卷或创建符号链接。 对于卷挂载点,它在技术上是有效的,我可以跨挂载点浏览卷。 但是当我运行 MOUNTVOL.EXE 时,我看不到我的挂载点。 最后一个名为“Registering MountMgr”的段落解释了如何将卷挂载点注册到 MountMGR。 这部分需要管理员权限,而且我仍然不知道为什么!
SDK API for mount points (开始于 W2K NT 5.0)
我在这里只提供一些列出挂载点的入口点。
HANDLE FindFirstVolumeMountPoint (LPTSTR lpszRootPathName, LPTSTR lpszVolumeMountPoint, DWORD cchBufferLength); BOOL FindNextVolumeMountPoint (HANDLE hFindVolumeMountPoint, LPTSTR lpszVolumeMountPoint, DWORD cchBufferLength); BOOL FindVolumeMountPointClose (HANDLE hFindVolumeMountPoint);
挂载卷。
BOOL SetVolumeMountPoint (LPCTSTR lpszVolumeMountPoint, LPCTSTR lpszVolumeName);
卸载卷。
BOOL DeleteVolumeMountPoint (LPCTSTR lpszVolumeMountPoint);
从卷挂载点获取卷的唯一名称。
BOOL GetVolumeNameForVolumeMountPoint (LPCTSTR lpszVolumeMountPoint, LPTSTR lpszVolumeName, DWORD cchBufferLength);
SDK API for directories
这是创建硬链接的入口点(开始于 W2K NT 5.0)。
BOOL CreateHardLink (LPCTSTR lpFileName, LPCTSTR lpExistingFileName, LPSECURITY_ATTRIBUTES lpSecurityAttributes);
这是创建符号链接的入口点(开始于 Vista NT 6.0)。
BOOL CreateSymbolicLink (LPCWSTR lpSymlinkFileName, LPCWSTR lpTargetFileName, DWORD dwFlags);
我对重新解析点的理解
文件或目录可以包含一个重新解析点,它是一组用户定义的数据。 例如,重新解析点用于实现 NTFS 文件系统链接和 Microsoft Remote Storage Server。
- 可以为目录建立重新解析点,但目录必须为空。
- 重新解析点和扩展属性是互斥的。
- 重新解析点数据,包括标签和可选的 GUID,不得超过 16 千字节。
在这一点上,我理解了创建重新解析点的方式是
- 打开一个空目录。
/* Open mounting folder */ . CreateFile (pszMountPointDir, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS , NULL); .
- 控制打开的目录。
/* Prepare Reparse data structure and send the device control to get the reparse point datas */ . dwSize = 0; if (!DeviceIoControl (hMountPointDir, FSCTL_GET_REPARSE_POINT, NULL, 0, prgdbReparseDataStruct, sizeof(ptrReparseDataStruct), &dwSize, NULL)) .
记录的数据结构是。
typedef struct _REPARSE_GUID_DATA_BUFFER { DWORD ReparseTag; WORD ReparseDataLength; WORD Reserved; GUID ReparseGuid; struct { BYTE DataBuffer[1]; } GenericReparseBuffer; } REPARSE_GUID_DATA_BUFFER;
控制代码是。
#define FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) // REPARSE_DATA_BUFFER, #define FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS) // REPARSE_DATA_BUFFER #define FSCTL_DELETE_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 43, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) // REPARSE_DATA_BUFFER,
这看起来很好,但是当我尝试在目录上放置 FSCTL_GET_REPARSE_POINT 时,我使用“MONTVOL.EXE”挂载 CDROM,给出了 REPARSE_GUID_DATA_BUFFER,它不起作用。 所以我理解了 REPARSE_GUID_DATA_BUFFER 是在“WINNT.H”文件中为用户重新解析点定义的。 这不是 Microsoft 挂载点的良好结构。 好的结构如下所示。 可以在 Visual Studio 6.0 附带的旧 WINNT.H 中找到(请参阅 FSCTL_* 定义末尾的注释)。
typedef struct _REPARSE_DATA_BUFFER { DWORD ReparseTag; WORD ReparseDataLength; WORD Reserved; union { struct _SymbolicLinkReparseBuffer { WORD SubstituteNameOffset; WORD SubstituteNameLength; WORD PrintNameOffset; WORD PrintNameLength; WCHAR PathBuffer[1]; } SymbolicLinkReparseBuffer; struct _MountPointReparseBuffer { WORD SubstituteNameOffset; WORD SubstituteNameLength; WORD PrintNameOffset; WORD PrintNameLength; WCHAR PathBuffer[1]; } MountPointReparseBuffer; struct { BYTE DataBuffer[1]; } GenericReparseBuffer; }; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
因此,再次尝试以下代码,我得到了卷名
/**********************************************************************/ /* Function SlxIsMountVol */ /**********************************************************************/ DWORD SlxIsMountVol (LPCTSTR pszMountPointDir, LPTSTR pszTarget) { DWORD dwRc=0, dwErrLen, dwSize; CHAR lpzErr[MAX_PATH]; HANDLE hPrvToken = NULL, hMountPointDir=NULL; CHAR pszTargetDir[MAX_PATH], *ptrVolume; PREPARSE_DATA_BUFFER prgdbReparseDataStruct; BYTE ptrReparseDataStruct[sizeof(REPARSE_DATA_BUFFER) + 2 * MAX_PATH * sizeof(WCHAR)]; /* Open the mount point directory */ hMountPointDir = CreateFile (pszMountPointDir, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hMountPointDir == INVALID_HANDLE_VALUE) { ... error ... return -1; } /* Prepare the junction point structure */ memset (pszTargetDir, 0, sizeof(pszTargetDir)); prgdbReparseDataStruct = (PREPARSE_DATA_BUFFER)ptrReparseDataStruct; memset(ptrReparseDataStruct, 0, sizeof(ptrReparseDataStruct)); prgdbReparseDataStruct->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; dwSize = 0; if (!DeviceIoControl (hMountPointDir, FSCTL_GET_REPARSE_POINT, NULL, 0, prgdbReparseDataStruct, sizeof(ptrReparseDataStruct), &dwSize, NULL)) { ... error ... return -2; } CloseHandle (hMountPointDir); dwRc = 1; /* Translate the volume name */ dwSize = WideCharToMultiByte (CP_ACP, 0, prgdbReparseDataStruct->MountPointReparseBuffer.PathBuffer, -1, pszTargetDir, sizeof(pszTargetDir), NULL, NULL); if (dwSize == 0) { ... error ... return -3; } /* Get the volume part */ ptrVolume = strstr (pszTargetDir, "Volume"); if (ptrVolume != NULL) { strcpy ((PCHAR)pszTarget, ptrVolume); *((PCHAR)pszTarget+strlen (pszTarget) - 1)= 0; } else return -4; return dwRc; }
调试此代码可以很容易地找到如何填充 REPARSE_DATA_BUFFER 以设置重新解析点,以便构建挂载点或符号链接。 最后一个细节围绕挂载目录和挂载卷的名称的语法。
EX
\??\c:\mnt\
\\?\Volume{afddc510-485b-11db-8167-806d6172696f}\
注册 MountMgr
创建一个重新解析点足以构建一个挂载点。 但是,使用 MOUNTVOL.EXE 时,此挂载点不会出现。 这似乎意味着挂载未在挂载管理器中注册。 我在 DDK 中找到了这些信息。 挂载管理器负责管理卷名。 对于每个卷,它存储一个唯一的名称,该名称与卷永久关联。 以下数据结构和 I/O 控制来自 DDK。
typedef struct _MOUNTMGR_VOLUME_MOUNT_POINT { USHORT SourceVolumeNameOffset; USHORT SourceVolumeNameLength; USHORT TargetVolumeNameOffset; USHORT TargetVolumeNameLength; } MOUNTMGR_VOLUME_MOUNT_POINT, *PMOUNTMGR_VOLUME_MOUNT_POINT, *PMVMP; #define MOUNTMGRCONTROLTYPE ((ULONG) 'm') #define IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED CTL_CODE(MOUNTMGRCONTROLTYPE,6, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED CTL_CODE(MOUNTMGRCONTROLTYPE, 7, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
以下代码允许注册已挂载的卷。 如果有人可以解释为什么必须是管理员才能这样做? 我确信这与驱动程序上的 ACL 有关,但我找不到在哪里。
#define MOUNTMGR_DEVICE_NAME "\\Device\\MountPointManager" #define MOUNTMGR_DOS_DEVICE_NAME \\\\.\\MountPointManager EX : pszMountPointDir = "c:\mnt\cdrom" pszTarget = Volume{afddc510-485b-11db-8167-806d6172696f} DWORD SlxRegisterMountVol (LPCTSTR pszMountPointDir, LPCTSTR pszTarget, BOOL blDelete) { DWORD dwRc=0, dwErrLen, dwSize, dwIOControl; CHAR lpzErr[MAX_PATH]; HANDLE hMountManager=NULL; CHAR pszTargetDir[MAX_PATH]; WCHAR wszTargetDir[MAX_PATH]; CHAR pszLocalMountPointDir[MAX_PATH]; WCHAR wszMountPointDir[MAX_PATH]; CHAR ptrVolumeBuffer[1024]; CHAR ptrVolumeMountPoint[sizeof(MOUNTMGR_VOLUME_MOUNT_POINT)+MAX_PATH+MAX_PATH]; PMOUNTMGR_VOLUME_MOUNT_POINT strVolumeMountPoint=(PMVMP)ptrVolumeMountPoint; /* Initialising sructure for registering mountpoint */ memset (pszTargetDir, 0, sizeof(pszTargetDir)); if (pszTarget != NULL) { strcpy (pszTargetDir, "$$??$$"); // replace $ by \ strcat (pszTargetDir, pszTarget); } memset (pszLocalMountPointDir, 0, sizeof(pszTargetDir)); strcpy (pszLocalMountPointDir, "$$DosDevices$$"); // replace $ by \ strcat (pszLocalMountPointDir, pszMountPointDir); /* Unicode conversion */ memset (wszTargetDir, 0, sizeof(wszTargetDir)); memset (wszMountPointDir, 0, sizeof(wszMountPointDir)); dwSize = MultiByteToWideChar (CP_ACP, 0, pszTargetDir, -1, wszTargetDir, sizeof(wszTargetDir)); if (dwSize == 0) { ... error ... return -1; } dwSize = MultiByteToWideChar (CP_ACP, 0, pszLocalMountPointDir, -1, wszMountPointDir, sizeof(wszMountPointDir)); if (dwSize == 0) { ... error ... return -2; } /* Building structure */ memset (ptrVolumeMountPoint, 0, sizeof(ptrVolumeMountPoint)); strVolumeMountPoint->SourceVolumeNameLength = wcslen (wszMountPointDir)*sizeof(WCHAR); strVolumeMountPoint->TargetVolumeNameLength = wcslen (wszTargetDir)*sizeof(WCHAR); strVolumeMountPoint->SourceVolumeNameOffset = sizeof(MOUNTMGR_VOLUME_MOUNT_POINT); memcpy (ptrVolumeMountPoint + strVolumeMountPoint->SourceVolumeNameOffset, wszMountPointDir, strVolumeMountPoint->SourceVolumeNameLength); strVolumeMountPoint->TargetVolumeNameOffset = sizeof(MOUNTMGR_VOLUME_MOUNT_POINT)+ strVolumeMountPoint->SourceVolumeNameLength; memcpy (ptrVolumeMountPoint + strVolumeMountPoint->TargetVolumeNameOffset, wszTargetDir, strVolumeMountPoint->TargetVolumeNameLength); /* Opening an Handle on the MountMgr */ hMountManager = CreateFile ((LPCSTR)MOUNTMGR_DOS_DEVICE_NAME, //MOUNTMGR_DEVICE_NAME, GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, /*FILE_FLAG_BACKUP_SEMANTICS*/0, NULL); if (hMountManager == INVALID_HANDLE_VALUE) { ... error ... return -3; } /* Registering information against the MountMgr */ dwSize = 0; dwIOControl = (blDelete)? IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED:IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED; if (!DeviceIoControl (hMountManager, dwIOControl, ptrVolumeMountPoint, sizeof(MOUNTMGR_VOLUME_MOUNT_POINT)+ strVolumeMountPoint->SourceVolumeNameLength+ strVolumeMountPoint->TargetVolumeNameLength, ptrVolumeBuffer, sizeof(ptrVolumeBuffer), &dwSize, NULL)) { ... error ... return -4; } CloseHandle (hMountManager); return dwRc; }
Using the Code
slxMountVol.exe 是用 Visual Studio 2005 编写的。 您可以在其中找到法语中的所有注释。
关注点
没有管理权限的挂载操作。
了解 MONTVOL.EXE 的工作原理。
历史
如果我得到更多信息,我会感觉一下