Subject: [PATCH] [FEAT LS1501] dasdfmt: Add new formatting modes From: Jan Höppner Summary: dasdfmt: Add new formatting modes Description: Introduce new formatting modes 'quick' and 'expand' to either format an earlier formatted DASD that could potentially be re-initialized very easily or format unformatted tracks at the end of a device that was previously extended. Also add the command line argument --check to provide a function that checks a DASD volume for correct formatting. Upstream-ID: - Problem-ID: LS1501 Upstream-Description: dasdfmt: Add command line argument --check Provide a function to check a DASD volume for correct formatting. This can be useful in cases where only a few tracks of a volume are incorrectly formatted. All tracks on the device are checked. In an error case, the check process is stopped and appropriate messages are displayed. The --check command line argument can be combined with either -P, -p or -m to display progress on the console. Signed-off-by: Jan Höppner Signed-off-by: Stefan Haberland Signed-off-by: Jan Höppner --- dasdfmt/dasdfmt.8 | 5 + dasdfmt/dasdfmt.c | 244 ++++++++++++++++++++++++++++++++++++++++-------------- dasdfmt/dasdfmt.h | 4 3 files changed, 190 insertions(+), 63 deletions(-) --- a/dasdfmt/dasdfmt.8 +++ b/dasdfmt/dasdfmt.8 @@ -121,6 +121,11 @@ using \fB-b\fR (\fB--blocksize\fR). .RE .TP +\fB--check\fR +Perform a complete format check on a DASD volume. A blocksize can be specified +with \fB-b\fR (\fB--blocksize\fR). + +.TP \fB-r\fR \fIcylindercount\fR or \fB--requestsize\fR=\fIcylindercount\fR Number of cylinders to be processed in one formatting step. The value must be an integer in the range 1 - 255. --- a/dasdfmt/dasdfmt.c +++ b/dasdfmt/dasdfmt.c @@ -76,7 +76,8 @@ static void exit_usage(int exitcode) " the host access open count to ensure the device\n" " is not online on another operating system instance\n" " --norecordzero prevent storage server from modifying" - " record 0\n\n" + " record 0\n" + " --check perform complete format check on device\n\n" " is the volume identifier, which is converted\n" " to EBCDIC and written to disk.\n" " (6 characters, e.g. LNX001\n" @@ -163,7 +164,8 @@ static void print_etr(int p_new, int sta * Draw the progress indicator depending on what command line argument is set. * This can either be a progressbar, hashmarks, or percentage. */ -static void draw_progress(dasdfmt_info_t *info, int cyl, unsigned int cylinders) +static void draw_progress(dasdfmt_info_t *info, int cyl, unsigned int cylinders, + int aborted) { static int hashcount; static int started; @@ -175,7 +177,7 @@ static void draw_progress(dasdfmt_info_t if (info->print_progressbar) { printf("cyl %7d of %7d |", cyl, cylinders); p_new = cyl * 100 / cylinders; - if (p_new != p_old || !started) { + if (p_new != p_old || !started || aborted) { /* percent value has changed */ p_old = p_new; barlength = cyl * 33 / cylinders; @@ -184,6 +186,8 @@ static void draw_progress(dasdfmt_info_t for (i = barlength + 1; i <= 33; i++) printf("-"); printf("|%3d%%", p_new); + if (aborted) + p_new = 100; print_etr(p_new, started); started = 1; } @@ -284,11 +288,17 @@ static void evaluate_format_error(dasdfm unsigned int kl = 0; int blksize = cdata->expect.blksize; + if (info->print_progressbar || info->print_hashmarks) + printf("\n"); + /* - * Reading record zero will never happen. If the record in error is 0 - * nonetheless, the device is not formatted at all! + * If mode is not QUICK and a device format couldn't be determined, the + * device is considered not formatted. + * Also, reading record zero will never happen. If the record in error + * is 0 nonetheless, the device is not formatted at all as well! */ - if (cdata->rec == 0) { + if ((info->dasd_info.format == DASD_FORMAT_NONE && mode != QUICK) || + cdata->rec == 0) { ERRMSG("WARNING: The specified device is not " "formatted at all.\n"); return; @@ -529,6 +539,35 @@ static void check_blocksize(dasdfmt_info } /* + * Check whether a specified layout matches the layout + * a device is formatted with. + */ +static void check_layout(dasdfmt_info_t *info, unsigned int intensity) +{ + char layout[4]; + + if (!info->layout_specified || + info->dasd_info.format == DASD_FORMAT_NONE) + return; + + if ((intensity & DASD_FMT_INT_COMPAT) && + info->dasd_info.format == DASD_FORMAT_CDL) + return; + + if (!(intensity & DASD_FMT_INT_COMPAT) && + info->dasd_info.format == DASD_FORMAT_LDL) + return; + + if (info->dasd_info.format == DASD_FORMAT_CDL) + sprintf(layout, "CDL"); + if (info->dasd_info.format == DASD_FORMAT_LDL) + sprintf(layout, "LDL"); + + ERRMSG_EXIT(EXIT_FAILURE, "WARNING: Device is formatted with a " + "different layout (%s).\n", layout); +} + +/* * check for disk type and set some variables (e.g. usage count) */ static void check_disk(dasdfmt_info_t *info) @@ -715,10 +754,29 @@ static void set_label(dasdfmt_info_t *in } /* + * Check whether hashsteps are within the correct interval. + */ +static void check_hashmarks(dasdfmt_info_t *info) +{ + if (info->print_hashmarks) { + if (info->hashstep < reqsize) + info->hashstep = reqsize; + if ((info->hashstep < 1) || (info->hashstep > 1000)) { + printf("Hashmark increment is not in range <1,1000>, " + "using the default.\n"); + info->hashstep = 10; + } + + printf("Printing hashmark every %d cylinders.\n", + info->hashstep); + } +} + +/* * This function checks whether a range of tracks is in regular format * with the specified block size. */ -static format_check_t check_track_format(format_data_t *p) +static format_check_t check_track_format(dasdfmt_info_t *info, format_data_t *p) { format_check_t cdata = { .expect = { @@ -731,9 +789,11 @@ static format_check_t check_track_format if (ioctl(filedes, BIODASDCHECKFMT, &cdata)) { if (errno == ENOTTY) { - ERRMSG_EXIT(EXIT_FAILURE, "%s: Missing kernel support " - "for format checking (--force to " - "override)\n", prog_name); + ERRMSG("%s: Missing kernel support for format checking", + prog_name); + if (!info->check) + ERRMSG(" (--force to override)"); + ERRMSG_EXIT(EXIT_FAILURE, ".\n"); } ERRMSG_EXIT(EXIT_FAILURE, "%s: Could no check format: %s\n", prog_name, strerror(errno)); @@ -742,6 +802,97 @@ static format_check_t check_track_format return cdata; } +/* + * Either do the actual format or check depending on the check-value. + */ +static int process_tracks(dasdfmt_info_t *info, unsigned int cylinders, + unsigned int heads, format_data_t *format_params) +{ + format_check_t cdata = { .expect = {0}, 0}; + format_data_t step = *format_params; + unsigned long step_value; + unsigned long cur_trk; + int cyl = 0; + + check_hashmarks(info); + + cur_trk = format_params->start_unit; + + while (cur_trk < format_params->stop_unit) { + step_value = reqsize * heads - (cur_trk % heads); + step.start_unit = cur_trk; + if (cur_trk + heads * reqsize >= format_params->stop_unit) + step.stop_unit = format_params->stop_unit; + else + step.stop_unit = cur_trk + step_value - 1; + + if (info->check) { + cdata = check_track_format(info, &step); + if (cdata.result) { + cyl = cur_trk / heads + 1; + draw_progress(info, cyl, cylinders, 1); + evaluate_format_error(info, &cdata, heads); + break; + } + } else { + if (ioctl(filedes, BIODASDFMT, &step) != 0) + ERRMSG_EXIT(EXIT_FAILURE, "%s: the ioctl call " + "to format tracks failed. (%s)\n", + prog_name, strerror(errno)); + } + + cyl = cur_trk / heads + 1; + draw_progress(info, cyl, cylinders, 0); + + cur_trk += step_value; + } + /* We're done, draw the 100% mark */ + if (!cdata.result) { + cyl = step.stop_unit / heads + 1; + draw_progress(info, cyl, cylinders, 0); + printf("\n"); + } + + return cdata.result; +} + +/* + * This function checks the format of the entire disk. + */ +static void check_disk_format(dasdfmt_info_t *info, unsigned int cylinders, + unsigned int heads, format_data_t *check_params) +{ + check_params->start_unit = 0; + check_params->stop_unit = (cylinders * heads) - 1; + + printf("Checking format of the entire disk...\n"); + + if (info->testmode) { + printf("Test mode active, omitting ioctl.\n"); + return; + } + + check_blocksize(info, check_params->blksize); + check_layout(info, check_params->intensity); + + /* + * If no layout was specified, set the intensity + * according to what the layout seems to be. + */ + if (!info->layout_specified) { + if (info->dasd_info.format == DASD_FORMAT_CDL) + check_params->intensity |= DASD_FMT_INT_COMPAT; + else if (info->dasd_info.format == DASD_FORMAT_LDL) + check_params->intensity &= ~DASD_FMT_INT_COMPAT; + } + + if (process_tracks(info, cylinders, heads, check_params)) { + ERRMSG_EXIT(EXIT_FAILURE, "Use --mode=full to perform a " + "clean format.\n"); + } + + printf("Done. Disk is fine.\n"); +} /* * ask the user to specify a blocksize @@ -1023,51 +1174,7 @@ static void dasdfmt_write_labels(dasdfmt static void dasdfmt_format(dasdfmt_info_t *info, unsigned int cylinders, unsigned int heads, format_data_t *format_params) { - unsigned int step_value; - unsigned long cur_trk; - format_data_t step; - int cyl = 0; - - if (info->print_hashmarks) { - if (info->hashstep < reqsize) - info->hashstep = reqsize; - if ((info->hashstep < 1) || (info->hashstep > 1000)) { - printf("Hashmark increment is not in range <1,1000>, " - "using the default.\n"); - info->hashstep = 10; - } - - if(!info->yast_mode) printf("Printing hashmark every %d cylinders.\n", - info->hashstep); - } - - step = *format_params; - cur_trk = format_params->start_unit; - - while (cur_trk < format_params->stop_unit) { - step_value = reqsize * heads - (cur_trk % heads); - step.start_unit = cur_trk; - if (cur_trk + reqsize * heads >= format_params->stop_unit) - step.stop_unit = format_params->stop_unit; - else - step.stop_unit = cur_trk + step_value - 1; - - if (ioctl(filedes, BIODASDFMT, &step) != 0) - ERRMSG_EXIT(EXIT_FAILURE, "%s: (format cylinder) IOCTL " - "BIODASDFMT failed. (%s)\n", - prog_name, strerror(errno)); - - cyl = cur_trk / heads + 1; - draw_progress(info, cyl, cylinders); - - cur_trk += step_value; - } - /* We're done, draw the 100% mark */ - cyl = step.stop_unit / heads + 1; - draw_progress(info, cyl, cylinders); - - if ((info->print_progressbar || info->print_hashmarks) && !info->yast_mode) - printf("\n"); + process_tracks(info, cylinders, heads, format_params); } static void dasdfmt_prepare_and_format(dasdfmt_info_t *info, @@ -1082,6 +1189,10 @@ static void dasdfmt_prepare_and_format(d | DASD_FMT_INT_INVAL) }; + if (!((info->withoutprompt) && (info->verbosity < 1))) + printf("Formatting the device. This may take a while " + "(get yourself a coffee).\n"); + if (info->verbosity > 0) printf("Detaching the device...\n"); @@ -1147,11 +1258,11 @@ static void dasdfmt_quick_format(dasdfmt /* Check device format on the first and last 3 regular tracks */ tmp.start_unit = 2; tmp.stop_unit = 4; - cdata = check_track_format(&tmp); + cdata = check_track_format(info, &tmp); if (!cdata.result) { tmp.start_unit = (cylinders * heads) - 3; tmp.stop_unit = (cylinders * heads) - 1; - cdata = check_track_format(&tmp); + cdata = check_track_format(info, &tmp); } if (cdata.result) { evaluate_format_error(info, &cdata, heads); @@ -1243,9 +1354,6 @@ static void do_format_dasd(dasdfmt_info_ switch (mode) { case FULL: - if (!((info->withoutprompt) && (info->verbosity < 1))) - printf("Formatting the device. This may take a " - "while (get yourself a coffee).\n"); dasdfmt_prepare_and_format(info, cylinders, heads, p); break; case QUICK: @@ -1337,6 +1445,7 @@ int main(int argc, char *argv[]) printf("%s is not a valid option!\n", optarg); exit(1); } + info.layout_specified = 1; break; case 'y': info.withoutprompt = 1; @@ -1424,6 +1533,9 @@ int main(int argc, char *argv[]) "more information.\n", prog_name, optarg); break; + case OPT_CHECK: + info.check = 1; + break; case -1: /* End of options string - start of devices list */ info.device_id = optind; @@ -1539,7 +1651,7 @@ int main(int argc, char *argv[]) /* Either let the user specify the blksize or get it from the kernel */ if (!info.blksize_specified) { if (!(mode == FULL || - info.dasd_info.format == DASD_FORMAT_NONE)) + info.dasd_info.format == DASD_FORMAT_NONE) || info.check) get_blocksize(&format_params.blksize); else format_params = ask_user_for_blksize(format_params); @@ -1568,8 +1680,14 @@ int main(int argc, char *argv[]) set_geo(&info, &cylinders, &heads); set_label(&info, &vlabel, &format_params, cylinders); - do_format_dasd(&info, &vlabel, &format_params, - cylinders, heads); + + if (info.check) { + check_disk_format(&info, cylinders, heads, + &format_params); + } else { + do_format_dasd(&info, &vlabel, &format_params, + cylinders, heads); + } if (close(filedes) != 0) ERRMSG("%s: error during close: %s\ncontinuing...\n", --- a/dasdfmt/dasdfmt.h +++ b/dasdfmt/dasdfmt.h @@ -273,6 +273,7 @@ typedef struct format_check_t { if (*endptr) ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ "is in invalid format\n",prog_name);} +#define OPT_CHECK 128 #define dasdfmt_getopt_string "b:n:l:f:d:m:M:r:hpQLtyvVFkCYP:" static struct option dasdfmt_getopt_long_options[]= @@ -296,6 +297,7 @@ static struct option dasdfmt_getopt_long { "norecordzero", 0, 0, 'z'}, { "check_host_count", 0, 0, 'C'}, { "mode", 1, 0, 'M'}, + { "check", 0, 0, OPT_CHECK}, {0, 0, 0, 0} }; @@ -328,6 +330,8 @@ typedef struct dasdfmt_info { int device_id; int keep_volser; int force_host; + int layout_specified; + int check; int yast_mode; int procnum; } dasdfmt_info_t;