github.com/tri-adam/singularity@v3.1.1+incompatible/internal/pkg/util/fs/layout/layer/underlay/underlay.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 underlay 7 8 import ( 9 "fmt" 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "sort" 14 "strings" 15 "syscall" 16 17 "github.com/sylabs/singularity/internal/pkg/sylog" 18 19 "github.com/sylabs/singularity/internal/pkg/util/fs/layout" 20 "github.com/sylabs/singularity/internal/pkg/util/fs/mount" 21 ) 22 23 const underlayDir = "/underlay" 24 25 type pathLen struct { 26 path string 27 len uint16 28 } 29 30 // Underlay layer manager 31 type Underlay struct { 32 session *layout.Session 33 } 34 35 // New creates and returns an overlay layer manager 36 func New() *Underlay { 37 return &Underlay{} 38 } 39 40 // Add adds required directory in session layout 41 func (u *Underlay) Add(session *layout.Session, system *mount.System) error { 42 u.session = session 43 if err := u.session.AddDir(underlayDir); err != nil { 44 return err 45 } 46 return system.RunBeforeTag(mount.PreLayerTag, u.createUnderlay) 47 } 48 49 func (u *Underlay) createUnderlay(system *mount.System) error { 50 points := system.Points.GetByTag(mount.RootfsTag) 51 if len(points) <= 0 { 52 return fmt.Errorf("no root fs image found") 53 } 54 return u.createLayer(points[0].Destination, system) 55 } 56 57 // createLayer creates underlay layer based on content of root filesystem 58 func (u *Underlay) createLayer(rootFsPath string, system *mount.System) error { 59 st := new(syscall.Stat_t) 60 points := system.Points 61 createdPath := make([]pathLen, 0) 62 63 sessionDir := u.session.Path() 64 for _, tag := range mount.GetTagList() { 65 for _, point := range points.GetByTag(tag) { 66 flags, _ := mount.ConvertOptions(point.Options) 67 if flags&syscall.MS_REMOUNT != 0 { 68 continue 69 } 70 if strings.HasPrefix(point.Destination, sessionDir) { 71 continue 72 } 73 if err := syscall.Stat(rootFsPath+point.Destination, st); err == nil { 74 continue 75 } 76 if err := syscall.Stat(point.Source, st); err != nil { 77 sylog.Warningf("skipping mount of %s: %s", point.Source, err) 78 continue 79 } 80 dst := underlayDir + point.Destination 81 if _, err := u.session.GetPath(dst); err == nil { 82 continue 83 } 84 switch st.Mode & syscall.S_IFMT { 85 case syscall.S_IFDIR: 86 if err := u.session.AddDir(dst); err != nil { 87 return err 88 } 89 default: 90 if err := u.session.AddFile(dst, nil); err != nil { 91 return err 92 } 93 } 94 createdPath = append(createdPath, pathLen{path: point.Destination, len: uint16(strings.Count(point.Destination, "/"))}) 95 } 96 } 97 98 sort.SliceStable(createdPath, func(i, j int) bool { return createdPath[i].len < createdPath[j].len }) 99 100 for _, pl := range createdPath { 101 splitted := strings.Split(filepath.Dir(pl.path), string(os.PathSeparator)) 102 l := len(splitted) 103 p := "" 104 for i := 1; i < l; i++ { 105 s := splitted[i : i+1][0] 106 p += "/" + s 107 if s != "" { 108 if _, err := u.session.GetPath(p); err != nil { 109 if err := u.session.AddDir(p); err != nil { 110 return err 111 } 112 } 113 if err := u.duplicateDir(p, system, pl.path); err != nil { 114 return err 115 } 116 } 117 } 118 } 119 120 if err := u.duplicateDir("/", system, ""); err != nil { 121 return err 122 } 123 124 flags := uintptr(syscall.MS_BIND | syscall.MS_REC | syscall.MS_RDONLY) 125 path, _ := u.session.GetPath(underlayDir) 126 127 err := system.Points.AddBind(mount.LayerTag, path, u.session.FinalPath(), flags) 128 if err != nil { 129 return err 130 } 131 err = system.Points.AddRemount(mount.LayerTag, u.session.FinalPath(), flags) 132 if err != nil { 133 return err 134 } 135 136 return u.session.Update() 137 } 138 139 func (u *Underlay) duplicateDir(dir string, system *mount.System, existingPath string) error { 140 binds := 0 141 path := filepath.Clean(u.session.RootFsPath() + dir) 142 files, err := ioutil.ReadDir(path) 143 if err != nil { 144 // directory doesn't exists, nothing to duplicate 145 return nil 146 } 147 for _, file := range files { 148 dst := filepath.Join(underlayDir+dir, file.Name()) 149 src := filepath.Join(path, file.Name()) 150 151 // no error means entry is already created 152 if _, err := u.session.GetPath(dst); err == nil { 153 continue 154 } 155 if file.IsDir() { 156 if err := u.session.AddDir(dst); err != nil { 157 return fmt.Errorf("can't add directory %s to underlay: %s", dst, err) 158 } 159 dst, _ = u.session.GetPath(dst) 160 if err := system.Points.AddBind(mount.PreLayerTag, src, dst, syscall.MS_BIND); err != nil { 161 return fmt.Errorf("can't add bind mount point: %s", err) 162 } 163 binds++ 164 } else if file.Mode()&os.ModeSymlink != 0 { 165 tgt, err := os.Readlink(src) 166 if err != nil { 167 return fmt.Errorf("can't read symlink information for %s: %s", src, err) 168 } 169 if err := u.session.AddSymlink(dst, tgt); err != nil { 170 return fmt.Errorf("can't add symlink: %s", err) 171 } 172 } else { 173 if err := u.session.AddFile(dst, nil); err != nil { 174 return fmt.Errorf("can't add directory %s to underlay: %s", dst, err) 175 } 176 dst, _ = u.session.GetPath(dst) 177 if err := system.Points.AddBind(mount.PreLayerTag, src, dst, syscall.MS_BIND); err != nil { 178 return fmt.Errorf("can't add bind mount point: %s", err) 179 } 180 binds++ 181 } 182 } 183 if binds > 50 && existingPath != "" { 184 sylog.Warningf("underlay of %s required more than 50 (%d) bind mounts", existingPath, binds) 185 } 186 return nil 187 }