1 /*-
2  * Copyright (c) 2002 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #if HAVE_NBTOOL_CONFIG_H
28 #include "nbtool_config.h"
29 #endif
30 
31 #include <sys/cdefs.h>
32 #ifdef __FBSDID
33 __FBSDID("$FreeBSD: src/sbin/gpt/migrate.c,v 1.16 2005/09/01 02:42:52 marcel Exp $");
34 #endif
35 #ifdef __RCSID
36 __RCSID("$NetBSD: migrate.c,v 1.36 2025/02/23 20:47:19 christos Exp $");
37 #endif
38 
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #define FSTYPENAMES
42 #define MBRPTYPENAMES
43 #ifdef HAVE_NBTOOL_CONFIG_H
44 #include <nbinclude/sys/bootblock.h>
45 #include <nbinclude/sys/disklabel.h>
46 #else
47 #include <sys/bootblock.h>
48 #include <sys/disklabel.h>
49 #endif
50 
51 #include <err.h>
52 #include <stddef.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 
58 #include "map.h"
59 #include "gpt.h"
60 #include "gpt_private.h"
61 
62 /*
63  * Allow compilation on platforms that do not have a BSD label.
64  * The values are valid for amd64, i386 and ia64 disklabels.
65  * XXX: use disklabel_params from disklabel.c
66  */
67 #ifndef LABELOFFSET
68 #define   LABELOFFSET         0
69 #endif
70 #ifndef LABELSECTOR
71 #define   LABELSECTOR         1
72 #endif
73 #ifndef RAW_PART
74 #define   RAW_PART  3
75 #endif
76 
77 /* FreeBSD filesystem types that don't match corresponding NetBSD types */
78 #define   FREEBSD_FS_VINUM    14
79 #define   FREEBSD_FS_ZFS                27
80 
81 static int cmd_migrate(gpt_t, int, char *[]);
82 
83 static const char *migratehelp[] = {
84           "[-Afs] [-p partitions]",
85 };
86 
87 const struct gpt_cmd c_migrate = {
88           "migrate",
89           cmd_migrate,
90           migratehelp, __arraycount(migratehelp),
91           GPT_SYNC,
92 };
93 
94 #define usage() gpt_usage(NULL, &c_migrate)
95 
96 static const char *
fstypename(u_int t)97 fstypename(u_int t)
98 {
99           static char buf[64];
100           if (t >= __arraycount(fstypenames)) {
101                     snprintf(buf, sizeof(buf), "*%u*", t);
102                     return buf;
103           }
104           return fstypenames[t];
105 }
106 
107 static const char *
mbrptypename(u_int t)108 mbrptypename(u_int t)
109 {
110           static char buf[64];
111           size_t i;
112 
113           for (i = 0; i < __arraycount(mbr_ptypes); i++)
114                     if ((u_int)mbr_ptypes[i].id == t)
115                               return mbr_ptypes[i].name;
116 
117           snprintf(buf, sizeof(buf), "*%u*", t);
118           return buf;
119 }
120 
121 static gpt_type_t
freebsd_fstype_to_gpt_type(gpt_t gpt,u_int i __unused,u_int fstype)122 freebsd_fstype_to_gpt_type(gpt_t gpt, u_int i __unused, u_int fstype)
123 {
124           switch (fstype) {
125           case FS_UNUSED:
126                     return GPT_TYPE_INVALID;
127           case FS_SWAP:
128                     return GPT_TYPE_FREEBSD_SWAP;
129           case FS_BSDFFS:
130                     return GPT_TYPE_FREEBSD_UFS;
131           case FREEBSD_FS_VINUM:
132                     return GPT_TYPE_FREEBSD_VINUM;
133           case FREEBSD_FS_ZFS:
134                     return GPT_TYPE_FREEBSD_ZFS;
135           default:
136                     gpt_warnx(gpt, "Unknown FreeBSD partition (%d)", fstype);
137                     return GPT_TYPE_INVALID;
138           }
139 }
140 
141 static gpt_type_t
netbsd_fstype_to_gpt_type(gpt_t gpt,u_int i,u_int fstype)142 netbsd_fstype_to_gpt_type(gpt_t gpt, u_int i, u_int fstype)
143 {
144           switch (fstype) {
145           case FS_UNUSED:
146                     return GPT_TYPE_INVALID;
147           case FS_HFS:
148                     return GPT_TYPE_APPLE_HFS;
149           case FS_EX2FS:
150                     return GPT_TYPE_LINUX_DATA;
151           case FS_SWAP:
152                     return GPT_TYPE_NETBSD_SWAP;
153           case FS_BSDFFS:
154                     return GPT_TYPE_NETBSD_FFS;
155           case FS_BSDLFS:
156                     return GPT_TYPE_NETBSD_LFS;
157           case FS_RAID:
158                     return GPT_TYPE_NETBSD_RAIDFRAME;
159           case FS_CCD:
160                     return GPT_TYPE_NETBSD_CCD;
161           case FS_CGD:
162                     return GPT_TYPE_NETBSD_CGD;
163           default:
164                     gpt_warnx(gpt, "Partition %u unknown type %s, "
165                         "using \"Microsoft Basic Data\"", i, fstypename(fstype));
166                     return GPT_TYPE_MS_BASIC_DATA;
167           }
168 }
169 
170 static struct gpt_ent *
migrate_disklabel(gpt_t gpt,off_t start,struct gpt_ent * ent,gpt_type_t (* convert)(gpt_t,u_int,u_int))171 migrate_disklabel(gpt_t gpt, off_t start, struct gpt_ent *ent,
172     gpt_type_t (*convert)(gpt_t, u_int, u_int))
173 {
174           char *buf;
175           struct disklabel *dl;
176           off_t ofs, rawofs;
177           unsigned int i;
178           gpt_type_t type;
179 
180           buf = gpt_read(gpt, start + LABELSECTOR, 1);
181           if (buf == NULL) {
182                     gpt_warn(gpt, "Error reading label");
183                     return NULL;
184           }
185           dl = (void*)(buf + LABELOFFSET);
186 
187           if (le32toh(dl->d_magic) != DISKMAGIC ||
188               le32toh(dl->d_magic2) != DISKMAGIC) {
189                     gpt_warnx(gpt, "MBR partition without disklabel");
190                     free(buf);
191                     return ent;
192           }
193 
194           rawofs = le32toh(dl->d_partitions[RAW_PART].p_offset) *
195               le32toh(dl->d_secsize);
196           for (i = 0; i < le16toh(dl->d_npartitions); i++) {
197                     if (dl->d_partitions[i].p_fstype == FS_UNUSED)
198                               continue;
199                     ofs = le32toh(dl->d_partitions[i].p_offset) *
200                         le32toh(dl->d_secsize);
201                     if (ofs < rawofs)
202                               rawofs = 0;
203           }
204 
205           if (gpt->verbose > 1)
206                     gpt_msg(gpt, "rawofs=%ju", (uintmax_t)rawofs);
207           rawofs /= gpt->secsz;
208 
209           for (i = 0; i < le16toh(dl->d_npartitions); i++) {
210                     if (gpt->verbose > 1)
211                               gpt_msg(gpt, "Disklabel partition %u type %s", i,
212                                   fstypename(dl->d_partitions[i].p_fstype));
213 
214                     type = (*convert)(gpt, i, dl->d_partitions[i].p_fstype);
215                     if (type == GPT_TYPE_INVALID)
216                               continue;
217 
218                     gpt_uuid_create(type, ent->ent_type,
219                         ent->ent_name, sizeof(ent->ent_name));
220 
221                     ofs = (le32toh(dl->d_partitions[i].p_offset) *
222                         le32toh(dl->d_secsize)) / gpt->secsz;
223                     ofs = (ofs > 0) ? ofs - rawofs : 0;
224                     ent->ent_lba_start = htole64((uint64_t)ofs);
225                     ent->ent_lba_end = htole64((uint64_t)(ofs +
226                         (off_t)le32toh((uint64_t)dl->d_partitions[i].p_size)
227                         - 1LL));
228                     ent++;
229           }
230 
231           free(buf);
232           return ent;
233 }
234 
235 static int
migrate(gpt_t gpt,u_int parts,int force,int slice,int active)236 migrate(gpt_t gpt, u_int parts, int force, int slice, int active)
237 {
238           off_t last = gpt_last(gpt);
239           map_t map;
240           struct gpt_ent *ent;
241           struct mbr *mbr;
242           uint32_t start, size;
243           unsigned int i;
244           gpt_type_t type = GPT_TYPE_INVALID;
245 
246           map = map_find(gpt, MAP_TYPE_MBR);
247           if (map == NULL || map->map_start != 0) {
248                     gpt_warnx(gpt, "No MBR in disk to convert");
249                     return -1;
250           }
251 
252           mbr = map->map_data;
253 
254           if (gpt_create(gpt, last, parts, 0) == -1)
255                     return -1;
256 
257           ent = gpt->tbl->map_data;
258 
259           /* Mirror partitions. */
260           for (i = 0; i < 4; i++) {
261                     start = le16toh(mbr->mbr_part[i].part_start_hi);
262                     start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
263                     size = le16toh(mbr->mbr_part[i].part_size_hi);
264                     size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
265 
266                     if (gpt->verbose > 1)
267                               gpt_msg(gpt, "MBR partition %u type %s", i,
268                                   mbrptypename(mbr->mbr_part[i].part_typ));
269                     switch (mbr->mbr_part[i].part_typ) {
270                     case MBR_PTYPE_UNUSED:
271                               continue;
272 
273                     case MBR_PTYPE_386BSD: /* FreeBSD */
274                               if (slice) {
275                                         type = GPT_TYPE_FREEBSD;
276                                         break;
277                               } else {
278                                         ent = migrate_disklabel(gpt, start, ent,
279                                             freebsd_fstype_to_gpt_type);
280                                         continue;
281                               }
282 
283                     case MBR_PTYPE_NETBSD:        /* NetBSD */
284                               ent = migrate_disklabel(gpt, start, ent,
285                                   netbsd_fstype_to_gpt_type);
286                               continue;
287 
288                     case MBR_PTYPE_EFI:
289                               type = GPT_TYPE_EFI;
290                               break;
291 
292                     case MBR_PTYPE_FAT12:
293                     case MBR_PTYPE_FAT16S:
294                     case MBR_PTYPE_FAT16B:
295                     case MBR_PTYPE_NTFS:
296                     case MBR_PTYPE_FAT32:
297                     case MBR_PTYPE_FAT32L:
298                     case MBR_PTYPE_FAT16L:
299                     case MBR_PTYPE_OS2_DOS12:
300                     case MBR_PTYPE_OS2_DOS16S:
301                     case MBR_PTYPE_OS2_DOS16B:
302                     case MBR_PTYPE_OS2_IFS:
303                     case MBR_PTYPE_HID_FAT32:
304                     case MBR_PTYPE_HID_FAT32_LBA:
305                     case MBR_PTYPE_HID_FAT16_LBA:
306                               type = GPT_TYPE_MS_BASIC_DATA;
307                               break;
308 
309                     default:
310                               if (!force) {
311                                         gpt_warnx(gpt, "unknown partition type (%d)",
312                                             mbr->mbr_part[i].part_typ);
313                                         return -1;
314                               }
315                               continue;
316                     }
317                     gpt_uuid_create(type, ent->ent_type, ent->ent_name,
318                         sizeof(ent->ent_name));
319                     ent->ent_lba_start = htole64((uint64_t)start);
320                     ent->ent_lba_end = htole64((uint64_t)(start + size - 1LL));
321                     ent++;
322           }
323 
324           if (gpt_write_primary(gpt) == -1)
325                     return -1;
326 
327           if (gpt_write_backup(gpt) == -1)
328                     return -1;
329 
330           /*
331            * Turn the MBR into a Protective MBR.
332            */
333           memset(mbr->mbr_part, 0, sizeof(mbr->mbr_part));
334           gpt_create_pmbr_part(mbr->mbr_part, last, active);
335           if (gpt_write(gpt, map) == -1) {
336                     gpt_warn(gpt, "Cant write PMBR");
337                     return -1;
338           }
339           return 0;
340 }
341 
342 static int
cmd_migrate(gpt_t gpt,int argc,char * argv[])343 cmd_migrate(gpt_t gpt, int argc, char *argv[])
344 {
345           int ch;
346           int force = 0;
347           int slice = 0;
348           int active = 0;
349           u_int parts = 128;
350 
351           /* Get the migrate options */
352           while ((ch = getopt(argc, argv, "Afp:s")) != -1) {
353                     switch(ch) {
354                     case 'A':
355                               active = 1;
356                               break;
357                     case 'f':
358                               force = 1;
359                               break;
360                     case 'p':
361                               if (gpt_uint_get(gpt, &parts) == -1)
362                                         return usage();
363                               break;
364                     case 's':
365                               slice = 1;
366                               break;
367                     default:
368                               return usage();
369                     }
370           }
371 
372           if (argc != optind)
373                     return usage();
374 
375           return migrate(gpt, parts, force, slice, active);
376 }
377