Unverified Commit a95d6cb0 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #62364 from dixudx/automated-cherry-pick-of-#62154-upstream-release-1.10

Automatic merge from submit-queue. Automated cherry pick of #62154: fix incompatible file type checking on Windows Cherry pick of #62154 on release-1.10. #62154: fix incompatible file type checking on Windows
parents 568afb4e 3437d8b8
...@@ -102,6 +102,7 @@ go_test( ...@@ -102,6 +102,7 @@ go_test(
] + select({ ] + select({
"@io_bazel_rules_go//go/platform:linux": [ "@io_bazel_rules_go//go/platform:linux": [
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library",
], ],
"@io_bazel_rules_go//go/platform:windows": [ "@io_bazel_rules_go//go/platform:windows": [
"//vendor/github.com/stretchr/testify/assert:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library",
......
...@@ -19,6 +19,7 @@ limitations under the License. ...@@ -19,6 +19,7 @@ limitations under the License.
package mount package mount
import ( import (
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
...@@ -337,3 +338,37 @@ func startsWithBackstep(rel string) bool { ...@@ -337,3 +338,37 @@ func startsWithBackstep(rel string) bool {
// normalize to / and check for ../ // normalize to / and check for ../
return rel == ".." || strings.HasPrefix(filepath.ToSlash(rel), "../") return rel == ".." || strings.HasPrefix(filepath.ToSlash(rel), "../")
} }
// getFileType checks for file/directory/socket and block/character devices
func getFileType(pathname string) (FileType, error) {
var pathType FileType
info, err := os.Stat(pathname)
if os.IsNotExist(err) {
return pathType, fmt.Errorf("path %q does not exist", pathname)
}
// err in call to os.Stat
if err != nil {
return pathType, err
}
// checks whether the mode is the target mode
isSpecificMode := func(mode, targetMode os.FileMode) bool {
return mode&targetMode == targetMode
}
mode := info.Mode()
if mode.IsDir() {
return FileTypeDirectory, nil
} else if mode.IsRegular() {
return FileTypeFile, nil
} else if isSpecificMode(mode, os.ModeSocket) {
return FileTypeSocket, nil
} else if isSpecificMode(mode, os.ModeDevice) {
if isSpecificMode(mode, os.ModeCharDevice) {
return FileTypeCharDev, nil
}
return FileTypeBlockDev, nil
}
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
}
...@@ -423,31 +423,7 @@ func (mounter *Mounter) MakeRShared(path string) error { ...@@ -423,31 +423,7 @@ func (mounter *Mounter) MakeRShared(path string) error {
} }
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) { func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
var pathType FileType return getFileType(pathname)
finfo, err := os.Stat(pathname)
if os.IsNotExist(err) {
return pathType, fmt.Errorf("path %q does not exist", pathname)
}
// err in call to os.Stat
if err != nil {
return pathType, err
}
mode := finfo.Sys().(*syscall.Stat_t).Mode
switch mode & syscall.S_IFMT {
case syscall.S_IFSOCK:
return FileTypeSocket, nil
case syscall.S_IFBLK:
return FileTypeBlockDev, nil
case syscall.S_IFCHR:
return FileTypeCharDev, nil
case syscall.S_IFDIR:
return FileTypeDirectory, nil
case syscall.S_IFREG:
return FileTypeFile, nil
}
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
} }
func (mounter *Mounter) MakeDir(pathname string) error { func (mounter *Mounter) MakeDir(pathname string) error {
......
...@@ -25,10 +25,12 @@ import ( ...@@ -25,10 +25,12 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strconv"
"strings"
"syscall" "syscall"
"testing" "testing"
"strconv" "k8s.io/utils/exec"
"github.com/golang/glog" "github.com/golang/glog"
) )
...@@ -1181,7 +1183,7 @@ func TestBindSubPath(t *testing.T) { ...@@ -1181,7 +1183,7 @@ func TestBindSubPath(t *testing.T) {
expectError: false, expectError: false,
}, },
{ {
name: "subpath-mounting-unix-socket", name: "mount-unix-socket",
prepare: func(base string) ([]string, string, string, error) { prepare: func(base string) ([]string, string, string, error) {
volpath, subpathMount := getTestPaths(base) volpath, subpathMount := getTestPaths(base)
mounts := []string{subpathMount} mounts := []string{subpathMount}
...@@ -1193,9 +1195,9 @@ func TestBindSubPath(t *testing.T) { ...@@ -1193,9 +1195,9 @@ func TestBindSubPath(t *testing.T) {
return nil, "", "", err return nil, "", "", err
} }
testSocketFile := filepath.Join(volpath, "mount_test.sock") socketFile, socketCreateError := createSocketFile(volpath)
_, err := net.Listen("unix", testSocketFile)
return mounts, volpath, testSocketFile, err return mounts, volpath, socketFile, socketCreateError
}, },
expectError: false, expectError: false,
}, },
...@@ -1226,6 +1228,7 @@ func TestBindSubPath(t *testing.T) { ...@@ -1226,6 +1228,7 @@ func TestBindSubPath(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf(err.Error()) t.Fatalf(err.Error())
} }
mounts, volPath, subPath, err := test.prepare(base) mounts, volPath, subPath, err := test.prepare(base)
if err != nil { if err != nil {
os.RemoveAll(base) os.RemoveAll(base)
...@@ -1476,29 +1479,29 @@ func TestSafeOpen(t *testing.T) { ...@@ -1476,29 +1479,29 @@ func TestSafeOpen(t *testing.T) {
true, true,
}, },
{ {
"mounting-unix-socket", "mount-unix-socket",
func(base string) error { func(base string) error {
testSocketFile := filepath.Join(base, "mount_test.sock") socketFile, socketError := createSocketFile(base)
_, err := net.Listen("unix", testSocketFile)
if err != nil { if socketError != nil {
return fmt.Errorf("Error preparing socket file %s with %v", testSocketFile, err) return fmt.Errorf("Error preparing socket file %s with %v", socketFile, socketError)
} }
return nil return nil
}, },
"mount_test.sock", "mt.sock",
false, false,
}, },
{ {
"mounting-unix-socket-in-middle", "mounting-unix-socket-in-middle",
func(base string) error { func(base string) error {
testSocketFile := filepath.Join(base, "mount_test.sock") testSocketFile, socketError := createSocketFile(base)
_, err := net.Listen("unix", testSocketFile)
if err != nil { if socketError != nil {
return fmt.Errorf("Error preparing socket file %s with %v", testSocketFile, err) return fmt.Errorf("Error preparing socket file %s with %v", testSocketFile, socketError)
} }
return nil return nil
}, },
"mount_test.sock/bar", "mt.sock/bar",
true, true,
}, },
} }
...@@ -1509,6 +1512,7 @@ func TestSafeOpen(t *testing.T) { ...@@ -1509,6 +1512,7 @@ func TestSafeOpen(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf(err.Error()) t.Fatalf(err.Error())
} }
test.prepare(base) test.prepare(base)
pathToCreate := filepath.Join(base, test.path) pathToCreate := filepath.Join(base, test.path)
fd, err := doSafeOpen(pathToCreate, base) fd, err := doSafeOpen(pathToCreate, base)
...@@ -1527,6 +1531,25 @@ func TestSafeOpen(t *testing.T) { ...@@ -1527,6 +1531,25 @@ func TestSafeOpen(t *testing.T) {
} }
} }
func createSocketFile(socketDir string) (string, error) {
testSocketFile := filepath.Join(socketDir, "mt.sock")
// Switch to volume path and create the socket file
// socket file can not have length of more than 108 character
// and hence we must use relative path
oldDir, _ := os.Getwd()
err := os.Chdir(socketDir)
if err != nil {
return "", err
}
defer func() {
os.Chdir(oldDir)
}()
_, socketCreateError := net.Listen("unix", "mt.sock")
return testSocketFile, socketCreateError
}
func TestFindExistingPrefix(t *testing.T) { func TestFindExistingPrefix(t *testing.T) {
defaultPerm := os.FileMode(0750) defaultPerm := os.FileMode(0750)
tests := []struct { tests := []struct {
...@@ -1659,3 +1682,110 @@ func TestFindExistingPrefix(t *testing.T) { ...@@ -1659,3 +1682,110 @@ func TestFindExistingPrefix(t *testing.T) {
os.RemoveAll(base) os.RemoveAll(base)
} }
} }
func TestGetFileType(t *testing.T) {
mounter := Mounter{"fake/path", false}
testCase := []struct {
name string
expectedType FileType
setUp func() (string, string, error)
}{
{
"Directory Test",
FileTypeDirectory,
func() (string, string, error) {
tempDir, err := ioutil.TempDir("", "test-get-filetype-")
return tempDir, tempDir, err
},
},
{
"File Test",
FileTypeFile,
func() (string, string, error) {
tempFile, err := ioutil.TempFile("", "test-get-filetype")
if err != nil {
return "", "", err
}
tempFile.Close()
return tempFile.Name(), tempFile.Name(), nil
},
},
{
"Socket Test",
FileTypeSocket,
func() (string, string, error) {
tempDir, err := ioutil.TempDir("", "test-get-filetype-")
if err != nil {
return "", "", err
}
tempSocketFile, err := createSocketFile(tempDir)
return tempSocketFile, tempDir, err
},
},
{
"Block Device Test",
FileTypeBlockDev,
func() (string, string, error) {
tempDir, err := ioutil.TempDir("", "test-get-filetype-")
if err != nil {
return "", "", err
}
tempBlockFile := filepath.Join(tempDir, "test_blk_dev")
outputBytes, err := exec.New().Command("mknod", tempBlockFile, "b", "89", "1").CombinedOutput()
if err != nil {
err = fmt.Errorf("%v: %s ", err, outputBytes)
}
return tempBlockFile, tempDir, err
},
},
{
"Character Device Test",
FileTypeCharDev,
func() (string, string, error) {
tempDir, err := ioutil.TempDir("", "test-get-filetype-")
if err != nil {
return "", "", err
}
tempCharFile := filepath.Join(tempDir, "test_char_dev")
outputBytes, err := exec.New().Command("mknod", tempCharFile, "c", "89", "1").CombinedOutput()
if err != nil {
err = fmt.Errorf("%v: %s ", err, outputBytes)
}
return tempCharFile, tempDir, err
},
},
}
for idx, tc := range testCase {
path, cleanUpPath, err := tc.setUp()
if err != nil {
// Locally passed, but upstream CI is not friendly to create such device files
// Leave "Operation not permitted" out, which can be covered in an e2e test
if isOperationNotPermittedError(err) {
continue
}
t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err)
}
if len(cleanUpPath) > 0 {
defer os.RemoveAll(cleanUpPath)
}
fileType, err := mounter.GetFileType(path)
if err != nil {
t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err)
}
if fileType != tc.expectedType {
t.Fatalf("[%d-%s] expected %s, but got %s", idx, tc.name, tc.expectedType, fileType)
}
}
}
func isOperationNotPermittedError(err error) bool {
if strings.Contains(err.Error(), "Operation not permitted") {
return true
}
return false
}
...@@ -201,31 +201,7 @@ func (mounter *Mounter) MakeRShared(path string) error { ...@@ -201,31 +201,7 @@ func (mounter *Mounter) MakeRShared(path string) error {
// GetFileType checks for sockets/block/character devices // GetFileType checks for sockets/block/character devices
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) { func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
var pathType FileType return getFileType(pathname)
info, err := os.Stat(pathname)
if os.IsNotExist(err) {
return pathType, fmt.Errorf("path %q does not exist", pathname)
}
// err in call to os.Stat
if err != nil {
return pathType, err
}
mode := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
switch mode & syscall.S_IFMT {
case syscall.S_IFSOCK:
return FileTypeSocket, nil
case syscall.S_IFBLK:
return FileTypeBlockDev, nil
case syscall.S_IFCHR:
return FileTypeCharDev, nil
case syscall.S_IFDIR:
return FileTypeDirectory, nil
case syscall.S_IFREG:
return FileTypeFile, nil
}
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
} }
// MakeFile creates a new directory // MakeFile creates a new directory
......
...@@ -20,6 +20,7 @@ package mount ...@@ -20,6 +20,7 @@ package mount
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
...@@ -551,3 +552,52 @@ func TestPathWithinBase(t *testing.T) { ...@@ -551,3 +552,52 @@ func TestPathWithinBase(t *testing.T) {
test.fullPath, test.basePath, result, test.expectedResult) test.fullPath, test.basePath, result, test.expectedResult)
} }
} }
func TestGetFileType(t *testing.T) {
mounter := New("fake/path")
testCase := []struct {
name string
expectedType FileType
setUp func() (string, string, error)
}{
{
"Directory Test",
FileTypeDirectory,
func() (string, string, error) {
tempDir, err := ioutil.TempDir("", "test-get-filetype-")
return tempDir, tempDir, err
},
},
{
"File Test",
FileTypeFile,
func() (string, string, error) {
tempFile, err := ioutil.TempFile("", "test-get-filetype")
if err != nil {
return "", "", err
}
tempFile.Close()
return tempFile.Name(), tempFile.Name(), nil
},
},
}
for idx, tc := range testCase {
path, cleanUpPath, err := tc.setUp()
if err != nil {
t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err)
}
if len(cleanUpPath) > 0 {
defer os.RemoveAll(cleanUpPath)
}
fileType, err := mounter.GetFileType(path)
if err != nil {
t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err)
}
if fileType != tc.expectedType {
t.Fatalf("[%d-%s] expected %s, but got %s", idx, tc.name, tc.expectedType, fileType)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment