audit_bsm_klib.c revision 173142
1155192Srwatson/* 2155192Srwatson * Copyright (c) 1999-2005 Apple Computer, Inc. 3155192Srwatson * Copyright (c) 2005 Robert N. M. Watson 4155192Srwatson * All rights reserved. 5155192Srwatson * 6155192Srwatson * Redistribution and use in source and binary forms, with or without 7155192Srwatson * modification, are permitted provided that the following conditions 8155192Srwatson * are met: 9155192Srwatson * 1. Redistributions of source code must retain the above copyright 10155192Srwatson * notice, this list of conditions and the following disclaimer. 11155192Srwatson * 2. Redistributions in binary form must reproduce the above copyright 12155192Srwatson * notice, this list of conditions and the following disclaimer in the 13155192Srwatson * documentation and/or other materials provided with the distribution. 14155192Srwatson * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15155192Srwatson * its contributors may be used to endorse or promote products derived 16155192Srwatson * from this software without specific prior written permission. 17155192Srwatson * 18155192Srwatson * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND 19155192Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20155192Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21155192Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR 22155192Srwatson * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23155192Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24155192Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25155192Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26155192Srwatson * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27155192Srwatson * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28155192Srwatson * POSSIBILITY OF SUCH DAMAGE. 29155192Srwatson * 30155192Srwatson * $FreeBSD: head/sys/security/audit/audit_bsm_klib.c 173142 2007-10-29 18:07:48Z rwatson $ 31155192Srwatson */ 32155192Srwatson 33155192Srwatson#include <sys/param.h> 34155192Srwatson#include <sys/fcntl.h> 35155192Srwatson#include <sys/filedesc.h> 36155192Srwatson#include <sys/libkern.h> 37155192Srwatson#include <sys/malloc.h> 38155192Srwatson#include <sys/mount.h> 39155192Srwatson#include <sys/proc.h> 40155192Srwatson#include <sys/sem.h> 41155192Srwatson#include <sys/syscall.h> 42155192Srwatson#include <sys/sysctl.h> 43155192Srwatson#include <sys/sysent.h> 44155192Srwatson#include <sys/vnode.h> 45155192Srwatson 46155192Srwatson#include <bsm/audit.h> 47155192Srwatson#include <bsm/audit_kevents.h> 48155192Srwatson#include <security/audit/audit.h> 49155192Srwatson#include <security/audit/audit_private.h> 50155192Srwatson 51155192Srwatson/* 52155192Srwatson * Hash table functions for the audit event number to event class mask 53155192Srwatson * mapping. 54155192Srwatson */ 55155192Srwatson#define EVCLASSMAP_HASH_TABLE_SIZE 251 56155192Srwatsonstruct evclass_elem { 57155192Srwatson au_event_t event; 58155192Srwatson au_class_t class; 59155192Srwatson LIST_ENTRY(evclass_elem) entry; 60155192Srwatson}; 61155192Srwatsonstruct evclass_list { 62155192Srwatson LIST_HEAD(, evclass_elem) head; 63155192Srwatson}; 64155192Srwatson 65155192Srwatsonstatic MALLOC_DEFINE(M_AUDITEVCLASS, "audit_evclass", "Audit event class"); 66156889Srwatsonstatic struct mtx evclass_mtx; 67156889Srwatsonstatic struct evclass_list evclass_hash[EVCLASSMAP_HASH_TABLE_SIZE]; 68155192Srwatson 69155192Srwatson/* 70155192Srwatson * Look up the class for an audit event in the class mapping table. 71155192Srwatson */ 72155192Srwatsonau_class_t 73155192Srwatsonau_event_class(au_event_t event) 74155192Srwatson{ 75155192Srwatson struct evclass_list *evcl; 76155192Srwatson struct evclass_elem *evc; 77155192Srwatson au_class_t class; 78155192Srwatson 79155192Srwatson mtx_lock(&evclass_mtx); 80155192Srwatson evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE]; 81173142Srwatson class = 0; 82155192Srwatson LIST_FOREACH(evc, &evcl->head, entry) { 83155192Srwatson if (evc->event == event) { 84155192Srwatson class = evc->class; 85155192Srwatson goto out; 86155192Srwatson } 87155192Srwatson } 88155192Srwatsonout: 89155192Srwatson mtx_unlock(&evclass_mtx); 90155192Srwatson return (class); 91155192Srwatson} 92155192Srwatson 93156889Srwatson/* 94155192Srwatson * Insert a event to class mapping. If the event already exists in the 95155192Srwatson * mapping, then replace the mapping with the new one. 96156889Srwatson * 97155192Srwatson * XXX There is currently no constraints placed on the number of mappings. 98156889Srwatson * May want to either limit to a number, or in terms of memory usage. 99155192Srwatson */ 100155192Srwatsonvoid 101156889Srwatsonau_evclassmap_insert(au_event_t event, au_class_t class) 102155192Srwatson{ 103155192Srwatson struct evclass_list *evcl; 104155192Srwatson struct evclass_elem *evc, *evc_new; 105155192Srwatson 106155192Srwatson /* 107155192Srwatson * Pessimistically, always allocate storage before acquiring mutex. 108155192Srwatson * Free if there is already a mapping for this event. 109155192Srwatson */ 110155192Srwatson evc_new = malloc(sizeof(*evc), M_AUDITEVCLASS, M_WAITOK); 111155192Srwatson 112155192Srwatson mtx_lock(&evclass_mtx); 113155192Srwatson evcl = &evclass_hash[event % EVCLASSMAP_HASH_TABLE_SIZE]; 114155192Srwatson LIST_FOREACH(evc, &evcl->head, entry) { 115155192Srwatson if (evc->event == event) { 116155192Srwatson evc->class = class; 117155192Srwatson mtx_unlock(&evclass_mtx); 118155192Srwatson free(evc_new, M_AUDITEVCLASS); 119155192Srwatson return; 120155192Srwatson } 121155192Srwatson } 122155192Srwatson evc = evc_new; 123155192Srwatson evc->event = event; 124155192Srwatson evc->class = class; 125155192Srwatson LIST_INSERT_HEAD(&evcl->head, evc, entry); 126155192Srwatson mtx_unlock(&evclass_mtx); 127155192Srwatson} 128155192Srwatson 129155192Srwatsonvoid 130156889Srwatsonau_evclassmap_init(void) 131155192Srwatson{ 132155192Srwatson int i; 133155192Srwatson 134155192Srwatson mtx_init(&evclass_mtx, "evclass_mtx", NULL, MTX_DEF); 135155192Srwatson for (i = 0; i < EVCLASSMAP_HASH_TABLE_SIZE; i++) 136155192Srwatson LIST_INIT(&evclass_hash[i].head); 137155192Srwatson 138155192Srwatson /* 139155192Srwatson * Set up the initial event to class mapping for system calls. 140155192Srwatson * 141155192Srwatson * XXXRW: Really, this should walk all possible audit events, not all 142155192Srwatson * native ABI system calls, as there may be audit events reachable 143155192Srwatson * only through non-native system calls. It also seems a shame to 144155192Srwatson * frob the mutex this early. 145156889Srwatson */ 146155192Srwatson for (i = 0; i < SYS_MAXSYSCALL; i++) { 147155192Srwatson if (sysent[i].sy_auevent != AUE_NULL) 148173142Srwatson au_evclassmap_insert(sysent[i].sy_auevent, 0); 149155192Srwatson } 150155192Srwatson} 151155192Srwatson 152155192Srwatson/* 153155192Srwatson * Check whether an event is aditable by comparing the mask of classes this 154155192Srwatson * event is part of against the given mask. 155155192Srwatson */ 156155192Srwatsonint 157159269Srwatsonau_preselect(au_event_t event, au_class_t class, au_mask_t *mask_p, int sorf) 158155192Srwatson{ 159155192Srwatson au_class_t effmask = 0; 160155192Srwatson 161155192Srwatson if (mask_p == NULL) 162155192Srwatson return (-1); 163155192Srwatson 164156889Srwatson /* 165155192Srwatson * Perform the actual check of the masks against the event. 166155192Srwatson */ 167155192Srwatson if (sorf & AU_PRS_SUCCESS) 168159269Srwatson effmask |= (mask_p->am_success & class); 169156889Srwatson 170155192Srwatson if (sorf & AU_PRS_FAILURE) 171159269Srwatson effmask |= (mask_p->am_failure & class); 172156889Srwatson 173155192Srwatson if (effmask) 174155192Srwatson return (1); 175156889Srwatson else 176155192Srwatson return (0); 177155192Srwatson} 178155192Srwatson 179155192Srwatson/* 180156889Srwatson * Convert sysctl names and present arguments to events. 181155192Srwatson */ 182155192Srwatsonau_event_t 183155192Srwatsonctlname_to_sysctlevent(int name[], uint64_t valid_arg) 184155192Srwatson{ 185155192Srwatson 186155192Srwatson /* can't parse it - so return the worst case */ 187156889Srwatson if ((valid_arg & (ARG_CTLNAME | ARG_LEN)) != (ARG_CTLNAME | ARG_LEN)) 188155192Srwatson return (AUE_SYSCTL); 189155192Srwatson 190155192Srwatson switch (name[0]) { 191155192Srwatson /* non-admin "lookups" treat them special */ 192155192Srwatson case KERN_OSTYPE: 193155192Srwatson case KERN_OSRELEASE: 194155192Srwatson case KERN_OSREV: 195155192Srwatson case KERN_VERSION: 196155192Srwatson case KERN_ARGMAX: 197155192Srwatson case KERN_CLOCKRATE: 198155192Srwatson case KERN_BOOTTIME: 199155192Srwatson case KERN_POSIX1: 200155192Srwatson case KERN_NGROUPS: 201155192Srwatson case KERN_JOB_CONTROL: 202155192Srwatson case KERN_SAVED_IDS: 203155192Srwatson case KERN_OSRELDATE: 204155192Srwatson case KERN_DUMMY: 205155192Srwatson return (AUE_SYSCTL_NONADMIN); 206155192Srwatson 207155192Srwatson /* only treat the changeable controls as admin */ 208155192Srwatson case KERN_MAXVNODES: 209155192Srwatson case KERN_MAXPROC: 210155192Srwatson case KERN_MAXFILES: 211155192Srwatson case KERN_MAXPROCPERUID: 212155192Srwatson case KERN_MAXFILESPERPROC: 213155192Srwatson case KERN_HOSTID: 214155192Srwatson case KERN_SECURELVL: 215155192Srwatson case KERN_HOSTNAME: 216155192Srwatson case KERN_VNODE: 217155192Srwatson case KERN_PROC: 218155192Srwatson case KERN_FILE: 219155192Srwatson case KERN_PROF: 220155192Srwatson case KERN_NISDOMAINNAME: 221155192Srwatson case KERN_UPDATEINTERVAL: 222155192Srwatson case KERN_NTP_PLL: 223155192Srwatson case KERN_BOOTFILE: 224155192Srwatson case KERN_DUMPDEV: 225155192Srwatson case KERN_IPC: 226155192Srwatson case KERN_PS_STRINGS: 227155192Srwatson case KERN_USRSTACK: 228155192Srwatson case KERN_LOGSIGEXIT: 229155192Srwatson case KERN_IOV_MAX: 230155192Srwatson case KERN_MAXID: 231155192Srwatson return ((valid_arg & ARG_VALUE) ? 232155192Srwatson AUE_SYSCTL : AUE_SYSCTL_NONADMIN); 233155192Srwatson 234155192Srwatson default: 235155192Srwatson return (AUE_SYSCTL); 236155192Srwatson } 237155192Srwatson /* NOTREACHED */ 238155192Srwatson} 239155192Srwatson 240155192Srwatson/* 241156889Srwatson * Convert an open flags specifier into a specific type of open event for 242155192Srwatson * auditing purposes. 243155192Srwatson */ 244155192Srwatsonau_event_t 245156889Srwatsonflags_and_error_to_openevent(int oflags, int error) 246156889Srwatson{ 247155192Srwatson au_event_t aevent; 248155192Srwatson 249156889Srwatson /* 250156889Srwatson * Need to check only those flags we care about. 251156889Srwatson */ 252155192Srwatson oflags = oflags & (O_RDONLY | O_CREAT | O_TRUNC | O_RDWR | O_WRONLY); 253155192Srwatson 254155192Srwatson /* 255155192Srwatson * These checks determine what flags are on with the condition that 256155192Srwatson * ONLY that combination is on, and no other flags are on. 257155192Srwatson */ 258155192Srwatson switch (oflags) { 259155192Srwatson case O_RDONLY: 260155192Srwatson aevent = AUE_OPEN_R; 261155192Srwatson break; 262155192Srwatson 263155192Srwatson case (O_RDONLY | O_CREAT): 264155192Srwatson aevent = AUE_OPEN_RC; 265155192Srwatson break; 266155192Srwatson 267155192Srwatson case (O_RDONLY | O_CREAT | O_TRUNC): 268155192Srwatson aevent = AUE_OPEN_RTC; 269155192Srwatson break; 270155192Srwatson 271155192Srwatson case (O_RDONLY | O_TRUNC): 272155192Srwatson aevent = AUE_OPEN_RT; 273155192Srwatson break; 274155192Srwatson 275155192Srwatson case O_RDWR: 276155192Srwatson aevent = AUE_OPEN_RW; 277155192Srwatson break; 278155192Srwatson 279155192Srwatson case (O_RDWR | O_CREAT): 280155192Srwatson aevent = AUE_OPEN_RWC; 281155192Srwatson break; 282155192Srwatson 283155192Srwatson case (O_RDWR | O_CREAT | O_TRUNC): 284155192Srwatson aevent = AUE_OPEN_RWTC; 285155192Srwatson break; 286155192Srwatson 287155192Srwatson case (O_RDWR | O_TRUNC): 288155192Srwatson aevent = AUE_OPEN_RWT; 289155192Srwatson break; 290155192Srwatson 291155192Srwatson case O_WRONLY: 292155192Srwatson aevent = AUE_OPEN_W; 293155192Srwatson break; 294155192Srwatson 295155192Srwatson case (O_WRONLY | O_CREAT): 296155192Srwatson aevent = AUE_OPEN_WC; 297155192Srwatson break; 298155192Srwatson 299155192Srwatson case (O_WRONLY | O_CREAT | O_TRUNC): 300155192Srwatson aevent = AUE_OPEN_WTC; 301155192Srwatson break; 302155192Srwatson 303155192Srwatson case (O_WRONLY | O_TRUNC): 304155192Srwatson aevent = AUE_OPEN_WT; 305155192Srwatson break; 306155192Srwatson 307155192Srwatson default: 308155192Srwatson aevent = AUE_OPEN; 309155192Srwatson break; 310155192Srwatson } 311155192Srwatson 312155192Srwatson#if 0 313156889Srwatson /* 314170196Srwatson * Convert chatty errors to better matching events. Failures to 315170196Srwatson * find a file are really just attribute events -- so recast them as 316170196Srwatson * such. 317155192Srwatson * 318155192Srwatson * XXXAUDIT: Solaris defines that AUE_OPEN will never be returned, it 319155192Srwatson * is just a placeholder. However, in Darwin we return that in 320155192Srwatson * preference to other events. For now, comment this out as we don't 321155192Srwatson * have a BSM conversion routine for AUE_OPEN. 322155192Srwatson */ 323155192Srwatson switch (aevent) { 324155192Srwatson case AUE_OPEN_R: 325155192Srwatson case AUE_OPEN_RT: 326155192Srwatson case AUE_OPEN_RW: 327155192Srwatson case AUE_OPEN_RWT: 328155192Srwatson case AUE_OPEN_W: 329155192Srwatson case AUE_OPEN_WT: 330155192Srwatson if (error == ENOENT) 331155192Srwatson aevent = AUE_OPEN; 332155192Srwatson } 333155192Srwatson#endif 334155192Srwatson return (aevent); 335155192Srwatson} 336155192Srwatson 337155192Srwatson/* 338155192Srwatson * Convert a MSGCTL command to a specific event. 339155192Srwatson */ 340155192Srwatsonint 341155192Srwatsonmsgctl_to_event(int cmd) 342155192Srwatson{ 343155192Srwatson 344155192Srwatson switch (cmd) { 345155192Srwatson case IPC_RMID: 346155192Srwatson return (AUE_MSGCTL_RMID); 347155192Srwatson 348155192Srwatson case IPC_SET: 349155192Srwatson return (AUE_MSGCTL_SET); 350155192Srwatson 351155192Srwatson case IPC_STAT: 352155192Srwatson return (AUE_MSGCTL_STAT); 353155192Srwatson 354155192Srwatson default: 355170196Srwatson /* We will audit a bad command. */ 356155192Srwatson return (AUE_MSGCTL); 357155192Srwatson } 358155192Srwatson} 359155192Srwatson 360155192Srwatson/* 361155192Srwatson * Convert a SEMCTL command to a specific event. 362155192Srwatson */ 363155192Srwatsonint 364155192Srwatsonsemctl_to_event(int cmd) 365155192Srwatson{ 366155192Srwatson 367155192Srwatson switch (cmd) { 368155192Srwatson case GETALL: 369155192Srwatson return (AUE_SEMCTL_GETALL); 370155192Srwatson 371155192Srwatson case GETNCNT: 372155192Srwatson return (AUE_SEMCTL_GETNCNT); 373155192Srwatson 374155192Srwatson case GETPID: 375155192Srwatson return (AUE_SEMCTL_GETPID); 376155192Srwatson 377155192Srwatson case GETVAL: 378155192Srwatson return (AUE_SEMCTL_GETVAL); 379155192Srwatson 380155192Srwatson case GETZCNT: 381155192Srwatson return (AUE_SEMCTL_GETZCNT); 382155192Srwatson 383155192Srwatson case IPC_RMID: 384155192Srwatson return (AUE_SEMCTL_RMID); 385155192Srwatson 386155192Srwatson case IPC_SET: 387155192Srwatson return (AUE_SEMCTL_SET); 388155192Srwatson 389155192Srwatson case SETALL: 390155192Srwatson return (AUE_SEMCTL_SETALL); 391155192Srwatson 392155192Srwatson case SETVAL: 393155192Srwatson return (AUE_SEMCTL_SETVAL); 394155192Srwatson 395155192Srwatson case IPC_STAT: 396155192Srwatson return (AUE_SEMCTL_STAT); 397155192Srwatson 398155192Srwatson default: 399155192Srwatson /* We will audit a bad command */ 400155192Srwatson return (AUE_SEMCTL); 401155192Srwatson } 402155192Srwatson} 403155192Srwatson 404155192Srwatson/* 405155192Srwatson * Convert a command for the auditon() system call to a audit event. 406155192Srwatson */ 407155192Srwatsonint 408155192Srwatsonauditon_command_event(int cmd) 409155192Srwatson{ 410155192Srwatson 411155192Srwatson switch(cmd) { 412155192Srwatson case A_GETPOLICY: 413155192Srwatson return (AUE_AUDITON_GPOLICY); 414155192Srwatson 415155192Srwatson case A_SETPOLICY: 416155192Srwatson return (AUE_AUDITON_SPOLICY); 417155192Srwatson 418155192Srwatson case A_GETKMASK: 419155192Srwatson return (AUE_AUDITON_GETKMASK); 420155192Srwatson 421155192Srwatson case A_SETKMASK: 422155192Srwatson return (AUE_AUDITON_SETKMASK); 423155192Srwatson 424155192Srwatson case A_GETQCTRL: 425155192Srwatson return (AUE_AUDITON_GQCTRL); 426155192Srwatson 427155192Srwatson case A_SETQCTRL: 428155192Srwatson return (AUE_AUDITON_SQCTRL); 429155192Srwatson 430155192Srwatson case A_GETCWD: 431155192Srwatson return (AUE_AUDITON_GETCWD); 432155192Srwatson 433155192Srwatson case A_GETCAR: 434155192Srwatson return (AUE_AUDITON_GETCAR); 435155192Srwatson 436155192Srwatson case A_GETSTAT: 437155192Srwatson return (AUE_AUDITON_GETSTAT); 438155192Srwatson 439155192Srwatson case A_SETSTAT: 440155192Srwatson return (AUE_AUDITON_SETSTAT); 441155192Srwatson 442155192Srwatson case A_SETUMASK: 443155192Srwatson return (AUE_AUDITON_SETUMASK); 444155192Srwatson 445155192Srwatson case A_SETSMASK: 446155192Srwatson return (AUE_AUDITON_SETSMASK); 447155192Srwatson 448155192Srwatson case A_GETCOND: 449155192Srwatson return (AUE_AUDITON_GETCOND); 450155192Srwatson 451155192Srwatson case A_SETCOND: 452155192Srwatson return (AUE_AUDITON_SETCOND); 453155192Srwatson 454155192Srwatson case A_GETCLASS: 455155192Srwatson return (AUE_AUDITON_GETCLASS); 456155192Srwatson 457155192Srwatson case A_SETCLASS: 458155192Srwatson return (AUE_AUDITON_SETCLASS); 459155192Srwatson 460155192Srwatson case A_GETPINFO: 461155192Srwatson case A_SETPMASK: 462155192Srwatson case A_SETFSIZE: 463155192Srwatson case A_GETFSIZE: 464155192Srwatson case A_GETPINFO_ADDR: 465155192Srwatson case A_GETKAUDIT: 466155192Srwatson case A_SETKAUDIT: 467155192Srwatson default: 468155192Srwatson return (AUE_AUDITON); /* No special record */ 469155192Srwatson } 470155192Srwatson} 471155192Srwatson 472156889Srwatson/* 473156889Srwatson * Create a canonical path from given path by prefixing either the root 474156889Srwatson * directory, or the current working directory. If the process working 475170196Srwatson * directory is NULL, we could use 'rootvnode' to obtain the root directory, 476156889Srwatson * but this results in a volfs name written to the audit log. So we will 477156889Srwatson * leave the filename starting with '/' in the audit log in this case. 478155192Srwatson * 479155192Srwatson * XXXRW: Since we combine two paths here, ideally a buffer of size 480155192Srwatson * MAXPATHLEN * 2 would be passed in. 481155192Srwatson */ 482155192Srwatsonvoid 483155192Srwatsoncanon_path(struct thread *td, char *path, char *cpath) 484155192Srwatson{ 485155192Srwatson char *bufp; 486155192Srwatson char *retbuf, *freebuf; 487155192Srwatson struct vnode *vnp; 488155192Srwatson struct filedesc *fdp; 489159143Scsjp int cisr, error, vfslocked; 490155192Srwatson 491165621Srwatson WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, 492165621Srwatson "canon_path() at %s:%d", __FILE__, __LINE__); 493165621Srwatson 494155192Srwatson fdp = td->td_proc->p_fd; 495155192Srwatson bufp = path; 496159143Scsjp cisr = 0; 497168355Srwatson FILEDESC_SLOCK(fdp); 498155192Srwatson if (*(path) == '/') { 499156889Srwatson while (*(bufp) == '/') 500156889Srwatson bufp++; /* Skip leading '/'s. */ 501156889Srwatson /* 502156889Srwatson * If no process root, or it is the same as the system root, 503155192Srwatson * audit the path as passed in with a single '/'. 504155192Srwatson */ 505155192Srwatson if ((fdp->fd_rdir == NULL) || 506156889Srwatson (fdp->fd_rdir == rootvnode)) { 507155192Srwatson vnp = NULL; 508156889Srwatson bufp--; /* Restore one '/'. */ 509155192Srwatson } else { 510156889Srwatson vnp = fdp->fd_rdir; /* Use process root. */ 511155192Srwatson vref(vnp); 512155192Srwatson } 513155192Srwatson } else { 514156889Srwatson vnp = fdp->fd_cdir; /* Prepend the current dir. */ 515159143Scsjp cisr = (fdp->fd_rdir == fdp->fd_cdir); 516155192Srwatson vref(vnp); 517155192Srwatson bufp = path; 518155192Srwatson } 519168355Srwatson FILEDESC_SUNLOCK(fdp); 520155192Srwatson if (vnp != NULL) { 521155192Srwatson /* 522156889Srwatson * XXX: vn_fullpath() on FreeBSD is "less reliable" than 523156889Srwatson * vn_getpath() on Darwin, so this will need more attention 524156889Srwatson * in the future. Also, the question and string bounding 525156889Srwatson * here seems a bit questionable and will also require 526156889Srwatson * attention. 527155192Srwatson */ 528155192Srwatson vfslocked = VFS_LOCK_GIANT(vnp->v_mount); 529155192Srwatson vn_lock(vnp, LK_EXCLUSIVE | LK_RETRY, td); 530155192Srwatson error = vn_fullpath(td, vnp, &retbuf, &freebuf); 531155192Srwatson if (error == 0) { 532159143Scsjp /* Copy and free buffer allocated by vn_fullpath(). 533159143Scsjp * If the current working directory was the same as 534159143Scsjp * the root directory, and the path was a relative 535159143Scsjp * pathname, do not separate the two components with 536159143Scsjp * the '/' character. 537159143Scsjp */ 538159143Scsjp snprintf(cpath, MAXPATHLEN, "%s%s%s", retbuf, 539159143Scsjp cisr ? "" : "/", bufp); 540156889Srwatson free(freebuf, M_TEMP); 541156889Srwatson } else 542155192Srwatson cpath[0] = '\0'; 543155192Srwatson vput(vnp); 544155192Srwatson VFS_UNLOCK_GIANT(vfslocked); 545170196Srwatson } else 546155192Srwatson strlcpy(cpath, bufp, MAXPATHLEN); 547155192Srwatson} 548