in internal/pkg/agent/application/upgrade/step_unpack.go [317:462]
func untar(log *logger.Logger, archivePath, dataDir string, flavor string) (UnpackResult, error) {
var versionedHome string
var rootDir string
var hash string
// Look up manifest in the archive and prepare path mappings, if any
pm := pathMapper{}
metadata, err := getPackageMetadataFromTar(archivePath)
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 {
// set the path mappings
pm.mappings = metadata.manifest.Package.PathMappings
versionedHome = filepath.FromSlash(pm.Map(metadata.manifest.Package.VersionedHome))
registry = metadata.manifest.Package.Flavors
} else {
// set default value of versioned home if it wasn't set by reading the manifest
versionedHome = createVersionedHomeFromHash(metadata.hash)
}
skipFn, err := skipFnFromTar(log, archivePath, flavor, registry)
if err != nil {
return UnpackResult{}, err
}
r, err := os.Open(archivePath)
if err != nil {
return UnpackResult{}, errors.New(fmt.Sprintf("artifact for 'elastic-agent' could not be found at '%s'", archivePath), errors.TypeFilesystem, errors.M(errors.MetaKeyPath, archivePath))
}
defer r.Close()
zr, err := gzip.NewReader(r)
if err != nil {
return UnpackResult{}, errors.New("requires gzip-compressed body", err, errors.TypeFilesystem)
}
tr := tar.NewReader(zr)
fileNamePrefix := getFileNamePrefix(archivePath)
// go through all the content of a tar archive
// if elastic-agent.active.commit file is found, get commit of the version unpacked
// otherwise copy everything inside data directory (everything related to new version),
// pieces outside of data we already have and should not be overwritten as they are usually configs
for {
f, err := tr.Next()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return UnpackResult{}, err
}
if !validFileName(f.Name) {
return UnpackResult{}, errors.New("tar contained invalid filename: %q", f.Name, errors.TypeFilesystem, errors.M(errors.MetaKeyPath, f.Name))
}
fileName := strings.TrimPrefix(f.Name, fileNamePrefix)
if fileName == agentCommitFile {
// we already loaded the hash, skip this one
continue
}
// map the filename
fileName = pm.Map(fileName)
// we should check that the path is a local one but since we discard anything that is not under "data/" we can
// skip the additional check
// skip everything outside data/
if !strings.HasPrefix(fileName, "data/") {
continue
}
if skipFn(fileName) {
continue
}
rel := filepath.FromSlash(strings.TrimPrefix(fileName, "data/"))
abs := filepath.Join(dataDir, rel)
// find the root dir
if currentDir := filepath.Dir(abs); rootDir == "" || len(filepath.Dir(rootDir)) > len(currentDir) {
rootDir = currentDir
}
fi := f.FileInfo()
mode := fi.Mode()
switch {
case mode.IsRegular():
log.Debugw("Unpacking file", "archive", "tar", "file.path", abs)
// create non-existing containing folders with 0750 permissions right now, we'll fix the permission of each
// directory as we come across them while processing the other package entries
if err = os.MkdirAll(filepath.Dir(abs), 0750); err != nil {
return UnpackResult{}, errors.New(err, "TarInstaller: creating directory for file "+abs, errors.TypeFilesystem, errors.M(errors.MetaKeyPath, abs))
}
// remove any world permissions from the file
wf, err := os.OpenFile(abs, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode.Perm()&0770)
if err != nil {
return UnpackResult{}, errors.New(err, "TarInstaller: creating file "+abs, errors.TypeFilesystem, errors.M(errors.MetaKeyPath, abs))
}
//nolint:gosec // legacy
_, err = io.Copy(wf, tr)
if closeErr := wf.Close(); closeErr != nil && err == nil {
err = closeErr
}
if err != nil {
return UnpackResult{}, fmt.Errorf("TarInstaller: error writing to %s: %w", abs, err)
}
case mode.IsDir():
log.Debugw("Unpacking directory", "archive", "tar", "file.path", abs)
// check if the directory already exists
_, err = os.Stat(abs)
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(abs, mode.Perm()&0770); err != nil {
return UnpackResult{}, errors.New(err, "TarInstaller: creating directory for file "+abs, errors.TypeFilesystem, errors.M(errors.MetaKeyPath, abs))
}
} else if err != nil {
return UnpackResult{}, errors.New(err, "TarInstaller: stat() directory for file "+abs, errors.TypeFilesystem, errors.M(errors.MetaKeyPath, abs))
} else {
// directory already exists, set the appropriate permissions
err = os.Chmod(abs, mode.Perm()&0770)
if err != nil {
return UnpackResult{}, errors.New(err, fmt.Sprintf("TarInstaller: setting permissions %O for directory %q", mode.Perm()&0770, abs), errors.TypeFilesystem, errors.M(errors.MetaKeyPath, abs))
}
}
default:
return UnpackResult{}, errors.New(fmt.Sprintf("tar file entry %s contained unsupported file type %v", fileName, mode), errors.TypeFilesystem, errors.M(errors.MetaKeyPath, fileName))
}
}
return UnpackResult{
Hash: hash,
VersionedHome: versionedHome,
}, nil
}