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