xref: /xnu-11215/libkern/OSKextVersion.c (revision bb611c8f)
1 /*
2  * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 #ifdef KERNEL
29 #include <sys/systm.h>
30 #include <libkern/OSKextLib.h>
31 #include <libkern/OSKextLibPrivate.h>
32 #else
33 #include <libc.h>
34 #include <libkern/OSKextLib.h>
35 #include <System/libkern/OSKextLibPrivate.h>
36 #endif /* KERNEL */
37 
38 #include <libkern/OSKextLibPrivate.h>
39 
40 #define VERS_MAJOR_DIGITS        (4)
41 #define VERS_MINOR_DIGITS        (4)
42 #define VERS_REVISION_DIGITS     (4)
43 #define VERS_STAGE_DIGITS        (1)
44 #define VERS_STAGE_LEVEL_DIGITS  (3)
45 
46 #define VERS_MAJOR_MAX           (9999)
47 #define VERS_STAGE_LEVEL_MAX      (255)
48 
49 #define VERS_MAJOR_MULT  (1000000000000)
50 #define VERS_MINOR_MULT      (100000000)
51 #define VERS_REVISION_MULT       (10000)
52 #define VERS_STAGE_MULT           (1000)
53 
54 
55 typedef enum {
56 	kOSKextVersionStageInvalid     = 0,
57 	kOSKextVersionStageDevelopment = 1,
58 	kOSKextVersionStageAlpha       = 3,
59 	kOSKextVersionStageBeta        = 5,
60 	kOSKextVersionStageCandidate   = 7,
61 	kOSKextVersionStageRelease     = 9,
62 } OSKextVersionStage;
63 
64 
65 /*********************************************************************
66 *********************************************************************/
67 static int
__vers_isdigit(char c)68 __vers_isdigit(char c)
69 {
70 	return c == '0' ||
71 	       c == '1' || c == '2' || c == '3' ||
72 	       c == '4' || c == '5' || c == '6' ||
73 	       c == '7' || c == '8' || c == '9';
74 }
75 
76 /*********************************************************************
77 *********************************************************************/
78 static int
__vers_isspace(char c)79 __vers_isspace(char c)
80 {
81 	return c == ' ' ||
82 	       c == '\t' ||
83 	       c == '\r' ||
84 	       c == '\n';
85 }
86 
87 /*********************************************************************
88 *********************************************************************/
89 static int
__vers_digit_for_char(char c)90 __vers_digit_for_char(char c)
91 {
92 	switch (c) {
93 	case '0': return 0;
94 	case '1': return 1;
95 	case '2': return 2;
96 	case '3': return 3;
97 	case '4': return 4;
98 	case '5': return 5;
99 	case '6': return 6;
100 	case '7': return 7;
101 	case '8': return 8;
102 	case '9': return 9;
103 	default:  return -1;
104 	}
105 }
106 
107 /*********************************************************************
108 *********************************************************************/
109 static int
__VERS_isreleasestate(char c)110 __VERS_isreleasestate(char c)
111 {
112 	return c == 'd' || c == 'a' || c == 'b' || c == 'f';
113 }
114 
115 
116 /*********************************************************************
117 *********************************************************************/
118 static OSKextVersionStage
__OSKextVersionStageForString(const char ** string_p)119 __OSKextVersionStageForString(const char ** string_p)
120 {
121 	const char * string;
122 
123 	if (!string_p || !*string_p) {
124 		return kOSKextVersionStageInvalid;
125 	}
126 
127 	string = *string_p;
128 
129 	if (__vers_isspace(string[0]) || string[0] == '\0') {
130 		return kOSKextVersionStageRelease;
131 	} else {
132 		switch (string[0]) {
133 		case 'd':
134 			if (__vers_isdigit(string[1])) {
135 				*string_p = &string[1];
136 				return kOSKextVersionStageDevelopment;
137 			}
138 			break;
139 		case 'a':
140 			if (__vers_isdigit(string[1])) {
141 				*string_p = &string[1];
142 				return kOSKextVersionStageAlpha;
143 			}
144 			break;
145 		case 'b':
146 			if (__vers_isdigit(string[1])) {
147 				*string_p = &string[1];
148 				return kOSKextVersionStageBeta;
149 			}
150 			break;
151 		case 'f':
152 			if (__vers_isdigit(string[1])) {
153 				*string_p = &string[1];
154 				return kOSKextVersionStageCandidate;
155 			} else if (string[1] == 'c' && __vers_isdigit(string[2])) {
156 				*string_p = &string[2];
157 				return kOSKextVersionStageCandidate;
158 			} else {
159 				return kOSKextVersionStageInvalid;
160 			}
161 		default:
162 			return kOSKextVersionStageInvalid;
163 		}
164 	}
165 
166 	return kOSKextVersionStageInvalid;
167 }
168 
169 /*********************************************************************
170 *********************************************************************/
171 static const char *
__OSKextVersionStringForStage(OSKextVersion stage)172 __OSKextVersionStringForStage(OSKextVersion stage)
173 {
174 	switch (stage) {
175 	default:
176 		OS_FALLTHROUGH;
177 	case kOSKextVersionStageInvalid:     return NULL;
178 	case kOSKextVersionStageDevelopment: return "d";
179 	case kOSKextVersionStageAlpha:       return "a";
180 	case kOSKextVersionStageBeta:        return "b";
181 	case kOSKextVersionStageCandidate:   return "f";
182 	case kOSKextVersionStageRelease:     return "";
183 	}
184 }
185 
186 /*********************************************************************
187 *********************************************************************/
188 OSKextVersion
OSKextParseVersionString(const char * versionString)189 OSKextParseVersionString(const char * versionString)
190 {
191 	OSKextVersion   result             = -1;
192 	int             vers_digit         = -1;
193 	int             num_digits_scanned = 0;
194 	OSKextVersion   vers_major         = 0;
195 	OSKextVersion   vers_minor         = 0;
196 	OSKextVersion   vers_revision      = 0;
197 	OSKextVersionStage   vers_stage         = 0;
198 	OSKextVersion   vers_stage_level   = 0;
199 	const char    * current_char_p;
200 
201 	if (!versionString || *versionString == '\0') {
202 		return -1;
203 	}
204 
205 	current_char_p = (const char *)&versionString[0];
206 
207 	/*****
208 	 * Check for an initial digit of the major release number.
209 	 */
210 	vers_major = __vers_digit_for_char(*current_char_p);
211 	if (vers_major < 0) {
212 		return -1;
213 	}
214 
215 	current_char_p++;
216 	num_digits_scanned = 1;
217 
218 	/* Complete scan for major version number. Legal characters are
219 	 * any digit, period, any buildstage letter.
220 	 */
221 	while (num_digits_scanned < VERS_MAJOR_DIGITS) {
222 		if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
223 			vers_stage = kOSKextVersionStageRelease;
224 			goto finish;
225 		} else if (__vers_isdigit(*current_char_p)) {
226 			vers_digit = __vers_digit_for_char(*current_char_p);
227 			if (vers_digit < 0) {
228 				return -1;
229 			}
230 			vers_major = (vers_major) * 10 + vers_digit;
231 			current_char_p++;
232 			num_digits_scanned++;
233 		} else if (__VERS_isreleasestate(*current_char_p)) {
234 			goto release_state;
235 		} else if (*current_char_p == '.') {
236 			current_char_p++;
237 			goto minor_version;
238 		} else {
239 			return -1;
240 		}
241 	}
242 
243 	/* Check for too many digits.
244 	 */
245 	if (num_digits_scanned == VERS_MAJOR_DIGITS) {
246 		if (*current_char_p == '.') {
247 			current_char_p++;
248 		} else if (__vers_isdigit(*current_char_p)) {
249 			return -1;
250 		}
251 	}
252 
253 minor_version:
254 
255 	num_digits_scanned = 0;
256 
257 	/* Scan for minor version number. Legal characters are
258 	 * any digit, period, any buildstage letter.
259 	 */
260 	while (num_digits_scanned < VERS_MINOR_DIGITS) {
261 		if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
262 			vers_stage = kOSKextVersionStageRelease;
263 			goto finish;
264 		} else if (__vers_isdigit(*current_char_p)) {
265 			vers_digit = __vers_digit_for_char(*current_char_p);
266 			if (vers_digit < 0) {
267 				return -1;
268 			}
269 			vers_minor = (vers_minor) * 10 + vers_digit;
270 			current_char_p++;
271 			num_digits_scanned++;
272 		} else if (__VERS_isreleasestate(*current_char_p)) {
273 			goto release_state;
274 		} else if (*current_char_p == '.') {
275 			current_char_p++;
276 			goto revision;
277 		} else {
278 			return -1;
279 		}
280 	}
281 
282 	/* Check for too many digits.
283 	 */
284 	if (num_digits_scanned == VERS_MINOR_DIGITS) {
285 		if (*current_char_p == '.') {
286 			current_char_p++;
287 		} else if (__vers_isdigit(*current_char_p)) {
288 			return -1;
289 		}
290 	}
291 
292 revision:
293 
294 	num_digits_scanned = 0;
295 
296 	/* Scan for revision version number. Legal characters are
297 	 * any digit, any buildstage letter (NOT PERIOD).
298 	 */
299 	while (num_digits_scanned < VERS_REVISION_DIGITS) {
300 		if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
301 			vers_stage = kOSKextVersionStageRelease;
302 			goto finish;
303 		} else if (__vers_isdigit(*current_char_p)) {
304 			vers_digit = __vers_digit_for_char(*current_char_p);
305 			if (vers_digit < 0) {
306 				return -1;
307 			}
308 			vers_revision = (vers_revision) * 10 + vers_digit;
309 			current_char_p++;
310 			num_digits_scanned++;
311 		} else if (__VERS_isreleasestate(*current_char_p)) {
312 			goto release_state;
313 		} else {
314 			return -1;
315 		}
316 	}
317 
318 	/* Check for too many digits.
319 	 */
320 	if (num_digits_scanned == VERS_REVISION_DIGITS) {
321 		if (*current_char_p == '.') {
322 			current_char_p++;
323 		} else if (__vers_isdigit(*current_char_p)) {
324 			return -1;
325 		}
326 	}
327 
328 release_state:
329 
330 	/*****
331 	 * Check for the release state.
332 	 */
333 	if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
334 		vers_stage = kOSKextVersionStageRelease;
335 		goto finish;
336 	} else {
337 		vers_stage = __OSKextVersionStageForString(&current_char_p);
338 		if (vers_stage == kOSKextVersionStageInvalid) {
339 			return -1;
340 		}
341 	}
342 
343 
344 // stage level
345 
346 	num_digits_scanned = 0;
347 
348 	/* Scan for stage level number. Legal characters are
349 	 * any digit only.
350 	 */
351 	while (num_digits_scanned < VERS_STAGE_LEVEL_DIGITS) {
352 		if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
353 			if (num_digits_scanned) {
354 				goto finish;
355 			} else {
356 				return -1;
357 			}
358 		} else if (__vers_isdigit(*current_char_p)) {
359 			vers_digit = __vers_digit_for_char(*current_char_p);
360 			if (vers_digit < 0) {
361 				return -1;
362 			}
363 			vers_stage_level = (vers_stage_level) * 10 + vers_digit;
364 			current_char_p++;
365 			num_digits_scanned++;
366 		} else {
367 			return -1;
368 		}
369 	}
370 
371 	/* Check for too many digits.
372 	 */
373 	if ((num_digits_scanned == VERS_STAGE_LEVEL_DIGITS) &&
374 	    !(__vers_isspace(*current_char_p) || (*current_char_p == '\0'))) {
375 		return -1;
376 	}
377 
378 	if (vers_stage_level > VERS_STAGE_LEVEL_MAX) {
379 		return -1;
380 	}
381 
382 finish:
383 
384 	if (vers_stage == kOSKextVersionStageCandidate && vers_stage_level == 0) {
385 		return -1;
386 	}
387 
388 	result = (vers_major * VERS_MAJOR_MULT) +
389 	    (vers_minor * VERS_MINOR_MULT) +
390 	    (vers_revision * VERS_REVISION_MULT) +
391 	    (vers_stage * VERS_STAGE_MULT) +
392 	    vers_stage_level;
393 
394 	return result;
395 }
396 
397 /*********************************************************************
398 * This function must be safe to call in panic context.
399 *********************************************************************/
400 Boolean
OSKextVersionGetString(OSKextVersion aVersion,char * buffer,uint32_t bufferLength)401 OSKextVersionGetString(
402 	OSKextVersion   aVersion,
403 	char          * buffer,
404 	uint32_t        bufferLength)
405 {
406 	int             cpos = 0;
407 	OSKextVersion   vers_major = 0;
408 	OSKextVersion   vers_minor = 0;
409 	OSKextVersion   vers_revision = 0;
410 	OSKextVersion   vers_stage = 0;
411 	OSKextVersion   vers_stage_level = 0;
412 	const char    * stage_string = NULL;// don't free
413 
414 	/* No buffer or length less than longest possible vers string,
415 	 * return 0.
416 	 */
417 	if (!buffer || bufferLength < kOSKextVersionMaxLength) {
418 		return FALSE;
419 	}
420 
421 	bzero(buffer, bufferLength * sizeof(char));
422 
423 	if (aVersion < 0) {
424 		strlcpy(buffer, "(invalid)", bufferLength);
425 		return TRUE;
426 	}
427 	if (aVersion == 0) {
428 		strlcpy(buffer, "(missing)", bufferLength);
429 		return TRUE;
430 	}
431 
432 	vers_major = aVersion / VERS_MAJOR_MULT;
433 	if (vers_major > VERS_MAJOR_MAX) {
434 		strlcpy(buffer, "(invalid)", bufferLength);
435 		return TRUE;
436 	}
437 
438 	vers_minor = aVersion - (vers_major * VERS_MAJOR_MULT);
439 	vers_minor /= VERS_MINOR_MULT;
440 
441 	vers_revision = aVersion -
442 	    ((vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT));
443 	vers_revision /= VERS_REVISION_MULT;
444 
445 	vers_stage = aVersion -
446 	    ((vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
447 	    (vers_revision * VERS_REVISION_MULT));
448 	vers_stage /= VERS_STAGE_MULT;
449 
450 	vers_stage_level = aVersion -
451 	    ((vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
452 	    (vers_revision * VERS_REVISION_MULT) + (vers_stage * VERS_STAGE_MULT));
453 	if (vers_stage_level > VERS_STAGE_LEVEL_MAX) {
454 		strlcpy(buffer, "(invalid)", bufferLength);
455 		return TRUE;
456 	}
457 
458 	cpos = scnprintf(buffer, bufferLength, "%u", (uint32_t)vers_major);
459 
460 	/* Always include the minor version; it just looks weird without.
461 	 */
462 	buffer[cpos] = '.';
463 	cpos++;
464 	cpos += scnprintf(buffer + cpos, bufferLength - cpos, "%u", (uint32_t)vers_minor);
465 
466 	/* The revision is displayed only if nonzero.
467 	 */
468 	if (vers_revision) {
469 		buffer[cpos] = '.';
470 		cpos++;
471 		cpos += scnprintf(buffer + cpos, bufferLength - cpos, "%u",
472 		    (uint32_t)vers_revision);
473 	}
474 
475 	stage_string = __OSKextVersionStringForStage(vers_stage);
476 	if (!stage_string) {
477 		strlcpy(buffer, "(invalid)", bufferLength);
478 		return TRUE;
479 	}
480 	if (stage_string[0]) {
481 		strlcat(buffer, stage_string, bufferLength);
482 		cpos += strlen(stage_string);
483 	}
484 
485 	if (vers_stage < kOSKextVersionStageRelease) {
486 		snprintf(buffer + cpos, bufferLength - cpos, "%u", (uint32_t)vers_stage_level);
487 	}
488 
489 	return TRUE;
490 }
491 
492 /*********************************************************************
493 *********************************************************************/
494 #ifndef KERNEL
495 OSKextVersion
OSKextParseVersionCFString(CFStringRef versionString)496 OSKextParseVersionCFString(CFStringRef versionString)
497 {
498 	OSKextVersion result = -1;
499 	char         versBuffer[kOSKextVersionMaxLength];
500 
501 	if (CFStringGetCString(versionString, versBuffer,
502 	    sizeof(versBuffer), kCFStringEncodingASCII)) {
503 		result = OSKextParseVersionString(versBuffer);
504 	}
505 	return result;
506 }
507 #endif
508