|
|
nono 1.7.0
//
// nono
// Copyright (C) 2021 nono project
// Licensed under nono-license.txt
//
//
// ディスクイメージ
//
#include "diskimage.h"
#include "diskdriver_qcow2.h"
#include "diskdriver_raw.h"
#include "mainapp.h"
#include <fcntl.h>
// コンストラクタ
DiskImage::DiskImage()
: inherited(OBJ_NONE)
{
// ログ名は SetName() で設定するのと、
// このオブジェクトを ID で検索することはないので、OBJ_NONE でいい。
}
// デストラクタ
DiskImage::~DiskImage()
{
Close();
}
// このディスクイメージのログ表示名とログ指定名を設定する。
// 歴史的経緯によりコンストラクタで指定できないので、ここで指定する。
void
DiskImage::SetName(const std::string& name)
{
// 名前は長いけどもうしょうがない。しょっちゅう指定するわけじゃないし…。
inherited::SetName("HostImage-" + name);
ClearAlias();
AddAlias(GetName());
}
void
DiskImage::SetLogLevel(int loglevel_)
{
inherited::SetLogLevel(loglevel_);
if ((bool)driver) {
driver->SetLogLevel(loglevel_);
}
}
// パス名を指定して、下位ドライバをオープン
bool
DiskImage::CreateHandler(const std::string& pathname_)
{
pathname = pathname_;
cow_enable = false;
// ドライバを順に試す。
std::string errmsg;
try {
driver.reset(new DiskDriverQcow2(GetName(), pathname));
} catch (...) { }
if ((bool)driver && driver->Match()) {
goto match;
}
try {
driver.reset(new DiskDriverRaw(GetName(), pathname));
} catch (...) { }
if ((bool)driver && driver->Match()) {
goto match;
}
return false;
match:
// 初期ログレベルをセット。
driver->SetLogLevel(loglevel);
if (driver->Attach(errmsg) == false) {
warn("%s: %s", pathname.c_str(), errmsg.c_str());
return false;
}
return true;
}
// このディスクイメージが書き込み可能かどうかを返す。
// Open() 前でも動作する。
int
DiskImage::IsWriteable() const
{
assert((bool)driver);
return driver->IsWriteable();
}
// オープン
//
// ro cow
// false false : RW オープン、書き込み可
// true false : RO オープン、読み込み専用
// * true : RO オープン、CoW
bool
DiskImage::Open(bool read_only, bool cow_enable_)
{
assert((bool)driver);
cow_enable = cow_enable_;
// ベースファイルをオープン
if (cow_enable) {
read_only = true;
}
if (driver->Open(read_only) == false) {
return false;
}
if (cow_enable) {
// CoW なら一時ファイルもオープン
// VM ディレクトリに適当な名前の隠しファイルで作成
auto pos = pathname.rfind('/');
if (pos != std::string::npos) {
// パス区切りがあればファイル名部分
cowname = pathname.substr(pos + 1);
} else {
// なければ全部
cowname = pathname;
}
// 空になることはないはず
assert(cowname.empty() == false);
cowname = gMainApp.GetVMDir() + "/." + cowname + ".cow";
cowfd = open(cowname.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600);
if (cowfd < 0) {
return false;
}
}
return true;
}
// クローズ
void
DiskImage::Close()
{
if (cow_enable) {
if (cowfd.Valid()) {
cowfd.Close();
unlink(cowname.c_str());
}
cow_enable = false;
}
if ((bool)driver) {
driver.reset();
}
}
// このディスクイメージのサイズ(バイト数)を返す。
// オープン後のみ使用可能。
off_t
DiskImage::GetSize() const
{
assert((bool)driver);
return driver->GetSize();
}
// 読み出し
bool
DiskImage::Read(void *buf, off_t offset, size_t size) const
{
assert((bool)driver);
if (__predict_false(offset + size > GetSize())) {
return false;
}
if (__predict_true(cow_enable == false)) {
// 通常モード
return driver->Read(buf, offset, size);
} else {
// Copy-on-Write モード
while (size > 0) {
off_t blk_addr = offset / BLKSIZE;
size_t blk_off = offset % BLKSIZE;
size_t blk_size = std::min(size, BLKSIZE - blk_off);
if (map_contains(blk_addr)) {
// キャッシュにあればそこから読み出す。
auto cow_offset = map.at(blk_addr);
if (lseek(cowfd, cow_offset + blk_off, SEEK_SET) < 0) {
return false;
}
if (read(cowfd, buf, blk_size) < 0) {
return false;
}
} else {
// キャッシュになければファイルから直接読む
if (driver->Read(buf, offset, blk_size) == false) {
return false;
}
}
buf = (uint8 *)buf + blk_size;
offset += blk_size;
size -= blk_size;
}
}
return true;
}
// 書き込み
bool
DiskImage::Write(const void *buf, off_t offset, size_t size)
{
assert((bool)driver);
if (__predict_false(offset + size > GetSize())) {
return false;
}
if (__predict_true(cow_enable == false)) {
// 通常モード
return driver->Write(buf, offset, size);
} else {
// Copy-on-Write モード
off_t cow_offset;
while (size > 0) {
off_t blk_addr = offset / BLKSIZE;
size_t blk_off = offset % BLKSIZE;
size_t blk_size = std::min(size, BLKSIZE - blk_off);
if (map_contains(blk_addr) == false) {
// キャッシュになければファイルから読んで..
SectBuf newbuf;
off_t read_off = blk_addr * BLKSIZE;
if (driver->Read(&newbuf[0], read_off, BLKSIZE) == false) {
return false;
}
// それをキャッシュの末尾に追加
cow_offset = lseek(cowfd, 0, SEEK_END);
if (cow_offset == (off_t)-1) {
return false;
}
if (write(cowfd, &newbuf[0], BLKSIZE) < BLKSIZE) {
return false;
}
map.emplace(blk_addr, cow_offset);
}
// キャッシュに書き込む
cow_offset = map.at(blk_addr);
if (lseek(cowfd, cow_offset + blk_off, SEEK_SET) < 0) {
return false;
}
if (write(cowfd, buf, blk_size) < 0) {
return false;
}
buf = (const uint8 *)buf + blk_size;
offset += blk_size;
size -= blk_size;
}
}
return true;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.