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 <sys/errno.h> 30#include <sys/types.h> 31#include <sys/malloc.h> 32#include <sys/buf.h> 33#include <sys/time.h> 34#include <sys/kauth.h> 35#include <sys/mount.h> 36#include <sys/vnode.h> 37#include <sys/syslog.h> 38#include <sys/vnode_internal.h> 39#include <sys/fslog.h> 40#include <sys/mount_internal.h> 41#include <sys/kasl.h> 42 43#include <dev/random/randomdev.h> 44 45#include <uuid/uuid.h> 46 47#include <stdarg.h> 48 49/* Log information about external modification of a process, 50 * using MessageTracer formatting. Assumes that both the caller 51 * and target are appropriately locked. 52 * Currently prints following information - 53 * 1. Caller process name (truncated to 16 characters) 54 * 2. Caller process Mach-O UUID 55 * 3. Target process name (truncated to 16 characters) 56 * 4. Target process Mach-O UUID 57 */ 58void 59fslog_extmod_msgtracer(proc_t caller, proc_t target) 60{ 61 if ((caller != PROC_NULL) && (target != PROC_NULL)) { 62 63 /* 64 * Print into buffer large enough for "ThisIsAnApplicat(BC223DD7-B314-42E0-B6B0-C5D2E6638337)", 65 * including space for escaping, and NUL byte included in sizeof(uuid_string_t). 66 */ 67 68 uuid_string_t uuidstr; 69 char c_name[2*MAXCOMLEN + 2 /* () */ + sizeof(uuid_string_t)]; 70 char t_name[2*MAXCOMLEN + 2 /* () */ + sizeof(uuid_string_t)]; 71 72 strlcpy(c_name, caller->p_comm, sizeof(c_name)); 73 uuid_unparse_upper(caller->p_uuid, uuidstr); 74 strlcat(c_name, "(", sizeof(c_name)); 75 strlcat(c_name, uuidstr, sizeof(c_name)); 76 strlcat(c_name, ")", sizeof(c_name)); 77 if (0 != escape_str(c_name, strlen(c_name), sizeof(c_name))) { 78 return; 79 } 80 81 strlcpy(t_name, target->p_comm, sizeof(t_name)); 82 uuid_unparse_upper(target->p_uuid, uuidstr); 83 strlcat(t_name, "(", sizeof(t_name)); 84 strlcat(t_name, uuidstr, sizeof(t_name)); 85 strlcat(t_name, ")", sizeof(t_name)); 86 if (0 != escape_str(t_name, strlen(t_name), sizeof(t_name))) { 87 return; 88 } 89#if DEBUG 90 printf("EXTMOD: %s(%d) -> %s(%d)\n", 91 c_name, 92 proc_pid(caller), 93 t_name, 94 proc_pid(target)); 95#endif 96 97 kern_asl_msg(LOG_DEBUG, "messagetracer", 98 5, 99 "com.apple.message.domain", "com.apple.kernel.external_modification", /* 0 */ 100 "com.apple.message.signature", c_name, /* 1 */ 101 "com.apple.message.signature2", t_name, /* 2 */ 102 "com.apple.message.result", "noop", /* 3 */ 103 "com.apple.message.summarize", "YES", /* 4 */ 104 NULL); 105 } 106} 107 108/* Log file system related error in key-value format identified by Apple 109 * system log (ASL) facility. The key-value pairs are string pointers 110 * (char *) and are provided as variable arguments list. A NULL value 111 * indicates end of the list. 112 * 113 * Keys can not contain '[', ']', space, and newline. Values can not 114 * contain '[', ']', and newline. If any key-value contains any of the 115 * reserved characters, the behavior is undefined. The caller of the 116 * function should escape any occurrences of '[' and ']' by prefixing 117 * it with '\'. 118 * 119 * The function takes a message ID which can be used to logically group 120 * different ASL messages. Messages in same logical group have same message 121 * ID and have information to describe order of the message --- first, 122 * middle, or last. 123 * 124 * The following message IDs have special meaning - 125 * FSLOG_MSG_FIRST - This message is the first message in its logical 126 * group. This generates a unique message ID, creates two key-value 127 * pairs with message ID and order of the message as "First". 128 * FSLOG_MSG_LAST - This is really a MASK which should be logically OR'ed 129 * with message ID to indicate the last message for a logical group. 130 * This also creates two key-value pairs with message ID and order of 131 * message as "Last". 132 * FSLOG_MSG_SINGLE - This signifies that the message is the only message 133 * in its logical group. Therefore no extra key-values are generated 134 * for this option. 135 * For all other values of message IDs, it regards them as intermediate 136 * message and generates two key-value pairs with message ID and order of 137 * message as "Middle". 138 * 139 * Returns - 140 * Message ID of the ASL message printed. The caller should use 141 * this value to print intermediate messages or end the logical message 142 * group. 143 * For FSLOG_MSG_SINGLE option, it returns FSLOG_MSG_SINGLE. 144 */ 145unsigned long fslog_err(unsigned long msg_id, ... ) 146{ 147 va_list ap; 148 int num_pairs; 149 char msg_id_str[21]; /* To convert 64-bit number to string with NULL char */ 150 char *arg; 151 const char *msg_order_ptr; 152 153 /* Count number of arguments and key-value pairs provided by user */ 154 num_pairs = 0; 155 va_start(ap, msg_id); 156 arg = va_arg(ap, char *); 157 while (arg) { 158 num_pairs++; 159 arg = va_arg(ap, char *); 160 } 161 num_pairs /= 2; 162 va_end(ap); 163 164 va_start(ap, msg_id); 165 if (msg_id == FSLOG_MSG_SINGLE) { 166 /* Single message, do not print message ID and message order */ 167 (void) kern_asl_msg_va(FSLOG_VAL_LEVEL, FSLOG_VAL_FACILITY, 168 num_pairs, ap, 169 FSLOG_KEY_READ_UID, FSLOG_VAL_READ_UID, 170 NULL); 171 } else { 172 if (msg_id == FSLOG_MSG_FIRST) { 173 /* First message, generate random message ID */ 174 while ((msg_id == FSLOG_MSG_FIRST) || 175 (msg_id == FSLOG_MSG_LAST) || 176 (msg_id == FSLOG_MSG_SINGLE)) { 177 msg_id = RandomULong(); 178 /* MSB is reserved for indicating last message 179 * in sequence. Clear the MSB while generating 180 * new message ID. 181 */ 182 msg_id = msg_id >> 1; 183 } 184 msg_order_ptr = FSLOG_VAL_ORDER_FIRST; 185 } else if (msg_id & FSLOG_MSG_LAST) { 186 /* MSB set to indicate last message for this ID */ 187 msg_order_ptr = FSLOG_VAL_ORDER_LAST; 188 /* MSB of message ID is set to indicate last message 189 * in sequence. Clear the bit to get real message ID. 190 */ 191 msg_id = msg_id & ~FSLOG_MSG_LAST; 192 } else { 193 /* Intermediate message for this ID */ 194 msg_order_ptr = FSLOG_VAL_ORDER_MIDDLE; 195 } 196 197 snprintf(msg_id_str, sizeof(msg_id_str), "%lu", msg_id); 198 (void) kern_asl_msg_va(FSLOG_VAL_LEVEL, FSLOG_VAL_FACILITY, 199 num_pairs, ap, 200 FSLOG_KEY_READ_UID, FSLOG_VAL_READ_UID, 201 FSLOG_KEY_MSG_ID, msg_id_str, 202 FSLOG_KEY_MSG_ORDER, msg_order_ptr, NULL); 203 } 204 va_end(ap); 205 return msg_id; 206} 207 208/* Log information about runtime file system corruption detected by 209 * the file system. It takes the VFS mount structure as 210 * parameter which is used to access the mount point of the 211 * corrupt volume. If no mount structure or mount point string 212 * string exists, nothing is logged to ASL database. 213 * 214 * Currently prints following information - 215 * 1. Mount Point 216 */ 217void fslog_fs_corrupt(struct mount *mnt) 218{ 219 if (mnt != NULL) { 220 fslog_err(FSLOG_MSG_SINGLE, 221 FSLOG_KEY_ERR_TYPE, FSLOG_VAL_ERR_TYPE_FS, 222 FSLOG_KEY_MNTPT, mnt->mnt_vfsstat.f_mntonname, 223 NULL); 224 } 225 226 return; 227} 228 229/* Log information about IO error detected in buf_biodone() 230 * Currently prints following information - 231 * 1. Physical block number 232 * 2. Logical block number 233 * 3. Device node 234 * 4. Mount point 235 * 5. Path for file, if any 236 * 6. Error number 237 * 7. Type of IO (read/write) 238 */ 239void fslog_io_error(const buf_t bp) 240{ 241 int err; 242 unsigned long msg_id; 243 char blknum_str[21]; 244 char lblknum_str[21]; 245 char errno_str[6]; 246 const char *iotype; 247 unsigned char print_last = 0; 248 vnode_t vp; 249 250 if (buf_error(bp) == 0) { 251 return; 252 } 253 254 /* Convert error number to string */ 255 snprintf (errno_str, sizeof(errno_str), "%d", buf_error(bp)); 256 257 /* Determine type of IO operation */ 258 if (buf_flags(bp) & B_READ) { 259 iotype = FSLOG_VAL_IOTYPE_READ; 260 } else { 261 iotype = FSLOG_VAL_IOTYPE_WRITE; 262 } 263 264 /* Convert physical block number to string */ 265 snprintf (blknum_str, sizeof(blknum_str), "%lld", buf_blkno(bp)); 266 267 /* Convert logical block number to string */ 268 snprintf (lblknum_str, sizeof(lblknum_str), "%lld", buf_lblkno(bp)); 269 270 msg_id = fslog_err(FSLOG_MSG_FIRST, 271 FSLOG_KEY_ERR_TYPE, FSLOG_VAL_ERR_TYPE_IO, 272 FSLOG_KEY_ERRNO, errno_str, 273 FSLOG_KEY_IOTYPE, iotype, 274 FSLOG_KEY_PHYS_BLKNUM, blknum_str, 275 FSLOG_KEY_LOG_BLKNUM, lblknum_str, 276 NULL); 277 278 /* Access the vnode for this buffer */ 279 vp = buf_vnode(bp); 280 if (vp) { 281 struct vfsstatfs *sp; 282 mount_t mp; 283 char *path; 284 int len; 285 struct vfs_context context; 286 287 mp = vnode_mount(vp); 288 /* mp should be NULL only for bdevvp during boot */ 289 if (mp == NULL) { 290 goto out; 291 } 292 sp = vfs_statfs(mp); 293 294 /* Access the file path */ 295 MALLOC(path, char *, MAXPATHLEN, M_TEMP, M_WAITOK); 296 if (path) { 297 len = MAXPATHLEN; 298 context.vc_thread = current_thread(); 299 context.vc_ucred = kauth_cred_get(); 300 /* Find path without entering file system */ 301 err = build_path(vp, path, len, &len, BUILDPATH_NO_FS_ENTER, 302 &context); 303 if (!err) { 304 err = escape_str(path, len, MAXPATHLEN); 305 if (!err) { 306 /* Print device node, mount point, path */ 307 msg_id = fslog_err(msg_id | FSLOG_MSG_LAST, 308 FSLOG_KEY_DEVNODE, sp->f_mntfromname, 309 FSLOG_KEY_MNTPT, sp->f_mntonname, 310 FSLOG_KEY_PATH, path, 311 NULL); 312 print_last = 1; 313 } 314 } 315 FREE(path, M_TEMP); 316 } 317 318 if (print_last == 0) { 319 /* Print device node and mount point */ 320 msg_id = fslog_err(msg_id | FSLOG_MSG_LAST, 321 FSLOG_KEY_DEVNODE, sp->f_mntfromname, 322 FSLOG_KEY_MNTPT, sp->f_mntonname, 323 NULL); 324 print_last = 1; 325 } 326 } 327 328out: 329 if (print_last == 0) { 330 msg_id = fslog_err(msg_id | FSLOG_MSG_LAST, NULL); 331 } 332 333 return; 334} 335 336