Introduce Path#readSymbolicLinkUnchecked, intended to only be used when the caller already knows the path is a symlink, and use this new method throughout the codebase.

--
MOS_MIGRATED_REVID=103229983
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java
index 0be5c31..140504e 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java
@@ -88,7 +88,7 @@
     } else if (statNoFollow.isDirectory()) {
       return DIRECTORY_FILE_STATE_NODE;
     } else if (statNoFollow.isSymbolicLink()) {
-      return new SymlinkFileStateValue(path.readSymbolicLink());
+      return new SymlinkFileStateValue(path.readSymbolicLinkUnchecked());
     }
     throw new InconsistentFilesystemException("according to stat, existing path " + path + " is "
         + "neither a file nor directory nor symlink.");
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 fc5a9e8..bf74b96 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
@@ -478,6 +478,17 @@
   protected abstract PathFragment readSymbolicLink(Path path) throws IOException;
 
   /**
+   * Returns the target of a symbolic link, under the assumption that the given path is indeed a
+   * symbolic link (this assumption permits efficient implementations). See
+   * {@link Path#readSymbolicLinkUnchecked} for specification.
+   *
+   * @throws IOException if the contents of the link could not be read for any reason.
+   */
+  protected PathFragment readSymbolicLinkUnchecked(Path path) throws IOException {
+    return readSymbolicLink(path);
+  }
+
+  /**
    * Returns true iff {@code path} denotes an existing file of any kind. See
    * {@link Path#exists(Symlinks)} for specification.
    */
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 840433a..7ce6a9a 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
@@ -608,7 +608,7 @@
         copyTreesBelow(entry, subDir);
       } else if (entry.isSymbolicLink()) {
         Path newLink = to.getChild(entry.getBaseName());
-        newLink.createSymbolicLink(entry.readSymbolicLink());
+        newLink.createSymbolicLink(entry.readSymbolicLinkUnchecked());
       } else {
         Path newEntry = to.getChild(entry.getBaseName());
         copyFile(entry, newEntry);
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 38a29ac..88e8d3b 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
@@ -758,7 +758,7 @@
   public void createSymbolicLink(PathFragment target) throws IOException {
     fileSystem.createSymbolicLink(this, target);
   }
-
+  
   /**
    * Returns the target of the current path, which must be a symbolic link. The
    * link contents are returned exactly, and may contain an absolute or relative
@@ -773,6 +773,18 @@
   }
 
   /**
+   * If the current path is a symbolic link, returns the target of this symbolic link. The
+   * semantics are intentionally left underspecified otherwise to permit efficient implementations.
+   *
+   * @return the content (i.e. target) of the symbolic link
+   * @throws IOException if the current path is not a symbolic link, or the
+   *         contents of the link could not be read for any reason
+   */
+  public PathFragment readSymbolicLinkUnchecked() throws IOException {
+    return fileSystem.readSymbolicLinkUnchecked(this);
+  }
+
+  /**
    * Returns the canonical path for this path, by repeatedly replacing symbolic
    * links with their referents. Analogous to realpath(3).
    *
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java
index 2a86b22..74ae375 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/UnixFileSystem.java
@@ -329,6 +329,8 @@
 
   @Override
   protected PathFragment readSymbolicLink(Path path) throws IOException {
+    // Note that the default implementation of readSymbolicLinkUnchecked calls this method and thus
+    // is optimal since we only make one system call in here.
     String name = path.toString();
     long startTime = Profiler.nanoTimeMaybe();
     try {