14
14
package main
15
15
16
16
import (
17
+ "context"
18
+ "fmt"
17
19
"io/ioutil"
18
20
"os"
19
21
"path/filepath"
20
22
"strings"
23
+ "time"
21
24
25
+ "github.com/containerd/containerd/log"
26
+ "github.com/containerd/containerd/mount"
22
27
"github.com/firecracker-microvm/firecracker-containerd/internal"
28
+ drivemount "github.com/firecracker-microvm/firecracker-containerd/proto/service/drivemount/ttrpc"
29
+ "github.com/golang/protobuf/ptypes/empty"
30
+ "github.com/pkg/errors"
23
31
)
24
32
25
33
const (
@@ -28,6 +36,14 @@ const (
28
36
blockMajorMinor = "dev"
29
37
)
30
38
39
+ var (
40
+ bannedSystemDirs = []string {
41
+ "/proc" ,
42
+ "/sys" ,
43
+ "/dev" ,
44
+ }
45
+ )
46
+
31
47
type drive struct {
32
48
Name string
33
49
DriveID string
@@ -45,6 +61,8 @@ type driveHandler struct {
45
61
DrivePath string
46
62
}
47
63
64
+ var _ drivemount.DriveMounterService = & driveHandler {}
65
+
48
66
func newDriveHandler (blockPath , drivePath string ) (* driveHandler , error ) {
49
67
d := & driveHandler {
50
68
drives : map [string ]drive {},
@@ -146,3 +164,87 @@ func isStubDrive(d drive) bool {
146
164
147
165
return internal .IsStubDrive (f )
148
166
}
167
+
168
+ func (dh driveHandler ) MountDrive (ctx context.Context , req * drivemount.MountDriveRequest ) (* empty.Empty , error ) {
169
+ logger := log .G (ctx ).WithField ("MountDriveRequest" , req .String ())
170
+ logger .Debug ()
171
+
172
+ driveID := strings .TrimSpace (req .DriveID )
173
+ drive , ok := dh .GetDrive (driveID )
174
+ if ! ok {
175
+ return nil , fmt .Errorf ("Drive %q could not be found" , driveID )
176
+ }
177
+ logger = logger .WithField ("drive_path" , drive .Path ())
178
+
179
+ // Do a basic check that we won't be mounting over any important system directories
180
+ resolvedDest , err := evalAnySymlinks (req .DestinationPath )
181
+ if err != nil {
182
+ return nil , errors .Wrapf (err ,
183
+ "failed to evaluate any symlinks in drive mount destination %q" , req .DestinationPath )
184
+ }
185
+
186
+ for _ , systemDir := range bannedSystemDirs {
187
+ if strings .HasPrefix (filepath .Clean (resolvedDest ), filepath .Clean (systemDir )) {
188
+ return nil , errors .Errorf (
189
+ "drive mount destination %q resolves to path %q under banned system directory %q" ,
190
+ req .DestinationPath , resolvedDest , systemDir ,
191
+ )
192
+ }
193
+ }
194
+
195
+ err = os .MkdirAll (req .DestinationPath , 0700 )
196
+ if err != nil {
197
+ return nil , errors .Wrapf (err , "failed to create drive mount destination %q" , req .DestinationPath )
198
+ }
199
+
200
+ const (
201
+ maxRetries = 100
202
+ retryDelay = 10 * time .Millisecond
203
+ )
204
+
205
+ for i := 0 ; i < maxRetries ; i ++ {
206
+ err := mount .All ([]mount.Mount {{
207
+ Source : drive .Path (),
208
+ Type : req .FilesytemType ,
209
+ Options : req .Options ,
210
+ }}, req .DestinationPath )
211
+ if err == nil {
212
+ return & empty.Empty {}, nil
213
+ }
214
+
215
+ if isRetryableMountError (err ) {
216
+ logger .WithError (err ).Warnf ("retryable failure mounting drive" )
217
+ time .Sleep (retryDelay )
218
+ continue
219
+ }
220
+
221
+ return nil , errors .Wrapf (err , "non-retryable failure mounting drive from %q to %q" ,
222
+ drive .Path (), req .DestinationPath )
223
+ }
224
+
225
+ return nil , errors .Errorf ("exhausted retries mounting drive from %q to %q" ,
226
+ drive .Path (), req .DestinationPath )
227
+ }
228
+
229
+ // evalAnySymlinks is similar to filepath.EvalSymlinks, except it will not return an error if part of the
230
+ // provided path does not exist. It will evaluate symlinks present in the path up to a component that doesn't
231
+ // exist, at which point it will just append the rest of the provided path to what has been resolved so far.
232
+ func evalAnySymlinks (path string ) (string , error ) {
233
+ curPath := "/"
234
+ pathSplit := strings .Split (filepath .Clean (path ), "/" )
235
+ for len (pathSplit ) > 0 {
236
+ curPath = filepath .Join (curPath , pathSplit [0 ])
237
+ pathSplit = pathSplit [1 :]
238
+
239
+ resolvedPath , err := filepath .EvalSymlinks (curPath )
240
+ if os .IsNotExist (err ) {
241
+ return filepath .Join (append ([]string {curPath }, pathSplit ... )... ), nil
242
+ }
243
+ if err != nil {
244
+ return "" , err
245
+ }
246
+ curPath = resolvedPath
247
+ }
248
+
249
+ return curPath , nil
250
+ }
0 commit comments