disk_image.cc revision 2
1/* 2 * Copyright (c) 2003 The Regents of The University of Michigan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29/* @file 30 * Disk Image Definitions 31 */ 32 33#include <sys/types.h> 34#include <sys/uio.h> 35#include <errno.h> 36#include <string.h> 37#include <unistd.h> 38 39#include <cstdio> 40#include <fstream> 41#include <string> 42 43#include "disk_image.hh" 44#include "misc.hh" 45#include "trace.hh" 46#include "sim_exit.hh" 47#include "callback.hh" 48 49using namespace std; 50 51//////////////////////////////////////////////////////////////////////// 52// 53// Raw Disk image 54// 55RawDiskImage::RawDiskImage(const string &name, const string &filename, 56 bool rd_only) 57 : DiskImage(name), disk_size(0) 58{ open(filename, rd_only); } 59 60RawDiskImage::~RawDiskImage() 61{ close(); } 62 63void 64RawDiskImage::open(const string &filename, bool rd_only) 65{ 66 if (!filename.empty()) { 67 initialized = true; 68 readonly = rd_only; 69 file = filename; 70 71 ios::openmode mode = ios::in | ios::binary; 72 if (!readonly) 73 mode |= ios::out; 74 stream.open(file.c_str(), mode); 75 if (!stream.is_open()) 76 panic("Error opening %s", filename); 77 } 78} 79 80void 81RawDiskImage::close() 82{ 83 stream.close(); 84} 85 86off_t 87RawDiskImage::size() const 88{ 89 if (disk_size == 0) { 90 if (!stream.is_open()) 91 panic("file not open!\n"); 92 stream.seekg(0, ios::end); 93 disk_size = stream.tellg(); 94 } 95 96 return disk_size / SectorSize; 97} 98 99off_t 100RawDiskImage::read(uint8_t *data, off_t offset) const 101{ 102 if (!initialized) 103 panic("RawDiskImage not initialized"); 104 105 if (!stream.is_open()) 106 panic("file not open!\n"); 107 108 if (stream.seekg(offset * SectorSize, ios::beg) < 0) 109 panic("Could not seek to location in file"); 110 111 off_t pos = stream.tellg(); 112 stream.read((char *)data, SectorSize); 113 114 DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset); 115 DDUMP(DiskImageRead, data, SectorSize); 116 117 return stream.tellg() - pos; 118} 119 120off_t 121RawDiskImage::write(const uint8_t *data, off_t offset) 122{ 123 if (!initialized) 124 panic("RawDiskImage not initialized"); 125 126 if (readonly) 127 panic("Cannot write to a read only disk image"); 128 129 if (!stream.is_open()) 130 panic("file not open!\n"); 131 132 if (stream.seekp(offset * SectorSize, ios::beg) < 0) 133 panic("Could not seek to location in file"); 134 135 DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset); 136 DDUMP(DiskImageWrite, data, SectorSize); 137 138 off_t pos = stream.tellp(); 139 stream.write((const char *)data, SectorSize); 140 return stream.tellp() - pos; 141} 142 143DEFINE_SIM_OBJECT_CLASS_NAME("DiskImage", DiskImage) 144 145BEGIN_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage) 146 147 Param<string> image_file; 148 Param<bool> read_only; 149 150END_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage) 151 152BEGIN_INIT_SIM_OBJECT_PARAMS(RawDiskImage) 153 154 INIT_PARAM(image_file, "disk image file"), 155 INIT_PARAM_DFLT(read_only, "read only image", false) 156 157END_INIT_SIM_OBJECT_PARAMS(RawDiskImage) 158 159 160CREATE_SIM_OBJECT(RawDiskImage) 161{ 162 return new RawDiskImage(getInstanceName(), image_file, read_only); 163} 164 165REGISTER_SIM_OBJECT("RawDiskImage", RawDiskImage) 166 167//////////////////////////////////////////////////////////////////////// 168// 169// Copy on Write Disk image 170// 171const int CowDiskImage::VersionMajor = 1; 172const int CowDiskImage::VersionMinor = 0; 173 174CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size) 175 : DiskImage(name), child(kid), table(NULL) 176{ init(hash_size); } 177 178class CowDiskCallback : public Callback 179{ 180 private: 181 CowDiskImage *image; 182 183 public: 184 CowDiskCallback(CowDiskImage *i) : image(i) {} 185 void process() { image->save(); delete this; } 186}; 187 188CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size, 189 const string &file, bool read_only) 190 : DiskImage(name), filename(file), child(kid), table(NULL) 191{ 192 if (!open()) { 193 assert(!read_only && "why have a non-existent read only file?"); 194 init(hash_size); 195 } 196 197 if (!read_only) 198 registerExitCallback(new CowDiskCallback(this)); 199} 200 201CowDiskImage::~CowDiskImage() 202{ 203 SectorTable::iterator i = table->begin(); 204 SectorTable::iterator end = table->end(); 205 206 while (i != end) { 207 delete (*i).second; 208 ++i; 209 } 210} 211 212void 213SafeRead(ifstream &stream, void *data, int count) 214{ 215 stream.read((char *)data, count); 216 if (!stream.is_open()) 217 panic("file not open"); 218 219 if (stream.eof()) 220 panic("premature end-of-file"); 221 222 if (stream.bad() || stream.fail()) 223 panic("error reading cowdisk image"); 224} 225 226template<class T> 227void 228SafeRead(ifstream &stream, T &data) 229{ SafeRead(stream, &data, sizeof(data)); } 230 231bool 232CowDiskImage::open() 233{ 234 ifstream stream(filename.c_str()); 235 if (!stream.is_open()) 236 return false; 237 238 if (stream.fail() || stream.bad()) 239 panic("Error opening %s", filename); 240 241 uint64_t magic; 242 SafeRead(stream, magic); 243 244 if (memcmp(&magic, "COWDISK!", sizeof(magic)) != 0) 245 panic("Could not open %s: Invalid magic", filename); 246 247 uint32_t major, minor; 248 SafeRead(stream, major); 249 SafeRead(stream, minor); 250 251 if (major != VersionMajor && minor != VersionMinor) 252 panic("Could not open %s: invalid version %d.%d != %d.%d", 253 filename, major, minor, VersionMajor, VersionMinor); 254 255 uint64_t sector_count; 256 SafeRead(stream, sector_count); 257 table = new SectorTable(sector_count); 258 259 260 for (uint64_t i = 0; i < sector_count; i++) { 261 uint64_t offset; 262 SafeRead(stream, offset); 263 264 Sector *sector = new Sector; 265 SafeRead(stream, sector, sizeof(Sector)); 266 267 assert(table->find(offset) == table->end()); 268 (*table)[offset] = sector; 269 } 270 271 stream.close(); 272 273 initialized = true; 274 return true; 275} 276 277void 278CowDiskImage::init(int hash_size) 279{ 280 table = new SectorTable(hash_size); 281 282 initialized = true; 283} 284 285void 286SafeWrite(ofstream &stream, const void *data, int count) 287{ 288 stream.write((const char *)data, count); 289 if (!stream.is_open()) 290 panic("file not open"); 291 292 if (stream.eof()) 293 panic("premature end-of-file"); 294 295 if (stream.bad() || stream.fail()) 296 panic("error reading cowdisk image"); 297} 298 299template<class T> 300void 301SafeWrite(ofstream &stream, const T &data) 302{ SafeWrite(stream, &data, sizeof(data)); } 303 304void 305CowDiskImage::save() 306{ 307 if (!initialized) 308 panic("RawDiskImage not initialized"); 309 310 ofstream stream(filename.c_str()); 311 if (!stream.is_open() || stream.fail() || stream.bad()) 312 panic("Error opening %s", filename); 313 314 uint64_t magic; 315 memcpy(&magic, "COWDISK!", sizeof(magic)); 316 SafeWrite(stream, magic); 317 318 SafeWrite(stream, (uint32_t)VersionMajor); 319 SafeWrite(stream, (uint32_t)VersionMinor); 320 SafeWrite(stream, (uint64_t)table->size()); 321 322 uint64_t size = table->size(); 323 SectorTable::iterator iter = table->begin(); 324 SectorTable::iterator end = table->end(); 325 326 for (uint64_t i = 0; i < size; i++) { 327 if (iter == end) 328 panic("Incorrect Table Size during save of COW disk image"); 329 330 SafeWrite(stream, (uint64_t)(*iter).first); 331 SafeWrite(stream, (*iter).second->data, sizeof(Sector)); 332 ++iter; 333 } 334 335 stream.close(); 336} 337 338void 339CowDiskImage::writeback() 340{ 341 SectorTable::iterator i = table->begin(); 342 SectorTable::iterator end = table->end(); 343 344 while (i != end) { 345 child->write((*i).second->data, (*i).first); 346 ++i; 347 } 348} 349 350off_t 351CowDiskImage::size() const 352{ return child->size(); } 353 354off_t 355CowDiskImage::read(uint8_t *data, off_t offset) const 356{ 357 if (!initialized) 358 panic("CowDiskImage not initialized"); 359 360 if (offset > size()) 361 panic("access out of bounds"); 362 363 SectorTable::const_iterator i = table->find(offset); 364 if (i == table->end()) 365 return child->read(data, offset); 366 else { 367 memcpy(data, (*i).second->data, SectorSize); 368 DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset); 369 DDUMP(DiskImageRead, data, SectorSize); 370 return SectorSize; 371 } 372} 373 374off_t 375CowDiskImage::write(const uint8_t *data, off_t offset) 376{ 377 if (!initialized) 378 panic("RawDiskImage not initialized"); 379 380 if (offset > size()) 381 panic("access out of bounds"); 382 383 SectorTable::iterator i = table->find(offset); 384 if (i == table->end()) { 385 Sector *sector = new Sector; 386 memcpy(sector, data, SectorSize); 387 table->insert(make_pair(offset, sector)); 388 } else { 389 memcpy((*i).second->data, data, SectorSize); 390 } 391 392 DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset); 393 DDUMP(DiskImageWrite, data, SectorSize); 394 395 return SectorSize; 396} 397 398BEGIN_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage) 399 400 SimObjectParam<DiskImage *> child; 401 Param<string> image_file; 402 Param<int> table_size; 403 Param<bool> read_only; 404 405END_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage) 406 407BEGIN_INIT_SIM_OBJECT_PARAMS(CowDiskImage) 408 409 INIT_PARAM(child, "child image"), 410 INIT_PARAM_DFLT(image_file, "disk image file", ""), 411 INIT_PARAM_DFLT(table_size, "initial table size", 65536), 412 INIT_PARAM_DFLT(read_only, "don't write back to the copy-on-write file", 413 true) 414 415END_INIT_SIM_OBJECT_PARAMS(CowDiskImage) 416 417 418CREATE_SIM_OBJECT(CowDiskImage) 419{ 420 if (((string)image_file).empty()) 421 return new CowDiskImage(getInstanceName(), child, table_size); 422 else 423 return new CowDiskImage(getInstanceName(), child, table_size, 424 image_file, read_only); 425} 426 427REGISTER_SIM_OBJECT("CowDiskImage", CowDiskImage) 428