]> git.saurik.com Git - apple/xnu.git/blame - libkern/OSKextVersion.c
xnu-6153.121.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@
0a7de745 5 *
2d21ac55
A
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.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
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.
0a7de745 25 *
2d21ac55
A
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)
d9a64523
A
41#define VERS_MINOR_DIGITS (4)
42#define VERS_REVISION_DIGITS (4)
55e303ae
A
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
d9a64523
A
49#define VERS_MAJOR_MULT (1000000000000)
50#define VERS_MINOR_MULT (100000000)
51#define VERS_REVISION_MULT (10000)
52#define VERS_STAGE_MULT (1000)
55e303ae 53
b0d623f7 54
55e303ae 55typedef enum {
0a7de745
A
56 kOSKextVersionStageInvalid = 0,
57 kOSKextVersionStageDevelopment = 1,
58 kOSKextVersionStageAlpha = 3,
59 kOSKextVersionStageBeta = 5,
60 kOSKextVersionStageCandidate = 7,
61 kOSKextVersionStageRelease = 9,
b0d623f7 62} OSKextVersionStage;
55e303ae
A
63
64
b0d623f7
A
65/*********************************************************************
66*********************************************************************/
0a7de745
A
67static 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';
1c79356b
A
74}
75
b0d623f7
A
76/*********************************************************************
77*********************************************************************/
0a7de745
A
78static int
79__vers_isspace(char c)
80{
81 return c == ' ' ||
82 c == '\t' ||
83 c == '\r' ||
84 c == '\n';
1c79356b
A
85}
86
b0d623f7
A
87/*********************************************************************
88*********************************************************************/
39037602 89static int
0a7de745
A
90__vers_digit_for_char(char c)
91{
39037602 92 switch (c) {
0a7de745
A
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;
39037602 104 }
55e303ae 105}
1c79356b 106
b0d623f7
A
107/*********************************************************************
108*********************************************************************/
0a7de745
A
109static int
110__VERS_isreleasestate(char c)
111{
112 return c == 'd' || c == 'a' || c == 'b' || c == 'f';
1c79356b
A
113}
114
115
b0d623f7
A
116/*********************************************************************
117*********************************************************************/
0a7de745
A
118static 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;
1c79356b
A
167}
168
b0d623f7
A
169/*********************************************************************
170*********************************************************************/
39037602
A
171static const char *
172__OSKextVersionStringForStage(OSKextVersionStage stage)
b0d623f7 173{
39037602 174 switch (stage) {
0a7de745
A
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 "";
39037602 181 }
55e303ae
A
182}
183
b0d623f7
A
184/*********************************************************************
185*********************************************************************/
0a7de745
A
186OSKextVersion
187OSKextParseVersionString(const char * versionString)
b0d623f7 188{
0a7de745
A
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 }
1c79356b 250
55e303ae 251minor_version:
1c79356b 252
0a7de745
A
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 }
1c79356b 289
55e303ae 290revision:
1c79356b 291
0a7de745
A
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 }
1c79356b
A
325
326release_state:
327
0a7de745
A
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 }
1c79356b
A
340
341
55e303ae 342// stage level
1c79356b 343
0a7de745
A
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 }
1c79356b 379
55e303ae 380finish:
1c79356b 381
0a7de745
A
382 if (vers_stage == kOSKextVersionStageCandidate && vers_stage_level == 0) {
383 return -1;
384 }
1c79356b 385
0a7de745
A
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;
1c79356b 391
0a7de745 392 return result;
55e303ae
A
393}
394
b0d623f7 395/*********************************************************************
6d2010ae 396* This function must be safe to call in panic context.
b0d623f7 397*********************************************************************/
0a7de745
A
398Boolean
399OSKextVersionGetString(
400 OSKextVersion aVersion,
401 char * buffer,
402 uint32_t bufferLength)
b0d623f7 403{
0a7de745
A
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
4ba76501 456 cpos = scnprintf(buffer, bufferLength, "%u", (uint32_t)vers_major);
0a7de745
A
457
458 /* Always include the minor version; it just looks weird without.
459 */
460 buffer[cpos] = '.';
461 cpos++;
4ba76501 462 cpos += scnprintf(buffer + cpos, bufferLength - cpos, "%u", (uint32_t)vers_minor);
0a7de745
A
463
464 /* The revision is displayed only if nonzero.
465 */
466 if (vers_revision) {
467 buffer[cpos] = '.';
468 cpos++;
4ba76501 469 cpos += scnprintf(buffer + cpos, bufferLength - cpos, "%u",
0a7de745
A
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;
b0d623f7
A
488}
489
490/*********************************************************************
491*********************************************************************/
492#ifndef KERNEL
0a7de745
A
493OSKextVersion
494OSKextParseVersionCFString(CFStringRef versionString)
b0d623f7 495{
0a7de745
A
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;
1c79356b 504}
b0d623f7 505#endif