mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-18 00:47:48 +08:00
142 lines
4.5 KiB
Go
142 lines
4.5 KiB
Go
package user
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// MkdirOpt is a type for options to pass to Mkdir calls
|
|
type MkdirOpt func(*mkdirOptions)
|
|
|
|
type mkdirOptions struct {
|
|
onlyNew bool
|
|
}
|
|
|
|
// WithOnlyNew is an option for MkdirAllAndChown that will only change ownership and permissions
|
|
// on newly created directories. If the directory already exists, it will not be modified
|
|
func WithOnlyNew(o *mkdirOptions) {
|
|
o.onlyNew = true
|
|
}
|
|
|
|
// MkdirAllAndChown creates a directory (include any along the path) and then modifies
|
|
// ownership to the requested uid/gid. By default, if the directory already exists, this
|
|
// function will still change ownership and permissions. If WithOnlyNew is passed as an
|
|
// option, then only the newly created directories will have ownership and permissions changed.
|
|
func MkdirAllAndChown(path string, mode os.FileMode, uid, gid int, opts ...MkdirOpt) error {
|
|
var options mkdirOptions
|
|
for _, opt := range opts {
|
|
opt(&options)
|
|
}
|
|
|
|
return mkdirAs(path, mode, uid, gid, true, options.onlyNew)
|
|
}
|
|
|
|
// MkdirAndChown creates a directory and then modifies ownership to the requested uid/gid.
|
|
// By default, if the directory already exists, this function still changes ownership and permissions.
|
|
// If WithOnlyNew is passed as an option, then only the newly created directory will have ownership
|
|
// and permissions changed.
|
|
// Note that unlike os.Mkdir(), this function does not return IsExist error
|
|
// in case path already exists.
|
|
func MkdirAndChown(path string, mode os.FileMode, uid, gid int, opts ...MkdirOpt) error {
|
|
var options mkdirOptions
|
|
for _, opt := range opts {
|
|
opt(&options)
|
|
}
|
|
return mkdirAs(path, mode, uid, gid, false, options.onlyNew)
|
|
}
|
|
|
|
// getRootUIDGID retrieves the remapped root uid/gid pair from the set of maps.
|
|
// If the maps are empty, then the root uid/gid will default to "real" 0/0
|
|
func getRootUIDGID(uidMap, gidMap []IDMap) (int, int, error) {
|
|
uid, err := toHost(0, uidMap)
|
|
if err != nil {
|
|
return -1, -1, err
|
|
}
|
|
gid, err := toHost(0, gidMap)
|
|
if err != nil {
|
|
return -1, -1, err
|
|
}
|
|
return uid, gid, nil
|
|
}
|
|
|
|
// toContainer takes an id mapping, and uses it to translate a
|
|
// host ID to the remapped ID. If no map is provided, then the translation
|
|
// assumes a 1-to-1 mapping and returns the passed in id
|
|
func toContainer(hostID int, idMap []IDMap) (int, error) {
|
|
if idMap == nil {
|
|
return hostID, nil
|
|
}
|
|
for _, m := range idMap {
|
|
if (int64(hostID) >= m.ParentID) && (int64(hostID) <= (m.ParentID + m.Count - 1)) {
|
|
contID := int(m.ID + (int64(hostID) - m.ParentID))
|
|
return contID, nil
|
|
}
|
|
}
|
|
return -1, fmt.Errorf("host ID %d cannot be mapped to a container ID", hostID)
|
|
}
|
|
|
|
// toHost takes an id mapping and a remapped ID, and translates the
|
|
// ID to the mapped host ID. If no map is provided, then the translation
|
|
// assumes a 1-to-1 mapping and returns the passed in id #
|
|
func toHost(contID int, idMap []IDMap) (int, error) {
|
|
if idMap == nil {
|
|
return contID, nil
|
|
}
|
|
for _, m := range idMap {
|
|
if (int64(contID) >= m.ID) && (int64(contID) <= (m.ID + m.Count - 1)) {
|
|
hostID := int(m.ParentID + (int64(contID) - m.ID))
|
|
return hostID, nil
|
|
}
|
|
}
|
|
return -1, fmt.Errorf("container ID %d cannot be mapped to a host ID", contID)
|
|
}
|
|
|
|
// IdentityMapping contains a mappings of UIDs and GIDs.
|
|
// The zero value represents an empty mapping.
|
|
type IdentityMapping struct {
|
|
UIDMaps []IDMap `json:"UIDMaps"`
|
|
GIDMaps []IDMap `json:"GIDMaps"`
|
|
}
|
|
|
|
// RootPair returns a uid and gid pair for the root user. The error is ignored
|
|
// because a root user always exists, and the defaults are correct when the uid
|
|
// and gid maps are empty.
|
|
func (i IdentityMapping) RootPair() (int, int) {
|
|
uid, gid, _ := getRootUIDGID(i.UIDMaps, i.GIDMaps)
|
|
return uid, gid
|
|
}
|
|
|
|
// ToHost returns the host UID and GID for the container uid, gid.
|
|
// Remapping is only performed if the ids aren't already the remapped root ids
|
|
func (i IdentityMapping) ToHost(uid, gid int) (int, int, error) {
|
|
var err error
|
|
ruid, rgid := i.RootPair()
|
|
|
|
if uid != ruid {
|
|
ruid, err = toHost(uid, i.UIDMaps)
|
|
if err != nil {
|
|
return ruid, rgid, err
|
|
}
|
|
}
|
|
|
|
if gid != rgid {
|
|
rgid, err = toHost(gid, i.GIDMaps)
|
|
}
|
|
return ruid, rgid, err
|
|
}
|
|
|
|
// ToContainer returns the container UID and GID for the host uid and gid
|
|
func (i IdentityMapping) ToContainer(uid, gid int) (int, int, error) {
|
|
ruid, err := toContainer(uid, i.UIDMaps)
|
|
if err != nil {
|
|
return -1, -1, err
|
|
}
|
|
rgid, err := toContainer(gid, i.GIDMaps)
|
|
return ruid, rgid, err
|
|
}
|
|
|
|
// Empty returns true if there are no id mappings
|
|
func (i IdentityMapping) Empty() bool {
|
|
return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0
|
|
}
|