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_fnreaddir.c 24 * 25 * Copyright (c) 1995 - 1996, by Sun Microsystems, Inc. 26 * All rights reserved. 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <syslog.h> 35 #include <rpc/rpc.h> 36 #include <xfn/xfn.h> 37 #include "automount.h" 38 #include "ns_fnutils.h" 39 40 41 /* 42 * Given the name of an XFN map, create a list of the map entries for a 43 * given user. Set error to zero on success. 44 * 45 * extern void 46 * getmapkeys_fn(const char *map, struct dir_entry **, int *error, 47 * int *cache_time, uid_t); 48 */ 49 50 /* 51 * Given a multi-component composite name, construct the corresponding 52 * context handle and the context handle of its prefix. The prefix is 53 * that part of the name up to (and possibly including) the last slash 54 * in the name. Return zero on success. 55 * 56 * eg: user/jane/service => user/jane + service 57 * org/ssi.eng/user => org/ssi.eng/ + user 58 */ 59 static int 60 get_contexts(const FN_composite_name_t *, FN_ctx_t **ctxp, 61 FN_ctx_t **prefix_ctxp, FN_ctx_t *init_ctx, FN_status_t *); 62 63 /* 64 * Split a multi-component composite name into its last component and 65 * its other components. Return zero on success. 66 */ 67 static int 68 split_cname(const FN_composite_name_t *name, FN_composite_name_t **last, 69 FN_composite_name_t **lead); 70 71 /* 72 * Given a context and its prefix context (defined above), determine 73 * whether the context, its NNS context, or both should be listed. 74 * (The syntaxes of the contexts are used to help make this 75 * determination.) Add the subdirectories of the appropriate 76 * context(s) to the dir_entry list. Return zero on success. 77 * 78 * eg: "ls /xfn/user => list context only 79 * "ls /xfn/org/ssi.eng" => list NNS only 80 * "ls /xfn/.../c=us" => list context and NNS 81 */ 82 static int 83 list_ctx_and_or_nns(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx, struct dir_entry **, 84 FN_status_t *); 85 86 /* 87 * Given a context and its prefix context (defined above), return true 88 * if the NNS of the context should be listed but the context itself 89 * should not. 90 */ 91 static bool_t 92 need_nns_only(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx, FN_status_t *); 93 94 /* 95 * Return true if both the given context and its NNS should be listed. 96 */ 97 static bool_t 98 need_ctx_and_nns(FN_ctx_t *, FN_status_t *); 99 100 /* 101 * Add the subdirectories of a context to the dir_entry list. Return 102 * zero on success. 103 */ 104 static int 105 list_ctx(FN_ctx_t *, struct dir_entry **, FN_status_t *); 106 107 /* 108 * Given a context and its name relative to the root of its rightmost 109 * naming system, add the context's subdirectories to the dir_entry 110 * list. If syntax is non-NULL recursively list names until a context 111 * with a different syntax is encountered, otherwise list one level 112 * only. May modify "name". Return zero on success. 113 * 114 * eg: For the context org/eng with syntax "dot-separated, right-to-left", 115 * the compound name "eng" would be passed in, and the following might 116 * be added to the dir_entry list: 117 * ssi.eng 118 * feds.ssi.eng 119 * ste.eng 120 */ 121 static int 122 list_ctx_aux(FN_ctx_t *, FN_compound_name_t *name, const FN_attrset_t *syntax, 123 struct dir_entry **, FN_status_t *); 124 125 /* 126 * Add a name to a dir_entry list. Return zero on success. 127 */ 128 static int 129 add_name_to_dirlist(const FN_compound_name_t *, struct dir_entry **); 130 131 /* 132 * Return true if a set of syntax attributes correspond to a 133 * hierarchical namespace with a slash separator. Return false on 134 * error. 135 */ 136 static bool_t 137 slash_hierarchy(const FN_attrset_t *syntax); 138 139 /* 140 * Return true if a set of syntax attributes correspond to a 141 * hierarchical namespace with a separator other than a slash. 142 * Return false on error. 143 */ 144 static bool_t 145 non_slash_hierarchy(const FN_attrset_t *syntax); 146 147 /* 148 * Return true if two syntax attribute sets are equal. 149 */ 150 static bool_t 151 syntax_attrs_equal(const FN_attrset_t *, const FN_attrset_t *); 152 153 /* 154 * Return a value of a given attribute in an attribute set, or NULL 155 * on error. 156 */ 157 static const FN_attrvalue_t * 158 get_attrval(const FN_attrset_t *, const FN_identifier_t *attr_id); 159 160 /* 161 * Lookup a name and return the corresponding context handle. On 162 * error return NULL and, if "log" is true or the error is transient, 163 * log an error message. 164 */ 165 static FN_ctx_t * 166 lookup_ctx(FN_ctx_t *, const FN_composite_name_t *, bool_t log, FN_status_t *); 167 168 169 /* 170 * Unlike during a lookup or mount, transient errors are tolerated. A 171 * potentially transient error during a readdir() (such as no response 172 * from an X.500 server) could result in an incomplete listing, but at 173 * least readdir() will return everything that it can. Note that it 174 * is still possible to mount a directory that for some reason did not 175 * show up in a prior readdir(). 176 */ 177 void 178 getmapkeys_fn(const char *map, struct dir_entry **entries_p, int *error, 179 int *cache_time, uid_t uid) 180 { 181 FN_composite_name_t *name; 182 FN_status_t *status; 183 FN_ctx_t *init_ctx; 184 FN_ctx_t *ctx; 185 FN_ctx_t *prefix_ctx; 186 struct dir_entry *p; 187 188 *cache_time = RDDIR_CACHE_TIME; 189 190 if ((init_fn() != 0) || (status = fn_status_create()) == NULL) { 191 log_mem_failure(); 192 *error = -1; 193 return; 194 } 195 init_ctx = _fn_ctx_handle_from_initial_with_uid(uid, 0, status); 196 if (init_ctx == NULL) { 197 logstat(status, "", "No initial context"); 198 fn_status_destroy(status); 199 return; 200 } 201 202 if (strcmp(map, FNPREFIX) == 0) { 203 /* 204 * List the initial context. 205 * Contents of initial ctx is user-relative 206 */ 207 *cache_time = 0; 208 *error = list_ctx(init_ctx, entries_p, status); 209 } else if (strcmp(map, FNPREFIX "/_dns") == 0) { 210 /* Cannot list DNS; report success but no entries. */ 211 *cache_time = 1000000; /* no sense trying again */ 212 *error = 0; 213 } else { 214 if (strcmp(map, FNPREFIX "/...") == 0) { 215 /* List X.500 but not DNS. */ 216 name = new_cname("_x500"); 217 } else { 218 name = new_cname(map + FNPREFIXLEN + 1); 219 } 220 if (name == NULL) { 221 *error = -1; 222 } else if (fn_composite_name_count(name) == 1) { 223 224 /* List an atomic name. */ 225 ctx = lookup_ctx(init_ctx, name, TRUE, status); 226 if (ctx != NULL) { 227 *error = list_ctx_and_or_nns(ctx, init_ctx, 228 entries_p, status); 229 fn_ctx_handle_destroy(ctx); 230 } else { 231 *error = -1; 232 } 233 } else { 234 235 /* List a multi-component name. */ 236 *error = get_contexts(name, &ctx, &prefix_ctx, 237 init_ctx, status); 238 if (*error == 0) { 239 *error = list_ctx_and_or_nns(ctx, prefix_ctx, 240 entries_p, status); 241 fn_ctx_handle_destroy(ctx); 242 fn_ctx_handle_destroy(prefix_ctx); 243 } 244 } 245 fn_composite_name_destroy(name); 246 } 247 fn_status_destroy(status); 248 fn_ctx_handle_destroy(init_ctx); 249 250 if (*error == 0) { 251 /* 252 * create the binary tree of entries 253 */ 254 for (p = *entries_p; p != NULL; p = p->next) 255 btree_enter(entries_p, p); 256 } 257 } 258 259 260 static int 261 get_contexts(const FN_composite_name_t *name, FN_ctx_t **ctxp, 262 FN_ctx_t **prefix_ctxp, FN_ctx_t *init_ctx, FN_status_t *status) 263 { 264 FN_composite_name_t *prefix = NULL; 265 FN_composite_name_t *suffix = NULL; 266 FN_ctx_t *nns_ctx; 267 268 /* 269 * Break a name such as "pre/fix/suffix" into "pre/fix/" and 270 * "suffix". If that fails, try "pre/fix" and "suffix". This 271 * can be more efficient than doing it the reverse order. 272 */ 273 if (split_cname(name, &suffix, &prefix) != 0) { 274 return (-1); 275 } 276 *ctxp = NULL; 277 *prefix_ctxp = lookup_ctx(init_ctx, prefix, TRUE, status); 278 fn_composite_name_destroy(prefix); 279 280 if (*prefix_ctxp != NULL) { 281 nns_ctx = lookup_ctx(*prefix_ctxp, slash_cname, FALSE, status); 282 if (nns_ctx != NULL) { 283 *ctxp = lookup_ctx(nns_ctx, suffix, FALSE, status); 284 if (*ctxp != NULL) { 285 fn_ctx_handle_destroy(*prefix_ctxp); 286 *prefix_ctxp = nns_ctx; 287 } else { 288 fn_ctx_handle_destroy(nns_ctx); 289 } 290 } 291 if (*ctxp == NULL) { 292 *ctxp = 293 lookup_ctx(*prefix_ctxp, suffix, FALSE, status); 294 } 295 } 296 fn_composite_name_destroy(suffix); 297 return (*ctxp != NULL ? 0 : -1); 298 } 299 300 301 static int 302 split_cname(const FN_composite_name_t *name, FN_composite_name_t **last, 303 FN_composite_name_t **lead) 304 { 305 void *iter; 306 307 (void) fn_composite_name_last(name, &iter); 308 *last = fn_composite_name_suffix(name, iter); 309 *lead = fn_composite_name_prefix(name, iter); 310 if (*last == NULL || *lead == NULL) { 311 log_mem_failure(); 312 fn_composite_name_destroy(*last); 313 fn_composite_name_destroy(*lead); 314 return (-1); 315 } 316 return (0); 317 } 318 319 320 static int 321 list_ctx_and_or_nns(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx, 322 struct dir_entry **entries_p, FN_status_t *status) 323 { 324 FN_ctx_t *nns_ctx; 325 int rc; 326 327 if (!need_nns_only(ctx, prefix_ctx, status)) { 328 if (list_ctx(ctx, entries_p, status) != 0) { 329 return (-1); 330 } 331 if (!need_ctx_and_nns(ctx, status)) { 332 return (0); 333 } 334 } 335 nns_ctx = lookup_ctx(ctx, slash_cname, FALSE, status); 336 if (nns_ctx == NULL) { 337 return (0); 338 } 339 rc = list_ctx(nns_ctx, entries_p, status); 340 fn_ctx_handle_destroy(nns_ctx); 341 return (rc); 342 } 343 344 345 /* 346 * True if ctx has a hierarchical syntax with a non-slash separator 347 * and prefix_ctx either has the same syntax or does not provide any 348 * syntax ("..." should be the only example of the latter condition). 349 */ 350 static bool_t 351 need_nns_only(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx, FN_status_t *status) 352 { 353 FN_attrset_t *syn; 354 FN_attrset_t *prefix_syn; 355 bool_t retval; 356 357 syn = fn_ctx_get_syntax_attrs(ctx, empty_cname, status); 358 if (syn == NULL || !non_slash_hierarchy(syn)) { 359 fn_attrset_destroy(syn); 360 return (FALSE); 361 } 362 /* 363 * ctx is hierarchical and not slash-separated. How about prefix_ctx? 364 */ 365 prefix_syn = fn_ctx_get_syntax_attrs(prefix_ctx, empty_cname, status); 366 retval = (prefix_syn == NULL) || syntax_attrs_equal(syn, prefix_syn); 367 368 fn_attrset_destroy(syn); 369 fn_attrset_destroy(prefix_syn); 370 return (retval); 371 } 372 373 374 /* 375 * True if ctx has a slash-separated hierarchical syntax. 376 */ 377 static bool_t 378 need_ctx_and_nns(FN_ctx_t *ctx, FN_status_t *status) 379 { 380 FN_attrset_t *syn; 381 bool_t retval; 382 383 syn = fn_ctx_get_syntax_attrs(ctx, empty_cname, status); 384 if (syn == NULL) { 385 return (FALSE); 386 } 387 retval = slash_hierarchy(syn); 388 fn_attrset_destroy(syn); 389 return (retval); 390 } 391 392 393 static int 394 list_ctx(FN_ctx_t *ctx, struct dir_entry **entries_p, FN_status_t *status) 395 { 396 FN_attrset_t *syntax; 397 FN_compound_name_t *name; 398 int retval; 399 400 syntax = fn_ctx_get_syntax_attrs(ctx, empty_cname, status); 401 if (syntax == NULL) { 402 logstat(status, "", "bad syntax attributes"); 403 return (-1); 404 } 405 name = 406 fn_compound_name_from_syntax_attrs(syntax, empty_string, status); 407 if (name == NULL) { 408 logstat(status, "", "could not create compound name"); 409 fn_attrset_destroy(syntax); 410 return (-1); 411 } 412 if (!non_slash_hierarchy(syntax)) { 413 fn_attrset_destroy(syntax); 414 syntax = NULL; 415 } 416 retval = list_ctx_aux(ctx, name, syntax, entries_p, status); 417 fn_attrset_destroy(syntax); 418 fn_compound_name_destroy(name); 419 return (retval); 420 } 421 422 423 static int 424 list_ctx_aux(FN_ctx_t *ctx, FN_compound_name_t *name, 425 const FN_attrset_t *syntax, struct dir_entry **entries_p, 426 FN_status_t *status) 427 { 428 FN_bindinglist_t *bindings; 429 FN_string_t *child; 430 FN_ref_t *ref; 431 unsigned int stat; 432 int rc = 0; 433 void *iter; 434 435 bindings = fn_ctx_list_bindings(ctx, empty_cname, status); 436 if (bindings == NULL) { 437 return (0); 438 } 439 while ((child = fn_bindinglist_next(bindings, &ref, status)) != NULL) { 440 if (fn_compound_name_append_comp(name, child, &stat) == 0) { 441 rc = -1; 442 break; 443 } 444 if (add_name_to_dirlist(name, entries_p) != 0) { 445 rc = -1; 446 break; 447 } 448 if (syntax != NULL) { 449 /* Traverse hierarchy. */ 450 ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status); 451 if (ctx != NULL) { 452 rc = list_ctx_aux(ctx, name, syntax, entries_p, 453 status); 454 fn_ctx_handle_destroy(ctx); 455 if (rc != 0) { 456 break; 457 } 458 } 459 } 460 fn_ref_destroy(ref); 461 fn_string_destroy(child); 462 (void) fn_compound_name_last(name, &iter); 463 (void) fn_compound_name_next(name, &iter); 464 (void) fn_compound_name_delete_comp(name, &iter); 465 } 466 fn_string_destroy(child); 467 fn_bindinglist_destroy(bindings XFN1(status)); 468 return (rc); 469 } 470 471 472 static int 473 add_name_to_dirlist(const FN_compound_name_t *name, 474 struct dir_entry **entries_p) 475 { 476 FN_string_t *string; 477 char *str; 478 unsigned int stat; 479 struct dir_entry *entry; 480 481 string = fn_string_from_compound_name(name); 482 if (string == NULL) { 483 log_mem_failure(); 484 return (-1); 485 } 486 str = (char *)fn_string_str(string, &stat); 487 if (str != NULL) { 488 str = auto_rddir_strdup(str); 489 } 490 fn_string_destroy(string); 491 if (str == NULL) { 492 log_mem_failure(); 493 return (-1); 494 } 495 496 /* LINTED pointer alignment */ 497 entry = (struct dir_entry *) 498 auto_rddir_malloc(sizeof (*entry)); 499 if (entry == NULL) { 500 log_mem_failure(); 501 free(str); 502 return (-1); 503 } 504 (void) memset((char *)entry, 0, sizeof (*entry)); 505 entry->name = str; 506 entry->next = *entries_p; 507 *entries_p = entry; 508 return (0); 509 } 510 511 512 /* 513 * Identifiers of syntax attributes for direction and separator. 514 */ 515 516 static const FN_identifier_t syntax_direction = { 517 FN_ID_STRING, 518 sizeof ("fn_std_syntax_direction") - 1, 519 "fn_std_syntax_direction" 520 }; 521 522 static const FN_identifier_t syntax_separator = { 523 FN_ID_STRING, 524 sizeof ("fn_std_syntax_separator") - 1, 525 "fn_std_syntax_separator" 526 }; 527 528 529 static bool_t 530 slash_hierarchy(const FN_attrset_t *syntax) 531 { 532 const FN_attrvalue_t *dir = get_attrval(syntax, &syntax_direction); 533 const FN_attrvalue_t *sep = get_attrval(syntax, &syntax_separator); 534 535 return (dir != NULL && 536 memcmp("flat", dir->contents, dir->length) != 0 && 537 sep != NULL && 538 memcmp("/", sep->contents, sep->length) == 0); 539 } 540 541 542 static bool_t 543 non_slash_hierarchy(const FN_attrset_t *syntax) 544 { 545 const FN_attrvalue_t *dir = get_attrval(syntax, &syntax_direction); 546 const FN_attrvalue_t *sep = get_attrval(syntax, &syntax_separator); 547 548 return (dir != NULL && 549 memcmp("flat", dir->contents, dir->length) != 0 && 550 sep != NULL && 551 memcmp("/", sep->contents, sep->length) != 0); 552 } 553 554 555 static bool_t 556 syntax_attrs_equal(const FN_attrset_t *syn1, const FN_attrset_t *syn2) 557 { 558 const FN_attribute_t *attr; 559 const FN_attrvalue_t *val1; 560 const FN_attrvalue_t *val2; 561 void *iter1; 562 void *iter2; 563 564 if (fn_attrset_count(syn1) != fn_attrset_count(syn2)) { 565 return (FALSE); 566 } 567 for (attr = fn_attrset_first(syn1, &iter1); 568 attr != NULL; 569 attr = fn_attrset_next(syn1, &iter1)) { 570 val1 = fn_attribute_first(attr, &iter2); 571 val2 = get_attrval(syn2, fn_attribute_identifier(attr)); 572 if ((val1 == NULL && val2 != NULL) || 573 (val1 != NULL && val2 == NULL)) { 574 return (FALSE); 575 } 576 if (val1 != NULL && val2 != NULL) { 577 if (val1->length != val2->length || 578 memcmp(val1->contents, val2->contents, 579 val1->length) != 0) { 580 return (FALSE); 581 } 582 } 583 } 584 return (TRUE); 585 } 586 587 588 static const FN_attrvalue_t * 589 get_attrval(const FN_attrset_t *attrs, const FN_identifier_t *attr_id) 590 { 591 const FN_attribute_t *attr; 592 void *iter; 593 594 attr = fn_attrset_get(attrs, attr_id); 595 if (attr != NULL) { 596 return (fn_attribute_first(attr, &iter)); 597 } else { 598 return (NULL); 599 } 600 } 601 602 603 static FN_ctx_t * 604 lookup_ctx(FN_ctx_t *ctx, const FN_composite_name_t *name, bool_t log, 605 FN_status_t *status) 606 { 607 FN_ref_t *ref; 608 char *msg; 609 610 ref = fn_ctx_lookup(ctx, name, status); 611 if (ref == NULL) { 612 ctx = NULL; 613 msg = "lookup failed"; 614 } else { 615 ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status); 616 fn_ref_destroy(ref); 617 if (ctx == NULL) { 618 msg = "could not construct context handle"; 619 } 620 } 621 if (ctx == NULL && verbose && (log || transient(status))) { 622 logstat(status, "", msg); 623 } 624 return (ctx); 625 } 626