func unzip()

in internal/pkg/agent/application/upgrade/step_unpack.go [81:207]


func unzip(log *logger.Logger, archivePath, dataDir string, flavor string) (UnpackResult, error) {
	var hash, rootDir string
	r, err := zip.OpenReader(archivePath)
	if err != nil {
		return UnpackResult{}, err
	}
	defer r.Close()

	fileNamePrefix := strings.TrimSuffix(filepath.Base(archivePath), ".zip") + "/" // omitting `elastic-agent-{version}-{os}-{arch}/` in filename

	pm := pathMapper{}
	var versionedHome string

	metadata, err := getPackageMetadataFromZipReader(r, fileNamePrefix)
	if err != nil {
		return UnpackResult{}, fmt.Errorf("retrieving package metadata from %q: %w", archivePath, err)
	}

	hash = metadata.hash[:hashLen]
	var registry map[string][]string
	if metadata.manifest != nil {
		pm.mappings = metadata.manifest.Package.PathMappings
		versionedHome = filepath.FromSlash(pm.Map(metadata.manifest.Package.VersionedHome))
		registry = metadata.manifest.Package.Flavors
	} else {
		// if at this point we didn't load the manifest, set the versioned to the backup value
		versionedHome = createVersionedHomeFromHash(hash)
	}

	skipFn, err := skipFnFromZip(log, r, flavor, fileNamePrefix, createVersionedHomeFromHash(hash), registry)
	if err != nil {
		return UnpackResult{}, err
	}

	unpackFile := func(f *zip.File) (err error) {
		rc, err := f.Open()
		if err != nil {
			return err
		}
		defer func() {
			if cerr := rc.Close(); cerr != nil {
				err = goerrors.Join(err, cerr)
			}
		}()

		fileName := strings.TrimPrefix(f.Name, fileNamePrefix)
		if fileName == agentCommitFile {
			// we already loaded the hash, skip this one
			return nil
		}

		mappedPackagePath := pm.Map(fileName)

		// skip everything outside data/
		if !strings.HasPrefix(mappedPackagePath, "data/") {
			return nil
		}

		dstPath := strings.TrimPrefix(mappedPackagePath, "data/")
		dstPath = filepath.Join(dataDir, dstPath)

		if skipFn(dstPath) {
			return nil
		}

		if f.FileInfo().IsDir() {
			log.Debugw("Unpacking directory", "archive", "zip", "file.path", dstPath)
			// check if the directory already exists
			_, err = os.Stat(dstPath)
			if errors.Is(err, fs.ErrNotExist) {
				// the directory does not exist, create it and any non-existing parent directory with the same permissions
				if err := os.MkdirAll(dstPath, f.Mode().Perm()&0770); err != nil {
					return fmt.Errorf("creating directory %q: %w", dstPath, err)
				}
			} else if err != nil {
				return fmt.Errorf("stat() directory %q: %w", dstPath, err)
			} else {
				// directory already exists, set the appropriate permissions
				err = os.Chmod(dstPath, f.Mode().Perm()&0770)
				if err != nil {
					return fmt.Errorf("setting permissions %O for directory %q: %w", f.Mode().Perm()&0770, dstPath, err)
				}
			}

			_ = os.MkdirAll(dstPath, f.Mode()&0770)
		} else {
			log.Debugw("Unpacking file", "archive", "zip", "file.path", dstPath)
			// create non-existing containing folders with 0770 permissions right now, we'll fix the permission of each
			// directory as we come across them while processing the other package entries
			_ = os.MkdirAll(filepath.Dir(dstPath), 0770)
			f, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()&0770)
			if err != nil {
				return err
			}
			defer func() {
				if cerr := f.Close(); cerr != nil {
					err = goerrors.Join(err, cerr)
				}
			}()

			//nolint:gosec // legacy
			if _, err = io.Copy(f, rc); err != nil {
				return err
			}
		}
		return nil
	}

	for _, f := range r.File {
		if rootDir == "" && filepath.Base(f.Name) == filepath.Dir(f.Name) {
			// skip top level files
			continue
		}
		if currentDir := filepath.Dir(f.Name); rootDir == "" || len(currentDir) < len(rootDir) {
			rootDir = currentDir
		}

		if err := unpackFile(f); err != nil {
			return UnpackResult{}, err
		}
	}

	return UnpackResult{
		Hash:          hash,
		VersionedHome: versionedHome,
	}, nil
}