Improving handling of last access time Updating the adaptor to call the Win Api SetFileTime with the value of -1 for the last access time before reading the content of a file. This will stop Windows from updating the last access time as the file content is read by the adaptor. As a fail over, the adaptor also queries the last access time of a document before reading the content and then check if the time has changed. If it has, then the adaptor will attempt to reset the last access time of the document back to the original.
diff --git a/src/com/google/enterprise/adaptor/fs/FileDelegate.java b/src/com/google/enterprise/adaptor/fs/FileDelegate.java index 1bdd715..a480cb0 100644 --- a/src/com/google/enterprise/adaptor/fs/FileDelegate.java +++ b/src/com/google/enterprise/adaptor/fs/FileDelegate.java
@@ -61,6 +61,13 @@ BasicFileAttributes readBasicAttributes(Path doc) throws IOException; /** + * Gets the lastAccess time for the file or directory. + * + * @param doc the file/folder to set the last accessed time on + */ + FileTime getLastAccessTime(Path doc) throws IOException; + + /** * Sets the lastAccess time for the file or directory. * * @param doc the file/folder to set the last accessed time on
diff --git a/src/com/google/enterprise/adaptor/fs/FsAdaptor.java b/src/com/google/enterprise/adaptor/fs/FsAdaptor.java index faa36f5..609588b 100644 --- a/src/com/google/enterprise/adaptor/fs/FsAdaptor.java +++ b/src/com/google/enterprise/adaptor/fs/FsAdaptor.java
@@ -490,7 +490,7 @@ } final boolean docIsDirectory = attrs.isDirectory(); - final FileTime lastAccessTime = attrs.lastAccessTime(); + final FileTime lastAccessTime = delegate.getLastAccessTime(doc); if (!docIsDirectory) { if (lastAccessTimeFilter.excluded(lastAccessTime)) { @@ -614,13 +614,18 @@ try { input.close(); } finally { - try { - delegate.setLastAccessTime(doc, lastAccessTime); - } catch (IOException e) { - // This failure can be expected. We can have full permissions - // to read but not write/update permissions. - log.log(Level.CONFIG, - "Unable to restore last access time for {0}.", doc); + // Do a follow up check to see if the last access time has changed. + // If the last access time has changed, attempt to reset it. + if (!lastAccessTime.equals(delegate.getLastAccessTime(doc))) { + log.log(Level.FINE, "Restoring last access time for {0}.", doc); + try { + delegate.setLastAccessTime(doc, lastAccessTime); + } catch (IOException e) { + // This failure can be expected. We can have full permissions + // to read but not write/update permissions. + log.log(Level.FINE, + "Unable to restore last access time for {0}.", doc); + } } } }
diff --git a/src/com/google/enterprise/adaptor/fs/NioFileDelegate.java b/src/com/google/enterprise/adaptor/fs/NioFileDelegate.java index 84ab250..ebb737e 100644 --- a/src/com/google/enterprise/adaptor/fs/NioFileDelegate.java +++ b/src/com/google/enterprise/adaptor/fs/NioFileDelegate.java
@@ -64,6 +64,12 @@ } @Override + public FileTime getLastAccessTime(Path doc) throws IOException { + return (FileTime)Files.getAttribute(doc, "lastAccessTime", + LinkOption.NOFOLLOW_LINKS); + } + + @Override public void setLastAccessTime(Path doc, FileTime time) throws IOException { Files.setAttribute(doc, "lastAccessTime", time, LinkOption.NOFOLLOW_LINKS); }
diff --git a/src/com/google/enterprise/adaptor/fs/WindowsFileDelegate.java b/src/com/google/enterprise/adaptor/fs/WindowsFileDelegate.java index ff23659..19bedfa 100644 --- a/src/com/google/enterprise/adaptor/fs/WindowsFileDelegate.java +++ b/src/com/google/enterprise/adaptor/fs/WindowsFileDelegate.java
@@ -43,14 +43,18 @@ import com.sun.jna.ptr.PointerByReference; import com.sun.jna.win32.W32APIOptions; +import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.nio.file.attribute.AclEntry; import java.nio.file.attribute.AclFileAttributeView; +import java.nio.file.attribute.FileTime; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -82,6 +86,29 @@ } @Override + public InputStream newInputStream(Path doc) throws IOException { + return new BufferedInputStream(new WinFileInputStream(doc)); + } + + @Override + public FileTime getLastAccessTime(Path doc) throws IOException { + HANDLE handle = kernel32.CreateFile(doc.toString(), WinNT.GENERIC_READ, + WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE, + new WinBase.SECURITY_ATTRIBUTES(), WinNT.OPEN_EXISTING, + WinNT.FILE_ATTRIBUTE_NORMAL, null); + if (Kernel32.INVALID_HANDLE_VALUE.equals(handle)) { + throw new IOException("Unable to open " + doc + + ". GetLastError: " + kernel32.GetLastError()); + } + + WinBase.FILETIME.ByReference accessTime = + new WinBase.FILETIME.ByReference(); + kernel32.GetFileTime(handle, null, accessTime, null); + kernel32.CloseHandle(handle); + return FileTime.fromMillis(accessTime.toDate().getTime()); + } + + @Override public AclFileAttributeViews getAclViews(Path doc) throws IOException { return aclViews.getAclViews(doc); } @@ -474,4 +501,55 @@ public void destroy() { stopMonitorPath(); } + + private class WinFileInputStream extends InputStream { + private final HANDLE handle; + + public WinFileInputStream(Path path) throws IOException { + handle = kernel32.CreateFile(path.toString(), + WinNT.GENERIC_READ | WinNT.GENERIC_WRITE, + WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE, + new WinBase.SECURITY_ATTRIBUTES(), + WinNT.OPEN_EXISTING, WinNT.FILE_ATTRIBUTE_NORMAL, null); + if (Kernel32.INVALID_HANDLE_VALUE.equals(handle)) { + throw new IOException("Unable to open " + path + + ". GetLastError: " + kernel32.GetLastError()); + } + + // Call SetFileTime with a value of 0xFFFFFFFF for lpLastAccessTime + // to keep Windows from updating the last access time + // when reading the file content. + WinBase.FILETIME ft = new WinBase.FILETIME(); + ft.dwHighDateTime = 0xFFFFFFFF; + ft.dwLowDateTime = 0xFFFFFFFF; + kernel32.SetFileTime(handle, null, ft, null); + } + + @Override + public int read() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public int read(byte[] inBuf, int start, int count) throws IOException { + IntByReference lpNumberOfBytesRead = new IntByReference(0); + boolean result = kernel32.ReadFile(handle, + ByteBuffer.wrap(inBuf, start, count), count, + lpNumberOfBytesRead, null); + if (!result) { + throw new IOException("Unable to read file data. " + + "GetLastError: " + kernel32.GetLastError()); + } + if (lpNumberOfBytesRead.getValue() != 0) { + return lpNumberOfBytesRead.getValue(); + } else { + return -1; + } + } + + @Override + public void close() throws IOException { + kernel32.CloseHandle(handle); + } + } }
diff --git a/test/com/google/enterprise/adaptor/fs/MockFileDelegate.java b/test/com/google/enterprise/adaptor/fs/MockFileDelegate.java index 4764a8a..c8fd6a1 100644 --- a/test/com/google/enterprise/adaptor/fs/MockFileDelegate.java +++ b/test/com/google/enterprise/adaptor/fs/MockFileDelegate.java
@@ -105,6 +105,11 @@ } @Override + public FileTime getLastAccessTime(Path doc) throws IOException { + return readBasicAttributes(doc).lastAccessTime(); + } + + @Override public void setLastAccessTime(Path doc, FileTime time) throws IOException { getFile(doc).setLastAccessTime(time); }