Merge branch 'master' of https://code.google.com/p/plexi.fs
diff --git a/src/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViews.java b/src/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViews.java
index b70e455..f71296c 100644
--- a/src/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViews.java
+++ b/src/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViews.java
@@ -432,7 +432,7 @@
   }
 
   @VisibleForTesting
-  interface Mpr extends StdCallLibrary {
+  public static interface Mpr extends StdCallLibrary {
     Mpr INSTANCE = (Mpr) Native.loadLibrary("Mpr", Mpr.class,
         W32APIOptions.UNICODE_OPTIONS);
 
diff --git a/test/com/google/enterprise/adaptor/fs/UnsupportedNetapi32.java b/test/com/google/enterprise/adaptor/fs/UnsupportedNetapi32.java
new file mode 100644
index 0000000..e6252ee
--- /dev/null
+++ b/test/com/google/enterprise/adaptor/fs/UnsupportedNetapi32.java
@@ -0,0 +1,153 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.enterprise.adaptor.fs;
+
+import com.google.enterprise.adaptor.fs.WinApi.Netapi32Ex;
+
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import com.sun.jna.platform.win32.Netapi32;
+import com.sun.jna.platform.win32.DsGetDC.PDOMAIN_CONTROLLER_INFO;
+import com.sun.jna.platform.win32.Guid.GUID;
+import com.sun.jna.platform.win32.NTSecApi.PLSA_FOREST_TRUST_INFORMATION;
+import com.sun.jna.ptr.IntByReference;
+import com.sun.jna.ptr.PointerByReference;
+import com.sun.jna.win32.StdCallLibrary;
+import com.sun.jna.win32.W32APIOptions;
+
+/**
+ * An implementation of the Netapi32 Interface that throws
+ * UnsupportedOperationException for everything.  Tests may
+ * subclass this and override those methods used by the object
+ * under test.
+ */
+public class UnsupportedNetapi32 implements Netapi32, Netapi32Ex {
+
+  @Override
+  public int NetShareGetInfo(String serverName, String netName, int level,
+      PointerByReference bufptr) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetDfsGetSecurity(String dfsEntryPath, int securityInformation,
+      PointerByReference ppSecurityDescriptor,
+      IntByReference lpcbSecurityDescriptor) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetDfsGetInfo(String dfsEntryPath, String serverName,
+      String shareName, int Level, PointerByReference buffer) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetGetJoinInformation(String lpServer,
+      PointerByReference lpNameBuffer, IntByReference bufferType) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetApiBufferFree(Pointer buffer) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetLocalGroupEnum(String serverName, int level,
+      PointerByReference bufptr, int prefmaxlen,
+      IntByReference entriesRead, IntByReference totalEntries,
+      IntByReference resumeHandle) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetGetDCName(String serverName, String domainName,
+      PointerByReference bufptr) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetGroupEnum(String servername, int level,
+      PointerByReference bufptr, int prefmaxlen, IntByReference entriesRead,
+      IntByReference totalEntries, IntByReference resumeHandle) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetUserEnum(String serverName, int level, int filter,
+      PointerByReference bufptr, int prefmaxlen, IntByReference entriesRead,
+      IntByReference totalEntries, IntByReference resumeHandle) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetUserGetGroups(String serverName, String userName, int level,
+      PointerByReference bufptr, int prefmaxlen,
+      IntByReference entriesRead, IntByReference totalEntries) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetUserGetLocalGroups(String serverName, String userName,
+      int level, int flags, PointerByReference bufptr, int prefmaxlen,
+      IntByReference entriesRead, IntByReference totalEntries) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetUserAdd(String serverName, int level,
+      Structure buf, IntByReference parmError) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetUserDel(String serverName, String userName) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetUserChangePassword(String domainName, String userName,
+      String oldPassword, String newPassword) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int NetUserGetInfo( String serverName, String userName,
+      int level, PointerByReference bufptr ) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int DsGetDcName(String computerName, String domainName,
+      GUID domainGuid, String siteName, int flags,
+      PDOMAIN_CONTROLLER_INFO domainControllerInfo) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int DsGetForestTrustInformation(String serverName,
+      String trustedDomainName, int flags,
+      PLSA_FOREST_TRUST_INFORMATION dorestTrustInfo) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public int DsEnumerateDomainTrusts(String serverName, int flags,
+      PointerByReference domains, IntByReference domainCount) {
+    throw new UnsupportedOperationException();
+  }
+}
diff --git a/test/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViewsTest.java b/test/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViewsTest.java
index 8852150..dddbc93 100644
--- a/test/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViewsTest.java
+++ b/test/com/google/enterprise/adaptor/fs/WindowsAclFileAttributeViewsTest.java
@@ -29,6 +29,7 @@
 
 import com.google.enterprise.adaptor.fs.WinApi.Netapi32Ex;
 import com.google.enterprise.adaptor.fs.WinApi.Shlwapi;
+import com.google.enterprise.adaptor.fs.WindowsAclFileAttributeViews.Mpr;
 
 import org.junit.*;
 import org.junit.rules.ExpectedException;
@@ -41,17 +42,20 @@
 import com.sun.jna.platform.win32.Advapi32;
 import com.sun.jna.platform.win32.Advapi32Util.Account;
 import com.sun.jna.platform.win32.Kernel32;
+import com.sun.jna.platform.win32.LMErr;
 import com.sun.jna.platform.win32.W32Errors;
 import com.sun.jna.platform.win32.Win32Exception;
 import com.sun.jna.platform.win32.WinError;
 import com.sun.jna.platform.win32.WinNT;
 import com.sun.jna.platform.win32.WinNT.SID_NAME_USE;
 import com.sun.jna.ptr.IntByReference;
+import com.sun.jna.ptr.PointerByReference;
 
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.nio.file.attribute.AclEntry;
 import java.nio.file.attribute.AclEntryFlag;
 import java.nio.file.attribute.AclEntryPermission;
@@ -365,6 +369,36 @@
 
   private AclFileAttributeViews getAclViews(WinNT.ACCESS_ACEStructure... aces)
       throws Exception {
+    final byte[] dacl = buildDaclMemory(aces);
+    Kernel32 kernel32 = new UnsupportedKernel32() {
+        @Override
+        public int GetLastError() {
+          // For when GetFileSecurity returns false.
+          return W32Errors.ERROR_INSUFFICIENT_BUFFER;
+        }
+      };
+    Advapi32 advapi32 = new UnsupportedAdvapi32() {
+        @Override
+        public boolean GetFileSecurity(WString lpFileName,
+            int RequestedInformation, Pointer pointer, int nLength,
+            IntByReference lpnLengthNeeded) {
+          if (nLength < dacl.length) {
+            lpnLengthNeeded.setValue(dacl.length);
+            return false;
+          } else {
+            pointer.write(0, dacl, 0, nLength);
+            return true;
+          }
+        }
+      };
+
+    WindowsAclFileAttributeViews wafav =
+        new TestAclFileAttributeViews(advapi32, kernel32, null, null, null);
+    return wafav.getAclViews(newTempFile("test"));
+  }
+
+  private byte[] buildDaclMemory(WinNT.ACCESS_ACEStructure... aces)
+      throws Exception {
     WinNT.ACL acl = new WinNT.ACL();
     WinNT.SECURITY_DESCRIPTOR_RELATIVE securityDescriptor =
         new WinNT.SECURITY_DESCRIPTOR_RELATIVE();
@@ -391,32 +425,7 @@
       ace.getPointer().read(0, buffer, offset, ace.AceSize);
       offset += ace.AceSize;
     }
-
-    Kernel32 kernel32 = new UnsupportedKernel32() {
-        @Override
-        public int GetLastError() {
-          // For when GetFileSecurity returns false.
-          return W32Errors.ERROR_INSUFFICIENT_BUFFER;
-        }
-      };
-    Advapi32 advapi32 = new UnsupportedAdvapi32() {
-        @Override
-        public boolean GetFileSecurity(WString lpFileName,
-            int RequestedInformation, Pointer pointer, int nLength,
-            IntByReference lpnLengthNeeded) {
-          if (nLength < buffer.length) {
-            lpnLengthNeeded.setValue(buffer.length);
-            return false;
-          } else {
-            pointer.write(0, buffer, 0, nLength);
-            return true;
-          }
-        }
-      };
-
-    WindowsAclFileAttributeViews wafav =
-        new TestAclFileAttributeViews(advapi32, kernel32, null, null, null);
-    return wafav.getAclViews(newTempFile("test"));
+    return buffer;
   }
 
   /**
@@ -468,6 +477,237 @@
     wafav.getAclViews(newTempFile("test"));
   }
 
+  @Test
+  public void testGetShareAclViewLocalDrive() throws Exception {
+    Shlwapi shlwapi = new Shlwapi() {
+        @Override
+        public boolean PathIsNetworkPath(String pszPath) {
+          return false;
+        }
+        @Override
+        public boolean PathIsUNC(String pszPath) {
+          return false;
+        }
+      };
+    WindowsAclFileAttributeViews wafav =
+        new TestAclFileAttributeViews(null, null, null, null, shlwapi);
+    AclFileAttributeView aclView = wafav.getShareAclView(newTempDir("test"));
+    assertNotNull(aclView);
+    assertTrue(aclView.getAcl().isEmpty());
+  }
+
+  @Test
+  public void testGetShareAclViewUncPath() throws Exception {
+    TestHelper.assumeOsIsWindows();
+    Shlwapi shlwapi = new Shlwapi() {
+        @Override
+        public boolean PathIsNetworkPath(String pszPath) {
+          return false;
+        }
+        @Override
+        public boolean PathIsUNC(String pszPath) {
+          return true;
+        }
+      };
+    Path share = Paths.get("\\\\server\\share");
+    testGetShareAclView(share, shlwapi, null);
+  }
+
+  @Test
+  public void testGetShareAclViewBadUncPath() throws Exception {
+    TestHelper.assumeOsIsWindows();
+    Shlwapi shlwapi = new Shlwapi() {
+        @Override
+        public boolean PathIsNetworkPath(String pszPath) {
+          return false;
+        }
+        @Override
+        public boolean PathIsUNC(String pszPath) {
+          return true;
+        }
+      };
+    thrown.expect(IOException.class);
+    testGetShareAclView(newTempDir("test"), shlwapi, null);
+  }
+
+
+  @Test
+  public void testGetShareAclViewNetworkPath() throws Exception {
+    TestHelper.assumeOsIsWindows();
+    Shlwapi shlwapi = new Shlwapi() {
+        @Override
+        public boolean PathIsNetworkPath(String pszPath) {
+          return true;
+        }
+        @Override
+        public boolean PathIsUNC(String pszPath) {
+          return false;
+        }
+      };
+    Mpr mpr = new Mpr() {
+        @Override
+        public int WNetGetUniversalNameW(String lpLocalPath, int dwInfoLevel,
+            Pointer lpBuffer, IntByReference lpBufferSize) {
+          Mpr.UNIVERSAL_NAME_INFO info = new Mpr.UNIVERSAL_NAME_INFO();
+          info.lpUniversalName = "\\\\server\\share";
+          info.write();
+          // Force a reallocation, even though we do not need it.
+          if (lpBufferSize.getValue() != info.size()) {
+            lpBufferSize.setValue(info.size());
+            return WinNT.ERROR_MORE_DATA;
+          }
+          byte[] buf = new byte[info.size()];
+          info.getPointer().read(0, buf, 0, buf.length);
+          lpBuffer.write(0, buf, 0, buf.length);
+          return WinNT.NO_ERROR;
+        }
+      };
+    testGetShareAclView(newTempDir("test"), shlwapi, mpr);
+  }
+
+  @Test
+  public void testGetShareAclViewNetworkPathFailure() throws Exception {
+    TestHelper.assumeOsIsWindows();
+    Shlwapi shlwapi = new Shlwapi() {
+        @Override
+        public boolean PathIsNetworkPath(String pszPath) {
+          return true;
+        }
+        @Override
+        public boolean PathIsUNC(String pszPath) {
+          return false;
+        }
+      };
+    Mpr mpr = new Mpr() {
+        @Override
+        public int WNetGetUniversalNameW(String lpLocalPath, int dwInfoLevel,
+            Pointer lpBuffer, IntByReference lpBufferSize) {
+          return WinNT.ERROR_INVALID_PARAMETER;
+        }
+      };
+    thrown.expect(IOException.class);
+    testGetShareAclView(newTempDir("test"), shlwapi, mpr);
+  }
+
+  private void testGetShareAclView(Path share, Shlwapi shlwapi, Mpr mpr)
+      throws Exception {
+    AclFileAttributeView expectedAcl = new AclView(
+        group("Everyone").type(ALLOW).perms(GENERIC_READ)
+        .flags(FILE_INHERIT, DIRECTORY_INHERIT),
+        group("Administrators").type(ALLOW).perms(GENERIC_ALL)
+        .flags(FILE_INHERIT, DIRECTORY_INHERIT));
+    byte[] dacl = buildDaclMemory(
+        new AceBuilder()
+        .setSid(AccountSid.group("Everyone", null))
+        .setPerms(WinNT.GENERIC_READ)
+        .setFlags(WinNT.OBJECT_INHERIT_ACE, WinNT.CONTAINER_INHERIT_ACE)
+        .build(),
+        new AceBuilder()
+        .setSid(AccountSid.group("Administrators", null))
+        .setPerms(WinNT.GENERIC_ALL)
+        .setFlags(WinNT.OBJECT_INHERIT_ACE, WinNT.CONTAINER_INHERIT_ACE)
+        .build());
+
+    Memory memory = new Memory(dacl.length);
+    memory.write(0, dacl, 0, dacl.length);
+    final Netapi32Ex.SHARE_INFO_502 info = new Netapi32Ex.SHARE_INFO_502();
+    info.shi502_security_descriptor = memory;
+    info.write();
+
+    Netapi32Ex netapi = new UnsupportedNetapi32() {
+        @Override
+        public int NetShareGetInfo(String serverName, String netName, int level,
+            PointerByReference bufptr) {
+          bufptr.setValue(info.getPointer());
+          return WinError.ERROR_SUCCESS;
+        }
+        @Override
+        public int NetApiBufferFree(Pointer buf) {
+          return WinError.ERROR_SUCCESS;
+        }
+      };
+
+    WindowsAclFileAttributeViews wafav =
+        new TestAclFileAttributeViews(null, null, mpr, netapi, shlwapi);
+
+    AclFileAttributeView aclView = wafav.getShareAclView(share);
+    assertNotNull(aclView);
+    assertEquals(expectedAcl.getAcl(), aclView.getAcl());
+  }
+
+  @Test
+  public void testGetShareAclViewNetSareGetInfoFailureAccessDenied()
+      throws Exception {
+    TestHelper.assumeOsIsWindows();
+    testGetShareAclViewNetSareGetInfoFailure(WinError.ERROR_ACCESS_DENIED);
+  }
+
+  @Test
+  public void testGetShareAclViewNetSareGetInfoFailureInvalidLevel()
+      throws Exception {
+    TestHelper.assumeOsIsWindows();
+    testGetShareAclViewNetSareGetInfoFailure(WinError.ERROR_INVALID_LEVEL);
+  }
+
+  @Test
+  public void testGetShareAclViewNetSareGetInfoFailureInvalidParameter()
+      throws Exception {
+    TestHelper.assumeOsIsWindows();
+    testGetShareAclViewNetSareGetInfoFailure(WinError.ERROR_INVALID_PARAMETER);
+  }
+
+  @Test
+  public void testGetShareAclViewNetSareGetInfoFailureInsufficientMemory()
+      throws Exception {
+    TestHelper.assumeOsIsWindows();
+    testGetShareAclViewNetSareGetInfoFailure(WinError.ERROR_NOT_ENOUGH_MEMORY);
+  }
+
+  @Test
+  public void testGetShareAclViewNetSareGetInfoFailureNetNameNotFound()
+      throws Exception {
+    TestHelper.assumeOsIsWindows();
+    testGetShareAclViewNetSareGetInfoFailure(LMErr.NERR_NetNameNotFound);
+  }
+
+  @Test
+  public void testGetShareAclViewNetSareGetInfoFailureOther()
+      throws Exception {
+    TestHelper.assumeOsIsWindows();
+    testGetShareAclViewNetSareGetInfoFailure(WinError.ERROR_NOT_READY);
+  }
+
+  private void testGetShareAclViewNetSareGetInfoFailure(final int error)
+      throws Exception {
+    Shlwapi shlwapi = new Shlwapi() {
+        @Override
+        public boolean PathIsNetworkPath(String pszPath) {
+          return false;
+        }
+        @Override
+        public boolean PathIsUNC(String pszPath) {
+          return true;
+        }
+      };
+    Netapi32Ex netapi = new UnsupportedNetapi32() {
+        @Override
+        public int NetShareGetInfo(String serverName, String netName, int level,
+            PointerByReference bufptr) {
+          return error;
+        }
+        @Override
+        public int NetApiBufferFree(Pointer buf) {
+          return WinError.ERROR_SUCCESS;
+        }
+      };
+    WindowsAclFileAttributeViews wafav =
+        new TestAclFileAttributeViews(null, null, null, netapi, shlwapi);
+    Path share = Paths.get("\\\\server\\share");
+
+    thrown.expect(IOException.class);
+    AclFileAttributeView aclView = wafav.getShareAclView(share);
+  }
+
   static class AceBuilder {
     private byte type;
     private byte flags;