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