150465Smarcel/*- 250465Smarcel * Copyright (c) 1999 Marcel Moolenaar 350465Smarcel * All rights reserved. 450465Smarcel * 550465Smarcel * Redistribution and use in source and binary forms, with or without 650465Smarcel * modification, are permitted provided that the following conditions 750465Smarcel * are met: 850465Smarcel * 1. Redistributions of source code must retain the above copyright 950465Smarcel * notice, this list of conditions and the following disclaimer 1050465Smarcel * in this position and unchanged. 1150465Smarcel * 2. Redistributions in binary form must reproduce the above copyright 1250465Smarcel * notice, this list of conditions and the following disclaimer in the 1350465Smarcel * documentation and/or other materials provided with the distribution. 1450465Smarcel * 3. The name of the author may not be used to endorse or promote products 1565067Smarcel * derived from this software without specific prior written permission. 1650465Smarcel * 1750465Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1850465Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1950465Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2050465Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2150465Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2250465Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2350465Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2450465Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2550465Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2650465Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2750465Smarcel */ 2850465Smarcel 29116173Sobrien#include <sys/cdefs.h> 30116173Sobrien__FBSDID("$FreeBSD$"); 31116173Sobrien 32235063Snetchild#include "opt_compat.h" 33235063Snetchild#include "opt_kdtrace.h" 34235063Snetchild 3550465Smarcel#include <sys/param.h> 3650465Smarcel#include <sys/kernel.h> 37235063Snetchild#include <sys/sdt.h> 3850465Smarcel#include <sys/systm.h> 3950465Smarcel#include <sys/sysctl.h> 4050465Smarcel#include <sys/proc.h> 4150465Smarcel#include <sys/malloc.h> 42191896Sjamie#include <sys/mount.h> 4350465Smarcel#include <sys/jail.h> 4487275Srwatson#include <sys/lock.h> 4587275Srwatson#include <sys/mutex.h> 46191896Sjamie#include <sys/sx.h> 4750465Smarcel 48140214Sobrien#ifdef COMPAT_LINUX32 49140214Sobrien#include <machine/../linux32/linux.h> 50140214Sobrien#else 5164907Smarcel#include <machine/../linux/linux.h> 52133816Stjr#endif 53235063Snetchild#include <compat/linux/linux_dtrace.h> 5464907Smarcel#include <compat/linux/linux_mib.h> 55246085Sjhb#include <compat/linux/linux_misc.h> 5650465Smarcel 57235063Snetchild/* DTrace init */ 58235063SnetchildLIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); 59235063Snetchild 60235063Snetchild/** 61235063Snetchild * DTrace probes in this module. 62235063Snetchild */ 63235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_osname, entry); 64235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osname, sysctl_string_error, "int"); 65235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osname, return, "int"); 66235063Snetchild 67235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_osrelease, entry); 68235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osrelease, sysctl_string_error, "int"); 69235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_osrelease, return, "int"); 70235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_sysctl_oss_version, entry); 71235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_oss_version, sysctl_string_error, 72235063Snetchild "int"); 73235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_sysctl_oss_version, return, "int"); 74235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_map_osrel, entry, "char *", "int *"); 75235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_map_osrel, return, "int"); 76235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_get_prison, entry, "struct prison *", 77235063Snetchild "struct prison **"); 78235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_get_prison, return, "struct linux_prison *"); 79235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_alloc_prison, entry, "struct prison *", 80235063Snetchild "struct linux_prison **"); 81235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_alloc_prison, return, "int"); 82235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_prison_create, entry, "void *", "void *"); 83235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_create, vfs_copyopt_error, "int"); 84235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_create, return, "int"); 85235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_prison_check, entry, "void *", "void *"); 86235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, vfs_copyopt_error, "int"); 87235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, vfs_getopt_error, "int"); 88235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_check, return, "int"); 89235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_prison_set, entry, "void *", "void *"); 90235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, vfs_copyopt_error, "int"); 91235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, vfs_getopt_error, "int"); 92235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_set, return, "int"); 93235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_prison_get, entry, "void *", "void *"); 94235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, vfs_setopt_error, "int"); 95235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, vfs_setopts_error, "int"); 96235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_get, return, "int"); 97235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_prison_destructor, entry, "void *"); 98235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_prison_destructor, return); 99235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_register, entry); 100235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_register, return); 101235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_deregister, entry); 102235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_osd_jail_deregister, return); 103235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_get_osname, entry, "struct thread *", 104235063Snetchild "char *"); 105235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_get_osname, return); 106235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_set_osname, entry, "struct thread *", 107235063Snetchild "char *"); 108235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_set_osname, return, "int"); 109235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_get_osrelease, entry, "struct thread *", 110235063Snetchild "char *"); 111235063SnetchildLIN_SDT_PROBE_DEFINE0(mib, linux_get_osrelease, return); 112235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_kernver, entry, "struct thread *"); 113235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_kernver, return, "int"); 114235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_set_osrelease, entry, "struct thread *", 115235063Snetchild "char *"); 116235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_set_osrelease, return, "int"); 117235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_get_oss_version, entry, "struct thread *"); 118235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_get_oss_version, return, "int"); 119235063Snetchild 120235063SnetchildLIN_SDT_PROBE_DEFINE2(mib, linux_set_oss_version, entry, "struct thread *", 121235063Snetchild "int"); 122235063SnetchildLIN_SDT_PROBE_DEFINE1(mib, linux_set_oss_version, return, "int"); 123235063Snetchild 12450465Smarcelstruct linux_prison { 12550465Smarcel char pr_osname[LINUX_MAX_UTSNAME]; 12650465Smarcel char pr_osrelease[LINUX_MAX_UTSNAME]; 12750465Smarcel int pr_oss_version; 128191972Sdchagin int pr_osrel; 12950465Smarcel}; 13050465Smarcel 131192895Sjamiestatic struct linux_prison lprison0 = { 132192895Sjamie .pr_osname = "Linux", 133192895Sjamie .pr_osrelease = "2.6.16", 134192895Sjamie .pr_oss_version = 0x030600, 135192895Sjamie .pr_osrel = 2006016 136192895Sjamie}; 137192895Sjamie 138191896Sjamiestatic unsigned linux_osd_jail_slot; 139191896Sjamie 140227309Sedstatic SYSCTL_NODE(_compat, OID_AUTO, linux, CTLFLAG_RW, 0, 14150465Smarcel "Linux mode"); 14250465Smarcel 143219668Snetchildstatic int linux_set_osname(struct thread *td, char *osname); 144219668Snetchildstatic int linux_set_osrelease(struct thread *td, char *osrelease); 145219668Snetchildstatic int linux_set_oss_version(struct thread *td, int oss_version); 146219668Snetchild 14750465Smarcelstatic int 14862573Sphklinux_sysctl_osname(SYSCTL_HANDLER_ARGS) 14950465Smarcel{ 15050465Smarcel char osname[LINUX_MAX_UTSNAME]; 15150465Smarcel int error; 15250465Smarcel 153235063Snetchild LIN_SDT_PROBE0(mib, linux_sysctl_osname, entry); 154235063Snetchild 155112206Sjhb linux_get_osname(req->td, osname); 15650465Smarcel error = sysctl_handle_string(oidp, osname, LINUX_MAX_UTSNAME, req); 157235063Snetchild if (error != 0 || req->newptr == NULL) { 158235063Snetchild LIN_SDT_PROBE1(mib, linux_sysctl_osname, sysctl_string_error, 159235063Snetchild error); 160235063Snetchild LIN_SDT_PROBE1(mib, linux_sysctl_osname, return, error); 16150465Smarcel return (error); 162235063Snetchild } 163112206Sjhb error = linux_set_osname(req->td, osname); 164235063Snetchild 165235063Snetchild LIN_SDT_PROBE1(mib, linux_sysctl_osname, return, error); 16650465Smarcel return (error); 16750465Smarcel} 16850465Smarcel 16950465SmarcelSYSCTL_PROC(_compat_linux, OID_AUTO, osname, 170191792Sjamie CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 17150465Smarcel 0, 0, linux_sysctl_osname, "A", 17250465Smarcel "Linux kernel OS name"); 17350465Smarcel 17450465Smarcelstatic int 17562573Sphklinux_sysctl_osrelease(SYSCTL_HANDLER_ARGS) 17650465Smarcel{ 17750465Smarcel char osrelease[LINUX_MAX_UTSNAME]; 17850465Smarcel int error; 17950465Smarcel 180235063Snetchild LIN_SDT_PROBE0(mib, linux_sysctl_osrelease, entry); 181235063Snetchild 182112206Sjhb linux_get_osrelease(req->td, osrelease); 18350465Smarcel error = sysctl_handle_string(oidp, osrelease, LINUX_MAX_UTSNAME, req); 184235063Snetchild if (error != 0 || req->newptr == NULL) { 185235063Snetchild LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, sysctl_string_error, 186235063Snetchild error); 187235063Snetchild LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, return, error); 18850465Smarcel return (error); 189235063Snetchild } 190112206Sjhb error = linux_set_osrelease(req->td, osrelease); 191235063Snetchild 192235063Snetchild LIN_SDT_PROBE1(mib, linux_sysctl_osrelease, return, error); 19350465Smarcel return (error); 19450465Smarcel} 19550465Smarcel 19650465SmarcelSYSCTL_PROC(_compat_linux, OID_AUTO, osrelease, 197191792Sjamie CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 19850465Smarcel 0, 0, linux_sysctl_osrelease, "A", 19950465Smarcel "Linux kernel OS release"); 20050465Smarcel 20150465Smarcelstatic int 20262573Sphklinux_sysctl_oss_version(SYSCTL_HANDLER_ARGS) 20350465Smarcel{ 20450465Smarcel int oss_version; 20550465Smarcel int error; 20650465Smarcel 207235063Snetchild LIN_SDT_PROBE0(mib, linux_sysctl_oss_version, entry); 208235063Snetchild 209112206Sjhb oss_version = linux_get_oss_version(req->td); 21050465Smarcel error = sysctl_handle_int(oidp, &oss_version, 0, req); 211235063Snetchild if (error != 0 || req->newptr == NULL) { 212235063Snetchild LIN_SDT_PROBE1(mib, linux_sysctl_oss_version, 213235063Snetchild sysctl_string_error, error); 214235063Snetchild LIN_SDT_PROBE1(mib, linux_sysctl_oss_version, return, error); 21550465Smarcel return (error); 216235063Snetchild } 217112206Sjhb error = linux_set_oss_version(req->td, oss_version); 218235063Snetchild 219235063Snetchild LIN_SDT_PROBE1(mib, linux_sysctl_oss_version, return, error); 22050465Smarcel return (error); 22150465Smarcel} 22250465Smarcel 22350465SmarcelSYSCTL_PROC(_compat_linux, OID_AUTO, oss_version, 224191792Sjamie CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_PRISON | CTLFLAG_MPSAFE, 22550465Smarcel 0, 0, linux_sysctl_oss_version, "I", 22650465Smarcel "Linux OSS version"); 22750465Smarcel 22887275Srwatson/* 229191972Sdchagin * Map the osrelease into integer 230191972Sdchagin */ 231191972Sdchaginstatic int 232191972Sdchaginlinux_map_osrel(char *osrelease, int *osrel) 233191972Sdchagin{ 234191972Sdchagin char *sep, *eosrelease; 235191972Sdchagin int len, v0, v1, v2, v; 236191972Sdchagin 237235063Snetchild LIN_SDT_PROBE2(mib, linux_map_osrel, entry, osrelease, osrel); 238235063Snetchild 239191972Sdchagin len = strlen(osrelease); 240191972Sdchagin eosrelease = osrelease + len; 241191972Sdchagin v0 = strtol(osrelease, &sep, 10); 242235063Snetchild if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.') { 243235063Snetchild LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 244191972Sdchagin return (EINVAL); 245235063Snetchild } 246191972Sdchagin osrelease = sep + 1; 247191972Sdchagin v1 = strtol(osrelease, &sep, 10); 248235063Snetchild if (osrelease == sep || sep + 1 >= eosrelease || *sep != '.') { 249235063Snetchild LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 250191972Sdchagin return (EINVAL); 251235063Snetchild } 252191972Sdchagin osrelease = sep + 1; 253191972Sdchagin v2 = strtol(osrelease, &sep, 10); 254235063Snetchild if (osrelease == sep || sep != eosrelease) { 255235063Snetchild LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 256191972Sdchagin return (EINVAL); 257235063Snetchild } 258191972Sdchagin 259191972Sdchagin v = v0 * 1000000 + v1 * 1000 + v2; 260235063Snetchild if (v < 1000000) { 261235063Snetchild LIN_SDT_PROBE1(mib, linux_map_osrel, return, EINVAL); 262191972Sdchagin return (EINVAL); 263235063Snetchild } 264191972Sdchagin 265191972Sdchagin *osrel = v; 266235063Snetchild 267235063Snetchild LIN_SDT_PROBE1(mib, linux_map_osrel, return, 0); 268191972Sdchagin return (0); 269191972Sdchagin} 270191972Sdchagin 271191972Sdchagin/* 272192895Sjamie * Find a prison with Linux info. 273192895Sjamie * Return the Linux info and the (locked) prison. 27487275Srwatson */ 275191896Sjamiestatic struct linux_prison * 276192895Sjamielinux_find_prison(struct prison *spr, struct prison **prp) 27750465Smarcel{ 278191896Sjamie struct prison *pr; 279191896Sjamie struct linux_prison *lpr; 28050465Smarcel 281235063Snetchild LIN_SDT_PROBE2(mib, linux_get_prison, entry, spr, prp); 282235063Snetchild 283192895Sjamie if (!linux_osd_jail_slot) 284192895Sjamie /* In case osd_register failed. */ 285192895Sjamie spr = &prison0; 286192895Sjamie for (pr = spr;; pr = pr->pr_parent) { 287192895Sjamie mtx_lock(&pr->pr_mtx); 288192895Sjamie lpr = (pr == &prison0) 289192895Sjamie ? &lprison0 290192895Sjamie : osd_jail_get(pr, linux_osd_jail_slot); 291192895Sjamie if (lpr != NULL) 292192895Sjamie break; 293191896Sjamie mtx_unlock(&pr->pr_mtx); 294192895Sjamie } 295192895Sjamie *prp = pr; 296235063Snetchild 297235063Snetchild LIN_SDT_PROBE1(mib, linux_get_prison, return, lpr); 298191896Sjamie return (lpr); 299191896Sjamie} 300191896Sjamie 301191896Sjamie/* 302192895Sjamie * Ensure a prison has its own Linux info. If lprp is non-null, point it to 303192895Sjamie * the Linux info and lock the prison. 304191896Sjamie */ 305191896Sjamiestatic int 306191896Sjamielinux_alloc_prison(struct prison *pr, struct linux_prison **lprp) 307191896Sjamie{ 308192895Sjamie struct prison *ppr; 309191896Sjamie struct linux_prison *lpr, *nlpr; 310191896Sjamie int error; 311191896Sjamie 312235063Snetchild LIN_SDT_PROBE2(mib, linux_alloc_prison, entry, pr, lprp); 313235063Snetchild 314191896Sjamie /* If this prison already has Linux info, return that. */ 315191896Sjamie error = 0; 316192895Sjamie lpr = linux_find_prison(pr, &ppr); 317192895Sjamie if (ppr == pr) 318191896Sjamie goto done; 319191896Sjamie /* 320191896Sjamie * Allocate a new info record. Then check again, in case something 321191896Sjamie * changed during the allocation. 322191896Sjamie */ 323192895Sjamie mtx_unlock(&ppr->pr_mtx); 324191896Sjamie nlpr = malloc(sizeof(struct linux_prison), M_PRISON, M_WAITOK); 325192895Sjamie lpr = linux_find_prison(pr, &ppr); 326192895Sjamie if (ppr == pr) { 327191896Sjamie free(nlpr, M_PRISON); 328191896Sjamie goto done; 329191896Sjamie } 330192895Sjamie /* Inherit the initial values from the ancestor. */ 331192895Sjamie mtx_lock(&pr->pr_mtx); 332191896Sjamie error = osd_jail_set(pr, linux_osd_jail_slot, nlpr); 333192895Sjamie if (error == 0) { 334192895Sjamie bcopy(lpr, nlpr, sizeof(*lpr)); 335192895Sjamie lpr = nlpr; 336192895Sjamie } else { 337191896Sjamie free(nlpr, M_PRISON); 338192895Sjamie lpr = NULL; 339191896Sjamie } 340192895Sjamie mtx_unlock(&ppr->pr_mtx); 341192895Sjamie done: 342191896Sjamie if (lprp != NULL) 343191896Sjamie *lprp = lpr; 344192895Sjamie else 345192895Sjamie mtx_unlock(&pr->pr_mtx); 346235063Snetchild 347235063Snetchild LIN_SDT_PROBE1(mib, linux_alloc_prison, return, error); 348191896Sjamie return (error); 349191896Sjamie} 350191896Sjamie 351191896Sjamie/* 352191896Sjamie * Jail OSD methods for Linux prison data. 353191896Sjamie */ 354191896Sjamiestatic int 355191896Sjamielinux_prison_create(void *obj, void *data) 356191896Sjamie{ 357191896Sjamie struct prison *pr = obj; 358191896Sjamie struct vfsoptlist *opts = data; 359235063Snetchild int jsys, error; 360191896Sjamie 361235063Snetchild LIN_SDT_PROBE2(mib, linux_prison_create, entry, obj, data); 362235063Snetchild 363235063Snetchild error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)); 364235063Snetchild if (error != 0) { 365235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_create, vfs_copyopt_error, 366235063Snetchild error); 367235063Snetchild } else if (jsys == JAIL_SYS_INHERIT) { 368235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_create, return, 0); 369191896Sjamie return (0); 370235063Snetchild } 371191896Sjamie /* 372191896Sjamie * Inherit a prison's initial values from its parent 373195870Sjamie * (different from JAIL_SYS_INHERIT which also inherits changes). 374191896Sjamie */ 375235063Snetchild error = linux_alloc_prison(pr, NULL); 376235063Snetchild 377235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_create, return, error); 378235063Snetchild return (error); 379191896Sjamie} 380191896Sjamie 381191896Sjamiestatic int 382191896Sjamielinux_prison_check(void *obj __unused, void *data) 383191896Sjamie{ 384191896Sjamie struct vfsoptlist *opts = data; 385191896Sjamie char *osname, *osrelease; 386195870Sjamie int error, jsys, len, osrel, oss_version; 387191896Sjamie 388235063Snetchild LIN_SDT_PROBE2(mib, linux_prison_check, entry, obj, data); 389235063Snetchild 390191896Sjamie /* Check that the parameters are correct. */ 391195870Sjamie error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)); 392235063Snetchild if (error != 0) { 393235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, vfs_copyopt_error, 394235063Snetchild error); 395235063Snetchild } 396195870Sjamie if (error != ENOENT) { 397235063Snetchild if (error != 0) { 398235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 399195870Sjamie return (error); 400235063Snetchild } 401235063Snetchild if (jsys != JAIL_SYS_NEW && jsys != JAIL_SYS_INHERIT) { 402235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL); 403195870Sjamie return (EINVAL); 404235063Snetchild } 405195870Sjamie } 406191896Sjamie error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len); 407235063Snetchild if (error != 0) { 408235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, vfs_getopt_error, 409235063Snetchild error); 410235063Snetchild } 411191896Sjamie if (error != ENOENT) { 412235063Snetchild if (error != 0) { 413235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 414191896Sjamie return (error); 415235063Snetchild } 416235063Snetchild if (len == 0 || osname[len - 1] != '\0') { 417235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL); 418191896Sjamie return (EINVAL); 419235063Snetchild } 420191896Sjamie if (len > LINUX_MAX_UTSNAME) { 421191896Sjamie vfs_opterror(opts, "linux.osname too long"); 422235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, return, 423235063Snetchild ENAMETOOLONG); 424191896Sjamie return (ENAMETOOLONG); 425191896Sjamie } 426191896Sjamie } 427191896Sjamie error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len); 428235063Snetchild if (error != 0) { 429235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, vfs_getopt_error, 430235063Snetchild error); 431235063Snetchild } 432191896Sjamie if (error != ENOENT) { 433235063Snetchild if (error != 0) { 434235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 435191896Sjamie return (error); 436235063Snetchild } 437235063Snetchild if (len == 0 || osrelease[len - 1] != '\0') { 438235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, return, EINVAL); 439191896Sjamie return (EINVAL); 440235063Snetchild } 441191896Sjamie if (len > LINUX_MAX_UTSNAME) { 442191896Sjamie vfs_opterror(opts, "linux.osrelease too long"); 443235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, return, 444235063Snetchild ENAMETOOLONG); 445191896Sjamie return (ENAMETOOLONG); 446191896Sjamie } 447192895Sjamie error = linux_map_osrel(osrelease, &osrel); 448192895Sjamie if (error != 0) { 449192895Sjamie vfs_opterror(opts, "linux.osrelease format error"); 450235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 451192895Sjamie return (error); 452192895Sjamie } 453191896Sjamie } 454191896Sjamie error = vfs_copyopt(opts, "linux.oss_version", &oss_version, 455191896Sjamie sizeof(oss_version)); 456235063Snetchild if (error != 0) 457235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, vfs_copyopt_error, error); 458235063Snetchild 459235063Snetchild if (error == ENOENT) 460235063Snetchild error = 0; 461235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_check, return, error); 462235063Snetchild return (error); 463191896Sjamie} 464191896Sjamie 465191896Sjamiestatic int 466191896Sjamielinux_prison_set(void *obj, void *data) 467191896Sjamie{ 468191896Sjamie struct linux_prison *lpr; 469191896Sjamie struct prison *pr = obj; 470191896Sjamie struct vfsoptlist *opts = data; 471191896Sjamie char *osname, *osrelease; 472195870Sjamie int error, gotversion, jsys, len, oss_version; 473191896Sjamie 474235063Snetchild LIN_SDT_PROBE2(mib, linux_prison_set, entry, obj, data); 475235063Snetchild 476191896Sjamie /* Set the parameters, which should be correct. */ 477195870Sjamie error = vfs_copyopt(opts, "linux", &jsys, sizeof(jsys)); 478235063Snetchild if (error != 0) 479235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_set, vfs_copyopt_error, error); 480195870Sjamie if (error == ENOENT) 481195870Sjamie jsys = -1; 482191896Sjamie error = vfs_getopt(opts, "linux.osname", (void **)&osname, &len); 483235063Snetchild if (error != 0) 484235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_set, vfs_getopt_error, error); 485191896Sjamie if (error == ENOENT) 486191896Sjamie osname = NULL; 487191896Sjamie else 488195870Sjamie jsys = JAIL_SYS_NEW; 489191896Sjamie error = vfs_getopt(opts, "linux.osrelease", (void **)&osrelease, &len); 490235063Snetchild if (error != 0) 491235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_set, vfs_getopt_error, error); 492191896Sjamie if (error == ENOENT) 493191896Sjamie osrelease = NULL; 494191896Sjamie else 495195870Sjamie jsys = JAIL_SYS_NEW; 496191896Sjamie error = vfs_copyopt(opts, "linux.oss_version", &oss_version, 497191896Sjamie sizeof(oss_version)); 498235063Snetchild if (error != 0) 499235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_set, vfs_copyopt_error, error); 500195870Sjamie if (error == ENOENT) 501195870Sjamie gotversion = 0; 502195870Sjamie else { 503195870Sjamie gotversion = 1; 504195870Sjamie jsys = JAIL_SYS_NEW; 505195870Sjamie } 506195870Sjamie switch (jsys) { 507195870Sjamie case JAIL_SYS_INHERIT: 508195870Sjamie /* "linux=inherit": inherit the parent's Linux info. */ 509191896Sjamie mtx_lock(&pr->pr_mtx); 510191896Sjamie osd_jail_del(pr, linux_osd_jail_slot); 511191896Sjamie mtx_unlock(&pr->pr_mtx); 512195870Sjamie break; 513195870Sjamie case JAIL_SYS_NEW: 514112206Sjhb /* 515195870Sjamie * "linux=new" or "linux.*": 516191896Sjamie * the prison gets its own Linux info. 517112206Sjhb */ 518191896Sjamie error = linux_alloc_prison(pr, &lpr); 519191896Sjamie if (error) { 520191896Sjamie mtx_unlock(&pr->pr_mtx); 521235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_set, return, error); 522191896Sjamie return (error); 523191896Sjamie } 524191896Sjamie if (osrelease) { 525191972Sdchagin error = linux_map_osrel(osrelease, &lpr->pr_osrel); 526191972Sdchagin if (error) { 527191972Sdchagin mtx_unlock(&pr->pr_mtx); 528235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_set, return, 529235063Snetchild error); 530191972Sdchagin return (error); 531191972Sdchagin } 532191896Sjamie strlcpy(lpr->pr_osrelease, osrelease, 533191896Sjamie LINUX_MAX_UTSNAME); 534191896Sjamie } 535191972Sdchagin if (osname) 536191972Sdchagin strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME); 537191896Sjamie if (gotversion) 538191896Sjamie lpr->pr_oss_version = oss_version; 53987275Srwatson mtx_unlock(&pr->pr_mtx); 54050465Smarcel } 541235063Snetchild 542235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_set, return, 0); 543191896Sjamie return (0); 54450465Smarcel} 54550465Smarcel 546195870SjamieSYSCTL_JAIL_PARAM_SYS_NODE(linux, CTLFLAG_RW, "Jail Linux parameters"); 547191896SjamieSYSCTL_JAIL_PARAM_STRING(_linux, osname, CTLFLAG_RW, LINUX_MAX_UTSNAME, 548191896Sjamie "Jail Linux kernel OS name"); 549191896SjamieSYSCTL_JAIL_PARAM_STRING(_linux, osrelease, CTLFLAG_RW, LINUX_MAX_UTSNAME, 550191896Sjamie "Jail Linux kernel OS release"); 551191896SjamieSYSCTL_JAIL_PARAM(_linux, oss_version, CTLTYPE_INT | CTLFLAG_RW, 552191896Sjamie "I", "Jail Linux OSS version"); 553191896Sjamie 554191896Sjamiestatic int 555191896Sjamielinux_prison_get(void *obj, void *data) 556191896Sjamie{ 557191896Sjamie struct linux_prison *lpr; 558192895Sjamie struct prison *ppr; 559191896Sjamie struct prison *pr = obj; 560191896Sjamie struct vfsoptlist *opts = data; 561191896Sjamie int error, i; 562191896Sjamie 563192895Sjamie static int version0; 564192895Sjamie 565235063Snetchild LIN_SDT_PROBE2(mib, linux_prison_get, entry, obj, data); 566235063Snetchild 567192895Sjamie /* See if this prison is the one with the Linux info. */ 568192895Sjamie lpr = linux_find_prison(pr, &ppr); 569195870Sjamie i = (ppr == pr) ? JAIL_SYS_NEW : JAIL_SYS_INHERIT; 570191896Sjamie error = vfs_setopt(opts, "linux", &i, sizeof(i)); 571235063Snetchild if (error != 0) { 572235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error, error); 573235063Snetchild if (error != ENOENT) 574235063Snetchild goto done; 575235063Snetchild } 576192895Sjamie if (i) { 577195870Sjamie error = vfs_setopts(opts, "linux.osname", lpr->pr_osname); 578235063Snetchild if (error != 0) { 579235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 580235063Snetchild error); 581235063Snetchild if (error != ENOENT) 582235063Snetchild goto done; 583235063Snetchild } 584195870Sjamie error = vfs_setopts(opts, "linux.osrelease", lpr->pr_osrelease); 585235063Snetchild if (error != 0) { 586235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 587235063Snetchild error); 588235063Snetchild if (error != ENOENT) 589235063Snetchild goto done; 590235063Snetchild } 591195870Sjamie error = vfs_setopt(opts, "linux.oss_version", 592195870Sjamie &lpr->pr_oss_version, sizeof(lpr->pr_oss_version)); 593235063Snetchild if (error != 0) { 594235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error, 595235063Snetchild error); 596235063Snetchild if(error != ENOENT) 597235063Snetchild goto done; 598235063Snetchild } 599195870Sjamie } else { 600192895Sjamie /* 601192895Sjamie * If this prison is inheriting its Linux info, report 602192895Sjamie * empty/zero parameters. 603192895Sjamie */ 604192895Sjamie error = vfs_setopts(opts, "linux.osname", ""); 605235063Snetchild if (error != 0) { 606235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 607235063Snetchild error); 608235063Snetchild if(error != ENOENT) 609235063Snetchild goto done; 610235063Snetchild } 611192895Sjamie error = vfs_setopts(opts, "linux.osrelease", ""); 612235063Snetchild if (error != 0) { 613235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopts_error, 614235063Snetchild error); 615235063Snetchild if(error != ENOENT) 616235063Snetchild goto done; 617235063Snetchild } 618192895Sjamie error = vfs_setopt(opts, "linux.oss_version", &version0, 619192895Sjamie sizeof(lpr->pr_oss_version)); 620235063Snetchild if (error != 0) { 621235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_get, vfs_setopt_error, 622235063Snetchild error); 623235063Snetchild if(error != ENOENT) 624235063Snetchild goto done; 625235063Snetchild } 626191896Sjamie } 627191896Sjamie error = 0; 628191896Sjamie 629191896Sjamie done: 630192895Sjamie mtx_unlock(&ppr->pr_mtx); 631235063Snetchild 632235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_get, return, error); 633191896Sjamie return (error); 634191896Sjamie} 635191896Sjamie 636191896Sjamiestatic void 637191896Sjamielinux_prison_destructor(void *data) 638191896Sjamie{ 639191896Sjamie 640235063Snetchild LIN_SDT_PROBE1(mib, linux_prison_destructor, entry, data); 641191896Sjamie free(data, M_PRISON); 642235063Snetchild LIN_SDT_PROBE0(mib, linux_prison_destructor, return); 643191896Sjamie} 644191896Sjamie 64587275Srwatsonvoid 646191896Sjamielinux_osd_jail_register(void) 64750465Smarcel{ 648191896Sjamie struct prison *pr; 649191896Sjamie osd_method_t methods[PR_MAXMETHOD] = { 650191896Sjamie [PR_METHOD_CREATE] = linux_prison_create, 651191896Sjamie [PR_METHOD_GET] = linux_prison_get, 652191896Sjamie [PR_METHOD_SET] = linux_prison_set, 653191896Sjamie [PR_METHOD_CHECK] = linux_prison_check 654191896Sjamie }; 65550465Smarcel 656235063Snetchild LIN_SDT_PROBE0(mib, linux_osd_jail_register, entry); 657235063Snetchild 658191896Sjamie linux_osd_jail_slot = 659191896Sjamie osd_jail_register(linux_prison_destructor, methods); 660191896Sjamie if (linux_osd_jail_slot > 0) { 661191896Sjamie /* Copy the system linux info to any current prisons. */ 662191896Sjamie sx_xlock(&allprison_lock); 663192895Sjamie TAILQ_FOREACH(pr, &allprison, pr_list) 664191896Sjamie (void)linux_alloc_prison(pr, NULL); 665191896Sjamie sx_xunlock(&allprison_lock); 66687275Srwatson } 667235063Snetchild 668235063Snetchild LIN_SDT_PROBE0(mib, linux_osd_jail_register, return); 669191896Sjamie} 67087275Srwatson 671191896Sjamievoid 672191896Sjamielinux_osd_jail_deregister(void) 673191896Sjamie{ 674191896Sjamie 675235063Snetchild LIN_SDT_PROBE0(mib, linux_osd_jail_register, entry); 676235063Snetchild 677191896Sjamie if (linux_osd_jail_slot) 678191896Sjamie osd_jail_deregister(linux_osd_jail_slot); 679235063Snetchild 680235063Snetchild LIN_SDT_PROBE0(mib, linux_osd_jail_register, return); 68150465Smarcel} 68250465Smarcel 683191896Sjamievoid 684191896Sjamielinux_get_osname(struct thread *td, char *dst) 685191896Sjamie{ 686191896Sjamie struct prison *pr; 687191896Sjamie struct linux_prison *lpr; 688191896Sjamie 689235063Snetchild LIN_SDT_PROBE2(mib, linux_get_osname, entry, td, dst); 690235063Snetchild 691192895Sjamie lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 692192895Sjamie bcopy(lpr->pr_osname, dst, LINUX_MAX_UTSNAME); 693192895Sjamie mtx_unlock(&pr->pr_mtx); 694235063Snetchild 695235063Snetchild LIN_SDT_PROBE0(mib, linux_get_osname, return); 696191896Sjamie} 697191896Sjamie 698219668Snetchildstatic int 699112206Sjhblinux_set_osname(struct thread *td, char *osname) 70050465Smarcel{ 701112206Sjhb struct prison *pr; 702112206Sjhb struct linux_prison *lpr; 70350465Smarcel 704235063Snetchild LIN_SDT_PROBE2(mib, linux_set_osname, entry, td, osname); 705235063Snetchild 706192895Sjamie lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 707192895Sjamie strlcpy(lpr->pr_osname, osname, LINUX_MAX_UTSNAME); 708192895Sjamie mtx_unlock(&pr->pr_mtx); 709235063Snetchild 710235063Snetchild LIN_SDT_PROBE1(mib, linux_set_osname, return, 0); 71150465Smarcel return (0); 71250465Smarcel} 71350465Smarcel 71487275Srwatsonvoid 715112206Sjhblinux_get_osrelease(struct thread *td, char *dst) 71650465Smarcel{ 717191896Sjamie struct prison *pr; 71887275Srwatson struct linux_prison *lpr; 71950465Smarcel 720235063Snetchild LIN_SDT_PROBE2(mib, linux_get_osrelease, entry, td, dst); 721235063Snetchild 722192895Sjamie lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 723192895Sjamie bcopy(lpr->pr_osrelease, dst, LINUX_MAX_UTSNAME); 724192895Sjamie mtx_unlock(&pr->pr_mtx); 725235063Snetchild 726235063Snetchild LIN_SDT_PROBE0(mib, linux_get_osrelease, return); 72750465Smarcel} 72850465Smarcel 72950465Smarcelint 730191972Sdchaginlinux_kernver(struct thread *td) 731165687Snetchild{ 732165687Snetchild struct prison *pr; 733165687Snetchild struct linux_prison *lpr; 734191972Sdchagin int osrel; 735165687Snetchild 736235063Snetchild LIN_SDT_PROBE1(mib, linux_kernver, entry, td); 737235063Snetchild 738192895Sjamie lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 739192895Sjamie osrel = lpr->pr_osrel; 740192895Sjamie mtx_unlock(&pr->pr_mtx); 741235063Snetchild 742235063Snetchild LIN_SDT_PROBE1(mib, linux_kernver, return, osrel); 743191972Sdchagin return (osrel); 744165687Snetchild} 745165687Snetchild 746219668Snetchildstatic int 747112206Sjhblinux_set_osrelease(struct thread *td, char *osrelease) 74850465Smarcel{ 749112206Sjhb struct prison *pr; 750112206Sjhb struct linux_prison *lpr; 751191972Sdchagin int error; 75250465Smarcel 753235063Snetchild LIN_SDT_PROBE2(mib, linux_set_osrelease, entry, td, osrelease); 754235063Snetchild 755192895Sjamie lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 756192895Sjamie error = linux_map_osrel(osrelease, &lpr->pr_osrel); 757192895Sjamie if (error == 0) 758191896Sjamie strlcpy(lpr->pr_osrelease, osrelease, LINUX_MAX_UTSNAME); 759192895Sjamie mtx_unlock(&pr->pr_mtx); 760235063Snetchild 761235063Snetchild LIN_SDT_PROBE1(mib, linux_set_osrelease, return, error); 762192895Sjamie return (error); 76350465Smarcel} 76450465Smarcel 76550465Smarcelint 766112206Sjhblinux_get_oss_version(struct thread *td) 76750465Smarcel{ 768191896Sjamie struct prison *pr; 769191896Sjamie struct linux_prison *lpr; 77087275Srwatson int version; 77150465Smarcel 772235063Snetchild LIN_SDT_PROBE1(mib, linux_get_oss_version, entry, td); 773235063Snetchild 774192895Sjamie lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 775192895Sjamie version = lpr->pr_oss_version; 776192895Sjamie mtx_unlock(&pr->pr_mtx); 777235063Snetchild 778235063Snetchild LIN_SDT_PROBE1(mib, linux_get_oss_version, return, version); 77987275Srwatson return (version); 78050465Smarcel} 78150465Smarcel 782219668Snetchildstatic int 783112206Sjhblinux_set_oss_version(struct thread *td, int oss_version) 78450465Smarcel{ 785112206Sjhb struct prison *pr; 786112206Sjhb struct linux_prison *lpr; 78750465Smarcel 788235063Snetchild LIN_SDT_PROBE2(mib, linux_set_oss_version, entry, td, oss_version); 789235063Snetchild 790192895Sjamie lpr = linux_find_prison(td->td_ucred->cr_prison, &pr); 791192895Sjamie lpr->pr_oss_version = oss_version; 792192895Sjamie mtx_unlock(&pr->pr_mtx); 793235063Snetchild 794235063Snetchild LIN_SDT_PROBE1(mib, linux_set_oss_version, return, 0); 79550465Smarcel return (0); 79650465Smarcel} 79772543Sjlemon 798191877Sdchagin#if defined(DEBUG) || defined(KTR) 799235063Snetchild/* XXX: can be removed when every ldebug(...) and KTR stuff are removed. */ 80072543Sjlemon 80178264Speteru_char linux_debug_map[howmany(LINUX_SYS_MAXSYSCALL, sizeof(u_char))]; 80272543Sjlemon 80378258Speterstatic int 80472543Sjlemonlinux_debug(int syscall, int toggle, int global) 80572543Sjlemon{ 80672543Sjlemon 80772543Sjlemon if (global) { 80872543Sjlemon char c = toggle ? 0 : 0xff; 80972543Sjlemon 81072543Sjlemon memset(linux_debug_map, c, sizeof(linux_debug_map)); 81172543Sjlemon return (0); 81272543Sjlemon } 81372543Sjlemon if (syscall < 0 || syscall >= LINUX_SYS_MAXSYSCALL) 81472543Sjlemon return (EINVAL); 81572543Sjlemon if (toggle) 81672543Sjlemon clrbit(linux_debug_map, syscall); 81772543Sjlemon else 81872543Sjlemon setbit(linux_debug_map, syscall); 81972543Sjlemon return (0); 82072543Sjlemon} 82172543Sjlemon 82272543Sjlemon/* 82396398Sdd * Usage: sysctl linux.debug=<syscall_nr>.<0/1> 82472543Sjlemon * 82596398Sdd * E.g.: sysctl linux.debug=21.0 82672543Sjlemon * 82772543Sjlemon * As a special case, syscall "all" will apply to all syscalls globally. 82872543Sjlemon */ 82972543Sjlemon#define LINUX_MAX_DEBUGSTR 16 83072543Sjlemonstatic int 83172543Sjlemonlinux_sysctl_debug(SYSCTL_HANDLER_ARGS) 83272543Sjlemon{ 83372543Sjlemon char value[LINUX_MAX_DEBUGSTR], *p; 83472543Sjlemon int error, sysc, toggle; 83572543Sjlemon int global = 0; 83672543Sjlemon 83772543Sjlemon value[0] = '\0'; 83872543Sjlemon error = sysctl_handle_string(oidp, value, LINUX_MAX_DEBUGSTR, req); 83972543Sjlemon if (error || req->newptr == NULL) 84072543Sjlemon return (error); 84172543Sjlemon for (p = value; *p != '\0' && *p != '.'; p++); 84272543Sjlemon if (*p == '\0') 84372543Sjlemon return (EINVAL); 84472543Sjlemon *p++ = '\0'; 84572543Sjlemon sysc = strtol(value, NULL, 0); 84672543Sjlemon toggle = strtol(p, NULL, 0); 84772543Sjlemon if (strcmp(value, "all") == 0) 84872543Sjlemon global = 1; 84972543Sjlemon error = linux_debug(sysc, toggle, global); 85072543Sjlemon return (error); 85172543Sjlemon} 85272543Sjlemon 85372543SjlemonSYSCTL_PROC(_compat_linux, OID_AUTO, debug, 85472543Sjlemon CTLTYPE_STRING | CTLFLAG_RW, 85572543Sjlemon 0, 0, linux_sysctl_debug, "A", 85672543Sjlemon "Linux debugging control"); 85772543Sjlemon 858191877Sdchagin#endif /* DEBUG || KTR */ 859