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
55typedef 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*********************************************************************/
67static int __vers_isdigit(char c) {
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
74/*********************************************************************
75*********************************************************************/
76static int __vers_isspace(char c) {
77 return (c == ' ' ||
78 c == '\t' ||
79 c == '\r' ||
80 c == '\n');
81}
82
83/*********************************************************************
84*********************************************************************/
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 }
100}
101
102/*********************************************************************
103*********************************************************************/
104static int __VERS_isreleasestate(char c) {
105 return (c == 'd' || c == 'a' || c == 'b' || c == 'f');
106}
107
108
109/*********************************************************************
110*********************************************************************/
111static OSKextVersionStage __OSKextVersionStageForString(const char ** string_p) {
112 const char * string;
113
114 if (!string_p || !*string_p) {
115 return kOSKextVersionStageInvalid;
116 }
117
118 string = *string_p;
119
120 if (__vers_isspace(string[0]) || string[0] == '\0') {
121 return kOSKextVersionStageRelease;
122 } else {
123 switch (string[0]) {
124 case 'd':
125 if (__vers_isdigit(string[1])) {
126 *string_p = &string[1];
127 return kOSKextVersionStageDevelopment;
128 }
129 break;
130 case 'a':
131 if (__vers_isdigit(string[1])) {
132 *string_p = &string[1];
133 return kOSKextVersionStageAlpha;
134 }
135 break;
136 case 'b':
137 if (__vers_isdigit(string[1])) {
138 *string_p = &string[1];
139 return kOSKextVersionStageBeta;
140 }
141 break;
142 case 'f':
143 if (__vers_isdigit(string[1])) {
144 *string_p = &string[1];
145 return kOSKextVersionStageCandidate;
146 } else if (string[1] == 'c' && __vers_isdigit(string[2])) {
147 *string_p = &string[2];
148 return kOSKextVersionStageCandidate;
149 } else {
150 return kOSKextVersionStageInvalid;
151 }
152 default:
153 return kOSKextVersionStageInvalid;
154 }
155 }
156
157 return kOSKextVersionStageInvalid;
158}
159
160/*********************************************************************
161*********************************************************************/
162static const char *
163__OSKextVersionStringForStage(OSKextVersionStage stage)
164{
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 }
173}
174
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') {
190 return -1;
191 }
192
193 current_char_p = (const char *)&versionString[0];
194
195 /*****
196 * Check for an initial digit of the major release number.
197 */
198 vers_major = __vers_digit_for_char(*current_char_p);
199 if (vers_major < 0) {
200 return -1;
201 }
202
203 current_char_p++;
204 num_digits_scanned = 1;
205
206 /* Complete scan for major version number. Legal characters are
207 * any digit, period, any buildstage letter.
208 */
209 while (num_digits_scanned < VERS_MAJOR_DIGITS) {
210 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
211 vers_stage = kOSKextVersionStageRelease;
212 goto finish;
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)) {
222 goto release_state;
223 } else if (*current_char_p == '.') {
224 current_char_p++;
225 goto minor_version;
226 } else {
227 return -1;
228 }
229 }
230
231 /* Check for too many digits.
232 */
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;
238 }
239 }
240
241minor_version:
242
243 num_digits_scanned = 0;
244
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') {
250 vers_stage = kOSKextVersionStageRelease;
251 goto finish;
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)) {
261 goto release_state;
262 } else if (*current_char_p == '.') {
263 current_char_p++;
264 goto revision;
265 } else {
266 return -1;
267 }
268 }
269
270 /* Check for too many digits.
271 */
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;
277 }
278 }
279
280revision:
281
282 num_digits_scanned = 0;
283
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') {
289 vers_stage = kOSKextVersionStageRelease;
290 goto finish;
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)) {
300 goto release_state;
301 } else {
302 return -1;
303 }
304 }
305
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 }
315
316release_state:
317
318 /*****
319 * Check for the release state.
320 */
321 if (__vers_isspace(*current_char_p) || *current_char_p == '\0') {
322 vers_stage = kOSKextVersionStageRelease;
323 goto finish;
324 } else {
325 vers_stage = __OSKextVersionStageForString(&current_char_p);
326 if (vers_stage == kOSKextVersionStageInvalid) {
327 return -1;
328 }
329 }
330
331
332// stage level
333
334 num_digits_scanned = 0;
335
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;
343 } else {
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;
350 }
351 vers_stage_level = (vers_stage_level) * 10 + vers_digit;
352 current_char_p++;
353 num_digits_scanned++;
354 } else {
355 return -1;
356 }
357 }
358
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'))) {
363
364 return -1;
365 }
366
367 if (vers_stage_level > VERS_STAGE_LEVEL_MAX) {
368 return -1;
369 }
370
371finish:
372
373 if (vers_stage == kOSKextVersionStageCandidate && vers_stage_level == 0) {
374 return -1;
375 }
376
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;
382
383 return result;
384}
385
386/*********************************************************************
387* This function must be safe to call in panic context.
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
401
402 /* No buffer or length less than longest possible vers string,
403 * return 0.
404 */
405 if (!buffer || bufferLength < kOSKextVersionMaxLength) {
406 return FALSE;
407 }
408
409 bzero(buffer, bufferLength * sizeof(char));
410
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;
418 }
419
420 vers_major = aVersion / VERS_MAJOR_MULT;
421 if (vers_major > VERS_MAJOR_MAX) {
422 strlcpy(buffer, "(invalid)", bufferLength);
423 return TRUE;
424 }
425
426 vers_minor = aVersion - (vers_major * VERS_MAJOR_MULT);
427 vers_minor /= VERS_MINOR_MULT;
428
429 vers_revision = aVersion -
430 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) );
431 vers_revision /= VERS_REVISION_MULT;
432
433 vers_stage = aVersion -
434 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
435 (vers_revision * VERS_REVISION_MULT));
436 vers_stage /= VERS_STAGE_MULT;
437
438 vers_stage_level = aVersion -
439 ( (vers_major * VERS_MAJOR_MULT) + (vers_minor * VERS_MINOR_MULT) +
440 (vers_revision * VERS_REVISION_MULT) + (vers_stage * VERS_STAGE_MULT));
441 if (vers_stage_level > VERS_STAGE_LEVEL_MAX) {
442 strlcpy(buffer, "(invalid)", bufferLength);
443 return TRUE;
444 }
445
446 cpos = snprintf(buffer, bufferLength, "%u", (uint32_t)vers_major);
447
448 /* Always include the minor version; it just looks weird without.
449 */
450 buffer[cpos] = '.';
451 cpos++;
452 cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_minor);
453
454 /* The revision is displayed only if nonzero.
455 */
456 if (vers_revision) {
457 buffer[cpos] = '.';
458 cpos++;
459 cpos += snprintf(buffer+cpos, bufferLength - cpos, "%u",
460 (uint32_t)vers_revision);
461 }
462
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);
470 cpos += strlen(stage_string);
471 }
472
473 if (vers_stage < kOSKextVersionStageRelease) {
474 snprintf(buffer+cpos, bufferLength - cpos, "%u", (uint32_t)vers_stage_level);
475 }
476
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;
494}
495#endif
496