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