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 /* 27 * Copyright 2018 Nexenta Systems, Inc. 28 * Copyright 2020 Joyent, Inc. 29 */ 30 31 #include <sys/types.h> 32 #include <sys/types32.h> 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <rpc/types.h> 36 #include <sys/vfs.h> 37 #include <sys/siginfo.h> 38 #include <sys/proc.h> /* for exit() declaration */ 39 #include <sys/kmem.h> 40 #include <sys/pathname.h> 41 #include <sys/debug.h> 42 #include <sys/vtrace.h> 43 #include <sys/cmn_err.h> 44 #include <sys/atomic.h> 45 #include <sys/policy.h> 46 47 #include <sharefs/sharefs.h> 48 49 /* 50 * A macro to avoid cut-and-paste errors on getting a string field 51 * from user-land. 52 */ 53 #define SHARETAB_COPYIN(field) \ 54 if (copyinstr(STRUCT_FGETP(u_sh, sh_##field), \ 55 buf, \ 56 bufsz + 1, /* Add one for extra NUL */ \ 57 &len)) { \ 58 error = EFAULT; \ 59 goto cleanup; \ 60 } \ 61 /* Need to remove 1 because copyinstr() counts the NUL */ \ 62 len--; \ 63 sh->sh_##field = kmem_alloc(len + 1, KM_SLEEP); \ 64 bcopy(buf, sh->sh_##field, len); \ 65 sh->sh_##field[len] = '\0'; \ 66 shl.shl_##field = (int)len; \ 67 sh->sh_size += shl.shl_##field; /* Debug counting */ 68 69 #define SHARETAB_DELETE_FIELD(field) \ 70 if (sh->sh_##field != NULL) { \ 71 kmem_free(sh->sh_##field, \ 72 shl ? shl->shl_##field + 1 : \ 73 strlen(sh->sh_##field) + 1); \ 74 } 75 76 static zone_key_t sharetab_zone_key; 77 78 /* 79 * Take care of cleaning up a share. 80 * If passed in a length array, use it to determine how much 81 * space to clean up. Else, figure that out. 82 */ 83 static void 84 sharefree(share_t *sh, sharefs_lens_t *shl) 85 { 86 if (sh == NULL) 87 return; 88 89 SHARETAB_DELETE_FIELD(path); 90 SHARETAB_DELETE_FIELD(res); 91 SHARETAB_DELETE_FIELD(fstype); 92 SHARETAB_DELETE_FIELD(opts); 93 SHARETAB_DELETE_FIELD(descr); 94 95 kmem_free(sh, sizeof (*sh)); 96 } 97 98 /* 99 * If there is no error, then this function is responsible for 100 * cleaning up the memory associated with the share argument. 101 */ 102 static int 103 sharefs_remove(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl) 104 { 105 int iHash; 106 sharetab_t *sht; 107 share_t *s, *p; 108 int iPath; 109 110 if (!sh) 111 return (ENOENT); 112 113 rw_enter(&sg->sharetab_lock, RW_WRITER); 114 for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) { 115 if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) 116 break; 117 } 118 119 /* 120 * There does not exist a fstype in memory which 121 * matches the share passed in. 122 */ 123 if (sht == NULL) { 124 rw_exit(&sg->sharetab_lock); 125 return (ENOENT); 126 } 127 128 iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path); 129 iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path)); 130 131 /* 132 * Now walk down the hash table and find the entry to free! 133 */ 134 for (p = NULL, s = sht->s_buckets[iHash].ssh_sh; 135 s != NULL; s = s->sh_next) { 136 /* 137 * We need exact matches. 138 */ 139 if (strcmp(sh->sh_path, s->sh_path) == 0 && 140 strlen(s->sh_path) == iPath) { 141 if (p != NULL) 142 p->sh_next = s->sh_next; 143 else 144 sht->s_buckets[iHash].ssh_sh = s->sh_next; 145 146 ASSERT(sht->s_buckets[iHash].ssh_count != 0); 147 atomic_dec_32(&sht->s_buckets[iHash].ssh_count); 148 atomic_dec_32(&sht->s_count); 149 atomic_dec_32(&sg->sharetab_count); 150 151 ASSERT(sg->sharetab_size >= s->sh_size); 152 sg->sharetab_size -= s->sh_size; 153 154 gethrestime(&sg->sharetab_mtime); 155 atomic_inc_32(&sg->sharetab_generation); 156 157 break; 158 } 159 160 p = s; 161 } 162 163 rw_exit(&sg->sharetab_lock); 164 165 if (s == NULL) 166 return (ENOENT); 167 168 s->sh_next = NULL; 169 sharefree(s, NULL); 170 171 /* We need to free the share for the caller */ 172 sharefree(sh, shl); 173 174 return (0); 175 } 176 177 /* 178 * The caller must have allocated memory for us to use. 179 */ 180 static int 181 sharefs_add(sharetab_globals_t *sg, share_t *sh, sharefs_lens_t *shl) 182 { 183 int iHash; 184 sharetab_t *sht; 185 share_t *s, *p; 186 int iPath; 187 int n; 188 189 if (sh == NULL) 190 return (ENOENT); 191 192 /* We need to find the hash buckets for the fstype */ 193 rw_enter(&sg->sharetab_lock, RW_WRITER); 194 for (sht = sg->sharefs_sharetab; sht != NULL; sht = sht->s_next) { 195 if (strcmp(sh->sh_fstype, sht->s_fstype) == 0) 196 break; 197 } 198 199 /* Did not exist, so allocate one and add it to the sharetab */ 200 if (sht == NULL) { 201 sht = kmem_zalloc(sizeof (*sht), KM_SLEEP); 202 n = strlen(sh->sh_fstype); 203 sht->s_fstype = kmem_zalloc(n + 1, KM_SLEEP); 204 (void) strncpy(sht->s_fstype, sh->sh_fstype, n); 205 206 sht->s_next = sg->sharefs_sharetab; 207 sg->sharefs_sharetab = sht; 208 } 209 210 /* Now we need to find where we have to add the entry */ 211 iPath = shl != NULL ? shl->shl_path : strlen(sh->sh_path); 212 iHash = pkp_tab_hash(sh->sh_path, strlen(sh->sh_path)); 213 214 if (shl) { 215 sh->sh_size = shl->shl_path + shl->shl_res + 216 shl->shl_fstype + shl->shl_opts + shl->shl_descr; 217 } else { 218 sh->sh_size = strlen(sh->sh_path) + 219 strlen(sh->sh_res) + strlen(sh->sh_fstype) + 220 strlen(sh->sh_opts) + strlen(sh->sh_descr); 221 } 222 223 /* We need to account for field separators and the EOL */ 224 sh->sh_size += 5; 225 226 /* Now walk down the hash table and add the new entry */ 227 for (p = NULL, s = sht->s_buckets[iHash].ssh_sh; 228 s != NULL; s = s->sh_next) { 229 /* 230 * We need exact matches. 231 * 232 * We found a matching path. Either we have a 233 * duplicate path in a share command or we are 234 * being asked to replace an existing entry. 235 */ 236 if (strcmp(sh->sh_path, s->sh_path) == 0 && 237 strlen(s->sh_path) == iPath) { 238 if (p != NULL) 239 p->sh_next = sh; 240 else 241 sht->s_buckets[iHash].ssh_sh = sh; 242 243 sh->sh_next = s->sh_next; 244 245 ASSERT(sg->sharetab_size >= s->sh_size); 246 sg->sharetab_size -= s->sh_size; 247 sg->sharetab_size += sh->sh_size; 248 249 /* Get rid of the old node */ 250 sharefree(s, NULL); 251 252 gethrestime(&sg->sharetab_mtime); 253 atomic_inc_32(&sg->sharetab_generation); 254 255 ASSERT(sht->s_buckets[iHash].ssh_count != 0); 256 rw_exit(&sg->sharetab_lock); 257 258 return (0); 259 } 260 261 p = s; 262 } 263 264 /* 265 * Okay, we have gone through the entire hash chain and not 266 * found a match. We just need to add this node. 267 */ 268 sh->sh_next = sht->s_buckets[iHash].ssh_sh; 269 sht->s_buckets[iHash].ssh_sh = sh; 270 atomic_inc_32(&sht->s_buckets[iHash].ssh_count); 271 atomic_inc_32(&sht->s_count); 272 atomic_inc_32(&sg->sharetab_count); 273 sg->sharetab_size += sh->sh_size; 274 275 gethrestime(&sg->sharetab_mtime); 276 atomic_inc_32(&sg->sharetab_generation); 277 278 rw_exit(&sg->sharetab_lock); 279 280 return (0); 281 } 282 283 /* ARGSUSED */ 284 static void * 285 sharetab_zone_init(zoneid_t zoneid) 286 { 287 sharetab_globals_t *sg; 288 289 sg = kmem_zalloc(sizeof (*sg), KM_SLEEP); 290 291 rw_init(&sg->sharetab_lock, NULL, RW_DEFAULT, NULL); 292 rw_init(&sg->sharefs_lock, NULL, RW_DEFAULT, NULL); 293 294 sg->sharetab_size = 0; 295 sg->sharetab_count = 0; 296 sg->sharetab_generation = 1; 297 298 gethrestime(&sg->sharetab_mtime); 299 gethrestime(&sg->sharetab_snap_time); 300 301 return (sg); 302 } 303 304 /* ARGSUSED */ 305 static void 306 sharetab_zone_fini(zoneid_t zoneid, void *data) 307 { 308 sharetab_globals_t *sg = data; 309 310 rw_destroy(&sg->sharefs_lock); 311 rw_destroy(&sg->sharetab_lock); 312 313 /* ALL of the allocated things must be cleaned before we free sg. */ 314 while (sg->sharefs_sharetab != NULL) { 315 int i; 316 sharetab_t *freeing = sg->sharefs_sharetab; 317 318 sg->sharefs_sharetab = freeing->s_next; 319 kmem_free(freeing->s_fstype, strlen(freeing->s_fstype) + 1); 320 for (i = 0; i < PKP_HASH_SIZE; i++) { 321 sharefs_hash_head_t *bucket; 322 323 bucket = &(freeing->s_buckets[i]); 324 while (bucket->ssh_sh != NULL) { 325 share_t *share = bucket->ssh_sh; 326 327 bucket->ssh_sh = share->sh_next; 328 sharefree(share, NULL); 329 } 330 } 331 kmem_free(freeing, sizeof (*freeing)); 332 } 333 334 kmem_free(sg, sizeof (*sg)); 335 } 336 337 void 338 sharefs_sharetab_init(void) 339 { 340 zone_key_create(&sharetab_zone_key, sharetab_zone_init, 341 NULL, sharetab_zone_fini); 342 } 343 344 sharetab_globals_t * 345 sharetab_get_globals(zone_t *zone) 346 { 347 return (zone_getspecific(sharetab_zone_key, zone)); 348 } 349 350 int 351 sharefs_impl(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen) 352 { 353 int error = 0; 354 size_t len; 355 size_t bufsz; 356 share_t *sh; 357 sharefs_lens_t shl; 358 model_t model; 359 char *buf = NULL; 360 sharetab_globals_t *sg = sharetab_get_globals(curzone); 361 362 STRUCT_DECL(share, u_sh); 363 364 bufsz = iMaxLen; 365 366 /* 367 * Before we do anything, lets make sure we have 368 * a sharetab in memory if we need one. 369 */ 370 rw_enter(&sg->sharetab_lock, RW_READER); 371 switch (opcode) { 372 case SHAREFS_REMOVE: 373 case SHAREFS_REPLACE: 374 if (!sg->sharefs_sharetab) { 375 rw_exit(&sg->sharetab_lock); 376 return (set_errno(ENOENT)); 377 } 378 break; 379 case SHAREFS_ADD: 380 default: 381 break; 382 } 383 rw_exit(&sg->sharetab_lock); 384 385 model = get_udatamodel(); 386 387 /* 388 * Initialize the data pointers. 389 */ 390 STRUCT_INIT(u_sh, model); 391 if (copyin(sh_in, STRUCT_BUF(u_sh), STRUCT_SIZE(u_sh))) 392 return (set_errno(EFAULT)); 393 394 /* Get the share */ 395 sh = kmem_zalloc(sizeof (share_t), KM_SLEEP); 396 397 /* Get some storage for copying in the strings */ 398 buf = kmem_zalloc(bufsz + 1, KM_SLEEP); 399 bzero(&shl, sizeof (sharefs_lens_t)); 400 401 /* Only grab these two until we know what we want */ 402 SHARETAB_COPYIN(path); 403 SHARETAB_COPYIN(fstype); 404 405 switch (opcode) { 406 case SHAREFS_ADD: 407 case SHAREFS_REPLACE: 408 SHARETAB_COPYIN(res); 409 SHARETAB_COPYIN(opts); 410 SHARETAB_COPYIN(descr); 411 error = sharefs_add(sg, sh, &shl); 412 break; 413 case SHAREFS_REMOVE: 414 error = sharefs_remove(sg, sh, &shl); 415 break; 416 default: 417 error = EINVAL; 418 break; 419 } 420 421 cleanup: 422 /* 423 * If there is no error, then we have stashed the structure 424 * away in the sharetab hash table or have deleted it. 425 * 426 * Either way, the only reason to blow away the data is if 427 * there was an error. 428 */ 429 if (error != 0) 430 sharefree(sh, &shl); 431 432 if (buf != NULL) 433 kmem_free(buf, bufsz + 1); 434 435 return (error != 0 ? set_errno(error) : 0); 436 } 437 438 int 439 sharefs(enum sharefs_sys_op opcode, share_t *sh_in, uint32_t iMaxLen) 440 { 441 /* 442 * If we're in the global zone PRIV_SYS_CONFIG gives us the 443 * privileges needed to act on sharetab. However if we're in 444 * a non-global zone PRIV_SYS_CONFIG is not allowed. To work 445 * around this issue PRIV_SYS_NFS is used in this case. 446 * 447 * TODO: This basically overloads the definition/use of 448 * PRIV_SYS_NFS to work around the limitation of PRIV_SYS_CONFIG 449 * in a zone. Solaris 11 solved this by implementing a PRIV_SYS_SHARE 450 * we should do the same and replace the use of PRIV_SYS_NFS here and 451 * in zfs_secpolicy_share. 452 */ 453 if (INGLOBALZONE(curproc)) { 454 if (secpolicy_sys_config(CRED(), B_FALSE) != 0) 455 return (set_errno(EPERM)); 456 } else { 457 /* behave like zfs_secpolicy_share() */ 458 if (secpolicy_nfs(CRED()) != 0) 459 return (set_errno(EPERM)); 460 461 } 462 return (sharefs_impl(opcode, sh_in, iMaxLen)); 463 } 464