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