all: sync with master
This commit is contained in:
@@ -146,16 +146,6 @@ func IsOpenWrt() (ok bool) {
|
||||
return isOpenWrt()
|
||||
}
|
||||
|
||||
// NotifyReconfigureSignal notifies c on receiving reconfigure signals.
|
||||
func NotifyReconfigureSignal(c chan<- os.Signal) {
|
||||
notifyReconfigureSignal(c)
|
||||
}
|
||||
|
||||
// IsReconfigureSignal returns true if sig is a reconfigure signal.
|
||||
func IsReconfigureSignal(sig os.Signal) (ok bool) {
|
||||
return isReconfigureSignal(sig)
|
||||
}
|
||||
|
||||
// SendShutdownSignal sends the shutdown signal to the channel.
|
||||
func SendShutdownSignal(c chan<- os.Signal) {
|
||||
sendShutdownSignal(c)
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
//go:build darwin || freebsd || linux || openbsd
|
||||
//go:build unix
|
||||
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func notifyReconfigureSignal(c chan<- os.Signal) {
|
||||
signal.Notify(c, unix.SIGHUP)
|
||||
}
|
||||
|
||||
func isReconfigureSignal(sig os.Signal) (ok bool) {
|
||||
return sig == unix.SIGHUP
|
||||
}
|
||||
|
||||
func sendShutdownSignal(_ chan<- os.Signal) {
|
||||
// On Unix we are already notified by the system.
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ package aghos
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func setRlimit(val uint64) (err error) {
|
||||
func setRlimit(_ uint64) (err error) {
|
||||
return Unsupported("setrlimit")
|
||||
}
|
||||
|
||||
@@ -38,14 +37,6 @@ func isOpenWrt() (ok bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
func notifyReconfigureSignal(c chan<- os.Signal) {
|
||||
signal.Notify(c, windows.SIGHUP)
|
||||
}
|
||||
|
||||
func isReconfigureSignal(sig os.Signal) (ok bool) {
|
||||
return sig == windows.SIGHUP
|
||||
}
|
||||
|
||||
func sendShutdownSignal(c chan<- os.Signal) {
|
||||
c <- os.Interrupt
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
// TODO(e.burkov): Add platform-independent tests.
|
||||
|
||||
// Chmod is an extension for [os.Chmod] that properly handles Windows access
|
||||
// rights.
|
||||
func Chmod(name string, perm fs.FileMode) (err error) {
|
||||
return chmod(name, perm)
|
||||
}
|
||||
|
||||
// Mkdir is an extension for [os.Mkdir] that properly handles Windows access
|
||||
// rights.
|
||||
func Mkdir(name string, perm fs.FileMode) (err error) {
|
||||
return mkdir(name, perm)
|
||||
}
|
||||
|
||||
// MkdirAll is an extension for [os.MkdirAll] that properly handles Windows
|
||||
// access rights.
|
||||
func MkdirAll(path string, perm fs.FileMode) (err error) {
|
||||
return mkdirAll(path, perm)
|
||||
}
|
||||
|
||||
// WriteFile is an extension for [os.WriteFile] that properly handles Windows
|
||||
// access rights.
|
||||
func WriteFile(filename string, data []byte, perm fs.FileMode) (err error) {
|
||||
return writeFile(filename, data, perm)
|
||||
}
|
||||
|
||||
// OpenFile is an extension for [os.OpenFile] that properly handles Windows
|
||||
// access rights.
|
||||
func OpenFile(name string, flag int, perm fs.FileMode) (file *os.File, err error) {
|
||||
return openFile(name, flag, perm)
|
||||
}
|
||||
|
||||
// Stat is an extension for [os.Stat] that properly handles Windows access
|
||||
// rights.
|
||||
//
|
||||
// Note that on Windows the "other" permission bits combines the access rights
|
||||
// of any trustee that is neither the owner nor the owning group for the file.
|
||||
//
|
||||
// TODO(e.burkov): Inspect the behavior for the World (everyone) well-known
|
||||
// SID and, perhaps, use it.
|
||||
func Stat(name string) (fi fs.FileInfo, err error) {
|
||||
return stat(name)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
//go:build unix
|
||||
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/google/renameio/v2/maybe"
|
||||
)
|
||||
|
||||
// chmod is a Unix implementation of [Chmod].
|
||||
func chmod(name string, perm fs.FileMode) (err error) {
|
||||
return os.Chmod(name, perm)
|
||||
}
|
||||
|
||||
// mkdir is a Unix implementation of [Mkdir].
|
||||
func mkdir(name string, perm fs.FileMode) (err error) {
|
||||
return os.Mkdir(name, perm)
|
||||
}
|
||||
|
||||
// mkdirAll is a Unix implementation of [MkdirAll].
|
||||
func mkdirAll(path string, perm fs.FileMode) (err error) {
|
||||
return os.MkdirAll(path, perm)
|
||||
}
|
||||
|
||||
// writeFile is a Unix implementation of [WriteFile].
|
||||
func writeFile(filename string, data []byte, perm fs.FileMode) (err error) {
|
||||
return maybe.WriteFile(filename, data, perm)
|
||||
}
|
||||
|
||||
// openFile is a Unix implementation of [OpenFile].
|
||||
func openFile(name string, flag int, perm fs.FileMode) (file *os.File, err error) {
|
||||
// #nosec G304 -- This function simply wraps the [os.OpenFile] function, so
|
||||
// the security concerns should be addressed to the [OpenFile] calls.
|
||||
return os.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
// stat is a Unix implementation of [Stat].
|
||||
func stat(name string) (fi os.FileInfo, err error) {
|
||||
return os.Stat(name)
|
||||
}
|
||||
@@ -1,392 +0,0 @@
|
||||
//go:build windows
|
||||
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"unsafe"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// fileInfo is a Windows implementation of [fs.FileInfo], that contains the
|
||||
// filemode converted from the security descriptor.
|
||||
type fileInfo struct {
|
||||
// fs.FileInfo is embedded to provide the default implementations and data
|
||||
// successfully retrieved by [os.Stat].
|
||||
fs.FileInfo
|
||||
|
||||
// mode is the file mode converted from the security descriptor.
|
||||
mode fs.FileMode
|
||||
}
|
||||
|
||||
// type check
|
||||
var _ fs.FileInfo = (*fileInfo)(nil)
|
||||
|
||||
// Mode implements [fs.FileInfo.Mode] for [*fileInfo].
|
||||
func (fi *fileInfo) Mode() (mode fs.FileMode) { return fi.mode }
|
||||
|
||||
// stat is a Windows implementation of [Stat].
|
||||
func stat(name string) (fi os.FileInfo, err error) {
|
||||
absName, err := filepath.Abs(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("computing absolute path: %w", err)
|
||||
}
|
||||
|
||||
fi, err = os.Stat(absName)
|
||||
if err != nil {
|
||||
// Don't wrap the error, since it's informative enough as is.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dacl, owner, group, err := retrieveDACL(absName)
|
||||
if err != nil {
|
||||
// Don't wrap the error, since it's informative enough as is.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ownerMask, groupMask, otherMask windows.ACCESS_MASK
|
||||
for i := range uint32(dacl.AceCount) {
|
||||
var ace *windows.ACCESS_ALLOWED_ACE
|
||||
err = windows.GetAce(dacl, i, &ace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting access control entry at index %d: %w", i, err)
|
||||
}
|
||||
|
||||
entrySid := (*windows.SID)(unsafe.Pointer(&ace.SidStart))
|
||||
switch {
|
||||
case entrySid.Equals(owner):
|
||||
ownerMask |= ace.Mask
|
||||
case entrySid.Equals(group):
|
||||
groupMask |= ace.Mask
|
||||
default:
|
||||
otherMask |= ace.Mask
|
||||
}
|
||||
}
|
||||
|
||||
mode := fi.Mode()
|
||||
perm := masksToPerm(ownerMask, groupMask, otherMask, mode.IsDir())
|
||||
|
||||
return &fileInfo{
|
||||
FileInfo: fi,
|
||||
// Use the file mode from the security descriptor, but use the
|
||||
// calculated permission bits.
|
||||
mode: perm | mode&^fs.FileMode(0o777),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// retrieveDACL retrieves the discretionary access control list, owner, and
|
||||
// group from the security descriptor of the file with the specified absolute
|
||||
// name.
|
||||
func retrieveDACL(absName string) (dacl *windows.ACL, owner, group *windows.SID, err error) {
|
||||
// desiredSecInfo defines the parts of a security descriptor to retrieve.
|
||||
const desiredSecInfo windows.SECURITY_INFORMATION = windows.OWNER_SECURITY_INFORMATION |
|
||||
windows.GROUP_SECURITY_INFORMATION |
|
||||
windows.DACL_SECURITY_INFORMATION |
|
||||
windows.PROTECTED_DACL_SECURITY_INFORMATION |
|
||||
windows.UNPROTECTED_DACL_SECURITY_INFORMATION
|
||||
|
||||
sd, err := windows.GetNamedSecurityInfo(absName, windows.SE_FILE_OBJECT, desiredSecInfo)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("getting security descriptor: %w", err)
|
||||
}
|
||||
|
||||
dacl, _, err = sd.DACL()
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("getting discretionary access control list: %w", err)
|
||||
}
|
||||
|
||||
owner, _, err = sd.Owner()
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("getting owner sid: %w", err)
|
||||
}
|
||||
|
||||
group, _, err = sd.Group()
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("getting group sid: %w", err)
|
||||
}
|
||||
|
||||
return dacl, owner, group, nil
|
||||
}
|
||||
|
||||
// chmod is a Windows implementation of [Chmod].
|
||||
func chmod(name string, perm fs.FileMode) (err error) {
|
||||
fi, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting file info: %w", err)
|
||||
}
|
||||
|
||||
entries := make([]windows.EXPLICIT_ACCESS, 0, 3)
|
||||
creatorMask, groupMask, worldMask := permToMasks(perm, fi.IsDir())
|
||||
|
||||
sidMasks := []struct {
|
||||
Key windows.WELL_KNOWN_SID_TYPE
|
||||
Value windows.ACCESS_MASK
|
||||
}{{
|
||||
Key: windows.WinCreatorOwnerSid,
|
||||
Value: creatorMask,
|
||||
}, {
|
||||
Key: windows.WinCreatorGroupSid,
|
||||
Value: groupMask,
|
||||
}, {
|
||||
Key: windows.WinWorldSid,
|
||||
Value: worldMask,
|
||||
}}
|
||||
|
||||
var errs []error
|
||||
for _, sidMask := range sidMasks {
|
||||
if sidMask.Value == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var trustee windows.TRUSTEE
|
||||
trustee, err = newWellKnownTrustee(sidMask.Key)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
entries = append(entries, windows.EXPLICIT_ACCESS{
|
||||
AccessPermissions: sidMask.Value,
|
||||
AccessMode: windows.GRANT_ACCESS,
|
||||
Inheritance: windows.NO_INHERITANCE,
|
||||
Trustee: trustee,
|
||||
})
|
||||
}
|
||||
|
||||
if err = errors.Join(errs...); err != nil {
|
||||
return fmt.Errorf("creating access control entries: %w", err)
|
||||
}
|
||||
|
||||
acl, err := windows.ACLFromEntries(entries, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating access control list: %w", err)
|
||||
}
|
||||
|
||||
// secInfo defines the parts of a security descriptor to set.
|
||||
const secInfo windows.SECURITY_INFORMATION = windows.DACL_SECURITY_INFORMATION |
|
||||
windows.PROTECTED_DACL_SECURITY_INFORMATION
|
||||
|
||||
err = windows.SetNamedSecurityInfo(name, windows.SE_FILE_OBJECT, secInfo, nil, nil, acl, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting security descriptor: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// mkdir is a Windows implementation of [Mkdir].
|
||||
//
|
||||
// TODO(e.burkov): Consider using [windows.CreateDirectory] instead of
|
||||
// [os.Mkdir] to reduce the number of syscalls.
|
||||
func mkdir(name string, perm os.FileMode) (err error) {
|
||||
name, err = filepath.Abs(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("computing absolute path: %w", err)
|
||||
}
|
||||
|
||||
err = os.Mkdir(name, perm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating directory: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = errors.WithDeferred(err, os.Remove(name))
|
||||
}
|
||||
}()
|
||||
|
||||
return chmod(name, perm)
|
||||
}
|
||||
|
||||
// mkdirAll is a Windows implementation of [MkdirAll].
|
||||
func mkdirAll(path string, perm os.FileMode) (err error) {
|
||||
parent, _ := filepath.Split(path)
|
||||
|
||||
if parent != "" {
|
||||
err = os.MkdirAll(parent, perm)
|
||||
if err != nil && !errors.Is(err, os.ErrExist) {
|
||||
return fmt.Errorf("creating parent directories: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = mkdir(path, perm)
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// writeFile is a Windows implementation of [WriteFile].
|
||||
func writeFile(filename string, data []byte, perm os.FileMode) (err error) {
|
||||
file, err := openFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, perm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening file: %w", err)
|
||||
}
|
||||
defer func() { err = errors.WithDeferred(err, file.Close()) }()
|
||||
|
||||
_, err = file.Write(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("writing data: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// openFile is a Windows implementation of [OpenFile].
|
||||
func openFile(name string, flag int, perm os.FileMode) (file *os.File, err error) {
|
||||
// Only change permissions if the file not yet exists, but should be
|
||||
// created.
|
||||
if flag&os.O_CREATE == 0 {
|
||||
return os.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
_, err = stat(name)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
defer func() { err = errors.WithDeferred(err, chmod(name, perm)) }()
|
||||
} else {
|
||||
return nil, fmt.Errorf("getting file info: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return os.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
// newWellKnownTrustee returns a trustee for a well-known SID.
|
||||
func newWellKnownTrustee(stype windows.WELL_KNOWN_SID_TYPE) (t windows.TRUSTEE, err error) {
|
||||
sid, err := windows.CreateWellKnownSid(stype)
|
||||
if err != nil {
|
||||
return windows.TRUSTEE{}, fmt.Errorf("creating sid for type %d: %w", stype, err)
|
||||
}
|
||||
|
||||
return windows.TRUSTEE{
|
||||
TrusteeForm: windows.TRUSTEE_IS_SID,
|
||||
TrusteeValue: windows.TrusteeValueFromSID(sid),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UNIX file mode permission bits.
|
||||
const (
|
||||
permRead = 0b100
|
||||
permWrite = 0b010
|
||||
permExecute = 0b001
|
||||
)
|
||||
|
||||
// Windows access masks for appropriate UNIX file mode permission bits and
|
||||
// file types.
|
||||
const (
|
||||
fileReadRights windows.ACCESS_MASK = windows.READ_CONTROL |
|
||||
windows.FILE_READ_DATA |
|
||||
windows.FILE_READ_ATTRIBUTES |
|
||||
windows.FILE_READ_EA |
|
||||
windows.SYNCHRONIZE |
|
||||
windows.ACCESS_SYSTEM_SECURITY
|
||||
|
||||
fileWriteRights windows.ACCESS_MASK = windows.WRITE_DAC |
|
||||
windows.WRITE_OWNER |
|
||||
windows.FILE_WRITE_DATA |
|
||||
windows.FILE_WRITE_ATTRIBUTES |
|
||||
windows.FILE_WRITE_EA |
|
||||
windows.DELETE |
|
||||
windows.FILE_APPEND_DATA |
|
||||
windows.SYNCHRONIZE |
|
||||
windows.ACCESS_SYSTEM_SECURITY
|
||||
|
||||
fileExecuteRights windows.ACCESS_MASK = windows.FILE_EXECUTE
|
||||
|
||||
dirReadRights windows.ACCESS_MASK = windows.READ_CONTROL |
|
||||
windows.FILE_LIST_DIRECTORY |
|
||||
windows.FILE_READ_EA |
|
||||
windows.FILE_READ_ATTRIBUTES<<1 |
|
||||
windows.SYNCHRONIZE |
|
||||
windows.ACCESS_SYSTEM_SECURITY
|
||||
|
||||
dirWriteRights windows.ACCESS_MASK = windows.WRITE_DAC |
|
||||
windows.WRITE_OWNER |
|
||||
windows.DELETE |
|
||||
windows.FILE_WRITE_DATA |
|
||||
windows.FILE_APPEND_DATA |
|
||||
windows.FILE_WRITE_EA |
|
||||
windows.FILE_WRITE_ATTRIBUTES<<1 |
|
||||
windows.SYNCHRONIZE |
|
||||
windows.ACCESS_SYSTEM_SECURITY
|
||||
|
||||
dirExecuteRights windows.ACCESS_MASK = windows.FILE_TRAVERSE
|
||||
)
|
||||
|
||||
// permToMasks converts a UNIX file mode permissions to the corresponding
|
||||
// Windows access masks. The [isDir] argument is used to set specific access
|
||||
// bits for directories.
|
||||
func permToMasks(fm os.FileMode, isDir bool) (owner, group, world windows.ACCESS_MASK) {
|
||||
mask := fm.Perm()
|
||||
|
||||
owner = permToMask(byte((mask>>6)&0b111), isDir)
|
||||
group = permToMask(byte((mask>>3)&0b111), isDir)
|
||||
world = permToMask(byte(mask&0b111), isDir)
|
||||
|
||||
return owner, group, world
|
||||
}
|
||||
|
||||
// permToMask converts a UNIX file mode permission bits within p byte to the
|
||||
// corresponding Windows access mask. The [isDir] argument is used to set
|
||||
// specific access bits for directories.
|
||||
func permToMask(p byte, isDir bool) (mask windows.ACCESS_MASK) {
|
||||
readRights, writeRights, executeRights := fileReadRights, fileWriteRights, fileExecuteRights
|
||||
if isDir {
|
||||
readRights, writeRights, executeRights = dirReadRights, dirWriteRights, dirExecuteRights
|
||||
}
|
||||
|
||||
if p&permRead != 0 {
|
||||
mask |= readRights
|
||||
}
|
||||
if p&permWrite != 0 {
|
||||
mask |= writeRights
|
||||
}
|
||||
if p&permExecute != 0 {
|
||||
mask |= executeRights
|
||||
}
|
||||
|
||||
return mask
|
||||
}
|
||||
|
||||
// masksToPerm converts Windows access masks to the corresponding UNIX file
|
||||
// mode permission bits.
|
||||
func masksToPerm(u, g, o windows.ACCESS_MASK, isDir bool) (perm fs.FileMode) {
|
||||
perm |= fs.FileMode(maskToPerm(u, isDir)) << 6
|
||||
perm |= fs.FileMode(maskToPerm(g, isDir)) << 3
|
||||
perm |= fs.FileMode(maskToPerm(o, isDir))
|
||||
|
||||
return perm
|
||||
}
|
||||
|
||||
// maskToPerm converts a Windows access mask to the corresponding UNIX file
|
||||
// mode permission bits.
|
||||
func maskToPerm(mask windows.ACCESS_MASK, isDir bool) (perm byte) {
|
||||
readMask, writeMask, executeMask := fileReadRights, fileWriteRights, fileExecuteRights
|
||||
if isDir {
|
||||
readMask, writeMask, executeMask = dirReadRights, dirWriteRights, dirExecuteRights
|
||||
}
|
||||
|
||||
// Remove common bits to avoid false positive detection of unset rights.
|
||||
readMask ^= windows.SYNCHRONIZE | windows.ACCESS_SYSTEM_SECURITY
|
||||
writeMask ^= windows.SYNCHRONIZE | windows.ACCESS_SYSTEM_SECURITY
|
||||
|
||||
if mask&readMask != 0 {
|
||||
perm |= permRead
|
||||
}
|
||||
if mask&writeMask != 0 {
|
||||
perm |= permWrite
|
||||
}
|
||||
if mask&executeMask != 0 {
|
||||
perm |= permExecute
|
||||
}
|
||||
|
||||
return perm
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
//go:build windows
|
||||
|
||||
package aghos
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func TestPermToMasks(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
perm fs.FileMode
|
||||
wantUser windows.ACCESS_MASK
|
||||
wantGroup windows.ACCESS_MASK
|
||||
wantOther windows.ACCESS_MASK
|
||||
isDir bool
|
||||
}{{
|
||||
name: "all",
|
||||
perm: 0b111_111_111,
|
||||
wantUser: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||
wantGroup: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||
wantOther: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||
isDir: false,
|
||||
}, {
|
||||
name: "user_write",
|
||||
perm: 0b010_000_000,
|
||||
wantUser: fileWriteRights,
|
||||
wantGroup: 0,
|
||||
wantOther: 0,
|
||||
isDir: false,
|
||||
}, {
|
||||
name: "group_read",
|
||||
perm: 0b000_100_000,
|
||||
wantUser: 0,
|
||||
wantGroup: fileReadRights,
|
||||
wantOther: 0,
|
||||
isDir: false,
|
||||
}, {
|
||||
name: "all_dir",
|
||||
perm: 0b111_111_111,
|
||||
wantUser: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||
wantGroup: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||
wantOther: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||
isDir: true,
|
||||
}, {
|
||||
name: "user_write_dir",
|
||||
perm: 0b010_000_000,
|
||||
wantUser: dirWriteRights,
|
||||
wantGroup: 0,
|
||||
wantOther: 0,
|
||||
isDir: true,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
user, group, other := permToMasks(tc.perm, tc.isDir)
|
||||
assert.Equal(t, tc.wantUser, user)
|
||||
assert.Equal(t, tc.wantGroup, group)
|
||||
assert.Equal(t, tc.wantOther, other)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMasksToPerm(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
user windows.ACCESS_MASK
|
||||
group windows.ACCESS_MASK
|
||||
other windows.ACCESS_MASK
|
||||
wantPerm fs.FileMode
|
||||
isDir bool
|
||||
}{{
|
||||
name: "all",
|
||||
user: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||
group: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||
other: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||
wantPerm: 0b111_111_111,
|
||||
isDir: false,
|
||||
}, {
|
||||
name: "user_write",
|
||||
user: fileWriteRights,
|
||||
group: 0,
|
||||
other: 0,
|
||||
wantPerm: 0b010_000_000,
|
||||
isDir: false,
|
||||
}, {
|
||||
name: "group_read",
|
||||
user: 0,
|
||||
group: fileReadRights,
|
||||
other: 0,
|
||||
wantPerm: 0b000_100_000,
|
||||
isDir: false,
|
||||
}, {
|
||||
name: "no_access",
|
||||
user: 0,
|
||||
group: 0,
|
||||
other: 0,
|
||||
wantPerm: 0,
|
||||
isDir: false,
|
||||
}, {
|
||||
name: "all_dir",
|
||||
user: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||
group: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||
other: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||
wantPerm: 0b111_111_111,
|
||||
isDir: true,
|
||||
}, {
|
||||
name: "user_write_dir",
|
||||
user: dirWriteRights,
|
||||
group: 0,
|
||||
other: 0,
|
||||
wantPerm: 0b010_000_000,
|
||||
isDir: true,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Don't call [fs.FileMode.Perm] since the result is expected to
|
||||
// contain only the permission bits.
|
||||
assert.Equal(t, tc.wantPerm, masksToPerm(tc.user, tc.group, tc.other, tc.isDir))
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user