mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-08 15:41:15 +02:00
Don't mangle filer paths with the OS separator on Windows (#9878)
fix: don't mangle filer paths with the OS separator on Windows filepath.Dir/Join use the platform separator, so on Windows they rewrite a forward-slash filer path like /buckets/x into \buckets\x. The mangled value then goes into a filer RPC and operates on the wrong key, so the op silently targets nothing. The admin file browser hit this in New Folder (the entry landed under \buckets\my-bucket and never showed up under /buckets/my-bucket), and the same way in delete, view and properties. MQ topic retention and consumer-offset listing, and the SFTP home dir plus create-permission parent lookup, had the same bug. Switch all of these to the path package, which always uses "/".
This commit is contained in:
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -325,7 +325,7 @@ func (s *AdminServer) GetConsumerGroupOffsets(namespace, topicName string) ([]Co
|
|||||||
|
|
||||||
// Only process directories that are versions (start with "v")
|
// Only process directories that are versions (start with "v")
|
||||||
if versionResp.Entry != nil && versionResp.Entry.IsDirectory && strings.HasPrefix(versionResp.Entry.Name, "v") {
|
if versionResp.Entry != nil && versionResp.Entry.IsDirectory && strings.HasPrefix(versionResp.Entry.Name, "v") {
|
||||||
versionDir := filepath.Join(topicDir, versionResp.Entry.Name)
|
versionDir := path.Join(topicDir, versionResp.Entry.Name)
|
||||||
|
|
||||||
// List all partition directories under the version directory (e.g., 0315-0630)
|
// List all partition directories under the version directory (e.g., 0315-0630)
|
||||||
partitionStream, err := client.ListEntries(context.Background(), &filer_pb.ListEntriesRequest{
|
partitionStream, err := client.ListEntries(context.Background(), &filer_pb.ListEntriesRequest{
|
||||||
@@ -360,7 +360,7 @@ func (s *AdminServer) GetConsumerGroupOffsets(namespace, topicName string) ([]Co
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
partitionDir := filepath.Join(versionDir, partitionResp.Entry.Name)
|
partitionDir := path.Join(versionDir, partitionResp.Entry.Name)
|
||||||
|
|
||||||
// List all .offset files in this partition directory
|
// List all .offset files in this partition directory
|
||||||
offsetStream, err := client.ListEntries(context.Background(), &filer_pb.ListEntriesRequest{
|
offsetStream, err := client.ListEntries(context.Background(), &filer_pb.ListEntriesRequest{
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -187,7 +187,7 @@ func (p *TopicRetentionPurger) purgeTopicData(topicRetention TopicRetentionConfi
|
|||||||
|
|
||||||
// Check if this version directory is expired
|
// Check if this version directory is expired
|
||||||
if versionDir.VersionTime.Before(cutoffTime) {
|
if versionDir.VersionTime.Before(cutoffTime) {
|
||||||
dirPath := filepath.Join(topicDir, versionDir.Name)
|
dirPath := path.Join(topicDir, versionDir.Name)
|
||||||
|
|
||||||
// Delete the entire version directory
|
// Delete the entire version directory
|
||||||
err := p.deleteDirectoryRecursively(client, dirPath)
|
err := p.deleteDirectoryRecursively(client, dirPath)
|
||||||
@@ -267,7 +267,7 @@ func (p *TopicRetentionPurger) deleteDirectoryRecursively(client filer_pb.Seawee
|
|||||||
if resp.Entry == nil {
|
if resp.Entry == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
entryPath := filepath.Join(dirPath, resp.Entry.Name)
|
entryPath := path.Join(dirPath, resp.Entry.Name)
|
||||||
|
|
||||||
if resp.Entry.IsDirectory {
|
if resp.Entry.IsDirectory {
|
||||||
// Recursively delete subdirectory
|
// Recursively delete subdirectory
|
||||||
@@ -288,8 +288,8 @@ func (p *TopicRetentionPurger) deleteDirectoryRecursively(client filer_pb.Seawee
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete the directory itself
|
// Delete the directory itself
|
||||||
parentDir := filepath.Dir(dirPath)
|
parentDir := path.Dir(dirPath)
|
||||||
dirName := filepath.Base(dirPath)
|
dirName := path.Base(dirPath)
|
||||||
|
|
||||||
_, err = client.DeleteEntry(context.Background(), &filer_pb.DeleteEntryRequest{
|
_, err = client.DeleteEntry(context.Background(), &filer_pb.DeleteEntryRequest{
|
||||||
Directory: parentDir,
|
Directory: parentDir,
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -111,8 +110,8 @@ func (h *FileBrowserHandlers) DeleteFile(w http.ResponseWriter, r *http.Request)
|
|||||||
// Delete file via filer
|
// Delete file via filer
|
||||||
err := h.adminServer.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
err := h.adminServer.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
||||||
_, err := client.DeleteEntry(context.Background(), &filer_pb.DeleteEntryRequest{
|
_, err := client.DeleteEntry(context.Background(), &filer_pb.DeleteEntryRequest{
|
||||||
Directory: filepath.Dir(request.Path),
|
Directory: path.Dir(request.Path),
|
||||||
Name: filepath.Base(request.Path),
|
Name: path.Base(request.Path),
|
||||||
IsDeleteData: true,
|
IsDeleteData: true,
|
||||||
IsRecursive: true,
|
IsRecursive: true,
|
||||||
IgnoreRecursiveError: false,
|
IgnoreRecursiveError: false,
|
||||||
@@ -143,8 +142,8 @@ func (h *FileBrowserHandlers) DeleteMultipleFiles(w http.ResponseWriter, r *http
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, path := range request.Paths {
|
for _, p := range request.Paths {
|
||||||
if strings.TrimSpace(path) == "" {
|
if strings.TrimSpace(p) == "" {
|
||||||
writeJSONError(w, http.StatusBadRequest, "path is required")
|
writeJSONError(w, http.StatusBadRequest, "path is required")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -155,11 +154,11 @@ func (h *FileBrowserHandlers) DeleteMultipleFiles(w http.ResponseWriter, r *http
|
|||||||
var errors []string
|
var errors []string
|
||||||
|
|
||||||
// Delete each file/folder
|
// Delete each file/folder
|
||||||
for _, path := range request.Paths {
|
for _, p := range request.Paths {
|
||||||
err := h.adminServer.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
err := h.adminServer.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
||||||
_, err := client.DeleteEntry(context.Background(), &filer_pb.DeleteEntryRequest{
|
_, err := client.DeleteEntry(context.Background(), &filer_pb.DeleteEntryRequest{
|
||||||
Directory: filepath.Dir(path),
|
Directory: path.Dir(p),
|
||||||
Name: filepath.Base(path),
|
Name: path.Base(p),
|
||||||
IsDeleteData: true,
|
IsDeleteData: true,
|
||||||
IsRecursive: true,
|
IsRecursive: true,
|
||||||
IgnoreRecursiveError: false,
|
IgnoreRecursiveError: false,
|
||||||
@@ -169,7 +168,7 @@ func (h *FileBrowserHandlers) DeleteMultipleFiles(w http.ResponseWriter, r *http
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
failedCount++
|
failedCount++
|
||||||
errors = append(errors, fmt.Sprintf("%s: %v", path, err))
|
errors = append(errors, fmt.Sprintf("%s: %v", p, err))
|
||||||
} else {
|
} else {
|
||||||
deletedCount++
|
deletedCount++
|
||||||
}
|
}
|
||||||
@@ -230,9 +229,9 @@ func (h *FileBrowserHandlers) CreateFolder(w http.ResponseWriter, r *http.Reques
|
|||||||
// Create folder via filer
|
// Create folder via filer
|
||||||
err := h.adminServer.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
err := h.adminServer.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
||||||
_, err := client.CreateEntry(context.Background(), &filer_pb.CreateEntryRequest{
|
_, err := client.CreateEntry(context.Background(), &filer_pb.CreateEntryRequest{
|
||||||
Directory: filepath.Dir(fullPath),
|
Directory: path.Dir(fullPath),
|
||||||
Entry: &filer_pb.Entry{
|
Entry: &filer_pb.Entry{
|
||||||
Name: filepath.Base(fullPath),
|
Name: path.Base(fullPath),
|
||||||
IsDirectory: true,
|
IsDirectory: true,
|
||||||
Attributes: &filer_pb.FuseAttributes{
|
Attributes: &filer_pb.FuseAttributes{
|
||||||
FileMode: uint32(0o755 | os.ModeDir), // Directory mode
|
FileMode: uint32(0o755 | os.ModeDir), // Directory mode
|
||||||
@@ -444,8 +443,8 @@ func (h *FileBrowserHandlers) ViewFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
var fileEntry dash.FileEntry
|
var fileEntry dash.FileEntry
|
||||||
err := h.adminServer.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
err := h.adminServer.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
||||||
resp, err := client.LookupDirectoryEntry(context.Background(), &filer_pb.LookupDirectoryEntryRequest{
|
resp, err := client.LookupDirectoryEntry(context.Background(), &filer_pb.LookupDirectoryEntryRequest{
|
||||||
Directory: filepath.Dir(filePath),
|
Directory: path.Dir(filePath),
|
||||||
Name: filepath.Base(filePath),
|
Name: path.Base(filePath),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -549,8 +548,8 @@ func (h *FileBrowserHandlers) GetFileProperties(w http.ResponseWriter, r *http.R
|
|||||||
var properties map[string]interface{}
|
var properties map[string]interface{}
|
||||||
err := h.adminServer.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
err := h.adminServer.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
|
||||||
resp, err := client.LookupDirectoryEntry(context.Background(), &filer_pb.LookupDirectoryEntryRequest{
|
resp, err := client.LookupDirectoryEntry(context.Background(), &filer_pb.LookupDirectoryEntryRequest{
|
||||||
Directory: filepath.Dir(filePath),
|
Directory: path.Dir(filePath),
|
||||||
Name: filepath.Base(filePath),
|
Name: path.Base(filePath),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package sftpd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
stdpath "path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||||
@@ -66,7 +66,7 @@ func (fs *SftpServer) CheckFilePermission(path string, perm string) error {
|
|||||||
// If the path doesn't exist and we're checking for create/write/mkdir permission,
|
// If the path doesn't exist and we're checking for create/write/mkdir permission,
|
||||||
// check permissions on the parent directory instead
|
// check permissions on the parent directory instead
|
||||||
if err == os.ErrNotExist {
|
if err == os.ErrNotExist {
|
||||||
parentPath := filepath.Dir(path)
|
parentPath := stdpath.Dir(path)
|
||||||
// Check if user can write to the parent directory
|
// Check if user can write to the parent directory
|
||||||
return fs.CheckFilePermission(parentPath, perm)
|
return fs.CheckFilePermission(parentPath, perm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand/v2"
|
"math/rand/v2"
|
||||||
"path/filepath"
|
"path"
|
||||||
)
|
)
|
||||||
|
|
||||||
// User represents an SFTP user with authentication and permission details
|
// User represents an SFTP user with authentication and permission details
|
||||||
@@ -27,7 +27,7 @@ func NewUser(username string) *User {
|
|||||||
return &User{
|
return &User{
|
||||||
Username: username,
|
Username: username,
|
||||||
Permissions: make(map[string][]string),
|
Permissions: make(map[string][]string),
|
||||||
HomeDir: filepath.Join("/home", username),
|
HomeDir: path.Join("/home", username),
|
||||||
Uid: uint32(randomId),
|
Uid: uint32(randomId),
|
||||||
Gid: uint32(randomId),
|
Gid: uint32(randomId),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user