1/* 2 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include <stdarg.h> 30#include <sys/errno.h> 31#include <sys/types.h> 32#include <sys/malloc.h> 33#include <sys/buf.h> 34#include <sys/time.h> 35#include <sys/kauth.h> 36#include <sys/mount.h> 37#include <sys/vnode.h> 38#include <sys/syslog.h> /* for vaddlog() */ 39#include <sys/vnode_internal.h> 40#include <dev/random/randomdev.h> 41 42#include <sys/fslog.h> 43#include <sys/mount_internal.h> 44 45#include <uuid/uuid.h> 46 47/* String to append as format modifier for each key-value pair */ 48#define FSLOG_KEYVAL_FMT "[%s %s] " 49#define FSLOG_KEYVAL_FMT_LEN (sizeof(FSLOG_KEYVAL_FMT) - 1) 50 51#define FSLOG_NEWLINE_CHAR "\n" 52#define FSLOG_NEWLINE_CHAR_LEN (sizeof(FSLOG_NEWLINE_CHAR) - 1) 53 54/* Length of entire ASL message in 10 characters. Kernel defaults to zero */ 55#define FSLOG_ASL_MSG_LEN " 0" 56 57/* Length of default format string to be used by printf */ 58#define MAX_FMT_LEN 256 59 60/* Internal function to print input values as key-value pairs in format 61 * identifiable by Apple system log (ASL) facility. All key-value pairs 62 * are assumed to be pointer to strings and are provided using two ways - 63 * (a) va_list argument which is a list of varying number of arguments 64 * created by the caller of this function. 65 * (b) variable number of arguments passed to this function. 66 * 67 * Parameters - 68 * level - Priority level for this ASL message 69 * facility - Facility for this ASL message. 70 * num_pairs - Number of key-value pairs provided by vargs argument. 71 * vargs - List of key-value pairs. 72 * ... - Additional key-value pairs (apart from vargs) as variable 73 * argument list. A NULL value indicates the end of the 74 * variable argument list. 75 * 76 * Returns - 77 * zero - On success, when it prints all key-values pairs provided. 78 * E2BIG - When it cannot print all key-value pairs provided and had 79 * to truncate the output. 80 */ 81static int fslog_asl_msg(int level, const char *facility, int num_pairs, va_list vargs, ...) 82{ 83 int err = 0; 84 char fmt[MAX_FMT_LEN]; /* Format string to use with vaddlog */ 85 int calc_pairs = 0; 86 size_t len; 87 int i; 88 va_list ap; 89 char *ptr; 90 91 /* Mask extra bits, if any, from priority level */ 92 level = LOG_PRI(level); 93 94 /* Create the first part of format string consisting of ASL 95 * message length, level, and facility. 96 */ 97 if (facility) { 98 snprintf(fmt, MAX_FMT_LEN, "%s [%s %d] [%s %d] [%s %s] ", 99 FSLOG_ASL_MSG_LEN, 100 FSLOG_KEY_LEVEL, level, 101 FSLOG_KEY_READ_UID, FSLOG_VAL_READ_UID, 102 FSLOG_KEY_FACILITY, facility); 103 } else { 104 snprintf(fmt, MAX_FMT_LEN, "%s [%s %d] [%s %d] ", 105 FSLOG_ASL_MSG_LEN, 106 FSLOG_KEY_LEVEL, level, 107 FSLOG_KEY_READ_UID, FSLOG_VAL_READ_UID); 108 } 109 110 /* Determine the number of key-value format string [%s %s] that 111 * should be added in format string for every key-value pair provided 112 * in va_list. Calculate maximum number of format string that can be 113 * accommodated in the remaining format buffer (after saving space 114 * for newline character). If the caller provided pairs in va_list 115 * is more than calculated pairs, truncate extra pairs. 116 */ 117 len = MAX_FMT_LEN - strlen(fmt) - FSLOG_NEWLINE_CHAR_LEN - 1; 118 calc_pairs = len / FSLOG_KEYVAL_FMT_LEN; 119 if (num_pairs <= calc_pairs) { 120 calc_pairs = num_pairs; 121 } else { 122 err = E2BIG; 123 } 124 125 /* Append format strings [%s %s] for the key-value pairs in vargs */ 126 len = MAX_FMT_LEN - FSLOG_NEWLINE_CHAR_LEN; 127 for (i = 0; i < calc_pairs; i++) { 128 (void) strlcat(fmt, FSLOG_KEYVAL_FMT, len); 129 } 130 131 /* Count number of variable arguments provided to this function 132 * and determine total number of key-value pairs. 133 */ 134 calc_pairs = 0; 135 va_start(ap, vargs); 136 ptr = va_arg(ap, char *); 137 while (ptr) { 138 calc_pairs++; 139 ptr = va_arg(ap, char *); 140 } 141 calc_pairs /= 2; 142 va_end(ap); 143 144 /* If user provided variable number of arguments, append them as 145 * as real key-value "[k v]" into the format string. If the format 146 * string is too small, ignore the key-value pair completely. 147 */ 148 if (calc_pairs) { 149 char *key, *val; 150 size_t pairlen; 151 int offset; 152 153 /* Calculate bytes available for key-value pairs after reserving 154 * bytes for newline character and NULL terminator 155 */ 156 len = MAX_FMT_LEN - strlen(fmt) - FSLOG_NEWLINE_CHAR_LEN - 1; 157 offset = strlen(fmt); 158 159 va_start(ap, vargs); 160 for (i = 0; i < calc_pairs; i++) { 161 key = va_arg(ap, char *); 162 val = va_arg(ap, char *); 163 164 /* Calculate bytes required to store next key-value pair as 165 * "[key val] " including space for '[', ']', and two spaces. 166 */ 167 pairlen = strlen(key) + strlen(val) + 4; 168 if (pairlen > len) { 169 err = E2BIG; 170 break; 171 } 172 173 /* len + 1 because one byte has been set aside for NULL 174 * terminator in calculation of 'len' above 175 */ 176 snprintf((fmt + offset), len + 1, FSLOG_KEYVAL_FMT, key, val); 177 offset += pairlen; 178 len -= pairlen; 179 } 180 va_end(ap); 181 } 182 183 /* Append newline */ 184 (void) strlcat(fmt, FSLOG_NEWLINE_CHAR, MAX_FMT_LEN); 185 186 /* Print the key-value pairs in ASL format */ 187 vaddlog(fmt, vargs); 188 189 return err; 190} 191 192/* Log file system related error in key-value format identified by Apple 193 * system log (ASL) facility. The key-value pairs are string pointers 194 * (char *) and are provided as variable arguments list. A NULL value 195 * indicates end of the list. 196 * 197 * Keys can not contain '[', ']', space, and newline. Values can not 198 * contain '[', ']', and newline. If any key-value contains any of the 199 * reserved characters, the behavior is undefined. The caller of the 200 * function should escape any occurrences of '[' and ']' by prefixing 201 * it with '\'. 202 * 203 * The function takes a message ID which can be used to logically group 204 * different ASL messages. Messages in same logical group have same message 205 * ID and have information to describe order of the message --- first, 206 * middle, or last. 207 * 208 * The following message IDs have special meaning - 209 * FSLOG_MSG_FIRST - This message is the first message in its logical 210 * group. This generates a unique message ID, creates two key-value 211 * pairs with message ID and order of the message as "First". 212 * FSLOG_MSG_LAST - This is really a MASK which should be logically OR'ed 213 * with message ID to indicate the last message for a logical group. 214 * This also creates two key-value pairs with message ID and order of 215 * message as "Last". 216 * FSLOG_MSG_SINGLE - This signifies that the message is the only message 217 * in its logical group. Therefore no extra key-values are generated 218 * for this option. 219 * For all other values of message IDs, it regards them as intermediate 220 * message and generates two key-value pairs with message ID and order of 221 * message as "Middle". 222 * 223 * Returns - 224 * Message ID of the ASL message printed. The caller should use 225 * this value to print intermediate messages or end the logical message 226 * group. 227 * For FSLOG_MSG_SINGLE option, it returns FSLOG_MSG_SINGLE. 228 */ 229unsigned long fslog_err(unsigned long msg_id, ... ) 230{ 231 va_list ap; 232 int num_pairs; 233 char msg_id_str[21]; /* To convert 64-bit number to string with NULL char */ 234 char *arg; 235 const char *msg_order_ptr; 236 237 /* Count number of arguments and key-value pairs provided by user */ 238 num_pairs = 0; 239 va_start(ap, msg_id); 240 arg = va_arg(ap, char *); 241 while (arg) { 242 num_pairs++; 243 arg = va_arg(ap, char *); 244 } 245 num_pairs /= 2; 246 va_end(ap); 247 248 va_start(ap, msg_id); 249 if (msg_id == FSLOG_MSG_SINGLE) { 250 /* Single message, do not print message ID and message order */ 251 (void) fslog_asl_msg(FSLOG_VAL_LEVEL, FSLOG_VAL_FACILITY, 252 num_pairs, ap, NULL); 253 } else { 254 if (msg_id == FSLOG_MSG_FIRST) { 255 /* First message, generate random message ID */ 256 while ((msg_id == FSLOG_MSG_FIRST) || 257 (msg_id == FSLOG_MSG_LAST) || 258 (msg_id == FSLOG_MSG_SINGLE)) { 259 msg_id = RandomULong(); 260 /* MSB is reserved for indicating last message 261 * in sequence. Clear the MSB while generating 262 * new message ID. 263 */ 264 msg_id = msg_id >> 1; 265 } 266 msg_order_ptr = FSLOG_VAL_ORDER_FIRST; 267 } else if (msg_id & FSLOG_MSG_LAST) { 268 /* MSB set to indicate last message for this ID */ 269 msg_order_ptr = FSLOG_VAL_ORDER_LAST; 270 /* MSB of message ID is set to indicate last message 271 * in sequence. Clear the bit to get real message ID. 272 */ 273 msg_id = msg_id & ~FSLOG_MSG_LAST; 274 } else { 275 /* Intermediate message for this ID */ 276 msg_order_ptr = FSLOG_VAL_ORDER_MIDDLE; 277 } 278 279 snprintf(msg_id_str, sizeof(msg_id_str), "%lu", msg_id); 280 (void) fslog_asl_msg(FSLOG_VAL_LEVEL, FSLOG_VAL_FACILITY, 281 num_pairs, ap, 282 FSLOG_KEY_MSG_ID, msg_id_str, 283 FSLOG_KEY_MSG_ORDER, msg_order_ptr, NULL); 284 } 285 va_end(ap); 286 return msg_id; 287} 288 289/* Search if given string contains '[' and ']'. If any, escape it by 290 * prefixing with a '\'. If the length of the string is not big enough, 291 * no changes are done and error is returned. 292 * 293 * Parameters - 294 * str - string that can contain '[' or ']', should be NULL terminated 295 * len - length, in bytes, of valid data, including NULL character. 296 * buflen - size of buffer that contains the string 297 */ 298static int escape_str(char *str, int len, int buflen) 299{ 300 int count; 301 char *src, *dst; 302 303 /* Count number of characters to escape */ 304 src = str; 305 count = 0; 306 do { 307 if ((*src == '[') || (*src == ']')) { 308 count++; 309 } 310 } while (*src++); 311 312 if (count) { 313 /* Check if the buffer has enough space to escape all characters */ 314 if ((buflen - len) < count) { 315 return ENOSPC; 316 } 317 318 src = str + len; 319 dst = src + count; 320 while (count) { 321 *dst-- = *src; 322 if ((*src == '[') || (*src == ']')) { 323 /* Last char copied needs to be escaped */ 324 *dst-- = '\\'; 325 count--; 326 } 327 src--; 328 } 329 } 330 331 return 0; 332} 333 334/* Log information about runtime file system corruption detected by 335 * the file system. It takes the VFS mount structure as 336 * parameter which is used to access the mount point of the 337 * corrupt volume. If no mount structure or mount point string 338 * string exists, nothing is logged to ASL database. 339 * 340 * Currently prints following information - 341 * 1. Mount Point 342 */ 343void fslog_fs_corrupt(struct mount *mnt) 344{ 345 if (mnt != NULL) { 346 fslog_err(FSLOG_MSG_SINGLE, 347 FSLOG_KEY_ERR_TYPE, FSLOG_VAL_ERR_TYPE_FS, 348 FSLOG_KEY_MNTPT, mnt->mnt_vfsstat.f_mntonname, 349 NULL); 350 } 351 352 return; 353} 354 355/* Log information about IO error detected in buf_biodone() 356 * Currently prints following information - 357 * 1. Physical block number 358 * 2. Logical block number 359 * 3. Device node 360 * 4. Mount point 361 * 5. Path for file, if any 362 * 6. Error number 363 * 7. Type of IO (read/write) 364 */ 365void fslog_io_error(const buf_t bp) 366{ 367 int err; 368 unsigned long msg_id; 369 char blknum_str[21]; 370 char lblknum_str[21]; 371 char errno_str[6]; 372 const char *iotype; 373 unsigned char print_last = 0; 374 vnode_t vp; 375 376 if (buf_error(bp) == 0) { 377 return; 378 } 379 380 /* Convert error number to string */ 381 snprintf (errno_str, sizeof(errno_str), "%d", buf_error(bp)); 382 383 /* Determine type of IO operation */ 384 if (buf_flags(bp) & B_READ) { 385 iotype = FSLOG_VAL_IOTYPE_READ; 386 } else { 387 iotype = FSLOG_VAL_IOTYPE_WRITE; 388 } 389 390 /* Convert physical block number to string */ 391 snprintf (blknum_str, sizeof(blknum_str), "%lld", buf_blkno(bp)); 392 393 /* Convert logical block number to string */ 394 snprintf (lblknum_str, sizeof(lblknum_str), "%lld", buf_lblkno(bp)); 395 396 msg_id = fslog_err(FSLOG_MSG_FIRST, 397 FSLOG_KEY_ERR_TYPE, FSLOG_VAL_ERR_TYPE_IO, 398 FSLOG_KEY_ERRNO, errno_str, 399 FSLOG_KEY_IOTYPE, iotype, 400 FSLOG_KEY_PHYS_BLKNUM, blknum_str, 401 FSLOG_KEY_LOG_BLKNUM, lblknum_str, 402 NULL); 403 404 /* Access the vnode for this buffer */ 405 vp = buf_vnode(bp); 406 if (vp) { 407 struct vfsstatfs *sp; 408 mount_t mp; 409 char *path; 410 int len; 411 struct vfs_context context; 412 413 mp = vnode_mount(vp); 414 /* mp should be NULL only for bdevvp during boot */ 415 if (mp == NULL) { 416 goto out; 417 } 418 sp = vfs_statfs(mp); 419 420 /* Access the file path */ 421 MALLOC(path, char *, MAXPATHLEN, M_TEMP, M_WAITOK); 422 if (path) { 423 len = MAXPATHLEN; 424 context.vc_thread = current_thread(); 425 context.vc_ucred = kauth_cred_get(); 426 /* Find path without entering file system */ 427 err = build_path(vp, path, len, &len, BUILDPATH_NO_FS_ENTER, 428 &context); 429 if (!err) { 430 err = escape_str(path, len, MAXPATHLEN); 431 if (!err) { 432 /* Print device node, mount point, path */ 433 msg_id = fslog_err(msg_id | FSLOG_MSG_LAST, 434 FSLOG_KEY_DEVNODE, sp->f_mntfromname, 435 FSLOG_KEY_MNTPT, sp->f_mntonname, 436 FSLOG_KEY_PATH, path, 437 NULL); 438 print_last = 1; 439 } 440 } 441 FREE(path, M_TEMP); 442 } 443 444 if (print_last == 0) { 445 /* Print device node and mount point */ 446 msg_id = fslog_err(msg_id | FSLOG_MSG_LAST, 447 FSLOG_KEY_DEVNODE, sp->f_mntfromname, 448 FSLOG_KEY_MNTPT, sp->f_mntonname, 449 NULL); 450 print_last = 1; 451 } 452 } 453 454out: 455 if (print_last == 0) { 456 msg_id = fslog_err(msg_id | FSLOG_MSG_LAST, NULL); 457 } 458 459 return; 460} 461 462static void 463_fslog_extmod_msgtracer_internal(int level, const char *facility, int num_pairs, ...) 464{ 465 va_list ap; 466 467 va_start(ap, num_pairs); 468 (void) fslog_asl_msg(level, facility, 469 num_pairs, ap, NULL); 470 va_end(ap); 471} 472 473/* Log information about external modification of a process, 474 * using MessageTracer formatting. Assumes that both the caller 475 * and target are appropriately locked. 476 * Currently prints following information - 477 * 1. Caller process name (truncated to 16 characters) 478 * 2. Caller process Mach-O UUID 479 * 3. Target process name (truncated to 16 characters) 480 * 4. Target process Mach-O UUID 481 */ 482void 483fslog_extmod_msgtracer(proc_t caller, proc_t target) 484{ 485 if ((caller != PROC_NULL) && (target != PROC_NULL)) { 486 487 /* 488 * Print into buffer large enough for "ThisIsAnApplicat(BC223DD7-B314-42E0-B6B0-C5D2E6638337)", 489 * including space for escaping, and NUL byte included in sizeof(uuid_string_t). 490 */ 491 492 uuid_string_t uuidstr; 493 char c_name[2*MAXCOMLEN + 2 /* () */ + sizeof(uuid_string_t)]; 494 char t_name[2*MAXCOMLEN + 2 /* () */ + sizeof(uuid_string_t)]; 495 496 strlcpy(c_name, caller->p_comm, sizeof(c_name)); 497 uuid_unparse_upper(caller->p_uuid, uuidstr); 498 strlcat(c_name, "(", sizeof(c_name)); 499 strlcat(c_name, uuidstr, sizeof(c_name)); 500 strlcat(c_name, ")", sizeof(c_name)); 501 if (0 != escape_str(c_name, strlen(c_name), sizeof(c_name))) { 502 return; 503 } 504 505 strlcpy(t_name, target->p_comm, sizeof(t_name)); 506 uuid_unparse_upper(target->p_uuid, uuidstr); 507 strlcat(t_name, "(", sizeof(t_name)); 508 strlcat(t_name, uuidstr, sizeof(t_name)); 509 strlcat(t_name, ")", sizeof(t_name)); 510 if (0 != escape_str(t_name, strlen(t_name), sizeof(t_name))) { 511 return; 512 } 513 514#if DEBUG 515 printf("EXTMOD: %s(%d) -> %s(%d)\n", 516 c_name, 517 proc_pid(caller), 518 t_name, 519 proc_pid(target)); 520#endif 521 522 _fslog_extmod_msgtracer_internal(LOG_DEBUG, "messagetracer", 523 4, 524 "com.apple.message.domain", "com.apple.kernel.external_modification", /* 0 */ 525 "com.apple.message.signature", c_name, /* 1 */ 526 "com.apple.message.signature2", t_name, /* 2 */ 527 "com.apple.message.result", "noop", /* 3 */ 528 NULL); 529 } 530} 531