1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate /* 28*0Sstevel@tonic-gate * autod_readdir.c 29*0Sstevel@tonic-gate */ 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate #include <stdio.h> 34*0Sstevel@tonic-gate #include <ctype.h> 35*0Sstevel@tonic-gate #include <string.h> 36*0Sstevel@tonic-gate #include <syslog.h> 37*0Sstevel@tonic-gate #include <sys/types.h> 38*0Sstevel@tonic-gate #include <sys/param.h> 39*0Sstevel@tonic-gate #include <errno.h> 40*0Sstevel@tonic-gate #include <pwd.h> 41*0Sstevel@tonic-gate #include <locale.h> 42*0Sstevel@tonic-gate #include <stdlib.h> 43*0Sstevel@tonic-gate #include <unistd.h> 44*0Sstevel@tonic-gate #include <assert.h> 45*0Sstevel@tonic-gate #include <fcntl.h> 46*0Sstevel@tonic-gate #include "automount.h" 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate static void build_dir_entry_list(struct rddir_cache *rdcp, 49*0Sstevel@tonic-gate struct dir_entry *list); 50*0Sstevel@tonic-gate static int rddir_cache_enter(char *map, ulong_t bucket_size, 51*0Sstevel@tonic-gate struct rddir_cache **rdcpp); 52*0Sstevel@tonic-gate int rddir_cache_lookup(char *map, struct rddir_cache **rdcpp); 53*0Sstevel@tonic-gate static int rddir_cache_delete(struct rddir_cache *rdcp); 54*0Sstevel@tonic-gate static int create_dirents(struct rddir_cache *rdcp, ulong_t offset, 55*0Sstevel@tonic-gate autofs_rddirres *res); 56*0Sstevel@tonic-gate struct dir_entry *rddir_entry_lookup(char *name, struct dir_entry *list); 57*0Sstevel@tonic-gate static void free_offset_tbl(struct off_tbl *head); 58*0Sstevel@tonic-gate static void free_dir_list(struct dir_entry *head); 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate #define OFFSET_BUCKET_SIZE 100 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate rwlock_t rddir_cache_lock; /* readdir cache lock */ 63*0Sstevel@tonic-gate struct rddir_cache *rddir_head; /* readdir cache head */ 64*0Sstevel@tonic-gate 65*0Sstevel@tonic-gate int 66*0Sstevel@tonic-gate do_readdir(struct autofs_rddirargs *rda, struct autofs_rddirres *rd, 67*0Sstevel@tonic-gate struct authunix_parms *cred) 68*0Sstevel@tonic-gate { 69*0Sstevel@tonic-gate struct dir_entry *list = NULL, *l; 70*0Sstevel@tonic-gate struct rddir_cache *rdcp = NULL; 71*0Sstevel@tonic-gate int error; 72*0Sstevel@tonic-gate int cache_time = RDDIR_CACHE_TIME; 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gate if (automountd_nobrowse) { 75*0Sstevel@tonic-gate /* 76*0Sstevel@tonic-gate * Browsability was disabled return an empty list. 77*0Sstevel@tonic-gate */ 78*0Sstevel@tonic-gate rd->rd_status = AUTOFS_OK; 79*0Sstevel@tonic-gate rd->rd_rddir.rddir_size = 0; 80*0Sstevel@tonic-gate rd->rd_rddir.rddir_eof = 1; 81*0Sstevel@tonic-gate rd->rd_rddir.rddir_entries = NULL; 82*0Sstevel@tonic-gate 83*0Sstevel@tonic-gate return (0); 84*0Sstevel@tonic-gate } 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gate rw_rdlock(&rddir_cache_lock); 87*0Sstevel@tonic-gate error = rddir_cache_lookup(rda->rda_map, &rdcp); 88*0Sstevel@tonic-gate if (error) { 89*0Sstevel@tonic-gate rw_unlock(&rddir_cache_lock); 90*0Sstevel@tonic-gate rw_wrlock(&rddir_cache_lock); 91*0Sstevel@tonic-gate error = rddir_cache_lookup(rda->rda_map, &rdcp); 92*0Sstevel@tonic-gate if (error) { 93*0Sstevel@tonic-gate if (trace > 2) 94*0Sstevel@tonic-gate trace_prt(1, 95*0Sstevel@tonic-gate "map %s not found, adding...\n", rda->rda_map); 96*0Sstevel@tonic-gate /* 97*0Sstevel@tonic-gate * entry doesn't exist, add it. 98*0Sstevel@tonic-gate */ 99*0Sstevel@tonic-gate error = rddir_cache_enter(rda->rda_map, 100*0Sstevel@tonic-gate OFFSET_BUCKET_SIZE, &rdcp); 101*0Sstevel@tonic-gate } 102*0Sstevel@tonic-gate } 103*0Sstevel@tonic-gate rw_unlock(&rddir_cache_lock); 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate if (error) 106*0Sstevel@tonic-gate return (error); 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate assert(rdcp != NULL); 109*0Sstevel@tonic-gate assert(rdcp->in_use); 110*0Sstevel@tonic-gate 111*0Sstevel@tonic-gate if (!rdcp->full) { 112*0Sstevel@tonic-gate rw_wrlock(&rdcp->rwlock); 113*0Sstevel@tonic-gate if (!rdcp->full) { 114*0Sstevel@tonic-gate /* 115*0Sstevel@tonic-gate * cache entry hasn't been filled up, do it now. 116*0Sstevel@tonic-gate */ 117*0Sstevel@tonic-gate char *stack[STACKSIZ]; 118*0Sstevel@tonic-gate char **stkptr; 119*0Sstevel@tonic-gate 120*0Sstevel@tonic-gate /* 121*0Sstevel@tonic-gate * Initialize the stack of open files 122*0Sstevel@tonic-gate * for this thread 123*0Sstevel@tonic-gate */ 124*0Sstevel@tonic-gate stack_op(INIT, NULL, stack, &stkptr); 125*0Sstevel@tonic-gate (void) getmapkeys(rda->rda_map, &list, &error, 126*0Sstevel@tonic-gate &cache_time, stack, &stkptr, cred->aup_uid); 127*0Sstevel@tonic-gate if (!error) 128*0Sstevel@tonic-gate build_dir_entry_list(rdcp, list); 129*0Sstevel@tonic-gate else if (list) { 130*0Sstevel@tonic-gate free_dir_list(list); 131*0Sstevel@tonic-gate list = NULL; 132*0Sstevel@tonic-gate } 133*0Sstevel@tonic-gate } 134*0Sstevel@tonic-gate } else 135*0Sstevel@tonic-gate rw_rdlock(&rdcp->rwlock); 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate rd->rd_bufsize = rda->rda_count; 138*0Sstevel@tonic-gate if (!error) { 139*0Sstevel@tonic-gate error = create_dirents(rdcp, rda->rda_offset, rd); 140*0Sstevel@tonic-gate if (error) { 141*0Sstevel@tonic-gate if (rdcp->offtp) { 142*0Sstevel@tonic-gate free_offset_tbl(rdcp->offtp); 143*0Sstevel@tonic-gate rdcp->offtp = NULL; 144*0Sstevel@tonic-gate } 145*0Sstevel@tonic-gate if (rdcp->entp) { 146*0Sstevel@tonic-gate free_dir_list(rdcp->entp); 147*0Sstevel@tonic-gate rdcp->entp = NULL; 148*0Sstevel@tonic-gate } 149*0Sstevel@tonic-gate rdcp->full = 0; 150*0Sstevel@tonic-gate list = NULL; 151*0Sstevel@tonic-gate } 152*0Sstevel@tonic-gate } 153*0Sstevel@tonic-gate 154*0Sstevel@tonic-gate if (trace > 2) { 155*0Sstevel@tonic-gate /* 156*0Sstevel@tonic-gate * print this list only once 157*0Sstevel@tonic-gate */ 158*0Sstevel@tonic-gate for (l = list; l != NULL; l = l->next) 159*0Sstevel@tonic-gate trace_prt(0, "%s\n", l->name); 160*0Sstevel@tonic-gate trace_prt(0, "\n"); 161*0Sstevel@tonic-gate } 162*0Sstevel@tonic-gate 163*0Sstevel@tonic-gate if (!error) { 164*0Sstevel@tonic-gate rd->rd_status = AUTOFS_OK; 165*0Sstevel@tonic-gate if (cache_time) { 166*0Sstevel@tonic-gate /* 167*0Sstevel@tonic-gate * keep list of entries for up to 168*0Sstevel@tonic-gate * 'cache_time' seconds 169*0Sstevel@tonic-gate */ 170*0Sstevel@tonic-gate rdcp->ttl = time((time_t *)NULL) + cache_time; 171*0Sstevel@tonic-gate } else { 172*0Sstevel@tonic-gate /* 173*0Sstevel@tonic-gate * the underlying name service indicated not 174*0Sstevel@tonic-gate * to cache contents. 175*0Sstevel@tonic-gate */ 176*0Sstevel@tonic-gate if (rdcp->offtp) { 177*0Sstevel@tonic-gate free_offset_tbl(rdcp->offtp); 178*0Sstevel@tonic-gate rdcp->offtp = NULL; 179*0Sstevel@tonic-gate } 180*0Sstevel@tonic-gate if (rdcp->entp) { 181*0Sstevel@tonic-gate free_dir_list(rdcp->entp); 182*0Sstevel@tonic-gate rdcp->entp = NULL; 183*0Sstevel@tonic-gate } 184*0Sstevel@tonic-gate rdcp->full = 0; 185*0Sstevel@tonic-gate } 186*0Sstevel@tonic-gate } else { 187*0Sstevel@tonic-gate /* 188*0Sstevel@tonic-gate * return an empty list 189*0Sstevel@tonic-gate */ 190*0Sstevel@tonic-gate rd->rd_rddir.rddir_size = 0; 191*0Sstevel@tonic-gate rd->rd_rddir.rddir_eof = 1; 192*0Sstevel@tonic-gate rd->rd_rddir.rddir_entries = NULL; 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gate /* 195*0Sstevel@tonic-gate * Invalidate cache and set error 196*0Sstevel@tonic-gate */ 197*0Sstevel@tonic-gate switch (error) { 198*0Sstevel@tonic-gate case ENOENT: 199*0Sstevel@tonic-gate rd->rd_status = AUTOFS_NOENT; 200*0Sstevel@tonic-gate break; 201*0Sstevel@tonic-gate case ENOMEM: 202*0Sstevel@tonic-gate rd->rd_status = AUTOFS_NOMEM; 203*0Sstevel@tonic-gate break; 204*0Sstevel@tonic-gate default: 205*0Sstevel@tonic-gate rd->rd_status = AUTOFS_ECOMM; 206*0Sstevel@tonic-gate } 207*0Sstevel@tonic-gate } 208*0Sstevel@tonic-gate rw_unlock(&rdcp->rwlock); 209*0Sstevel@tonic-gate 210*0Sstevel@tonic-gate mutex_lock(&rdcp->lock); 211*0Sstevel@tonic-gate rdcp->in_use--; 212*0Sstevel@tonic-gate mutex_unlock(&rdcp->lock); 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate assert(rdcp->in_use >= 0); 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gate return (error); 217*0Sstevel@tonic-gate } 218*0Sstevel@tonic-gate 219*0Sstevel@tonic-gate #define roundtoint(x) (((x) + sizeof (int) - 1) & ~(sizeof (int) - 1)) 220*0Sstevel@tonic-gate #define DIRENT64_RECLEN(namelen) \ 221*0Sstevel@tonic-gate (((int)(((dirent64_t *)0)->d_name) + 1 + (namelen) + 7) & ~ 7) 222*0Sstevel@tonic-gate 223*0Sstevel@tonic-gate static int 224*0Sstevel@tonic-gate create_dirents(struct rddir_cache *rdcp, ulong_t offset, autofs_rddirres *res) 225*0Sstevel@tonic-gate { 226*0Sstevel@tonic-gate uint_t total_bytes_wanted; 227*0Sstevel@tonic-gate int bufsize; 228*0Sstevel@tonic-gate ushort_t this_reclen; 229*0Sstevel@tonic-gate int outcount = 0; 230*0Sstevel@tonic-gate int namelen; 231*0Sstevel@tonic-gate struct dir_entry *list = NULL, *l, *nl; 232*0Sstevel@tonic-gate struct dirent64 *dp; 233*0Sstevel@tonic-gate char *outbuf; 234*0Sstevel@tonic-gate struct off_tbl *offtp, *next = NULL; 235*0Sstevel@tonic-gate int this_bucket = 0; 236*0Sstevel@tonic-gate int error = 0; 237*0Sstevel@tonic-gate int x = 0, y = 0; 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate assert(RW_LOCK_HELD(&rdcp->rwlock)); 240*0Sstevel@tonic-gate for (offtp = rdcp->offtp; offtp != NULL; offtp = next) { 241*0Sstevel@tonic-gate x++; 242*0Sstevel@tonic-gate next = offtp->next; 243*0Sstevel@tonic-gate this_bucket = (next == NULL); 244*0Sstevel@tonic-gate if (!this_bucket) 245*0Sstevel@tonic-gate this_bucket = (offset < next->offset); 246*0Sstevel@tonic-gate if (this_bucket) { 247*0Sstevel@tonic-gate /* 248*0Sstevel@tonic-gate * has to be in this bucket 249*0Sstevel@tonic-gate */ 250*0Sstevel@tonic-gate assert(offset >= offtp->offset); 251*0Sstevel@tonic-gate list = offtp->first; 252*0Sstevel@tonic-gate break; 253*0Sstevel@tonic-gate } 254*0Sstevel@tonic-gate /* 255*0Sstevel@tonic-gate * loop to look in next bucket 256*0Sstevel@tonic-gate */ 257*0Sstevel@tonic-gate } 258*0Sstevel@tonic-gate 259*0Sstevel@tonic-gate for (l = list; l != NULL && l->offset < offset; l = l->next) 260*0Sstevel@tonic-gate y++; 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gate if (l == NULL) { 263*0Sstevel@tonic-gate /* 264*0Sstevel@tonic-gate * reached end of directory 265*0Sstevel@tonic-gate */ 266*0Sstevel@tonic-gate error = 0; 267*0Sstevel@tonic-gate goto empty; 268*0Sstevel@tonic-gate } 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate if (trace > 2) 271*0Sstevel@tonic-gate trace_prt(1, "%s: offset searches (%d, %d)\n", rdcp->map, x, y); 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate total_bytes_wanted = res->rd_bufsize; 274*0Sstevel@tonic-gate bufsize = total_bytes_wanted + sizeof (struct dirent64); 275*0Sstevel@tonic-gate outbuf = malloc(bufsize); 276*0Sstevel@tonic-gate if (outbuf == NULL) { 277*0Sstevel@tonic-gate syslog(LOG_ERR, "memory allocation error\n"); 278*0Sstevel@tonic-gate error = ENOMEM; 279*0Sstevel@tonic-gate goto empty; 280*0Sstevel@tonic-gate } 281*0Sstevel@tonic-gate memset(outbuf, 0, bufsize); 282*0Sstevel@tonic-gate /* LINTED pointer alignment */ 283*0Sstevel@tonic-gate dp = (struct dirent64 *)outbuf; 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate while (l) { 286*0Sstevel@tonic-gate nl = l->next; 287*0Sstevel@tonic-gate namelen = strlen(l->name); 288*0Sstevel@tonic-gate this_reclen = DIRENT64_RECLEN(namelen); 289*0Sstevel@tonic-gate if (outcount + this_reclen > total_bytes_wanted) { 290*0Sstevel@tonic-gate break; 291*0Sstevel@tonic-gate } 292*0Sstevel@tonic-gate dp->d_ino = (ino64_t)l->nodeid; 293*0Sstevel@tonic-gate if (nl) { 294*0Sstevel@tonic-gate /* 295*0Sstevel@tonic-gate * get the next elements offset 296*0Sstevel@tonic-gate */ 297*0Sstevel@tonic-gate dp->d_off = (off64_t)nl->offset; 298*0Sstevel@tonic-gate } else { 299*0Sstevel@tonic-gate /* 300*0Sstevel@tonic-gate * This is the last element 301*0Sstevel@tonic-gate * make offset one plus the current. 302*0Sstevel@tonic-gate */ 303*0Sstevel@tonic-gate dp->d_off = (off64_t)l->offset + 1; 304*0Sstevel@tonic-gate } 305*0Sstevel@tonic-gate (void) strcpy(dp->d_name, l->name); 306*0Sstevel@tonic-gate dp->d_reclen = (ushort_t)this_reclen; 307*0Sstevel@tonic-gate outcount += dp->d_reclen; 308*0Sstevel@tonic-gate dp = (struct dirent64 *)((int)dp + dp->d_reclen); 309*0Sstevel@tonic-gate assert(outcount <= total_bytes_wanted); 310*0Sstevel@tonic-gate l = l->next; 311*0Sstevel@tonic-gate } 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate res->rd_rddir.rddir_size = (long)outcount; 314*0Sstevel@tonic-gate if (outcount > 0) { 315*0Sstevel@tonic-gate /* 316*0Sstevel@tonic-gate * have some entries 317*0Sstevel@tonic-gate */ 318*0Sstevel@tonic-gate res->rd_rddir.rddir_eof = (l == NULL); 319*0Sstevel@tonic-gate /* LINTED pointer alignment */ 320*0Sstevel@tonic-gate res->rd_rddir.rddir_entries = (struct dirent64 *)outbuf; 321*0Sstevel@tonic-gate error = 0; 322*0Sstevel@tonic-gate } else { 323*0Sstevel@tonic-gate /* 324*0Sstevel@tonic-gate * total_bytes_wanted is not large enough for one 325*0Sstevel@tonic-gate * directory entry 326*0Sstevel@tonic-gate */ 327*0Sstevel@tonic-gate res->rd_rddir.rddir_eof = 0; 328*0Sstevel@tonic-gate res->rd_rddir.rddir_entries = NULL; 329*0Sstevel@tonic-gate free(outbuf); 330*0Sstevel@tonic-gate error = EIO; 331*0Sstevel@tonic-gate } 332*0Sstevel@tonic-gate return (error); 333*0Sstevel@tonic-gate 334*0Sstevel@tonic-gate empty: res->rd_rddir.rddir_size = (long)0; 335*0Sstevel@tonic-gate res->rd_rddir.rddir_eof = TRUE; 336*0Sstevel@tonic-gate res->rd_rddir.rddir_entries = NULL; 337*0Sstevel@tonic-gate return (error); 338*0Sstevel@tonic-gate } 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gate /* 342*0Sstevel@tonic-gate * add new entry to cache for 'map' 343*0Sstevel@tonic-gate */ 344*0Sstevel@tonic-gate static int 345*0Sstevel@tonic-gate rddir_cache_enter(char *map, ulong_t bucket_size, struct rddir_cache **rdcpp) 346*0Sstevel@tonic-gate { 347*0Sstevel@tonic-gate struct rddir_cache *p; 348*0Sstevel@tonic-gate assert(RW_LOCK_HELD(&rddir_cache_lock)); 349*0Sstevel@tonic-gate 350*0Sstevel@tonic-gate /* 351*0Sstevel@tonic-gate * Add to front of the list at this time 352*0Sstevel@tonic-gate */ 353*0Sstevel@tonic-gate p = (struct rddir_cache *)malloc(sizeof (*p)); 354*0Sstevel@tonic-gate if (p == NULL) { 355*0Sstevel@tonic-gate syslog(LOG_ERR, 356*0Sstevel@tonic-gate "rddir_cache_enter: memory allocation failed\n"); 357*0Sstevel@tonic-gate return (ENOMEM); 358*0Sstevel@tonic-gate } 359*0Sstevel@tonic-gate memset((char *)p, 0, sizeof (*p)); 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate p->map = malloc(strlen(map) + 1); 362*0Sstevel@tonic-gate if (p->map == NULL) { 363*0Sstevel@tonic-gate syslog(LOG_ERR, 364*0Sstevel@tonic-gate "rddir_cache_enter: memory allocation failed\n"); 365*0Sstevel@tonic-gate free(p); 366*0Sstevel@tonic-gate return (ENOMEM); 367*0Sstevel@tonic-gate } 368*0Sstevel@tonic-gate strcpy(p->map, map); 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate p->bucket_size = bucket_size; 371*0Sstevel@tonic-gate /* 372*0Sstevel@tonic-gate * no need to grab mutex lock since I haven't yet made the 373*0Sstevel@tonic-gate * node visible to the list 374*0Sstevel@tonic-gate */ 375*0Sstevel@tonic-gate p->in_use = 1; 376*0Sstevel@tonic-gate (void) rwlock_init(&p->rwlock, USYNC_THREAD, NULL); 377*0Sstevel@tonic-gate (void) mutex_init(&p->lock, USYNC_THREAD, NULL); 378*0Sstevel@tonic-gate 379*0Sstevel@tonic-gate if (rddir_head == NULL) 380*0Sstevel@tonic-gate rddir_head = p; 381*0Sstevel@tonic-gate else { 382*0Sstevel@tonic-gate p->next = rddir_head; 383*0Sstevel@tonic-gate rddir_head = p; 384*0Sstevel@tonic-gate } 385*0Sstevel@tonic-gate *rdcpp = p; 386*0Sstevel@tonic-gate 387*0Sstevel@tonic-gate return (0); 388*0Sstevel@tonic-gate } 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate /* 391*0Sstevel@tonic-gate * find 'map' in readdir cache 392*0Sstevel@tonic-gate */ 393*0Sstevel@tonic-gate int 394*0Sstevel@tonic-gate rddir_cache_lookup(char *map, struct rddir_cache **rdcpp) 395*0Sstevel@tonic-gate { 396*0Sstevel@tonic-gate struct rddir_cache *p; 397*0Sstevel@tonic-gate 398*0Sstevel@tonic-gate assert(RW_LOCK_HELD(&rddir_cache_lock)); 399*0Sstevel@tonic-gate for (p = rddir_head; p != NULL; p = p->next) { 400*0Sstevel@tonic-gate if (strcmp(p->map, map) == 0) { 401*0Sstevel@tonic-gate /* 402*0Sstevel@tonic-gate * found matching entry 403*0Sstevel@tonic-gate */ 404*0Sstevel@tonic-gate *rdcpp = p; 405*0Sstevel@tonic-gate mutex_lock(&p->lock); 406*0Sstevel@tonic-gate p->in_use++; 407*0Sstevel@tonic-gate mutex_unlock(&p->lock); 408*0Sstevel@tonic-gate return (0); 409*0Sstevel@tonic-gate } 410*0Sstevel@tonic-gate } 411*0Sstevel@tonic-gate /* 412*0Sstevel@tonic-gate * didn't find entry 413*0Sstevel@tonic-gate */ 414*0Sstevel@tonic-gate return (ENOENT); 415*0Sstevel@tonic-gate } 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate /* 418*0Sstevel@tonic-gate * free the offset table 419*0Sstevel@tonic-gate */ 420*0Sstevel@tonic-gate static void 421*0Sstevel@tonic-gate free_offset_tbl(struct off_tbl *head) 422*0Sstevel@tonic-gate { 423*0Sstevel@tonic-gate struct off_tbl *p, *next = NULL; 424*0Sstevel@tonic-gate 425*0Sstevel@tonic-gate for (p = head; p != NULL; p = next) { 426*0Sstevel@tonic-gate next = p->next; 427*0Sstevel@tonic-gate free(p); 428*0Sstevel@tonic-gate } 429*0Sstevel@tonic-gate } 430*0Sstevel@tonic-gate 431*0Sstevel@tonic-gate /* 432*0Sstevel@tonic-gate * free the directory entries 433*0Sstevel@tonic-gate */ 434*0Sstevel@tonic-gate static void 435*0Sstevel@tonic-gate free_dir_list(struct dir_entry *head) 436*0Sstevel@tonic-gate { 437*0Sstevel@tonic-gate struct dir_entry *p, *next = NULL; 438*0Sstevel@tonic-gate 439*0Sstevel@tonic-gate for (p = head; p != NULL; p = next) { 440*0Sstevel@tonic-gate next = p->next; 441*0Sstevel@tonic-gate assert(p->name); 442*0Sstevel@tonic-gate free(p->name); 443*0Sstevel@tonic-gate free(p); 444*0Sstevel@tonic-gate } 445*0Sstevel@tonic-gate } 446*0Sstevel@tonic-gate 447*0Sstevel@tonic-gate static void 448*0Sstevel@tonic-gate rddir_cache_entry_free(struct rddir_cache *p) 449*0Sstevel@tonic-gate { 450*0Sstevel@tonic-gate assert(RW_LOCK_HELD(&rddir_cache_lock)); 451*0Sstevel@tonic-gate assert(!p->in_use); 452*0Sstevel@tonic-gate if (p->map) 453*0Sstevel@tonic-gate free(p->map); 454*0Sstevel@tonic-gate if (p->offtp) 455*0Sstevel@tonic-gate free_offset_tbl(p->offtp); 456*0Sstevel@tonic-gate if (p->entp) 457*0Sstevel@tonic-gate free_dir_list(p->entp); 458*0Sstevel@tonic-gate free(p); 459*0Sstevel@tonic-gate } 460*0Sstevel@tonic-gate 461*0Sstevel@tonic-gate /* 462*0Sstevel@tonic-gate * Remove entry from the rddircache 463*0Sstevel@tonic-gate * the caller must own the rddir_cache_lock. 464*0Sstevel@tonic-gate */ 465*0Sstevel@tonic-gate static int 466*0Sstevel@tonic-gate rddir_cache_delete(struct rddir_cache *rdcp) 467*0Sstevel@tonic-gate { 468*0Sstevel@tonic-gate struct rddir_cache *p, *prev; 469*0Sstevel@tonic-gate 470*0Sstevel@tonic-gate assert(RW_LOCK_HELD(&rddir_cache_lock)); 471*0Sstevel@tonic-gate /* 472*0Sstevel@tonic-gate * Search cache for entry 473*0Sstevel@tonic-gate */ 474*0Sstevel@tonic-gate prev = NULL; 475*0Sstevel@tonic-gate for (p = rddir_head; p != NULL; p = p->next) { 476*0Sstevel@tonic-gate if (p == rdcp) { 477*0Sstevel@tonic-gate /* 478*0Sstevel@tonic-gate * entry found, remove from list if not in use 479*0Sstevel@tonic-gate */ 480*0Sstevel@tonic-gate if (p->in_use) 481*0Sstevel@tonic-gate return (EBUSY); 482*0Sstevel@tonic-gate if (prev) 483*0Sstevel@tonic-gate prev->next = p->next; 484*0Sstevel@tonic-gate else 485*0Sstevel@tonic-gate rddir_head = p->next; 486*0Sstevel@tonic-gate rddir_cache_entry_free(p); 487*0Sstevel@tonic-gate return (0); 488*0Sstevel@tonic-gate } 489*0Sstevel@tonic-gate prev = p; 490*0Sstevel@tonic-gate } 491*0Sstevel@tonic-gate syslog(LOG_ERR, "Couldn't find entry %x in cache\n", p); 492*0Sstevel@tonic-gate return (ENOENT); 493*0Sstevel@tonic-gate } 494*0Sstevel@tonic-gate 495*0Sstevel@tonic-gate /* 496*0Sstevel@tonic-gate * Return entry that matches name, NULL otherwise. 497*0Sstevel@tonic-gate * Assumes the readers lock for this list has been grabed. 498*0Sstevel@tonic-gate */ 499*0Sstevel@tonic-gate struct dir_entry * 500*0Sstevel@tonic-gate rddir_entry_lookup(char *name, struct dir_entry *list) 501*0Sstevel@tonic-gate { 502*0Sstevel@tonic-gate return (btree_lookup(list, name)); 503*0Sstevel@tonic-gate } 504*0Sstevel@tonic-gate 505*0Sstevel@tonic-gate static void 506*0Sstevel@tonic-gate build_dir_entry_list(struct rddir_cache *rdcp, struct dir_entry *list) 507*0Sstevel@tonic-gate { 508*0Sstevel@tonic-gate struct dir_entry *p; 509*0Sstevel@tonic-gate ulong_t offset = AUTOFS_DAEMONCOOKIE, offset_list = AUTOFS_DAEMONCOOKIE; 510*0Sstevel@tonic-gate struct off_tbl *offtp, *last = NULL; 511*0Sstevel@tonic-gate ino_t inonum = 4; 512*0Sstevel@tonic-gate 513*0Sstevel@tonic-gate assert(RW_LOCK_HELD(&rdcp->rwlock)); 514*0Sstevel@tonic-gate assert(rdcp->entp == NULL); 515*0Sstevel@tonic-gate rdcp->entp = list; 516*0Sstevel@tonic-gate for (p = list; p != NULL; p = p->next) { 517*0Sstevel@tonic-gate p->nodeid = inonum; 518*0Sstevel@tonic-gate p->offset = offset; 519*0Sstevel@tonic-gate if (offset >= offset_list) { 520*0Sstevel@tonic-gate /* 521*0Sstevel@tonic-gate * add node to index table 522*0Sstevel@tonic-gate */ 523*0Sstevel@tonic-gate offtp = (struct off_tbl *) 524*0Sstevel@tonic-gate malloc(sizeof (struct off_tbl)); 525*0Sstevel@tonic-gate if (offtp != NULL) { 526*0Sstevel@tonic-gate offtp->offset = offset; 527*0Sstevel@tonic-gate offtp->first = p; 528*0Sstevel@tonic-gate offtp->next = NULL; 529*0Sstevel@tonic-gate offset_list += rdcp->bucket_size; 530*0Sstevel@tonic-gate } else { 531*0Sstevel@tonic-gate syslog(LOG_ERR, 532*0Sstevel@tonic-gate "WARNING: build_dir_entry_list: could not add offset to index table\n"); 533*0Sstevel@tonic-gate continue; 534*0Sstevel@tonic-gate } 535*0Sstevel@tonic-gate /* 536*0Sstevel@tonic-gate * add to cache 537*0Sstevel@tonic-gate */ 538*0Sstevel@tonic-gate if (rdcp->offtp == NULL) 539*0Sstevel@tonic-gate rdcp->offtp = offtp; 540*0Sstevel@tonic-gate else 541*0Sstevel@tonic-gate last->next = offtp; 542*0Sstevel@tonic-gate last = offtp; 543*0Sstevel@tonic-gate } 544*0Sstevel@tonic-gate offset++; 545*0Sstevel@tonic-gate inonum += 2; /* use even numbers in daemon */ 546*0Sstevel@tonic-gate } 547*0Sstevel@tonic-gate rdcp->full = 1; 548*0Sstevel@tonic-gate } 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate mutex_t cleanup_lock; 551*0Sstevel@tonic-gate cond_t cleanup_start_cv; 552*0Sstevel@tonic-gate cond_t cleanup_done_cv; 553*0Sstevel@tonic-gate 554*0Sstevel@tonic-gate /* 555*0Sstevel@tonic-gate * cache cleanup thread starting point 556*0Sstevel@tonic-gate */ 557*0Sstevel@tonic-gate void 558*0Sstevel@tonic-gate cache_cleanup(void) 559*0Sstevel@tonic-gate { 560*0Sstevel@tonic-gate timestruc_t reltime; 561*0Sstevel@tonic-gate struct rddir_cache *p, *next = NULL; 562*0Sstevel@tonic-gate int error; 563*0Sstevel@tonic-gate 564*0Sstevel@tonic-gate mutex_init(&cleanup_lock, USYNC_THREAD, NULL); 565*0Sstevel@tonic-gate cond_init(&cleanup_start_cv, USYNC_THREAD, NULL); 566*0Sstevel@tonic-gate cond_init(&cleanup_done_cv, USYNC_THREAD, NULL); 567*0Sstevel@tonic-gate 568*0Sstevel@tonic-gate mutex_lock(&cleanup_lock); 569*0Sstevel@tonic-gate for (;;) { 570*0Sstevel@tonic-gate reltime.tv_sec = RDDIR_CACHE_TIME/2; 571*0Sstevel@tonic-gate reltime.tv_nsec = 0; 572*0Sstevel@tonic-gate 573*0Sstevel@tonic-gate /* 574*0Sstevel@tonic-gate * delay RDDIR_CACHE_TIME seconds, or until some other thread 575*0Sstevel@tonic-gate * requests that I cleanup the caches 576*0Sstevel@tonic-gate */ 577*0Sstevel@tonic-gate if (error = cond_reltimedwait( 578*0Sstevel@tonic-gate &cleanup_start_cv, &cleanup_lock, &reltime)) { 579*0Sstevel@tonic-gate if (error != ETIME) { 580*0Sstevel@tonic-gate if (trace > 1) 581*0Sstevel@tonic-gate trace_prt(1, 582*0Sstevel@tonic-gate "cleanup thread wakeup (%d)\n", error); 583*0Sstevel@tonic-gate continue; 584*0Sstevel@tonic-gate } 585*0Sstevel@tonic-gate } 586*0Sstevel@tonic-gate mutex_unlock(&cleanup_lock); 587*0Sstevel@tonic-gate 588*0Sstevel@tonic-gate /* 589*0Sstevel@tonic-gate * Perform the cache cleanup 590*0Sstevel@tonic-gate */ 591*0Sstevel@tonic-gate rw_wrlock(&rddir_cache_lock); 592*0Sstevel@tonic-gate for (p = rddir_head; p != NULL; p = next) { 593*0Sstevel@tonic-gate next = p->next; 594*0Sstevel@tonic-gate if (p->in_use > 0) { 595*0Sstevel@tonic-gate /* 596*0Sstevel@tonic-gate * cache entry busy, skip it 597*0Sstevel@tonic-gate */ 598*0Sstevel@tonic-gate if (trace > 1) { 599*0Sstevel@tonic-gate trace_prt(1, 600*0Sstevel@tonic-gate "%s cache in use\n", p->map); 601*0Sstevel@tonic-gate } 602*0Sstevel@tonic-gate continue; 603*0Sstevel@tonic-gate } 604*0Sstevel@tonic-gate /* 605*0Sstevel@tonic-gate * Cache entry is not in use, and nobody can grab a 606*0Sstevel@tonic-gate * new reference since I'm holding the rddir_cache_lock 607*0Sstevel@tonic-gate */ 608*0Sstevel@tonic-gate 609*0Sstevel@tonic-gate /* 610*0Sstevel@tonic-gate * error will be zero if some thread signaled us asking 611*0Sstevel@tonic-gate * that the caches be freed. In such case, free caches 612*0Sstevel@tonic-gate * even if they're still valid and nobody is referencing 613*0Sstevel@tonic-gate * them at this time. Otherwise, free caches only 614*0Sstevel@tonic-gate * if their time to live (ttl) has expired. 615*0Sstevel@tonic-gate */ 616*0Sstevel@tonic-gate if (error == ETIME && (p->ttl > time((time_t *)NULL))) { 617*0Sstevel@tonic-gate /* 618*0Sstevel@tonic-gate * Scheduled cache cleanup, if cache is still 619*0Sstevel@tonic-gate * valid don't free. 620*0Sstevel@tonic-gate */ 621*0Sstevel@tonic-gate if (trace > 1) { 622*0Sstevel@tonic-gate trace_prt(1, 623*0Sstevel@tonic-gate "%s cache still valid\n", p->map); 624*0Sstevel@tonic-gate } 625*0Sstevel@tonic-gate continue; 626*0Sstevel@tonic-gate } 627*0Sstevel@tonic-gate if (trace > 1) 628*0Sstevel@tonic-gate trace_prt(1, "%s freeing cache\n", p->map); 629*0Sstevel@tonic-gate assert(!p->in_use); 630*0Sstevel@tonic-gate error = rddir_cache_delete(p); 631*0Sstevel@tonic-gate assert(!error); 632*0Sstevel@tonic-gate } 633*0Sstevel@tonic-gate rw_unlock(&rddir_cache_lock); 634*0Sstevel@tonic-gate 635*0Sstevel@tonic-gate /* 636*0Sstevel@tonic-gate * wakeup the thread/threads waiting for the 637*0Sstevel@tonic-gate * cleanup to finish 638*0Sstevel@tonic-gate */ 639*0Sstevel@tonic-gate mutex_lock(&cleanup_lock); 640*0Sstevel@tonic-gate cond_broadcast(&cleanup_done_cv); 641*0Sstevel@tonic-gate } 642*0Sstevel@tonic-gate /* NOTREACHED */ 643*0Sstevel@tonic-gate } 644