mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-10 21:47:13 +08:00
config: fix file/folder ownership
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
546
vendor/github.com/tonistiigi/dchapes-mode/mode.go
generated
vendored
Normal file
546
vendor/github.com/tonistiigi/dchapes-mode/mode.go
generated
vendored
Normal file
@ -0,0 +1,546 @@
|
||||
/*
|
||||
|
||||
Parts of this file are a heavily modified C to Go
|
||||
translation of BSD's /usr/src/lib/libc/gen/setmode.c
|
||||
that contains the following copyright notice:
|
||||
|
||||
* Copyright (c) 1989, 1993, 1994
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to Berkeley by
|
||||
* Dave Borman at Cray Research, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
// Package mode provides a native Go implementation of BSD's setmode and getmode
|
||||
// which can be used to modify the mode bits of an os.FileMode value based on
|
||||
// a symbolic value as described by the Unix chmod command.
|
||||
//
|
||||
// For a full description of the mode string see chmod(1).
|
||||
// Some examples include:
|
||||
//
|
||||
// 644 make a file readable by anyone and writable by the owner
|
||||
// only.
|
||||
//
|
||||
// go-w deny write permission to group and others.
|
||||
//
|
||||
// =rw,+X set the read and write permissions to the usual defaults,
|
||||
// but retain any execute permissions that are currently set.
|
||||
//
|
||||
// +X make a directory or file searchable/executable by everyone
|
||||
// if it is already searchable/executable by anyone.
|
||||
//
|
||||
// 755
|
||||
// u=rwx,go=rx
|
||||
// u=rwx,go=u-w make a file readable/executable by everyone and writable by
|
||||
// the owner only.
|
||||
//
|
||||
// go= clear all mode bits for group and others.
|
||||
//
|
||||
// go=u-w set the group bits equal to the user bits, but clear the
|
||||
// group write bit.
|
||||
//
|
||||
// See Also:
|
||||
//
|
||||
// setmode(3): https://www.freebsd.org/cgi/man.cgi?query=setmode&sektion=3
|
||||
// chmod(1): https://www.freebsd.org/cgi/man.cgi?query=chmod&sektion=1
|
||||
package mode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Set is a set of changes to apply to an os.FileMode.
|
||||
// Changes include setting or clearing specific bits, copying bits from one
|
||||
// user class to another (e.g. "u=go" sets the user permissions to a copy of
|
||||
// the group and other permsissions), etc.
|
||||
type Set struct {
|
||||
cmds []bitcmd
|
||||
}
|
||||
|
||||
type bitcmd struct {
|
||||
cmd byte
|
||||
cmd2 byte
|
||||
bits modet
|
||||
}
|
||||
|
||||
const (
|
||||
cmd2Clear byte = 1 << iota
|
||||
cmd2Set
|
||||
cmd2GBits
|
||||
cmd2OBits
|
||||
cmd2UBits
|
||||
)
|
||||
|
||||
func (c bitcmd) String() string {
|
||||
c2 := ""
|
||||
if c.cmd2 != 0 {
|
||||
c2 = " cmd2:"
|
||||
if c.cmd2&cmd2Clear != 0 {
|
||||
c2 += " CLR"
|
||||
}
|
||||
if c.cmd2&cmd2Set != 0 {
|
||||
c2 += " SET"
|
||||
}
|
||||
if c.cmd2&cmd2UBits != 0 {
|
||||
c2 += " UBITS"
|
||||
}
|
||||
if c.cmd2&cmd2GBits != 0 {
|
||||
c2 += " GBITS"
|
||||
}
|
||||
if c.cmd2&cmd2OBits != 0 {
|
||||
c2 += " OBITS"
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("cmd: %q bits %#05o%s", c.cmd, c.bits, c2)
|
||||
}
|
||||
|
||||
// The String method will likely only be useful when testing.
|
||||
func (s Set) String() string {
|
||||
var buf strings.Builder
|
||||
buf.Grow(21*len(s.cmds) + 10)
|
||||
_, _ = buf.WriteString("set: {\n")
|
||||
for _, c := range s.cmds {
|
||||
_, _ = buf.WriteString(c.String())
|
||||
_ = buf.WriteByte('\n')
|
||||
}
|
||||
_, _ = buf.WriteString("}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// ErrSyntax indicates an argument does not represent a valid mode.
|
||||
var ErrSyntax = errors.New("invalid syntax")
|
||||
|
||||
// Apply changes the provided os.FileMode based on the given umask and
|
||||
// absolute or symbolic mode value.
|
||||
//
|
||||
// Apply is a convience to calling ParseWithUmask followed by Apply.
|
||||
// Since it needs to parse the mode value string on each call it
|
||||
// should only be used when mode value string will not be reapplied.
|
||||
func Apply(s string, perm os.FileMode, umask uint) (os.FileMode, error) {
|
||||
set, err := ParseWithUmask(s, umask)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return set.Apply(perm), nil
|
||||
}
|
||||
|
||||
// Parse takes an absolute (octal) or symbolic mode value,
|
||||
// as described in chmod(1), as an argument and returns
|
||||
// the set of bit operations representing the mode value
|
||||
// that can be applied to specific os.FileMode values.
|
||||
//
|
||||
// Same as ParseWithUmask(s, 0).
|
||||
func Parse(s string) (Set, error) {
|
||||
return ParseWithUmask(s, 0)
|
||||
}
|
||||
|
||||
// TODO(dchapes): A Set.Parse method that reuses existing memory.
|
||||
|
||||
// TODO(dchapes): Only call syscall.Umask when abosolutely necessary and
|
||||
// provide a Set method to query if set is umask dependant (and perhaps
|
||||
// the umask that was in effect when parsed).
|
||||
|
||||
// ParseWithUmask is like Parse but uses the provided
|
||||
// file creation mask instead of calling syscall.Umask.
|
||||
func ParseWithUmask(s string, umask uint) (Set, error) {
|
||||
var m Set
|
||||
if s == "" {
|
||||
return m, ErrSyntax
|
||||
}
|
||||
|
||||
// If an absolute number, get it and return;
|
||||
// disallow non-octal digits or illegal bits.
|
||||
if d := s[0]; '0' <= d && d <= '9' {
|
||||
v, err := strconv.ParseInt(s, 8, 16)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
if v&^(standardBits|isTXT) != 0 {
|
||||
return m, ErrSyntax
|
||||
}
|
||||
// We know this takes exactly two bitcmds.
|
||||
m.cmds = make([]bitcmd, 0, 2)
|
||||
m.addcmd('=', standardBits|isTXT, modet(v), 0)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Get a copy of the mask for the permissions that are mask relative.
|
||||
// Flip the bits, we want what's not set.
|
||||
var mask modet = ^modet(umask)
|
||||
|
||||
// Pre-allocate room for several commands.
|
||||
//m.cmds = make([]bitcmd, 0, 8)
|
||||
|
||||
// Build list of bitcmd structs to set/clear/copy bits as described by
|
||||
// each clause of the symbolic mode.
|
||||
equalOpDone := false
|
||||
for {
|
||||
// First, find out which bits might be modified.
|
||||
var who modet
|
||||
whoLoop:
|
||||
for {
|
||||
if len(s) == 0 {
|
||||
return Set{}, ErrSyntax
|
||||
}
|
||||
switch s[0] {
|
||||
case 'a':
|
||||
who |= standardBits
|
||||
case 'u':
|
||||
who |= isUID | iRWXU
|
||||
case 'g':
|
||||
who |= isGID | iRWXG
|
||||
case 'o':
|
||||
who |= iRWXO
|
||||
default:
|
||||
break whoLoop
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
|
||||
var op byte
|
||||
getop:
|
||||
op, s = s[0], s[1:]
|
||||
switch op {
|
||||
case '+', '-':
|
||||
// Nothing.
|
||||
case '=':
|
||||
equalOpDone = false
|
||||
default:
|
||||
return Set{}, ErrSyntax
|
||||
}
|
||||
|
||||
who &^= isTXT
|
||||
permLoop:
|
||||
for perm, permX := modet(0), modet(0); ; s = s[1:] {
|
||||
var b byte
|
||||
if len(s) > 0 {
|
||||
b = s[0]
|
||||
}
|
||||
switch b {
|
||||
case 'r':
|
||||
perm |= iRUser | iRGroup | iROther
|
||||
case 's':
|
||||
// If only "other" bits ignore set-id.
|
||||
if who == 0 || who&^iRWXO != 0 {
|
||||
perm |= isUID | isGID
|
||||
}
|
||||
case 't':
|
||||
// If only "other bits ignore sticky.
|
||||
if who == 0 || who&^iRWXO != 0 {
|
||||
who |= isTXT
|
||||
perm |= isTXT
|
||||
}
|
||||
case 'w':
|
||||
perm |= iWUser | iWGroup | iWOther
|
||||
case 'X':
|
||||
if op == '+' {
|
||||
permX = iXUser | iXGroup | iXOther
|
||||
}
|
||||
case 'x':
|
||||
perm |= iXUser | iXGroup | iXOther
|
||||
case 'u', 'g', 'o':
|
||||
// Whenever we hit 'u', 'g', or 'o', we have
|
||||
// to flush out any partial mode that we have,
|
||||
// and then do the copying of the mode bits.
|
||||
if perm != 0 {
|
||||
m.addcmd(op, who, perm, mask)
|
||||
perm = 0
|
||||
}
|
||||
if op == '=' {
|
||||
equalOpDone = true
|
||||
}
|
||||
if permX != 0 {
|
||||
m.addcmd('X', who, permX, mask)
|
||||
permX = 0
|
||||
}
|
||||
m.addcmd(b, who, modet(op), mask)
|
||||
default:
|
||||
// Add any permissions that we haven't alread done.
|
||||
if perm != 0 || op == '=' && !equalOpDone {
|
||||
if op == '=' {
|
||||
equalOpDone = true
|
||||
}
|
||||
m.addcmd(op, who, perm, mask)
|
||||
//perm = 0
|
||||
}
|
||||
if permX != 0 {
|
||||
m.addcmd('X', who, permX, mask)
|
||||
//permX = 0
|
||||
}
|
||||
break permLoop
|
||||
}
|
||||
}
|
||||
|
||||
if s == "" {
|
||||
break
|
||||
}
|
||||
if s[0] != ',' {
|
||||
goto getop
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
|
||||
m.compress()
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Apply returns the os.FileMode after applying the set of changes.
|
||||
func (s Set) Apply(perm os.FileMode) os.FileMode {
|
||||
omode := fileModeToBits(perm)
|
||||
newmode := omode
|
||||
|
||||
// When copying the user, group or other bits around, we "know"
|
||||
// where the bits are in the mode so that we can do shifts to
|
||||
// copy them around. If we don't use shifts, it gets real
|
||||
// grundgy with lots of single bit checks and bit sets.
|
||||
common := func(c bitcmd, value modet) {
|
||||
if c.cmd2&cmd2Clear != 0 {
|
||||
var clrval modet
|
||||
if c.cmd2&cmd2Set != 0 {
|
||||
clrval = iRWXO
|
||||
} else {
|
||||
clrval = value
|
||||
}
|
||||
if c.cmd2&cmd2UBits != 0 {
|
||||
newmode &^= clrval << 6 & c.bits
|
||||
}
|
||||
if c.cmd2&cmd2GBits != 0 {
|
||||
newmode &^= clrval << 3 & c.bits
|
||||
}
|
||||
if c.cmd2&cmd2OBits != 0 {
|
||||
newmode &^= clrval & c.bits
|
||||
}
|
||||
}
|
||||
if c.cmd2&cmd2Set != 0 {
|
||||
if c.cmd2&cmd2UBits != 0 {
|
||||
newmode |= value << 6 & c.bits
|
||||
}
|
||||
if c.cmd2&cmd2GBits != 0 {
|
||||
newmode |= value << 3 & c.bits
|
||||
}
|
||||
if c.cmd2&cmd2OBits != 0 {
|
||||
newmode |= value & c.bits
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range s.cmds {
|
||||
switch c.cmd {
|
||||
case 'u':
|
||||
common(c, newmode&iRWXU>>6)
|
||||
case 'g':
|
||||
common(c, newmode&iRWXG>>3)
|
||||
case 'o':
|
||||
common(c, newmode&iRWXO)
|
||||
|
||||
case '+':
|
||||
newmode |= c.bits
|
||||
case '-':
|
||||
newmode &^= c.bits
|
||||
|
||||
case 'X':
|
||||
if omode&(iXUser|iXGroup|iXOther) != 0 || perm.IsDir() {
|
||||
newmode |= c.bits
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bitsToFileMode(perm, newmode)
|
||||
}
|
||||
|
||||
// Chmod is a convience routine that applies the changes in
|
||||
// Set to the named file. To avoid some race conditions,
|
||||
// it opens the file and uses os.File.Stat and
|
||||
// os.File.Chmod rather than os.Stat and os.Chmod if possible.
|
||||
func (s *Set) Chmod(name string) (old, new os.FileMode, err error) {
|
||||
if f, err := os.Open(name); err == nil { // nolint: vetshadow
|
||||
defer f.Close() // nolint: errcheck
|
||||
return s.ChmodFile(f)
|
||||
}
|
||||
// Fallback to os.Stat and os.Chmod if we
|
||||
// don't have permission to open the file.
|
||||
fi, err := os.Stat(name)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
old = fi.Mode()
|
||||
new = s.Apply(old)
|
||||
if new != old {
|
||||
err = os.Chmod(name, new)
|
||||
}
|
||||
return old, new, err
|
||||
|
||||
}
|
||||
|
||||
// ChmodFile is a convience routine that applies
|
||||
// the changes in Set to the open file f.
|
||||
func (s *Set) ChmodFile(f *os.File) (old, new os.FileMode, err error) {
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
old = fi.Mode()
|
||||
new = s.Apply(old)
|
||||
if new != old {
|
||||
err = f.Chmod(new)
|
||||
}
|
||||
return old, new, err
|
||||
}
|
||||
|
||||
func (s *Set) addcmd(op byte, who, oparg, mask modet) {
|
||||
c := bitcmd{}
|
||||
switch op {
|
||||
case '=':
|
||||
c.cmd = '-'
|
||||
if who != 0 {
|
||||
c.bits = who
|
||||
} else {
|
||||
c.bits = standardBits
|
||||
}
|
||||
|
||||
s.cmds = append(s.cmds, c)
|
||||
//c = bitcmd{} // reset, not actually needed
|
||||
op = '+'
|
||||
fallthrough
|
||||
case '+', '-', 'X':
|
||||
c.cmd = op
|
||||
if who != 0 {
|
||||
c.bits = who & oparg
|
||||
} else {
|
||||
c.bits = mask & oparg
|
||||
}
|
||||
|
||||
case 'u', 'g', 'o':
|
||||
c.cmd = op
|
||||
if who != 0 {
|
||||
if who&iRUser != 0 {
|
||||
c.cmd2 |= cmd2UBits
|
||||
}
|
||||
if who&iRGroup != 0 {
|
||||
c.cmd2 |= cmd2GBits
|
||||
}
|
||||
if who&iROther != 0 {
|
||||
c.cmd2 |= cmd2OBits
|
||||
}
|
||||
c.bits = ^modet(0)
|
||||
} else {
|
||||
c.cmd2 = cmd2UBits | cmd2GBits | cmd2OBits
|
||||
c.bits = mask
|
||||
}
|
||||
|
||||
switch oparg {
|
||||
case '+':
|
||||
c.cmd2 |= cmd2Set
|
||||
case '-':
|
||||
c.cmd2 |= cmd2Clear
|
||||
case '=':
|
||||
c.cmd2 |= cmd2Set | cmd2Clear
|
||||
}
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
s.cmds = append(s.cmds, c)
|
||||
}
|
||||
|
||||
// compress by compacting consecutive '+', '-' and 'X'
|
||||
// commands into at most 3 commands, one of each. The 'u',
|
||||
// 'g' and 'o' commands continue to be separate. They could
|
||||
// probably be compacted, but it's not worth the effort.
|
||||
func (s *Set) compress() {
|
||||
//log.Println("before:", *m)
|
||||
//log.Println("Start compress:")
|
||||
j := 0
|
||||
for i := 0; i < len(s.cmds); i++ {
|
||||
c := s.cmds[i]
|
||||
//log.Println(" read", i, c)
|
||||
if strings.IndexByte("+-X", c.cmd) < 0 {
|
||||
// Copy over any 'u', 'g', and 'o' commands.
|
||||
if i != j {
|
||||
s.cmds[j] = c
|
||||
}
|
||||
//log.Println(" wrote", j, "from", i)
|
||||
j++
|
||||
continue
|
||||
}
|
||||
var setbits, clrbits, Xbits modet
|
||||
for ; i < len(s.cmds); i++ {
|
||||
c = s.cmds[i]
|
||||
//log.Println(" scan", i, c)
|
||||
switch c.cmd {
|
||||
case '-':
|
||||
clrbits |= c.bits
|
||||
setbits &^= c.bits
|
||||
Xbits &^= c.bits
|
||||
continue
|
||||
case '+':
|
||||
setbits |= c.bits
|
||||
clrbits &^= c.bits
|
||||
Xbits &^= c.bits
|
||||
continue
|
||||
case 'X':
|
||||
Xbits |= c.bits &^ setbits
|
||||
continue
|
||||
default:
|
||||
i--
|
||||
}
|
||||
break
|
||||
}
|
||||
if clrbits != 0 {
|
||||
s.cmds[j].cmd = '-'
|
||||
s.cmds[j].cmd2 = 0
|
||||
s.cmds[j].bits = clrbits
|
||||
//log.Println(" wrote", j, "clrbits")
|
||||
j++
|
||||
}
|
||||
if setbits != 0 {
|
||||
s.cmds[j].cmd = '+'
|
||||
s.cmds[j].cmd2 = 0
|
||||
s.cmds[j].bits = setbits
|
||||
//log.Println(" wrote", j, "setbits")
|
||||
j++
|
||||
}
|
||||
if Xbits != 0 {
|
||||
s.cmds[j].cmd = 'X'
|
||||
s.cmds[j].cmd2 = 0
|
||||
s.cmds[j].bits = Xbits
|
||||
//log.Println(" wrote", j, "Xbits")
|
||||
j++
|
||||
}
|
||||
}
|
||||
/*
|
||||
if len(m.cmds) != j {
|
||||
log.Println("compressed", len(m.cmds), "down to", j)
|
||||
}
|
||||
*/
|
||||
s.cmds = s.cmds[:j]
|
||||
//log.Println("after:", *m)
|
||||
}
|
Reference in New Issue
Block a user