mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-10-19 05:39:51 +00:00
e9c729b692
Bumps the k8s-dependencies group with 1 update: [k8s.io/kubernetes](https://github.com/kubernetes/kubernetes). Updates `k8s.io/kubernetes` from 1.30.2 to 1.30.3 - [Release notes](https://github.com/kubernetes/kubernetes/releases) - [Commits](https://github.com/kubernetes/kubernetes/compare/v1.30.2...v1.30.3) --- updated-dependencies: - dependency-name: k8s.io/kubernetes dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s-dependencies ... Signed-off-by: dependabot[bot] <support@github.com>
256 lines
9.1 KiB
Go
256 lines
9.1 KiB
Go
//go:build windows
|
|
// +build windows
|
|
|
|
/*
|
|
Copyright 2023 The Kubernetes Authors.
|
|
|
|
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 filesystem
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"k8s.io/apimachinery/pkg/util/wait"
|
|
"k8s.io/klog/v2"
|
|
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
const (
|
|
// Amount of time to wait between attempting to use a Unix domain socket.
|
|
// As detailed in https://github.com/kubernetes/kubernetes/issues/104584
|
|
// the first attempt will most likely fail, hence the need to retry
|
|
socketDialRetryPeriod = 1 * time.Second
|
|
// Overall timeout value to dial a Unix domain socket, including retries
|
|
socketDialTimeout = 4 * time.Second
|
|
)
|
|
|
|
// IsUnixDomainSocket returns whether a given file is a AF_UNIX socket file
|
|
// Note that due to the retry logic inside, it could take up to 4 seconds
|
|
// to determine whether or not the file path supplied is a Unix domain socket
|
|
func IsUnixDomainSocket(filePath string) (bool, error) {
|
|
// Due to the absence of golang support for os.ModeSocket in Windows (https://github.com/golang/go/issues/33357)
|
|
// we need to dial the file and check if we receive an error to determine if a file is Unix Domain Socket file.
|
|
|
|
// Note that querrying for the Reparse Points (https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points)
|
|
// for the file (using FSCTL_GET_REPARSE_POINT) and checking for reparse tag: reparseTagSocket
|
|
// does NOT work in 1809 if the socket file is created within a bind mounted directory by a container
|
|
// and the FSCTL is issued in the host by the kubelet.
|
|
|
|
// If the file does not exist, it cannot be a Unix domain socket.
|
|
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
|
return false, fmt.Errorf("File %s not found. Err: %v", filePath, err)
|
|
}
|
|
|
|
klog.V(6).InfoS("Function IsUnixDomainSocket starts", "filePath", filePath)
|
|
// As detailed in https://github.com/kubernetes/kubernetes/issues/104584 we cannot rely
|
|
// on the Unix Domain socket working on the very first try, hence the potential need to
|
|
// dial multiple times
|
|
var lastSocketErr error
|
|
err := wait.PollImmediate(socketDialRetryPeriod, socketDialTimeout,
|
|
func() (bool, error) {
|
|
klog.V(6).InfoS("Dialing the socket", "filePath", filePath)
|
|
var c net.Conn
|
|
c, lastSocketErr = net.Dial("unix", filePath)
|
|
if lastSocketErr == nil {
|
|
c.Close()
|
|
klog.V(6).InfoS("Socket dialed successfully", "filePath", filePath)
|
|
return true, nil
|
|
}
|
|
klog.V(6).InfoS("Failed the current attempt to dial the socket, so pausing before retry",
|
|
"filePath", filePath, "err", lastSocketErr, "socketDialRetryPeriod",
|
|
socketDialRetryPeriod)
|
|
return false, nil
|
|
})
|
|
|
|
// PollImmediate will return "timed out waiting for the condition" if the function it
|
|
// invokes never returns true
|
|
if err != nil {
|
|
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",
|
|
"filePath", filePath, "lastSocketErr", lastSocketErr, "err", err)
|
|
return false, nil
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// On Windows os.Mkdir all doesn't set any permissions so call the Chown function below to set
|
|
// permissions once the directory is created.
|
|
func MkdirAll(path string, perm os.FileMode) error {
|
|
klog.V(6).InfoS("Function MkdirAll starts", "path", path, "perm", perm)
|
|
err := os.MkdirAll(path, perm)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating directory %s: %v", path, err)
|
|
}
|
|
|
|
err = Chmod(path, perm)
|
|
if err != nil {
|
|
return fmt.Errorf("Error setting permissions for directory %s: %v", path, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const (
|
|
// These aren't defined in the syscall package for Windows :(
|
|
USER_READ = 0x100
|
|
USER_WRITE = 0x80
|
|
USER_EXECUTE = 0x40
|
|
GROUP_READ = 0x20
|
|
GROUP_WRITE = 0x10
|
|
GROUP_EXECUTE = 0x8
|
|
OTHERS_READ = 0x4
|
|
OTHERS_WRITE = 0x2
|
|
OTHERS_EXECUTE = 0x1
|
|
USER_ALL = USER_READ | USER_WRITE | USER_EXECUTE
|
|
GROUP_ALL = GROUP_READ | GROUP_WRITE | GROUP_EXECUTE
|
|
OTHERS_ALL = OTHERS_READ | OTHERS_WRITE | OTHERS_EXECUTE
|
|
)
|
|
|
|
// On Windows os.Chmod only sets the read-only flag on files, so we need to use Windows APIs to set the desired access on files / directories.
|
|
// The OWNER mode will set file permissions for the file owner SID, the GROUP mode will set file permissions for the file group SID,
|
|
// and the OTHERS mode will set file permissions for BUILTIN\Users.
|
|
// Please note that Windows containers can be run as one of two user accounts; ContainerUser or ContainerAdministrator.
|
|
// Containers run as ContainerAdministrator will inherit permissions from BUILTIN\Administrators,
|
|
// while containers run as ContainerUser will inherit permissions from BUILTIN\Users.
|
|
// Windows containers do not have the ability to run as a custom user account that is known to the host so the OTHERS group mode
|
|
// is used to grant / deny permissions of files on the hosts to the ContainerUser account.
|
|
func Chmod(path string, filemode os.FileMode) error {
|
|
klog.V(6).InfoS("Function Chmod starts", "path", path, "filemode", filemode)
|
|
// Get security descriptor for the file
|
|
sd, err := windows.GetNamedSecurityInfo(
|
|
path,
|
|
windows.SE_FILE_OBJECT,
|
|
windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION|windows.OWNER_SECURITY_INFORMATION|windows.GROUP_SECURITY_INFORMATION)
|
|
if err != nil {
|
|
return fmt.Errorf("Error getting security descriptor for file %s: %v", path, err)
|
|
}
|
|
|
|
// Get owner SID from the security descriptor for assigning USER permissions
|
|
owner, _, err := sd.Owner()
|
|
if err != nil {
|
|
return fmt.Errorf("Error getting owner SID for file %s: %v", path, err)
|
|
}
|
|
ownerString := owner.String()
|
|
|
|
// Get the group SID from the security descriptor for assigning GROUP permissions
|
|
group, _, err := sd.Group()
|
|
if err != nil {
|
|
return fmt.Errorf("Error getting group SID for file %s: %v", path, err)
|
|
}
|
|
groupString := group.String()
|
|
|
|
mask := uint32(windows.ACCESS_MASK(filemode))
|
|
|
|
// Build a new Discretionary Access Control List (DACL) with the desired permissions using
|
|
//the Security Descriptor Definition Language (SDDL) format.
|
|
// https://learn.microsoft.com/windows/win32/secauthz/security-descriptor-definition-language
|
|
// the DACL is a list of Access Control Entries (ACEs) where each ACE represents the permissions (Allow or Deny) for a specific SID.
|
|
// Each ACE has the following format:
|
|
// (AceType;AceFlags;Rights;ObjectGuid;InheritObjectGuid;AccountSid)
|
|
// We can leave ObjectGuid and InheritObjectGuid empty for our purposes.
|
|
|
|
dacl := "D:"
|
|
|
|
// build the owner ACE
|
|
dacl += "(A;OICI;"
|
|
if mask&USER_ALL == USER_ALL {
|
|
dacl += "FA"
|
|
} else {
|
|
if mask&USER_READ == USER_READ {
|
|
dacl += "FR"
|
|
}
|
|
if mask&USER_WRITE == USER_WRITE {
|
|
dacl += "FW"
|
|
}
|
|
if mask&USER_EXECUTE == USER_EXECUTE {
|
|
dacl += "FX"
|
|
}
|
|
}
|
|
dacl += ";;;" + ownerString + ")"
|
|
|
|
// Build the group ACE
|
|
dacl += "(A;OICI;"
|
|
if mask&GROUP_ALL == GROUP_ALL {
|
|
dacl += "FA"
|
|
} else {
|
|
if mask&GROUP_READ == GROUP_READ {
|
|
dacl += "FR"
|
|
}
|
|
if mask&GROUP_WRITE == GROUP_WRITE {
|
|
dacl += "FW"
|
|
}
|
|
if mask&GROUP_EXECUTE == GROUP_EXECUTE {
|
|
dacl += "FX"
|
|
}
|
|
}
|
|
dacl += ";;;" + groupString + ")"
|
|
|
|
// Build the others ACE
|
|
dacl += "(A;OICI;"
|
|
if mask&OTHERS_ALL == OTHERS_ALL {
|
|
dacl += "FA"
|
|
} else {
|
|
if mask&OTHERS_READ == OTHERS_READ {
|
|
dacl += "FR"
|
|
}
|
|
if mask&OTHERS_WRITE == OTHERS_WRITE {
|
|
dacl += "FW"
|
|
}
|
|
if mask&OTHERS_EXECUTE == OTHERS_EXECUTE {
|
|
dacl += "FX"
|
|
}
|
|
}
|
|
dacl += ";;;BU)"
|
|
|
|
klog.V(6).InfoS("Setting new DACL for path", "path", path, "dacl", dacl)
|
|
|
|
// create a new security descriptor from the DACL string
|
|
newSD, err := windows.SecurityDescriptorFromString(dacl)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating new security descriptor from DACL string: %v", err)
|
|
}
|
|
|
|
// get the DACL in binary format from the newly created security descriptor
|
|
newDACL, _, err := newSD.DACL()
|
|
if err != nil {
|
|
return fmt.Errorf("Error getting DACL from new security descriptor: %v", err)
|
|
}
|
|
|
|
// Write the new security descriptor to the file
|
|
return windows.SetNamedSecurityInfo(
|
|
path,
|
|
windows.SE_FILE_OBJECT,
|
|
windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION,
|
|
nil, // owner SID
|
|
nil, // group SID
|
|
newDACL,
|
|
nil) // SACL
|
|
}
|
|
|
|
// IsAbs returns whether the given path is absolute or not.
|
|
// On Windows, filepath.IsAbs will not return True for paths prefixed with a slash, even
|
|
// though they can be used as absolute paths (https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats).
|
|
//
|
|
// WARN: It isn't safe to use this for API values which will propagate across systems (e.g. REST API values
|
|
// that get validated on Unix, persisted, then consumed by Windows, etc).
|
|
func IsAbs(path string) bool {
|
|
return filepath.IsAbs(path) || strings.HasPrefix(path, `\`) || strings.HasPrefix(path, `/`)
|
|
}
|