Rollback of commit e0d7a540e3c615c628f63fcaaaba0c47fca2cb25.

*** Reason for rollback ***

Suspected root cause for Windows bootstrap on Bazel CI breakage:

java.lang.NullPointerException
	at com.google.devtools.build.lib.vfs.Path$1.run(Path.java:123)

http://ci.bazel.io/view/Bazel%20bootstrap%20and%20maintenance/job/Bazel/922/JAVA_VERSION=1.8,PLATFORM_NAME=windows-x86_64/console

*** Original change description ***

VFS: implement a Windows-specific Path subclass

The new subclass WindowsFileSystem.WindowsPath is
aware of Windows drives.

This change:
- introduces a new factory for Path objects so
  FileSystems can return a custom implementation
  that instantiates filesystem-specific Paths
- implements the WindowsPath subclass of Path that
  is aware of Windows drives
- introduces the bazel.windows_unix_root JVM
  argument that defines the MSYS root, which
  defines the absolute Windows path that is the...

***

--
MOS_MIGRATED_REVID=136583352
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
index 768792a..e7f1fa2 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/FileSystem.java
@@ -22,7 +22,6 @@
 import com.google.common.io.CharStreams;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
 import com.google.devtools.build.lib.vfs.Dirent.Type;
-import com.google.devtools.build.lib.vfs.Path.PathFactory;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -38,20 +37,6 @@
 @ThreadSafe
 public abstract class FileSystem {
 
-  private enum UnixPathFactory implements PathFactory {
-    INSTANCE {
-      @Override
-      public Path createRootPath(FileSystem filesystem) {
-        return new Path(filesystem, PathFragment.ROOT_DIR, null);
-      }
-
-      @Override
-      public Path createChildPath(Path parent, String childName) {
-        return new Path(parent.getFileSystem(), childName, parent);
-      }
-    };
-  }
-
   /**
    * An exception thrown when attempting to resolve an ordinary file as a symlink.
    */
@@ -64,12 +49,19 @@
   protected final Path rootPath;
 
   protected FileSystem() {
-    this.rootPath = getPathFactory().createRootPath(this);
+    this.rootPath = createRootPath();
   }
 
-  /** Returns filesystem-specific path factory. */
-  protected PathFactory getPathFactory() {
-    return UnixPathFactory.INSTANCE;
+  /**
+   * Creates the root of all paths used by this filesystem. This is a hook
+   * allowing subclasses to define their own root path class. All other paths
+   * are created via the root path's {@link Path#createChildPath(String)} method.
+   * <p>
+   * Beware: this is called during the FileSystem constructor which may occur
+   * before subclasses are completely initialized.
+   */
+  protected Path createRootPath() {
+    return new Path(this);
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/Path.java b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
index be95e3b..dbec227 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/Path.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/Path.java
@@ -17,6 +17,7 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
+import com.google.devtools.build.lib.util.OS;
 import com.google.devtools.build.lib.util.Preconditions;
 import com.google.devtools.build.lib.util.StringCanonicalizer;
 import java.io.File;
@@ -30,6 +31,7 @@
 import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.WeakReference;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.IdentityHashMap;
 import java.util.Objects;
@@ -51,28 +53,6 @@
 @ThreadSafe
 public class Path implements Comparable<Path>, Serializable {
 
-  /** Filesystem-specific factory for {@link Path} objects. */
-  public static interface PathFactory {
-    /**
-     * Creates the root of all paths used by a filesystem.
-     *
-     * <p>All other paths are instantiated via {@link Path#createChildPath(String)} which calls
-     * {@link #createChildPath(Path, String)}.
-     *
-     * <p>Beware: this is called during the FileSystem constructor which may occur before subclasses
-     * are completely initialized.
-     */
-    Path createRootPath(FileSystem filesystem);
-
-    /**
-     * Create a child path of the given parent.
-     *
-     * <p>All {@link Path} objects are instantiated via this method, with the sole exception of the
-     * filesystem root, which is created by {@link #createRootPath(FileSystem)}.
-     */
-    Path createChildPath(Path parent, String childName);
-  }
-
   private static FileSystem fileSystemForSerialization;
 
   /**
@@ -171,12 +151,10 @@
   private volatile IdentityHashMap<String, Reference<Path>> children;
 
   /**
-   * Create a path instance.
-   *
-   * <p>Should only be called by {@link PathFactory#createChildPath(Path, String)}.
+   * Create a path instance.  Should only be called by {@link #createChildPath}.
    *
    * @param name the name of this path; it must be canonicalized with {@link
-   *     StringCanonicalizer#intern}
+   *             StringCanonicalizer#intern}
    * @param parent this path's parent
    */
   protected Path(FileSystem fileSystem, String name, Path parent) {
@@ -184,9 +162,6 @@
     this.name = name;
     this.parent = parent;
     this.depth = parent == null ? 0 : parent.depth + 1;
-
-    // No need to include the drive letter in the hash code, because it's derived from the parent
-    // and/or the name.
     if (fileSystem == null || fileSystem.isFilePathCaseSensitive()) {
       this.hashCode = Objects.hash(parent, name);
     } else {
@@ -195,9 +170,8 @@
   }
 
   /**
-   * Create the root path.
-   *
-   * <p>Should only be called by {@link PathFactory#createRootPath(FileSystem)}.
+   * Create the root path.  Should only be called by
+   * {@link FileSystem#createRootPath()}.
    */
   protected Path(FileSystem fileSystem) {
     this(fileSystem, StringCanonicalizer.intern("/"), null);
@@ -223,7 +197,6 @@
       this.depth = this.parent.depth + 1;
     }
     this.hashCode = Objects.hash(parent, name);
-    reinitializeAfterDeserialization();
   }
 
   /**
@@ -238,45 +211,7 @@
   }
 
   protected Path createChildPath(String childName) {
-    return fileSystem.getPathFactory().createChildPath(this, childName);
-  }
-
-  /**
-   * Reinitializes this object after deserialization.
-   *
-   * <p>Derived classes should use this hook to initialize additional state.
-   */
-  protected void reinitializeAfterDeserialization() {}
-
-  /**
-   * Returns true if {@code ancestorPath} may be an ancestor of {@code path}.
-   *
-   * <p>The return value may be a false positive, but it cannot be a false negative. This means that
-   * a true return value doesn't mean the ancestor candidate is really an ancestor, however a false
-   * return value means it's guaranteed that {@code ancestorCandidate} is not an ancestor of this
-   * path.
-   *
-   * <p>Subclasses may override this method with filesystem-specific logic, e.g. a Windows
-   * filesystem may return false if the ancestor path is on a different drive than this one, because
-   * it is then guaranteed that the ancestor candidate cannot be an ancestor of this path.
-   *
-   * @param ancestorCandidate the path that may or may not be an ancestor of this one
-   */
-  protected boolean isMaybeRelativeTo(Path ancestorCandidate) {
-    return true;
-  }
-
-  /**
-   * Returns true if this directory is top-level, i.e. it is its own parent.
-   *
-   * <p>When canonicalizing paths the ".." segment of a top-level directory always resolves to the
-   * directory itself.
-   *
-   * <p>On Unix, a top-level directory would be just the filesystem root ("/), on Windows it would
-   * be the filesystem root and the volume roots.
-   */
-  protected boolean isTopLevelDirectory() {
-    return isRootDirectory();
+    return new Path(fileSystem, childName, this);
   }
 
   /**
@@ -302,7 +237,7 @@
       Reference<Path> childRef = children.get(childName);
       Path child;
       if (childRef == null || (child = childRef.get()) == null) {
-        child = fileSystem.getPathFactory().createChildPath(this, childName);
+        child = createChildPath(childName);
         children.put(childName, new PathWeakReferenceForCleanup(child, REFERENCE_QUEUE));
       }
       return child;
@@ -349,22 +284,37 @@
   }
 
   /**
-   * Computes a string representation of this path, and writes it to the given string builder. Only
-   * called locally with a new instance.
+   * Computes a string representation of this path, and writes it to the
+   * given string builder. Only called locally with a new instance.
    */
-  protected void buildPathString(StringBuilder result) {
+  private void buildPathString(StringBuilder result) {
     if (isRootDirectory()) {
-      result.append(PathFragment.ROOT_DIR);
+      result.append('/');
     } else {
-      parent.buildPathString(result);
+      if (parent.isWindowsVolumeName()) {
+        result.append(parent.name);
+      } else {
+        parent.buildPathString(result);
+      }
       if (!parent.isRootDirectory()) {
-        result.append(PathFragment.SEPARATOR_CHAR);
+        result.append('/');
       }
       result.append(name);
     }
   }
 
   /**
+   * Returns true if the current path represents a Windows volume name (such as "c:" or "d:").
+   *
+   * <p>Paths such as '\\\\vol\\foo' are not supported.
+   */
+  private boolean isWindowsVolumeName() {
+    return OS.getCurrent() == OS.WINDOWS
+        && parent != null && parent.isRootDirectory() && name.length() == 2
+        && PathFragment.getWindowsDriveLetter(name) != '\0';
+  }
+
+  /**
    * Returns the path as a string.
    */
   public String getPathString() {
@@ -647,8 +597,8 @@
     if (segment.equals(".") || segment.isEmpty()) {
       return this; // that's a noop
     } else if (segment.equals("..")) {
-      // top-level directory's parent is root, when canonicalising:
-      return isTopLevelDirectory() ? this : parent;
+      // root's parent is root, when canonicalising:
+      return parent == null || isWindowsVolumeName() ? this : parent;
     } else {
       return getCachedChildPath(segment);
     }
@@ -670,10 +620,6 @@
     return getCachedChildPath(baseName);
   }
 
-  protected Path getRootForRelativePathComputation(PathFragment suffix) {
-    return suffix.isAbsolute() ? fileSystem.getRootDirectory() : this;
-  }
-
   /**
    * Returns the path formed by appending the relative or absolute path fragment
    * {@code suffix} to this path.
@@ -684,7 +630,10 @@
    * is canonical.
    */
   public Path getRelative(PathFragment suffix) {
-    Path result = getRootForRelativePathComputation(suffix);
+    Path result = suffix.isAbsolute() ? fileSystem.getRootDirectory() : this;
+    if (!suffix.windowsVolume().isEmpty()) {
+      result = result.getCanonicalPath(suffix.windowsVolume());
+    }
     for (String segment : suffix.segments()) {
       result = result.getCanonicalPath(segment);
     }
@@ -707,7 +656,7 @@
     if ((path.length() == 0) || (path.equals("."))) {
       return this;
     } else if (path.equals("..")) {
-      return isTopLevelDirectory() ? this : parent;
+      return parent == null ? this : parent;
     } else if (path.indexOf('/') != -1) {
       return getRelative(new PathFragment(path));
     } else if (path.indexOf(PathFragment.EXTRA_SEPARATOR_CHAR) != -1) {
@@ -717,20 +666,29 @@
     }
   }
 
-  protected final String[] getSegments() {
+  /**
+   * Returns an absolute PathFragment representing this path.
+   */
+  public PathFragment asFragment() {
     String[] resultSegments = new String[depth];
     Path currentPath = this;
     for (int pos = depth - 1; pos >= 0; pos--) {
       resultSegments[pos] = currentPath.getBaseName();
       currentPath = currentPath.getParentDirectory();
     }
-    return resultSegments;
+
+    char driveLetter = '\0';
+    if (resultSegments.length > 0) {
+      driveLetter = PathFragment.getWindowsDriveLetter(resultSegments[0]);
+      if (driveLetter != '\0') {
+        // Strip off the first segment that contains the volume name.
+        resultSegments = Arrays.copyOfRange(resultSegments, 1, resultSegments.length);
+      }
+    }
+
+    return new PathFragment(driveLetter, true, resultSegments);
   }
 
-  /** Returns an absolute PathFragment representing this path. */
-  public PathFragment asFragment() {
-    return new PathFragment('\0', true, getSegments());
-  }
 
   /**
    * Returns a relative path fragment to this path, relative to {@code
@@ -750,19 +708,17 @@
   public PathFragment relativeTo(Path ancestorPath) {
     checkSameFilesystem(ancestorPath);
 
-    if (isMaybeRelativeTo(ancestorPath)) {
-      // Fast path: when otherPath is the ancestor of this path
-      int resultSegmentCount = depth - ancestorPath.depth;
-      if (resultSegmentCount >= 0) {
-        String[] resultSegments = new String[resultSegmentCount];
-        Path currentPath = this;
-        for (int pos = resultSegmentCount - 1; pos >= 0; pos--) {
-          resultSegments[pos] = currentPath.getBaseName();
-          currentPath = currentPath.getParentDirectory();
-        }
-        if (ancestorPath.equals(currentPath)) {
-          return new PathFragment('\0', false, resultSegments);
-        }
+    // Fast path: when otherPath is the ancestor of this path
+    int resultSegmentCount = depth - ancestorPath.depth;
+    if (resultSegmentCount >= 0) {
+      String[] resultSegments = new String[resultSegmentCount];
+      Path currentPath = this;
+      for (int pos = resultSegmentCount - 1; pos >= 0; pos--) {
+        resultSegments[pos] = currentPath.getBaseName();
+        currentPath = currentPath.getParentDirectory();
+      }
+      if (ancestorPath.equals(currentPath)) {
+        return new PathFragment('\0', false, resultSegments);
       }
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
index c6a6ad6..1459379 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/PathFragment.java
@@ -14,7 +14,6 @@
 package com.google.devtools.build.lib.vfs;
 
 import com.google.common.base.Function;
-import com.google.common.base.Joiner;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -107,14 +106,13 @@
   // live PathFragments, so do not add further fields on a whim.
 
   // The individual path components.
-  // Does *not* include the Windows drive letter.
   private final String[] segments;
 
   // True both for UNIX-style absolute paths ("/foo") and Windows-style ("C:/foo").
-  // False for a Windows-style volume label ("C:") which is actually a relative path.
   private final boolean isAbsolute;
 
-  // Upper case Windows drive letter, or '\0' if none or unknown.
+  // Upper case windows drive letter, or '\0' if none. While a volumeName string is more
+  // general, we create a lot of these objects, so space is at a premium.
   private final char driveLetter;
 
   // hashCode and path are lazily initialized but semantically immutable.
@@ -125,47 +123,13 @@
    * Construct a PathFragment from a string, which is an absolute or relative UNIX or Windows path.
    */
   public PathFragment(String path) {
-    char drive = '\0';
-    boolean abs = false;
-    if (OS.getCurrent() == OS.WINDOWS) {
-      if (path.length() == 2 && isSeparator(path.charAt(0)) && Character.isLetter(path.charAt(1))) {
-        // Path is "/C" or other drive letter
-        drive = Character.toUpperCase(path.charAt(1));
-        abs = true;
-      } else if (path.length() >= 2
-          && Character.isLetter(path.charAt(0))
-          && path.charAt(1) == ':') {
-        // Path is like "C:", "C:/", "C:foo", or "C:/foo"
-        drive = Character.toUpperCase(path.charAt(0));
-      } else if (path.length() >= 3
-          && isSeparator(path.charAt(0))
-          && Character.isLetter(path.charAt(1))) {
-        if (isSeparator(path.charAt(2))) {
-          // Path is like "/C/" or "/C/foo"
-          drive = Character.toUpperCase(path.charAt(1));
-          abs = true;
-        } else if (path.charAt(2) == ':') {
-          // Path is like "/C:" or "/C:/" or "/C:/foo", neither of which is a valid path on MSYS.
-          // They are also very confusing because they would be valid, absolute PathFragments with
-          // no drive letters and the first segment being "C:".
-          // We should not be constructing such PathFragments on Windows because it would allow
-          // creating a valid Path object that would nevertheless be non-equal to "C:/" (because
-          // the internal representation would be different).
-          throw new IllegalArgumentException("Illegal path string \"" + path + "\"");
-        }
-      }
-    }
-
-    if (drive != '\0') {
+    this.driveLetter = getWindowsDriveLetter(path);
+    if (driveLetter != '\0') {
       path = path.substring(2);
+      // TODO(bazel-team): Decide what to do about non-absolute paths with a volume name, e.g. C:x.
     }
-    this.isAbsolute = abs || (path.length() > 0 && isSeparator(path.charAt(0)));
+    this.isAbsolute = path.length() > 0 && isSeparator(path.charAt(0));
     this.segments = segment(path, isAbsolute ? 1 : 0);
-
-    // If the only difference between this object and EMPTY_FRAGMENT is the drive letter, then this
-    // object is equivalent with the empty fragment. To make them compare equal we must use a null
-    // drive letter.
-    this.driveLetter = (this.isAbsolute || this.segments.length > 0) ? drive : '\0';
   }
 
   private static boolean isSeparator(char c) {
@@ -186,17 +150,6 @@
    * here in PathFragment, and by Path.asFragment() and Path.relativeTo().
    */
   PathFragment(char driveLetter, boolean isAbsolute, String[] segments) {
-    driveLetter = Character.toUpperCase(driveLetter);
-    if (OS.getCurrent() == OS.WINDOWS
-        && segments.length > 0
-        && segments[0].length() == 2
-        && Character.toUpperCase(segments[0].charAt(0)) == driveLetter
-        && segments[0].charAt(1) == ':') {
-      throw new IllegalStateException(
-          String.format(
-              "the drive letter should not be a path segment; drive='%c', segments=[%s]",
-              driveLetter, Joiner.on(", ").join(segments)));
-    }
     this.driveLetter = driveLetter;
     this.isAbsolute = isAbsolute;
     this.segments = segments;
@@ -385,11 +338,7 @@
         ((driveLetter != '\0') ? 2 : 0)
         + ((segments.length == 0) ? 0 : (segments.length + 1) * 20);
     StringBuilder result = new StringBuilder(estimateSize);
-    if (isAbsolute) {
-      // Only print the Windows volume label if the PathFragment is absolute. Do not print relative
-      // Windows paths like "C:foo/bar", it would break all kinds of things, e.g. glob().
-      result.append(windowsVolume());
-    }
+    result.append(windowsVolume());
     boolean initialSegment = true;
     for (String segment : segments) {
       if (!initialSegment || isAbsolute) {
@@ -463,14 +412,9 @@
     if (otherFragment == EMPTY_FRAGMENT) {
       return this;
     }
-
-    if (otherFragment.isAbsolute()) {
-      return this.driveLetter == '\0' || otherFragment.driveLetter != '\0'
-          ? otherFragment
-          : new PathFragment(this.driveLetter, true, otherFragment.segments);
-    } else {
-      return new PathFragment(this, otherFragment);
-    }
+    return otherFragment.isAbsolute()
+        ? otherFragment
+        : new PathFragment(this, otherFragment);
   }
 
   /**
@@ -595,9 +539,9 @@
    * order)
    */
   public boolean startsWith(PathFragment prefix) {
-    if (this.isAbsolute != prefix.isAbsolute
-        || this.segments.length < prefix.segments.length
-        || (isAbsolute && this.driveLetter != prefix.driveLetter)) {
+    if (this.isAbsolute != prefix.isAbsolute ||
+        this.segments.length < prefix.segments.length ||
+        this.driveLetter != prefix.driveLetter) {
       return false;
     }
     for (int i = 0, len = prefix.segments.length; i < len; i++) {
@@ -683,6 +627,9 @@
   }
 
   public String windowsVolume() {
+    if (OS.getCurrent() != OS.WINDOWS) {
+      return "";
+    }
     return (driveLetter != '\0') ? driveLetter + ":" : "";
   }
 
@@ -740,8 +687,16 @@
     return new PathFragment(driveLetter, false, segments);
   }
 
-  private boolean isEmpty() {
-    return !isAbsolute && segments.length == 0;
+  /**
+   * Given a path, returns the Windows drive letter ('X'), or an null character if no volume
+   * name was specified.
+   */
+  static char getWindowsDriveLetter(String path) {
+    if (OS.getCurrent() == OS.WINDOWS
+        && path.length() >= 2 && path.charAt(1) == ':' && Character.isLetter(path.charAt(0))) {
+      return Character.toUpperCase(path.charAt(0));
+    }
+    return '\0';
   }
 
   @Override
@@ -753,7 +708,7 @@
     // Yes, this means that if the hash code is really 0 then we will "recompute" it each time. But
     // this isn't a problem in practice since a hash code of 0 is rare.
     //
-    // (2) Since we have no synchronization, multiple threads can race here thinking they are the
+    // (2) Since we have no synchronization, multiple threads can race here thinking there are the
     // first one to compute and cache the hash code.
     //
     // (3) Moreover, since 'hashCode' is non-volatile, the cached hash code value written from one
@@ -764,13 +719,10 @@
     // once.
     int h = hashCode;
     if (h == 0) {
-      h = Boolean.hashCode(isAbsolute);
+      h = isAbsolute ? 1 : 0;
       for (String segment : segments) {
         h = h * 31 + segment.hashCode();
       }
-      if (!isEmpty()) {
-        h = h * 31 + Character.hashCode(driveLetter);
-      }
       hashCode = h;
     }
     return h;
@@ -785,13 +737,8 @@
       return false;
     }
     PathFragment otherPath = (PathFragment) other;
-    if (isEmpty() && otherPath.isEmpty()) {
-      return true;
-    } else {
-      return isAbsolute == otherPath.isAbsolute
-          && driveLetter == otherPath.driveLetter
-          && Arrays.equals(otherPath.segments, segments);
-    }
+    return isAbsolute == otherPath.isAbsolute &&
+        Arrays.equals(otherPath.segments, segments);
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/RootedPath.java b/src/main/java/com/google/devtools/build/lib/vfs/RootedPath.java
index 23e7ad7..a982595 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/RootedPath.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/RootedPath.java
@@ -14,6 +14,7 @@
 package com.google.devtools.build.lib.vfs;
 
 import com.google.devtools.build.lib.util.Preconditions;
+
 import java.io.Serializable;
 import java.util.Objects;
 
@@ -48,21 +49,7 @@
    * Returns a rooted path representing {@code relativePath} relative to {@code root}.
    */
   public static RootedPath toRootedPath(Path root, PathFragment relativePath) {
-    if (relativePath.isAbsolute()) {
-      if (root.isRootDirectory()) {
-        return new RootedPath(
-            root.getRelative(relativePath.windowsVolume()), relativePath.toRelative());
-      } else {
-        Preconditions.checkArgument(
-            relativePath.startsWith(root.asFragment()),
-            "relativePath '%s' is absolute, but it's not under root '%s'",
-            relativePath,
-            root);
-        return new RootedPath(root, relativePath.relativeTo(root.asFragment()));
-      }
-    } else {
-      return new RootedPath(root, relativePath);
-    }
+    return new RootedPath(root, relativePath);
   }
 
   /**
@@ -70,7 +57,7 @@
    */
   public static RootedPath toRootedPath(Path root, Path path) {
     Preconditions.checkState(path.startsWith(root), "path: %s root: %s", path, root);
-    return toRootedPath(root, path.asFragment());
+    return new RootedPath(root, path.relativeTo(root));
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java
index bc9a10d..da04735 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/WindowsFileSystem.java
@@ -15,205 +15,22 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
-import com.google.devtools.build.lib.util.Preconditions;
-import com.google.devtools.build.lib.vfs.Path.PathFactory;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.nio.file.Files;
 import java.nio.file.LinkOption;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.nio.file.attribute.DosFileAttributes;
-import java.util.Arrays;
-import java.util.concurrent.TimeUnit;
 
 /** Jury-rigged file system for Windows. */
 @ThreadSafe
 public class WindowsFileSystem extends JavaIoFileSystem {
 
-  @VisibleForTesting
-  enum WindowsPathFactory implements PathFactory {
-    INSTANCE {
-      @Override
-      public Path createRootPath(FileSystem filesystem) {
-        return new WindowsPath(filesystem, PathFragment.ROOT_DIR, null);
-      }
-
-      @Override
-      public Path createChildPath(Path parent, String childName) {
-        Preconditions.checkState(parent instanceof WindowsPath);
-        return new WindowsPath(parent.getFileSystem(), childName, (WindowsPath) parent);
-      }
-    };
-  }
-
-  private static final class WindowsPath extends Path {
-
-    private static final String WINDOWS_UNIX_ROOT_JVM_ARG = "bazel.windows_unix_root";
-    private static final String BAZEL_SH_ENV_VAR = "BAZEL_SH";
-
-    // Absolute Windows path specifying the root of absolute Unix paths.
-    // This is typically the MSYS installation root, e.g. C:\\tools\\msys64
-    private static final PathFragment UNIX_ROOT =
-        determineUnixRoot(WINDOWS_UNIX_ROOT_JVM_ARG, BAZEL_SH_ENV_VAR);
-
-    // The drive letter is '\0' if and only if this Path is the filesystem root "/".
-    private char driveLetter;
-
-    private WindowsPath(FileSystem fileSystem) {
-      super(fileSystem);
-      this.driveLetter = '\0';
-    }
-
-    private WindowsPath(FileSystem fileSystem, String name, WindowsPath parent) {
-      super(fileSystem, translateName(parent, name), translateParent(parent, name));
-      this.driveLetter = getDriveLetter((WindowsPath) getParentDirectory(), getBaseName());
-    }
-
-    @Override
-    protected void buildPathString(StringBuilder result) {
-      if (isRootDirectory()) {
-        result.append(PathFragment.ROOT_DIR);
-      } else {
-        if (isTopLevelDirectory()) {
-          result.append(driveLetter).append(':').append(PathFragment.SEPARATOR_CHAR);
-        } else {
-          getParentDirectory().buildPathString(result);
-          if (!getParentDirectory().isTopLevelDirectory()) {
-            result.append(PathFragment.SEPARATOR_CHAR);
-          }
-          result.append(getBaseName());
-        }
-      }
-    }
-
-    @Override
-    public void reinitializeAfterDeserialization() {
-      Preconditions.checkState(
-          getParentDirectory().isRootDirectory() || getParentDirectory() instanceof WindowsPath);
-      this.driveLetter =
-          (getParentDirectory() != null) ? ((WindowsPath) getParentDirectory()).driveLetter : '\0';
-    }
-
-    @Override
-    public boolean isMaybeRelativeTo(Path ancestorCandidate) {
-      Preconditions.checkState(ancestorCandidate instanceof WindowsPath);
-      return ancestorCandidate.isRootDirectory()
-          || driveLetter == ((WindowsPath) ancestorCandidate).driveLetter;
-    }
-
-    @Override
-    public boolean isTopLevelDirectory() {
-      return isRootDirectory() || getParentDirectory().isRootDirectory();
-    }
-
-    @Override
-    public PathFragment asFragment() {
-      String[] segments = getSegments();
-      if (segments.length > 0) {
-        // Strip off the first segment that contains the volume name.
-        segments = Arrays.copyOfRange(segments, 1, segments.length);
-      }
-
-      return new PathFragment(driveLetter, true, segments);
-    }
-
-    @Override
-    protected Path getRootForRelativePathComputation(PathFragment relative) {
-      Path result = this;
-      if (relative.isAbsolute()) {
-        result = getFileSystem().getRootDirectory();
-        if (!relative.windowsVolume().isEmpty()) {
-          result = result.getRelative(relative.windowsVolume());
-        }
-      }
-      return result;
-    }
-
-    private static boolean isWindowsVolumeName(String name) {
-      return (name.length() == 1 || (name.length() == 2 && name.charAt(1) == ':'))
-          && Character.isLetter(name.charAt(0));
-    }
-
-    private static String translateName(WindowsPath parent, String name) {
-      if (parent != null && parent.isRootDirectory() && name.length() == 1) {
-        // Path is /c (or similar), which is equivalent to C:/
-        return name.toUpperCase() + ":";
-      } else {
-        return name;
-      }
-    }
-
-    /**
-     * Heuristic to translate absolute Unix paths to Windows paths.
-     *
-     * <p>Unix paths under MSYS should be resolved using /etc/mtab, but reading that every time we
-     * create a Path object would be too expensive.
-     *
-     * <p>As a heuristic, we check if the paths looks like an absolute Unix path (e.g. "/usr/lib")
-     * and make it relative to the MSYS root (yielding "c:/tools/msys64/usr/lib"), but only if the
-     * path doesn't also look like an absolute path on a drive (e.g. /c/windows which means
-     * C:/windows).
-     *
-     * <p>This is an imperfect workaround because the user may have other paths mounted as well, and
-     * this heuristic won't handle those properly, but it's good enough.
-     *
-     * <p>The correct long-term solution is to update all tools on Windows to output Windows paths
-     * and lock down WindowsPath creation to disallow absolute Unix paths.
-     */
-    private static WindowsPath translateParent(WindowsPath parent, String name) {
-      if (parent != null && parent.isRootDirectory() && !isWindowsVolumeName(name)) {
-        // This is a top-level directory which is not a drive name, e.g. "/usr".
-        // Make it relative to UNIX_ROOT.
-        Preconditions.checkNotNull(
-            UNIX_ROOT,
-            "Could not determine Unix path root or it is not an absolute Windows path. Set the "
-                + "\"%s\" JVM argument, or export the \"%s\" environment variable for the MSYS bash"
-                + " and have /usr/bin/cygpath installed",
-            WINDOWS_UNIX_ROOT_JVM_ARG,
-            BAZEL_SH_ENV_VAR);
-
-        return (WindowsPath) parent.getRelative(UNIX_ROOT);
-      } else {
-        // This is not a top-level directory.
-        return parent;
-      }
-    }
-
-    private char getDriveLetter(WindowsPath parent, String name) {
-      if (parent == null) {
-        return '\0';
-      } else {
-        if (parent.isRootDirectory()) {
-          Preconditions.checkState(
-              isWindowsVolumeName(name),
-              "top-level directory on Windows must be a drive (name = '%s')",
-              name);
-          return Character.toUpperCase(name.charAt(0));
-        } else {
-          return parent.driveLetter;
-        }
-      }
-    }
-  }
-
   public static final LinkOption[] NO_OPTIONS = new LinkOption[0];
   public static final LinkOption[] NO_FOLLOW = new LinkOption[] {LinkOption.NOFOLLOW_LINKS};
 
   @Override
-  protected PathFactory getPathFactory() {
-    return WindowsPathFactory.INSTANCE;
-  }
-
-  @Override
-  public String getFileSystemType(Path path) {
-    // TODO(laszlocsomor): implement this properly, i.e. actually query this information from
-    // somewhere (java.nio.Filesystem? System.getProperty? implement JNI method and use WinAPI?).
-    return "ntfs";
-  }
-
-  @Override
   protected void createSymbolicLink(Path linkPath, PathFragment targetFragment) throws IOException {
     // TODO(lberki): Add some JNI to create hard links/junctions instead of calling out to
     // cmd.exe
@@ -396,45 +213,4 @@
     }
     return false;
   }
-
-  private static PathFragment determineUnixRoot(String jvmArgName, String bazelShEnvVar) {
-    // Get the path from a JVM argument, if specified.
-    String path = System.getProperty(jvmArgName);
-
-    if (path == null || path.isEmpty()) {
-      path = "";
-
-      // Fall back to executing cygpath.
-      String bash = System.getenv(bazelShEnvVar);
-      Process process = null;
-      try {
-        process = Runtime.getRuntime().exec(bash + "-c \"/usr/bin/cygpath -m /\"");
-
-        // Wait 3 seconds max, that should be enough to run this command.
-        process.waitFor(3, TimeUnit.SECONDS);
-
-        if (process.exitValue() == 0) {
-          char[] buf = new char[256];
-          try (InputStreamReader r = new InputStreamReader(process.getInputStream())) {
-            int len = 0;
-            while ((len = r.read(buf)) > 0) {
-              path = path + new String(buf, 0, len);
-            }
-          }
-        }
-      } catch (InterruptedException | IOException e) {
-        // Silently ignore failure. Either MSYS is installed at a different location, or not
-        // installed at all, or some error occurred. We can't do anything anymore but throw an
-        // exception if someone tries to create a Path from an absolute Unix path.
-      }
-    }
-
-    path = path.trim();
-    PathFragment result = new PathFragment(path);
-    if (path.isEmpty() || result.getDriveLetter() == '\0' || !result.isAbsolute()) {
-      return null;
-    } else {
-      return result;
-    }
-  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/ZipFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/ZipFileSystem.java
index 892fecb..4830d8f 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/ZipFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/ZipFileSystem.java
@@ -16,7 +16,7 @@
 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.Path.PathFactory;
+
 import java.io.Closeable;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -96,23 +96,6 @@
   // #getDirectoryEntries}.  Then this field becomes redundant.
   @ThreadSafe
   private static class ZipPath extends Path {
-
-    private enum Factory implements PathFactory {
-      INSTANCE {
-        @Override
-        public Path createRootPath(FileSystem filesystem) {
-          Preconditions.checkArgument(filesystem instanceof ZipFileSystem);
-          return new ZipPath((ZipFileSystem) filesystem);
-        }
-
-        @Override
-        public Path createChildPath(Path parent, String childName) {
-          Preconditions.checkState(parent instanceof ZipPath);
-          return new ZipPath((ZipFileSystem) parent.getFileSystem(), childName, (ZipPath) parent);
-        }
-      };
-    }
-
     /**
      * Non-null iff this file/directory exists.  Set by setZipEntry for files
      * explicitly mentioned in the zipfile's table of contents, or implicitly
@@ -121,12 +104,12 @@
     ZipEntry entry = null;
 
     // Root path.
-    private ZipPath(ZipFileSystem fileSystem) {
+    ZipPath(ZipFileSystem fileSystem) {
       super(fileSystem);
     }
 
     // Non-root paths.
-    private ZipPath(ZipFileSystem fileSystem, String name, ZipPath parent) {
+    ZipPath(ZipFileSystem fileSystem, String name, ZipPath parent) {
       super(fileSystem, name, parent);
     }
 
@@ -145,6 +128,11 @@
         path.setZipEntry(new ZipEntry(path + "/")); // trailing "/" => isDir
       }
     }
+
+    @Override
+    protected ZipPath createChildPath(String childName) {
+      return new ZipPath((ZipFileSystem) getFileSystem(), childName, this);
+    }
   }
 
   /**
@@ -169,8 +157,8 @@
   }
 
   @Override
-  protected PathFactory getPathFactory() {
-    return ZipPath.Factory.INSTANCE;
+  protected Path createRootPath() {
+    return new ZipPath(this);
   }
 
   /** Returns the ZipEntry associated with a given path name, if any. */
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 0556e41..942d9a3 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -169,10 +169,7 @@
 java_test(
     name = "windows_test",
     srcs = CROSS_PLATFORM_WINDOWS_TESTS,
-    jvm_flags = [
-        "-Dblaze.os=Windows",
-        "-Dbazel.windows_unix_root=C:/fake/msys",
-    ],
+    jvm_flags = ["-Dblaze.os=Windows"],
     test_class = "com.google.devtools.build.lib.AllTests",
     deps = [
         ":foundations_testutil",
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentTest.java
index c4ac7c1..2a41b10 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentTest.java
@@ -26,13 +26,15 @@
 import com.google.common.testing.EqualsTester;
 import com.google.devtools.build.lib.testutil.TestUtils;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
-import java.io.File;
-import java.util.Collections;
-import java.util.List;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * This class tests the functionality of the PathFragment.
  */
@@ -67,16 +69,15 @@
     InMemoryFileSystem filesystem = new InMemoryFileSystem();
 
     new EqualsTester()
-        .addEqualityGroup(
-            new PathFragment("../relative/path"),
-            new PathFragment("..").getRelative("relative").getRelative("path"),
-            new PathFragment('\0', false, new String[] {"..", "relative", "path"}),
-            new PathFragment(new File("../relative/path")))
+        .addEqualityGroup(new PathFragment("../relative/path"),
+                          new PathFragment("../relative/path"),
+                          new PathFragment(new File("../relative/path")))
         .addEqualityGroup(new PathFragment("something/else"))
         .addEqualityGroup(new PathFragment("/something/else"))
-        .addEqualityGroup(new PathFragment("/"), new PathFragment("//////"))
-        .addEqualityGroup(new PathFragment(""), PathFragment.EMPTY_FRAGMENT)
-        .addEqualityGroup(filesystem.getRootDirectory()) // A Path object.
+        .addEqualityGroup(new PathFragment("/"),
+                          new PathFragment("//////"))
+        .addEqualityGroup(new PathFragment(""))
+        .addEqualityGroup(filesystem.getRootDirectory())  // A Path object.
         .testEquals();
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentWindowsTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentWindowsTest.java
index df2c770..48a63e3 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentWindowsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/PathFragmentWindowsTest.java
@@ -14,24 +14,23 @@
 package com.google.devtools.build.lib.vfs;
 
 import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
-import java.io.File;
-import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.File;
+
 /**
  * This class tests the functionality of the PathFragment.
  */
 @RunWith(JUnit4.class)
 public class PathFragmentWindowsTest {
-
+  
   @Test
   public void testWindowsSeparator() {
     assertEquals("bar/baz", new PathFragment("bar\\baz").toString());
@@ -53,26 +52,6 @@
   }
 
   @Test
-  public void testAbsolutePathsWithDrive() {
-    PathFragment p1 = new PathFragment("/c");
-    assertThat(p1.isAbsolute()).isTrue();
-    assertThat(p1.getDriveLetter()).isEqualTo('C');
-
-    PathFragment p2 = new PathFragment("/c/");
-    assertThat(p2.isAbsolute()).isTrue();
-    assertThat(p2.getDriveLetter()).isEqualTo('C');
-
-    assertThat(p1).isEqualTo(p2);
-
-    try {
-      new PathFragment("/c:");
-      Assert.fail("expected failure");
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains("Illegal path string \"/c:\"");
-    }
-  }
-
-  @Test
   public void testIsAbsoluteWindowsBackslash() {
     assertTrue(new PathFragment(new File("C:\\blah")).isAbsolute());
     assertTrue(new PathFragment(new File("C:\\")).isAbsolute());
@@ -104,75 +83,10 @@
     assertEquals("C:/c/d", new PathFragment("a/b").getRelative("C:/c/d").getPathString());
   }
 
-  private void assertGetRelative(String path, String relative, PathFragment expected)
-      throws Exception {
-    PathFragment actual = new PathFragment(path).getRelative(relative);
-    assertThat(actual.getPathString()).isEqualTo(expected.getPathString());
-    assertThat(actual).isEqualTo(expected);
-    assertThat(actual.getDriveLetter()).isEqualTo(expected.getDriveLetter());
-    assertThat(actual.hashCode()).isEqualTo(expected.hashCode());
-  }
-
-  private void assertRelativeTo(String path, String relativeTo, String... expectedPathSegments)
-      throws Exception {
-    PathFragment expected = new PathFragment('\0', false, expectedPathSegments);
-    PathFragment actual = new PathFragment(path).relativeTo(relativeTo);
-    assertThat(actual.getPathString()).isEqualTo(expected.getPathString());
-    assertThat(actual).isEqualTo(expected);
-    assertThat(actual.getDriveLetter()).isEqualTo(expected.getDriveLetter());
-    assertThat(actual.hashCode()).isEqualTo(expected.hashCode());
-  }
-
-  private void assertCantComputeRelativeTo(String path, String relativeTo) throws Exception {
-    try {
-      new PathFragment(path).relativeTo(relativeTo);
-      Assert.fail("expected failure");
-    } catch (Exception e) {
-      assertThat(e.getMessage()).contains("is not beneath");
-    }
-  }
-
-  private static PathFragment makePath(char drive, boolean absolute, String... segments) {
-    return new PathFragment(drive, absolute, segments);
-  }
-
   @Test
-  public void testGetRelativeMixed() throws Exception {
-    assertGetRelative("a", "b", makePath('\0', false, "a", "b"));
-    assertGetRelative("a", "/b", makePath('B', true));
-    assertGetRelative("a", "E:b", makePath('\0', false, "a", "b"));
-    assertGetRelative("a", "E:/b", makePath('E', true, "b"));
-
-    assertGetRelative("/a", "b", makePath('A', true, "b"));
-    assertGetRelative("/a", "/b", makePath('B', true));
-    assertGetRelative("/a", "E:b", makePath('A', true, "b"));
-    assertGetRelative("/a", "E:/b", makePath('E', true, "b"));
-
-    assertGetRelative("D:a", "b", makePath('D', false, "a", "b"));
-    assertGetRelative("D:a", "/b", makePath('B', true));
-    assertGetRelative("D:a", "E:b", makePath('D', false, "a", "b"));
-    assertGetRelative("D:a", "E:/b", makePath('E', true, "b"));
-
-    assertGetRelative("D:/a", "b", makePath('D', true, "a", "b"));
-    assertGetRelative("D:/a", "/b", makePath('B', true));
-    assertGetRelative("D:/a", "E:b", makePath('D', true, "a", "b"));
-    assertGetRelative("D:/a", "E:/b", makePath('E', true, "b"));
-  }
-
-  @Test
-  public void testRelativeTo() throws Exception {
-    assertRelativeTo("", "");
-    assertCantComputeRelativeTo("", "a");
-
-    assertRelativeTo("a", "", "a");
-    assertRelativeTo("a", "a");
-    assertCantComputeRelativeTo("a", "b");
-    assertRelativeTo("a/b", "a", "b");
-
-    assertRelativeTo("C:", "");
-    assertRelativeTo("C:", "C:");
-    assertCantComputeRelativeTo("C:/", "");
-    assertRelativeTo("C:/", "C:/");
+  public void testGetRelativeMixed() {
+    assertEquals("/b", new PathFragment("C:/a").getRelative("/b").getPathString());
+    assertEquals("C:/b", new PathFragment("/a").getRelative("C:/b").getPathString());
   }
 
   @Test
@@ -183,12 +97,8 @@
 
   // Tests after here test the canonicalization
   private void assertRegular(String expected, String actual) {
-    PathFragment exp = new PathFragment(expected);
-    PathFragment act = new PathFragment(actual);
-    assertThat(exp.getPathString()).isEqualTo(expected);
-    assertThat(act.getPathString()).isEqualTo(expected);
-    assertThat(act).isEqualTo(exp);
-    assertThat(act.hashCode()).isEqualTo(exp.hashCode());
+    assertEquals(expected, new PathFragment(actual).getPathString()); // compare string forms
+    assertEquals(new PathFragment(expected), new PathFragment(actual)); // compare fragment forms
   }
 
   @Test
@@ -196,38 +106,9 @@
     assertRegular("C:/", "C:/");
   }
 
-  private void assertAllEqual(PathFragment... ps) {
-    assertThat(ps.length).isGreaterThan(1);
-    for (int i = 1; i < ps.length; i++) {
-      String msg = "comparing items 0 and " + i;
-      assertWithMessage(msg + " for getPathString")
-          .that(ps[i].getPathString())
-          .isEqualTo(ps[0].getPathString());
-      assertWithMessage(msg + " for equals").that(ps[0]).isEqualTo(ps[i]);
-      assertWithMessage(msg + " for hashCode").that(ps[0].hashCode()).isEqualTo(ps[i].hashCode());
-    }
-  }
-
   @Test
   public void testEmptyRelativePathToEmptyPathWindows() {
-    // Surprising but correct behavior: a PathFragment made of just a drive identifier (and not the
-    // absolute path "C:/") is equal not only to the empty fragment, but (therefore) also to other
-    // drive identifiers.
-    // This makes sense if you consider that these are still empty paths, the drive letter adds no
-    // information to the path itself.
-    assertAllEqual(
-        PathFragment.EMPTY_FRAGMENT,
-        new PathFragment("C:"),
-        new PathFragment("D:"),
-        new PathFragment('\0', false, new String[0]),
-        new PathFragment('C', false, new String[0]),
-        new PathFragment('D', false, new String[0]));
-    assertAllEqual(new PathFragment("C:/"), new PathFragment("/c"), new PathFragment("/c/"));
-    assertAllEqual(new PathFragment("C:/foo"), new PathFragment("/c/foo"));
-
-    assertThat(new PathFragment("C:/")).isNotEqualTo(new PathFragment("C:"));
-    assertThat(new PathFragment("C:/").getPathString())
-        .isNotEqualTo(new PathFragment("C:").getPathString());
+    assertRegular("C:", "C:");
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/PathWindowsTest.java b/src/test/java/com/google/devtools/build/lib/vfs/PathWindowsTest.java
index 864a483..cb8b23b 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/PathWindowsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/PathWindowsTest.java
@@ -18,10 +18,8 @@
 import static org.junit.Assert.assertSame;
 
 import com.google.devtools.build.lib.util.BlazeClock;
-import com.google.devtools.build.lib.vfs.Path.PathFactory;
-import com.google.devtools.build.lib.vfs.WindowsFileSystem.WindowsPathFactory;
 import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
-import org.junit.Assert;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,16 +35,8 @@
 
   @Before
   public final void initializeFileSystem() throws Exception  {
-    filesystem =
-        new InMemoryFileSystem(BlazeClock.instance()) {
-          @Override
-          protected PathFactory getPathFactory() {
-            return WindowsPathFactory.INSTANCE;
-          }
-        };
-    root = filesystem.getRootDirectory().getRelative("C:/");
-    root.createDirectory();
-
+    filesystem = new InMemoryFileSystem(BlazeClock.instance());
+    root = filesystem.getRootDirectory();
     Path first = root.getChild("first");
     first.createDirectory();
   }
@@ -108,38 +98,10 @@
   }
 
   @Test
-  public void testAbsoluteUnixPathIsRelativeToWindowsUnixRoot() {
-    Path actual = root.getRelative("/foo/bar");
-    Path expected = root.getRelative("C:/fake/msys/foo/bar");
-    assertThat(actual.getPathString()).isEqualTo(expected.getPathString());
-    assertThat(actual).isEqualTo(expected);
-  }
-
-  @Test
-  public void testAbsoluteUnixPathReferringToDriveIsRecognized() {
-    Path actual = root.getRelative("/c/foo");
-    Path expected = root.getRelative("C:/foo");
-    assertThat(actual.getPathString()).isEqualTo(expected.getPathString());
-    assertThat(actual).isEqualTo(expected);
-
-    // "unexpected" is not a valid MSYS path, we should not be able to create it.
-    try {
-      root.getRelative("/c:");
-      Assert.fail("expected failure");
-    } catch (IllegalArgumentException e) {
-      assertThat(e.getMessage()).contains("Illegal path string \"/c:\"");
-    }
-  }
-
-  @Test
   public void testStartsWithWorksOnWindows() {
     assertStartsWithReturnsOnWindows(true, "C:/first/x", "C:/first/x/y");
     assertStartsWithReturnsOnWindows(true, "c:/first/x", "C:/FIRST/X/Y");
     assertStartsWithReturnsOnWindows(true, "C:/FIRST/X", "c:/first/x/y");
-    assertStartsWithReturnsOnWindows(true, "/", "C:/");
-    assertStartsWithReturnsOnWindows(false, "C:/", "/");
-    assertStartsWithReturnsOnWindows(false, "C:/", "D:/");
-    assertStartsWithReturnsOnWindows(false, "C:/", "D:/foo");
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathTest.java b/src/test/java/com/google/devtools/build/lib/vfs/UnixPathTest.java
index e2317bf..2be5055 100644
--- a/src/test/java/com/google/devtools/build/lib/vfs/UnixPathTest.java
+++ b/src/test/java/com/google/devtools/build/lib/vfs/UnixPathTest.java
@@ -24,6 +24,12 @@
 import com.google.common.testing.EqualsTester;
 import com.google.devtools.build.lib.testutil.TestUtils;
 import com.google.devtools.build.lib.vfs.util.FileSystems;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -33,10 +39,6 @@
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
 
 /**
  * Tests for {@link Path}.
@@ -267,7 +269,7 @@
 
   @Test
   public void testDerivedSegmentEquality() {
-    Path absoluteSegment = unixFs.getRootDirectory();
+    Path absoluteSegment = new Path(null);
 
     Path derivedNode = absoluteSegment.getChild("derivedSegment");
     Path otherDerivedNode = absoluteSegment.getChild("derivedSegment");