blob: d25dd93708a9b84a30fde69f2b93e7204ee57771 [file] [log] [blame]
// Copyright 2017 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#include "android/snapshot/interface.h"
#include "android/base/files/PathUtils.h"
#include "android/base/Log.h"
#include "android/base/system/System.h"
#include "android/emulation/CpuAccelerator.h"
#include "android/snapshot/common.h"
#include "android/snapshot/Loader.h"
#include "android/snapshot/PathUtils.h"
#include "android/snapshot/Snapshotter.h"
#include "android/utils/debug.h"
#include "android/utils/path.h"
#include <fstream>
using android::base::System;
using android::snapshot::FailureReason;
using android::snapshot::OperationStatus;
using android::snapshot::Snapshotter;
AndroidSnapshotStatus androidSnapshot_prepareForLoading(const char* name) {
auto res = Snapshotter::get().prepareForLoading(name);
if (res == OperationStatus::Error) {
return SNAPSHOT_STATUS_ERROR_NOT_CHANGED;
} else {
return AndroidSnapshotStatus(res);
}
}
AndroidSnapshotStatus androidSnapshot_load(const char* name) {
return AndroidSnapshotStatus(Snapshotter::get().loadGeneric(name));
}
const char* androidSnapshot_loadedSnapshotFile() {
return Snapshotter::get().loadedSnapshotFile().c_str();
}
AndroidSnapshotStatus androidSnapshot_prepareForSaving(const char* name) {
return AndroidSnapshotStatus(Snapshotter::get().prepareForSaving(name));
}
AndroidSnapshotStatus androidSnapshot_save(const char* name) {
return AndroidSnapshotStatus(Snapshotter::get().saveGeneric(name));
}
void androidSnapshot_cancelSave() {
Snapshotter::get().cancelSave();
}
void androidSnapshot_delete(const char* name) {
Snapshotter::get().deleteSnapshot(name);
}
void androidSnapshot_invalidate(const char* name) {
Snapshotter::get().invalidateSnapshot(name);
}
bool androidSnapshot_areSavesSlow(const char* name) {
return Snapshotter::get().areSavesSlow(name);
}
int64_t androidSnapshot_lastLoadUptimeMs() {
return Snapshotter::get().lastLoadUptimeMs();
}
static int sStdoutLineConsumer(void* opaque, const char* buf, int strlen) {
(void)opaque;
printf("%s", buf);
return strlen;
}
static int sStderrLineConsumer(void* opaque, const char* buf, int strlen) {
(void)opaque;
fprintf(stderr, "%s", buf);
return strlen;
}
void androidSnapshot_listStdout() {
androidSnapshot_list(nullptr,
sStdoutLineConsumer,
sStderrLineConsumer);
}
void androidSnapshot_list(void* opaque,
int (*cbOut)(void*, const char*, int),
int (*cbErr)(void*, const char*, int)) {
Snapshotter::get().listSnapshots(opaque, cbOut, cbErr);
}
void androidSnapshot_setRamFile(const char* path, int shared) {
Snapshotter::get().setRamFile(path, shared);
}
const char* androidSnapshot_prepareAutosave(int memSizeMb, const char* _name) {
const char* name =
_name ? _name : android::snapshot::kDefaultBootSnapshot;
std::string dir = android::snapshot::getSnapshotDir(name);
path_mkdir_if_needed_no_cow(dir.c_str(), 0744);
auto mapPath =
android::base::PathUtils::join(
dir, android::snapshot::kMappedRamFileName);
System::FileSize filePageSize = System::getFilePageSizeForPath(mapPath.c_str());
// QEMU adds an extra |filePageSize| padding on the end on Windows.
System::FileSize ramSizeBytesWithAlign =
#ifdef _WIN32
System::getAlignedFileSize(filePageSize, memSizeMb * 1048576ULL) + filePageSize;
#else
System::getAlignedFileSize(filePageSize, memSizeMb * 1048576ULL);
#endif
System::FileSize existingSize = 0;
// Delete the snapshot dir if RAM file still dirty.
if (androidSnapshot_isRamFileDirty(name)) {
VLOG(snapshot) << "Found invalid RAM file. Deleting snapshot.";
path_delete_dir(dir.c_str());
// Reinitialize the directory since QEMU might need it created already
// for the next RAM file.
path_mkdir_if_needed_no_cow(dir.c_str(), 0744);
} else {
// Address the case where there was a previous ram.img there
// and RAM size was reconfigured.
System::get()->pathFileSize(mapPath, &existingSize);
if (existingSize != ramSizeBytesWithAlign) {
VLOG(snapshot) <<
"Refreshing RAM file (size mismatch): existing " <<
existingSize << " current " << ramSizeBytesWithAlign;
path_delete_file(mapPath.c_str());
existingSize = 0;
}
}
System::FileSize spaceNeeded = (System::FileSize)ramSizeBytesWithAlign - existingSize;
System::FileSize availableSpace;
if (!System::get()->pathFreeSpace(mapPath, &availableSpace)) {
return strdup(mapPath.c_str());
}
static constexpr System::FileSize kSafetyFactor = System::kDiskPressureLimitBytes;
if (availableSpace < spaceNeeded + kSafetyFactor) {
return nullptr;
}
return strdup(mapPath.c_str());
}
void androidSnapshot_setRamFileDirty(const char* _name, bool setDirty) {
const char* name =
_name ? _name : android::snapshot::kDefaultBootSnapshot;
std::string dir = android::snapshot::getSnapshotDir(name);
path_mkdir_if_needed_no_cow(dir.c_str(), 0744);
auto dirtyPath =
android::base::PathUtils::join(
dir, android::snapshot::kMappedRamFileDirtyName);
if (setDirty) {
std::ofstream file(dirtyPath.c_str(), std::ios::trunc);
file << "1";
} else {
path_delete_file(dirtyPath.c_str());
}
}
bool androidSnapshot_isRamFileDirty(const char* _name) {
const char* name =
_name ? _name : android::snapshot::kDefaultBootSnapshot;
std::string dir = android::snapshot::getSnapshotDir(name);
auto dirtyPath =
android::base::PathUtils::join(
dir, android::snapshot::kMappedRamFileDirtyName);
System::FileSize existingSize = 0;
bool result = System::get()->pathFileSize(dirtyPath, &existingSize);
return result || existingSize;
}
AndroidSnapshotRamFileMode androidSnapshot_getRamFileInfo() {
if (Snapshotter::get().hasRamFile()) {
if (Snapshotter::get().isRamFileShared()) {
return SNAPSHOT_RAM_FILE_SHARED;
} else {
return SNAPSHOT_RAM_FILE_PRIVATE;
}
} else {
return SNAPSHOT_RAM_FILE_NONE;
}
}
void androidSnapshot_setUsingHdd(bool usingHdd) {
Snapshotter::get().setUsingHdd(usingHdd);
}
bool androidSnapshot_isUsingHdd() {
return Snapshotter::get().isUsingHdd();
}