17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 539d3e169Sevanl * Common Development and Distribution License (the "License"). 639d3e169Sevanl * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*3bfb48feSsemery * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * autod_readdir.c 287c478bd9Sstevel@tonic-gate */ 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate #include <stdio.h> 337c478bd9Sstevel@tonic-gate #include <ctype.h> 347c478bd9Sstevel@tonic-gate #include <string.h> 357c478bd9Sstevel@tonic-gate #include <syslog.h> 367c478bd9Sstevel@tonic-gate #include <sys/types.h> 377c478bd9Sstevel@tonic-gate #include <sys/param.h> 387c478bd9Sstevel@tonic-gate #include <errno.h> 397c478bd9Sstevel@tonic-gate #include <pwd.h> 407c478bd9Sstevel@tonic-gate #include <locale.h> 417c478bd9Sstevel@tonic-gate #include <stdlib.h> 427c478bd9Sstevel@tonic-gate #include <unistd.h> 437c478bd9Sstevel@tonic-gate #include <assert.h> 447c478bd9Sstevel@tonic-gate #include <fcntl.h> 457c478bd9Sstevel@tonic-gate #include "automount.h" 467c478bd9Sstevel@tonic-gate 4739d3e169Sevanl static void build_dir_entry_list(struct autofs_rddir_cache *rdcp, 487c478bd9Sstevel@tonic-gate struct dir_entry *list); 4939d3e169Sevanl static int autofs_rddir_cache_enter(char *map, ulong_t bucket_size, 5039d3e169Sevanl struct autofs_rddir_cache **rdcpp); 5139d3e169Sevanl int autofs_rddir_cache_lookup(char *map, struct autofs_rddir_cache **rdcpp); 5239d3e169Sevanl static int autofs_rddir_cache_delete(struct autofs_rddir_cache *rdcp); 5339d3e169Sevanl static int create_dirents(struct autofs_rddir_cache *rdcp, ulong_t offset, 547c478bd9Sstevel@tonic-gate autofs_rddirres *res); 557c478bd9Sstevel@tonic-gate struct dir_entry *rddir_entry_lookup(char *name, struct dir_entry *list); 567c478bd9Sstevel@tonic-gate static void free_offset_tbl(struct off_tbl *head); 577c478bd9Sstevel@tonic-gate static void free_dir_list(struct dir_entry *head); 587c478bd9Sstevel@tonic-gate 597c478bd9Sstevel@tonic-gate #define OFFSET_BUCKET_SIZE 100 607c478bd9Sstevel@tonic-gate 6139d3e169Sevanl rwlock_t autofs_rddir_cache_lock; /* readdir cache lock */ 6239d3e169Sevanl struct autofs_rddir_cache *rddir_head; /* readdir cache head */ 637c478bd9Sstevel@tonic-gate 647c478bd9Sstevel@tonic-gate int 65*3bfb48feSsemery do_readdir(autofs_rddirargs *rda, autofs_rddirres *rd) 667c478bd9Sstevel@tonic-gate { 677c478bd9Sstevel@tonic-gate struct dir_entry *list = NULL, *l; 6839d3e169Sevanl struct autofs_rddir_cache *rdcp = NULL; 697c478bd9Sstevel@tonic-gate int error; 707c478bd9Sstevel@tonic-gate int cache_time = RDDIR_CACHE_TIME; 717c478bd9Sstevel@tonic-gate 727c478bd9Sstevel@tonic-gate if (automountd_nobrowse) { 737c478bd9Sstevel@tonic-gate /* 747c478bd9Sstevel@tonic-gate * Browsability was disabled return an empty list. 757c478bd9Sstevel@tonic-gate */ 767c478bd9Sstevel@tonic-gate rd->rd_status = AUTOFS_OK; 777c478bd9Sstevel@tonic-gate rd->rd_rddir.rddir_size = 0; 787c478bd9Sstevel@tonic-gate rd->rd_rddir.rddir_eof = 1; 797c478bd9Sstevel@tonic-gate rd->rd_rddir.rddir_entries = NULL; 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate return (0); 827c478bd9Sstevel@tonic-gate } 837c478bd9Sstevel@tonic-gate 8439d3e169Sevanl rw_rdlock(&autofs_rddir_cache_lock); 8539d3e169Sevanl error = autofs_rddir_cache_lookup(rda->rda_map, &rdcp); 867c478bd9Sstevel@tonic-gate if (error) { 8739d3e169Sevanl rw_unlock(&autofs_rddir_cache_lock); 8839d3e169Sevanl rw_wrlock(&autofs_rddir_cache_lock); 8939d3e169Sevanl error = autofs_rddir_cache_lookup(rda->rda_map, &rdcp); 907c478bd9Sstevel@tonic-gate if (error) { 917c478bd9Sstevel@tonic-gate if (trace > 2) 927c478bd9Sstevel@tonic-gate trace_prt(1, 937c478bd9Sstevel@tonic-gate "map %s not found, adding...\n", rda->rda_map); 947c478bd9Sstevel@tonic-gate /* 957c478bd9Sstevel@tonic-gate * entry doesn't exist, add it. 967c478bd9Sstevel@tonic-gate */ 9739d3e169Sevanl error = autofs_rddir_cache_enter(rda->rda_map, 987c478bd9Sstevel@tonic-gate OFFSET_BUCKET_SIZE, &rdcp); 997c478bd9Sstevel@tonic-gate } 1007c478bd9Sstevel@tonic-gate } 10139d3e169Sevanl rw_unlock(&autofs_rddir_cache_lock); 1027c478bd9Sstevel@tonic-gate 1037c478bd9Sstevel@tonic-gate if (error) 1047c478bd9Sstevel@tonic-gate return (error); 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate assert(rdcp != NULL); 1077c478bd9Sstevel@tonic-gate assert(rdcp->in_use); 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate if (!rdcp->full) { 1107c478bd9Sstevel@tonic-gate rw_wrlock(&rdcp->rwlock); 1117c478bd9Sstevel@tonic-gate if (!rdcp->full) { 1127c478bd9Sstevel@tonic-gate /* 1137c478bd9Sstevel@tonic-gate * cache entry hasn't been filled up, do it now. 1147c478bd9Sstevel@tonic-gate */ 1157c478bd9Sstevel@tonic-gate char *stack[STACKSIZ]; 1167c478bd9Sstevel@tonic-gate char **stkptr; 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate /* 1197c478bd9Sstevel@tonic-gate * Initialize the stack of open files 1207c478bd9Sstevel@tonic-gate * for this thread 1217c478bd9Sstevel@tonic-gate */ 1227c478bd9Sstevel@tonic-gate stack_op(INIT, NULL, stack, &stkptr); 1237c478bd9Sstevel@tonic-gate (void) getmapkeys(rda->rda_map, &list, &error, 124*3bfb48feSsemery &cache_time, stack, &stkptr, rda->uid); 1257c478bd9Sstevel@tonic-gate if (!error) 1267c478bd9Sstevel@tonic-gate build_dir_entry_list(rdcp, list); 1277c478bd9Sstevel@tonic-gate else if (list) { 1287c478bd9Sstevel@tonic-gate free_dir_list(list); 1297c478bd9Sstevel@tonic-gate list = NULL; 1307c478bd9Sstevel@tonic-gate } 1317c478bd9Sstevel@tonic-gate } 1327c478bd9Sstevel@tonic-gate } else 1337c478bd9Sstevel@tonic-gate rw_rdlock(&rdcp->rwlock); 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate rd->rd_bufsize = rda->rda_count; 1367c478bd9Sstevel@tonic-gate if (!error) { 1377c478bd9Sstevel@tonic-gate error = create_dirents(rdcp, rda->rda_offset, rd); 1387c478bd9Sstevel@tonic-gate if (error) { 1397c478bd9Sstevel@tonic-gate if (rdcp->offtp) { 1407c478bd9Sstevel@tonic-gate free_offset_tbl(rdcp->offtp); 1417c478bd9Sstevel@tonic-gate rdcp->offtp = NULL; 1427c478bd9Sstevel@tonic-gate } 1437c478bd9Sstevel@tonic-gate if (rdcp->entp) { 1447c478bd9Sstevel@tonic-gate free_dir_list(rdcp->entp); 1457c478bd9Sstevel@tonic-gate rdcp->entp = NULL; 1467c478bd9Sstevel@tonic-gate } 1477c478bd9Sstevel@tonic-gate rdcp->full = 0; 1487c478bd9Sstevel@tonic-gate list = NULL; 1497c478bd9Sstevel@tonic-gate } 1507c478bd9Sstevel@tonic-gate } 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate if (trace > 2) { 1537c478bd9Sstevel@tonic-gate /* 1547c478bd9Sstevel@tonic-gate * print this list only once 1557c478bd9Sstevel@tonic-gate */ 1567c478bd9Sstevel@tonic-gate for (l = list; l != NULL; l = l->next) 1577c478bd9Sstevel@tonic-gate trace_prt(0, "%s\n", l->name); 1587c478bd9Sstevel@tonic-gate trace_prt(0, "\n"); 1597c478bd9Sstevel@tonic-gate } 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate if (!error) { 1627c478bd9Sstevel@tonic-gate rd->rd_status = AUTOFS_OK; 1637c478bd9Sstevel@tonic-gate if (cache_time) { 1647c478bd9Sstevel@tonic-gate /* 1657c478bd9Sstevel@tonic-gate * keep list of entries for up to 1667c478bd9Sstevel@tonic-gate * 'cache_time' seconds 1677c478bd9Sstevel@tonic-gate */ 1687c478bd9Sstevel@tonic-gate rdcp->ttl = time((time_t *)NULL) + cache_time; 1697c478bd9Sstevel@tonic-gate } else { 1707c478bd9Sstevel@tonic-gate /* 1717c478bd9Sstevel@tonic-gate * the underlying name service indicated not 1727c478bd9Sstevel@tonic-gate * to cache contents. 1737c478bd9Sstevel@tonic-gate */ 1747c478bd9Sstevel@tonic-gate if (rdcp->offtp) { 1757c478bd9Sstevel@tonic-gate free_offset_tbl(rdcp->offtp); 1767c478bd9Sstevel@tonic-gate rdcp->offtp = NULL; 1777c478bd9Sstevel@tonic-gate } 1787c478bd9Sstevel@tonic-gate if (rdcp->entp) { 1797c478bd9Sstevel@tonic-gate free_dir_list(rdcp->entp); 1807c478bd9Sstevel@tonic-gate rdcp->entp = NULL; 1817c478bd9Sstevel@tonic-gate } 1827c478bd9Sstevel@tonic-gate rdcp->full = 0; 1837c478bd9Sstevel@tonic-gate } 1847c478bd9Sstevel@tonic-gate } else { 1857c478bd9Sstevel@tonic-gate /* 1867c478bd9Sstevel@tonic-gate * return an empty list 1877c478bd9Sstevel@tonic-gate */ 1887c478bd9Sstevel@tonic-gate rd->rd_rddir.rddir_size = 0; 1897c478bd9Sstevel@tonic-gate rd->rd_rddir.rddir_eof = 1; 1907c478bd9Sstevel@tonic-gate rd->rd_rddir.rddir_entries = NULL; 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate /* 1937c478bd9Sstevel@tonic-gate * Invalidate cache and set error 1947c478bd9Sstevel@tonic-gate */ 1957c478bd9Sstevel@tonic-gate switch (error) { 1967c478bd9Sstevel@tonic-gate case ENOENT: 1977c478bd9Sstevel@tonic-gate rd->rd_status = AUTOFS_NOENT; 1987c478bd9Sstevel@tonic-gate break; 1997c478bd9Sstevel@tonic-gate case ENOMEM: 2007c478bd9Sstevel@tonic-gate rd->rd_status = AUTOFS_NOMEM; 2017c478bd9Sstevel@tonic-gate break; 2027c478bd9Sstevel@tonic-gate default: 2037c478bd9Sstevel@tonic-gate rd->rd_status = AUTOFS_ECOMM; 2047c478bd9Sstevel@tonic-gate } 2057c478bd9Sstevel@tonic-gate } 2067c478bd9Sstevel@tonic-gate rw_unlock(&rdcp->rwlock); 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate mutex_lock(&rdcp->lock); 2097c478bd9Sstevel@tonic-gate rdcp->in_use--; 2107c478bd9Sstevel@tonic-gate mutex_unlock(&rdcp->lock); 2117c478bd9Sstevel@tonic-gate 2127c478bd9Sstevel@tonic-gate assert(rdcp->in_use >= 0); 2137c478bd9Sstevel@tonic-gate 2147c478bd9Sstevel@tonic-gate return (error); 2157c478bd9Sstevel@tonic-gate } 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate #define roundtoint(x) (((x) + sizeof (int) - 1) & ~(sizeof (int) - 1)) 2187c478bd9Sstevel@tonic-gate #define DIRENT64_RECLEN(namelen) \ 2197c478bd9Sstevel@tonic-gate (((int)(((dirent64_t *)0)->d_name) + 1 + (namelen) + 7) & ~ 7) 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate static int 22239d3e169Sevanl create_dirents( 22339d3e169Sevanl struct autofs_rddir_cache *rdcp, 22439d3e169Sevanl ulong_t offset, 22539d3e169Sevanl autofs_rddirres *res) 2267c478bd9Sstevel@tonic-gate { 2277c478bd9Sstevel@tonic-gate uint_t total_bytes_wanted; 2287c478bd9Sstevel@tonic-gate int bufsize; 2297c478bd9Sstevel@tonic-gate ushort_t this_reclen; 2307c478bd9Sstevel@tonic-gate int outcount = 0; 2317c478bd9Sstevel@tonic-gate int namelen; 2327c478bd9Sstevel@tonic-gate struct dir_entry *list = NULL, *l, *nl; 2337c478bd9Sstevel@tonic-gate struct dirent64 *dp; 2347c478bd9Sstevel@tonic-gate char *outbuf; 2357c478bd9Sstevel@tonic-gate struct off_tbl *offtp, *next = NULL; 2367c478bd9Sstevel@tonic-gate int this_bucket = 0; 2377c478bd9Sstevel@tonic-gate int error = 0; 2387c478bd9Sstevel@tonic-gate int x = 0, y = 0; 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate assert(RW_LOCK_HELD(&rdcp->rwlock)); 2417c478bd9Sstevel@tonic-gate for (offtp = rdcp->offtp; offtp != NULL; offtp = next) { 2427c478bd9Sstevel@tonic-gate x++; 2437c478bd9Sstevel@tonic-gate next = offtp->next; 2447c478bd9Sstevel@tonic-gate this_bucket = (next == NULL); 2457c478bd9Sstevel@tonic-gate if (!this_bucket) 2467c478bd9Sstevel@tonic-gate this_bucket = (offset < next->offset); 2477c478bd9Sstevel@tonic-gate if (this_bucket) { 2487c478bd9Sstevel@tonic-gate /* 2497c478bd9Sstevel@tonic-gate * has to be in this bucket 2507c478bd9Sstevel@tonic-gate */ 2517c478bd9Sstevel@tonic-gate assert(offset >= offtp->offset); 2527c478bd9Sstevel@tonic-gate list = offtp->first; 2537c478bd9Sstevel@tonic-gate break; 2547c478bd9Sstevel@tonic-gate } 2557c478bd9Sstevel@tonic-gate /* 2567c478bd9Sstevel@tonic-gate * loop to look in next bucket 2577c478bd9Sstevel@tonic-gate */ 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate for (l = list; l != NULL && l->offset < offset; l = l->next) 2617c478bd9Sstevel@tonic-gate y++; 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate if (l == NULL) { 2647c478bd9Sstevel@tonic-gate /* 2657c478bd9Sstevel@tonic-gate * reached end of directory 2667c478bd9Sstevel@tonic-gate */ 2677c478bd9Sstevel@tonic-gate error = 0; 2687c478bd9Sstevel@tonic-gate goto empty; 2697c478bd9Sstevel@tonic-gate } 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate if (trace > 2) 2727c478bd9Sstevel@tonic-gate trace_prt(1, "%s: offset searches (%d, %d)\n", rdcp->map, x, y); 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate total_bytes_wanted = res->rd_bufsize; 2757c478bd9Sstevel@tonic-gate bufsize = total_bytes_wanted + sizeof (struct dirent64); 2767c478bd9Sstevel@tonic-gate outbuf = malloc(bufsize); 2777c478bd9Sstevel@tonic-gate if (outbuf == NULL) { 2787c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "memory allocation error\n"); 2797c478bd9Sstevel@tonic-gate error = ENOMEM; 2807c478bd9Sstevel@tonic-gate goto empty; 2817c478bd9Sstevel@tonic-gate } 2827c478bd9Sstevel@tonic-gate memset(outbuf, 0, bufsize); 2837c478bd9Sstevel@tonic-gate /* LINTED pointer alignment */ 2847c478bd9Sstevel@tonic-gate dp = (struct dirent64 *)outbuf; 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate while (l) { 2877c478bd9Sstevel@tonic-gate nl = l->next; 2887c478bd9Sstevel@tonic-gate namelen = strlen(l->name); 2897c478bd9Sstevel@tonic-gate this_reclen = DIRENT64_RECLEN(namelen); 2907c478bd9Sstevel@tonic-gate if (outcount + this_reclen > total_bytes_wanted) { 2917c478bd9Sstevel@tonic-gate break; 2927c478bd9Sstevel@tonic-gate } 2937c478bd9Sstevel@tonic-gate dp->d_ino = (ino64_t)l->nodeid; 2947c478bd9Sstevel@tonic-gate if (nl) { 2957c478bd9Sstevel@tonic-gate /* 2967c478bd9Sstevel@tonic-gate * get the next elements offset 2977c478bd9Sstevel@tonic-gate */ 2987c478bd9Sstevel@tonic-gate dp->d_off = (off64_t)nl->offset; 2997c478bd9Sstevel@tonic-gate } else { 3007c478bd9Sstevel@tonic-gate /* 3017c478bd9Sstevel@tonic-gate * This is the last element 3027c478bd9Sstevel@tonic-gate * make offset one plus the current. 3037c478bd9Sstevel@tonic-gate */ 3047c478bd9Sstevel@tonic-gate dp->d_off = (off64_t)l->offset + 1; 3057c478bd9Sstevel@tonic-gate } 3067c478bd9Sstevel@tonic-gate (void) strcpy(dp->d_name, l->name); 3077c478bd9Sstevel@tonic-gate dp->d_reclen = (ushort_t)this_reclen; 3087c478bd9Sstevel@tonic-gate outcount += dp->d_reclen; 3097c478bd9Sstevel@tonic-gate dp = (struct dirent64 *)((int)dp + dp->d_reclen); 3107c478bd9Sstevel@tonic-gate assert(outcount <= total_bytes_wanted); 3117c478bd9Sstevel@tonic-gate l = l->next; 3127c478bd9Sstevel@tonic-gate } 3137c478bd9Sstevel@tonic-gate 3147c478bd9Sstevel@tonic-gate res->rd_rddir.rddir_size = (long)outcount; 3157c478bd9Sstevel@tonic-gate if (outcount > 0) { 3167c478bd9Sstevel@tonic-gate /* 3177c478bd9Sstevel@tonic-gate * have some entries 3187c478bd9Sstevel@tonic-gate */ 3197c478bd9Sstevel@tonic-gate res->rd_rddir.rddir_eof = (l == NULL); 3207c478bd9Sstevel@tonic-gate /* LINTED pointer alignment */ 3217c478bd9Sstevel@tonic-gate res->rd_rddir.rddir_entries = (struct dirent64 *)outbuf; 3227c478bd9Sstevel@tonic-gate error = 0; 3237c478bd9Sstevel@tonic-gate } else { 3247c478bd9Sstevel@tonic-gate /* 3257c478bd9Sstevel@tonic-gate * total_bytes_wanted is not large enough for one 3267c478bd9Sstevel@tonic-gate * directory entry 3277c478bd9Sstevel@tonic-gate */ 3287c478bd9Sstevel@tonic-gate res->rd_rddir.rddir_eof = 0; 3297c478bd9Sstevel@tonic-gate res->rd_rddir.rddir_entries = NULL; 3307c478bd9Sstevel@tonic-gate free(outbuf); 3317c478bd9Sstevel@tonic-gate error = EIO; 3327c478bd9Sstevel@tonic-gate } 3337c478bd9Sstevel@tonic-gate return (error); 3347c478bd9Sstevel@tonic-gate 33539d3e169Sevanl empty: 33639d3e169Sevanl res->rd_rddir.rddir_size = 0L; 3377c478bd9Sstevel@tonic-gate res->rd_rddir.rddir_eof = TRUE; 3387c478bd9Sstevel@tonic-gate res->rd_rddir.rddir_entries = NULL; 3397c478bd9Sstevel@tonic-gate return (error); 3407c478bd9Sstevel@tonic-gate } 3417c478bd9Sstevel@tonic-gate 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate /* 3447c478bd9Sstevel@tonic-gate * add new entry to cache for 'map' 3457c478bd9Sstevel@tonic-gate */ 3467c478bd9Sstevel@tonic-gate static int 34739d3e169Sevanl autofs_rddir_cache_enter( 34839d3e169Sevanl char *map, 34939d3e169Sevanl ulong_t bucket_size, 35039d3e169Sevanl struct autofs_rddir_cache **rdcpp) 3517c478bd9Sstevel@tonic-gate { 35239d3e169Sevanl struct autofs_rddir_cache *p; 35339d3e169Sevanl assert(RW_LOCK_HELD(&autofs_rddir_cache_lock)); 3547c478bd9Sstevel@tonic-gate 3557c478bd9Sstevel@tonic-gate /* 3567c478bd9Sstevel@tonic-gate * Add to front of the list at this time 3577c478bd9Sstevel@tonic-gate */ 35839d3e169Sevanl p = (struct autofs_rddir_cache *)malloc(sizeof (*p)); 3597c478bd9Sstevel@tonic-gate if (p == NULL) { 3607c478bd9Sstevel@tonic-gate syslog(LOG_ERR, 36139d3e169Sevanl "autofs_rddir_cache_enter: memory allocation failed\n"); 3627c478bd9Sstevel@tonic-gate return (ENOMEM); 3637c478bd9Sstevel@tonic-gate } 3647c478bd9Sstevel@tonic-gate memset((char *)p, 0, sizeof (*p)); 3657c478bd9Sstevel@tonic-gate 3667c478bd9Sstevel@tonic-gate p->map = malloc(strlen(map) + 1); 3677c478bd9Sstevel@tonic-gate if (p->map == NULL) { 3687c478bd9Sstevel@tonic-gate syslog(LOG_ERR, 36939d3e169Sevanl "autofs_rddir_cache_enter: memory allocation failed\n"); 3707c478bd9Sstevel@tonic-gate free(p); 3717c478bd9Sstevel@tonic-gate return (ENOMEM); 3727c478bd9Sstevel@tonic-gate } 3737c478bd9Sstevel@tonic-gate strcpy(p->map, map); 3747c478bd9Sstevel@tonic-gate 3757c478bd9Sstevel@tonic-gate p->bucket_size = bucket_size; 3767c478bd9Sstevel@tonic-gate /* 3777c478bd9Sstevel@tonic-gate * no need to grab mutex lock since I haven't yet made the 3787c478bd9Sstevel@tonic-gate * node visible to the list 3797c478bd9Sstevel@tonic-gate */ 3807c478bd9Sstevel@tonic-gate p->in_use = 1; 3817c478bd9Sstevel@tonic-gate (void) rwlock_init(&p->rwlock, USYNC_THREAD, NULL); 3827c478bd9Sstevel@tonic-gate (void) mutex_init(&p->lock, USYNC_THREAD, NULL); 3837c478bd9Sstevel@tonic-gate 3847c478bd9Sstevel@tonic-gate if (rddir_head == NULL) 3857c478bd9Sstevel@tonic-gate rddir_head = p; 3867c478bd9Sstevel@tonic-gate else { 3877c478bd9Sstevel@tonic-gate p->next = rddir_head; 3887c478bd9Sstevel@tonic-gate rddir_head = p; 3897c478bd9Sstevel@tonic-gate } 3907c478bd9Sstevel@tonic-gate *rdcpp = p; 3917c478bd9Sstevel@tonic-gate 3927c478bd9Sstevel@tonic-gate return (0); 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate 3957c478bd9Sstevel@tonic-gate /* 3967c478bd9Sstevel@tonic-gate * find 'map' in readdir cache 3977c478bd9Sstevel@tonic-gate */ 3987c478bd9Sstevel@tonic-gate int 39939d3e169Sevanl autofs_rddir_cache_lookup(char *map, struct autofs_rddir_cache **rdcpp) 4007c478bd9Sstevel@tonic-gate { 40139d3e169Sevanl struct autofs_rddir_cache *p; 4027c478bd9Sstevel@tonic-gate 40339d3e169Sevanl assert(RW_LOCK_HELD(&autofs_rddir_cache_lock)); 4047c478bd9Sstevel@tonic-gate for (p = rddir_head; p != NULL; p = p->next) { 4057c478bd9Sstevel@tonic-gate if (strcmp(p->map, map) == 0) { 4067c478bd9Sstevel@tonic-gate /* 4077c478bd9Sstevel@tonic-gate * found matching entry 4087c478bd9Sstevel@tonic-gate */ 4097c478bd9Sstevel@tonic-gate *rdcpp = p; 4107c478bd9Sstevel@tonic-gate mutex_lock(&p->lock); 4117c478bd9Sstevel@tonic-gate p->in_use++; 4127c478bd9Sstevel@tonic-gate mutex_unlock(&p->lock); 4137c478bd9Sstevel@tonic-gate return (0); 4147c478bd9Sstevel@tonic-gate } 4157c478bd9Sstevel@tonic-gate } 4167c478bd9Sstevel@tonic-gate /* 4177c478bd9Sstevel@tonic-gate * didn't find entry 4187c478bd9Sstevel@tonic-gate */ 4197c478bd9Sstevel@tonic-gate return (ENOENT); 4207c478bd9Sstevel@tonic-gate } 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate /* 4237c478bd9Sstevel@tonic-gate * free the offset table 4247c478bd9Sstevel@tonic-gate */ 4257c478bd9Sstevel@tonic-gate static void 4267c478bd9Sstevel@tonic-gate free_offset_tbl(struct off_tbl *head) 4277c478bd9Sstevel@tonic-gate { 4287c478bd9Sstevel@tonic-gate struct off_tbl *p, *next = NULL; 4297c478bd9Sstevel@tonic-gate 4307c478bd9Sstevel@tonic-gate for (p = head; p != NULL; p = next) { 4317c478bd9Sstevel@tonic-gate next = p->next; 4327c478bd9Sstevel@tonic-gate free(p); 4337c478bd9Sstevel@tonic-gate } 4347c478bd9Sstevel@tonic-gate } 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate /* 4377c478bd9Sstevel@tonic-gate * free the directory entries 4387c478bd9Sstevel@tonic-gate */ 4397c478bd9Sstevel@tonic-gate static void 4407c478bd9Sstevel@tonic-gate free_dir_list(struct dir_entry *head) 4417c478bd9Sstevel@tonic-gate { 4427c478bd9Sstevel@tonic-gate struct dir_entry *p, *next = NULL; 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate for (p = head; p != NULL; p = next) { 4457c478bd9Sstevel@tonic-gate next = p->next; 4467c478bd9Sstevel@tonic-gate assert(p->name); 4477c478bd9Sstevel@tonic-gate free(p->name); 4487c478bd9Sstevel@tonic-gate free(p); 4497c478bd9Sstevel@tonic-gate } 4507c478bd9Sstevel@tonic-gate } 4517c478bd9Sstevel@tonic-gate 4527c478bd9Sstevel@tonic-gate static void 45339d3e169Sevanl autofs_rddir_cache_entry_free(struct autofs_rddir_cache *p) 4547c478bd9Sstevel@tonic-gate { 45539d3e169Sevanl assert(RW_LOCK_HELD(&autofs_rddir_cache_lock)); 4567c478bd9Sstevel@tonic-gate assert(!p->in_use); 4577c478bd9Sstevel@tonic-gate if (p->map) 4587c478bd9Sstevel@tonic-gate free(p->map); 4597c478bd9Sstevel@tonic-gate if (p->offtp) 4607c478bd9Sstevel@tonic-gate free_offset_tbl(p->offtp); 4617c478bd9Sstevel@tonic-gate if (p->entp) 4627c478bd9Sstevel@tonic-gate free_dir_list(p->entp); 4637c478bd9Sstevel@tonic-gate free(p); 4647c478bd9Sstevel@tonic-gate } 4657c478bd9Sstevel@tonic-gate 4667c478bd9Sstevel@tonic-gate /* 4677c478bd9Sstevel@tonic-gate * Remove entry from the rddircache 46839d3e169Sevanl * the caller must own the autofs_rddir_cache_lock. 4697c478bd9Sstevel@tonic-gate */ 4707c478bd9Sstevel@tonic-gate static int 47139d3e169Sevanl autofs_rddir_cache_delete(struct autofs_rddir_cache *rdcp) 4727c478bd9Sstevel@tonic-gate { 47339d3e169Sevanl struct autofs_rddir_cache *p, *prev; 4747c478bd9Sstevel@tonic-gate 47539d3e169Sevanl assert(RW_LOCK_HELD(&autofs_rddir_cache_lock)); 4767c478bd9Sstevel@tonic-gate /* 4777c478bd9Sstevel@tonic-gate * Search cache for entry 4787c478bd9Sstevel@tonic-gate */ 4797c478bd9Sstevel@tonic-gate prev = NULL; 4807c478bd9Sstevel@tonic-gate for (p = rddir_head; p != NULL; p = p->next) { 4817c478bd9Sstevel@tonic-gate if (p == rdcp) { 4827c478bd9Sstevel@tonic-gate /* 4837c478bd9Sstevel@tonic-gate * entry found, remove from list if not in use 4847c478bd9Sstevel@tonic-gate */ 4857c478bd9Sstevel@tonic-gate if (p->in_use) 4867c478bd9Sstevel@tonic-gate return (EBUSY); 4877c478bd9Sstevel@tonic-gate if (prev) 4887c478bd9Sstevel@tonic-gate prev->next = p->next; 4897c478bd9Sstevel@tonic-gate else 4907c478bd9Sstevel@tonic-gate rddir_head = p->next; 49139d3e169Sevanl autofs_rddir_cache_entry_free(p); 4927c478bd9Sstevel@tonic-gate return (0); 4937c478bd9Sstevel@tonic-gate } 4947c478bd9Sstevel@tonic-gate prev = p; 4957c478bd9Sstevel@tonic-gate } 4967c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "Couldn't find entry %x in cache\n", p); 4977c478bd9Sstevel@tonic-gate return (ENOENT); 4987c478bd9Sstevel@tonic-gate } 4997c478bd9Sstevel@tonic-gate 5007c478bd9Sstevel@tonic-gate /* 5017c478bd9Sstevel@tonic-gate * Return entry that matches name, NULL otherwise. 5027c478bd9Sstevel@tonic-gate * Assumes the readers lock for this list has been grabed. 5037c478bd9Sstevel@tonic-gate */ 5047c478bd9Sstevel@tonic-gate struct dir_entry * 5057c478bd9Sstevel@tonic-gate rddir_entry_lookup(char *name, struct dir_entry *list) 5067c478bd9Sstevel@tonic-gate { 5077c478bd9Sstevel@tonic-gate return (btree_lookup(list, name)); 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate static void 51139d3e169Sevanl build_dir_entry_list(struct autofs_rddir_cache *rdcp, struct dir_entry *list) 5127c478bd9Sstevel@tonic-gate { 5137c478bd9Sstevel@tonic-gate struct dir_entry *p; 5147c478bd9Sstevel@tonic-gate ulong_t offset = AUTOFS_DAEMONCOOKIE, offset_list = AUTOFS_DAEMONCOOKIE; 5157c478bd9Sstevel@tonic-gate struct off_tbl *offtp, *last = NULL; 5167c478bd9Sstevel@tonic-gate ino_t inonum = 4; 5177c478bd9Sstevel@tonic-gate 5187c478bd9Sstevel@tonic-gate assert(RW_LOCK_HELD(&rdcp->rwlock)); 5197c478bd9Sstevel@tonic-gate assert(rdcp->entp == NULL); 5207c478bd9Sstevel@tonic-gate rdcp->entp = list; 5217c478bd9Sstevel@tonic-gate for (p = list; p != NULL; p = p->next) { 5227c478bd9Sstevel@tonic-gate p->nodeid = inonum; 5237c478bd9Sstevel@tonic-gate p->offset = offset; 5247c478bd9Sstevel@tonic-gate if (offset >= offset_list) { 5257c478bd9Sstevel@tonic-gate /* 5267c478bd9Sstevel@tonic-gate * add node to index table 5277c478bd9Sstevel@tonic-gate */ 5287c478bd9Sstevel@tonic-gate offtp = (struct off_tbl *) 5297c478bd9Sstevel@tonic-gate malloc(sizeof (struct off_tbl)); 5307c478bd9Sstevel@tonic-gate if (offtp != NULL) { 5317c478bd9Sstevel@tonic-gate offtp->offset = offset; 5327c478bd9Sstevel@tonic-gate offtp->first = p; 5337c478bd9Sstevel@tonic-gate offtp->next = NULL; 5347c478bd9Sstevel@tonic-gate offset_list += rdcp->bucket_size; 5357c478bd9Sstevel@tonic-gate } else { 5367c478bd9Sstevel@tonic-gate syslog(LOG_ERR, 5377c478bd9Sstevel@tonic-gate "WARNING: build_dir_entry_list: could not add offset to index table\n"); 5387c478bd9Sstevel@tonic-gate continue; 5397c478bd9Sstevel@tonic-gate } 5407c478bd9Sstevel@tonic-gate /* 5417c478bd9Sstevel@tonic-gate * add to cache 5427c478bd9Sstevel@tonic-gate */ 5437c478bd9Sstevel@tonic-gate if (rdcp->offtp == NULL) 5447c478bd9Sstevel@tonic-gate rdcp->offtp = offtp; 5457c478bd9Sstevel@tonic-gate else 5467c478bd9Sstevel@tonic-gate last->next = offtp; 5477c478bd9Sstevel@tonic-gate last = offtp; 5487c478bd9Sstevel@tonic-gate } 5497c478bd9Sstevel@tonic-gate offset++; 5507c478bd9Sstevel@tonic-gate inonum += 2; /* use even numbers in daemon */ 5517c478bd9Sstevel@tonic-gate } 5527c478bd9Sstevel@tonic-gate rdcp->full = 1; 5537c478bd9Sstevel@tonic-gate } 5547c478bd9Sstevel@tonic-gate 5557c478bd9Sstevel@tonic-gate mutex_t cleanup_lock; 5567c478bd9Sstevel@tonic-gate cond_t cleanup_start_cv; 5577c478bd9Sstevel@tonic-gate cond_t cleanup_done_cv; 5587c478bd9Sstevel@tonic-gate 5597c478bd9Sstevel@tonic-gate /* 5607c478bd9Sstevel@tonic-gate * cache cleanup thread starting point 5617c478bd9Sstevel@tonic-gate */ 5627c478bd9Sstevel@tonic-gate void 5637c478bd9Sstevel@tonic-gate cache_cleanup(void) 5647c478bd9Sstevel@tonic-gate { 5657c478bd9Sstevel@tonic-gate timestruc_t reltime; 56639d3e169Sevanl struct autofs_rddir_cache *p, *next = NULL; 5677c478bd9Sstevel@tonic-gate int error; 5687c478bd9Sstevel@tonic-gate 5697c478bd9Sstevel@tonic-gate mutex_init(&cleanup_lock, USYNC_THREAD, NULL); 5707c478bd9Sstevel@tonic-gate cond_init(&cleanup_start_cv, USYNC_THREAD, NULL); 5717c478bd9Sstevel@tonic-gate cond_init(&cleanup_done_cv, USYNC_THREAD, NULL); 5727c478bd9Sstevel@tonic-gate 5737c478bd9Sstevel@tonic-gate mutex_lock(&cleanup_lock); 5747c478bd9Sstevel@tonic-gate for (;;) { 5757c478bd9Sstevel@tonic-gate reltime.tv_sec = RDDIR_CACHE_TIME/2; 5767c478bd9Sstevel@tonic-gate reltime.tv_nsec = 0; 5777c478bd9Sstevel@tonic-gate 5787c478bd9Sstevel@tonic-gate /* 5797c478bd9Sstevel@tonic-gate * delay RDDIR_CACHE_TIME seconds, or until some other thread 5807c478bd9Sstevel@tonic-gate * requests that I cleanup the caches 5817c478bd9Sstevel@tonic-gate */ 5827c478bd9Sstevel@tonic-gate if (error = cond_reltimedwait( 5837c478bd9Sstevel@tonic-gate &cleanup_start_cv, &cleanup_lock, &reltime)) { 5847c478bd9Sstevel@tonic-gate if (error != ETIME) { 5857c478bd9Sstevel@tonic-gate if (trace > 1) 5867c478bd9Sstevel@tonic-gate trace_prt(1, 5877c478bd9Sstevel@tonic-gate "cleanup thread wakeup (%d)\n", error); 5887c478bd9Sstevel@tonic-gate continue; 5897c478bd9Sstevel@tonic-gate } 5907c478bd9Sstevel@tonic-gate } 5917c478bd9Sstevel@tonic-gate mutex_unlock(&cleanup_lock); 5927c478bd9Sstevel@tonic-gate 5937c478bd9Sstevel@tonic-gate /* 5947c478bd9Sstevel@tonic-gate * Perform the cache cleanup 5957c478bd9Sstevel@tonic-gate */ 59639d3e169Sevanl rw_wrlock(&autofs_rddir_cache_lock); 5977c478bd9Sstevel@tonic-gate for (p = rddir_head; p != NULL; p = next) { 5987c478bd9Sstevel@tonic-gate next = p->next; 5997c478bd9Sstevel@tonic-gate if (p->in_use > 0) { 6007c478bd9Sstevel@tonic-gate /* 6017c478bd9Sstevel@tonic-gate * cache entry busy, skip it 6027c478bd9Sstevel@tonic-gate */ 6037c478bd9Sstevel@tonic-gate if (trace > 1) { 6047c478bd9Sstevel@tonic-gate trace_prt(1, 6057c478bd9Sstevel@tonic-gate "%s cache in use\n", p->map); 6067c478bd9Sstevel@tonic-gate } 6077c478bd9Sstevel@tonic-gate continue; 6087c478bd9Sstevel@tonic-gate } 6097c478bd9Sstevel@tonic-gate /* 6107c478bd9Sstevel@tonic-gate * Cache entry is not in use, and nobody can grab a 61139d3e169Sevanl * new reference since I'm holding the 61239d3e169Sevanl * autofs_rddir_cache_lock 6137c478bd9Sstevel@tonic-gate */ 6147c478bd9Sstevel@tonic-gate 6157c478bd9Sstevel@tonic-gate /* 6167c478bd9Sstevel@tonic-gate * error will be zero if some thread signaled us asking 6177c478bd9Sstevel@tonic-gate * that the caches be freed. In such case, free caches 6187c478bd9Sstevel@tonic-gate * even if they're still valid and nobody is referencing 6197c478bd9Sstevel@tonic-gate * them at this time. Otherwise, free caches only 6207c478bd9Sstevel@tonic-gate * if their time to live (ttl) has expired. 6217c478bd9Sstevel@tonic-gate */ 6227c478bd9Sstevel@tonic-gate if (error == ETIME && (p->ttl > time((time_t *)NULL))) { 6237c478bd9Sstevel@tonic-gate /* 6247c478bd9Sstevel@tonic-gate * Scheduled cache cleanup, if cache is still 6257c478bd9Sstevel@tonic-gate * valid don't free. 6267c478bd9Sstevel@tonic-gate */ 6277c478bd9Sstevel@tonic-gate if (trace > 1) { 6287c478bd9Sstevel@tonic-gate trace_prt(1, 6297c478bd9Sstevel@tonic-gate "%s cache still valid\n", p->map); 6307c478bd9Sstevel@tonic-gate } 6317c478bd9Sstevel@tonic-gate continue; 6327c478bd9Sstevel@tonic-gate } 6337c478bd9Sstevel@tonic-gate if (trace > 1) 6347c478bd9Sstevel@tonic-gate trace_prt(1, "%s freeing cache\n", p->map); 6357c478bd9Sstevel@tonic-gate assert(!p->in_use); 63639d3e169Sevanl error = autofs_rddir_cache_delete(p); 6377c478bd9Sstevel@tonic-gate assert(!error); 6387c478bd9Sstevel@tonic-gate } 63939d3e169Sevanl rw_unlock(&autofs_rddir_cache_lock); 6407c478bd9Sstevel@tonic-gate 6417c478bd9Sstevel@tonic-gate /* 6427c478bd9Sstevel@tonic-gate * wakeup the thread/threads waiting for the 6437c478bd9Sstevel@tonic-gate * cleanup to finish 6447c478bd9Sstevel@tonic-gate */ 6457c478bd9Sstevel@tonic-gate mutex_lock(&cleanup_lock); 6467c478bd9Sstevel@tonic-gate cond_broadcast(&cleanup_done_cv); 6477c478bd9Sstevel@tonic-gate } 6487c478bd9Sstevel@tonic-gate /* NOTREACHED */ 6497c478bd9Sstevel@tonic-gate } 650