65.9K
CodeProject 正在变化。 阅读更多。
Home

Inside Mountvol.exe

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (8投票s)

2008 年 6 月 29 日

CPOL

4分钟阅读

viewsIcon

45380

downloadIcon

1092

以普通用户的身份以编程方式挂载卷

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 的工作原理。

历史

如果我得到更多信息,我会感觉一下

© . All rights reserved.