]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/fdisk-list.c
fdisk: add 'F' command to list free unpartitioned space
[thirdparty/util-linux.git] / disk-utils / fdisk-list.c
1 #include <libfdisk.h>
2 #include <libsmartcols.h>
3 #include <assert.h>
4
5 #include "c.h"
6 #include "xalloc.h"
7 #include "nls.h"
8 #include "blkdev.h"
9 #include "mbsalign.h"
10 #include "pathnames.h"
11 #include "canonicalize.h"
12 #include "strutils.h"
13 #include "sysfs.h"
14 #include "colors.h"
15 #include "ttyutils.h"
16
17 #include "fdisk-list.h"
18
19 /* see init_fields() */
20 static const char *fields_string;
21 static int *fields_ids;
22 static size_t fields_nids;
23 static const struct fdisk_label *fields_label;
24
25 static int is_ide_cdrom_or_tape(char *device)
26 {
27 int fd, ret;
28
29 if ((fd = open(device, O_RDONLY)) < 0)
30 return 0;
31 ret = blkdev_is_cdrom(fd);
32
33 close(fd);
34 return ret;
35 }
36
37
38 void list_disk_geometry(struct fdisk_context *cxt)
39 {
40 char *id = NULL;
41 struct fdisk_label *lb = fdisk_get_label(cxt, NULL);
42 uint64_t bytes = fdisk_get_nsectors(cxt) * fdisk_get_sector_size(cxt);
43 char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE
44 | SIZE_SUFFIX_3LETTER, bytes);
45
46 color_scheme_enable("header", UL_COLOR_BOLD);
47 fdisk_info(cxt, _("Disk %s: %s, %ju bytes, %ju sectors"),
48 fdisk_get_devname(cxt), strsz,
49 bytes, (uintmax_t) fdisk_get_nsectors(cxt));
50 color_disable();
51 free(strsz);
52
53 if (lb && (fdisk_label_require_geometry(lb) || fdisk_use_cylinders(cxt)))
54 fdisk_info(cxt, _("Geometry: %d heads, %llu sectors/track, %llu cylinders"),
55 fdisk_get_geom_heads(cxt),
56 fdisk_get_geom_sectors(cxt),
57 fdisk_get_geom_cylinders(cxt));
58
59 fdisk_info(cxt, _("Units: %s of %d * %ld = %ld bytes"),
60 fdisk_get_unit(cxt, FDISK_PLURAL),
61 fdisk_get_units_per_sector(cxt),
62 fdisk_get_sector_size(cxt),
63 fdisk_get_units_per_sector(cxt) * fdisk_get_sector_size(cxt));
64
65 fdisk_info(cxt, _("Sector size (logical/physical): %lu bytes / %lu bytes"),
66 fdisk_get_sector_size(cxt),
67 fdisk_get_physector_size(cxt));
68 fdisk_info(cxt, _("I/O size (minimum/optimal): %lu bytes / %lu bytes"),
69 fdisk_get_minimal_iosize(cxt),
70 fdisk_get_optimal_iosize(cxt));
71 if (fdisk_get_alignment_offset(cxt))
72 fdisk_info(cxt, _("Alignment offset: %lu bytes"),
73 fdisk_get_alignment_offset(cxt));
74 if (fdisk_has_label(cxt))
75 fdisk_info(cxt, _("Disklabel type: %s"),
76 fdisk_label_get_name(lb));
77
78 if (!fdisk_is_details(cxt) && fdisk_get_disklabel_id(cxt, &id) == 0 && id)
79 fdisk_info(cxt, _("Disk identifier: %s"), id);
80 }
81
82 void list_disklabel(struct fdisk_context *cxt)
83 {
84 struct fdisk_table *tb = NULL;
85 struct fdisk_partition *pa = NULL;
86 struct fdisk_iter *itr = NULL;
87 struct fdisk_label *lb;
88 struct libscols_table *out = NULL;
89 const char *bold = NULL;
90 int *ids = NULL; /* IDs of fdisk_fields */
91 size_t nids = 0, i;
92 int post = 0;
93
94 /* print label specific stuff by libfdisk FDISK_ASK_INFO API */
95 fdisk_list_disklabel(cxt);
96
97 /* get partitions and generate output */
98 if (fdisk_get_partitions(cxt, &tb) || fdisk_table_get_nents(tb) <= 0)
99 goto done;
100
101 ids = init_fields(cxt, NULL, &nids);
102 if (!ids)
103 goto done;
104
105 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
106 if (!itr) {
107 fdisk_warn(cxt, _("failed to allocate iterator"));
108 goto done;
109 }
110
111 out = scols_new_table();
112 if (!out) {
113 fdisk_warn(cxt, _("failed to allocate output table"));
114 goto done;
115 }
116
117 if (colors_wanted()) {
118 scols_table_enable_colors(out, 1);
119 bold = color_scheme_get_sequence("header", UL_COLOR_BOLD);
120 }
121
122 lb = fdisk_get_label(cxt, NULL);
123 assert(lb);
124
125 /* define output table columns */
126 for (i = 0; i < nids; i++) {
127 int fl = 0;
128 struct libscols_column *co;
129 const struct fdisk_field *field =
130 fdisk_label_get_field(lb, ids[i]);
131 if (!field)
132 continue;
133 if (fdisk_field_is_number(field))
134 fl |= SCOLS_FL_RIGHT;
135 if (fdisk_field_get_id(field) == FDISK_FIELD_TYPE)
136 fl |= SCOLS_FL_TRUNC;
137
138 co = scols_table_new_column(out,
139 _(fdisk_field_get_name(field)),
140 fdisk_field_get_width(field), fl);
141 if (!co)
142 goto done;
143
144 /* set colum header color */
145 if (bold)
146 scols_cell_set_color(scols_column_get_header(co), bold);
147 }
148
149 /* fill-in output table */
150 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
151 struct libscols_line *ln = scols_table_new_line(out, NULL);
152
153 if (!ln) {
154 fdisk_warn(cxt, _("failed to allocate output line"));
155 goto done;
156 }
157
158 for (i = 0; i < nids; i++) {
159 char *data = NULL;
160
161 if (fdisk_partition_to_string(pa, cxt, ids[i], &data))
162 continue;
163 scols_line_refer_data(ln, i, data);
164 }
165 }
166
167 /* print */
168 if (!scols_table_is_empty(out)) {
169 fputc('\n', stdout);
170 scols_print_table(out);
171 }
172
173 /* print warnings */
174 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
175 while (itr && fdisk_table_next_partition(tb, itr, &pa) == 0) {
176 if (!fdisk_partition_has_start(pa))
177 continue;
178 if (!fdisk_lba_is_phy_aligned(cxt, fdisk_partition_get_start(pa))) {
179 if (!post)
180 fputc('\n', stdout);
181 fdisk_warnx(cxt, _("Partition %zu does not start on physical sector boundary."),
182 fdisk_partition_get_partno(pa) + 1);
183 post++;
184 }
185 }
186
187 if (fdisk_table_wrong_order(tb)) {
188 if (!post)
189 fputc('\n', stdout);
190 fdisk_info(cxt, _("Partition table entries are not in disk order."));
191 }
192 done:
193 scols_unref_table(out);
194 fdisk_unref_table(tb);
195 fdisk_free_iter(itr);
196 }
197
198 void list_freespace(struct fdisk_context *cxt)
199 {
200 struct fdisk_table *tb = NULL;
201 struct fdisk_partition *pa = NULL;
202 struct fdisk_iter *itr = NULL;
203 struct libscols_table *out = NULL;
204 const char *bold = NULL;
205 size_t i;
206 uintmax_t sumsize = 0, bytes = 0;
207 char *strsz;
208
209 static const char *colnames[] = { N_("Start"), N_("End"), N_("Sectors"), N_("Size") };
210 static const int colids[] = { FDISK_FIELD_START, FDISK_FIELD_END, FDISK_FIELD_SECTORS, FDISK_FIELD_SIZE };
211
212 if (fdisk_get_freespaces(cxt, &tb))
213 goto done;
214
215 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
216 if (!itr) {
217 fdisk_warn(cxt, _("failed to allocate iterator"));
218 goto done;
219 }
220
221 out = scols_new_table();
222 if (!out) {
223 fdisk_warn(cxt, _("failed to allocate output table"));
224 goto done;
225 }
226
227 if (colors_wanted()) {
228 scols_table_enable_colors(out, 1);
229 bold = color_scheme_get_sequence("header", UL_COLOR_BOLD);
230 }
231
232 for (i = 0; i < ARRAY_SIZE(colnames); i++) {
233 struct libscols_column *co = scols_table_new_column(out, _(colnames[i]), 5, SCOLS_FL_RIGHT);
234
235 if (!co)
236 goto done;
237 if (bold)
238 scols_cell_set_color(scols_column_get_header(co), bold);
239 }
240
241 /* fill-in output table */
242 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
243 struct libscols_line *ln = scols_table_new_line(out, NULL);
244 char *data;
245
246 if (!ln) {
247 fdisk_warn(cxt, _("failed to allocate output line"));
248 goto done;
249 }
250 for (i = 0; i < ARRAY_SIZE(colids); i++) {
251 if (fdisk_partition_to_string(pa, cxt, colids[i], &data))
252 continue;
253 scols_line_refer_data(ln, i, data);
254 }
255
256 if (fdisk_partition_has_size(pa))
257 sumsize += fdisk_partition_get_size(pa);
258 }
259
260 bytes = sumsize * fdisk_get_sector_size(cxt);
261 strsz = size_to_human_string(SIZE_SUFFIX_SPACE
262 | SIZE_SUFFIX_3LETTER, bytes);
263
264 color_scheme_enable("header", UL_COLOR_BOLD);
265 fdisk_info(cxt, _("Unpartitioned space %s: %s, %ju bytes, %ju sectors"),
266 fdisk_get_devname(cxt), strsz,
267 bytes, sumsize);
268 color_disable();
269 free(strsz);
270
271 fdisk_info(cxt, _("Units: %s of %d * %ld = %ld bytes"),
272 fdisk_get_unit(cxt, FDISK_PLURAL),
273 fdisk_get_units_per_sector(cxt),
274 fdisk_get_sector_size(cxt),
275 fdisk_get_units_per_sector(cxt) * fdisk_get_sector_size(cxt));
276
277 fdisk_info(cxt, _("Sector size (logical/physical): %lu bytes / %lu bytes"),
278 fdisk_get_sector_size(cxt),
279 fdisk_get_physector_size(cxt));
280
281 /* print */
282 if (!scols_table_is_empty(out)) {
283 fputc('\n', stdout);
284 scols_print_table(out);
285 }
286 done:
287 scols_unref_table(out);
288 fdisk_unref_table(tb);
289 fdisk_free_iter(itr);
290 }
291
292 char *next_proc_partition(FILE **f)
293 {
294 char line[128 + 1];
295
296 if (!*f) {
297 *f = fopen(_PATH_PROC_PARTITIONS, "r");
298 if (!*f) {
299 warn(_("cannot open %s"), _PATH_PROC_PARTITIONS);
300 return NULL;
301 }
302 }
303
304 while (fgets(line, sizeof(line), *f)) {
305 char buf[PATH_MAX], *cn;
306 dev_t devno;
307
308 if (sscanf(line, " %*d %*d %*d %128[^\n ]", buf) != 1)
309 continue;
310
311 devno = sysfs_devname_to_devno(buf, NULL);
312 if (devno <= 0)
313 continue;
314
315 if (sysfs_devno_is_lvm_private(devno) ||
316 sysfs_devno_is_wholedisk(devno) <= 0)
317 continue;
318
319 if (!sysfs_devno_to_devpath(devno, buf, sizeof(buf)))
320 continue;
321
322 cn = canonicalize_path(buf);
323 if (!cn)
324 continue;
325
326 if (!is_ide_cdrom_or_tape(cn))
327 return cn;
328 }
329 fclose(*f);
330 *f = NULL;
331
332 return NULL;
333 }
334
335 int print_device_pt(struct fdisk_context *cxt, char *device, int warnme, int verify)
336 {
337 if (fdisk_assign_device(cxt, device, 1) != 0) { /* read-only */
338 if (warnme || errno == EACCES)
339 warn(_("cannot open %s"), device);
340 return -1;
341 }
342
343 list_disk_geometry(cxt);
344
345 if (fdisk_has_label(cxt)) {
346 list_disklabel(cxt);
347 if (verify)
348 fdisk_verify_disklabel(cxt);
349 }
350 fdisk_deassign_device(cxt, 1);
351 return 0;
352 }
353
354 int print_device_freespace(struct fdisk_context *cxt, char *device, int warnme)
355 {
356 if (fdisk_assign_device(cxt, device, 1) != 0) { /* read-only */
357 if (warnme || errno == EACCES)
358 warn(_("cannot open %s"), device);
359 return -1;
360 }
361
362 list_freespace(cxt);
363 fdisk_deassign_device(cxt, 1);
364 return 0;
365 }
366
367 void print_all_devices_pt(struct fdisk_context *cxt, int verify)
368 {
369 FILE *f = NULL;
370 int ct = 0;
371 char *dev;
372
373 while ((dev = next_proc_partition(&f))) {
374 if (ct)
375 fputs("\n\n", stdout);
376 if (print_device_pt(cxt, dev, 0, verify) == 0)
377 ct++;
378 free(dev);
379 }
380 }
381
382 void print_all_devices_freespace(struct fdisk_context *cxt)
383 {
384 FILE *f = NULL;
385 int ct = 0;
386 char *dev;
387
388 while ((dev = next_proc_partition(&f))) {
389 if (ct)
390 fputs("\n\n", stdout);
391 if (print_device_freespace(cxt, dev, 0) == 0)
392 ct++;
393 free(dev);
394 }
395 }
396
397 /* usable for example in usage() */
398 void list_available_columns(FILE *out)
399 {
400 size_t i;
401 int termwidth;
402 struct fdisk_label *lb = NULL;
403 struct fdisk_context *cxt = fdisk_new_context();
404
405 if (!cxt)
406 return;
407
408 termwidth = get_terminal_width();
409 if (termwidth <= 0)
410 termwidth = 80;
411
412 fprintf(out, _("\nAvailable columns (for -o):\n"));
413
414 while (fdisk_next_label(cxt, &lb) == 0) {
415 size_t width = 6; /* label name and separators */
416
417 fprintf(out, " %s:", fdisk_label_get_name(lb));
418 for (i = 1; i < FDISK_NFIELDS; i++) {
419 const struct fdisk_field *fl = fdisk_label_get_field(lb, i);
420 const char *name = fl ? fdisk_field_get_name(fl) : NULL;
421 size_t len;
422
423 if (!name)
424 continue;
425 len = strlen(name) + 1;
426 if (width + len > (size_t) termwidth) {
427 fputs("\n ", out);
428 width = 6;
429 }
430 fprintf(out, " %s", name);
431 width += len;
432 }
433 fputc('\n', out);
434 }
435
436 fdisk_unref_context(cxt);
437 }
438
439 static int fieldname_to_id(const char *name, size_t namesz)
440 {
441 const struct fdisk_field *fl;
442 char buf[namesz + 1];
443
444 assert(name);
445 assert(namesz);
446 assert(fields_label);
447
448 memcpy(buf, name, namesz);
449 buf[namesz] = '\0';
450
451 fl = fdisk_label_get_field_by_name(fields_label, buf);
452 if (!fl) {
453 warnx(_("%s unknown column: %s"),
454 fdisk_label_get_name(fields_label), buf);
455 return -1;
456 }
457 return fdisk_field_get_id(fl);
458 }
459
460 /*
461 * Initialize array with output columns (fields_ids[]) according to
462 * comma delimited list of columns (@str). If the list string is not
463 * defined then use library defaults. This function is "-o <list>"
464 * backend.
465 *
466 * If the columns are already initialized then returns already existing columns.
467 */
468 int *init_fields(struct fdisk_context *cxt, const char *str, size_t *n)
469 {
470 int *dflt_ids = NULL;
471 struct fdisk_label *lb;
472
473 if (!fields_string)
474 fields_string = str;
475 if (!cxt)
476 goto done;
477
478 lb = fdisk_get_label(cxt, NULL);
479
480 if (!lb || fields_label != lb) { /* label changed: reset */
481 free(fields_ids);
482 fields_ids = NULL;
483 fields_label = lb;
484 fields_nids = 0;
485 }
486
487 if (!fields_label) /* no label */
488 goto done;
489 if (fields_nids)
490 goto done; /* already initialized */
491
492 /* library default */
493 if (fdisk_label_get_fields_ids(NULL, cxt, &dflt_ids, &fields_nids))
494 goto done;
495
496 fields_ids = xcalloc(FDISK_NFIELDS * 2, sizeof(int));
497
498 /* copy defaults to the list with wanted fields */
499 memcpy(fields_ids, dflt_ids, fields_nids * sizeof(int));
500 free(dflt_ids);
501
502 /* extend or replace fields_nids[] according to fields_string */
503 if (fields_string &&
504 string_add_to_idarray(fields_string, fields_ids, FDISK_NFIELDS * 2,
505 &fields_nids, fieldname_to_id) < 0)
506 exit(EXIT_FAILURE);
507 done:
508 fields_label = NULL;
509 if (n)
510 *n = fields_nids;
511 return fields_ids;
512 }
513