xref: /aosp_15_r20/external/sg3_utils/src/sg_sanitize.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2011-2022 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 <errno.h>
18 #include <limits.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <getopt.h>
22 #define __STDC_FORMAT_MACROS 1
23 #include <inttypes.h>
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include "sg_lib.h"
30 #include "sg_pt.h"
31 #include "sg_cmds_basic.h"
32 #include "sg_cmds_extra.h"
33 #include "sg_unaligned.h"
34 #include "sg_pr2serr.h"
35 
36 static const char * version_str = "1.19 20220608";
37 
38 #define ME "sg_sanitize: "
39 
40 #define SANITIZE_OP 0x48
41 #define SANITIZE_OP_LEN 10
42 #define SANITIZE_SA_OVERWRITE 0x1
43 #define SANITIZE_SA_BLOCK_ERASE 0x2
44 #define SANITIZE_SA_CRYPTO_ERASE 0x3
45 #define SANITIZE_SA_EXIT_FAIL_MODE 0x1f
46 #define DEF_REQS_RESP_LEN 252
47 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
48 #define MAX_XFER_LEN 65535
49 #define EBUFF_SZ 256
50 
51 #define SHORT_TIMEOUT 20   /* 20 seconds unless immed=0 ... */
52 #define LONG_TIMEOUT (15 * 3600)       /* 15 hours ! */
53                 /* Seagate ST32000444SS 2TB disk takes 9.5 hours to format */
54 #define POLL_DURATION_SECS 60
55 
56 
57 static struct option long_options[] = {
58     {"ause", no_argument, 0, 'A'},
59     {"block", no_argument, 0, 'B'},
60     {"count", required_argument, 0, 'c'},
61     {"crypto", no_argument, 0, 'C'},
62     {"desc", no_argument, 0, 'd'},
63     {"dry-run", no_argument, 0, 'D'},
64     {"dry_run", no_argument, 0, 'D'},
65     {"early", no_argument, 0, 'e'},
66     {"fail", no_argument, 0, 'F'},
67     {"help", no_argument, 0, 'h'},
68     {"invert", no_argument, 0, 'I'},
69     {"ipl", required_argument, 0, 'i'},
70     {"overwrite", no_argument, 0, 'O'},
71     {"pattern", required_argument, 0, 'p'},
72     {"quick", no_argument, 0, 'Q'},
73     {"test", required_argument, 0, 'T'},
74     {"timeout", required_argument, 0, 't'},
75     {"verbose", no_argument, 0, 'v'},
76     {"version", no_argument, 0, 'V'},
77     {"wait", no_argument, 0, 'w'},
78     {"zero", no_argument, 0, 'z'},
79     {0, 0, 0, 0},
80 };
81 
82 struct opts_t {
83     bool ause;
84     bool block;
85     bool crypto;
86     bool desc;
87     bool dry_run;
88     bool early;
89     bool fail;
90     bool invert;
91     bool overwrite;
92     bool quick;
93     bool verbose_given;
94     bool version_given;
95     bool wait;
96     bool znr;
97     int count;
98     int ipl;    /* initialization pattern length */
99     int test;
100     int timeout;        /* in seconds */
101     int verbose;
102     int zero;
103     const char * pattern_fn;
104 };
105 
106 
107 static void
usage()108 usage()
109 {
110   pr2serr("Usage: sg_sanitize [--ause] [--block] [--count=OC] [--crypto] "
111           "[--dry-run]\n"
112           "                   [--early] [--fail] [--help] [--invert] "
113           "[--ipl=LEN]\n"
114           "                   [--overwrite] [--pattern=PF] [--quick] "
115           "[--test=TE]\n"
116           "                   [--timeout=SECS] [--verbose] [--version] "
117           "[--wait]\n"
118           "                   [--zero] [--znr] DEVICE\n"
119           "  where:\n"
120           "    --ause|-A            set AUSE bit in cdb\n"
121           "    --block|-B           do BLOCK ERASE sanitize\n"
122           "    --count=OC|-c OC     OC is overwrite count field (from 1 "
123           "(def) to 31)\n"
124           "    --crypto|-C          do CRYPTOGRAPHIC ERASE sanitize\n"
125           "    --desc|-d            polling request sense sets 'desc' "
126           "field\n"
127           "                         (def: clear 'desc' field)\n"
128           "    --dry-run|-D         to preparation but bypass SANITIZE "
129           "command\n"
130           "    --early|-e           exit once sanitize started (IMMED set "
131           "in cdb)\n"
132           "                         user can monitor progress with REQUEST "
133           "SENSE\n"
134           "    --fail|-F            do EXIT FAILURE MODE sanitize\n"
135           "    --help|-h            print out usage message\n"
136           "    --invert|-I          set INVERT bit in OVERWRITE parameter "
137           "list\n"
138           "    --ipl=LEN|-i LEN     initialization pattern length (in "
139           "bytes)\n"
140           "    --overwrite|-O       do OVERWRITE sanitize\n"
141           "    --pattern=PF|-p PF    PF is file containing initialization "
142           "pattern\n"
143           "                          for OVERWRITE\n"
144           "    --quick|-Q           start sanitize without pause for user\n"
145           "                         intervention (i.e. no time to "
146           "reconsider)\n"
147           "    --test=TE|-T TE      TE is placed in TEST field of "
148           "OVERWRITE\n"
149           "                         parameter list (def: 0)\n"
150           "    --timeout=SECS|-t SECS    SANITIZE command timeout in "
151           "seconds\n"
152           "    --verbose|-v         increase verbosity\n"
153           "    --version|-V         print version string then exit\n"
154           "    --wait|-w            wait for command to finish (could "
155           "take hours)\n"
156           "    --zero|-z            use pattern of zeros for "
157           "OVERWRITE\n"
158           "    --znr|-Z             set ZNR (zone no reset) bit in cdb\n\n"
159           "Performs a SCSI SANITIZE command.\n    <<<WARNING>>>: all data "
160           "on DEVICE will be lost.\nDefault action is to give user time to "
161           "reconsider; then execute SANITIZE\ncommand with IMMED bit set; "
162           "then use REQUEST SENSE command every 60\nseconds to poll for a "
163           "progress indication; then exit when there is no\nmore progress "
164           "indication.\n"
165           );
166 }
167 
168 /* Invoke SCSI SANITIZE command. Returns 0 if successful, otherwise error */
169 static int
do_sanitize(int sg_fd,const struct opts_t * op,const void * param_lstp,int param_lst_len)170 do_sanitize(int sg_fd, const struct opts_t * op, const void * param_lstp,
171             int param_lst_len)
172 {
173     bool immed;
174     int ret, res, sense_cat, timeout;
175     uint8_t san_cdb[SANITIZE_OP_LEN];
176     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
177     struct sg_pt_base * ptvp;
178 
179     if (op->early || op->wait)
180         immed = op->early;
181     else
182         immed = true;
183     timeout = (immed ? SHORT_TIMEOUT : LONG_TIMEOUT);
184     /* only use command line timeout if it exceeds previous defaults */
185     if (op->timeout > timeout)
186         timeout = op->timeout;
187     memset(san_cdb, 0, sizeof(san_cdb));
188     san_cdb[0] = SANITIZE_OP;
189     if (op->overwrite)
190         san_cdb[1] = SANITIZE_SA_OVERWRITE;
191     else if (op->block)
192         san_cdb[1] = SANITIZE_SA_BLOCK_ERASE;
193     else if (op->crypto)
194         san_cdb[1] = SANITIZE_SA_CRYPTO_ERASE;
195     else if (op->fail)
196         san_cdb[1] = SANITIZE_SA_EXIT_FAIL_MODE;
197     else
198         return SG_LIB_SYNTAX_ERROR;
199     if (immed)
200         san_cdb[1] |= 0x80;
201     if (op->znr)        /* added sbc4r07 */
202         san_cdb[1] |= 0x40;
203     if (op->ause)
204         san_cdb[1] |= 0x20;
205     sg_put_unaligned_be16((uint16_t)param_lst_len, san_cdb + 7);
206 
207     if (op->verbose > 1) {
208         char b[128];
209 
210         pr2serr("    Sanitize cdb: %s\n",
211                 sg_get_command_str(san_cdb, SANITIZE_OP_LEN, false,
212                                    sizeof(b), b));
213         if (op->verbose > 2) {
214             if (param_lst_len > 0) {
215                 pr2serr("    Parameter list contents:\n");
216                 hex2stderr((const uint8_t *)param_lstp, param_lst_len, -1);
217             }
218             pr2serr("    Sanitize command timeout: %d seconds\n", timeout);
219         }
220     }
221     if (op->dry_run) {
222         pr2serr("Due to --dry-run option, bypassing SANITIZE command\n");
223         return 0;
224     }
225     ptvp = construct_scsi_pt_obj();
226     if (NULL == ptvp) {
227         pr2serr("Sanitize: out of memory\n");
228         return -1;
229     }
230     set_scsi_pt_cdb(ptvp, san_cdb, sizeof(san_cdb));
231     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
232     set_scsi_pt_data_out(ptvp, (uint8_t *)param_lstp, param_lst_len);
233     res = do_scsi_pt(ptvp, sg_fd, timeout, op->verbose);
234     ret = sg_cmds_process_resp(ptvp, "Sanitize", res, true /*noisy */,
235                                op->verbose, &sense_cat);
236     if (-1 == ret) {
237         if (get_scsi_pt_transport_err(ptvp))
238             ret = SG_LIB_TRANSPORT_ERROR;
239         else
240             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
241     } else if (-2 == ret) {
242         switch (sense_cat) {
243         case SG_LIB_CAT_RECOVERED:
244         case SG_LIB_CAT_NO_SENSE:
245             ret = 0;
246             break;
247         case SG_LIB_CAT_MEDIUM_HARD:
248             {
249                 bool valid;
250                 int slen;
251                 uint64_t ull = 0;
252 
253                 slen = get_scsi_pt_sense_len(ptvp);
254                 valid = sg_get_sense_info_fld(sense_b, slen, &ull);
255                 if (valid)
256                     pr2serr("Medium or hardware error starting at "
257                             "lba=%" PRIu64 " [0x%" PRIx64 "]\n", ull, ull);
258             }
259             ret = sense_cat;
260             break;
261         default:
262             ret = sense_cat;
263             break;
264         }
265     } else {
266         ret = 0;
267         if (op->verbose)
268             pr2serr("Sanitize command %s without error\n",
269                     (immed ? "launched" : "completed"));
270     }
271 
272     destruct_scsi_pt_obj(ptvp);
273     return ret;
274 }
275 
276 #define VPD_DEVICE_ID 0x83
277 #define VPD_ASSOC_LU 0
278 #define VPD_ASSOC_TPORT 1
279 #define TPROTO_ISCSI 5
280 
281 static char *
get_lu_name(const uint8_t * bp,int u_len,char * b,int b_len)282 get_lu_name(const uint8_t * bp, int u_len, char * b, int b_len)
283 {
284     int len, off, sns_dlen, dlen, k;
285     uint8_t u_sns[512];
286     char * cp;
287 
288     len = u_len - 4;
289     bp += 4;
290     off = -1;
291     if (0 == sg_vpd_dev_id_iter(bp, len, &off, VPD_ASSOC_LU,
292                                 8 /* SCSI name string (sns) */,
293                                 3 /* UTF-8 */)) {
294         sns_dlen = bp[off + 3];
295         memcpy(u_sns, bp + off + 4, sns_dlen);
296         /* now want to check if this is iSCSI */
297         off = -1;
298         if (0 == sg_vpd_dev_id_iter(bp, len, &off, VPD_ASSOC_TPORT,
299                                     8 /* SCSI name string (sns) */,
300                                     3 /* UTF-8 */)) {
301             if ((0x80 & bp[1]) && (TPROTO_ISCSI == (bp[0] >> 4))) {
302                 snprintf(b, b_len, "%.*s", sns_dlen, u_sns);
303                 return b;
304             }
305         }
306     } else
307         sns_dlen = 0;
308     if (0 == sg_vpd_dev_id_iter(bp, len, &off, VPD_ASSOC_LU,
309                                 3 /* NAA */, 1 /* binary */)) {
310         dlen = bp[off + 3];
311         if (! ((8 == dlen) || (16 ==dlen)))
312             return b;
313         cp = b;
314         for (k = 0; ((k < dlen) && (b_len > 1)); ++k) {
315             snprintf(cp, b_len, "%02x", bp[off + 4 + k]);
316             cp += 2;
317             b_len -= 2;
318         }
319     } else if (0 == sg_vpd_dev_id_iter(bp, len, &off, VPD_ASSOC_LU,
320                                        2 /* EUI */, 1 /* binary */)) {
321         dlen = bp[off + 3];
322         if (! ((8 == dlen) || (12 == dlen) || (16 ==dlen)))
323             return b;
324         cp = b;
325         for (k = 0; ((k < dlen) && (b_len > 1)); ++k) {
326             snprintf(cp, b_len, "%02x", bp[off + 4 + k]);
327             cp += 2;
328             b_len -= 2;
329         }
330     } else if (sns_dlen > 0)
331         snprintf(b, b_len, "%.*s", sns_dlen, u_sns);
332     return b;
333 }
334 
335 #define SAFE_STD_INQ_RESP_LEN 36
336 #define VPD_SUPPORTED_VPDS 0x0
337 #define VPD_UNIT_SERIAL_NUM 0x80
338 #define VPD_DEVICE_ID 0x83
339 
340 static int
print_dev_id(int fd,uint8_t * sinq_resp,int max_rlen,int verbose)341 print_dev_id(int fd, uint8_t * sinq_resp, int max_rlen, int verbose)
342 {
343     int res, k, n, verb, pdt, has_sn, has_di;
344     uint8_t b[256];
345     char a[256];
346     char pdt_name[64];
347 
348     verb = (verbose > 1) ? verbose - 1 : 0;
349     memset(sinq_resp, 0, max_rlen);
350     res = sg_ll_inquiry(fd, false, false /* evpd */, 0 /* pg_op */, b,
351                         SAFE_STD_INQ_RESP_LEN, 1, verb);
352     if (res)
353         return res;
354     n = b[4] + 5;
355     if (n > SAFE_STD_INQ_RESP_LEN)
356         n = SAFE_STD_INQ_RESP_LEN;
357     memcpy(sinq_resp, b, (n < max_rlen) ? n : max_rlen);
358     if (n == SAFE_STD_INQ_RESP_LEN) {
359         pdt = b[0] & PDT_MASK;
360         printf("    %.8s  %.16s  %.4s   peripheral_type: %s [0x%x]\n",
361                (const char *)(b + 8), (const char *)(b + 16),
362                (const char *)(b + 32),
363                sg_get_pdt_str(pdt, sizeof(pdt_name), pdt_name), pdt);
364         if (verbose)
365             printf("      PROTECT=%d\n", !!(b[5] & 1));
366         if (b[5] & 1)
367             printf("      << supports protection information>>\n");
368     } else {
369         pr2serr("Short INQUIRY response: %d bytes, expect at least 36\n", n);
370         return SG_LIB_CAT_OTHER;
371     }
372     res = sg_ll_inquiry(fd, false, true /* evpd */, VPD_SUPPORTED_VPDS, b,
373                         SAFE_STD_INQ_RESP_LEN, 1, verb);
374     if (res) {
375         if (verbose)
376             pr2serr("VPD_SUPPORTED_VPDS gave res=%d\n", res);
377         return 0;
378     }
379     if (VPD_SUPPORTED_VPDS != b[1]) {
380         if (verbose)
381             pr2serr("VPD_SUPPORTED_VPDS corrupted\n");
382         return 0;
383     }
384     n = sg_get_unaligned_be16(b + 2);
385     if (n > (SAFE_STD_INQ_RESP_LEN - 4))
386         n = (SAFE_STD_INQ_RESP_LEN - 4);
387     for (k = 0, has_sn = 0, has_di = 0; k < n; ++k) {
388         if (VPD_UNIT_SERIAL_NUM == b[4 + k])
389             ++has_sn;
390         else if (VPD_DEVICE_ID == b[4 + k]) {
391             ++has_di;
392             break;
393         }
394     }
395     if (has_sn) {
396         res = sg_ll_inquiry(fd, false, true /* evpd */, VPD_UNIT_SERIAL_NUM,
397                             b, sizeof(b), 1, verb);
398         if (res) {
399             if (verbose)
400                 pr2serr("VPD_UNIT_SERIAL_NUM gave res=%d\n", res);
401             return 0;
402         }
403         if (VPD_UNIT_SERIAL_NUM != b[1]) {
404             if (verbose)
405                 pr2serr("VPD_UNIT_SERIAL_NUM corrupted\n");
406             return 0;
407         }
408         n = sg_get_unaligned_be16(b + 2);
409         if (n > (int)(sizeof(b) - 4))
410             n = (sizeof(b) - 4);
411         printf("      Unit serial number: %.*s\n", n, (const char *)(b + 4));
412     }
413     if (has_di) {
414         res = sg_ll_inquiry(fd, false, true /* evpd */, VPD_DEVICE_ID, b,
415                             sizeof(b), 1, verb);
416         if (res) {
417             if (verbose)
418                 pr2serr("VPD_DEVICE_ID gave res=%d\n", res);
419             return 0;
420         }
421         if (VPD_DEVICE_ID != b[1]) {
422             if (verbose)
423                 pr2serr("VPD_DEVICE_ID corrupted\n");
424             return 0;
425         }
426         n = sg_get_unaligned_be16(b + 2);
427         if (n > (int)(sizeof(b) - 4))
428             n = (sizeof(b) - 4);
429         n = strlen(get_lu_name(b, n + 4, a, sizeof(a)));
430         if (n > 0)
431             printf("      LU name: %.*s\n", n, a);
432     }
433     return 0;
434 }
435 
436 
437 int
main(int argc,char * argv[])438 main(int argc, char * argv[])
439 {
440     bool got_stdin = false;
441     int k, res, c, infd, progress, vb, n, resp_len, err;
442     int sg_fd = -1;
443     int param_lst_len = 0;
444     int ret = -1;
445     const char * device_name = NULL;
446     char ebuff[EBUFF_SZ];
447     char b[80];
448     uint8_t rsBuff[DEF_REQS_RESP_LEN];
449     uint8_t * wBuff = NULL;
450     uint8_t * free_wBuff = NULL;
451     struct opts_t opts;
452     struct opts_t * op;
453     struct stat a_stat;
454     uint8_t inq_resp[SAFE_STD_INQ_RESP_LEN];
455 
456     op = &opts;
457     memset(op, 0, sizeof(opts));
458     op->count = 1;
459     while (1) {
460         int option_index = 0;
461 
462         c = getopt_long(argc, argv, "ABc:CdDeFhi:IOp:Qt:T:vVwzZ",
463                         long_options, &option_index);
464         if (c == -1)
465             break;
466 
467         switch (c) {
468         case 'A':
469             op->ause = true;
470             break;
471         case 'B':
472             op->block = true;
473             break;
474         case 'c':
475             op->count = sg_get_num(optarg);
476             if ((op->count < 1) || (op->count > 31))  {
477                 pr2serr("bad argument to '--count', expect 1 to 31\n");
478                 return SG_LIB_SYNTAX_ERROR;
479             }
480             break;
481         case 'C':
482             op->crypto = true;
483             break;
484         case 'd':
485             op->desc = true;
486             break;
487         case 'D':
488             op->dry_run = true;
489             break;
490         case 'e':
491             op->early = true;
492             break;
493         case 'F':
494             op->fail = true;
495             break;
496         case 'h':
497         case '?':
498             usage();
499             return 0;
500         case 'i':
501             op->ipl = sg_get_num(optarg);
502             if ((op->ipl < 1) || (op->ipl > 65535))  {
503                 pr2serr("bad argument to '--ipl', expect 1 to 65535\n");
504                 return SG_LIB_SYNTAX_ERROR;
505             }
506             break;
507         case 'I':
508             op->invert = true;
509             break;
510         case 'O':
511             op->overwrite = true;
512             break;
513         case 'p':
514             op->pattern_fn = optarg;
515             break;
516         case 'Q':
517             op->quick = true;
518             break;
519         case 't':
520             op->timeout = sg_get_num(optarg);
521             if (op->timeout < 0) {
522                 pr2serr("bad argument to '--timeout=SECS', want 0 or more\n");
523                 return SG_LIB_SYNTAX_ERROR;
524             }
525             break;
526         case 'T':
527             op->test = sg_get_num(optarg);
528             if ((op->test < 0) || (op->test > 3))  {
529                 pr2serr("bad argument to '--test', expect 0 to 3\n");
530                 return SG_LIB_SYNTAX_ERROR;
531             }
532             break;
533         case 'v':
534             op->verbose_given = true;
535             ++op->verbose;
536             break;
537         case 'V':
538             op->version_given = true;
539             break;
540         case 'w':
541             op->wait = true;
542             break;
543         case 'z':
544             ++op->zero;
545             break;
546         case 'Z':
547             op->znr = true;
548             break;
549         default:
550             pr2serr("unrecognised option code 0x%x ??\n", c);
551             usage();
552             return SG_LIB_SYNTAX_ERROR;
553         }
554     }
555     if (optind < argc) {
556         if (NULL == device_name) {
557             device_name = argv[optind];
558             ++optind;
559         }
560         if (optind < argc) {
561             for (; optind < argc; ++optind)
562                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
563             usage();
564             return SG_LIB_SYNTAX_ERROR;
565         }
566     }
567 #ifdef DEBUG
568     pr2serr("In DEBUG mode, ");
569     if (op->verbose_given && op->version_given) {
570         pr2serr("but override: '-vV' given, zero verbose and continue\n");
571         op->verbose_given = false;
572         op->version_given = false;
573         op->verbose = 0;
574     } else if (! op->verbose_given) {
575         pr2serr("set '-vv'\n");
576         op->verbose = 2;
577     } else
578         pr2serr("keep verbose=%d\n", op->verbose);
579 #else
580     if (op->verbose_given && op->version_given)
581         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
582 #endif
583     if (op->version_given) {
584         pr2serr(ME "version: %s\n", version_str);
585         return 0;
586     }
587 
588     if (NULL == device_name) {
589         pr2serr("Missing device name!\n\n");
590         usage();
591         return SG_LIB_SYNTAX_ERROR;
592     }
593     vb = op->verbose;
594     n = (int)op->block + (int)op->crypto + (int)op->fail + (int)op->overwrite;
595     if (1 != n) {
596         pr2serr("one and only one of '--block', '--crypto', '--fail' or "
597                 "'--overwrite' please\n");
598         return SG_LIB_CONTRADICT;
599     }
600     if (op->overwrite) {
601         if (op->zero) {
602             if (op->pattern_fn) {
603                 pr2serr("confused: both '--pattern=PF' and '--zero' "
604                         "options\n");
605                 return SG_LIB_CONTRADICT;
606             }
607             op->ipl = 4;
608         } else {
609             if (NULL == op->pattern_fn) {
610                 pr2serr("'--overwrite' requires '--pattern=PF' or '--zero' "
611                         "option\n");
612                 return SG_LIB_CONTRADICT;
613             }
614             got_stdin = (0 == strcmp(op->pattern_fn, "-"));
615             if (! got_stdin) {
616                 memset(&a_stat, 0, sizeof(a_stat));
617                 if (stat(op->pattern_fn, &a_stat) < 0) {
618                     err = errno;
619                     pr2serr("pattern file: unable to stat(%s): %s\n",
620                             op->pattern_fn, safe_strerror(err));
621                     ret = sg_convert_errno(err);
622                     goto err_out;
623                 }
624                 if (op->ipl <= 0) {
625                     op->ipl = (int)a_stat.st_size;
626                     if (op->ipl > MAX_XFER_LEN) {
627                         pr2serr("pattern file length exceeds 65535 bytes, "
628                                 "need '--ipl=LEN' option\n");
629                          return SG_LIB_FILE_ERROR;
630                     }
631                 }
632             }
633             if (op->ipl < 1) {
634                 pr2serr("'--overwrite' requires '--ipl=LEN' option if can't "
635                         "get PF length\n");
636                 return SG_LIB_CONTRADICT;
637             }
638         }
639     }
640 
641     sg_fd = sg_cmds_open_device(device_name, false /* rw */, vb);
642     if (sg_fd < 0) {
643         if (op->verbose)
644             pr2serr(ME "open error: %s: %s\n", device_name,
645                     safe_strerror(-sg_fd));
646         ret = sg_convert_errno(-sg_fd);
647         goto err_out;
648     }
649 
650     ret = print_dev_id(sg_fd, inq_resp, sizeof(inq_resp), op->verbose);
651     if (ret)
652         goto err_out;
653 
654     if (op->overwrite) {
655         param_lst_len = op->ipl + 4;
656         wBuff = (uint8_t*)sg_memalign(op->ipl + 4, 0, &free_wBuff, false);
657         if (NULL == wBuff) {
658             pr2serr("unable to allocate %d bytes of memory with calloc()\n",
659                     op->ipl + 4);
660             ret = sg_convert_errno(ENOMEM);
661             goto err_out;
662         }
663         if (op->zero) {
664             if (2 == op->zero)  /* treat -zz as fill with 0xff bytes */
665                 memset(wBuff + 4, 0xff, op->ipl);
666             else
667                 memset(wBuff + 4, 0, op->ipl);
668         } else {
669             if (got_stdin) {
670                 infd = STDIN_FILENO;
671                 if (sg_set_binary_mode(STDIN_FILENO) < 0)
672                     perror("sg_set_binary_mode");
673             } else {
674                 if ((infd = open(op->pattern_fn, O_RDONLY)) < 0) {
675                     err = errno;
676                     snprintf(ebuff, EBUFF_SZ, ME "could not open %s for "
677                              "reading", op->pattern_fn);
678                     perror(ebuff);
679                     ret = sg_convert_errno(err);
680                     goto err_out;
681                 } else if (sg_set_binary_mode(infd) < 0)
682                     perror("sg_set_binary_mode");
683             }
684             res = read(infd, wBuff + 4, op->ipl);
685             if (res < 0) {
686                 err = errno;
687                 snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
688                          op->pattern_fn);
689                 perror(ebuff);
690                 if (! got_stdin)
691                     close(infd);
692                 ret = sg_convert_errno(err);
693                 goto err_out;
694             }
695             if (res < op->ipl) {
696                 pr2serr("tried to read %d bytes from %s, got %d bytes\n",
697                          op->ipl, op->pattern_fn, res);
698                 pr2serr("  so pad with 0x0 bytes and continue\n");
699             }
700             if (! got_stdin)
701                 close(infd);
702         }
703         wBuff[0] = op->count & 0x1f;
704         if (op->test)
705             wBuff[0] |= ((op->test & 0x3) << 5);
706         if (op->invert)
707             wBuff[0] |= 0x80;
708         sg_put_unaligned_be16((uint16_t)op->ipl, wBuff + 2);
709     }
710 
711     if ((! op->quick) && (! op->fail))
712         sg_warn_and_wait("SANITIZE", device_name, true);
713 
714     ret = do_sanitize(sg_fd, op, wBuff, param_lst_len);
715     if (ret) {
716         sg_get_category_sense_str(ret, sizeof(b), b, vb);
717         pr2serr("Sanitize failed: %s\n", b);
718     }
719 
720     if ((0 == ret) && (! op->early) && (! op->wait)) {
721         for (k = 0; ;++k) {     /* unbounded, exits via break */
722             if (op->dry_run && (k > 0)) {
723                 pr2serr("Due to --dry-run option, leave poll loop\n");
724                 break;
725             }
726             sg_sleep_secs(POLL_DURATION_SECS);
727             memset(rsBuff, 0x0, sizeof(rsBuff));
728             res = sg_ll_request_sense(sg_fd, op->desc, rsBuff, sizeof(rsBuff),
729                                       1, vb);
730             if (res) {
731                 ret = res;
732                 if (SG_LIB_CAT_INVALID_OP == res)
733                     pr2serr("Request Sense command not supported\n");
734                 else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
735                     pr2serr("bad field in Request Sense cdb\n");
736                     if (op->desc) {
737                         pr2serr("Descriptor type sense may not be supported, "
738                                 "try again with fixed type\n");
739                         op->desc = false;
740                         continue;
741                     }
742                 } else {
743                     sg_get_category_sense_str(res, sizeof(b), b, vb);
744                     pr2serr("Request Sense: %s\n", b);
745                     if (0 == vb)
746                         pr2serr("    try the '-v' option for more "
747                                 "information\n");
748                 }
749                 break;
750             }
751             /* "Additional sense length" same in descriptor and fixed */
752             resp_len = rsBuff[7] + 8;
753             if (vb > 2) {
754                 pr2serr("Parameter data in hex\n");
755                 hex2stderr(rsBuff, resp_len, -1);
756             }
757             progress = -1;
758             sg_get_sense_progress_fld(rsBuff, resp_len, &progress);
759             if (progress < 0) {
760                 ret = res;
761                 if (vb > 1)
762                      pr2serr("No progress indication found, iteration %d\n",
763                              k + 1);
764                 if ((0 == k) && vb)
765                      pr2serr("Sanitize seems to be successful and finished "
766                              "quickly\n");
767                 /* N.B. exits first time there isn't a progress indication */
768                 break;
769             } else
770                 printf("Progress indication: %d%% done\n",
771                        (progress * 100) / 65536);
772         }
773     }
774 
775 err_out:
776     if (free_wBuff)
777         free(free_wBuff);
778     if (sg_fd >= 0) {
779         res = sg_cmds_close_device(sg_fd);
780         if (res < 0) {
781             pr2serr("close error: %s\n", safe_strerror(-res));
782             if (0 == ret)
783                 ret = sg_convert_errno(-res);
784         }
785     }
786     if (0 == op->verbose) {
787         if (! sg_if_can2stderr("sg_sanitize failed: ", ret))
788             pr2serr("Some error occurred, try again with '-v' "
789                     "or '-vv' for more information\n");
790     }
791     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
792 }
793