Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | // ========== Copyright Header Begin ========================================== |
2 | // | |
3 | // OpenSPARC T2 Processor File: disk.cc | |
4 | // Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
5 | // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
6 | // | |
7 | // The above named program is free software; you can redistribute it and/or | |
8 | // modify it under the terms of the GNU General Public | |
9 | // License version 2 as published by the Free Software Foundation. | |
10 | // | |
11 | // The above named program is distributed in the hope that it will be | |
12 | // useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | // General Public License for more details. | |
15 | // | |
16 | // You should have received a copy of the GNU General Public | |
17 | // License along with this work; if not, write to the Free Software | |
18 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 | // | |
20 | // ========== Copyright Header End ============================================ | |
21 | #include "disk.h" | |
22 | ||
23 | ||
24 | SCSIDisk * parser_disk; | |
25 | ||
26 | std::list<Disk *> * diskList; | |
27 | std::list<Disk *>::iterator diskListIter; | |
28 | ||
29 | bool Disk::UI_registered = false; | |
30 | ||
31 | // parser for disk configuration | |
32 | extern int scsi_parse_config(const char * fname, FILE *fp, int target); | |
33 | ||
34 | ||
35 | int gdisk_ui_cmd(void*, int argc, char * argv[]){ | |
36 | ||
37 | if(argc == 1){ | |
38 | for(diskListIter = diskList->begin(); diskListIter != diskList->end(); diskListIter++) | |
39 | fprintf(stderr," %s\n",(*diskListIter)->get_name()); | |
40 | return 0; | |
41 | }else if(argc == 2){ | |
42 | if(!strcmp("help",argv[1])){ | |
43 | diskListIter = diskList->begin(); | |
44 | (*diskListIter)->display_help(); | |
45 | return 0; | |
46 | }else{ | |
47 | fprintf(stderr,"gdisk: unknown command %s\n",argv[1]); | |
48 | fprintf(stderr,"gdisk: Try \"gdisk help\"\n"); | |
49 | return 0; | |
50 | } | |
51 | } | |
52 | ||
53 | ||
54 | for(diskListIter = diskList->begin(); diskListIter != diskList->end(); diskListIter++) | |
55 | if(!strcmp((*diskListIter)->get_name(),argv[1])){ | |
56 | (*diskListIter)->handle_ui(argc,argv); | |
57 | return 0; | |
58 | } | |
59 | ||
60 | if (!strcmp(argv[1], "all")) { | |
61 | for(diskListIter = diskList->begin(); diskListIter != diskList->end(); diskListIter++) | |
62 | (*diskListIter)->handle_ui(argc,argv); | |
63 | return 0; | |
64 | } | |
65 | ||
66 | fprintf(stderr,"gdisk: No disk %s configured\n",argv[1]); | |
67 | return 0; | |
68 | } | |
69 | ||
70 | ||
71 | Partition::Partition(){ | |
72 | ||
73 | debug_level = 0; | |
74 | debug_file = stderr; | |
75 | ||
76 | part_filename = 0; | |
77 | part_name = 0; | |
78 | ||
79 | overlay_filename = 0; | |
80 | ckpt_filename = 0; | |
81 | ||
82 | rw = false; | |
83 | has_vtoc = false; | |
84 | ||
85 | size = 0; | |
86 | nblks = 0; | |
87 | start_lblk = 0; | |
88 | end_lblk = 0; | |
89 | ||
90 | index = -1; | |
91 | primary_fd = -1; | |
92 | overlay_fd = -1; | |
93 | ckpt_fd = -1; | |
94 | ||
95 | bitmap_size = 0; | |
96 | overlay_bitmap = 0; | |
97 | ckpt_bitmap = 0; | |
98 | ||
99 | lblk_reads = 0; | |
100 | lblk_writes = 0; | |
101 | lblk_reads_primary = 0; | |
102 | lblk_writes_primary = 0; | |
103 | lblk_reads_overlay = 0; | |
104 | lblk_writes_overlay = 0; | |
105 | lblk_reads_ckpt = 0; | |
106 | } | |
107 | ||
108 | ||
109 | Disk::Disk(const char * name){ | |
110 | disk_name = name ? strdup(name) : strdup("Disk"); | |
111 | for(int i = 0; i < MAX_PARTITIONS; i++) | |
112 | partitions[i] = 0; | |
113 | ||
114 | target_id = -1; | |
115 | is_s2 = false; | |
116 | debug_level = 0; | |
117 | debug_file = stderr; | |
118 | strcpy(debug_file_name,"stderr"); | |
119 | num_partitions = 0; | |
120 | vtoc_partition = -1; | |
121 | fake_vtoc = false; | |
122 | num_cylinder = 0; | |
123 | num_blks = 0; | |
124 | user_geometry = false; | |
125 | read_delay = READ_DELAY_DEFAULT; | |
126 | write_delay = WRITE_DELAY_DEFAULT; | |
127 | ||
128 | bzero((void*)&disk_label, sizeof(disk_label)); | |
129 | ||
130 | struct stat statbuf; | |
131 | if(stat(overlaydir, &statbuf) == 0){ | |
132 | if (!S_ISDIR(statbuf.st_mode) ) { | |
133 | fprintf(debug_file,"%s: overlay dir %s does not exist\n",disk_name,overlaydir); | |
134 | exit(1); | |
135 | } | |
136 | } | |
137 | ||
138 | // default values, in case vtoc needs to be faked | |
139 | sectors_per_track = 32; | |
140 | tracks_per_cylinder = 16; | |
141 | bytes_per_sector = BYTES_PER_SECTOR; // constant | |
142 | ||
143 | ||
144 | if(!UI_registered){ | |
145 | UI_registered = true; | |
146 | UI_register_cmd_2 ("gdisk", "", gdisk_ui_cmd, NULL); | |
147 | diskList = new std::list<Disk *>; | |
148 | } | |
149 | } | |
150 | ||
151 | ||
152 | SCSIDisk::SCSIDisk():Disk(0){ | |
153 | vendorid = sid.vendor; | |
154 | productid = sid.product; | |
155 | revisionid = sid.revision; | |
156 | serialno = sid.serialnum; | |
157 | prodserialno = usn.productSerialNumber; | |
158 | brdserialno = usn.boardSerialNumber; | |
159 | portwwn = fdi.id0; | |
160 | nodewwn = fdi.id1; | |
161 | ||
162 | sizeof_vendorid = sizeof(sid.vendor); | |
163 | sizeof_productid = sizeof(sid.product); | |
164 | sizeof_revisionid = sizeof(sid.revision); | |
165 | sizeof_serialno = sizeof(sid.serialnum); | |
166 | sizeof_prodserialno = sizeof(usn.productSerialNumber); | |
167 | sizeof_brdserialno= sizeof(usn.boardSerialNumber); | |
168 | sizeof_portwwn = sizeof(fdi.id0); | |
169 | sizeof_nodewwn = sizeof(fdi.id1); | |
170 | ||
171 | ||
172 | Supported_VPD_size = 0x8; | |
173 | sv.pageLength = 0x4; // support the first 4 VPD only | |
174 | } | |
175 | ||
176 | SCSIDisk::~SCSIDisk(){} | |
177 | ||
178 | ||
179 | bool SCSIDisk::parse_config(const char * filename, FILE * fp, int target){ | |
180 | assert(fp); | |
181 | parser_disk = this; | |
182 | num_partitions = 0; | |
183 | ||
184 | int ret = scsi_parse_config(filename, fp, target); | |
185 | ||
186 | if(ret){ | |
187 | fprintf(stderr,"Error parsing disk config file %s\n",filename); | |
188 | exit(1); | |
189 | } | |
190 | ||
191 | if (num_partitions == 0) | |
192 | return true; | |
193 | ||
194 | add_partition_fini(); | |
195 | ||
196 | if(!fake_vtoc && user_geometry){ | |
197 | fprintf(stderr,"%s Error:Can't specify VTOC geometry for real disk labels\n",get_name()); | |
198 | assert(0); | |
199 | } | |
200 | ||
201 | if(!create_overlays()) | |
202 | exit(1); | |
203 | ||
204 | for(diskListIter = diskList->begin(); diskListIter != diskList->end(); diskListIter++){ | |
205 | if(!strcmp(get_name(),(*diskListIter)->get_name())){ | |
206 | fprintf(stderr,"Error: Disk %s already configured,\n Provide unique names to disks.\n",get_name()); | |
207 | exit(1); | |
208 | } | |
209 | } | |
210 | ||
211 | diskList->push_front(this); | |
212 | ||
213 | if(!fake_vtoc) { | |
214 | disk_read_lblk(0,(uint8_t*)&disk_label,1); | |
215 | #if defined(ARCH_X64) | |
216 | label_endian_convert(&disk_label); | |
217 | #endif | |
218 | } | |
219 | ||
220 | return true; | |
221 | } | |
222 | ||
223 | ||
224 | ||
225 | bool Disk::add_partition_fini(){ | |
226 | // assumes _SUNOS_VTOC_8 | |
227 | ||
228 | // all paritions have been read. Now create the vtoc if needed, adjust the | |
229 | // start, end lblks etc. | |
230 | ||
231 | char buf[BYTES_PER_SECTOR]; | |
232 | ||
233 | if(vtoc_partition == -1) | |
234 | // need to fake the vtoc | |
235 | fake_vtoc = true; | |
236 | ||
237 | if(!fake_vtoc){ | |
238 | // Do not need to fake a vtoc | |
239 | // 1. Read the vtoc and initialize partition start/end/number blocks according to | |
240 | // the vtoc | |
241 | lseek(partitions[vtoc_partition]->primary_fd,0,SEEK_SET); | |
242 | if(read(partitions[vtoc_partition]->primary_fd,(void*)(buf),BYTES_PER_SECTOR) != BYTES_PER_SECTOR){ | |
243 | fprintf(debug_file,"%s: Cannot read sector 0 on %s\n",disk_name,partitions[vtoc_partition]->part_filename); | |
244 | perror(""); | |
245 | exit(1); | |
246 | } | |
247 | struct vtoc8_dk_label * dkl = (vtoc8_dk_label*) buf; | |
248 | ||
249 | if(is_s2){ | |
250 | // 1. See if this is s2 partition | |
251 | assert(vtoc_partition == 2); | |
252 | partitions[2]->nblks = dkl->dkl_map[2].dkl_nblk; | |
253 | partitions[2]->start_lblk = dkl->dkl_map[2].dkl_cylno * dkl->dkl_nsect * dkl->dkl_nhead; | |
254 | partitions[2]->end_lblk = partitions[2]->start_lblk + partitions[2]->nblks - 1; | |
255 | num_blks = partitions[2]->nblks; | |
256 | if(partitions[2]->nblks * BYTES_PER_SECTOR != partitions[2]->size){ | |
257 | fprintf(debug_file,"%s Warning: %s size (%lld) not same as size in vtoc (%lld)\n",\ | |
258 | disk_name,partitions[2]->part_filename,partitions[2]->size,partitions[2]->nblks * BYTES_PER_SECTOR); | |
259 | } | |
260 | }else{ | |
261 | // initialize other partitions from the vtoc | |
262 | partitions[vtoc_partition]->nblks = dkl->dkl_map[vtoc_partition].dkl_nblk; | |
263 | partitions[vtoc_partition]->start_lblk = dkl->dkl_map[vtoc_partition].dkl_cylno * dkl->dkl_nsect * dkl->dkl_nhead; | |
264 | partitions[vtoc_partition]->end_lblk = partitions[vtoc_partition]->start_lblk + partitions[vtoc_partition]->nblks - 1; | |
265 | ||
266 | for(int i = 0; i < MAX_PARTITIONS; i++){ | |
267 | if(i == vtoc_partition) | |
268 | continue; | |
269 | if(partitions[i] == 0) | |
270 | continue; | |
271 | partitions[i]->nblks = dkl->dkl_map[i].dkl_nblk; | |
272 | if(partitions[i]->nblks == 0){ | |
273 | // nblk == 0 means undefined partition. | |
274 | // The user has added a partition which | |
275 | // is not present in the real | |
276 | // vtoc in the disk. Treat as fatal error. | |
277 | fprintf(debug_file,"%s: configuring parition %d(%s) not present in disk vtoc.\n",\ | |
278 | disk_name,i,partitions[i]->part_filename); | |
279 | assert(partitions[i]->nblks); | |
280 | } | |
281 | partitions[i]->start_lblk = dkl->dkl_map[i].dkl_cylno * dkl->dkl_nsect * dkl->dkl_nhead; | |
282 | partitions[i]->end_lblk = partitions[i]->start_lblk + partitions[i]->nblks - 1; | |
283 | num_blks += partitions[i]->nblks; | |
284 | if(partitions[i]->nblks * BYTES_PER_SECTOR != partitions[i]->size) | |
285 | fprintf(debug_file,"%s: Warning: %s size (%lld) not same as size in vtoc (%lld)\n",\ | |
286 | disk_name,partitions[i]->part_filename,partitions[i]->size,partitions[i]->nblks * BYTES_PER_SECTOR); | |
287 | } | |
288 | for(int i = 0; i < VTOC8_NDKMAP; i++) | |
289 | if(i != 2 && dkl->dkl_map[i].dkl_nblk != 0){ | |
290 | if(partitions[i] == 0){ | |
291 | // some partitions present in VTOV are not bein added. | |
292 | // Reads/writes will be ignored. | |
293 | fprintf(debug_file,"%s: Warning - partition %d present in VTOC not configured\n",disk_name,i); | |
294 | fprintf(debug_file,"%s: Warning - some reads/writes MAY not succeed \n",disk_name); | |
295 | } | |
296 | } | |
297 | } | |
298 | ||
299 | sectors_per_track = dkl->dkl_nsect; // default 32 | |
300 | tracks_per_cylinder = dkl->dkl_nhead; // default 16 | |
301 | num_cylinder = ceil(1.0 * num_blks/sectors_per_track/tracks_per_cylinder);// total number of cyinders in disk | |
302 | ||
303 | return true; | |
304 | } | |
305 | ||
306 | // create the fake vtoc. | |
307 | ||
308 | // start init'ing the disk label | |
309 | strcpy(disk_label.dkl_ascii_label,"SUN"); | |
310 | disk_label.dkl_rpm = DKLABEL_RPM; | |
311 | disk_label.dkl_intrlv = DKLABEL_INTRLV; | |
312 | disk_label.dkl_acyl = DKLABEL_ACYL; | |
313 | disk_label.dkl_magic = DKLABEL_MAGIC; | |
314 | disk_label.dkl_write_reinstruct = 0; | |
315 | disk_label.dkl_read_reinstruct = 0; | |
316 | disk_label.dkl_nsect = sectors_per_track; | |
317 | disk_label.dkl_nhead = tracks_per_cylinder; | |
318 | ||
319 | disk_label.dkl_vtoc.v_nparts = MAX_PARTITIONS; | |
320 | disk_label.dkl_vtoc.v_version = 0x1; | |
321 | disk_label.dkl_vtoc.v_sanity = VTOC_SANITY; | |
322 | strcpy(disk_label.dkl_vtoc.v_volume,"SAM_VOL"); | |
323 | ||
324 | // start the lowest specified partition from cylinder 0 or 1. | |
325 | if(is_s2) | |
326 | num_cylinder = 0; | |
327 | else | |
328 | num_cylinder = 1; | |
329 | ||
330 | for( int i = 0; i < MAX_PARTITIONS; i++){ | |
331 | if( (partitions[i] == 0) ) | |
332 | continue; | |
333 | if(!is_s2){ | |
334 | // we are creating a fake vtoc. check to see if the partition | |
335 | // itself does not contain a vtoc. If it does, the access will fail | |
336 | // as sector 0 will be duplicated. We bail out on such cases. The | |
337 | // user needs to use the vtoc on partition here. This does not apply | |
338 | // to s2 since the OS knows there is a label at sector 0, whether | |
339 | // faked or real. Treat as fatal. | |
340 | struct vtoc8_dk_label * dkl = (vtoc8_dk_label*) buf; | |
341 | lseek(partitions[i]->primary_fd,0,SEEK_SET); | |
342 | read(partitions[i]->primary_fd,buf,BYTES_PER_SECTOR); | |
343 | if(dkl->dkl_magic == DKLABEL_MAGIC){ | |
344 | fprintf(stderr,"%s.%s: Disk Label found on sector 0 of %s\n",\ | |
345 | disk_name,partitions[i]->part_name,partitions[i]->part_filename); | |
346 | fprintf(stderr," : Use the label on image or clip sector 0\n."); | |
347 | assert(0); | |
348 | } | |
349 | } | |
350 | if(!is_s2) | |
351 | disk_label.dkl_vtoc.v_part[i].p_tag = 0x04; // User | |
352 | else | |
353 | disk_label.dkl_vtoc.v_part[2].p_tag = 0x02; // Root | |
354 | disk_label.dkl_vtoc.v_part[i].p_flag = 0x00; // Mountable, rw | |
355 | disk_label.dkl_map[i].dkl_cylno = num_cylinder; | |
356 | disk_label.dkl_map[i].dkl_nblk = ceil(1.0 * partitions[i]->size / bytes_per_sector); | |
357 | int64_t cylinders_in_partition = ceil ( 1.0 * disk_label.dkl_map[i].dkl_nblk / sectors_per_track / tracks_per_cylinder ); | |
358 | if( num_cylinder + cylinders_in_partition > 0x7fffffffLL){ | |
359 | fprintf(debug_file,"%s: number of cylinders exceed 0x7fffffff. Modify VTOC\n",disk_name); | |
360 | exit(1); | |
361 | }else | |
362 | num_cylinder += cylinders_in_partition; | |
363 | num_blks += disk_label.dkl_map[i].dkl_nblk; | |
364 | } | |
365 | ||
366 | disk_label.dkl_pcyl = num_cylinder; | |
367 | disk_label.dkl_ncyl = num_cylinder; | |
368 | ||
369 | set_dkl_cksum(); | |
370 | ||
371 | // fill in the start/end/num blocks in partitions | |
372 | ||
373 | for( int i = 0; i < MAX_PARTITIONS; i++){ | |
374 | if( partitions[i] == 0 ) | |
375 | continue; | |
376 | partitions[i]->nblks = disk_label.dkl_map[i].dkl_nblk; | |
377 | partitions[i]->start_lblk = disk_label.dkl_map[i].dkl_cylno * disk_label.dkl_nsect * disk_label.dkl_nhead; | |
378 | partitions[i]->end_lblk = partitions[i]->start_lblk + partitions[i]->nblks - 1; | |
379 | } | |
380 | ||
381 | // SCSI CAPACITY command reads the numberof nblocks in the disk. It is a 32 bit | |
382 | // unsigned number. That gives a limit on size of disk as 2^32 * 512 = 2^41 | |
383 | // bytes or 2 TB. Put a check here for this limit. Note that this would not | |
384 | // work in case of real VTOC, where partitions can overlap. SAM does not | |
385 | // create disks from partitions that overlap, hence this check works. | |
386 | if(num_blks > 0xffffffffULL ){ | |
387 | fprintf(debug_file,"%s: Fatal - Total disk size exceeds 2 TB!! LBLKS = %lld\n",disk_name,num_blks); | |
388 | assert(0); | |
389 | } | |
390 | ||
391 | ||
392 | return true; | |
393 | } | |
394 | ||
395 | ||
396 | // return true on success, else false | |
397 | bool Disk::add_partition(int partition, const char * filename, bool rw, bool has_vtoc){ | |
398 | ||
399 | assert(partition <= 7); | |
400 | if(partition == 2){ | |
401 | // only one partition is allowed. It may or may not have a vtoc | |
402 | if(num_partitions != 0){ | |
403 | fprintf(stderr,"Cannot have more than 1 partitions with s2\n"); | |
404 | assert(num_partitions == 0); | |
405 | } | |
406 | is_s2 = true; | |
407 | }else{ | |
408 | if(is_s2){ | |
409 | // this disk is being configured with an s2 along with other | |
410 | // partition. Not allowed | |
411 | fprintf(stderr,"Can't have another partition with s2\n"); | |
412 | assert(!is_s2); | |
413 | } | |
414 | } | |
415 | ||
416 | if(has_vtoc){ | |
417 | assert(vtoc_partition == -1); | |
418 | vtoc_partition = partition; | |
419 | } | |
420 | ||
421 | char buf[128]; | |
422 | struct stat statbuf; | |
423 | int fd = -1; | |
424 | ||
425 | int oflag = O_RDONLY|O_LARGEFILE; // XXX choose based upon 'rw' mode | |
426 | ||
427 | if( (fd = open(filename, oflag)) == -1){ | |
428 | fprintf(debug_file,"%s: open() file %s errno: %d , %s \n", disk_name, filename, errno, strerror(errno)); | |
429 | return false; | |
430 | } | |
431 | ||
432 | ||
433 | if(stat(filename, &statbuf) == -1){ | |
434 | fprintf(debug_file,"%s: Could not stat() file %s \n",disk_name, filename); | |
435 | return false; | |
436 | } | |
437 | ||
438 | assert(partitions[partition] == 0); | |
439 | Partition * P = partitions[partition] = new Partition(); | |
440 | P->index = partition; | |
441 | P->has_vtoc = has_vtoc; | |
442 | P->part_filename = strdup(filename); | |
443 | P->primary_fd = fd; | |
444 | sprintf(buf,"s%d",partition); | |
445 | P->part_name = strdup(buf); | |
446 | P->rw = rw; | |
447 | ||
448 | ||
449 | P->is_device_file = statbuf.st_rdev; | |
450 | if(!P->is_device_file) | |
451 | P->size = statbuf.st_size; | |
452 | else{ | |
453 | P->size = lseek(fd,0,SEEK_END); | |
454 | lseek(fd,0,SEEK_SET); | |
455 | } | |
456 | ||
457 | if(P->size % BYTES_PER_SECTOR){ | |
458 | fprintf(debug_file,"partition %d(%s) size not multiple of %d bytes\n",partition,filename,BYTES_PER_SECTOR); | |
459 | assert(0); | |
460 | } | |
461 | ||
462 | num_partitions++; | |
463 | return true; | |
464 | ||
465 | } | |
466 | ||
467 | // return number of bytes written, -1 on error | |
468 | uint32_t Disk::disk_write_lblk( uint64_t lblkno, uint8_t *buf, uint32_t nblks ){ | |
469 | uint32_t ret = 0; | |
470 | uint32_t blocks_to_write = nblks; | |
471 | if(lblkno == 0 && fake_vtoc){ | |
472 | bcopy(buf,&disk_label,BYTES_PER_SECTOR); | |
473 | #if defined(ARCH_X64) | |
474 | label_endian_convert(&disk_label); | |
475 | #endif | |
476 | buf += BYTES_PER_SECTOR; | |
477 | lblkno++; | |
478 | blocks_to_write--; | |
479 | ret++; | |
480 | } | |
481 | for(int i = 0; i < MAX_PARTITIONS && blocks_to_write; i++){ | |
482 | if(partitions[i] && partitions[i]->lblk_inrange(lblkno)){ | |
483 | // handle case of overlapping partitions. The lowest numbered | |
484 | // partition having the block is read/written to | |
485 | uint32_t retp = partitions[i]->Write(lblkno, buf, blocks_to_write); | |
486 | blocks_to_write -= retp; | |
487 | lblkno += retp; | |
488 | ret += retp; | |
489 | } | |
490 | } | |
491 | if(ret != nblks) | |
492 | fprintf(debug_file,"%s:Warning. Write request at lblk %lld of size %ld wrote %ld lblks\n",disk_name,lblkno,nblks,ret); | |
493 | ||
494 | return ret; | |
495 | } | |
496 | ||
497 | // return number of bytes written | |
498 | uint32_t Disk::disk_read_lblk( uint64_t lblkno, uint8_t *buf, uint32_t nblks ){ | |
499 | ||
500 | bzero(buf,nblks * BYTES_PER_SECTOR); | |
501 | uint32_t ret = 0; | |
502 | uint32_t blocks_to_read = nblks; | |
503 | ||
504 | if(lblkno == 0 && fake_vtoc){ | |
505 | #if defined(ARCH_X64) | |
506 | struct vtoc8_dk_label tmp; | |
507 | bcopy(&disk_label,&tmp,sizeof(disk_label)); | |
508 | label_endian_convert(&tmp); | |
509 | bcopy(&tmp,buf,BYTES_PER_SECTOR); | |
510 | #else | |
511 | bcopy(&disk_label,buf,BYTES_PER_SECTOR); | |
512 | #endif | |
513 | buf += BYTES_PER_SECTOR; | |
514 | lblkno++; | |
515 | blocks_to_read--; | |
516 | ret++; | |
517 | } | |
518 | ||
519 | for(int i = 0; i < MAX_PARTITIONS && blocks_to_read; i++){ | |
520 | if(partitions[i] && partitions[i]->lblk_inrange(lblkno)){ | |
521 | // handle case of overlapping partitions. The lowest numbered | |
522 | // partition having the block is read/written to | |
523 | uint32_t retp = partitions[i]->Read(lblkno, buf, blocks_to_read); | |
524 | blocks_to_read -= retp; | |
525 | lblkno += retp; | |
526 | ret += retp; | |
527 | } | |
528 | } | |
529 | ||
530 | if(ret != nblks) | |
531 | fprintf(debug_file,"%s:Warning. Read request at lblk %lld of size %ld read %ld lblks\n",disk_name,lblkno,nblks,ret); | |
532 | ||
533 | return ret; | |
534 | } | |
535 | ||
536 | bool Disk::dump(const char * dir){ | |
537 | ||
538 | bool ret = true; | |
539 | for(int i = 0; i < MAX_PARTITIONS; i++){ | |
540 | if(partitions[i] ){ | |
541 | ret &= partitions[i]->dump(dir,disk_name); | |
542 | } | |
543 | } | |
544 | ||
545 | ret &= dump_misc(dir); | |
546 | ||
547 | return ret; | |
548 | } | |
549 | ||
550 | bool Disk::restore(const char * dir){ | |
551 | bool ret = true; | |
552 | for(int i = 0; i < MAX_PARTITIONS; i++){ | |
553 | if(partitions[i] ){ | |
554 | ret &= partitions[i]->restore(dir,disk_name); | |
555 | } | |
556 | } | |
557 | ||
558 | ret &= restore_misc(dir); | |
559 | ||
560 | return ret; | |
561 | } | |
562 | ||
563 | ||
564 | bool Partition::dump(const char * dir, const char * disk_name){ | |
565 | bool ret = true; | |
566 | ||
567 | bitset * b = new bitset (bitmap_size); | |
568 | b->Or(overlay_bitmap); | |
569 | if(ckpt_bitmap) b->Or(ckpt_bitmap); | |
570 | ||
571 | char buf[1024]; | |
572 | sprintf(buf, "%s/%s.%s.dmp",dir,disk_name,part_name); | |
573 | ||
574 | FILE * fp = fopen(buf,"w"); | |
575 | ret &= b->dump(fp); | |
576 | fclose(fp); | |
577 | ||
578 | sprintf(buf, "%s/%s.%s.shadow",dir,disk_name,part_name); | |
579 | int to_fd = open(buf,O_WRONLY|O_CREAT|O_LARGEFILE,0777); | |
580 | assert(to_fd != -1); | |
581 | lseek(to_fd,nblks*BYTES_PER_SECTOR - 1,SEEK_SET); | |
582 | assert(write(to_fd,"",1) == 1); | |
583 | ||
584 | int src_fd = -1; | |
585 | const char * src_file= 0; | |
586 | for(uint64_t i = 0; i < bitmap_size; i++){ | |
587 | char tmpbuf[DISK_PAGE_SIZE]; | |
588 | if(overlay_bitmap->get(i)){ | |
589 | src_fd = overlay_fd; | |
590 | src_file = overlay_filename; | |
591 | }else if(ckpt_bitmap && ckpt_bitmap->get(i)){ | |
592 | src_fd = ckpt_fd; | |
593 | src_file = ckpt_filename; | |
594 | }else | |
595 | continue; | |
596 | lseek(src_fd,i * DISK_PAGE_SIZE, SEEK_SET); | |
597 | read(src_fd,tmpbuf,DISK_PAGE_SIZE); | |
598 | lseek(to_fd,i * DISK_PAGE_SIZE, SEEK_SET); | |
599 | if(write(to_fd,tmpbuf,DISK_PAGE_SIZE) != DISK_PAGE_SIZE){ | |
600 | fprintf(debug_file,"%s:Error writing checkpoint file %s offset %llx\n",part_name,buf,i*DISK_PAGE_SIZE); | |
601 | perror(""); | |
602 | return false; | |
603 | } | |
604 | } | |
605 | close(to_fd); | |
606 | return ret; | |
607 | } | |
608 | bool Partition::restore(const char * dir, const char * disk_name){ | |
609 | ||
610 | bool ret = true; | |
611 | char buf[1024]; | |
612 | sprintf(buf, "%s/%s.%s.dmp",dir,disk_name,part_name); | |
613 | FILE * fp = fopen(buf,"r"); | |
614 | assert(fp); | |
615 | ||
616 | ckpt_bitmap = new bitset(bitmap_size,part_name); | |
617 | ckpt_bitmap->restore(fp); | |
618 | fclose(fp); | |
619 | ||
620 | sprintf(buf,"%s/%s.%s.shadow",dir,disk_name,part_name); | |
621 | ckpt_filename = strdup(buf); | |
622 | ||
623 | if( (ckpt_fd = open(ckpt_filename, O_RDONLY|O_LARGEFILE)) == -1){ | |
624 | fprintf(debug_file,"%s: open() file %s errno: %d , %s \n", disk_name, ckpt_filename, errno, strerror(errno)); | |
625 | ret &= false; | |
626 | } | |
627 | ||
628 | return ret; | |
629 | } | |
630 | ||
631 | uint32_t Partition::Read(uint64_t lblkno, uint8_t *buf, uint32_t numblks){ | |
632 | ||
633 | uint64_t relative_lblk = lblkno - start_lblk; // lblk from start of backup file | |
634 | uint64_t bitmap_pos; | |
635 | int src_fd = -1; | |
636 | const char * src_file = 0; | |
637 | ||
638 | uint32_t blocks_read = 0; | |
639 | ||
640 | char tmpbuf[DISK_PAGE_SIZE]; | |
641 | bzero(tmpbuf,DISK_PAGE_SIZE); | |
642 | ||
643 | while(numblks != blocks_read){ | |
644 | uint32_t blks_in_cur_page = (BLOCKS_PER_PAGE - relative_lblk % BLOCKS_PER_PAGE)\ | |
645 | < (numblks - blocks_read) ? (BLOCKS_PER_PAGE - relative_lblk % BLOCKS_PER_PAGE):numblks - blocks_read; | |
646 | ||
647 | bitmap_pos = relative_lblk/BLOCKS_PER_PAGE; // 1 bit for every BLOCKS_PER_PAGE lblks | |
648 | ||
649 | if(overlay_bitmap->get(bitmap_pos)){ | |
650 | src_fd = overlay_fd; // page has been modified earlier | |
651 | src_file = overlay_filename; | |
652 | lblk_reads_overlay += blks_in_cur_page; | |
653 | }else if(ckpt_bitmap && ckpt_bitmap->get(bitmap_pos)){ | |
654 | src_fd = ckpt_fd; // page has been modified and is present in the chpt | |
655 | src_file = ckpt_filename; | |
656 | lblk_reads_ckpt += blks_in_cur_page; | |
657 | }else{ | |
658 | src_fd = primary_fd; // page has not been written to yet. read from | |
659 | // actual partition | |
660 | src_file = part_filename; | |
661 | lblk_reads_primary += blks_in_cur_page; | |
662 | } | |
663 | ||
664 | if(lseek(src_fd,bitmap_pos * DISK_PAGE_SIZE ,SEEK_SET) != bitmap_pos * DISK_PAGE_SIZE){ | |
665 | // the partition file does not have requested lblk. This may happen if | |
666 | // vtoc specified a different parition size than the actual file size, | |
667 | // or there may be partially filled lblk. In this case we can't | |
668 | // write(barring certain cases). We print an error message and | |
669 | // continue. The user needs to decide if this is fatal or not. | |
670 | 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); | |
671 | perror(""); | |
672 | return blocks_read; | |
673 | } | |
674 | ||
675 | if(read(src_fd, tmpbuf, DISK_PAGE_SIZE) != DISK_PAGE_SIZE){ | |
676 | // src_fd is smaller than we thought. Flag an error, leave it to | |
677 | // user to decide if its fatal. XXX cannot happen if DISK_PAGE_SIZE | |
678 | // == BYTES_PER_SECTOR | |
679 | 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); | |
680 | perror(""); | |
681 | } | |
682 | ||
683 | bcopy(tmpbuf + (relative_lblk % BLOCKS_PER_PAGE) *BYTES_PER_SECTOR,\ | |
684 | buf, blks_in_cur_page*BYTES_PER_SECTOR); | |
685 | ||
686 | blocks_read += blks_in_cur_page; | |
687 | buf += blks_in_cur_page * BYTES_PER_SECTOR; | |
688 | relative_lblk += blks_in_cur_page; | |
689 | } | |
690 | ||
691 | lblk_reads += blocks_read; | |
692 | return blocks_read; | |
693 | } | |
694 | ||
695 | // return the number of lblks written | |
696 | uint32_t Partition::Write(uint64_t lblkno, uint8_t *buf, uint32_t numblks){ | |
697 | ||
698 | const uint32_t blocks_to_write = numblks; | |
699 | uint64_t relative_lblk = lblkno - start_lblk; // lblk from start of backup file | |
700 | uint64_t bitmap_pos; | |
701 | int src_fd = -1; | |
702 | int to_fd = rw ? primary_fd:overlay_fd; // file to which data would be written | |
703 | assert(to_fd != -1); | |
704 | char tmpbuf[DISK_PAGE_SIZE]; // read/write DISK_PAGE_SIZE at a time | |
705 | const char * src_file = 0; | |
706 | const char * to_file = rw?part_filename: overlay_filename; | |
707 | ||
708 | bzero(tmpbuf,DISK_PAGE_SIZE); | |
709 | uint32_t blocks_written = 0; | |
710 | ||
711 | while(numblks != blocks_written){ | |
712 | ||
713 | uint32_t blks_in_cur_page = (BLOCKS_PER_PAGE - relative_lblk % BLOCKS_PER_PAGE)\ | |
714 | < (numblks - blocks_written) ? (BLOCKS_PER_PAGE - relative_lblk % BLOCKS_PER_PAGE):numblks - blocks_written; | |
715 | ||
716 | ||
717 | bitmap_pos = relative_lblk/BLOCKS_PER_PAGE; // 1 bit for every BLOCKS_PER_PAGE lblks | |
718 | ||
719 | if(overlay_bitmap->get(bitmap_pos)){ | |
720 | src_fd = overlay_fd; // page has been modified earlier | |
721 | src_file = overlay_filename; | |
722 | }else if(ckpt_bitmap && ckpt_bitmap->get(bitmap_pos)){ | |
723 | src_fd = ckpt_fd; // page has been modified and is present in the chpt | |
724 | src_file = ckpt_filename; | |
725 | }else{ | |
726 | src_fd = primary_fd; // page has not been written to yet. Next | |
727 | // writes will have src_fd = overlay_fd if this | |
728 | // is ro option | |
729 | src_file = part_filename; | |
730 | if(!rw) overlay_bitmap->set(bitmap_pos); | |
731 | } | |
732 | ||
733 | if(lseek(src_fd,bitmap_pos * DISK_PAGE_SIZE ,SEEK_SET) != bitmap_pos * DISK_PAGE_SIZE){ | |
734 | // the parition file does not have requested lblk. This may happen if | |
735 | // vtoc specified a different parition size than the actual file size, | |
736 | // or there may be partially filled lblk. In this case we can't | |
737 | // write(barring certain cases). We print an error message and | |
738 | // continue. The user needs to decide if this is fatal or not. | |
739 | 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); | |
740 | perror(""); | |
741 | return blocks_written; | |
742 | } | |
743 | ||
744 | if(read(src_fd, tmpbuf, DISK_PAGE_SIZE) != DISK_PAGE_SIZE){ | |
745 | // src_fd is smaller than we thought. Flag an error, leave it to | |
746 | // user to decide if its fatal | |
747 | 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); | |
748 | perror(""); | |
749 | } | |
750 | ||
751 | bcopy(buf,tmpbuf + (relative_lblk % BLOCKS_PER_PAGE)*BYTES_PER_SECTOR,\ | |
752 | blks_in_cur_page*BYTES_PER_SECTOR); | |
753 | ||
754 | if(lseek(to_fd,bitmap_pos * DISK_PAGE_SIZE ,SEEK_SET) != bitmap_pos * DISK_PAGE_SIZE ){ | |
755 | // should not happen, since this is atleast the vtoc specified size, | |
756 | // or the real file(rw=true), in which case the lseek would fail above | |
757 | 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); | |
758 | perror(""); | |
759 | return blocks_written; | |
760 | } | |
761 | ||
762 | if(write(to_fd, tmpbuf, DISK_PAGE_SIZE) != DISK_PAGE_SIZE){ | |
763 | // should not happen | |
764 | 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); | |
765 | perror(""); | |
766 | } | |
767 | ||
768 | buf += blks_in_cur_page * BYTES_PER_SECTOR; | |
769 | blocks_written += blks_in_cur_page; | |
770 | relative_lblk += blks_in_cur_page; | |
771 | ||
772 | if(rw) | |
773 | lblk_writes_primary += blks_in_cur_page; | |
774 | else | |
775 | lblk_writes_overlay += blks_in_cur_page; | |
776 | ||
777 | } | |
778 | ||
779 | lblk_writes += blocks_written; | |
780 | return blocks_written; | |
781 | ||
782 | } |