bsdlabel.c 7.66 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/* bsdlabel.c - Read BSD style partition tables.  */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2002,2004,2005,2006,2007,2008,2009  Free Software Foundation, Inc.
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/partition.h>
#include <grub/bsdlabel.h>
#include <grub/disk.h>
#include <grub/mm.h>
#include <grub/misc.h>
#include <grub/dl.h>
26
#include <grub/msdos_partition.h>
27
#include <grub/i18n.h>
28

29 30
GRUB_MOD_LICENSE ("GPLv3+");

31
#ifdef GRUB_UTIL
32
#include <grub/emu/misc.h>
33 34
#endif

35
static struct grub_partition_map grub_bsdlabel_partition_map;
36 37 38
static struct grub_partition_map grub_netbsdlabel_partition_map;
static struct grub_partition_map grub_openbsdlabel_partition_map;

39 40 41


static grub_err_t
42
iterate_real (grub_disk_t disk, grub_disk_addr_t sector, int freebsd,
43
	      struct grub_partition_map *pmap,
44
	      grub_partition_iterate_hook_t hook, void *hook_data)
45 46 47 48
{
  struct grub_partition_bsd_disk_label label;
  struct grub_partition p;
  grub_disk_addr_t delta = 0;
49
  grub_disk_addr_t pos;
50 51

  /* Read the BSD label.  */
52
  if (grub_disk_read (disk, sector, 0, sizeof (label), &label))
53 54 55
    return grub_errno;

  /* Check if it is valid.  */
56
  if (label.magic != grub_cpu_to_le32_compile_time (GRUB_PC_PARTITION_BSD_LABEL_MAGIC))
57
    return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");
58

59 60
  /* A kludge to determine a base of be.offset.  */
  if (GRUB_PC_PARTITION_BSD_LABEL_WHOLE_DISK_PARTITION
61
      < grub_cpu_to_le16 (label.num_partitions) && freebsd)
62 63 64
    {
      struct grub_partition_bsd_entry whole_disk_be;

65 66
      pos = sizeof (label) + sector * GRUB_DISK_SECTOR_SIZE
	+ sizeof (struct grub_partition_bsd_entry)
67 68 69 70 71 72 73 74 75 76
	* GRUB_PC_PARTITION_BSD_LABEL_WHOLE_DISK_PARTITION;

      if (grub_disk_read (disk, pos / GRUB_DISK_SECTOR_SIZE,
			  pos % GRUB_DISK_SECTOR_SIZE, sizeof (whole_disk_be),
			  &whole_disk_be))
	return grub_errno;

      delta = grub_le_to_cpu32 (whole_disk_be.offset);
    }

77 78
  pos = sizeof (label) + sector * GRUB_DISK_SECTOR_SIZE;

79 80
  for (p.number = 0;
       p.number < grub_cpu_to_le16 (label.num_partitions);
81
       p.number++, pos += sizeof (struct grub_partition_bsd_entry))
82 83 84
    {
      struct grub_partition_bsd_entry be;

85 86 87
      if (p.number == GRUB_PC_PARTITION_BSD_LABEL_WHOLE_DISK_PARTITION)
	continue;

88 89 90 91 92 93
      p.offset = pos / GRUB_DISK_SECTOR_SIZE;
      p.index = pos % GRUB_DISK_SECTOR_SIZE;

      if (grub_disk_read (disk, p.offset, p.index, sizeof (be),  &be))
	return grub_errno;

94
      p.start = grub_le_to_cpu32 (be.offset);
95
      p.len = grub_le_to_cpu32 (be.size);
96
      p.partmap = pmap;
97

98
      if (p.len == 0)
99 100 101 102 103 104 105
	continue;

      if (p.start < delta)
	{
#ifdef GRUB_UTIL
	  char *partname;
	  /* disk->partition != NULL as 0 < delta */
106
	  partname = disk->partition ? grub_partition_get_name (disk->partition)
107
	    : 0;
108
	  grub_util_warn (_("Discarding improperly nested partition (%s,%s,%s%d)"),
109 110
			  disk->name, partname ? : "", p.partmap->name,
			  p.number + 1);
111 112 113 114 115 116 117
	  grub_free (partname);
#endif
	  continue;
	}

      p.start -= delta;

118
      if (hook (disk, &p, hook_data))
119
	return grub_errno;
120 121 122 123
    }
  return GRUB_ERR_NONE;
}

124 125
static grub_err_t
bsdlabel_partition_map_iterate (grub_disk_t disk,
126 127
				grub_partition_iterate_hook_t hook,
				void *hook_data)
128 129 130 131
{

  if (disk->partition && grub_strcmp (disk->partition->partmap->name, "msdos")
      == 0 && disk->partition->msdostype == GRUB_PC_PARTITION_TYPE_FREEBSD)
132
    return iterate_real (disk, GRUB_PC_PARTITION_BSD_LABEL_SECTOR, 1,
133
			 &grub_bsdlabel_partition_map, hook, hook_data);
134 135 136

  if (disk->partition 
      && (grub_strcmp (disk->partition->partmap->name, "msdos") == 0
137 138 139
	  || disk->partition->partmap == &grub_bsdlabel_partition_map
	  || disk->partition->partmap == &grub_netbsdlabel_partition_map
	  || disk->partition->partmap == &grub_openbsdlabel_partition_map))
140 141
      return grub_error (GRUB_ERR_BAD_PART_TABLE, "no embedding supported");

142
  return iterate_real (disk, GRUB_PC_PARTITION_BSD_LABEL_SECTOR, 0, 
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
		       &grub_bsdlabel_partition_map, hook, hook_data);
}

/* Context for netopenbsdlabel_partition_map_iterate.  */
struct netopenbsdlabel_ctx
{
  grub_uint8_t type;
  struct grub_partition_map *pmap;
  grub_partition_iterate_hook_t hook;
  void *hook_data;
  int count;
};

/* Helper for netopenbsdlabel_partition_map_iterate.  */
static int
check_msdos (grub_disk_t dsk, const grub_partition_t partition, void *data)
{
  struct netopenbsdlabel_ctx *ctx = data;
  grub_err_t err;

  if (partition->msdostype != ctx->type)
    return 0;

  err = iterate_real (dsk, partition->start
		      + GRUB_PC_PARTITION_BSD_LABEL_SECTOR, 0, ctx->pmap,
		      ctx->hook, ctx->hook_data);
  if (err == GRUB_ERR_NONE)
    {
      ctx->count++;
      return 1;
    }
  if (err == GRUB_ERR_BAD_PART_TABLE)
    {
      grub_errno = GRUB_ERR_NONE;
      return 0;
    }
  grub_print_error ();
  return 0;
181 182
}

183 184 185
/* This is a total breakage. Even when net-/openbsd label is inside partition
   it actually describes the whole disk.
 */
186
static grub_err_t
187 188
netopenbsdlabel_partition_map_iterate (grub_disk_t disk, grub_uint8_t type,
				       struct grub_partition_map *pmap,
189 190
				       grub_partition_iterate_hook_t hook,
				       void *hook_data)
191 192 193 194 195 196
{
  if (disk->partition && grub_strcmp (disk->partition->partmap->name, "msdos")
      == 0)
    return grub_error (GRUB_ERR_BAD_PART_TABLE, "no embedding supported");

  {
197 198 199 200 201
    struct netopenbsdlabel_ctx ctx = {
      .type = type,
      .pmap = pmap,
      .hook = hook,
      .hook_data = hook_data,
202
      .count = 0
203
    };
204
    grub_err_t err;
205 206

    err = grub_partition_msdos_iterate (disk, check_msdos, &ctx);
207 208 209

    if (err)
      return err;
210
    if (!ctx.count)
211
      return grub_error (GRUB_ERR_BAD_PART_TABLE, "no bsdlabel found");
212
  }
213
  return GRUB_ERR_NONE;
214 215
}

216 217
static grub_err_t
netbsdlabel_partition_map_iterate (grub_disk_t disk,
218 219
				   grub_partition_iterate_hook_t hook,
				   void *hook_data)
220 221 222 223
{
  return netopenbsdlabel_partition_map_iterate (disk,
						GRUB_PC_PARTITION_TYPE_NETBSD,
						&grub_netbsdlabel_partition_map,
224
						hook, hook_data);
225 226 227 228
}

static grub_err_t
openbsdlabel_partition_map_iterate (grub_disk_t disk,
229 230
				    grub_partition_iterate_hook_t hook,
				    void *hook_data)
231 232 233 234
{
  return netopenbsdlabel_partition_map_iterate (disk,
						GRUB_PC_PARTITION_TYPE_OPENBSD,
						&grub_openbsdlabel_partition_map,
235
						hook, hook_data);
236 237
}

238

239 240
static struct grub_partition_map grub_bsdlabel_partition_map =
  {
241
    .name = "bsd",
242 243 244
    .iterate = bsdlabel_partition_map_iterate,
  };

245 246 247 248 249 250 251 252 253 254 255 256 257 258
static struct grub_partition_map grub_openbsdlabel_partition_map =
  {
    .name = "openbsd",
    .iterate = openbsdlabel_partition_map_iterate,
  };

static struct grub_partition_map grub_netbsdlabel_partition_map =
  {
    .name = "netbsd",
    .iterate = netbsdlabel_partition_map_iterate,
  };



259
GRUB_MOD_INIT(part_bsd)
260 261
{
  grub_partition_map_register (&grub_bsdlabel_partition_map);
262 263
  grub_partition_map_register (&grub_netbsdlabel_partition_map);
  grub_partition_map_register (&grub_openbsdlabel_partition_map);
264 265
}

266
GRUB_MOD_FINI(part_bsd)
267 268
{
  grub_partition_map_unregister (&grub_bsdlabel_partition_map);
269 270
  grub_partition_map_unregister (&grub_netbsdlabel_partition_map);
  grub_partition_map_unregister (&grub_openbsdlabel_partition_map);
271
}