From ad22356ec660844ec43ccbe9a834845f1a6f7cf8 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Wed, 24 Apr 2024 10:24:05 -0700 Subject: all: rename internal/safefilepath to internal/filepathlite The safefilepath package was originally added to contain the FromFS function. We subsequently added FromFS to path/filepath as Localize. The safefilepath package now exists only to permit the os package to import Localize. Rename safefilepath to filepathlite to better indicate that it's a low-dependency version of filepath. Change-Id: I4c5f9b28e8581f841947b48c5cac9954cd0c9535 Reviewed-on: https://go-review.googlesource.com/c/go/+/581517 LUCI-TryBot-Result: Go LUCI Reviewed-by: Ian Lance Taylor --- src/go/build/deps_test.go | 4 +- src/internal/filepathlite/path.go | 26 ++++++ src/internal/filepathlite/path_plan9.go | 14 +++ src/internal/filepathlite/path_unix.go | 16 ++++ src/internal/filepathlite/path_windows.go | 136 ++++++++++++++++++++++++++++++ src/internal/safefilepath/path.go | 26 ------ src/internal/safefilepath/path_plan9.go | 14 --- src/internal/safefilepath/path_unix.go | 16 ---- src/internal/safefilepath/path_windows.go | 136 ------------------------------ src/os/dir.go | 6 +- src/os/file.go | 4 +- src/path/filepath/path.go | 4 +- src/path/filepath/path_windows.go | 4 +- 13 files changed, 203 insertions(+), 203 deletions(-) create mode 100644 src/internal/filepathlite/path.go create mode 100644 src/internal/filepathlite/path_plan9.go create mode 100644 src/internal/filepathlite/path_unix.go create mode 100644 src/internal/filepathlite/path_windows.go delete mode 100644 src/internal/safefilepath/path.go delete mode 100644 src/internal/safefilepath/path_plan9.go delete mode 100644 src/internal/safefilepath/path_unix.go delete mode 100644 src/internal/safefilepath/path_windows.go (limited to 'src') diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index a3ba8092be..14880d9ef1 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -174,7 +174,7 @@ var depsRules = ` io/fs < internal/testlog < internal/poll - < internal/safefilepath + < internal/filepathlite < os < os/signal; @@ -183,7 +183,7 @@ var depsRules = ` unicode, fmt !< net, os, os/signal; - os/signal, internal/safefilepath, STR + os/signal, internal/filepathlite, STR < path/filepath < io/ioutil; diff --git a/src/internal/filepathlite/path.go b/src/internal/filepathlite/path.go new file mode 100644 index 0000000000..b452987b6b --- /dev/null +++ b/src/internal/filepathlite/path.go @@ -0,0 +1,26 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package filepathlite manipulates operating-system file paths. +package filepathlite + +import ( + "errors" + "io/fs" +) + +var errInvalidPath = errors.New("invalid path") + +// Localize is filepath.Localize. +// +// It is implemented in this package to avoid a dependency cycle +// between os and file/filepath. +// +// Tests for this function are in path/filepath. +func Localize(path string) (string, error) { + if !fs.ValidPath(path) { + return "", errInvalidPath + } + return localize(path) +} diff --git a/src/internal/filepathlite/path_plan9.go b/src/internal/filepathlite/path_plan9.go new file mode 100644 index 0000000000..91a95ddb06 --- /dev/null +++ b/src/internal/filepathlite/path_plan9.go @@ -0,0 +1,14 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package filepathlite + +import "internal/bytealg" + +func localize(path string) (string, error) { + if path[0] == '#' || bytealg.IndexByteString(path, 0) >= 0 { + return "", errInvalidPath + } + return path, nil +} diff --git a/src/internal/filepathlite/path_unix.go b/src/internal/filepathlite/path_unix.go new file mode 100644 index 0000000000..edad20817f --- /dev/null +++ b/src/internal/filepathlite/path_unix.go @@ -0,0 +1,16 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build unix || (js && wasm) || wasip1 + +package filepathlite + +import "internal/bytealg" + +func localize(path string) (string, error) { + if bytealg.IndexByteString(path, 0) >= 0 { + return "", errInvalidPath + } + return path, nil +} diff --git a/src/internal/filepathlite/path_windows.go b/src/internal/filepathlite/path_windows.go new file mode 100644 index 0000000000..3d7290b14c --- /dev/null +++ b/src/internal/filepathlite/path_windows.go @@ -0,0 +1,136 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package filepathlite + +import ( + "internal/bytealg" + "syscall" +) + +func localize(path string) (string, error) { + for i := 0; i < len(path); i++ { + switch path[i] { + case ':', '\\', 0: + return "", errInvalidPath + } + } + containsSlash := false + for p := path; p != ""; { + // Find the next path element. + var element string + i := bytealg.IndexByteString(p, '/') + if i < 0 { + element = p + p = "" + } else { + containsSlash = true + element = p[:i] + p = p[i+1:] + } + if IsReservedName(element) { + return "", errInvalidPath + } + } + if containsSlash { + // We can't depend on strings, so substitute \ for / manually. + buf := []byte(path) + for i, b := range buf { + if b == '/' { + buf[i] = '\\' + } + } + path = string(buf) + } + return path, nil +} + +// IsReservedName reports if name is a Windows reserved device name. +// It does not detect names with an extension, which are also reserved on some Windows versions. +// +// For details, search for PRN in +// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file. +func IsReservedName(name string) bool { + // Device names can have arbitrary trailing characters following a dot or colon. + base := name + for i := 0; i < len(base); i++ { + switch base[i] { + case ':', '.': + base = base[:i] + } + } + // Trailing spaces in the last path element are ignored. + for len(base) > 0 && base[len(base)-1] == ' ' { + base = base[:len(base)-1] + } + if !isReservedBaseName(base) { + return false + } + if len(base) == len(name) { + return true + } + // The path element is a reserved name with an extension. + // Some Windows versions consider this a reserved name, + // while others do not. Use FullPath to see if the name is + // reserved. + if p, _ := syscall.FullPath(name); len(p) >= 4 && p[:4] == `\\.\` { + return true + } + return false +} + +func isReservedBaseName(name string) bool { + if len(name) == 3 { + switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) { + case "CON", "PRN", "AUX", "NUL": + return true + } + } + if len(name) >= 4 { + switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) { + case "COM", "LPT": + if len(name) == 4 && '1' <= name[3] && name[3] <= '9' { + return true + } + // Superscript ¹, ², and ³ are considered numbers as well. + switch name[3:] { + case "\u00b2", "\u00b3", "\u00b9": + return true + } + return false + } + } + + // Passing CONIN$ or CONOUT$ to CreateFile opens a console handle. + // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#consoles + // + // While CONIN$ and CONOUT$ aren't documented as being files, + // they behave the same as CON. For example, ./CONIN$ also opens the console input. + if len(name) == 6 && name[5] == '$' && equalFold(name, "CONIN$") { + return true + } + if len(name) == 7 && name[6] == '$' && equalFold(name, "CONOUT$") { + return true + } + return false +} + +func equalFold(a, b string) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if toUpper(a[i]) != toUpper(b[i]) { + return false + } + } + return true +} + +func toUpper(c byte) byte { + if 'a' <= c && c <= 'z' { + return c - ('a' - 'A') + } + return c +} diff --git a/src/internal/safefilepath/path.go b/src/internal/safefilepath/path.go deleted file mode 100644 index c2cc6ce5d4..0000000000 --- a/src/internal/safefilepath/path.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package safefilepath manipulates operating-system file paths. -package safefilepath - -import ( - "errors" - "io/fs" -) - -var errInvalidPath = errors.New("invalid path") - -// Localize is filepath.Localize. -// -// It is implemented in this package to avoid a dependency cycle -// between os and file/filepath. -// -// Tests for this function are in path/filepath. -func Localize(path string) (string, error) { - if !fs.ValidPath(path) { - return "", errInvalidPath - } - return localize(path) -} diff --git a/src/internal/safefilepath/path_plan9.go b/src/internal/safefilepath/path_plan9.go deleted file mode 100644 index 55627c5102..0000000000 --- a/src/internal/safefilepath/path_plan9.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package safefilepath - -import "internal/bytealg" - -func localize(path string) (string, error) { - if path[0] == '#' || bytealg.IndexByteString(path, 0) >= 0 { - return "", errInvalidPath - } - return path, nil -} diff --git a/src/internal/safefilepath/path_unix.go b/src/internal/safefilepath/path_unix.go deleted file mode 100644 index 873d0935ec..0000000000 --- a/src/internal/safefilepath/path_unix.go +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build unix || (js && wasm) || wasip1 - -package safefilepath - -import "internal/bytealg" - -func localize(path string) (string, error) { - if bytealg.IndexByteString(path, 0) >= 0 { - return "", errInvalidPath - } - return path, nil -} diff --git a/src/internal/safefilepath/path_windows.go b/src/internal/safefilepath/path_windows.go deleted file mode 100644 index b626196f11..0000000000 --- a/src/internal/safefilepath/path_windows.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package safefilepath - -import ( - "internal/bytealg" - "syscall" -) - -func localize(path string) (string, error) { - for i := 0; i < len(path); i++ { - switch path[i] { - case ':', '\\', 0: - return "", errInvalidPath - } - } - containsSlash := false - for p := path; p != ""; { - // Find the next path element. - var element string - i := bytealg.IndexByteString(p, '/') - if i < 0 { - element = p - p = "" - } else { - containsSlash = true - element = p[:i] - p = p[i+1:] - } - if IsReservedName(element) { - return "", errInvalidPath - } - } - if containsSlash { - // We can't depend on strings, so substitute \ for / manually. - buf := []byte(path) - for i, b := range buf { - if b == '/' { - buf[i] = '\\' - } - } - path = string(buf) - } - return path, nil -} - -// IsReservedName reports if name is a Windows reserved device name. -// It does not detect names with an extension, which are also reserved on some Windows versions. -// -// For details, search for PRN in -// https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file. -func IsReservedName(name string) bool { - // Device names can have arbitrary trailing characters following a dot or colon. - base := name - for i := 0; i < len(base); i++ { - switch base[i] { - case ':', '.': - base = base[:i] - } - } - // Trailing spaces in the last path element are ignored. - for len(base) > 0 && base[len(base)-1] == ' ' { - base = base[:len(base)-1] - } - if !isReservedBaseName(base) { - return false - } - if len(base) == len(name) { - return true - } - // The path element is a reserved name with an extension. - // Some Windows versions consider this a reserved name, - // while others do not. Use FullPath to see if the name is - // reserved. - if p, _ := syscall.FullPath(name); len(p) >= 4 && p[:4] == `\\.\` { - return true - } - return false -} - -func isReservedBaseName(name string) bool { - if len(name) == 3 { - switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) { - case "CON", "PRN", "AUX", "NUL": - return true - } - } - if len(name) >= 4 { - switch string([]byte{toUpper(name[0]), toUpper(name[1]), toUpper(name[2])}) { - case "COM", "LPT": - if len(name) == 4 && '1' <= name[3] && name[3] <= '9' { - return true - } - // Superscript ¹, ², and ³ are considered numbers as well. - switch name[3:] { - case "\u00b2", "\u00b3", "\u00b9": - return true - } - return false - } - } - - // Passing CONIN$ or CONOUT$ to CreateFile opens a console handle. - // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#consoles - // - // While CONIN$ and CONOUT$ aren't documented as being files, - // they behave the same as CON. For example, ./CONIN$ also opens the console input. - if len(name) == 6 && name[5] == '$' && equalFold(name, "CONIN$") { - return true - } - if len(name) == 7 && name[6] == '$' && equalFold(name, "CONOUT$") { - return true - } - return false -} - -func equalFold(a, b string) bool { - if len(a) != len(b) { - return false - } - for i := 0; i < len(a); i++ { - if toUpper(a[i]) != toUpper(b[i]) { - return false - } - } - return true -} - -func toUpper(c byte) byte { - if 'a' <= c && c <= 'z' { - return c - ('a' - 'A') - } - return c -} diff --git a/src/os/dir.go b/src/os/dir.go index dcc18e1814..d75ac17193 100644 --- a/src/os/dir.go +++ b/src/os/dir.go @@ -5,7 +5,7 @@ package os import ( - "internal/safefilepath" + "internal/filepathlite" "io" "io/fs" "sort" @@ -146,7 +146,7 @@ func CopyFS(dir string, fsys fs.FS) error { return err } - fpath, err := safefilepath.Localize(path) + fpath, err := filepathlite.Localize(path) if err != nil { return err } @@ -157,7 +157,7 @@ func CopyFS(dir string, fsys fs.FS) error { // TODO(panjf2000): handle symlinks with the help of fs.ReadLinkFS // once https://go.dev/issue/49580 is done. - // we also need safefilepath.IsLocal from https://go.dev/cl/564295. + // we also need filepathlite.IsLocal from https://go.dev/cl/564295. if !d.Type().IsRegular() { return &PathError{Op: "CopyFS", Path: path, Err: ErrInvalid} } diff --git a/src/os/file.go b/src/os/file.go index ec8ad70660..c3ee31583e 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -45,8 +45,8 @@ package os import ( "errors" + "internal/filepathlite" "internal/poll" - "internal/safefilepath" "internal/testlog" "io" "io/fs" @@ -766,7 +766,7 @@ func (dir dirFS) join(name string) (string, error) { if dir == "" { return "", errors.New("os: DirFS with empty root") } - name, err := safefilepath.Localize(name) + name, err := filepathlite.Localize(name) if err != nil { return "", ErrInvalid } diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go index 6c8a0aa8b3..cd70c2b318 100644 --- a/src/path/filepath/path.go +++ b/src/path/filepath/path.go @@ -13,7 +13,7 @@ package filepath import ( "errors" - "internal/safefilepath" + "internal/filepathlite" "io/fs" "os" "slices" @@ -221,7 +221,7 @@ func unixIsLocal(path string) bool { // // The path returned by Localize will always be local, as reported by IsLocal. func Localize(path string) (string, error) { - return safefilepath.Localize(path) + return filepathlite.Localize(path) } // ToSlash returns the result of replacing each separator character diff --git a/src/path/filepath/path_windows.go b/src/path/filepath/path_windows.go index 6adb7d4bc4..44037c45ac 100644 --- a/src/path/filepath/path_windows.go +++ b/src/path/filepath/path_windows.go @@ -5,7 +5,7 @@ package filepath import ( - "internal/safefilepath" + "internal/filepathlite" "os" "strings" "syscall" @@ -42,7 +42,7 @@ func isLocal(path string) bool { if part == "." || part == ".." { hasDots = true } - if safefilepath.IsReservedName(part) { + if filepathlite.IsReservedName(part) { return false } } -- cgit v1.2.3-54-g00ecf