ste/sourceInfoProvider-Local_linux.go (165 lines of code) (raw):
//go:build linux
// +build linux
package ste
import (
"fmt"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file"
"github.com/Azure/azure-storage-azcopy/v10/common"
"github.com/Azure/azure-storage-azcopy/v10/sddl"
"golang.org/x/sys/unix"
"strings"
"time"
)
func (f localFileSourceInfoProvider) HasUNIXProperties() bool {
return true
}
func (f localFileSourceInfoProvider) GetUNIXProperties() (common.UnixStatAdapter, error) {
{ // attempt to call statx, if ENOSYS is returned, statx is unavailable
var stat unix.Statx_t
statxFlags := unix.AT_STATX_SYNC_AS_STAT
if f.EntityType() == common.EEntityType.Symlink() {
statxFlags |= unix.AT_SYMLINK_NOFOLLOW
}
// dirfd is a null pointer, because we should only ever be passing relative paths here, and directories will be passed via transferInfo.Source.
// AT_SYMLINK_NOFOLLOW is not used, because we automagically resolve symlinks. TODO: Add option to not follow symlinks, and use AT_SYMLINK_NOFOLLOW when resolving is disabled.
err := unix.Statx(0, f.transferInfo.Source,
statxFlags,
unix.STATX_ALL,
&stat)
if err != nil && err != unix.ENOSYS {
return nil, err
} else if err == nil {
return StatxTAdapter(stat), nil
}
}
var stat unix.Stat_t
err := unix.Stat(f.transferInfo.Source, &stat)
if err != nil {
return nil, err
}
return StatTAdapter(stat), nil
}
type StatxTAdapter unix.Statx_t
func (s StatxTAdapter) Extended() bool {
return true
}
func (s StatxTAdapter) StatxMask() uint32 {
return s.Mask
}
func (s StatxTAdapter) Attribute() uint64 {
return s.Attributes
}
func (s StatxTAdapter) AttributeMask() uint64 {
return s.Attributes_mask
}
func (s StatxTAdapter) BTime() time.Time {
return time.Unix(s.Btime.Sec, int64(s.Btime.Nsec))
}
func (s StatxTAdapter) NLink() uint64 {
return uint64(s.Nlink)
}
func (s StatxTAdapter) Owner() uint32 {
return s.Uid
}
func (s StatxTAdapter) Group() uint32 {
return s.Gid
}
func (s StatxTAdapter) FileMode() uint32 {
return uint32(s.Mode)
}
func (s StatxTAdapter) INode() uint64 {
return s.Ino
}
func (s StatxTAdapter) Device() uint64 {
return unix.Mkdev(s.Dev_major, s.Dev_minor)
}
func (s StatxTAdapter) RDevice() uint64 {
return unix.Mkdev(s.Rdev_major, s.Rdev_minor)
}
func (s StatxTAdapter) ATime() time.Time {
return time.Unix(s.Atime.Sec, int64(s.Atime.Nsec))
}
func (s StatxTAdapter) MTime() time.Time {
return time.Unix(s.Mtime.Sec, int64(s.Mtime.Nsec))
}
func (s StatxTAdapter) CTime() time.Time {
return time.Unix(s.Ctime.Sec, int64(s.Ctime.Nsec))
}
type StatTAdapter unix.Stat_t
func (s StatTAdapter) Extended() bool {
return false
}
func (s StatTAdapter) StatxMask() uint32 {
return 0
}
func (s StatTAdapter) Attribute() uint64 {
return 0
}
func (s StatTAdapter) AttributeMask() uint64 {
return 0
}
func (s StatTAdapter) BTime() time.Time {
return time.Time{}
}
func (s StatTAdapter) NLink() uint64 {
return uint64(s.Nlink) // On amd64, this is a uint64. On arm64, this is a uint32. Do not remove this typecast.
}
func (s StatTAdapter) Owner() uint32 {
return s.Uid
}
func (s StatTAdapter) Group() uint32 {
return s.Gid
}
func (s StatTAdapter) FileMode() uint32 {
return s.Mode
}
func (s StatTAdapter) INode() uint64 {
return s.Ino
}
func (s StatTAdapter) Device() uint64 {
return s.Dev
}
func (s StatTAdapter) RDevice() uint64 {
return s.Rdev
}
func (s StatTAdapter) ATime() time.Time {
return time.Unix(s.Atim.Unix())
}
func (s StatTAdapter) MTime() time.Time {
return time.Unix(s.Mtim.Unix())
}
func (s StatTAdapter) CTime() time.Time {
return time.Unix(s.Ctim.Unix())
}
// This file os-triggers the ISMBPropertyBearingSourceInfoProvider interface on a local SIP.
// Note: Linux SIP doesn't implement the ICustomLocalOpener since it doesn't need to do anything special, unlike
// Windows where we need to pass FILE_FLAG_BACKUP_SEMANTICS flag for opening file.
func (f localFileSourceInfoProvider) GetSDDL() (string, error) {
// We only need Owner, Group, and DACLs for azure files, CIFS_XATTR_CIFS_NTSD gets us that.
const securityInfoFlags sddl.SECURITY_INFORMATION = sddl.DACL_SECURITY_INFORMATION | sddl.OWNER_SECURITY_INFORMATION | sddl.GROUP_SECURITY_INFORMATION
// Query the Security Descriptor object for the given file.
sd, err := sddl.QuerySecurityObject(f.jptm.Info().Source, securityInfoFlags)
if err != nil {
return "", fmt.Errorf("sddl.QuerySecurityObject(%s, 0x%x) failed: %w",
f.jptm.Info().Source, securityInfoFlags, err)
}
// Convert the binary Security Descriptor to string in SDDL format.
// This is the Windows equivalent of ConvertSecurityDescriptorToStringSecurityDescriptorW().
sdStr, err := sddl.SecurityDescriptorToString(sd)
if err != nil {
// Panic, as it's unexpected and we would want to know.
panic(fmt.Errorf("Cannot parse binary Security Descriptor returned by QuerySecurityObject(%s, 0x%x): %v", f.jptm.Info().Source, securityInfoFlags, err))
}
fSDDL, err := sddl.ParseSDDL(sdStr)
if err != nil {
return "", fmt.Errorf("sddl.ParseSDDL(%s) failed: %w", sdStr, err)
}
if strings.TrimSpace(fSDDL.String()) != strings.TrimSpace(sdStr) {
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 {
common.ByHandleFileInformation
}
func (hi HandleInfo) FileCreationTime() time.Time {
// This returns nanoseconds since Unix Epoch.
return time.Unix(0, hi.CreationTime.Nanoseconds())
}
func (hi HandleInfo) FileLastWriteTime() time.Time {
// This returns nanoseconds since Unix Epoch.
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)
}