// tarCreate creates a tarfile for a clickdeb, all files in the archive // belong to root (same as dpkg-deb) func tarCreate(tarname string, sourceDir string, fn tarExcludeFunc) error { w, err := os.Create(tarname) if err != nil { return err } defer w.Close() var compressor io.WriteCloser switch { case strings.HasSuffix(tarname, ".gz"): compressor, err = gzip.NewWriterLevel(w, 9) case strings.HasSuffix(tarname, ".xz"): compressor = newXZPipeWriter(w) default: return fmt.Errorf("unknown compression extension %s", tarname) } if err != nil { return err } defer compressor.Close() tarWriter := tar.NewWriter(compressor) defer tarWriter.Close() err = filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error { st, err := os.Lstat(path) if err != nil { return err } // check if we support this type if !st.Mode().IsRegular() && !helpers.IsSymlink(st.Mode()) && !st.Mode().IsDir() && !helpers.IsDevice(st.Mode()) { return fmt.Errorf("unsupported file type for %s", path) } // check our exclude function if fn != nil { if !fn(path) { return nil } } // huh? golang, come on! the symlink stuff is a bit complicated target, _ := os.Readlink(path) hdr, err := tar.FileInfoHeader(info, target) if err != nil { return err } // tar.FileInfoHeader does include the fact that its a // char/block device, but does not set major/minor :/ if helpers.IsDevice(st.Mode()) { major, minor, err := helpers.MajorMinor(st) if err != nil { return err } hdr.Devmajor = int64(major) hdr.Devminor = int64(minor) } // exclude "." relativePath := "." + path[len(sourceDir):] if relativePath == "." { return nil } // add header, note that all files belong to root hdr.Name = relativePath hdr.Uid = 0 hdr.Gid = 0 hdr.Uname = "root" hdr.Gname = "root" if err := tarWriter.WriteHeader(hdr); err != nil { return err } // add content if st.Mode().IsRegular() { f, err := os.Open(path) if err != nil { return err } defer f.Close() _, err = io.Copy(tarWriter, f) if err != nil { return err } } return nil }) return err }
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() { return os.Mkdir(dest, info.Mode()) } // handle char/block devices if helpers.IsDevice(info.Mode()) { return helpers.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 helpers.CopyFile(path, dest, helpers.CopyFlagDefault) }) }