@@ -25,12 +25,16 @@ import (
25
25
"gvisor.dev/gvisor/pkg/errors/linuxerr"
26
26
"gvisor.dev/gvisor/pkg/fdnotifier"
27
27
"gvisor.dev/gvisor/pkg/hostarch"
28
+ "gvisor.dev/gvisor/pkg/log"
28
29
"gvisor.dev/gvisor/pkg/refs"
29
30
"gvisor.dev/gvisor/pkg/safemem"
31
+ "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
30
32
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
31
33
"gvisor.dev/gvisor/pkg/sentry/vfs"
32
34
)
33
35
36
+ var _ vfs.FilesystemImplSaveRestoreExtension = (* filesystem )(nil )
37
+
34
38
// +stateify savable
35
39
type savedDentryRW struct {
36
40
read bool
@@ -48,6 +52,7 @@ func (fs *filesystem) PrepareSave(ctx context.Context) error {
48
52
fs .renameMu .Lock ()
49
53
fs .evictAllCachedDentriesLocked (ctx )
50
54
fs .renameMu .Unlock ()
55
+ fs .savedDentryRW = make (map [* dentry ]savedDentryRW )
51
56
52
57
// Buffer pipe data so that it's available for reading after restore. (This
53
58
// is a legacy VFS1 feature.)
@@ -60,14 +65,23 @@ func (fs *filesystem) PrepareSave(ctx context.Context) error {
60
65
}
61
66
}
62
67
}
68
+ // Save file data for deleted regular files which are still accessible via
69
+ // open application FDs.
70
+ for sd := fs .syncableDentries .Front (); sd != nil ; sd = sd .Next () {
71
+ if sd .d .vfsd .IsDead () {
72
+ if err := sd .d .prepareSaveDead (ctx ); err != nil {
73
+ fs .syncMu .Unlock ()
74
+ return err
75
+ }
76
+ }
77
+ }
63
78
fs .syncMu .Unlock ()
64
79
65
80
// Flush local state to the remote filesystem.
66
81
if err := fs .Sync (ctx ); err != nil {
67
82
return err
68
83
}
69
84
70
- fs .savedDentryRW = make (map [* dentry ]savedDentryRW )
71
85
return fs .root .prepareSaveRecursive (ctx )
72
86
}
73
87
@@ -96,6 +110,59 @@ func (fd *specialFileFD) savePipeData(ctx context.Context) error {
96
110
return nil
97
111
}
98
112
113
+ func (d * dentry ) prepareSaveDead (ctx context.Context ) error {
114
+ if ! d .isRegularFile () {
115
+ return fmt .Errorf ("gofer.dentry(%q).prepareSaveDead: only regular deleted dentries can be saved, got %s" , genericDebugPathname (d .fs , d ), linux .FileMode (d .mode .Load ()))
116
+ }
117
+ if ! d .isDeleted () {
118
+ return fmt .Errorf ("gofer.dentry(%q).prepareSaveDead: invalidated dentries can't be saved" , genericDebugPathname (d .fs , d ))
119
+ }
120
+ if ! d .cachedMetadataAuthoritative () {
121
+ if err := d .updateMetadata (ctx ); err != nil {
122
+ return err
123
+ }
124
+ }
125
+ if d .isReadHandleOk () || d .isWriteHandleOk () {
126
+ d .fs .savedDentryRW [d ] = savedDentryRW {
127
+ read : d .isReadHandleOk (),
128
+ write : d .isWriteHandleOk (),
129
+ }
130
+ }
131
+ d .handleMu .RLock ()
132
+ defer d .handleMu .RUnlock ()
133
+ var h handle
134
+ if d .isReadHandleOk () {
135
+ h = d .readHandle ()
136
+ } else {
137
+ var err error
138
+ h , err = d .openHandle (ctx , true /* read */ , false /* write */ , false /* trunc */ )
139
+ if err != nil {
140
+ return fmt .Errorf ("failed to open read handle for deleted file %q: %w" , genericDebugPathname (d .fs , d ), err )
141
+ }
142
+ defer h .close (ctx )
143
+ }
144
+ d .dataMu .RLock ()
145
+ defer d .dataMu .RUnlock ()
146
+ d .deletedDataSR = make ([]byte , d .size .Load ())
147
+ done := uint64 (0 )
148
+ for done < uint64 (len (d .deletedDataSR )) {
149
+ n , err := h .readToBlocksAt (ctx , safemem .BlockSeqOf (safemem .BlockFromSafeSlice (d .deletedDataSR [done :])), done )
150
+ done += n
151
+ if err != nil {
152
+ if err == io .EOF {
153
+ break
154
+ }
155
+ return fmt .Errorf ("failed to read deleted file %q: %w" , genericDebugPathname (d .fs , d ), err )
156
+ }
157
+ }
158
+ d .deletedDataSR = d .deletedDataSR [:done ]
159
+ if d .fs .savedDeletedOpenDentries == nil {
160
+ d .fs .savedDeletedOpenDentries = make (map [* dentry ]struct {})
161
+ }
162
+ d .fs .savedDeletedOpenDentries [d ] = struct {}{}
163
+ return nil
164
+ }
165
+
99
166
func (d * dentry ) prepareSaveRecursive (ctx context.Context ) error {
100
167
if d .isRegularFile () && ! d .cachedMetadataAuthoritative () {
101
168
// Get updated metadata for d in case we need to perform metadata
@@ -129,11 +196,20 @@ func (d *dentry) prepareSaveRecursive(ctx context.Context) error {
129
196
130
197
// beforeSave is invoked by stateify.
131
198
func (d * dentry ) beforeSave () {
132
- if d .vfsd .IsDead () {
199
+ if d .vfsd .IsDead () && d . deletedDataSR == nil {
133
200
panic (fmt .Sprintf ("gofer.dentry(%q).beforeSave: deleted and invalidated dentries can't be restored" , genericDebugPathname (d .fs , d )))
134
201
}
135
202
}
136
203
204
+ // PostSave implements vfs.FilesystemImplSaveRestoreExtension.PostSave.
205
+ func (fs * filesystem ) PostSave (ctx context.Context ) {
206
+ for d := range fs .savedDeletedOpenDentries {
207
+ d .deletedDataSR = nil
208
+ }
209
+ fs .savedDeletedOpenDentries = nil
210
+ fs .savedDentryRW = nil
211
+ }
212
+
137
213
// afterLoad is invoked by stateify.
138
214
func (fs * filesystem ) afterLoad (ctx goContext.Context ) {
139
215
fs .mf = pgalloc .MemoryFileFromContext (ctx )
@@ -229,7 +305,17 @@ func (fs *filesystem) CompleteRestore(ctx context.Context, opts vfs.CompleteRest
229
305
}
230
306
}
231
307
308
+ // Restore deleted files which are still accessible via open application FDs.
309
+ for d := range fs .savedDeletedOpenDentries {
310
+ if d .deletedDataSR != nil {
311
+ if err := d .restoreDead (ctx , & opts ); err != nil {
312
+ return err
313
+ }
314
+ }
315
+ }
316
+
232
317
// Discard state only required during restore.
318
+ fs .savedDeletedOpenDentries = nil
233
319
fs .savedDentryRW = nil
234
320
235
321
return nil
@@ -256,6 +342,37 @@ func (d *dentry) restoreDescendantsRecursive(ctx context.Context, opts *vfs.Comp
256
342
return nil
257
343
}
258
344
345
+ // Preconditions: d.deletedDataSR != nil.
346
+ func (d * dentry ) restoreDead (ctx context.Context , opts * vfs.CompleteRestoreOptions ) error {
347
+ parent := d .parent .Load ()
348
+ // This is a deleted regular file. Recreate it on the host filesystem
349
+ // temporarily, fill it with data and then proceed with the restore.
350
+ _ , h , err := parent .openCreate (ctx , d .name , linux .O_WRONLY , linux .FileMode (d .mode .Load ()), auth .KUID (d .uid .Load ()), auth .KGID (d .gid .Load ()), false /* createDentry */ )
351
+ if err != nil {
352
+ return fmt .Errorf ("failed to re-create deleted file %q: %w" , genericDebugPathname (d .fs , d ), err )
353
+ }
354
+ defer h .close (ctx )
355
+ n , err := h .writeFromBlocksAt (ctx , safemem .BlockSeqOf (safemem .BlockFromSafeSlice (d .deletedDataSR )), 0 )
356
+ if err != nil {
357
+ return fmt .Errorf ("failed to write deleted file %q: %w" , genericDebugPathname (d .fs , d ), err )
358
+ }
359
+ if n != uint64 (len (d .deletedDataSR )) {
360
+ return fmt .Errorf ("failed to write all of deleted file %q: wrote %d bytes, expected %d" , genericDebugPathname (d .fs , d ), n , len (d .deletedDataSR ))
361
+ }
362
+ d .deletedDataSR = nil
363
+ if err := d .restoreFile (ctx , opts ); err != nil {
364
+ if err := parent .unlink (ctx , d .name , 0 /* flags */ ); err != nil {
365
+ // Log warning, give preference to the restore error.
366
+ log .Warningf ("failed to clean up recreated deleted file %q: %v" , genericDebugPathname (d .fs , d ), err )
367
+ }
368
+ return err
369
+ }
370
+ if err := parent .unlink (ctx , d .name , 0 /* flags */ ); err != nil {
371
+ return fmt .Errorf ("failed to clean up recreated deleted file %q: %v" , genericDebugPathname (d .fs , d ), err )
372
+ }
373
+ return nil
374
+ }
375
+
259
376
func (fd * specialFileFD ) completeRestore (ctx context.Context ) error {
260
377
d := fd .dentry ()
261
378
h , err := d .openHandle (ctx , fd .vfsfd .IsReadable (), fd .vfsfd .IsWritable (), false /* trunc */ )
0 commit comments