@@ -112,14 +112,21 @@ func (fd *specialFileFD) savePipeData(ctx context.Context) error {
112
112
}
113
113
114
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 ()))
115
+ if ! d .isRegularFile () && ! d . isDir () {
116
+ return fmt .Errorf ("gofer.dentry(%q).prepareSaveDead: only deleted dentries for regular files and directories can be saved, got %s" , genericDebugPathname (d .fs , d ), linux .FileMode (d .mode .Load ()))
117
117
}
118
118
if ! d .isDeleted () {
119
119
return fmt .Errorf ("gofer.dentry(%q).prepareSaveDead: invalidated dentries can't be saved" , genericDebugPathname (d .fs , d ))
120
120
}
121
- if ! d .cachedMetadataAuthoritative () {
122
- if err := d .updateMetadata (ctx ); err != nil {
121
+ if d .isRegularFile () {
122
+ if ! d .cachedMetadataAuthoritative () {
123
+ // Get updated metadata for d in case we need to perform metadata
124
+ // validation during restore.
125
+ if err := d .updateMetadata (ctx ); err != nil {
126
+ return err
127
+ }
128
+ }
129
+ if err := d .prepareSaveDeletedRegularFile (ctx ); err != nil {
123
130
return err
124
131
}
125
132
}
@@ -129,6 +136,18 @@ func (d *dentry) prepareSaveDead(ctx context.Context) error {
129
136
write : d .isWriteHandleOk (),
130
137
}
131
138
}
139
+ if d .fs .savedDeletedOpenDentries == nil {
140
+ d .fs .savedDeletedOpenDentries = make (map [* dentry ]struct {})
141
+ }
142
+ d .fs .savedDeletedOpenDentries [d ] = struct {}{}
143
+ return nil
144
+ }
145
+
146
+ // Preconditions:
147
+ // - d.isRegularFile()
148
+ // - d.isDeleted()
149
+ func (d * dentry ) prepareSaveDeletedRegularFile (ctx context.Context ) error {
150
+ // Fetch an appropriate handle to read the deleted file.
132
151
d .handleMu .RLock ()
133
152
defer d .handleMu .RUnlock ()
134
153
var h handle
@@ -142,12 +161,13 @@ func (d *dentry) prepareSaveDead(ctx context.Context) error {
142
161
}
143
162
defer h .close (ctx )
144
163
}
164
+ // Read the file data and store it in d.savedDeletedData.
145
165
d .dataMu .RLock ()
146
166
defer d .dataMu .RUnlock ()
147
- d .deletedDataSR = make ([]byte , d .size .Load ())
167
+ d .savedDeletedData = make ([]byte , d .size .Load ())
148
168
done := uint64 (0 )
149
- for done < uint64 (len (d .deletedDataSR )) {
150
- n , err := h .readToBlocksAt (ctx , safemem .BlockSeqOf (safemem .BlockFromSafeSlice (d .deletedDataSR [done :])), done )
169
+ for done < uint64 (len (d .savedDeletedData )) {
170
+ n , err := h .readToBlocksAt (ctx , safemem .BlockSeqOf (safemem .BlockFromSafeSlice (d .savedDeletedData [done :])), done )
151
171
done += n
152
172
if err != nil {
153
173
if err == io .EOF {
@@ -156,14 +176,9 @@ func (d *dentry) prepareSaveDead(ctx context.Context) error {
156
176
return fmt .Errorf ("failed to read deleted file %q: %w" , genericDebugPathname (d .fs , d ), err )
157
177
}
158
178
}
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 {})
179
+ if done < uint64 (len (d .savedDeletedData )) {
180
+ return fmt .Errorf ("failed to read all of deleted file %q: read %d bytes, expected %d" , genericDebugPathname (d .fs , d ), done , len (d .savedDeletedData ))
165
181
}
166
- d .fs .savedDeletedOpenDentries [d ] = struct {}{}
167
182
return nil
168
183
}
169
184
@@ -200,15 +215,17 @@ func (d *dentry) prepareSaveRecursive(ctx context.Context) error {
200
215
201
216
// beforeSave is invoked by stateify.
202
217
func (d * dentry ) beforeSave () {
203
- if d .vfsd .IsDead () && d .deletedDataSR == nil {
204
- panic (fmt .Sprintf ("gofer.dentry(%q).beforeSave: deletedDataSR is nil for dead dentry (deleted=%t, synthetic=%t)" , genericDebugPathname (d .fs , d ), d .isDeleted (), d .isSynthetic ()))
218
+ if d .vfsd .IsDead () {
219
+ if _ , ok := d .fs .savedDeletedOpenDentries [d ]; ! ok {
220
+ panic (fmt .Sprintf ("gofer.dentry(%q).beforeSave: dead dentry is not saved in fs.savedDeletedOpenDentries (deleted=%t, synthetic=%t)" , genericDebugPathname (d .fs , d ), d .isDeleted (), d .isSynthetic ()))
221
+ }
205
222
}
206
223
}
207
224
208
225
// BeforeResume implements vfs.FilesystemImplSaveRestoreExtension.BeforeResume.
209
226
func (fs * filesystem ) BeforeResume (ctx context.Context ) {
210
227
for d := range fs .savedDeletedOpenDentries {
211
- d .deletedDataSR = nil
228
+ d .savedDeletedData = nil
212
229
}
213
230
fs .savedDeletedOpenDentries = nil
214
231
fs .savedDentryRW = nil
@@ -310,11 +327,29 @@ func (fs *filesystem) CompleteRestore(ctx context.Context, opts vfs.CompleteRest
310
327
}
311
328
312
329
// Restore deleted files which are still accessible via open application FDs.
330
+ dirsToDelete := make (map [* dentry ]struct {})
313
331
for d := range fs .savedDeletedOpenDentries {
314
- if err := d .restoreDead (ctx , & opts ); err != nil {
332
+ if err := d .restoreDeleted (ctx , & opts , dirsToDelete ); err != nil {
315
333
return err
316
334
}
317
335
}
336
+ for len (dirsToDelete ) > 0 {
337
+ // In case of nested deleted directories, only leaf directories can be
338
+ // deleted. Then repeat as parent directories become leaves.
339
+ leafDirectories := make (map [* dentry ]struct {})
340
+ for d := range dirsToDelete {
341
+ leafDirectories [d ] = struct {}{}
342
+ }
343
+ for d := range dirsToDelete {
344
+ delete (leafDirectories , d .parent .Load ())
345
+ }
346
+ for leafD := range leafDirectories {
347
+ if err := leafD .parent .Load ().unlink (ctx , leafD .name , linux .AT_REMOVEDIR ); err != nil {
348
+ return fmt .Errorf ("failed to clean up recreated deleted directory %q: %v" , genericDebugPathname (fs , leafD ), err )
349
+ }
350
+ delete (dirsToDelete , leafD )
351
+ }
352
+ }
318
353
319
354
// Discard state only required during restore.
320
355
fs .savedDeletedOpenDentries = nil
@@ -344,10 +379,51 @@ func (d *dentry) restoreDescendantsRecursive(ctx context.Context, opts *vfs.Comp
344
379
return nil
345
380
}
346
381
347
- // restoreDead restores a deleted regular file.
382
+ // restoreDeleted restores a deleted dentry for a directory or regular file.
348
383
//
349
- // Preconditions: d.deletedDataSR != nil.
350
- func (d * dentry ) restoreDead (ctx context.Context , opts * vfs.CompleteRestoreOptions ) error {
384
+ // Preconditions:
385
+ // - d.isRegularFile() || d.isDir()
386
+ // - d.savedDeletedData != nil iff d.isRegularFile()
387
+ func (d * dentry ) restoreDeleted (ctx context.Context , opts * vfs.CompleteRestoreOptions , dirsToDelete map [* dentry ]struct {}) error {
388
+ parent := d .parent .Load ()
389
+ if _ , ok := d .fs .savedDeletedOpenDentries [parent ]; ok {
390
+ // Recursively restore the parent first if the parent is also deleted.
391
+ if err := parent .restoreDeleted (ctx , opts , dirsToDelete ); err != nil {
392
+ return err
393
+ }
394
+ }
395
+ switch {
396
+ case d .isRegularFile ():
397
+ return d .restoreDeletedRegularFile (ctx , opts )
398
+ case d .isDir ():
399
+ return d .restoreDeletedDirectory (ctx , opts , dirsToDelete )
400
+ default :
401
+ return fmt .Errorf ("gofer.dentry(%q).restoreDeleted: invalid file type %s" , genericDebugPathname (d .fs , d ), linux .FileMode (d .mode .Load ()))
402
+ }
403
+ }
404
+
405
+ func (d * dentry ) restoreDeletedDirectory (ctx context.Context , opts * vfs.CompleteRestoreOptions , dirsToDelete map [* dentry ]struct {}) error {
406
+ // Recreate the directory on the host filesystem. This will be deleted later.
407
+ parent := d .parent .Load ()
408
+ _ , err := parent .mkdir (ctx , d .name , linux .FileMode (d .mode .Load ()), auth .KUID (d .uid .Load ()), auth .KGID (d .gid .Load ()), false /* createDentry */ )
409
+ if err != nil {
410
+ return fmt .Errorf ("failed to re-create deleted directory %q: %w" , genericDebugPathname (d .fs , d ), err )
411
+ }
412
+ // Restore the directory.
413
+ if err := d .restoreFile (ctx , opts ); err != nil {
414
+ if err := parent .unlink (ctx , d .name , linux .AT_REMOVEDIR ); err != nil {
415
+ log .Warningf ("failed to clean up recreated deleted directory %q: %v" , genericDebugPathname (d .fs , d ), err )
416
+ }
417
+ return fmt .Errorf ("failed to restore deleted directory: %w" , err )
418
+ }
419
+ // We will delete the directory later. We need to keep it around in case any
420
+ // of its children need to be restored after this.
421
+ dirsToDelete [d ] = struct {}{}
422
+ delete (d .fs .savedDeletedOpenDentries , d )
423
+ return nil
424
+ }
425
+
426
+ func (d * dentry ) restoreDeletedRegularFile (ctx context.Context , opts * vfs.CompleteRestoreOptions ) error {
351
427
// Recreate the file on the host filesystem (this is temporary).
352
428
parent := d .parent .Load ()
353
429
_ , 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 */ )
@@ -363,26 +439,27 @@ func (d *dentry) restoreDead(ctx context.Context, opts *vfs.CompleteRestoreOptio
363
439
})
364
440
defer unlinkCU .Clean ()
365
441
// Write the file data to the recreated file.
366
- n , err := h .writeFromBlocksAt (ctx , safemem .BlockSeqOf (safemem .BlockFromSafeSlice (d .deletedDataSR )), 0 )
442
+ n , err := h .writeFromBlocksAt (ctx , safemem .BlockSeqOf (safemem .BlockFromSafeSlice (d .savedDeletedData )), 0 )
367
443
if err != nil {
368
444
return fmt .Errorf ("failed to write deleted file %q: %w" , genericDebugPathname (d .fs , d ), err )
369
445
}
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 ))
446
+ if n != uint64 (len (d .savedDeletedData )) {
447
+ return fmt .Errorf ("failed to write all of deleted file %q: wrote %d bytes, expected %d" , genericDebugPathname (d .fs , d ), n , len (d .savedDeletedData ))
372
448
}
373
- d .deletedDataSR = nil
449
+ d .savedDeletedData = nil
374
450
// Restore the file. Note that timestamps may not match since we re-created
375
451
// the file on the host.
376
452
recreateOpts := * opts
377
453
recreateOpts .ValidateFileModificationTimestamps = false
378
454
if err := d .restoreFile (ctx , & recreateOpts ); err != nil {
379
- return err
455
+ return fmt . Errorf ( "failed to restore deleted regular file: %w" , err )
380
456
}
381
457
// Finally, unlink the recreated file.
382
458
unlinkCU .Release ()
383
459
if err := parent .unlink (ctx , d .name , 0 /* flags */ ); err != nil {
384
460
return fmt .Errorf ("failed to clean up recreated deleted file %q: %v" , genericDebugPathname (d .fs , d ), err )
385
461
}
462
+ delete (d .fs .savedDeletedOpenDentries , d )
386
463
return nil
387
464
}
388
465
0 commit comments