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