Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / common / disk.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: disk.cc
// Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
//
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
//
// The above named 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.
//
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
// ========== Copyright Header End ============================================
#include "disk.h"
SCSIDisk * parser_disk;
std::list<Disk *> * diskList;
std::list<Disk *>::iterator diskListIter;
bool Disk::UI_registered = false;
// parser for disk configuration
extern int scsi_parse_config(const char * fname, FILE *fp, int target);
int gdisk_ui_cmd(void*, int argc, char * argv[]){
if(argc == 1){
for(diskListIter = diskList->begin(); diskListIter != diskList->end(); diskListIter++)
fprintf(stderr," %s\n",(*diskListIter)->get_name());
return 0;
}else if(argc == 2){
if(!strcmp("help",argv[1])){
diskListIter = diskList->begin();
(*diskListIter)->display_help();
return 0;
}else{
fprintf(stderr,"gdisk: unknown command %s\n",argv[1]);
fprintf(stderr,"gdisk: Try \"gdisk help\"\n");
return 0;
}
}
for(diskListIter = diskList->begin(); diskListIter != diskList->end(); diskListIter++)
if(!strcmp((*diskListIter)->get_name(),argv[1])){
(*diskListIter)->handle_ui(argc,argv);
return 0;
}
if (!strcmp(argv[1], "all")) {
for(diskListIter = diskList->begin(); diskListIter != diskList->end(); diskListIter++)
(*diskListIter)->handle_ui(argc,argv);
return 0;
}
fprintf(stderr,"gdisk: No disk %s configured\n",argv[1]);
return 0;
}
Partition::Partition(){
debug_level = 0;
debug_file = stderr;
part_filename = 0;
part_name = 0;
overlay_filename = 0;
ckpt_filename = 0;
rw = false;
has_vtoc = false;
size = 0;
nblks = 0;
start_lblk = 0;
end_lblk = 0;
index = -1;
primary_fd = -1;
overlay_fd = -1;
ckpt_fd = -1;
bitmap_size = 0;
overlay_bitmap = 0;
ckpt_bitmap = 0;
lblk_reads = 0;
lblk_writes = 0;
lblk_reads_primary = 0;
lblk_writes_primary = 0;
lblk_reads_overlay = 0;
lblk_writes_overlay = 0;
lblk_reads_ckpt = 0;
}
Disk::Disk(const char * name){
disk_name = name ? strdup(name) : strdup("Disk");
for(int i = 0; i < MAX_PARTITIONS; i++)
partitions[i] = 0;
target_id = -1;
is_s2 = false;
debug_level = 0;
debug_file = stderr;
strcpy(debug_file_name,"stderr");
num_partitions = 0;
vtoc_partition = -1;
fake_vtoc = false;
num_cylinder = 0;
num_blks = 0;
user_geometry = false;
read_delay = READ_DELAY_DEFAULT;
write_delay = WRITE_DELAY_DEFAULT;
bzero((void*)&disk_label, sizeof(disk_label));
struct stat statbuf;
if(stat(overlaydir, &statbuf) == 0){
if (!S_ISDIR(statbuf.st_mode) ) {
fprintf(debug_file,"%s: overlay dir %s does not exist\n",disk_name,overlaydir);
exit(1);
}
}
// default values, in case vtoc needs to be faked
sectors_per_track = 32;
tracks_per_cylinder = 16;
bytes_per_sector = BYTES_PER_SECTOR; // constant
if(!UI_registered){
UI_registered = true;
UI_register_cmd_2 ("gdisk", "", gdisk_ui_cmd, NULL);
diskList = new std::list<Disk *>;
}
}
SCSIDisk::SCSIDisk():Disk(0){
vendorid = sid.vendor;
productid = sid.product;
revisionid = sid.revision;
serialno = sid.serialnum;
prodserialno = usn.productSerialNumber;
brdserialno = usn.boardSerialNumber;
portwwn = fdi.id0;
nodewwn = fdi.id1;
sizeof_vendorid = sizeof(sid.vendor);
sizeof_productid = sizeof(sid.product);
sizeof_revisionid = sizeof(sid.revision);
sizeof_serialno = sizeof(sid.serialnum);
sizeof_prodserialno = sizeof(usn.productSerialNumber);
sizeof_brdserialno= sizeof(usn.boardSerialNumber);
sizeof_portwwn = sizeof(fdi.id0);
sizeof_nodewwn = sizeof(fdi.id1);
Supported_VPD_size = 0x8;
sv.pageLength = 0x4; // support the first 4 VPD only
}
SCSIDisk::~SCSIDisk(){}
bool SCSIDisk::parse_config(const char * filename, FILE * fp, int target){
assert(fp);
parser_disk = this;
num_partitions = 0;
int ret = scsi_parse_config(filename, fp, target);
if(ret){
fprintf(stderr,"Error parsing disk config file %s\n",filename);
exit(1);
}
if (num_partitions == 0)
return true;
add_partition_fini();
if(!fake_vtoc && user_geometry){
fprintf(stderr,"%s Error:Can't specify VTOC geometry for real disk labels\n",get_name());
assert(0);
}
if(!create_overlays())
exit(1);
for(diskListIter = diskList->begin(); diskListIter != diskList->end(); diskListIter++){
if(!strcmp(get_name(),(*diskListIter)->get_name())){
fprintf(stderr,"Error: Disk %s already configured,\n Provide unique names to disks.\n",get_name());
exit(1);
}
}
diskList->push_front(this);
if(!fake_vtoc) {
disk_read_lblk(0,(uint8_t*)&disk_label,1);
#if defined(ARCH_X64)
label_endian_convert(&disk_label);
#endif
}
return true;
}
bool Disk::add_partition_fini(){
// assumes _SUNOS_VTOC_8
// all paritions have been read. Now create the vtoc if needed, adjust the
// start, end lblks etc.
char buf[BYTES_PER_SECTOR];
if(vtoc_partition == -1)
// need to fake the vtoc
fake_vtoc = true;
if(!fake_vtoc){
// Do not need to fake a vtoc
// 1. Read the vtoc and initialize partition start/end/number blocks according to
// the vtoc
lseek(partitions[vtoc_partition]->primary_fd,0,SEEK_SET);
if(read(partitions[vtoc_partition]->primary_fd,(void*)(buf),BYTES_PER_SECTOR) != BYTES_PER_SECTOR){
fprintf(debug_file,"%s: Cannot read sector 0 on %s\n",disk_name,partitions[vtoc_partition]->part_filename);
perror("");
exit(1);
}
struct vtoc8_dk_label * dkl = (vtoc8_dk_label*) buf;
if(is_s2){
// 1. See if this is s2 partition
assert(vtoc_partition == 2);
partitions[2]->nblks = dkl->dkl_map[2].dkl_nblk;
partitions[2]->start_lblk = dkl->dkl_map[2].dkl_cylno * dkl->dkl_nsect * dkl->dkl_nhead;
partitions[2]->end_lblk = partitions[2]->start_lblk + partitions[2]->nblks - 1;
num_blks = partitions[2]->nblks;
if(partitions[2]->nblks * BYTES_PER_SECTOR != partitions[2]->size){
fprintf(debug_file,"%s Warning: %s size (%lld) not same as size in vtoc (%lld)\n",\
disk_name,partitions[2]->part_filename,partitions[2]->size,partitions[2]->nblks * BYTES_PER_SECTOR);
}
}else{
// initialize other partitions from the vtoc
partitions[vtoc_partition]->nblks = dkl->dkl_map[vtoc_partition].dkl_nblk;
partitions[vtoc_partition]->start_lblk = dkl->dkl_map[vtoc_partition].dkl_cylno * dkl->dkl_nsect * dkl->dkl_nhead;
partitions[vtoc_partition]->end_lblk = partitions[vtoc_partition]->start_lblk + partitions[vtoc_partition]->nblks - 1;
for(int i = 0; i < MAX_PARTITIONS; i++){
if(i == vtoc_partition)
continue;
if(partitions[i] == 0)
continue;
partitions[i]->nblks = dkl->dkl_map[i].dkl_nblk;
if(partitions[i]->nblks == 0){
// nblk == 0 means undefined partition.
// The user has added a partition which
// is not present in the real
// vtoc in the disk. Treat as fatal error.
fprintf(debug_file,"%s: configuring parition %d(%s) not present in disk vtoc.\n",\
disk_name,i,partitions[i]->part_filename);
assert(partitions[i]->nblks);
}
partitions[i]->start_lblk = dkl->dkl_map[i].dkl_cylno * dkl->dkl_nsect * dkl->dkl_nhead;
partitions[i]->end_lblk = partitions[i]->start_lblk + partitions[i]->nblks - 1;
num_blks += partitions[i]->nblks;
if(partitions[i]->nblks * BYTES_PER_SECTOR != partitions[i]->size)
fprintf(debug_file,"%s: Warning: %s size (%lld) not same as size in vtoc (%lld)\n",\
disk_name,partitions[i]->part_filename,partitions[i]->size,partitions[i]->nblks * BYTES_PER_SECTOR);
}
for(int i = 0; i < VTOC8_NDKMAP; i++)
if(i != 2 && dkl->dkl_map[i].dkl_nblk != 0){
if(partitions[i] == 0){
// some partitions present in VTOV are not bein added.
// Reads/writes will be ignored.
fprintf(debug_file,"%s: Warning - partition %d present in VTOC not configured\n",disk_name,i);
fprintf(debug_file,"%s: Warning - some reads/writes MAY not succeed \n",disk_name);
}
}
}
sectors_per_track = dkl->dkl_nsect; // default 32
tracks_per_cylinder = dkl->dkl_nhead; // default 16
num_cylinder = ceil(1.0 * num_blks/sectors_per_track/tracks_per_cylinder);// total number of cyinders in disk
return true;
}
// create the fake vtoc.
// start init'ing the disk label
strcpy(disk_label.dkl_ascii_label,"SUN");
disk_label.dkl_rpm = DKLABEL_RPM;
disk_label.dkl_intrlv = DKLABEL_INTRLV;
disk_label.dkl_acyl = DKLABEL_ACYL;
disk_label.dkl_magic = DKLABEL_MAGIC;
disk_label.dkl_write_reinstruct = 0;
disk_label.dkl_read_reinstruct = 0;
disk_label.dkl_nsect = sectors_per_track;
disk_label.dkl_nhead = tracks_per_cylinder;
disk_label.dkl_vtoc.v_nparts = MAX_PARTITIONS;
disk_label.dkl_vtoc.v_version = 0x1;
disk_label.dkl_vtoc.v_sanity = VTOC_SANITY;
strcpy(disk_label.dkl_vtoc.v_volume,"SAM_VOL");
// start the lowest specified partition from cylinder 0 or 1.
if(is_s2)
num_cylinder = 0;
else
num_cylinder = 1;
for( int i = 0; i < MAX_PARTITIONS; i++){
if( (partitions[i] == 0) )
continue;
if(!is_s2){
// we are creating a fake vtoc. check to see if the partition
// itself does not contain a vtoc. If it does, the access will fail
// as sector 0 will be duplicated. We bail out on such cases. The
// user needs to use the vtoc on partition here. This does not apply
// to s2 since the OS knows there is a label at sector 0, whether
// faked or real. Treat as fatal.
struct vtoc8_dk_label * dkl = (vtoc8_dk_label*) buf;
lseek(partitions[i]->primary_fd,0,SEEK_SET);
read(partitions[i]->primary_fd,buf,BYTES_PER_SECTOR);
if(dkl->dkl_magic == DKLABEL_MAGIC){
fprintf(stderr,"%s.%s: Disk Label found on sector 0 of %s\n",\
disk_name,partitions[i]->part_name,partitions[i]->part_filename);
fprintf(stderr," : Use the label on image or clip sector 0\n.");
assert(0);
}
}
if(!is_s2)
disk_label.dkl_vtoc.v_part[i].p_tag = 0x04; // User
else
disk_label.dkl_vtoc.v_part[2].p_tag = 0x02; // Root
disk_label.dkl_vtoc.v_part[i].p_flag = 0x00; // Mountable, rw
disk_label.dkl_map[i].dkl_cylno = num_cylinder;
disk_label.dkl_map[i].dkl_nblk = ceil(1.0 * partitions[i]->size / bytes_per_sector);
int64_t cylinders_in_partition = ceil ( 1.0 * disk_label.dkl_map[i].dkl_nblk / sectors_per_track / tracks_per_cylinder );
if( num_cylinder + cylinders_in_partition > 0x7fffffffLL){
fprintf(debug_file,"%s: number of cylinders exceed 0x7fffffff. Modify VTOC\n",disk_name);
exit(1);
}else
num_cylinder += cylinders_in_partition;
num_blks += disk_label.dkl_map[i].dkl_nblk;
}
disk_label.dkl_pcyl = num_cylinder;
disk_label.dkl_ncyl = num_cylinder;
set_dkl_cksum();
// fill in the start/end/num blocks in partitions
for( int i = 0; i < MAX_PARTITIONS; i++){
if( partitions[i] == 0 )
continue;
partitions[i]->nblks = disk_label.dkl_map[i].dkl_nblk;
partitions[i]->start_lblk = disk_label.dkl_map[i].dkl_cylno * disk_label.dkl_nsect * disk_label.dkl_nhead;
partitions[i]->end_lblk = partitions[i]->start_lblk + partitions[i]->nblks - 1;
}
// SCSI CAPACITY command reads the numberof nblocks in the disk. It is a 32 bit
// unsigned number. That gives a limit on size of disk as 2^32 * 512 = 2^41
// bytes or 2 TB. Put a check here for this limit. Note that this would not
// work in case of real VTOC, where partitions can overlap. SAM does not
// create disks from partitions that overlap, hence this check works.
if(num_blks > 0xffffffffULL ){
fprintf(debug_file,"%s: Fatal - Total disk size exceeds 2 TB!! LBLKS = %lld\n",disk_name,num_blks);
assert(0);
}
return true;
}
// return true on success, else false
bool Disk::add_partition(int partition, const char * filename, bool rw, bool has_vtoc){
assert(partition <= 7);
if(partition == 2){
// only one partition is allowed. It may or may not have a vtoc
if(num_partitions != 0){
fprintf(stderr,"Cannot have more than 1 partitions with s2\n");
assert(num_partitions == 0);
}
is_s2 = true;
}else{
if(is_s2){
// this disk is being configured with an s2 along with other
// partition. Not allowed
fprintf(stderr,"Can't have another partition with s2\n");
assert(!is_s2);
}
}
if(has_vtoc){
assert(vtoc_partition == -1);
vtoc_partition = partition;
}
char buf[128];
struct stat statbuf;
int fd = -1;
int oflag = O_RDONLY|O_LARGEFILE; // XXX choose based upon 'rw' mode
if( (fd = open(filename, oflag)) == -1){
fprintf(debug_file,"%s: open() file %s errno: %d , %s \n", disk_name, filename, errno, strerror(errno));
return false;
}
if(stat(filename, &statbuf) == -1){
fprintf(debug_file,"%s: Could not stat() file %s \n",disk_name, filename);
return false;
}
assert(partitions[partition] == 0);
Partition * P = partitions[partition] = new Partition();
P->index = partition;
P->has_vtoc = has_vtoc;
P->part_filename = strdup(filename);
P->primary_fd = fd;
sprintf(buf,"s%d",partition);
P->part_name = strdup(buf);
P->rw = rw;
P->is_device_file = statbuf.st_rdev;
if(!P->is_device_file)
P->size = statbuf.st_size;
else{
P->size = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
}
if(P->size % BYTES_PER_SECTOR){
fprintf(debug_file,"partition %d(%s) size not multiple of %d bytes\n",partition,filename,BYTES_PER_SECTOR);
assert(0);
}
num_partitions++;
return true;
}
// return number of bytes written, -1 on error
uint32_t Disk::disk_write_lblk( uint64_t lblkno, uint8_t *buf, uint32_t nblks ){
uint32_t ret = 0;
uint32_t blocks_to_write = nblks;
if(lblkno == 0 && fake_vtoc){
bcopy(buf,&disk_label,BYTES_PER_SECTOR);
#if defined(ARCH_X64)
label_endian_convert(&disk_label);
#endif
buf += BYTES_PER_SECTOR;
lblkno++;
blocks_to_write--;
ret++;
}
for(int i = 0; i < MAX_PARTITIONS && blocks_to_write; i++){
if(partitions[i] && partitions[i]->lblk_inrange(lblkno)){
// handle case of overlapping partitions. The lowest numbered
// partition having the block is read/written to
uint32_t retp = partitions[i]->Write(lblkno, buf, blocks_to_write);
blocks_to_write -= retp;
lblkno += retp;
ret += retp;
}
}
if(ret != nblks)
fprintf(debug_file,"%s:Warning. Write request at lblk %lld of size %ld wrote %ld lblks\n",disk_name,lblkno,nblks,ret);
return ret;
}
// return number of bytes written
uint32_t Disk::disk_read_lblk( uint64_t lblkno, uint8_t *buf, uint32_t nblks ){
bzero(buf,nblks * BYTES_PER_SECTOR);
uint32_t ret = 0;
uint32_t blocks_to_read = nblks;
if(lblkno == 0 && fake_vtoc){
#if defined(ARCH_X64)
struct vtoc8_dk_label tmp;
bcopy(&disk_label,&tmp,sizeof(disk_label));
label_endian_convert(&tmp);
bcopy(&tmp,buf,BYTES_PER_SECTOR);
#else
bcopy(&disk_label,buf,BYTES_PER_SECTOR);
#endif
buf += BYTES_PER_SECTOR;
lblkno++;
blocks_to_read--;
ret++;
}
for(int i = 0; i < MAX_PARTITIONS && blocks_to_read; i++){
if(partitions[i] && partitions[i]->lblk_inrange(lblkno)){
// handle case of overlapping partitions. The lowest numbered
// partition having the block is read/written to
uint32_t retp = partitions[i]->Read(lblkno, buf, blocks_to_read);
blocks_to_read -= retp;
lblkno += retp;
ret += retp;
}
}
if(ret != nblks)
fprintf(debug_file,"%s:Warning. Read request at lblk %lld of size %ld read %ld lblks\n",disk_name,lblkno,nblks,ret);
return ret;
}
bool Disk::dump(const char * dir){
bool ret = true;
for(int i = 0; i < MAX_PARTITIONS; i++){
if(partitions[i] ){
ret &= partitions[i]->dump(dir,disk_name);
}
}
ret &= dump_misc(dir);
return ret;
}
bool Disk::restore(const char * dir){
bool ret = true;
for(int i = 0; i < MAX_PARTITIONS; i++){
if(partitions[i] ){
ret &= partitions[i]->restore(dir,disk_name);
}
}
ret &= restore_misc(dir);
return ret;
}
bool Partition::dump(const char * dir, const char * disk_name){
bool ret = true;
bitset * b = new bitset (bitmap_size);
b->Or(overlay_bitmap);
if(ckpt_bitmap) b->Or(ckpt_bitmap);
char buf[1024];
sprintf(buf, "%s/%s.%s.dmp",dir,disk_name,part_name);
FILE * fp = fopen(buf,"w");
ret &= b->dump(fp);
fclose(fp);
sprintf(buf, "%s/%s.%s.shadow",dir,disk_name,part_name);
int to_fd = open(buf,O_WRONLY|O_CREAT|O_LARGEFILE,0777);
assert(to_fd != -1);
lseek(to_fd,nblks*BYTES_PER_SECTOR - 1,SEEK_SET);
assert(write(to_fd,"",1) == 1);
int src_fd = -1;
const char * src_file= 0;
for(uint64_t i = 0; i < bitmap_size; i++){
char tmpbuf[DISK_PAGE_SIZE];
if(overlay_bitmap->get(i)){
src_fd = overlay_fd;
src_file = overlay_filename;
}else if(ckpt_bitmap && ckpt_bitmap->get(i)){
src_fd = ckpt_fd;
src_file = ckpt_filename;
}else
continue;
lseek(src_fd,i * DISK_PAGE_SIZE, SEEK_SET);
read(src_fd,tmpbuf,DISK_PAGE_SIZE);
lseek(to_fd,i * DISK_PAGE_SIZE, SEEK_SET);
if(write(to_fd,tmpbuf,DISK_PAGE_SIZE) != DISK_PAGE_SIZE){
fprintf(debug_file,"%s:Error writing checkpoint file %s offset %llx\n",part_name,buf,i*DISK_PAGE_SIZE);
perror("");
return false;
}
}
close(to_fd);
return ret;
}
bool Partition::restore(const char * dir, const char * disk_name){
bool ret = true;
char buf[1024];
sprintf(buf, "%s/%s.%s.dmp",dir,disk_name,part_name);
FILE * fp = fopen(buf,"r");
assert(fp);
ckpt_bitmap = new bitset(bitmap_size,part_name);
ckpt_bitmap->restore(fp);
fclose(fp);
sprintf(buf,"%s/%s.%s.shadow",dir,disk_name,part_name);
ckpt_filename = strdup(buf);
if( (ckpt_fd = open(ckpt_filename, O_RDONLY|O_LARGEFILE)) == -1){
fprintf(debug_file,"%s: open() file %s errno: %d , %s \n", disk_name, ckpt_filename, errno, strerror(errno));
ret &= false;
}
return ret;
}
uint32_t Partition::Read(uint64_t lblkno, uint8_t *buf, uint32_t numblks){
uint64_t relative_lblk = lblkno - start_lblk; // lblk from start of backup file
uint64_t bitmap_pos;
int src_fd = -1;
const char * src_file = 0;
uint32_t blocks_read = 0;
char tmpbuf[DISK_PAGE_SIZE];
bzero(tmpbuf,DISK_PAGE_SIZE);
while(numblks != blocks_read){
uint32_t blks_in_cur_page = (BLOCKS_PER_PAGE - relative_lblk % BLOCKS_PER_PAGE)\
< (numblks - blocks_read) ? (BLOCKS_PER_PAGE - relative_lblk % BLOCKS_PER_PAGE):numblks - blocks_read;
bitmap_pos = relative_lblk/BLOCKS_PER_PAGE; // 1 bit for every BLOCKS_PER_PAGE lblks
if(overlay_bitmap->get(bitmap_pos)){
src_fd = overlay_fd; // page has been modified earlier
src_file = overlay_filename;
lblk_reads_overlay += blks_in_cur_page;
}else if(ckpt_bitmap && ckpt_bitmap->get(bitmap_pos)){
src_fd = ckpt_fd; // page has been modified and is present in the chpt
src_file = ckpt_filename;
lblk_reads_ckpt += blks_in_cur_page;
}else{
src_fd = primary_fd; // page has not been written to yet. read from
// actual partition
src_file = part_filename;
lblk_reads_primary += blks_in_cur_page;
}
if(lseek(src_fd,bitmap_pos * DISK_PAGE_SIZE ,SEEK_SET) != bitmap_pos * DISK_PAGE_SIZE){
// the partition file does not have requested lblk. This may happen if
// vtoc specified a different parition size than the actual file size,
// or there may be partially filled lblk. In this case we can't
// write(barring certain cases). We print an error message and
// continue. The user needs to decide if this is fatal or not.
fprintf(debug_file,"%s: Request to seek at lblk %llx, backup file %s map<%llx,%llx> failed.\n",part_name,relative_lblk+start_lblk,src_file,start_lblk,end_lblk);
perror("");
return blocks_read;
}
if(read(src_fd, tmpbuf, DISK_PAGE_SIZE) != DISK_PAGE_SIZE){
// src_fd is smaller than we thought. Flag an error, leave it to
// user to decide if its fatal. XXX cannot happen if DISK_PAGE_SIZE
// == BYTES_PER_SECTOR
fprintf(debug_file,"%s: Request to read at lblk %llx, backup file %s map<%llx,%llx> failed.\n Data could be partial content\n",part_name,relative_lblk+start_lblk,src_file,start_lblk,end_lblk);
perror("");
}
bcopy(tmpbuf + (relative_lblk % BLOCKS_PER_PAGE) *BYTES_PER_SECTOR,\
buf, blks_in_cur_page*BYTES_PER_SECTOR);
blocks_read += blks_in_cur_page;
buf += blks_in_cur_page * BYTES_PER_SECTOR;
relative_lblk += blks_in_cur_page;
}
lblk_reads += blocks_read;
return blocks_read;
}
// return the number of lblks written
uint32_t Partition::Write(uint64_t lblkno, uint8_t *buf, uint32_t numblks){
const uint32_t blocks_to_write = numblks;
uint64_t relative_lblk = lblkno - start_lblk; // lblk from start of backup file
uint64_t bitmap_pos;
int src_fd = -1;
int to_fd = rw ? primary_fd:overlay_fd; // file to which data would be written
assert(to_fd != -1);
char tmpbuf[DISK_PAGE_SIZE]; // read/write DISK_PAGE_SIZE at a time
const char * src_file = 0;
const char * to_file = rw?part_filename: overlay_filename;
bzero(tmpbuf,DISK_PAGE_SIZE);
uint32_t blocks_written = 0;
while(numblks != blocks_written){
uint32_t blks_in_cur_page = (BLOCKS_PER_PAGE - relative_lblk % BLOCKS_PER_PAGE)\
< (numblks - blocks_written) ? (BLOCKS_PER_PAGE - relative_lblk % BLOCKS_PER_PAGE):numblks - blocks_written;
bitmap_pos = relative_lblk/BLOCKS_PER_PAGE; // 1 bit for every BLOCKS_PER_PAGE lblks
if(overlay_bitmap->get(bitmap_pos)){
src_fd = overlay_fd; // page has been modified earlier
src_file = overlay_filename;
}else if(ckpt_bitmap && ckpt_bitmap->get(bitmap_pos)){
src_fd = ckpt_fd; // page has been modified and is present in the chpt
src_file = ckpt_filename;
}else{
src_fd = primary_fd; // page has not been written to yet. Next
// writes will have src_fd = overlay_fd if this
// is ro option
src_file = part_filename;
if(!rw) overlay_bitmap->set(bitmap_pos);
}
if(lseek(src_fd,bitmap_pos * DISK_PAGE_SIZE ,SEEK_SET) != bitmap_pos * DISK_PAGE_SIZE){
// the parition file does not have requested lblk. This may happen if
// vtoc specified a different parition size than the actual file size,
// or there may be partially filled lblk. In this case we can't
// write(barring certain cases). We print an error message and
// continue. The user needs to decide if this is fatal or not.
fprintf(debug_file,"%s: Request to seek at lblk %llx, file %s map<%llx,%llx> failed\n",part_name,relative_lblk+start_lblk,src_file,start_lblk,end_lblk);
perror("");
return blocks_written;
}
if(read(src_fd, tmpbuf, DISK_PAGE_SIZE) != DISK_PAGE_SIZE){
// src_fd is smaller than we thought. Flag an error, leave it to
// user to decide if its fatal
fprintf(debug_file,"%s: Request to read at lblk %llx, file %s map<%llx,%llx> failed.\n Data write could be partial.\n",part_name,relative_lblk+start_lblk,src_file,start_lblk,end_lblk);
perror("");
}
bcopy(buf,tmpbuf + (relative_lblk % BLOCKS_PER_PAGE)*BYTES_PER_SECTOR,\
blks_in_cur_page*BYTES_PER_SECTOR);
if(lseek(to_fd,bitmap_pos * DISK_PAGE_SIZE ,SEEK_SET) != bitmap_pos * DISK_PAGE_SIZE ){
// should not happen, since this is atleast the vtoc specified size,
// or the real file(rw=true), in which case the lseek would fail above
fprintf(debug_file,"%s: Request to seek at lblk %llx, file %s map<%llx,%llx>. Failed\n",part_name,relative_lblk+start_lblk,to_file,start_lblk,end_lblk);
perror("");
return blocks_written;
}
if(write(to_fd, tmpbuf, DISK_PAGE_SIZE) != DISK_PAGE_SIZE){
// should not happen
fprintf(debug_file,"%s: Request to write at lblk %llx, file %s map<%llx,%llx>. Failed\n",part_name,relative_lblk+start_lblk,to_file,start_lblk,end_lblk);
perror("");
}
buf += blks_in_cur_page * BYTES_PER_SECTOR;
blocks_written += blks_in_cur_page;
relative_lblk += blks_in_cur_page;
if(rw)
lblk_writes_primary += blks_in_cur_page;
else
lblk_writes_overlay += blks_in_cur_page;
}
lblk_writes += blocks_written;
return blocks_written;
}