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