]> git.saurik.com Git - apple/syslog.git/blob - libsystem_asl.tproj/src/asl_file.c
syslog-217.1.4.tar.gz
[apple/syslog.git] / libsystem_asl.tproj / src / asl_file.c
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
43 extern time_t asl_parse_time(const char *str);
44 extern 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
59 typedef 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
83 typedef struct
84 {
85 asl_file_list_t *list;
86 int dir;
87 } asl_file_match_token_t;
88
89 static 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
98 static 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
107 static 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
116 static 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
125 static 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
134 static 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
143 static uint32_t
144 asl_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
164 static uint32_t
165 asl_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
186 uint32_t
187 asl_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
215 asl_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
268 asl_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
341 asl_file_create_return:
342
343 acl_free(acl);
344 return fd;
345 #endif
346 }
347
348 uint32_t
349 asl_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
480 uint32_t
481 asl_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
522 static uint32_t
523 asl_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 */
636 uint32_t
637 asl_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
995 static uint32_t
996 asl_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
1067 static uint16_t
1068 asl_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
1084 static uint32_t
1085 asl_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
1106 static uint64_t
1107 asl_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
1123 static uint64_t
1124 asl_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
1148 static uint32_t
1149 asl_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
1343 uint32_t
1344 asl_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
1458 static uint32_t
1459 asl_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
1475 static uint32_t
1476 asl_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
1530 uint32_t
1531 asl_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
1590 uint32_t
1591 asl_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
1599 uint32_t
1600 asl_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
1608 uint32_t
1609 asl_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
1651 asl_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
1661 asl_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
1689 asl_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
1740 uint32_t
1741 asl_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
1840 size_t
1841 asl_file_size(asl_file_t *s)
1842 {
1843 if (s == NULL) return 0;
1844 return s->file_size;
1845 }
1846
1847 uint64_t
1848 asl_file_ctime(asl_file_t *s)
1849 {
1850 if (s == NULL) return 0;
1851 return s->dob;
1852 }
1853
1854 void
1855 asl_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
1868 static void
1869 asl_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
1881 static asl_file_list_t *
1882 asl_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
1919 asl_file_list_t *
1920 asl_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
1935 void *
1936 asl_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
1961 uint32_t
1962 asl_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
2035 void
2036 asl_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
2049 uint32_t
2050 asl_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
2163 uint32_t
2164 asl_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 }