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