components/golang-17/patches/0010-release-branch.go1.7-path-filepath-handle-.-in-norma.patch
changeset 7518 c388d4e1d3ad
equal deleted inserted replaced
7517:42ae3923b8fe 7518:c388d4e1d3ad
       
     1 From 3129c67db76bc8ee13a1edc38a6c25f9eddcbc6c Mon Sep 17 00:00:00 2001
       
     2 From: Hiroshi Ioka <[email protected]>
       
     3 Date: Fri, 19 Aug 2016 09:37:19 +0900
       
     4 Subject: [PATCH 10/38] [release-branch.go1.7] path/filepath: handle ".." in
       
     5  normalizing a path on Windows
       
     6 
       
     7 Current code assumes there are not ".." in the Clean(path).
       
     8 That's not true. Clean doesn't handle leading "..", so we need to stop
       
     9 normalization if we see "..".
       
    10 
       
    11 Fixes #16793
       
    12 
       
    13 Change-Id: I0a7901bedac17f1210b134d593ebd9f5e8483775
       
    14 Reviewed-on: https://go-review.googlesource.com/27410
       
    15 Reviewed-by: Ian Lance Taylor <[email protected]>
       
    16 Reviewed-by: Alex Brainman <[email protected]>
       
    17 Run-TryBot: Ian Lance Taylor <[email protected]>
       
    18 TryBot-Result: Gobot Gobot <[email protected]>
       
    19 Reviewed-on: https://go-review.googlesource.com/28641
       
    20 Reviewed-by: Brad Fitzpatrick <[email protected]>
       
    21 ---
       
    22  src/path/filepath/export_windows_test.go |   5 +-
       
    23  src/path/filepath/path_test.go           |  32 +++++++++-
       
    24  src/path/filepath/path_windows_test.go   | 101 ++++++++++++++++++++++++++++++-
       
    25  src/path/filepath/symlink_windows.go     |  29 ++++++++-
       
    26  4 files changed, 159 insertions(+), 8 deletions(-)
       
    27 
       
    28 diff --git a/src/path/filepath/export_windows_test.go b/src/path/filepath/export_windows_test.go
       
    29 index 8ca007f..a7e2e64 100644
       
    30 --- a/src/path/filepath/export_windows_test.go
       
    31 +++ b/src/path/filepath/export_windows_test.go
       
    32 @@ -4,4 +4,7 @@
       
    33  
       
    34  package filepath
       
    35  
       
    36 -var ToNorm = toNorm
       
    37 +var (
       
    38 +	ToNorm   = toNorm
       
    39 +	NormBase = normBase
       
    40 +)
       
    41 diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go
       
    42 index 1a4a9d2..a3990e2 100644
       
    43 --- a/src/path/filepath/path_test.go
       
    44 +++ b/src/path/filepath/path_test.go
       
    45 @@ -840,7 +840,7 @@ func TestEvalSymlinks(t *testing.T) {
       
    46  		if p, err := filepath.EvalSymlinks(path); err != nil {
       
    47  			t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
       
    48  		} else if filepath.Clean(p) != filepath.Clean(dest) {
       
    49 -			t.Errorf("Clean(%q)=%q, want %q", path, p, dest)
       
    50 +			t.Errorf("EvalSymlinks(%q)=%q, want %q", path, p, dest)
       
    51  		}
       
    52  
       
    53  		// test EvalSymlinks(".")
       
    54 @@ -872,6 +872,34 @@ func TestEvalSymlinks(t *testing.T) {
       
    55  			t.Errorf(`EvalSymlinks(".") in %q directory returns %q, want "." or %q`, d.path, p, want)
       
    56  		}()
       
    57  
       
    58 +		// test EvalSymlinks(".."+path)
       
    59 +		func() {
       
    60 +			defer func() {
       
    61 +				err := os.Chdir(wd)
       
    62 +				if err != nil {
       
    63 +					t.Fatal(err)
       
    64 +				}
       
    65 +			}()
       
    66 +
       
    67 +			err := os.Chdir(simpleJoin(tmpDir, "test"))
       
    68 +			if err != nil {
       
    69 +				t.Error(err)
       
    70 +				return
       
    71 +			}
       
    72 +
       
    73 +			path := simpleJoin("..", d.path)
       
    74 +			dest := simpleJoin("..", d.dest)
       
    75 +			if filepath.IsAbs(d.dest) || os.IsPathSeparator(d.dest[0]) {
       
    76 +				dest = d.dest
       
    77 +			}
       
    78 +
       
    79 +			if p, err := filepath.EvalSymlinks(path); err != nil {
       
    80 +				t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
       
    81 +			} else if filepath.Clean(p) != filepath.Clean(dest) {
       
    82 +				t.Errorf("EvalSymlinks(%q)=%q, want %q", path, p, dest)
       
    83 +			}
       
    84 +		}()
       
    85 +
       
    86  		// test EvalSymlinks where parameter is relative path
       
    87  		func() {
       
    88  			defer func() {
       
    89 @@ -889,7 +917,7 @@ func TestEvalSymlinks(t *testing.T) {
       
    90  			if p, err := filepath.EvalSymlinks(d.path); err != nil {
       
    91  				t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
       
    92  			} else if filepath.Clean(p) != filepath.Clean(d.dest) {
       
    93 -				t.Errorf("Clean(%q)=%q, want %q", d.path, p, d.dest)
       
    94 +				t.Errorf("EvalSymlinks(%q)=%q, want %q", d.path, p, d.dest)
       
    95  			}
       
    96  		}()
       
    97  	}
       
    98 diff --git a/src/path/filepath/path_windows_test.go b/src/path/filepath/path_windows_test.go
       
    99 index b47cdfd..9c82a0b 100644
       
   100 --- a/src/path/filepath/path_windows_test.go
       
   101 +++ b/src/path/filepath/path_windows_test.go
       
   102 @@ -329,9 +329,106 @@ func TestToNorm(t *testing.T) {
       
   103  	for _, test := range tests {
       
   104  		got, err := filepath.ToNorm(test.arg, stubBase)
       
   105  		if err != nil {
       
   106 -			t.Errorf("unexpected toNorm error, arg: %s, err: %v\n", test.arg, err)
       
   107 +			t.Errorf("toNorm(%s) failed: %v\n", test.arg, err)
       
   108  		} else if got != test.want {
       
   109 -			t.Errorf("toNorm error, arg: %s, want: %s, got: %s\n", test.arg, test.want, got)
       
   110 +			t.Errorf("toNorm(%s) returns %s, but %s expected\n", test.arg, got, test.want)
       
   111 +		}
       
   112 +	}
       
   113 +
       
   114 +	testPath := `{{tmp}}\test\foo\bar`
       
   115 +
       
   116 +	testsDir := []struct {
       
   117 +		wd   string
       
   118 +		arg  string
       
   119 +		want string
       
   120 +	}{
       
   121 +		// test absolute paths
       
   122 +		{".", `{{tmp}}\test\foo\bar`, `{{tmp}}\test\foo\bar`},
       
   123 +		{".", `{{tmp}}\.\test/foo\bar`, `{{tmp}}\test\foo\bar`},
       
   124 +		{".", `{{tmp}}\test\..\test\foo\bar`, `{{tmp}}\test\foo\bar`},
       
   125 +		{".", `{{tmp}}\TEST\FOO\BAR`, `{{tmp}}\test\foo\bar`},
       
   126 +
       
   127 +		// test relative paths begin with drive letter
       
   128 +		{`{{tmp}}\test`, `{{tmpvol}}.`, `{{tmpvol}}.`},
       
   129 +		{`{{tmp}}\test`, `{{tmpvol}}..`, `{{tmpvol}}..`},
       
   130 +		{`{{tmp}}\test`, `{{tmpvol}}foo\bar`, `{{tmpvol}}foo\bar`},
       
   131 +		{`{{tmp}}\test`, `{{tmpvol}}.\foo\bar`, `{{tmpvol}}foo\bar`},
       
   132 +		{`{{tmp}}\test`, `{{tmpvol}}foo\..\foo\bar`, `{{tmpvol}}foo\bar`},
       
   133 +		{`{{tmp}}\test`, `{{tmpvol}}FOO\BAR`, `{{tmpvol}}foo\bar`},
       
   134 +
       
   135 +		// test relative paths begin with '\'
       
   136 +		{".", `{{tmpnovol}}\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
       
   137 +		{".", `{{tmpnovol}}\.\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
       
   138 +		{".", `{{tmpnovol}}\test\..\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
       
   139 +		{".", `{{tmpnovol}}\TEST\FOO\BAR`, `{{tmpnovol}}\test\foo\bar`},
       
   140 +
       
   141 +		// test relative paths begin without '\'
       
   142 +		{`{{tmp}}\test`, ".", `.`},
       
   143 +		{`{{tmp}}\test`, "..", `..`},
       
   144 +		{`{{tmp}}\test`, `foo\bar`, `foo\bar`},
       
   145 +		{`{{tmp}}\test`, `.\foo\bar`, `foo\bar`},
       
   146 +		{`{{tmp}}\test`, `foo\..\foo\bar`, `foo\bar`},
       
   147 +		{`{{tmp}}\test`, `FOO\BAR`, `foo\bar`},
       
   148 +	}
       
   149 +
       
   150 +	cwd, err := os.Getwd()
       
   151 +	if err != nil {
       
   152 +		t.Fatal(err)
       
   153 +	}
       
   154 +
       
   155 +	defer func() {
       
   156 +		err := os.Chdir(cwd)
       
   157 +		if err != nil {
       
   158 +			t.Fatal(err)
       
   159 +		}
       
   160 +	}()
       
   161 +
       
   162 +	tmp, err := ioutil.TempDir("", "testToNorm")
       
   163 +	if err != nil {
       
   164 +		t.Fatal(err)
       
   165 +	}
       
   166 +	defer os.RemoveAll(tmp)
       
   167 +
       
   168 +	// ioutil.TempDir might return "non-canonical" name.
       
   169 +	tmp, err = filepath.EvalSymlinks(tmp)
       
   170 +	if err != nil {
       
   171 +		t.Fatal(err)
       
   172 +	}
       
   173 +
       
   174 +	err = os.MkdirAll(strings.Replace(testPath, "{{tmp}}", tmp, -1), 0777)
       
   175 +	if err != nil {
       
   176 +		t.Fatal(err)
       
   177 +	}
       
   178 +
       
   179 +	tmpVol := filepath.VolumeName(tmp)
       
   180 +	tmpNoVol := tmp[len(tmpVol):]
       
   181 +
       
   182 +	for _, test := range testsDir {
       
   183 +		wd := strings.Replace(strings.Replace(strings.Replace(test.wd, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
       
   184 +		arg := strings.Replace(strings.Replace(strings.Replace(test.arg, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
       
   185 +		want := strings.Replace(strings.Replace(strings.Replace(test.want, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
       
   186 +
       
   187 +		if test.wd == "." {
       
   188 +			err := os.Chdir(cwd)
       
   189 +			if err != nil {
       
   190 +				t.Error(err)
       
   191 +
       
   192 +				continue
       
   193 +			}
       
   194 +		} else {
       
   195 +			err := os.Chdir(wd)
       
   196 +			if err != nil {
       
   197 +				t.Error(err)
       
   198 +
       
   199 +				continue
       
   200 +			}
       
   201 +		}
       
   202 +
       
   203 +		got, err := filepath.ToNorm(arg, filepath.NormBase)
       
   204 +		if err != nil {
       
   205 +			t.Errorf("toNorm(%s) failed: %v (wd=%s)\n", arg, err, wd)
       
   206 +		} else if got != want {
       
   207 +			t.Errorf("toNorm(%s) returns %s, but %s expected (wd=%s)\n", arg, got, want, wd)
       
   208  		}
       
   209  	}
       
   210  }
       
   211 diff --git a/src/path/filepath/symlink_windows.go b/src/path/filepath/symlink_windows.go
       
   212 index 2433528..bb05aab 100644
       
   213 --- a/src/path/filepath/symlink_windows.go
       
   214 +++ b/src/path/filepath/symlink_windows.go
       
   215 @@ -22,7 +22,7 @@ func normVolumeName(path string) string {
       
   216  	return strings.ToUpper(volume)
       
   217  }
       
   218  
       
   219 -// normBase retruns the last element of path.
       
   220 +// normBase returns the last element of path with correct case.
       
   221  func normBase(path string) (string, error) {
       
   222  	p, err := syscall.UTF16PtrFromString(path)
       
   223  	if err != nil {
       
   224 @@ -40,7 +40,24 @@ func normBase(path string) (string, error) {
       
   225  	return syscall.UTF16ToString(data.FileName[:]), nil
       
   226  }
       
   227  
       
   228 -func toNorm(path string, base func(string) (string, error)) (string, error) {
       
   229 +// baseIsDotDot returns whether the last element of path is "..".
       
   230 +// The given path should be 'Clean'-ed in advance.
       
   231 +func baseIsDotDot(path string) bool {
       
   232 +	i := strings.LastIndexByte(path, Separator)
       
   233 +	return path[i+1:] == ".."
       
   234 +}
       
   235 +
       
   236 +// toNorm returns the normalized path that is guranteed to be unique.
       
   237 +// It should accept the following formats:
       
   238 +//   * UNC paths                              (e.g \\server\share\foo\bar)
       
   239 +//   * absolute paths                         (e.g C:\foo\bar)
       
   240 +//   * relative paths begin with drive letter (e.g C:foo\bar, C:..\foo\bar, C:.., C:.)
       
   241 +//   * relative paths begin with '\'          (e.g \foo\bar)
       
   242 +//   * relative paths begin without '\'       (e.g foo\bar, ..\foo\bar, .., .)
       
   243 +// The returned normalized path will be in the same form (of 5 listed above) as the input path.
       
   244 +// If two paths A and B are indicating the same file with the same format, toNorm(A) should be equal to toNorm(B).
       
   245 +// The normBase parameter should be equal to the normBase func, except for in tests.  See docs on the normBase func.
       
   246 +func toNorm(path string, normBase func(string) (string, error)) (string, error) {
       
   247  	if path == "" {
       
   248  		return path, nil
       
   249  	}
       
   250 @@ -58,7 +75,13 @@ func toNorm(path string, base func(string) (string, error)) (string, error) {
       
   251  	var normPath string
       
   252  
       
   253  	for {
       
   254 -		name, err := base(volume + path)
       
   255 +		if baseIsDotDot(path) {
       
   256 +			normPath = path + `\` + normPath
       
   257 +
       
   258 +			break
       
   259 +		}
       
   260 +
       
   261 +		name, err := normBase(volume + path)
       
   262  		if err != nil {
       
   263  			return "", err
       
   264  		}
       
   265 -- 
       
   266 2.7.4
       
   267