Beispiel #1
0
func (s *storeTestSuite) makeTestSnap(c *C, snapYamlContent string) string {
	fn := snaptest.MakeTestSnapWithFiles(c, snapYamlContent, nil)
	dst := filepath.Join(s.store.blobDir, filepath.Base(fn))
	err := osutil.CopyFile(fn, dst, 0)
	c.Assert(err, IsNil)
	return dst
}
Beispiel #2
0
func queueFile(src string) error {
	// refuse huge files, this is for assertions
	fi, err := os.Stat(src)
	if err != nil {
		return err
	}
	// 640kb ought be to enough for anyone
	if fi.Size() > 640*1024 {
		msg := fmt.Errorf("cannot queue %s, file size too big: %v", src, fi.Size())
		logger.Noticef("error: %v", msg)
		return msg
	}

	// ensure name is predictable, weak hash is ok
	hash, _, err := osutil.FileDigest(src, crypto.SHA3_384)
	if err != nil {
		return err
	}

	dst := filepath.Join(dirs.SnapAssertsSpoolDir, fmt.Sprintf("%s.assert", base64.URLEncoding.EncodeToString(hash)))
	if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
		return err
	}

	return osutil.CopyFile(src, dst, osutil.CopyFlagOverwrite)
}
Beispiel #3
0
// DownloadSnap downloads the snap with the given name and optionally revision  using the provided store and options. It returns the final full path of the snap inside the opts.TargetDir and a snap.Info for the snap.
func DownloadSnap(sto Store, name string, revision snap.Revision, opts *DownloadOptions) (targetPath string, info *snap.Info, err error) {
	if opts == nil {
		opts = &DownloadOptions{}
	}

	targetDir := opts.TargetDir
	if targetDir == "" {
		pwd, err := os.Getwd()
		if err != nil {
			return "", nil, err
		}
		targetDir = pwd
	}

	snap, err := sto.Snap(name, opts.Channel, opts.DevMode, revision, opts.User)
	if err != nil {
		return "", nil, fmt.Errorf("cannot find snap %q: %v", name, err)
	}
	pb := progress.NewTextProgress()
	tmpName, err := sto.Download(name, &snap.DownloadInfo, pb, opts.User)
	if err != nil {
		return "", nil, err
	}
	defer os.Remove(tmpName)

	baseName := filepath.Base(snap.MountFile())
	targetPath = filepath.Join(targetDir, baseName)
	if err := osutil.CopyFile(tmpName, targetPath, 0); err != nil {
		return "", nil, err
	}

	return targetPath, snap, nil
}
Beispiel #4
0
// InstallBootConfig installs the bootloader config from the gadget
// snap dir into the right place.
func InstallBootConfig(gadgetDir string) error {
	for _, bl := range []Bootloader{&grub{}, &uboot{}} {
		// the bootloader config file has to be root of the gadget snap
		gadgetFile := filepath.Join(gadgetDir, bl.Name()+".conf")
		if !osutil.FileExists(gadgetFile) {
			continue
		}

		systemFile := bl.ConfigFile()
		if err := os.MkdirAll(filepath.Dir(systemFile), 0755); err != nil {
			return err
		}
		return osutil.CopyFile(gadgetFile, systemFile, osutil.CopyFlagOverwrite)
	}

	return fmt.Errorf("cannot find boot config in %q", gadgetDir)
}
Beispiel #5
0
// Lowlevel copy the snap data (but never override existing data)
func copySnapDataDirectory(oldPath, newPath string) (err error) {
	if _, err := os.Stat(oldPath); err == nil {
		if err := trash(newPath); err != nil {
			return err
		}

		if _, err := os.Stat(newPath); err != nil {
			if err := osutil.CopyFile(oldPath, newPath, osutil.CopyFlagPreserveAll|osutil.CopyFlagSync); err != nil {
				return fmt.Errorf("cannot copy %q to %q: %v", oldPath, newPath, err)
			}
		}
	} else if !os.IsNotExist(err) {
		return err
	}

	return nil
}
Beispiel #6
0
func installCloudConfig(gadgetDir string) error {
	var err error

	cloudDir := filepath.Join(dirs.GlobalRootDir, "/etc/cloud")
	if err := os.MkdirAll(cloudDir, 0755); err != nil {
		return err
	}

	cloudConfig := filepath.Join(gadgetDir, "cloud.conf")
	if osutil.FileExists(cloudConfig) {
		dst := filepath.Join(cloudDir, "cloud.cfg")
		err = osutil.CopyFile(cloudConfig, dst, osutil.CopyFlagOverwrite)
	} else {
		dst := filepath.Join(cloudDir, "cloud-init.disabled")
		err = osutil.AtomicWriteFile(dst, nil, 0644, 0)
	}
	return err
}
Beispiel #7
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
}
Beispiel #8
0
func (s *Snap) Install(targetPath, mountDir string) error {

	// ensure mount-point and blob target dir.
	for _, dir := range []string{mountDir, filepath.Dir(targetPath)} {
		if err := os.MkdirAll(dir, 0755); err != nil {
			return err
		}
	}

	// This is required so that the tests can simulate a mounted
	// snap when we "install" a squashfs snap in the tests.
	// We can not mount it for real in the tests, so we just unpack
	// it to the location which is good enough for the tests.
	if os.Getenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") != "" {
		if err := s.Unpack("*", mountDir); err != nil {
			return err
		}
	}

	// nothing to do, happens on e.g. first-boot when we already
	// booted with the OS snap but its also in the seed.yaml
	if s.path == targetPath || osutil.FilesAreEqual(s.path, targetPath) {
		return nil
	}

	// try to (hard)link the file, but go on to trying to copy it
	// if it fails for whatever reason
	//
	// link(2) returns EPERM on filesystems that don't support
	// hard links (like vfat), so checking the error here doesn't
	// make sense vs just trying to copy it.
	if err := os.Link(s.path, targetPath); err == nil {
		return nil
	}

	return osutil.CopyFile(s.path, targetPath, osutil.CopyFlagPreserveAll|osutil.CopyFlagSync)
}
Beispiel #9
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 permissions 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("cannot 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)
	})
}
Beispiel #10
0
	}

	bsha3_384, _, err := osutil.FileDigest(partialTargetPath, crypto.SHA3_384)
	if err != nil {
		return err
	}
	sha3_384 := fmt.Sprintf("%x", bsha3_384)
	if targetSha3_384 != "" && sha3_384 != targetSha3_384 {
		if err := os.Remove(partialTargetPath); err != nil {
			logger.Noticef("failed to remove partial delta target %q: %s", partialTargetPath, err)
		}
		return HashError{name, sha3_384, targetSha3_384}
	}

	if err := os.Rename(partialTargetPath, targetPath); err != nil {
		return osutil.CopyFile(partialTargetPath, targetPath, 0)
	}

	return nil
}

// downloadAndApplyDelta downloads and then applies the delta to the current snap.
func (s *Store) downloadAndApplyDelta(name, targetPath string, downloadInfo *snap.DownloadInfo, pbar progress.Meter, user *auth.UserState) error {
	deltaInfo := &downloadInfo.Deltas[0]

	deltaPath := fmt.Sprintf("%s.%s-%d-to-%d.partial", targetPath, deltaInfo.Format, deltaInfo.FromRevision, deltaInfo.ToRevision)
	deltaName := filepath.Base(deltaPath)

	w, err := os.Create(deltaPath)
	if err != nil {
		return err
Beispiel #11
0
func (s *imageSuite) Download(name, targetFn string, downloadInfo *snap.DownloadInfo, pbar progress.Meter, user *auth.UserState) error {
	return osutil.CopyFile(s.downloadedSnaps[name], targetFn, 0)
}
Beispiel #12
0
func copyLocalSnapFile(snapPath, targetDir string, info *snap.Info) (dstPath string, err error) {
	dst := filepath.Join(targetDir, filepath.Base(info.MountFile()))
	return dst, osutil.CopyFile(snapPath, dst, 0)
}
Beispiel #13
0
func bootstrapToRootDir(sto Store, model *asserts.Model, opts *Options, local *localInfos) error {
	// FIXME: try to avoid doing this
	if opts.RootDir != "" {
		dirs.SetRootDir(opts.RootDir)
		defer dirs.SetRootDir("/")
	}

	// sanity check target
	if osutil.FileExists(dirs.SnapStateFile) {
		return fmt.Errorf("cannot bootstrap over existing system")
	}

	// TODO: developer database in home or use snapd (but need
	// a bit more API there, potential issues when crossing stores/series)
	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
		Backstore: asserts.NewMemoryBackstore(),
		Trusted:   sysdb.Trusted(),
	})
	if err != nil {
		return err
	}
	f := makeFetcher(sto, &DownloadOptions{}, db)

	if err := f.Save(model); err != nil {
		if !osutil.GetenvBool("UBUNTU_IMAGE_SKIP_COPY_UNVERIFIED_MODEL") {
			return fmt.Errorf("cannot fetch and check prerequisites for the model assertion: %v", err)
		} else {
			logger.Noticef("Cannot fetch and check prerequisites for the model assertion, it will not be copied into the image: %v", err)
			f.addedRefs = nil
		}
	}

	// put snaps in place
	if err := os.MkdirAll(dirs.SnapBlobDir, 0755); err != nil {
		return err
	}

	snapSeedDir := filepath.Join(dirs.SnapSeedDir, "snaps")
	assertSeedDir := filepath.Join(dirs.SnapSeedDir, "assertions")
	dlOpts := &DownloadOptions{
		TargetDir: snapSeedDir,
		Channel:   opts.Channel,
		DevMode:   false, // XXX: should this be true?
	}

	for _, d := range []string{snapSeedDir, assertSeedDir} {
		if err := os.MkdirAll(d, 0755); err != nil {
			return err
		}
	}

	snaps := []string{}
	// core,kernel,gadget first
	snaps = append(snaps, local.PreferLocal(defaultCore))
	snaps = append(snaps, local.PreferLocal(model.Kernel()))
	snaps = append(snaps, local.PreferLocal(model.Gadget()))
	// then required and the user requested stuff
	for _, snapName := range model.RequiredSnaps() {
		snaps = append(snaps, local.PreferLocal(snapName))
	}
	snaps = append(snaps, opts.Snaps...)

	seen := make(map[string]bool)
	downloadedSnapsInfo := map[string]*snap.Info{}
	var seedYaml snap.Seed
	for _, snapName := range snaps {
		name := local.Name(snapName)
		if seen[name] {
			fmt.Fprintf(Stdout, "%s already prepared, skipping\n", name)
			continue
		}

		if name != snapName {
			fmt.Fprintf(Stdout, "Copying %q (%s)\n", snapName, name)
		} else {
			fmt.Fprintf(Stdout, "Fetching %s\n", snapName)
		}

		fn, info, err := acquireSnap(sto, name, dlOpts, local)
		if err != nil {
			return err
		}

		seen[name] = true

		// if it comes from the store fetch the snap assertions too
		// TODO: support somehow including available assertions
		// also for local snaps
		if info.SnapID != "" {
			err = FetchAndCheckSnapAssertions(fn, info, f, db)
			if err != nil {
				return err
			}
		}

		typ := info.Type
		// kernel/os are required for booting
		if typ == snap.TypeKernel || typ == snap.TypeOS {
			dst := filepath.Join(dirs.SnapBlobDir, filepath.Base(fn))
			if err := osutil.CopyFile(fn, dst, 0); err != nil {
				return err
			}
			// store the snap.Info for kernel/os so
			// that the bootload can DTRT
			downloadedSnapsInfo[dst] = info
		}

		// set seed.yaml
		seedYaml.Snaps = append(seedYaml.Snaps, &snap.SeedSnap{
			Name:    info.Name(),
			SnapID:  info.SnapID, // cross-ref
			Channel: info.Channel,
			File:    filepath.Base(fn),
			DevMode: info.NeedsDevMode(),
			// no assertions for this snap were put in the seed
			Unasserted: info.SnapID == "",
		})
	}

	for _, aRef := range f.addedRefs {
		var afn string
		// the names don't matter in practice as long as they don't conflict
		if aRef.Type == asserts.ModelType {
			afn = "model"
		} else {
			afn = fmt.Sprintf("%s.%s", strings.Join(aRef.PrimaryKey, ","), aRef.Type.Name)
		}
		a, err := aRef.Resolve(db.Find)
		if err != nil {
			return fmt.Errorf("internal error: lost saved assertion")
		}
		err = ioutil.WriteFile(filepath.Join(assertSeedDir, afn), asserts.Encode(a), 0644)
		if err != nil {
			return err
		}
	}

	// TODO: add the refs as an assertions list of maps section to seed.yaml

	seedFn := filepath.Join(dirs.SnapSeedDir, "seed.yaml")
	if err := seedYaml.Write(seedFn); err != nil {
		return fmt.Errorf("cannot write seed.yaml: %s", err)
	}

	// now do the bootloader stuff
	if err := partition.InstallBootConfig(opts.GadgetUnpackDir); err != nil {
		return err
	}

	if err := setBootvars(downloadedSnapsInfo); err != nil {
		return err
	}

	// and the cloud-init things
	if err := installCloudConfig(opts.GadgetUnpackDir); err != nil {
		return err
	}

	return nil
}
Beispiel #14
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
		}