Browse code

Refactor and improve libcontainer and driver

Remove logging for now because it is complicating things
Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)

Michael Crosby authored on 2014/02/25 14:11:52
Showing 12 changed files
... ...
@@ -11,7 +11,6 @@ import (
11 11
 	"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
12 12
 	"io"
13 13
 	"io/ioutil"
14
-	"log"
15 14
 	"os"
16 15
 	"os/exec"
17 16
 	"path/filepath"
... ...
@@ -27,7 +26,6 @@ const (
27 27
 
28 28
 var (
29 29
 	ErrNotSupported = errors.New("not supported")
30
-	noOpLog         = log.New(ioutil.Discard, "[nsinit] ", log.LstdFlags)
31 30
 )
32 31
 
33 32
 func init() {
... ...
@@ -51,7 +49,7 @@ func init() {
51 51
 		if err != nil {
52 52
 			return err
53 53
 		}
54
-		ns := nsinit.NewNsInit(noOpLog, "", &nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{})
54
+		ns := nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{})
55 55
 		if err := ns.Init(container, cwd, args.Console, syncPipe, args.Args); err != nil {
56 56
 			return err
57 57
 		}
... ...
@@ -93,7 +91,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
93 93
 			dsw:      &nsinit.DefaultStateWriter{c.Rootfs},
94 94
 		}
95 95
 	)
96
-	ns := nsinit.NewNsInit(noOpLog, "", factory, stateWriter)
96
+	ns := nsinit.NewNsInit(factory, stateWriter)
97 97
 	if c.Tty {
98 98
 		term = &dockerTtyTerm{
99 99
 			pipes: pipes,
... ...
@@ -147,6 +145,8 @@ func (d *driver) Name() string {
147 147
 	return fmt.Sprintf("%s-%s", DriverName, Version)
148 148
 }
149 149
 
150
+// TODO: this can be improved with our driver
151
+// there has to be a better way to do this
150 152
 func (d *driver) GetPidsForContainer(id string) ([]int, error) {
151 153
 	pids := []int{}
152 154
 
... ...
@@ -207,27 +207,24 @@ type dockerCommandFactory struct {
207 207
 // createCommand will return an exec.Cmd with the Cloneflags set to the proper namespaces
208 208
 // defined on the container's configuration and use the current binary as the init with the
209 209
 // args provided
210
-func (d *dockerCommandFactory) Create(container *libcontainer.Container,
211
-	console, logFile string, syncFd uintptr, args []string) *exec.Cmd {
212
-	c := d.c
210
+func (d *dockerCommandFactory) Create(container *libcontainer.Container, console string, syncFd uintptr, args []string) *exec.Cmd {
213 211
 	// we need to join the rootfs because nsinit will setup the rootfs and chroot
214
-	initPath := filepath.Join(c.Rootfs, c.InitPath)
212
+	initPath := filepath.Join(d.c.Rootfs, d.c.InitPath)
215 213
 
216
-	c.Path = initPath
217
-	c.Args = append([]string{
214
+	d.c.Path = initPath
215
+	d.c.Args = append([]string{
218 216
 		initPath,
219 217
 		"-driver", DriverName,
220 218
 		"-console", console,
221 219
 		"-pipe", fmt.Sprint(syncFd),
222
-		"-log", logFile,
223 220
 	}, args...)
224
-	c.SysProcAttr = &syscall.SysProcAttr{
221
+	d.c.SysProcAttr = &syscall.SysProcAttr{
225 222
 		Cloneflags: uintptr(nsinit.GetNamespaceFlags(container.Namespaces)),
226 223
 	}
227
-	c.Env = container.Env
228
-	c.Dir = c.Rootfs
224
+	d.c.Env = container.Env
225
+	d.c.Dir = d.c.Rootfs
229 226
 
230
-	return &c.Cmd
227
+	return &d.c.Cmd
231 228
 }
232 229
 
233 230
 type dockerStateWriter struct {
... ...
@@ -6,6 +6,9 @@ import (
6 6
 	"github.com/dotcloud/docker/pkg/libcontainer/utils"
7 7
 )
8 8
 
9
+// Veth is a network strategy that uses a bridge and creates
10
+// a veth pair, one that stays outside on the host and the other
11
+// is placed inside the container's namespace
9 12
 type Veth struct {
10 13
 }
11 14
 
... ...
@@ -8,8 +8,11 @@ import (
8 8
 	"syscall"
9 9
 )
10 10
 
11
+// CommandFactory takes the container's configuration and options passed by the
12
+// parent processes and creates an *exec.Cmd that will be used to fork/exec the
13
+// namespaced init process
11 14
 type CommandFactory interface {
12
-	Create(container *libcontainer.Container, console, logFile string, syncFd uintptr, args []string) *exec.Cmd
15
+	Create(container *libcontainer.Container, console string, syncFd uintptr, args []string) *exec.Cmd
13 16
 }
14 17
 
15 18
 type DefaultCommandFactory struct{}
... ...
@@ -17,13 +20,12 @@ type DefaultCommandFactory struct{}
17 17
 // Create will return an exec.Cmd with the Cloneflags set to the proper namespaces
18 18
 // defined on the container's configuration and use the current binary as the init with the
19 19
 // args provided
20
-func (c *DefaultCommandFactory) Create(container *libcontainer.Container, console, logFile string, pipe uintptr, args []string) *exec.Cmd {
20
+func (c *DefaultCommandFactory) Create(container *libcontainer.Container, console string, pipe uintptr, args []string) *exec.Cmd {
21 21
 	// get our binary name so we can always reexec ourself
22 22
 	name := os.Args[0]
23 23
 	command := exec.Command(name, append([]string{
24 24
 		"-console", console,
25 25
 		"-pipe", fmt.Sprint(pipe),
26
-		"-log", logFile,
27 26
 		"init"}, args...)...)
28 27
 
29 28
 	command.SysProcAttr = &syscall.SysProcAttr{
... ...
@@ -28,31 +28,27 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
28 28
 	}
29 29
 
30 30
 	if container.Tty {
31
-		ns.logger.Printf("setting up master and console")
32
-		master, console, err = CreateMasterAndConsole()
31
+		master, console, err = system.CreateMasterAndConsole()
33 32
 		if err != nil {
34 33
 			return -1, err
35 34
 		}
36 35
 		term.SetMaster(master)
37 36
 	}
38 37
 
39
-	command := ns.commandFactory.Create(container, console, ns.logFile, syncPipe.child.Fd(), args)
38
+	command := ns.commandFactory.Create(container, console, syncPipe.child.Fd(), args)
40 39
 	if err := term.Attach(command); err != nil {
41 40
 		return -1, err
42 41
 	}
43 42
 	defer term.Close()
44 43
 
45
-	ns.logger.Printf("staring init")
46 44
 	if err := command.Start(); err != nil {
47 45
 		return -1, err
48 46
 	}
49
-	ns.logger.Printf("writing state file")
50 47
 	if err := ns.stateWriter.WritePid(command.Process.Pid); err != nil {
51 48
 		command.Process.Kill()
52 49
 		return -1, err
53 50
 	}
54 51
 	defer func() {
55
-		ns.logger.Printf("removing state file")
56 52
 		ns.stateWriter.DeletePid()
57 53
 	}()
58 54
 
... ...
@@ -68,24 +64,18 @@ func (ns *linuxNs) Exec(container *libcontainer.Container, term Terminal, args [
68 68
 	}
69 69
 
70 70
 	// Sync with child
71
-	ns.logger.Printf("closing sync pipes")
72 71
 	syncPipe.Close()
73 72
 
74
-	ns.logger.Printf("waiting on process")
75 73
 	if err := command.Wait(); err != nil {
76 74
 		if _, ok := err.(*exec.ExitError); !ok {
77 75
 			return -1, err
78 76
 		}
79 77
 	}
80
-
81
-	exitCode := command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
82
-	ns.logger.Printf("process ended with exit code %d", exitCode)
83
-	return exitCode, nil
78
+	return command.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
84 79
 }
85 80
 
86 81
 func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) error {
87 82
 	if container.Cgroups != nil {
88
-		ns.logger.Printf("setting up cgroups")
89 83
 		if err := container.Cgroups.Apply(nspid); err != nil {
90 84
 			return err
91 85
 		}
... ...
@@ -95,7 +85,6 @@ func (ns *linuxNs) SetupCgroups(container *libcontainer.Container, nspid int) er
95 95
 
96 96
 func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error {
97 97
 	if container.Network != nil {
98
-		ns.logger.Printf("creating host network configuration type %s", container.Network.Type)
99 98
 		strategy, err := network.GetStrategy(container.Network.Type)
100 99
 		if err != nil {
101 100
 			return err
... ...
@@ -104,27 +93,9 @@ func (ns *linuxNs) InitializeNetworking(container *libcontainer.Container, nspid
104 104
 		if err != nil {
105 105
 			return err
106 106
 		}
107
-		ns.logger.Printf("sending %v as network context", networkContext)
108 107
 		if err := pipe.SendToChild(networkContext); err != nil {
109 108
 			return err
110 109
 		}
111 110
 	}
112 111
 	return nil
113 112
 }
114
-
115
-// CreateMasterAndConsole will open /dev/ptmx on the host and retreive the
116
-// pts name for use as the pty slave inside the container
117
-func CreateMasterAndConsole() (*os.File, string, error) {
118
-	master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
119
-	if err != nil {
120
-		return nil, "", err
121
-	}
122
-	console, err := system.Ptsname(master)
123
-	if err != nil {
124
-		return nil, "", err
125
-	}
126
-	if err := system.Unlockpt(master); err != nil {
127
-		return nil, "", err
128
-	}
129
-	return master, console, nil
130
-}
... ...
@@ -18,7 +18,7 @@ func (ns *linuxNs) ExecIn(container *libcontainer.Container, nspid int, args []s
18 18
 			return -1, err
19 19
 		}
20 20
 	}
21
-	fds, err := getNsFds(nspid, container)
21
+	fds, err := ns.getNsFds(nspid, container)
22 22
 	closeFds := func() {
23 23
 		for _, f := range fds {
24 24
 			system.Closefd(f)
... ...
@@ -75,13 +75,13 @@ dropAndExec:
75 75
 	if err := capabilities.DropCapabilities(container); err != nil {
76 76
 		return -1, fmt.Errorf("drop capabilities %s", err)
77 77
 	}
78
-	if err := system.Exec(args[0], args[0:], container.Env); err != nil {
78
+	if err := system.Execv(args[0], args[0:], container.Env); err != nil {
79 79
 		return -1, err
80 80
 	}
81 81
 	panic("unreachable")
82 82
 }
83 83
 
84
-func getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) {
84
+func (ns *linuxNs) getNsFds(pid int, container *libcontainer.Container) ([]uintptr, error) {
85 85
 	fds := make([]uintptr, len(container.Namespaces))
86 86
 	for i, ns := range container.Namespaces {
87 87
 		f, err := os.OpenFile(filepath.Join("/proc/", strconv.Itoa(pid), "ns", namespaceFileMap[ns]), os.O_RDONLY, 0)
... ...
@@ -7,18 +7,17 @@ import (
7 7
 	"github.com/dotcloud/docker/pkg/libcontainer"
8 8
 	"github.com/dotcloud/docker/pkg/libcontainer/capabilities"
9 9
 	"github.com/dotcloud/docker/pkg/libcontainer/network"
10
+	"github.com/dotcloud/docker/pkg/libcontainer/utils"
10 11
 	"github.com/dotcloud/docker/pkg/system"
11 12
 	"github.com/dotcloud/docker/pkg/user"
12 13
 	"os"
13
-	"os/exec"
14
-	"path/filepath"
15 14
 	"syscall"
16 15
 )
17 16
 
18 17
 // Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
19 18
 // and other options required for the new container.
20 19
 func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, console string, syncPipe *SyncPipe, args []string) error {
21
-	rootfs, err := resolveRootfs(uncleanRootfs)
20
+	rootfs, err := utils.ResolveRootfs(uncleanRootfs)
22 21
 	if err != nil {
23 22
 		return err
24 23
 	}
... ...
@@ -34,7 +33,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
34 34
 	if console != "" {
35 35
 		// close pipes so that we can replace it with the pty
36 36
 		closeStdPipes()
37
-		slave, err := openTerminal(console, syscall.O_RDWR)
37
+		slave, err := system.OpenTerminal(console, syscall.O_RDWR)
38 38
 		if err != nil {
39 39
 			return fmt.Errorf("open terminal %s", err)
40 40
 		}
... ...
@@ -50,6 +49,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
50 50
 			return fmt.Errorf("setctty %s", err)
51 51
 		}
52 52
 	}
53
+
53 54
 	if err := system.ParentDeathSignal(); err != nil {
54 55
 		return fmt.Errorf("parent deth signal %s", err)
55 56
 	}
... ...
@@ -73,18 +73,7 @@ func (ns *linuxNs) Init(container *libcontainer.Container, uncleanRootfs, consol
73 73
 			return fmt.Errorf("chdir to %s %s", container.WorkingDir, err)
74 74
 		}
75 75
 	}
76
-	return execArgs(args, container.Env)
77
-}
78
-
79
-func execArgs(args []string, env []string) error {
80
-	name, err := exec.LookPath(args[0])
81
-	if err != nil {
82
-		return err
83
-	}
84
-	if err := system.Exec(name, args[0:], env); err != nil {
85
-		return fmt.Errorf("exec %s", err)
86
-	}
87
-	panic("unreachable")
76
+	return system.Execv(args[0], args[0:], container.Env)
88 77
 }
89 78
 
90 79
 func closeStdPipes() {
... ...
@@ -93,39 +82,30 @@ func closeStdPipes() {
93 93
 	os.Stderr.Close()
94 94
 }
95 95
 
96
-// resolveRootfs ensures that the current working directory is
97
-// not a symlink and returns the absolute path to the rootfs
98
-func resolveRootfs(uncleanRootfs string) (string, error) {
99
-	rootfs, err := filepath.Abs(uncleanRootfs)
100
-	if err != nil {
101
-		return "", err
102
-	}
103
-	return filepath.EvalSymlinks(rootfs)
104
-}
105
-
106 96
 func setupUser(container *libcontainer.Container) error {
107
-	if container.User != "" && container.User != "root" {
108
-		uid, gid, suppGids, err := user.GetUserGroupSupplementary(container.User, syscall.Getuid(), syscall.Getgid())
109
-		if err != nil {
97
+	switch container.User {
98
+	case "root", "":
99
+		if err := system.Setgroups(nil); err != nil {
110 100
 			return err
111 101
 		}
112
-		if err := system.Setgroups(suppGids); err != nil {
102
+		if err := system.Setresgid(0, 0, 0); err != nil {
113 103
 			return err
114 104
 		}
115
-		if err := system.Setgid(gid); err != nil {
105
+		if err := system.Setresuid(0, 0, 0); err != nil {
116 106
 			return err
117 107
 		}
118
-		if err := system.Setuid(uid); err != nil {
108
+	default:
109
+		uid, gid, suppGids, err := user.GetUserGroupSupplementary(container.User, syscall.Getuid(), syscall.Getgid())
110
+		if err != nil {
119 111
 			return err
120 112
 		}
121
-	} else {
122
-		if err := system.Setgroups(nil); err != nil {
113
+		if err := system.Setgroups(suppGids); err != nil {
123 114
 			return err
124 115
 		}
125
-		if err := system.Setresgid(0, 0, 0); err != nil {
116
+		if err := system.Setgid(gid); err != nil {
126 117
 			return err
127 118
 		}
128
-		if err := system.Setresuid(0, 0, 0); err != nil {
119
+		if err := system.Setuid(uid); err != nil {
129 120
 			return err
130 121
 		}
131 122
 	}
... ...
@@ -147,16 +127,6 @@ func dupSlave(slave *os.File) error {
147 147
 	return nil
148 148
 }
149 149
 
150
-// openTerminal is a clone of os.OpenFile without the O_CLOEXEC
151
-// used to open the pty slave inside the container namespace
152
-func openTerminal(name string, flag int) (*os.File, error) {
153
-	r, e := syscall.Open(name, flag, 0)
154
-	if e != nil {
155
-		return nil, &os.PathError{"open", name, e}
156
-	}
157
-	return os.NewFile(uintptr(r), name), nil
158
-}
159
-
160 150
 // setupVethNetwork uses the Network config if it is not nil to initialize
161 151
 // the new veth interface inside the container for use by changing the name to eth0
162 152
 // setting the MTU and IP address along with the default gateway
... ...
@@ -2,9 +2,10 @@ package nsinit
2 2
 
3 3
 import (
4 4
 	"github.com/dotcloud/docker/pkg/libcontainer"
5
-	"log"
6 5
 )
7 6
 
7
+// NsInit is an interface with the public facing methods to provide high level
8
+// exec operations on a container
8 9
 type NsInit interface {
9 10
 	Exec(container *libcontainer.Container, term Terminal, args []string) (int, error)
10 11
 	ExecIn(container *libcontainer.Container, nspid int, args []string) (int, error)
... ...
@@ -13,17 +14,13 @@ type NsInit interface {
13 13
 
14 14
 type linuxNs struct {
15 15
 	root           string
16
-	logFile        string
17
-	logger         *log.Logger
18 16
 	commandFactory CommandFactory
19 17
 	stateWriter    StateWriter
20 18
 }
21 19
 
22
-func NewNsInit(logger *log.Logger, logFile string, command CommandFactory, state StateWriter) NsInit {
20
+func NewNsInit(command CommandFactory, state StateWriter) NsInit {
23 21
 	return &linuxNs{
24
-		logger:         logger,
25 22
 		commandFactory: command,
26 23
 		stateWriter:    state,
27
-		logFile:        logFile,
28 24
 	}
29 25
 }
... ...
@@ -6,7 +6,6 @@ import (
6 6
 	"flag"
7 7
 	"github.com/dotcloud/docker/pkg/libcontainer"
8 8
 	"github.com/dotcloud/docker/pkg/libcontainer/nsinit"
9
-	"io"
10 9
 	"io/ioutil"
11 10
 	"log"
12 11
 	"os"
... ...
@@ -16,7 +15,6 @@ import (
16 16
 var (
17 17
 	console string
18 18
 	pipeFd  int
19
-	logFile string
20 19
 )
21 20
 
22 21
 var (
... ...
@@ -26,7 +24,6 @@ var (
26 26
 
27 27
 func registerFlags() {
28 28
 	flag.StringVar(&console, "console", "", "console (pty slave) path")
29
-	flag.StringVar(&logFile, "log", "none", "log options (none, stderr, or a file path)")
30 29
 	flag.IntVar(&pipeFd, "pipe", 0, "sync pipe fd")
31 30
 
32 31
 	flag.Parse()
... ...
@@ -113,26 +110,5 @@ func readPid() (int, error) {
113 113
 }
114 114
 
115 115
 func newNsInit() (nsinit.NsInit, error) {
116
-	logger, err := setupLogging()
117
-	if err != nil {
118
-		return nil, err
119
-	}
120
-	return nsinit.NewNsInit(logger, logFile, &nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{}), nil
121
-}
122
-
123
-func setupLogging() (logger *log.Logger, err error) {
124
-	var writer io.Writer
125
-
126
-	switch logFile {
127
-	case "stderr":
128
-		writer = os.Stderr
129
-	case "none", "":
130
-		writer = ioutil.Discard
131
-	default:
132
-		if writer, err = os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755); err != nil {
133
-			return
134
-		}
135
-	}
136
-	logger = log.New(writer, "", log.LstdFlags)
137
-	return
116
+	return nsinit.NewNsInit(&nsinit.DefaultCommandFactory{}, &nsinit.DefaultStateWriter{}), nil
138 117
 }
... ...
@@ -7,6 +7,8 @@ import (
7 7
 	"path/filepath"
8 8
 )
9 9
 
10
+// StateWriter handles writing and deleting the pid file
11
+// on disk
10 12
 type StateWriter interface {
11 13
 	WritePid(pid int) error
12 14
 	DeletePid() error
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"crypto/rand"
5 5
 	"encoding/hex"
6 6
 	"io"
7
+	"path/filepath"
7 8
 )
8 9
 
9 10
 // GenerateRandomName returns a new name joined with a prefix.  This size
... ...
@@ -15,3 +16,13 @@ func GenerateRandomName(prefix string, size int) (string, error) {
15 15
 	}
16 16
 	return prefix + hex.EncodeToString(id)[:size], nil
17 17
 }
18
+
19
+// ResolveRootfs ensures that the current working directory is
20
+// not a symlink and returns the absolute path to the rootfs
21
+func ResolveRootfs(uncleanRootfs string) (string, error) {
22
+	rootfs, err := filepath.Abs(uncleanRootfs)
23
+	if err != nil {
24
+		return "", err
25
+	}
26
+	return filepath.EvalSymlinks(rootfs)
27
+}
... ...
@@ -1,6 +1,7 @@
1 1
 package system
2 2
 
3 3
 import (
4
+	"os/exec"
4 5
 	"syscall"
5 6
 )
6 7
 
... ...
@@ -16,6 +17,14 @@ func Exec(cmd string, args []string, env []string) error {
16 16
 	return syscall.Exec(cmd, args, env)
17 17
 }
18 18
 
19
+func Execv(cmd string, args []string, env []string) error {
20
+	name, err := exec.LookPath(cmd)
21
+	if err != nil {
22
+		return err
23
+	}
24
+	return Exec(name, args, env)
25
+}
26
+
19 27
 func Fork() (int, error) {
20 28
 	syscall.ForkLock.Lock()
21 29
 	pid, _, err := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
... ...
@@ -24,8 +24,35 @@ func Ptsname(f *os.File) (string, error) {
24 24
 	return fmt.Sprintf("/dev/pts/%d", n), nil
25 25
 }
26 26
 
27
+// CreateMasterAndConsole will open /dev/ptmx on the host and retreive the
28
+// pts name for use as the pty slave inside the container
29
+func CreateMasterAndConsole() (*os.File, string, error) {
30
+	master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
31
+	if err != nil {
32
+		return nil, "", err
33
+	}
34
+	console, err := Ptsname(master)
35
+	if err != nil {
36
+		return nil, "", err
37
+	}
38
+	if err := Unlockpt(master); err != nil {
39
+		return nil, "", err
40
+	}
41
+	return master, console, nil
42
+}
43
+
27 44
 // OpenPtmx opens /dev/ptmx, i.e. the PTY master.
28 45
 func OpenPtmx() (*os.File, error) {
29 46
 	// O_NOCTTY and O_CLOEXEC are not present in os package so we use the syscall's one for all.
30 47
 	return os.OpenFile("/dev/ptmx", syscall.O_RDONLY|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
31 48
 }
49
+
50
+// OpenTerminal is a clone of os.OpenFile without the O_CLOEXEC
51
+// used to open the pty slave inside the container namespace
52
+func OpenTerminal(name string, flag int) (*os.File, error) {
53
+	r, e := syscall.Open(name, flag, 0)
54
+	if e != nil {
55
+		return nil, &os.PathError{"open", name, e}
56
+	}
57
+	return os.NewFile(uintptr(r), name), nil
58
+}