// iterOp iterates over all the files found with the given glob, making the // basename (with the given prefix prepended) the target file in the given // target directory. It then performs op on that target file: either copying // from the globbed file to the target file, or removing the target file. // Directories are created as needed. Errors out with any of the things that // could go wrong with this, including a file found by glob not being a // regular file. func iterOp(op policyOp, glob, targetDir, prefix string) (err error) { if err = os.MkdirAll(targetDir, 0755); err != nil { return fmt.Errorf("unable to make %v directory: %v", targetDir, err) } files, err := filepath.Glob(glob) if err != nil { // filepath.Glob seems to not return errors ever right // now. This might be a bug in Go, or it might be by // design. Better play safe. return fmt.Errorf("unable to glob %v: %v", glob, err) } for _, file := range files { s, err := os.Lstat(file) if err != nil { return fmt.Errorf("unable to stat %v: %v", file, err) } if !s.Mode().IsRegular() { return fmt.Errorf("unable to do %s for %v: not a regular file", op, file) } targetFile := filepath.Join(targetDir, prefix+filepath.Base(file)) switch op { case remove: if err := os.Remove(targetFile); err != nil { return fmt.Errorf("unable to remove %v: %v", targetFile, err) } case install: // do the copy if err := osutil.CopyFile(file, targetFile, osutil.CopyFlagSync|osutil.CopyFlagOverwrite); err != nil { return err } default: return fmt.Errorf("unknown operation %s", op) } } return nil }
func customizeClassicChroot() error { // create the snappy mountpoint if err := os.MkdirAll(filepath.Join(dirs.ClassicDir, "snappy"), 0755); err != nil { return fmt.Errorf("failed to create snappy mount point: %s", err) } // copy configs for _, f := range []string{"hostname", "hosts", "timezone", "localtime"} { src := filepath.Join("/etc/", f) dst := filepath.Join(dirs.ClassicDir, "etc", f) if err := osutil.CopyFile(src, dst, osutil.CopyFlagPreserveAll); err != nil { return err } } // ensure daemons do not start if err := ioutil.WriteFile(filepath.Join(dirs.ClassicDir, "/usr/sbin/policy-rc.d"), policyRc, 0755); err != nil { return fmt.Errorf("failed to write policy-rc.d: %s", err) } // remove ubuntu user, will come from snappy OS if err := runInChroot(dirs.ClassicDir, "deluser", "ubuntu"); err != nil { return err } // install extra packages; make sure chroot can resolve DNS resolveDir := filepath.Join(dirs.ClassicDir, "/run/resolvconf/") if err := os.MkdirAll(resolveDir, 0755); err != nil { return fmt.Errorf("failed to create %s: %s", resolveDir, err) } src := filepath.Join(dirs.GlobalRootDir, "/run/resolvconf/resolv.conf") dst := filepath.Join(dirs.ClassicDir, "/run/resolvconf/") if err := osutil.CopyFile(src, dst, osutil.CopyFlagPreserveAll); err != nil { return err } // enable libnss-extrausers if err := runInChroot(dirs.ClassicDir, "apt-get", "install", "-y", "libnss-extrausers"); err != nil { return err } // this regexp adds "extrausers" after the passwd/group/shadow // lines in /etc/nsswitch.conf cmd := exec.Command("sed", "-i", "-r", "/^(passwd|group|shadow):/ s/$/ extrausers/", filepath.Join(dirs.ClassicDir, "/etc/nsswitch.conf")) if output, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("failed to enable libness-extrausers: %s", output) } // clean up cruft (bad lxd rootfs!) if output, err := exec.Command("sh", "-c", fmt.Sprintf("rm -rf %s/run/*", dirs.ClassicDir)).CombinedOutput(); err != nil { return fmt.Errorf("failed to cleanup classic /run dir: %s (%s)", err, output) } // Add hosts "sudo" group into the classic env grp, err := getgrnam("sudo") if err != nil { return fmt.Errorf("failed to get group info for the 'sudo' group: %s", err) } for _, sudoUser := range grp.Mem { // We need to use "runInClassicEnv" so that we get the // bind mount of the /var/lib/extrausers directory. // Without that the "SUDO_USER" will not exist in the chroot if err := runInClassicEnv("usermod", "-a", "-G", "sudo", sudoUser); err != nil { if err := unmountBindMounts(); err != nil { // we can not return an error here if we // still have bind mounts in place, the // writable dir may still have /home mounted // so we can not remove /writable/classic // again panic("cannot undo bind mounts") } return fmt.Errorf("failed to add %s to the sudo users: %s", sudoUser, err) } } return nil }
func copyToBuildDir(sourceDir, buildDir string) error { sourceDir, err := filepath.Abs(sourceDir) if err != nil { return err } err = os.Remove(buildDir) if err != nil && !os.IsNotExist(err) { // this shouldn't happen, but. return err } // no umask here so that we get the permissions correct oldUmask := syscall.Umask(0) defer syscall.Umask(oldUmask) return filepath.Walk(sourceDir, func(path string, info os.FileInfo, errin error) (err error) { if errin != nil { return errin } relpath := path[len(sourceDir):] if relpath == "/DEBIAN" || shouldExclude(sourceDir, filepath.Base(path)) { if info.IsDir() { return filepath.SkipDir } return nil } dest := filepath.Join(buildDir, relpath) // handle dirs if info.IsDir() { if err := os.Mkdir(dest, info.Mode()); err != nil { return err } // ensure that premissions are preserved uid := int(info.Sys().(*syscall.Stat_t).Uid) gid := int(info.Sys().(*syscall.Stat_t).Gid) return os.Chown(dest, uid, gid) } // handle char/block devices if osutil.IsDevice(info.Mode()) { return osutil.CopySpecialFile(path, dest) } if (info.Mode() & os.ModeSymlink) != 0 { target, err := os.Readlink(path) if err != nil { return err } return os.Symlink(target, dest) } // fail if its unsupported if !info.Mode().IsRegular() { return fmt.Errorf("can not handle type of file %s", path) } // it's a file. Maybe we can link it? if os.Link(path, dest) == nil { // whee return nil } // sigh. ok, copy it is. return osutil.CopyFile(path, dest, osutil.CopyFlagDefault) }) }
// getTimezone returns the current timezone the system is set to or an error // if it can't. var getTimezone = func() (timezone string, err error) { tz, err := ioutil.ReadFile(tzFile()) if err != nil { return "", err } return strings.TrimSpace(string(tz)), nil } // setTimezone sets the specified timezone for the system, an error is returned // if it can't. var setTimezone = func(timezone string) error { if err := osutil.CopyFile(filepath.Join(tzZoneInfoPath, timezone), tzZoneInfoTarget, osutil.CopyFlagOverwrite); err != nil { return err } return osutil.AtomicWriteFile(tzFile(), []byte(timezone), 0644, osutil.AtomicWriteFollow) } func getPassthrough(rootDir string) (pc []passthroughConfig, err error) { filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { if info.IsDir() { return nil } content, err := ioutil.ReadFile(path) if err != nil { return err }