1/*
2   NetBSD disklabel editor for Linux fdisk
3   Written by Bernhard Fastenrath (fasten@informatik.uni-bonn.de)
4   with code from the NetBSD disklabel command:
5
6   Copyright (c) 1987, 1988 Regents of the University of California.
7   All rights reserved.
8
9   Redistribution and use in source and binary forms, with or without
10   modification, are permitted provided that the following conditions
11   are met:
12   1. Redistributions of source code must retain the above copyright
13      notice, this list of conditions and the following disclaimer.
14   2. Redistributions in binary form must reproduce the above copyright
15      notice, this list of conditions and the following disclaimer in the
16      documentation and/or other materials provided with the distribution.
17   3. All advertising materials mentioning features or use of this software
18      must display the following acknowledgement:
19	This product includes software developed by the University of
20	California, Berkeley and its contributors.
21   4. Neither the name of the University nor the names of its contributors
22      may be used to endorse or promote products derived from this software
23      without specific prior written permission.
24
25   THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28   ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35   SUCH DAMAGE.
36
37   Changes:
38   19990319 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - i18n/nls
39
40   20000101 - David Huggins-Daines <dhuggins@linuxcare.com> - Better
41   support for OSF/1 disklabels on Alpha.
42   Also fixed unaligned accesses in alpha_bootblock_checksum()
43*/
44
45#include <unistd.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <string.h>
49#include <fcntl.h>
50#include <ctype.h>
51#include <setjmp.h>
52#include <errno.h>
53#include "nls.h"
54
55#include <sys/param.h>
56
57#include "common.h"
58#include "fdisk.h"
59#define FREEBSD_PARTITION	0xa5
60#define NETBSD_PARTITION	0xa9
61#define DKTYPENAMES
62#include "fdiskbsdlabel.h"
63#include "fdiskdoslabel.h"
64
65/*
66 * in-memory fdisk BSD stuff
67 */
68struct fdisk_bsd_label {
69	struct fdisk_label	head;		/* generic part */
70};
71
72
73static int xbsd_delete_part (struct fdisk_context *cxt, size_t partnum);
74static void xbsd_edit_disklabel (struct fdisk_context *cxt);
75static void xbsd_write_bootstrap (struct fdisk_context *cxt);
76static void xbsd_change_fstype (struct fdisk_context *cxt);
77static int xbsd_get_part_index (struct fdisk_context *cxt, int max);
78static int xbsd_check_new_partition (struct fdisk_context *cxt, int *i);
79static unsigned short xbsd_dkcksum (struct xbsd_disklabel *lp);
80static int xbsd_initlabel  (struct fdisk_context *cxt,
81			    struct partition *p, struct xbsd_disklabel *d,
82			    int pindex);
83static int xbsd_readlabel  (struct fdisk_context *cxt,
84			    struct partition *p, struct xbsd_disklabel *d);
85static int xbsd_writelabel (struct fdisk_context *cxt, struct partition *p, struct xbsd_disklabel *d);
86static void sync_disks (void);
87
88#if defined (__alpha__)
89void alpha_bootblock_checksum (char *boot);
90#endif
91
92#if !defined (__alpha__)
93static int xbsd_translate_fstype (int linux_type);
94static void xbsd_link_part (struct fdisk_context *cxt);
95static struct partition *xbsd_part;
96static int xbsd_part_index;
97#endif
98
99#if defined (__alpha__)
100/* We access this through a u_int64_t * when checksumming */
101static char disklabelbuffer[BSD_BBSIZE] __attribute__((aligned(8)));
102#else
103static char disklabelbuffer[BSD_BBSIZE];
104#endif
105
106static struct xbsd_disklabel xbsd_dlabel;
107
108#define bsd_cround(c, n) \
109	(fdisk_context_use_cylinders(c) ? ((n)/xbsd_dlabel.d_secpercyl) + 1 : (n))
110
111/*
112 * Test whether the whole disk has BSD disk label magic.
113 *
114 * Note: often reformatting with DOS-type label leaves the BSD magic,
115 * so this does not mean that there is a BSD disk label.
116 */
117static int
118osf_probe_label(struct fdisk_context *cxt)
119{
120	assert(cxt);
121	assert(cxt->label);
122	assert(fdisk_is_disklabel(cxt, OSF));
123
124	if (xbsd_readlabel (cxt, NULL, &xbsd_dlabel) == 0)
125		return 0;
126	return 1;
127}
128
129#if !defined (__alpha__)
130static int hidden(int type)
131{
132	return type ^ 0x10;
133}
134
135static int is_bsd_partition_type(int type)
136{
137	return (type == FREEBSD_PARTITION ||
138		type == hidden(FREEBSD_PARTITION) ||
139		type == NETBSD_PARTITION ||
140		type == hidden(NETBSD_PARTITION));
141}
142#endif
143
144static int xbsd_write_disklabel (struct fdisk_context *cxt)
145{
146	printf (_("Writing disklabel to %s.\n"), cxt->dev_path);
147#if defined (__alpha__)
148	xbsd_writelabel (cxt, NULL, &xbsd_dlabel);
149#else
150	xbsd_writelabel (cxt, xbsd_part, &xbsd_dlabel);
151#endif
152	reread_partition_table(cxt, 0);	/* no exit yet */
153
154	return 0;
155}
156
157static int xbsd_add_part (struct fdisk_context *cxt,
158		size_t partnum __attribute__((__unused__)),
159		struct fdisk_parttype *t __attribute__((__unused__)))
160{
161	unsigned int begin, end;
162	char mesg[256];
163	int i, rc;
164
165	assert(cxt);
166	assert(cxt->label);
167	assert(fdisk_is_disklabel(cxt, OSF));
168
169	rc = xbsd_check_new_partition(cxt, &i);
170	if (rc)
171		return rc;
172
173#if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__)
174	begin = get_start_sect(xbsd_part);
175	end = begin + get_nr_sects(xbsd_part) - 1;
176#else
177	begin = 0;
178	end = xbsd_dlabel.d_secperunit - 1;
179#endif
180
181	snprintf (mesg, sizeof(mesg), _("First %s"),
182			fdisk_context_get_unit(cxt, SINGULAR));
183	begin = read_int(cxt, bsd_cround (cxt, begin),
184			      bsd_cround (cxt, begin),
185			      bsd_cround (cxt, end),
186			  0, mesg);
187
188	if (fdisk_context_use_cylinders(cxt))
189		begin = (begin - 1) * xbsd_dlabel.d_secpercyl;
190
191	snprintf (mesg, sizeof(mesg), _("Last %s or +size or +sizeM or +sizeK"),
192		  fdisk_context_get_unit(cxt, SINGULAR));
193	end = read_int (cxt, bsd_cround (cxt, begin),
194			bsd_cround (cxt, end),
195			bsd_cround (cxt, end),
196			bsd_cround (cxt, begin), mesg);
197
198	if (fdisk_context_use_cylinders(cxt))
199		end = end * xbsd_dlabel.d_secpercyl - 1;
200
201	xbsd_dlabel.d_partitions[i].p_size   = end - begin + 1;
202	xbsd_dlabel.d_partitions[i].p_offset = begin;
203	xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
204
205	cxt->label->nparts_cur = xbsd_dlabel.d_npartitions;
206	fdisk_label_set_changed(cxt->label, 1);
207
208	return 0;
209}
210
211static int xbsd_create_disklabel(struct fdisk_context *cxt)
212{
213	char c;
214
215	assert(cxt);
216	assert(cxt->label);
217	assert(fdisk_is_disklabel(cxt, OSF));
218
219	fprintf (stderr, _("%s contains no disklabel.\n"), cxt->dev_path);
220
221	while (1) {
222		c = read_char(cxt, _("Do you want to create a disklabel? (y/n) "));
223		if (tolower(c) == 'y') {
224			if (xbsd_initlabel (cxt,
225#if defined (__alpha__) || defined (__powerpc__) || defined (__hppa__) || \
226    defined (__s390__) || defined (__s390x__)
227				NULL, &xbsd_dlabel, 0
228#else
229				xbsd_part, &xbsd_dlabel, xbsd_part_index
230#endif
231				) == 1) {
232				xbsd_print_disklabel (cxt, 1);
233				cxt->label->nparts_cur = xbsd_dlabel.d_npartitions;
234				cxt->label->nparts_max = BSD_MAXPARTITIONS;
235				return 1;
236			} else
237				return 0;
238		} else if (c == 'n')
239			return 0;
240	}
241}
242
243void
244bsd_command_prompt (struct fdisk_context *cxt)
245{
246#if !defined (__alpha__)
247  int t, ss;
248  struct partition *p;
249
250  assert(cxt);
251  assert(cxt->parent);
252
253  for (t=0; t<4; t++) {
254    p = get_part_table(t);
255    if (p && is_bsd_partition_type(p->sys_ind)) {
256      xbsd_part = p;
257      xbsd_part_index = t;
258      ss = get_start_sect(xbsd_part);
259
260      /* TODO - partname uses static buffer!!! */
261      cxt->dev_path = partname(cxt->parent->dev_path, t+1, 0);
262      if (cxt->dev_path)
263	      cxt->dev_path = strdup(cxt->dev_path);
264
265      if (ss == 0) {
266	fprintf (stderr, _("Partition %s has invalid starting sector 0.\n"),
267		 cxt->dev_path);
268	return;
269      }
270      printf (_("Reading disklabel of %s at sector %d.\n"),
271		   cxt->dev_path, ss + BSD_LABELSECTOR);
272      if (xbsd_readlabel (cxt, xbsd_part, &xbsd_dlabel) == 0)
273	if (xbsd_create_disklabel (cxt) == 0)
274	  return;
275      break;
276    }
277  }
278
279  if (t == 4) {
280    printf (_("There is no *BSD partition on %s.\n"), cxt->dev_path);
281    return;
282  }
283
284#elif defined (__alpha__)
285
286  if (xbsd_readlabel (cxt, NULL, &xbsd_dlabel) == 0)
287    if (xbsd_create_disklabel (cxt) == 0)
288      exit ( EXIT_SUCCESS );
289
290#endif
291
292  while (1) {
293    putchar ('\n');
294    switch (tolower (read_char(cxt, _("BSD disklabel command (m for help): ")))) {
295      case 'd':
296	      xbsd_delete_part(cxt, xbsd_get_part_index(cxt, xbsd_dlabel.d_npartitions));
297	      break;
298      case 'e':
299	xbsd_edit_disklabel (cxt);
300	break;
301      case 'i':
302	xbsd_write_bootstrap (cxt);
303	break;
304      case 'l':
305	list_partition_types (cxt);
306	break;
307      case 'n':
308	      xbsd_add_part (cxt, 0, 0);
309	      break;
310      case 'p':
311	      xbsd_print_disklabel (cxt, 0);
312	break;
313      case 'q':
314	close (cxt->dev_fd);
315	exit ( EXIT_SUCCESS );
316      case 'r':
317	return;
318      case 's':
319	      xbsd_print_disklabel (cxt, 1);
320	break;
321      case 't':
322	xbsd_change_fstype (cxt);
323	break;
324      case 'u':
325	toggle_units(cxt);
326	break;
327      case 'w':
328	xbsd_write_disklabel (cxt);
329	break;
330#if !defined (__alpha__)
331      case 'x':
332	xbsd_link_part (cxt);
333	break;
334#endif
335      default:
336	print_menu(cxt, MAIN_MENU);
337	break;
338    }
339  }
340}
341
342static int xbsd_delete_part(
343		struct fdisk_context *cxt,
344		size_t partnum)
345{
346	assert(cxt);
347	assert(cxt->label);
348	assert(fdisk_is_disklabel(cxt, OSF));
349
350	xbsd_dlabel.d_partitions[partnum].p_size   = 0;
351	xbsd_dlabel.d_partitions[partnum].p_offset = 0;
352	xbsd_dlabel.d_partitions[partnum].p_fstype = BSD_FS_UNUSED;
353	if (xbsd_dlabel.d_npartitions == partnum + 1)
354		while (!xbsd_dlabel.d_partitions[xbsd_dlabel.d_npartitions-1].p_size)
355			xbsd_dlabel.d_npartitions--;
356
357	cxt->label->nparts_cur = xbsd_dlabel.d_npartitions;
358	fdisk_label_set_changed(cxt->label, 1);
359	return 0;
360}
361
362void
363xbsd_print_disklabel (struct fdisk_context *cxt, int show_all)
364{
365  struct xbsd_disklabel *lp = &xbsd_dlabel;
366  struct xbsd_partition *pp;
367  FILE *f = stdout;
368  int i, j;
369
370  if (show_all) {
371    fprintf(f, "# %s:\n", cxt->dev_path);
372    if ((unsigned) lp->d_type < BSD_DKMAXTYPES)
373      fprintf(f, _("type: %s\n"), xbsd_dktypenames[lp->d_type]);
374    else
375      fprintf(f, _("type: %d\n"), lp->d_type);
376    fprintf(f, _("disk: %.*s\n"), (int) sizeof(lp->d_typename), lp->d_typename);
377    fprintf(f, _("label: %.*s\n"), (int) sizeof(lp->d_packname), lp->d_packname);
378    fprintf(f, _("flags:"));
379    if (lp->d_flags & BSD_D_REMOVABLE)
380      fprintf(f, _(" removable"));
381    if (lp->d_flags & BSD_D_ECC)
382      fprintf(f, _(" ecc"));
383    if (lp->d_flags & BSD_D_BADSECT)
384      fprintf(f, _(" badsect"));
385    fprintf(f, "\n");
386    /* On various machines the fields of *lp are short/int/long */
387    /* In order to avoid problems, we cast them all to long. */
388    fprintf(f, _("bytes/sector: %ld\n"), (long) lp->d_secsize);
389    fprintf(f, _("sectors/track: %ld\n"), (long) lp->d_nsectors);
390    fprintf(f, _("tracks/cylinder: %ld\n"), (long) lp->d_ntracks);
391    fprintf(f, _("sectors/cylinder: %ld\n"), (long) lp->d_secpercyl);
392    fprintf(f, _("cylinders: %ld\n"), (long) lp->d_ncylinders);
393    fprintf(f, _("rpm: %d\n"), lp->d_rpm);
394    fprintf(f, _("interleave: %d\n"), lp->d_interleave);
395    fprintf(f, _("trackskew: %d\n"), lp->d_trackskew);
396    fprintf(f, _("cylinderskew: %d\n"), lp->d_cylskew);
397    fprintf(f, _("headswitch: %ld\t\t# milliseconds\n"),
398	    (long) lp->d_headswitch);
399    fprintf(f, _("track-to-track seek: %ld\t# milliseconds\n"),
400	    (long) lp->d_trkseek);
401    fprintf(f, _("drivedata: "));
402    for (i = NDDATA - 1; i >= 0; i--)
403      if (lp->d_drivedata[i])
404	break;
405    if (i < 0)
406      i = 0;
407    for (j = 0; j <= i; j++)
408      fprintf(f, "%ld ", (long) lp->d_drivedata[j]);
409  }
410  fprintf (f, _("\n%d partitions:\n"), lp->d_npartitions);
411  fprintf (f, _("#       start       end      size     fstype   [fsize bsize   cpg]\n"));
412  pp = lp->d_partitions;
413  for (i = 0; i < lp->d_npartitions; i++, pp++) {
414    if (pp->p_size) {
415      if (fdisk_context_use_cylinders(cxt) && lp->d_secpercyl) {
416	fprintf(f, "  %c: %8ld%c %8ld%c %8ld%c  ",
417		'a' + i,
418		(long) pp->p_offset / lp->d_secpercyl + 1,
419		(pp->p_offset % lp->d_secpercyl) ? '*' : ' ',
420		(long) (pp->p_offset + pp->p_size + lp->d_secpercyl - 1)
421			/ lp->d_secpercyl,
422		((pp->p_offset + pp->p_size) % lp->d_secpercyl) ? '*' : ' ',
423		(long) pp->p_size / lp->d_secpercyl,
424		(pp->p_size % lp->d_secpercyl) ? '*' : ' ');
425      } else {
426	fprintf(f, "  %c: %8ld  %8ld  %8ld   ",
427		'a' + i,
428		(long) pp->p_offset,
429		(long) pp->p_offset + pp->p_size - 1,
430		(long) pp->p_size);
431      }
432      if ((unsigned) pp->p_fstype < BSD_FSMAXTYPES)
433	fprintf(f, "%8.8s", xbsd_fstypes[pp->p_fstype].name);
434      else
435	fprintf(f, "%8x", pp->p_fstype);
436      switch (pp->p_fstype) {
437	case BSD_FS_UNUSED:
438	  fprintf(f, "    %5ld %5ld %5.5s ",
439		  (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, "");
440	  break;
441
442	case BSD_FS_BSDFFS:
443	  fprintf(f, "    %5ld %5ld %5d ",
444		  (long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag,
445		  pp->p_cpg);
446	  break;
447
448	default:
449	  fprintf(f, "%22.22s", "");
450	  break;
451      }
452      fprintf(f, "\n");
453    }
454  }
455}
456
457static unsigned long
458edit_int(struct fdisk_context *cxt, unsigned long def, char *mesg)
459{
460  do {
461    fputs (mesg, stdout);
462    printf (" (%lu): ", def);
463    if (!read_line(cxt, NULL))
464      return def;
465  } while (!isdigit (*line_ptr));
466
467  return strtoul(line_ptr, NULL, 10);	/* TODO check it! */
468}
469
470static void
471xbsd_edit_disklabel(struct fdisk_context *cxt)
472{
473  struct xbsd_disklabel *d;
474
475  d = &xbsd_dlabel;
476
477#if defined (__alpha__) || defined (__ia64__)
478  d -> d_secsize    = edit_int(cxt, d->d_secsize     ,_("bytes/sector"));
479  d -> d_nsectors   = edit_int(cxt, d->d_nsectors    ,_("sectors/track"));
480  d -> d_ntracks    = edit_int(cxt, d->d_ntracks     ,_("tracks/cylinder"));
481  d -> d_ncylinders = edit_int(cxt, d->d_ncylinders  ,_("cylinders"));
482#endif
483
484  /* d -> d_secpercyl can be != d -> d_nsectors * d -> d_ntracks */
485  while (1)
486  {
487    d -> d_secpercyl = edit_int(cxt, (unsigned long) d->d_nsectors * d -> d_ntracks,
488					  _("sectors/cylinder"));
489    if (d -> d_secpercyl <= d -> d_nsectors * d -> d_ntracks)
490      break;
491
492    printf (_("Must be <= sectors/track * tracks/cylinder (default).\n"));
493  }
494  d -> d_rpm        = (unsigned short) edit_int(cxt, d->d_rpm       ,_("rpm"));
495  d -> d_interleave = (unsigned short) edit_int(cxt, d->d_interleave,_("interleave"));
496  d -> d_trackskew  = (unsigned short) edit_int(cxt, d->d_trackskew ,_("trackskew"));
497  d -> d_cylskew    = (unsigned short) edit_int(cxt, d->d_cylskew   ,_("cylinderskew"));
498  d -> d_headswitch = edit_int(cxt, d->d_headswitch  ,_("headswitch"));
499  d -> d_trkseek    = edit_int(cxt, d->d_trkseek     ,_("track-to-track seek"));
500
501  d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders;
502}
503
504static int
505xbsd_get_bootstrap (char *path, void *ptr, int size)
506{
507  int fd;
508
509  if ((fd = open (path, O_RDONLY)) < 0)
510  {
511    perror (path);
512    return 0;
513  }
514  if (read (fd, ptr, size) < 0)
515  {
516    perror (path);
517    close (fd);
518    return 0;
519  }
520  printf (" ... %s\n", path);
521  close (fd);
522  return 1;
523}
524
525static void
526xbsd_write_bootstrap (struct fdisk_context *cxt)
527{
528  char *bootdir = BSD_LINUX_BOOTDIR;
529  char path[sizeof(BSD_LINUX_BOOTDIR) + 1 + 2 + 4];  /* BSD_LINUX_BOOTDIR + / + {sd,wd} + boot */
530  char *dkbasename;
531  struct xbsd_disklabel dl;
532  char *d, *p, *e;
533  int sector;
534
535  if (xbsd_dlabel.d_type == BSD_DTYPE_SCSI)
536    dkbasename = "sd";
537  else
538    dkbasename = "wd";
539
540  printf (_("Bootstrap: %sboot -> boot%s (%s): "),
541	  dkbasename, dkbasename, dkbasename);
542  if (read_line(cxt, NULL)) {
543    line_ptr[strlen (line_ptr)-1] = '\0';
544    dkbasename = line_ptr;
545  }
546  snprintf (path, sizeof(path), "%s/%sboot", bootdir, dkbasename);
547  if (!xbsd_get_bootstrap (path, disklabelbuffer, (int) xbsd_dlabel.d_secsize))
548    return;
549
550  /* We need a backup of the disklabel (xbsd_dlabel might have changed). */
551  d = &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE];
552  memmove (&dl, d, sizeof (struct xbsd_disklabel));
553
554  /* The disklabel will be overwritten by 0's from bootxx anyway */
555  memset (d, 0, sizeof (struct xbsd_disklabel));
556
557  snprintf (path, sizeof(path), "%s/boot%s", bootdir, dkbasename);
558  if (!xbsd_get_bootstrap (path, &disklabelbuffer[xbsd_dlabel.d_secsize],
559			  (int) xbsd_dlabel.d_bbsize - xbsd_dlabel.d_secsize))
560    return;
561
562  e = d + sizeof (struct xbsd_disklabel);
563  for (p=d; p < e; p++)
564    if (*p) {
565      fprintf (stderr, _("Bootstrap overlaps with disk label!\n"));
566      exit ( EXIT_FAILURE );
567    }
568
569  memmove (d, &dl, sizeof (struct xbsd_disklabel));
570
571#if defined (__powerpc__) || defined (__hppa__)
572  sector = 0;
573#elif defined (__alpha__)
574  sector = 0;
575  alpha_bootblock_checksum (disklabelbuffer);
576#else
577  sector = get_start_sect(xbsd_part);
578#endif
579
580  if (lseek (cxt->dev_fd, (off_t) sector * SECTOR_SIZE, SEEK_SET) == -1)
581	  fatal (cxt, unable_to_seek);
582  if (BSD_BBSIZE != write (cxt->dev_fd, disklabelbuffer, BSD_BBSIZE))
583	  fatal (cxt, unable_to_write);
584
585  printf (_("Bootstrap installed on %s.\n"), cxt->dev_path);
586
587  sync_disks ();
588}
589
590/* TODO: remove this, use regular change_partition_type() in fdisk.c */
591static void xbsd_change_fstype (struct fdisk_context *cxt)
592{
593  int i;
594  struct fdisk_parttype *t;
595
596  assert(cxt);
597  assert(cxt->label);
598  assert(fdisk_is_disklabel(cxt, OSF));
599
600  i = xbsd_get_part_index (cxt, xbsd_dlabel.d_npartitions);
601  t = read_partition_type(cxt);
602
603  if (t) {
604    xbsd_dlabel.d_partitions[i].p_fstype = t->type;
605    fdisk_free_parttype(t);
606    fdisk_label_set_changed(cxt->label, 1);
607  }
608}
609
610static int
611xbsd_get_part_index(struct fdisk_context *cxt, int max)
612{
613  char prompt[256];
614  char l;
615
616  snprintf (prompt, sizeof(prompt), _("Partition (a-%c): "), 'a' + max - 1);
617  do
618     l = tolower(read_char(cxt, prompt));
619  while (l < 'a' || l > 'a' + max - 1);
620  return l - 'a';
621}
622
623static int
624xbsd_check_new_partition(struct fdisk_context *cxt, int *i)
625{
626	/* room for more? various BSD flavours have different maxima */
627	if (xbsd_dlabel.d_npartitions == BSD_MAXPARTITIONS) {
628		int t;
629
630		for (t = 0; t < BSD_MAXPARTITIONS; t++)
631			if (xbsd_dlabel.d_partitions[t].p_size == 0)
632				break;
633
634		if (t == BSD_MAXPARTITIONS) {
635			fprintf (stderr, _("The maximum number of partitions "
636					   "has been created\n"));
637			return -EINVAL;
638		}
639	}
640
641	*i = xbsd_get_part_index(cxt, BSD_MAXPARTITIONS);
642
643	if (*i >= xbsd_dlabel.d_npartitions)
644		xbsd_dlabel.d_npartitions = (*i) + 1;
645
646	if (xbsd_dlabel.d_partitions[*i].p_size != 0) {
647		fprintf (stderr, _("This partition already exists.\n"));
648		return -EINVAL;
649	}
650
651	return 0;
652}
653
654static unsigned short
655xbsd_dkcksum (struct xbsd_disklabel *lp) {
656	unsigned short *start, *end;
657	unsigned short sum = 0;
658
659	start = (unsigned short *) lp;
660	end = (unsigned short *) &lp->d_partitions[lp->d_npartitions];
661	while (start < end)
662		sum ^= *start++;
663	return sum;
664}
665
666static int
667xbsd_initlabel (struct fdisk_context *cxt, struct partition *p, struct xbsd_disklabel *d,
668		int pindex __attribute__((__unused__))) {
669	struct xbsd_partition *pp;
670
671	memset (d, 0, sizeof (struct xbsd_disklabel));
672
673	d -> d_magic = BSD_DISKMAGIC;
674
675	if (strncmp (cxt->dev_path, "/dev/sd", 7) == 0)
676		d -> d_type = BSD_DTYPE_SCSI;
677	else
678		d -> d_type = BSD_DTYPE_ST506;
679
680#if 0 /* not used (at least not written to disk) by NetBSD/i386 1.0 */
681	d -> d_subtype = BSD_DSTYPE_INDOSPART & pindex;
682#endif
683
684#if !defined (__alpha__)
685	d -> d_flags = BSD_D_DOSPART;
686#else
687	d -> d_flags = 0;
688#endif
689	d -> d_secsize = SECTOR_SIZE;		/* bytes/sector  */
690	d -> d_nsectors = cxt->geom.sectors;		/* sectors/track */
691	d -> d_ntracks = cxt->geom.heads;		/* tracks/cylinder (heads) */
692	d -> d_ncylinders = cxt->geom.cylinders;
693	d -> d_secpercyl  = cxt->geom.sectors * cxt->geom.heads;/* sectors/cylinder */
694	if (d -> d_secpercyl == 0)
695		d -> d_secpercyl = 1;		/* avoid segfaults */
696	d -> d_secperunit = d -> d_secpercyl * d -> d_ncylinders;
697
698	d -> d_rpm = 3600;
699	d -> d_interleave = 1;
700	d -> d_trackskew = 0;
701	d -> d_cylskew = 0;
702	d -> d_headswitch = 0;
703	d -> d_trkseek = 0;
704
705	d -> d_magic2 = BSD_DISKMAGIC;
706	d -> d_bbsize = BSD_BBSIZE;
707	d -> d_sbsize = BSD_SBSIZE;
708
709#if !defined (__alpha__)
710	d -> d_npartitions = 4;
711	pp = &d -> d_partitions[2];		/* Partition C should be
712						   the NetBSD partition */
713	pp -> p_offset = get_start_sect(p);
714	pp -> p_size   = get_nr_sects(p);
715	pp -> p_fstype = BSD_FS_UNUSED;
716	pp = &d -> d_partitions[3];		/* Partition D should be
717						   the whole disk */
718	pp -> p_offset = 0;
719	pp -> p_size   = d -> d_secperunit;
720	pp -> p_fstype = BSD_FS_UNUSED;
721#elif defined (__alpha__)
722	d -> d_npartitions = 3;
723	pp = &d -> d_partitions[2];		/* Partition C should be
724						   the whole disk */
725	pp -> p_offset = 0;
726	pp -> p_size   = d -> d_secperunit;
727	pp -> p_fstype = BSD_FS_UNUSED;
728#endif
729
730	return 1;
731}
732
733/*
734 * Read a xbsd_disklabel from sector 0 or from the starting sector of p.
735 * If it has the right magic, return 1.
736 */
737static int
738xbsd_readlabel (struct fdisk_context *cxt, struct partition *p, struct xbsd_disklabel *d)
739{
740	int t, sector;
741
742	assert(cxt);
743	assert(cxt->label);
744
745	/* p is used only to get the starting sector */
746#if !defined (__alpha__)
747	sector = (p ? get_start_sect(p) : 0);
748#elif defined (__alpha__)
749	sector = 0;
750#endif
751
752	if (lseek (cxt->dev_fd, (off_t) sector * SECTOR_SIZE, SEEK_SET) == -1)
753		return 0;
754	if (BSD_BBSIZE != read (cxt->dev_fd, disklabelbuffer, BSD_BBSIZE))
755		return 0;
756
757	memmove (d,
758	         &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
759	         sizeof (struct xbsd_disklabel));
760
761	if (d -> d_magic != BSD_DISKMAGIC || d -> d_magic2 != BSD_DISKMAGIC)
762		return 0;
763
764	for (t = d -> d_npartitions; t < BSD_MAXPARTITIONS; t++) {
765		d -> d_partitions[t].p_size   = 0;
766		d -> d_partitions[t].p_offset = 0;
767		d -> d_partitions[t].p_fstype = BSD_FS_UNUSED;
768	}
769
770	if (d -> d_npartitions > BSD_MAXPARTITIONS)
771		fprintf (stderr, _("Warning: too many partitions "
772				   "(%d, maximum is %d).\n"),
773			 d -> d_npartitions, BSD_MAXPARTITIONS);
774
775	cxt->label->nparts_cur = d->d_npartitions;
776	cxt->label->nparts_max = BSD_MAXPARTITIONS;
777	return 1;
778}
779
780static int
781xbsd_writelabel (struct fdisk_context *cxt, struct partition *p, struct xbsd_disklabel *d)
782{
783  unsigned int sector;
784
785#if !defined (__alpha__) && !defined (__powerpc__) && !defined (__hppa__)
786  sector = get_start_sect(p) + BSD_LABELSECTOR;
787#else
788  sector = BSD_LABELSECTOR;
789#endif
790
791  d -> d_checksum = 0;
792  d -> d_checksum = xbsd_dkcksum (d);
793
794  /* This is necessary if we want to write the bootstrap later,
795     otherwise we'd write the old disklabel with the bootstrap.
796  */
797  memmove (&disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET], d,
798           sizeof (struct xbsd_disklabel));
799
800#if defined (__alpha__) && BSD_LABELSECTOR == 0
801  alpha_bootblock_checksum (disklabelbuffer);
802  if (lseek (cxt->dev_fd, (off_t) 0, SEEK_SET) == -1)
803	  fatal (cxt, unable_to_seek);
804  if (BSD_BBSIZE != write (cxt->dev_fd, disklabelbuffer, BSD_BBSIZE))
805	  fatal (cxt, unable_to_write);
806#else
807  if (lseek (cxt->dev_fd, (off_t) sector * SECTOR_SIZE + BSD_LABELOFFSET,
808		   SEEK_SET) == -1)
809	  fatal (cxt, unable_to_seek);
810  if (sizeof (struct xbsd_disklabel) != write (cxt->dev_fd, d, sizeof (struct xbsd_disklabel)))
811	  fatal (cxt, unable_to_write);
812#endif
813
814  sync_disks ();
815
816  return 1;
817}
818
819static void
820sync_disks (void)
821{
822  printf (_("\nSyncing disks.\n"));
823  sync ();
824  sleep (4);
825}
826
827#if !defined (__alpha__)
828static int
829xbsd_translate_fstype (int linux_type)
830{
831  switch (linux_type)
832  {
833    case 0x01: /* DOS 12-bit FAT   */
834    case 0x04: /* DOS 16-bit <32M  */
835    case 0x06: /* DOS 16-bit >=32M */
836    case 0xe1: /* DOS access       */
837    case 0xe3: /* DOS R/O          */
838    case 0xf2: /* DOS secondary    */
839      return BSD_FS_MSDOS;
840    case 0x07: /* OS/2 HPFS        */
841      return BSD_FS_HPFS;
842    default:
843      return BSD_FS_OTHER;
844  }
845}
846
847/*
848 * link partition from parent (DOS) to nested BSD partition table
849 */
850static void
851xbsd_link_part (struct fdisk_context *cxt)
852{
853	size_t k;
854	int i;
855	struct partition *p;
856
857	if (!cxt->parent)
858		return;		/* not nested PT */
859
860	if (fdisk_ask_partnum(cxt->parent, &k, FALSE))
861		return;
862
863	if (xbsd_check_new_partition(cxt, &i))
864		return;
865
866	p = get_part_table(k);
867
868	xbsd_dlabel.d_partitions[i].p_size   = get_nr_sects(p);
869	xbsd_dlabel.d_partitions[i].p_offset = get_start_sect(p);
870	xbsd_dlabel.d_partitions[i].p_fstype = xbsd_translate_fstype(p->sys_ind);
871}
872#endif
873
874#if defined (__alpha__)
875
876#if !defined(__GLIBC__)
877typedef unsigned long long u_int64_t;
878#endif
879
880void
881alpha_bootblock_checksum (char *boot)
882{
883  u_int64_t *dp, sum;
884  int i;
885
886  dp = (u_int64_t *)boot;
887  sum = 0;
888  for (i = 0; i < 63; i++)
889    sum += dp[i];
890  dp[63] = sum;
891}
892#endif /* __alpha__ */
893
894static struct fdisk_parttype *xbsd_get_parttype(
895		struct fdisk_context *cxt,
896		size_t n)
897{
898	struct fdisk_parttype *t;
899
900	assert(cxt);
901	assert(cxt->label);
902	assert(fdisk_is_disklabel(cxt, OSF));
903
904	if (n >= xbsd_dlabel.d_npartitions)
905		return NULL;
906
907	t = fdisk_get_parttype_from_code(cxt, xbsd_dlabel.d_partitions[n].p_fstype);
908	if (!t)
909		t = fdisk_new_unknown_parttype(xbsd_dlabel.d_partitions[n].p_fstype, NULL);
910	return t;
911}
912
913static int xbsd_set_parttype(
914		struct fdisk_context *cxt,
915		size_t partnum,
916		struct fdisk_parttype *t)
917{
918	struct xbsd_partition *p;
919
920	assert(cxt);
921	assert(cxt->label);
922	assert(fdisk_is_disklabel(cxt, OSF));
923
924	if (partnum >= xbsd_dlabel.d_npartitions || !t || t->type > UINT8_MAX)
925		return -EINVAL;
926
927	p = &xbsd_dlabel.d_partitions[partnum];
928	if (t->type == p->p_fstype)
929		return 0;
930
931	p->p_fstype = t->type;
932	fdisk_label_set_changed(cxt->label, 1);
933	return 0;
934}
935
936static const struct fdisk_label_operations bsd_operations =
937{
938	.probe		= osf_probe_label,
939	.write		= xbsd_write_disklabel,
940	.create		= xbsd_create_disklabel,
941	.part_add	= xbsd_add_part,
942	.part_delete	= xbsd_delete_part,
943	.part_get_type	= xbsd_get_parttype,
944	.part_set_type	= xbsd_set_parttype
945};
946
947
948/*
949 * allocates BSD label driver
950 */
951struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt)
952{
953	struct fdisk_label *lb;
954	struct fdisk_bsd_label *bsd;
955
956	assert(cxt);
957
958	bsd = calloc(1, sizeof(*bsd));
959	if (!bsd)
960		return NULL;
961
962	/* initialize generic part of the driver */
963	lb = (struct fdisk_label *) bsd;
964	lb->name = "bsd";
965	lb->id = FDISK_DISKLABEL_OSF;
966	lb->op = &bsd_operations;
967	lb->parttypes = xbsd_fstypes;
968	lb->nparttypes = ARRAY_SIZE(xbsd_fstypes);
969
970	/* don't ask for partition number for op->part_add() */
971	lb->flags = FDISK_LABEL_FL_ADDPART_NOPARTNO;
972
973	return lb;
974}
975