如何显式锁定已挂载的文件系统?

Posted

技术标签:

【中文标题】如何显式锁定已挂载的文件系统?【英文标题】:How to explicitly lock a mounted file system? 【发布时间】:2015-05-24 02:25:40 【问题描述】:

如何在 Windows 7 中写入物理驱动器?

我正在尝试在 Windows 7 中写入物理磁盘(例如 \\.\PhysicalDrive0)。

这个问题已经被问死了,但从未得到回答。它曾经在 Windows XP 中工作,但微软故意破坏了 Windows Vista。微软提供了有关如何做到这一点的提示,但甚至没有人能够弄清楚。

它曾经工作过

在过去,允许写入物理磁盘(只要您是管理员)。执行此操作的方法甚至记录在知识库文章中:

INFO: Direct Drive Access Under Win32

要在基于 Win32 的应用程序中打开物理硬盘驱动器以直接访问磁盘(原始 I/O),请使用以下形式的设备名称

\\.\PhysicalDriveN

其中 N 是 0、1、2 等,代表系统中的每个物理驱动器。

您可以使用带有这些设备名称的 CreateFile() 应用程序编程接口 (API) 打开物理或逻辑驱动器,前提是您对驱动器具有适当的访问权限(即,您必须是管理员)。您必须同时使用 CreateFile() FILE_SHARE_READ 和 FILE_SHARE_WRITE 标志才能访问驱动器。

所有这些都在 Windows Vista 中发生了变化,因为附加了安全限制。

如何写入物理磁盘?

许多人,许多答案,对许多***问题感到困惑:

写入物理磁盘(例如\\.\PhysicalDrive0),以及 写入逻辑卷(例如\\.\C$

微软注意到restrictions placed on both kinds of operations:

阻止对卷和磁盘的直接写入操作

在 DASD(直接访问存储设备)卷句柄上的写操作将成功如果:

文件系统没有挂载,或者如果 写入的扇区是引导扇区。 要写入的扇区位于文件系统空间之外。 文件系统已通过请求独占写入权限被隐式锁定。 已通过发送锁定/卸载请求明确锁定文件系统。 内核模式驱动程序已标记写入请求,指示应绕过此检查。该标志称为 SL_FORCE_DIRECT_WRITE,它位于 IrpSp->flags 字段中。文件系统和存储驱动程序都会检查此标志。

在我的情况下,我问的是写一个Physical,而不是一个Logical。 Microsoft 注意到写入物理磁盘句柄的新限制:

在以下情况下,磁盘句柄上的写操作将成功:

写入的扇区不属于文件系统。 写入的扇区属于已挂载的文件系统,该文件系统已被明确锁定。 正在写入的扇区属于未安装的文件系统或卷没有文件系统。
我正在写入的扇区确实属于文件系统 --> 失败 我正在写入的扇区确实属于已安装、未锁定的文件系统 --> 失败 我正在写入的扇区确实位于已挂载的文件系统中,并且位于具有文件系统的逻辑卷中。

关于如何使它工作的提示围绕着:

卸载文件系统 锁定文件系统

但问题是如何卸载文件系统?如何锁定文件系统?

你现在在做什么?

我能够读取磁盘的所有物理扇区;那没问题。问题是当我想写入到磁盘的物理扇区时。

我的当前代码是伪代码:

void ZeroSector(Int64 PhysicalSectorNumber)

    String diskName := '\\.\PhysicalDrive0'; 

    DWORD desiredAccess := GENERIC_READ or GENERIC_WRITE;

    //INFO: Direct Drive Access Under Win32
    //https://support.microsoft.com/en-us/kb/100027
    //says you nedd both
    DWORD shareMode := FILE_SHARE_READ or FILE_SHARE_WRITE;

    //Open the physical disk
    hDisk := CreateFile(diskName, desiredAccess, shareMode,
            nil, OPEN_EXISTING, 0, 0);
    if hDisk = INVALID_HANDLE_VALUE
        RaiseLastWin32Error();
    try
    
        Int32 bytesPerPhysicalSector := 4096; //Determined elsewhere using IOCTL_STORAGE_QUERY_PROPERTY+STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR

        //Setup buffer for what we will be writing
        Byte[] buffer = new Byte[bytesPerPhysicalSector];

        //Calculate the byte offset of where the sector is
        Int64 byteOffset = PhysicalSectorNumber * bytesPerPhysicalSector;

        //Seek to that byte offset
        SetFilePointer(hDisk, byteOffset.Lo, byteOffset.Hi, FILE_BEGIN); 

        //Write the buffer
        DWORD numberOfBytesWritten;
        if (!WriteFile(hDisk, buffer, bytesPerPhysicalSector, out numberOfBytesWritten, nil))
            RaiseLastWin32Error();
    
    finally
    
        CloseHandle(hDisk);
    

令人惊讶的是:

可以打开物理磁盘以供GENERIC_READ + GENERIC_WRITE 访问

直到实际的WriteFile 失败,它才会失败:

ERROR_ACCESS_DENIED

如何按照微软所说的去做

微软说我的写作会失败,他们是对的。他们说我需要明确锁定文件系统:

在以下情况下,磁盘句柄上的写操作将成功:

写入的扇区属于已挂载的文件系统,该文件系统已被明确锁定。

除非我不知道该怎么做。

我知道我可能必须使用DeviceIoControlIOCTLS 之一来“锁定”一个卷。但这带来了三个挑战:

找出所选物理磁盘上的卷 找出要使用的 IOCTL 了解如何解锁锁定的卷

忽略这些问题,我盲目地尝试了LockFile API。就在致电WriteFile之前:

//Try to lock the physical sector we will be writing
if (!LockFile(DiskHandle, byteOffset.Lo, byteOffset.Hi, bytesPerPhysicalSector, 0)
   RaiseLastWin32Error();

失败了:

ERROR_INVALID_FUNCTION (1)

【问题讨论】:

【参考方案1】:

查看 FSCTL_LOCK_VOLUME、FSCTL_DISMOUNT_VOLUME 控制代码。我相信您必须枚举磁盘上的所有卷,然后卸载并锁定它们。锁定成功后,磁盘就是你的了。

您可能无法在系统驱动器上执行此操作。我还猜想包含页面文件的卷会有一些警告。

完成此操作后,我可以写入相应的 \\.\PhysicalDrive3。我以前不能:

HANDLE hVol = CreateFileA(
    "\\\\.\\X:", 
    FILE_READ_DATA | FILE_WRITE_DATA,
    FILE_SHARE_READ | FILE_SHARE_WRITE,
    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD unused;
BOOL b = DeviceIoControl(hVol, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &unused, NULL);
if (!b) 
    printf("%u", GetLastError());
    abort();


...
HANDLE h = CreateFileA(
    argv[1], // that's my \\physicaldrive3
    FILE_READ_DATA | FILE_WRITE_DATA, 
    FILE_SHARE_READ | FILE_SHARE_WRITE, 
    NULL,
    OPEN_EXISTING,
    0,
    NULL);
...
    bResult = WriteFile(h, buf, cb, &dwWritten, &foo);
    if (!bResult) 
        // used to fail without messing with vol handle
        printf("Failed writing data. Error = %d.\n", GetLastError()); 
        return 0;
    

【讨论】:

稍有不同的是,您正在锁定一个 volume(即\\.\x:),然后写入同一卷。在我的情况下,我想锁定/卸载卷,以便我可以写入 disk(即\\.\PhysicalDrive3)。想象一下\\.\PhysicalDrive3 有多个卷(例如D:F:)的情况。 我锁定卷(句柄 hVol),然后写入物理驱动器(句柄 h)。您必须枚举您的卷并检查它们在哪个驱动器上。 所以基本上这需要通过FindFirstVolume 枚举所有卷并查询IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 以查找包含目标磁盘范围(物理驱动器号和扇区范围)的卷。然后在尝试写入物理磁盘之前卸载或锁定所有受影响的卷。 是的,按照这些思路。 我想从这个答案链接到我的相关问题:***.com/questions/65225665 - 我正在解释这种方法的一个问题,并希望在我的情况下有一个更好的方法。

以上是关于如何显式锁定已挂载的文件系统?的主要内容,如果未能解决你的问题,请参考以下文章

CentOS查看已挂载的文件系统和文件系统信息

如何查看linux文件系统的类型?

“已挂载”的文件系统空间占用查看

如何在Linux系统下挂载光盘

findmnt命令查找已挂载的文件系统

linux文件挂载?