blob: ceae7a23576284571e9fd94ea9982c9d8e0509d2 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android-base/file.h>
#include <android-base/logging.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <ziparchive/zip_archive.h>
#include <memory>
#include <string>
#include "apex_file.h"
#include "string_log.h"
namespace android {
namespace apex {
ApexFile::ApexFile(ApexFile&& other)
: apex_path_(other.apex_path_),
flattened_(other.flattened_),
image_offset_(other.image_offset_),
image_size_(other.image_size_),
manifest_(other.manifest_),
handle_(other.handle_) {
other.handle_ = nullptr;
}
StatusOr<std::unique_ptr<ApexFile>> ApexFile::Open(
const std::string& apex_path) {
std::unique_ptr<ApexFile> ret(new ApexFile(apex_path));
std::string error_msg;
if (ret->OpenInternal(&error_msg) < 0) {
return StatusOr<std::unique_ptr<ApexFile>>::MakeError(error_msg);
}
return StatusOr<std::unique_ptr<ApexFile>>(std::move(ret));
}
ApexFile::~ApexFile() {
if (handle_ != nullptr) {
CloseArchive(handle_);
}
}
static constexpr const char* kImageFilename = "apex_payload.img";
static constexpr const char* kManifestFilename = "apex_manifest.json";
// Tests if <path>/apex_manifest.json file exists.
static bool isFlattenedApex(const std::string& path) {
struct stat buf;
const std::string manifest = path + "/" + kManifestFilename;
if (stat(manifest.c_str(), &buf) != 0) {
if (errno == ENOENT) {
return false;
}
// If the APEX is there but not a flatttened apex, the final component
// of path will be a file, and stat will complain that it's not a directory.
// We are OK with that to avoid two stat calls.
if (errno != ENOTDIR) {
PLOG(ERROR) << "Failed to stat " << path;
}
return false;
}
if (!S_ISREG(buf.st_mode)) {
return false;
}
return true;
}
int ApexFile::OpenInternal(std::string* error_msg) {
if (handle_ != nullptr) {
// Already opened.
return 0;
}
if (isFlattenedApex(apex_path_)) {
image_offset_ = 0;
image_size_ = 0;
const std::string manifest_path = apex_path_ + "/" + kManifestFilename;
if (!android::base::ReadFileToString(manifest_path, &manifest_)) {
*error_msg = StringLog()
<< "Failed to read manifest file: " << manifest_path;
return -1;
}
flattened_ = true;
return 0;
}
flattened_ = false;
int ret = OpenArchive(apex_path_.c_str(), &handle_);
if (ret < 0) {
*error_msg = StringLog() << "Failed to open package " << apex_path_ << ": "
<< ErrorCodeString(ret);
return ret;
}
// Locate the mountable image within the zipfile and store offset and size.
ZipEntry entry;
ret = FindEntry(handle_, ZipString(kImageFilename), &entry);
if (ret < 0) {
*error_msg = StringLog() << "Could not find entry \"" << kImageFilename
<< "\" in package " << apex_path_ << ": "
<< ErrorCodeString(ret);
return ret;
}
image_offset_ = entry.offset;
image_size_ = entry.uncompressed_length;
ret = FindEntry(handle_, ZipString(kManifestFilename), &entry);
if (ret < 0) {
*error_msg = StringLog() << "Could not find entry \"" << kManifestFilename
<< "\" in package " << apex_path_ << ": "
<< ErrorCodeString(ret);
return ret;
}
uint32_t length = entry.uncompressed_length;
manifest_.resize(length, '\0');
ret = ExtractToMemory(handle_, &entry,
reinterpret_cast<uint8_t*>(&(manifest_)[0]), length);
if (ret != 0) {
*error_msg = StringLog() << "Failed to extract manifest from package "
<< apex_path_ << ": " << ErrorCodeString(ret);
return ret;
}
return 0;
}
} // namespace apex
} // namespace android