@@ -39,9 +39,13 @@ import (
3939 exec "golang.org/x/sys/execabs"
4040)
4141
42+ type fsType string
43+
4244const (
43- metadataFileName = "metadata.db"
44- fsTypeExt4 = "ext4"
45+ metadataFileName = "metadata.db"
46+ fsTypeExt4 fsType = "ext4"
47+ fsTypeXFS fsType = "xfs"
48+ devmapperSnapshotFsType = "containerd.io/snapshot/devmapper/fstype"
4549)
4650
4751type closeFunc func () error
@@ -183,7 +187,13 @@ func (s *Snapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, er
183187 return err
184188 })
185189
186- return s .buildMounts (snap ), nil
190+ snapInfo , err := s .Stat (ctx , key )
191+ if err != nil {
192+ log .G (ctx ).WithError (err ).Errorf ("cannot retrieve snapshot info for key %s" , key )
193+ return nil , err
194+ }
195+
196+ return s .buildMounts (ctx , snap , fsType (snapInfo .Labels [devmapperSnapshotFsType ])), nil
187197}
188198
189199// Prepare creates thin device for an active snapshot identified by key
@@ -227,7 +237,7 @@ func (s *Snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
227237 log .G (ctx ).WithFields (logrus.Fields {"name" : name , "key" : key }).Debug ("commit" )
228238
229239 return s .withTransaction (ctx , true , func (ctx context.Context ) error {
230- id , _ , _ , err := storage .GetInfo (ctx , key )
240+ id , snapInfo , _ , err := storage .GetInfo (ctx , key )
231241 if err != nil {
232242 return err
233243 }
@@ -242,6 +252,15 @@ func (s *Snapshotter) Commit(ctx context.Context, name, key string, opts ...snap
242252 Size : size ,
243253 }
244254
255+ // Add file system type label if present. In case more than one file system
256+ // type is supported file system type from parent will be used for creating
257+ // snapshot.
258+ fsTypeActive := snapInfo .Labels [devmapperSnapshotFsType ]
259+ if fsTypeActive != "" {
260+ fsLabel := make (map [string ]string )
261+ fsLabel [devmapperSnapshotFsType ] = fsTypeActive
262+ opts = append (opts , snapshots .WithLabels (fsLabel ))
263+ }
245264 _ , err = storage .CommitActive (ctx , key , name , usage , opts ... )
246265 if err != nil {
247266 return err
@@ -351,6 +370,33 @@ func (s *Snapshotter) Close() error {
351370}
352371
353372func (s * Snapshotter ) createSnapshot (ctx context.Context , kind snapshots.Kind , key , parent string , opts ... snapshots.Opt ) ([]mount.Mount , error ) {
373+ var fileSystemType fsType
374+
375+ // For snapshots with no parents, we use file system type as configured in config.
376+ // For snapshots with parents, we inherit the file system type. We use the same
377+ // file system type derived here for building mount points later.
378+ fsLabel := make (map [string ]string )
379+ if len (parent ) == 0 {
380+ fileSystemType = s .config .FileSystemType
381+ } else {
382+ _ , snapInfo , _ , err := storage .GetInfo (ctx , parent )
383+ if err != nil {
384+ log .G (ctx ).Errorf ("failed to read snapshotInfo for %s" , parent )
385+ return nil , err
386+ }
387+ fileSystemType = fsType (snapInfo .Labels [devmapperSnapshotFsType ])
388+ if fileSystemType == "" {
389+ // For parent snapshots created without label support, we can assume that
390+ // they are ext4 type. Children of parents with no label for fsType will
391+ // now have correct label and committed snapshots from them will carry fs type
392+ // label. TODO: find out if it is better to update the parent's label with
393+ // fsType as ext4.
394+ fileSystemType = fsTypeExt4
395+ }
396+ }
397+ fsLabel [devmapperSnapshotFsType ] = string (fileSystemType )
398+ opts = append (opts , snapshots .WithLabels (fsLabel ))
399+
354400 snap , err := storage .CreateSnapshot (ctx , kind , key , parent , opts ... )
355401 if err != nil {
356402 return nil , err
@@ -366,7 +412,7 @@ func (s *Snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
366412 return nil , err
367413 }
368414
369- if err := mkfs (ctx , dmsetup .GetFullDevicePath (deviceName )); err != nil {
415+ if err := mkfs (ctx , s . config . FileSystemType , dmsetup .GetFullDevicePath (deviceName )); err != nil {
370416 status , sErr := dmsetup .Status (s .pool .poolName )
371417 if sErr != nil {
372418 multierror .Append (err , sErr )
@@ -380,16 +426,17 @@ func (s *Snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
380426 } else {
381427 parentDeviceName := s .getDeviceName (snap .ParentIDs [0 ])
382428 snapDeviceName := s .getDeviceName (snap .ID )
383- log .G (ctx ).Debugf ("creating snapshot device '%s' from '%s'" , snapDeviceName , parentDeviceName )
384429
385- err := s .pool .CreateSnapshotDevice (ctx , parentDeviceName , snapDeviceName , s .config .BaseImageSizeBytes )
430+ log .G (ctx ).Debugf ("creating snapshot device '%s' from '%s' with fsType: '%s'" , snapDeviceName , parentDeviceName , fileSystemType )
431+
432+ err = s .pool .CreateSnapshotDevice (ctx , parentDeviceName , snapDeviceName , s .config .BaseImageSizeBytes )
386433 if err != nil {
387434 log .G (ctx ).WithError (err ).Errorf ("failed to create snapshot device from parent %s" , parentDeviceName )
388435 return nil , err
389436 }
390437 }
391438
392- mounts := s .buildMounts (snap )
439+ mounts := s .buildMounts (ctx , snap , fileSystemType )
393440
394441 // Remove default directories not expected by the container image
395442 _ = mount .WithTempMount (ctx , mounts , func (root string ) error {
@@ -399,20 +446,35 @@ func (s *Snapshotter) createSnapshot(ctx context.Context, kind snapshots.Kind, k
399446 return mounts , nil
400447}
401448
402- // mkfs creates ext4 filesystem on the given devmapper device
403- func mkfs (ctx context.Context , path string ) error {
404- args := []string {
405- "-E" ,
406- // We don't want any zeroing in advance when running mkfs on thin devices (see "man mkfs.ext4")
407- "nodiscard,lazy_itable_init=0,lazy_journal_init=0" ,
408- path ,
449+ // mkfs creates filesystem on the given devmapper device based on type
450+ // specified in config.
451+ func mkfs (ctx context.Context , fs fsType , path string ) error {
452+ mkfsCommand := ""
453+ var args []string
454+
455+ switch fs {
456+ case fsTypeExt4 :
457+ mkfsCommand = "mkfs.ext4"
458+ args = []string {
459+ "-E" ,
460+ // We don't want any zeroing in advance when running mkfs on thin devices (see "man mkfs.ext4")
461+ "nodiscard,lazy_itable_init=0,lazy_journal_init=0" ,
462+ path ,
463+ }
464+ case fsTypeXFS :
465+ mkfsCommand = "mkfs.xfs"
466+ args = []string {
467+ path ,
468+ }
469+ default :
470+ return errors .New ("file system not supported" )
409471 }
410472
411- log .G (ctx ).Debugf ("mkfs.ext4 %s" , strings .Join (args , " " ))
412- b , err := exec .Command ("mkfs.ext4" , args ... ).CombinedOutput ()
473+ log .G (ctx ).Debugf ("%s %s" , mkfsCommand , strings .Join (args , " " ))
474+ b , err := exec .Command (mkfsCommand , args ... ).CombinedOutput ()
413475 out := string (b )
414476 if err != nil {
415- return errors .Wrapf (err , "mkfs.ext4 couldn't initialize %q: %s" , path , out )
477+ return errors .Wrapf (err , "%s couldn't initialize %q: %s" , mkfsCommand , path , out )
416478 }
417479
418480 log .G (ctx ).Debugf ("mkfs:\n %s" , out )
@@ -429,17 +491,21 @@ func (s *Snapshotter) getDevicePath(snap storage.Snapshot) string {
429491 return dmsetup .GetFullDevicePath (name )
430492}
431493
432- func (s * Snapshotter ) buildMounts (snap storage.Snapshot ) []mount.Mount {
494+ func (s * Snapshotter ) buildMounts (ctx context. Context , snap storage.Snapshot , fileSystemType fsType ) []mount.Mount {
433495 var options []string
434496
497+ if fileSystemType == "" {
498+ log .G (ctx ).Error ("File system type cannot be empty" )
499+ return nil
500+ }
435501 if snap .Kind != snapshots .KindActive {
436502 options = append (options , "ro" )
437503 }
438504
439505 mounts := []mount.Mount {
440506 {
441507 Source : s .getDevicePath (snap ),
442- Type : fsTypeExt4 ,
508+ Type : string ( fileSystemType ) ,
443509 Options : options ,
444510 },
445511 }
0 commit comments