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 }
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) }
// 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 }
// 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) }
// 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 }
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 }
// 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 (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) }
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) }) }
} 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
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) }
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) }
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 }
// 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 }