Browse Source

fix: normalize Windows backslash paths in weed admin file uploads (#7636)

fix: normalize Windows backslash paths in file uploads

When uploading files from a Windows client to a Linux server,
file paths containing backslashes were not being properly interpreted as
directory separators. This caused files intended for subdirectories to be
created in the root directory with backslashes in their filenames.

Changes:
- Add util.CleanWindowsPath and util.CleanWindowsPathBase helper functions
  in weed/util/fullpath.go for reusable path normalization
- Use path.Join/path.Clean/path.Base instead of filepath equivalents
  for URL path semantics (filepath is OS-specific)
- Apply normalization in weed admin handlers and filer upload parsing

Fixes #7628
pull/7641/head
Chris Lu 4 days ago
committed by GitHub
parent
commit
28ac536280
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 22
      weed/admin/handlers/file_browser_handlers.go
  2. 6
      weed/storage/needle/needle_parse_upload.go
  3. 13
      weed/util/fullpath.go

22
weed/admin/handlers/file_browser_handlers.go

@ -10,6 +10,7 @@ import (
"net"
"net/http"
"os"
"path"
"path/filepath"
"strconv"
"strings"
@ -21,6 +22,7 @@ import (
"github.com/seaweedfs/seaweedfs/weed/admin/view/layout"
"github.com/seaweedfs/seaweedfs/weed/glog"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
"github.com/seaweedfs/seaweedfs/weed/util"
"github.com/seaweedfs/seaweedfs/weed/util/http/client"
)
@ -267,8 +269,12 @@ func (h *FileBrowserHandlers) UploadFile(c *gin.Context) {
continue
}
// Create full path for the file
fullPath := filepath.Join(currentPath, fileName)
// Normalize Windows-style backslashes to forward slashes
fileName = util.CleanWindowsPath(fileName)
// Create full path for the file using path.Join for URL path semantics
// path.Join handles double slashes and is not OS-specific like filepath.Join
fullPath := path.Join(currentPath, fileName)
if !strings.HasPrefix(fullPath, "/") {
fullPath = "/" + fullPath
}
@ -349,8 +355,10 @@ func (h *FileBrowserHandlers) uploadFileToFiler(filePath string, fileHeader *mul
var body bytes.Buffer
writer := multipart.NewWriter(&body)
// Create form file field
part, err := writer.CreateFormFile("file", fileHeader.Filename)
// Create form file field with normalized base filename
// Use path.Base (not filepath.Base) since cleanFilePath uses URL path semantics
baseFileName := path.Base(cleanFilePath)
part, err := writer.CreateFormFile("file", baseFileName)
if err != nil {
return fmt.Errorf("failed to create form file: %w", err)
}
@ -452,8 +460,12 @@ func (h *FileBrowserHandlers) validateAndCleanFilePath(filePath string) (string,
return "", fmt.Errorf("file path cannot be empty")
}
// Normalize Windows-style backslashes to forward slashes
filePath = util.CleanWindowsPath(filePath)
// Clean the path to remove any .. or . components
cleanPath := filepath.Clean(filePath)
// Use path.Clean (not filepath.Clean) since this is a URL path
cleanPath := path.Clean(filePath)
// Ensure the path starts with /
if !strings.HasPrefix(cleanPath, "/") {

6
weed/storage/needle/needle_parse_upload.go

@ -128,7 +128,7 @@ func parseUpload(r *http.Request, sizeLimit int64, pu *ParsedUpload) (e error) {
pu.FileName = part.FileName()
if pu.FileName != "" {
pu.FileName = path.Base(pu.FileName)
pu.FileName = util.CleanWindowsPathBase(pu.FileName)
}
dataSize, e = pu.bytesBuffer.ReadFrom(io.LimitReader(part, sizeLimit+1))
@ -169,7 +169,7 @@ func parseUpload(r *http.Request, sizeLimit int64, pu *ParsedUpload) (e error) {
// update
pu.Data = pu.bytesBuffer.Bytes()
pu.FileName = path.Base(fName)
pu.FileName = util.CleanWindowsPathBase(fName)
contentType = part.Header.Get("Content-Type")
part = part2
break
@ -207,7 +207,7 @@ func parseUpload(r *http.Request, sizeLimit int64, pu *ParsedUpload) (e error) {
}
if pu.FileName != "" {
pu.FileName = path.Base(pu.FileName)
pu.FileName = util.CleanWindowsPathBase(pu.FileName)
} else {
pu.FileName = path.Base(r.URL.Path)
}

13
weed/util/fullpath.go

@ -1,6 +1,7 @@
package util
import (
"path"
"path/filepath"
"strings"
)
@ -85,3 +86,15 @@ func StringSplit(separatedValues string, sep string) []string {
}
return strings.Split(separatedValues, sep)
}
// CleanWindowsPath normalizes Windows-style backslashes to forward slashes.
// This handles paths from Windows clients where paths use backslashes.
func CleanWindowsPath(p string) string {
return strings.ReplaceAll(p, "\\", "/")
}
// CleanWindowsPathBase normalizes Windows-style backslashes to forward slashes
// and returns the base name of the path.
func CleanWindowsPathBase(p string) string {
return path.Base(strings.ReplaceAll(p, "\\", "/"))
}
Loading…
Cancel
Save