ste/sourceInfoProvider-Local_windows.go (92 lines of code) (raw):

// +build windows package ste import ( "fmt" "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file" "os" "strings" "syscall" "time" "unsafe" "github.com/hillu/go-ntdll" "github.com/Azure/azure-storage-azcopy/v10/common" "golang.org/x/sys/windows" "github.com/Azure/azure-storage-azcopy/v10/sddl" ) // This file os-triggers the ISMBPropertyBearingSourceInfoProvider and CustomLocalOpener interfaces on a local SIP. // getHandle obtains a windows file handle with generic read permissions & backup semantics func (f localFileSourceInfoProvider) getHandle(path string) (ntdll.Handle, error) { srcPtr, err := syscall.UTF16PtrFromString(path) if err != nil { return 0, err } // custom open call, because must specify FILE_FLAG_BACKUP_SEMANTICS to make --backup mode work properly (i.e. our use of SeBackupPrivilege) fd, err := windows.CreateFile(srcPtr, windows.GENERIC_READ, windows.FILE_SHARE_READ, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0) if err != nil { return 0, err } return ntdll.Handle(fd), err } func (f localFileSourceInfoProvider) Open(path string) (*os.File, error) { fd, err := f.getHandle(path) if err != nil { return nil, err } file := os.NewFile(uintptr(fd), path) if file == nil { return nil, os.ErrInvalid } return file, nil } func (f localFileSourceInfoProvider) GetSDDL() (string, error) { // We only need Owner, Group, and DACLs for azure files. fd, err := f.getHandle(f.jptm.Info().Source) if err != nil { return "", err } buf := make([]byte, 512) bufLen := uint32(len(buf)) needValidate := false status := ntdll.CallWithExpandingBuffer(func() ntdll.NtStatus { status := ntdll.NtQuerySecurityObject( fd, windows.OWNER_SECURITY_INFORMATION|windows.GROUP_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION, (*ntdll.SecurityDescriptor)(unsafe.Pointer(&buf[0])), uint32(len(buf)), &bufLen) /* On certain older versions of Windows/Server and certain SAN/SMB emulator software, on any status but STATUS_BUFFER_TOO_SMALL, bufLen will be returned as 0. CallWithExpandingBuffer does not handle this correctly. Thus, we have to attain the real length of the security descriptor and correct the output, otherwise we panic due to an OOB error on the array. */ // get real buffer length, since what's returned by ntquerysecurityobject is questionable for STATUS_SUCCESS if status == ntdll.STATUS_SUCCESS { sd := (*windows.SECURITY_DESCRIPTOR)(unsafe.Pointer(&buf[0])) // ntdll.SecurityDescriptor is equivalent bufLen = sd.Length() needValidate = true } return status }, &buf, &bufLen) if status != ntdll.STATUS_SUCCESS { return "", fmt.Errorf("failed to query security object %s (ntstatus: %s)", f.jptm.Info().Source, status.String()) } sd := (*windows.SECURITY_DESCRIPTOR)(unsafe.Pointer(&buf[0])) // ntdll.SecurityDescriptor is equivalent if needValidate && !sd.IsValid() { return "", fmt.Errorf("failed to query security object %s (invalid security descriptor returned w/ success status)", f.jptm.Info().Source) } fSDDL, err := sddl.ParseSDDL(sd.String()) if err != nil { return "", err } if strings.TrimSpace(fSDDL.String()) != strings.TrimSpace(sd.String()) { panic("SDDL sanity check failed (parsed string output != original string.)") } return fSDDL.PortableString(), nil } func (f localFileSourceInfoProvider) GetSMBProperties() (TypedSMBPropertyHolder, error) { info, err := common.GetFileInformation(f.jptm.Info().Source) return HandleInfo{info}, err } type HandleInfo struct { windows.ByHandleFileInformation } func (hi HandleInfo) FileCreationTime() time.Time { return time.Unix(0, hi.CreationTime.Nanoseconds()) } func (hi HandleInfo) FileLastWriteTime() time.Time { return time.Unix(0, hi.LastWriteTime.Nanoseconds()) } func (hi HandleInfo) FileAttributes() (*file.NTFSFileAttributes, error) { // Can't shorthand it because the function name overrides. return FileAttributesFromUint32(hi.ByHandleFileInformation.FileAttributes) }