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