File:  [Isaki's NoNo m68k/m88k emulator] / nono / host / diskimage.cpp
Revision 1.1.1.8 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:02 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
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;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.