// Export for testing.
var (
- AddExtendedPrefix = addExtendedPrefix
- NewConsoleFile = newConsoleFile
- CommandLineToArgv = commandLineToArgv
- AllowReadDirFileID = &allowReadDirFileID
- ValidatePathForCreate = validatePathForCreate
+ AddExtendedPrefix = addExtendedPrefix
+ NewConsoleFile = newConsoleFile
+ CommandLineToArgv = commandLineToArgv
+ AllowReadDirFileID = &allowReadDirFileID
)
// bits (before umask).
// If there is an error, it will be of type *PathError.
func Mkdir(name string, perm FileMode) error {
- err := mkdir(name, perm)
- if err != nil {
- return &PathError{Op: "mkdir", Path: name, Err: err}
+ longName := fixLongPath(name)
+ e := ignoringEINTR(func() error {
+ return syscall.Mkdir(longName, syscallMode(perm))
+ })
+
+ if e != nil {
+ return &PathError{Op: "mkdir", Path: name, Err: e}
}
+
// mkdir(2) itself won't handle the sticky bit on *BSD and Solaris
if !supportsCreateWithStickyBit && perm&ModeSticky != 0 {
- err = setStickyBit(name)
- if err != nil {
+ e = setStickyBit(name)
+
+ if e != nil {
Remove(name)
- return err
+ return e
}
}
+
return nil
}
+++ /dev/null
-// Copyright 2024 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 !windows
-
-package os
-
-import "syscall"
-
-func mkdir(name string, perm FileMode) error {
- return ignoringEINTR(func() error {
- return syscall.Mkdir(name, syscallMode(perm))
- })
-}
"unsafe"
)
-var errInvalidPath = errors.New("invalid path: cannot end with a space or period")
-
// This matches the value in syscall/syscall_windows.go.
const _UTIME_OMIT = -1
if name == "" {
return nil, &PathError{Op: "open", Path: name, Err: syscall.ENOENT}
}
- if flag&O_CREATE != 0 && !validatePathForCreate(name) {
- return nil, &PathError{Op: "open", Path: name, Err: errInvalidPath}
- }
path := fixLongPath(name)
r, err := syscall.Open(path, flag|syscall.O_CLOEXEC, syscallMode(perm))
if err != nil {
return openFileNolog(name, O_RDONLY, 0)
}
-func mkdir(name string, perm FileMode) error {
- if !validatePathForCreate(name) {
- return errInvalidPath
- }
- longName := fixLongPath(name)
- return syscall.Mkdir(longName, syscallMode(perm))
-}
-
func (file *file) close() error {
if file == nil {
return syscall.EINVAL
}
func rename(oldname, newname string) error {
- if !validatePathForCreate(newname) {
- return &LinkError{"rename", oldname, newname, errInvalidPath}
- }
e := windows.Rename(fixLongPath(oldname), fixLongPath(newname))
if e != nil {
return &LinkError{"rename", oldname, newname, e}
// Link creates newname as a hard link to the oldname file.
// If there is an error, it will be of type *LinkError.
func Link(oldname, newname string) error {
- if !validatePathForCreate(newname) {
- return &LinkError{"link", oldname, newname, errInvalidPath}
- }
n, err := syscall.UTF16PtrFromString(fixLongPath(newname))
if err != nil {
return &LinkError{"link", oldname, newname, err}
// if oldname is later created as a directory the symlink will not work.
// If there is an error, it will be of type *LinkError.
func Symlink(oldname, newname string) error {
- if !validatePathForCreate(newname) {
- return &LinkError{"symlink", oldname, newname, errInvalidPath}
- }
// '/' does not work in link's content
oldname = filepathlite.FromSlash(oldname)
t.Errorf("SameFile(%v, %v) = false; want true", f2, f2s)
}
}
-
-func TestOpen_InvalidPath(t *testing.T) {
- dir := t.TempDir()
-
- file, err := os.Open(dir + ".")
- if err != nil {
- t.Errorf("Open(%q) should have succeeded, got %v", dir+".", err)
- } else {
- file.Close()
- }
-
- file, err = os.Open(dir + " ")
- if err != nil {
- t.Errorf("Open(%q) should have succeeded, got %v", dir+" ", err)
- } else {
- file.Close()
- }
-}
-
-func TestMkdirAll_InvalidPath(t *testing.T) {
- // Parent folder contains traling spaces
- path := `C:\temp\folder \this one fails`
- err := os.MkdirAll(path, 0644)
- if err == nil {
- t.Errorf("MkdirAll(%q) should have failed", path)
- } else if !strings.Contains(err.Error(), "invalid path: cannot end with a space or period") {
- t.Errorf("expected errInvalidPath for path %q, got %v", path, err)
- }
-}
-
-func TestCreate_InvalidPath(t *testing.T) {
- testInvalidPath(t, func(_, path string) error {
- _, err := os.Create(path)
- return err
- })
-}
-
-func TestMkdir_InvalidPath(t *testing.T) {
- testInvalidPath(t, func(_, path string) error {
- return os.Mkdir(path, 0644)
- })
-}
-
-func TestRename_InvalidPath(t *testing.T) {
- testInvalidPath(t, os.Rename)
-}
-
-func TestLink_InvalidPath(t *testing.T) {
- testInvalidPath(t, os.Link)
-}
-
-func TestSymlink_InvalidPath(t *testing.T) {
- testInvalidPath(t, os.Symlink)
-}
-
-func testInvalidPath(t *testing.T, fn func(src, dest string) error) {
- dir := t.TempDir()
-
- // Test invalid paths (with trailing space and period)
- invalidPaths := []string{
- filepath.Join(dir, "invalid_dir "), // path ending in space
- filepath.Join(dir, "invalid_dir."), // path ending in period
- }
-
- for _, path := range invalidPaths {
- err := fn(dir, path)
- if err == nil {
- t.Errorf("(%q, %q) should have failed", dir, path)
- } else if !strings.Contains(err.Error(), "invalid path: cannot end with a space or period") {
- t.Errorf("expected errInvalidPath for path %q, got %v", path, err)
- }
- }
-}
import (
"internal/filepathlite"
- "internal/stringslite"
"internal/syscall/windows"
"syscall"
)
copy(buf, prefix)
return syscall.UTF16ToString(buf)
}
-
-// validatePathForCreate checks if a given path is valid for creation on a Windows system.
-// It returns true if the path is considered valid, and false otherwise.
-// The function performs the following checks:
-// 1. If the path is empty, it is considered valid.
-// 2. If the path starts with `\\?\` or \??\, it is considered valid without further checks.
-// 3. Otherwise, a path ending with a space or . is invalid.
-func validatePathForCreate(path string) bool {
- // Check if the path is empty.
- if len(path) == 0 {
- return true
- }
- // Paths starting with \\?\ should be considered valid without further checks.
- if stringslite.HasPrefix(path, `\\?\`) || stringslite.HasPrefix(path, `\??\`) {
- return true
- }
- // Get the base name of the path to check only the last component.
- base := filepathlite.Base(path)
- // Check if the last character of the base name is a space or period, which is invalid.
- switch base[len(base)-1] {
- case ' ':
- return false
- case '.':
- return base == "." || base == ".."
- default:
- return true
- }
-}
os.AddExtendedPrefix(veryLong)
}
}
-
-func TestValidatePathForCreate(t *testing.T) {
- tests := []struct {
- path string
- pass bool
- }{
- {`C:\foo`, true},
- {`C:\foo\ `, false},
- {`C:\foo\.`, true},
- {`C:\foo.`, false},
- {`C:\foo\ .`, false},
- {`C:\foo \`, false},
- {"C:/foo/", true},
- {"C:/foo", true},
- {"C:/foo/.", true},
- {"C:/foo/ .", false},
- {".", true},
- {"..", true},
- {"...", false},
- {`\\?\C:\foo`, true},
- {`\\?\C:\foo\ `, true},
- {`\\?\C:\foo\.`, true},
- {`\??\C:\foo`, true},
- {`\??\C:\foo\ `, true},
- {`\??\C:\foo\.`, true},
- {`\\server\share\path`, true},
- {"", true},
- {" ", false},
- {"C:.", true},
- {"C: ", false},
- {"C:..", true},
- {"C:...", false},
- {"foo:.", false},
- {"C:bar:..", false},
- {`\..`, true},
- {`\...`, false},
- {"/.", true},
- {"/..", true},
- {"a..", false},
- {"aa..", false},
- {"a ", false},
- {`a\ `, false},
- {`a \`, false},
- {`.\`, true},
- {`..\`, true},
- }
-
- for _, tt := range tests {
- if os.ValidatePathForCreate(tt.path) != tt.pass {
- t.Errorf("validatePathForCreate(%q) = %v, want %v", tt.path, !tt.pass, tt.pass)
- }
- }
-}