wrapper: resolve /proc/self/cwd only when seen.

This wrapper may run on Mac, which doesn't have /proc/self/cwd
available. This also adds a test to be sure this selective Readlink()
behavior continues to work.

BUG=None
TEST=SDK Tryjob; tests pass

Change-Id: I6f6aaeb7ea45ab8b3d68299422f0594a689ecfcd
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/2095763
Reviewed-by: Manoj Gupta <manojgupta@chromium.org>
Reviewed-by: Tiancong Wang <tcwang@google.com>
Tested-by: George Burgess <gbiv@chromium.org>
diff --git a/compiler_wrapper/env.go b/compiler_wrapper/env.go
index 56b3a91..2c48ad3 100644
--- a/compiler_wrapper/env.go
+++ b/compiler_wrapper/env.go
@@ -28,19 +28,25 @@
 }
 
 func newProcessEnv() (env, error) {
-	// Note: We are not using os.getwd() as this sometimes uses the value of the PWD
-	// env variable. This has the following problems:
-	// - if PWD=/proc/self/cwd, os.getwd() will return "/proc/self/cwd",
-	//   and we need to read the link to get the actual wd. However, we can't always
-	//   do this as we are calculating
-	//   the path to clang, and following a symlinked cwd first would make
-	//   this calculation invalid.
-	// - the old python wrapper doesn't respect the PWD env variable either, so if we
-	//   did we would fail the comparison to the old wrapper.
-	wd, err := os.Readlink("/proc/self/cwd")
+	wd, err := os.Getwd()
 	if err != nil {
 		return nil, wrapErrorwithSourceLocf(err, "failed to read working directory")
 	}
+
+	// Note: On Linux, Getwd may resolve to /proc/self/cwd, since it checks the PWD environment
+	// variable. We need to read the link to get the actual working directory. We can't always
+	// do this as we are calculating the path to clang, since following a symlinked cwd first
+	// would make this calculation invalid.
+	//
+	// FIXME(gbiv): It's not clear why always Readlink()ing here an issue. crrev.com/c/1764624
+	// might provide helpful context?
+	if wd == "/proc/self/cwd" {
+		wd, err = os.Readlink(wd)
+		if err != nil {
+			return nil, wrapErrorwithSourceLocf(err, "resolving /proc/self/cwd")
+		}
+	}
+
 	return &processEnv{wd: wd}, nil
 }
 
diff --git a/compiler_wrapper/env_test.go b/compiler_wrapper/env_test.go
index 8580d4a..e03d60a 100644
--- a/compiler_wrapper/env_test.go
+++ b/compiler_wrapper/env_test.go
@@ -7,6 +7,7 @@
 import (
 	"bytes"
 	"flag"
+	"io/ioutil"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -163,6 +164,79 @@
 	})
 }
 
+func TestNewProcessEnvResolvesPwdAwayProperly(t *testing.T) {
+	// This test cannot be t.Parallel(), since it modifies our environment.
+	const envPwd = "PWD"
+
+	oldEnvPwd := os.Getenv(envPwd)
+	defer func() {
+		if oldEnvPwd == "" {
+			os.Unsetenv(envPwd)
+		} else {
+			os.Setenv(envPwd, oldEnvPwd)
+		}
+	}()
+
+	os.Unsetenv(envPwd)
+
+	initialWd, err := os.Getwd()
+	if initialWd == "/proc/self/cwd" {
+		t.Fatalf("Working directory should never be %q when env is unset", initialWd)
+	}
+
+	defer func() {
+		if err := os.Chdir(initialWd); err != nil {
+			t.Errorf("Changing back to %q failed: %v", initialWd, err)
+		}
+	}()
+
+	tempDir, err := ioutil.TempDir("", "wrapper_env_test")
+	if err != nil {
+		t.Fatalf("Failed making temp dir: %v", err)
+	}
+
+	// Nothing we can do if this breaks, unfortunately.
+	defer os.RemoveAll(tempDir)
+
+	tempDirLink := tempDir + ".symlink"
+	if err := os.Symlink(tempDir, tempDirLink); err != nil {
+		t.Fatalf("Failed creating symlink %q => %q: %v", tempDirLink, tempDir, err)
+	}
+
+	if err := os.Chdir(tempDir); err != nil {
+		t.Fatalf("Failed chdir'ing to tempdir at %q: %v", tempDirLink, err)
+	}
+
+	if err := os.Setenv(envPwd, tempDirLink); err != nil {
+		t.Fatalf("Failed setting pwd to tempdir at %q: %v", tempDirLink, err)
+	}
+
+	// Ensure that we don't resolve symlinks if they're present in our CWD somehow, except for
+	// /proc/self/cwd, which tells us nothing about where we are.
+	env, err := newProcessEnv()
+	if err != nil {
+		t.Fatalf("Failed making a new env: %v", err)
+	}
+
+	if wd := env.getwd(); wd != tempDirLink {
+		t.Errorf("Environment setup had a wd of %q; wanted %q", wd, tempDirLink)
+	}
+
+	const cwdLink = "/proc/self/cwd"
+	if err := os.Setenv(envPwd, cwdLink); err != nil {
+		t.Fatalf("Failed setting pwd to /proc/self/cwd: %v", err)
+	}
+
+	env, err = newProcessEnv()
+	if err != nil {
+		t.Fatalf("Failed making a new env: %v", err)
+	}
+
+	if wd := env.getwd(); wd != tempDir {
+		t.Errorf("Environment setup had a wd of %q; wanted %q", cwdLink, tempDir)
+	}
+}
+
 func execEcho(ctx *testContext, cmd *command) {
 	env := &processEnv{}
 	err := env.exec(createEcho(ctx, cmd))