1 /*
2  * ----------------------------------------------------------------------------
3  * "THE BEER-WARE LICENSE" (Revision 42):
4  * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
5  * can do whatever you want with this stuff. If we meet some day, and you think
6  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7  * ----------------------------------------------------------------------------
8  */
9 
10 #include <sys/cdefs.h>
11 __FBSDID("$FreeBSD: stable/9/lib/libdisk/rules.c 127595 2004-03-30 01:39:00Z kuriyama $");
12 
13 #include <sys/types.h>
14 #include <sys/stdint.h>
15 #include <sys/disklabel.h>
16 #ifdef PC98
17 #include <sys/diskpc98.h>
18 #else
19 #include <sys/diskmbr.h>
20 #endif
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include "libdisk.h"
26 
27 int
Track_Aligned(const struct disk * d,daddr_t offset)28 Track_Aligned(const struct disk *d, daddr_t offset)
29 {
30 #ifndef __ia64__
31 	if (!d->bios_sect)
32 		return 1;
33 	if (offset % d->bios_sect)
34 		return 0;
35 #endif /* __ia64__ */
36 	return 1;
37 }
38 
39 daddr_t
Prev_Track_Aligned(const struct disk * d,daddr_t offset)40 Prev_Track_Aligned(const struct disk *d, daddr_t offset)
41 {
42 #ifndef __ia64__
43 	if (!d->bios_sect)
44 		return offset;
45 	return (offset / d->bios_sect) * d->bios_sect;
46 #else
47 	return 1;
48 #endif
49 }
50 
51 daddr_t
Next_Track_Aligned(const struct disk * d,daddr_t offset)52 Next_Track_Aligned(const struct disk *d, daddr_t offset)
53 {
54 #ifndef __ia64__
55 	if (!d->bios_sect)
56 		return offset;
57 	return Prev_Track_Aligned(d, offset + d->bios_sect-1);
58 #else
59 	return 1;
60 #endif
61 }
62 
63 static int
Cyl_Aligned(const struct disk * d,daddr_t offset)64 Cyl_Aligned(const struct disk *d, daddr_t offset)
65 {
66 #ifndef __ia64__
67 	if (!d->bios_sect || !d->bios_hd)
68 		return 1;
69 	if (offset % (d->bios_sect * d->bios_hd))
70 		return 0;
71 #endif
72 	return 1;
73 }
74 
75 daddr_t
Prev_Cyl_Aligned(const struct disk * d,daddr_t offset)76 Prev_Cyl_Aligned(const struct disk *d, daddr_t offset)
77 {
78 #ifndef __ia64__
79 	if (!d->bios_sect || !d->bios_hd)
80 		return offset;
81 	return (offset / (d->bios_sect * d->bios_hd)) * d->bios_sect *
82 	    d->bios_hd;
83 #else
84 	return 1;
85 #endif
86 }
87 
88 daddr_t
Next_Cyl_Aligned(const struct disk * d,daddr_t offset)89 Next_Cyl_Aligned(const struct disk *d, daddr_t offset)
90 {
91 #ifndef __ia64__
92 	if (!d->bios_sect || !d->bios_hd)
93 		return offset;
94 	return Prev_Cyl_Aligned(d,offset + (d->bios_sect * d->bios_hd) - 1);
95 #else
96 	return 1;
97 #endif
98 }
99 
100 /*
101  *  Rule#0:
102  *	Chunks of type 'whole' can have max NDOSPART children.
103  *	Only one of them can have the "active" flag
104  */
105 static void
Rule_000(__unused const struct disk * d,const struct chunk * c,char * msg)106 Rule_000(__unused const struct disk *d, const struct chunk *c, char *msg)
107 {
108 #ifdef PC98
109 	int i = 0;
110 #else
111 	int i = 0, j = 0;
112 #endif
113 	struct chunk *c1;
114 
115 	if (c->type != whole)
116 		return;
117 	for (c1 = c->part; c1; c1 = c1->next) {
118 		if (c1->type != unused)
119 			continue;
120 #ifndef PC98
121 		if (c1->flags & CHUNK_ACTIVE)
122 			j++;
123 #endif
124 		i++;
125 	}
126 	if (i > NDOSPART)
127 		sprintf(msg + strlen(msg),
128 			"%d is too many children of the 'whole' chunk."
129 			"  Max is %d\n", i, NDOSPART);
130 #ifndef PC98
131 	if (j > 1)
132 		sprintf(msg + strlen(msg),
133 			"Too many active children of 'whole'");
134 #endif
135 }
136 
137 /*
138  * Rule#1:
139  *	All children of 'whole' and 'extended'  must be track-aligned.
140  *	Exception: the end can be unaligned if it matches the end of 'whole'
141  */
142 static void
Rule_001(const struct disk * d,const struct chunk * c,char * msg)143 Rule_001(const struct disk *d, const struct chunk *c, char *msg)
144 {
145 	struct chunk *c1;
146 
147 	if (c->type != whole && c->type != extended)
148 		return;
149 	for (c1 = c->part; c1; c1 = c1->next) {
150 		if (c1->type == unused)
151 			continue;
152 		c1->flags |= CHUNK_ALIGN;
153 #ifdef PC98
154 		if (!Cyl_Aligned(d, c1->offset))
155 #else
156 		if (!Track_Aligned(d, c1->offset))
157 #endif
158 			sprintf(msg + strlen(msg),
159 #ifdef PC98
160 				"chunk '%s' [%jd..%jd] does not start"
161 				" on a cylinder boundary\n",
162 #else
163 				"chunk '%s' [%jd..%jd] does not start"
164 				" on a track boundary\n",
165 #endif
166 			    c1->name, (intmax_t)c1->offset, (intmax_t)c1->end);
167 		if ((c->type == whole || c->end == c1->end)
168 		    || Cyl_Aligned(d, c1->end + 1))
169 			;
170 		else
171 			sprintf(msg + strlen(msg),
172 				"chunk '%s' [%jd..%jd] does not end"
173 				" on a cylinder boundary\n",
174 			    c1->name, (intmax_t)c1->offset, (intmax_t)c1->end);
175 	}
176 }
177 
178 /*
179  * Rule#2:
180  *	Max one 'fat' as child of 'whole'
181  */
182 static void
Rule_002(__unused const struct disk * d,const struct chunk * c,char * msg)183 Rule_002(__unused const struct disk *d, const struct chunk *c, char *msg)
184 {
185 	int i;
186 	struct chunk *c1;
187 
188 	if (c->type != whole)
189 		return;
190 	for (i = 0, c1 = c->part; c1; c1 = c1->next) {
191 		if (c1->type != fat)
192 			continue;
193 		i++;
194 	}
195 	if (i > 1) {
196 		sprintf(msg + strlen(msg),
197 			"Max one 'fat' allowed as child of 'whole'\n");
198 	}
199 }
200 
201 /*
202  * Rule#3:
203  *	Max one extended as child of 'whole'
204  */
205 static void
Rule_003(__unused const struct disk * d,const struct chunk * c,char * msg)206 Rule_003(__unused const struct disk *d, const struct chunk *c, char *msg)
207 {
208 	int i;
209 	struct chunk *c1;
210 
211 	if (c->type != whole)
212 		return;
213 	for (i = 0, c1 = c->part; c1; c1 = c1->next) {
214 		if (c1->type != extended)
215 			continue;
216 		i++;
217 	}
218 	if (i > 1) {
219 		sprintf(msg + strlen(msg),
220 			"Max one 'extended' allowed as child of 'whole'\n");
221 	}
222 }
223 
224 /*
225  * Rule#4:
226  *	Max seven 'part' as children of 'freebsd'
227  *	Max one CHUNK_IS_ROOT child per 'freebsd'
228  */
229 static void
Rule_004(__unused const struct disk * d,const struct chunk * c,char * msg)230 Rule_004(__unused const struct disk *d, const struct chunk *c, char *msg)
231 {
232 	int i = 0, k = 0;
233 	struct chunk *c1;
234 
235 	if (c->type != freebsd)
236 		return;
237 
238 	for (c1 = c->part; c1; c1 = c1->next) {
239 		if (c1->type != part)
240 			continue;
241 		if (c1->flags & CHUNK_IS_ROOT)
242 			k++;
243 		i++;
244 	}
245 	if (i > 7) {
246 		sprintf(msg + strlen(msg),
247 			"Max seven partitions per freebsd slice\n");
248 	}
249 	if (k > 1) {
250 		sprintf(msg + strlen(msg),
251 			"Max one root partition child per freebsd slice\n");
252 	}
253 }
254 
255 static void
Check_Chunk(const struct disk * d,const struct chunk * c,char * msg)256 Check_Chunk(const struct disk *d, const struct chunk *c, char *msg)
257 {
258 
259 	switch (platform) {
260 	case p_i386:
261 	case p_amd64:
262 		Rule_000(d, c, msg);
263 		Rule_001(d, c, msg);
264 		Rule_002(d, c, msg);
265 		Rule_003(d, c, msg);
266 		Rule_004(d, c, msg);
267 		if (c->part)
268 			Check_Chunk(d, c->part, msg);
269 		if (c->next)
270 			Check_Chunk(d, c->next, msg);
271 		break;
272 	case p_pc98:
273 		Rule_000(d, c, msg);
274 		Rule_001(d, c, msg);
275 		Rule_004(d, c, msg);
276 		if (c->part)
277 			Check_Chunk(d, c->part, msg);
278 		if (c->next)
279 			Check_Chunk(d, c->next, msg);
280 		break;
281 	default:
282 		break;
283 	}
284 }
285 
286 char *
CheckRules(const struct disk * d)287 CheckRules(const struct disk *d)
288 {
289 	char msg[BUFSIZ];
290 
291 	*msg = '\0';
292 	Check_Chunk(d, d->chunks, msg);
293 	if (*msg)
294 		return strdup(msg);
295 	return 0;
296 }
297