Support following symlinks in tar when setting default kernel (#575)

## Type of Change
- [x] Bug fix

## Description
This PR checks if the found entry in the archive for the requested
kernel path is a symlink, and if so, follows the symlink. I've opened a
separate issue to track creating tests for kernel downloading here
https://github.com/apple/container/issues/574 and will create a
follow-up PR for those.

## Motivation and Context
This PR fixes an issue reported in
https://github.com/apple/container/issues/475 where installing a kernel
from a tar archive does not follow symlinks in the archive.

## Testing
- [x] Tested locally

Signed-off-by: Kathryn Baldauf <k_baldauf@apple.com>
This commit is contained in:
Kathryn Baldauf
2025-09-04 14:10:37 -07:00
committed by GitHub
parent 20de8504de
commit cb2f424420
+20 -7
View File
@@ -82,8 +82,7 @@ actor KernelService {
await progressUpdate?([
.setDescription("Unpacking kernel")
])
let archiveReader = try ArchiveReader(file: tarFile)
let kernelFile = try archiveReader.extractFile(from: kernelFilePath, to: tempDir)
let kernelFile = try self.extractFile(tarFile: tarFile, at: kernelFilePath, to: tempDir)
try self.installKernel(kernelFile: kernelFile, platform: platform)
if !FileManager.default.fileExists(atPath: tar.absoluteString) {
@@ -112,13 +111,27 @@ actor KernelService {
}
return Kernel(path: defaultKernelPath, platform: platform)
}
}
extension ArchiveReader {
fileprivate func extractFile(from: String, to directory: URL) throws -> URL {
let (_, data) = try self.extractFile(path: from)
private func extractFile(tarFile: URL, at: String, to directory: URL) throws -> URL {
var target = at
var archiveReader = try ArchiveReader(file: tarFile)
var (entry, data) = try archiveReader.extractFile(path: target)
// if the target file is a symlink, get the data for the actual file
if entry.fileType == .symbolicLink, let symlinkRelative = entry.symlinkTarget {
// the previous extractFile changes the underlying file pointer, so we need to reopen the file
// to ensure we traverse all the files in the archive
archiveReader = try ArchiveReader(file: tarFile)
let symlinkTarget = URL(filePath: target).deletingLastPathComponent().appending(path: symlinkRelative)
// standardize so that we remove any and all ../ and ./ in the path since symlink targets
// are relative paths to the target file from the symlink's parent dir itself
target = symlinkTarget.standardized.relativePath
let (_, targetData) = try archiveReader.extractFile(path: target)
data = targetData
}
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
let fileName = URL(filePath: from).lastPathComponent
let fileName = URL(filePath: target).lastPathComponent
let fileURL = directory.appendingPathComponent(fileName)
try data.write(to: fileURL, options: .atomic)
return fileURL