Merge branch 'master' of https://code.google.com/p/plexi.fs
diff --git a/lib/plexi b/lib/plexi
index ef7a357..7bb4377 160000
--- a/lib/plexi
+++ b/lib/plexi
@@ -1 +1 @@
-Subproject commit ef7a357a5c3171f0a9e4fac117e6446b4820cf2a
+Subproject commit 7bb4377649cc5426db2ce26e883c695f2935a040
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 efd8fae..609588b 100644
--- a/src/com/google/enterprise/adaptor/fs/FsAdaptor.java
+++ b/src/com/google/enterprise/adaptor/fs/FsAdaptor.java
@@ -34,12 +34,14 @@
import com.google.enterprise.adaptor.Response;
import com.google.enterprise.adaptor.StartupException;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.file.AccessDeniedException;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.attribute.AclFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
@@ -206,7 +208,7 @@
@Override
public void initConfig(Config config) {
- config.addKey(CONFIG_SRC, "");
+ config.addKey(CONFIG_SRC, null);
config.addKey(CONFIG_SUPPORTED_ACCOUNTS,
"BUILTIN\\Administrators,Everyone,BUILTIN\\Users,BUILTIN\\Guest,"
+ "NT AUTHORITY\\INTERACTIVE,NT AUTHORITY\\Authenticated Users");
@@ -454,14 +456,6 @@
DocId id = req.getDocId();
Path doc = delegate.getPath(id.getUniqueId());
- if (!isSupportedPath(doc)) {
- log.log(Level.WARNING, "The path {0} is not a supported file type.", doc);
- resp.respondNotFound();
- return;
- }
-
- final boolean docIsDirectory = delegate.isDirectory(doc);
-
if (!id.equals(delegate.newDocId(doc))) {
log.log(Level.WARNING,
"The {0} is not a valid id generated by the adaptor.", id);
@@ -475,18 +469,38 @@
}
// Populate the document metadata.
- BasicFileAttributes attrs = delegate.readBasicAttributes(doc);
- final FileTime lastAccessTime = attrs.lastAccessTime();
+ BasicFileAttributes attrs;
+ try {
+ attrs = delegate.readBasicAttributes(doc);
+ } catch (FileNotFoundException e) {
+ log.log(Level.INFO, "Not found: {0}", doc);
+ resp.respondNotFound();
+ return;
+ } catch (NoSuchFileException e) {
+ log.log(Level.INFO, "Not found: {0}", doc);
+ resp.respondNotFound();
+ return;
+ }
+
+ if (!isFileOrFolder(doc)) {
+ log.log(Level.INFO, "The path {0} is not a regular file or directory.",
+ doc);
+ resp.respondNotFound();
+ return;
+ }
+
+ final boolean docIsDirectory = attrs.isDirectory();
+ final FileTime lastAccessTime = delegate.getLastAccessTime(doc);
if (!docIsDirectory) {
if (lastAccessTimeFilter.excluded(lastAccessTime)) {
- log.log(Level.WARNING, "Skipping {0} because it was last accessed {1}.",
+ log.log(Level.FINE, "Skipping {0} because it was last accessed {1}.",
new Object[] {doc, lastAccessTime.toString().substring(0, 10)});
resp.respondNotFound();
return;
}
if (lastModifiedTimeFilter.excluded(attrs.lastModifiedTime())) {
- log.log(Level.WARNING, "Skipping {0} because it was last modified {1}.",
+ log.log(Level.FINE, "Skipping {0} because it was last modified {1}.",
new Object[] {doc,
attrs.lastModifiedTime().toString().substring(0, 10)});
resp.respondNotFound();
@@ -587,7 +601,7 @@
HtmlResponseWriter writer = createHtmlResponseWriter(resp);
writer.start(id, getFileName(doc));
for (Path file : delegate.newDirectoryStream(doc)) {
- if (isSupportedPath(file)) {
+ if (isFileOrFolder(file)) {
writer.addLink(delegate.newDocId(file), getFileName(file));
}
}
@@ -600,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);
+ }
}
}
}
@@ -631,8 +650,12 @@
return name.isEmpty() ? file.getRoot().toString() : name;
}
+ /**
+ * Returns true if the path is a regular file or a folder;
+ * false if the path is a link, a special file, or doesn't exist.
+ */
@VisibleForTesting
- boolean isSupportedPath(Path p) throws IOException {
+ boolean isFileOrFolder(Path p) throws IOException {
return delegate.isRegularFile(p) || delegate.isDirectory(p);
}
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/WindowsAclFileAttributeViews.java b/src/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViews.java
index f71296c..90c3b06 100644
--- a/src/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViews.java
+++ b/src/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViews.java
@@ -288,10 +288,9 @@
Netapi32Ex.SHARE_INFO_502 info =
new Netapi32Ex.SHARE_INFO_502(buf.getValue());
- netapi32.NetApiBufferFree(buf.getValue());
-
WinNT.SECURITY_DESCRIPTOR_RELATIVE sdr =
new WinNT.SECURITY_DESCRIPTOR_RELATIVE(info.shi502_security_descriptor);
+ netapi32.NetApiBufferFree(buf.getValue());
WinNT.ACL dacl = sdr.getDiscretionaryACL();
ImmutableList.Builder<AclEntry> builder = ImmutableList.builder();
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/FsAdaptorTest.java b/test/com/google/enterprise/adaptor/fs/FsAdaptorTest.java
index c756af8..6a5b25f 100644
--- a/test/com/google/enterprise/adaptor/fs/FsAdaptorTest.java
+++ b/test/com/google/enterprise/adaptor/fs/FsAdaptorTest.java
@@ -202,13 +202,13 @@
}
@Test
- public void testIsSupportedPath() throws Exception {
+ public void testIsFileOrFolder() throws Exception {
root.addChildren(new MockFile("foo"), new MockFile("bar", true),
new MockFile("link").setIsRegularFile(false));
- assertTrue(adaptor.isSupportedPath(rootPath));
- assertTrue(adaptor.isSupportedPath(getPath("foo")));
- assertTrue(adaptor.isSupportedPath(getPath("bar")));
- assertFalse(adaptor.isSupportedPath(getPath("link")));
+ assertTrue(adaptor.isFileOrFolder(rootPath));
+ assertTrue(adaptor.isFileOrFolder(getPath("foo")));
+ assertTrue(adaptor.isFileOrFolder(getPath("bar")));
+ assertFalse(adaptor.isFileOrFolder(getPath("link")));
}
@Test
@@ -321,13 +321,6 @@
assertTrue(response.notFound);
}
- /*
- * This test was invalidated when the check for isSupportedPath()
- * was moved above the check for bad DocIds in the beginning of
- * FsAdaptor.getDocumentContent(). This test still passes, but
- * the notFound response is coming from the earlier check for
- * isSupportedPath(), not from the bad DocId.
- */
@Test
public void testGetDocContentBadDocId() throws Exception {
root.addChildren(new MockFile("badfile"));
@@ -339,6 +332,15 @@
}
@Test
+ public void testGetDocContentFileNotFound() throws Exception {
+ adaptor.init(context);
+ MockResponse response = new MockResponse();
+ // The requested DocId is missing the root component of the path.
+ adaptor.getDocContent(new MockRequest(getDocId("non-existent")), response);
+ assertTrue(response.notFound);
+ }
+
+ @Test
public void testGetDocContentHiddenFile() throws Exception {
root.addChildren(new MockFile("hidden.txt").setIsHidden(true));
adaptor.init(context);
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);
}
diff --git a/test/com/google/enterprise/adaptor/fs/NioFileDelegateTest.java b/test/com/google/enterprise/adaptor/fs/NioFileDelegateTest.java
index 761fdd9..ee79e96 100644
--- a/test/com/google/enterprise/adaptor/fs/NioFileDelegateTest.java
+++ b/test/com/google/enterprise/adaptor/fs/NioFileDelegateTest.java
@@ -30,6 +30,7 @@
import java.io.InputStreamReader;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.AclFileAttributeView;
@@ -43,6 +44,9 @@
private FileDelegate delegate = new TestNioFileDelegate();
@Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
public TemporaryFolder temp = new TemporaryFolder();
private Path newTempDir(String name) throws IOException {
@@ -131,6 +135,13 @@
}
@Test
+ public void testReadBasicAttributesFileNotFound() throws Exception {
+ Path file = Paths.get(temp.getRoot().toString(), "notFound");
+ thrown.expect(NoSuchFileException.class);
+ BasicFileAttributes attrs = delegate.readBasicAttributes(file);
+ }
+
+ @Test
public void testProbeContentType() throws Exception {
TestHelper.assumeOsIsNotMac();
String content = "<html><title>Foo</title><body>Bar</body></html>";