diff --git a/Sources/ContainerizationArchive/ArchiveReader.swift b/Sources/ContainerizationArchive/ArchiveReader.swift index 3c291d1a5..9e254e6fb 100644 --- a/Sources/ContainerizationArchive/ArchiveReader.swift +++ b/Sources/ContainerizationArchive/ArchiveReader.swift @@ -398,7 +398,7 @@ extension ArchiveReader { } private func setFileAttributes(fd: Int32, entry: WriteEntry) { - fchmod(fd, entry.permissions & 0o777) + fchmod(fd, entry.permissions & 0o7777) if let owner = entry.owner, let group = entry.group { fchown(fd, owner, group) } diff --git a/Tests/ContainerizationArchiveTests/ArchiveReaderTests.swift b/Tests/ContainerizationArchiveTests/ArchiveReaderTests.swift index c7c0e563a..e2158d804 100644 --- a/Tests/ContainerizationArchiveTests/ArchiveReaderTests.swift +++ b/Tests/ContainerizationArchiveTests/ArchiveReaderTests.swift @@ -438,6 +438,36 @@ struct ArchiveReaderTests { #expect((perms & permMask) == 0o755, "Permissions should be preserved") } + @Test func preserveSpecialPermissionBits() throws { + let testDirectory = createTemporaryDirectory(baseName: "ArchiveReaderTests")! + let archiveURL = testDirectory.appendingPathComponent("special-permissions.tar") + let archiver = try ArchiveWriter(format: .paxRestricted, filter: .none, file: archiveURL) + + let writeEntry = WriteEntry() + writeEntry.path = "sticky" + writeEntry.fileType = .directory + writeEntry.permissions = 0o1777 + writeEntry.size = 0 + try archiver.writeEntry(entry: writeEntry, data: nil) + try archiver.finishEncoding() + + defer { try? FileManager.default.removeItem(at: testDirectory) } + + let extractDir = try createExtractionDirectory(name: "special-permissions") + defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) } + + let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL) + let rejectedPaths = try reader.extractContents(to: extractDir) + + #expect(rejectedPaths.isEmpty) + + let directoryPath = extractDir.appendingPathComponent("sticky").path + let attrs = try FileManager.default.attributesOfItem(atPath: directoryPath) + let perms = (attrs[.posixPermissions] as? NSNumber)?.uint16Value ?? 0 + let permMask: UInt16 = 0o7777 + #expect((perms & permMask) == 0o1777, "Special permission bits should be preserved") + } + // MARK: - Duplicate Entry Tests @Test func duplicateRegularFiles() throws {