]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPPath.c
configd-1061.141.1.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCPPath.c
1 /*
2 * Copyright (c) 2000-2006, 2008, 2011, 2012, 2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
29 *
30 * November 16, 2000 Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
34 #include "SCPreferencesInternal.h"
35
36 #define MAXLINKS 8
37
38 static CF_RETURNS_RETAINED CFMutableArrayRef
39 normalizePath(CFStringRef path)
40 {
41 CFMutableArrayRef elements;
42 CFIndex n;
43 CFArrayRef tmpElements;
44
45 if (!isA_CFString(path)) {
46 _SCErrorSet(kSCStatusInvalidArgument);
47 return NULL;
48 }
49
50 if (!CFStringHasPrefix(path, CFSTR("/"))) {
51 /* if no root separator */
52 return NULL;
53 }
54
55 tmpElements = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/"));
56 elements = CFArrayCreateMutableCopy(NULL, 0, tmpElements);
57 CFRelease(tmpElements);
58
59 /* remove empty path components */
60 n = CFArrayGetCount(elements);
61 while (n-- > 0) {
62 CFStringRef pathElement;
63
64 pathElement = CFArrayGetValueAtIndex(elements, n);
65 if (CFStringGetLength(pathElement) == 0) {
66 CFArrayRemoveValueAtIndex(elements, n);
67 }
68 }
69
70 return elements;
71 }
72
73
74 static Boolean
75 getPath(SCPreferencesRef prefs, CFStringRef path, CFDictionaryRef *entity)
76 {
77 CFStringRef element;
78 CFMutableArrayRef elements;
79 CFIndex i;
80 CFStringRef link;
81 CFIndex nElements;
82 CFIndex nLinks = 0;
83 Boolean ok = FALSE;
84 CFDictionaryRef value = NULL;
85
86 elements = normalizePath(path);
87 if (elements == NULL) {
88 _SCErrorSet(kSCStatusNoKey);
89 return FALSE;
90 }
91
92 restart :
93
94 nElements = CFArrayGetCount(elements);
95
96 if (nElements < 1) {
97 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
98
99 __SCPreferencesAccess(prefs);
100
101 *entity = prefsPrivate->prefs;
102 ok = TRUE;
103 goto done;
104 }
105
106 for (i = 0; i < nElements; i++) {
107 element = CFArrayGetValueAtIndex(elements, i);
108 if (i == 0) {
109 value = SCPreferencesGetValue(prefs, CFArrayGetValueAtIndex(elements, 0));
110 } else {
111 value = CFDictionaryGetValue(value, element);
112 }
113 if (value == NULL) {
114 /* if path component does not exist */
115 _SCErrorSet(kSCStatusNoKey);
116 goto done;
117 }
118
119 if (!isA_CFDictionary(value)) {
120 /* if path component not a dictionary */
121 _SCErrorSet(kSCStatusNoKey);
122 goto done;
123 }
124
125 if ((i < nElements - 1) &&
126 CFDictionaryGetValueIfPresent(value, kSCResvLink, (const void **)&link)) {
127 /*
128 * if not the last path component and this
129 * element is a link
130 */
131 CFMutableArrayRef linkElements;
132
133 if (++nLinks > MAXLINKS) {
134 /* if we are chasing our tail */
135 _SCErrorSet(kSCStatusMaxLink);
136 goto done;
137 }
138
139 linkElements = normalizePath(link);
140 if (linkElements == NULL) {
141 /* if the link is bad */
142 _SCErrorSet(kSCStatusNoKey);
143 goto done;
144 }
145
146 CFArrayAppendArray(linkElements,
147 elements,
148 CFRangeMake(i + 1, nElements-i - 1));
149 CFRelease(elements);
150 elements = linkElements;
151
152 goto restart;
153 }
154 }
155
156 *entity = value;
157 ok = TRUE;
158
159 done :
160
161 CFRelease(elements);
162 return ok;
163 }
164
165
166 static Boolean
167 setPath(SCPreferencesRef prefs, CFStringRef path, CFDictionaryRef entity)
168 {
169 CFStringRef element;
170 CFMutableArrayRef elements;
171 CFIndex i;
172 CFStringRef link;
173 CFIndex nElements;
174 CFIndex nLinks = 0;
175 CFDictionaryRef newEntity = NULL;
176 CFDictionaryRef node = NULL;
177 CFMutableArrayRef nodes = NULL;
178 Boolean ok = FALSE;
179
180 if ((entity != NULL) && !isA_CFDictionary(entity)) {
181 _SCErrorSet(kSCStatusInvalidArgument);
182 return FALSE;
183 }
184
185 elements = normalizePath(path);
186 if (elements == NULL) {
187 _SCErrorSet(kSCStatusNoKey);
188 return FALSE;
189 }
190
191 restart :
192
193 nElements = CFArrayGetCount(elements);
194
195 if (nElements < 1) {
196 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
197
198 __SCPreferencesAccess(prefs);
199
200 if (prefsPrivate->prefs != NULL) {
201 CFRelease(prefsPrivate->prefs);
202 }
203
204 if (entity == NULL) {
205 prefsPrivate->prefs = CFDictionaryCreateMutable(NULL,
206 0,
207 &kCFTypeDictionaryKeyCallBacks,
208 &kCFTypeDictionaryValueCallBacks);
209 } else {
210 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, entity);
211 }
212
213 prefsPrivate->changed = TRUE;
214 ok = TRUE;
215 goto done;
216 }
217
218 nodes = CFArrayCreateMutable(NULL, nElements - 1, &kCFTypeArrayCallBacks);
219 for (i = 0; i < nElements - 1; i++) {
220 element = CFArrayGetValueAtIndex(elements, i);
221 if (i == 0) {
222 node = SCPreferencesGetValue(prefs, element);
223 } else {
224 node = CFDictionaryGetValue(node, element);
225
226 }
227
228 if (node) {
229 /* if path component exists */
230 CFArrayAppendValue(nodes, node);
231 } else {
232 /* if path component does not exist */
233 node = CFDictionaryCreate(NULL,
234 NULL,
235 NULL,
236 0,
237 &kCFTypeDictionaryKeyCallBacks,
238 &kCFTypeDictionaryValueCallBacks);
239 CFArrayAppendValue(nodes, node);
240 CFRelease(node);
241 }
242
243 if (!isA_CFDictionary(node)) {
244 _SCErrorSet(kSCStatusNoKey);
245 goto done;
246 }
247
248 if ((i < nElements - 1) &&
249 CFDictionaryGetValueIfPresent(node, kSCResvLink, (const void **)&link)) {
250 /*
251 * if not the last path component and this
252 * element is a link
253 */
254 CFMutableArrayRef linkElements;
255
256 if (++nLinks > MAXLINKS) {
257 /* if we are chasing our tail */
258 _SCErrorSet(kSCStatusMaxLink);
259 goto done;
260 }
261
262 linkElements = normalizePath(link);
263 if (linkElements == NULL) {
264 /* if the link is bad */
265 _SCErrorSet(kSCStatusNoKey);
266 goto done;
267 }
268
269 CFArrayAppendArray(linkElements,
270 elements,
271 CFRangeMake(i + 1, nElements-i - 1));
272 CFRelease(elements);
273 elements = linkElements;
274
275 CFRelease(nodes);
276 nodes = NULL;
277 goto restart;
278 }
279 }
280
281 /*
282 * make sure that the last component doesn't step on top
283 * of a non-dictionary component.
284 */
285 element = CFArrayGetValueAtIndex(elements, nElements - 1);
286 if (nElements > 1) {
287 node = CFArrayGetValueAtIndex(nodes, nElements - 2);
288 node = CFDictionaryGetValue(node, element);
289 } else {
290 node = SCPreferencesGetValue(prefs, element);
291 }
292 if ((node != NULL) && !isA_CFDictionary(node)) {
293 // we won't step on a non-dictionary component
294 _SCErrorSet(kSCStatusInvalidArgument);
295 goto done;
296 }
297
298 if (entity != NULL) {
299 newEntity = CFRetain(entity);
300 }
301 for (i = nElements - 1; i >= 0; i--) {
302 element = CFArrayGetValueAtIndex(elements, i);
303 if (i == 0) {
304 if (newEntity != NULL) {
305 ok = SCPreferencesSetValue(prefs, element, newEntity);
306 } else {
307 ok = SCPreferencesRemoveValue(prefs, element);
308 }
309 } else {
310 CFMutableDictionaryRef newNode;
311
312 node = CFArrayGetValueAtIndex(nodes, i - 1);
313 newNode = CFDictionaryCreateMutableCopy(NULL, 0, node);
314 if (newEntity != NULL) {
315 CFDictionarySetValue(newNode, element, newEntity);
316 CFRelease(newEntity);
317 } else {
318 CFDictionaryRemoveValue(newNode, element);
319 if (CFDictionaryGetCount(newNode) == 0) {
320 // prune the (now empty) parent
321 CFRelease(newNode);
322 newNode = NULL;
323 }
324 }
325 newEntity = newNode;
326 }
327 }
328 if (newEntity != NULL) {
329 CFRelease(newEntity);
330 }
331
332 done :
333
334 if (nodes != NULL) CFRelease(nodes);
335 CFRelease(elements);
336 return ok;
337 }
338
339
340 CFStringRef
341 SCPreferencesPathCreateUniqueChild(SCPreferencesRef prefs,
342 CFStringRef prefix)
343 {
344 CFStringRef child;
345 CFStringRef newPath = NULL;
346 CFDictionaryRef newDict;
347 CFUUIDRef uuid;
348 CFDictionaryRef entity;
349
350 if (prefs == NULL) {
351 /* sorry, you must provide a session */
352 _SCErrorSet(kSCStatusNoPrefsSession);
353 return NULL;
354 }
355
356 if (getPath(prefs, prefix, &entity)) {
357 // if prefix path exists
358 if (CFDictionaryContainsKey(entity, kSCResvLink)) {
359 /* the path is a link... */
360 _SCErrorSet(kSCStatusFailed);
361 return NULL;
362 }
363 } else if (SCError() != kSCStatusNoKey) {
364 // if any error except for a missing prefix path component
365 return NULL;
366 }
367
368 uuid = CFUUIDCreate(NULL);
369 child = CFUUIDCreateString(NULL, uuid);
370 newPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), prefix, child);
371 CFRelease(child);
372 CFRelease(uuid);
373
374 /* save a new/empty dictionary */
375 newDict = CFDictionaryCreate(NULL,
376 NULL, NULL, 0,
377 &kCFTypeDictionaryKeyCallBacks,
378 &kCFTypeDictionaryValueCallBacks);
379 assert(newDict != NULL);
380
381 if (!setPath(prefs, newPath, newDict)) {
382 CFRelease(newPath);
383 newPath = NULL;
384 }
385 CFRelease(newDict);
386
387 return newPath;
388 }
389
390
391 CFDictionaryRef
392 SCPreferencesPathGetValue(SCPreferencesRef prefs,
393 CFStringRef path)
394 {
395 CFDictionaryRef entity;
396 CFStringRef entityLink;
397
398 if (prefs == NULL) {
399 /* sorry, you must provide a session */
400 _SCErrorSet(kSCStatusNoPrefsSession);
401 return NULL;
402 }
403
404 if (!getPath(prefs, path, &entity)) {
405 return NULL;
406 }
407
408 if (isA_CFDictionary(entity) &&
409 (CFDictionaryGetValueIfPresent(entity, kSCResvLink, (const void **)&entityLink))) {
410 /* if this is a dictionary AND it is a link */
411 if (!getPath(prefs, entityLink, &entity)) {
412 /* if it was a bad link */
413 return NULL;
414 }
415 }
416
417 return entity;
418 }
419
420
421 CFStringRef
422 SCPreferencesPathGetLink(SCPreferencesRef prefs,
423 CFStringRef path)
424 {
425 CFDictionaryRef entity;
426 CFStringRef entityLink;
427
428 if (prefs == NULL) {
429 /* sorry, you must provide a session */
430 _SCErrorSet(kSCStatusNoPrefsSession);
431 return NULL;
432 }
433
434 if (!getPath(prefs, path, &entity)) {
435 return NULL;
436 }
437
438 if (isA_CFDictionary(entity) &&
439 (CFDictionaryGetValueIfPresent(entity, kSCResvLink, (const void **)&entityLink))) {
440 /* if this is a dictionary AND it is a link */
441 return entityLink;
442 }
443
444 return NULL;
445 }
446
447
448 Boolean
449 SCPreferencesPathSetValue(SCPreferencesRef prefs,
450 CFStringRef path,
451 CFDictionaryRef value)
452 {
453 Boolean ok;
454
455 if (prefs == NULL) {
456 /* sorry, you must provide a session */
457 _SCErrorSet(kSCStatusNoPrefsSession);
458 return FALSE;
459 }
460
461 #define NETPREF_NEEDS_REPAIR
462 #ifdef NETPREF_NEEDS_REPAIR
463 if (CFEqual(path, CFSTR("/CurrentSet")) && isA_CFString(value)) {
464 static Boolean warned = FALSE;
465 if (!warned) {
466 SCPrint(TRUE, stderr, CFSTR("SCPreferencesPathSetValue(, %@, ) called with non-dictionary value\n"), path);
467 warned = TRUE;
468 }
469 return SCPreferencesSetValue(prefs, CFSTR("CurrentSet"), value);
470 }
471 #endif // NETPREF_NEEDS_REPAIR
472
473 if (!isA_CFDictionary(value)) {
474 #ifdef NETPREF_NEEDS_REPAIR
475 SCPrint(TRUE, stderr, CFSTR("SCPreferencesPathSetValue(, %@, ) called with non-dictionary value\n"), path);
476 #endif // NETPREF_NEEDS_REPAIR
477 _SCErrorSet(kSCStatusInvalidArgument);
478 return FALSE;
479 }
480
481 ok = setPath(prefs, path, value);
482 return ok;
483 }
484
485
486 Boolean
487 SCPreferencesPathSetLink(SCPreferencesRef prefs,
488 CFStringRef path,
489 CFStringRef link)
490 {
491 CFMutableDictionaryRef dict;
492 CFDictionaryRef entity;
493 Boolean ok;
494
495 if (prefs == NULL) {
496 /* sorry, you must provide a session */
497 _SCErrorSet(kSCStatusNoPrefsSession);
498 return FALSE;
499 }
500
501 if (!isA_CFString(link)) {
502 _SCErrorSet(kSCStatusInvalidArgument);
503 return FALSE;
504 }
505
506 if (!getPath(prefs, link, &entity)) {
507 // if bad link
508 return FALSE;
509 }
510
511 dict = CFDictionaryCreateMutable(NULL,
512 0,
513 &kCFTypeDictionaryKeyCallBacks,
514 &kCFTypeDictionaryValueCallBacks);
515 CFDictionarySetValue(dict, kSCResvLink, link);
516 ok = setPath(prefs, path, dict);
517 CFRelease(dict);
518
519 return ok;
520 }
521
522
523 Boolean
524 SCPreferencesPathRemoveValue(SCPreferencesRef prefs,
525 CFStringRef path)
526 {
527 CFMutableArrayRef elements = NULL;
528 Boolean ok = FALSE;
529 CFDictionaryRef value;
530
531 if (prefs == NULL) {
532 /* sorry, you must provide a session */
533 _SCErrorSet(kSCStatusNoPrefsSession);
534 return FALSE;
535 }
536
537 if (!getPath(prefs, path, &value)) {
538 // if no such path
539 return FALSE;
540 }
541
542 elements = normalizePath(path);
543 if (elements == NULL) {
544 _SCErrorSet(kSCStatusNoKey);
545 return FALSE;
546 }
547
548 ok = setPath(prefs, path, NULL);
549
550 CFRelease(elements);
551 return ok;
552 }