gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/devutil/devutil.go (about) 1 // Copyright 2023 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package devutil provides device specific utilities. 16 package devutil 17 18 import ( 19 "fmt" 20 21 "golang.org/x/sys/unix" 22 "gvisor.dev/gvisor/pkg/context" 23 "gvisor.dev/gvisor/pkg/fsutil" 24 "gvisor.dev/gvisor/pkg/lisafs" 25 "gvisor.dev/gvisor/pkg/log" 26 "gvisor.dev/gvisor/pkg/unet" 27 ) 28 29 // GoferClient is the lisafs client for the /dev gofer connection. 30 type GoferClient struct { 31 clientFD lisafs.ClientFD 32 hostFD int 33 contName string 34 } 35 36 // NewGoferClient establishes the LISAFS connection to the dev gofer server. 37 // It takes ownership of fd. contName is the owning container name. 38 func NewGoferClient(ctx context.Context, contName string, fd int) (*GoferClient, error) { 39 ctx.UninterruptibleSleepStart(false) 40 defer ctx.UninterruptibleSleepFinish(false) 41 42 sock, err := unet.NewSocket(fd) 43 if err != nil { 44 ctx.Warningf("failed to create socket for dev gofer client: %v", err) 45 return nil, err 46 } 47 client, devInode, devHostFD, err := lisafs.NewClient(sock) 48 if err != nil { 49 ctx.Warningf("failed to create dev gofer client: %v", err) 50 return nil, err 51 } 52 return &GoferClient{ 53 clientFD: client.NewFD(devInode.ControlFD), 54 hostFD: devHostFD, 55 contName: contName, 56 }, nil 57 } 58 59 // Close closes the LISAFS connection. 60 func (g *GoferClient) Close() { 61 // Close the connection to the server. This implicitly closes all FDs. 62 g.clientFD.Client().Close() 63 if g.hostFD >= 0 { 64 _ = unix.Close(g.hostFD) 65 } 66 } 67 68 // ContainerName returns the name of the container that owns this gofer. 69 func (g *GoferClient) ContainerName() string { 70 return g.contName 71 } 72 73 // DirentNames returns names of all the dirents for /dev on the gofer. 74 func (g *GoferClient) DirentNames(ctx context.Context) ([]string, error) { 75 if g.hostFD >= 0 { 76 return fsutil.DirentNames(g.hostFD) 77 } 78 client := g.clientFD.Client() 79 openFDID, _, err := g.clientFD.OpenAt(ctx, unix.O_RDONLY) 80 if err != nil { 81 return nil, fmt.Errorf("failed to open dev from gofer: %v", err) 82 } 83 defer client.CloseFD(ctx, openFDID, true /* flush */) 84 openFD := client.NewFD(openFDID) 85 const count = int32(64 * 1024) 86 var names []string 87 for { 88 dirents, err := openFD.Getdents64(ctx, count) 89 if err != nil { 90 return nil, fmt.Errorf("Getdents64 RPC failed: %v", err) 91 } 92 if len(dirents) == 0 { 93 break 94 } 95 for i := range dirents { 96 names = append(names, string(dirents[i].Name)) 97 } 98 } 99 return names, nil 100 } 101 102 // OpenAt opens the device file at /dev/{name} on the gofer. 103 func (g *GoferClient) OpenAt(ctx context.Context, name string, flags uint32) (int, error) { 104 flags &= unix.O_ACCMODE 105 if g.hostFD >= 0 { 106 return unix.Openat(g.hostFD, name, int(flags|unix.O_NOFOLLOW), 0) 107 } 108 childInode, err := g.clientFD.Walk(ctx, name) 109 if err != nil { 110 log.Infof("failed to walk %q from dev gofer FD", name) 111 return 0, err 112 } 113 client := g.clientFD.Client() 114 childFD := client.NewFD(childInode.ControlFD) 115 116 childOpenFD, childHostFD, err := childFD.OpenAt(ctx, flags) 117 if err != nil { 118 log.Infof("failed to open %q from child FD", name) 119 client.CloseFD(ctx, childFD.ID(), true /* flush */) 120 return 0, err 121 } 122 client.CloseFD(ctx, childFD.ID(), false /* flush */) 123 client.CloseFD(ctx, childOpenFD, true /* flush */) 124 return childHostFD, nil 125 } 126 127 // GoferClientProvider provides a GoferClient for a given container. 128 type GoferClientProvider interface { 129 GetDevGoferClient(contName string) *GoferClient 130 }