1/*- 2 * Copyright (c) 1999 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32#include "opt_compat.h" 33#include "opt_kdtrace.h" 34 35#include <sys/param.h> 36#include <sys/kernel.h> 37#include <sys/sdt.h> 38#include <sys/systm.h> 39#include <sys/sysctl.h> 40#include <sys/proc.h> 41#include <sys/malloc.h> 42#include <sys/mount.h> 43#include <sys/jail.h> 44#include <sys/lock.h> 45#include <sys/mutex.h> 46#include <sys/sx.h> 47 48#ifdef COMPAT_LINUX32 49#include <machine/../linux32/linux.h> 50#else 51#include <machine/../linux/linux.h> 52#endif 53#include <compat/linux/linux_dtrace.h> 54#include <compat/linux/linux_mib.h> 55#include <compat/linux/linux_misc.h> 56 57/* DTrace init */ 58LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); 59 60/** 61 * DTrace probes in this module. 62 */ 63LIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_osname, entry); 64LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osname, sysctl_string_error, "int"); 65LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osname, return, "int"); 66 67LIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_osrelease, entry); 68LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osrelease, sysctl_string_error, "int"); 69LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osrelease, return, "int"); 70LIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_oss_version, entry); 71LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_oss_version, sysctl_string_error, 72 "int"); 73LIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_oss_version, return, "int"); 74LIN_SDT_PROBE_DEFINE2(mib, linux_map_osrel, entry, "char *", "int *"); 75LIN_SDT_PROBE_DEFINE1(mib, linux_map_osrel, return, "int"); 76LIN_SDT_PROBE_DEFINE2(mib, linux_get_prison, entry, "struct prison *", 77 "struct prison **"); 78LIN_SDT_PROBE_DEFINE1(mib, linux_get_prison, return, "struct linux_prison *"); 79LIN_SDT_PROBE_DEFINE2(mib, linux_alloc_prison, entry, "struct prison *", 80 "struct linux_prison **"); 81LIN_SDT_PROBE_DEFINE1(mib, linux_alloc_prison, return, "int"); 82LIN_SDT_PROBE_DEFINE2(mib, linux_prison_create, entry, "void *", "void *"); 83LIN_SDT_PROBE_DEFINE1(mib, linux_prison_create, vfs_copyopt_error, "int"); 84LIN_SDT_PROBE_DEFINE1(mib, linux_prison_create, return, "int"); 85LIN_SDT_PROBE_DEFINE2(mib, linux_prison_check, entry, "void *", "void *"); 86LIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, vfs_copyopt_error, "int"); 87LIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, vfs_getopt_error, "int"); 88LIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, return, "int"); 89LIN_SDT_PROBE_DEFINE2(mib, linux_prison_set, entry, "void *", "void *"); 90LIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, vfs_copyopt_error, "int"); 91LIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, vfs_getopt_error, "int"); 92LIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, return, "int"); 93LIN_SDT_PROBE_DEFINE2(mib, linux_prison_get, entry, "void *", "void *"); 94LIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, vfs_setopt_error, "int"); 95LIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, vfs_setopts_error, "int"); 96LIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, return, "int"); 97LIN_SDT_PROBE_DEFINE1(mib, linux_prison_destructor, entry, "void *"); 98LIN_SDT_PROBE_DEFINE0(mib, linux_prison_destructor, return); 99LIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_register, entry); 100LIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_register, return); 101LIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_deregister, entry); 102LIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_deregister, return); 103LIN_SDT_PROBE_DEFINE2(mib, linux_get_osname, entry, "struct thread *", 104 "char *"); 105LIN_SDT_PROBE_DEFINE0(mib, linux_get_osname, return); 106LIN_SDT_PROBE_DEFINE2(mib, linux_set_osname, entry, "struct thread *", 107 "char *"); 108LIN_SDT_PROBE_DEFINE1(mib, linux_set_osname, return, "int"); 109LIN_SDT_PROBE_DEFINE2(mib, linux_get_osrelease, entry, "struct thread *", 110 "char *"); 111LIN_SDT_PROBE_DEFINE0(mib, linux_get_osrelease, return); 112LIN_SDT_PROBE_DEFINE1(mib, linux_kernver, entry, "struct thread *"); 113LIN_SDT_PROBE_DEFINE1(mib, linux_kernver, return, "int"); 114LIN_SDT_PROBE_DEFINE2(mib, linux_set_osrelease, entry, "struct thread *", 115 "char *"); 116LIN_SDT_PROBE_DEFINE1(mib, linux_set_osrelease, return, "int"); 117LIN_SDT_PROBE_DEFINE1(mib, linux_get_oss_version, entry, "struct thread *"); 118LIN_SDT_PROBE_DEFINE1(mib, linux_get_oss_version, return, "int"); 119 120LIN_SDT_PROBE_DEFINE2(mib, linux_set_oss_version, entry, "struct thread *", 121 "int"); 122LIN_SDT_PROBE_DEFINE1(mib, linux_set_oss_version, return, "int"); 123 124struct linux_prison { 125 char pr_osname[LINUX_MAX_UTSNAME]; 126 char pr_osrelease[LINUX_MAX_UTSNAME]; 127 int pr_oss_version; 128 int pr_osrel; 129}; 130 131static struct linux_prison lprison0 = { 132 .pr_osname = "Linux", 133 .pr_osrelease = "2.6.16", 134 .pr_oss_version = 0x030600, 135 .pr_osrel = 2006016 136}; 137 138static unsigned linux_osd_jail_slot; 139 140static SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW, 0, 141 "Linux mode"); 142 143static int linux_set_osname(struct thread *td, char *osname); 144static int linux_set_osrelease(struct thread *td, char *osrelease); 145static int linux_set_oss_version(struct thread *td, int oss_version); 146 147static int 148linux_sysctl_osname(SYSCTL_HANDLER_ARGS) 149{ 150 char osname[LINUX_MAX_UTSNAME]; 151 int error; 152 153 LIN_SDT_PROBE0(mib, linux_sysctl_osname, entry); 154 155 linux_get_osname(req->td, osname); 156 error = sysctl_handle_string(oidp, osname, LINUX_MAX_UTSNAME, req); 157 if (error != 0 || req->newptr == NULL) { 158 LIN_SDT_PROBE1(mib, linux_sysctl_osname, sysctl_string_error, 159 error); 160 LIN_SDT_PROBE1(mib, linux_sysctl_osname, return, error); 161 return (error); 162 } 163 error = linux_set_osname(req->td, osname); 164 165 LIN_SDT_PROBE1(mib, linux_sysctl_osname, return, error); 166 return (error); 167} 168 169SYSCTL_PROC(_compat_linux, OID_AUTO, osname, 170 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 171 0, 0, linux_sysctl_osname, "A", 172 "Linux kernel OS name"); 173 174static int 175linux_sysctl_osrelease(SYSCTL_HANDLER_ARGS) 176{ 177 char osrelease[LINUX_MAX_UTSNAME]; 178 int error; 179 180 LIN_SDT_PROBE0(mib, linux_sysctl_osrelease, entry); 181 182 linux_get_osrelease(req->td, osrelease); 183 error = sysctl_handle_string(oidp, osrelease, LINUX_MAX_UTSNAME, req); 184 if (error != 0 || req->newptr == NULL) { 185 LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, sysctl_string_error, 186 error); 187 LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, return, error); 188 return (error); 189 } 190 error = linux_set_osrelease(req->td, osrelease); 191 192 LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, return, error); 193 return (error); 194} 195 196SYSCTL_PROC(_compat_linux, OID_AUTO, osrelease, 197 CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 198 0, 0, linux_sysctl_osrelease, "A", 199 "Linux kernel OS release"); 200 201static int 202linux_sysctl_oss_version(SYSCTL_HANDLER_ARGS) 203{ 204 int oss_version; 205 int error; 206 207 LIN_SDT_PROBE0(mib, linux_sysctl_oss_version, entry); 208 209 oss_version = linux_get_oss_version(req->td); 210 error = sysctl_handle_int(oidp, &oss_version, 0, req); 211 if (error != 0 || req->newptr == NULL) { 212 LIN_SDT_PROBE1(mib, linux_sysctl_oss_version, 213 sysctl_string_error, error); 214 LIN_SDT_PROBE1(mib, linux_sysctl_oss_version, return, error); 215 return (error); 216 } 217 error = linux_set_oss_version(req->td, oss_version); 218 219 LIN_SDT_PROBE1(mib, linux_sysctl_oss_version, return, error); 220 return (error); 221} 222 223SYSCTL_PROC(_compat_linux, OID_AUTO, oss_version, 224 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 225 0, 0, linux_sysctl_oss_version, "I", 226 "Linux OSS version"); 227 228/* 229 * Map the osrelease into integer 230 */ 231static int 232linux_map_osrel(char *osrelease, int *osrel) 233{ 234 char *sep, *eosrelease; 235 int len, v0, v1, v2, v; 236 237 LIN_SDT_PROBE2(mib, linux_map_osrel, entry, osrelease, osrel); 238 239 len = strlen(osrelease); 240 eosrelease = osrelease + len; 241 v0 = strtol(osrelease, &sep, 10); 242 if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.') { 243 LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 244 return (EINVAL); 245 } 246 osrelease = sep + 1; 247 v1 = strtol(osrelease, &sep, 10); 248 if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.') { 249 LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 250 return (EINVAL); 251 } 252 osrelease = sep + 1; 253 v2 = strtol(osrelease, &sep, 10); 254 if (osrelease == sep || sep != eosrelease) { 255 LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 256 return (EINVAL); 257 } 258 259 v = v0 * 1000000 + v1 * 1000 + v2; 260 if (v < 1000000) { 261 LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 262 return (EINVAL); 263 } 264 265 *osrel = v; 266 267 LIN_SDT_PROBE1(mib, linux_map_osrel, return, 0); 268 return (0); 269} 270 271/* 272 * Find a prison with Linux info. 273 * Return the Linux info and the (locked) prison. 274 */ 275static struct linux_prison * 276linux_find_prison(struct prison *spr, struct prison **prp) 277{ 278 struct prison *pr; 279 struct linux_prison *lpr; 280 281 LIN_SDT_PROBE2(mib, linux_get_prison, entry, spr, prp); 282 283 if (!linux_osd_jail_slot) 284 /* In case osd_register failed. */ 285 spr = &prison0; 286 for (pr = spr;; pr = pr->pr_parent) { 287 mtx_lock(&pr->pr_mtx); 288 lpr = (pr == &prison0) 289 ? &lprison0 290 : osd_jail_get(pr, linux_osd_jail_slot); 291 if (lpr != NULL) 292 break; 293 mtx_unlock(&pr->pr_mtx); 294 } 295 *prp = pr; 296 297 LIN_SDT_PROBE1(mib, linux_get_prison, return, lpr); 298 return (lpr); 299} 300 301/* 302 * Ensure a prison has its own Linux info. If lprp is non-null, point it to 303 * the Linux info and lock the prison. 304 */ 305static int 306linux_alloc_prison(struct prison *pr, struct linux_prison **lprp) 307{ 308 struct prison *ppr; 309 struct linux_prison *lpr, *nlpr; 310 int error; 311 312 LIN_SDT_PROBE2(mib, linux_alloc_prison, entry, pr, lprp); 313 314 /* If this prison already has Linux info, return that. */ 315 error = 0; 316 lpr = linux_find_prison(pr, &ppr); 317 if (ppr == pr) 318 goto done; 319 /* 320 * Allocate a new info record. Then check again, in case something 321 * changed during the allocation. 322 */ 323 mtx_unlock(&ppr->pr_mtx); 324 nlpr = malloc(sizeof(struct linux_prison), M_PRISON, M_WAITOK); 325 lpr = linux_find_prison(pr, &ppr); 326 if (ppr == pr) { 327 free(nlpr, M_PRISON); 328 goto done; 329 } 330 /* Inherit the initial values from the ancestor. */ 331 mtx_lock(&pr->pr_mtx); 332 error = osd_jail_set(pr, linux_osd_jail_slot, nlpr); 333 if (error == 0) { 334 bcopy(lpr, nlpr, sizeof(*lpr)); 335 lpr = nlpr; 336 } else { 337 free(nlpr, M_PRISON); 338 lpr = NULL; 339 } 340 mtx_unlock(&ppr->pr_mtx); 341 done: 342 if (lprp != NULL) 343 *lprp = lpr; 344 else 345 mtx_unlock(&pr->pr_mtx); 346 347 LIN_SDT_PROBE1(mib, linux_alloc_prison, return, error); 348 return (error); 349} 350 351/* 352 * Jail OSD methods for Linux prison data. 353 */ 354static int 355linux_prison_create(void *obj, void *data) 356{ 357 struct prison *pr = obj; 358 struct vfsoptlist *opts = data; 359 int jsys, error; 360 361 LIN_SDT_PROBE2(mib, linux_prison_create, entry, obj, data); 362 363 error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)); 364 if (error != 0) { 365 LIN_SDT_PROBE1(mib, linux_prison_create, vfs_copyopt_error, 366 error); 367 } else if (jsys == JAIL_SYS_INHERIT) { 368 LIN_SDT_PROBE1(mib, linux_prison_create, return, 0); 369 return (0); 370 } 371 /* 372 * Inherit a prison's initial values from its parent 373 * (different from JAIL_SYS_INHERIT which also inherits changes). 374 */ 375 error = linux_alloc_prison(pr, NULL); 376 377 LIN_SDT_PROBE1(mib, linux_prison_create, return, error); 378 return (error); 379} 380 381static int 382linux_prison_check(void *obj __unused, void *data) 383{ 384 struct vfsoptlist *opts = data; 385 char *osname, *osrelease; 386 int error, jsys, len, osrel, oss_version; 387 388 LIN_SDT_PROBE2(mib, linux_prison_check, entry, obj, data); 389 390 /* Check that the parameters are correct. */ 391 error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)); 392 if (error != 0) { 393 LIN_SDT_PROBE1(mib, linux_prison_check, vfs_copyopt_error, 394 error); 395 } 396 if (error != ENOENT) { 397 if (error != 0) { 398 LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 399 return (error); 400 } 401 if (jsys != JAIL_SYS_NEW && jsys != JAIL_SYS_INHERIT) { 402 LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL); 403 return (EINVAL); 404 } 405 } 406 error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len); 407 if (error != 0) { 408 LIN_SDT_PROBE1(mib, linux_prison_check, vfs_getopt_error, 409 error); 410 } 411 if (error != ENOENT) { 412 if (error != 0) { 413 LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 414 return (error); 415 } 416 if (len == 0 || osname[len - 1] != '\0') { 417 LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL); 418 return (EINVAL); 419 } 420 if (len > LINUX_MAX_UTSNAME) { 421 vfs_opterror(opts, "linux.osname too long"); 422 LIN_SDT_PROBE1(mib, linux_prison_check, return, 423 ENAMETOOLONG); 424 return (ENAMETOOLONG); 425 } 426 } 427 error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len); 428 if (error != 0) { 429 LIN_SDT_PROBE1(mib, linux_prison_check, vfs_getopt_error, 430 error); 431 } 432 if (error != ENOENT) { 433 if (error != 0) { 434 LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 435 return (error); 436 } 437 if (len == 0 || osrelease[len - 1] != '\0') { 438 LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL); 439 return (EINVAL); 440 } 441 if (len > LINUX_MAX_UTSNAME) { 442 vfs_opterror(opts, "linux.osrelease too long"); 443 LIN_SDT_PROBE1(mib, linux_prison_check, return, 444 ENAMETOOLONG); 445 return (ENAMETOOLONG); 446 } 447 error = linux_map_osrel(osrelease, &osrel); 448 if (error != 0) { 449 vfs_opterror(opts, "linux.osrelease format error"); 450 LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 451 return (error); 452 } 453 } 454 error = vfs_copyopt(opts, "linux.oss_version", &oss_version, 455 sizeof(oss_version)); 456 if (error != 0) 457 LIN_SDT_PROBE1(mib, linux_prison_check, vfs_copyopt_error, error); 458 459 if (error == ENOENT) 460 error = 0; 461 LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 462 return (error); 463} 464 465static int 466linux_prison_set(void *obj, void *data) 467{ 468 struct linux_prison *lpr; 469 struct prison *pr = obj; 470 struct vfsoptlist *opts = data; 471 char *osname, *osrelease; 472 int error, gotversion, jsys, len, oss_version; 473 474 LIN_SDT_PROBE2(mib, linux_prison_set, entry, obj, data); 475 476 /* Set the parameters, which should be correct. */ 477 error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)); 478 if (error != 0) 479 LIN_SDT_PROBE1(mib, linux_prison_set, vfs_copyopt_error, error); 480 if (error == ENOENT) 481 jsys = -1; 482 error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len); 483 if (error != 0) 484 LIN_SDT_PROBE1(mib, linux_prison_set, vfs_getopt_error, error); 485 if (error == ENOENT) 486 osname = NULL; 487 else 488 jsys = JAIL_SYS_NEW; 489 error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len); 490 if (error != 0) 491 LIN_SDT_PROBE1(mib, linux_prison_set, vfs_getopt_error, error); 492 if (error == ENOENT) 493 osrelease = NULL; 494 else 495 jsys = JAIL_SYS_NEW; 496 error = vfs_copyopt(opts, "linux.oss_version", &oss_version, 497 sizeof(oss_version)); 498 if (error != 0) 499 LIN_SDT_PROBE1(mib, linux_prison_set, vfs_copyopt_error, error); 500 if (error == ENOENT) 501 gotversion = 0; 502 else { 503 gotversion = 1; 504 jsys = JAIL_SYS_NEW; 505 } 506 switch (jsys) { 507 case JAIL_SYS_INHERIT: 508 /* "linux=inherit": inherit the parent's Linux info. */ 509 mtx_lock(&pr->pr_mtx); 510 osd_jail_del(pr, linux_osd_jail_slot); 511 mtx_unlock(&pr->pr_mtx); 512 break; 513 case JAIL_SYS_NEW: 514 /* 515 * "linux=new" or "linux.*": 516 * the prison gets its own Linux info. 517 */ 518 error = linux_alloc_prison(pr, &lpr); 519 if (error) { 520 mtx_unlock(&pr->pr_mtx); 521 LIN_SDT_PROBE1(mib, linux_prison_set, return, error); 522 return (error); 523 } 524 if (osrelease) { 525 error = linux_map_osrel(osrelease, &lpr->pr_osrel); 526 if (error) { 527 mtx_unlock(&pr->pr_mtx); 528 LIN_SDT_PROBE1(mib, linux_prison_set, return, 529 error); 530 return (error); 531 } 532 strlcpy(lpr->pr_osrelease, osrelease, 533 LINUX_MAX_UTSNAME); 534 } 535 if (osname) 536 strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME); 537 if (gotversion) 538 lpr->pr_oss_version = oss_version; 539 mtx_unlock(&pr->pr_mtx); 540 } 541 542 LIN_SDT_PROBE1(mib, linux_prison_set, return, 0); 543 return (0); 544} 545 546SYSCTL_JAIL_PARAM_SYS_NODE(linux, CTLFLAG_RW, "Jail Linux parameters"); 547SYSCTL_JAIL_PARAM_STRING(_linux, osname, CTLFLAG_RW, LINUX_MAX_UTSNAME, 548 "Jail Linux kernel OS name"); 549SYSCTL_JAIL_PARAM_STRING(_linux, osrelease, CTLFLAG_RW, LINUX_MAX_UTSNAME, 550 "Jail Linux kernel OS release"); 551SYSCTL_JAIL_PARAM(_linux, oss_version, CTLTYPE_INT | CTLFLAG_RW, 552 "I", "Jail Linux OSS version"); 553 554static int 555linux_prison_get(void *obj, void *data) 556{ 557 struct linux_prison *lpr; 558 struct prison *ppr; 559 struct prison *pr = obj; 560 struct vfsoptlist *opts = data; 561 int error, i; 562 563 static int version0; 564 565 LIN_SDT_PROBE2(mib, linux_prison_get, entry, obj, data); 566 567 /* See if this prison is the one with the Linux info. */ 568 lpr = linux_find_prison(pr, &ppr); 569 i = (ppr == pr) ? JAIL_SYS_NEW : JAIL_SYS_INHERIT; 570 error = vfs_setopt(opts, "linux", &i, sizeof(i)); 571 if (error != 0) { 572 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error, error); 573 if (error != ENOENT) 574 goto done; 575 } 576 if (i) { 577 error = vfs_setopts(opts, "linux.osname", lpr->pr_osname); 578 if (error != 0) { 579 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 580 error); 581 if (error != ENOENT) 582 goto done; 583 } 584 error = vfs_setopts(opts, "linux.osrelease", lpr->pr_osrelease); 585 if (error != 0) { 586 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 587 error); 588 if (error != ENOENT) 589 goto done; 590 } 591 error = vfs_setopt(opts, "linux.oss_version", 592 &lpr->pr_oss_version, sizeof(lpr->pr_oss_version)); 593 if (error != 0) { 594 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error, 595 error); 596 if(error != ENOENT) 597 goto done; 598 } 599 } else { 600 /* 601 * If this prison is inheriting its Linux info, report 602 * empty/zero parameters. 603 */ 604 error = vfs_setopts(opts, "linux.osname", ""); 605 if (error != 0) { 606 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 607 error); 608 if(error != ENOENT) 609 goto done; 610 } 611 error = vfs_setopts(opts, "linux.osrelease", ""); 612 if (error != 0) { 613 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 614 error); 615 if(error != ENOENT) 616 goto done; 617 } 618 error = vfs_setopt(opts, "linux.oss_version", &version0, 619 sizeof(lpr->pr_oss_version)); 620 if (error != 0) { 621 LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error, 622 error); 623 if(error != ENOENT) 624 goto done; 625 } 626 } 627 error = 0; 628 629 done: 630 mtx_unlock(&ppr->pr_mtx); 631 632 LIN_SDT_PROBE1(mib, linux_prison_get, return, error); 633 return (error); 634} 635 636static void 637linux_prison_destructor(void *data) 638{ 639 640 LIN_SDT_PROBE1(mib, linux_prison_destructor, entry, data); 641 free(data, M_PRISON); 642 LIN_SDT_PROBE0(mib, linux_prison_destructor, return); 643} 644 645void 646linux_osd_jail_register(void) 647{ 648 struct prison *pr; 649 osd_method_t methods[PR_MAXMETHOD] = { 650 [PR_METHOD_CREATE] = linux_prison_create, 651 [PR_METHOD_GET] = linux_prison_get, 652 [PR_METHOD_SET] = linux_prison_set, 653 [PR_METHOD_CHECK] = linux_prison_check 654 }; 655 656 LIN_SDT_PROBE0(mib, linux_osd_jail_register, entry); 657 658 linux_osd_jail_slot = 659 osd_jail_register(linux_prison_destructor, methods); 660 if (linux_osd_jail_slot > 0) { 661 /* Copy the system linux info to any current prisons. */ 662 sx_xlock(&allprison_lock); 663 TAILQ_FOREACH(pr, &allprison, pr_list) 664 (void)linux_alloc_prison(pr, NULL); 665 sx_xunlock(&allprison_lock); 666 } 667 668 LIN_SDT_PROBE0(mib, linux_osd_jail_register, return); 669} 670 671void 672linux_osd_jail_deregister(void) 673{ 674 675 LIN_SDT_PROBE0(mib, linux_osd_jail_register, entry); 676 677 if (linux_osd_jail_slot) 678 osd_jail_deregister(linux_osd_jail_slot); 679 680 LIN_SDT_PROBE0(mib, linux_osd_jail_register, return); 681} 682 683void 684linux_get_osname(struct thread *td, char *dst) 685{ 686 struct prison *pr; 687 struct linux_prison *lpr; 688 689 LIN_SDT_PROBE2(mib, linux_get_osname, entry, td, dst); 690 691 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 692 bcopy(lpr->pr_osname, dst, LINUX_MAX_UTSNAME); 693 mtx_unlock(&pr->pr_mtx); 694 695 LIN_SDT_PROBE0(mib, linux_get_osname, return); 696} 697 698static int 699linux_set_osname(struct thread *td, char *osname) 700{ 701 struct prison *pr; 702 struct linux_prison *lpr; 703 704 LIN_SDT_PROBE2(mib, linux_set_osname, entry, td, osname); 705 706 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 707 strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME); 708 mtx_unlock(&pr->pr_mtx); 709 710 LIN_SDT_PROBE1(mib, linux_set_osname, return, 0); 711 return (0); 712} 713 714void 715linux_get_osrelease(struct thread *td, char *dst) 716{ 717 struct prison *pr; 718 struct linux_prison *lpr; 719 720 LIN_SDT_PROBE2(mib, linux_get_osrelease, entry, td, dst); 721 722 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 723 bcopy(lpr->pr_osrelease, dst, LINUX_MAX_UTSNAME); 724 mtx_unlock(&pr->pr_mtx); 725 726 LIN_SDT_PROBE0(mib, linux_get_osrelease, return); 727} 728 729int 730linux_kernver(struct thread *td) 731{ 732 struct prison *pr; 733 struct linux_prison *lpr; 734 int osrel; 735 736 LIN_SDT_PROBE1(mib, linux_kernver, entry, td); 737 738 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 739 osrel = lpr->pr_osrel; 740 mtx_unlock(&pr->pr_mtx); 741 742 LIN_SDT_PROBE1(mib, linux_kernver, return, osrel); 743 return (osrel); 744} 745 746static int 747linux_set_osrelease(struct thread *td, char *osrelease) 748{ 749 struct prison *pr; 750 struct linux_prison *lpr; 751 int error; 752 753 LIN_SDT_PROBE2(mib, linux_set_osrelease, entry, td, osrelease); 754 755 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 756 error = linux_map_osrel(osrelease, &lpr->pr_osrel); 757 if (error == 0) 758 strlcpy(lpr->pr_osrelease, osrelease, LINUX_MAX_UTSNAME); 759 mtx_unlock(&pr->pr_mtx); 760 761 LIN_SDT_PROBE1(mib, linux_set_osrelease, return, error); 762 return (error); 763} 764 765int 766linux_get_oss_version(struct thread *td) 767{ 768 struct prison *pr; 769 struct linux_prison *lpr; 770 int version; 771 772 LIN_SDT_PROBE1(mib, linux_get_oss_version, entry, td); 773 774 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 775 version = lpr->pr_oss_version; 776 mtx_unlock(&pr->pr_mtx); 777 778 LIN_SDT_PROBE1(mib, linux_get_oss_version, return, version); 779 return (version); 780} 781 782static int 783linux_set_oss_version(struct thread *td, int oss_version) 784{ 785 struct prison *pr; 786 struct linux_prison *lpr; 787 788 LIN_SDT_PROBE2(mib, linux_set_oss_version, entry, td, oss_version); 789 790 lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 791 lpr->pr_oss_version = oss_version; 792 mtx_unlock(&pr->pr_mtx); 793 794 LIN_SDT_PROBE1(mib, linux_set_oss_version, return, 0); 795 return (0); 796} 797 798#if defined(DEBUG) || defined(KTR) 799/* XXX: can be removed when every ldebug(...) and KTR stuff are removed. */ 800 801u_char linux_debug_map[howmany(LINUX_SYS_MAXSYSCALL, sizeof(u_char))]; 802 803static int 804linux_debug(int syscall, int toggle, int global) 805{ 806 807 if (global) { 808 char c = toggle ? 0 : 0xff; 809 810 memset(linux_debug_map, c, sizeof(linux_debug_map)); 811 return (0); 812 } 813 if (syscall < 0 || syscall >= LINUX_SYS_MAXSYSCALL) 814 return (EINVAL); 815 if (toggle) 816 clrbit(linux_debug_map, syscall); 817 else 818 setbit(linux_debug_map, syscall); 819 return (0); 820} 821 822/* 823 * Usage: sysctl linux.debug=<syscall_nr>.<0/1> 824 * 825 * E.g.: sysctl linux.debug=21.0 826 * 827 * As a special case, syscall "all" will apply to all syscalls globally. 828 */ 829#define LINUX_MAX_DEBUGSTR 16 830static int 831linux_sysctl_debug(SYSCTL_HANDLER_ARGS) 832{ 833 char value[LINUX_MAX_DEBUGSTR], *p; 834 int error, sysc, toggle; 835 int global = 0; 836 837 value[0] = '\0'; 838 error = sysctl_handle_string(oidp, value, LINUX_MAX_DEBUGSTR, req); 839 if (error || req->newptr == NULL) 840 return (error); 841 for (p = value; *p != '\0' && *p != '.'; p++); 842 if (*p == '\0') 843 return (EINVAL); 844 *p++ = '\0'; 845 sysc = strtol(value, NULL, 0); 846 toggle = strtol(p, NULL, 0); 847 if (strcmp(value, "all") == 0) 848 global = 1; 849 error = linux_debug(sysc, toggle, global); 850 return (error); 851} 852 853SYSCTL_PROC(_compat_linux, OID_AUTO, debug, 854 CTLTYPE_STRING | CTLFLAG_RW, 855 0, 0, linux_sysctl_debug, "A", 856 "Linux debugging control"); 857 858#endif /* DEBUG || KTR */ 859