]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPPath.c
configd-136.2.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCPPath.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, 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 CFArrayRef
42 normalizePath(CFStringRef path)
43 {
44 CFArrayRef tmpElements;
45 CFMutableArrayRef elements;
46 CFIndex nElements;
47 CFIndex i;
48
49 if (!CFStringHasPrefix(path, CFSTR("/"))) {
50 /* if no root separator */
51 return NULL;
52 }
53
54 tmpElements = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/"));
55 elements = CFArrayCreateMutableCopy(NULL, 0, tmpElements);
56 CFRelease(tmpElements);
57
58 /* remove empty path components */
59 nElements = CFArrayGetCount(elements);
60 for (i = nElements; i > 0; i--) {
61 CFStringRef pathElement;
62
63 pathElement = CFArrayGetValueAtIndex(elements, i - 1);
64 if (CFStringGetLength(pathElement) == 0) {
65 CFArrayRemoveValueAtIndex(elements, i - 1);
66 nElements--;
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 CFArrayRef 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 value = prefsPrivate->prefs;
101 }
102
103 for (i = 0; i < nElements; i++) {
104 element = CFArrayGetValueAtIndex(elements, i);
105 if (i == 0) {
106 value = SCPreferencesGetValue(prefs, CFArrayGetValueAtIndex(elements, 0));
107 } else {
108 value = CFDictionaryGetValue(value, element);
109 }
110 if (value == NULL) {
111 /* if path component does not exist */
112 _SCErrorSet(kSCStatusNoKey);
113 goto done;
114 }
115
116 if (!isA_CFDictionary(value)) {
117 /* if path component not a dictionary */
118 _SCErrorSet(kSCStatusNoKey);
119 goto done;
120 }
121
122 if ((i < nElements - 1) &&
123 CFDictionaryGetValueIfPresent(value, kSCResvLink, (const void **)&link)) {
124 /*
125 * if not the last path component and this
126 * element is a link
127 */
128 CFArrayRef linkElements;
129 CFMutableArrayRef newElements;
130
131 if (++nLinks > MAXLINKS) {
132 /* if we are chasing our tail */
133 _SCErrorSet(kSCStatusMaxLink);
134 goto done;
135 }
136
137 linkElements = normalizePath(link);
138 if (linkElements == NULL) {
139 /* if the link is bad */
140 _SCErrorSet(kSCStatusNoKey);
141 goto done;
142 }
143
144 newElements = CFArrayCreateMutableCopy(NULL, 0, linkElements);
145 CFArrayAppendArray(newElements,
146 elements,
147 CFRangeMake(i + 1, nElements-i - 1));
148 CFRelease(elements);
149 elements = newElements;
150
151 goto restart;
152 }
153 }
154
155 *entity = value;
156 ok = TRUE;
157
158 done :
159
160 CFRelease(elements);
161 return ok;
162 }
163
164
165 static Boolean
166 setPath(SCPreferencesRef prefs, CFStringRef path, CFDictionaryRef entity)
167 {
168 CFStringRef element;
169 CFArrayRef elements;
170 CFIndex i;
171 CFStringRef link;
172 CFIndex nElements;
173 CFIndex nLinks = 0;
174 CFDictionaryRef newEntity = NULL;
175 CFDictionaryRef node = NULL;
176 CFMutableArrayRef nodes = NULL;
177 Boolean ok = FALSE;
178
179 if ((entity != NULL) && !isA_CFDictionary(entity)) {
180 _SCErrorSet(kSCStatusInvalidArgument);
181 return FALSE;
182 }
183
184 elements = normalizePath(path);
185 if (elements == NULL) {
186 _SCErrorSet(kSCStatusNoKey);
187 return FALSE;
188 }
189
190 restart :
191
192 nElements = CFArrayGetCount(elements);
193
194 if (nElements < 1) {
195 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
196
197 __SCPreferencesAccess(prefs);
198
199 if (prefsPrivate->prefs != NULL) {
200 CFRelease(prefsPrivate->prefs);
201 }
202
203 if (entity == NULL) {
204 prefsPrivate->prefs = CFDictionaryCreateMutable(NULL,
205 0,
206 &kCFTypeDictionaryKeyCallBacks,
207 &kCFTypeDictionaryValueCallBacks);
208 } else {
209 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, entity);
210 }
211
212 prefsPrivate->changed = TRUE;
213 ok = TRUE;
214 goto done;
215 }
216
217 nodes = CFArrayCreateMutable(NULL, nElements - 1, &kCFTypeArrayCallBacks);
218 for (i = 0; i < nElements - 1; i++) {
219 element = CFArrayGetValueAtIndex(elements, i);
220 if (i == 0) {
221 node = SCPreferencesGetValue(prefs, element);
222 } else {
223 node = CFDictionaryGetValue(node, element);
224
225 }
226
227 if (node) {
228 /* if path component exists */
229 CFArrayAppendValue(nodes, node);
230 } else {
231 /* if path component does not exist */
232 node = CFDictionaryCreate(NULL,
233 NULL,
234 NULL,
235 0,
236 &kCFTypeDictionaryKeyCallBacks,
237 &kCFTypeDictionaryValueCallBacks);
238 CFArrayAppendValue(nodes, node);
239 CFRelease(node);
240 }
241
242 if (!isA_CFDictionary(node)) {
243 _SCErrorSet(kSCStatusNoKey);
244 goto done;
245 }
246
247 if ((i < nElements - 1) &&
248 CFDictionaryGetValueIfPresent(node, kSCResvLink, (const void **)&link)) {
249 /*
250 * if not the last path component and this
251 * element is a link
252 */
253 CFArrayRef linkElements;
254 CFMutableArrayRef newElements;
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 newElements = CFArrayCreateMutableCopy(NULL, 0, linkElements);
270 CFArrayAppendArray(newElements,
271 elements,
272 CFRangeMake(i + 1, nElements-i - 1));
273 CFRelease(elements);
274 elements = newElements;
275
276 CFRelease(nodes);
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 CFMutableDictionaryRef newDict = NULL;
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 the new dictionary */
375 newDict = CFDictionaryCreateMutable(NULL,
376 0,
377 &kCFTypeDictionaryKeyCallBacks,
378 &kCFTypeDictionaryValueCallBacks);
379 if (!setPath(prefs, newPath, newDict)) {
380 CFRelease(newPath);
381 newPath = NULL;
382 }
383 CFRelease(newDict);
384
385 return newPath;
386 }
387
388
389 CFDictionaryRef
390 SCPreferencesPathGetValue(SCPreferencesRef prefs,
391 CFStringRef path)
392 {
393 CFDictionaryRef entity;
394 CFStringRef entityLink;
395
396 if (prefs == NULL) {
397 /* sorry, you must provide a session */
398 _SCErrorSet(kSCStatusNoPrefsSession);
399 return NULL;
400 }
401
402 if (!getPath(prefs, path, &entity)) {
403 return NULL;
404 }
405
406 if (isA_CFDictionary(entity) &&
407 (CFDictionaryGetValueIfPresent(entity, kSCResvLink, (const void **)&entityLink))) {
408 /* if this is a dictionary AND it is a link */
409 if (!getPath(prefs, entityLink, &entity)) {
410 /* if it was a bad link */
411 return NULL;
412 }
413 }
414
415 return entity;
416 }
417
418
419 CFStringRef
420 SCPreferencesPathGetLink(SCPreferencesRef prefs,
421 CFStringRef path)
422 {
423 CFDictionaryRef entity;
424 CFStringRef entityLink;
425
426 if (prefs == NULL) {
427 /* sorry, you must provide a session */
428 _SCErrorSet(kSCStatusNoPrefsSession);
429 return NULL;
430 }
431
432 if (!getPath(prefs, path, &entity)) {
433 return NULL;
434 }
435
436 if (isA_CFDictionary(entity) &&
437 (CFDictionaryGetValueIfPresent(entity, kSCResvLink, (const void **)&entityLink))) {
438 /* if this is a dictionary AND it is a link */
439 return entityLink;
440 }
441
442 return NULL;
443 }
444
445
446 Boolean
447 SCPreferencesPathSetValue(SCPreferencesRef prefs,
448 CFStringRef path,
449 CFDictionaryRef value)
450 {
451 Boolean ok;
452
453 if (prefs == NULL) {
454 /* sorry, you must provide a session */
455 _SCErrorSet(kSCStatusNoPrefsSession);
456 return FALSE;
457 }
458
459 #define NETPREF_NEEDS_REPAIR
460 #ifdef NETPREF_NEEDS_REPAIR
461 if (CFEqual(path, CFSTR("/CurrentSet")) && isA_CFString(value)) {
462 // static Boolean warned = FALSE;
463 // if (!warned) {
464 // SCPrint(TRUE, stderr, CFSTR("SCPreferencesPathSetValue(, %@, ) called with non-dictionary value\n"), path);
465 // warned = TRUE;
466 // }
467 return SCPreferencesSetValue(prefs, CFSTR("CurrentSet"), value);
468 }
469 #endif // NETPREF_NEEDS_REPAIR
470
471 if (!isA_CFDictionary(value)) {
472 #ifdef NETPREF_NEEDS_REPAIR
473 SCPrint(TRUE, stderr, CFSTR("SCPreferencesPathSetValue(, %@, ) called with non-dictionary value\n"), path);
474 #endif // NETPREF_NEEDS_REPAIR
475 _SCErrorSet(kSCStatusInvalidArgument);
476 return FALSE;
477 }
478
479 ok = setPath(prefs, path, value);
480 return ok;
481 }
482
483
484 Boolean
485 SCPreferencesPathSetLink(SCPreferencesRef prefs,
486 CFStringRef path,
487 CFStringRef link)
488 {
489 CFMutableDictionaryRef dict;
490 CFDictionaryRef entity;
491 Boolean ok;
492
493 if (prefs == NULL) {
494 /* sorry, you must provide a session */
495 _SCErrorSet(kSCStatusNoPrefsSession);
496 return FALSE;
497 }
498
499 if (!isA_CFString(link)) {
500 _SCErrorSet(kSCStatusInvalidArgument);
501 return FALSE;
502 }
503
504 if (!getPath(prefs, link, &entity)) {
505 // if bad link
506 return FALSE;
507 }
508
509 dict = CFDictionaryCreateMutable(NULL,
510 0,
511 &kCFTypeDictionaryKeyCallBacks,
512 &kCFTypeDictionaryValueCallBacks);
513 CFDictionaryAddValue(dict, kSCResvLink, link);
514 ok = setPath(prefs, path, dict);
515 CFRelease(dict);
516
517 return ok;
518 }
519
520
521 Boolean
522 SCPreferencesPathRemoveValue(SCPreferencesRef prefs,
523 CFStringRef path)
524 {
525 CFArrayRef elements = NULL;
526 Boolean ok = FALSE;
527 CFDictionaryRef value;
528
529 if (prefs == NULL) {
530 /* sorry, you must provide a session */
531 _SCErrorSet(kSCStatusNoPrefsSession);
532 return FALSE;
533 }
534
535 if (!getPath(prefs, path, &value)) {
536 // if no such path
537 return FALSE;
538 }
539
540 elements = normalizePath(path);
541 if (elements == NULL) {
542 _SCErrorSet(kSCStatusNoKey);
543 return FALSE;
544 }
545
546 ok = setPath(prefs, path, NULL);
547
548 CFRelease(elements);
549 return ok;
550 }