github.com/apptainer/singularity@v3.1.1+incompatible/internal/pkg/util/fs/layout/layer/overlay/overlay.go (about)

     1  // Copyright (c) 2018, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package overlay
     7  
     8  import (
     9  	"fmt"
    10  	"path/filepath"
    11  	"strings"
    12  	"syscall"
    13  
    14  	"github.com/sylabs/singularity/internal/pkg/sylog"
    15  	"github.com/sylabs/singularity/internal/pkg/util/fs"
    16  	"github.com/sylabs/singularity/internal/pkg/util/fs/layout"
    17  	"github.com/sylabs/singularity/internal/pkg/util/fs/mount"
    18  )
    19  
    20  const (
    21  	lowerDir = "/overlay-lowerdir"
    22  	upperDir = "/overlay-upperdir"
    23  	workDir  = "/overlay-workdir"
    24  )
    25  
    26  // Overlay layer manager
    27  type Overlay struct {
    28  	session   *layout.Session
    29  	lowerDirs []string
    30  	upperDir  string
    31  	workDir   string
    32  }
    33  
    34  // New creates and returns an overlay layer manager
    35  func New() *Overlay {
    36  	return &Overlay{}
    37  }
    38  
    39  // Add adds required directory in session layout
    40  func (o *Overlay) Add(session *layout.Session, system *mount.System) error {
    41  	o.session = session
    42  	if err := o.session.AddDir(lowerDir); err != nil {
    43  		return err
    44  	}
    45  	if o.lowerDirs == nil {
    46  		o.lowerDirs = make([]string, 0)
    47  	}
    48  	path, _ := o.session.GetPath(lowerDir)
    49  	o.lowerDirs = append(o.lowerDirs, path)
    50  
    51  	return system.RunBeforeTag(mount.LayerTag, o.createOverlay)
    52  }
    53  
    54  func (o *Overlay) createOverlay(system *mount.System) error {
    55  	flags := uintptr(syscall.MS_NODEV)
    56  	o.lowerDirs = append(o.lowerDirs, o.session.RootFsPath())
    57  
    58  	lowerdir := strings.Join(o.lowerDirs, ":")
    59  	err := system.Points.AddOverlay(mount.LayerTag, o.session.FinalPath(), flags, lowerdir, o.upperDir, o.workDir)
    60  	if err != nil {
    61  		return err
    62  	}
    63  
    64  	points := system.Points.GetByTag(mount.RootfsTag)
    65  	if len(points) <= 0 {
    66  		return fmt.Errorf("no root fs image found")
    67  	}
    68  	return o.createLayer(points[0].Destination, system)
    69  }
    70  
    71  // AddLowerDir adds a lower directory to overlay mount
    72  func (o *Overlay) AddLowerDir(path string) error {
    73  	o.lowerDirs = append([]string{path}, o.lowerDirs...)
    74  	return nil
    75  }
    76  
    77  // SetUpperDir sets upper directory to overlay mount
    78  func (o *Overlay) SetUpperDir(path string) error {
    79  	if o.upperDir != "" {
    80  		return fmt.Errorf("upper directory was already set")
    81  	}
    82  	o.upperDir = path
    83  	return nil
    84  }
    85  
    86  // GetUpperDir returns upper directory path
    87  func (o *Overlay) GetUpperDir() string {
    88  	return o.upperDir
    89  }
    90  
    91  // SetWorkDir sets work directory to overlay mount
    92  func (o *Overlay) SetWorkDir(path string) error {
    93  	if o.workDir != "" {
    94  		return fmt.Errorf("upper directory was already set")
    95  	}
    96  	o.workDir = path
    97  	return nil
    98  }
    99  
   100  // GetWorkDir returns work directory path
   101  func (o *Overlay) GetWorkDir() string {
   102  	return o.workDir
   103  }
   104  
   105  // createLayer creates overlay layer based on content of root filesystem
   106  // given by rootFsPath
   107  func (o *Overlay) createLayer(rootFsPath string, system *mount.System) error {
   108  	sessionDir := o.session.Path()
   109  	st := new(syscall.Stat_t)
   110  
   111  	if sessionDir == "" {
   112  		return fmt.Errorf("can't determine session path")
   113  	}
   114  	for _, tag := range mount.GetTagList() {
   115  		for _, point := range system.Points.GetByTag(tag) {
   116  			flags, _ := mount.ConvertOptions(point.Options)
   117  			if flags&syscall.MS_REMOUNT != 0 {
   118  				continue
   119  			}
   120  			if strings.HasPrefix(point.Destination, sessionDir) {
   121  				continue
   122  			}
   123  			p := rootFsPath + point.Destination
   124  			if syscall.Stat(p, st) == nil {
   125  				continue
   126  			}
   127  			if point.Type == "" {
   128  				if err := syscall.Stat(point.Source, st); err != nil {
   129  					sylog.Warningf("skipping mount of %s: %s", point.Source, err)
   130  					continue
   131  				}
   132  			}
   133  
   134  			dest := fs.EvalRelative(point.Destination, rootFsPath)
   135  
   136  			dest = filepath.Join(lowerDir, dest)
   137  			if _, err := o.session.GetPath(dest); err == nil {
   138  				continue
   139  			}
   140  			// don't exist create it in overlay
   141  			switch st.Mode & syscall.S_IFMT {
   142  			case syscall.S_IFDIR:
   143  				if err := o.session.AddDir(dest); err != nil {
   144  					return err
   145  				}
   146  			default:
   147  				if point.Type == "" {
   148  					if err := o.session.AddFile(dest, nil); err != nil {
   149  						return err
   150  					}
   151  				} else {
   152  					if err := o.session.AddDir(dest); err != nil {
   153  						return err
   154  					}
   155  				}
   156  			}
   157  		}
   158  	}
   159  	return o.session.Update()
   160  }