mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
posix: Support UNC paths on windows. (#1887)
This allows us to now use 32K paths names on windows. Fixes #1620
This commit is contained in:
parent
4ab57f7d60
commit
ed4fe689b4
@ -49,7 +49,7 @@ func purgeV1() {
|
||||
fatalIf(err, "Unable to retrieve config path.")
|
||||
|
||||
configFile := filepath.Join(configPath, "fsUsers.json")
|
||||
os.RemoveAll(configFile)
|
||||
removeAll(configFile)
|
||||
}
|
||||
fatalIf(errors.New(""), "Failed to migrate unrecognized config version ‘"+cv1.Version+"’.")
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
@ -116,7 +115,7 @@ func BenchmarkGetObjectFS(b *testing.B) {
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(directory)
|
||||
defer removeAll(directory)
|
||||
|
||||
// Create the obj.
|
||||
obj, err := newFSObjects(directory)
|
||||
|
@ -577,7 +577,7 @@ func BenchmarkListObjects(b *testing.B) {
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(directory)
|
||||
defer removeAll(directory)
|
||||
|
||||
// Create the obj.
|
||||
obj, err := newFSObjects(directory)
|
||||
|
@ -69,6 +69,6 @@ func (s *MySuite) TestXLAPISuite(c *C) {
|
||||
|
||||
func removeRootsC(c *C, roots []string) {
|
||||
for _, root := range roots {
|
||||
os.RemoveAll(root)
|
||||
removeAll(root)
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
@ -26,10 +27,11 @@ import (
|
||||
|
||||
// Return all the entries at the directory dirPath.
|
||||
func readDir(dirPath string) (entries []string, err error) {
|
||||
d, err := os.Open(dirPath)
|
||||
d, err := os.Open(preparePath(dirPath))
|
||||
if err != nil {
|
||||
// File is really not found.
|
||||
if os.IsNotExist(err) {
|
||||
fmt.Println(preparePath(dirPath), err)
|
||||
return nil, errFileNotFound
|
||||
}
|
||||
|
||||
@ -50,12 +52,12 @@ func readDir(dirPath string) (entries []string, err error) {
|
||||
return nil, err
|
||||
}
|
||||
for _, fi := range fis {
|
||||
// Skip special files.
|
||||
// Skip special files, if found.
|
||||
if hasPosixReservedPrefix(fi.Name()) {
|
||||
continue
|
||||
}
|
||||
if fi.Mode().IsDir() {
|
||||
// append "/" instead of "\" so that sorting is done as expected.
|
||||
// Append "/" instead of "\" so that sorting is achieved as expected.
|
||||
entries = append(entries, fi.Name()+slashSeparator)
|
||||
} else if fi.Mode().IsRegular() {
|
||||
entries = append(entries, fi.Name())
|
||||
|
56
posix-prepare-path.go
Normal file
56
posix-prepare-path.go
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// preparePath rewrites path to handle any OS specific details.
|
||||
func preparePath(path string) string {
|
||||
if runtime.GOOS == "windows" {
|
||||
// Microsoft Windows supports long path names using
|
||||
// uniform naming convention (UNC).
|
||||
return UNCPath(path)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// UNCPath converts a absolute windows path to a UNC long path.
|
||||
func UNCPath(path string) string {
|
||||
// Clean the path for any trailing "/".
|
||||
path = filepath.Clean(path)
|
||||
|
||||
// UNC can NOT use "/", so convert all to "\".
|
||||
path = filepath.FromSlash(path)
|
||||
|
||||
// If prefix is "\\", we already have a UNC path or server.
|
||||
if strings.HasPrefix(path, `\\`) {
|
||||
|
||||
// If already long path, just keep it
|
||||
if strings.HasPrefix(path, `\\?\`) {
|
||||
return path
|
||||
}
|
||||
|
||||
// Trim "\\" from path and add UNC prefix.
|
||||
return `\\?\UNC\` + strings.TrimPrefix(path, `\\`)
|
||||
}
|
||||
path = `\\?\` + path
|
||||
return path
|
||||
}
|
37
posix-utils_common.go
Normal file
37
posix-utils_common.go
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import "strings"
|
||||
|
||||
// List of reserved words for files, includes old and new ones.
|
||||
var posixReservedPrefix = []string{
|
||||
"$tmpfile",
|
||||
// Add new reserved words if any used in future.
|
||||
}
|
||||
|
||||
// hasPosixReservedPrefix - has reserved prefix.
|
||||
func hasPosixReservedPrefix(name string) (isReserved bool) {
|
||||
for _, reservedKey := range posixReservedPrefix {
|
||||
if strings.HasPrefix(name, reservedKey) {
|
||||
isReserved = true
|
||||
break
|
||||
}
|
||||
isReserved = false
|
||||
}
|
||||
return isReserved
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
// +build linux darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
||||
*
|
||||
@ -17,33 +19,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// isValidVolname verifies a volname name in accordance with object
|
||||
// layer requirements.
|
||||
func isValidVolname(volname string) bool {
|
||||
if len(volname) < 3 || len(volname) > 63 {
|
||||
return false
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
// Volname shouldn't have reserved characters on windows in it.
|
||||
return !strings.ContainsAny(volname, "/\\:*?\"<>|")
|
||||
default:
|
||||
// Volname shouldn't have '/' in it.
|
||||
return !strings.ContainsAny(volname, "/")
|
||||
}
|
||||
}
|
||||
|
||||
// Keeping this as lower bound value supporting Linux, Darwin and Windows operating systems.
|
||||
const pathMax = 4096
|
||||
const pathMax = 4096 // 4k limit on all unixes.
|
||||
|
||||
// isValidPath verifies if a path name is in accordance with FS limitations.
|
||||
func isValidPath(path string) bool {
|
||||
// TODO: Make this FSType or Operating system specific.
|
||||
if len(path) > pathMax || len(path) == 0 {
|
||||
return false
|
||||
}
|
||||
@ -53,20 +37,29 @@ func isValidPath(path string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// List of reserved words for files, includes old and new ones.
|
||||
var posixReservedPrefix = []string{
|
||||
"$tmpfile",
|
||||
// Add new reserved words if any used in future.
|
||||
// isValidVolname verifies a volname name in accordance with object
|
||||
// layer requirements.
|
||||
func isValidVolname(volname string) bool {
|
||||
if len(volname) < 3 || len(volname) > 63 {
|
||||
return false
|
||||
}
|
||||
// Volname shouldn't have '/' in it.
|
||||
return !strings.ContainsAny(volname, "/")
|
||||
}
|
||||
|
||||
// hasPosixReservedPrefix - has reserved prefix.
|
||||
func hasPosixReservedPrefix(name string) (isReserved bool) {
|
||||
for _, reservedKey := range posixReservedPrefix {
|
||||
if strings.HasPrefix(name, reservedKey) {
|
||||
isReserved = true
|
||||
break
|
||||
}
|
||||
isReserved = false
|
||||
}
|
||||
return isReserved
|
||||
// mkdirAll creates a directory named path,
|
||||
// along with any necessary parents, and returns nil,
|
||||
// or else returns an error. The permission bits perm are used
|
||||
// for all directories that mkdirAll creates. If path is already
|
||||
// a directory, mkdirAll does nothing and returns nil.
|
||||
func mkdirAll(path string, perm os.FileMode) error {
|
||||
return os.MkdirAll(path, perm)
|
||||
}
|
||||
|
||||
// removeAll removes path and any children it contains.
|
||||
// It removes everything it can but returns the first error
|
||||
// it encounters. If the path does not exist, RemoveAll
|
||||
// returns nil (no error).
|
||||
func removeAll(path string) error {
|
||||
return os.RemoveAll(path)
|
||||
}
|
177
posix-utils_windows.go
Normal file
177
posix-utils_windows.go
Normal file
@ -0,0 +1,177 @@
|
||||
// +build windows
|
||||
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const pathMax = 32 * 1024 // 32K is the maximum limit for UNC paths.
|
||||
|
||||
// isValidPath verifies if a path name is in accordance with FS limitations.
|
||||
func isValidPath(path string) bool {
|
||||
if len(path) > pathMax || len(path) == 0 {
|
||||
return false
|
||||
}
|
||||
if !utf8.ValidString(path) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isValidVolname verifies a volname name in accordance with object
|
||||
// layer requirements.
|
||||
func isValidVolname(volname string) bool {
|
||||
if len(volname) < 3 || len(volname) > 63 {
|
||||
return false
|
||||
}
|
||||
// Volname shouldn't have reserved characters on windows in it.
|
||||
return !strings.ContainsAny(volname, "/\\:*?\"<>|")
|
||||
}
|
||||
|
||||
// mkdirAll creates a directory named path,
|
||||
// along with any necessary parents, and returns nil,
|
||||
// or else returns an error. The permission bits perm are used
|
||||
// for all directories that mkdirAll creates. If path is already
|
||||
// a directory, mkdirAll does nothing and returns nil.
|
||||
func mkdirAll(path string, perm os.FileMode) error {
|
||||
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
|
||||
dir, err := os.Stat(preparePath(path))
|
||||
if err == nil {
|
||||
if dir.IsDir() {
|
||||
return nil
|
||||
}
|
||||
return &os.PathError{
|
||||
Op: "mkdir",
|
||||
Path: path,
|
||||
Err: syscall.ENOTDIR,
|
||||
}
|
||||
}
|
||||
|
||||
// Slow path: make sure parent exists and then call Mkdir for path.
|
||||
i := len(path)
|
||||
for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
|
||||
i--
|
||||
}
|
||||
|
||||
j := i
|
||||
for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
|
||||
j--
|
||||
}
|
||||
|
||||
if j > 1 {
|
||||
// Create parent
|
||||
parent := path[0 : j-1]
|
||||
if parent != filepath.VolumeName(parent) {
|
||||
err = mkdirAll(parent, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parent now exists; invoke Mkdir and use its result.
|
||||
err = os.Mkdir(preparePath(path), perm)
|
||||
if err != nil {
|
||||
// Handle arguments like "foo/." by
|
||||
// double-checking that directory doesn't exist.
|
||||
dir, err1 := os.Lstat(preparePath(path))
|
||||
if err1 == nil && dir.IsDir() {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeAll removes path and any children it contains.
|
||||
// It removes everything it can but returns the first error
|
||||
// it encounters. If the path does not exist, RemoveAll
|
||||
// returns nil (no error).
|
||||
func removeAll(path string) error {
|
||||
// Simple case: if Remove works, we're done.
|
||||
err := os.Remove(preparePath(path))
|
||||
if err == nil || os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Otherwise, is this a directory we need to recurse into?
|
||||
dir, serr := os.Lstat(preparePath(path))
|
||||
if serr != nil {
|
||||
if serr, ok := serr.(*os.PathError); ok && (os.IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
|
||||
return nil
|
||||
}
|
||||
return serr
|
||||
}
|
||||
if !dir.IsDir() {
|
||||
// Not a directory; return the error from Remove.
|
||||
return err
|
||||
}
|
||||
|
||||
// Directory.
|
||||
fd, err := os.Open(preparePath(path))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// Race. It was deleted between the Lstat and Open.
|
||||
// Return nil per RemoveAll's docs.
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove contents & return first error.
|
||||
err = nil
|
||||
for {
|
||||
names, err1 := fd.Readdirnames(4096) // Get 4k entries.
|
||||
for _, name := range names {
|
||||
err1 = removeAll(path + string(os.PathSeparator) + name)
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
}
|
||||
if err1 == io.EOF {
|
||||
break
|
||||
}
|
||||
// If Readdirnames returned an error, use it.
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
if len(names) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Close directory, because windows won't remove opened directory.
|
||||
fd.Close()
|
||||
|
||||
// Remove directory.
|
||||
err1 := os.Remove(preparePath(path))
|
||||
if err1 == nil || os.IsNotExist(err1) {
|
||||
return nil
|
||||
}
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
}
|
65
posix.go
65
posix.go
@ -23,7 +23,6 @@ import (
|
||||
"os"
|
||||
slashpath "path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
@ -46,16 +45,7 @@ var errFaultyDisk = errors.New("Faulty disk")
|
||||
|
||||
// checkPathLength - returns error if given path name length more than 255
|
||||
func checkPathLength(pathName string) error {
|
||||
// For MS Windows, the maximum path length is 255
|
||||
if runtime.GOOS == "windows" {
|
||||
if len(pathName) > 255 {
|
||||
return errFileNameTooLong
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// For non-windows system, check each path segment length is > 255
|
||||
// Check each path segment length is > 255
|
||||
for len(pathName) > 0 && pathName != "." && pathName != "/" {
|
||||
dir, file := slashpath.Dir(pathName), slashpath.Base(pathName)
|
||||
|
||||
@ -64,8 +54,7 @@ func checkPathLength(pathName string) error {
|
||||
}
|
||||
|
||||
pathName = dir
|
||||
}
|
||||
|
||||
} // Success.
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -96,11 +85,17 @@ func newPosix(diskPath string) (StorageAPI, error) {
|
||||
if diskPath == "" {
|
||||
return nil, errInvalidArgument
|
||||
}
|
||||
var err error
|
||||
// Disallow relative paths, figure out absolute paths.
|
||||
diskPath, err = filepath.Abs(diskPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fs := posix{
|
||||
diskPath: diskPath,
|
||||
minFreeDisk: fsMinSpacePercent, // Minimum 5% disk should be free.
|
||||
}
|
||||
st, err := os.Stat(diskPath)
|
||||
st, err := os.Stat(preparePath(diskPath))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return fs, errDiskNotFound
|
||||
@ -153,7 +148,7 @@ func listVols(dirPath string) ([]VolInfo, error) {
|
||||
continue
|
||||
}
|
||||
var fi os.FileInfo
|
||||
fi, err = os.Stat(pathJoin(dirPath, entry))
|
||||
fi, err = os.Stat(preparePath(pathJoin(dirPath, entry)))
|
||||
if err != nil {
|
||||
// If the file does not exist, skip the entry.
|
||||
if os.IsNotExist(err) {
|
||||
@ -208,7 +203,7 @@ func (s posix) MakeVol(volume string) (err error) {
|
||||
return err
|
||||
}
|
||||
// Make a volume entry.
|
||||
err = os.Mkdir(volumeDir, 0700)
|
||||
err = os.Mkdir(preparePath(volumeDir), 0700)
|
||||
if err != nil && os.IsExist(err) {
|
||||
return errVolumeExists
|
||||
}
|
||||
@ -266,7 +261,7 @@ func (s posix) StatVol(volume string) (volInfo VolInfo, err error) {
|
||||
}
|
||||
// Stat a volume entry.
|
||||
var st os.FileInfo
|
||||
st, err = os.Stat(volumeDir)
|
||||
st, err = os.Stat(preparePath(volumeDir))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return VolInfo{}, errVolumeNotFound
|
||||
@ -304,7 +299,7 @@ func (s posix) DeleteVol(volume string) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Remove(volumeDir)
|
||||
err = os.Remove(preparePath(volumeDir))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return errVolumeNotFound
|
||||
@ -345,7 +340,7 @@ func (s posix) ListDir(volume, dirPath string) (entries []string, err error) {
|
||||
return nil, err
|
||||
}
|
||||
// Stat a volume entry.
|
||||
_, err = os.Stat(volumeDir)
|
||||
_, err = os.Stat(preparePath(volumeDir))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, errVolumeNotFound
|
||||
@ -381,7 +376,7 @@ func (s posix) ReadFile(volume string, path string, offset int64, buf []byte) (n
|
||||
return 0, err
|
||||
}
|
||||
// Stat a volume entry.
|
||||
_, err = os.Stat(volumeDir)
|
||||
_, err = os.Stat(preparePath(volumeDir))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return 0, errVolumeNotFound
|
||||
@ -393,7 +388,7 @@ func (s posix) ReadFile(volume string, path string, offset int64, buf []byte) (n
|
||||
if err = checkPathLength(filePath); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
file, err := os.Open(filePath)
|
||||
file, err := os.Open(preparePath(filePath))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return 0, errFileNotFound
|
||||
@ -456,7 +451,7 @@ func (s posix) AppendFile(volume, path string, buf []byte) (n int64, err error)
|
||||
return 0, err
|
||||
}
|
||||
// Stat a volume entry.
|
||||
_, err = os.Stat(volumeDir)
|
||||
_, err = os.Stat(preparePath(volumeDir))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return 0, errVolumeNotFound
|
||||
@ -472,16 +467,16 @@ func (s posix) AppendFile(volume, path string, buf []byte) (n int64, err error)
|
||||
}
|
||||
// Verify if the file already exists and is not of regular type.
|
||||
var st os.FileInfo
|
||||
if st, err = os.Stat(filePath); err == nil {
|
||||
if st, err = os.Stat(preparePath(filePath)); err == nil {
|
||||
if st.IsDir() {
|
||||
return 0, errIsNotRegular
|
||||
}
|
||||
}
|
||||
// Create top level directories if they don't exist.
|
||||
if err = os.MkdirAll(filepath.Dir(filePath), 0700); err != nil {
|
||||
if err = mkdirAll(filepath.Dir(filePath), 0700); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
w, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
|
||||
w, err := os.OpenFile(preparePath(filePath), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
// File path cannot be verified since one of the parents is a file.
|
||||
if strings.Contains(err.Error(), "not a directory") {
|
||||
@ -518,7 +513,7 @@ func (s posix) StatFile(volume, path string) (file FileInfo, err error) {
|
||||
return FileInfo{}, err
|
||||
}
|
||||
// Stat a volume entry.
|
||||
_, err = os.Stat(volumeDir)
|
||||
_, err = os.Stat(preparePath(volumeDir))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return FileInfo{}, errVolumeNotFound
|
||||
@ -530,7 +525,7 @@ func (s posix) StatFile(volume, path string) (file FileInfo, err error) {
|
||||
if err = checkPathLength(filePath); err != nil {
|
||||
return FileInfo{}, err
|
||||
}
|
||||
st, err := os.Stat(filePath)
|
||||
st, err := os.Stat(preparePath(filePath))
|
||||
if err != nil {
|
||||
// File is really not found.
|
||||
if os.IsNotExist(err) {
|
||||
@ -564,7 +559,7 @@ func deleteFile(basePath, deletePath string) error {
|
||||
return nil
|
||||
}
|
||||
// Verify if the path exists.
|
||||
pathSt, err := os.Stat(deletePath)
|
||||
pathSt, err := os.Stat(preparePath(deletePath))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return errFileNotFound
|
||||
@ -578,7 +573,7 @@ func deleteFile(basePath, deletePath string) error {
|
||||
return nil
|
||||
}
|
||||
// Attempt to remove path.
|
||||
if err := os.Remove(deletePath); err != nil {
|
||||
if err := os.Remove(preparePath(deletePath)); err != nil {
|
||||
return err
|
||||
}
|
||||
// Recursively go down the next path and delete again.
|
||||
@ -610,7 +605,7 @@ func (s posix) DeleteFile(volume, path string) (err error) {
|
||||
return err
|
||||
}
|
||||
// Stat a volume entry.
|
||||
_, err = os.Stat(volumeDir)
|
||||
_, err = os.Stat(preparePath(volumeDir))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return errVolumeNotFound
|
||||
@ -655,14 +650,14 @@ func (s posix) RenameFile(srcVolume, srcPath, dstVolume, dstPath string) (err er
|
||||
return err
|
||||
}
|
||||
// Stat a volume entry.
|
||||
_, err = os.Stat(srcVolumeDir)
|
||||
_, err = os.Stat(preparePath(srcVolumeDir))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return errVolumeNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
_, err = os.Stat(dstVolumeDir)
|
||||
_, err = os.Stat(preparePath(dstVolumeDir))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return errVolumeNotFound
|
||||
@ -677,7 +672,7 @@ func (s posix) RenameFile(srcVolume, srcPath, dstVolume, dstPath string) (err er
|
||||
}
|
||||
if srcIsDir {
|
||||
// If source is a directory we expect the destination to be non-existent always.
|
||||
_, err = os.Stat(slashpath.Join(dstVolumeDir, dstPath))
|
||||
_, err = os.Stat(preparePath(slashpath.Join(dstVolumeDir, dstPath)))
|
||||
if err == nil {
|
||||
return errFileAccessDenied
|
||||
}
|
||||
@ -686,14 +681,14 @@ func (s posix) RenameFile(srcVolume, srcPath, dstVolume, dstPath string) (err er
|
||||
}
|
||||
// Destination does not exist, hence proceed with the rename.
|
||||
}
|
||||
if err = os.MkdirAll(slashpath.Dir(slashpath.Join(dstVolumeDir, dstPath)), 0755); err != nil {
|
||||
if err = mkdirAll(preparePath(slashpath.Dir(slashpath.Join(dstVolumeDir, dstPath))), 0755); err != nil {
|
||||
// File path cannot be verified since one of the parents is a file.
|
||||
if strings.Contains(err.Error(), "not a directory") {
|
||||
return errFileAccessDenied
|
||||
}
|
||||
return err
|
||||
}
|
||||
err = os.Rename(slashpath.Join(srcVolumeDir, srcPath), slashpath.Join(dstVolumeDir, dstPath))
|
||||
err = os.Rename(preparePath(slashpath.Join(srcVolumeDir, srcPath)), preparePath(slashpath.Join(dstVolumeDir, dstPath)))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return errFileNotFound
|
||||
|
@ -19,6 +19,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@ -86,7 +87,7 @@ func (s *MyAPISuite) SetUpSuite(c *C) {
|
||||
}
|
||||
|
||||
func (s *MyAPISuite) TearDownSuite(c *C) {
|
||||
os.RemoveAll(s.root)
|
||||
removeAll(s.root)
|
||||
testAPIFSCacheServer.Close()
|
||||
}
|
||||
|
||||
@ -686,6 +687,34 @@ func (s *MyAPISuite) TestListBuckets(c *C) {
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
// Tests put object with long names.
|
||||
func (s *MyAPISuite) TestPutObjectLongName(c *C) {
|
||||
request, err := s.newRequest("PUT", testAPIFSCacheServer.URL+"/put-object-long-name", 0, nil)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
client := http.Client{}
|
||||
response, err := client.Do(request)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
buffer := bytes.NewReader([]byte("hello world"))
|
||||
longObjName := fmt.Sprintf("%0255d/%0255d/%0255d", 1, 1, 1)
|
||||
request, err = s.newRequest("PUT", testAPIFSCacheServer.URL+"/put-object-long-name/"+longObjName, int64(buffer.Len()), buffer)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
response, err = client.Do(request)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
longObjName = fmt.Sprintf("%0256d", 1)
|
||||
request, err = s.newRequest("PUT", testAPIFSCacheServer.URL+"/put-object-long-name/"+longObjName, int64(buffer.Len()), buffer)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
response, err = client.Do(request)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusNotFound)
|
||||
}
|
||||
|
||||
func (s *MyAPISuite) TestNotBeAbleToCreateObjectInNonexistentBucket(c *C) {
|
||||
buffer1 := bytes.NewReader([]byte("hello world"))
|
||||
request, err := s.newRequest("PUT", testAPIFSCacheServer.URL+"/innonexistentbucket/object", int64(buffer1.Len()), buffer1)
|
||||
|
@ -94,9 +94,9 @@ func (s *MyAPIXLSuite) SetUpSuite(c *C) {
|
||||
}
|
||||
|
||||
func (s *MyAPIXLSuite) TearDownSuite(c *C) {
|
||||
os.RemoveAll(s.root)
|
||||
removeAll(s.root)
|
||||
for _, disk := range s.erasureDisks {
|
||||
os.RemoveAll(disk)
|
||||
removeAll(disk)
|
||||
}
|
||||
testAPIXLServer.Close()
|
||||
}
|
||||
@ -706,7 +706,15 @@ func (s *MyAPIXLSuite) TestPutObjectLongName(c *C) {
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
buffer := bytes.NewReader([]byte("hello world"))
|
||||
longObjName := fmt.Sprintf("%0256d", 1)
|
||||
longObjName := fmt.Sprintf("%0255d/%0255d/%0255d", 1, 1, 1)
|
||||
request, err = s.newRequest("PUT", testAPIXLServer.URL+"/put-object-long-name/"+longObjName, int64(buffer.Len()), buffer)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
response, err = client.Do(request)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
longObjName = fmt.Sprintf("%0256d", 1)
|
||||
request, err = s.newRequest("PUT", testAPIXLServer.URL+"/put-object-long-name/"+longObjName, int64(buffer.Len()), buffer)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
|
@ -73,7 +73,7 @@ func getSingleNodeObjectLayer() (ObjectLayer, string, error) {
|
||||
// removeRoots - Cleans up initialized directories during tests.
|
||||
func removeRoots(roots []string) {
|
||||
for _, root := range roots {
|
||||
os.RemoveAll(root)
|
||||
removeAll(root)
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ func removeRoots(roots []string) {
|
||||
func removeRandomDisk(disks []string, removeCount int) {
|
||||
ints := randInts(len(disks))
|
||||
for _, i := range ints[:removeCount] {
|
||||
os.RemoveAll(disks[i-1])
|
||||
removeAll(disks[i-1])
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user