1 /*
2 * Copyright (c) 2005-2019 Douglas Gilbert.
3 * All rights reserved.
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the BSD_LICENSE file.
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 */
9
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <getopt.h>
19 #include <limits.h>
20 #define __STDC_FORMAT_MACROS 1
21 #include <inttypes.h>
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "sg_lib.h"
28 #include "sg_cmds_basic.h"
29 #include "sg_cmds_extra.h"
30 #include "sg_unaligned.h"
31 #include "sg_pr2serr.h"
32
33 /* A utility program originally written for the Linux OS SCSI subsystem.
34 *
35 * This utility invokes the REASSIGN BLOCKS SCSI command to reassign
36 * an existing (possibly damaged) lba on a direct access device (e.g.
37 * a disk) to a new physical location. The previous contents is
38 * recoverable then it is written to the remapped lba otherwise
39 * vendor specific data is written.
40 */
41
42 static const char * version_str = "1.27 20191001";
43
44 #define DEF_DEFECT_LIST_FORMAT 4 /* bytes from index */
45
46 #define MAX_NUM_ADDR 1024
47
48 #ifndef UINT32_MAX
49 #define UINT32_MAX ((uint32_t)-1)
50 #endif
51
52
53 static struct option long_options[] = {
54 {"address", required_argument, 0, 'a'},
55 {"dummy", no_argument, 0, 'd'},
56 {"eight", required_argument, 0, 'e'},
57 {"grown", no_argument, 0, 'g'},
58 {"help", no_argument, 0, 'h'},
59 {"hex", no_argument, 0, 'H'},
60 {"longlist", required_argument, 0, 'l'},
61 {"primary", no_argument, 0, 'p'},
62 {"verbose", no_argument, 0, 'v'},
63 {"version", no_argument, 0, 'V'},
64 {0, 0, 0, 0},
65 };
66
67 static void
usage()68 usage()
69 {
70 pr2serr("Usage: sg_reassign [--address=A,A...] [--dummy] [--eight=0|1] "
71 "[--grown]\n"
72 " [--help] [--hex] [--longlist=0|1] "
73 "[--primary] [--verbose]\n"
74 " [--version] DEVICE\n"
75 " where:\n"
76 " --address=A,A...|-a A,A... comma separated logical block "
77 "addresses\n"
78 " one or more, assumed to be "
79 "decimal\n"
80 " --address=-|-a - read stdin for logical block "
81 "addresses\n"
82 " --dummy|-d prepare but do not execute REASSIGN "
83 "BLOCKS command\n"
84 " --eight=0|1\n"
85 " -e 0|1 force eight byte (64 bit) lbas "
86 "when 1,\n"
87 " four byte (32 bit) lbas when 0 "
88 "(def)\n"
89 " --grown|-g fetch grown defect list length, "
90 "don't reassign\n"
91 " --help|-h print out usage message\n"
92 " --hex|-H print response in hex (for '-g' or "
93 "'-p')\n"
94 " --longlist=0|1\n"
95 " -l 0|1 use 4 byte list length when 1, safe to "
96 "ignore\n"
97 " (def: 0 (2 byte list length))\n"
98 " --primary|-p fetch primary defect list length, "
99 "don't reassign\n"
100 " --verbose|-v increase verbosity\n"
101 " --version|-V print version string and exit\n\n"
102 "Perform a SCSI REASSIGN BLOCKS command (or READ DEFECT LIST)\n");
103 }
104
105 /* Read numbers (up to 64 bits in size) from command line (comma (or
106 * (single) space) separated list) or from stdin (one per line, comma
107 * separated list or space separated list). Assumed decimal unless prefixed
108 * by '0x', '0X' or contains trailing 'h' or 'H' (which indicate hex).
109 * Returns 0 if ok, or error code. */
110 static int
build_lba_arr(const char * inp,uint64_t * lba_arr,int * lba_arr_len,int max_arr_len)111 build_lba_arr(const char * inp, uint64_t * lba_arr,
112 int * lba_arr_len, int max_arr_len)
113 {
114 int in_len, k, j, m;
115 const char * lcp;
116 int64_t ll;
117 char * cp;
118 char * c2p;
119
120 if ((NULL == inp) || (NULL == lba_arr) ||
121 (NULL == lba_arr_len))
122 return SG_LIB_LOGIC_ERROR;
123 lcp = inp;
124 in_len = strlen(inp);
125 if (0 == in_len)
126 *lba_arr_len = 0;
127 if ('-' == inp[0]) { /* read from stdin */
128 char line[1024];
129 int off = 0;
130
131 for (j = 0; j < 512; ++j) {
132 if (NULL == fgets(line, sizeof(line), stdin))
133 break;
134 // could improve with carry_over logic if sizeof(line) too small
135 in_len = strlen(line);
136 if (in_len > 0) {
137 if ('\n' == line[in_len - 1]) {
138 --in_len;
139 line[in_len] = '\0';
140 }
141 }
142 if (in_len < 1)
143 continue;
144 lcp = line;
145 m = strspn(lcp, " \t");
146 if (m == in_len)
147 continue;
148 lcp += m;
149 in_len -= m;
150 if ('#' == *lcp)
151 continue;
152 k = strspn(lcp, "0123456789aAbBcCdDeEfFhHxX ,\t");
153 if ((k < in_len) && ('#' != lcp[k])) {
154 pr2serr("%s: syntax error at line %d, pos %d\n", __func__,
155 j + 1, m + k + 1);
156 return SG_LIB_SYNTAX_ERROR;
157 }
158 for (k = 0; k < 1024; ++k) {
159 ll = sg_get_llnum_nomult(lcp);
160 if (-1 != ll) {
161 if ((off + k) >= max_arr_len) {
162 pr2serr("%s: array length exceeded\n", __func__);
163 return SG_LIB_SYNTAX_ERROR;
164 }
165 lba_arr[off + k] = (uint64_t)ll;
166 lcp = strpbrk(lcp, " ,\t");
167 if (NULL == lcp)
168 break;
169 lcp += strspn(lcp, " ,\t");
170 if ('\0' == *lcp)
171 break;
172 } else {
173 if ('#' == *lcp) {
174 --k;
175 break;
176 }
177 pr2serr("%s: error in line %d, at pos %d\n", __func__,
178 j + 1, (int)(lcp - line + 1));
179 return SG_LIB_SYNTAX_ERROR;
180 }
181 }
182 off += (k + 1);
183 }
184 *lba_arr_len = off;
185 } else { /* list of numbers (default decimal) on command line */
186 k = strspn(inp, "0123456789aAbBcCdDeEfFhHxX, ");
187 if (in_len != k) {
188 pr2serr("%s: error at pos %d\n", __func__, k + 1);
189 return SG_LIB_SYNTAX_ERROR;
190 }
191 for (k = 0; k < max_arr_len; ++k) {
192 ll = sg_get_llnum_nomult(lcp);
193 if (-1 != ll) {
194 lba_arr[k] = (uint64_t)ll;
195 cp = (char *)strchr(lcp, ',');
196 c2p = (char *)strchr(lcp, ' ');
197 if (NULL == cp)
198 cp = c2p;
199 if (NULL == cp)
200 break;
201 if (c2p && (c2p < cp))
202 cp = c2p;
203 lcp = cp + 1;
204 } else {
205 pr2serr("%s: error at pos %d\n", __func__,
206 (int)(lcp - inp + 1));
207 return SG_LIB_SYNTAX_ERROR;
208 }
209 }
210 *lba_arr_len = k + 1;
211 if (k == max_arr_len) {
212 pr2serr("%s: array length exceeded\n", __func__);
213 return SG_LIB_SYNTAX_ERROR;
214 }
215 }
216 return 0;
217 }
218
219
220 int
main(int argc,char * argv[])221 main(int argc, char * argv[])
222 {
223 bool dummy = false;
224 bool eight = false;
225 bool eight_given = false;
226 bool got_addr = false;
227 bool longlist = false;
228 bool primary = false;
229 bool grown = false;
230 bool verbose_given = false;
231 bool version_given = false;
232 int res, c, num, k, j;
233 int sg_fd = -1;
234 int addr_arr_len = 0;
235 int do_hex = 0;
236 int verbose = 0;
237 const char * device_name = NULL;
238 uint64_t addr_arr[MAX_NUM_ADDR];
239 uint8_t param_arr[4 + (MAX_NUM_ADDR * 8)];
240 char b[80];
241 int param_len = 4;
242 int ret = 0;
243
244 while (1) {
245 int option_index = 0;
246
247 c = getopt_long(argc, argv, "a:de:ghHl:pvV", long_options,
248 &option_index);
249 if (c == -1)
250 break;
251
252 switch (c) {
253 case 'a':
254 memset(addr_arr, 0, sizeof(addr_arr));
255 if ((res = build_lba_arr(optarg, addr_arr, &addr_arr_len,
256 MAX_NUM_ADDR))) {
257 pr2serr("bad argument to '--address'\n");
258 return res;
259 }
260 got_addr = true;
261 break;
262 case 'd':
263 dummy = true;
264 break;
265 case 'e':
266 num = sscanf(optarg, "%d", &res);
267 if ((1 == num) && ((0 == res) || (1 == res)))
268 eight = !! res;
269 else {
270 pr2serr("value for '--eight=' must be 0 or 1\n");
271 return SG_LIB_SYNTAX_ERROR;
272 }
273 eight_given = true;
274 break;
275 case 'g':
276 grown = true;
277 break;
278 case 'h':
279 case '?':
280 usage();
281 return 0;
282 case 'H':
283 ++do_hex;
284 break;
285 case 'l':
286 num = sscanf(optarg, "%d", &res);
287 if ((1 == num) && ((0 == res) || (1 == res)))
288 longlist = !!res;
289 else {
290 pr2serr("value for '--longlist=' must be 0 or 1\n");
291 return SG_LIB_SYNTAX_ERROR;
292 }
293 break;
294 case 'p':
295 primary = true;
296 break;
297 case 'v':
298 verbose_given = true;
299 ++verbose;
300 break;
301 case 'V':
302 version_given = true;
303 break;
304 default:
305 pr2serr("unrecognised option code 0x%x ??\n", c);
306 usage();
307 return SG_LIB_SYNTAX_ERROR;
308 }
309 }
310 if (optind < argc) {
311 if (NULL == device_name) {
312 device_name = argv[optind];
313 ++optind;
314 }
315 if (optind < argc) {
316 for (; optind < argc; ++optind)
317 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
318 usage();
319 return SG_LIB_SYNTAX_ERROR;
320 }
321 }
322 #ifdef DEBUG
323 pr2serr("In DEBUG mode, ");
324 if (verbose_given && version_given) {
325 pr2serr("but override: '-vV' given, zero verbose and continue\n");
326 verbose_given = false;
327 version_given = false;
328 verbose = 0;
329 } else if (! verbose_given) {
330 pr2serr("set '-vv'\n");
331 verbose = 2;
332 } else
333 pr2serr("keep verbose=%d\n", verbose);
334 #else
335 if (verbose_given && version_given)
336 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
337 #endif
338 if (version_given) {
339 pr2serr("version: %s\n", version_str);
340 return 0;
341 }
342
343 if (NULL == device_name) {
344 pr2serr("Missing device name!\n\n");
345 usage();
346 return SG_LIB_SYNTAX_ERROR;
347 }
348 if (grown || primary) {
349 if (got_addr) {
350 pr2serr("can't have '--address=' with '--grown' or '--primary'\n");
351 usage();
352 return SG_LIB_CONTRADICT;
353 }
354 } else if ((! got_addr) || (addr_arr_len < 1)) {
355 pr2serr("need at least one address (see '--address=')\n");
356 usage();
357 return SG_LIB_SYNTAX_ERROR;
358 }
359 if (got_addr) {
360 for (k = 0; k < addr_arr_len; ++k) {
361 if (addr_arr[k] >= UINT32_MAX) {
362 if (! eight_given) {
363 eight = true;
364 break;
365 } else if (! eight) {
366 pr2serr("address number %d exceeds 32 bits so "
367 "'--eight=0' invalid\n", k + 1);
368 return SG_LIB_CONTRADICT;
369 }
370 }
371 }
372 if (! eight_given)
373 eight = false;
374
375 k = 4;
376 for (j = 0; j < addr_arr_len; ++j) {
377 if (eight) {
378 sg_put_unaligned_be64(addr_arr[j], param_arr + k);
379 k += 8;
380 } else {
381 sg_put_unaligned_be32((uint32_t)addr_arr[j], param_arr + k);
382 k += 4;
383 }
384 }
385 param_len = k;
386 k -= 4;
387 if (longlist)
388 sg_put_unaligned_be32((uint32_t)k, param_arr + 0);
389 else
390 sg_put_unaligned_be16((uint16_t)k, param_arr + 2);
391 }
392
393 sg_fd = sg_cmds_open_device(device_name, false /* rw */, verbose);
394 if (sg_fd < 0) {
395 if (verbose)
396 pr2serr("open error: %s: %s\n", device_name,
397 safe_strerror(-sg_fd));
398 ret = sg_convert_errno(-sg_fd);
399 goto err_out;
400 }
401
402 if (got_addr) {
403 if (dummy) {
404 pr2serr(">>> dummy: REASSIGN BLOCKS not executed\n");
405 if (verbose) {
406 pr2serr(" Would have reassigned these blocks:\n");
407 for (j = 0; j < addr_arr_len; ++j)
408 printf(" 0x%" PRIx64 "\n", addr_arr[j]);
409 }
410 return 0;
411 }
412 res = sg_ll_reassign_blocks(sg_fd, eight, longlist, param_arr,
413 param_len, true, verbose);
414 ret = res;
415 if (res) {
416 sg_get_category_sense_str(res, sizeof(b), b, verbose);
417 pr2serr("REASSIGN BLOCKS: %s\n", b);
418 goto err_out;
419 }
420 } else /* if (grown || primary) */ {
421 int dl_format = DEF_DEFECT_LIST_FORMAT;
422 int div = 0;
423 int dl_len;
424 bool got_grown, got_primary;
425 const char * lstp;
426
427 param_len = 4;
428 memset(param_arr, 0, param_len);
429 res = sg_ll_read_defect10(sg_fd, primary, grown, dl_format,
430 param_arr, param_len, false, verbose);
431 ret = res;
432 if (res) {
433 sg_get_category_sense_str(res, sizeof(b), b, verbose);
434 pr2serr("READ DEFECT DATA(10): %s\n", b);
435 goto err_out;
436 }
437 if (do_hex) {
438 hex2stdout(param_arr, param_len, 1);
439 goto err_out; /* ret is zero */
440 }
441 got_grown = !!(param_arr[1] & 0x8);
442 got_primary = !!(param_arr[1] & 0x10);
443 if (got_grown && got_primary)
444 lstp = "grown and primary defect lists";
445 else if (got_grown)
446 lstp = "grown defect list";
447 else if (got_primary)
448 lstp = "primary defect list";
449 else {
450 pr2serr("didn't get grown or primary list in response\n");
451 goto err_out;
452 }
453 if (verbose)
454 pr2serr("asked for defect list format %d, got %d\n", dl_format,
455 (param_arr[1] & 0x7));
456 dl_format = (param_arr[1] & 0x7);
457 switch (dl_format) { /* Defect list formats: */
458 case 0: /* short block */
459 div = 4;
460 break;
461 case 1: /* extended bytes from index */
462 div = 8;
463 break;
464 case 2: /* extended physical sector */
465 div = 8;
466 break;
467 case 3: /* long block */
468 case 4: /* bytes from index */
469 case 5: /* physical sector */
470 div = 8;
471 break;
472 case 6: /* vendor specific */
473 if (verbose)
474 pr2serr("defect list format: vendor specific\n");
475 break;
476 default:
477 pr2serr("defect list format %d unknown\n", dl_format);
478 break;
479 }
480 dl_len = sg_get_unaligned_be16(param_arr + 2);
481 if (0 == dl_len)
482 printf(">> Elements in %s: 0\n", lstp);
483 else {
484 if (0 == div)
485 printf(">> %s length=%d bytes [unknown number of elements]\n",
486 lstp, dl_len);
487 else
488 printf(">> Elements in %s: %d\n", lstp,
489 dl_len / div);
490 }
491 }
492
493 err_out:
494 if (sg_fd >= 0) {
495 res = sg_cmds_close_device(sg_fd);
496 if (res < 0) {
497 pr2serr("close error: %s\n", safe_strerror(-res));
498 if (0 == ret)
499 ret = sg_convert_errno(-res);
500 }
501 }
502 if (0 == verbose) {
503 if (! sg_if_can2stderr("sg_reassign failed: ", ret))
504 pr2serr("Some error occurred, try again with '-v' "
505 "or '-vv' for more information\n");
506 }
507 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
508 }
509