1/* 2 * Copyright (c) 2007-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_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. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <asl_core.h> 25#include <asl_file.h> 26#include <fcntl.h> 27#include <stdlib.h> 28#include <stddef.h> 29#include <stdio.h> 30#include <unistd.h> 31#include <sys/errno.h> 32#include <string.h> 33#include <sys/stat.h> 34#include <sys/types.h> 35#include <sys/acl.h> 36#include <membership.h> 37#include <time.h> 38#include <sys/time.h> 39#include <asl_private.h> 40#include <asl_legacy1.h> 41#include <TargetConditionals.h> 42 43extern time_t asl_parse_time(const char *str); 44extern int asl_msg_cmp(aslmsg a, aslmsg b); 45 46#define forever for(;;) 47#define MILLION 1000000 48 49/* 50 * MSG and STR records have (at least) a type (uint16_t) and a length (uint32_t) 51 * type and level are both 16 bit fields so that alignment isn't a pain. 52 */ 53#define RECORD_COMMON_LEN 6 54#define RECORD_TYPE_LEN 2 55#define BUFFER_OFFSET_KVCOUNT 56 56 57#define SCRATCH_BUFFER_SIZE (MSG_RECORD_FIXED_LENGTH + (20 * sizeof(uint64_t))) 58 59typedef struct 60{ 61 uint64_t next; 62 uint64_t mid; 63 uint64_t time; 64 uint32_t nano; 65 uint16_t level; 66 uint16_t flags; 67 uint32_t pid; 68 uint32_t uid; 69 uint32_t gid; 70 uint32_t ruid; 71 uint32_t rgid; 72 uint32_t refpid; 73 uint32_t kvcount; 74 uint64_t host; 75 uint64_t sender; 76 uint64_t facility; 77 uint64_t message; 78 uint64_t refproc; 79 uint64_t session; 80 uint64_t prev; 81} file_record_t; 82 83typedef struct 84{ 85 asl_file_list_t *list; 86 int dir; 87} asl_file_match_token_t; 88 89static uint16_t 90_asl_get_16(char *h) 91{ 92 uint16_t x; 93 94 memcpy(&x, h, 2); 95 return ntohs(x); 96} 97 98static void 99_asl_put_16(uint16_t i, char *h) 100{ 101 uint16_t x; 102 103 x = htons(i); 104 memcpy(h, &x, 2); 105} 106 107static uint32_t 108_asl_get_32(char *h) 109{ 110 uint32_t x; 111 112 memcpy(&x, h, 4); 113 return ntohl(x); 114} 115 116static void 117_asl_put_32(uint32_t i, char *h) 118{ 119 uint32_t x; 120 121 x = htonl(i); 122 memcpy(h, &x, 4); 123} 124 125static uint64_t 126_asl_get_64(char *h) 127{ 128 uint64_t x; 129 130 memcpy(&x, h, 8); 131 return asl_core_ntohq(x); 132} 133 134static void 135_asl_put_64(uint64_t i, char *h) 136{ 137 uint64_t x; 138 139 x = asl_core_htonq(i); 140 memcpy(h, &x, 8); 141} 142 143static uint32_t 144asl_file_read_uint32(asl_file_t *s, off_t off, uint32_t *out) 145{ 146 uint32_t status, val; 147 148 if (s == NULL) return ASL_STATUS_INVALID_STORE; 149 if (s->store == NULL) return ASL_STATUS_INVALID_STORE; 150 if ((off + sizeof(uint32_t)) > s->file_size) return ASL_STATUS_READ_FAILED; 151 152 status = fseeko(s->store, off, SEEK_SET); 153 if (status != 0) return ASL_STATUS_READ_FAILED; 154 155 val = 0; 156 157 status = fread(&val, sizeof(uint32_t), 1, s->store); 158 if (status != 1) return ASL_STATUS_READ_FAILED; 159 160 if (out != NULL) *out = ntohl(val); 161 return ASL_STATUS_OK; 162} 163 164static uint32_t 165asl_file_read_uint64(asl_file_t *s, off_t off, uint64_t *out) 166{ 167 uint32_t status; 168 uint64_t val; 169 170 if (s == NULL) return ASL_STATUS_INVALID_STORE; 171 if (s->store == NULL) return ASL_STATUS_INVALID_STORE; 172 if ((off + sizeof(uint64_t)) > s->file_size) return ASL_STATUS_READ_FAILED; 173 174 status = fseeko(s->store, off, SEEK_SET); 175 if (status != 0) return ASL_STATUS_READ_FAILED; 176 177 val = 0; 178 179 status = fread(&val, sizeof(uint64_t), 1, s->store); 180 if (status != 1) return ASL_STATUS_READ_FAILED; 181 182 if (out != NULL) *out = asl_core_ntohq(val); 183 return ASL_STATUS_OK; 184} 185 186uint32_t 187asl_file_close(asl_file_t *s) 188{ 189 file_string_t *x; 190 191 if (s == NULL) return ASL_STATUS_OK; 192 193 if (s->version == 1) 194 { 195 return asl_legacy1_close((asl_legacy1_t *)s->legacy); 196 } 197 198 while (s->string_list != NULL) 199 { 200 x = s->string_list->next; 201 free(s->string_list); 202 s->string_list = x; 203 } 204 205 if (s->store != NULL) fclose(s->store); 206 if (s->scratch != NULL) free(s->scratch); 207 208 memset(s, 0, sizeof(asl_file_t)); 209 free(s); 210 211 return ASL_STATUS_OK; 212} 213 214__private_extern__ uint32_t 215asl_file_open_write_fd(int fd, asl_file_t **s) 216{ 217 time_t now; 218 int status; 219 char buf[DB_HEADER_LEN]; 220 asl_file_t *out; 221 222 if (fd < 0) return ASL_STATUS_FAILED; 223 if (s == NULL) return ASL_STATUS_FAILED; 224 225 out = (asl_file_t *)calloc(1, sizeof(asl_file_t)); 226 if (out == NULL) return ASL_STATUS_NO_MEMORY; 227 228 out->store = fdopen(fd, "w+"); 229 if (out->store == NULL) 230 { 231 free(out); 232 return ASL_STATUS_FAILED; 233 } 234 235 memset(buf, 0, sizeof(buf)); 236 memcpy(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN); 237 238 _asl_put_32(DB_VERSION, buf + DB_HEADER_VERS_OFFSET); 239 240 now = time(NULL); 241 out->dob = now; 242 _asl_put_64(out->dob, buf + DB_HEADER_TIME_OFFSET); 243 244 _asl_put_32(CACHE_SIZE, buf + DB_HEADER_CSIZE_OFFSET); 245 246 status = fwrite(buf, sizeof(buf), 1, out->store); 247 if (status != 1) 248 { 249 fclose(out->store); 250 free(out); 251 return ASL_STATUS_FAILED; 252 } 253 254 /* flush data */ 255 fflush(out->store); 256 257 out->file_size = sizeof(buf); 258 259 /* scratch buffer for file writes (we test for NULL before using it) */ 260 out->scratch = malloc(SCRATCH_BUFFER_SIZE); 261 262 *s = out; 263 264 return ASL_STATUS_OK; 265} 266 267__private_extern__ int 268asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode) 269{ 270#if TARGET_OS_IPHONE 271 return open(path, O_RDWR | O_CREAT | O_EXCL, mode); 272#else 273 acl_t acl; 274 uuid_t uuid; 275 acl_entry_t entry; 276 acl_permset_t perms; 277 int status; 278 int fd = -1; 279 280 /* -1 means don't set ACL for uid or gid */ 281 if ((uid == -1) && (gid == -1)) 282 { 283 return open(path, O_RDWR | O_CREAT | O_EXCL, mode); 284 } 285 286 acl = acl_init(1); 287 288 if ((gid != 0) && (gid != -1)) 289 { 290 status = mbr_gid_to_uuid(gid, uuid); 291 if (status != 0) goto asl_file_create_return; 292 293 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); 294 if (status != 0) goto asl_file_create_return; 295 296 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); 297 if (status != 0) goto asl_file_create_return; 298 299 status = acl_set_qualifier(entry, &uuid); 300 if (status != 0) goto asl_file_create_return; 301 302 status = acl_get_permset(entry, &perms); 303 if (status != 0) goto asl_file_create_return; 304 305 status = acl_add_perm(perms, ACL_READ_DATA); 306 if (status != 0) goto asl_file_create_return; 307 } 308 309 if ((uid != 0) && (uid != -1)) 310 { 311 status = mbr_uid_to_uuid(uid, uuid); 312 if (status != 0) goto asl_file_create_return; 313 314 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); 315 if (status != 0) goto asl_file_create_return; 316 317 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); 318 if (status != 0) goto asl_file_create_return; 319 320 status = acl_set_qualifier(entry, &uuid); 321 if (status != 0) goto asl_file_create_return; 322 323 status = acl_get_permset(entry, &perms); 324 if (status != 0) goto asl_file_create_return; 325 326 status = acl_add_perm(perms, ACL_READ_DATA); 327 if (status != 0) goto asl_file_create_return; 328 } 329 330 fd = open(path, O_RDWR | O_CREAT | O_EXCL, mode); 331 if (fd < 0) goto asl_file_create_return; 332 333 status = acl_set_fd(fd, acl); 334 if (status != 0) 335 { 336 close(fd); 337 fd = -1; 338 unlink(path); 339 } 340 341asl_file_create_return: 342 343 acl_free(acl); 344 return fd; 345#endif 346} 347 348uint32_t 349asl_file_open_write(const char *path, mode_t mode, uid_t uid, gid_t gid, asl_file_t **s) 350{ 351 int i, status, fd; 352 struct stat sb; 353 char buf[DB_HEADER_LEN]; 354 asl_file_t *out; 355 uint32_t aslstatus, vers, last_len; 356 off_t off; 357 358 memset(&sb, 0, sizeof(struct stat)); 359 360 status = stat(path, &sb); 361 if (status == 0) 362 { 363 /* must be a plain file */ 364 if (!S_ISREG(sb.st_mode)) return ASL_STATUS_INVALID_STORE; 365 366 if (sb.st_size == 0) 367 { 368 fd = open(path, O_RDWR | O_EXCL, mode); 369 if (fd < 0) return ASL_STATUS_FAILED; 370 371 return asl_file_open_write_fd(fd, s); 372 } 373 else 374 { 375 /* XXX Check that mode, uid, and gid are correct */ 376 out = (asl_file_t *)calloc(1, sizeof(asl_file_t)); 377 if (out == NULL) return ASL_STATUS_NO_MEMORY; 378 379 out->store = fopen(path, "r+"); 380 if (out->store == NULL) 381 { 382 free(out); 383 return ASL_STATUS_FAILED; 384 } 385 386 i = fread(buf, DB_HEADER_LEN, 1, out->store); 387 if (i < 1) 388 { 389 asl_file_close(out); 390 return ASL_STATUS_READ_FAILED; 391 } 392 393 /* check cookie */ 394 if (strncmp(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN)) 395 { 396 asl_file_close(out); 397 return ASL_STATUS_INVALID_STORE; 398 } 399 400 /* check version */ 401 vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET); 402 if (vers != DB_VERSION) 403 { 404 asl_file_close(out); 405 return ASL_STATUS_INVALID_STORE; 406 } 407 408 out->dob = _asl_get_64(buf + DB_HEADER_TIME_OFFSET); 409 out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET); 410 out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET); 411 out->file_size = (size_t)sb.st_size; 412 413 /* 414 * Detect bogus last pointer and check for odd-sized files. 415 * Setting out->last to zero forces asl_file_read_set_position to 416 * follow the linked list of records in the file to the last record. 417 * It's slower, but it's better at preventing crashes in corrupt files. 418 */ 419 420 /* records are at least MSG_RECORD_FIXED_LENGTH bytes */ 421 if ((out->last + MSG_RECORD_FIXED_LENGTH) > out->file_size) 422 { 423 out->last = 0; 424 } 425 else 426 { 427 /* read last record length and make sure the file is at least that large */ 428 off = out->last + RECORD_TYPE_LEN; 429 status = asl_file_read_uint32(out, off, &last_len); 430 if (status != ASL_STATUS_OK) 431 { 432 asl_file_close(out); 433 return status; 434 } 435 436 if ((out->last + last_len) > out->file_size) out->last = 0; 437 } 438 439 aslstatus = asl_file_read_set_position(out, ASL_FILE_POSITION_LAST); 440 if (aslstatus != ASL_STATUS_OK) 441 { 442 asl_file_close(out); 443 return aslstatus; 444 } 445 446 out->prev = out->cursor; 447 status = fseeko(out->store, 0, SEEK_END); 448 if (status != 0) 449 { 450 asl_file_close(out); 451 return ASL_STATUS_READ_FAILED; 452 } 453 454 out->file_size = (size_t)ftello(out->store); 455 456 /* scratch buffer for file writes (we test for NULL before using it) */ 457 out->scratch = malloc(SCRATCH_BUFFER_SIZE); 458 459 *s = out; 460 461 return ASL_STATUS_OK; 462 } 463 } 464 else if (errno != ENOENT) 465 { 466 /* unexpected status */ 467 return ASL_STATUS_FAILED; 468 } 469 470 /* the file does not exist */ 471 fd = asl_file_create(path, uid, gid, mode); 472 if (fd < 0) return ASL_STATUS_FAILED; 473 474 aslstatus = asl_file_open_write_fd(fd, s); 475 if (aslstatus != ASL_STATUS_OK) unlink(path); 476 477 return aslstatus; 478} 479 480uint32_t 481asl_file_compact(asl_file_t *s, const char *path, mode_t mode, uid_t uid, gid_t gid) 482{ 483 asl_file_t *new; 484 struct stat sb; 485 aslmsg m; 486 uint64_t xid; 487 uint32_t status; 488 489 if (s == NULL) return ASL_STATUS_INVALID_STORE; 490 if (path == NULL) return ASL_STATUS_INVALID_ARG; 491 492 if (s->version == 1) return ASL_STATUS_FAILED; 493 494 memset(&sb, 0, sizeof(struct stat)); 495 496 if (stat(path, &sb) == 0) return ASL_STATUS_FAILED; 497 if (errno != ENOENT) return ASL_STATUS_FAILED; 498 499 status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); 500 if (status != ASL_STATUS_OK) return status; 501 502 new = NULL; 503 status = asl_file_open_write(path, mode, uid, gid, &new); 504 if (status != ASL_STATUS_OK) return status; 505 new->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID; 506 507 while ((status == ASL_STATUS_OK) && (s->cursor != 0)) 508 { 509 m = NULL; 510 status = asl_file_fetch_next(s, &m); 511 if (status != ASL_STATUS_OK) break; 512 513 xid = 0; 514 status = asl_file_save(new, m, &xid); 515 asl_free(m); 516 } 517 518 asl_file_close(new); 519 return status; 520} 521 522static uint32_t 523asl_file_string_encode(asl_file_t *s, const char *str, uint64_t *out) 524{ 525 uint32_t i, hash, len, x32; 526 file_string_t *sp, *sx, *sl; 527 uint64_t x64; 528 uint8_t inls; 529 uint16_t type; 530 off_t off; 531 char *p; 532 533 if (s == NULL) return ASL_STATUS_INVALID_STORE; 534 if (str == NULL) return ASL_STATUS_INVALID_ARG; 535 536 len = strlen(str); 537 538 /* inline strings */ 539 if (len < 8) 540 { 541 /* inline string */ 542 inls = len; 543 inls |= 0x80; 544 545 x64 = 0; 546 p = (char *)&x64; 547 memcpy(p, &inls, 1); 548 memcpy(p + 1, str, len); 549 *out = asl_core_ntohq(x64); 550 return ASL_STATUS_OK; 551 } 552 553 /* check the cache */ 554 hash = asl_core_string_hash(str, len); 555 556 sp = NULL; 557 for (sx = s->string_list; sx != NULL; sx = sx->next) 558 { 559 if ((hash == sx->hash) && (!strcmp(str, sx->str))) 560 { 561 /* Move this string to the head of the list */ 562 if (sp != NULL) 563 { 564 sl = s->string_list; 565 sp->next = sx->next; 566 sx->next = sl; 567 s->string_list = sx; 568 } 569 570 *out = sx->where; 571 return ASL_STATUS_OK; 572 } 573 574 sp = sx; 575 } 576 577 off = ftello(s->store); 578 579 /* Type */ 580 type = htons(ASL_FILE_TYPE_STR); 581 i = fwrite(&type, sizeof(uint16_t), 1, s->store); 582 if (i != 1) return ASL_STATUS_WRITE_FAILED; 583 584 /* Length (includes trailing nul) */ 585 x32 = htonl(len + 1); 586 i = fwrite(&x32, sizeof(uint32_t), 1, s->store); 587 if (i != 1) return ASL_STATUS_WRITE_FAILED; 588 589 /* String data (nul terminated) */ 590 i = fwrite(str, len + 1, 1, s->store); 591 if (i != 1) return ASL_STATUS_WRITE_FAILED; 592 593 /* flush data */ 594 fflush(s->store); 595 596 /* create file_string_t and insert into the cache */ 597 sx = (file_string_t *)calloc(1, offsetof(file_string_t, str) + len + 1); 598 if (sx == NULL) return ASL_STATUS_NO_MEMORY; 599 600 sx->where = off; 601 sx->hash = hash; 602 sx->next = s->string_list; 603 memcpy(sx->str, str, len); 604 605 s->string_list = sx; 606 607 if (((s->flags & ASL_FILE_FLAG_UNLIMITED_CACHE) == 0) && (s->string_count == CACHE_SIZE)) 608 { 609 /* drop last (lru) string from cache */ 610 sp = s->string_list; 611 sx = sp->next; 612 613 /* NB CACHE_SIZE must be > 1 */ 614 while (sx->next != NULL) 615 { 616 sp = sx; 617 sx = sx->next; 618 } 619 620 sp->next = NULL; 621 free(sx); 622 } 623 else 624 { 625 s->string_count++; 626 } 627 628 *out = off; 629 return ASL_STATUS_OK; 630} 631 632/* 633 * Encode an aslmsg as a record structure. 634 * Creates and caches strings. 635 */ 636uint32_t 637asl_file_save(asl_file_t *s, aslmsg in, uint64_t *mid) 638{ 639 char *buf, *p; 640 uint32_t i, len, x, status; 641 file_record_t r; 642 uint64_t k, v; 643 uint64_t *kvlist; 644 off_t off; 645 asl_msg_t *msg; 646 const char *key, *val; 647 648 if (s == NULL) return ASL_STATUS_INVALID_STORE; 649 if (in == NULL) return ASL_STATUS_INVALID_MESSAGE; 650 651 if (s->flags & ASL_FILE_FLAG_READ_ONLY) return ASL_STATUS_READ_ONLY; 652 653 msg = (asl_msg_t *)in; 654 655 memset(&r, 0, sizeof(file_record_t)); 656 657 r.flags = 0; 658 r.level = ASL_LEVEL_DEBUG; 659 r.pid = -1; 660 r.uid = -2; 661 r.gid = -2; 662 r.ruid = -1; 663 r.rgid = -1; 664 r.time = 0; 665 r.nano = 0; 666 r.prev = s->prev; 667 kvlist = NULL; 668 669 key = NULL; 670 val = NULL; 671 672 for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL)) 673 { 674 if (key == NULL) 675 { 676 continue; 677 } 678 else if (!strcmp(key, ASL_KEY_TIME)) 679 { 680 if (val != NULL) r.time = asl_parse_time(val); 681 } 682 else if (!strcmp(key, ASL_KEY_TIME_NSEC)) 683 { 684 if (val != NULL) r.nano = atoi(val); 685 } 686 else if (!strcmp(key, ASL_KEY_HOST)) 687 { 688 if (val != NULL) 689 { 690 status = asl_file_string_encode(s, val, &(r.host)); 691 if (status != ASL_STATUS_OK) 692 { 693 if (kvlist != NULL) free(kvlist); 694 return status; 695 } 696 } 697 } 698 else if (!strcmp(key, ASL_KEY_SENDER)) 699 { 700 if (val != NULL) 701 { 702 status = asl_file_string_encode(s, val, &(r.sender)); 703 if (status != ASL_STATUS_OK) 704 { 705 if (kvlist != NULL) free(kvlist); 706 return status; 707 } 708 } 709 } 710 else if (!strcmp(key, ASL_KEY_PID)) 711 { 712 if (val != NULL) r.pid = atoi(val); 713 } 714 else if (!strcmp(key, ASL_KEY_REF_PID)) 715 { 716 if (val != NULL) r.refpid = atoi(val); 717 } 718 else if (!strcmp(key, ASL_KEY_UID)) 719 { 720 if (val != NULL) r.uid = atoi(val); 721 } 722 else if (!strcmp(key, ASL_KEY_GID)) 723 { 724 if (val != NULL) r.gid = atoi(val); 725 } 726 else if (!strcmp(key, ASL_KEY_LEVEL)) 727 { 728 if (val != NULL) r.level = atoi(val); 729 } 730 else if (!strcmp(key, ASL_KEY_MSG)) 731 { 732 if (val != NULL) 733 { 734 status = asl_file_string_encode(s, val, &(r.message)); 735 if (status != ASL_STATUS_OK) 736 { 737 if (kvlist != NULL) free(kvlist); 738 return status; 739 } 740 } 741 } 742 else if (!strcmp(key, ASL_KEY_FACILITY)) 743 { 744 if (val != NULL) 745 { 746 status = asl_file_string_encode(s, val, &(r.facility)); 747 if (status != ASL_STATUS_OK) 748 { 749 if (kvlist != NULL) free(kvlist); 750 return status; 751 } 752 } 753 } 754 else if (!strcmp(key, ASL_KEY_REF_PROC)) 755 { 756 if (val != NULL) 757 { 758 status = asl_file_string_encode(s, val, &(r.refproc)); 759 if (status != ASL_STATUS_OK) 760 { 761 if (kvlist != NULL) free(kvlist); 762 return status; 763 } 764 } 765 } 766 else if (!strcmp(key, ASL_KEY_SESSION)) 767 { 768 if (val != NULL) 769 { 770 status = asl_file_string_encode(s, val, &(r.session)); 771 if (status != ASL_STATUS_OK) 772 { 773 if (kvlist != NULL) free(kvlist); 774 return status; 775 } 776 } 777 } 778 else if (!strcmp(key, ASL_KEY_READ_UID)) 779 { 780 if (((r.flags & ASL_MSG_FLAG_READ_UID_SET) == 0) && (val != NULL)) 781 { 782 r.ruid = atoi(val); 783 r.flags |= ASL_MSG_FLAG_READ_UID_SET; 784 } 785 } 786 else if (!strcmp(key, ASL_KEY_READ_GID)) 787 { 788 if (((r.flags & ASL_MSG_FLAG_READ_GID_SET) == 0) && (val != NULL)) 789 { 790 r.rgid = atoi(val); 791 r.flags |= ASL_MSG_FLAG_READ_GID_SET; 792 } 793 } 794 else if (!strcmp(key, ASL_KEY_MSG_ID)) 795 { 796 if (s->flags & ASL_FILE_FLAG_PRESERVE_MSG_ID) *mid = atoll(val); 797 } 798 else if (!strcmp(key, ASL_KEY_OPTION)) 799 { 800 /* ignore - we don't save ASLOption */ 801 } 802 else 803 { 804 status = asl_file_string_encode(s, key, &k); 805 if (status != ASL_STATUS_OK) 806 { 807 if (kvlist != NULL) free(kvlist); 808 return status; 809 } 810 811 v = 0; 812 if (val != NULL) 813 { 814 status = asl_file_string_encode(s, val, &v); 815 if (status != ASL_STATUS_OK) 816 { 817 if (kvlist != NULL) free(kvlist); 818 return status; 819 } 820 } 821 822 if (r.kvcount == 0) 823 { 824 kvlist = (uint64_t *)calloc(2, sizeof(uint64_t)); 825 } 826 else 827 { 828 kvlist = (uint64_t *)reallocf(kvlist, (r.kvcount + 2) * sizeof(uint64_t)); 829 } 830 831 if (kvlist == NULL) 832 { 833 return ASL_STATUS_NO_MEMORY; 834 } 835 836 kvlist[r.kvcount++] = k; 837 kvlist[r.kvcount++] = v; 838 } 839 } 840 841 len = MSG_RECORD_FIXED_LENGTH + (r.kvcount * sizeof(uint64_t)); 842 buf = NULL; 843 844 /* use the scratch buffer if it exists and is large enough */ 845 if ((s->scratch != NULL) && (len <= SCRATCH_BUFFER_SIZE)) 846 { 847 memset(s->scratch, 0, SCRATCH_BUFFER_SIZE); 848 buf = s->scratch; 849 } 850 else 851 { 852 buf = calloc(1, len); 853 } 854 855 if (buf == NULL) return ASL_STATUS_NO_MEMORY; 856 857 if (*mid != 0) 858 { 859 r.mid = *mid; 860 } 861 else 862 { 863 r.mid = asl_core_new_msg_id(0); 864 *mid = r.mid; 865 } 866 867 p = buf; 868 869 /* Type */ 870 _asl_put_16(ASL_FILE_TYPE_MSG, p); 871 p += sizeof(uint16_t); 872 873 /* Length of message (excludes type and length fields) */ 874 _asl_put_32(len - RECORD_COMMON_LEN, p); 875 p += sizeof(uint32_t); 876 877 /* Message data... */ 878 879 _asl_put_64(r.next, p); 880 p += sizeof(uint64_t); 881 882 _asl_put_64(r.mid, p); 883 p += sizeof(uint64_t); 884 885 _asl_put_64(r.time, p); 886 p += sizeof(uint64_t); 887 888 _asl_put_32(r.nano, p); 889 p += sizeof(uint32_t); 890 891 _asl_put_16(r.level, p); 892 p += sizeof(uint16_t); 893 894 _asl_put_16(r.flags, p); 895 p += sizeof(uint16_t); 896 897 _asl_put_32(r.pid, p); 898 p += sizeof(uint32_t); 899 900 _asl_put_32(r.uid, p); 901 p += sizeof(uint32_t); 902 903 _asl_put_32(r.gid, p); 904 p += sizeof(uint32_t); 905 906 _asl_put_32(r.ruid, p); 907 p += sizeof(uint32_t); 908 909 _asl_put_32(r.rgid, p); 910 p += sizeof(uint32_t); 911 912 _asl_put_32(r.refpid, p); 913 p += sizeof(uint32_t); 914 915 _asl_put_32(r.kvcount, p); 916 p += sizeof(uint32_t); 917 918 _asl_put_64(r.host, p); 919 p += sizeof(uint64_t); 920 921 _asl_put_64(r.sender, p); 922 p += sizeof(uint64_t); 923 924 _asl_put_64(r.facility, p); 925 p += sizeof(uint64_t); 926 927 _asl_put_64(r.message, p); 928 p += sizeof(uint64_t); 929 930 _asl_put_64(r.refproc, p); 931 p += sizeof(uint64_t); 932 933 _asl_put_64(r.session, p); 934 p += sizeof(uint64_t); 935 936 for (i = 0; i < r.kvcount; i++) 937 { 938 _asl_put_64(kvlist[i], p); 939 p += sizeof(uint64_t); 940 } 941 942 _asl_put_64(r.prev, p); 943 p += sizeof(uint64_t); 944 945 free(kvlist); 946 kvlist = NULL; 947 948 /* write record at end of file */ 949 status = fseeko(s->store, 0, SEEK_END); 950 if (status != 0) return ASL_STATUS_WRITE_FAILED; 951 952 s->last = (uint64_t)ftello(s->store); 953 954 v = asl_core_htonq(s->last); 955 956 status = fwrite(buf, len, 1, s->store); 957 fflush(s->store); 958 959 /* free the buffer if it was allocated here */ 960 if (buf != s->scratch) free(buf); 961 962 /* seek to "next" field of previous record, write last offset */ 963 off = s->prev + RECORD_COMMON_LEN; 964 if (s->prev == 0) off = DB_HEADER_FIRST_OFFSET; 965 966 status = fseeko(s->store, off, SEEK_SET); 967 if (status != 0) return ASL_STATUS_WRITE_FAILED; 968 969 status = fwrite(&v, sizeof(uint64_t), 1, s->store); 970 if (status != 1) return ASL_STATUS_WRITE_FAILED; 971 972 /* seek to DB_HEADER_LAST_OFFSET, write last record offset */ 973 off = DB_HEADER_LAST_OFFSET; 974 975 status = fseeko(s->store, off, SEEK_SET); 976 if (status != 0) return ASL_STATUS_WRITE_FAILED; 977 978 status = fwrite(&v, sizeof(uint64_t), 1, s->store); 979 if (status != 1) return ASL_STATUS_WRITE_FAILED; 980 981 /* return to the end of the store (this is expected by other routines) */ 982 status = fseeko(s->store, 0, SEEK_END); 983 if (status != 0) return ASL_STATUS_WRITE_FAILED; 984 985 /* flush data */ 986 fflush(s->store); 987 988 s->file_size = (size_t)ftello(s->store); 989 990 s->prev = s->last; 991 992 return ASL_STATUS_OK; 993} 994 995static uint32_t 996asl_file_fetch_object(asl_file_t *s, uint64_t where, char **out, uint32_t *outlen) 997{ 998 char ils[9]; 999 char *p; 1000 uint32_t len; 1001 int status; 1002 uint64_t x64; 1003 uint8_t inls; 1004 uint16_t type; 1005 off_t off; 1006 1007 *out = NULL; 1008 *outlen = 0; 1009 1010 if (s == NULL) return ASL_STATUS_INVALID_STORE; 1011 if (out == NULL) return ASL_STATUS_INVALID_ARG; 1012 if (where == 0) return ASL_STATUS_INVALID_ARG; 1013 1014 inls = 0; 1015 x64 = asl_core_htonq(where); 1016 memcpy(&inls, &x64, 1); 1017 if (inls & 0x80) 1018 { 1019 /* inline string */ 1020 inls &= 0x0f; 1021 if (inls > 7) return ASL_STATUS_INVALID_STORE; 1022 1023 p = 1 + (char *)&x64; 1024 memset(ils, 0, sizeof(ils)); 1025 memcpy(ils, p, inls); 1026 *out = strdup(ils); 1027 if (*out == NULL) return ASL_STATUS_NO_MEMORY; 1028 1029 *outlen = inls; 1030 return ASL_STATUS_OK; 1031 } 1032 1033 off = where; 1034 if ((off + sizeof(uint16_t) + sizeof(uint32_t)) > s->file_size) return ASL_STATUS_READ_FAILED; 1035 1036 status = fseeko(s->store, off, SEEK_SET); 1037 if (status != 0) return ASL_STATUS_READ_FAILED; 1038 1039 /* Type */ 1040 status = fread(&type, sizeof(uint16_t), 1, s->store); 1041 if (status != 1) return ASL_STATUS_READ_FAILED; 1042 off += sizeof(uint16_t); 1043 1044 /* Length */ 1045 len = 0; 1046 status = fread(&len, sizeof(uint32_t), 1, s->store); 1047 if (status != 1) return ASL_STATUS_READ_FAILED; 1048 off += sizeof(uint32_t); 1049 1050 len = ntohl(len); 1051 if ((off + len) > s->file_size) return ASL_STATUS_READ_FAILED; 1052 1053 *out = calloc(1, len); 1054 if (*out == NULL) return ASL_STATUS_NO_MEMORY; 1055 1056 status = fread(*out, len, 1, s->store); 1057 if (status != 1) 1058 { 1059 free(*out); 1060 return ASL_STATUS_READ_FAILED; 1061 } 1062 1063 *outlen = len; 1064 return ASL_STATUS_OK; 1065} 1066 1067static uint16_t 1068asl_file_fetch_helper_16(asl_file_t *s, char **p, aslmsg m, const char *key) 1069{ 1070 uint16_t out; 1071 char str[256]; 1072 1073 out = _asl_get_16(*p); 1074 *p += sizeof(uint16_t); 1075 1076 if ((m == NULL) || (key == NULL)) return out; 1077 1078 snprintf(str, sizeof(str), "%hu", out); 1079 asl_set(m, key, str); 1080 1081 return out; 1082} 1083 1084static uint32_t 1085asl_file_fetch_helper_32(asl_file_t *s, char **p, aslmsg m, const char *key, int ignore, uint32_t ignoreval) 1086{ 1087 uint32_t out, doit; 1088 char str[256]; 1089 1090 out = _asl_get_32(*p); 1091 *p += sizeof(uint32_t); 1092 1093 if ((m == NULL) || (key == NULL)) return out; 1094 1095 doit = 1; 1096 if ((ignore != 0) && (out == ignoreval)) doit = 0; 1097 if (doit != 0) 1098 { 1099 snprintf(str, sizeof(str), "%u", out); 1100 asl_set(m, key, str); 1101 } 1102 1103 return out; 1104} 1105 1106static uint64_t 1107asl_file_fetch_helper_64(asl_file_t *s, char **p, aslmsg m, const char *key) 1108{ 1109 uint64_t out; 1110 char str[256]; 1111 1112 out = _asl_get_64(*p); 1113 *p += sizeof(uint64_t); 1114 1115 if ((m == NULL) || (key == NULL)) return out; 1116 1117 snprintf(str, sizeof(str), "%llu", out); 1118 asl_set(m, key, str); 1119 1120 return out; 1121} 1122 1123static uint64_t 1124asl_file_fetch_helper_str(asl_file_t *s, char **p, aslmsg m, const char *key, uint32_t *err) 1125{ 1126 uint64_t out; 1127 char *val; 1128 uint32_t status, len; 1129 1130 out = _asl_get_64(*p); 1131 *p += sizeof(uint64_t); 1132 1133 val = NULL; 1134 len = 0; 1135 status = ASL_STATUS_OK; 1136 if (out != 0) status = asl_file_fetch_object(s, out, &val, &len); 1137 1138 if (err != NULL) *err = status; 1139 if ((status == ASL_STATUS_OK) && (val != NULL)) 1140 { 1141 asl_set(m, key, val); 1142 free(val); 1143 } 1144 1145 return out; 1146} 1147 1148static uint32_t 1149asl_file_fetch_pos(asl_file_t *s, uint64_t where, int dir, aslmsg *msg) 1150{ 1151 char *buf, *p, *k, *v; 1152 file_record_t r; 1153 uint32_t i, status, len, buflen, kvn; 1154 uint64_t x64, kv; 1155 aslmsg out; 1156 off_t off; 1157 1158 if (s == NULL) return ASL_STATUS_INVALID_STORE; 1159 if (msg == NULL) return ASL_STATUS_INVALID_ARG; 1160 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; 1161 1162 buf = NULL; 1163 buflen = 0; 1164 status = asl_file_fetch_object(s, where, &buf, &buflen); 1165 if ((status != ASL_STATUS_OK) || (buf == NULL)) 1166 { 1167 s->cursor = 0; 1168 s->cursor_xid = 0; 1169 return status; 1170 } 1171 1172 /* check buffer size */ 1173 kvn = _asl_get_32(buf + BUFFER_OFFSET_KVCOUNT); 1174 if (buflen < (MSG_RECORD_FIXED_LENGTH - RECORD_COMMON_LEN + (kvn * sizeof(uint64_t)))) 1175 { 1176 free(buf); 1177 s->cursor = 0; 1178 s->cursor_xid = 0; 1179 return ASL_STATUS_READ_FAILED; 1180 } 1181 1182 out = asl_new(ASL_TYPE_MSG); 1183 if (out == NULL) return ASL_STATUS_NO_MEMORY; 1184 1185 memset(&r, 0, sizeof(file_record_t)); 1186 p = buf; 1187 1188 r.next = asl_file_fetch_helper_64(s, &p, NULL, NULL); 1189 r.mid = asl_file_fetch_helper_64(s, &p, out, ASL_KEY_MSG_ID); 1190 r.time = asl_file_fetch_helper_64(s, &p, out, ASL_KEY_TIME); 1191 r.nano = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_TIME_NSEC, 0, 0); 1192 r.level = asl_file_fetch_helper_16(s, &p, out, ASL_KEY_LEVEL); 1193 r.flags = asl_file_fetch_helper_16(s, &p, NULL, NULL); 1194 r.pid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_PID, 0, 0); 1195 r.uid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_UID, 1, (uint32_t)-1); 1196 r.gid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_GID, 1, (uint32_t)-1); 1197 r.ruid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_READ_UID, 1, (uint32_t)-1); 1198 r.rgid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_READ_GID, 1, (uint32_t)-1); 1199 r.refpid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_REF_PID, 1, 0); 1200 r.kvcount = asl_file_fetch_helper_32(s, &p, NULL, NULL, 0, 0); 1201 1202 status = ASL_STATUS_OK; 1203 r.host = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_HOST, &status); /* 68 */ 1204 if (status == ASL_STATUS_OK) r.sender = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_SENDER, &status); /* 76 */ 1205 if (status == ASL_STATUS_OK) r.facility = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_FACILITY, &status); /* 84 */ 1206 if (status == ASL_STATUS_OK) r.message = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_MSG, &status); /* 92 */ 1207 if (status == ASL_STATUS_OK) r.refproc = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_REF_PROC, &status); /* 100 */ 1208 if (status == ASL_STATUS_OK) r.session = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_SESSION, &status); /* 108 */ 1209 1210 if (status != ASL_STATUS_OK) 1211 { 1212 asl_free(out); 1213 free(buf); 1214 s->cursor = 0; 1215 s->cursor_xid = 0; 1216 return status; 1217 } 1218 1219 kvn = r.kvcount / 2; 1220 1221 for (i = 0; i < kvn; i++) 1222 { 1223 kv = _asl_get_64(p); 1224 p += sizeof(uint64_t); 1225 k = NULL; 1226 len = 0; 1227 status = asl_file_fetch_object(s, kv, &k, &len); 1228 if (status != ASL_STATUS_OK) 1229 { 1230 asl_free(out); 1231 free(buf); 1232 s->cursor = 0; 1233 s->cursor_xid = 0; 1234 return status; 1235 } 1236 1237 kv = _asl_get_64(p); 1238 p += sizeof(uint64_t); 1239 v = NULL; 1240 len = 0; 1241 1242 if (kv != 0) 1243 { 1244 status = asl_file_fetch_object(s, kv, &v, &len); 1245 if (status != ASL_STATUS_OK) 1246 { 1247 asl_free(out); 1248 free(buf); 1249 s->cursor = 0; 1250 s->cursor_xid = 0; 1251 return status; 1252 } 1253 } 1254 1255 if ((status == ASL_STATUS_OK) && (k != NULL)) 1256 { 1257 asl_set(out, k, v); 1258 if (v != NULL) free(v); 1259 free(k); 1260 } 1261 } 1262 1263 r.prev = asl_file_fetch_helper_64(s, &p, NULL, NULL); /* 116 */ 1264 1265 free(buf); 1266 1267 if (dir >= 0) 1268 { 1269 if ((r.next != 0) && (r.next <= s->cursor)) 1270 { 1271 /* 1272 * Next offset goes backwards or loops. 1273 * The database is corrupt, but we allow this call to fail 1274 * quietly so that the current record fetch succeeds. 1275 */ 1276 s->cursor = 0; 1277 s->cursor_xid = 0; 1278 return ASL_STATUS_OK; 1279 } 1280 1281 s->cursor = r.next; 1282 } 1283 else 1284 { 1285 if ((r.prev != 0) && (r.prev >= s->cursor)) 1286 { 1287 /* 1288 * Prev offset goes forward or loops. 1289 * The database is corrupt, but we allow this call to fail 1290 * quietly so that the current record fetch succeeds. 1291 */ 1292 s->cursor = 0; 1293 s->cursor_xid = 0; 1294 return ASL_STATUS_OK; 1295 } 1296 1297 s->cursor = r.prev; 1298 } 1299 1300 s->cursor_xid = 0; 1301 1302 if (s->cursor != 0) 1303 { 1304 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); 1305 if (off > s->file_size) 1306 { 1307 s->cursor = 0; 1308 s->cursor_xid = 0; 1309 /* 1310 * Next record offset is past the end of the file. 1311 * This is an error, but we allow it to fail quietly 1312 * so that the current record fetch succeeds. 1313 */ 1314 *msg = out; 1315 return ASL_STATUS_OK; 1316 } 1317 1318 status = fseeko(s->store, off, SEEK_SET); 1319 if (status != 0) 1320 { 1321 asl_free(out); 1322 s->cursor = 0; 1323 s->cursor_xid = 0; 1324 return ASL_STATUS_READ_FAILED; 1325 } 1326 1327 status = fread(&x64, sizeof(uint64_t), 1, s->store); 1328 if (status != 1) 1329 { 1330 asl_free(out); 1331 s->cursor = 0; 1332 s->cursor_xid = 0; 1333 return ASL_STATUS_READ_FAILED; 1334 } 1335 1336 s->cursor_xid = asl_core_ntohq(x64); 1337 } 1338 1339 *msg = out; 1340 return ASL_STATUS_OK; 1341} 1342 1343uint32_t 1344asl_file_open_read(const char *path, asl_file_t **s) 1345{ 1346 asl_file_t *out; 1347 FILE *f; 1348 int i; 1349 uint32_t status, vers, last_len; 1350 char buf[DB_HEADER_LEN]; 1351 off_t off; 1352 asl_legacy1_t *legacy; 1353 struct stat sb; 1354 1355 memset(&sb, 0, sizeof(struct stat)); 1356 if (stat(path, &sb) != 0) return ASL_STATUS_FAILED; 1357 1358 f = fopen(path, "r"); 1359 if (f == NULL) 1360 { 1361 if (errno == EACCES) return ASL_STATUS_ACCESS_DENIED; 1362 return ASL_STATUS_FAILED; 1363 } 1364 1365 i = fread(buf, DB_HEADER_LEN, 1, f); 1366 if (i < 1) 1367 { 1368 fclose(f); 1369 return ASL_STATUS_INVALID_STORE; 1370 } 1371 1372 /* validate header */ 1373 if (strncmp(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN)) 1374 { 1375 fclose(f); 1376 return ASL_STATUS_INVALID_STORE; 1377 } 1378 1379 legacy = NULL; 1380 1381 vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET); 1382 if (vers == DB_VERSION_LEGACY_1) 1383 { 1384 fclose(f); 1385 status = asl_legacy1_open(path, &legacy); 1386 if (status != ASL_STATUS_OK) return status; 1387 } 1388 1389 out = (asl_file_t *)calloc(1, sizeof(asl_file_t)); 1390 if (out == NULL) 1391 { 1392 fclose(f); 1393 return ASL_STATUS_NO_MEMORY; 1394 } 1395 1396 out->store = f; 1397 out->flags = ASL_FILE_FLAG_READ_ONLY; 1398 out->version = vers; 1399 1400 if (legacy != NULL) 1401 { 1402 out->flags |= ASL_FILE_FLAG_LEGACY_STORE; 1403 out->legacy = (void *)legacy; 1404 1405 *s = out; 1406 return ASL_STATUS_OK; 1407 } 1408 1409 out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET); 1410 out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET); 1411 out->file_size = (size_t)sb.st_size; 1412 1413 /* 1414 * Detect bogus last pointer and check for odd-sized files. 1415 * Setting out->last to zero forces us to follow the linked 1416 * list of records in the file to the last record. That's 1417 * done in the set_position code. It's a bit slower, but it's 1418 * better at preventing crashes in corrupt files. 1419 */ 1420 1421 /* records are at least MSG_RECORD_FIXED_LENGTH bytes */ 1422 if ((out->last + MSG_RECORD_FIXED_LENGTH) > out->file_size) 1423 { 1424 out->last = 0; 1425 } 1426 else 1427 { 1428 /* read last record length and make sure the file is at least that large */ 1429 off = out->last + RECORD_TYPE_LEN; 1430 status = asl_file_read_uint32(out, off, &last_len); 1431 if (status != ASL_STATUS_OK) 1432 { 1433 fclose(out->store); 1434 free(out); 1435 return status; 1436 } 1437 1438 if ((out->last + last_len) > out->file_size) out->last = 0; 1439 } 1440 1441 out->cursor = out->first; 1442 if (out->cursor != 0) 1443 { 1444 off = out->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); 1445 status = asl_file_read_uint64(out, off, &(out->cursor_xid)); 1446 if (status != ASL_STATUS_OK) 1447 { 1448 fclose(out->store); 1449 free(out); 1450 return status; 1451 } 1452 } 1453 1454 *s = out; 1455 return ASL_STATUS_OK; 1456} 1457 1458static uint32_t 1459asl_file_read_set_position_first(asl_file_t *s) 1460{ 1461 uint32_t status; 1462 off_t off; 1463 1464 s->cursor = s->first; 1465 s->cursor_xid = 0; 1466 1467 if (s->cursor == 0) return ASL_STATUS_OK; 1468 1469 /* read ID of the first record */ 1470 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); 1471 status = asl_file_read_uint64(s, off, &(s->cursor_xid)); 1472 return status; 1473} 1474 1475static uint32_t 1476asl_file_read_set_position_last(asl_file_t *s) 1477{ 1478 uint64_t next; 1479 uint32_t status; 1480 off_t off; 1481 1482 /* 1483 * If the file has the offset of the last record, we just go there. 1484 * The last record offset was added to improve performance, so it may 1485 * or may not be there. If we don't have the last record offset, we 1486 * just iterate down the record links to find the last one. 1487 * 1488 * Note that s->last may be zero if the file is empty. 1489 */ 1490 1491 if (s->last != 0) 1492 { 1493 s->cursor = s->last; 1494 off = s->last + RECORD_COMMON_LEN + sizeof(uint64_t); 1495 1496 /* read ID of the last record */ 1497 status = asl_file_read_uint64(s, off, &(s->cursor_xid)); 1498 return status; 1499 } 1500 1501 /* start at the first record and iterate */ 1502 s->cursor = s->first; 1503 s->cursor_xid = 0; 1504 1505 forever 1506 { 1507 off = s->cursor + RECORD_COMMON_LEN; 1508 next = 0; 1509 1510 /* read next offset */ 1511 status = asl_file_read_uint64(s, off, &next); 1512 if (status != ASL_STATUS_OK) return status; 1513 1514 /* detect bogus next pointer */ 1515 if (((next + MSG_RECORD_FIXED_LENGTH) > s->file_size) || (next <= s->cursor)) next = 0; 1516 1517 if (next == 0) 1518 { 1519 if (s->cursor == 0) return ASL_STATUS_OK; 1520 1521 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); 1522 status = asl_file_read_uint64(s, off, &(s->cursor_xid)); 1523 return ASL_STATUS_OK; 1524 } 1525 1526 s->cursor = next; 1527 } 1528} 1529 1530uint32_t 1531asl_file_read_set_position(asl_file_t *s, uint32_t pos) 1532{ 1533 uint64_t next; 1534 uint32_t len, status; 1535 off_t off; 1536 1537 if (s == NULL) return ASL_STATUS_INVALID_STORE; 1538 if (s->version == 1) return ASL_STATUS_FAILED; 1539 1540 if (pos == ASL_FILE_POSITION_FIRST) return asl_file_read_set_position_first(s); 1541 if (pos == ASL_FILE_POSITION_LAST) return asl_file_read_set_position_last(s); 1542 1543 off = 0; 1544 1545 if (pos == ASL_FILE_POSITION_PREVIOUS) 1546 { 1547 if (s->cursor == s->first) return ASL_STATUS_NO_RECORDS; 1548 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; 1549 1550 off = s->cursor + RECORD_TYPE_LEN; 1551 status = asl_file_read_uint32(s, off, &len); 1552 if (status != ASL_STATUS_OK) return status; 1553 1554 /* set offset to read the "previous" field at the end of the record */ 1555 off = s->cursor + RECORD_COMMON_LEN + len - sizeof(uint64_t); 1556 } 1557 else if (pos == ASL_FILE_POSITION_NEXT) 1558 { 1559 if (s->cursor == s->last) return ASL_STATUS_NO_RECORDS; 1560 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; 1561 1562 /* set offset to read the "next" field in the current record */ 1563 off = s->cursor + RECORD_COMMON_LEN; 1564 } 1565 else return ASL_STATUS_INVALID_ARG; 1566 1567 s->cursor_xid = 0; 1568 1569 /* 1570 * read offset of next / previous 1571 */ 1572 next = 0; 1573 status = asl_file_read_uint64(s, off, &next); 1574 if (status != ASL_STATUS_OK) return ASL_STATUS_READ_FAILED; 1575 1576 /* detect bogus next pointer */ 1577 if ((next + MSG_RECORD_FIXED_LENGTH) > s->file_size) next = 0; 1578 else if ((pos == ASL_FILE_POSITION_PREVIOUS) && (next >= s->cursor)) next = 0; 1579 else if ((pos == ASL_FILE_POSITION_NEXT) && (next <= s->cursor)) next = 0; 1580 1581 s->cursor = next; 1582 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; 1583 1584 /* read ID of the record */ 1585 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); 1586 status = asl_file_read_uint64(s, off, &(s->cursor_xid)); 1587 return status; 1588} 1589 1590uint32_t 1591asl_file_fetch_next(asl_file_t *s, aslmsg *msg) 1592{ 1593 if (s == NULL) return ASL_STATUS_INVALID_STORE; 1594 if (s->version == 1) return ASL_STATUS_FAILED; 1595 1596 return asl_file_fetch_pos(s, s->cursor, 1, msg); 1597} 1598 1599uint32_t 1600asl_file_fetch_previous(asl_file_t *s, aslmsg *msg) 1601{ 1602 if (s == NULL) return ASL_STATUS_INVALID_STORE; 1603 if (s->version == 1) return ASL_STATUS_FAILED; 1604 1605 return asl_file_fetch_pos(s, s->cursor, -1, msg); 1606} 1607 1608uint32_t 1609asl_file_fetch(asl_file_t *s, uint64_t mid, aslmsg *msg) 1610{ 1611 uint32_t status; 1612 1613 if (s == NULL) return ASL_STATUS_INVALID_STORE; 1614 if (msg == NULL) return ASL_STATUS_INVALID_ARG; 1615 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; 1616 1617 if (s->version == 1) 1618 { 1619 return asl_legacy1_fetch((asl_legacy1_t *)s->legacy, mid, msg); 1620 } 1621 1622 if (s->cursor_xid == 0) 1623 { 1624 status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); 1625 if (status != ASL_STATUS_OK) return status; 1626 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID; 1627 } 1628 1629 while (s->cursor_xid < mid) 1630 { 1631 status = asl_file_read_set_position(s, ASL_FILE_POSITION_NEXT); 1632 if (status != ASL_STATUS_OK) return status; 1633 if (s->cursor_xid > mid) return ASL_STATUS_INVALID_ID; 1634 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID; 1635 } 1636 1637 while (s->cursor_xid > mid) 1638 { 1639 status = asl_file_read_set_position(s, ASL_FILE_POSITION_PREVIOUS); 1640 if (status != ASL_STATUS_OK) return status; 1641 if (s->cursor_xid < mid) return ASL_STATUS_INVALID_ID; 1642 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID; 1643 } 1644 1645 if (s->cursor_xid != mid) return ASL_STATUS_INVALID_ID; 1646 1647 return asl_file_fetch_pos(s, s->cursor, 1, msg); 1648} 1649 1650__private_extern__ uint64_t 1651asl_file_cursor(asl_file_t *s) 1652{ 1653 if (s == NULL) return 0; 1654 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return 0; 1655 if (s->version == 1) return 0; 1656 1657 return s->cursor_xid; 1658} 1659 1660__private_extern__ uint32_t 1661asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction) 1662{ 1663 uint32_t status, d; 1664 1665 if (s == NULL) return ASL_STATUS_INVALID_STORE; 1666 if (s->version == 1) return ASL_STATUS_INVALID_STORE; 1667 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; 1668 1669 d = ASL_FILE_POSITION_NEXT; 1670 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS; 1671 1672 /* 1673 * find starting point 1674 */ 1675 status = ASL_STATUS_OK; 1676 if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); 1677 else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST); 1678 if (status != ASL_STATUS_OK) return status; 1679 1680 while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start_id)) || ((direction < 0) && (s->cursor_xid > start_id)))) 1681 { 1682 status = asl_file_read_set_position(s, d); 1683 } 1684 1685 return status; 1686} 1687 1688__private_extern__ uint32_t 1689asl_file_match_next(asl_file_t *s, aslresponse query, aslmsg *msg, uint64_t *last_id, int32_t direction) 1690{ 1691 uint32_t status, d, i, do_match, did_match; 1692 aslmsg m; 1693 1694 if (s == NULL) return ASL_STATUS_INVALID_STORE; 1695 if (msg == NULL) return ASL_STATUS_INVALID_ARG; 1696 if (s->version == 1) return ASL_STATUS_INVALID_STORE; 1697 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; 1698 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; 1699 1700 *msg = NULL; 1701 do_match = 1; 1702 1703 d = ASL_FILE_POSITION_NEXT; 1704 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS; 1705 1706 if ((query == NULL) || ((query != NULL) && (query->count == 0))) do_match = 0; 1707 1708 m = NULL; 1709 1710 *last_id = s->cursor_xid; 1711 1712 status = asl_file_fetch_pos(s, s->cursor, direction, &m); 1713 if (status == ASL_STATUS_ACCESS_DENIED) return ASL_STATUS_MATCH_FAILED; 1714 if ((status == ASL_STATUS_INVALID_ARG) && (s->cursor == 0)) return ASL_STATUS_NO_RECORDS; 1715 if (status != ASL_STATUS_OK) return status; 1716 1717 did_match = 1; 1718 1719 if (do_match != 0) 1720 { 1721 did_match = 0; 1722 1723 for (i = 0; (i < query->count) && (did_match == 0); i++) 1724 { 1725 did_match = asl_msg_cmp((aslmsg)(query->msg[i]), m); 1726 } 1727 } 1728 1729 if (did_match != 0) 1730 { 1731 *msg = m; 1732 return ASL_STATUS_OK; 1733 } 1734 1735 *msg = NULL; 1736 asl_free(m); 1737 return ASL_STATUS_MATCH_FAILED; 1738} 1739 1740uint32_t 1741asl_file_match(asl_file_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) 1742{ 1743 uint32_t status, d, i, do_match, did_match, rescount; 1744 aslmsg m; 1745 1746 if (s == NULL) return ASL_STATUS_INVALID_STORE; 1747 if (res == NULL) return ASL_STATUS_INVALID_ARG; 1748 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; 1749 1750 if (s->version == 1) 1751 { 1752 return asl_legacy1_match((asl_legacy1_t *)s->legacy, query, res, last_id, start_id, count, direction); 1753 } 1754 1755 do_match = 1; 1756 rescount = 0; 1757 1758 d = ASL_FILE_POSITION_NEXT; 1759 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS; 1760 1761 if ((query == NULL) || ((query != NULL) && (query->count == 0))) do_match = 0; 1762 1763 /* 1764 * find starting point 1765 */ 1766 status = ASL_STATUS_OK; 1767 if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); 1768 else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST); 1769 if (status != ASL_STATUS_OK) return status; 1770 1771 while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start_id)) || ((direction < 0) && (s->cursor_xid > start_id)))) 1772 { 1773 status = asl_file_read_set_position(s, d); 1774 } 1775 1776 /* 1777 * loop through records 1778 */ 1779 forever 1780 { 1781 m = NULL; 1782 status = asl_file_fetch_pos(s, s->cursor, direction, &m); 1783 if (status == ASL_STATUS_ACCESS_DENIED) continue; 1784 if (status != ASL_STATUS_OK) break; 1785 1786 *last_id = s->cursor_xid; 1787 1788 did_match = 1; 1789 1790 if (do_match != 0) 1791 { 1792 did_match = 0; 1793 1794 for (i = 0; (i < query->count) && (did_match == 0); i++) 1795 { 1796 did_match = asl_msg_cmp((aslmsg)query->msg[i], m); 1797 } 1798 } 1799 1800 if (did_match == 1) 1801 { 1802 /* append m to res */ 1803 if (*res == NULL) 1804 { 1805 *res = (aslresponse)calloc(1, sizeof(aslresponse)); 1806 if (*res == NULL) return ASL_STATUS_NO_MEMORY; 1807 (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg)); 1808 if ((*res)->msg == NULL) 1809 { 1810 free(*res); 1811 return ASL_STATUS_NO_MEMORY; 1812 } 1813 } 1814 else 1815 { 1816 (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg)); 1817 if ((*res)->msg == NULL) 1818 { 1819 free(*res); 1820 return ASL_STATUS_NO_MEMORY; 1821 } 1822 } 1823 1824 (*res)->msg[(*res)->count] = (asl_msg_t *)m; 1825 (*res)->count++; 1826 1827 rescount++; 1828 if ((count != 0) && (rescount >= count)) break; 1829 } 1830 else 1831 { 1832 asl_free(m); 1833 } 1834 } 1835 1836 /* NOT REACHED */ 1837 return ASL_STATUS_OK; 1838} 1839 1840size_t 1841asl_file_size(asl_file_t *s) 1842{ 1843 if (s == NULL) return 0; 1844 return s->file_size; 1845} 1846 1847uint64_t 1848asl_file_ctime(asl_file_t *s) 1849{ 1850 if (s == NULL) return 0; 1851 return s->dob; 1852} 1853 1854void 1855asl_file_list_close(asl_file_list_t *head) 1856{ 1857 asl_file_list_t *next; 1858 1859 while (head != NULL) 1860 { 1861 next = head->next; 1862 asl_file_close(head->file); 1863 free(head); 1864 head = next; 1865 } 1866} 1867 1868static void 1869asl_file_list_free(asl_file_list_t *head) 1870{ 1871 asl_file_list_t *next; 1872 1873 while (head != NULL) 1874 { 1875 next = head->next; 1876 free(head); 1877 head = next; 1878 } 1879} 1880 1881static asl_file_list_t * 1882asl_file_list_insert(asl_file_list_t *list, asl_file_t *f, int32_t dir) 1883{ 1884 asl_file_list_t *a, *b, *tmp; 1885 1886 if (f == NULL) return list; 1887 1888 tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t)); 1889 if (tmp == NULL) return NULL; 1890 tmp->file = f; 1891 1892 if (list == NULL) return tmp; 1893 1894 a = list; 1895 if (((dir < 0) && (f->cursor_xid > a->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < a->file->cursor_xid))) 1896 { 1897 tmp->next = list; 1898 return tmp; 1899 } 1900 1901 b = a->next; 1902 while (b != NULL) 1903 { 1904 if (((dir < 0) && (f->cursor_xid > b->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < b->file->cursor_xid))) 1905 { 1906 tmp->next = b; 1907 a->next = tmp; 1908 return list; 1909 } 1910 1911 a = b; 1912 b = a->next; 1913 } 1914 1915 a->next = tmp; 1916 return list; 1917} 1918 1919asl_file_list_t * 1920asl_file_list_add(asl_file_list_t *list, asl_file_t *f) 1921{ 1922 asl_file_list_t *tmp; 1923 1924 if (f == NULL) return list; 1925 if (f->version == 1) return list; 1926 1927 tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t)); 1928 if (tmp == NULL) return NULL; 1929 tmp->file = f; 1930 1931 tmp->next = list; 1932 return tmp; 1933} 1934 1935void * 1936asl_file_list_match_start(asl_file_list_t *list, uint64_t start_id, int32_t direction) 1937{ 1938 uint32_t status; 1939 asl_file_list_t *n; 1940 asl_file_match_token_t *out; 1941 1942 if (list == NULL) return NULL; 1943 1944 out = (asl_file_match_token_t *)calloc(1, sizeof(asl_file_match_token_t)); 1945 if (out == NULL) return NULL; 1946 1947 for (n = list; n != NULL; n = n->next) 1948 { 1949 /* init file for the search */ 1950 status = asl_file_match_start(n->file, start_id, direction); 1951 if (status != ASL_STATUS_OK) continue; 1952 if (n->file->cursor_xid == 0) continue; 1953 1954 out->list = asl_file_list_insert(out->list, n->file, direction); 1955 } 1956 1957 out->dir = direction; 1958 return out; 1959} 1960 1961uint32_t 1962asl_file_list_match_next(void *token, aslresponse query, aslresponse *res, uint32_t count) 1963{ 1964 uint32_t status, rescount; 1965 asl_file_list_t *n; 1966 aslmsg m; 1967 asl_file_match_token_t *work; 1968 uint64_t last_id; 1969 1970 if (token == NULL) return ASL_STATUS_OK; 1971 if (res == NULL) return ASL_STATUS_INVALID_ARG; 1972 1973 work = (asl_file_match_token_t *)token; 1974 1975 rescount = 0; 1976 last_id = 0; 1977 1978 while ((work->list != NULL) && ((rescount < count) || (count == 0))) 1979 { 1980 m = NULL; 1981 status = asl_file_match_next(work->list->file, query, &m, &last_id, work->dir); 1982 if (m != NULL) 1983 { 1984 if (*res == NULL) *res = (aslresponse)calloc(1, sizeof(asl_search_result_t)); 1985 if (*res == NULL) 1986 { 1987 asl_file_list_free(work->list); 1988 work->list = NULL; 1989 return ASL_STATUS_NO_MEMORY; 1990 } 1991 1992 if ((*res)->msg == NULL) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg)); 1993 else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg)); 1994 if ((*res)->msg == NULL) 1995 { 1996 free(*res); 1997 *res = NULL; 1998 asl_file_list_free(work->list); 1999 work->list = NULL; 2000 return ASL_STATUS_NO_MEMORY; 2001 } 2002 2003 (*res)->msg[(*res)->count] = (asl_msg_t *)m; 2004 (*res)->count++; 2005 rescount++; 2006 } 2007 2008 if ((status != ASL_STATUS_OK) || (work->list->file->cursor_xid == 0)) 2009 { 2010 n = work->list->next; 2011 free(work->list); 2012 work->list = n; 2013 } 2014 2015 if (work->list != NULL) 2016 { 2017 n = work->list->next; 2018 if (n != NULL) 2019 { 2020 if (((work->dir < 0) && (work->list->file->cursor_xid <= n->file->cursor_xid)) || ((work->dir >= 0) && (work->list->file->cursor_xid > n->file->cursor_xid))) 2021 { 2022 n = work->list; 2023 work->list = work->list->next; 2024 n->next = NULL; 2025 work->list = asl_file_list_insert(work->list, n->file, work->dir); 2026 free(n); 2027 } 2028 } 2029 } 2030 } 2031 2032 return ASL_STATUS_OK; 2033} 2034 2035void 2036asl_file_list_match_end(void *token) 2037{ 2038 asl_file_match_token_t *work; 2039 2040 if (token == NULL) return; 2041 2042 work = (asl_file_match_token_t *)token; 2043 asl_file_list_free(work->list); 2044 work->list = NULL; 2045 2046 free(token); 2047} 2048 2049uint32_t 2050asl_file_list_match_timeout(asl_file_list_t *list, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec) 2051{ 2052 uint32_t status, rescount; 2053 asl_file_list_t *files, *n; 2054 aslmsg m; 2055 struct timeval now, finish; 2056 2057 if (list == NULL) return ASL_STATUS_INVALID_ARG; 2058 if (res == NULL) return ASL_STATUS_INVALID_ARG; 2059 if (last_id == NULL) return ASL_STATUS_INVALID_ARG; 2060 2061 files = NULL; 2062 2063 for (n = list; n != NULL; n = n->next) 2064 { 2065 /* init file for the search */ 2066 status = asl_file_match_start(n->file, start_id, direction); 2067 if (status != ASL_STATUS_OK) continue; 2068 if (n->file->cursor_xid == 0) continue; 2069 2070 files = asl_file_list_insert(files, n->file, direction); 2071 } 2072 2073 if (files == NULL) 2074 { 2075 asl_file_list_free(files); 2076 return ASL_STATUS_OK; 2077 } 2078 2079 /* start the timer if a timeout was specified */ 2080 memset(&finish, 0, sizeof(struct timeval)); 2081 if (usec != 0) 2082 { 2083 if (gettimeofday(&finish, NULL) == 0) 2084 { 2085 finish.tv_sec += (usec / MILLION); 2086 finish.tv_usec += (usec % MILLION); 2087 if (finish.tv_usec > MILLION) 2088 { 2089 finish.tv_usec -= MILLION; 2090 finish.tv_sec += 1; 2091 } 2092 } 2093 else 2094 { 2095 /* shouldn't happen, but if gettimeofday failed we just run without a timeout */ 2096 memset(&finish, 0, sizeof(struct timeval)); 2097 } 2098 } 2099 2100 rescount = 0; 2101 while ((files != NULL) && ((rescount < count) || (count == 0))) 2102 { 2103 m = NULL; 2104 status = asl_file_match_next(files->file, query, &m, last_id, direction); 2105 if (m != NULL) 2106 { 2107 if (*res == NULL) *res = (aslresponse)calloc(1, sizeof(asl_search_result_t)); 2108 if (*res == NULL) 2109 { 2110 asl_file_list_free(files); 2111 return ASL_STATUS_NO_MEMORY; 2112 } 2113 2114 if ((*res)->msg == NULL) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg)); 2115 else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg)); 2116 if ((*res)->msg == NULL) 2117 { 2118 free(*res); 2119 *res = NULL; 2120 asl_file_list_free(files); 2121 return ASL_STATUS_NO_MEMORY; 2122 } 2123 2124 (*res)->msg[(*res)->count] = (asl_msg_t *)m; 2125 (*res)->count++; 2126 rescount++; 2127 } 2128 2129 if (files->file->cursor_xid == 0) 2130 { 2131 n = files->next; 2132 free(files); 2133 files = n; 2134 } 2135 2136 if (files != NULL) 2137 { 2138 n = files->next; 2139 if (n != NULL) 2140 { 2141 if (((direction < 0) && (files->file->cursor_xid <= n->file->cursor_xid)) || ((direction >= 0) && (files->file->cursor_xid > n->file->cursor_xid))) 2142 { 2143 n = files; 2144 files = files->next; 2145 n->next = NULL; 2146 files = asl_file_list_insert(files, n->file, direction); 2147 free(n); 2148 } 2149 } 2150 } 2151 2152 /* check the timer */ 2153 if ((finish.tv_sec != 0) && (gettimeofday(&now, NULL) == 0)) 2154 { 2155 if ((now.tv_sec > finish.tv_sec) || ((now.tv_sec == finish.tv_sec) && (now.tv_usec > finish.tv_usec))) break; 2156 } 2157 } 2158 2159 asl_file_list_free(files); 2160 return ASL_STATUS_OK; 2161} 2162 2163uint32_t 2164asl_file_list_match(asl_file_list_t *list, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) 2165{ 2166 return asl_file_list_match_timeout(list, query, res, last_id, start_id, count, direction, 0); 2167} 2168