]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPPath.c
configd-289.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCPPath.c
1 /*
2 * Copyright (c) 2000-2006, 2008 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 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 CFRelease(linkElements);
146 CFArrayAppendArray(newElements,
147 elements,
148 CFRangeMake(i + 1, nElements-i - 1));
149 CFRelease(elements);
150 elements = newElements;
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 CFArrayRef 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 CFArrayRef linkElements;
255 CFMutableArrayRef newElements;
256
257 if (++nLinks > MAXLINKS) {
258 /* if we are chasing our tail */
259 _SCErrorSet(kSCStatusMaxLink);
260 goto done;
261 }
262
263 linkElements = normalizePath(link);
264 if (linkElements == NULL) {
265 /* if the link is bad */
266 _SCErrorSet(kSCStatusNoKey);
267 goto done;
268 }
269
270 newElements = CFArrayCreateMutableCopy(NULL, 0, linkElements);
271 CFRelease(linkElements);
272 CFArrayAppendArray(newElements,
273 elements,
274 CFRangeMake(i + 1, nElements-i - 1));
275 CFRelease(elements);
276 elements = newElements;
277
278 CFRelease(nodes);
279 goto restart;
280 }
281 }
282
283 /*
284 * make sure that the last component doesn't step on top
285 * of a non-dictionary component.
286 */
287 element = CFArrayGetValueAtIndex(elements, nElements - 1);
288 if (nElements > 1) {
289 node = CFArrayGetValueAtIndex(nodes, nElements - 2);
290 node = CFDictionaryGetValue(node, element);
291 } else {
292 node = SCPreferencesGetValue(prefs, element);
293 }
294 if ((node != NULL) && !isA_CFDictionary(node)) {
295 // we won't step on a non-dictionary component
296 _SCErrorSet(kSCStatusInvalidArgument);
297 goto done;
298 }
299
300 if (entity != NULL) {
301 newEntity = CFRetain(entity);
302 }
303 for (i = nElements - 1; i >= 0; i--) {
304 element = CFArrayGetValueAtIndex(elements, i);
305 if (i == 0) {
306 if (newEntity != NULL) {
307 ok = SCPreferencesSetValue(prefs, element, newEntity);
308 } else {
309 ok = SCPreferencesRemoveValue(prefs, element);
310 }
311 } else {
312 CFMutableDictionaryRef newNode;
313
314 node = CFArrayGetValueAtIndex(nodes, i - 1);
315 newNode = CFDictionaryCreateMutableCopy(NULL, 0, node);
316 if (newEntity != NULL) {
317 CFDictionarySetValue(newNode, element, newEntity);
318 CFRelease(newEntity);
319 } else {
320 CFDictionaryRemoveValue(newNode, element);
321 if (CFDictionaryGetCount(newNode) == 0) {
322 // prune the (now empty) parent
323 CFRelease(newNode);
324 newNode = NULL;
325 }
326 }
327 newEntity = newNode;
328 }
329 }
330 if (newEntity != NULL) {
331 CFRelease(newEntity);
332 }
333
334 done :
335
336 if (nodes != NULL) CFRelease(nodes);
337 CFRelease(elements);
338 return ok;
339 }
340
341
342 CFStringRef
343 SCPreferencesPathCreateUniqueChild(SCPreferencesRef prefs,
344 CFStringRef prefix)
345 {
346 CFStringRef child;
347 CFStringRef newPath = NULL;
348 CFDictionaryRef newDict;
349 CFUUIDRef uuid;
350 CFDictionaryRef entity;
351
352 if (prefs == NULL) {
353 /* sorry, you must provide a session */
354 _SCErrorSet(kSCStatusNoPrefsSession);
355 return NULL;
356 }
357
358 if (getPath(prefs, prefix, &entity)) {
359 // if prefix path exists
360 if (CFDictionaryContainsKey(entity, kSCResvLink)) {
361 /* the path is a link... */
362 _SCErrorSet(kSCStatusFailed);
363 return NULL;
364 }
365 } else if (SCError() != kSCStatusNoKey) {
366 // if any error except for a missing prefix path component
367 return NULL;
368 }
369
370 uuid = CFUUIDCreate(NULL);
371 child = CFUUIDCreateString(NULL, uuid);
372 newPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), prefix, child);
373 CFRelease(child);
374 CFRelease(uuid);
375
376 /* save a new/empty dictionary */
377 newDict = CFDictionaryCreate(NULL,
378 NULL, NULL, 0,
379 &kCFTypeDictionaryKeyCallBacks,
380 &kCFTypeDictionaryValueCallBacks);
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 CFArrayRef 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 }