Exemple #1
0
// 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
}
Exemple #2
0
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
}
Exemple #3
0
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)
	})
}
Exemple #4
0
// 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
		}