Move UnixFileSystem to lib.unix, WindowsFileSystem to lib.windows

--
PiperOrigin-RevId: 148749485
MOS_MIGRATED_REVID=148749485
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index a2915e2..d6663ea 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -73,12 +73,35 @@
         "//conditions:default": ["//src/main/native:libunix.so"],
     }),
     deps = [
+        ":concurrent",
         ":os_util",
+        ":preconditions",
         ":shell",
+        ":vfs",
         "//third_party:guava",
     ],
 )
 
+java_library(
+    name = "windows",
+    srcs = glob([
+        "windows/*.java",
+    ]),
+    visibility = ["//visibility:public"],
+    deps = [
+        ":base-util",
+        ":clock",
+        ":concurrent",
+        ":os_util",
+        ":preconditions",
+        ":shell",
+        ":vfs",
+        "//src/main/java/com/google/devtools/common/options",
+        "//third_party:guava",
+        "//third_party:jsr305",
+    ],
+)
+
 # Library of concurrency utilities.
 java_library(
     name = "concurrent",
@@ -126,7 +149,6 @@
     srcs = glob([
         "profiler/*.java",
         "vfs/*.java",
-        "windows/*.java",
     ]),
     visibility = ["//visibility:public"],
     deps = [
@@ -136,7 +158,6 @@
         ":os_util",
         ":preconditions",
         ":shell",
-        ":unix",
         "//src/main/java/com/google/devtools/common/options",
         "//third_party:guava",
         "//third_party:jsr305",
@@ -238,6 +259,17 @@
 )
 
 java_library(
+    name = "process_util",
+    srcs = ["util/ProcessUtils.java"],
+    deps = [
+        ":concurrent",
+        ":os_util",
+        ":unix",
+        ":windows",
+    ],
+)
+
+java_library(
     name = "util",
     srcs = glob(
         ["util/*.java"],
@@ -248,6 +280,7 @@
             "util/JavaClock.java",
             "util/OS.java",
             "util/Preconditions.java",
+            "util/ProcessUtils.java",
             "util/StringCanonicalizer.java",
             "util/StringTrie.java",
             "util/VarInt.java",
@@ -1020,12 +1053,14 @@
         ":flags",
         ":io",
         ":packages-internal",
+        ":process_util",
         ":profiler-output",
         ":shared-base-rules",
         ":shell",
         ":unix",
         ":util",
         ":vfs",
+        ":windows",
         "//src/main/java/com/google/devtools/build/docgen:docgen_javalib",
         "//src/main/java/com/google/devtools/build/lib/actions",
         "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto",
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
index 2612048..efe5a21 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeRuntime.java
@@ -56,6 +56,7 @@
 import com.google.devtools.build.lib.shell.JavaSubprocessFactory;
 import com.google.devtools.build.lib.shell.Subprocess;
 import com.google.devtools.build.lib.shell.SubprocessBuilder;
+import com.google.devtools.build.lib.unix.UnixFileSystem;
 import com.google.devtools.build.lib.util.AbruptExitException;
 import com.google.devtools.build.lib.util.BlazeClock;
 import com.google.devtools.build.lib.util.Clock;
@@ -63,16 +64,15 @@
 import com.google.devtools.build.lib.util.ExitCode;
 import com.google.devtools.build.lib.util.LoggingUtil;
 import com.google.devtools.build.lib.util.OS;
-import com.google.devtools.build.lib.util.OsUtils;
 import com.google.devtools.build.lib.util.Preconditions;
+import com.google.devtools.build.lib.util.ProcessUtils;
 import com.google.devtools.build.lib.util.ThreadUtils;
 import com.google.devtools.build.lib.util.io.OutErr;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
-import com.google.devtools.build.lib.vfs.UnixFileSystem;
-import com.google.devtools.build.lib.vfs.WindowsFileSystem;
+import com.google.devtools.build.lib.windows.WindowsFileSystem;
 import com.google.devtools.build.lib.windows.WindowsSubprocessFactory;
 import com.google.devtools.common.options.Option;
 import com.google.devtools.common.options.OptionPriority;
@@ -934,7 +934,7 @@
     PathFragment installBase = startupOptions.installBase;
     PathFragment outputBase = startupOptions.outputBase;
 
-    OsUtils.maybeForceJNI(installBase);  // Must be before first use of JNI.
+    maybeForceJNI(installBase);  // Must be before first use of JNI.
 
     // From the point of view of the Java program --install_base and --output_base
     // are mandatory options, despite the comment in their declarations.
@@ -1019,6 +1019,32 @@
   }
 
   /**
+   * Loads JNI libraries, if necessary under the current platform.
+   */
+  public static void maybeForceJNI(PathFragment installBase) {
+    if (jniLibsAvailable()) {
+      forceJNI(installBase);
+    }
+  }
+
+  private static boolean jniLibsAvailable() {
+    return !"0".equals(System.getProperty("io.bazel.EnableJni"));
+  }
+
+  // Force JNI linking at a moment when we have 'installBase' handy, and print
+  // an informative error if it fails.
+  private static void forceJNI(PathFragment installBase) {
+    try {
+      ProcessUtils.getpid(); // force JNI initialization
+    } catch (UnsatisfiedLinkError t) {
+      System.err.println("JNI initialization failed: " + t.getMessage() + ".  "
+          + "Possibly your installation has been corrupted; "
+          + "if this problem persists, try 'rm -fr " + installBase + "'.");
+      throw t;
+    }
+  }
+
+  /**
    * Returns a logger that crashes as soon as it's written to, since tests should not cause events
    * that would be logged.
    */
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoItem.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoItem.java
index 579c3f4..08bd03a 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoItem.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/InfoItem.java
@@ -36,7 +36,7 @@
 import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher;
 import com.google.devtools.build.lib.runtime.CommandEnvironment;
 import com.google.devtools.build.lib.util.AbruptExitException;
-import com.google.devtools.build.lib.util.OsUtils;
+import com.google.devtools.build.lib.util.ProcessUtils;
 import com.google.devtools.build.lib.util.StringUtilities;
 import com.google.devtools.common.options.OptionsProvider;
 import java.io.ByteArrayOutputStream;
@@ -334,7 +334,7 @@
     @Override
     public byte[] get(Supplier<BuildConfiguration> configurationSupplier, CommandEnvironment env)
         throws AbruptExitException {
-      return print(OsUtils.getpid());
+      return print(ProcessUtils.getpid());
     }
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD
index 593aafa..82010f3 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD
@@ -14,6 +14,7 @@
         "//src/main/java/com/google/devtools/build/lib:events",
         "//src/main/java/com/google/devtools/build/lib:io",
         "//src/main/java/com/google/devtools/build/lib:packages-internal",
+        "//src/main/java/com/google/devtools/build/lib:process_util",
         "//src/main/java/com/google/devtools/build/lib:runtime",
         "//src/main/java/com/google/devtools/build/lib:shell",
         "//src/main/java/com/google/devtools/build/lib:unix",
diff --git a/src/main/java/com/google/devtools/build/lib/standalone/BUILD b/src/main/java/com/google/devtools/build/lib/standalone/BUILD
index de704cd..0b2171e 100644
--- a/src/main/java/com/google/devtools/build/lib/standalone/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/standalone/BUILD
@@ -12,6 +12,7 @@
         "//src/main/java/com/google/devtools/build/lib:build-base",
         "//src/main/java/com/google/devtools/build/lib:io",
         "//src/main/java/com/google/devtools/build/lib:packages-internal",
+        "//src/main/java/com/google/devtools/build/lib:process_util",
         "//src/main/java/com/google/devtools/build/lib:runtime",
         "//src/main/java/com/google/devtools/build/lib:shell",
         "//src/main/java/com/google/devtools/build/lib:util",
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java b/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java
similarity index 97%
rename from src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java
rename to src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java
index 53db44c..c21848a 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/unix/UnixFileSystem.java
@@ -11,18 +11,21 @@
 // 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.devtools.build.lib.vfs;
+package com.google.devtools.build.lib.unix;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
 import com.google.devtools.build.lib.profiler.Profiler;
 import com.google.devtools.build.lib.profiler.ProfilerTask;
-import com.google.devtools.build.lib.unix.ErrnoFileStatus;
-import com.google.devtools.build.lib.unix.NativePosixFiles;
 import com.google.devtools.build.lib.unix.NativePosixFiles.Dirents;
 import com.google.devtools.build.lib.unix.NativePosixFiles.ReadTypes;
 import com.google.devtools.build.lib.util.Preconditions;
+import com.google.devtools.build.lib.vfs.AbstractFileSystemWithCustomStat;
+import com.google.devtools.build.lib.vfs.Dirent;
+import com.google.devtools.build.lib.vfs.FileStatus;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
diff --git a/src/main/java/com/google/devtools/build/lib/util/OsUtils.java b/src/main/java/com/google/devtools/build/lib/util/OsUtils.java
index 85c8920..a25e64b 100644
--- a/src/main/java/com/google/devtools/build/lib/util/OsUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/util/OsUtils.java
@@ -14,8 +14,6 @@
 
 package com.google.devtools.build.lib.util;
 
-import com.google.devtools.build.lib.vfs.PathFragment;
-
 /**
  * Operating system-specific utilities.
  */
@@ -34,40 +32,4 @@
   public static String executableExtension() {
     return EXECUTABLE_EXTENSION;
   }
-
-  /**
-   * Loads JNI libraries, if necessary under the current platform.
-   */
-  public static void maybeForceJNI(PathFragment installBase) {
-    if (jniLibsAvailable()) {
-      forceJNI(installBase);
-    }
-  }
-
-  private static boolean jniLibsAvailable() {
-    return !"0".equals(System.getProperty("io.bazel.EnableJni"));
-  }
-
-  // Force JNI linking at a moment when we have 'installBase' handy, and print
-  // an informative error if it fails.
-  private static void forceJNI(PathFragment installBase) {
-    try {
-      ProcessUtils.getpid(); // force JNI initialization
-    } catch (UnsatisfiedLinkError t) {
-      System.err.println("JNI initialization failed: " + t.getMessage() + ".  "
-          + "Possibly your installation has been corrupted; "
-          + "if this problem persists, try 'rm -fr " + installBase + "'.");
-      throw t;
-    }
-  }
-
-  /**
-   * Returns the PID of the current process, or -1 if not available.
-   */
-  public static int getpid() {
-    if (jniLibsAvailable()) {
-      return ProcessUtils.getpid();
-    }
-    return -1;
-  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java b/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
index fd5b711..9cc3dc7 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
@@ -976,11 +976,6 @@
    * <p>Its results are unspecified and MUST NOT be interpreted programmatically.
    */
   public static void dump(FileSystem fs, final PrintStream out) {
-    if (!(fs instanceof UnixFileSystem)) {
-      out.println("  Not a UnixFileSystem.");
-      return;
-    }
-
     // Unfortunately there's no "letrec" for anonymous functions so we have to
     // (a) name the function, (b) put it in a box and (c) use List not array
     // because of the generic type.  *sigh*.
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java
index 221659f..e409969 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/JavaIoFileSystem.java
@@ -19,7 +19,6 @@
 import com.google.devtools.build.lib.profiler.ProfilerTask;
 import com.google.devtools.build.lib.util.Clock;
 import com.google.devtools.build.lib.util.JavaClock;
-
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
similarity index 95%
rename from src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java
rename to src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
index 4167d81..ab1ee20 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/windows/WindowsFileSystem.java
@@ -11,16 +11,19 @@
 // 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.devtools.build.lib.vfs;
+package com.google.devtools.build.lib.windows;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
 import com.google.devtools.build.lib.util.Preconditions;
+import com.google.devtools.build.lib.vfs.FileStatus;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
+import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.Path.PathFactory;
-import com.google.devtools.build.lib.vfs.Path.PathFactory.TranslatedPath;
-import com.google.devtools.build.lib.windows.WindowsFileOperations;
+import com.google.devtools.build.lib.vfs.PathFragment;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -200,7 +203,9 @@
     }
   }
 
-  private static final class WindowsPath extends Path {
+  /** A windows-specific subclass of Path. */
+  @VisibleForTesting
+  protected static final class WindowsPath extends Path {
 
     // The drive letter is '\0' if and only if this Path is the filesystem root "/".
     private char driveLetter;
@@ -223,8 +228,9 @@
         if (isTopLevelDirectory()) {
           result.append(driveLetter).append(':').append(PathFragment.SEPARATOR_CHAR);
         } else {
-          getParentDirectory().buildPathString(result);
-          if (!getParentDirectory().isTopLevelDirectory()) {
+          WindowsPath parent = (WindowsPath) getParentDirectory();
+          parent.buildPathString(result);
+          if (!parent.isTopLevelDirectory()) {
             result.append(PathFragment.SEPARATOR_CHAR);
           }
           result.append(getBaseName());
@@ -260,7 +266,7 @@
         segments = Arrays.copyOfRange(segments, 1, segments.length);
       }
 
-      return new PathFragment(driveLetter, true, segments);
+      return PathFragment.create(driveLetter, true, segments);
     }
 
     @Override
@@ -295,6 +301,12 @@
         }
       }
     }
+
+    @VisibleForTesting
+    @Override
+    protected void applyToChildren(Predicate<Path> function) {
+      super.applyToChildren(function);
+    }
   }
 
   @VisibleForTesting
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 56958d9..b1efe10 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -6,16 +6,17 @@
 CROSS_PLATFORM_WINDOWS_TESTS = [
     "util/DependencySetWindowsTest.java",
     "vfs/PathFragmentWindowsTest.java",
-    "vfs/PathWindowsTest.java",
+    "windows/PathWindowsTest.java",
 ]
 
 # Tests for Windows-specific functionality that run on Windows.
 WINDOWS_ON_WINDOWS_TESTS = glob(
     ["windows/*.java"],
-    exclude = ["windows/MockSubprocess.java"],
-) + [
-    "vfs/WindowsFileSystemTest.java",
-]
+    exclude = [
+        "windows/MockSubprocess.java",
+        "windows/PathWindowsTest.java",
+    ],
+)
 
 # All Windows-specific tests. Use this to exclude Windows tests from globs.
 ALL_WINDOWS_TESTS = CROSS_PLATFORM_WINDOWS_TESTS + WINDOWS_ON_WINDOWS_TESTS
@@ -98,8 +99,10 @@
         "//src/main/java/com/google/devtools/build/lib:io",
         "//src/main/java/com/google/devtools/build/lib:packages",
         "//src/main/java/com/google/devtools/build/lib:shell",
+        "//src/main/java/com/google/devtools/build/lib:unix",
         "//src/main/java/com/google/devtools/build/lib:util",
         "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib:windows",
         "//third_party:guava",
         "//third_party:guava-testlib",
         "//third_party:junit4",
@@ -191,6 +194,7 @@
         "//src/main/java/com/google/devtools/build/lib:inmemoryfs",
         "//src/main/java/com/google/devtools/build/lib:util",
         "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib:windows",
         "//src/main/java/com/google/devtools/common/options",
         "//third_party:guava",
         "//third_party:guava-testlib",
@@ -206,6 +210,7 @@
     ],
     deps = [
         "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib:windows",
         "//third_party:guava",
         "//third_party:guava-testlib",
         "//third_party:junit4",
@@ -220,13 +225,19 @@
     data = [
         ":MockSubprocess_deploy.jar",
     ] + JNI_LIB,
+    jvm_flags = [
+        "-Dbazel.windows_unix_root=C:/fake/msys",
+    ],
     test_class = "com.google.devtools.build.lib.AllTests",
     deps = [
         ":test_runner",
         ":testutil",
         ":windows_testutil",
+        "//src/main/java/com/google/devtools/build/lib:clock",
+        "//src/main/java/com/google/devtools/build/lib:inmemoryfs",
         "//src/main/java/com/google/devtools/build/lib:os_util",
         "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib:windows",
         "//third_party:guava",
         "//third_party:junit4",
         "//third_party:truth",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD b/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD
index b24f8d1..2b281f0 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD
@@ -20,6 +20,7 @@
         "//src/main/java/com/google/devtools/build/lib:build-base",
         "//src/main/java/com/google/devtools/build/lib:packages-internal",
         "//src/main/java/com/google/devtools/build/lib:runtime",
+        "//src/main/java/com/google/devtools/build/lib:unix",
         "//src/main/java/com/google/devtools/build/lib:util",
         "//src/main/java/com/google/devtools/build/lib:vfs",
         "//src/main/java/com/google/devtools/build/lib/actions",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/repository/CompressedTarFunctionTest.java b/src/test/java/com/google/devtools/build/lib/rules/repository/CompressedTarFunctionTest.java
index 2002c62..6eac63e 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/repository/CompressedTarFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/repository/CompressedTarFunctionTest.java
@@ -22,11 +22,11 @@
 import com.google.devtools.build.lib.testutil.BlazeTestUtils;
 import com.google.devtools.build.lib.testutil.TestConstants;
 import com.google.devtools.build.lib.testutil.TestUtils;
+import com.google.devtools.build.lib.unix.UnixFileSystem;
 import com.google.devtools.build.lib.util.OS;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
 import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.UnixFileSystem;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
index 3294d09..52cf1ba 100644
--- a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
+++ b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
@@ -63,7 +63,7 @@
   public static final String TEST_RULE_MODULE =
         "com.google.devtools.build.lib.bazel.rules.BazelRulesModule";
   public static final String TEST_REAL_UNIX_FILE_SYSTEM =
-      "com.google.devtools.build.lib.vfs.UnixFileSystem";
+      "com.google.devtools.build.lib.unix.UnixFileSystem";
 
   public static final ImmutableList<String> IGNORED_MESSAGE_PREFIXES = ImmutableList.<String>of();
 
diff --git a/src/test/java/com/google/devtools/build/lib/unix/NativePosixFilesTest.java b/src/test/java/com/google/devtools/build/lib/unix/NativePosixFilesTest.java
index c2e0330..ea5039d 100644
--- a/src/test/java/com/google/devtools/build/lib/unix/NativePosixFilesTest.java
+++ b/src/test/java/com/google/devtools/build/lib/unix/NativePosixFilesTest.java
@@ -23,7 +23,6 @@
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.UnixFileSystem;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/unix/UnixFileSystemTest.java
similarity index 84%
rename from src/test/java/com/google/devtools/build/lib/vfs/UnixFileSystemTest.java
rename to src/test/java/com/google/devtools/build/lib/unix/UnixFileSystemTest.java
index 46b200b..567b2f3 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnixFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/unix/UnixFileSystemTest.java
@@ -11,24 +11,24 @@
 // 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.devtools.build.lib.vfs;
+package com.google.devtools.build.lib.unix;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import com.google.devtools.build.lib.unix.NativePosixFiles;
-
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.SymlinkAwareFileSystemTest;
+import com.google.devtools.build.lib.vfs.Symlinks;
+import java.io.IOException;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.io.IOException;
-
-/**
- * Tests for the {@link UnixFileSystem} class.
- */
+/** Tests for the {@link com.google.devtools.build.lib.unix.UnixFileSystem} class. */
 @RunWith(JUnit4.class)
 public class UnixFileSystemTest extends SymlinkAwareFileSystemTest {
 
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathEqualityTest.java b/src/test/java/com/google/devtools/build/lib/unix/UnixPathEqualityTest.java
similarity index 96%
rename from src/test/java/com/google/devtools/build/lib/vfs/UnixPathEqualityTest.java
rename to src/test/java/com/google/devtools/build/lib/unix/UnixPathEqualityTest.java
index fc4b253..bf8091a 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathEqualityTest.java
+++ b/src/test/java/com/google/devtools/build/lib/unix/UnixPathEqualityTest.java
@@ -11,7 +11,7 @@
 // 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.devtools.build.lib.vfs;
+package com.google.devtools.build.lib.unix;
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -20,7 +20,8 @@
 import static org.junit.Assert.fail;
 
 import com.google.common.testing.EqualsTester;
-
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.Path;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java b/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java
index 15e32e0c..c0099d2 100644
--- a/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/util/DependencySetWindowsTest.java
@@ -19,14 +19,12 @@
 import com.google.devtools.build.lib.testutil.Scratch;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.WindowsFileSystem;
-
+import com.google.devtools.build.lib.windows.WindowsFileSystem;
+import java.util.Set;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.Set;
-
 @RunWith(JUnit4.class)
 public class DependencySetWindowsTest {
 
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/NativePathTest.java
similarity index 70%
rename from src/test/java/com/google/devtools/build/lib/vfs/UnixPathTest.java
rename to src/test/java/com/google/devtools/build/lib/vfs/NativePathTest.java
index e2317bf..15a1842 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/NativePathTest.java
@@ -25,7 +25,7 @@
 import com.google.devtools.build.lib.testutil.TestUtils;
 import com.google.devtools.build.lib.vfs.util.FileSystems;
 import java.io.File;
-import java.io.FileWriter;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -38,49 +38,47 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-/**
- * Tests for {@link Path}.
- */
+/** Tests for {@link Path} in combination with the native file system for the current platform. */
 @RunWith(JUnit4.class)
-public class UnixPathTest {
+public class NativePathTest {
 
-  private FileSystem unixFs;
+  private FileSystem fs;
   private File aDirectory;
   private File aFile;
   private File anotherFile;
   private File tmpDir;
 
-  protected FileSystem getUnixFileSystem() {
+  protected FileSystem getNativeFileSystem() {
     return FileSystems.getNativeFileSystem();
   }
 
   @Before
   public final void createFiles() throws Exception  {
-    unixFs = getUnixFileSystem();
+    fs = getNativeFileSystem();
     tmpDir = new File(TestUtils.tmpDir(), "tmpDir");
     tmpDir.mkdirs();
     aDirectory = new File(tmpDir, "a_directory");
     aDirectory.mkdirs();
     aFile = new File(tmpDir, "a_file");
-    new FileWriter(aFile).close();
+    new FileOutputStream(aFile).close();
     anotherFile = new File(aDirectory, "another_file.txt");
-    new FileWriter(anotherFile).close();
+    new FileOutputStream(anotherFile).close();
   }
 
   @Test
   public void testExists() {
-    assertTrue(unixFs.getPath(aDirectory.getPath()).exists());
-    assertTrue(unixFs.getPath(aFile.getPath()).exists());
-    assertFalse(unixFs.getPath("/does/not/exist").exists());
+    assertTrue(fs.getPath(aDirectory.getPath()).exists());
+    assertTrue(fs.getPath(aFile.getPath()).exists());
+    assertFalse(fs.getPath("/does/not/exist").exists());
   }
 
   @Test
   public void testDirectoryEntriesForDirectory() throws IOException {
     Collection<Path> entries =
-        unixFs.getPath(tmpDir.getPath()).getDirectoryEntries();
+        fs.getPath(tmpDir.getPath()).getDirectoryEntries();
     List<Path> expectedEntries = Arrays.asList(
-      unixFs.getPath(tmpDir.getPath() + "/a_file"),
-      unixFs.getPath(tmpDir.getPath() + "/a_directory"));
+      fs.getPath(tmpDir.getPath() + "/a_file"),
+      fs.getPath(tmpDir.getPath() + "/a_directory"));
 
     assertEquals(new HashSet<Object>(expectedEntries),
         new HashSet<Object>(entries));
@@ -89,7 +87,7 @@
   @Test
   public void testDirectoryEntriesForFileThrowsException() {
     try {
-      unixFs.getPath(aFile.getPath()).getDirectoryEntries();
+      fs.getPath(aFile.getPath()).getDirectoryEntries();
       fail("No exception thrown.");
     } catch (IOException x) {
       // The expected result.
@@ -98,42 +96,42 @@
 
   @Test
   public void testIsFileIsTrueForFile() {
-    assertTrue(unixFs.getPath(aFile.getPath()).isFile());
+    assertTrue(fs.getPath(aFile.getPath()).isFile());
   }
 
   @Test
   public void testIsFileIsFalseForDirectory() {
-    assertFalse(unixFs.getPath(aDirectory.getPath()).isFile());
+    assertFalse(fs.getPath(aDirectory.getPath()).isFile());
   }
 
   @Test
   public void testBaseName() {
-    assertEquals("base", unixFs.getPath("/foo/base").getBaseName());
+    assertEquals("base", fs.getPath("/foo/base").getBaseName());
   }
 
   @Test
   public void testBaseNameRunsAfterDotDotInterpretation() {
-    assertEquals("base", unixFs.getPath("/base/foo/..").getBaseName());
+    assertEquals("base", fs.getPath("/base/foo/..").getBaseName());
   }
 
   @Test
   public void testParentOfRootIsRoot() {
-    assertEquals(unixFs.getPath("/"), unixFs.getPath("/.."));
-    assertEquals(unixFs.getPath("/"), unixFs.getPath("/../../../../../.."));
-    assertEquals(unixFs.getPath("/foo"), unixFs.getPath("/../../../foo"));
+    assertEquals(fs.getPath("/"), fs.getPath("/.."));
+    assertEquals(fs.getPath("/"), fs.getPath("/../../../../../.."));
+    assertEquals(fs.getPath("/foo"), fs.getPath("/../../../foo"));
   }
 
   @Test
   public void testIsDirectory() {
-    assertTrue(unixFs.getPath(aDirectory.getPath()).isDirectory());
-    assertFalse(unixFs.getPath(aFile.getPath()).isDirectory());
-    assertFalse(unixFs.getPath("/does/not/exist").isDirectory());
+    assertTrue(fs.getPath(aDirectory.getPath()).isDirectory());
+    assertFalse(fs.getPath(aFile.getPath()).isDirectory());
+    assertFalse(fs.getPath("/does/not/exist").isDirectory());
   }
 
   @Test
   public void testListNonExistingDirectoryThrowsException() {
     try {
-      unixFs.getPath("/does/not/exist").getDirectoryEntries();
+      fs.getPath("/does/not/exist").getDirectoryEntries();
       fail("No exception thrown.");
     } catch (IOException ex) {
       // success!
@@ -152,22 +150,22 @@
 
   @Test
   public void testGlob() throws Exception {
-    Collection<Path> textFiles = UnixGlob.forPath(unixFs.getPath(tmpDir.getPath()))
+    Collection<Path> textFiles = UnixGlob.forPath(fs.getPath(tmpDir.getPath()))
         .addPattern("*/*.txt")
         .globInterruptible();
     assertThat(textFiles).hasSize(1);
     Path onlyFile = textFiles.iterator().next();
-    assertEquals(unixFs.getPath(anotherFile.getPath()), onlyFile);
+    assertEquals(fs.getPath(anotherFile.getPath()), onlyFile);
 
     Collection<Path> onlyFiles =
-        UnixGlob.forPath(unixFs.getPath(tmpDir.getPath()))
+        UnixGlob.forPath(fs.getPath(tmpDir.getPath()))
         .addPattern("*")
         .setExcludeDirectories(true)
         .globInterruptible();
     assertPathSet(onlyFiles, aFile.getPath());
 
     Collection<Path> directoriesToo =
-        UnixGlob.forPath(unixFs.getPath(tmpDir.getPath()))
+        UnixGlob.forPath(fs.getPath(tmpDir.getPath()))
         .addPattern("*")
         .setExcludeDirectories(false)
         .globInterruptible();
@@ -176,16 +174,16 @@
 
   @Test
   public void testGetRelative() {
-    Path relative = unixFs.getPath("/foo").getChild("bar");
-    Path expected = unixFs.getPath("/foo/bar");
+    Path relative = fs.getPath("/foo").getChild("bar");
+    Path expected = fs.getPath("/foo/bar");
     assertEquals(expected, relative);
   }
 
   @Test
   public void testEqualsAndHash() {
-    Path path = unixFs.getPath("/foo/bar");
-    Path equalPath = unixFs.getPath("/foo/bar");
-    Path differentPath = unixFs.getPath("/foo/bar/baz");
+    Path path = fs.getPath("/foo/bar");
+    Path equalPath = fs.getPath("/foo/bar");
+    Path differentPath = fs.getPath("/foo/bar/baz");
     Object differentType = new Object();
 
     new EqualsTester().addEqualityGroup(path, equalPath).testEquals();
@@ -199,7 +197,7 @@
     for (int i = 0; i < 256; i++) {
       allLatin1Chars[i] = (char) i;
     }
-    Path path = unixFs.getPath(aFile.getPath());
+    Path path = fs.getPath(aFile.getPath());
     String latin1String = new String(allLatin1Chars);
     FileSystemUtils.writeContentAsLatin1(path, latin1String);
     String fileContent = new String(FileSystemUtils.readContentAsLatin1(path));
@@ -207,9 +205,9 @@
   }
 
   /**
-   * Verify that the encoding implemented by
-   * {@link FileSystemUtils#writeContentAsLatin1(Path, String)}
-   * really is 8859-1 (latin1).
+   * Verify that the encoding implemented by {@link
+   * com.google.devtools.build.lib.vfs.FileSystemUtils#writeContentAsLatin1(Path, String)} really is
+   * 8859-1 (latin1).
    */
   @Test
   public void testVerifyLatin1() throws IOException {
@@ -217,7 +215,7 @@
     for( int i = 0; i < 256; i++) {
       allLatin1Chars[i] = (char)i;
     }
-    Path path = unixFs.getPath(aFile.getPath());
+    Path path = fs.getPath(aFile.getPath());
     String latin1String = new String(allLatin1Chars);
     FileSystemUtils.writeContentAsLatin1(path, latin1String);
     byte[] bytes = FileSystemUtils.readContent(path);
@@ -228,7 +226,7 @@
   public void testBytesReadAndWrite() throws IOException {
     byte[] bytes = new byte[] { (byte) 0xdeadbeef, (byte) 0xdeadbeef>>8,
                                 (byte) 0xdeadbeef>>16, (byte) 0xdeadbeef>>24 };
-    Path path = unixFs.getPath(aFile.getPath());
+    Path path = fs.getPath(aFile.getPath());
     FileSystemUtils.writeContent(path, bytes);
     byte[] content = FileSystemUtils.readContent(path);
     assertEquals(bytes.length, content.length);
@@ -239,7 +237,7 @@
 
   @Test
   public void testInputOutputStreams() throws IOException {
-    Path path = unixFs.getPath(aFile.getPath());
+    Path path = fs.getPath(aFile.getPath());
     OutputStream out = path.getOutputStream();
     for (int i = 0; i < 256; i++) {
       out.write(i);
@@ -254,20 +252,8 @@
   }
 
   @Test
-  public void testAbsolutePathRoot() {
-    assertEquals("/", new Path(null).toString());
-  }
-
-  @Test
-  public void testAbsolutePath() {
-    Path segment = new Path(null, "bar.txt",
-      new Path(null, "foo", new Path(null)));
-    assertEquals("/foo/bar.txt", segment.toString());
-  }
-
-  @Test
   public void testDerivedSegmentEquality() {
-    Path absoluteSegment = unixFs.getRootDirectory();
+    Path absoluteSegment = fs.getRootDirectory();
 
     Path derivedNode = absoluteSegment.getChild("derivedSegment");
     Path otherDerivedNode = absoluteSegment.getChild("derivedSegment");
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathGetParentTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathGetParentTest.java
similarity index 87%
rename from src/test/java/com/google/devtools/build/lib/vfs/UnixPathGetParentTest.java
rename to src/test/java/com/google/devtools/build/lib/vfs/PathGetParentTest.java
index d4719f6..5e6224c 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathGetParentTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/PathGetParentTest.java
@@ -18,28 +18,26 @@
 
 import com.google.devtools.build.lib.testutil.TestUtils;
 import com.google.devtools.build.lib.vfs.util.FileSystems;
-
+import java.io.IOException;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.io.IOException;
-
 /**
- * A test for {@link Path} in the context of {@link UnixFileSystem}.
+ * A test for {@link Path}.
  */
 @RunWith(JUnit4.class)
-public class UnixPathGetParentTest {
+public class PathGetParentTest {
 
-  private FileSystem unixFs;
+  private FileSystem fs;
   private Path testRoot;
 
   @Before
   public final void createTestRoot() throws Exception  {
-    unixFs = FileSystems.getNativeFileSystem();
-    testRoot = unixFs.getPath(TestUtils.tmpDir()).getRelative("UnixPathGetParentTest");
+    fs = FileSystems.getNativeFileSystem();
+    testRoot = fs.getPath(TestUtils.tmpDir()).getRelative("UnixPathGetParentTest");
     FileSystemUtils.createDirectoryAndParents(testRoot);
   }
 
@@ -49,7 +47,7 @@
   }
 
   private Path getParent(String path) {
-    return unixFs.getPath(path).getParentDirectory();
+    return fs.getPath(path).getParentDirectory();
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathTest.java
index 482ef8a..1976f05 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/PathTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/PathTest.java
@@ -25,12 +25,6 @@
 import com.google.common.testing.GcFinalization;
 import com.google.devtools.build.lib.util.BlazeClock;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.ObjectInputStream;
@@ -38,6 +32,10 @@
 import java.lang.ref.WeakReference;
 import java.util.Collections;
 import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 /**
  * A test for {@link Path}.
@@ -304,6 +302,18 @@
     }
   }
 
+  @Test
+  public void testAbsolutePathRoot() {
+    assertEquals("/", new Path(null).toString());
+  }
+
+  @Test
+  public void testAbsolutePath() {
+    Path segment = new Path(null, "bar.txt",
+      new Path(null, "foo", new Path(null)));
+    assertEquals("/foo/bar.txt", segment.toString());
+  }
+
   private void assertAsFragmentWorks(String expected) {
     assertEquals(new PathFragment(expected), filesystem.getPath(expected).asFragment());
   }
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java
index a860c9a..50329d6 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/UnionFileSystemTest.java
@@ -25,17 +25,16 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.unix.UnixFileSystem;
 import com.google.devtools.build.lib.util.BlazeClock;
 import com.google.devtools.build.lib.util.Clock;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 /**
  * Tests for the UnionFileSystem, both of generic FileSystem functionality
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/util/FileSystems.java b/src/test/java/com/google/devtools/build/lib/vfs/util/FileSystems.java
index 1da3198..f7e6d6c 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/util/FileSystems.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/util/FileSystems.java
@@ -16,16 +16,15 @@
 import com.google.common.base.Verify;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
 import com.google.devtools.build.lib.testutil.TestConstants;
+import com.google.devtools.build.lib.unix.UnixFileSystem;
 import com.google.devtools.build.lib.util.OS;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.UnionFileSystem;
-import com.google.devtools.build.lib.vfs.UnixFileSystem;
-import com.google.devtools.build.lib.vfs.WindowsFileSystem;
 import com.google.devtools.build.lib.vfs.ZipFileSystem;
-
+import com.google.devtools.build.lib.windows.WindowsFileSystem;
 import java.io.IOException;
 import java.util.Map;
 
@@ -58,7 +57,8 @@
       if (defaultNativeFileSystem == null) {
         try {
           defaultNativeFileSystem = (FileSystem)
-              Class.forName(TestConstants.TEST_REAL_UNIX_FILE_SYSTEM).newInstance();
+              Class.forName(TestConstants.TEST_REAL_UNIX_FILE_SYSTEM)
+                  .getDeclaredConstructor().newInstance();
         } catch (Exception e) {
           throw new IllegalStateException(e);
         }
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathWindowsTest.java b/src/test/java/com/google/devtools/build/lib/windows/PathWindowsTest.java
similarity index 76%
rename from src/test/java/com/google/devtools/build/lib/vfs/PathWindowsTest.java
rename to src/test/java/com/google/devtools/build/lib/windows/PathWindowsTest.java
index 9ef6bc7..388c3fd 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/PathWindowsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/windows/PathWindowsTest.java
@@ -11,18 +11,21 @@
 // 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.devtools.build.lib.vfs;
+package com.google.devtools.build.lib.windows;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.devtools.build.lib.vfs.WindowsFileSystem.SHORT_NAME_MATCHER;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.devtools.build.lib.util.BlazeClock;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.Path.PathFactory;
+import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
+import com.google.devtools.build.lib.windows.WindowsFileSystem.WindowsPath;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -51,7 +54,7 @@
   }
 
   private FileSystem filesystem;
-  private Path root;
+  private WindowsPath root;
   private final MockShortPathResolver shortPathResolver = new MockShortPathResolver();
 
   @Before
@@ -63,7 +66,7 @@
             return WindowsFileSystem.getPathFactoryForTesting(shortPathResolver);
           }
         };
-    root = filesystem.getRootDirectory().getRelative("C:/");
+    root = (WindowsPath) filesystem.getRootDirectory().getRelative("C:/");
     root.createDirectory();
   }
 
@@ -163,7 +166,7 @@
   public void testChildRegistrationWithTranslatedPaths() {
     // Ensure the Path to "/usr" (actually "C:/fake/msys/usr") is created, path parents/children
     // properly registered.
-    Path usrPath = root.getRelative("/usr");
+    WindowsPath usrPath = (WindowsPath) root.getRelative("/usr");
     root.getRelative("dummy_path");
 
     // Assert that "usr" is not registered as a child of "/".
@@ -180,7 +183,7 @@
 
     // Assert that "usr" is registered as a child of "C:/fake/msys/".
     children.clear();
-    root.getRelative("C:/fake/msys")
+    ((WindowsPath) root.getRelative("C:/fake/msys"))
         .applyToChildren(
             new Predicate<Path>() {
               @Override
@@ -195,48 +198,6 @@
   }
 
   @Test
-  public void testShortNameMatcher() {
-    assertThat(SHORT_NAME_MATCHER.apply("abc")).isFalse(); // no ~ in the name
-    assertThat(SHORT_NAME_MATCHER.apply("abc~")).isFalse(); // no number after the ~
-    assertThat(SHORT_NAME_MATCHER.apply("~abc")).isFalse(); // no ~ followed by number
-    assertThat(SHORT_NAME_MATCHER.apply("too_long_path")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("too_long_path~1")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("abcd~1234")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("h~1")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("h~12")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("h~12.")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("h~12.a")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("h~12.abc")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("h~123456")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~1")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.a")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.abc")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hello~1.abcd")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.abcd")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hello~12")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hello~12.")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hello~12.a")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hello~12.abc")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("hello~12.abcd")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~12")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.a")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.ab")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("~h~1")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~1.")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~1.a")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~1.abc")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~1.abcd")).isFalse(); // too long for 8dot3
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.a")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.abc")).isTrue();
-    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.abcd")).isFalse(); // too long for 8dot3
-  }
-
-  @Test
   public void testResolvesShortenedPaths() {
     shortPathResolver.resolutions.put("d:/progra~1", "program files");
     shortPathResolver.resolutions.put("d:/program files/micros~1", "microsoft something");
@@ -279,7 +240,7 @@
           }
         };
 
-    Path msRoot = root.getRelative("d:/progra~1/micros~1");
+    WindowsPath msRoot = (WindowsPath) root.getRelative("d:/progra~1/micros~1");
     assertThat(msRoot.getPathString()).isEqualTo("D:/program files/microsoft something");
     msRoot.applyToChildren(collector);
     // The path string has an upper-case drive letter because that's how path printing works.
@@ -287,7 +248,8 @@
 
     // Assert that the non-resolvable path was not cached.
     children.clear();
-    msRoot.getRelative("foo").applyToChildren(collector);
+    WindowsPath foo = (WindowsPath) msRoot.getRelative("foo");
+    foo.applyToChildren(collector);
     assertThat(children).containsExactly("D:/program files/microsoft something/foo/~bar_hello");
 
     // Pretend that a path we already failed to resolve once came into existence.
@@ -309,10 +271,10 @@
 
     // Assert that this time we cached the previously non-existent path.
     children.clear();
-    msRoot.getRelative("foo").applyToChildren(collector);
+    foo.applyToChildren(collector);
     // The path strings have upper-case drive letters because that's how path printing works.
     children.clear();
-    msRoot.getRelative("foo").applyToChildren(collector);
+    foo.applyToChildren(collector);
     assertThat(children)
         .containsExactly(
             "D:/program files/microsoft something/foo/~bar_hello",
diff --git a/src/test/java/com/google/devtools/build/lib/windows/WindowsFileOperationsTest.java b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileOperationsTest.java
index 773daee..1fd4223 100644
--- a/src/test/java/com/google/devtools/build/lib/windows/WindowsFileOperationsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileOperationsTest.java
@@ -20,7 +20,6 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.testutil.TestSpec;
 import com.google.devtools.build.lib.util.OS;
-import com.google.devtools.build.lib.vfs.WindowsFileSystem;
 import com.google.devtools.build.lib.windows.util.WindowsTestUtil;
 import java.io.File;
 import java.io.IOException;
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/WindowsFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
similarity index 79%
rename from src/test/java/com/google/devtools/build/lib/vfs/WindowsFileSystemTest.java
rename to src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
index ea08b22..75a5497 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/WindowsFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/windows/WindowsFileSystemTest.java
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.devtools.build.lib.vfs;
+package com.google.devtools.build.lib.windows;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.windows.WindowsFileSystem.SHORT_NAME_MATCHER;
 import static org.junit.Assert.assertSame;
 
 import com.google.common.base.Function;
@@ -24,7 +25,10 @@
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.testutil.TestSpec;
 import com.google.devtools.build.lib.util.OS;
-import com.google.devtools.build.lib.windows.WindowsFileOperations;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Symlinks;
+import com.google.devtools.build.lib.windows.WindowsFileSystem.WindowsPath;
 import com.google.devtools.build.lib.windows.util.WindowsTestUtil;
 import java.io.File;
 import java.io.IOException;
@@ -65,6 +69,48 @@
   }
 
   @Test
+  public void testShortNameMatcher() {
+    assertThat(SHORT_NAME_MATCHER.apply("abc")).isFalse(); // no ~ in the name
+    assertThat(SHORT_NAME_MATCHER.apply("abc~")).isFalse(); // no number after the ~
+    assertThat(SHORT_NAME_MATCHER.apply("~abc")).isFalse(); // no ~ followed by number
+    assertThat(SHORT_NAME_MATCHER.apply("too_long_path")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("too_long_path~1")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("abcd~1234")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("h~1")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("h~12")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("h~12.")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("h~12.a")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("h~12.abc")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("h~123456")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~1")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.a")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.abc")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hello~1.abcd")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~1.abcd")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hello~12")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hello~12.")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hello~12.a")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hello~12.abc")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("hello~12.abcd")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~12")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.a")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("hellow~12.ab")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("~h~1")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~1.")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~1.a")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~1.abc")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~1.abcd")).isFalse(); // too long for 8dot3
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.a")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.abc")).isTrue();
+    assertThat(SHORT_NAME_MATCHER.apply("~h~12~1.abcd")).isFalse(); // too long for 8dot3
+  }
+
+  @Test
   public void testCanWorkWithJunctionSymlinks() throws Exception {
     testUtil.scratchFile("dir\\hello.txt", "hello");
     testUtil.scratchDir("non_existent");
@@ -76,26 +122,26 @@
     Path nonExistentPath = testUtil.createVfsPath(fs, "non_existent");
 
     // Test junction creation.
-    assertThat(fs.exists(juncPath, /* followSymlinks */ false)).isTrue();
-    assertThat(fs.exists(dirPath, /* followSymlinks */ false)).isTrue();
-    assertThat(fs.exists(juncBadPath, /* followSymlinks */ false)).isTrue();
-    assertThat(fs.exists(nonExistentPath, /* followSymlinks */ false)).isTrue();
+    assertThat(juncPath.exists(Symlinks.NOFOLLOW)).isTrue();
+    assertThat(dirPath.exists(Symlinks.NOFOLLOW)).isTrue();
+    assertThat(juncBadPath.exists(Symlinks.NOFOLLOW)).isTrue();
+    assertThat(nonExistentPath.exists(Symlinks.NOFOLLOW)).isTrue();
 
     // Test recognizing and dereferencing a directory junction.
-    assertThat(fs.isSymbolicLink(juncPath)).isTrue();
-    assertThat(fs.isDirectory(juncPath, /* followSymlinks */ true)).isTrue();
-    assertThat(fs.isDirectory(juncPath, /* followSymlinks */ false)).isFalse();
-    assertThat(fs.getDirectoryEntries(juncPath))
+    assertThat(juncPath.isSymbolicLink()).isTrue();
+    assertThat(juncPath.isDirectory(Symlinks.FOLLOW)).isTrue();
+    assertThat(juncPath.isDirectory(Symlinks.NOFOLLOW)).isFalse();
+    assertThat(juncPath.getDirectoryEntries())
         .containsExactly(testUtil.createVfsPath(fs, "junc\\hello.txt"));
 
     // Test deleting a directory junction.
-    assertThat(fs.delete(juncPath)).isTrue();
-    assertThat(fs.exists(juncPath, /* followSymlinks */ false)).isFalse();
+    assertThat(juncPath.delete()).isTrue();
+    assertThat(juncPath.exists(Symlinks.NOFOLLOW)).isFalse();
 
     // Test recognizing a dangling directory junction.
-    assertThat(fs.delete(nonExistentPath)).isTrue();
-    assertThat(fs.exists(nonExistentPath, /* followSymlinks */ false)).isFalse();
-    assertThat(fs.exists(juncBadPath, /* followSymlinks */ false)).isTrue();
+    assertThat(nonExistentPath.delete()).isTrue();
+    assertThat(nonExistentPath.exists(Symlinks.NOFOLLOW)).isFalse();
+    assertThat(juncBadPath.exists(Symlinks.NOFOLLOW)).isTrue();
     // TODO(bazel-team): fix https://github.com/bazelbuild/bazel/issues/1690 and uncomment the
     // assertion below.
     //assertThat(fs.isSymbolicLink(juncBadPath)).isTrue();
@@ -103,8 +149,8 @@
     assertThat(fs.isDirectory(juncBadPath, /* followSymlinks */ false)).isFalse();
 
     // Test deleting a dangling junction.
-    assertThat(fs.delete(juncBadPath)).isTrue();
-    assertThat(fs.exists(juncBadPath, /* followSymlinks */ false)).isFalse();
+    assertThat(juncBadPath.delete()).isTrue();
+    assertThat(juncBadPath.exists(Symlinks.NOFOLLOW)).isFalse();
   }
 
   @Test
@@ -254,7 +300,7 @@
     String longPrefix = "unresolvable.shortpath/foo/";
     String longPath = longPrefix + "will.exist/bar/hello.txt";
     testUtil.scratchDir(longPrefix);
-    final Path foo = fs.getPath(scratchRoot).getRelative(longPrefix);
+    final WindowsPath foo = (WindowsPath) fs.getPath(scratchRoot).getRelative(longPrefix);
 
     // Assert that we can create an unresolvable path.
     Path p = fs.getPath(scratchRoot).getRelative(shortPath);
diff --git a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/BUILD b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/BUILD
index 705f601..ee318dc 100644
--- a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/BUILD
+++ b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/BUILD
@@ -10,6 +10,7 @@
         "//src/main/java/com/google/devtools/build/lib:os_util",
         "//src/main/java/com/google/devtools/build/lib:packages",
         "//src/main/java/com/google/devtools/build/lib:runtime",
+        "//src/main/java/com/google/devtools/build/lib:unix",
         "//src/main/java/com/google/devtools/build/lib:vfs",
         "//src/main/java/com/google/devtools/common/options",
         "//src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven",
diff --git a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspace.java b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspace.java
index 8cea85b..1700dc1 100644
--- a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspace.java
+++ b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspace.java
@@ -19,11 +19,11 @@
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.StoredEventHandler;
 import com.google.devtools.build.lib.packages.Package;
+import com.google.devtools.build.lib.unix.UnixFileSystem;
 import com.google.devtools.build.lib.util.OS;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
 import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.UnixFileSystem;
 import com.google.devtools.build.workspace.maven.DefaultModelResolver;
 import com.google.devtools.build.workspace.maven.Resolver;
 import com.google.devtools.common.options.OptionsParser;
diff --git a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/BUILD b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/BUILD
index fcd8264..9972928 100644
--- a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/BUILD
+++ b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/BUILD
@@ -16,8 +16,10 @@
         "//src/main/java/com/google/devtools/build/lib:io",
         "//src/main/java/com/google/devtools/build/lib:os_util",
         "//src/main/java/com/google/devtools/build/lib:packages-internal",
+        "//src/main/java/com/google/devtools/build/lib:process_util",
         "//src/main/java/com/google/devtools/build/lib:runtime",
         "//src/main/java/com/google/devtools/build/lib:shell",
+        "//src/main/java/com/google/devtools/build/lib:unix",
         "//src/main/java/com/google/devtools/build/lib:util",
         "//src/main/java/com/google/devtools/build/lib:vfs",
         "//src/main/java/com/google/devtools/build/lib/remote",
diff --git a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/RemoteWorker.java b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/RemoteWorker.java
index 5325b8f..cca971a 100644
--- a/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/RemoteWorker.java
+++ b/src/tools/remote_worker/src/main/java/com/google/devtools/build/remote/RemoteWorker.java
@@ -33,13 +33,13 @@
 import com.google.devtools.build.lib.shell.AbnormalTerminationException;
 import com.google.devtools.build.lib.shell.Command;
 import com.google.devtools.build.lib.shell.CommandException;
+import com.google.devtools.build.lib.unix.UnixFileSystem;
 import com.google.devtools.build.lib.util.OS;
 import com.google.devtools.build.lib.util.ProcessUtils;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
 import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.vfs.UnixFileSystem;
 import com.google.devtools.common.options.OptionsParser;
 import io.grpc.Server;
 import io.grpc.ServerBuilder;