1
- use crate :: common;
1
+ use crate :: { common, info } ;
2
2
use anyhow:: { anyhow, bail, Context , Result } ;
3
- use libmount:: { mountinfo:: Parser , Overlay } ;
3
+ use libmount:: { mountinfo:: Parser , Overlay , Tmpfs } ;
4
4
use nix:: mount:: { umount2, MntFlags } ;
5
- use std:: fs;
6
5
use std:: os:: unix:: ffi:: OsStrExt ;
7
6
use std:: os:: unix:: fs:: { FileTypeExt , MetadataExt , PermissionsExt } ;
8
7
use std:: path:: { Path , PathBuf } ;
@@ -11,6 +10,7 @@ use std::{
11
10
ffi:: OsStr ,
12
11
io:: { BufRead , BufReader } ,
13
12
} ;
13
+ use std:: { fs, path} ;
14
14
15
15
pub trait LayerManager {
16
16
/// Return the name of the layer manager, e.g. "overlay".
@@ -31,12 +31,18 @@ pub trait LayerManager {
31
31
fn mount ( & mut self , to : & Path ) -> Result < ( ) > ;
32
32
/// Return if the filesystem is mounted
33
33
fn is_mounted ( & self , target : & Path ) -> Result < bool > ;
34
+ /// Return if the filesystem uses tmpfs for upper layer.
35
+ fn is_tmpfs ( & self ) -> bool ;
36
+ /// Return if tmpfs is mounted.
37
+ fn is_tmpfs_mounted ( & self ) -> Result < bool > ;
34
38
/// Rollback the filesystem to the distribution state
35
39
fn rollback ( & mut self ) -> Result < ( ) > ;
36
40
/// Commit the current state of the instance filesystem to the distribution state
37
41
fn commit ( & mut self ) -> Result < ( ) > ;
38
42
/// Un-mount the filesystem
39
43
fn unmount ( & mut self , target : & Path ) -> Result < ( ) > ;
44
+ /// Un-mount tmpfs.
45
+ fn unmount_tmpfs ( & self ) -> Result < ( ) > ;
40
46
/// Return the directory where the configuration layer is located
41
47
/// You may temporary mount this directory if your backend does not expose this directory directly
42
48
fn get_config_layer ( & mut self ) -> Result < PathBuf > ;
@@ -55,12 +61,20 @@ struct OverlayFS {
55
61
upper : PathBuf ,
56
62
work : PathBuf ,
57
63
volatile : bool ,
64
+ tmpfs : Option < PathBuf > ,
58
65
}
59
66
60
67
/// Create a new overlay filesystem on the host system
61
- pub fn create_new_instance_fs < P : AsRef < Path > > ( inst_path : P , inst_name : P ) -> Result < ( ) > {
68
+ pub fn create_new_instance_fs < P : AsRef < Path > > (
69
+ inst_path : P ,
70
+ inst_name : P ,
71
+ tmpfs : bool ,
72
+ ) -> Result < ( ) > {
62
73
let inst = inst_path. as_ref ( ) . join ( inst_name. as_ref ( ) ) ;
63
- fs:: create_dir_all ( inst) ?;
74
+ fs:: create_dir_all ( & inst) ?;
75
+ if tmpfs {
76
+ fs:: create_dir_all ( inst. join ( "layers/tmpfs" ) ) ?;
77
+ }
64
78
Ok ( ( ) )
65
79
}
66
80
@@ -174,28 +188,56 @@ impl LayerManager for OverlayFS {
174
188
{
175
189
let dist = dist_path. as_ref ( ) ;
176
190
let inst = inst_path. as_ref ( ) . join ( inst_name. as_ref ( ) ) ;
177
- Ok ( Box :: new ( OverlayFS {
178
- inst : inst. to_owned ( ) ,
179
- base : dist. to_owned ( ) ,
180
- lower : inst. join ( "layers/local" ) ,
181
- upper : inst. join ( "layers/diff" ) ,
182
- work : inst. join ( "layers/diff.tmp" ) ,
183
- volatile : false ,
184
- } ) )
191
+ if inst. join ( "layers/tmpfs" ) . exists ( ) {
192
+ Ok ( Box :: new ( OverlayFS {
193
+ inst : inst. to_owned ( ) ,
194
+ base : dist. to_owned ( ) ,
195
+ lower : inst. join ( "layers/local" ) ,
196
+ upper : inst. join ( "layers/tmpfs/upper" ) ,
197
+ work : inst. join ( "layers/tmpfs/work" ) ,
198
+ volatile : false ,
199
+ tmpfs : Some ( inst. join ( "layers/tmpfs" ) ) ,
200
+ } ) )
201
+ } else {
202
+ Ok ( Box :: new ( OverlayFS {
203
+ inst : inst. to_owned ( ) ,
204
+ base : dist. to_owned ( ) ,
205
+ lower : inst. join ( "layers/local" ) ,
206
+ upper : inst. join ( "layers/diff" ) ,
207
+ work : inst. join ( "layers/diff.tmp" ) ,
208
+ volatile : false ,
209
+ tmpfs : None ,
210
+ } ) )
211
+ }
185
212
}
213
+
186
214
fn mount ( & mut self , to : & Path ) -> Result < ( ) > {
187
215
let base_dirs = [ self . lower . clone ( ) , self . base . clone ( ) ] ;
216
+
217
+ // mount tmpfs if needed
218
+ if let Some ( tmpfs) = & self . tmpfs {
219
+ fs:: create_dir_all ( & tmpfs) ?;
220
+ if !self . is_tmpfs_mounted ( ) ? {
221
+ info ! ( "Mounting container upper tmpfs" ) ;
222
+ let tmpfs = Tmpfs :: new ( tmpfs) . size_bytes ( 4 * 1024 * 1024 * 1024 ) ;
223
+ tmpfs
224
+ . mount ( )
225
+ . map_err ( |e| anyhow ! ( "failed to mount tmpfs: {}" , e. to_string( ) ) ) ?;
226
+ }
227
+ }
228
+
229
+ // create the directories if they don't exist (work directory may be missing)
230
+ fs:: create_dir_all ( & self . upper ) ?;
231
+ fs:: create_dir_all ( & self . work ) ?;
232
+ fs:: create_dir_all ( & self . lower ) ?;
233
+
188
234
let mut overlay = Overlay :: writable (
189
235
// base_dirs variable contains the base and lower directories
190
236
base_dirs. iter ( ) . map ( |x| x. as_ref ( ) ) ,
191
237
self . upper . clone ( ) ,
192
238
self . work . clone ( ) ,
193
239
to,
194
240
) ;
195
- // create the directories if they don't exist (work directory may be missing)
196
- fs:: create_dir_all ( & self . work ) ?;
197
- fs:: create_dir_all ( & self . upper ) ?;
198
- fs:: create_dir_all ( & self . lower ) ?;
199
241
// check overlay usability
200
242
load_overlayfs_support ( ) ?;
201
243
if self . volatile {
@@ -218,11 +260,28 @@ impl LayerManager for OverlayFS {
218
260
is_mounted ( target, OsStr :: new ( "overlay" ) )
219
261
}
220
262
263
+ fn is_tmpfs ( & self ) -> bool {
264
+ self . tmpfs . is_some ( )
265
+ }
266
+
267
+ fn is_tmpfs_mounted ( & self ) -> Result < bool > {
268
+ if let Some ( tmpfs) = & self . tmpfs {
269
+ is_mounted ( & path:: absolute ( & tmpfs) ?, OsStr :: new ( "tmpfs" ) )
270
+ } else {
271
+ bail ! ( "the container does not use tmpfs" )
272
+ }
273
+ }
274
+
221
275
fn rollback ( & mut self ) -> Result < ( ) > {
222
- fs:: remove_dir_all ( & self . upper ) ?;
223
- fs:: remove_dir_all ( & self . work ) ?;
224
- fs:: create_dir ( & self . upper ) ?;
225
- fs:: create_dir ( & self . work ) ?;
276
+ if self . is_tmpfs ( ) {
277
+ // for mounted tmpfs containers, simply un-mount the tmpfs
278
+ self . unmount_tmpfs ( ) ?;
279
+ } else {
280
+ fs:: remove_dir_all ( & self . upper ) ?;
281
+ fs:: remove_dir_all ( & self . work ) ?;
282
+ fs:: create_dir ( & self . upper ) ?;
283
+ fs:: create_dir ( & self . work ) ?;
284
+ }
226
285
227
286
Ok ( ( ) )
228
287
}
@@ -261,6 +320,16 @@ impl LayerManager for OverlayFS {
261
320
Ok ( ( ) )
262
321
}
263
322
323
+ fn unmount_tmpfs ( & self ) -> Result < ( ) > {
324
+ if let Some ( tmpfs) = & self . tmpfs {
325
+ if self . is_tmpfs_mounted ( ) ? {
326
+ info ! ( "Un-mounting tmpfs ..." ) ;
327
+ umount2 ( tmpfs, MntFlags :: MNT_DETACH ) ?;
328
+ }
329
+ }
330
+ Ok ( ( ) )
331
+ }
332
+
264
333
fn get_config_layer ( & mut self ) -> Result < PathBuf > {
265
334
Ok ( self . lower . clone ( ) )
266
335
}
@@ -270,6 +339,9 @@ impl LayerManager for OverlayFS {
270
339
}
271
340
272
341
fn destroy ( & mut self ) -> Result < ( ) > {
342
+ if self . is_tmpfs ( ) {
343
+ self . unmount_tmpfs ( ) ?;
344
+ }
273
345
fs:: remove_dir_all ( & self . inst ) ?;
274
346
275
347
Ok ( ( ) )
@@ -351,14 +423,50 @@ fn sync_permission(from: &Path, to: &Path) -> Result<()> {
351
423
Ok ( ( ) )
352
424
}
353
425
426
+ fn rename_file ( from : & Path , to : & Path , overlay : & OverlayFS ) -> Result < ( ) > {
427
+ if overlay. is_tmpfs ( ) {
428
+ if to. symlink_metadata ( ) . is_ok ( ) {
429
+ if to. is_dir ( ) {
430
+ fs:: remove_dir_all ( to) ?;
431
+ } else {
432
+ fs:: remove_file ( to) ?;
433
+ }
434
+ }
435
+ if from. is_symlink ( ) {
436
+ std:: os:: unix:: fs:: symlink ( fs:: read_link ( from) ?, to) ?;
437
+ fs:: remove_file ( from) ?;
438
+ } else if from. is_file ( ) {
439
+ fs:: copy ( from, to) ?;
440
+ fs:: remove_file ( from) ?;
441
+ } else if from. is_dir ( ) {
442
+ fs:: create_dir_all ( to) ?;
443
+ fs:: set_permissions ( to, from. metadata ( ) ?. permissions ( ) ) ?;
444
+ for entry in fs:: read_dir ( from) ? {
445
+ let entry = entry?;
446
+ rename_file (
447
+ & from. join ( entry. file_name ( ) ) ,
448
+ & to. join ( entry. file_name ( ) ) ,
449
+ overlay,
450
+ ) ?;
451
+ }
452
+ fs:: remove_dir_all ( from) ?;
453
+ } else {
454
+ bail ! ( "unsupported file type" ) ;
455
+ }
456
+ } else {
457
+ fs:: rename ( from, to) ?;
458
+ }
459
+ Ok ( ( ) )
460
+ }
461
+
354
462
#[ inline]
355
463
fn overlay_exec_action ( action : & Diff , overlay : & OverlayFS ) -> Result < ( ) > {
356
464
match action {
357
465
Diff :: Symlink ( path) => {
358
466
let upper_path = overlay. upper . join ( path) ;
359
467
let lower_path = overlay. base . join ( path) ;
360
468
// Replace lower dir with upper
361
- fs :: rename ( upper_path, lower_path) ?;
469
+ rename_file ( & upper_path, & lower_path, overlay ) ?;
362
470
}
363
471
Diff :: OverrideDir ( path) => {
364
472
let upper_path = overlay. upper . join ( path) ;
@@ -371,7 +479,7 @@ fn overlay_exec_action(action: &Diff, overlay: &OverlayFS) -> Result<()> {
371
479
// If it's a file, then remove it as well
372
480
fs:: remove_file ( & lower_path) ?;
373
481
}
374
- fs :: rename ( upper_path, & lower_path) ?;
482
+ rename_file ( & upper_path, & lower_path, overlay ) ?;
375
483
}
376
484
Diff :: RenamedDir ( from, to) => {
377
485
// TODO: Implement copy down
@@ -381,7 +489,7 @@ fn overlay_exec_action(action: &Diff, overlay: &OverlayFS) -> Result<()> {
381
489
let to_path = overlay. base . join ( to) ;
382
490
// TODO: Merge files from upper to lower
383
491
// Replace lower dir with upper
384
- fs :: rename ( from_path, to_path) ?;
492
+ rename_file ( & from_path, & to_path, overlay ) ?;
385
493
}
386
494
Diff :: NewDir ( path) => {
387
495
let lower_path = overlay. base . join ( path) ;
@@ -408,7 +516,7 @@ fn overlay_exec_action(action: &Diff, overlay: &OverlayFS) -> Result<()> {
408
516
let upper_path = overlay. upper . join ( path) ;
409
517
let lower_path = overlay. base . join ( path) ;
410
518
// Move upper file to overwrite the lower
411
- fs :: rename ( upper_path, lower_path) ?;
519
+ rename_file ( & upper_path, & lower_path, overlay ) ?;
412
520
}
413
521
}
414
522
0 commit comments