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
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*********************************************************************/
78static int
79__vers_isspace(char c)
80{
81 return c == ' ' ||
82 c == '\t' ||
83 c == '\r' ||
84 c == '\n';
85}
86
87/*********************************************************************
88*********************************************************************/
89static 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*********************************************************************/
109static int
110__VERS_isreleasestate(char c)
111{
112 return c == 'd' || c == 'a' || c == 'b' || c == 'f';
113}
114
115
116/*********************************************************************
117*********************************************************************/
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(c: string[0]) || string[0] == '\0') {
130 return kOSKextVersionStageRelease;
131 } else {
132 switch (string[0]) {
133 case 'd':
134 if (__vers_isdigit(c: string[1])) {
135 *string_p = &string[1];
136 return kOSKextVersionStageDevelopment;
137 }
138 break;
139 case 'a':
140 if (__vers_isdigit(c: string[1])) {
141 *string_p = &string[1];
142 return kOSKextVersionStageAlpha;
143 }
144 break;
145 case 'b':
146 if (__vers_isdigit(c: string[1])) {
147 *string_p = &string[1];
148 return kOSKextVersionStageBeta;
149 }
150 break;
151 case 'f':
152 if (__vers_isdigit(c: string[1])) {
153 *string_p = &string[1];
154 return kOSKextVersionStageCandidate;
155 } else if (string[1] == 'c' && __vers_isdigit(c: 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*********************************************************************/
171static const char *
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*********************************************************************/
188OSKextVersion
189OSKextParseVersionString(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(c: *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(c: *current_char_p) || *current_char_p == '\0') {
223 vers_stage = kOSKextVersionStageRelease;
224 goto finish;
225 } else if (__vers_isdigit(c: *current_char_p)) {
226 vers_digit = __vers_digit_for_char(c: *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(c: *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(c: *current_char_p)) {
249 return -1;
250 }
251 }
252
253minor_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(c: *current_char_p) || *current_char_p == '\0') {
262 vers_stage = kOSKextVersionStageRelease;
263 goto finish;
264 } else if (__vers_isdigit(c: *current_char_p)) {
265 vers_digit = __vers_digit_for_char(c: *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(c: *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(c: *current_char_p)) {
288 return -1;
289 }
290 }
291
292revision:
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(c: *current_char_p) || *current_char_p == '\0') {
301 vers_stage = kOSKextVersionStageRelease;
302 goto finish;
303 } else if (__vers_isdigit(c: *current_char_p)) {
304 vers_digit = __vers_digit_for_char(c: *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(c: *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(c: *current_char_p)) {
324 return -1;
325 }
326 }
327
328release_state:
329
330 /*****
331 * Check for the release state.
332 */
333 if (__vers_isspace(c: *current_char_p) || *current_char_p == '\0') {
334 vers_stage = kOSKextVersionStageRelease;
335 goto finish;
336 } else {
337 vers_stage = __OSKextVersionStageForString(string_p: &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(c: *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(c: *current_char_p)) {
359 vers_digit = __vers_digit_for_char(c: *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(c: *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
382finish:
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*********************************************************************/
400Boolean
401OSKextVersionGetString(
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(s: buffer, n: bufferLength * sizeof(char));
422
423 if (aVersion < 0) {
424 strlcpy(dst: buffer, src: "(invalid)", n: bufferLength);
425 return TRUE;
426 }
427 if (aVersion == 0) {
428 strlcpy(dst: buffer, src: "(missing)", n: bufferLength);
429 return TRUE;
430 }
431
432 vers_major = aVersion / VERS_MAJOR_MULT;
433 if (vers_major > VERS_MAJOR_MAX) {
434 strlcpy(dst: buffer, src: "(invalid)", n: 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(dst: buffer, src: "(invalid)", n: bufferLength);
455 return TRUE;
456 }
457
458 cpos = scnprintf(buffer, count: 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, count: 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, count: bufferLength - cpos, "%u",
472 (uint32_t)vers_revision);
473 }
474
475 stage_string = __OSKextVersionStringForStage(stage: vers_stage);
476 if (!stage_string) {
477 strlcpy(dst: buffer, src: "(invalid)", n: bufferLength);
478 return TRUE;
479 }
480 if (stage_string[0]) {
481 strlcat(dst: buffer, src: stage_string, n: bufferLength);
482 cpos += strlen(s: stage_string);
483 }
484
485 if (vers_stage < kOSKextVersionStageRelease) {
486 snprintf(buffer + cpos, count: bufferLength - cpos, "%u", (uint32_t)vers_stage_level);
487 }
488
489 return TRUE;
490}
491
492/*********************************************************************
493*********************************************************************/
494#ifndef KERNEL
495OSKextVersion
496OSKextParseVersionCFString(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