1 /* $OpenBSD: disksubr.c,v 1.74 2022/10/11 23:39:08 krw Exp $ */
2 /* $NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk Exp $ */
3
4 /*
5 * Copyright (c) 1994, 1995 Gordon W. Ross
6 * Copyright (c) 1994 Theo de Raadt
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. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/buf.h>
35 #include <sys/disklabel.h>
36 #include <sys/disk.h>
37
38 #include <dev/sun/disklabel.h>
39
40 #include "cd.h"
41
42 static int disklabel_sun_to_bsd(dev_t dev, struct sun_disklabel *,
43 struct disklabel *);
44 static int disklabel_bsd_to_sun(struct disklabel *, struct sun_disklabel *);
45 static __inline u_int sun_extended_sum(struct sun_disklabel *, void *);
46
47 #if NCD > 0
48 extern void cdstrategy(struct buf *);
49 #endif
50
51 /*
52 * Attempt to read a disk label from a device
53 * using the indicated strategy routine.
54 * The label must be partly set up before this:
55 * secpercyl, secsize and anything required for a block i/o read
56 * operation in the driver's strategy/start routines
57 * must be filled in before calling us.
58 */
59 int
readdisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,int spoofonly)60 readdisklabel(dev_t dev, void (*strat)(struct buf *),
61 struct disklabel *lp, int spoofonly)
62 {
63 struct sun_disklabel *slp;
64 struct buf *bp = NULL;
65 int error;
66
67 if ((error = initdisklabel(lp)))
68 goto done;
69 lp->d_flags |= D_VENDOR;
70
71 /*
72 * On sparc64 we check for a CD label first, because our
73 * CD install media contains both sparc & sparc64 labels.
74 * We want the sparc64 machine to find the "CD label", not
75 * the SunOS label, for loading its kernel.
76 */
77 #if NCD > 0
78 if (strat == cdstrategy) {
79 #if defined(CD9660)
80 if (iso_disklabelspoof(dev, strat, lp) == 0)
81 goto done;
82 #endif
83 #if defined(UDF)
84 if (udf_disklabelspoof(dev, strat, lp) == 0)
85 goto done;
86 #endif
87 }
88 #endif /* NCD > 0 */
89
90 /* get buffer and initialize it */
91 bp = geteblk(lp->d_secsize);
92 bp->b_dev = dev;
93
94 if (spoofonly)
95 goto doslabel;
96
97 error = readdisksector(bp, strat, lp, DL_BLKTOSEC(lp, LABELSECTOR));
98 if (error)
99 goto done;
100
101 slp = (struct sun_disklabel *)bp->b_data;
102 if (slp->sl_magic == SUN_DKMAGIC) {
103 error = disklabel_sun_to_bsd(bp->b_dev, slp, lp);
104 goto done;
105 }
106
107 error = checkdisklabel(bp->b_dev, bp->b_data + LABELOFFSET, lp, 0,
108 DL_GETDSIZE(lp));
109 if (error == 0)
110 goto done;
111
112 doslabel:
113 error = readdoslabel(bp, strat, lp, NULL, spoofonly);
114 if (error == 0)
115 goto done;
116
117 /* A CD9660/UDF label may be on a non-CD drive, so recheck */
118 #if defined(CD9660)
119 error = iso_disklabelspoof(dev, strat, lp);
120 if (error == 0)
121 goto done;
122 #endif
123 #if defined(UDF)
124 error = udf_disklabelspoof(dev, strat, lp);
125 if (error == 0)
126 goto done;
127 #endif
128
129 done:
130 if (bp) {
131 bp->b_flags |= B_INVAL;
132 brelse(bp);
133 }
134 disk_change = 1;
135 return (error);
136 }
137
138 /*
139 * Write disk label back to device after modification.
140 */
141 int
writedisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp)142 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp)
143 {
144 struct buf *bp = NULL;
145 int error;
146
147 /* get buffer and initialize it */
148 bp = geteblk(lp->d_secsize);
149 bp->b_dev = dev;
150
151 error = disklabel_bsd_to_sun(lp, (struct sun_disklabel *)bp->b_data);
152 if (error)
153 goto done;
154
155 /* Write out the updated label. */
156 bp->b_blkno = LABELSECTOR;
157 bp->b_bcount = lp->d_secsize;
158 CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
159 SET(bp->b_flags, B_BUSY | B_WRITE | B_RAW);
160 (*strat)(bp);
161 error = biowait(bp);
162
163 done:
164 if (bp) {
165 bp->b_flags |= B_INVAL;
166 brelse(bp);
167 }
168 disk_change = 1;
169 return (error);
170 }
171
172 /************************************************************************
173 *
174 * The rest of this was taken from arch/sparc/scsi/sun_disklabel.c
175 * and then substantially rewritten by Gordon W. Ross
176 *
177 ************************************************************************/
178
179 /* What partition types to assume for Sun disklabels: */
180 static u_char
181 sun_fstypes[16] = {
182 FS_BSDFFS, /* a */
183 FS_SWAP, /* b */
184 FS_UNUSED, /* c - whole disk */
185 FS_BSDFFS, /* d */
186 FS_BSDFFS, /* e */
187 FS_BSDFFS, /* f */
188 FS_BSDFFS, /* g */
189 FS_BSDFFS, /* h */
190 FS_BSDFFS, /* i */
191 FS_BSDFFS, /* j */
192 FS_BSDFFS, /* k */
193 FS_BSDFFS, /* l */
194 FS_BSDFFS, /* m */
195 FS_BSDFFS, /* n */
196 FS_BSDFFS, /* o */
197 FS_BSDFFS /* p */
198 };
199
200 /*
201 * Given a struct sun_disklabel, assume it has an extended partition
202 * table and compute the correct value for sl_xpsum.
203 */
204 static __inline u_int
sun_extended_sum(struct sun_disklabel * sl,void * end)205 sun_extended_sum(struct sun_disklabel *sl, void *end)
206 {
207 u_int sum, *xp, *ep;
208
209 xp = (u_int *)&sl->sl_xpmag;
210 ep = (u_int *)end;
211
212 sum = 0;
213 for (; xp < ep; xp++)
214 sum += *xp;
215 return (sum);
216 }
217
218 /*
219 * Given a SunOS disk label, set lp to a BSD disk label.
220 * The BSD label is cleared out before this is called.
221 */
222 static int
disklabel_sun_to_bsd(dev_t dev,struct sun_disklabel * sl,struct disklabel * lp)223 disklabel_sun_to_bsd(dev_t dev, struct sun_disklabel *sl, struct disklabel *lp)
224 {
225 struct sun_preamble *preamble = (struct sun_preamble *)sl;
226 struct sun_partinfo *ppp;
227 struct sun_dkpart *spp;
228 struct partition *npp;
229 u_short cksum = 0, *sp1, *sp2;
230 int i, secpercyl;
231
232 /* Verify the XOR check. */
233 sp1 = (u_short *)sl;
234 sp2 = (u_short *)(sl + 1);
235 while (sp1 < sp2)
236 cksum ^= *sp1++;
237 if (cksum != 0)
238 return (EINVAL); /* SunOS disk label, bad checksum */
239
240 /* Format conversion. */
241 lp->d_magic = DISKMAGIC;
242 lp->d_magic2 = DISKMAGIC;
243 lp->d_flags = D_VENDOR;
244 memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname));
245
246 lp->d_secsize = DEV_BSIZE;
247 lp->d_nsectors = sl->sl_nsectors;
248 lp->d_ntracks = sl->sl_ntracks;
249 lp->d_ncylinders = sl->sl_ncylinders;
250
251 secpercyl = sl->sl_nsectors * sl->sl_ntracks;
252 lp->d_secpercyl = secpercyl;
253 /* If unset or initialized as full disk, permit refinement */
254 if (DL_GETDSIZE(lp) == 0 || DL_GETDSIZE(lp) == MAXDISKSIZE)
255 DL_SETDSIZE(lp, (u_int64_t)secpercyl * sl->sl_ncylinders);
256 lp->d_version = 1;
257
258 memcpy(&lp->d_uid, &sl->sl_uid, sizeof(lp->d_uid));
259
260 lp->d_acylinders = sl->sl_acylinders;
261
262 lp->d_npartitions = MAXPARTITIONS;
263
264 for (i = 0; i < 8; i++) {
265 spp = &sl->sl_part[i];
266 npp = &lp->d_partitions[i];
267 DL_SETPOFFSET(npp, spp->sdkp_cyloffset * secpercyl);
268 DL_SETPSIZE(npp, spp->sdkp_nsectors);
269 if (DL_GETPSIZE(npp) == 0) {
270 npp->p_fstype = FS_UNUSED;
271 } else {
272 npp->p_fstype = sun_fstypes[i];
273 if (npp->p_fstype == FS_BSDFFS) {
274 /*
275 * The sun label does not store the FFS fields,
276 * so just set them with default values here.
277 */
278 npp->p_fragblock =
279 DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
280 npp->p_cpg = 16;
281 }
282 }
283 }
284
285 /* Clear "extended" partition info, tentatively */
286 for (i = 0; i < SUNXPART; i++) {
287 npp = &lp->d_partitions[i+8];
288 DL_SETPOFFSET(npp, 0);
289 DL_SETPSIZE(npp, 0);
290 npp->p_fstype = FS_UNUSED;
291 }
292
293 /* Check to see if there's an "extended" partition table
294 * SL_XPMAG partitions had checksums up to just before the
295 * (new) sl_types variable, while SL_XPMAGTYP partitions have
296 * checksums up to the just before the (new) sl_xxx1 variable.
297 * Also, disklabels created prior to the addition of sl_uid will
298 * have a checksum to just before the sl_uid variable.
299 */
300 if ((sl->sl_xpmag == SL_XPMAG &&
301 sun_extended_sum(sl, &sl->sl_types) == sl->sl_xpsum) ||
302 (sl->sl_xpmag == SL_XPMAGTYP &&
303 sun_extended_sum(sl, &sl->sl_uid) == sl->sl_xpsum) ||
304 (sl->sl_xpmag == SL_XPMAGTYP &&
305 sun_extended_sum(sl, &sl->sl_xxx1) == sl->sl_xpsum)) {
306 /*
307 * There is. Copy over the "extended" partitions.
308 * This code parallels the loop for partitions a-h.
309 */
310 for (i = 0; i < SUNXPART; i++) {
311 spp = &sl->sl_xpart[i];
312 npp = &lp->d_partitions[i+8];
313 DL_SETPOFFSET(npp, spp->sdkp_cyloffset * secpercyl);
314 DL_SETPSIZE(npp, spp->sdkp_nsectors);
315 if (DL_GETPSIZE(npp) == 0) {
316 npp->p_fstype = FS_UNUSED;
317 continue;
318 }
319 npp->p_fstype = sun_fstypes[i+8];
320 if (npp->p_fstype == FS_BSDFFS) {
321 npp->p_fragblock =
322 DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
323 npp->p_cpg = 16;
324 }
325 }
326 if (sl->sl_xpmag == SL_XPMAGTYP) {
327 for (i = 0; i < MAXPARTITIONS; i++) {
328 npp = &lp->d_partitions[i];
329 npp->p_fstype = sl->sl_types[i];
330 npp->p_fragblock = sl->sl_fragblock[i];
331 npp->p_cpg = sl->sl_cpg[i];
332 }
333 }
334 } else if (preamble->sl_nparts <= 8) {
335 /*
336 * A more traditional Sun label. Recognise certain filesystem
337 * types from it, if they are available.
338 */
339 i = preamble->sl_nparts;
340 if (i == 0)
341 i = 8;
342
343 npp = &lp->d_partitions[i-1];
344 ppp = &preamble->sl_part[i-1];
345 for (; i > 0; i--, npp--, ppp--) {
346 if (npp->p_size == 0)
347 continue;
348 if ((ppp->spi_tag == 0) && (ppp->spi_flag == 0))
349 continue;
350
351 switch (ppp->spi_tag) {
352 case SPTAG_SUNOS_ROOT:
353 case SPTAG_SUNOS_USR:
354 case SPTAG_SUNOS_VAR:
355 case SPTAG_SUNOS_HOME:
356 npp->p_fstype = FS_BSDFFS;
357 npp->p_fragblock =
358 DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
359 npp->p_cpg = 16;
360 break;
361 case SPTAG_LINUX_EXT2:
362 npp->p_fstype = FS_EXT2FS;
363 break;
364 default:
365 /* FS_SWAP for _SUNOS_SWAP and _LINUX_SWAP? */
366 npp->p_fstype = FS_UNUSED;
367 break;
368 }
369 }
370 }
371
372 lp->d_checksum = 0;
373 lp->d_checksum = dkcksum(lp);
374 return (checkdisklabel(dev, lp, lp, 0, DL_GETDSIZE(lp)));
375 }
376
377 /*
378 * Given a BSD disk label, update the Sun disklabel
379 * pointed to by cp with the new info. Note that the
380 * Sun disklabel may have other info we need to keep.
381 */
382 static int
disklabel_bsd_to_sun(struct disklabel * lp,struct sun_disklabel * sl)383 disklabel_bsd_to_sun(struct disklabel *lp, struct sun_disklabel *sl)
384 {
385 struct partition *npp;
386 struct sun_dkpart *spp;
387 int i, secpercyl;
388 u_short cksum, *sp1, *sp2;
389
390 /* Enforce preconditions */
391 if (lp->d_secsize != DEV_BSIZE || lp->d_nsectors == 0 ||
392 lp->d_ntracks == 0)
393 return (EINVAL);
394
395 /* Format conversion. */
396 bzero(sl, sizeof(*sl));
397 memcpy(sl->sl_text, lp->d_packname, sizeof(lp->d_packname));
398 sl->sl_pcylinders = lp->d_ncylinders + lp->d_acylinders; /* XXX */
399 sl->sl_ncylinders = lp->d_ncylinders;
400 sl->sl_acylinders = lp->d_acylinders;
401 sl->sl_ntracks = lp->d_ntracks;
402 sl->sl_nsectors = lp->d_nsectors;
403
404 memcpy(&sl->sl_uid, &lp->d_uid, sizeof(lp->d_uid));
405
406 secpercyl = sl->sl_nsectors * sl->sl_ntracks;
407 for (i = 0; i < 8; i++) {
408 spp = &sl->sl_part[i];
409 npp = &lp->d_partitions[i];
410 spp->sdkp_cyloffset = 0;
411 spp->sdkp_nsectors = 0;
412 if (DL_GETPSIZE(npp)) {
413 if (DL_GETPOFFSET(npp) % secpercyl)
414 return (EINVAL);
415 spp->sdkp_cyloffset = DL_GETPOFFSET(npp) / secpercyl;
416 spp->sdkp_nsectors = DL_GETPSIZE(npp);
417 }
418 }
419 sl->sl_magic = SUN_DKMAGIC;
420
421 for (i = 0; i < SUNXPART; i++) {
422 spp = &sl->sl_xpart[i];
423 npp = &lp->d_partitions[i+8];
424 spp->sdkp_cyloffset = 0;
425 spp->sdkp_nsectors = 0;
426 if (DL_GETPSIZE(npp)) {
427 if (DL_GETPOFFSET(npp) % secpercyl)
428 return (EINVAL);
429 spp->sdkp_cyloffset = DL_GETPOFFSET(npp) / secpercyl;
430 spp->sdkp_nsectors = DL_GETPSIZE(npp);
431 }
432 }
433 for (i = 0; i < MAXPARTITIONS; i++) {
434 npp = &lp->d_partitions[i];
435 sl->sl_types[i] = npp->p_fstype;
436 sl->sl_fragblock[i] = npp->p_fragblock;
437 sl->sl_cpg[i] = npp->p_cpg;
438 }
439 sl->sl_xpmag = SL_XPMAGTYP;
440 sl->sl_xpsum = sun_extended_sum(sl, &sl->sl_xxx1);
441
442 /* Correct the XOR check. */
443 sp1 = (u_short *)sl;
444 sp2 = (u_short *)(sl + 1);
445 sl->sl_cksum = cksum = 0;
446 while (sp1 < sp2)
447 cksum ^= *sp1++;
448 sl->sl_cksum = cksum;
449
450 return (0);
451 }
452