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