Procházet zdrojové kódy

Merge pull request #3015 from jpoimboe/dockerinit-libvirt-prereqs

dockerinit: drop capabilities
Guillaume J. Charmes před 11 roky
rodič
revize
912bf8ff92

+ 4 - 0
container.go

@@ -594,6 +594,10 @@ func (container *Container) Start() (err error) {
 		env = append(env, "TERM=xterm")
 	}
 
+	if container.hostConfig.Privileged {
+		params = append(params, "-privileged")
+	}
+
 	// Init any links between the parent and children
 	runtime := container.runtime
 

+ 2 - 0
hack/vendor.sh

@@ -27,6 +27,8 @@ git_clone github.com/gorilla/context/ 708054d61e5
 
 git_clone github.com/gorilla/mux/ 9b36453141c
 
+git_clone github.com/syndtr/gocapability 3454319be2
+
 # Docker requires code.google.com/p/go.net/websocket
 PKG=code.google.com/p/go.net REV=84a4013f96e0
 (

+ 0 - 7
lxc_template.go

@@ -110,18 +110,11 @@ lxc.mount.entry = {{escapeFstabSpaces $realPath}} {{escapeFstabSpaces $ROOTFS}}/
 {{end}}
 
 {{if (getHostConfig .).Privileged}}
-# retain all capabilities; no lxc.cap.drop line
 {{if (getCapabilities .).AppArmor}}
 lxc.aa_profile = unconfined
 {{else}}
 #lxc.aa_profile = unconfined
 {{end}}
-{{else}}
-# drop linux capabilities (apply mainly to the user root in the container)
-#  (Note: 'lxc.cap.keep' is coming soon and should replace this under the
-#         security principle 'deny all unless explicitly permitted', see
-#         http://sourceforge.net/mailarchive/message.php?msg_id=31054627 )
-lxc.cap.drop = audit_control audit_write mac_admin mac_override mknod setpcap sys_admin sys_module sys_nice sys_pacct sys_rawio sys_resource sys_time sys_tty_config
 {{end}}
 
 # limits

+ 124 - 43
sysinit/sysinit.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"github.com/dotcloud/docker/netlink"
 	"github.com/dotcloud/docker/utils"
+	"github.com/syndtr/gocapability/capability"
 	"io/ioutil"
 	"log"
 	"net"
@@ -16,73 +17,113 @@ import (
 	"syscall"
 )
 
+type DockerInitArgs struct {
+	user       string
+	gateway    string
+	workDir    string
+	privileged bool
+	env        []string
+	args       []string
+}
+
 // Setup networking
-func setupNetworking(gw string) {
-	if gw == "" {
-		return
+func setupNetworking(args *DockerInitArgs) error {
+	if args.gateway == "" {
+		return nil
 	}
 
-	ip := net.ParseIP(gw)
+	ip := net.ParseIP(args.gateway)
 	if ip == nil {
-		log.Fatalf("Unable to set up networking, %s is not a valid IP", gw)
-		return
+		return fmt.Errorf("Unable to set up networking, %s is not a valid IP", args.gateway)
 	}
 
 	if err := netlink.AddDefaultGw(ip); err != nil {
-		log.Fatalf("Unable to set up networking: %v", err)
+		return fmt.Errorf("Unable to set up networking: %v", err)
 	}
+
+	return nil
 }
 
 // Setup working directory
-func setupWorkingDirectory(workdir string) {
-	if workdir == "" {
-		return
+func setupWorkingDirectory(args *DockerInitArgs) error {
+	if args.workDir == "" {
+		return nil
 	}
-	if err := syscall.Chdir(workdir); err != nil {
-		log.Fatalf("Unable to change dir to %v: %v", workdir, err)
+	if err := syscall.Chdir(args.workDir); err != nil {
+		return fmt.Errorf("Unable to change dir to %v: %v", args.workDir, err)
 	}
+	return nil
 }
 
 // Takes care of dropping privileges to the desired user
-func changeUser(u string) {
-	if u == "" {
-		return
+func changeUser(args *DockerInitArgs) error {
+	if args.user == "" {
+		return nil
 	}
-	userent, err := utils.UserLookup(u)
+	userent, err := utils.UserLookup(args.user)
 	if err != nil {
-		log.Fatalf("Unable to find user %v: %v", u, err)
+		return fmt.Errorf("Unable to find user %v: %v", args.user, err)
 	}
 
 	uid, err := strconv.Atoi(userent.Uid)
 	if err != nil {
-		log.Fatalf("Invalid uid: %v", userent.Uid)
+		return fmt.Errorf("Invalid uid: %v", userent.Uid)
 	}
 	gid, err := strconv.Atoi(userent.Gid)
 	if err != nil {
-		log.Fatalf("Invalid gid: %v", userent.Gid)
+		return fmt.Errorf("Invalid gid: %v", userent.Gid)
 	}
 
 	if err := syscall.Setgid(gid); err != nil {
-		log.Fatalf("setgid failed: %v", err)
+		return fmt.Errorf("setgid failed: %v", err)
 	}
 	if err := syscall.Setuid(uid); err != nil {
-		log.Fatalf("setuid failed: %v", err)
+		return fmt.Errorf("setuid failed: %v", err)
 	}
+
+	return nil
 }
 
-// Clear environment pollution introduced by lxc-start
-func cleanupEnv() {
-	os.Clearenv()
-	var lines []string
-	content, err := ioutil.ReadFile("/.dockerenv")
-	if err != nil {
-		log.Fatalf("Unable to load environment variables: %v", err)
+func setupCapabilities(args *DockerInitArgs) error {
+
+	if args.privileged {
+		return nil
 	}
-	err = json.Unmarshal(content, &lines)
+
+	drop := []capability.Cap{
+		capability.CAP_SETPCAP,
+		capability.CAP_SYS_MODULE,
+		capability.CAP_SYS_RAWIO,
+		capability.CAP_SYS_PACCT,
+		capability.CAP_SYS_ADMIN,
+		capability.CAP_SYS_NICE,
+		capability.CAP_SYS_RESOURCE,
+		capability.CAP_SYS_TIME,
+		capability.CAP_SYS_TTY_CONFIG,
+		capability.CAP_MKNOD,
+		capability.CAP_AUDIT_WRITE,
+		capability.CAP_AUDIT_CONTROL,
+		capability.CAP_MAC_OVERRIDE,
+		capability.CAP_MAC_ADMIN,
+	}
+
+	c, err := capability.NewPid(os.Getpid())
 	if err != nil {
-		log.Fatalf("Unable to unmarshal environment variables: %v", err)
+		return err
+	}
+
+	c.Unset(capability.CAPS|capability.BOUNDS, drop...)
+
+	if err := c.Apply(capability.CAPS | capability.BOUNDS); err != nil {
+		return err
 	}
-	for _, kv := range lines {
+	return nil
+}
+
+// Clear environment pollution introduced by lxc-start
+func setupEnv(args *DockerInitArgs) {
+	os.Clearenv()
+	for _, kv := range args.env {
 		parts := strings.SplitN(kv, "=", 2)
 		if len(parts) == 1 {
 			parts = append(parts, "")
@@ -91,16 +132,37 @@ func cleanupEnv() {
 	}
 }
 
-func executeProgram(name string, args []string) {
-	path, err := exec.LookPath(name)
+func executeProgram(args *DockerInitArgs) error {
+	setupEnv(args)
+
+	if err := setupNetworking(args); err != nil {
+		return err
+	}
+
+	if err := setupCapabilities(args); err != nil {
+		return err
+	}
+
+	if err := setupWorkingDirectory(args); err != nil {
+		return err
+	}
+
+	if err := changeUser(args); err != nil {
+		return err
+	}
+
+	path, err := exec.LookPath(args.args[0])
 	if err != nil {
-		log.Printf("Unable to locate %v", name)
+		log.Printf("Unable to locate %v", args.args[0])
 		os.Exit(127)
 	}
 
-	if err := syscall.Exec(path, args, os.Environ()); err != nil {
+	if err := syscall.Exec(path, args.args, os.Environ()); err != nil {
 		panic(err)
 	}
+
+	// Will never reach here
+	return nil
 }
 
 // Sys Init code
@@ -111,15 +173,34 @@ func SysInit() {
 		fmt.Println("You should not invoke dockerinit manually")
 		os.Exit(1)
 	}
-	var u = flag.String("u", "", "username or uid")
-	var gw = flag.String("g", "", "gateway address")
-	var workdir = flag.String("w", "", "workdir")
 
+	// Get cmdline arguments
+	user := flag.String("u", "", "username or uid")
+	gateway := flag.String("g", "", "gateway address")
+	workDir := flag.String("w", "", "workdir")
+	privileged := flag.Bool("privileged", false, "privileged mode")
 	flag.Parse()
 
-	cleanupEnv()
-	setupNetworking(*gw)
-	setupWorkingDirectory(*workdir)
-	changeUser(*u)
-	executeProgram(flag.Arg(0), flag.Args())
+	// Get env
+	var env []string
+	content, err := ioutil.ReadFile("/.dockerenv")
+	if err != nil {
+		log.Fatalf("Unable to load environment variables: %v", err)
+	}
+	if err := json.Unmarshal(content, &env); err != nil {
+		log.Fatalf("Unable to unmarshal environment variables: %v", err)
+	}
+
+	args := &DockerInitArgs{
+		user:       *user,
+		gateway:    *gateway,
+		workDir:    *workDir,
+		privileged: *privileged,
+		env:        env,
+		args:       flag.Args(),
+	}
+
+	if err := executeProgram(args); err != nil {
+		log.Fatal(err)
+	}
 }

+ 24 - 0
vendor/src/github.com/syndtr/gocapability/LICENSE

@@ -0,0 +1,24 @@
+Copyright 2013 Suryandaru Triandana <syndtr@gmail.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+    * 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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+HOLDER 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.

+ 71 - 0
vendor/src/github.com/syndtr/gocapability/capability/capability.go

@@ -0,0 +1,71 @@
+// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
+// All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Package capability provides utilities for manipulating POSIX capabilities.
+package capability
+
+type Capabilities interface {
+	// Get check whether a capability present in the given
+	// capabilities set. The 'which' value should be one of EFFECTIVE,
+	// PERMITTED, INHERITABLE or BOUNDING.
+	Get(which CapType, what Cap) bool
+
+	// Empty check whether all capability bits of the given capabilities
+	// set are zero. The 'which' value should be one of EFFECTIVE,
+	// PERMITTED, INHERITABLE or BOUNDING.
+	Empty(which CapType) bool
+
+	// Full check whether all capability bits of the given capabilities
+	// set are one. The 'which' value should be one of EFFECTIVE,
+	// PERMITTED, INHERITABLE or BOUNDING.
+	Full(which CapType) bool
+
+	// Set sets capabilities of the given capabilities sets. The
+	// 'which' value should be one or combination (OR'ed) of EFFECTIVE,
+	// PERMITTED, INHERITABLE or BOUNDING.
+	Set(which CapType, caps ...Cap)
+
+	// Unset unsets capabilities of the given capabilities sets. The
+	// 'which' value should be one or combination (OR'ed) of EFFECTIVE,
+	// PERMITTED, INHERITABLE or BOUNDING.
+	Unset(which CapType, caps ...Cap)
+
+	// Fill sets all bits of the given capabilities kind to one. The
+	// 'kind' value should be one or combination (OR'ed) of CAPS or
+	// BOUNDS.
+	Fill(kind CapType)
+
+	// Clear sets all bits of the given capabilities kind to zero. The
+	// 'kind' value should be one or combination (OR'ed) of CAPS or
+	// BOUNDS.
+	Clear(kind CapType)
+
+	// String return current capabilities state of the given capabilities
+	// set as string. The 'which' value should be one of EFFECTIVE,
+	// PERMITTED, INHERITABLE or BOUNDING.
+	StringCap(which CapType) string
+
+	// String return current capabilities state as string.
+	String() string
+
+	// Load load actual capabilities value. This will overwrite all
+	// outstanding changes.
+	Load() error
+
+	// Apply apply the capabilities settings, so all changes will take
+	// effect.
+	Apply(kind CapType) error
+}
+
+// NewPid create new initialized Capabilities object for given pid.
+func NewPid(pid int) (Capabilities, error) {
+	return newPid(pid)
+}
+
+// NewFile create new initialized Capabilities object for given named file.
+func NewFile(name string) (Capabilities, error) {
+	return newFile(name)
+}

+ 561 - 0
vendor/src/github.com/syndtr/gocapability/capability/capability_linux.go

@@ -0,0 +1,561 @@
+// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
+// All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package capability
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"strings"
+	"syscall"
+)
+
+var errUnknownVers = errors.New("unknown capability version")
+
+const (
+	linuxCapVer1 = 0x19980330
+	linuxCapVer2 = 0x20071026
+	linuxCapVer3 = 0x20080522
+)
+
+var capVers uint32
+
+func init() {
+	var hdr capHeader
+	capget(&hdr, nil)
+	capVers = hdr.version
+}
+
+func mkStringCap(c Capabilities, which CapType) (ret string) {
+	for i, first := Cap(0), true; i <= CAP_LAST_CAP; i++ {
+		if !c.Get(which, i) {
+			continue
+		}
+		if first {
+			first = false
+		} else {
+			ret += ", "
+		}
+		ret += i.String()
+	}
+	return
+}
+
+func mkString(c Capabilities, max CapType) (ret string) {
+	ret = "{"
+	for i := CapType(1); i <= max; i <<= 1 {
+		ret += " " + i.String() + "=\""
+		if c.Empty(i) {
+			ret += "empty"
+		} else if c.Full(i) {
+			ret += "full"
+		} else {
+			ret += c.StringCap(i)
+		}
+		ret += "\""
+	}
+	ret += " }"
+	return
+}
+
+func newPid(pid int) (c Capabilities, err error) {
+	switch capVers {
+	case linuxCapVer1:
+		p := new(capsV1)
+		p.hdr.version = capVers
+		p.hdr.pid = pid
+		c = p
+	case linuxCapVer2, linuxCapVer3:
+		p := new(capsV3)
+		p.hdr.version = capVers
+		p.hdr.pid = pid
+		c = p
+	default:
+		err = errUnknownVers
+		return
+	}
+	err = c.Load()
+	if err != nil {
+		c = nil
+	}
+	return
+}
+
+type capsV1 struct {
+	hdr  capHeader
+	data capData
+}
+
+func (c *capsV1) Get(which CapType, what Cap) bool {
+	if what > 32 {
+		return false
+	}
+
+	switch which {
+	case EFFECTIVE:
+		return (1<<uint(what))&c.data.effective != 0
+	case PERMITTED:
+		return (1<<uint(what))&c.data.permitted != 0
+	case INHERITABLE:
+		return (1<<uint(what))&c.data.inheritable != 0
+	}
+
+	return false
+}
+
+func (c *capsV1) getData(which CapType) (ret uint32) {
+	switch which {
+	case EFFECTIVE:
+		ret = c.data.effective
+	case PERMITTED:
+		ret = c.data.permitted
+	case INHERITABLE:
+		ret = c.data.inheritable
+	}
+	return
+}
+
+func (c *capsV1) Empty(which CapType) bool {
+	return c.getData(which) == 0
+}
+
+func (c *capsV1) Full(which CapType) bool {
+	return (c.getData(which) & 0x7fffffff) == 0x7fffffff
+}
+
+func (c *capsV1) Set(which CapType, caps ...Cap) {
+	for _, what := range caps {
+		if what > 32 {
+			continue
+		}
+
+		if which&EFFECTIVE != 0 {
+			c.data.effective |= 1 << uint(what)
+		}
+		if which&PERMITTED != 0 {
+			c.data.permitted |= 1 << uint(what)
+		}
+		if which&INHERITABLE != 0 {
+			c.data.inheritable |= 1 << uint(what)
+		}
+	}
+}
+
+func (c *capsV1) Unset(which CapType, caps ...Cap) {
+	for _, what := range caps {
+		if what > 32 {
+			continue
+		}
+
+		if which&EFFECTIVE != 0 {
+			c.data.effective &= ^(1 << uint(what))
+		}
+		if which&PERMITTED != 0 {
+			c.data.permitted &= ^(1 << uint(what))
+		}
+		if which&INHERITABLE != 0 {
+			c.data.inheritable &= ^(1 << uint(what))
+		}
+	}
+}
+
+func (c *capsV1) Fill(kind CapType) {
+	if kind&CAPS == CAPS {
+		c.data.effective = 0x7fffffff
+		c.data.permitted = 0x7fffffff
+		c.data.inheritable = 0
+	}
+}
+
+func (c *capsV1) Clear(kind CapType) {
+	if kind&CAPS == CAPS {
+		c.data.effective = 0
+		c.data.permitted = 0
+		c.data.inheritable = 0
+	}
+}
+
+func (c *capsV1) StringCap(which CapType) (ret string) {
+	return mkStringCap(c, which)
+}
+
+func (c *capsV1) String() (ret string) {
+	return mkString(c, BOUNDING)
+}
+
+func (c *capsV1) Load() (err error) {
+	return capget(&c.hdr, &c.data)
+}
+
+func (c *capsV1) Apply(kind CapType) error {
+	if kind&CAPS == CAPS {
+		return capset(&c.hdr, &c.data)
+	}
+	return nil
+}
+
+type capsV3 struct {
+	hdr    capHeader
+	data   [2]capData
+	bounds [2]uint32
+}
+
+func (c *capsV3) Get(which CapType, what Cap) bool {
+	var i uint
+	if what > 31 {
+		i = uint(what) >> 5
+		what %= 32
+	}
+
+	switch which {
+	case EFFECTIVE:
+		return (1<<uint(what))&c.data[i].effective != 0
+	case PERMITTED:
+		return (1<<uint(what))&c.data[i].permitted != 0
+	case INHERITABLE:
+		return (1<<uint(what))&c.data[i].inheritable != 0
+	case BOUNDING:
+		return (1<<uint(what))&c.bounds[i] != 0
+	}
+
+	return false
+}
+
+func (c *capsV3) getData(which CapType, dest []uint32) {
+	switch which {
+	case EFFECTIVE:
+		dest[0] = c.data[0].effective
+		dest[1] = c.data[1].effective
+	case PERMITTED:
+		dest[0] = c.data[0].permitted
+		dest[1] = c.data[1].permitted
+	case INHERITABLE:
+		dest[0] = c.data[0].inheritable
+		dest[1] = c.data[1].inheritable
+	case BOUNDING:
+		dest[0] = c.bounds[0]
+		dest[1] = c.bounds[1]
+	}
+}
+
+func (c *capsV3) Empty(which CapType) bool {
+	var data [2]uint32
+	c.getData(which, data[:])
+	return data[0] == 0 && data[1] == 0
+}
+
+func (c *capsV3) Full(which CapType) bool {
+	var data [2]uint32
+	c.getData(which, data[:])
+	if (data[0] & 0xffffffff) != 0xffffffff {
+		return false
+	}
+	return (data[1] & capUpperMask) == capUpperMask
+}
+
+func (c *capsV3) Set(which CapType, caps ...Cap) {
+	for _, what := range caps {
+		var i uint
+		if what > 31 {
+			i = uint(what) >> 5
+			what %= 32
+		}
+
+		if which&EFFECTIVE != 0 {
+			c.data[i].effective |= 1 << uint(what)
+		}
+		if which&PERMITTED != 0 {
+			c.data[i].permitted |= 1 << uint(what)
+		}
+		if which&INHERITABLE != 0 {
+			c.data[i].inheritable |= 1 << uint(what)
+		}
+		if which&BOUNDING != 0 {
+			c.bounds[i] |= 1 << uint(what)
+		}
+	}
+}
+
+func (c *capsV3) Unset(which CapType, caps ...Cap) {
+	for _, what := range caps {
+		var i uint
+		if what > 31 {
+			i = uint(what) >> 5
+			what %= 32
+		}
+
+		if which&EFFECTIVE != 0 {
+			c.data[i].effective &= ^(1 << uint(what))
+		}
+		if which&PERMITTED != 0 {
+			c.data[i].permitted &= ^(1 << uint(what))
+		}
+		if which&INHERITABLE != 0 {
+			c.data[i].inheritable &= ^(1 << uint(what))
+		}
+		if which&BOUNDING != 0 {
+			c.bounds[i] &= ^(1 << uint(what))
+		}
+	}
+}
+
+func (c *capsV3) Fill(kind CapType) {
+	if kind&CAPS == CAPS {
+		c.data[0].effective = 0xffffffff
+		c.data[0].permitted = 0xffffffff
+		c.data[0].inheritable = 0
+		c.data[1].effective = 0xffffffff
+		c.data[1].permitted = 0xffffffff
+		c.data[1].inheritable = 0
+	}
+
+	if kind&BOUNDS == BOUNDS {
+		c.bounds[0] = 0xffffffff
+		c.bounds[1] = 0xffffffff
+	}
+}
+
+func (c *capsV3) Clear(kind CapType) {
+	if kind&CAPS == CAPS {
+		c.data[0].effective = 0
+		c.data[0].permitted = 0
+		c.data[0].inheritable = 0
+		c.data[1].effective = 0
+		c.data[1].permitted = 0
+		c.data[1].inheritable = 0
+	}
+
+	if kind&BOUNDS == BOUNDS {
+		c.bounds[0] = 0
+		c.bounds[1] = 0
+	}
+}
+
+func (c *capsV3) StringCap(which CapType) (ret string) {
+	return mkStringCap(c, which)
+}
+
+func (c *capsV3) String() (ret string) {
+	return mkString(c, BOUNDING)
+}
+
+func (c *capsV3) Load() (err error) {
+	err = capget(&c.hdr, &c.data[0])
+	if err != nil {
+		return
+	}
+
+	f, err := os.Open(fmt.Sprintf("/proc/%d/status", c.hdr.pid))
+	if err != nil {
+		return
+	}
+	b := bufio.NewReader(f)
+	for {
+		line, e := b.ReadString('\n')
+		if e != nil {
+			if e != io.EOF {
+				err = e
+			}
+			break
+		}
+		if strings.HasPrefix(line, "CapB") {
+			fmt.Sscanf(line[4:], "nd:  %08x%08x", &c.bounds[1], &c.bounds[0])
+			break
+		}
+	}
+	f.Close()
+
+	return
+}
+
+func (c *capsV3) Apply(kind CapType) (err error) {
+	if kind&BOUNDS == BOUNDS {
+		var data [2]capData
+		err = capget(&c.hdr, &data[0])
+		if err != nil {
+			return
+		}
+		if (1<<uint(CAP_SETPCAP))&data[0].effective != 0 {
+			for i := Cap(0); i <= CAP_LAST_CAP; i++ {
+				if c.Get(BOUNDING, i) {
+					continue
+				}
+				err = prctl(syscall.PR_CAPBSET_DROP, uintptr(i), 0, 0, 0)
+				if err != nil {
+					return
+				}
+			}
+		}
+	}
+
+	if kind&CAPS == CAPS {
+		return capset(&c.hdr, &c.data[0])
+	}
+
+	return
+}
+
+func newFile(path string) (c Capabilities, err error) {
+	c = &capsFile{path: path}
+	err = c.Load()
+	if err != nil {
+		c = nil
+	}
+	return
+}
+
+type capsFile struct {
+	path string
+	data vfscapData
+}
+
+func (c *capsFile) Get(which CapType, what Cap) bool {
+	var i uint
+	if what > 31 {
+		if c.data.version == 1 {
+			return false
+		}
+		i = uint(what) >> 5
+		what %= 32
+	}
+
+	switch which {
+	case EFFECTIVE:
+		return (1<<uint(what))&c.data.effective[i] != 0
+	case PERMITTED:
+		return (1<<uint(what))&c.data.data[i].permitted != 0
+	case INHERITABLE:
+		return (1<<uint(what))&c.data.data[i].inheritable != 0
+	}
+
+	return false
+}
+
+func (c *capsFile) getData(which CapType, dest []uint32) {
+	switch which {
+	case EFFECTIVE:
+		dest[0] = c.data.effective[0]
+		dest[1] = c.data.effective[1]
+	case PERMITTED:
+		dest[0] = c.data.data[0].permitted
+		dest[1] = c.data.data[1].permitted
+	case INHERITABLE:
+		dest[0] = c.data.data[0].inheritable
+		dest[1] = c.data.data[1].inheritable
+	}
+}
+
+func (c *capsFile) Empty(which CapType) bool {
+	var data [2]uint32
+	c.getData(which, data[:])
+	return data[0] == 0 && data[1] == 0
+}
+
+func (c *capsFile) Full(which CapType) bool {
+	var data [2]uint32
+	c.getData(which, data[:])
+	if c.data.version == 0 {
+		return (data[0] & 0x7fffffff) == 0x7fffffff
+	}
+	if (data[0] & 0xffffffff) != 0xffffffff {
+		return false
+	}
+	return (data[1] & capUpperMask) == capUpperMask
+}
+
+func (c *capsFile) Set(which CapType, caps ...Cap) {
+	for _, what := range caps {
+		var i uint
+		if what > 31 {
+			if c.data.version == 1 {
+				continue
+			}
+			i = uint(what) >> 5
+			what %= 32
+		}
+
+		if which&EFFECTIVE != 0 {
+			c.data.effective[i] |= 1 << uint(what)
+		}
+		if which&PERMITTED != 0 {
+			c.data.data[i].permitted |= 1 << uint(what)
+		}
+		if which&INHERITABLE != 0 {
+			c.data.data[i].inheritable |= 1 << uint(what)
+		}
+	}
+}
+
+func (c *capsFile) Unset(which CapType, caps ...Cap) {
+	for _, what := range caps {
+		var i uint
+		if what > 31 {
+			if c.data.version == 1 {
+				continue
+			}
+			i = uint(what) >> 5
+			what %= 32
+		}
+
+		if which&EFFECTIVE != 0 {
+			c.data.effective[i] &= ^(1 << uint(what))
+		}
+		if which&PERMITTED != 0 {
+			c.data.data[i].permitted &= ^(1 << uint(what))
+		}
+		if which&INHERITABLE != 0 {
+			c.data.data[i].inheritable &= ^(1 << uint(what))
+		}
+	}
+}
+
+func (c *capsFile) Fill(kind CapType) {
+	if kind&CAPS == CAPS {
+		c.data.effective[0] = 0xffffffff
+		c.data.data[0].permitted = 0xffffffff
+		c.data.data[0].inheritable = 0
+		if c.data.version == 2 {
+			c.data.effective[1] = 0xffffffff
+			c.data.data[1].permitted = 0xffffffff
+			c.data.data[1].inheritable = 0
+		}
+	}
+}
+
+func (c *capsFile) Clear(kind CapType) {
+	if kind&CAPS == CAPS {
+		c.data.effective[0] = 0
+		c.data.data[0].permitted = 0
+		c.data.data[0].inheritable = 0
+		if c.data.version == 2 {
+			c.data.effective[1] = 0
+			c.data.data[1].permitted = 0
+			c.data.data[1].inheritable = 0
+		}
+	}
+}
+
+func (c *capsFile) StringCap(which CapType) (ret string) {
+	return mkStringCap(c, which)
+}
+
+func (c *capsFile) String() (ret string) {
+	return mkString(c, INHERITABLE)
+}
+
+func (c *capsFile) Load() (err error) {
+	return getVfsCap(c.path, &c.data)
+}
+
+func (c *capsFile) Apply(kind CapType) (err error) {
+	if kind&CAPS == CAPS {
+		return setVfsCap(c.path, &c.data)
+	}
+	return
+}

+ 19 - 0
vendor/src/github.com/syndtr/gocapability/capability/capability_noop.go

@@ -0,0 +1,19 @@
+// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
+// All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// +build !linux
+
+package capability
+
+import "errors"
+
+func newPid(pid int) (Capabilities, error) {
+	return nil, errors.New("not supported")
+}
+
+func newFile(path string) (Capabilities, error) {
+	return nil, errors.New("not supported")
+}

+ 83 - 0
vendor/src/github.com/syndtr/gocapability/capability/capability_test.go

@@ -0,0 +1,83 @@
+// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
+// All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package capability
+
+import "testing"
+
+func TestState(t *testing.T) {
+	testEmpty := func(name string, c Capabilities, whats CapType) {
+		for i := CapType(1); i <= BOUNDING; i <<= 1 {
+			if (i&whats) != 0 && !c.Empty(i) {
+				t.Errorf(name+": capabilities set %q wasn't empty", i)
+			}
+		}
+	}
+	testFull := func(name string, c Capabilities, whats CapType) {
+		for i := CapType(1); i <= BOUNDING; i <<= 1 {
+			if (i&whats) != 0 && !c.Full(i) {
+				t.Errorf(name+": capabilities set %q wasn't full", i)
+			}
+		}
+	}
+	testPartial := func(name string, c Capabilities, whats CapType) {
+		for i := CapType(1); i <= BOUNDING; i <<= 1 {
+			if (i&whats) != 0 && (c.Empty(i) || c.Full(i)) {
+				t.Errorf(name+": capabilities set %q wasn't partial", i)
+			}
+		}
+	}
+	testGet := func(name string, c Capabilities, whats CapType, max Cap) {
+		for i := CapType(1); i <= BOUNDING; i <<= 1 {
+			if (i & whats) == 0 {
+				continue
+			}
+			for j := Cap(0); j <= max; j++ {
+				if !c.Get(i, j) {
+					t.Errorf(name+": capability %q wasn't found on %q", j, i)
+				}
+			}
+		}
+	}
+
+	capf := new(capsFile)
+	capf.data.version = 2
+	for _, tc := range []struct {
+		name string
+		c    Capabilities
+		sets CapType
+		max  Cap
+	}{
+		{"v1", new(capsV1), EFFECTIVE | PERMITTED, CAP_AUDIT_CONTROL},
+		{"v3", new(capsV3), EFFECTIVE | PERMITTED | BOUNDING, CAP_LAST_CAP},
+		{"file_v1", new(capsFile), EFFECTIVE | PERMITTED, CAP_AUDIT_CONTROL},
+		{"file_v2", capf, EFFECTIVE | PERMITTED, CAP_LAST_CAP},
+	} {
+		testEmpty(tc.name, tc.c, tc.sets)
+		tc.c.Fill(CAPS | BOUNDS)
+		testFull(tc.name, tc.c, tc.sets)
+		testGet(tc.name, tc.c, tc.sets, tc.max)
+		tc.c.Clear(CAPS | BOUNDS)
+		testEmpty(tc.name, tc.c, tc.sets)
+		for i := CapType(1); i <= BOUNDING; i <<= 1 {
+			for j := Cap(0); j <= CAP_LAST_CAP; j++ {
+				tc.c.Set(i, j)
+			}
+		}
+		testFull(tc.name, tc.c, tc.sets)
+		testGet(tc.name, tc.c, tc.sets, tc.max)
+		for i := CapType(1); i <= BOUNDING; i <<= 1 {
+			for j := Cap(0); j <= CAP_LAST_CAP; j++ {
+				tc.c.Unset(i, j)
+			}
+		}
+		testEmpty(tc.name, tc.c, tc.sets)
+		tc.c.Set(PERMITTED, CAP_CHOWN)
+		testPartial(tc.name, tc.c, PERMITTED)
+		tc.c.Clear(CAPS | BOUNDS)
+		testEmpty(tc.name, tc.c, tc.sets)
+	}
+}

+ 338 - 0
vendor/src/github.com/syndtr/gocapability/capability/enum.go

@@ -0,0 +1,338 @@
+// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
+// All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package capability
+
+type CapType uint
+
+func (c CapType) String() string {
+	switch c {
+	case EFFECTIVE:
+		return "effective"
+	case PERMITTED:
+		return "permitted"
+	case INHERITABLE:
+		return "inheritable"
+	case BOUNDING:
+		return "bounding"
+	case CAPS:
+		return "caps"
+	}
+	return "unknown"
+}
+
+const (
+	EFFECTIVE CapType = 1 << iota
+	PERMITTED
+	INHERITABLE
+	BOUNDING
+
+	CAPS   = EFFECTIVE | PERMITTED | INHERITABLE
+	BOUNDS = BOUNDING
+)
+
+type Cap int
+
+func (c Cap) String() string {
+	switch c {
+	case CAP_CHOWN:
+		return "chown"
+	case CAP_DAC_OVERRIDE:
+		return "dac_override"
+	case CAP_DAC_READ_SEARCH:
+		return "dac_read_search"
+	case CAP_FOWNER:
+		return "fowner"
+	case CAP_FSETID:
+		return "fsetid"
+	case CAP_KILL:
+		return "kill"
+	case CAP_SETGID:
+		return "setgid"
+	case CAP_SETUID:
+		return "setuid"
+	case CAP_SETPCAP:
+		return "setpcap"
+	case CAP_LINUX_IMMUTABLE:
+		return "linux_immutable"
+	case CAP_NET_BIND_SERVICE:
+		return "net_bind_service"
+	case CAP_NET_BROADCAST:
+		return "net_broadcast"
+	case CAP_NET_ADMIN:
+		return "net_admin"
+	case CAP_NET_RAW:
+		return "net_raw"
+	case CAP_IPC_LOCK:
+		return "ipc_lock"
+	case CAP_IPC_OWNER:
+		return "ipc_owner"
+	case CAP_SYS_MODULE:
+		return "sys_module"
+	case CAP_SYS_RAWIO:
+		return "sys_rawio"
+	case CAP_SYS_CHROOT:
+		return "sys_chroot"
+	case CAP_SYS_PTRACE:
+		return "sys_ptrace"
+	case CAP_SYS_PACCT:
+		return "sys_psacct"
+	case CAP_SYS_ADMIN:
+		return "sys_admin"
+	case CAP_SYS_BOOT:
+		return "sys_boot"
+	case CAP_SYS_NICE:
+		return "sys_nice"
+	case CAP_SYS_RESOURCE:
+		return "sys_resource"
+	case CAP_SYS_TIME:
+		return "sys_time"
+	case CAP_SYS_TTY_CONFIG:
+		return "sys_tty_config"
+	case CAP_MKNOD:
+		return "mknod"
+	case CAP_LEASE:
+		return "lease"
+	case CAP_AUDIT_WRITE:
+		return "audit_write"
+	case CAP_AUDIT_CONTROL:
+		return "audit_control"
+	case CAP_SETFCAP:
+		return "setfcap"
+	case CAP_MAC_OVERRIDE:
+		return "mac_override"
+	case CAP_MAC_ADMIN:
+		return "mac_admin"
+	case CAP_SYSLOG:
+		return "syslog"
+	case CAP_WAKE_ALARM:
+		return "wake_alarm"
+	case CAP_BLOCK_SUSPEND:
+		return "block_suspend"
+	}
+	return "unknown"
+}
+
+const (
+	// POSIX-draft defined capabilities.
+
+	// In a system with the [_POSIX_CHOWN_RESTRICTED] option defined, this
+	// overrides the restriction of changing file ownership and group
+	// ownership.
+	CAP_CHOWN Cap = 0
+
+	// Override all DAC access, including ACL execute access if
+	// [_POSIX_ACL] is defined. Excluding DAC access covered by
+	// CAP_LINUX_IMMUTABLE.
+	CAP_DAC_OVERRIDE Cap = 1
+
+	// Overrides all DAC restrictions regarding read and search on files
+	// and directories, including ACL restrictions if [_POSIX_ACL] is
+	// defined. Excluding DAC access covered by CAP_LINUX_IMMUTABLE.
+	CAP_DAC_READ_SEARCH Cap = 2
+
+	// Overrides all restrictions about allowed operations on files, where
+	// file owner ID must be equal to the user ID, except where CAP_FSETID
+	// is applicable. It doesn't override MAC and DAC restrictions.
+	CAP_FOWNER Cap = 3
+
+	// Overrides the following restrictions that the effective user ID
+	// shall match the file owner ID when setting the S_ISUID and S_ISGID
+	// bits on that file; that the effective group ID (or one of the
+	// supplementary group IDs) shall match the file owner ID when setting
+	// the S_ISGID bit on that file; that the S_ISUID and S_ISGID bits are
+	// cleared on successful return from chown(2) (not implemented).
+	CAP_FSETID Cap = 4
+
+	// Overrides the restriction that the real or effective user ID of a
+	// process sending a signal must match the real or effective user ID
+	// of the process receiving the signal.
+	CAP_KILL Cap = 5
+
+	// Allows setgid(2) manipulation
+	// Allows setgroups(2)
+	// Allows forged gids on socket credentials passing.
+	CAP_SETGID Cap = 6
+
+	// Allows set*uid(2) manipulation (including fsuid).
+	// Allows forged pids on socket credentials passing.
+	CAP_SETUID Cap = 7
+
+	// Linux-specific capabilities
+
+	// Without VFS support for capabilities:
+	//   Transfer any capability in your permitted set to any pid,
+	//   remove any capability in your permitted set from any pid
+	// With VFS support for capabilities (neither of above, but)
+	//   Add any capability from current's capability bounding set
+	//     to the current process' inheritable set
+	//   Allow taking bits out of capability bounding set
+	//   Allow modification of the securebits for a process
+	CAP_SETPCAP Cap = 8
+
+	// Allow modification of S_IMMUTABLE and S_APPEND file attributes
+	CAP_LINUX_IMMUTABLE Cap = 9
+
+	// Allows binding to TCP/UDP sockets below 1024
+	// Allows binding to ATM VCIs below 32
+	CAP_NET_BIND_SERVICE Cap = 10
+
+	// Allow broadcasting, listen to multicast
+	CAP_NET_BROADCAST Cap = 11
+
+	// Allow interface configuration
+	// Allow administration of IP firewall, masquerading and accounting
+	// Allow setting debug option on sockets
+	// Allow modification of routing tables
+	// Allow setting arbitrary process / process group ownership on
+	// sockets
+	// Allow binding to any address for transparent proxying (also via NET_RAW)
+	// Allow setting TOS (type of service)
+	// Allow setting promiscuous mode
+	// Allow clearing driver statistics
+	// Allow multicasting
+	// Allow read/write of device-specific registers
+	// Allow activation of ATM control sockets
+	CAP_NET_ADMIN Cap = 12
+
+	// Allow use of RAW sockets
+	// Allow use of PACKET sockets
+	// Allow binding to any address for transparent proxying (also via NET_ADMIN)
+	CAP_NET_RAW Cap = 13
+
+	// Allow locking of shared memory segments
+	// Allow mlock and mlockall (which doesn't really have anything to do
+	// with IPC)
+	CAP_IPC_LOCK Cap = 14
+
+	// Override IPC ownership checks
+	CAP_IPC_OWNER Cap = 15
+
+	// Insert and remove kernel modules - modify kernel without limit
+	CAP_SYS_MODULE Cap = 16
+
+	// Allow ioperm/iopl access
+	// Allow sending USB messages to any device via /proc/bus/usb
+	CAP_SYS_RAWIO Cap = 17
+
+	// Allow use of chroot()
+	CAP_SYS_CHROOT Cap = 18
+
+	// Allow ptrace() of any process
+	CAP_SYS_PTRACE Cap = 19
+
+	// Allow configuration of process accounting
+	CAP_SYS_PACCT Cap = 20
+
+	// Allow configuration of the secure attention key
+	// Allow administration of the random device
+	// Allow examination and configuration of disk quotas
+	// Allow setting the domainname
+	// Allow setting the hostname
+	// Allow calling bdflush()
+	// Allow mount() and umount(), setting up new smb connection
+	// Allow some autofs root ioctls
+	// Allow nfsservctl
+	// Allow VM86_REQUEST_IRQ
+	// Allow to read/write pci config on alpha
+	// Allow irix_prctl on mips (setstacksize)
+	// Allow flushing all cache on m68k (sys_cacheflush)
+	// Allow removing semaphores
+	// Used instead of CAP_CHOWN to "chown" IPC message queues, semaphores
+	// and shared memory
+	// Allow locking/unlocking of shared memory segment
+	// Allow turning swap on/off
+	// Allow forged pids on socket credentials passing
+	// Allow setting readahead and flushing buffers on block devices
+	// Allow setting geometry in floppy driver
+	// Allow turning DMA on/off in xd driver
+	// Allow administration of md devices (mostly the above, but some
+	// extra ioctls)
+	// Allow tuning the ide driver
+	// Allow access to the nvram device
+	// Allow administration of apm_bios, serial and bttv (TV) device
+	// Allow manufacturer commands in isdn CAPI support driver
+	// Allow reading non-standardized portions of pci configuration space
+	// Allow DDI debug ioctl on sbpcd driver
+	// Allow setting up serial ports
+	// Allow sending raw qic-117 commands
+	// Allow enabling/disabling tagged queuing on SCSI controllers and sending
+	// arbitrary SCSI commands
+	// Allow setting encryption key on loopback filesystem
+	// Allow setting zone reclaim policy
+	CAP_SYS_ADMIN Cap = 21
+
+	// Allow use of reboot()
+	CAP_SYS_BOOT Cap = 22
+
+	// Allow raising priority and setting priority on other (different
+	// UID) processes
+	// Allow use of FIFO and round-robin (realtime) scheduling on own
+	// processes and setting the scheduling algorithm used by another
+	// process.
+	// Allow setting cpu affinity on other processes
+	CAP_SYS_NICE Cap = 23
+
+	// Override resource limits. Set resource limits.
+	// Override quota limits.
+	// Override reserved space on ext2 filesystem
+	// Modify data journaling mode on ext3 filesystem (uses journaling
+	// resources)
+	// NOTE: ext2 honors fsuid when checking for resource overrides, so
+	// you can override using fsuid too
+	// Override size restrictions on IPC message queues
+	// Allow more than 64hz interrupts from the real-time clock
+	// Override max number of consoles on console allocation
+	// Override max number of keymaps
+	CAP_SYS_RESOURCE Cap = 24
+
+	// Allow manipulation of system clock
+	// Allow irix_stime on mips
+	// Allow setting the real-time clock
+	CAP_SYS_TIME Cap = 25
+
+	// Allow configuration of tty devices
+	// Allow vhangup() of tty
+	CAP_SYS_TTY_CONFIG Cap = 26
+
+	// Allow the privileged aspects of mknod()
+	CAP_MKNOD Cap = 27
+
+	// Allow taking of leases on files
+	CAP_LEASE Cap = 28
+
+	CAP_AUDIT_WRITE   Cap = 29
+	CAP_AUDIT_CONTROL Cap = 30
+	CAP_SETFCAP       Cap = 31
+
+	// Override MAC access.
+	// The base kernel enforces no MAC policy.
+	// An LSM may enforce a MAC policy, and if it does and it chooses
+	// to implement capability based overrides of that policy, this is
+	// the capability it should use to do so.
+	CAP_MAC_OVERRIDE Cap = 32
+
+	// Allow MAC configuration or state changes.
+	// The base kernel requires no MAC configuration.
+	// An LSM may enforce a MAC policy, and if it does and it chooses
+	// to implement capability based checks on modifications to that
+	// policy or the data required to maintain it, this is the
+	// capability it should use to do so.
+	CAP_MAC_ADMIN Cap = 33
+
+	// Allow configuring the kernel's syslog (printk behaviour)
+	CAP_SYSLOG Cap = 34
+
+	// Allow triggering something that will wake the system
+	CAP_WAKE_ALARM Cap = 35
+
+	// Allow preventing system suspends
+	CAP_BLOCK_SUSPEND Cap = 36
+
+	CAP_LAST_CAP = CAP_BLOCK_SUSPEND
+)
+
+const capUpperMask = (uint32(1) << (uint(CAP_LAST_CAP) - 31)) - 1

+ 143 - 0
vendor/src/github.com/syndtr/gocapability/capability/syscall_linux.go

@@ -0,0 +1,143 @@
+// Copyright (c) 2013, Suryandaru Triandana <syndtr@gmail.com>
+// All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package capability
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+type capHeader struct {
+	version uint32
+	pid     int
+}
+
+type capData struct {
+	effective   uint32
+	permitted   uint32
+	inheritable uint32
+}
+
+func capget(hdr *capHeader, data *capData) (err error) {
+	_, _, e1 := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
+	if e1 != 0 {
+		err = e1
+	}
+	return
+}
+
+func capset(hdr *capHeader, data *capData) (err error) {
+	_, _, e1 := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
+	if e1 != 0 {
+		err = e1
+	}
+	return
+}
+
+func prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) {
+	_, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0)
+	if e1 != 0 {
+		err = e1
+	}
+	return
+}
+
+const (
+	vfsXattrName = "security.capability"
+
+	vfsCapVerMask = 0xff000000
+	vfsCapVer1    = 0x01000000
+	vfsCapVer2    = 0x02000000
+
+	vfsCapFlagMask      = ^vfsCapVerMask
+	vfsCapFlageffective = 0x000001
+
+	vfscapDataSizeV1 = 4 * (1 + 2*1)
+	vfscapDataSizeV2 = 4 * (1 + 2*2)
+)
+
+type vfscapData struct {
+	magic uint32
+	data  [2]struct {
+		permitted   uint32
+		inheritable uint32
+	}
+	effective [2]uint32
+	version   int8
+}
+
+var (
+	_vfsXattrName *byte
+)
+
+func init() {
+	_vfsXattrName, _ = syscall.BytePtrFromString(vfsXattrName)
+}
+
+func getVfsCap(path string, dest *vfscapData) (err error) {
+	var _p0 *byte
+	_p0, err = syscall.BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(dest)), vfscapDataSizeV2, 0, 0)
+	if e1 != 0 {
+		err = e1
+	}
+	switch dest.magic & vfsCapVerMask {
+	case vfsCapVer1:
+		dest.version = 1
+		if r0 != vfscapDataSizeV1 {
+			return syscall.EINVAL
+		}
+		dest.data[1].permitted = 0
+		dest.data[1].inheritable = 0
+	case vfsCapVer2:
+		dest.version = 2
+		if r0 != vfscapDataSizeV2 {
+			return syscall.EINVAL
+		}
+	default:
+		return syscall.EINVAL
+	}
+	if dest.magic&vfsCapFlageffective != 0 {
+		dest.effective[0] = dest.data[0].permitted | dest.data[0].inheritable
+		dest.effective[1] = dest.data[1].permitted | dest.data[1].inheritable
+	} else {
+		dest.effective[0] = 0
+		dest.effective[1] = 0
+	}
+	return
+}
+
+func setVfsCap(path string, data *vfscapData) (err error) {
+	var _p0 *byte
+	_p0, err = syscall.BytePtrFromString(path)
+	if err != nil {
+		return
+	}
+	var size uintptr
+	if data.version == 1 {
+		data.magic = vfsCapVer1
+		size = vfscapDataSizeV1
+	} else if data.version == 2 {
+		data.magic = vfsCapVer2
+		if data.effective[0] != 0 || data.effective[1] != 0 {
+			data.magic |= vfsCapFlageffective
+			data.data[0].permitted |= data.effective[0]
+			data.data[1].permitted |= data.effective[1]
+		}
+		size = vfscapDataSizeV2
+	} else {
+		return syscall.EINVAL
+	}
+	_, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(data)), size, 0, 0)
+	if e1 != 0 {
+		err = e1
+	}
+	return
+}