How do I get *change* file time in Windows?
Asked Answered
M

2

7

I am trying to use FreeNAS CIFS share with Windows (synchronizing files from Windows to FreeNAS) and hit a problem that robocopy.exe thinks that some files need to be copied again every time I run robocopy.exe (/COPY:DAT).

My favorite file management tool, Far Manager, tells me that "Change time" is different:

File times on Windows: On Windows

File times on CIFS share backed by FreeNAS (ZFS): On CIFS share backed by FreeNAS (ZFS)

I am perfectly fine with the fact that "change time" is lost, and I'd be happy to reset change time on the Windows drive, but I can't find how I can do that programmatically.

Python's os.stat(filename) returns st_atime, st_mtime and st_ctime, and, I've tested, this tuple has equal values between Windows and FreeNAS. Presumably "change time" isn't there.

Windows API also gives only 3 numbers (creation, last access and last write) but not "change time": http://msdn.microsoft.com/en-us/library/windows/desktop/ms724320(v=vs.85).aspx

Apparently both robocopy.exe and Far Manager somehow get 4 values from the system. I'd really like to avoid disassembling them, hoping to get an answer here. How do I get and set "change time"?

Any solution will do (Python, C++, WinAPI, external command line tools that can manipulate that, etc).

Malley answered 17/12, 2014 at 20:39 Comment(6)
Please do not tag bomb the site. You should pick one language that you want to work with and ask a question about that in particular. Otherwise, your question is too broad for SO.Legwork
Removed C++. Does it look better now? (If not, give me suggested list of tags). I actually don't care in which language I get solution. From what I researched, there are no solutions for this problem in SO in any language.Malley
If it helps farmanager is open source. You could probably just go and see how they're calculating the "change time" on windows.Trinidadtrinitarian
cocarin: Good point, thanks! This somehow slipped off my attention. Will look at sources.Malley
What's the difference between "last write time" and "change time" anyway?Heartburning
Common convention is that last write is about changing file contents, and last change is about changing metadata (such as attributes). But you should confirm actual semantics for every OS + filesystem combination you're interested in.Malley
M
6

OK, I seem to have figured it out, thanks to cocarin for pointing to Far sources. Far uses NtQueryInformationFile to get times and NtSetInformationFile to set them, and FILE_BASIC_INFORMATION structure contains all 4 times, including change time.

QueryInformationFile docs: http://msdn.microsoft.com/en-us/library/windows/hardware/ff567052(v=vs.85).aspx (ZwQueryInformationFile)

SetInformationFile docs: http://msdn.microsoft.com/en-us/library/windows/hardware/ff567096(v=vs.85).aspx (ZwSetInformationFile)

FILE_BASIC_INFORMATION docs: http://msdn.microsoft.com/en-us/library/windows/hardware/ff545762(v=vs.85).aspx

typedef struct _FILE_BASIC_INFORMATION {
  LARGE_INTEGER CreationTime;
  LARGE_INTEGER LastAccessTime;
  LARGE_INTEGER LastWriteTime;
  LARGE_INTEGER ChangeTime;        // <--- win!
  ULONG         FileAttributes;
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;

Not sure if there are any nice wrappers for Python but this is good enough, I can write my own wrapper.

Malley answered 17/12, 2014 at 21:13 Comment(0)
M
3

There is a better way than the accepted answer, without having to resort to native system Zw* calls on Windows Vista and higher (Server 2008 and higher).

Please take a look at the following sample code I whipped up to show how:

// Get a handle to the file whose change time you want
const WCHAR pathName[]=L"..."; // The name of the file
HANDLE hFile=::CreateFileW(
               pathName, 
               FILE_READ_ATTRIBUTES, // No need for GENERIC_READ
               FILE_SHARE_READ /* | FILE_SHARE_WRITE */,
               NULL, 
               OPEN_EXISTING,  // Fail if file does not exist
               FILE_ATTRIBUTE_NORMAL, 
               NULL);

// If file exists and can be read...
if (hFile!=INVALID_HANDLE_VALUE)
{
  FILE_BASIC_INFO fileBasicInfo={};

  // Get the info we are after
  if (::GetFileInformationByHandleEx(
        hFile,
        FileBasicInfo,          // Info class from FILE_INFO_BY_HANDLE_CLASS enum
        &fileBasicInfo,         // Where to store the info
        sizeof(fileBasicInfo))  // Buffer size
  {
      // Use fileBasicInfo.ChangeTime for the file change time
      ...
  }

  ::CloseHandle(hFile);
}

Here are the corresponding MSDN entry links for your convenience:

Note: Also, take a look at the other information classes that are available with this treasure trove of a function for all your advanced file info needs. For example, the FileStandardInfo class gives you such useful tidbits as the amount of space actually allocated to the file (e.g., for sparse file actual physical space use), the number of links, is a delete pending, is it a directory, etc...

Misplay answered 2/11, 2019 at 21:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.