1a237e38eSth199096 /* 2a237e38eSth199096 * CDDL HEADER START 3a237e38eSth199096 * 4a237e38eSth199096 * The contents of this file are subject to the terms of the 5a237e38eSth199096 * Common Development and Distribution License (the "License"). 6a237e38eSth199096 * You may not use this file except in compliance with the License. 7a237e38eSth199096 * 8a237e38eSth199096 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9a237e38eSth199096 * or http://www.opensolaris.org/os/licensing. 10a237e38eSth199096 * See the License for the specific language governing permissions 11a237e38eSth199096 * and limitations under the License. 12a237e38eSth199096 * 13a237e38eSth199096 * When distributing Covered Code, include this CDDL HEADER in each 14a237e38eSth199096 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15a237e38eSth199096 * If applicable, add the following below this CDDL HEADER, with the 16a237e38eSth199096 * fields enclosed by brackets "[]" replaced with your own identifying 17a237e38eSth199096 * information: Portions Copyright [yyyy] [name of copyright owner] 18a237e38eSth199096 * 19a237e38eSth199096 * CDDL HEADER END 20a237e38eSth199096 */ 21a237e38eSth199096 22a237e38eSth199096 /* 230616fd7fSPavel Filipensky * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 24a237e38eSth199096 */ 25a237e38eSth199096 26a237e38eSth199096 #include <sys/types.h> 27a237e38eSth199096 #include <sys/types32.h> 28a237e38eSth199096 #include <sys/param.h> 29a237e38eSth199096 #include <sys/systm.h> 30a237e38eSth199096 #include <rpc/types.h> 31a237e38eSth199096 #include <sys/vfs.h> 32a237e38eSth199096 #include <sys/siginfo.h> 33a237e38eSth199096 #include <sys/proc.h> /* for exit() declaration */ 34a237e38eSth199096 #include <sys/kmem.h> 35a237e38eSth199096 #include <sys/pathname.h> 36a237e38eSth199096 #include <sys/debug.h> 37a237e38eSth199096 #include <sys/vtrace.h> 38a237e38eSth199096 #include <sys/cmn_err.h> 39a237e38eSth199096 #include <sys/atomic.h> 40ecd6cf80Smarks #include <sys/policy.h> 41a237e38eSth199096 42a237e38eSth199096 #include <sharefs/sharefs.h> 43a237e38eSth199096 44a237e38eSth199096 /* 45a237e38eSth199096 * A macro to avoid cut-and-paste errors on getting a string field 46a237e38eSth199096 * from user-land. 47a237e38eSth199096 */ 48a237e38eSth199096 #define SHARETAB_COPYIN(field) \ 49a237e38eSth199096 if (copyinstr(STRUCT_FGETP(u_sh, sh_##field), \ 50a237e38eSth199096 buf, \ 51a237e38eSth199096 bufsz + 1, /* Add one for extra NUL */ \ 52a237e38eSth199096 &len)) { \ 53a237e38eSth199096 error = EFAULT; \ 54a237e38eSth199096 goto cleanup; \ 55a237e38eSth199096 } \ 56a237e38eSth199096 /* \ 57a237e38eSth199096 * Need to remove 1 because copyinstr() counts the NUL. \ 58a237e38eSth199096 */ \ 59a237e38eSth199096 len--; \ 60a237e38eSth199096 sh->sh_##field = kmem_alloc(len + 1, KM_SLEEP); \ 61a237e38eSth199096 bcopy(buf, sh->sh_##field, len); \ 62a237e38eSth199096 sh->sh_##field[len] = '\0'; \ 63a237e38eSth199096 shl.shl_##field = (int)len; \ 64a237e38eSth199096 sh->sh_size += shl.shl_##field; /* Debug counting */ 65a237e38eSth199096 66a237e38eSth199096 #define SHARETAB_DELETE_FIELD(field) \ 67a237e38eSth199096 if (sh->sh_##field) { \ 68a237e38eSth199096 kmem_free(sh->sh_##field, \ 69a237e38eSth199096 shl ? shl->shl_##field + 1 : \ 70a237e38eSth199096 strlen(sh->sh_##field) + 1); \ 71a237e38eSth199096 } 72a237e38eSth199096 73a237e38eSth199096 sharetab_t *sharefs_sharetab = NULL; /* The incore sharetab. */ 74a237e38eSth199096 size_t sharetab_size; 75a237e38eSth199096 uint_t sharetab_count; 76a237e38eSth199096 77a237e38eSth199096 krwlock_t sharetab_lock; /* lock to protect the cached sharetab */ 78a237e38eSth199096 79a237e38eSth199096 krwlock_t sharefs_lock; /* lock to protect the vnode ops */ 80a237e38eSth199096 81a237e38eSth199096 timestruc_t sharetab_mtime; 82a237e38eSth199096 timestruc_t sharetab_snap_time; 83a237e38eSth199096 84a237e38eSth199096 uint_t sharetab_generation; /* Only increments and wraps! */ 85a237e38eSth199096 86a237e38eSth199096 /* 87a237e38eSth199096 * Take care of cleaning up a share. 88a237e38eSth199096 * If passed in a length array, use it to determine how much 89a237e38eSth199096 * space to clean up. Else, figure that out. 90a237e38eSth199096 */ 91a237e38eSth199096 static void 92a237e38eSth199096 sharefree(share_t *sh, sharefs_lens_t *shl) 93a237e38eSth199096 { 94a237e38eSth199096 if (!sh) 95a237e38eSth199096 return; 96a237e38eSth199096 97a237e38eSth199096 SHARETAB_DELETE_FIELD(path); 98a237e38eSth199096 SHARETAB_DELETE_FIELD(res); 99a237e38eSth199096 SHARETAB_DELETE_FIELD(fstype); 100a237e38eSth199096 SHARETAB_DELETE_FIELD(opts); 101a237e38eSth199096 SHARETAB_DELETE_FIELD(descr); 102a237e38eSth199096 103a237e38eSth199096 kmem_free(sh, sizeof (share_t)); 104a237e38eSth199096 } 105a237e38eSth199096 106a237e38eSth199096 /* 107a237e38eSth199096 * If there is no error, then this function is responsible for 108a237e38eSth199096 * cleaning up the memory associated with the share argument. 109a237e38eSth199096 */ 110a237e38eSth199096 static int 111a237e38eSth199096 sharefs_remove(share_t *sh, sharefs_lens_t *shl) 112a237e38eSth199096 { 113a237e38eSth199096 int iHash; 114a237e38eSth199096 sharetab_t *sht; 115a237e38eSth199096 share_t *s, *p; 116a237e38eSth199096 int iPath; 117a237e38eSth199096 118a237e38eSth199096 if (!sh) 119a237e38eSth199096 return (ENOENT); 120a237e38eSth199096 121a237e38eSth199096 rw_enter(&sharetab_lock, RW_WRITER); 122a237e38eSth199096 for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) { 123a237e38eSth199096 if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) { 124a237e38eSth199096 break; 125a237e38eSth199096 } 126a237e38eSth199096 } 127a237e38eSth199096 128a237e38eSth199096 /* 129a237e38eSth199096 * There does not exist a fstype in memory which 130a237e38eSth199096 * matches the share passed in. 131a237e38eSth199096 */ 132a237e38eSth199096 if (!sht) { 133a237e38eSth199096 rw_exit(&sharetab_lock); 134a237e38eSth199096 return (ENOENT); 135a237e38eSth199096 } 136a237e38eSth199096 137a237e38eSth199096 iPath = shl ? shl->shl_path : strlen(sh->sh_path); 1380616fd7fSPavel Filipensky iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path)); 139a237e38eSth199096 140a237e38eSth199096 /* 141a237e38eSth199096 * Now walk down the hash table and find the entry to free! 142a237e38eSth199096 */ 143a237e38eSth199096 for (p = NULL, s = sht->s_buckets[iHash].ssh_sh; 144ecd6cf80Smarks s != NULL; s = s->sh_next) { 145a237e38eSth199096 /* 146a237e38eSth199096 * We need exact matches. 147a237e38eSth199096 */ 148a237e38eSth199096 if (strcmp(sh->sh_path, s->sh_path) == 0 && 149a237e38eSth199096 strlen(s->sh_path) == iPath) { 150a237e38eSth199096 if (p) { 151a237e38eSth199096 p->sh_next = s->sh_next; 152a237e38eSth199096 } else { 153a237e38eSth199096 sht->s_buckets[iHash].ssh_sh = s->sh_next; 154a237e38eSth199096 } 155a237e38eSth199096 156a237e38eSth199096 ASSERT(sht->s_buckets[iHash].ssh_count != 0); 157*1a5e258fSJosef 'Jeff' Sipek atomic_dec_32(&sht->s_buckets[iHash].ssh_count); 158*1a5e258fSJosef 'Jeff' Sipek atomic_dec_32(&sht->s_count); 159*1a5e258fSJosef 'Jeff' Sipek atomic_dec_32(&sharetab_count); 160a237e38eSth199096 161a237e38eSth199096 ASSERT(sharetab_size >= s->sh_size); 162a237e38eSth199096 sharetab_size -= s->sh_size; 163a237e38eSth199096 164a237e38eSth199096 gethrestime(&sharetab_mtime); 165*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&sharetab_generation); 166a237e38eSth199096 167a237e38eSth199096 break; 168a237e38eSth199096 } 169a237e38eSth199096 170a237e38eSth199096 p = s; 171a237e38eSth199096 } 172a237e38eSth199096 173a237e38eSth199096 rw_exit(&sharetab_lock); 174a237e38eSth199096 175a237e38eSth199096 if (!s) { 176a237e38eSth199096 return (ENOENT); 177a237e38eSth199096 } 178a237e38eSth199096 179a237e38eSth199096 s->sh_next = NULL; 180a237e38eSth199096 sharefree(s, NULL); 181a237e38eSth199096 182a237e38eSth199096 /* 183a237e38eSth199096 * We need to free the share for the caller. 184a237e38eSth199096 */ 185a237e38eSth199096 sharefree(sh, shl); 186a237e38eSth199096 187a237e38eSth199096 return (0); 188a237e38eSth199096 } 189a237e38eSth199096 190a237e38eSth199096 /* 191a237e38eSth199096 * The caller must have allocated memory for us to use. 192a237e38eSth199096 */ 193a237e38eSth199096 static int 194a237e38eSth199096 sharefs_add(share_t *sh, sharefs_lens_t *shl) 195a237e38eSth199096 { 196a237e38eSth199096 int iHash; 197a237e38eSth199096 sharetab_t *sht; 198a237e38eSth199096 share_t *s, *p; 199a237e38eSth199096 int iPath; 200a237e38eSth199096 int n; 201a237e38eSth199096 202a237e38eSth199096 if (!sh) { 203a237e38eSth199096 return (ENOENT); 204a237e38eSth199096 } 205a237e38eSth199096 206a237e38eSth199096 /* 207a237e38eSth199096 * We need to find the hash buckets for the fstype. 208a237e38eSth199096 */ 209a237e38eSth199096 rw_enter(&sharetab_lock, RW_WRITER); 210a237e38eSth199096 for (sht = sharefs_sharetab; sht != NULL; sht = sht->s_next) { 211a237e38eSth199096 if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) { 212a237e38eSth199096 break; 213a237e38eSth199096 } 214a237e38eSth199096 } 215a237e38eSth199096 216a237e38eSth199096 /* 217a237e38eSth199096 * Did not exist, so allocate one and add it to the 218a237e38eSth199096 * sharetab. 219a237e38eSth199096 */ 220a237e38eSth199096 if (!sht) { 221a237e38eSth199096 sht = kmem_zalloc(sizeof (*sht), KM_SLEEP); 222a237e38eSth199096 n = strlen(sh->sh_fstype); 223a237e38eSth199096 sht->s_fstype = kmem_zalloc(n + 1, KM_SLEEP); 224a237e38eSth199096 (void) strncpy(sht->s_fstype, sh->sh_fstype, n); 225a237e38eSth199096 226a237e38eSth199096 sht->s_next = sharefs_sharetab; 227a237e38eSth199096 sharefs_sharetab = sht; 228a237e38eSth199096 } 229a237e38eSth199096 230a237e38eSth199096 /* 231a237e38eSth199096 * Now we need to find where we have to add the entry. 232a237e38eSth199096 */ 2330616fd7fSPavel Filipensky iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path)); 234a237e38eSth199096 235a237e38eSth199096 iPath = shl ? shl->shl_path : strlen(sh->sh_path); 236a237e38eSth199096 237a237e38eSth199096 if (shl) { 238a237e38eSth199096 sh->sh_size = shl->shl_path + shl->shl_res + 239ecd6cf80Smarks shl->shl_fstype + shl->shl_opts + shl->shl_descr; 240a237e38eSth199096 } else { 241a237e38eSth199096 sh->sh_size = strlen(sh->sh_path) + 242ecd6cf80Smarks strlen(sh->sh_res) + strlen(sh->sh_fstype) + 243ecd6cf80Smarks strlen(sh->sh_opts) + strlen(sh->sh_descr); 244a237e38eSth199096 } 245a237e38eSth199096 246a237e38eSth199096 /* 247a237e38eSth199096 * We need to account for field seperators and 248a237e38eSth199096 * the EOL. 249a237e38eSth199096 */ 250a237e38eSth199096 sh->sh_size += 5; 251a237e38eSth199096 252a237e38eSth199096 /* 253a237e38eSth199096 * Now walk down the hash table and add the new entry! 254a237e38eSth199096 */ 255a237e38eSth199096 for (p = NULL, s = sht->s_buckets[iHash].ssh_sh; 256ecd6cf80Smarks s != NULL; s = s->sh_next) { 257a237e38eSth199096 /* 258a237e38eSth199096 * We need exact matches. 259a237e38eSth199096 * 260a237e38eSth199096 * We found a matching path. Either we have a 261a237e38eSth199096 * duplicate path in a share command or we are 262a237e38eSth199096 * being asked to replace an existing entry. 263a237e38eSth199096 */ 264a237e38eSth199096 if (strcmp(sh->sh_path, s->sh_path) == 0 && 265a237e38eSth199096 strlen(s->sh_path) == iPath) { 266a237e38eSth199096 if (p) { 267a237e38eSth199096 p->sh_next = sh; 268a237e38eSth199096 } else { 269a237e38eSth199096 sht->s_buckets[iHash].ssh_sh = sh; 270a237e38eSth199096 } 271a237e38eSth199096 272a237e38eSth199096 sh->sh_next = s->sh_next; 273a237e38eSth199096 274a237e38eSth199096 ASSERT(sharetab_size >= s->sh_size); 275a237e38eSth199096 sharetab_size -= s->sh_size; 276a237e38eSth199096 sharetab_size += sh->sh_size; 277a237e38eSth199096 278a237e38eSth199096 /* 279a237e38eSth199096 * Get rid of the old node. 280a237e38eSth199096 */ 281a237e38eSth199096 sharefree(s, NULL); 282a237e38eSth199096 283a237e38eSth199096 gethrestime(&sharetab_mtime); 284*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&sharetab_generation); 285a237e38eSth199096 286a237e38eSth199096 ASSERT(sht->s_buckets[iHash].ssh_count != 0); 287a237e38eSth199096 rw_exit(&sharetab_lock); 288a237e38eSth199096 289a237e38eSth199096 return (0); 290a237e38eSth199096 } 291a237e38eSth199096 292a237e38eSth199096 p = s; 293a237e38eSth199096 } 294a237e38eSth199096 295a237e38eSth199096 /* 296a237e38eSth199096 * Okay, we have gone through the entire hash chain and not 297a237e38eSth199096 * found a match. We just need to add this node. 298a237e38eSth199096 */ 299a237e38eSth199096 sh->sh_next = sht->s_buckets[iHash].ssh_sh; 300a237e38eSth199096 sht->s_buckets[iHash].ssh_sh = sh; 301*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&sht->s_buckets[iHash].ssh_count); 302*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&sht->s_count); 303*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&sharetab_count); 304a237e38eSth199096 sharetab_size += sh->sh_size; 305a237e38eSth199096 306a237e38eSth199096 gethrestime(&sharetab_mtime); 307*1a5e258fSJosef 'Jeff' Sipek atomic_inc_32(&sharetab_generation); 308a237e38eSth199096 309a237e38eSth199096 rw_exit(&sharetab_lock); 310a237e38eSth199096 311a237e38eSth199096 return (0); 312a237e38eSth199096 } 313a237e38eSth199096 314a237e38eSth199096 void 315a237e38eSth199096 sharefs_sharetab_init(void) 316a237e38eSth199096 { 317a237e38eSth199096 rw_init(&sharetab_lock, NULL, RW_DEFAULT, NULL); 318a237e38eSth199096 rw_init(&sharefs_lock, NULL, RW_DEFAULT, NULL); 319a237e38eSth199096 320a237e38eSth199096 sharetab_size = 0; 321a237e38eSth199096 sharetab_count = 0; 322a237e38eSth199096 sharetab_generation = 1; 323a237e38eSth199096 324a237e38eSth199096 gethrestime(&sharetab_mtime); 325a237e38eSth199096 gethrestime(&sharetab_snap_time); 326a237e38eSth199096 } 327a237e38eSth199096 328a237e38eSth199096 int 329ecd6cf80Smarks sharefs_impl(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen) 330a237e38eSth199096 { 331a237e38eSth199096 int error = 0; 332a237e38eSth199096 size_t len; 333a237e38eSth199096 size_t bufsz; 334a237e38eSth199096 share_t *sh; 335a237e38eSth199096 336a237e38eSth199096 sharefs_lens_t shl; 337a237e38eSth199096 338a237e38eSth199096 model_t model; 339a237e38eSth199096 340a237e38eSth199096 char *buf = NULL; 341a237e38eSth199096 342a237e38eSth199096 STRUCT_DECL(share, u_sh); 343a237e38eSth199096 344a237e38eSth199096 bufsz = iMaxLen; 345a237e38eSth199096 346a237e38eSth199096 /* 347a237e38eSth199096 * Before we do anything, lets make sure we have 348a237e38eSth199096 * a sharetab in memory if we need one. 349a237e38eSth199096 */ 350a237e38eSth199096 rw_enter(&sharetab_lock, RW_READER); 351a237e38eSth199096 switch (opcode) { 352a237e38eSth199096 case (SHAREFS_REMOVE) : 353a237e38eSth199096 case (SHAREFS_REPLACE) : 354a237e38eSth199096 if (!sharefs_sharetab) { 355a237e38eSth199096 rw_exit(&sharetab_lock); 356a237e38eSth199096 return (set_errno(ENOENT)); 357a237e38eSth199096 } 358a237e38eSth199096 break; 359a237e38eSth199096 case (SHAREFS_ADD) : 360a237e38eSth199096 default : 361a237e38eSth199096 break; 362a237e38eSth199096 } 363a237e38eSth199096 rw_exit(&sharetab_lock); 364a237e38eSth199096 365a237e38eSth199096 model = get_udatamodel(); 366a237e38eSth199096 367a237e38eSth199096 /* 368a237e38eSth199096 * Initialize the data pointers. 369a237e38eSth199096 */ 370a237e38eSth199096 STRUCT_INIT(u_sh, model); 371ecd6cf80Smarks if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh))) { 372a237e38eSth199096 return (set_errno(EFAULT)); 373a237e38eSth199096 } 374a237e38eSth199096 375a237e38eSth199096 /* 376a237e38eSth199096 * Get the share. 377a237e38eSth199096 */ 378a237e38eSth199096 sh = kmem_zalloc(sizeof (share_t), KM_SLEEP); 379a237e38eSth199096 380a237e38eSth199096 /* 381a237e38eSth199096 * Get some storage for copying in the strings. 382a237e38eSth199096 */ 383a237e38eSth199096 buf = kmem_zalloc(bufsz + 1, KM_SLEEP); 384a237e38eSth199096 bzero(&shl, sizeof (sharefs_lens_t)); 385a237e38eSth199096 386a237e38eSth199096 /* 387a237e38eSth199096 * Only grab these two until we know what we want. 388a237e38eSth199096 */ 389a237e38eSth199096 SHARETAB_COPYIN(path); 390a237e38eSth199096 SHARETAB_COPYIN(fstype); 391a237e38eSth199096 392a237e38eSth199096 switch (opcode) { 393a237e38eSth199096 case (SHAREFS_ADD) : 394a237e38eSth199096 case (SHAREFS_REPLACE) : 395a237e38eSth199096 SHARETAB_COPYIN(res); 396a237e38eSth199096 SHARETAB_COPYIN(opts); 397a237e38eSth199096 SHARETAB_COPYIN(descr); 398a237e38eSth199096 399a237e38eSth199096 error = sharefs_add(sh, &shl); 400a237e38eSth199096 break; 401a237e38eSth199096 402a237e38eSth199096 case (SHAREFS_REMOVE) : 403a237e38eSth199096 404a237e38eSth199096 error = sharefs_remove(sh, &shl); 405a237e38eSth199096 break; 406a237e38eSth199096 407a237e38eSth199096 default: 408a237e38eSth199096 error = EINVAL; 409a237e38eSth199096 break; 410a237e38eSth199096 } 411a237e38eSth199096 412a237e38eSth199096 cleanup: 413a237e38eSth199096 414a237e38eSth199096 /* 415a237e38eSth199096 * If there is no error, then we have stashed the structure 416a237e38eSth199096 * away in the sharetab hash table or have deleted it. 417a237e38eSth199096 * 418a237e38eSth199096 * Either way, the only reason to blow away the data is if 419a237e38eSth199096 * there was an error. 420a237e38eSth199096 */ 421a237e38eSth199096 if (error != 0) { 422a237e38eSth199096 sharefree(sh, &shl); 423a237e38eSth199096 } 424a237e38eSth199096 425a237e38eSth199096 if (buf) { 426a237e38eSth199096 kmem_free(buf, bufsz + 1); 427a237e38eSth199096 } 428a237e38eSth199096 429a237e38eSth199096 return ((error != 0) ? set_errno(error) : 0); 430a237e38eSth199096 } 431ecd6cf80Smarks 432ecd6cf80Smarks int 433ecd6cf80Smarks sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen) 434ecd6cf80Smarks { 435ecd6cf80Smarks if (secpolicy_sys_config(CRED(), B_FALSE) != 0) 436ecd6cf80Smarks return (set_errno(EPERM)); 437ecd6cf80Smarks 438ecd6cf80Smarks return (sharefs_impl(opcode, sh_in, iMaxLen)); 439ecd6cf80Smarks } 440