config: prevent use of windows reserved names in config file name
This commit is contained in:
parent
23a0d4a1e6
commit
f2d3264054
4 changed files with 77 additions and 4 deletions
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/rclone/rclone/fs/config/obscure"
|
"github.com/rclone/rclone/fs/config/obscure"
|
||||||
"github.com/rclone/rclone/fs/fspath"
|
"github.com/rclone/rclone/fs/fspath"
|
||||||
"github.com/rclone/rclone/fs/rc"
|
"github.com/rclone/rclone/fs/rc"
|
||||||
|
"github.com/rclone/rclone/lib/file"
|
||||||
"github.com/rclone/rclone/lib/random"
|
"github.com/rclone/rclone/lib/random"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -226,16 +227,20 @@ func GetConfigPath() string {
|
||||||
//
|
//
|
||||||
// Checks for empty string, os null device, or special path, all of which indicates in-memory config.
|
// Checks for empty string, os null device, or special path, all of which indicates in-memory config.
|
||||||
func SetConfigPath(path string) (err error) {
|
func SetConfigPath(path string) (err error) {
|
||||||
|
var cfgPath string
|
||||||
if path == "" || path == os.DevNull {
|
if path == "" || path == os.DevNull {
|
||||||
configPath = ""
|
cfgPath = ""
|
||||||
|
} else if err = file.IsReserved(path); err != nil {
|
||||||
|
return err
|
||||||
} else {
|
} else {
|
||||||
if configPath, err = filepath.Abs(path); err != nil {
|
if cfgPath, err = filepath.Abs(path); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if configPath == noConfigPath {
|
if cfgPath == noConfigPath {
|
||||||
configPath = ""
|
cfgPath = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
configPath = cfgPath
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,3 +13,8 @@ import "os"
|
||||||
// Under both Unix and Windows this will allow open files to be
|
// Under both Unix and Windows this will allow open files to be
|
||||||
// renamed and or deleted.
|
// renamed and or deleted.
|
||||||
var OpenFile = os.OpenFile
|
var OpenFile = os.OpenFile
|
||||||
|
|
||||||
|
// IsReserved checks if path contains a reserved name
|
||||||
|
func IsReserved(path string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -152,3 +153,29 @@ func TestOpenFileOperations(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Smoke test the IsReserved function
|
||||||
|
func TestIsReserved(t *testing.T) {
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
t.Skip("Skipping test on !windows")
|
||||||
|
}
|
||||||
|
// Regular name
|
||||||
|
require.NoError(t, IsReserved("readme.txt"))
|
||||||
|
require.NoError(t, IsReserved("some/path/readme.txt"))
|
||||||
|
// Empty
|
||||||
|
require.Error(t, IsReserved(""))
|
||||||
|
// Separators only
|
||||||
|
require.Error(t, IsReserved("/"))
|
||||||
|
require.Error(t, IsReserved("////"))
|
||||||
|
require.Error(t, IsReserved("./././././"))
|
||||||
|
// Legacy device name
|
||||||
|
require.Error(t, IsReserved("NUL"))
|
||||||
|
require.Error(t, IsReserved("nul"))
|
||||||
|
require.Error(t, IsReserved("Nul"))
|
||||||
|
require.Error(t, IsReserved("NUL.txt"))
|
||||||
|
require.Error(t, IsReserved("some/path/to/nul.txt"))
|
||||||
|
require.NoError(t, IsReserved("NULL"))
|
||||||
|
// Name end with a space or a period
|
||||||
|
require.Error(t, IsReserved("test."))
|
||||||
|
require.Error(t, IsReserved("test "))
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
package file
|
package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -64,3 +67,36 @@ func OpenFile(path string, mode int, perm os.FileMode) (*os.File, error) {
|
||||||
}
|
}
|
||||||
return os.NewFile(uintptr(h), path), nil
|
return os.NewFile(uintptr(h), path), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsReserved checks if path contains a reserved name
|
||||||
|
func IsReserved(path string) error {
|
||||||
|
if path == "" {
|
||||||
|
return errors.New("path is empty")
|
||||||
|
}
|
||||||
|
base := filepath.Base(path)
|
||||||
|
// If the path is empty or reduces to ".", Base returns ".".
|
||||||
|
if base == "." {
|
||||||
|
return errors.New("path is '.'")
|
||||||
|
}
|
||||||
|
// If the path consists entirely of separators, Base returns a single separator.
|
||||||
|
if base == string(filepath.Separator) {
|
||||||
|
return errors.New("path consists entirely of separators")
|
||||||
|
}
|
||||||
|
// Do not end a file or directory name with a space or a period. Although the underlying
|
||||||
|
// file system may support such names, the Windows shell and user interface does not.
|
||||||
|
// (https://docs.microsoft.com/en-gb/windows/win32/fileio/naming-a-file)
|
||||||
|
suffix := base[len(base)-1]
|
||||||
|
switch suffix {
|
||||||
|
case ' ':
|
||||||
|
return errors.New("base file name ends with a space")
|
||||||
|
case '.':
|
||||||
|
return errors.New("base file name ends with a period")
|
||||||
|
}
|
||||||
|
// Do not use names of legacy (DOS) devices, not even as basename without extension,
|
||||||
|
// as this will refer to the actual device.
|
||||||
|
// (https://docs.microsoft.com/en-gb/windows/win32/fileio/naming-a-file)
|
||||||
|
if reserved, _ := regexp.MatchString(`^(?i:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)`, base); reserved {
|
||||||
|
return errors.New("base file name is reserved windows device name (CON, PRN, AUX, NUL, COM[1-9], LPT[1-9])")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue