xref: /aosp_15_r20/external/sg3_utils/src/sg_unmap.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2009-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 <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 #include "sg_lib.h"
27 #include "sg_cmds_basic.h"
28 #include "sg_cmds_extra.h"
29 #include "sg_unaligned.h"
30 #include "sg_pr2serr.h"
31 
32 
33 /* A utility program originally written for the Linux OS SCSI subsystem.
34  *
35  * This utility invokes the UNMAP SCSI command to unmap (trim) one or more
36  * logical blocks. Note that DATA MAY BE LOST.
37  */
38 
39 static const char * version_str = "1.19 20220813";
40 
41 
42 #define DEF_TIMEOUT_SECS 60
43 #define MAX_NUM_ADDR 128
44 #define RCAP10_RESP_LEN 8
45 #define RCAP16_RESP_LEN 32
46 
47 #ifndef UINT32_MAX
48 #define UINT32_MAX ((uint32_t)-1)
49 #endif
50 
51 
52 static struct option long_options[] = {
53         {"all", required_argument, 0, 'A'},
54         {"anchor", no_argument, 0, 'a'},
55         {"dry-run", no_argument, 0, 'd'},
56         {"dry_run", no_argument, 0, 'd'},
57         {"force", no_argument, 0, 'f'},
58         {"grpnum", required_argument, 0, 'g'},
59         {"help", no_argument, 0, 'h'},
60         {"in", required_argument, 0, 'I'},
61         {"lba", required_argument, 0, 'l'},
62         {"num", required_argument, 0, 'n'},
63         {"timeout", required_argument, 0, 't'},
64         {"verbose", no_argument, 0, 'v'},
65         {"version", no_argument, 0, 'V'},
66         {0, 0, 0, 0},
67 };
68 
69 
70 static void
usage()71 usage()
72 {
73     pr2serr("Usage: "
74           "sg_unmap [--all=ST,RN[,LA]] [--anchor] [--dry-run] [--force]\n"
75           "                [--grpnum=GN] [--help] [--in=FILE] "
76           "[--lba=LBA,LBA...]\n"
77           "                [--num=NUM,NUM...] [--timeout=TO] [--verbose] "
78           "[--version]\n"
79           "                DEVICE\n"
80           "  where:\n"
81           "    --all=ST,RN[,LA]|-A ST,RN[,LA]    start unmaps at LBA ST, "
82           "RN blocks\n"
83           "                         per unmap until the end of disk, or "
84           "until\n"
85           "                         and including LBA LA (last)\n"
86           "    --anchor|-a          set anchor field in cdb\n"
87           "    --dry-run|-d         prepare but skip UNMAP call(s)\n"
88           "    --force|-f           don't ask for confirmation before "
89           "zapping media\n"
90           "    --grpnum=GN|-g GN    GN is group number field (def: 0)\n"
91           "    --help|-h            print out usage message\n"
92           "    --in=FILE|-I FILE    read LBA, NUM pairs from FILE (if "
93           "FILE is '-'\n"
94           "                         then stdin is read)\n"
95           "    --lba=LBA,LBA...|-l LBA,LBA...    LBA is the logical block "
96           "address\n"
97           "                                      to start NUM unmaps\n"
98           "    --num=NUM,NUM...|-n NUM,NUM...    NUM is number of logical "
99           "blocks to\n"
100           "                                      unmap starting at "
101           "corresponding LBA\n"
102           "    --timeout=TO|-t TO    command timeout (unit: seconds) "
103           "(def: 60)\n"
104           "    --verbose|-v         increase verbosity\n"
105           "    --version|-V         print version string and exit\n\n"
106           "Perform a SCSI UNMAP command. LBA, NUM and the values in FILE "
107           "are assumed\nto be decimal. Use '0x' prefix or 'h' suffix for "
108           "hex values.\n"
109           "Example to unmap LBA 0x12345:\n"
110           "    sg_unmap --lba=0x12345 --num=1 /dev/sdb\n"
111           "Example to unmap starting at LBA 0x12345, 256 blocks per command:"
112           "\n    sg_unmap --all=0x12345,256 /dev/sg2\n"
113           "until the end if /dev/sg2 (assumed to be a storage device)\n\n"
114           );
115     pr2serr("WARNING: This utility will destroy data on DEVICE in the given "
116             "range(s)\nthat will be unmapped. Unmap is also known as 'trim' "
117             "and is irreversible.\n");
118 }
119 
120 /* Read numbers (up to 64 bits in size) from command line (comma (or
121  * (single) space) separated list). Assumed decimal unless prefixed
122  * by '0x', '0X' or contains trailing 'h' or 'H' (which indicate hex).
123  * Returns 0 if ok, or 1 if error. */
124 static int
build_lba_arr(const char * inp,uint64_t * lba_arr,int * lba_arr_len,int max_arr_len)125 build_lba_arr(const char * inp, uint64_t * lba_arr, int * lba_arr_len,
126               int max_arr_len)
127 {
128     int in_len, k;
129     int64_t ll;
130     const char * lcp;
131     char * cp;
132     char * c2p;
133 
134     if ((NULL == inp) || (NULL == lba_arr) ||
135         (NULL == lba_arr_len))
136         return 1;
137     lcp = inp;
138     in_len = strlen(inp);
139     if (0 == in_len)
140         *lba_arr_len = 0;
141     if ('-' == inp[0]) {        /* read from stdin */
142         pr2serr("'--lba' cannot be read from stdin\n");
143         return 1;
144     } else {        /* list of numbers (default decimal) on command line */
145         k = strspn(inp, "0123456789aAbBcCdDeEfFhHxXiIkKmMgGtTpP, ");
146         if (in_len != k) {
147             pr2serr("build_lba_arr: error at pos %d\n", k + 1);
148             return 1;
149         }
150         for (k = 0; k < max_arr_len; ++k) {
151             ll = sg_get_llnum(lcp);
152             if (-1 != ll) {
153                 lba_arr[k] = (uint64_t)ll;
154                 cp = (char *)strchr(lcp, ',');
155                 c2p = (char *)strchr(lcp, ' ');
156                 if (NULL == cp)
157                     cp = c2p;
158                 if (NULL == cp)
159                     break;
160                 if (c2p && (c2p < cp))
161                     cp = c2p;
162                 lcp = cp + 1;
163             } else {
164                 pr2serr("build_lba_arr: error at pos %d\n",
165                         (int)(lcp - inp + 1));
166                 return 1;
167             }
168         }
169         *lba_arr_len = k + 1;
170         if (k == max_arr_len) {
171             pr2serr("build_lba_arr: array length exceeded\n");
172             return 1;
173         }
174     }
175     return 0;
176 }
177 
178 /* Read numbers (up to 32 bits in size) from command line (comma (or
179  * (single) space) separated list). Assumed decimal unless prefixed
180  * by '0x', '0X' or contains trailing 'h' or 'H' (which indicate hex).
181  * Returns 0 if ok, or 1 if error. */
182 static int
build_num_arr(const char * inp,uint32_t * num_arr,int * num_arr_len,int max_arr_len)183 build_num_arr(const char * inp, uint32_t * num_arr, int * num_arr_len,
184               int max_arr_len)
185 {
186     int in_len, k;
187     const char * lcp;
188     int64_t ll;
189     char * cp;
190     char * c2p;
191 
192     if ((NULL == inp) || (NULL == num_arr) ||
193         (NULL == num_arr_len))
194         return 1;
195     lcp = inp;
196     in_len = strlen(inp);
197     if (0 == in_len)
198         *num_arr_len = 0;
199     if ('-' == inp[0]) {        /* read from stdin */
200         pr2serr("'--len' cannot be read from stdin\n");
201         return 1;
202     } else {        /* list of numbers (default decimal) on command line */
203         k = strspn(inp, "0123456789aAbBcCdDeEfFhHxXiIkKmMgGtTpP, ");
204         if (in_len != k) {
205             pr2serr("build_num_arr: error at pos %d\n", k + 1);
206             return 1;
207         }
208         for (k = 0; k < max_arr_len; ++k) {
209             ll = sg_get_llnum(lcp);
210             if (-1 != ll) {
211                 if (ll > UINT32_MAX) {
212                     pr2serr("build_num_arr: number exceeds 32 bits at pos "
213                             "%d\n", (int)(lcp - inp + 1));
214                     return 1;
215                 }
216                 num_arr[k] = (uint32_t)ll;
217                 cp = (char *)strchr(lcp, ',');
218                 c2p = (char *)strchr(lcp, ' ');
219                 if (NULL == cp)
220                     cp = c2p;
221                 if (NULL == cp)
222                     break;
223                 if (c2p && (c2p < cp))
224                     cp = c2p;
225                 lcp = cp + 1;
226             } else {
227                 pr2serr("build_num_arr: error at pos %d\n",
228                         (int)(lcp - inp + 1));
229                 return 1;
230             }
231         }
232         *num_arr_len = k + 1;
233         if (k == max_arr_len) {
234             pr2serr("build_num_arr: array length exceeded\n");
235             return 1;
236         }
237     }
238     return 0;
239 }
240 
241 
242 /* Read numbers from filename (or stdin) line by line (comma (or
243  * (single) space) separated list). Assumed decimal unless prefixed
244  * by '0x', '0X' or contains trailing 'h' or 'H' (which indicate hex).
245  * Returns 0 if ok, or 1 if error. */
246 static int
build_joint_arr(const char * file_name,uint64_t * lba_arr,uint32_t * num_arr,int * arr_len,int max_arr_len)247 build_joint_arr(const char * file_name, uint64_t * lba_arr, uint32_t * num_arr,
248                 int * arr_len, int max_arr_len)
249 {
250     bool have_stdin;
251     int off = 0;
252     int in_len, k, j, m, ind, bit0;
253     int64_t ll;
254     char line[1024];
255     char * lcp;
256     FILE * fp = NULL;
257 
258     have_stdin = ((1 == strlen(file_name)) && ('-' == file_name[0]));
259     if (have_stdin)
260         fp = stdin;
261     else {
262         fp = fopen(file_name, "r");
263         if (NULL == fp) {
264             pr2serr("%s: unable to open %s\n", __func__, file_name);
265             return 1;
266         }
267     }
268 
269     for (j = 0; j < 512; ++j) {
270         if (NULL == fgets(line, sizeof(line), fp))
271             break;
272         // could improve with carry_over logic if sizeof(line) too small
273         in_len = strlen(line);
274         if (in_len > 0) {
275             if ('\n' == line[in_len - 1]) {
276                 --in_len;
277                 line[in_len] = '\0';
278             }
279         }
280         if (in_len < 1)
281             continue;
282         lcp = line;
283         m = strspn(lcp, " \t");
284         if (m == in_len)
285             continue;
286         lcp += m;
287         in_len -= m;
288         if ('#' == *lcp)
289             continue;
290         k = strspn(lcp, "0123456789aAbBcCdDeEfFhHxXiIkKmMgGtTpP ,\t");
291         if ((k < in_len) && ('#' != lcp[k])) {
292             pr2serr("%s: syntax error at line %d, pos %d\n", __func__, j + 1,
293                     m + k + 1);
294             goto bad_exit;
295         }
296         for (k = 0; k < 1024; ++k) {
297             ll = sg_get_llnum(lcp);
298             if (-1 != ll) {
299                 ind = ((off + k) >> 1);
300                 bit0 = 0x1 & (off + k);
301                 if (ind >= max_arr_len) {
302                     pr2serr("%s: array length exceeded\n", __func__);
303                     goto bad_exit;
304                 }
305                 if (bit0) {
306                     if (ll > UINT32_MAX) {
307                         pr2serr("%s: number exceeds 32 bits in line %d, at "
308                                 "pos %d\n", __func__, j + 1,
309                                 (int)(lcp - line + 1));
310                         goto bad_exit;
311                     }
312                     num_arr[ind] = (uint32_t)ll;
313                 } else
314                    lba_arr[ind] = (uint64_t)ll;
315                 lcp = strpbrk(lcp, " ,\t");
316                 if (NULL == lcp)
317                     break;
318                 lcp += strspn(lcp, " ,\t");
319                 if ('\0' == *lcp)
320                     break;
321             } else {
322                 if ('#' == *lcp) {
323                     --k;
324                     break;
325                 }
326                 pr2serr("%s: error on line %d, at pos %d\n", __func__, j + 1,
327                         (int)(lcp - line + 1));
328                 goto bad_exit;
329             }
330         }
331         off += (k + 1);
332     }
333     if (0x1 & off) {
334         pr2serr("%s: expect LBA,NUM pairs but decoded odd number\n  from "
335                 "%s\n", __func__, have_stdin ? "stdin" : file_name);
336         goto bad_exit;
337     }
338     *arr_len = off >> 1;
339     if (fp && (! have_stdin))
340         fclose(fp);
341     return 0;
342 
343 bad_exit:
344     if (fp && (! have_stdin))
345         fclose(fp);
346     return 1;
347 }
348 
349 
350 int
main(int argc,char * argv[])351 main(int argc, char * argv[])
352 {
353     bool anchor = false;
354     bool do_force = false;
355     bool dry_run = false;
356     bool err_printed = false;
357     bool verbose_given = false;
358     bool version_given = false;
359     int res, c, num, k, j;
360     int sg_fd = -1;
361     int grpnum = 0;
362     int addr_arr_len = 0;
363     int num_arr_len = 0;
364     int param_len = 4;
365     int ret = 0;
366     int timeout = DEF_TIMEOUT_SECS;
367     int vb = 0;
368     uint32_t all_rn = 0;        /* Repetition Number, 0 for inactive */
369     uint64_t all_start = 0;
370     uint64_t all_last = 0;
371     int64_t ll;
372     const char * lba_op = NULL;
373     const char * num_op = NULL;
374     const char * in_op = NULL;
375     const char * device_name = NULL;
376     char * first_comma = NULL;
377     char * second_comma = NULL;
378     struct sg_simple_inquiry_resp inq_resp;
379     uint64_t addr_arr[MAX_NUM_ADDR];
380     uint32_t num_arr[MAX_NUM_ADDR];
381     uint8_t param_arr[8 + (MAX_NUM_ADDR * 16)];
382 
383     while (1) {
384         int option_index = 0;
385 
386         c = getopt_long(argc, argv, "aA:dfg:hI:Hl:n:t:vV", long_options,
387                         &option_index);
388         if (c == -1)
389             break;
390 
391         switch (c) {
392         case 'a':
393             anchor = true;
394             break;
395         case 'A':
396             first_comma = strchr(optarg, ',');
397             if (NULL == first_comma) {
398                 pr2serr("--all=ST,RN[,LA] expects at least one comma in "
399                         "argument, found none\n");
400                 return SG_LIB_SYNTAX_ERROR;
401             }
402             ll = sg_get_llnum(optarg);
403             if (ll < 0) {
404                 pr2serr("unable to decode --all=ST,.... (starting LBA)\n");
405                 return SG_LIB_SYNTAX_ERROR;
406             }
407             all_start = (uint64_t)ll;
408             ll = sg_get_llnum(first_comma + 1);
409             if ((ll < 0) || (ll > UINT32_MAX)) {
410                 pr2serr("unable to decode --all=ST,RN.... (repeat number)\n");
411                 return SG_LIB_SYNTAX_ERROR;
412             }
413             all_rn = (uint32_t)ll;
414             if (0 == ll)
415                 pr2serr("warning: --all=ST,RN... being ignored because RN "
416                         "is 0\n");
417             second_comma = strchr(first_comma + 1, ',');
418             if (second_comma) {
419                 ll = sg_get_llnum(second_comma + 1);
420                 if (ll < 0) {
421                     pr2serr("unable to decode --all=ST,NR,LA (last LBA)\n");
422                     return SG_LIB_SYNTAX_ERROR;
423                 }
424                 all_last = (uint64_t)ll;
425             }
426             break;
427         case 'd':
428             dry_run = true;
429             break;
430         case 'f':
431             do_force = true;
432             break;
433         case 'g':
434             num = sscanf(optarg, "%d", &res);
435             if ((1 == num) && (res >= 0) && (res <= 63))
436                 grpnum = res;
437             else {
438                 pr2serr("value for '--grpnum=' must be 0 to 63\n");
439                 return SG_LIB_SYNTAX_ERROR;
440             }
441             break;
442         case 'h':
443         case '?':
444             usage();
445             return 0;
446         case 'I':
447             in_op = optarg;
448             break;
449         case 'l':
450             lba_op = optarg;
451             break;
452         case 'n':
453             num_op = optarg;
454             break;
455         case 't':
456             timeout = sg_get_num(optarg);
457             if (timeout < 0)  {
458                 pr2serr("bad argument to '--timeout'\n");
459                 return SG_LIB_SYNTAX_ERROR;
460             } else if (0 == timeout)
461                 timeout = DEF_TIMEOUT_SECS;
462             break;
463         case 'v':
464             verbose_given = true;
465             ++vb;
466             break;
467         case 'V':
468             version_given = true;
469             break;
470         default:
471             pr2serr("unrecognised option code 0x%x ??\n", c);
472             usage();
473             return SG_LIB_SYNTAX_ERROR;
474         }
475     }
476     if (optind < argc) {
477         if (NULL == device_name) {
478             device_name = argv[optind];
479             ++optind;
480         }
481         if (optind < argc) {
482             for (; optind < argc; ++optind)
483                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
484             usage();
485             return SG_LIB_SYNTAX_ERROR;
486         }
487     }
488 
489 #ifdef DEBUG
490     pr2serr("In DEBUG mode, ");
491     if (verbose_given && version_given) {
492         pr2serr("but override: '-vV' given, zero verbose and continue\n");
493         verbose_given = false;
494         version_given = false;
495         vb = 0;
496     } else if (! verbose_given) {
497         pr2serr("set '-vv'\n");
498         vb = 2;
499     } else
500         pr2serr("keep verbose=%d\n", vb);
501 #else
502     if (verbose_given && version_given)
503         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
504 #endif
505     if (version_given) {
506         pr2serr("version: %s\n", version_str);
507         return 0;
508     }
509 
510     if (NULL == device_name) {
511         pr2serr("missing device name!\n\n");
512         usage();
513         return SG_LIB_SYNTAX_ERROR;
514     }
515 
516     if (all_rn > 0) {
517         if (lba_op || num_op || in_op) {
518             pr2serr("Can't have --all= together with --lba=, --num= or "
519                     "--in=\n\n");
520             usage();
521             return SG_LIB_CONTRADICT;
522         }
523         /* here if --all= looks okay so far */
524     } else if (in_op && (lba_op || num_op)) {
525         pr2serr("expect '--in=' by itself, or both '--lba=' and "
526                 "'--num='\n\n");
527         usage();
528         return SG_LIB_CONTRADICT;
529     } else if (in_op || (lba_op && num_op))
530         ;
531     else {
532         if (lba_op)
533             pr2serr("since '--lba=' is given, also need '--num='\n\n");
534         else
535             pr2serr("expect either both '--lba=' and '--num=', or "
536                     "'--in=', or '--all='\n\n");
537         usage();
538         return SG_LIB_CONTRADICT;
539     }
540 
541     if (all_rn > 0) {
542         if ((all_last > 0) && (all_start > all_last)) {
543             pr2serr("in --all=ST,RN,LA start address (ST) exceeds last "
544                     "address (LA)\n");
545             return SG_LIB_CONTRADICT;
546         }
547     } else {
548         memset(addr_arr, 0, sizeof(addr_arr));
549         memset(num_arr, 0, sizeof(num_arr));
550         addr_arr_len = 0;
551         if (lba_op && num_op) {
552             if (0 != build_lba_arr(lba_op, addr_arr, &addr_arr_len,
553                                    MAX_NUM_ADDR)) {
554                 pr2serr("bad argument to '--lba'\n");
555                 return SG_LIB_SYNTAX_ERROR;
556             }
557             if (0 != build_num_arr(num_op, num_arr, &num_arr_len,
558                                    MAX_NUM_ADDR)) {
559                 pr2serr("bad argument to '--num'\n");
560                 return SG_LIB_SYNTAX_ERROR;
561             }
562             if ((addr_arr_len != num_arr_len) || (num_arr_len <= 0)) {
563                 pr2serr("need same number of arguments to '--lba=' "
564                         "and '--num=' options\n");
565                 return SG_LIB_CONTRADICT;
566             }
567         }
568         if (in_op) {
569             if (0 != build_joint_arr(in_op, addr_arr, num_arr, &addr_arr_len,
570                                      MAX_NUM_ADDR)) {
571                 pr2serr("bad argument to '--in'\n");
572                 return SG_LIB_SYNTAX_ERROR;
573             }
574             if (addr_arr_len <= 0) {
575                 pr2serr("no addresses found in '--in=' argument, file: %s\n",
576                         in_op);
577                 return SG_LIB_SYNTAX_ERROR;
578             }
579         }
580         param_len = 8 + (16 * addr_arr_len);
581         memset(param_arr, 0, param_len);
582         k = 8;
583         for (j = 0; j < addr_arr_len; ++j) {
584             sg_put_unaligned_be64(addr_arr[j], param_arr + k);
585             k += 8;
586             sg_put_unaligned_be32(num_arr[j], param_arr + k);
587             k += 4 + 4;
588         }
589         k = 0;
590         num = param_len - 2;
591         sg_put_unaligned_be16((uint16_t)num, param_arr + k);
592         k += 2;
593         num = param_len - 8;
594         sg_put_unaligned_be16((uint16_t)num, param_arr + k);
595     }
596 
597     sg_fd = sg_cmds_open_device(device_name, false /* rw */, vb);
598     if (sg_fd < 0) {
599         ret = sg_convert_errno(-sg_fd);
600         pr2serr("open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
601         goto err_out;
602     }
603     ret = sg_simple_inquiry(sg_fd, &inq_resp, true, vb);
604 
605     if (all_rn > 0) {
606         bool last_retry;
607         bool to_end_of_device = false;
608         uint64_t ull;
609         uint32_t bump;
610 
611         if (0 == all_last) {    /* READ CAPACITY(10 or 16) to find last */
612             uint8_t resp_buff[RCAP16_RESP_LEN];
613 
614             res = sg_ll_readcap_16(sg_fd, false /* pmi */, 0 /* llba */,
615                                    resp_buff, RCAP16_RESP_LEN, true, vb);
616             if (SG_LIB_CAT_UNIT_ATTENTION == res) {
617                 pr2serr("Read capacity(16) unit attention, try again\n");
618                 res = sg_ll_readcap_16(sg_fd, false, 0, resp_buff,
619                                        RCAP16_RESP_LEN, true, vb);
620             }
621             if (0 == res) {
622                 if (vb > 3) {
623                     pr2serr("Read capacity(16) response:\n");
624                     hex2stderr(resp_buff, RCAP16_RESP_LEN, 1);
625                 }
626                 all_last = sg_get_unaligned_be64(resp_buff + 0);
627             } else if ((SG_LIB_CAT_INVALID_OP == res) ||
628                        (SG_LIB_CAT_ILLEGAL_REQ == res)) {
629                 if (vb)
630                     pr2serr("Read capacity(16) not supported, try Read "
631                             "capacity(10)\n");
632                 res = sg_ll_readcap_10(sg_fd, false /* pmi */, 0 /* lba */,
633                                        resp_buff, RCAP10_RESP_LEN, true,
634                                        vb);
635                 if (0 == res) {
636                     if (vb > 3) {
637                         pr2serr("Read capacity(10) response:\n");
638                         hex2stderr(resp_buff, RCAP10_RESP_LEN, 1);
639                     }
640                     all_last = (uint64_t)sg_get_unaligned_be32(resp_buff + 0);
641                 } else {
642                     if (res < 0)
643                         res = sg_convert_errno(-res);
644                     pr2serr("Read capacity(10) failed\n");
645                     ret = res;
646                     goto err_out;
647                 }
648             } else {
649                 if (res < 0)
650                     res = sg_convert_errno(-res);
651                 pr2serr("Read capacity(16) failed\n");
652                 ret = res;
653                 goto err_out;
654             }
655             if (all_start > all_last) {
656                 pr2serr("after READ CAPACITY the last block (0x%" PRIx64
657                         ") less than start address (0x%" PRIx64 ")\n",
658                         all_start, all_last);
659                 ret = SG_LIB_CONTRADICT;
660                 goto err_out;
661             }
662             to_end_of_device = true;
663         }
664         if (! do_force) {
665             char b[120];
666 
667             printf("%s is:  %.8s  %.16s  %.4s\n", device_name,
668                    inq_resp.vendor, inq_resp.product, inq_resp.revision);
669             sg_sleep_secs(3);
670             if (to_end_of_device)
671                 snprintf(b, sizeof(b), "%s from LBA 0x%" PRIx64 " to end "
672                          "(0x%" PRIx64 ")", device_name, all_start, all_last);
673             else
674                 snprintf(b, sizeof(b), "%s from LBA 0x%" PRIx64 " to 0x%"
675                          PRIx64, device_name, all_start, all_last);
676             sg_warn_and_wait("UNMAP (a.k.a. trim)", b, false);
677         }
678         if (dry_run) {
679             pr2serr("Doing dry-run, would have unmapped from LBA 0x%" PRIx64
680                     " to 0x%" PRIx64 "\n    %u blocks per UNMAP command\n",
681                     all_start, all_last, all_rn);
682            goto err_out;
683         }
684         last_retry = false;
685         param_len = 8 + (16 * 1);
686         for (ull = all_start, j = 0; ull <= all_last; ull += bump, ++j) {
687             if ((all_last - ull) < all_rn)
688                 bump = (uint32_t)(all_last + 1 - ull);
689             else
690                 bump = all_rn;
691 retry:
692             memset(param_arr, 0, param_len);
693             k = 8;
694             sg_put_unaligned_be64(ull, param_arr + k);
695             k += 8;
696             sg_put_unaligned_be32(bump, param_arr + k);
697             k = 0;
698             num = param_len - 2;
699             sg_put_unaligned_be16((uint16_t)num, param_arr + k);
700             k += 2;
701             num = param_len - 8;
702             sg_put_unaligned_be16((uint16_t)num, param_arr + k);
703             ret = sg_ll_unmap_v2(sg_fd, anchor, grpnum, timeout, param_arr,
704                                  param_len, true, (vb > 2 ? vb - 2 : 0));
705             if (last_retry)
706                 break;
707             if (ret) {
708                 if ((SG_LIB_LBA_OUT_OF_RANGE == ret) &&
709                     ((ull + bump) > all_last)) {
710                     pr2serr("Typical end of disk out-of-range, decrement "
711                             "count and retry\n");
712                     if (bump > 1) {
713                         --bump;
714                         last_retry = true;
715                         goto retry;
716                     }  /* if bump==1 can't do last, so we are finished */
717                 }
718                 break;
719             }
720         }       /* end of for loop doing unmaps */
721         if (vb)
722             pr2serr("Completed %d UNMAP commands\n", j);
723     } else {            /* --all= not given */
724         if (dry_run) {
725             pr2serr("Doing dry-run so here is 'LBA, number_of_blocks' list "
726                     "of candidates\n");
727             k = 8;
728             for (j = 0; j < addr_arr_len; ++j) {
729                 printf("    0x%" PRIx64 ", 0x%u\n",
730                       sg_get_unaligned_be64(param_arr + k),
731                       sg_get_unaligned_be32(param_arr + k + 8));
732                 k += (8 + 4 + 4);
733             }
734             goto err_out;
735         }
736         if (! do_force) {
737             printf("%s is:  %.8s  %.16s  %.4s\n", device_name,
738                    inq_resp.vendor, inq_resp.product, inq_resp.revision);
739             sg_sleep_secs(3);
740             printf("\nAn UNMAP (a.k.a. trim) will commence in 15 seconds\n");
741             printf("    Some data will be LOST\n");
742             printf("        Press control-C to abort\n");
743             sg_sleep_secs(5);
744             printf("\nAn UNMAP will commence in 10 seconds\n");
745             printf("    Some data will be LOST\n");
746             printf("        Press control-C to abort\n");
747             sg_sleep_secs(5);
748             printf("\nAn UNMAP (a.k.a. trim) will commence in 5 seconds\n");
749             printf("    Some data will be LOST\n");
750             printf("        Press control-C to abort\n");
751             sg_sleep_secs(7);
752         }
753         res = sg_ll_unmap_v2(sg_fd, anchor, grpnum, timeout, param_arr,
754                              param_len, true, vb);
755         ret = res;
756         err_printed = true;
757         switch (ret) {
758         case SG_LIB_CAT_NOT_READY:
759             pr2serr("UNMAP failed, device not ready\n");
760             break;
761         case SG_LIB_CAT_UNIT_ATTENTION:
762             pr2serr("UNMAP, unit attention\n");
763             break;
764         case SG_LIB_CAT_ABORTED_COMMAND:
765             pr2serr("UNMAP, aborted command\n");
766             break;
767         case SG_LIB_CAT_INVALID_OP:
768             pr2serr("UNMAP not supported\n");
769             break;
770         case SG_LIB_CAT_ILLEGAL_REQ:
771             pr2serr("bad field in UNMAP cdb\n");
772             break;
773         default:
774             err_printed = false;
775             break;
776         }
777     }
778 
779 err_out:
780     if (sg_fd >= 0) {
781         res = sg_cmds_close_device(sg_fd);
782         if (res < 0) {
783             pr2serr("close error: %s\n", safe_strerror(-res));
784             if (0 == ret)
785                 ret = sg_convert_errno(-res);
786         }
787     }
788     if ((0 == vb) && (! err_printed)) {
789         if (! sg_if_can2stderr("sg_unmap failed: ", ret))
790             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
791                     "more information\n");
792     }
793     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
794 }
795