mirror of
https://github.com/lxc/incus
synced 2026-08-02 05:26:46 +00:00
incus: Fix remote path handling on Windows
Remote instance and volume paths are always POSIX, so use "path" rather than "path/filepath" when manipulating them. Closes #3614 Signed-off-by: Stéphane Graber <stgraber@stgraber.org>
This commit is contained in:
parent
bc9cc1586d
commit
a08f2fadd5
@ -7,6 +7,7 @@ import (
|
||||
"io/fs"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
@ -148,7 +149,7 @@ func (c *cmdFileCreate) run(cmd *cobra.Command, args []string) error {
|
||||
return errors.New(i18n.G(`Symlink target path can only be used for type "symlink"`))
|
||||
}
|
||||
|
||||
symlinkTargetPath = filepath.Clean(symlinkTargetPath)
|
||||
symlinkTargetPath = path.Clean(symlinkTargetPath)
|
||||
}
|
||||
|
||||
if isDir {
|
||||
@ -194,7 +195,7 @@ func (c *cmdFileCreate) run(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// Create needed paths if requested
|
||||
if c.file.flagMkdir {
|
||||
err = sftpRecursiveMkdir(sftpConn, filepath.Dir(targetPath), nil, int64(uid), int64(gid))
|
||||
err = sftpRecursiveMkdir(sftpConn, path.Dir(targetPath), nil, int64(uid), int64(gid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -307,7 +308,7 @@ func (c *cmdFileDelete) run(cmd *cobra.Command, args []string) error {
|
||||
err := func() error {
|
||||
d := p.RemoteServer
|
||||
instanceName := p.RemoteObject.List[0].String
|
||||
path, _ := normalizePath(p.RemoteObject.List[1].String)
|
||||
filePath, _ := normalizePath(p.RemoteObject.List[1].String)
|
||||
instanceID := p.RemoteName + ":" + instanceName
|
||||
|
||||
sftpConn, ok := sftpClients[instanceID]
|
||||
@ -321,7 +322,7 @@ func (c *cmdFileDelete) run(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
if c.flagForce {
|
||||
err = sftpConn.RemoveAll(path)
|
||||
err = sftpConn.RemoveAll(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -329,7 +330,7 @@ func (c *cmdFileDelete) run(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return sftpConn.Remove(path)
|
||||
return sftpConn.Remove(filePath)
|
||||
}()
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
@ -387,7 +388,7 @@ func (c *cmdFileEdit) run(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Create temp file
|
||||
f, err := os.CreateTemp("", fmt.Sprintf("incus_file_edit_*%s", filepath.Ext(fileName)))
|
||||
f, err := os.CreateTemp("", fmt.Sprintf("incus_file_edit_*%s", path.Ext(fileName)))
|
||||
if err != nil {
|
||||
return fmt.Errorf(i18n.G("Unable to create a temporary file: %v"), err)
|
||||
}
|
||||
@ -524,7 +525,7 @@ func (c *cmdFilePull) pull(parsedFiles []*u.Parsed, target string) error {
|
||||
err := func() error {
|
||||
d := p.RemoteServer
|
||||
instanceName := p.RemoteObject.List[0].String
|
||||
path := p.RemoteObject.List[1].String
|
||||
filePath := p.RemoteObject.List[1].String
|
||||
instanceID := p.RemoteName + ":" + instanceName
|
||||
|
||||
sftpConn, ok := sftpClients[instanceID]
|
||||
@ -537,20 +538,20 @@ func (c *cmdFilePull) pull(parsedFiles []*u.Parsed, target string) error {
|
||||
sftpClients[instanceID] = sftpConn
|
||||
}
|
||||
|
||||
srcInfo, normalizedPath, err := c.puller.statFile(sftpConn, path)
|
||||
srcInfo, normalizedPath, err := c.puller.statFile(sftpConn, filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Recursively copy directories.
|
||||
if srcInfo.IsDir() {
|
||||
return sftpRecursivePullFile(sftpConn, srcInfo, path, normalizedPath, target, c.global.flagQuiet, c.puller.flagDereference, len(parsedFiles) > 1 || util.PathExists(target))
|
||||
return sftpRecursivePullFile(sftpConn, srcInfo, filePath, normalizedPath, target, c.global.flagQuiet, c.puller.flagDereference, len(parsedFiles) > 1 || util.PathExists(target))
|
||||
}
|
||||
|
||||
// Determine the target path.
|
||||
var targetPath string
|
||||
if targetIsDir {
|
||||
targetPath = filepath.Join(target, filepath.Base(normalizedPath))
|
||||
targetPath = filepath.Join(target, path.Base(normalizedPath))
|
||||
} else {
|
||||
targetPath = target
|
||||
}
|
||||
@ -749,7 +750,7 @@ func (c *cmdFilePush) push(srcFiles []string, parsedTarget *u.Parsed) error {
|
||||
canProcessStdin := len(srcFiles) == 1
|
||||
|
||||
// Push the files
|
||||
for _, path := range srcFiles {
|
||||
for _, srcPath := range srcFiles {
|
||||
err := func() error {
|
||||
var f *os.File
|
||||
var linkTarget string
|
||||
@ -760,7 +761,7 @@ func (c *cmdFilePush) push(srcFiles []string, parsedTarget *u.Parsed) error {
|
||||
Mode: mode,
|
||||
}
|
||||
|
||||
if isStdin(path) {
|
||||
if isStdin(srcPath) {
|
||||
if !canProcessStdin {
|
||||
return errors.New(i18n.G("stdin can only be used once, with no other source arguments"))
|
||||
}
|
||||
@ -772,23 +773,23 @@ func (c *cmdFilePush) push(srcFiles []string, parsedTarget *u.Parsed) error {
|
||||
canProcessStdin = false
|
||||
f = os.Stdin
|
||||
} else {
|
||||
srcInfo, wPath, err := c.pusher.statFile(path)
|
||||
srcInfo, wPath, err := c.pusher.statFile(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Recursively copy directories.
|
||||
if srcInfo.IsDir() {
|
||||
return sftpRecursivePushFile(sftpConn, wPath, path, target, args, c.global.flagQuiet, c.pusher.flagDereference, len(srcFiles) > 1 || targetExists)
|
||||
return sftpRecursivePushFile(sftpConn, wPath, srcPath, target, args, c.global.flagQuiet, c.pusher.flagDereference, len(srcFiles) > 1 || targetExists)
|
||||
}
|
||||
|
||||
if srcInfo.Mode()&os.ModeSymlink != 0 {
|
||||
linkTarget, err = os.Readlink(path)
|
||||
linkTarget, err = os.Readlink(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
f, err = os.Open(path)
|
||||
f, err = os.Open(srcPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf(i18n.G("Failed to open source file %q: %v"), f, err)
|
||||
}
|
||||
@ -815,7 +816,7 @@ func (c *cmdFilePush) push(srcFiles []string, parsedTarget *u.Parsed) error {
|
||||
// Determine the target path.
|
||||
var targetPath string
|
||||
if targetIsDir {
|
||||
targetPath = filepath.Join(target, filepath.Base(path))
|
||||
targetPath = path.Join(target, filepath.Base(srcPath))
|
||||
} else {
|
||||
targetPath = target
|
||||
}
|
||||
@ -823,7 +824,7 @@ func (c *cmdFilePush) push(srcFiles []string, parsedTarget *u.Parsed) error {
|
||||
// Create needed paths if requested
|
||||
if c.file.flagMkdir {
|
||||
mode := os.FileMode(DirMode)
|
||||
err = sftpRecursiveMkdir(sftpConn, filepath.Dir(targetPath), &mode, int64(args.UID), int64(args.GID))
|
||||
err = sftpRecursiveMkdir(sftpConn, path.Dir(targetPath), &mode, int64(args.UID), int64(args.GID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -839,7 +840,7 @@ func (c *cmdFilePush) push(srcFiles []string, parsedTarget *u.Parsed) error {
|
||||
|
||||
// Transfer the files.
|
||||
progress := cli.ProgressRenderer{
|
||||
Format: fmt.Sprintf(i18n.G("Pushing %s to %s: %%s"), path, targetPath),
|
||||
Format: fmt.Sprintf(i18n.G("Pushing %s to %s: %%s"), srcPath, targetPath),
|
||||
Quiet: c.global.flagQuiet,
|
||||
}
|
||||
|
||||
@ -861,7 +862,7 @@ func (c *cmdFilePush) push(srcFiles []string, parsedTarget *u.Parsed) error {
|
||||
}, f)
|
||||
}
|
||||
|
||||
logger.Infof("Pushing %s to %s (%s)", path, targetPath, args.Type)
|
||||
logger.Infof("Pushing %s to %s (%s)", srcPath, targetPath, args.Type)
|
||||
err = sftpCreateFile(sftpConn, targetPath, args, true)
|
||||
progress.Done("")
|
||||
return err
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"io/fs"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
@ -134,7 +135,7 @@ func (c *cmdStorageVolumeFileCreate) run(cmd *cobra.Command, args []string) erro
|
||||
volName := parsed[1].List[0].String
|
||||
targetPath, isDir := normalizePath(parsed[1].List[1].String)
|
||||
isSymlink := !parsed[2].Skipped
|
||||
symlinkTargetPath := filepath.Clean(parsed[2].String)
|
||||
symlinkTargetPath := path.Clean(parsed[2].String)
|
||||
|
||||
if !slices.Contains([]string{"file", "symlink", "directory"}, c.flagType) {
|
||||
return fmt.Errorf(i18n.G("Invalid type %q"), c.flagType)
|
||||
@ -187,7 +188,7 @@ func (c *cmdStorageVolumeFileCreate) run(cmd *cobra.Command, args []string) erro
|
||||
|
||||
// Create needed paths if requested
|
||||
if c.storageVolumeFile.flagMkdir {
|
||||
err := sftpRecursiveMkdir(sftpConn, filepath.Dir(targetPath), nil, int64(uid), int64(gid))
|
||||
err := sftpRecursiveMkdir(sftpConn, path.Dir(targetPath), nil, int64(uid), int64(gid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -466,7 +467,7 @@ func (c *cmdStorageVolumeFileEdit) run(cmd *cobra.Command, args []string) error
|
||||
}
|
||||
|
||||
// Create temp file
|
||||
f, err := os.CreateTemp("", fmt.Sprintf("incus_file_edit_*%s", filepath.Ext(fPath)))
|
||||
f, err := os.CreateTemp("", fmt.Sprintf("incus_file_edit_*%s", path.Ext(fPath)))
|
||||
if err != nil {
|
||||
return fmt.Errorf(i18n.G("Unable to create a temporary file: %v"), err)
|
||||
}
|
||||
@ -609,7 +610,7 @@ func (c *cmdStorageVolumeFilePull) pull(parsedPool *u.Parsed, parsedPath *u.Pars
|
||||
|
||||
var targetPath string
|
||||
if targetIsDir {
|
||||
targetPath = filepath.Join(target, filepath.Base(normalizedPath))
|
||||
targetPath = filepath.Join(target, path.Base(normalizedPath))
|
||||
} else {
|
||||
targetPath = target
|
||||
}
|
||||
@ -847,7 +848,7 @@ func (c *cmdStorageVolumeFilePush) push(srcFile string, parsedPool *u.Parsed, pa
|
||||
// Determine the target path.
|
||||
var targetPath string
|
||||
if targetIsDir {
|
||||
targetPath = filepath.Join(target, filepath.Base(srcFile))
|
||||
targetPath = path.Join(target, filepath.Base(srcFile))
|
||||
} else {
|
||||
targetPath = target
|
||||
}
|
||||
@ -855,7 +856,7 @@ func (c *cmdStorageVolumeFilePush) push(srcFile string, parsedPool *u.Parsed, pa
|
||||
// Create needed paths if requested
|
||||
if c.storageVolumeFile.flagMkdir {
|
||||
mode := os.FileMode(DirMode)
|
||||
err = sftpRecursiveMkdir(sftpConn, filepath.Dir(targetPath), &mode, int64(args.UID), int64(args.GID))
|
||||
err = sftpRecursiveMkdir(sftpConn, path.Dir(targetPath), &mode, int64(args.UID), int64(args.GID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
@ -336,8 +336,8 @@ func unsetKey(s settable, cmd *cobra.Command, parsed []*u.Parsed) error {
|
||||
return s.set(cmd, parsed)
|
||||
}
|
||||
|
||||
func readEnvironmentFile(path string) (map[string]string, error) {
|
||||
content, err := os.ReadFile(path)
|
||||
func readEnvironmentFile(p string) (map[string]string, error) {
|
||||
content, err := os.ReadFile(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(i18n.G("Can't read from environment file: %w"), err)
|
||||
}
|
||||
@ -743,18 +743,19 @@ func formatRemote(conf *config.Config, p *u.Parsed) string {
|
||||
}
|
||||
|
||||
// normalizePath normalizes a path and return whether it looks like a directory.
|
||||
func normalizePath(path string) (string, bool) {
|
||||
// On Windows, the SFTP server expects the file path to start with `/`.
|
||||
path = "/" + path
|
||||
return filepath.Clean(path), strings.HasSuffix(path, "/")
|
||||
func normalizePath(p string) (string, bool) {
|
||||
// The SFTP server expects a `/` separated path starting with `/`, so use
|
||||
// the slash-only "path" logic regardless of the client platform.
|
||||
p = "/" + p
|
||||
return path.Clean(p), strings.HasSuffix(p, "/")
|
||||
}
|
||||
|
||||
// isStdin returns whether the provided path looks like stdin.
|
||||
func isStdin(path string) bool {
|
||||
return slices.Contains([]string{"-", "/dev/stdin", "/dev/fd/0"}, path)
|
||||
func isStdin(p string) bool {
|
||||
return slices.Contains([]string{"-", "/dev/stdin", "/dev/fd/0"}, p)
|
||||
}
|
||||
|
||||
// isStdout returns whether the provided path looks like stdout.
|
||||
func isStdout(path string) bool {
|
||||
return slices.Contains([]string{"-", "/dev/stdout", "/dev/fd/1"}, path)
|
||||
func isStdout(p string) bool {
|
||||
return slices.Contains([]string{"-", "/dev/stdout", "/dev/fd/1"}, p)
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@ -135,7 +136,7 @@ func sftpRecursivePullFile(sftpConn *sftp.Client, fInfo os.FileInfo, source stri
|
||||
|
||||
target := targetDir
|
||||
if createRoot {
|
||||
root := filepath.Base(source)
|
||||
root := path.Base(source)
|
||||
// `cp` has a special behavior with the following paths.
|
||||
if root == "." || root == ".." {
|
||||
root = ""
|
||||
@ -171,7 +172,7 @@ func sftpRecursivePullFile(sftpConn *sftp.Client, fInfo os.FileInfo, source stri
|
||||
}
|
||||
|
||||
for _, ent := range entries {
|
||||
nextP := filepath.Join(normalizedSource, ent.Name())
|
||||
nextP := path.Join(normalizedSource, ent.Name())
|
||||
stat := sftpConn.Lstat
|
||||
if dereference {
|
||||
stat = sftpConn.Stat
|
||||
@ -284,7 +285,7 @@ func sftpRecursivePushFile(sftpConn *sftp.Client, walkableSource string, source
|
||||
}
|
||||
|
||||
// Prepare for file transfer
|
||||
targetPath := filepath.Join(target, root, p[len(walkableSource):])
|
||||
targetPath := path.Join(target, root, filepath.ToSlash(p[len(walkableSource):]))
|
||||
mode, uid, gid := internalIO.GetOwnerMode(fInfo)
|
||||
fileArgs := incus.InstanceFileArgs{
|
||||
UID: int64(uid),
|
||||
@ -394,12 +395,12 @@ func sftpRecursiveMkdir(sftpConn *sftp.Client, p string, mode *os.FileMode, uid
|
||||
|
||||
// Remove trailing "/" e.g. /A/B/C/. Otherwise we will end up with an
|
||||
// empty array entry "" which will confuse the Mkdir() loop below.
|
||||
pclean := filepath.Clean(p)
|
||||
pclean := path.Clean(p)
|
||||
parts := strings.Split(pclean, "/")
|
||||
i := len(parts)
|
||||
|
||||
for ; i >= 1; i-- {
|
||||
cur := filepath.Join(parts[:i]...)
|
||||
cur := path.Join(parts[:i]...)
|
||||
fInfo, err := sftpConn.Lstat(cur)
|
||||
if err != nil {
|
||||
continue
|
||||
@ -414,7 +415,7 @@ func sftpRecursiveMkdir(sftpConn *sftp.Client, p string, mode *os.FileMode, uid
|
||||
}
|
||||
|
||||
for ; i <= len(parts); i++ {
|
||||
cur := filepath.Join(parts[:i]...)
|
||||
cur := path.Join(parts[:i]...)
|
||||
if cur == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user