]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/geom/part/g_part_bsd.c
Merge gnu cpio 2.6 -> 2.8 changes. Unfortunately, we have massive
[FreeBSD/FreeBSD.git] / sys / geom / part / g_part_bsd.c
1 /*-
2  * Copyright (c) 2007 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 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/bio.h>
32 #include <sys/disklabel.h>
33 #include <sys/endian.h>
34 #include <sys/kernel.h>
35 #include <sys/kobj.h>
36 #include <sys/limits.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mutex.h>
40 #include <sys/queue.h>
41 #include <sys/sbuf.h>
42 #include <sys/systm.h>
43 #include <geom/geom.h>
44 #include <geom/part/g_part.h>
45
46 #include "g_part_if.h"
47
48 struct g_part_bsd_table {
49         struct g_part_table     base;
50         u_char                  *label;
51         uint32_t                offset;
52 };
53
54 struct g_part_bsd_entry {
55         struct g_part_entry     base;
56         struct partition        part;
57 };
58
59 static int g_part_bsd_add(struct g_part_table *, struct g_part_entry *,
60     struct g_part_parms *);
61 static int g_part_bsd_create(struct g_part_table *, struct g_part_parms *);
62 static int g_part_bsd_destroy(struct g_part_table *, struct g_part_parms *);
63 static int g_part_bsd_dumpconf(struct g_part_table *, struct g_part_entry *,
64     struct sbuf *, const char *);
65 static int g_part_bsd_dumpto(struct g_part_table *, struct g_part_entry *);
66 static int g_part_bsd_modify(struct g_part_table *, struct g_part_entry *,  
67     struct g_part_parms *);
68 static char *g_part_bsd_name(struct g_part_table *, struct g_part_entry *,
69     char *, size_t);
70 static int g_part_bsd_probe(struct g_part_table *, struct g_consumer *);
71 static int g_part_bsd_read(struct g_part_table *, struct g_consumer *);
72 static const char *g_part_bsd_type(struct g_part_table *, struct g_part_entry *,
73     char *, size_t);
74 static int g_part_bsd_write(struct g_part_table *, struct g_consumer *);
75
76 static kobj_method_t g_part_bsd_methods[] = {
77         KOBJMETHOD(g_part_add,          g_part_bsd_add),
78         KOBJMETHOD(g_part_create,       g_part_bsd_create),
79         KOBJMETHOD(g_part_destroy,      g_part_bsd_destroy),
80         KOBJMETHOD(g_part_dumpconf,     g_part_bsd_dumpconf),
81         KOBJMETHOD(g_part_dumpto,       g_part_bsd_dumpto),
82         KOBJMETHOD(g_part_modify,       g_part_bsd_modify),
83         KOBJMETHOD(g_part_name,         g_part_bsd_name),
84         KOBJMETHOD(g_part_probe,        g_part_bsd_probe),
85         KOBJMETHOD(g_part_read,         g_part_bsd_read),
86         KOBJMETHOD(g_part_type,         g_part_bsd_type),
87         KOBJMETHOD(g_part_write,        g_part_bsd_write),
88         { 0, 0 }
89 };
90
91 static struct g_part_scheme g_part_bsd_scheme = {
92         "BSD",
93         g_part_bsd_methods,
94         sizeof(struct g_part_bsd_table),
95         .gps_entrysz = sizeof(struct g_part_bsd_entry),
96         .gps_minent = 8,
97         .gps_maxent = 20,
98 };
99 G_PART_SCHEME_DECLARE(g_part_bsd);
100
101 static int
102 bsd_parse_type(const char *type, uint8_t *fstype)
103 {
104         const char *alias;
105         char *endp;
106         long lt;
107
108         if (type[0] == '!') {
109                 lt = strtol(type + 1, &endp, 0);
110                 if (type[1] == '\0' || *endp != '\0' || lt <= 0 || lt >= 256)
111                         return (EINVAL);
112                 *fstype = (u_int)lt;
113                 return (0);
114         }
115         alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP);
116         if (!strcasecmp(type, alias)) {
117                 *fstype = FS_SWAP;
118                 return (0);
119         }
120         alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS);
121         if (!strcasecmp(type, alias)) {
122                 *fstype = FS_BSDFFS;
123                 return (0);
124         }
125         alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM);
126         if (!strcasecmp(type, alias)) {
127                 *fstype = FS_VINUM;
128                 return (0);
129         }
130         alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS);
131         if (!strcasecmp(type, alias)) {
132                 *fstype = FS_ZFS;
133                 return (0);
134         }
135         return (EINVAL);
136 }
137
138 static int
139 g_part_bsd_add(struct g_part_table *basetable, struct g_part_entry *baseentry,
140     struct g_part_parms *gpp)
141 {
142         struct g_part_bsd_entry *entry;
143         struct g_part_bsd_table *table;
144
145         if (gpp->gpp_parms & G_PART_PARM_LABEL)
146                 return (EINVAL);
147
148         entry = (struct g_part_bsd_entry *)baseentry;
149         table = (struct g_part_bsd_table *)basetable;
150
151         entry->part.p_size = gpp->gpp_size;
152         entry->part.p_offset = gpp->gpp_start + table->offset;
153         entry->part.p_fsize = 0;
154         entry->part.p_frag = 0;
155         entry->part.p_cpg = 0;
156         return (bsd_parse_type(gpp->gpp_type, &entry->part.p_fstype));
157 }
158
159 static int
160 g_part_bsd_create(struct g_part_table *basetable, struct g_part_parms *gpp)
161 {
162         struct g_consumer *cp;
163         struct g_provider *pp;
164         struct g_part_entry *baseentry;
165         struct g_part_bsd_entry *entry;
166         struct g_part_bsd_table *table;
167         u_char *ptr;
168         uint64_t msize;
169         uint32_t ncyls, secpercyl;
170
171         pp = gpp->gpp_provider;
172         cp = LIST_FIRST(&pp->consumers);
173
174         if (pp->sectorsize < sizeof(struct disklabel))
175                 return (ENOSPC);
176
177         msize = pp->mediasize / pp->sectorsize;
178         secpercyl = basetable->gpt_sectors * basetable->gpt_heads;
179         ncyls = msize / secpercyl;
180
181         table = (struct g_part_bsd_table *)basetable;
182         ptr = table->label = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
183
184         le32enc(ptr + 0, DISKMAGIC);                    /* d_magic */
185         le32enc(ptr + 40, pp->sectorsize);              /* d_secsize */
186         le32enc(ptr + 44, basetable->gpt_sectors);      /* d_nsectors */
187         le32enc(ptr + 48, basetable->gpt_heads);        /* d_ntracks */
188         le32enc(ptr + 52, ncyls);                       /* d_ncylinders */
189         le32enc(ptr + 56, secpercyl);                   /* d_secpercyl */
190         le32enc(ptr + 60, msize);                       /* d_secperunit */
191         le16enc(ptr + 72, 3600);                        /* d_rpm */
192         le32enc(ptr + 132, DISKMAGIC);                  /* d_magic2 */
193         le16enc(ptr + 138, basetable->gpt_entries);     /* d_npartitions */
194         le32enc(ptr + 140, BBSIZE);                     /* d_bbsize */
195
196         basetable->gpt_first = 0;
197         basetable->gpt_last = msize - 1;
198         basetable->gpt_isleaf = 1;
199
200         baseentry = g_part_new_entry(basetable, RAW_PART + 1,
201             basetable->gpt_first, basetable->gpt_last);
202         baseentry->gpe_internal = 1;
203         entry = (struct g_part_bsd_entry *)baseentry;
204         entry->part.p_size = basetable->gpt_last + 1;
205         entry->part.p_offset = table->offset;
206
207         return (0);
208 }
209
210 static int
211 g_part_bsd_destroy(struct g_part_table *basetable, struct g_part_parms *gpp)
212 {
213
214         /* Wipe the second sector to clear the partitioning. */
215         basetable->gpt_smhead |= 2;
216         return (0);
217 }
218
219 static int
220 g_part_bsd_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry, 
221     struct sbuf *sb, const char *indent)
222 {
223         struct g_part_bsd_entry *entry;
224
225         entry = (struct g_part_bsd_entry *)baseentry;
226         if (indent == NULL) {
227                 /* conftxt: libdisk compatibility */
228                 sbuf_printf(sb, " xs BSD xt %u", entry->part.p_fstype);
229         } else if (entry != NULL) {
230                 /* confxml: partition entry information */
231                 sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent,
232                     entry->part.p_fstype);
233         } else {
234                 /* confxml: scheme information */
235         }
236         return (0);
237 }
238
239 static int
240 g_part_bsd_dumpto(struct g_part_table *table, struct g_part_entry *baseentry)  
241 {
242         struct g_part_bsd_entry *entry;
243
244         /* Allow dumping to a swap partition only. */
245         entry = (struct g_part_bsd_entry *)baseentry;
246         return ((entry->part.p_fstype == FS_SWAP) ? 1 : 0);
247 }
248
249 static int
250 g_part_bsd_modify(struct g_part_table *basetable,
251     struct g_part_entry *baseentry, struct g_part_parms *gpp)
252 {
253         struct g_part_bsd_entry *entry;
254
255         if (gpp->gpp_parms & G_PART_PARM_LABEL)
256                 return (EINVAL);
257
258         entry = (struct g_part_bsd_entry *)baseentry;
259         if (gpp->gpp_parms & G_PART_PARM_TYPE)
260                 return (bsd_parse_type(gpp->gpp_type, &entry->part.p_fstype));
261         return (0);
262 }
263
264 static char *
265 g_part_bsd_name(struct g_part_table *table, struct g_part_entry *baseentry,
266     char *buf, size_t bufsz)
267 {
268
269         snprintf(buf, bufsz, "%c", 'a' + baseentry->gpe_index - 1);
270         return (buf);
271 }
272
273 static int
274 g_part_bsd_probe(struct g_part_table *table, struct g_consumer *cp)
275 {
276         struct g_provider *pp;
277         u_char *buf;
278         uint32_t magic1, magic2;
279         int error;
280
281         pp = cp->provider;
282
283         /* Sanity-check the provider. */
284         if (pp->sectorsize < sizeof(struct disklabel) ||
285             pp->mediasize < BBSIZE)
286                 return (ENOSPC);
287
288         /* Check that there's a disklabel. */
289         buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error);
290         if (buf == NULL)
291                 return (error);
292         magic1 = le32dec(buf + 0);
293         magic2 = le32dec(buf + 132);
294         g_free(buf);
295         return ((magic1 == DISKMAGIC && magic2 == DISKMAGIC)
296             ? G_PART_PROBE_PRI_NORM : ENXIO);
297 }
298
299 static int
300 g_part_bsd_read(struct g_part_table *basetable, struct g_consumer *cp)
301 {
302         struct g_provider *pp;
303         struct g_part_bsd_table *table;
304         struct g_part_entry *baseentry;
305         struct g_part_bsd_entry *entry;
306         struct partition part;
307         u_char *buf, *p;
308         off_t chs, msize;
309         u_int sectors, heads;
310         int error, index;
311
312         pp = cp->provider;
313         table = (struct g_part_bsd_table *)basetable;
314         msize = pp->mediasize / pp->sectorsize;
315
316         buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error);
317         if (buf == NULL)
318                 return (error);
319
320         table->label = buf;
321
322         if (le32dec(buf + 40) != pp->sectorsize)
323                 goto invalid_label;
324         sectors = le32dec(buf + 44);
325         if (sectors < 1 || sectors > 63)
326                 goto invalid_label;
327         if (sectors != basetable->gpt_sectors && !basetable->gpt_fixgeom) {
328                 g_part_geometry_heads(msize, sectors, &chs, &heads);
329                 if (chs != 0) {
330                         basetable->gpt_sectors = sectors;
331                         basetable->gpt_heads = heads;
332                 }
333         }
334         heads = le32dec(buf + 48);
335         if (heads < 1 || heads > 255)
336                 goto invalid_label;
337         if (heads != basetable->gpt_heads && !basetable->gpt_fixgeom)
338                 basetable->gpt_heads = heads;
339         if (sectors != basetable->gpt_sectors ||
340             heads != basetable->gpt_heads)
341                 printf("GEOM: %s: geometry does not match label.\n", pp->name);
342
343         chs = le32dec(buf + 60);
344         if (chs < 1 || chs > msize)
345                 goto invalid_label;
346         if (chs != msize)
347                 printf("GEOM: %s: media size does not match label.\n",
348                     pp->name);
349
350         basetable->gpt_first = 0;
351         basetable->gpt_last = msize - 1;
352         basetable->gpt_isleaf = 1;
353
354         basetable->gpt_entries = le16dec(buf + 138);
355         if (basetable->gpt_entries < g_part_bsd_scheme.gps_minent ||
356             basetable->gpt_entries > g_part_bsd_scheme.gps_maxent)
357                 goto invalid_label;
358
359         table->offset = le32dec(buf + 148 + RAW_PART * 16 + 4);
360         for (index = basetable->gpt_entries - 1; index >= 0; index--) {
361                 p = buf + 148 + index * 16;
362                 part.p_size = le32dec(p + 0);
363                 part.p_offset = le32dec(p + 4);
364                 part.p_fsize = le32dec(p + 8);
365                 part.p_fstype = p[12];
366                 part.p_frag = p[13];
367                 part.p_cpg = le16dec(p + 14);
368                 if (part.p_size == 0)
369                         continue;
370                 if (part.p_fstype == FS_UNUSED && index != RAW_PART)
371                         continue;
372                 if (part.p_offset < table->offset)
373                         continue;
374                 baseentry = g_part_new_entry(basetable, index + 1,
375                     part.p_offset - table->offset,
376                     part.p_offset - table->offset + part.p_size - 1);
377                 entry = (struct g_part_bsd_entry *)baseentry;
378                 entry->part = part;
379                 if (part.p_fstype == FS_UNUSED)
380                         baseentry->gpe_internal = 1;
381         }
382
383         return (0);
384
385  invalid_label:
386         printf("GEOM: %s: invalid disklabel.\n", pp->name);
387         g_free(table->label);
388         return (EINVAL);
389 }
390
391 static const char *
392 g_part_bsd_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 
393     char *buf, size_t bufsz)
394 {
395         struct g_part_bsd_entry *entry;
396         int type;
397
398         entry = (struct g_part_bsd_entry *)baseentry;
399         type = entry->part.p_fstype;
400         if (type == FS_SWAP)
401                 return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP));
402         if (type == FS_BSDFFS)
403                 return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS));
404         if (type == FS_VINUM)
405                 return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM));
406         if (type == FS_ZFS)
407                 return (g_part_alias_name(G_PART_ALIAS_FREEBSD_ZFS));
408         snprintf(buf, bufsz, "!%d", type);
409         return (buf);
410 }
411
412 static int
413 g_part_bsd_write(struct g_part_table *basetable, struct g_consumer *cp)
414 {
415         struct g_provider *pp;
416         struct g_part_entry *baseentry;
417         struct g_part_bsd_entry *entry;
418         struct g_part_bsd_table *table;
419         uint16_t sum;
420         u_char *p, *pe;
421         int error, index;
422
423         pp = cp->provider;
424         table = (struct g_part_bsd_table *)basetable;
425         baseentry = LIST_FIRST(&basetable->gpt_entry);
426         for (index = 1; index <= basetable->gpt_entries; index++) {
427                 p = table->label + 148 + (index - 1) * 16;
428                 entry = (baseentry != NULL && index == baseentry->gpe_index)
429                     ? (struct g_part_bsd_entry *)baseentry : NULL;
430                 if (entry != NULL && !baseentry->gpe_deleted) {
431                         le32enc(p + 0, entry->part.p_size);
432                         le32enc(p + 4, entry->part.p_offset);
433                         le32enc(p + 8, entry->part.p_fsize);
434                         p[12] = entry->part.p_fstype;
435                         p[13] = entry->part.p_frag;
436                         le16enc(p + 14, entry->part.p_cpg);
437                 } else
438                         bzero(p, 16);
439
440                 if (entry != NULL)
441                         baseentry = LIST_NEXT(baseentry, gpe_entry);
442         }
443
444         /* Calculate checksum. */
445         le16enc(table->label + 136, 0);
446         pe = table->label + 148 + basetable->gpt_entries * 16;
447         sum = 0;
448         for (p = table->label; p < pe; p += 2)
449                 sum ^= le16dec(p);
450         le16enc(table->label + 136, sum);
451
452         error = g_write_data(cp, pp->sectorsize, table->label, pp->sectorsize);
453         return (error);
454 }