@@ -21,12 +21,15 @@ import (
21
21
22
22
"gvisor.dev/gvisor/pkg/abi/linux"
23
23
"gvisor.dev/gvisor/pkg/atomicbitops"
24
+ "gvisor.dev/gvisor/pkg/cleanup"
24
25
"gvisor.dev/gvisor/pkg/context"
25
26
"gvisor.dev/gvisor/pkg/errors/linuxerr"
26
27
"gvisor.dev/gvisor/pkg/fdnotifier"
27
28
"gvisor.dev/gvisor/pkg/hostarch"
29
+ "gvisor.dev/gvisor/pkg/log"
28
30
"gvisor.dev/gvisor/pkg/refs"
29
31
"gvisor.dev/gvisor/pkg/safemem"
32
+ "gvisor.dev/gvisor/pkg/sentry/kernel/auth"
30
33
"gvisor.dev/gvisor/pkg/sentry/pgalloc"
31
34
"gvisor.dev/gvisor/pkg/sentry/vfs"
32
35
)
@@ -50,6 +53,7 @@ func (fs *filesystem) PrepareSave(ctx context.Context) error {
50
53
fs .renameMu .Lock ()
51
54
fs .evictAllCachedDentriesLocked (ctx )
52
55
fs .renameMu .Unlock ()
56
+ fs .savedDentryRW = make (map [* dentry ]savedDentryRW )
53
57
54
58
// Buffer pipe data so that it's available for reading after restore. (This
55
59
// is a legacy VFS1 feature.)
@@ -62,14 +66,23 @@ func (fs *filesystem) PrepareSave(ctx context.Context) error {
62
66
}
63
67
}
64
68
}
69
+ // Save file data for deleted regular files which are still accessible via
70
+ // open application FDs.
71
+ for sd := fs .syncableDentries .Front (); sd != nil ; sd = sd .Next () {
72
+ if sd .d .vfsd .IsDead () {
73
+ if err := sd .d .prepareSaveDead (ctx ); err != nil {
74
+ fs .syncMu .Unlock ()
75
+ return err
76
+ }
77
+ }
78
+ }
65
79
fs .syncMu .Unlock ()
66
80
67
81
// Flush local state to the remote filesystem.
68
82
if err := fs .Sync (ctx ); err != nil {
69
83
return err
70
84
}
71
85
72
- fs .savedDentryRW = make (map [* dentry ]savedDentryRW )
73
86
return fs .root .prepareSaveRecursive (ctx )
74
87
}
75
88
@@ -98,6 +111,62 @@ func (fd *specialFileFD) savePipeData(ctx context.Context) error {
98
111
return nil
99
112
}
100
113
114
+ func (d * dentry ) prepareSaveDead (ctx context.Context ) error {
115
+ if ! d .isRegularFile () {
116
+ 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 ()))
117
+ }
118
+ if ! d .isDeleted () {
119
+ return fmt .Errorf ("gofer.dentry(%q).prepareSaveDead: invalidated dentries can't be saved" , genericDebugPathname (d .fs , d ))
120
+ }
121
+ if ! d .cachedMetadataAuthoritative () {
122
+ if err := d .updateMetadata (ctx ); err != nil {
123
+ return err
124
+ }
125
+ }
126
+ if d .isReadHandleOk () || d .isWriteHandleOk () {
127
+ d .fs .savedDentryRW [d ] = savedDentryRW {
128
+ read : d .isReadHandleOk (),
129
+ write : d .isWriteHandleOk (),
130
+ }
131
+ }
132
+ d .handleMu .RLock ()
133
+ defer d .handleMu .RUnlock ()
134
+ var h handle
135
+ if d .isReadHandleOk () {
136
+ h = d .readHandle ()
137
+ } else {
138
+ var err error
139
+ h , err = d .openHandle (ctx , true /* read */ , false /* write */ , false /* trunc */ )
140
+ if err != nil {
141
+ return fmt .Errorf ("failed to open read handle for deleted file %q: %w" , genericDebugPathname (d .fs , d ), err )
142
+ }
143
+ defer h .close (ctx )
144
+ }
145
+ d .dataMu .RLock ()
146
+ defer d .dataMu .RUnlock ()
147
+ d .deletedDataSR = make ([]byte , d .size .Load ())
148
+ done := uint64 (0 )
149
+ for done < uint64 (len (d .deletedDataSR )) {
150
+ n , err := h .readToBlocksAt (ctx , safemem .BlockSeqOf (safemem .BlockFromSafeSlice (d .deletedDataSR [done :])), done )
151
+ done += n
152
+ if err != nil {
153
+ if err == io .EOF {
154
+ break
155
+ }
156
+ return fmt .Errorf ("failed to read deleted file %q: %w" , genericDebugPathname (d .fs , d ), err )
157
+ }
158
+ }
159
+ if done < uint64 (len (d .deletedDataSR )) {
160
+ return fmt .Errorf ("failed to read all of deleted file %q: read %d bytes, expected %d" , genericDebugPathname (d .fs , d ), done , len (d .deletedDataSR ))
161
+ }
162
+ d .deletedDataSR = d .deletedDataSR [:done ]
163
+ if d .fs .savedDeletedOpenDentries == nil {
164
+ d .fs .savedDeletedOpenDentries = make (map [* dentry ]struct {})
165
+ }
166
+ d .fs .savedDeletedOpenDentries [d ] = struct {}{}
167
+ return nil
168
+ }
169
+
101
170
func (d * dentry ) prepareSaveRecursive (ctx context.Context ) error {
102
171
if d .isRegularFile () && ! d .cachedMetadataAuthoritative () {
103
172
// Get updated metadata for d in case we need to perform metadata
@@ -131,13 +200,17 @@ func (d *dentry) prepareSaveRecursive(ctx context.Context) error {
131
200
132
201
// beforeSave is invoked by stateify.
133
202
func (d * dentry ) beforeSave () {
134
- if d .vfsd .IsDead () {
203
+ if d .vfsd .IsDead () && d . deletedDataSR == nil {
135
204
panic (fmt .Sprintf ("gofer.dentry(%q).beforeSave: deleted and invalidated dentries can't be restored" , genericDebugPathname (d .fs , d )))
136
205
}
137
206
}
138
207
139
208
// BeforeResume implements vfs.FilesystemImplSaveRestoreExtension.BeforeResume.
140
209
func (fs * filesystem ) BeforeResume (ctx context.Context ) {
210
+ for d := range fs .savedDeletedOpenDentries {
211
+ d .deletedDataSR = nil
212
+ }
213
+ fs .savedDeletedOpenDentries = nil
141
214
fs .savedDentryRW = nil
142
215
}
143
216
@@ -236,7 +309,15 @@ func (fs *filesystem) CompleteRestore(ctx context.Context, opts vfs.CompleteRest
236
309
}
237
310
}
238
311
312
+ // Restore deleted files which are still accessible via open application FDs.
313
+ for d := range fs .savedDeletedOpenDentries {
314
+ if err := d .restoreDead (ctx , & opts ); err != nil {
315
+ return err
316
+ }
317
+ }
318
+
239
319
// Discard state only required during restore.
320
+ fs .savedDeletedOpenDentries = nil
240
321
fs .savedDentryRW = nil
241
322
242
323
return nil
@@ -263,6 +344,48 @@ func (d *dentry) restoreDescendantsRecursive(ctx context.Context, opts *vfs.Comp
263
344
return nil
264
345
}
265
346
347
+ // restoreDead restores a deleted regular file.
348
+ //
349
+ // Preconditions: d.deletedDataSR != nil.
350
+ func (d * dentry ) restoreDead (ctx context.Context , opts * vfs.CompleteRestoreOptions ) error {
351
+ // Recreate the file on the host filesystem (this is temporary).
352
+ parent := d .parent .Load ()
353
+ _ , 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 */ )
354
+ if err != nil {
355
+ return fmt .Errorf ("failed to re-create deleted file %q: %w" , genericDebugPathname (d .fs , d ), err )
356
+ }
357
+ defer h .close (ctx )
358
+ // In case of errors, clean up the recreated file.
359
+ cu := cleanup .Make (func () {
360
+ if err := parent .unlink (ctx , d .name , 0 /* flags */ ); err != nil {
361
+ log .Warningf ("failed to clean up recreated deleted file %q: %v" , genericDebugPathname (d .fs , d ), err )
362
+ }
363
+ })
364
+ defer cu .Clean ()
365
+ // Write the file data to the recreated file.
366
+ n , err := h .writeFromBlocksAt (ctx , safemem .BlockSeqOf (safemem .BlockFromSafeSlice (d .deletedDataSR )), 0 )
367
+ if err != nil {
368
+ return fmt .Errorf ("failed to write deleted file %q: %w" , genericDebugPathname (d .fs , d ), err )
369
+ }
370
+ if n != uint64 (len (d .deletedDataSR )) {
371
+ return fmt .Errorf ("failed to write all of deleted file %q: wrote %d bytes, expected %d" , genericDebugPathname (d .fs , d ), n , len (d .deletedDataSR ))
372
+ }
373
+ d .deletedDataSR = nil
374
+ // Restore the file. Note that timestamps may not match since we re-created
375
+ // the file on the host.
376
+ recreateOpts := * opts
377
+ recreateOpts .ValidateFileModificationTimestamps = false
378
+ if err := d .restoreFile (ctx , & recreateOpts ); err != nil {
379
+ return err
380
+ }
381
+ // Finally, unlink the recreated file.
382
+ cu .Release ()
383
+ if err := parent .unlink (ctx , d .name , 0 /* flags */ ); err != nil {
384
+ return fmt .Errorf ("failed to clean up recreated deleted file %q: %v" , genericDebugPathname (d .fs , d ), err )
385
+ }
386
+ return nil
387
+ }
388
+
266
389
func (fd * specialFileFD ) completeRestore (ctx context.Context ) error {
267
390
d := fd .dentry ()
268
391
h , err := d .openHandle (ctx , fd .vfsfd .IsReadable (), fd .vfsfd .IsWritable (), false /* trunc */ )
0 commit comments