如何获取 VSS 快照的“NextUSN”日志条目?

2023-11-24

创建 VSS 快照后,我希望能够查询 USN 日志。这是否可能,或者 USN 日志是否无法从 VSS 快照访问?

my goal 是能够在两个 VSS 快照之间的增量备份中使用 USN 日志。备份的过程是

  1. 拍摄 VSS 快照并备份卷,记下每个文件的 USN 条目
  2. ...使用文件系统,添加/删除/修改文件
  3. 拍摄第二个 VSS 快照,然后使用 USN 日志检测步骤 #2 中发生的任何更改

我现在失败的是我试图获取 VSS 快照上最高 USN 条目的部分

  1. 创建 VSS 快照
  2. 使用 CreateFile(\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy25) 打开快照
  3. DeviceIoControl(FSCTL_QUERY_USN_JOURNAL) - 失败并显示 GLE:1179“卷更改日志未激活”

我可以从命令行模拟它,如下所示

C:\>vssadmin list shadows
vssadmin 1.1 - Volume Shadow Copy Service administrative command-line tool
(C) Copyright 2001-2005 Microsoft Corp.

Contents of shadow copy set ID: {54fc99fb-65f2-4558-8e12-9308979327f0}
   Contained 1 shadow copies at creation time: 5/10/2012 6:44:19 PM
      Shadow Copy ID: {a2d2c155-9916-47d3-96fd-94fae1c2f802}
         Original Volume: (T:)\\?\Volume{a420b1fa-9744-11e1-9082-889ffaf52b70}\
         Shadow Copy Volume: \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy25
         Originating Machine: computer
         Service Machine: computer
         Provider: 'Microsoft Software Shadow Copy provider 1.0'
         Type: Backup
         Attributes: Differential


C:\>fsutil usn queryjournal \\?\Volume{a420b1fa-9744-11e1-9082-889ffaf52b70}
Usn Journal ID   : 0x01cd2ebe9c795b57
First Usn        : 0x0000000000000000
Next Usn         : 0x000000000001b5f8
Lowest Valid Usn : 0x0000000000000000
Max Usn          : 0x7fffffffffff0000
Maximum Size     : 0x0000000000100000
Allocation Delta : 0x0000000000040000

C:\>fsutil usn queryjournal \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy25
Error:  The volume change journal is not active.

如果可能的话,我知道我做错了什么吗?


这个问题对于我正在从事的项目非常重要,所以我最终(几乎)100% 成功了。

注意:以下所有代码片段均采用 C# 编写

感谢 Hannes de Jager 之前的回答,他为我指明了正确的方向和文档,我现在可以从 VSS 快照或常规 API 无法使用的任何其他特殊设备读取 USN 日志;就我而言,我的意思是使用 VDDK(VMware SDK for VM disk)安装的 VMware 快照。

我还重用或导入了来自伟大项目的代码:

  • C# 中的 USN 期刊浏览器,来自 StCroixSkipper (http://www.dreamincode.net/forums/blog/1017-stcroixskippers-blog/)。仅使用官方 API 读取 USN(因此此处没有 VSS),但提供有用的 pinvoke 和 Win32 API 结构以及有关 USN 如何工作的一般信息

  • 阿尔法FS(https://github.com/alphaleonis/AlphaFS/),它模仿了大部分System.IO命名空间,但允许访问特殊的 Windows 路径(VSS 快照、原始设备)并提供有用的扩展。

如果其他人感兴趣,我分享我现在使用的代码,仍然处于相当粗糙的状态,但可以工作。

它是如何工作的?

首先,您必须评估所需的 Usn 日记帐组件。它们作为 ADS(备用数据流)位于设备根部的隐藏条目中。无法使用标准访问它们System.IO命名空间,这就是我之前告诉我使用 AlphaFS 项目的原因,但是 pinvokingCreateFile() and ReadFile()应该足够了。

1/2

入口\$Extend\$UsnJrnl:$Max拥有有关期刊当前状态的全球信息。最重要的部分是 usn 日志 ID(如果您想要比较多个 VSS 快照,可以使用它来检查日志是否未被重置)和最低的有效 USN 日志序列号。

USN期刊结构:

  // can be directly extracted from $MAX entry using Bitconverter.ToUint64
 public struct USN_JOURNAL_DATA{
        public UInt64 MaximumSize; //offset 0
        public UInt64 AllocationDelta; // offset 8
        public UInt64 UsnJournalID; // offset 16
        public Int64 LowestValidUsn; // offset 24
    }

2/2

入口\$Extend\$UsnJrnl:$J包含日志记录。它是一个稀疏文件,因此其磁盘使用量远低于其大小。

为了回答第一个问题,如何从先前的 VSS 快照中知道 Max 使用的 USN 序列并将其与另一个快照的序列进行比较? 那么,NextUsn 值就等于$Usnjrnl:$J entry.

在“新”vss 快照 USN 日志上,如果您想解析两个快照之间更改的记录,则可以在开始解析记录之前查找“参考”VSS 快照最大 USN。

一般来说,每个USN日志条目都是一个唯一的ID(USN号),它是内部的偏移量$J日记条目本身所在的位置。 每个条目的大小都是可变的,因此要顺序读取,我们必须计算:

next entry offset inside $J = 
    offset of current entry (or its USN sequennce number + length of current entry

幸运的是,记录长度也是 USN 条目记录的一个字段。说得够多了,这是美国海军的记录等级:

public class UsnEntry : IComparable<UsnEntry>{
        private const int FR_OFFSET = 8;
        private const int PFR_OFFSET = 16;
        private const int USN_OFFSET = 24;
        private const int REASON_OFFSET = 40;
        private const int FA_OFFSET = 52;
        private const int FNL_OFFSET = 56;
        private const int FN_OFFSET = 58;


        public UInt32 RecordLength {get; private set;}
        public Int64 USN {get; private set;}
        public UInt64 FileReferenceNumber {get;private set;}
        public UInt64 ParentFileReferenceNumber {get; private set;}
        public UInt32 Reason{get; set;}
        public string Name {get; private set;}
        public string OldName{get; private set;}

        private UInt32 _fileAttributes;
        public bool IsFolder{
            get{
                bool bRtn = false;
                if (0 != (_fileAttributes & Win32Api.FILE_ATTRIBUTE_DIRECTORY))
                    bRtn = true;
                return bRtn;
            }
        }

        public bool IsFile{
            get{
                bool bRtn = false;
                if (0 == (_fileAttributes & Win32Api.FILE_ATTRIBUTE_DIRECTORY))
                    bRtn = true;
                return bRtn;
            }
        }

         /// <summary>
        /// USN Record Constructor
        /// </summary>
        /// <param name="p">Buffer pointer to first byte of the USN Record</param>
        public UsnEntry(IntPtr ptrToUsnRecord){
            RecordLength = (UInt32)Marshal.ReadInt32(ptrToUsnRecord); //record size
            FileReferenceNumber = (UInt64)Marshal.ReadInt64(ptrToUsnRecord, FR_OFFSET);
            ParentFileReferenceNumber = (UInt64)Marshal.ReadInt64(ptrToUsnRecord, PFR_OFFSET);
            USN = (Int64)Marshal.ReadInt64(ptrToUsnRecord, USN_OFFSET);
            Reason = (UInt32)Marshal.ReadInt32(ptrToUsnRecord, REASON_OFFSET);
            _fileAttributes = (UInt32)Marshal.ReadInt32(ptrToUsnRecord, FA_OFFSET);
            short fileNameLength = Marshal.ReadInt16(ptrToUsnRecord, FNL_OFFSET);
            short fileNameOffset = Marshal.ReadInt16(ptrToUsnRecord, FN_OFFSET);
            Name = Marshal.PtrToStringUni(new IntPtr(ptrToUsnRecord.ToInt32() + fileNameOffset), fileNameLength / sizeof(char));
        }


        public int CompareTo(UsnEntry other){
            return string.Compare(this.Name, other.Name, true);
        }

        public override string ToString(){
            return string.Format ("[UsnEntry: RecordLength={0}, USN={1}, FileReferenceNumber={2}, ParentFileReferenceNumber={3}, Reason={4}, Name={5}, OldName={6}, IsFolder={7}, IsFile={8}", RecordLength, USN, (int)FileReferenceNumber, (int)ParentFileReferenceNumber, Reason, Name, OldName, IsFolder, IsFile);
        }
    }

我尝试隔离可以解析 USN 日志并提取其条目的最小代码部分,从最低的有效条目开始。请记住,记录的长度是可变的;另请注意,某些记录指向下一个空记录(前 4 个字节,通常是记录长度,被清零)。在本例中,我查找 4 个字节并重试解析,直到获得下一条记录。用 Python 编写类似解析工具的人也报告了这种行为,所以我想我在这里并没有太大错。

string vol = @"\\?\path_to_your_VSS_snapshot";
string maxHandle = vol + @"\$Extend\$UsnJrnl:$Max";
string rawJournal= vol + @"\$Extend\$UsnJrnl:$J";

// cannot use regular System.IO here, but pinvoking ReadFile() should be enough
FileStream maxStream = Alphaleonis.Win32.Filesystem.File.OpenRead(maxHandle);
byte[] maxData = new byte[32];
maxStream.Read(maxData, 0, 32);

//first valid entry
long lowestUsn = BitConverter.ToInt64(maxData, 24);

// max (last) entry, is the size of the $J ADS
IntPtr journalDataHandle = Win32Api.CreateFile(rawJournal, 
            0, 
            Win32Api.FILE_SHARE_READ| Win32Api.FILE_SHARE_WRITE,
            IntPtr.Zero, Win32Api.OPEN_EXISTING,  
            0, IntPtr.Zero);
Win32Api.BY_HANDLE_FILE_INFORMATION fileInfo = new Win32Api.BY_HANDLE_FILE_INFORMATION();
Win32Api.GetFileInformationByHandle(journalDataHandle, out fileInfo);
Win32Api.CloseHandle(journalDataHandle);
long lastUsn = fileInfo.FileSizeLow;


int read = 0;
byte[] usnrecord;
byte[] usnraw = new byte[4]; // first byte array is to store the record length

// same here : pinvoke ReadFile() to avoid AlphaFS dependancy
FileStream rawJStream = Alphaleonis.Win32.Filesystem.File.OpenRead(rawJournal);
int recordSize = 0;
long pos = lowestUsn;

while(pos < newUsnState.NextUsn){
seeked = rawJStream.Seek(pos, SeekOrigin.Begin);
read = rawJStream.Read(usnraw, 0, usnraw.Length);
recordSize = BitConverter.ToInt32(usnraw, 0);
    if(recordSize == 0){
    pos = pos+4;
    continue;
}
    usnrecord = new byte[recordSize];
rawJStream.Read(usnrecord, 4, recordSize-4);
Array.Copy(usnraw, 0, usnrecord, 0, 4);
fixed (byte* p = usnrecord){
    IntPtr ptr = (IntPtr)p;
        // here we use the previously defined UsnEntry class
    Win32Api.UsnEntry entry = new Win32Api.UsnEntry(ptr);
    Console.WriteLine ("entry: "+entry.ToString());
    ptr = IntPtr.Zero;

}
    pos += recordSize;
}

以下是我使用的 pinvoke:

public class Win32Api{

   [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct BY_HANDLE_FILE_INFORMATION{
        public uint FileAttributes;
        public FILETIME CreationTime;
        public FILETIME LastAccessTime;
        public FILETIME LastWriteTime;
        public uint VolumeSerialNumber;
        public uint FileSizeHigh;
        public uint FileSizeLow;
        public uint NumberOfLinks;
        /*public uint FileIndexHigh;
        public uint FileIndexLow;*/
        public FileID FileIndex;
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool 
        GetFileInformationByHandle(
        IntPtr hFile,
        out BY_HANDLE_FILE_INFORMATION lpFileInformation);

   [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr 
        CreateFile(string lpFileName, 
        uint dwDesiredAccess,
        uint dwShareMode, 
        IntPtr lpSecurityAttributes, 
        uint dwCreationDisposition,
        uint dwFlagsAndAttributes, 
        IntPtr hTemplateFile);


}

这绝对不是地球上最好的代码,但我认为它将为任何必须做同样事情的人提供一个很好的起点。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何获取 VSS 快照的“NextUSN”日志条目? 的相关文章

  • 从单击的按钮上移除焦点

    当我单击按钮时 焦点将设置到该按钮 如何使用 C 中的 WIN API 移除此按钮的焦点 我想也许以某种方式SendMessageWIN API 方法可以从按钮上移除焦点 我只有按钮的句柄 也无法再访问父窗口 我知道几乎每次都可以使用 Se
  • 没有窗口的 GetFontUnicodeRanges

    有机会打电话吗GetFontUnicodeRanges没有窗户 例如 它可能是不允许与桌面交互的 Windows 服务 目前我正在使用控制台应用程序对此进行测试 program UnicodeConsoleOutput APPTYPE CO
  • 有没有办法删除 FILE_FLAG_DELETE_ON_CLOSE?

    在我的应用程序中 我想使用 FILE FLAG DELETE ON CLOSE 打开一个临时文件 然而 在某些情况下 临时文件需要保留并且非常大 我想删除打开的句柄上的 FILE FLAG DELETE ON CLOSE 属性 这可能吗 复
  • 了解 VerQueryValue

    在 MSDN 上 我注意到 VerQueryValue 函数的以下内容 lplp缓冲区 输出 低电压空洞当此方法返回时 包含指向 pBlock 指向的缓冲区中所请求版本信息的指针的地址 当关联的 pBlock 内存被释放时 lplpBuff
  • 发送/捕获 SIGTERM 的 Win32 API 模拟

    在 POSIX OS 下 有信号 API 允许向进程发送信号以将其关闭 使用kill 您可以使用sigaction捕获它并执行您需要的操作 然而 Win32不是POSIX系统 所以 如何处理可能出现的关闭事件 例如来自 任务管理器 中的 结
  • 注册表碎片整理程序[重复]

    这个问题在这里已经有答案了 这是出于好奇 但我见过几个 其中一些非常流行 称为注册表碎片整理程序的软件 虽然我可以看到它们提供的好处 但我很好奇您到底是如何进行注册表碎片整理的 请注意 我并不是要求提供软件名称 只是询问其如何以编程方式完成
  • 避免 UAC 但使用 Windows 服务启动提升的进程

    我有一个非交互式服务作为 Windows 计算机上的特权系统用户运行 我需要它来启动给定的可执行文件作为提升的进程 我已设法使用 WTSGetActiveConsoleSessionId 以 SYSTEM 身份启动一个子进程 找到一个系统进
  • 微软怎么能说WinAPI中一个字的大小是16位呢?

    我刚刚开始学习WinAPI 在MSDN中 对WORD数据类型提供了以下解释 WORD16 位无符号整数 范围是十进制 0 到 65535 该类型在 WinDef h 中声明如下 typedef 无符号短 WORD 很简单 而且它与我一直在使
  • 如何在另一个应用程序中挂钩 api 调用

    我正在尝试挂钩另一个应用程序的 ExtTextOut 和 DrawTextExt GDI 方法调用 我知道我需要使用 GetProcAddress 来查找 gdi32 dll 中那些方法的地址 并用我的函数的地址覆盖我想要挂钩的进程中的地址
  • 无法从 Windows 注册表查询值

    我为 Windows 注册表编写程序并尝试从中查询值 但即使我以管理员权限运行自己的程序 我也无法读取所有参数并收到错误代码 5 某些值的访问被拒绝 但同时标准的注册表可以向我展示这个价值 我做错了什么 我有一个注册表类RegistryCl
  • 在64位环境中加载32位进程

    我有以下几个问题 CHM 是 编译的 HTML 文件 我的 CHM 文件有一个启动 32 位应用程序的链接 CHM 文件是用 Javascript 编码的 这在 32 位操作系统环境中运行良好 但这在 64 位操作系统环境中不起作用 原因是
  • 了解 IServiceProvider 和 QueryService

    谁能解释一下背后的想法IServiceProvider and QueryService 我不明白服务是什么以及它与其他类型的对象或接口有何不同 我看到的解释很少 主要是一些评论here https learn microsoft com
  • 设置透明背景Win32

    我想做的事情非常简单 但似乎没有太多信息 基本上我有一个普通的不透明父窗口 我想在该父窗口内放置一个子窗口 有自己的不透明控件 所以我唯一要做的就是将子窗口的背景画笔设置为透明 但它仍然绘制白色背景 尝试使用 WS EX LAYERED 样
  • 让 Windows 尝试读取文件

    我正在对 Windows 文件系统进行某种封装 当用户请求打开文件时 Windows 调用我的驱动程序来提供数据 在正常操作中 驱动程序返回缓存的文件内容 但是 在某些情况下 实际文件没有缓存 我需要从网络下载它 问题是是否有可能让 Win
  • Windows 内核中可能的最大文件名长度

    我想知道 什么是longestWindows 内核允许的可能名称长度 例如 我知道内核使用UNICODE STRING结构来保存所有对象路径 并且由于宽字符字符串的字节长度存储在USHORT 允许最大路径长度为 2 15 1 个字符 有没有
  • 使用 MapViewOfFile 有什么限制吗?

    我正在尝试将内存映射文件用作 hFile CreateFile State Path GENERIC READ FILE SHARE READ FILE SHARE WRITE 0 OPEN EXISTING FILE FLAG SEQUE
  • Win32 函数获取 C:\ProgramData 的路径

    我的应用程序需要安装一些可以由应用程序在运行时编辑的文件 Installshield提供了一个别名 CommonAppDataFolder 它将在Vista和Windows 7上解析为c programData 并且也适用于Windows
  • 使用createremotethread注入dll

    createremotethread如何在进程内执行dll 它使用的参数之一是 loadlibraryA 所以我得到了它在进程中执行 loadlibrary 函数的部分 然后它应该将上下文切换到内核模式 dll 是否由具有 loadlibr
  • 将 CreateThread 与 lambda 结合使用

    只是试验 但我想知道是否可以使该代码工作 如编译 void main int number 5 DWORD dontThreadOnMe PVOID PVOID data int value int data cout lt lt valu
  • 如何使用 COMMTIMEOUTS 等待字节可用但读取多个字节?

    我有一个 C 串行端口类 它具有用于读取操作的非阻塞和阻塞模式 对于阻塞模式 COMMTIMEOUTS cto GetCommTimeouts m hFile cto Set the new timeouts cto ReadInterva

随机推荐

  • jQuery 打开按按钮选择

    如何通过按钮打开选择下拉菜单 button on click function select trigger click 我的代码 http jsfiddle net UGkWp UPDATE 我找到了针对 webkit 浏览器的解决方案
  • 传递到字典中的模型项的类型为“System.Data.Entity.DynamicProxies.Object”

    有谁知道为什么我会收到此错误 The model item passed into the dictionary is of type system Data Entity DynamicProxies Object 3E186F80358
  • java中的KeyPressed事件

    我刚刚创建了一个java tic tac toe 游戏 我想弄清楚在特定条件下按下回车键后如何运行一个方法 示例如下 if condition is met keyListener 根据您想要捕获 enter 键的位置 您可以使用Actio
  • 清除 Magento 缓存

    我正在使用 Magento 1 6 1 版 我需要以编程方式清除 Magento 缓存 Mage app gt getCache gt clean 我使用了上面的代码 但它没有清除缓存 try this Mage app gt cleanC
  • 如何正确更新我的图表值? (实时)

    我最近遇到了一个名为 LiveChart 的工具 并决定对其进行测试 不幸的是 我在弄清楚如何实时更新图表值时遇到了一些问题 我很确定有一种干净且正确的方法可以做到这一点 但我无法找到它 我希望能够通过更新值private void或按钮
  • ExtJs:在组合框中搜索/过滤

    我在 ExtJs 2 3 中遇到以下问题 我想在组合框中进行搜索 我给你举个例子 Ext comboData names Peter Paul Amanda var store new Ext data SimpleStore fields
  • surfaceView.getHolder 不返回 SurfaceHolder

    我正在尝试编写一个使用相机的应用程序 当尝试获取最终传递给启动相机的 surfaceCreated 的 surfaceHolder 时 我收到 NullPointerException getHolder 是否有返回NULL的情况 than
  • 如何连接 sparql 中的值列表?

    假设我有一个urihttp dbpedia org page Manmohan Singh现在他的标签 dbpprop years 中有一个年份列表 当我写一个查询时 PREFIX rdf
  • 在匿名方法中使用 MethodInfo.GetCurrentMethod()

    public static void Main string args Action a gt Console WriteLine MethodInfo GetCurrentMethod Name a 这段代码将返回一个模糊的字符串 如下所
  • CORS - 是客户端、服务器端还是传输层? [复制]

    这个问题在这里已经有答案了 我试图理解CORS 根据我的理解 它使您能够限制哪些域可以访问服务器上的资源 然而 这似乎并不是故事的全部 例如 我有一个未启用 CORS 的 Web 服务 我无法通过 jQuery 从我的 Web 应用程序访问
  • 具有自签名证书的 Python 3 urllib

    我正在尝试使用 Python 从内部服务器下载一些数据 由于它是内部的 因此它使用自签名证书 我们不想为永远不会出现在 野外 的服务器付费 Python 2 6 版本的代码运行良好 response urllib2 urlopen URL
  • ReactJS:警告:setState(...):无法在现有状态转换期间更新

    我正在尝试从渲染视图重构以下代码
  • Python内存映射

    我正在处理大数据 并且我的矩阵大小约为 2000x100000 因此为了更快地工作 我尝试使用 numpy memmap 来避免由于 RAM 限制而在内存中存储这么大的矩阵 问题是 当我将相同的矩阵存储在两个变量中时 即一个使用 numpy
  • 在 Rails 中存储应用程序特定配置的最佳方法是什么?

    我需要在 Rails 中存储应用程序特定的配置 但它必须是 可在任何文件 模型 视图 助手和控制器 中访问 指定 或未指定 环境 这意味着每个环境都可以覆盖environment rb中指定的配置 我尝试使用environment rb并放
  • 致命错误:未找到类“Illuminate\Foundation\Application”

    当我打开使用 laravel 5 制作的网站时出现以下错误 致命错误 在第 14 行的 C cms bootstrap app php 中未找到 Illuminate Foundation Application 类 我尝试删除供应商文件夹
  • 在运行时加载 Java 字节代码

    我得到了一些在我的程序中生成的java字节代码 因此编译了java源代码 现在我想将此字节码加载到当前运行的 Java VM 中并运行特定的函数 我不知道如何实现这一点 我深入研究了 Java 类加载器 但没有找到直接的方法 我找到了一个解
  • 在 Android 模拟器上通过 appium 或 adb 启用“show_touches”

    是否可以从 appium 启用 android 上的 show touches 选项 还是通过adb 我有一个 appium 测试脚本 它的行为不正常 我不知道为什么 我想看看它到底在哪里点击 adb shell settings put
  • Apollo 客户端:变量未定义。收到状态码 400

    我正在尝试使用 Apollo 客户端在 GraphQL 查询中使用动态变量 我已遵循文档 但 Apollo 不断给我错误 说我的变量未定义 并最终以状态代码 400 进行响应 Apollo 的文档是这样说的 mutate 选项 Mutati
  • Rails 应用程序中的 Cookie 溢出?

    UsersController create 中的 ActionDispatch Cookies CookieOverflow 当我尝试打开该页面时出现此错误 我不知道如何调试这个错误 您对这个问题有什么建议吗 def create use
  • 如何获取 VSS 快照的“NextUSN”日志条目?

    创建 VSS 快照后 我希望能够查询 USN 日志 这是否可能 或者 USN 日志是否无法从 VSS 快照访问 my goal 是能够在两个 VSS 快照之间的增量备份中使用 USN 日志 备份的过程是 拍摄 VSS 快照并备份卷 记下每个