凌云的博客

行胜于言

NTFS Reparse point 是什么?

分类:ntfs| 发布时间:2024-10-23 19:04:00

概述

NTFS reparse point ,微软官方也译作重分析点、重新解析点、重新分析点,是NTFS文件系统中的一种对象类型。 它在Windows 2000及之后版本中的NTFS v3.0及以上版本中可用。重解析点提供一种扩展NTFS文件系统的方法。 一个 NTFS reparse point 包含一个重解析标签和数据,文件系统过滤器(file system filter)可以按标签解读它。 微软提供了几个默认标签,包括 NTFS symlink、NTFS directory junction points, 和 Volume mount points。

NTFS reparse point 是一个非常强大的机制,但必须注意每个文件只能有一个 reparse point 对象。

常见 NTFS reparse point

Volume mount points

Volume mount points 和 Unix mount points 类似, 就是将另一个文件系统的根附加到目录。 在 NTFS 中,这允许额外的文件系统不d单独占用驱动器号(如 C:、D:)。

如果一个卷被挂在到另一个卷的现有目录上,该目录以前列出的内容将被隐藏,已挂载卷的根目录将取代它。 挂载的卷仍然可以有自己单独分配的驱动器号。

挂载的卷可以使用非 NTFS 文件系统,并且可以使用自己的安全设置和访问权限(比如 Samba 挂载的文件系统)。

Volume mount points 中的 substitute names 可以使用类似 \??\DeviceName\ 这种格式。 而 Junctions 通常使用 \??\<drive>:\ 这种格式,对于真正的卷挂载点通常使用 \?\Volume{<guid>} 来执行具体的卷。 对于 Junctions 来说 UNC 路径是非法的。

Directory junctions

Directory junctions 和 Volume mount points 都是使用相同的机制来实现的(标签都是:IO_REPARSE_TAG_MOUNT_POINT)。 唯一的区别是 Directory junctions 的 substitute names 通常是指向已有磁盘分区的子目录。 Junctions 类似 Unix 中指向目录的软链接,区别是 Junctions 只能指向目录。

Directory junctions 可以通过 mklink /J junctionName targetDirectory 创建,可以通过 rmdir 删除。

为了兼容之前版本的 Windows,Windows Vista 及之后版本默认创建了一些 directory junctions,比如系统盘根目录下的“Documents and Settings”。

Directory junctions 类似软链接,但是对于后面实现的 NTFS symbolic links 来说更高效,对于远程共享目录来说 Directory junctions 由远程服务器进行解析。

Symbolic links

Windows Vista 及之后的版本开始支持 Symbolic links(也就是软链接)。 对于远程共享目录来说 Symbolic links 由客户端进行解析。

Symbolic links 分为文件软链接和目录软链接(这和 Unix 中的软链接不一样)。 文件软链接可以使用 MKLINK symLink targetFilename 创建。 目录软链接可以使用 MKLINK /D symLinkD targetDirectory 创建。 当通过软链接访问目标时,NTFS 会同时检查文件类型是否正确(文件或目录),如果类型不对会返回文件不存在错误。

Symbolic links 支持使用相对路径或者绝对路径。(Directory junctions 只支持绝对路径)

Symbolic links 支持 UNC 路径,但是不支持 Volume{guid} 的方式。

Unix domain socket (socket)

Windows 10 build 17063 (稳定版本为 version 1803),微软介绍了 Unix domain sockets(主要为了实现 WSL,类似的还有 FIFO、CHR、BLK)。 主要由 afunix.sys 内核模块来识别这类 reparse point。

OneDrive

Windows 定义了一系列的 tags 用来管理云盘文件的状态(例如:OneDrive)。

结构定义

REPARSE_DATA_BUFFER

对于微软自己定义的 reparse point 结构定义如下

typedef struct _REPARSE_DATA_BUFFER {
  ULONG  ReparseTag;
  USHORT ReparseDataLength;
  USHORT Reserved;
  union {
    struct {
      USHORT SubstituteNameOffset;
      USHORT SubstituteNameLength;
      USHORT PrintNameOffset;
      USHORT PrintNameLength;
      ULONG  Flags;
      WCHAR  PathBuffer[1];
    } SymbolicLinkReparseBuffer;
    struct {
      USHORT SubstituteNameOffset;
      USHORT SubstituteNameLength;
      USHORT PrintNameOffset;
      USHORT PrintNameLength;
      WCHAR  PathBuffer[1];
    } MountPointReparseBuffer;
    struct {
      UCHAR DataBuffer[1];
    } GenericReparseBuffer;
  } DUMMYUNIONNAME;
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;

具体结构定义可以参考:REPARSE_DATA_BUFFER structure

REPARSE_GUID_DATA_BUFFER

REPARSE_GUID_DATA_BUFFER 的格式如下:

typedef struct _REPARSE_GUID_DATA_BUFFER {
  ULONG  ReparseTag;
  USHORT ReparseDataLength;
  USHORT Reserved;
  GUID   ReparseGuid;
  struct {
    UCHAR DataBuffer[1];
  } GenericReparseBuffer;
} REPARSE_GUID_DATA_BUFFER, *PREPARSE_GUID_DATA_BUFFER;

第三方的文件系统,filters,minifilters 可以使用 REPARSE_GUID_DATA_BUFFER 来实现一些特殊的功能。

Reparse point tags 是由微软分配给第三方厂商的。你可以为你的文件系统,filter驱动,minifilter驱动申请一个或者多个 reparse point。

Reparse point GUIDs 不是微软分配的。 你必须自己选择一个 GUID,可以使用 Windows SDK 提供的工具 GUIDGen 来生成。

NTFS reparse point 是一个非常强大的机制,但必须注意每个文件只能有一个 reparse point 对象,微软的某些机制使用了 reparse points 来实现(HSM, Native Structured Storage)

对于使用 reparse point 来实现某些特殊功能开发者来说需要特别注意这点,对已经使用了 reparse point 的文件需要有备用策略。

如何操作 reparse point

命令行方式

可以通过 fsutil reparsePoint 来获取或删除 reparse point:

$ fsutil reparsePoint
---- 支持的 REPARSEPOINT 命令 ----

delete          删除重分析点
query           查询重分析点

API方式

应用程序

应用程序可以使用 DeviceIoControl 进行 reparse point 的获取,设置,删除操作

BOOL DeviceIoControl(
  [in]                HANDLE       hDevice,
  [in]                DWORD        dwIoControlCode,
  [in, optional]      LPVOID       lpInBuffer,
  [in]                DWORD        nInBufferSize,
  [out, optional]     LPVOID       lpOutBuffer,
  [in]                DWORD        nOutBufferSize,
  [out, optional]     LPDWORD      lpBytesReturned,
  [in, out, optional] LPOVERLAPPED lpOverlapped
);

对于备份恢复软件来说, BackupRead 可以获取 reparse point,而 BackupWrite 可以设置 reparse point。

驱动

驱动程序可以使用 FltFsControlFile(用户态)和 ZwFsControlFile(内核态)来进行 reparse point 的获取,设置,删除操作

NTSTATUS FLTAPI FltFsControlFile(
  [in]            PFLT_INSTANCE Instance,
  [in]            PFILE_OBJECT  FileObject,
  [in]            ULONG         FsControlCode,
  [in, optional]  PVOID         InputBuffer,
  [in]            ULONG         InputBufferLength,
  [out, optional] PVOID         OutputBuffer,
  [in]            ULONG         OutputBufferLength,
  [out, optional] PULONG        LengthReturned
);

NTSYSAPI NTSTATUS ZwFsControlFile(
  [in]            HANDLE           FileHandle,
  [in, optional]  HANDLE           Event,
  [in, optional]  PIO_APC_ROUTINE  ApcRoutine,
  [in, optional]  PVOID            ApcContext,
  [out]           PIO_STATUS_BLOCK IoStatusBlock,
  [in]            ULONG            FsControlCode,
  [in, optional]  PVOID            InputBuffer,
  [in]            ULONG            InputBufferLength,
  [out, optional] PVOID            OutputBuffer,
  [in]            ULONG            OutputBufferLength
);

参考