1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22/* 23 * ns_od.c 24 * 25 * Based on ns_ldap.c 26 * 27 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31/* 32 * Portions Copyright 2007-2012 Apple Inc. 33 */ 34 35#include <stdio.h> 36#include <stdlib.h> 37#include <syslog.h> 38#include <string.h> 39 40#include <OpenDirectory/OpenDirectory.h> 41 42#include "automount.h" 43#include "automount_od.h" 44 45/* 46 * Callback routines for od_process_record_attributes() and od_search(). 47 */ 48typedef enum { 49 OD_CB_KEEPGOING, /* continue the search */ 50 OD_CB_DONE, /* we're done with the search */ 51 OD_CB_REJECTED, /* this record had a problem - keep going */ 52 OD_CB_ERROR /* error - quit and return __NSW_UNAVAIL */ 53} callback_ret_t; 54typedef callback_ret_t (*callback_fn)(CFStringRef key, CFStringRef value, 55 void *udata); 56 57static callback_ret_t mastermap_callback(CFStringRef key, CFStringRef value, 58 void *udata); 59static callback_ret_t directmap_callback(CFStringRef key, CFStringRef value, 60 void *udata); 61static callback_ret_t match_callback(CFStringRef key, CFStringRef value, 62 void *udata); 63static callback_ret_t readdir_callback(CFStringRef key, CFStringRef value, 64 void *udata); 65 66struct match_cbdata { 67 const char *map; 68 const char *key; 69 char **od_line; 70 int *od_len; 71}; 72 73struct loadmaster_cbdata { 74 char *defopts; 75 char **stack; 76 char ***stkptr; 77}; 78 79struct loaddirect_cbdata { 80 char *opts; 81 char *localmap; 82 char **stack; 83 char ***stkptr; 84}; 85 86struct dir_cbdata { 87 struct dir_entry **list; 88 struct dir_entry *last; 89 int error; 90}; 91 92static int od_match(const char *map, const char *key, char **od_line, 93 int *od_len); 94static int od_search(CFStringRef attr_to_match, char *value_to_match, 95 callback_fn callback, void *udata); 96 97/* 98 * Get the C-string length of a CFString. 99 */ 100static inline CFIndex 101od_cfstrlen(CFStringRef cfstr) 102{ 103 return (CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), 104 kCFStringEncodingUTF8)); 105} 106 107/* 108 * Given a CFString and its C-string length, copy it to a buffer. 109 */ 110static inline Boolean 111od_cfstrlcpy(char *string, CFStringRef cfstr, size_t size) 112{ 113 return (CFStringGetCString(cfstr, string, (CFIndex)size, 114 kCFStringEncodingUTF8)); 115} 116 117/* 118 * Get a C string from a CFStringRef. 119 * The string is allocated with malloc(), and must be freed when it's 120 * no longer needed. 121 */ 122static char * 123od_CFStringtoCString(CFStringRef cfstr) 124{ 125 char *string; 126 CFIndex length; 127 128 length = od_cfstrlen(cfstr); 129 string = malloc(length + 1); 130 if (string == NULL) 131 return (NULL); 132 if (!od_cfstrlcpy(string, cfstr, length + 1)) { 133 free(string); 134 return (NULL); 135 } 136 return (string); 137} 138 139 140char * 141od_get_error_string(CFErrorRef err) 142{ 143 CFStringRef errstringref; 144 char *errstring; 145 146 if (err != NULL) { 147 errstringref = CFErrorCopyDescription(err); 148 errstring = od_CFStringtoCString(errstringref); 149 CFRelease(errstringref); 150 } else 151 errstring = strdup("Unknown error"); 152 return (errstring); 153} 154 155/*ARGSUSED*/ 156void 157init_od(__unused char **stack, __unused char ***stkptr) 158{ 159} 160 161/*ARGSUSED*/ 162int 163getmapent_od(const char *key, const char *map, struct mapline *ml, 164 __unused char **stack, __unused char ***stkptr, 165 bool_t *iswildcard, __unused bool_t isrestricted) 166{ 167 char *od_line = NULL; 168 char *lp; 169 int od_len; 170 size_t len; 171 int nserr; 172 173 if (trace > 1) 174 trace_prt(1, "getmapent_od called\n"); 175 176 if (trace > 1) { 177 trace_prt(1, "getmapent_od: key=[ %s ]\n", key); 178 } 179 180 if (iswildcard) 181 *iswildcard = FALSE; 182 nserr = od_match(map, key, &od_line, &od_len); 183 if (nserr) { 184 if (nserr == __NSW_NOTFOUND) { 185 /* Try the default entry "*" */ 186 if ((nserr = od_match(map, "*", &od_line, 187 &od_len))) 188 goto done; 189 else { 190 if (iswildcard) 191 *iswildcard = TRUE; 192 } 193 } else 194 goto done; 195 } 196 197 /* 198 * at this point we are sure that od_match 199 * succeeded so massage the entry by 200 * 1. ignoring # and beyond 201 * 2. trim the trailing whitespace 202 */ 203 if ((lp = strchr(od_line, '#')) != NULL) 204 *lp = '\0'; 205 len = strlen(od_line); 206 if (len == 0) { 207 nserr = __NSW_NOTFOUND; 208 goto done; 209 } 210 lp = &od_line[len - 1]; 211 while (lp > od_line && isspace(*lp)) 212 *lp-- = '\0'; 213 if (lp == od_line) { 214 nserr = __NSW_NOTFOUND; 215 goto done; 216 } 217 (void) strncpy(ml->linebuf, od_line, LINESZ); 218 unquote(ml->linebuf, ml->lineqbuf); 219 nserr = __NSW_SUCCESS; 220done: 221 if (od_line) 222 free((char *)od_line); 223 224 if (trace > 1) 225 trace_prt(1, "getmapent_od: exiting ...\n"); 226 227 return (nserr); 228} 229 230static callback_ret_t 231match_callback(CFStringRef key, CFStringRef value, void *udata) 232{ 233 char *key_str, *value_str; 234 CFIndex value_len; 235 struct match_cbdata *temp = (struct match_cbdata *)udata; 236 char **od_line = temp->od_line; 237 int *od_len = temp->od_len; 238 239 if (trace > 1) { 240 key_str = od_CFStringtoCString(key); 241 value_str = od_CFStringtoCString(value); 242 if (key_str != NULL && value_str != NULL) { 243 trace_prt(1, " match_callback called: key %s, value %s\n", 244 key_str, value_str); 245 } 246 free(value_str); 247 free(key_str); 248 } 249 250 /* 251 * value contains a list of mount options AND mount locations 252 * for a particular mount point (key). 253 * For example: 254 * 255 * key: /work 256 * ^^^^^ 257 * (mount point) 258 * 259 * value: 260 * -rw,intr,nosuid,noquota hosta:/export/work 261 * ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ 262 * ( mount options ) (remote mount location) 263 * 264 */ 265 value_len = od_cfstrlen(value); 266 *od_len = (int)(value_len + 1); 267 268 /* 269 * so check for the length; it should be less than 270 * LINESZ 271 */ 272 if (*od_len > LINESZ) { 273 key_str = od_CFStringtoCString(key); 274 pr_msg( 275 "Open Directory map %s, entry for %s" 276 " is too long %d chars (max %d)", 277 temp->map, key_str, *od_len, LINESZ); 278 free(key_str); 279 return (OD_CB_REJECTED); 280 } 281 *od_line = (char *)malloc(*od_len); 282 if (*od_line == NULL) { 283 pr_msg("match_callback: malloc failed"); 284 return (OD_CB_ERROR); 285 } 286 287 if (!od_cfstrlcpy(*od_line, value, *od_len)) { 288 key_str = od_CFStringtoCString(key); 289 pr_msg("match_callback: can't get line for %s", key_str); 290 free(key_str); 291 free(*od_line); 292 return (OD_CB_ERROR); 293 } 294 295 if (trace > 1) 296 trace_prt(1, " match_callback: found: %s\n", *od_line); 297 298 return (OD_CB_DONE); 299} 300 301static int 302od_match(const char *map, const char *key, char **od_line, int *od_len) 303{ 304 int ret; 305 char *pattern; 306 struct match_cbdata cbdata; 307 308 if (trace > 1) { 309 trace_prt(1, "od_match called\n"); 310 trace_prt(1, "od_match: key =[ %s ] map = %s\n", key, map); 311 } 312 313 /* Construct the string value to search for. */ 314 if (asprintf(&pattern, "%s,automountMapName=%s", key, map) == -1) { 315 pr_msg("od_match: malloc failed"); 316 ret = __NSW_UNAVAIL; 317 goto done; 318 } 319 320 if (trace > 1) 321 trace_prt(1, " od_match: Searching for %s\n", pattern); 322 323 cbdata.map = map; 324 cbdata.key = key; 325 cbdata.od_line = od_line; 326 cbdata.od_len = od_len; 327 ret = od_search(kODAttributeTypeRecordName, pattern, match_callback, 328 (void *) &cbdata); 329 free(pattern); 330 331 if (trace > 1) { 332 if (ret == __NSW_NOTFOUND) 333 trace_prt(1, " od_match: no entries found\n"); 334 else if (ret != __NSW_UNAVAIL) 335 trace_prt(1, 336 " od_match: od_search FAILED: %d\n", ret); 337 else 338 trace_prt(1, " od_match: od_search OK\n"); 339 } 340 341 if (verbose) { 342 if (ret == __NSW_NOTFOUND) 343 pr_msg("od_search for %s in %s failed", key, map); 344 } 345 346done: 347 return (ret); 348} 349 350int 351loadmaster_od(char *mapname, char *defopts, char **stack, char ***stkptr) 352{ 353 int res; 354 struct loadmaster_cbdata master_cbdata; 355 356 if (trace > 1) 357 trace_prt(1, "loadmaster_od called\n"); 358 359 master_cbdata.defopts = defopts; 360 master_cbdata.stack = stack; 361 master_cbdata.stkptr = stkptr; 362 363 if (trace > 1) 364 trace_prt(1, "loadmaster_od: Requesting list in %s\n", 365 mapname); 366 367 res = od_search(kODAttributeTypeMetaAutomountMap, mapname, 368 mastermap_callback, (void *) &master_cbdata); 369 370 if (trace > 1) 371 trace_prt(1, 372 "loadmaster_od: od_search just returned: %d\n", 373 res); 374 375 return (res); 376} 377 378int 379loaddirect_od(char *nsmap, char *localmap, char *opts, 380 char **stack, char ***stkptr) 381{ 382 struct loaddirect_cbdata direct_cbdata; 383 384 if (trace > 1) { 385 trace_prt(1, "loaddirect_od called\n"); 386 } 387 388 if (trace > 1) 389 trace_prt(1, "loaddirect_od: Requesting list for %s in %s\n", 390 localmap, nsmap); 391 392 direct_cbdata.opts = opts; 393 direct_cbdata.localmap = localmap; 394 direct_cbdata.stack = stack; 395 direct_cbdata.stkptr = stkptr; 396 return (od_search(kODAttributeTypeMetaAutomountMap, nsmap, 397 directmap_callback, (void *) &direct_cbdata)); 398} 399 400static callback_ret_t 401mastermap_callback(CFStringRef key, CFStringRef invalue, void *udata) 402{ 403 char *key_str, *value_str, *value; 404 CFIndex key_len, value_len; 405 char *pmap, *opts; 406 char dir[LINESZ], map[LINESZ], qbuff[LINESZ]; 407 struct loadmaster_cbdata *temp = (struct loadmaster_cbdata *)udata; 408 char *defopts = temp->defopts; 409 char **stack = temp->stack; 410 char ***stkptr = temp->stkptr; 411 CFIndex i; 412 413 if (trace > 1) { 414 key_str = od_CFStringtoCString(key); 415 value_str = od_CFStringtoCString(invalue); 416 if (key_str != NULL && value_str != NULL) { 417 trace_prt(1, " mastermap_callback called: key %s, value %s\n", 418 key_str, value_str); 419 } 420 free(value_str); 421 free(key_str); 422 } 423 424 key_len = od_cfstrlen(key); 425 value_len = od_cfstrlen(invalue); 426 if (key_len >= LINESZ || value_len >= LINESZ) 427 return (OD_CB_KEEPGOING); 428 if (key_len < 2 || value_len < 2) 429 return (OD_CB_KEEPGOING); 430 431 value_str = od_CFStringtoCString(invalue); 432 value = value_str; 433 i = value_len; 434 while (i > 0 && isspace((unsigned char)*value)) { 435 value++; 436 i--; 437 } 438 if (*value == '\0') { 439 free(value_str); 440 return (OD_CB_KEEPGOING); 441 } 442 if (!od_cfstrlcpy(dir, key, key_len)) { 443 free(value_str); 444 return (OD_CB_KEEPGOING); 445 } 446 if (isspace((unsigned char)dir[0]) || dir[0] == '#') { 447 free(value_str); 448 return (OD_CB_KEEPGOING); 449 } 450 451 if (trace > 1) 452 trace_prt(1, "mastermap_callback: dir= [ %s ]\n", dir); 453 for (i = 0; i < LINESZ; i++) 454 qbuff[i] = ' '; 455 switch (macro_expand("", dir, qbuff, sizeof (dir))) { 456 457 case MEXPAND_OK: 458 break; 459 460 case MEXPAND_LINE_TOO_LONG: 461 pr_msg( 462 "%s in Open Directory map: entry too long (max %zu chars)", 463 dir, sizeof (dir) - 1); 464 free(value_str); 465 return (OD_CB_KEEPGOING); 466 467 case MEXPAND_VARNAME_TOO_LONG: 468 pr_msg( 469 "%s in Open Directory map: variable name too long", 470 dir); 471 free(value_str); 472 return (OD_CB_KEEPGOING); 473 } 474 strlcpy(map, value, sizeof (map)); /* we know this will not truncate */ 475 free(value_str); 476 if (trace > 1) 477 trace_prt(1, "mastermap_callback: map= [ %s ]\n", map); 478 switch (macro_expand("", map, qbuff, sizeof (map))) { 479 480 case MEXPAND_OK: 481 break; 482 483 case MEXPAND_LINE_TOO_LONG: 484 pr_msg( 485 "%s in Open Directory map: entry too long (max %zu chars)", 486 map, sizeof (map) - 1); 487 return (OD_CB_KEEPGOING); 488 489 case MEXPAND_VARNAME_TOO_LONG: 490 pr_msg( 491 "%s in Open Directory map: variable name too long", 492 map); 493 return (OD_CB_KEEPGOING); 494 } 495 pmap = map; 496 while (*pmap && isspace(*pmap)) 497 pmap++; /* skip blanks in front of map */ 498 opts = pmap; 499 while (*opts && !isspace(*opts)) 500 opts++; 501 if (*opts) { 502 *opts++ = '\0'; 503 while (*opts && isspace(*opts)) 504 opts++; 505 if (*opts == '-') 506 opts++; 507 else 508 opts = defopts; 509 } 510 /* 511 * Check for no embedded blanks. 512 */ 513 if (strcspn(opts, " \t") == strlen(opts)) { 514 if (trace > 1) 515 trace_prt(1, 516 "mastermap_callback: dir=[ %s ], pmap=[ %s ]\n", 517 dir, pmap); 518 dirinit(dir, pmap, opts, 0, stack, stkptr); 519 } else { 520 /* XXX - this was "dn=" for LDAP; is that the server name? */ 521 pr_msg( 522 "Warning: invalid entry for %s in Open Directory ignored.\n", 523 dir); 524 } 525 if (trace > 1) 526 trace_prt(1, "mastermap_callback exiting...\n"); 527 return (OD_CB_KEEPGOING); 528} 529 530static callback_ret_t 531directmap_callback(CFStringRef key, __unused CFStringRef value, void *udata) 532{ 533 char *str; 534 CFIndex key_len; 535 char dir[MAXFILENAMELEN+1]; 536 struct loaddirect_cbdata *temp = (struct loaddirect_cbdata *)udata; 537 char *opts = temp->opts; 538 char *localmap = temp->localmap; 539 char **stack = temp->stack; 540 char ***stkptr = temp->stkptr; 541 542 if (trace > 1) { 543 str = od_CFStringtoCString(key); 544 if (str != NULL) { 545 trace_prt(1, " directmap_callback called: key %s\n", 546 str); 547 free(str); 548 } 549 } 550 551 key_len = od_cfstrlen(key); 552 if (key_len > (CFIndex)MAXFILENAMELEN || key_len < 2) 553 return (OD_CB_KEEPGOING); 554 555 if (!od_cfstrlcpy(dir, key, key_len)) 556 return (OD_CB_KEEPGOING); 557 if (isspace((unsigned char)dir[0]) || dir[0] == '#') 558 return (OD_CB_KEEPGOING); /* ignore blank lines and comments */ 559 560 dirinit(dir, localmap, opts, 1, stack, stkptr); 561 562 return (OD_CB_KEEPGOING); 563} 564 565int 566getmapkeys_od(char *nsmap, struct dir_entry **list, int *error, 567 int *cache_time, __unused char **stack, __unused char ***stkptr) 568{ 569 int res; 570 struct dir_cbdata readdir_cbdata; 571 572 if (trace > 1) 573 trace_prt(1, "getmapkeys_od called\n"); 574 575 *cache_time = RDDIR_CACHE_TIME; 576 *error = 0; 577 578 if (trace > 1) 579 trace_prt(1, "getmapkeys_od: Requesting list in %s\n", 580 nsmap); 581 582 readdir_cbdata.list = list; 583 readdir_cbdata.last = NULL; 584 readdir_cbdata.error = 0; 585 res = od_search(kODAttributeTypeMetaAutomountMap, nsmap, 586 readdir_callback, (void *) &readdir_cbdata); 587 588 if (trace > 1) 589 trace_prt(1, " getmapkeys_od: od_search returned %d\n", 590 res); 591 592 if (readdir_cbdata.error) 593 *error = readdir_cbdata.error; 594 595 if (res != __NSW_SUCCESS) { 596 if (*error == 0) 597 *error = EIO; 598 } 599 600 return (res); 601} 602 603static callback_ret_t 604readdir_callback(CFStringRef inkey, CFStringRef value, void *udata) 605{ 606 char *str; 607 CFIndex inkeylen, value_len; 608 struct dir_cbdata *temp = (struct dir_cbdata *)udata; 609 struct dir_entry **list = temp->list; 610 struct dir_entry *last = temp->last; 611 char key[MAXFILENAMELEN+1]; 612 char linebuf[LINESZ], lineqbuf[LINESZ]; 613 int error; 614 615 if (trace > 1) { 616 str = od_CFStringtoCString(inkey); 617 if (str != NULL) { 618 trace_prt(1, " readdir_callback called: key %s\n", 619 str); 620 free(str); 621 } 622 } 623 624 inkeylen = od_cfstrlen(inkey); 625 if (inkeylen > (CFIndex)MAXFILENAMELEN) 626 return (OD_CB_KEEPGOING); 627 628 if (inkeylen == 0) 629 return (OD_CB_KEEPGOING); /* ignore empty lines */ 630 if (!od_cfstrlcpy(key, inkey, inkeylen)) 631 return (OD_CB_KEEPGOING); 632 if (isspace((unsigned char)key[0]) || key[0] == '#') 633 return (OD_CB_KEEPGOING); /* ignore blank lines and comments */ 634 635 /* 636 * Wildcard entry should be ignored - following entries should continue 637 * to be read to corroborate with the way we search for entries in 638 * LDAP, i.e., first for an exact key match and then a wildcard 639 * if there's no exact key match. 640 */ 641 if (key[0] == '*' && key[1] == '\0') 642 return (OD_CB_KEEPGOING); 643 644 value_len = od_cfstrlen(value); 645 if (value_len >= LINESZ) 646 return (OD_CB_KEEPGOING); 647 if (value_len < 2) 648 return (OD_CB_KEEPGOING); 649 650 if (!od_cfstrlcpy(linebuf, value, value_len)) 651 return (OD_CB_KEEPGOING); 652 unquote(linebuf, lineqbuf); 653 error = add_dir_entry(key, linebuf, lineqbuf, list, &last); 654 if (error != -1) { 655 if (error != 0) { 656 temp->error = error; 657 return (OD_CB_ERROR); 658 } 659 temp->last = last; 660 } 661 662 if (trace > 1) 663 trace_prt(1, "readdir_callback returning OD_CB_KEEPGOING...\n"); 664 665 return (OD_CB_KEEPGOING); 666} 667 668/* 669 * Looks for kODAttributeTypeRecordName and 670 * kODAttributeTypeAutomountInformation; if it finds them, it calls 671 * the specified callback with that information. 672 */ 673static callback_ret_t 674od_process_record_attributes(ODRecordRef record, callback_fn callback, 675 void *udata) 676{ 677 CFErrorRef error; 678 char *errstring; 679 CFArrayRef keys; 680 CFStringRef key; 681 CFArrayRef values; 682 CFStringRef value; 683 callback_ret_t ret; 684 685 if (trace > 1) { 686 trace_prt(1, 687 "od_process_record_attributes entered\n"); 688 } 689 690 /* 691 * Get kODAttributeTypeRecordName and 692 * kODAttributeTypeAutomountInformation for this record. 693 * 694 * Even though LDAP allows for multiple values per attribute, we take 695 * only the 1st value for each attribute because the automount data is 696 * organized as such (same as NIS+). 697 */ 698 error = NULL; 699 keys = ODRecordCopyValues(record, kODAttributeTypeRecordName, &error); 700 if (keys == NULL) { 701 if (error != NULL) { 702 errstring = od_get_error_string(error); 703 pr_msg("od_process_record_attributes: can't get kODAttributeTypeRecordName attribute for record: %s", 704 errstring); 705 free(errstring); 706 return (OD_CB_ERROR); 707 } else { 708 /* 709 * We just reject records missing the attributes 710 * we need. 711 */ 712 pr_msg("od_process_record_attributes: record has no kODAttributeTypeRecordName attribute"); 713 return (OD_CB_REJECTED); 714 } 715 } 716 if (CFArrayGetCount(keys) == 0) { 717 /* 718 * We just reject records missing the attributes 719 * we need. 720 */ 721 CFRelease(keys); 722 pr_msg("od_process_record_attributes: record has no kODAttributeTypeRecordName attribute"); 723 return (OD_CB_REJECTED); 724 } 725 key = CFArrayGetValueAtIndex(keys, 0); 726 error = NULL; 727 values = ODRecordCopyValues(record, 728 kODAttributeTypeAutomountInformation, &error); 729 if (values == NULL) { 730 CFRelease(keys); 731 if (error != NULL) { 732 errstring = od_get_error_string(error); 733 pr_msg("od_process_record_attributes: can't get kODAttributeTypeAutomountInformation attribute for record: %s", 734 errstring); 735 free(errstring); 736 return (OD_CB_ERROR); 737 } else { 738 /* 739 * We just reject records missing the attributes 740 * we need. 741 */ 742 pr_msg("od_process_record_attributes: record has no kODAttributeTypeAutomountInformation attribute"); 743 return (OD_CB_REJECTED); 744 } 745 } 746 if (CFArrayGetCount(values) == 0) { 747 /* 748 * We just reject records missing the attributes 749 * we need. 750 */ 751 CFRelease(values); 752 CFRelease(keys); 753 pr_msg("od_process_record_attributes: record has no kODAttributeTypeRecordName attribute"); 754 return (OD_CB_REJECTED); 755 } 756 value = CFArrayGetValueAtIndex(values, 0); 757 758 /* 759 * We have both of the attributes we need. 760 */ 761 ret = (*callback)(key, value, udata); 762 CFRelease(values); 763 CFRelease(keys); 764 return (ret); 765} 766 767/* 768 * Fetch all the map records in Open Directory that have a certain attribute 769 * that matches a certain value and pass those records to a callback function. 770 */ 771static int 772od_search(CFStringRef attr_to_match, char *value_to_match, callback_fn callback, 773 void *udata) 774{ 775 int ret; 776 CFErrorRef error; 777 char *errstring; 778 ODNodeRef node_ref; 779 CFArrayRef attrs; 780 CFStringRef value_to_match_cfstr; 781 ODQueryRef query_ref; 782 CFArrayRef results; 783 CFIndex num_results; 784 CFIndex i; 785 ODRecordRef record; 786 callback_ret_t callback_ret; 787 788 /* 789 * Create the search node. 790 */ 791 error = NULL; 792 node_ref = ODNodeCreateWithNodeType(kCFAllocatorDefault, kODSessionDefault, 793 kODNodeTypeAuthentication, &error); 794 if (node_ref == NULL) { 795 errstring = od_get_error_string(error); 796 pr_msg("od_search: can't create search node for /Search: %s", 797 errstring); 798 free(errstring); 799 return (__NSW_UNAVAIL); 800 } 801 802 /* 803 * Create the query. 804 */ 805 value_to_match_cfstr = CFStringCreateWithCString(kCFAllocatorDefault, 806 value_to_match, kCFStringEncodingUTF8); 807 if (value_to_match_cfstr == NULL) { 808 CFRelease(node_ref); 809 pr_msg("od_search: can't make CFString from %s", 810 value_to_match); 811 return (__NSW_UNAVAIL); 812 } 813 attrs = CFArrayCreate(kCFAllocatorDefault, 814 (const void *[2]){kODAttributeTypeRecordName, 815 kODAttributeTypeAutomountInformation}, 2, 816 &kCFTypeArrayCallBacks); 817 if (attrs == NULL) { 818 CFRelease(value_to_match_cfstr); 819 CFRelease(node_ref); 820 pr_msg("od_search: can't make array of attribute types"); 821 return (__NSW_UNAVAIL); 822 } 823 error = NULL; 824 query_ref = ODQueryCreateWithNode(kCFAllocatorDefault, node_ref, 825 kODRecordTypeAutomount, attr_to_match, kODMatchEqualTo, 826 value_to_match_cfstr, attrs, 0, &error); 827 CFRelease(attrs); 828 CFRelease(value_to_match_cfstr); 829 if (query_ref == NULL) { 830 CFRelease(node_ref); 831 errstring = od_get_error_string(error); 832 pr_msg("od_search: can't create query: %s", 833 errstring); 834 free(errstring); 835 return (__NSW_UNAVAIL); 836 } 837 838 /* 839 * Wait for the query to get all the results, and then copy them. 840 */ 841 error = NULL; 842 results = ODQueryCopyResults(query_ref, false, &error); 843 if (results == NULL) { 844 CFRelease(query_ref); 845 CFRelease(node_ref); 846 errstring = od_get_error_string(error); 847 pr_msg("od_search: query failed: %s", errstring); 848 free(errstring); 849 return (__NSW_UNAVAIL); 850 } 851 852 ret = __NSW_NOTFOUND; /* we haven't found any records yet */ 853 num_results = CFArrayGetCount(results); 854 for (i = 0; i < num_results; i++) { 855 /* 856 * We've found a record. 857 */ 858 record = (ODRecordRef)CFArrayGetValueAtIndex(results, i); 859 callback_ret = od_process_record_attributes(record, 860 callback, udata); 861 if (callback_ret == OD_CB_KEEPGOING) { 862 /* 863 * We processed one record, but we want 864 * to keep processing records. 865 */ 866 ret = __NSW_SUCCESS; 867 } else if (callback_ret == OD_CB_DONE) { 868 /* 869 * We processed one record, and we don't 870 * want to see any more records. 871 */ 872 ret = __NSW_SUCCESS; 873 break; 874 } else if (callback_ret == OD_CB_ERROR) { 875 /* 876 * Fatal error - give up. 877 */ 878 ret = __NSW_UNAVAIL; 879 break; 880 } 881 882 /* 883 * Otherwise it's OD_CB_REJECTED, which is a non-fatal 884 * error. We haven't found a record, so we shouldn't 885 * return __NSW_SUCCESS yet, but if we do find a 886 * record, we shouldn't fail. 887 */ 888 } 889 CFRelease(results); 890 CFRelease(query_ref); 891 CFRelease(node_ref); 892 return (ret); 893} 894