github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/gadget/internal/mkfs.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package internal 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "os" 26 "os/exec" 27 "path/filepath" 28 29 "github.com/snapcore/snapd/osutil" 30 ) 31 32 type MkfsFunc func(imgFile, label, contentsRootDir string) error 33 34 var ( 35 mkfsHandlers = map[string]MkfsFunc{ 36 "vfat": mkfsVfat, 37 "ext4": mkfsExt4, 38 } 39 ) 40 41 // Mkfs creates a filesystem of given type and provided label in the device or file. 42 func Mkfs(typ, img, label string) error { 43 return MkfsWithContent(typ, img, label, "") 44 } 45 46 // Mkfs creates a filesystem of given type and provided label in the device or file. 47 // The filesystem is populated with contents of contentRootDir. 48 func MkfsWithContent(typ, img, label, contentRootDir string) error { 49 h, ok := mkfsHandlers[typ] 50 if !ok { 51 return fmt.Errorf("cannot create unsupported filesystem %q", typ) 52 } 53 return h(img, label, contentRootDir) 54 } 55 56 // mkfsExt4 creates an EXT4 filesystem in given image file, with an optional 57 // filesystem label, and populates it with the contents of provided root 58 // directory. 59 func mkfsExt4(img, label, contentsRootDir string) error { 60 // Originally taken from ubuntu-image 61 // Switched to use mkfs defaults for https://bugs.launchpad.net/snappy/+bug/1878374 62 // For caveats/requirements in case we need support for older systems: 63 // https://github.com/snapcore/snapd/pull/6997#discussion_r293967140 64 mkfsArgs := []string{ 65 "mkfs.ext4", 66 // default usage type 67 "-T", "default", 68 } 69 if contentsRootDir != "" { 70 // mkfs.ext4 can populate the filesystem with contents of given 71 // root directory 72 // TODO: support e2fsprogs 1.42 without -d in Ubuntu 16.04 73 mkfsArgs = append(mkfsArgs, "-d", contentsRootDir) 74 } 75 if label != "" { 76 mkfsArgs = append(mkfsArgs, "-L", label) 77 } 78 mkfsArgs = append(mkfsArgs, img) 79 80 var cmd *exec.Cmd 81 if os.Geteuid() != 0 { 82 // run through fakeroot so that files are owned by root 83 cmd = exec.Command("fakeroot", mkfsArgs...) 84 } else { 85 // no need to fake it if we're already root 86 cmd = exec.Command(mkfsArgs[0], mkfsArgs[1:]...) 87 } 88 out, err := cmd.CombinedOutput() 89 if err != nil { 90 return osutil.OutputErr(out, err) 91 } 92 return nil 93 } 94 95 // mkfsVfat creates a VFAT filesystem in given image file, with an optional 96 // filesystem label, and populates it with the contents of provided root 97 // directory. 98 func mkfsVfat(img, label, contentsRootDir string) error { 99 // taken from ubuntu-image 100 mkfsArgs := []string{ 101 // 512B logical sector size 102 "-S", "512", 103 // 1 sector per cluster 104 "-s", "1", 105 // 32b FAT size 106 "-F", "32", 107 } 108 if label != "" { 109 mkfsArgs = append(mkfsArgs, "-n", label) 110 } 111 mkfsArgs = append(mkfsArgs, img) 112 113 cmd := exec.Command("mkfs.vfat", mkfsArgs...) 114 out, err := cmd.CombinedOutput() 115 if err != nil { 116 return osutil.OutputErr(out, err) 117 } 118 119 // if there is no content to copy we are done now 120 if contentsRootDir == "" { 121 return nil 122 } 123 124 // mkfs.vfat does not know how to populate the filesystem with contents, 125 // we need to do the work ourselves 126 127 fis, err := ioutil.ReadDir(contentsRootDir) 128 if err != nil { 129 return fmt.Errorf("cannot list directory contents: %v", err) 130 } 131 if len(fis) == 0 { 132 // nothing to copy to the image 133 return nil 134 } 135 136 mcopyArgs := make([]string, 0, 4+len(fis)) 137 mcopyArgs = append(mcopyArgs, 138 // recursive copy 139 "-s", 140 // image file 141 "-i", img) 142 for _, fi := range fis { 143 mcopyArgs = append(mcopyArgs, filepath.Join(contentsRootDir, fi.Name())) 144 } 145 mcopyArgs = append(mcopyArgs, 146 // place content at the / of the filesystem 147 "::") 148 149 cmd = exec.Command("mcopy", mcopyArgs...) 150 cmd.Env = os.Environ() 151 // skip mtools checks to avoid unnecessary warnings 152 cmd.Env = append(cmd.Env, "MTOOLS_SKIP_CHECK=1") 153 154 out, err = cmd.CombinedOutput() 155 if err != nil { 156 return fmt.Errorf("cannot populate vfat filesystem with contents: %v", osutil.OutputErr(out, err)) 157 } 158 return nil 159 }