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