Import version 1.8.3
[s390-tools.git] / zipl / src / disk.c
blob06802c7f60c9357bf0af594fa787d48e6ad4cd90
1 /*
2 * s390-tools/zipl/src/disk.c
3 * Functions to handle disk layout specific operations.
5 * Copyright IBM Corp. 2001, 2009.
7 * Author(s): Carsten Otte <cotte@de.ibm.com>
8 * Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
9 */
11 #include "disk.h"
12 #include "job.h"
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/ioctl.h>
17 #include <sys/vfs.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <errno.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <dirent.h>
25 #include <limits.h>
27 #include "error.h"
28 #include "misc.h"
29 #include "proc.h"
31 /* from linux/fs.h */
32 #define FIBMAP _IO(0x00,1)
33 #define FIGETBSZ _IO(0x00,2)
34 #define BLKGETSIZE _IO(0x12,96)
35 #define BLKSSZGET _IO(0x12,104)
37 /* from linux/hdregs.h */
38 #define HDIO_GETGEO 0x0301
40 #define DASD_IOCTL_LETTER 'D'
41 #define BIODASDINFO _IOR(DASD_IOCTL_LETTER, 1, \
42 struct dasd_information)
43 #define DASD_PARTN_MASK 0x03
44 #define SCSI_PARTN_MASK 0x0f
46 /* Definitions for dasd device driver, taken from linux/include/asm/dasd.h */
47 struct dasd_information {
48 unsigned int devno; /* S/390 devno */
49 unsigned int real_devno; /* for aliases */
50 unsigned int schid; /* S/390 subchannel identifier */
51 unsigned int cu_type : 16; /* from SenseID */
52 unsigned int cu_model : 8; /* from SenseID */
53 unsigned int dev_type : 16; /* from SenseID */
54 unsigned int dev_model : 8; /* from SenseID */
55 unsigned int open_count;
56 unsigned int req_queue_len;
57 unsigned int chanq_len; /* length of chanq */
58 char type[4]; /* from discipline.name */
59 unsigned int status; /* current device level */
60 unsigned int label_block; /* where to find the VOLSER */
61 unsigned int FBA_layout; /* fixed block size (like AIXVOL) */
62 unsigned int characteristics_size;
63 unsigned int confdata_size;
64 char characteristics[64]; /* from read_device_characteristics */
65 char configuration_data[256]; /* from read_configuration_data */
68 static int
69 disk_determine_dasd_type(struct disk_info *data,
70 struct dasd_information dasd_info)
72 if (strncmp(dasd_info.type, "FBA ",4) == 0)
73 data->type = disk_type_fba;
74 else if (strncmp(dasd_info.type, "DIAG",4) == 0)
75 data->type = disk_type_diag;
76 else if (strncmp(dasd_info.type, "ECKD",4) == 0) {
77 if (dasd_info.FBA_layout)
78 data->type = disk_type_eckd_classic;
79 else
80 data->type = disk_type_eckd_compatible;
81 } else {
82 error_reason("Unknown DASD type");
83 return -1;
85 return 0;
88 /* Return non-zero for ECKD type. */
89 int
90 disk_is_eckd(disk_type_t type)
92 return (type == disk_type_eckd_classic ||
93 type == disk_type_eckd_compatible);
96 int
97 disk_get_info(const char* device, struct job_target_data* target,
98 struct disk_info** info)
100 struct stat stats;
101 struct stat script_stats;
102 struct proc_part_entry part_entry;
103 struct proc_dev_entry dev_entry;
104 struct dasd_information dasd_info;
105 struct disk_info *data;
106 int fd;
107 long devsize;
108 FILE *fh;
109 char *script_pre = "/lib/s390-tools/zipl_helper.";
110 char script_file[80];
111 char ppn_cmd[80];
112 char buffer[80];
113 char value[40];
114 int majnum, minnum;
115 int checkparm;
117 /* Get file information */
118 if (stat(device, &stats)) {
119 error_reason(strerror(errno));
120 return -1;
122 /* Open device file */
123 fd = open(device, O_RDWR);
124 if (fd == -1) {
125 error_reason(strerror(errno));
126 return -1;
128 /* Get memory for result */
129 data = (struct disk_info *) misc_malloc(sizeof(struct disk_info));
130 if (data == NULL) {
131 close(fd);
132 return -1;
134 memset((void *) data, 0, sizeof(struct disk_info));
135 /* Try to get device driver name */
136 if (proc_dev_get_entry(stats.st_rdev, 1, &dev_entry) == 0) {
137 data->drv_name = misc_strdup(dev_entry.name);
138 proc_dev_free_entry(&dev_entry);
139 } else {
140 fprintf(stderr, "Warning: Could not determine driver name for "
141 "major %d from /proc/devices\n", major(stats.st_rdev));
142 fprintf(stderr, "Warning: Preparing a logical device for boot "
143 "might fail\n");
145 data->source = source_user;
146 /* Check if targetbase script is available */
147 strcpy(script_file, script_pre);
148 if (data->drv_name) {
149 strcat(script_file, data->drv_name);
151 if ((target->targetbase == NULL) &&
152 (!stat(script_file, &script_stats))) {
153 data->source = source_script;
154 /* Run targetbase script */
155 strcpy(ppn_cmd, script_file);
156 strcat(ppn_cmd, " ");
157 strcat(ppn_cmd, target->bootmap_dir);
158 printf("Run %s\n", ppn_cmd);
159 fh = popen(ppn_cmd, "r");
160 if (fh == NULL) {
161 error_reason("Failed to run popen(%s,\"r\",)");
162 goto out_close;
164 checkparm = 0;
165 while (fgets(buffer, 80, fh) != NULL) {
166 if (sscanf(buffer, "targetbase=%s", value) == 1) {
167 target->targetbase = misc_strdup(value);
168 checkparm++;
170 if (sscanf(buffer, "targettype=%s", value) == 1) {
171 type_from_target(value, &target->targettype);
172 checkparm++;
174 if (sscanf(buffer, "targetgeometry=%s", value) == 1) {
175 target->targetcylinders =
176 atoi(strtok(value, ","));
177 target->targetheads = atoi(strtok(NULL, ","));
178 target->targetsectors = atoi(strtok(NULL, ","));
179 checkparm++;
181 if (sscanf(buffer, "targetblocksize=%s", value) == 1) {
182 target->targetblocksize = atoi(value);
183 checkparm++;
185 if (sscanf(buffer, "targetoffset=%s", value) == 1) {
186 target->targetoffset = atol(value);
187 checkparm++;
190 if (pclose(fh) == -1) {
191 error_reason("Failed to run pclose");
192 goto out_close;
194 if ((!disk_is_eckd(target->targettype) && checkparm < 4) ||
195 (disk_is_eckd(target->targettype) && checkparm != 5)) {
196 error_reason("Target parameters missing from script");
197 goto out_close;
201 /* Get disk geometry. Note: geo.start contains a sector number
202 * offset measured in physical blocks, not sectors (512 bytes) */
203 if (target->targetbase != NULL) {
204 data->geo.heads = target->targetheads;
205 data->geo.sectors = target->targetsectors;
206 data->geo.cylinders = target->targetcylinders;
207 data->geo.start = target->targetoffset;
208 } else {
209 data->source = source_auto;
210 if (ioctl(fd, HDIO_GETGEO, &data->geo)) {
211 error_reason("Could not get disk geometry");
212 goto out_close;
215 if ((data->source == source_user) || (data->source == source_script)) {
216 data->devno = -1;
217 data->phy_block_size = target->targetblocksize;
218 } else {
219 /* Get DASD information */
220 if (ioctl(fd, BIODASDINFO, &dasd_info))
221 data->devno = -1;
222 else
223 data->devno = dasd_info.devno;
224 /* Get physical block size */
225 if (ioctl(fd, BLKSSZGET, &data->phy_block_size)) {
226 error_reason("Could not get blocksize");
227 goto out_close;
230 /* Get size of device in sectors (512 byte) */
231 if (ioctl(fd, BLKGETSIZE, &devsize)) {
232 error_reason("Could not get device size");
233 goto out_close;
235 if ((data->source == source_user) || (data->source == source_script)) {
236 data->type = target->targettype;
237 data->partnum = 0;
238 /* Get file information */
239 if (sscanf(target->targetbase, "%d:%d", &majnum, &minnum) == 2)
240 data->device = makedev(majnum, minnum);
241 else {
242 if (stat(target->targetbase, &stats)) {
243 error_reason(strerror(errno));
244 goto out_close;
246 if (!S_ISBLK(stats.st_mode)) {
247 error_reason("Target base device '%s' is not "
248 "a block device",
249 target->targetbase);
250 goto out_close;
252 data->device = stats.st_rdev;
254 goto type_determined;
256 /* Determine disk type */
257 if (!data->drv_name) {
258 /* Driver name cannot be read */
259 if (data->devno == -1) {
260 if (data->geo.start) {
261 /* SCSI partition */
262 data->type = disk_type_scsi;
263 data->partnum = stats.st_rdev & SCSI_PARTN_MASK;
264 data->device = stats.st_rdev & ~SCSI_PARTN_MASK;
265 } else {
266 /* SCSI disk */
267 data->type = disk_type_scsi;
268 data->partnum = 0;
269 data->device = stats.st_rdev;
271 } else {
272 /* DASD */
273 if (disk_determine_dasd_type(data, dasd_info))
274 goto out_close;
275 data->partnum = stats.st_rdev & DASD_PARTN_MASK;
276 data->device = stats.st_rdev & ~DASD_PARTN_MASK;
278 } else if (strcmp(data->drv_name, "dasd") == 0) {
279 /* Driver name is 'dasd' */
280 if (data->devno == -1) {
281 error_reason("Could not determine DASD type");
282 goto out_close;
284 if (disk_determine_dasd_type(data, dasd_info))
285 goto out_close;
286 data->partnum = stats.st_rdev & DASD_PARTN_MASK;
287 data->device = stats.st_rdev & ~DASD_PARTN_MASK;
288 } else if (strcmp(data->drv_name, "sd") == 0 ||
289 strcmp(data->drv_name, "virtblk") == 0) {
290 /* Driver name is 'sd' or 'virtblk' */
291 if (data->devno != -1) {
292 error_reason("Unsupported device driver '%s' "
293 "for disk type DASD", data->drv_name);
294 goto out_close;
296 data->type = disk_type_scsi;
297 data->partnum = stats.st_rdev & SCSI_PARTN_MASK;
298 data->device = stats.st_rdev & ~SCSI_PARTN_MASK;
299 } else {
300 /* Driver name is unknown */
301 error_reason("Unsupported device driver '%s'", data->drv_name);
302 goto out_close;
305 type_determined:
306 /* Check for valid CHS geometry data. */
307 if (disk_is_eckd(data->type) && (data->geo.cylinders == 0 ||
308 data->geo.heads == 0 || data->geo.sectors == 0)) {
309 error_reason("Invalid disk geometry (CHS=%d/%d/%d)",
310 data->geo.cylinders, data->geo.heads,
311 data->geo.sectors);
312 goto out_close;
314 /* Convert device size to size in physical blocks */
315 data->phy_blocks = devsize / (data->phy_block_size / 512);
316 if (data->partnum != 0)
317 data->partition = stats.st_rdev;
318 /* Try to get device name */
319 if (proc_part_get_entry(data->device, &part_entry) == 0) {
320 data->name = misc_strdup(part_entry.name);
321 proc_part_free_entry(&part_entry);
322 if (data->name == NULL)
323 goto out_close;
325 /* There is no easy way to find out whether there is a file system
326 * on this device, so we set the respective block size to an invalid
327 * value. */
328 data->fs_block_size = -1;
329 close(fd);
330 *info = data;
331 return 0;
332 out_close:
333 close(fd);
334 free(data);
335 return -1;
341 disk_get_info_from_file(const char* filename, struct job_target_data* target,
342 struct disk_info** info)
344 struct stat stats;
345 char* device;
346 int blocksize;
347 int fd;
348 int rc;
350 if (stat(filename, &stats)) {
351 error_reason(strerror(errno));
352 return -1;
354 /* Retrieve file system block size */
355 fd = open(filename, O_RDONLY);
356 if (fd == -1) {
357 error_reason(strerror(errno));
358 return -1;
360 rc = ioctl(fd, FIGETBSZ, &blocksize);
361 close(fd);
362 if (rc == -1) {
363 error_reason("Could not get file system block size for '%s'",
364 filename);
365 return -1;
367 /* Create temporary device file */
368 rc = misc_temp_dev(stats.st_dev, 1, &device);
369 if (rc)
370 return -1;
371 /* Get device info */
372 rc = disk_get_info(device, target, info);
373 if (rc == 0)
374 (*info)->fs_block_size = blocksize;
375 /* Clean up */
376 misc_free_temp_dev(device);
377 return rc;
381 void
382 disk_free_info(struct disk_info* info)
384 if (info->name)
385 free(info->name);
386 if (info->drv_name)
387 free(info->drv_name);
388 free(info);
392 #ifndef REISERFS_SUPER_MAGIC
393 #define REISERFS_SUPER_MAGIC 0x52654973
394 #endif /* not REISERFS_SUPER_MAGIC */
396 #ifndef REISERFS_IOC_UNPACK
397 #define REISERFS_IOC_UNPACK _IOW(0xCD,1,long)
398 #endif /* not REISERFS_IOC_UNPACK */
400 /* Retrieve the physical blocknumber (block on disk) of the specified logical
401 * block (block in file). FD provides the file descriptor, LOGICAL is the
402 * logical block number. Upon success, return 0 and store the physical
403 * blocknumber in the variable pointed to by PHYSICAL. Return non-zero
404 * otherwise. */
406 disk_get_blocknum(int fd, blocknum_t logical, blocknum_t* physical,
407 struct disk_info* info)
409 struct statfs buf;
410 blocknum_t phy_per_fs;
411 int mapped;
412 int subblock;
414 /* Get file system type */
415 if (fstatfs(fd, &buf)) {
416 error_reason(strerror(errno));
417 return -1;
419 /* Files on ReiserFS need unpacking */
420 if (buf.f_type == REISERFS_SUPER_MAGIC) {
421 if (ioctl(fd, REISERFS_IOC_UNPACK, 1)) {
422 error_reason("Could not unpack ReiserFS file");
423 return -1;
426 /* Get mapping in file system blocks */
427 phy_per_fs = info->fs_block_size / info->phy_block_size;
428 mapped = logical / phy_per_fs;
429 subblock = logical % phy_per_fs;
430 if (ioctl(fd, FIBMAP, &mapped)) {
431 error_reason("Could not get file mapping");
432 return -1;
434 if (mapped == 0) {
435 /* This is a hole in the file */
436 *physical = 0;
437 } else {
438 /* Convert file system block to physical */
439 *physical = mapped * phy_per_fs + subblock;
440 /* Add partition start */
441 *physical += info->geo.start;
443 return 0;
447 /* Return the cylinder on which the block number BLOCKNUM is stored on the
448 * CHS device identified by INFO. */
450 disk_cyl_from_blocknum(blocknum_t blocknum, struct disk_info* info)
452 return blocknum / (info->geo.heads * info->geo.sectors);
456 /* Return the head on which the block number BLOCKNUM is stored on the
457 * CHS device identified by INFO. */
459 disk_head_from_blocknum(blocknum_t blocknum, struct disk_info* info)
461 return (blocknum / info->geo.sectors) % info->geo.heads;
465 /* Return the sector on which the block number BLOCKNUM is stored on the
466 * CHS device identified by INFO. */
468 disk_sec_from_blocknum(blocknum_t blocknum, struct disk_info* info)
470 return blocknum % info->geo.sectors + 1;
474 /* Create a block pointer in memory at location PTR which represents the
475 * given blocknumber BLOCKNUM. INFO provides information about the disk
476 * layout. */
477 void
478 disk_blockptr_from_blocknum(disk_blockptr_t* ptr, blocknum_t blocknum,
479 struct disk_info* info)
481 switch (info->type) {
482 case disk_type_scsi:
483 case disk_type_fba:
484 case disk_type_diag:
485 ptr->linear.block = blocknum;
486 ptr->linear.size = info->phy_block_size;
487 ptr->linear.blockct = 0;
488 break;
489 case disk_type_eckd_classic:
490 case disk_type_eckd_compatible:
491 if (blocknum == 0) {
492 /* Special case: zero blocknum will be expanded to
493 * size * (blockct+1) bytes of zeroes. */
494 ptr->chs.cyl = 0;
495 ptr->chs.head = 0;
496 ptr->chs.sec = 0;
497 } else {
499 ptr->chs.cyl = disk_cyl_from_blocknum(blocknum, info);
500 ptr->chs.head = disk_head_from_blocknum(blocknum,
501 info);
502 ptr->chs.sec = disk_sec_from_blocknum(blocknum, info);
504 ptr->chs.size = info->phy_block_size;
505 ptr->chs.blockct = 0;
506 break;
511 /* Write BYTECOUNT bytes of data from memory at location DATA as a block to
512 * the file identified by file descriptor FD. Make sure that the data is
513 * aligned on a block size boundary and that at most INFO->PHY_BLOCK_SIZE
514 * bytes are written. INFO provides information about the disk layout. Upon
515 * success, return 0 and store the pointer to the resulting disk block to BLOCK
516 * (if BLOCK is not NULL). Return non-zero otherwise. */
518 disk_write_block_aligned(int fd, const void* data, size_t bytecount,
519 disk_blockptr_t* block, struct disk_info* info)
521 blocknum_t current_block;
522 blocknum_t blocknum;
523 off_t current_pos;
524 int align;
526 current_pos = lseek(fd, 0, SEEK_CUR);
527 if (current_pos == -1) {
528 error_text(strerror(errno));
529 return -1;
531 /* Ensure block alignment of current file pos */
532 align = info->phy_block_size;
533 if (current_pos % align != 0) {
534 current_pos = lseek(fd, align - current_pos % align, SEEK_CUR);
535 if (current_pos == -1) {
536 error_text(strerror(errno));
537 return -1;
540 current_block = current_pos / align;
541 /* Ensure maximum size */
542 if (bytecount > (size_t) align)
543 bytecount = align;
544 /* Write data block */
545 if (misc_write(fd, data, bytecount))
546 return -1;
547 if (block != NULL) {
548 /* Store block pointer */
549 if (disk_get_blocknum(fd, current_block, &blocknum, info))
550 return -1;
551 disk_blockptr_from_blocknum(block, blocknum, info);
553 return 0;
557 /* Write BYTECOUNT bytes from memory at location BUFFER to the file identified
558 * by file descriptor FD and return the list of pointers to the disk blocks
559 * that make up the respective part of the file. Upon success return the number
560 * of blocks and set BLOCKLIST to point to the uncompressed list. Return zero
561 * otherwise. Note that the data is written to a file position which is aligned
562 * on a block size boundary. */
563 blocknum_t
564 disk_write_block_buffer(int fd, const void* buffer, size_t bytecount,
565 disk_blockptr_t** blocklist,
566 struct disk_info* info)
568 disk_blockptr_t* list;
569 blocknum_t count;
570 blocknum_t i;
571 size_t written;
572 size_t chunk_size;
573 int rc;
575 count = (bytecount + info->phy_block_size - 1) / info->phy_block_size;
576 list = (disk_blockptr_t *) misc_malloc(sizeof(disk_blockptr_t) *
577 count);
578 if (list == NULL) {
579 close(fd);
580 return 0;
582 memset((void *) list, 0, sizeof(disk_blockptr_t) * count);
583 /* Build list */
584 for (i=0, written=0; i < count; i++, written += chunk_size) {
585 chunk_size = bytecount - written;
586 if (chunk_size > (size_t) info->phy_block_size)
587 chunk_size = info->phy_block_size;
588 rc = disk_write_block_aligned(fd, VOID_ADD(buffer, written),
589 chunk_size,
590 &list[i], info);
591 if (rc) {
592 free(list);
593 return 0;
596 *blocklist = list;
597 return count;
601 /* Print device node. */
602 void
603 disk_print_devt(dev_t d)
605 printf("%02x:%02x", major(d), minor(d));
609 /* Return a name for a given disk TYPE. */
610 char *
611 disk_get_type_name(disk_type_t type)
613 switch (type) {
614 case disk_type_scsi:
615 return "SCSI disk layout";
616 case disk_type_fba:
617 return "FBA disk layout";
618 case disk_type_diag:
619 return "DIAG disk layout";
620 case disk_type_eckd_classic:
621 return "ECKD/linux disk layout";
622 case disk_type_eckd_compatible:
623 return "ECKD/compatible disk layout";
624 default:
625 return "Unknown disk type";
630 /* Return non-zero for ECKD large volumes. */
632 disk_is_large_volume(struct disk_info* info)
634 return (info->type == disk_type_eckd_classic ||
635 info->type == disk_type_eckd_compatible) &&
636 info->geo.cylinders == 0xfffe;
640 /* Print textual representation of INFO contents. */
641 void
642 disk_print_info(struct disk_info* info)
644 char footnote[4] = "";
645 if ((info->source == source_user) || (info->source == source_script))
646 strcpy(footnote, " *)");
648 printf(" Device..........................: ");
649 disk_print_devt(info->device);
650 printf("\n");
651 if (info->partnum != 0) {
652 printf(" Partition.......................: ");
653 disk_print_devt(info->partition);
654 printf("\n");
656 if (info->name) {
657 printf(" Device name.....................: %s%s\n",
658 info->name, footnote);
660 if (info->drv_name) {
661 printf(" Device driver name..............: %s\n",
662 info->drv_name);
664 if (((info->type == disk_type_fba) ||
665 (info->type == disk_type_diag) ||
666 (info->type == disk_type_eckd_classic) ||
667 (info->type == disk_type_eckd_compatible)) &&
668 (info->source == source_auto)) {
669 printf(" DASD device number..............: %04x\n",
670 info->devno);
672 printf(" Type............................: disk %s\n",
673 (info->partnum != 0) ? "partition" : "device");
674 printf(" Disk layout.....................: %s%s\n",
675 disk_get_type_name(info->type), footnote);
676 printf(" Geometry - heads................: %d%s\n",
677 info->geo.heads, footnote);
678 printf(" Geometry - sectors..............: %d%s\n",
679 info->geo.sectors, footnote);
680 if (disk_is_large_volume(info)) {
681 /* ECKD large volume. There is not enough information
682 * available in INFO to calculate disk cylinder size. */
683 printf(" Geometry - cylinders............: > 65534\n");
684 } else {
685 printf(" Geometry - cylinders............: %d%s\n",
686 info->geo.cylinders, footnote);
688 printf(" Geometry - start................: %ld%s\n",
689 info->geo.start, footnote);
690 if (info->fs_block_size >= 0)
691 printf(" File system block size..........: %d\n",
692 info->fs_block_size);
693 printf(" Physical block size.............: %d%s\n",
694 info->phy_block_size, footnote);
695 printf(" Device size in physical blocks..: %ld\n",
696 (long) info->phy_blocks);
697 if (info->source == source_user)
698 printf(" *) Data provided by user.\n");
699 if (info->source == source_script)
700 printf(" *) Data provided by script.\n");
704 /* Check whether a block is a zero block which identifies a hole in a file.
705 * Return non-zero if BLOCK is a zero block, 0 otherwise. */
707 disk_is_zero_block(disk_blockptr_t* block, struct disk_info* info)
709 switch (info->type) {
710 case disk_type_scsi:
711 case disk_type_fba:
712 return block->linear.block == 0;
713 case disk_type_eckd_classic:
714 case disk_type_eckd_compatible:
715 return (block->chs.cyl == 0) && (block->chs.head == 0) &&
716 (block->chs.sec == 0);
717 default:
718 break;
720 return 0;
724 #define DASD_MAX_LINK_COUNT 255
725 #define SCSI_MAX_LINK_COUNT 65535
727 /* Check whether two block pointers FIRST and SECOND can be merged into
728 * one block pointer by increasing the block count field of the first
729 * pointer. INFO provides information about the disk type. Return non-zero if
730 * blocks can be merged, 0 otherwise. */
731 static int
732 can_merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
733 struct disk_info* info)
735 int max_count;
737 /* Zero blocks can never be merged */
738 if (disk_is_zero_block(first, info) || disk_is_zero_block(second, info))
739 return 0;
740 if (info->type == disk_type_scsi)
741 max_count = SCSI_MAX_LINK_COUNT;
742 else
743 max_count = DASD_MAX_LINK_COUNT;
744 switch (info->type) {
745 case disk_type_scsi:
746 case disk_type_fba:
747 /* Check link count limits */
748 if (((int) first->linear.blockct) +
749 ((int) second->linear.blockct) + 1 > max_count)
750 return 0;
751 if (first->linear.block + first->linear.blockct + 1 ==
752 second->linear.block)
753 return 1;
754 break;
755 case disk_type_eckd_classic:
756 case disk_type_eckd_compatible:
757 /* Check link count limits */
758 if (((int) first->chs.blockct) +
759 ((int) second->chs.blockct) + 1 > max_count)
760 return 0;
761 if ((first->chs.cyl == second->chs.cyl) &&
762 (first->chs.head == second->chs.head) &&
763 (first->chs.sec + first->chs.blockct + 1 ==
764 second->chs.sec))
765 return 1;
766 break;
767 case disk_type_diag:
768 break;
770 return 0;
774 /* Merge two block pointers FIRST and SECOND into one pointer. The resulting
775 * pointer is stored in FIRST. INFO provides information about the disk
776 * type. */
777 static void
778 merge_blocks(disk_blockptr_t* first, disk_blockptr_t* second,
779 struct disk_info* info)
781 switch (info->type) {
782 case disk_type_scsi:
783 case disk_type_fba:
784 first->linear.blockct += second->linear.blockct + 1;
785 break;
786 case disk_type_eckd_classic:
787 case disk_type_eckd_compatible:
788 first->chs.blockct += second->chs.blockct + 1;
789 break;
790 case disk_type_diag:
791 /* Should not happen */
792 break;
797 /* Analyze COUNT elements in LIST and try to merge pointers to adjacent
798 * blocks. INFO provides information about the disk type. Return the new
799 * number of elements in the list. */
800 blocknum_t
801 disk_compact_blocklist(disk_blockptr_t* list, blocknum_t count,
802 struct disk_info* info)
804 blocknum_t i;
805 blocknum_t last;
807 if (count < 2)
808 return count;
809 for (i=1, last=0; i < count; i++) {
810 if (can_merge_blocks(&list[last], &list[i], info)) {
811 merge_blocks(&list[last], &list[i], info);
812 } else {
813 list[++last] = list[i];
816 return last + 1;
820 /* Retrieve a list of pointers to the disk blocks that make up the file
821 * specified by FILENAME. Upon success, return the number of blocks and set
822 * BLOCKLIST to point to the uncompacted list. INFO provides information
823 * about the device which contains the file. Return zero otherwise. */
824 blocknum_t
825 disk_get_blocklist_from_file(const char* filename, disk_blockptr_t** blocklist,
826 struct disk_info* info)
828 disk_blockptr_t* list;
829 struct stat stats;
830 int fd;
831 blocknum_t count;
832 blocknum_t i;
833 blocknum_t blocknum;
835 fd = open(filename, O_RDONLY);
836 if (fd == -1) {
837 error_reason(strerror(errno));
838 error_text("Could not open file '%s'", filename);
839 return 0;
841 if (fstat(fd, &stats)) {
842 error_reason(strerror(errno));
843 error_text("Could not get information for file '%s'",
844 filename);
845 close (fd);
846 return 0;
848 /* Get number of blocks */
849 count = ((blocknum_t) stats.st_size +
850 info->phy_block_size - 1) / info->phy_block_size;
851 if (count == 0) {
852 error_reason("Could not read empty file '%s'", filename);
853 close(fd);
854 return 0;
856 list = (disk_blockptr_t *) misc_malloc(sizeof(disk_blockptr_t) *
857 count);
858 if (list == NULL) {
859 close(fd);
860 return 0;
862 memset((void *) list, 0, sizeof(disk_blockptr_t) * count);
863 /* Build list */
864 for (i=0; i < count; i++) {
865 if (disk_get_blocknum(fd, i, &blocknum, info)) {
866 free(list);
867 close(fd);
868 return 0;
870 disk_blockptr_from_blocknum(&list[i], blocknum, info);
872 close(fd);
873 *blocklist = list;
874 return count;
877 /* Check whether input device is in subchannel set 0.
878 * Path to "dev" attribute containing the major/minor number depends on
879 * whether option CONFIG_SYSFS_DEPRECATED is set or not */
881 int disk_check_subchannel_set(int devno, dev_t device, char* dev_name)
883 struct dirent *direntp;
884 DIR* fdd;
885 static const char sys_bus_ccw_dev_filename[] = "/sys/bus/ccw/devices";
886 char dev_file[PATH_MAX];
887 char *buffer;
888 int minor, major;
890 snprintf(dev_file, PATH_MAX, "%s/0.0.%04x", sys_bus_ccw_dev_filename,
891 devno);
892 fdd = opendir(dev_file);
893 if (!fdd)
894 goto out_with_warning;
895 while ((direntp = readdir(fdd)))
896 if (strncmp(direntp->d_name, "block:", 6) == 0)
897 break;
898 if (direntp != NULL)
899 snprintf(dev_file, PATH_MAX, "%s/0.0.%04x/%s/dev",
900 sys_bus_ccw_dev_filename, devno, direntp->d_name);
901 else {
902 closedir(fdd);
903 snprintf(dev_file, PATH_MAX, "%s/0.0.%04x/block",
904 sys_bus_ccw_dev_filename, devno);
905 fdd = opendir(dev_file);
906 if (!fdd)
907 goto out_with_warning;
908 while ((direntp = readdir(fdd)))
909 if (strncmp(direntp->d_name, "dasd", 4) == 0)
910 break;
911 if (direntp == NULL)
912 goto out_with_warning;
913 snprintf(dev_file, PATH_MAX, "%s/0.0.%04x/block/%s/dev",
914 sys_bus_ccw_dev_filename, devno, direntp->d_name);
916 closedir(fdd);
917 if (misc_read_special_file(dev_file, &buffer, NULL, 1))
918 goto out_with_warning;
919 if (sscanf(buffer, "%i:%i", &major, &minor) != 2) {
920 free(buffer);
921 goto out_with_warning;
923 free(buffer);
924 if (makedev(major, minor) != device) {
925 error_reason("Dump target '%s' must belong to "
926 "subchannel set 0.", dev_name);
927 return -1;
929 return 0;
930 out_with_warning:
931 fprintf(stderr, "Warning: Could not determine whether dump target %s "
932 "belongs to subchannel set 0.\n", dev_name);
933 return 0;