Platform SDK: Files and I/O |
The two operations for the DeviceIoControl function that return change journal records, FSCTL_READ_USN_JOURNAL and FSCTL_ENUM_USN_DATA, return almost the same data. Both return zero or more change journal records, each in a USN_RECORD structure. Use FSCTL_ENUM_USN_DATA when you want a listing (enumeration) of all change journal records between two USNs. Use FSCTL_READ_USN_JOURNAL when you want to be more selective, such as selecting specific reasons for changes or returning when a file is closed.
USN_RECORD contains the name of the file to which the record in question applies. The file name varies in length, so USN_RECORD is a variable-length structure. Its first member, RecordLength, is the length of the structure (including the file name) in bytes. Returned USN_RECORD structures are aligned on 64-bit boundaries.
Both of these operations return only the subset of change journal records between the boundaries specified. These operations also return the next record number to be retrieved, so that you can continue reading records from the end boundary forward.
To walk the buffer of change journal records returned by either operation from the first entry onward, you must round up the length of the buffer to match the alignment of the USN_RECORD structures, as shown in the following example. Use a function such as the following to calculate the location of the next USN_RECORD structure in memory. This function accepts a pointer to a USN_RECORD structure. It does not test to see if a valid record is present at that address.
// Note that the following is an inline function. __inline PUSN_RECORD nextentry(const PUSN_RECORD input) { register DWORD output; // Get the base address of the current record. (PUSN_RECORD) output = input; // Add the size of the record (structure + file name after the end // of the structure). output += input->RecordLength; // Round up the record size to match the 64-bit alignment, if the // size is not already a multiple of 8. Perform a bitwise AND // operation here instead of division because it is much faster than // division. However, the bitwise AND operation only works because // the divisor 8 is a power of 2. if(output & 8-1) { // Round down to nearest multiple of 8. output &= -8; // Then add 8. output += 8; } return((PUSN_RECORD) output); }
To simplify matters, when you read records from a change journal declare your input buffer on a 64-bit boundary. That way, the first record always occurs at the boundary equal to the value of the DeviceIoControl lpOutBuffer parameter plus 8.
The size in bytes of any record specified by a USN_RECORD structure is at most ((
MaximumComponentLength - 1)
* Width)
+ Size where MaximumComponentLength is the maximum length, in characters, of the record's file name, Width is the size of a wide character, and Size is the size of the structure. To obtain this maximum length, call the GetVolumeInformation function and examine the value pointed to by the lpMaximumComponentLength parameter. You subtract one from MaximumComponentLength to account for the fact that the definition of USN_RECORD includes one character of the file name.
Thus, in C the largest possible record size is:
(MaximumComponentLength * sizeof(WCHAR) + sizeof(USN_RECORD) - sizeof(WCHAR))
When working with the FileName member of USN_RECORD, do not count on the change journal's file name containing a trailing '\0' delimiter. To determine the length of the file name, use the FileNameLength member.