Discussion:
Reading a file without modifying its Last Access time.
(too old to reply)
dmitri
2008-05-26 23:00:18 UTC
Permalink
Dear all,

I have been tasked with reading a file without modifying its access
time (this is for a sort of a backup application). I was under the
impression that to do a proper backup, I had to call CreateFile()
using FILE_FLAG_BACKUP_SEMANTICS flag. Then, Last Access time would
not be changed after the file contents were read. My test program is
attached at the end of this post; it does not work. That is, the Last
Access time stubbornly gets updated every time a file is read. I
tried several combinations of arguments to CreateFile() and using both
ReadFile() and BackupRead() calls to read the data, but to no avail.

I was then pointed to documentation about special trick one can do [1]
by calling SetFileTime() with atime set to 0xFFFFFFFF right after the
file is opened. This _does_ work; however, one has to call
CreateFile() with FILE_WRITE_ATTRIBUTES flag -- otherwise,
SetFileTime() call fails. In my case, the files to be read are on a
read-only share, so calling CreateFIle() with any FILE_WRITE_* flag
fails.

Then I decided to investigate whether other backup/copy applications
are able to do what my program could not. I looked at operation of
ntbackup and robocopy. While both worked (Last Access time remained
unchanged) when reading files from a regular directory, neither did
when reading files from a read-only share. Tracing both programs
using StraceNT [2], I discovered that ntbackup in fact relies on the
SetFileTime() trick; when reading the file from a read-only share the
call fails. robocopy uses CopyFileEx() and it also fails to preserve
Last Access time on a read-only share (how it does it reading files
from a regular directory remains a mystery to me).

The question, therefore, is as follows: is what I am trying to achieve
possible? or is it simply not done because of some limitations that
are not explicitly documented? Am I missing something obvious in my
code below?

Thanks in advance,

- Dmitri.

P.S. I am new to Windows programming, so it took a while to
understand that there are _two_ records of Last Access time [3] in
NTFS. I wrote a separate program (not attached) to print the actual
atime of the file as it is reported for the file, not by what's stored
in its directory entry (this is how dir /ta reports it).

1. http://msdn.microsoft.com/en-us/library/ms724933(VS.85).aspx
2. http://www.intellectualheaven.com/default.asp?BH=projects&H=strace.htm
3. http://technet2.microsoft.com/windowsserver/en/library/8cc5891d-bf8e-4164-862d-dac5418c59481033.mspx?mfr=true

/*
* mycat.cpp -- read a text file and print its contents to console.
*
* Purpose: check out this whole FILE_FLAG_BACKUP_SEMANTICS business.
Does
* atime of a file change when we read it?
*/

#include "stdafx.h"
#include "windows.h"

static BOOL
enable_privilege (LPCWSTR lpPrivName)
{
TOKEN_PRIVILEGES Privileges;

HANDLE hToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY|
TOKEN_ADJUST_PRIVILEGES, &hToken)) {
fprintf(stderr, "OpenProcessToken\n");
return FALSE;
}

Privileges.PrivilegeCount = 1;
Privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!LookupPrivilegeValue(NULL, lpPrivName,
&Privileges.Privileges[0].Luid)) {
fprintf(stderr, "LookupPrivilegeValue\n");
return FALSE;
}

BOOL bResult = AdjustTokenPrivileges(hToken, FALSE, &Privileges,
0, NULL, NULL);
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
fprintf(stderr, "The token does not have the specified
privilege. \n");
return FALSE;
}

CloseHandle(hToken);
if (!bResult)
fprintf(stderr, "Cannot get priv\n");

return bResult;
}

#define DIE_IF_ATIME_CHANGES 0

int _tmain(int argc, _TCHAR* argv[])
{
if (!enable_privilege(SE_BACKUP_NAME))
return 1;
if (!enable_privilege(SE_RESTORE_NAME))
return 1;

HANDLE hSrc = CreateFile(argv[1], GENERIC_READ, NULL, NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (INVALID_HANDLE_VALUE == hSrc) {
fprintf(stderr, "Invalid handle value!\n");
return 1;
}

HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (INVALID_HANDLE_VALUE == hConOut) {
fprintf(stderr, "Can't get stdout handle: %d\n",
GetLastError());
return 1;
}

/* Get atime before the read happens: */
FILETIME atime_before_read;
if (!GetFileTime(hSrc, NULL, &atime_before_read, NULL)) {
fprintf(stderr, "error: GetFileTime(): %d\n", GetLastError());
return 1;
}

while (1) {
unsigned char buf[4096];
DWORD nread;

if (!ReadFile(hSrc, buf, sizeof(buf), &nread, 0)) {
fprintf(stderr, "Error reading file: %d\n", GetLastError());
return 1;
}

if (0 == nread)
break; // EOF

/* Compare atime: */
FILETIME new_atime;
if (!GetFileTime(hSrc, NULL, &new_atime, NULL)) {
fprintf(stderr, "error: GetFileTime(): %d\n", GetLastError());
return 1;
}
if (!(new_atime.dwHighDateTime == atime_before_read.dwHighDateTime
&&
new_atime.dwLowDateTime == atime_before_read.dwHighDateTime))
{
fprintf(stderr, "atime changed!\n");
if (DIE_IF_ATIME_CHANGES)
return 1;
}

/* Print buffer to console: */
while (nread > 0) {
DWORD nwritten;
if (!WriteFile(hConOut, buf, nread, &nwritten, 0)) {
fprintf(stderr, "Error writing to console: %d\n", GetLastError());
return 1;
}
nread -= nwritten;
}
}

CloseHandle(hSrc);
CloseHandle(hConOut);

return 0;
}
Kellie Fitton
2008-05-27 00:00:33 UTC
Permalink
Post by dmitri
Dear all,
I have been tasked with reading a file without modifying its access
time (this is for a sort of a backup application).  I was under the
impression that to do a proper backup, I had to call CreateFile()
using FILE_FLAG_BACKUP_SEMANTICS flag.  Then, Last Access time would
not be changed after the file contents were read.  My test program is
attached at the end of this post; it does not work.  That is, the Last
Access time stubbornly gets updated every time a file is read.  I
tried several combinations of arguments to CreateFile() and using both
ReadFile() and BackupRead() calls to read the data, but to no avail.
I was then pointed to documentation about special trick one can do [1]
by calling SetFileTime() with atime set to 0xFFFFFFFF right after the
file is opened.  This _does_ work; however, one has to call
CreateFile() with FILE_WRITE_ATTRIBUTES flag -- otherwise,
SetFileTime() call fails.  In my case, the files to be read are on a
read-only share, so calling CreateFIle() with any FILE_WRITE_* flag
fails.
Then I decided to investigate whether other backup/copy applications
are able to do what my program could not.  I looked at operation of
ntbackup and robocopy.  While both worked (Last Access time remained
unchanged) when reading files from a regular directory, neither did
when reading files from a read-only share.  Tracing both programs
using StraceNT [2], I discovered that ntbackup in fact relies on the
SetFileTime() trick; when reading the file from a read-only share the
call fails.  robocopy uses CopyFileEx() and it also fails to preserve
Last Access time on a read-only share (how it does it reading files
from a regular directory remains a mystery to me).
The question, therefore, is as follows: is what I am trying to achieve
possible? or is it simply not done because of some limitations that
are not explicitly documented?  Am I missing something obvious in my
code below?
Thanks in advance,
  - Dmitri.
Hi,

Here is a viable workaround approach when possible:

- Copy the main file into a safe location

- Perform your normal read/backup function

- Copy or move the main file back into its
- original location

Kellie.
dmitri
2008-05-27 00:04:58 UTC
Permalink
        -  Copy the main file into a safe location
Hi Kellie,

the problem with this approach is that I do not have access to the
file other than via a read-only share. So copying it would result in
its Last Access time being updated, which is what I am trying to
avoid.

- Dmitri.
r***@pen_fact.com
2008-05-27 17:59:08 UTC
Permalink
Post by dmitri
        -  Copy the main file into a safe location
Hi Kellie,
the problem with this approach is that I do not have access to the
file other than via a read-only share. So copying it would result in
its Last Access time being updated, which is what I am trying to
avoid.
Naive and hopeful:
If you copy the file and read the copy, does the access time on the
original get changed?
Post by dmitri
- Dmitri.
-----------------------------------------
To reply to me, remove the underscores (_) from my email address (and please indicate which newsgroup and message).

Robert E. Zaret, eMVP
PenFact, Inc.
20 Park Plaza, Suite 400
Boston, MA 02116
www.penfact.com
Useful reading (be sure to read its disclaimer first):
http://catb.org/~esr/faqs/smart-questions.html
dmitri
2008-05-27 18:08:01 UTC
Permalink
Post by r***@pen_fact.com
If you copy the file and read the copy, does the access time on the
original get changed?
Yes.

- Dmitri.
Scott Seligman
2008-05-27 02:06:14 UTC
Permalink
Post by dmitri
Dear all,
I have been tasked with reading a file without modifying its access
time (this is for a sort of a backup application). I was under the
impression that to do a proper backup, I had to call CreateFile()
using FILE_FLAG_BACKUP_SEMANTICS flag. Then, Last Access time would
not be changed after the file contents were read. My test program is
I may be missing something, but shouldn't a back up application key off
of the last write time? If I play all of my music files, I don't want
my backup app to copy them again just because the access time has been
bumped.
--
--------- Scott Seligman <scott at <firstname> and michelle dot net> ---------
The beginning is the most important part of the work.
-- The Republic by Plato
dmitri
2008-05-27 16:22:56 UTC
Permalink
Post by Scott Seligman
I may be missing something, but shouldn't a back up application key off
of the last write time? If I play all of my music files, I don't want
my backup app to copy them again just because the access time has been
bumped.
That's true, but those are not my requirements.
Sten Westerback (MVP SDK 2005-6 :)
2008-06-10 19:17:57 UTC
Permalink
Post by dmitri
Post by Scott Seligman
I may be missing something, but shouldn't a back up application key off
of the last write time? If I play all of my music files, I don't want
my backup app to copy them again just because the access time has been
bumped.
That's true, but those are not my requirements.
That is not true.. backup apps use either ModifyDate or it's own tracking
metadata. Note however that some video clip formats save something into
file at every view...

I can't see why you would like to not change the AccessDate... it's purpose
is to tell when it was last accessed. ;)
But you really have to, then use FindFirstFile() to save the date, read the
file, open the file and use SetFileTime() to set the access date.

- Sten

Loading...