Skip to content

Commit

Permalink
config: prevent use of windows reserved names in config file name
Browse files Browse the repository at this point in the history
  • Loading branch information
albertony committed Apr 12, 2021
1 parent 23a0d4a commit f2d3264
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 4 deletions.
13 changes: 9 additions & 4 deletions fs/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/rclone/rclone/fs/config/obscure"
"github.com/rclone/rclone/fs/fspath"
"github.com/rclone/rclone/fs/rc"
"github.com/rclone/rclone/lib/file"
"github.com/rclone/rclone/lib/random"
)

Expand Down Expand Up @@ -226,16 +227,20 @@ func GetConfigPath() string {
//
// Checks for empty string, os null device, or special path, all of which indicates in-memory config.
func SetConfigPath(path string) (err error) {
var cfgPath string
if path == "" || path == os.DevNull {
configPath = ""
cfgPath = ""
} else if err = file.IsReserved(path); err != nil {
return err
} else {
if configPath, err = filepath.Abs(path); err != nil {
if cfgPath, err = filepath.Abs(path); err != nil {
return err
}
if configPath == noConfigPath {
configPath = ""
if cfgPath == noConfigPath {
cfgPath = ""
}
}
configPath = cfgPath
return nil
}

Expand Down
5 changes: 5 additions & 0 deletions lib/file/file_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ import "os"
// Under both Unix and Windows this will allow open files to be
// renamed and or deleted.
var OpenFile = os.OpenFile

// IsReserved checks if path contains a reserved name
func IsReserved(path string) error {
return nil
}
27 changes: 27 additions & 0 deletions lib/file/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"path"
"runtime"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -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 "))
}
36 changes: 36 additions & 0 deletions lib/file/file_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
package file

import (
"errors"
"os"
"path/filepath"
"regexp"
"syscall"
)

Expand Down Expand Up @@ -64,3 +67,36 @@ func OpenFile(path string, mode int, perm os.FileMode) (*os.File, error) {
}
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
}

0 comments on commit f2d3264

Please sign in to comment.