1 //go:build windows 2 // +build windows 3 4 /* 5 Copyright 2023 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package filesystem 21 22 import ( 23 "fmt" 24 "net" 25 "os" 26 "path/filepath" 27 "strings" 28 "time" 29 30 "k8s.io/apimachinery/pkg/util/wait" 31 "k8s.io/klog/v2" 32 ) 33 34 const ( 35 // Amount of time to wait between attempting to use a Unix domain socket. 36 // As detailed in https://github.com/kubernetes/kubernetes/issues/104584 37 // the first attempt will most likely fail, hence the need to retry 38 socketDialRetryPeriod = 1 * time.Second 39 // Overall timeout value to dial a Unix domain socket, including retries 40 socketDialTimeout = 4 * time.Second 41 ) 42 43 // IsUnixDomainSocket returns whether a given file is a AF_UNIX socket file 44 // Note that due to the retry logic inside, it could take up to 4 seconds 45 // to determine whether or not the file path supplied is a Unix domain socket 46 func IsUnixDomainSocket(filePath string) (bool, error) { 47 // Due to the absence of golang support for os.ModeSocket in Windows (https://github.com/golang/go/issues/33357) 48 // we need to dial the file and check if we receive an error to determine if a file is Unix Domain Socket file. 49 50 // Note that querrying for the Reparse Points (https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points) 51 // for the file (using FSCTL_GET_REPARSE_POINT) and checking for reparse tag: reparseTagSocket 52 // does NOT work in 1809 if the socket file is created within a bind mounted directory by a container 53 // and the FSCTL is issued in the host by the kubelet. 54 55 // If the file does not exist, it cannot be a Unix domain socket. 56 if _, err := os.Stat(filePath); os.IsNotExist(err) { 57 return false, fmt.Errorf("File %s not found. Err: %v", filePath, err) 58 } 59 60 klog.V(6).InfoS("Function IsUnixDomainSocket starts", "filePath", filePath) 61 // As detailed in https://github.com/kubernetes/kubernetes/issues/104584 we cannot rely 62 // on the Unix Domain socket working on the very first try, hence the potential need to 63 // dial multiple times 64 var lastSocketErr error 65 err := wait.PollImmediate(socketDialRetryPeriod, socketDialTimeout, 66 func() (bool, error) { 67 klog.V(6).InfoS("Dialing the socket", "filePath", filePath) 68 var c net.Conn 69 c, lastSocketErr = net.Dial("unix", filePath) 70 if lastSocketErr == nil { 71 c.Close() 72 klog.V(6).InfoS("Socket dialed successfully", "filePath", filePath) 73 return true, nil 74 } 75 klog.V(6).InfoS("Failed the current attempt to dial the socket, so pausing before retry", 76 "filePath", filePath, "err", lastSocketErr, "socketDialRetryPeriod", 77 socketDialRetryPeriod) 78 return false, nil 79 }) 80 81 // PollImmediate will return "timed out waiting for the condition" if the function it 82 // invokes never returns true 83 if err != nil { 84 klog.V(2).InfoS("Failed all attempts to dial the socket so marking it as a non-Unix Domain socket. Last socket error along with the error from PollImmediate follow", 85 "filePath", filePath, "lastSocketErr", lastSocketErr, "err", err) 86 return false, nil 87 } 88 return true, nil 89 } 90 91 // IsAbs returns whether the given path is absolute or not. 92 // On Windows, filepath.IsAbs will not return True for paths prefixed with a slash, even 93 // though they can be used as absolute paths (https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats). 94 // 95 // WARN: It isn't safe to use this for API values which will propagate across systems (e.g. REST API values 96 // that get validated on Unix, persisted, then consumed by Windows, etc). 97 func IsAbs(path string) bool { 98 return filepath.IsAbs(path) || strings.HasPrefix(path, `\`) || strings.HasPrefix(path, `/`) 99 } 100