in xar/XarParser.cpp [230:348]
XarParserResult parseXarHeader(int fd) noexcept {
// Rewind the output fd to the beginning
if (::lseek(fd, 0, SEEK_SET)) {
return makeErrorResult(
XarParserErrorType::FILE_READ,
"File offset for " + std::to_string(fd) +
" could not be zeroed. errno: " + std::to_string(errno));
}
// Read entire header and enough extra bytes to include the squashfs magic
// number
std::vector<char> buf(kMaxHeaderSize + sizeof(kSquashfsMagic), 0);
auto res = tools::xar::readFull(fd, buf.data(), buf.size());
if (res <= 0) {
std::string errMsg = "";
if (errno != 0) {
errMsg = std::to_string(errno);
}
return makeErrorResult(
XarParserErrorType::FILE_READ,
"Failed to read bytes from fd: " + std::to_string(fd) +
" read returned " + std::to_string(res) + " with errno: " + errMsg);
}
buf.resize(res);
// Verify first line is always shebang
const auto lines = tools::xar::split(
/* delim= */ '\n', std::string(buf.data(), buf.size()));
auto currentLine = lines.begin();
if (currentLine == lines.end()) {
return makeErrorResult(
XarParserErrorType::UNEXPECTED_END_OF_FILE,
"Failed to get first line which should contain shebang");
} else if (currentLine->rfind(kShebang, 0) != 0) {
return makeErrorResult(XarParserErrorType::INVALID_SHEBANG);
}
currentLine++;
std::set<std::string> foundNames;
XarHeader xarHeader;
// Get OFFSET from second line. OFFSET is guaranteed to be first parameter.
if (currentLine == lines.end()) {
return makeErrorResult(
XarParserErrorType::UNEXPECTED_END_OF_FILE,
"Failed to get next line which should contain offset");
}
if (auto maybeError =
detail::parseLine(*currentLine, &xarHeader, &foundNames)) {
return XarParserResult(*maybeError);
}
if (foundNames.find(kOffsetName) == foundNames.end()) {
return makeErrorResult(
XarParserErrorType::MISSING_PARAMETERS,
"Expected" + std::string(kOffsetName) + " to be on first line");
}
currentLine++;
// Verify offset is less than or equal to kMaxHeaderSize to ensure that we've
// read the entire header. This is not part of the contract, but it's
// reasonable to have some sort of upper bound on header size.
if (xarHeader.offset > kMaxHeaderSize) {
return makeErrorResult(
XarParserErrorType::INVALID_OFFSET,
std::to_string(xarHeader.offset) +
" is greater than max header size of " +
std::to_string(kMaxHeaderSize));
}
// Read until kXarStop or the last line
for (; currentLine != lines.end() && *currentLine != kXarStop;
++currentLine) {
if (auto maybeError =
detail::parseLine(*currentLine, &xarHeader, &foundNames)) {
return XarParserResult(*maybeError);
}
}
if (currentLine == lines.end()) {
return makeErrorResult(
XarParserErrorType::UNEXPECTED_END_OF_FILE,
"Failed to find " + std::string(kXarStop));
}
// Check all required fields are set
const std::set<std::string> requiredParameters = {
kOffsetName, kVersion, kUuidName, kXarexecTarget};
std::vector<std::string> difference;
std::set_difference(
requiredParameters.begin(),
requiredParameters.end(),
foundNames.begin(),
foundNames.end(),
std::inserter(difference, difference.begin()));
if (!difference.empty()) {
std::string missingParamsString = std::accumulate(
std::begin(difference),
std::end(difference),
std::string(),
[](const std::string& ss, const std::string& s) {
return ss.empty() ? s : ss + ", " + s;
});
return makeErrorResult(
XarParserErrorType::MISSING_PARAMETERS, missingParamsString);
}
// Check for squashfs magic at OFFSET
if (std::memcmp(
&buf[xarHeader.offset], kSquashfsMagic, sizeof(kSquashfsMagic)) !=
0) {
return makeErrorResult(XarParserErrorType::INCORRECT_MAGIC);
}
return XarParserResult(xarHeader);
}