在灾备项目组做CDP(Continual Data Protection),刚刚入门,最近看了下关于磁盘的操作,记录一下DeviceIoControl()。

NTFS中的文件结构

NTFS文件系统中,磁盘文件存储管理的最小单位是簇(Cluster)。一个簇由连续的若干个扇区构成。对于一个文件来说,我们将其在磁盘上占用的连续的簇称为簇流(Cluster Runs,不知道怎么翻译,有人称之为簇流,我也就这么叫吧),一个文件可以占用多个簇流。当文件的内容较小时,会与该文件的常驻属性一同放置在MFT的项中,每个MFT项的大小在引导扇区中被定义,但目前所有的NTFS文件系统的MFT项的大小均为1024Bytes。当文件内容较大时,就会占用额外的簇空间来存储数据,称之为非常驻属性。

非常驻属性被存储在簇流空间,簇流就是一组连续的簇,用起始簇号和长度表示一个簇流。例如,一个非常驻属性分成三部分,第一部分存储在簇号为30,31,32,33和34的簇中,那么该簇流的起始簇号就是30,流长度为5。第二部分存储在簇号为66和67的簇中,则该簇流起始簇号为66,流长度为2。第三部分存储在簇号为39~42的簇中,该簇流的起始簇号就是39,流长度为4。该属性一共占用了三个簇流,像下图这样(截图,侵删):

NTFS使用VCN(Virtual Cluster Number,虚拟簇号)和LCN(Logical Cluster Number,逻辑簇号)来描述文件或部分文件的位置。一个文件的VCN从0开始,是文件内容的内部编号,也就是说,将文件按照逻辑簇的大小进行划分,然后从0开始编号,就是所谓的VCN。而LCN就是文件系统的逻辑簇号,对于一个卷区来说,每一个簇的逻辑簇号是独一无二的。通过这种VCN和LCN的映射关系,我们就能在文件系统中定位文件。返回上面的例子,逻辑簇号30~34存储了文件内容的第一部分,对应的VCN为0~4,第二部分存储在逻辑簇号为66~67的簇中,其对应的VCN为5~6,第三部分存储在逻辑簇号39~42的簇中,对应的VCN为7~10。于是有下图(截图,侵删):

winioctl.h中的两个结构

//==================== FSCTL_GET_RETRIEVAL_POINTERS ======================
//
// Structure for FSCTL_GET_RETRIEVAL_POINTERS
//typedef struct {LARGE_INTEGER StartingVcn;} STARTING_VCN_INPUT_BUFFER, *PSTARTING_VCN_INPUT_BUFFER;typedef struct RETRIEVAL_POINTERS_BUFFER {DWORD ExtentCount;LARGE_INTEGER StartingVcn;struct {LARGE_INTEGER NextVcn;LARGE_INTEGER Lcn;} Extents[1];} RETRIEVAL_POINTERS_BUFFER, *PRETRIEVAL_POINTERS_BUFFER;

第一个比较简单,定义了文件的起始VCN,使用的时候置为0即可。

第二个结构定义了文件的VCN和LCN,用来在文件系统中定位文件。ExtentCount是文件的簇流个数,每一个簇流对应一个Extents,而Extents中定义了下一个簇流的起始VCN和本簇流的起始LCN。

DeviceIoControl()的参数解释

DeviceIoControl(_In_ HANDLE hDevice,_In_ DWORD dwIoControlCode,_In_reads_bytes_opt_(nInBufferSize) LPVOID lpInBuffer,_In_ DWORD nInBufferSize,_Out_writes_bytes_to_opt_(nOutBufferSize, *lpBytesReturned) LPVOID lpOutBuffer,_In_ DWORD nOutBufferSize,_Out_opt_ LPDWORD lpBytesReturned,_Inout_opt_ LPOVERLAPPED lpOverlapped);

其中,hDevice是要操作对象的句柄,dwIoControlCode使用FSCTL_GET_RETRIEVAL_POINTERS来获取文件的簇信息,第三和第四个参数对应了上述第一个结构,第五和第六个参数对应了上述第二个结构,lpBytesReturned是驱动程序实际返回给应用程序的数据字节数地址,最后一个参数用于重叠操作,这里置为NULL即可。

当然,对于常驻MFT的小文件,由于没有占用额外的簇空间,下面的代码是读取不到的。

源码

C代码如下,VS2015编译通过:

// FileCopy.cpp : 定义控制台应用程序的入口点。
//#include<stdio.h>
#include<windows.h>
#include<winioctl.h>
#include<tchar.h>
#include<Shlwapi.h>
#include<math.h>
using namespace std;
//#pragma comment(lib,"Shlwapi.lib")
//#pragma comment(lib,"Kernel32.lib")typedef struct clusterNode
{//存储文件逻辑簇号的链表ULONGLONG LCN;clusterNode *next;
}clusterNode, *clusterLinkList;void freeClusterLinkList(clusterLinkList Head)
{//释放链表clusterNode *p;while (Head != NULL) {p = Head;Head = Head->next;free(p);}free(Head);
}clusterNode *getFileClusters(TCHAR* fileName, ULONG *fileSize, ULONG *clusterNumber, ULONG *sectorsPerCluster, ULONG *bytesPerSector) {//记录文件信息FILE * fp;fp = fopen("F://fileInfo.txt", "a+");if (fp == NULL) {printf("Open F://fileInfo.txt error\n");return NULL;}//获取卷区信息DWORD sPc, bPs, fc;//分别表示簇内扇区数、扇区内字节数、磁盘剩余簇数wchar_t driverRoot[4] = { 0 };memset(driverRoot, 0, sizeof(driverRoot));memcpy(driverRoot, fileName, 6);if (!GetDiskFreeSpace(driverRoot, &sPc, &bPs, &fc, NULL))//获取磁盘剩余空间容量{printf("GetDiskFreeSpace Error %d\n", GetLastError());return NULL;}ULONGLONG lsPc = (ULONGLONG)sPc;//转unsigned long longULONGLONG lfc = (ULONGLONG)fc;ULONGLONG lbPs = (ULONGLONG)bPs;*sectorsPerCluster = sPc;*bytesPerSector = bPs;//创建/打开文件HANDLE hFile = INVALID_HANDLE_VALUE;hFile = CreateFile(fileName, FILE_READ_ATTRIBUTES,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,NULL,OPEN_EXISTING,NULL,NULL);if (hFile == INVALID_HANDLE_VALUE){printf("Open file error %d\n", GetLastError());return NULL;}//获取文件大小*fileSize = GetFileSize(hFile, NULL);//文件大小//获取文件占用逻辑簇号DWORD clusterCnt = DWORD(ceil(*fileSize * 1.0 / bPs / sPc));//文件占用簇数*clusterNumber = clusterCnt;fprintf(fp, "fileInfo:\nfileName : %ls\nfileSize : %lu Bytes\nclusterCount : %lu\nsPC : %lu\nbPS : %lu\n", fileName, *fileSize, *clusterNumber, *sectorsPerCluster, *bytesPerSector);printf("fileInfo:\nfileName : %ls\nfileSize : %lu Bytes\nclusterCount : %lu\nsPC : %lu\nbPS : %lu\n", fileName, *fileSize, *clusterNumber, *sectorsPerCluster, *bytesPerSector);STARTING_VCN_INPUT_BUFFER inputVcn;//是一个LARGE_INTEGER,表示文件的起始VCNinputVcn.StartingVcn.QuadPart = 0; //起始VCN=0BOOL result = FALSE;//DWORD dwBytesReturned = 0;LPDWORD dwBytesReturned = NULL;PRETRIEVAL_POINTERS_BUFFER rpBuffer;ULONGLONG rpBufferSize = sizeof(RETRIEVAL_POINTERS_BUFFER) + sizeof(rpBuffer->Extents)*clusterCnt;//考虑最差情况,所有的簇流均只包含 一个独立的簇rpBuffer = (PRETRIEVAL_POINTERS_BUFFER)malloc(rpBufferSize);result = DeviceIoControl(//直接发送控制代码到指定的设备驱动程序,返回非零表示成功hFile,FSCTL_GET_RETRIEVAL_POINTERS,&inputVcn,//应用程序传递给驱动程序的数据缓冲区地址sizeof(STARTING_VCN_INPUT_BUFFER),rpBuffer,//驱动程序返回给应用程序的数据缓冲区地址//sizeof(RETRIEVAL_POINTERS_BUFFER) + sizeof(rpBuffer->Extents),rpBufferSize,(DWORD*)dwBytesReturned,//驱动程序实际返回给应用程序的数据字节数地址NULL);if (result == TRUE){LARGE_INTEGER preVcn = rpBuffer->StartingVcn;LARGE_INTEGER Lcn;DWORD CnCount, r;clusterNode *fileClusters = NULL;//带头结点的逻辑簇号链fileClusters = (clusterNode*)malloc(sizeof(clusterNode));fileClusters->LCN = -1;fileClusters->next = NULL;clusterNode *rearCluster = fileClusters;//尾指针fprintf(fp, "RETRIEVAL_POINTERS_BUFFER->ExtentCount : %lu\n\n", rpBuffer->ExtentCount);int  cnt = 0;for (r = 0; r<rpBuffer->ExtentCount; r++)   //ExtentCount 簇流的个数(每个簇流中有几个连续的簇)  {Lcn = rpBuffer->Extents[r].Lcn;//本簇流起始逻辑簇号//簇流中连续簇的个数等于 下一个簇流的起始 Vcn 号 减去 上一个 簇流的 起始 Vcn号   for (CnCount = (ULONG)(rpBuffer->Extents[r].NextVcn.QuadPart - preVcn.QuadPart); CnCount; CnCount--, Lcn.QuadPart++){//fprintf(fp, "ERRORCODE->%d  preVcn--->%lld  NextVcn--->%lld  LCN--->%lld  clusterCNT-->%d\n", GetLastError(), preVcn.QuadPart, rpBuffer->Extents[r].NextVcn.QuadPart, Lcn.QuadPart, cnt++);clusterNode *cNode = (clusterNode*)malloc(sizeof(clusterNode));cNode->LCN = Lcn.QuadPart;//保存每个簇流中簇的 Lcn 号cNode->next = NULL;rearCluster->next = cNode;rearCluster = cNode;}preVcn = rpBuffer->Extents[r].NextVcn;}if (INVALID_HANDLE_VALUE != hFile){CloseHandle(hFile);hFile = INVALID_HANDLE_VALUE;}fclose(fp);return fileClusters;}else {printf("DeviceIoControl Error : %d\n", GetLastError());fclose(fp);return NULL;}
}int   main(int   argc, char   *argv[]) {clusterLinkList fileClusters = NULL;ULONG clusterSize, clusterCount;ULONG fileSize, blockSize;ULONG sectorsPerCluster, bytesPerSector;fileClusters = getFileClusters(L"F://TEST.txt", &fileSize, &clusterCount, §orsPerCluster, &bytesPerSector);clusterNode *p = fileClusters->next;while (p != NULL) {printf("LCN -> %lld\n", p->LCN);p = p->next;}freeClusterLinkList(fileClusters);return 0;
}

DeviceIoControl获取文件LCN相关推荐

  1. 结合WMI和DeviceIoControl获取网卡原生MAC地址和当前MAC地址

    虽然Win32_NetworkAdapter包含了属性PermanentAddress,但是在当前的WMI里只是个空值,微软目前还没有实现这个属性值.但是我们仍可以通过结合WMI和DeviceIoCo ...

  2. 通过DeviceIoControl获取U盘或移动硬盘的出品商、制造商和版本号等信息

    在U盘.移动硬盘.USB HUB和各种USB读卡器的主控芯片板上,都存有设备的出品商(vender).制造商(producter).版本号(version)和序列号(SN)信息,前两者一般是英文单词记 ...

  3. java 读取流的字符编码格式_如何使用Java代码获取文件、文件流或字符串的编码方式...

    标签: 今天通过网络资源研究了一下如何使用Java代码获取文件.文件流或字符串的编码方式,现将代码与大家分享: package com.ghj.packageoftool; import info.m ...

  4. 未获得计算机访问权限,如何获取文件夹的访问权限

    有些系统文件夹打不开,显示信息"拒绝你访问该文件夹",有点让人摸不着头脑,明明我是管理员账号,明明整台电脑都是我的,你凭什么不让我访问呢,原来系统内是有比较复杂的权限分配的,我们可 ...

  5. php文件夹列表,php获取文件夹下面的文件列表和文件夹列表

    function getDir($dir) { $dirArray[] = NULL; if (false != ($handle = opendir( $dir ))) { $i=0; while ...

  6. Python递归获取文件夹下面所有文件名字:

    Python递归获取文件夹下面所有文件名字: def getAllFiles(targetDir):files = []listFiles = os.listdir(targetDir)for i i ...

  7. Java 查看文件绝对路径,JAVA获取文件绝对路径的方法

    本文实例讲述了JAVA获取文件绝对路径的方法.分享给大家供大家参考.具体实现方法如下: /** * 获取一个类的class文件所在的绝对路径. 这个类可以是JDK自身的类,也可以是用户自定义的类,或者 ...

  8. java注解接收上传文件,前台:Input type=file 后台获取文件内容用的是spring注解,当地环境上传图片是好的,发布到服务器上图片读取不到,求大神指点...

    当前位置:我的异常网» Java Web开发 » 前台:Input type="file" 后台获取文件内 前台:Input type="file" 后台获取文 ...

  9. pandas读取csv文件发生编码(encoding)错误:获取文件编码格式之后再读取文件

    pandas读取csv文件发生编码(encoding)错误:获取文件编码格式之后再读取文件 目录

最新文章

  1. Ubuntu virtualbox
  2. JAVA高级工程师课程笔记整理——(八)tomcat与九大内置对象
  3. 遗传算法求函数最大值实验_小知识:什么是遗传算法
  4. 33 WM配置-策略-出库策略6-定义过期日期策略H(SLED)
  5. java对list里面按照分数排名_提前批警校排名,部属警校去省厅,省级警校去乡镇吗...
  6. 共享单车变“私有”、被毁、被盗:用户们都看不下去了,举报!
  7. java ee 程序_第一个 JavaEE 应用程序 - JavaWeb 入门开发教程
  8. 深度学习在推断阶段(inference)的硬件实现方法概述
  9. javascript中for循环里面套定时器,始终打印结束值原因
  10. Android studio导入项目报错Please refer to the user guide chapter on the daemon at http://gradle.org/docs/2
  11. matlab神经网络流程图,BP神经网络算法步骤.doc
  12. w7计算机虚拟内存设置,win7虚拟内存怎么设置最好
  13. JS中各种width和height的区别
  14. 计算机视觉之旅(Day5)
  15. 【数据库课设】机票预订系统 java+mysql实现 附源码
  16. 简述网桥的特点_网桥的工作原理和特点是什么呢?
  17. 多重比较和多重比较陷阱
  18. Java HashMap底层实现
  19. 一天上手Aurora 8B/10B IP核(2)----Aurora概述及数据接口(Framing接口、Streaming接口)
  20. 19蓝桥国赛B组C/C++ I第八大奇迹

热门文章

  1. 嵌入式面试题整理(一)
  2. java对接医疗设备_基于Java框架的医疗设备管理系统设计与实现
  3. CASIA-SURF
  4. [小程序开发] silk 转 MP3 ,第三步
  5. TDOA算法的matlab仿真
  6. 火车运煤(骆驼运胡萝卜)问题的最优解
  7. 华东师范大学 2017 计算机系暑期夏令营机试
  8. RPC实现和原理解析
  9. 捷联惯导系统学习2.5(等效旋转矢量微分方程)
  10. python展示两幅图_Tkinter,并排显示两幅图像