blob: cd4cedff07c1eb4b28427b7033373c0467fb1271 [file] [log] [blame]
bigbiff7b4c7a62015-01-01 19:44:14 -05001/*
2 *
3 * Copyright (C) 2012 Davidlohr Bueso <dave@gnu.org>
4 * 2013 Karel Zak <kzak@redhat.com>
5 *
6 * This is a re-written version for libfdisk, the original was fdisksgilabel.c
7 * from util-linux fdisk, by:
8 *
9 * Andreas Neuper, Sep 1998,
10 * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, Mar 1999,
11 * Phillip Kesling <pkesling@sgi.com>, Mar 2003.
12 */
13
14#include "c.h"
15#include "nls.h"
16#include "all-io.h"
17
18#include "blkdev.h"
19
20#include "bitops.h"
21#include "pt-sgi.h"
22#include "pt-mbr.h"
23#include "fdiskP.h"
24
25/**
26 * SECTION: sgi
27 * @title: SGI
28 * @short_description: disk label specific functions
29 *
30 */
31
32/*
33 * in-memory fdisk SGI stuff
34 */
35struct fdisk_sgi_label {
36 struct fdisk_label head; /* generic fdisk part */
37 struct sgi_disklabel *header; /* on-disk data (pointer to cxt->firstsector) */
38
39 struct sgi_freeblocks {
40 unsigned int first;
41 unsigned int last;
42 } freelist[SGI_MAXPARTITIONS + 1];
43};
44
45static struct fdisk_parttype sgi_parttypes[] =
46{
47 {SGI_TYPE_VOLHDR, N_("SGI volhdr")},
48 {SGI_TYPE_TRKREPL, N_("SGI trkrepl")},
49 {SGI_TYPE_SECREPL, N_("SGI secrepl")},
50 {SGI_TYPE_SWAP, N_("SGI raw")},
51 {SGI_TYPE_BSD, N_("SGI bsd")},
52 {SGI_TYPE_SYSV, N_("SGI sysv")},
53 {SGI_TYPE_ENTIRE_DISK, N_("SGI volume")},
54 {SGI_TYPE_EFS, N_("SGI efs")},
55 {SGI_TYPE_LVOL, N_("SGI lvol")},
56 {SGI_TYPE_RLVOL, N_("SGI rlvol")},
57 {SGI_TYPE_XFS, N_("SGI xfs")},
58 {SGI_TYPE_XFSLOG, N_("SGI xfslog")},
59 {SGI_TYPE_XLV, N_("SGI xlv")},
60 {SGI_TYPE_XVM, N_("SGI xvm")},
61 {MBR_LINUX_SWAP_PARTITION, N_("Linux swap")},
62 {MBR_LINUX_DATA_PARTITION, N_("Linux native")},
63 {MBR_LINUX_LVM_PARTITION, N_("Linux LVM")},
64 {MBR_LINUX_RAID_PARTITION, N_("Linux RAID")},
65 {0, NULL }
66};
67
68static unsigned int sgi_get_start_sector(struct fdisk_context *cxt, int i );
69static unsigned int sgi_get_num_sectors(struct fdisk_context *cxt, int i );
70static int sgi_get_bootpartition(struct fdisk_context *cxt);
71static int sgi_get_swappartition(struct fdisk_context *cxt);
72
73/* Returns a pointer buffer with on-disk data. */
74static inline struct sgi_disklabel *self_disklabel(struct fdisk_context *cxt)
75{
76 assert(cxt);
77 assert(cxt->label);
78 assert(fdisk_is_label(cxt, SGI));
79
80 return ((struct fdisk_sgi_label *) cxt->label)->header;
81}
82
83/* Returns in-memory fdisk data. */
84static inline struct fdisk_sgi_label *self_label(struct fdisk_context *cxt)
85{
86 assert(cxt);
87 assert(cxt->label);
88 assert(fdisk_is_label(cxt, SGI));
89
90 return (struct fdisk_sgi_label *) cxt->label;
91}
92
93/*
94 * Information within second on-disk block
95 */
96#define SGI_INFO_MAGIC 0x00072959
97
98struct sgi_info {
99 unsigned int magic; /* looks like a magic number */
100 unsigned int a2;
101 unsigned int a3;
102 unsigned int a4;
103 unsigned int b1;
104 unsigned short b2;
105 unsigned short b3;
106 unsigned int c[16];
107 unsigned short d[3];
108 unsigned char scsi_string[50];
109 unsigned char serial[137];
110 unsigned short check1816;
111 unsigned char installer[225];
112};
113
114static struct sgi_info *sgi_new_info(void)
115{
116 struct sgi_info *info = calloc(1, sizeof(struct sgi_info));
117
118 if (!info)
119 return NULL;
120
121 info->magic = cpu_to_be32(SGI_INFO_MAGIC);
122 info->b1 = cpu_to_be32(-1);
123 info->b2 = cpu_to_be16(-1);
124 info->b3 = cpu_to_be16(1);
125
126 /* You may want to replace this string !!!!!!! */
127 strcpy((char *) info->scsi_string, "IBM OEM 0662S12 3 30");
128 strcpy((char *) info->serial, "0000");
129 info->check1816 = cpu_to_be16(18 * 256 + 16);
130 strcpy((char *) info->installer, "Sfx version 5.3, Oct 18, 1994");
131
132 return info;
133}
134
135static void sgi_free_info(struct sgi_info *info)
136{
137 free(info);
138}
139
140/**
141 * fdisk_sgi_create_info:
142 * @cxt: context
143 *
144 * This function add hint about SGI label (e.g. set "sgilabel" as volume name)
145 * to the first SGI volume. This is probably old SGI convention without any
146 * effect to the device partitioning.
147 *
148 * Returns: 0 on success, <0 on error
149 */
150int fdisk_sgi_create_info(struct fdisk_context *cxt)
151{
152 struct sgi_disklabel *sgilabel = self_disklabel(cxt);
153
154 /* I keep SGI's habit to write the sgilabel to the second block */
155 sgilabel->volume[0].block_num = cpu_to_be32(2);
156 sgilabel->volume[0].num_bytes = cpu_to_be32(sizeof(struct sgi_info));
157 strncpy((char *) sgilabel->volume[0].name, "sgilabel", 8);
158
159 fdisk_info(cxt, _("SGI info created on second sector."));
160 return 0;
161}
162
163
164/*
165 * only dealing with free blocks here
166 */
167static void set_freelist(struct fdisk_context *cxt,
168 size_t i, unsigned int f, unsigned int l)
169{
170 struct fdisk_sgi_label *sgi = self_label(cxt);
171
172 if (i < ARRAY_SIZE(sgi->freelist)) {
173 sgi->freelist[i].first = f;
174 sgi->freelist[i].last = l;
175 }
176}
177
178static void add_to_freelist(struct fdisk_context *cxt,
179 unsigned int f, unsigned int l)
180{
181 struct fdisk_sgi_label *sgi = self_label(cxt);
182 size_t i;
183
184 for (i = 0; i < ARRAY_SIZE(sgi->freelist); i++) {
185 if (sgi->freelist[i].last == 0)
186 break;
187 }
188 set_freelist(cxt, i, f, l);
189}
190
191static void clear_freelist(struct fdisk_context *cxt)
192{
193 struct fdisk_sgi_label *sgi = self_label(cxt);
194
195 memset(sgi->freelist, 0, sizeof(sgi->freelist));
196}
197
198static unsigned int is_in_freelist(struct fdisk_context *cxt, unsigned int b)
199{
200 struct fdisk_sgi_label *sgi = self_label(cxt);
201 size_t i;
202
203 for (i = 0; i < ARRAY_SIZE(sgi->freelist); i++) {
204 if (sgi->freelist[i].first <= b
205 && sgi->freelist[i].last >= b)
206 return sgi->freelist[i].last;
207 }
208
209 return 0;
210}
211
212
213static int sgi_get_nsect(struct fdisk_context *cxt)
214{
215 struct sgi_disklabel *sgilabel = self_disklabel(cxt);
216 return be16_to_cpu(sgilabel->devparam.nsect);
217}
218
219static int sgi_get_ntrks(struct fdisk_context *cxt)
220{
221 struct sgi_disklabel *sgilabel = self_disklabel(cxt);
222 return be16_to_cpu(sgilabel->devparam.ntrks);
223}
224
225static size_t count_used_partitions(struct fdisk_context *cxt)
226{
227 size_t i, ct = 0;
228
229 for (i = 0; i < cxt->label->nparts_max; i++)
230 ct += sgi_get_num_sectors(cxt, i) > 0;
231
232 return ct;
233}
234
235static int sgi_probe_label(struct fdisk_context *cxt)
236{
237 struct fdisk_sgi_label *sgi;
238 struct sgi_disklabel *sgilabel;
239
240 assert(cxt);
241 assert(cxt->label);
242 assert(fdisk_is_label(cxt, SGI));
243 assert(sizeof(struct sgi_disklabel) <= 512);
244
245 /* map first sector to header */
246 sgi = (struct fdisk_sgi_label *) cxt->label;
247 sgi->header = (struct sgi_disklabel *) cxt->firstsector;
248 sgilabel = sgi->header;
249
250 if (be32_to_cpu(sgilabel->magic) != SGI_LABEL_MAGIC) {
251 sgi->header = NULL;
252 return 0;
253 }
254
255 /*
256 * test for correct checksum
257 */
258 if (sgi_pt_checksum(sgilabel) != 0)
259 fdisk_warnx(cxt, _("Detected an SGI disklabel with wrong checksum."));
260
261 clear_freelist(cxt);
262 cxt->label->nparts_max = SGI_MAXPARTITIONS;
263 cxt->label->nparts_cur = count_used_partitions(cxt);
264 return 1;
265}
266
267static int sgi_list_table(struct fdisk_context *cxt)
268{
269 struct sgi_disklabel *sgilabel = self_disklabel(cxt);
270 struct sgi_device_parameter *sgiparam = &sgilabel->devparam;
271 int rc = 0;
272
273 if (fdisk_is_details(cxt))
274 fdisk_info(cxt, _(
275 "Label geometry: %d heads, %llu sectors\n"
276 " %llu cylinders, %d physical cylinders\n"
277 " %d extra sects/cyl, interleave %d:1\n"),
278 cxt->geom.heads, cxt->geom.sectors,
279 cxt->geom.cylinders, be16_to_cpu(sgiparam->pcylcount),
280 (int) sgiparam->sparecyl, be16_to_cpu(sgiparam->ilfact));
281
282 fdisk_info(cxt, _("Bootfile: %s"), sgilabel->boot_file);
283 return rc;
284}
285
286static unsigned int sgi_get_start_sector(struct fdisk_context *cxt, int i)
287{
288 struct sgi_disklabel *sgilabel = self_disklabel(cxt);
289 return be32_to_cpu(sgilabel->partitions[i].first_block);
290}
291
292static unsigned int sgi_get_num_sectors(struct fdisk_context *cxt, int i)
293{
294 struct sgi_disklabel *sgilabel = self_disklabel(cxt);
295 return be32_to_cpu(sgilabel->partitions[i].num_blocks);
296}
297
298static int sgi_get_sysid(struct fdisk_context *cxt, int i)
299{
300 struct sgi_disklabel *sgilabel = self_disklabel(cxt);
301 return be32_to_cpu(sgilabel->partitions[i].type);
302}
303
304static int sgi_get_bootpartition(struct fdisk_context *cxt)
305{
306 struct sgi_disklabel *sgilabel = self_disklabel(cxt);
307 return be16_to_cpu(sgilabel->root_part_num);
308}
309
310static int sgi_get_swappartition(struct fdisk_context *cxt)
311{
312 struct sgi_disklabel *sgilabel = self_disklabel(cxt);
313 return be16_to_cpu(sgilabel->swap_part_num);
314}
315
316static unsigned int sgi_get_lastblock(struct fdisk_context *cxt)
317{
318 return cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders;
319}
320
321static struct fdisk_parttype *sgi_get_parttype(struct fdisk_context *cxt, size_t n)
322{
323 struct fdisk_parttype *t;
324
325 if (n >= cxt->label->nparts_max)
326 return NULL;
327
328 t = fdisk_label_get_parttype_from_code(cxt->label, sgi_get_sysid(cxt, n));
329 return t ? : fdisk_new_unknown_parttype(sgi_get_sysid(cxt, n), NULL);
330}
331
332/* fdisk_get_partition() backend */
333static int sgi_get_partition(struct fdisk_context *cxt, size_t n, struct fdisk_partition *pa)
334{
335 fdisk_sector_t start, len;
336
337 pa->used = sgi_get_num_sectors(cxt, n) > 0;
338 if (!pa->used)
339 return 0;
340
341 start = sgi_get_start_sector(cxt, n);
342 len = sgi_get_num_sectors(cxt, n);
343
344 pa->type = sgi_get_parttype(cxt, n);
345 pa->size = len;
346 pa->start = start;
347
348 if (pa->type && pa->type->code == SGI_TYPE_ENTIRE_DISK)
349 pa->wholedisk = 1;
350
351 pa->attrs = sgi_get_swappartition(cxt) == (int) n ? "swap" :
352 sgi_get_bootpartition(cxt) == (int) n ? "boot" : NULL;
353 if (pa->attrs)
354 pa->attrs = strdup(pa->attrs);
355
356 return 0;
357}
358
359
360static int sgi_check_bootfile(struct fdisk_context *cxt, const char *name)
361{
362 size_t sz;
363 struct sgi_disklabel *sgilabel = self_disklabel(cxt);
364
365 sz = strlen(name);
366
367 if (sz < 3) {
368 /* "/a\n" is minimum */
369 fdisk_warnx(cxt, _("Invalid bootfile! The bootfile must "
370 "be an absolute non-zero pathname, "
371 "e.g. \"/unix\" or \"/unix.save\"."));
372 return -EINVAL;
373
374 } else if (sz > sizeof(sgilabel->boot_file)) {
375 fdisk_warnx(cxt, P_("Name of bootfile is too long: %zu byte maximum.",
376 "Name of bootfile is too long: %zu bytes maximum.",
377 sizeof(sgilabel->boot_file)),
378 sizeof(sgilabel->boot_file));
379 return -EINVAL;
380
381 } else if (*name != '/') {
382 fdisk_warnx(cxt, _("Bootfile must have a fully qualified pathname."));
383 return -EINVAL;
384 }
385
386 if (strncmp(name, (char *) sgilabel->boot_file,
387 sizeof(sgilabel->boot_file))) {
388 fdisk_warnx(cxt, _("Be aware that the bootfile is not checked "
389 "for existence. SGI's default is \"/unix\", "
390 "and for backup \"/unix.save\"."));
391 return 0; /* filename is correct and did change */
392 }
393
394 return 1; /* filename did not change */
395}
396
397/**
398 * fdisk_sgi_set_bootfile:
399 * @cxt: context
400 *
401 * Allows to set SGI boot file. The function uses Ask API for dialog with
402 * user.
403 *
404 * Returns: 0 on success, <0 on error
405 */
406int fdisk_sgi_set_bootfile(struct fdisk_context *cxt)
407{
408 int rc = 0;
409 size_t sz;
410 char *name = NULL;
411 struct sgi_disklabel *sgilabel = self_disklabel(cxt);
412
413 fdisk_info(cxt, _("The current boot file is: %s"), sgilabel->boot_file);
414
415 rc = fdisk_ask_string(cxt, _("Enter of the new boot file"), &name);
416 if (rc == 0)
417 rc = sgi_check_bootfile(cxt, name);
418 if (rc) {
419 if (rc == 1)
420 fdisk_info(cxt, _("Boot file is unchanged."));
421 goto done;
422 }
423
424 memset(sgilabel->boot_file, 0, sizeof(sgilabel->boot_file));
425 sz = strlen(name);
426
427 assert(sz <= sizeof(sgilabel->boot_file)); /* see sgi_check_bootfile() */
428
429 memcpy(sgilabel->boot_file, name, sz);
430
431 fdisk_info(cxt, _("Bootfile has been changed to \"%s\"."), name);
432done:
433 free(name);
434 return rc;
435}
436
437static int sgi_write_disklabel(struct fdisk_context *cxt)
438{
439 struct sgi_disklabel *sgilabel;
440 struct sgi_info *info = NULL;
441
442 assert(cxt);
443 assert(cxt->label);
444 assert(fdisk_is_label(cxt, SGI));
445
446 sgilabel = self_disklabel(cxt);
447 sgilabel->csum = 0;
448 sgilabel->csum = cpu_to_be32(sgi_pt_checksum(sgilabel));
449
450 assert(sgi_pt_checksum(sgilabel) == 0);
451
452 if (lseek(cxt->dev_fd, 0, SEEK_SET) < 0)
453 goto err;
454 if (write_all(cxt->dev_fd, sgilabel, DEFAULT_SECTOR_SIZE))
455 goto err;
456 if (!strncmp((char *) sgilabel->volume[0].name, "sgilabel", 8)) {
457 /*
458 * Keep this habit of first writing the "sgilabel".
459 * I never tested whether it works without. (AN 1998-10-02)
460 */
461 int infostartblock
462 = be32_to_cpu(sgilabel->volume[0].block_num);
463
464 if (lseek(cxt->dev_fd, (off_t) infostartblock *
465 DEFAULT_SECTOR_SIZE, SEEK_SET) < 0)
466 goto err;
467 info = sgi_new_info();
468 if (!info)
469 goto err;
470 if (write_all(cxt->dev_fd, info, sizeof(*info)))
471 goto err;
472 }
473
474 sgi_free_info(info);
475 return 0;
476err:
477 sgi_free_info(info);
478 return -errno;
479}
480
481static int compare_start(struct fdisk_context *cxt,
482 const void *x, const void *y)
483{
484 /*
485 * Sort according to start sectors and prefer the largest partition:
486 * entry zero is the entire-disk entry.
487 */
488 unsigned int i = *(int *) x;
489 unsigned int j = *(int *) y;
490 unsigned int a = sgi_get_start_sector(cxt, i);
491 unsigned int b = sgi_get_start_sector(cxt, j);
492 unsigned int c = sgi_get_num_sectors(cxt, i);
493 unsigned int d = sgi_get_num_sectors(cxt, j);
494
495 if (a == b)
496 return (d > c) ? 1 : (d == c) ? 0 : -1;
497 return (a > b) ? 1 : -1;
498}
499
500static void generic_swap(void *a0, void *b0, int size)
501{
502 char *a = a0, *b = b0;
503
504 for (; size > 0; --size, a++, b++) {
505 char t = *a;
506 *a = *b;
507 *b = t;
508 }
509}
510
511
512/* heap sort, based on Matt Mackall's linux kernel version */
513static void sort(void *base0, size_t num, size_t size, struct fdisk_context *cxt,
514 int (*cmp_func)(struct fdisk_context *, const void *, const void *))
515{
516 /* pre-scale counters for performance */
517 int i = (num/2 - 1) * size;
518 size_t n = num * size, c, r;
519 char *base = base0;
520
521 /* heapify */
522 for ( ; i >= 0; i -= size) {
523 for (r = i; r * 2 + size < n; r = c) {
524 c = r * 2 + size;
525 if (c < n - size &&
526 cmp_func(cxt, base + c, base + c + size) < 0)
527 c += size;
528 if (cmp_func(cxt, base + r, base + c) >= 0)
529 break;
530 generic_swap(base + r, base + c, size);
531 }
532 }
533
534 /* sort */
535 for (i = n - size; i > 0; i -= size) {
536 generic_swap(base, base + i, size);
537 for (r = 0; r * 2 + size < (size_t) i; r = c) {
538 c = r * 2 + size;
539 if (c < i - size &&
540 cmp_func(cxt, base + c, base + c + size) < 0)
541 c += size;
542 if (cmp_func(cxt, base + r, base + c) >= 0)
543 break;
544 generic_swap(base + r, base + c, size);
545 }
546 }
547}
548
549static int verify_disklabel(struct fdisk_context *cxt, int verbose)
550{
551 int Index[SGI_MAXPARTITIONS]; /* list of valid partitions */
552 int sortcount = 0; /* number of used partitions, i.e. non-zero lengths */
553 int entire = 0, i = 0;
554 unsigned int start = 0;
555 long long gap = 0; /* count unused blocks */
556 unsigned int lastblock = sgi_get_lastblock(cxt);
557
558 assert(cxt);
559 assert(cxt->label);
560 assert(fdisk_is_label(cxt, SGI));
561
562 clear_freelist(cxt);
563 memset(Index, 0, sizeof(Index));
564
565 for (i=0; i < SGI_MAXPARTITIONS; i++) {
566 if (sgi_get_num_sectors(cxt, i) != 0) {
567 Index[sortcount++] = i;
568 if (sgi_get_sysid(cxt, i) == SGI_TYPE_ENTIRE_DISK
569 && entire++ == 1) {
570 if (verbose)
571 fdisk_info(cxt, _("More than one entire "
572 "disk entry present."));
573 }
574 }
575 }
576 if (sortcount == 0) {
577 if (verbose)
578 fdisk_info(cxt, _("No partitions defined."));
579 if (lastblock)
580 add_to_freelist(cxt, 0, lastblock);
581 return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1;
582 }
583
584 sort(Index, sortcount, sizeof(Index[0]), cxt, compare_start);
585
586 if (sgi_get_sysid(cxt, Index[0]) == SGI_TYPE_ENTIRE_DISK) {
587 if (verbose && Index[0] != 10)
588 fdisk_info(cxt, _("IRIX likes it when partition 11 "
589 "covers the entire disk."));
590
591 if (verbose && sgi_get_start_sector(cxt, Index[0]) != 0)
592 fdisk_info(cxt, _("The entire disk partition should "
593 "start at block 0, not at block %d."),
594 sgi_get_start_sector(cxt, Index[0]));
595
596 if (verbose && sgi_get_num_sectors(cxt, Index[0]) != lastblock)
597 DBG(LABEL, ul_debug(
598 "entire disk partition=%ds, but disk=%ds",
599 sgi_get_num_sectors(cxt, Index[0]),
600 lastblock));
601 lastblock = sgi_get_num_sectors(cxt, Index[0]);
602 } else if (verbose) {
603 fdisk_info(cxt, _("Partition 11 should cover the entire disk."));
604 DBG(LABEL, ul_debug("sysid=%d\tpartition=%d",
605 sgi_get_sysid(cxt, Index[0]), Index[0]+1));
606 }
607 for (i=1, start=0; i<sortcount; i++) {
608 int cylsize = sgi_get_nsect(cxt) * sgi_get_ntrks(cxt);
609
610 if (verbose && cylsize
611 && (sgi_get_start_sector(cxt, Index[i]) % cylsize) != 0)
612 DBG(LABEL, ul_debug("partition %d does not start on "
613 "cylinder boundary.", Index[i]+1));
614
615 if (verbose && cylsize
616 && sgi_get_num_sectors(cxt, Index[i]) % cylsize != 0)
617 DBG(LABEL, ul_debug("partition %d does not end on "
618 "cylinder boundary.", Index[i]+1));
619
620 /* We cannot handle several "entire disk" entries. */
621 if (sgi_get_sysid(cxt, Index[i]) == SGI_TYPE_ENTIRE_DISK)
622 continue;
623
624 if (start > sgi_get_start_sector(cxt, Index[i])) {
625 if (verbose)
626 fdisk_info(cxt,
627 P_("Partitions %d and %d overlap by %d sector.",
628 "Partitions %d and %d overlap by %d sectors.",
629 start - sgi_get_start_sector(cxt, Index[i])),
630 Index[i-1]+1, Index[i]+1,
631 start - sgi_get_start_sector(cxt, Index[i]));
632 if (gap > 0) gap = -gap;
633 if (gap == 0) gap = -1;
634 }
635 if (start < sgi_get_start_sector(cxt, Index[i])) {
636 if (verbose)
637 fdisk_info(cxt,
638 P_("Unused gap of %8u sector: sector %8u",
639 "Unused gap of %8u sectors: sectors %8u-%u",
640 sgi_get_start_sector(cxt, Index[i]) - start),
641 sgi_get_start_sector(cxt, Index[i]) - start,
642 start, sgi_get_start_sector(cxt, Index[i])-1);
643 gap += sgi_get_start_sector(cxt, Index[i]) - start;
644 add_to_freelist(cxt, start,
645 sgi_get_start_sector(cxt, Index[i]));
646 }
647 start = sgi_get_start_sector(cxt, Index[i])
648 + sgi_get_num_sectors(cxt, Index[i]);
649 /* Align free space on cylinder boundary. */
650 if (cylsize && start % cylsize)
651 start += cylsize - (start % cylsize);
652
653 DBG(LABEL, ul_debug("%2d:%12d\t%12d\t%12d", Index[i],
654 sgi_get_start_sector(cxt, Index[i]),
655 sgi_get_num_sectors(cxt, Index[i]),
656 sgi_get_sysid(cxt, Index[i])));
657 }
658 if (start < lastblock) {
659 if (verbose)
660 fdisk_info(cxt, P_("Unused gap of %8u sector: sector %8u",
661 "Unused gap of %8u sectors: sectors %8u-%u",
662 lastblock - start),
663 lastblock - start, start, lastblock-1);
664 gap += lastblock - start;
665 add_to_freelist(cxt, start, lastblock);
666 }
667 /*
668 * Done with arithmetics. Go for details now.
669 */
670 if (verbose) {
671 if (sgi_get_bootpartition(cxt) < 0
672 || !sgi_get_num_sectors(cxt, sgi_get_bootpartition(cxt)))
673 fdisk_info(cxt, _("The boot partition does not exist."));
674
675 if (sgi_get_swappartition(cxt) < 0
676 || !sgi_get_num_sectors(cxt, sgi_get_swappartition(cxt)))
677 fdisk_info(cxt, _("The swap partition does not exist."));
678
679 else if (sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != SGI_TYPE_SWAP
680 && sgi_get_sysid(cxt, sgi_get_swappartition(cxt)) != MBR_LINUX_SWAP_PARTITION)
681 fdisk_info(cxt, _("The swap partition has no swap type."));
682
683 if (sgi_check_bootfile(cxt, "/unix"))
684 fdisk_info(cxt, _("You have chosen an unusual bootfile name."));
685 }
686
687 return (gap > 0) ? 1 : (gap == 0) ? 0 : -1;
688}
689
690static int sgi_verify_disklabel(struct fdisk_context *cxt)
691{
692 return verify_disklabel(cxt, 1);
693}
694
695static int sgi_gaps(struct fdisk_context *cxt)
696{
697 /*
698 * returned value is:
699 * = 0 : disk is properly filled to the rim
700 * < 0 : there is an overlap
701 * > 0 : there is still some vacant space
702 */
703 return verify_disklabel(cxt, 0);
704}
705
706/* Returns partition index of first entry marked as entire disk. */
707static int sgi_entire(struct fdisk_context *cxt)
708{
709 size_t i;
710
711 for (i = 0; i < SGI_MAXPARTITIONS; i++)
712 if (sgi_get_sysid(cxt, i) == SGI_TYPE_ENTIRE_DISK)
713 return i;
714 return -1;
715}
716
717static int set_partition(struct fdisk_context *cxt, size_t i,
718 unsigned int start, unsigned int length, int sys)
719{
720 struct sgi_disklabel *sgilabel;
721
722 assert(cxt);
723 assert(cxt->label);
724 assert(fdisk_is_label(cxt, SGI));
725
726 sgilabel = self_disklabel(cxt);
727 sgilabel->partitions[i].type = cpu_to_be32(sys);
728 sgilabel->partitions[i].num_blocks = cpu_to_be32(length);
729 sgilabel->partitions[i].first_block = cpu_to_be32(start);
730
731 fdisk_label_set_changed(cxt->label, 1);
732
733 if (sgi_gaps(cxt) < 0) /* rebuild freelist */
734 fdisk_warnx(cxt, _("Partition overlap on the disk."));
735 if (length) {
736 struct fdisk_parttype *t =
737 fdisk_label_get_parttype_from_code(cxt->label, sys);
738 fdisk_info_new_partition(cxt, i + 1, start, start + length, t);
739 }
740
741 return 0;
742}
743
744static void sgi_set_entire(struct fdisk_context *cxt)
745{
746 size_t n;
747
748 for (n = 10; n < cxt->label->nparts_max; n++) {
749 if (!sgi_get_num_sectors(cxt, n)) {
750 set_partition(cxt, n, 0, sgi_get_lastblock(cxt), SGI_TYPE_ENTIRE_DISK);
751 break;
752 }
753 }
754}
755
756static void sgi_set_volhdr(struct fdisk_context *cxt)
757{
758 size_t n;
759
760 for (n = 8; n < cxt->label->nparts_max; n++) {
761 if (!sgi_get_num_sectors(cxt, n)) {
762 /* Choose same default volume header size as IRIX fx uses. */
763 if (4096 < sgi_get_lastblock(cxt))
764 set_partition(cxt, n, 0, 4096, SGI_TYPE_VOLHDR);
765 break;
766 }
767 }
768}
769
770static int sgi_delete_partition(struct fdisk_context *cxt, size_t partnum)
771{
772 int rc;
773
774 assert(cxt);
775 assert(cxt->label);
776
777 if (partnum > cxt->label->nparts_max)
778 return -EINVAL;
779
780 rc = set_partition(cxt, partnum, 0, 0, 0);
781
782 cxt->label->nparts_cur = count_used_partitions(cxt);
783
784 return rc;
785}
786
787static int sgi_add_partition(struct fdisk_context *cxt,
788 struct fdisk_partition *pa,
789 size_t *partno)
790{
791 struct fdisk_sgi_label *sgi;
792 char mesg[256];
793 unsigned int first = 0, last = 0;
794 struct fdisk_ask *ask;
795 int sys = pa && pa->type ? pa->type->code : SGI_TYPE_XFS;
796 int rc;
797 size_t n;
798
799 assert(cxt);
800 assert(cxt->label);
801 assert(fdisk_is_label(cxt, SGI));
802
803 rc = fdisk_partition_next_partno(pa, cxt, &n);
804 if (rc)
805 return rc;
806 if (n == 10)
807 sys = SGI_TYPE_ENTIRE_DISK;
808 else if (n == 8)
809 sys = 0;
810
811 sgi = self_label(cxt);
812
813 if (sgi_get_num_sectors(cxt, n)) {
814 fdisk_warnx(cxt, _("Partition %zu is already defined. "
815 "Delete it before re-adding it."), n + 1);
816 return -EINVAL;
817 }
818 if (!cxt->script && sgi_entire(cxt) == -1 && sys != SGI_TYPE_ENTIRE_DISK) {
819 fdisk_info(cxt, _("Attempting to generate entire disk entry automatically."));
820 sgi_set_entire(cxt);
821 sgi_set_volhdr(cxt);
822 }
823 if (sgi_gaps(cxt) == 0 && sys != SGI_TYPE_ENTIRE_DISK) {
824 fdisk_warnx(cxt, _("The entire disk is already covered with partitions."));
825 return -EINVAL;
826 }
827 if (sgi_gaps(cxt) < 0) {
828 fdisk_warnx(cxt, _("You got a partition overlap on the disk. Fix it first!"));
829 return -EINVAL;
830 }
831
832 if (sys == SGI_TYPE_ENTIRE_DISK) {
833 first = 0;
834 last = sgi_get_lastblock(cxt);
835 } else {
836 first = sgi->freelist[0].first;
837 last = sgi->freelist[0].last;
838 }
839
840 /* first sector */
841 if (pa && pa->start_follow_default)
842 ;
843 else if (pa && fdisk_partition_has_start(pa)) {
844 first = pa->start;
845 last = is_in_freelist(cxt, first);
846
847 if (sys != SGI_TYPE_ENTIRE_DISK && !last)
848 return -ERANGE;
849 } else {
850 snprintf(mesg, sizeof(mesg), _("First %s"),
851 fdisk_get_unit(cxt, FDISK_SINGULAR));
852 ask = fdisk_new_ask();
853 if (!ask)
854 return -ENOMEM;
855
856 fdisk_ask_set_query(ask, mesg);
857 fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
858
859 fdisk_ask_number_set_low(ask, fdisk_scround(cxt, first)); /* minimal */
860 fdisk_ask_number_set_default(ask, fdisk_scround(cxt, first)); /* default */
861 fdisk_ask_number_set_high(ask, fdisk_scround(cxt, last) - 1); /* maximal */
862
863 rc = fdisk_do_ask(cxt, ask);
864 first = fdisk_ask_number_get_result(ask);
865 fdisk_unref_ask(ask);
866
867 if (rc)
868 return rc;
869 if (fdisk_use_cylinders(cxt))
870 first *= fdisk_get_units_per_sector(cxt);
871 }
872
873 if (first && sys == SGI_TYPE_ENTIRE_DISK)
874 fdisk_info(cxt, _("It is highly recommended that the "
875 "eleventh partition covers the entire "
876 "disk and is of type 'SGI volume'."));
877 if (!last)
878 last = is_in_freelist(cxt, first);
879
880 /* last sector */
881 if (pa && pa->end_follow_default)
882 last -= 1ULL;
883 else if (pa && fdisk_partition_has_size(pa)) {
884 if (first + pa->size - 1ULL > last)
885 return -ERANGE;
886 last = first + pa->size - 1ULL;
887 } else {
888 snprintf(mesg, sizeof(mesg),
889 _("Last %s or +%s or +size{K,M,G,T,P}"),
890 fdisk_get_unit(cxt, FDISK_SINGULAR),
891 fdisk_get_unit(cxt, FDISK_PLURAL));
892
893 ask = fdisk_new_ask();
894 if (!ask)
895 return -ENOMEM;
896
897 fdisk_ask_set_query(ask, mesg);
898 fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
899
900 fdisk_ask_number_set_low(ask, fdisk_scround(cxt, first)); /* minimal */
901 fdisk_ask_number_set_default(ask, fdisk_scround(cxt, last) - 1);/* default */
902 fdisk_ask_number_set_high(ask, fdisk_scround(cxt, last) - 1);/* maximal */
903 fdisk_ask_number_set_base(ask, fdisk_scround(cxt, first));
904
905 if (fdisk_use_cylinders(cxt))
906 fdisk_ask_number_set_unit(ask,
907 cxt->sector_size *
908 fdisk_get_units_per_sector(cxt));
909 else
910 fdisk_ask_number_set_unit(ask,cxt->sector_size);
911
912 rc = fdisk_do_ask(cxt, ask);
913 last = fdisk_ask_number_get_result(ask) + 1;
914
915 fdisk_unref_ask(ask);
916 if (rc)
917 return rc;
918 if (fdisk_use_cylinders(cxt))
919 last *= fdisk_get_units_per_sector(cxt);
920 }
921
922 if (sys == SGI_TYPE_ENTIRE_DISK
923 && (first != 0 || last != sgi_get_lastblock(cxt)))
924 fdisk_info(cxt, _("It is highly recommended that the "
925 "eleventh partition covers the entire "
926 "disk and is of type 'SGI volume'."));
927
928 set_partition(cxt, n, first, last - first, sys);
929 cxt->label->nparts_cur = count_used_partitions(cxt);
930 if (partno)
931 *partno = n;
932 return 0;
933}
934
935static int sgi_create_disklabel(struct fdisk_context *cxt)
936{
937 struct fdisk_sgi_label *sgi;
938 struct sgi_disklabel *sgilabel;
939 int rc;
940
941 assert(cxt);
942 assert(cxt->label);
943 assert(fdisk_is_label(cxt, SGI));
944
945#ifdef HDIO_GETGEO
946 if (cxt->geom.heads && cxt->geom.sectors) {
947 fdisk_sector_t llsectors;
948
949 if (blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &llsectors) == 0) {
950 /* the get device size ioctl was successful */
951 fdisk_sector_t llcyls;
952 int sec_fac = cxt->sector_size / 512;
953
954 llcyls = llsectors / (cxt->geom.heads * cxt->geom.sectors * sec_fac);
955 cxt->geom.cylinders = llcyls;
956 if (cxt->geom.cylinders != llcyls) /* truncated? */
957 cxt->geom.cylinders = ~0;
958 } else {
959 /* otherwise print error and use truncated version */
960 fdisk_warnx(cxt,
961 _("BLKGETSIZE ioctl failed on %s. "
962 "Using geometry cylinder value of %llu. "
963 "This value may be truncated for devices "
964 "> 33.8 GB."), cxt->dev_path, cxt->geom.cylinders);
965 }
966 }
967#endif
968 rc = fdisk_init_firstsector_buffer(cxt);
969 if (rc)
970 return rc;
971
972 sgi = (struct fdisk_sgi_label *) cxt->label;
973 sgi->header = (struct sgi_disklabel *) cxt->firstsector;
974
975 sgilabel = sgi->header;
976
977 sgilabel->magic = cpu_to_be32(SGI_LABEL_MAGIC);
978 sgilabel->root_part_num = cpu_to_be16(0);
979 sgilabel->swap_part_num = cpu_to_be16(1);
980
981 /* sizeof(sgilabel->boot_file) = 16 > 6 */
982 memset(sgilabel->boot_file, 0, 16);
983 strcpy((char *) sgilabel->boot_file, "/unix");
984
985 sgilabel->devparam.skew = (0);
986 sgilabel->devparam.gap1 = (0);
987 sgilabel->devparam.gap2 = (0);
988 sgilabel->devparam.sparecyl = (0);
989 sgilabel->devparam.pcylcount = cpu_to_be16(cxt->geom.cylinders);
990 sgilabel->devparam.head_vol0 = cpu_to_be16(0);
991 sgilabel->devparam.ntrks = cpu_to_be16(cxt->geom.heads);
992 /* tracks/cylinder (heads) */
993 sgilabel->devparam.cmd_tag_queue_depth = (0);
994 sgilabel->devparam.unused0 = (0);
995 sgilabel->devparam.unused1 = cpu_to_be16(0);
996 sgilabel->devparam.nsect = cpu_to_be16(cxt->geom.sectors);
997 /* sectors/track */
998 sgilabel->devparam.bytes = cpu_to_be16(cxt->sector_size);
999 sgilabel->devparam.ilfact = cpu_to_be16(1);
1000 sgilabel->devparam.flags = cpu_to_be32(
1001 SGI_DEVPARAM_TRACK_FWD
1002 | SGI_DEVPARAM_IGNORE_ERRORS
1003 | SGI_DEVPARAM_RESEEK);
1004 sgilabel->devparam.datarate = cpu_to_be32(0);
1005 sgilabel->devparam.retries_on_error = cpu_to_be32(1);
1006 sgilabel->devparam.ms_per_word = cpu_to_be32(0);
1007 sgilabel->devparam.xylogics_gap1 = cpu_to_be16(0);
1008 sgilabel->devparam.xylogics_syncdelay = cpu_to_be16(0);
1009 sgilabel->devparam.xylogics_readdelay = cpu_to_be16(0);
1010 sgilabel->devparam.xylogics_gap2 = cpu_to_be16(0);
1011 sgilabel->devparam.xylogics_readgate = cpu_to_be16(0);
1012 sgilabel->devparam.xylogics_writecont = cpu_to_be16(0);
1013
1014 memset(&(sgilabel->volume), 0,
1015 sizeof(struct sgi_volume) * SGI_MAXVOLUMES);
1016 memset(&(sgilabel->partitions), 0,
1017 sizeof(struct sgi_partition) * SGI_MAXPARTITIONS);
1018 cxt->label->nparts_max = SGI_MAXPARTITIONS;
1019
1020 /* don't create default layout when a script defined */
1021 if (!cxt->script) {
1022 sgi_set_entire(cxt);
1023 sgi_set_volhdr(cxt);
1024 }
1025 cxt->label->nparts_cur = count_used_partitions(cxt);
1026
1027 fdisk_info(cxt, _("Created a new SGI disklabel."));
1028 return 0;
1029}
1030
1031static int sgi_set_partition(struct fdisk_context *cxt,
1032 size_t i,
1033 struct fdisk_partition *pa)
1034{
1035 struct sgi_disklabel *sgilabel;
1036
1037 if (i >= cxt->label->nparts_max)
1038 return -EINVAL;
1039
1040 sgilabel = self_disklabel(cxt);
1041
1042 if (pa->type) {
1043 struct fdisk_parttype *t = pa->type;
1044
1045 if (t->code > UINT32_MAX)
1046 return -EINVAL;
1047
1048 if (sgi_get_num_sectors(cxt, i) == 0) /* caught already before, ... */ {
1049 fdisk_warnx(cxt, _("Sorry, only for non-empty partitions you can change the tag."));
1050 return -EINVAL;
1051 }
1052
1053 if ((i == 10 && t->code != SGI_TYPE_ENTIRE_DISK)
1054 || (i == 8 && t->code != 0))
1055 fdisk_info(cxt, _("Consider leaving partition 9 as volume header (0), "
1056 "and partition 11 as entire volume (6), "
1057 "as IRIX expects it."));
1058
1059 if (cxt->script == NULL
1060 && ((t->code != SGI_TYPE_ENTIRE_DISK) && (t->code != SGI_TYPE_VOLHDR))
1061 && (sgi_get_start_sector(cxt, i) < 1)) {
1062 int yes = 0;
1063 fdisk_ask_yesno(cxt,
1064 _("It is highly recommended that the partition at offset 0 "
1065 "is of type \"SGI volhdr\", the IRIX system will rely on it to "
1066 "retrieve from its directory standalone tools like sash and fx. "
1067 "Only the \"SGI volume\" entire disk section may violate this. "
1068 "Are you sure about tagging this partition differently?"), &yes);
1069 if (!yes)
1070 return 1;
1071 }
1072
1073 sgilabel->partitions[i].type = cpu_to_be32(t->code);
1074 }
1075
1076 if (fdisk_partition_has_start(pa))
1077 sgilabel->partitions[i].first_block = cpu_to_be32(pa->start);
1078 if (fdisk_partition_has_size(pa))
1079 sgilabel->partitions[i].num_blocks = cpu_to_be32(pa->size);
1080
1081 fdisk_label_set_changed(cxt->label, 1);
1082 return 0;
1083}
1084
1085
1086static int sgi_partition_is_used(
1087 struct fdisk_context *cxt,
1088 size_t i)
1089{
1090 assert(cxt);
1091 assert(fdisk_is_label(cxt, SGI));
1092
1093 if (i >= cxt->label->nparts_max)
1094 return 0;
1095 return sgi_get_num_sectors(cxt, i) ? 1 : 0;
1096}
1097
1098static int sgi_toggle_partition_flag(struct fdisk_context *cxt, size_t i, unsigned long flag)
1099{
1100 struct sgi_disklabel *sgilabel;
1101 assert(cxt);
1102 assert(cxt->label);
1103 assert(fdisk_is_label(cxt, SGI));
1104
1105 if (i >= cxt->label->nparts_max)
1106 return -EINVAL;
1107
1108 sgilabel = self_disklabel(cxt);
1109
1110 switch (flag) {
1111 case SGI_FLAG_BOOT:
1112 sgilabel->root_part_num =
1113 be16_to_cpu(sgilabel->root_part_num) == i ?
1114 0 : cpu_to_be16(i);
1115 fdisk_label_set_changed(cxt->label, 1);
1116 break;
1117 case SGI_FLAG_SWAP:
1118 sgilabel->swap_part_num =
1119 be16_to_cpu(sgilabel->swap_part_num) == i ?
1120 0 : cpu_to_be16(i);
1121 fdisk_label_set_changed(cxt->label, 1);
1122 break;
1123 default:
1124 return 1;
1125 }
1126
1127 return 0;
1128}
1129
1130static const struct fdisk_field sgi_fields[] =
1131{
1132 { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
1133 { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
1134 { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
1135 { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
1136 { FDISK_FIELD_CYLINDERS,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER },
1137 { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
1138 { FDISK_FIELD_TYPEID, N_("Id"), 2, FDISK_FIELDFL_NUMBER },
1139 { FDISK_FIELD_TYPE, N_("Type"), 0.1, FDISK_FIELDFL_EYECANDY },
1140 { FDISK_FIELD_ATTR, N_("Attrs"), 0, FDISK_FIELDFL_NUMBER }
1141};
1142
1143static const struct fdisk_label_operations sgi_operations =
1144{
1145 .probe = sgi_probe_label,
1146 .write = sgi_write_disklabel,
1147 .verify = sgi_verify_disklabel,
1148 .create = sgi_create_disklabel,
1149 .list = sgi_list_table,
1150
1151 .get_part = sgi_get_partition,
1152 .set_part = sgi_set_partition,
1153 .add_part = sgi_add_partition,
1154 .del_part = sgi_delete_partition,
1155
1156 .part_is_used = sgi_partition_is_used,
1157 .part_toggle_flag = sgi_toggle_partition_flag
1158};
1159
1160/* Allocates an SGI label driver. */
1161struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt)
1162{
1163 struct fdisk_label *lb;
1164 struct fdisk_sgi_label *sgi;
1165
1166 assert(cxt);
1167
1168 sgi = calloc(1, sizeof(*sgi));
1169 if (!sgi)
1170 return NULL;
1171
1172 /* initialize generic part of the driver */
1173 lb = (struct fdisk_label *) sgi;
1174 lb->name = "sgi";
1175 lb->id = FDISK_DISKLABEL_SGI;
1176 lb->op = &sgi_operations;
1177 lb->parttypes = sgi_parttypes;
1178 lb->nparttypes = ARRAY_SIZE(sgi_parttypes) - 1;
1179 lb->fields = sgi_fields;
1180 lb->nfields = ARRAY_SIZE(sgi_fields);
1181
1182 lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
1183
1184 return lb;
1185}