]> git.saurik.com Git - apple/xnu.git/blob - libkern/OSKextVersion.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / libkern / OSKextVersion.c
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
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
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
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
110 __VERS_isreleasestate(char c)
111 {
112 return c == 'd' || c == 'a' || c == 'b' || c == 'f';
113 }
114
115
116 /*********************************************************************
117 *********************************************************************/
118 static OSKextVersionStage
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 *
172 __OSKextVersionStringForStage(OSKextVersionStage stage)
173 {
174 switch (stage) {
175 case kOSKextVersionStageInvalid: return NULL;
176 case kOSKextVersionStageDevelopment: return "d";
177 case kOSKextVersionStageAlpha: return "a";
178 case kOSKextVersionStageBeta: return "b";
179 case kOSKextVersionStageCandidate: return "f";
180 case kOSKextVersionStageRelease: return "";
181 }
182 }
183
184 /*********************************************************************
185 *********************************************************************/
186 OSKextVersion
187 OSKextParseVersionString(const char * versionString)
188 {
189 OSKextVersion result = -1;
190 int vers_digit = -1;
191 int num_digits_scanned = 0;
192 OSKextVersion vers_major = 0;
193 OSKextVersion vers_minor = 0;
194 OSKextVersion vers_revision = 0;
195 OSKextVersion vers_stage = 0;
196 OSKextVersion vers_stage_level = 0;
197 const char * current_char_p;
198
199 if (!versionString || *versionString == '\0') {
200 return -1;
201 }
202
203 current_char_p = (const char *)&versionString[0];
204
205 /*****
206 * Check for an initial digit of the major release number.
207 */
208 vers_major = __vers_digit_for_char(*current_char_p);
209 if (vers_major < 0) {
210 return -1;
211 }
212
213 current_char_p++;
214 num_digits_scanned = 1;
215
216 /* Complete scan for major version number. Legal characters are
217 * any digit, period, any buildstage letter.
218 */
219 while (num_digits_scanned < VERS_MAJOR_DIGITS) {
220 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
221 vers_stage = kOSKextVersionStageRelease;
222 goto finish;
223 } else if (__vers_isdigit(*current_char_p)) {
224 vers_digit = __vers_digit_for_char(*current_char_p);
225 if (vers_digit < 0) {
226 return -1;
227 }
228 vers_major = (vers_major) * 10 + vers_digit;
229 current_char_p++;
230 num_digits_scanned++;
231 } else if (__VERS_isreleasestate(*current_char_p)) {
232 goto release_state;
233 } else if (*current_char_p == '.') {
234 current_char_p++;
235 goto minor_version;
236 } else {
237 return -1;
238 }
239 }
240
241 /* Check for too many digits.
242 */
243 if (num_digits_scanned == VERS_MAJOR_DIGITS) {
244 if (*current_char_p == '.') {
245 current_char_p++;
246 } else if (__vers_isdigit(*current_char_p)) {
247 return -1;
248 }
249 }
250
251 minor_version:
252
253 num_digits_scanned = 0;
254
255 /* Scan for minor version number. Legal characters are
256 * any digit, period, any buildstage letter.
257 */
258 while (num_digits_scanned < VERS_MINOR_DIGITS) {
259 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
260 vers_stage = kOSKextVersionStageRelease;
261 goto finish;
262 } else if (__vers_isdigit(*current_char_p)) {
263 vers_digit = __vers_digit_for_char(*current_char_p);
264 if (vers_digit < 0) {
265 return -1;
266 }
267 vers_minor = (vers_minor) * 10 + vers_digit;
268 current_char_p++;
269 num_digits_scanned++;
270 } else if (__VERS_isreleasestate(*current_char_p)) {
271 goto release_state;
272 } else if (*current_char_p == '.') {
273 current_char_p++;
274 goto revision;
275 } else {
276 return -1;
277 }
278 }
279
280 /* Check for too many digits.
281 */
282 if (num_digits_scanned == VERS_MINOR_DIGITS) {
283 if (*current_char_p == '.') {
284 current_char_p++;
285 } else if (__vers_isdigit(*current_char_p)) {
286 return -1;
287 }
288 }
289
290 revision:
291
292 num_digits_scanned = 0;
293
294 /* Scan for revision version number. Legal characters are
295 * any digit, any buildstage letter (NOT PERIOD).
296 */
297 while (num_digits_scanned < VERS_REVISION_DIGITS) {
298 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
299 vers_stage = kOSKextVersionStageRelease;
300 goto finish;
301 } else if (__vers_isdigit(*current_char_p)) {
302 vers_digit = __vers_digit_for_char(*current_char_p);
303 if (vers_digit < 0) {
304 return -1;
305 }
306 vers_revision = (vers_revision) * 10 + vers_digit;
307 current_char_p++;
308 num_digits_scanned++;
309 } else if (__VERS_isreleasestate(*current_char_p)) {
310 goto release_state;
311 } else {
312 return -1;
313 }
314 }
315
316 /* Check for too many digits.
317 */
318 if (num_digits_scanned == VERS_REVISION_DIGITS) {
319 if (*current_char_p == '.') {
320 current_char_p++;
321 } else if (__vers_isdigit(*current_char_p)) {
322 return -1;
323 }
324 }
325
326 release_state:
327
328 /*****
329 * Check for the release state.
330 */
331 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
332 vers_stage = kOSKextVersionStageRelease;
333 goto finish;
334 } else {
335 vers_stage = __OSKextVersionStageForString(&current_char_p);
336 if (vers_stage == kOSKextVersionStageInvalid) {
337 return -1;
338 }
339 }
340
341
342 // stage level
343
344 num_digits_scanned = 0;
345
346 /* Scan for stage level number. Legal characters are
347 * any digit only.
348 */
349 while (num_digits_scanned < VERS_STAGE_LEVEL_DIGITS) {
350 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
351 if (num_digits_scanned) {
352 goto finish;
353 } else {
354 return -1;
355 }
356 } else if (__vers_isdigit(*current_char_p)) {
357 vers_digit = __vers_digit_for_char(*current_char_p);
358 if (vers_digit < 0) {
359 return -1;
360 }
361 vers_stage_level = (vers_stage_level) * 10 + vers_digit;
362 current_char_p++;
363 num_digits_scanned++;
364 } else {
365 return -1;
366 }
367 }
368
369 /* Check for too many digits.
370 */
371 if ((num_digits_scanned == VERS_STAGE_LEVEL_DIGITS) &&
372 !(__vers_isspace(*current_char_p) || (*current_char_p == '\0'))) {
373 return -1;
374 }
375
376 if (vers_stage_level > VERS_STAGE_LEVEL_MAX) {
377 return -1;
378 }
379
380 finish:
381
382 if (vers_stage == kOSKextVersionStageCandidate && vers_stage_level == 0) {
383 return -1;
384 }
385
386 result = (vers_major * VERS_MAJOR_MULT) +
387 (vers_minor * VERS_MINOR_MULT) +
388 (vers_revision * VERS_REVISION_MULT) +
389 (vers_stage * VERS_STAGE_MULT) +
390 vers_stage_level;
391
392 return result;
393 }
394
395 /*********************************************************************
396 * This function must be safe to call in panic context.
397 *********************************************************************/
398 Boolean
399 OSKextVersionGetString(
400 OSKextVersion aVersion,
401 char * buffer,
402 uint32_t bufferLength)
403 {
404 int cpos = 0;
405 OSKextVersion vers_major = 0;
406 OSKextVersion vers_minor = 0;
407 OSKextVersion vers_revision = 0;
408 OSKextVersion vers_stage = 0;
409 OSKextVersion vers_stage_level = 0;
410 const char * stage_string = NULL;// don't free
411
412 /* No buffer or length less than longest possible vers string,
413 * return 0.
414 */
415 if (!buffer || bufferLength < kOSKextVersionMaxLength) {
416 return FALSE;
417 }
418
419 bzero(buffer, bufferLength * sizeof(char));
420
421 if (aVersion < 0) {
422 strlcpy(buffer, "(invalid)", bufferLength);
423 return TRUE;
424 }
425 if (aVersion == 0) {
426 strlcpy(buffer, "(missing)", bufferLength);
427 return TRUE;
428 }
429
430 vers_major = aVersion / VERS_MAJOR_MULT;
431 if (vers_major > VERS_MAJOR_MAX) {
432 strlcpy(buffer, "(invalid)", bufferLength);
433 return TRUE;
434 }
435
436 vers_minor = aVersion - (vers_major * VERS_MAJOR_MULT);
437 vers_minor /= VERS_MINOR_MULT;
438
439 vers_revision = aVersion -
440 ((vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT));
441 vers_revision /= VERS_REVISION_MULT;
442
443 vers_stage = aVersion -
444 ((vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
445 (vers_revision * VERS_REVISION_MULT));
446 vers_stage /= VERS_STAGE_MULT;
447
448 vers_stage_level = aVersion -
449 ((vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
450 (vers_revision * VERS_REVISION_MULT) + (vers_stage * VERS_STAGE_MULT));
451 if (vers_stage_level > VERS_STAGE_LEVEL_MAX) {
452 strlcpy(buffer, "(invalid)", bufferLength);
453 return TRUE;
454 }
455
456 cpos = scnprintf(buffer, bufferLength, "%u", (uint32_t)vers_major);
457
458 /* Always include the minor version; it just looks weird without.
459 */
460 buffer[cpos] = '.';
461 cpos++;
462 cpos += scnprintf(buffer + cpos, bufferLength - cpos, "%u", (uint32_t)vers_minor);
463
464 /* The revision is displayed only if nonzero.
465 */
466 if (vers_revision) {
467 buffer[cpos] = '.';
468 cpos++;
469 cpos += scnprintf(buffer + cpos, bufferLength - cpos, "%u",
470 (uint32_t)vers_revision);
471 }
472
473 stage_string = __OSKextVersionStringForStage(vers_stage);
474 if (!stage_string) {
475 strlcpy(buffer, "(invalid)", bufferLength);
476 return TRUE;
477 }
478 if (stage_string[0]) {
479 strlcat(buffer, stage_string, bufferLength);
480 cpos += strlen(stage_string);
481 }
482
483 if (vers_stage < kOSKextVersionStageRelease) {
484 snprintf(buffer + cpos, bufferLength - cpos, "%u", (uint32_t)vers_stage_level);
485 }
486
487 return TRUE;
488 }
489
490 /*********************************************************************
491 *********************************************************************/
492 #ifndef KERNEL
493 OSKextVersion
494 OSKextParseVersionCFString(CFStringRef versionString)
495 {
496 OSKextVersion result = -1;
497 char versBuffer[kOSKextVersionMaxLength];
498
499 if (CFStringGetCString(versionString, versBuffer,
500 sizeof(versBuffer), kCFStringEncodingASCII)) {
501 result = OSKextParseVersionString(versBuffer);
502 }
503 return result;
504 }
505 #endif