]> git.saurik.com Git - apple/libc.git/blob - gen/asl_file.c
Libc-825.24.tar.gz
[apple/libc.git] / gen / 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_EMBEDDED
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 static 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) s->cursor = r.next;
1268 else s->cursor = r.prev;
1269
1270 s->cursor_xid = 0;
1271
1272 if (s->cursor != 0)
1273 {
1274 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1275 if (off > s->file_size)
1276 {
1277 asl_free(out);
1278 s->cursor = 0;
1279 s->cursor_xid = 0;
1280 /*
1281 * Next record offset is past the end of the file.
1282 * This is an error, but we allow it to fail quietly
1283 * so that the current record fetch succeeds.
1284 */
1285 return ASL_STATUS_OK;
1286 }
1287
1288 status = fseeko(s->store, off, SEEK_SET);
1289 if (status != 0)
1290 {
1291 asl_free(out);
1292 s->cursor = 0;
1293 s->cursor_xid = 0;
1294 return ASL_STATUS_READ_FAILED;
1295 }
1296
1297 status = fread(&x64, sizeof(uint64_t), 1, s->store);
1298 if (status != 1)
1299 {
1300 asl_free(out);
1301 s->cursor = 0;
1302 s->cursor_xid = 0;
1303 return ASL_STATUS_READ_FAILED;
1304 }
1305
1306 s->cursor_xid = asl_core_ntohq(x64);
1307 }
1308
1309 *msg = out;
1310 return ASL_STATUS_OK;
1311 }
1312
1313 uint32_t
1314 asl_file_open_read(const char *path, asl_file_t **s)
1315 {
1316 asl_file_t *out;
1317 FILE *f;
1318 int i;
1319 uint32_t status, vers, last_len;
1320 char buf[DB_HEADER_LEN];
1321 off_t off;
1322 asl_legacy1_t *legacy;
1323 struct stat sb;
1324
1325 memset(&sb, 0, sizeof(struct stat));
1326 if (stat(path, &sb) != 0) return ASL_STATUS_FAILED;
1327
1328 f = fopen(path, "r");
1329 if (f == NULL)
1330 {
1331 if (errno == EACCES) return ASL_STATUS_ACCESS_DENIED;
1332 return ASL_STATUS_FAILED;
1333 }
1334
1335 i = fread(buf, DB_HEADER_LEN, 1, f);
1336 if (i < 1)
1337 {
1338 fclose(f);
1339 return ASL_STATUS_INVALID_STORE;
1340 }
1341
1342 /* validate header */
1343 if (strncmp(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN))
1344 {
1345 fclose(f);
1346 return ASL_STATUS_INVALID_STORE;
1347 }
1348
1349 legacy = NULL;
1350
1351 vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET);
1352 if (vers == DB_VERSION_LEGACY_1)
1353 {
1354 fclose(f);
1355 status = asl_legacy1_open(path, &legacy);
1356 if (status != ASL_STATUS_OK) return status;
1357 }
1358
1359 out = (asl_file_t *)calloc(1, sizeof(asl_file_t));
1360 if (out == NULL)
1361 {
1362 fclose(f);
1363 return ASL_STATUS_NO_MEMORY;
1364 }
1365
1366 out->store = f;
1367 out->flags = ASL_FILE_FLAG_READ_ONLY;
1368 out->version = vers;
1369
1370 if (legacy != NULL)
1371 {
1372 out->flags |= ASL_FILE_FLAG_LEGACY_STORE;
1373 out->legacy = (void *)legacy;
1374
1375 *s = out;
1376 return ASL_STATUS_OK;
1377 }
1378
1379 out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET);
1380 out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET);
1381 out->file_size = (size_t)sb.st_size;
1382
1383 /*
1384 * Detect bogus last pointer and check for odd-sized files.
1385 * Setting out->last to zero forces us to follow the linked
1386 * list of records in the file to the last record. That's
1387 * done in the set_position code. It's a bit slower, but it's
1388 * better at preventing crashes in corrupt files.
1389 */
1390
1391 /* records are at least MSG_RECORD_FIXED_LENGTH bytes */
1392 if ((out->last + MSG_RECORD_FIXED_LENGTH) > out->file_size)
1393 {
1394 out->last = 0;
1395 }
1396 else
1397 {
1398 /* read last record length and make sure the file is at least that large */
1399 off = out->last + RECORD_TYPE_LEN;
1400 status = asl_file_read_uint32(out, off, &last_len);
1401 if (status != ASL_STATUS_OK)
1402 {
1403 fclose(out->store);
1404 free(out);
1405 return status;
1406 }
1407
1408 if ((out->last + last_len) > out->file_size) out->last = 0;
1409 }
1410
1411 out->cursor = out->first;
1412 if (out->cursor != 0)
1413 {
1414 off = out->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1415 status = asl_file_read_uint64(out, off, &(out->cursor_xid));
1416 if (status != ASL_STATUS_OK)
1417 {
1418 fclose(out->store);
1419 free(out);
1420 return status;
1421 }
1422 }
1423
1424 *s = out;
1425 return ASL_STATUS_OK;
1426 }
1427
1428 static uint32_t
1429 asl_file_read_set_position_first(asl_file_t *s)
1430 {
1431 uint32_t status;
1432 off_t off;
1433
1434 s->cursor = s->first;
1435 s->cursor_xid = 0;
1436
1437 if (s->cursor == 0) return ASL_STATUS_OK;
1438
1439 /* read ID of the first record */
1440 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1441 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1442 return status;
1443 }
1444
1445 static uint32_t
1446 asl_file_read_set_position_last(asl_file_t *s)
1447 {
1448 uint64_t next;
1449 uint32_t status;
1450 off_t off;
1451
1452 /*
1453 * If the file has the offset of the last record, we just go there.
1454 * The last record offset was added to improve performance, so it may
1455 * or may not be there. If we don't have the last record offset, we
1456 * just iterate down the record links to find the last one.
1457 *
1458 * Note that s->last may be zero if the file is empty.
1459 */
1460
1461 if (s->last != 0)
1462 {
1463 s->cursor = s->last;
1464 off = s->last + RECORD_COMMON_LEN + sizeof(uint64_t);
1465
1466 /* read ID of the last record */
1467 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1468 return status;
1469 }
1470
1471 /* start at the first record and iterate */
1472 s->cursor = s->first;
1473 s->cursor_xid = 0;
1474
1475 forever
1476 {
1477 off = s->cursor + RECORD_COMMON_LEN;
1478 next = 0;
1479
1480 /* read next offset */
1481 status = asl_file_read_uint64(s, off, &next);
1482 if (status != ASL_STATUS_OK) return status;
1483
1484 /* detect bogus next pointer */
1485 if (((next + MSG_RECORD_FIXED_LENGTH) > s->file_size) || (next <= s->cursor)) next = 0;
1486
1487 if (next == 0)
1488 {
1489 if (s->cursor == 0) return ASL_STATUS_OK;
1490
1491 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1492 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1493 return ASL_STATUS_OK;
1494 }
1495
1496 s->cursor = next;
1497 }
1498 }
1499
1500 uint32_t
1501 asl_file_read_set_position(asl_file_t *s, uint32_t pos)
1502 {
1503 uint64_t next;
1504 uint32_t len, status;
1505 off_t off;
1506
1507 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1508 if (s->version == 1) return ASL_STATUS_FAILED;
1509
1510 if (pos == ASL_FILE_POSITION_FIRST) return asl_file_read_set_position_first(s);
1511 if (pos == ASL_FILE_POSITION_LAST) return asl_file_read_set_position_last(s);
1512
1513 off = 0;
1514
1515 if (pos == ASL_FILE_POSITION_PREVIOUS)
1516 {
1517 if (s->cursor == s->first) return ASL_STATUS_NO_RECORDS;
1518 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
1519
1520 off = s->cursor + RECORD_TYPE_LEN;
1521 status = asl_file_read_uint32(s, off, &len);
1522 if (status != ASL_STATUS_OK) return status;
1523
1524 /* set offset to read the "previous" field at the end of the record */
1525 off = s->cursor + RECORD_COMMON_LEN + len - sizeof(uint64_t);
1526 }
1527 else if (pos == ASL_FILE_POSITION_NEXT)
1528 {
1529 if (s->cursor == s->last) return ASL_STATUS_NO_RECORDS;
1530 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
1531
1532 /* set offset to read the "next" field in the current record */
1533 off = s->cursor + RECORD_COMMON_LEN;
1534 }
1535 else return ASL_STATUS_INVALID_ARG;
1536
1537 s->cursor_xid = 0;
1538
1539 /*
1540 * read offset of next / previous
1541 */
1542 next = 0;
1543 status = asl_file_read_uint64(s, off, &next);
1544 if (status != ASL_STATUS_OK) return ASL_STATUS_READ_FAILED;
1545
1546 /* detect bogus next pointer */
1547 if ((next + MSG_RECORD_FIXED_LENGTH) > s->file_size) next = 0;
1548 else if ((pos == ASL_FILE_POSITION_PREVIOUS) && (next >= s->cursor)) next = 0;
1549 else if ((pos == ASL_FILE_POSITION_NEXT) && (next <= s->cursor)) next = 0;
1550
1551 s->cursor = next;
1552 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
1553
1554 /* read ID of the record */
1555 off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t);
1556 status = asl_file_read_uint64(s, off, &(s->cursor_xid));
1557 return status;
1558 }
1559
1560 uint32_t
1561 asl_file_fetch_next(asl_file_t *s, aslmsg *msg)
1562 {
1563 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1564 if (s->version == 1) return ASL_STATUS_FAILED;
1565
1566 return asl_file_fetch_pos(s, s->cursor, 1, msg);
1567 }
1568
1569 uint32_t
1570 asl_file_fetch_previous(asl_file_t *s, aslmsg *msg)
1571 {
1572 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1573 if (s->version == 1) return ASL_STATUS_FAILED;
1574
1575 return asl_file_fetch_pos(s, s->cursor, -1, msg);
1576 }
1577
1578 uint32_t
1579 asl_file_fetch(asl_file_t *s, uint64_t mid, aslmsg *msg)
1580 {
1581 uint32_t status;
1582
1583 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1584 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
1585 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY;
1586
1587 if (s->version == 1)
1588 {
1589 return asl_legacy1_fetch((asl_legacy1_t *)s->legacy, mid, msg);
1590 }
1591
1592 if (s->cursor_xid == 0)
1593 {
1594 status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
1595 if (status != ASL_STATUS_OK) return status;
1596 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID;
1597 }
1598
1599 while (s->cursor_xid < mid)
1600 {
1601 status = asl_file_read_set_position(s, ASL_FILE_POSITION_NEXT);
1602 if (status != ASL_STATUS_OK) return status;
1603 if (s->cursor_xid > mid) return ASL_STATUS_INVALID_ID;
1604 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID;
1605 }
1606
1607 while (s->cursor_xid > mid)
1608 {
1609 status = asl_file_read_set_position(s, ASL_FILE_POSITION_PREVIOUS);
1610 if (status != ASL_STATUS_OK) return status;
1611 if (s->cursor_xid < mid) return ASL_STATUS_INVALID_ID;
1612 if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID;
1613 }
1614
1615 if (s->cursor_xid != mid) return ASL_STATUS_INVALID_ID;
1616
1617 return asl_file_fetch_pos(s, s->cursor, 1, msg);
1618 }
1619
1620 __private_extern__ uint64_t
1621 asl_file_cursor(asl_file_t *s)
1622 {
1623 if (s == NULL) return 0;
1624 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return 0;
1625 if (s->version == 1) return 0;
1626
1627 return s->cursor_xid;
1628 }
1629
1630 __private_extern__ uint32_t
1631 asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction)
1632 {
1633 uint32_t status, d;
1634
1635 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1636 if (s->version == 1) return ASL_STATUS_INVALID_STORE;
1637 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY;
1638
1639 d = ASL_FILE_POSITION_NEXT;
1640 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS;
1641
1642 /*
1643 * find starting point
1644 */
1645 status = ASL_STATUS_OK;
1646 if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
1647 else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST);
1648 if (status != ASL_STATUS_OK) return status;
1649
1650 while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start_id)) || ((direction < 0) && (s->cursor_xid > start_id))))
1651 {
1652 status = asl_file_read_set_position(s, d);
1653 }
1654
1655 return status;
1656 }
1657
1658 __private_extern__ uint32_t
1659 asl_file_match_next(asl_file_t *s, aslresponse query, aslmsg *msg, uint64_t *last_id, int32_t direction)
1660 {
1661 uint32_t status, d, i, do_match, did_match;
1662 aslmsg m;
1663
1664 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1665 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
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 if (s->cursor == 0) return ASL_STATUS_NO_RECORDS;
1669
1670 *msg = NULL;
1671 do_match = 1;
1672
1673 d = ASL_FILE_POSITION_NEXT;
1674 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS;
1675
1676 if ((query == NULL) || ((query != NULL) && (query->count == 0))) do_match = 0;
1677
1678 m = NULL;
1679
1680 *last_id = s->cursor_xid;
1681
1682 status = asl_file_fetch_pos(s, s->cursor, direction, &m);
1683 if (status == ASL_STATUS_ACCESS_DENIED) return ASL_STATUS_MATCH_FAILED;
1684 if ((status == ASL_STATUS_INVALID_ARG) && (s->cursor == 0)) return ASL_STATUS_NO_RECORDS;
1685 if (status != ASL_STATUS_OK) return status;
1686
1687 did_match = 1;
1688
1689 if (do_match != 0)
1690 {
1691 did_match = 0;
1692
1693 for (i = 0; (i < query->count) && (did_match == 0); i++)
1694 {
1695 did_match = asl_msg_cmp((aslmsg)(query->msg[i]), m);
1696 }
1697 }
1698
1699 if (did_match != 0)
1700 {
1701 *msg = m;
1702 return ASL_STATUS_OK;
1703 }
1704
1705 asl_free(m);
1706 return ASL_STATUS_MATCH_FAILED;
1707 }
1708
1709 uint32_t
1710 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)
1711 {
1712 uint32_t status, d, i, do_match, did_match, rescount;
1713 aslmsg m;
1714
1715 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1716 if (res == NULL) return ASL_STATUS_INVALID_ARG;
1717 if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY;
1718
1719 if (s->version == 1)
1720 {
1721 return asl_legacy1_match((asl_legacy1_t *)s->legacy, query, res, last_id, start_id, count, direction);
1722 }
1723
1724 do_match = 1;
1725 rescount = 0;
1726
1727 d = ASL_FILE_POSITION_NEXT;
1728 if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS;
1729
1730 if ((query == NULL) || ((query != NULL) && (query->count == 0))) do_match = 0;
1731
1732 /*
1733 * find starting point
1734 */
1735 status = ASL_STATUS_OK;
1736 if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST);
1737 else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST);
1738 if (status != ASL_STATUS_OK) return status;
1739
1740 while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start_id)) || ((direction < 0) && (s->cursor_xid > start_id))))
1741 {
1742 status = asl_file_read_set_position(s, d);
1743 }
1744
1745 /*
1746 * loop through records
1747 */
1748 forever
1749 {
1750 m = NULL;
1751 status = asl_file_fetch_pos(s, s->cursor, direction, &m);
1752 if (status == ASL_STATUS_ACCESS_DENIED) continue;
1753 if (status != ASL_STATUS_OK) break;
1754
1755 *last_id = s->cursor_xid;
1756
1757 did_match = 1;
1758
1759 if (do_match != 0)
1760 {
1761 did_match = 0;
1762
1763 for (i = 0; (i < query->count) && (did_match == 0); i++)
1764 {
1765 did_match = asl_msg_cmp((aslmsg)query->msg[i], m);
1766 }
1767 }
1768
1769 if (did_match == 1)
1770 {
1771 /* append m to res */
1772 if (*res == NULL)
1773 {
1774 *res = (aslresponse)calloc(1, sizeof(aslresponse));
1775 if (*res == NULL) return ASL_STATUS_NO_MEMORY;
1776 (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg));
1777 if ((*res)->msg == NULL)
1778 {
1779 free(*res);
1780 return ASL_STATUS_NO_MEMORY;
1781 }
1782 }
1783 else
1784 {
1785 (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg));
1786 if ((*res)->msg == NULL)
1787 {
1788 free(*res);
1789 return ASL_STATUS_NO_MEMORY;
1790 }
1791 }
1792
1793 (*res)->msg[(*res)->count] = (asl_msg_t *)m;
1794 (*res)->count++;
1795
1796 rescount++;
1797 if ((count != 0) && (rescount >= count)) break;
1798 }
1799 else
1800 {
1801 asl_free(m);
1802 }
1803 }
1804
1805 /* NOT REACHED */
1806 return ASL_STATUS_OK;
1807 }
1808
1809 size_t
1810 asl_file_size(asl_file_t *s)
1811 {
1812 if (s == NULL) return 0;
1813 return s->file_size;
1814 }
1815
1816 uint64_t
1817 asl_file_ctime(asl_file_t *s)
1818 {
1819 if (s == NULL) return 0;
1820 return s->dob;
1821 }
1822
1823 void
1824 asl_file_list_close(asl_file_list_t *head)
1825 {
1826 asl_file_list_t *next;
1827
1828 while (head != NULL)
1829 {
1830 next = head->next;
1831 asl_file_close(head->file);
1832 free(head);
1833 head = next;
1834 }
1835 }
1836
1837 static void
1838 asl_file_list_free(asl_file_list_t *head)
1839 {
1840 asl_file_list_t *next;
1841
1842 while (head != NULL)
1843 {
1844 next = head->next;
1845 free(head);
1846 head = next;
1847 }
1848 }
1849
1850 static asl_file_list_t *
1851 asl_file_list_insert(asl_file_list_t *list, asl_file_t *f, int32_t dir)
1852 {
1853 asl_file_list_t *a, *b, *tmp;
1854
1855 if (f == NULL) return list;
1856
1857 tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t));
1858 if (tmp == NULL) return NULL;
1859 tmp->file = f;
1860
1861 if (list == NULL) return tmp;
1862
1863 a = list;
1864 if (((dir < 0) && (f->cursor_xid > a->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < a->file->cursor_xid)))
1865 {
1866 tmp->next = list;
1867 return tmp;
1868 }
1869
1870 b = a->next;
1871 while (b != NULL)
1872 {
1873 if (((dir < 0) && (f->cursor_xid > b->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < b->file->cursor_xid)))
1874 {
1875 tmp->next = b;
1876 a->next = tmp;
1877 return list;
1878 }
1879
1880 a = b;
1881 b = a->next;
1882 }
1883
1884 a->next = tmp;
1885 return list;
1886 }
1887
1888 asl_file_list_t *
1889 asl_file_list_add(asl_file_list_t *list, asl_file_t *f)
1890 {
1891 asl_file_list_t *tmp;
1892
1893 if (f == NULL) return list;
1894 if (f->version == 1) return list;
1895
1896 tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t));
1897 if (tmp == NULL) return NULL;
1898 tmp->file = f;
1899
1900 tmp->next = list;
1901 return tmp;
1902 }
1903
1904 void *
1905 asl_file_list_match_start(asl_file_list_t *list, uint64_t start_id, int32_t direction)
1906 {
1907 uint32_t status;
1908 asl_file_list_t *n;
1909 asl_file_match_token_t *out;
1910
1911 if (list == NULL) return NULL;
1912
1913 out = (asl_file_match_token_t *)calloc(1, sizeof(asl_file_match_token_t));
1914 if (out == NULL) return NULL;
1915
1916 for (n = list; n != NULL; n = n->next)
1917 {
1918 /* init file for the search */
1919 status = asl_file_match_start(n->file, start_id, direction);
1920 if (status != ASL_STATUS_OK) continue;
1921 if (n->file->cursor_xid == 0) continue;
1922
1923 out->list = asl_file_list_insert(out->list, n->file, direction);
1924 }
1925
1926 out->dir = direction;
1927 return out;
1928 }
1929
1930 uint32_t
1931 asl_file_list_match_next(void *token, aslresponse query, aslresponse *res, uint32_t count)
1932 {
1933 uint32_t status, rescount;
1934 asl_file_list_t *n;
1935 aslmsg m;
1936 asl_file_match_token_t *work;
1937 uint64_t last_id;
1938
1939 if (token == NULL) return ASL_STATUS_OK;
1940 if (res == NULL) return ASL_STATUS_INVALID_ARG;
1941
1942 work = (asl_file_match_token_t *)token;
1943
1944 rescount = 0;
1945 last_id = 0;
1946
1947 while ((work->list != NULL) && ((rescount < count) || (count == 0)))
1948 {
1949 m = NULL;
1950 status = asl_file_match_next(work->list->file, query, &m, &last_id, work->dir);
1951 if (m != NULL)
1952 {
1953 if (*res == NULL) *res = (aslresponse)calloc(1, sizeof(asl_search_result_t));
1954 if (*res == NULL)
1955 {
1956 asl_file_list_free(work->list);
1957 work->list = NULL;
1958 return ASL_STATUS_NO_MEMORY;
1959 }
1960
1961 if ((*res)->msg == NULL) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg));
1962 else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg));
1963 if ((*res)->msg == NULL)
1964 {
1965 free(*res);
1966 *res = NULL;
1967 asl_file_list_free(work->list);
1968 work->list = NULL;
1969 return ASL_STATUS_NO_MEMORY;
1970 }
1971
1972 (*res)->msg[(*res)->count] = (asl_msg_t *)m;
1973 (*res)->count++;
1974 rescount++;
1975 }
1976
1977 if ((status != ASL_STATUS_OK) || (work->list->file->cursor_xid == 0))
1978 {
1979 n = work->list->next;
1980 free(work->list);
1981 work->list = n;
1982 }
1983
1984 if (work->list != NULL)
1985 {
1986 n = work->list->next;
1987 if (n != NULL)
1988 {
1989 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)))
1990 {
1991 n = work->list;
1992 work->list = work->list->next;
1993 n->next = NULL;
1994 work->list = asl_file_list_insert(work->list, n->file, work->dir);
1995 free(n);
1996 }
1997 }
1998 }
1999 }
2000
2001 return ASL_STATUS_OK;
2002 }
2003
2004 void
2005 asl_file_list_match_end(void *token)
2006 {
2007 asl_file_match_token_t *work;
2008
2009 if (token == NULL) return;
2010
2011 work = (asl_file_match_token_t *)token;
2012 asl_file_list_free(work->list);
2013 work->list = NULL;
2014
2015 free(token);
2016 }
2017
2018 uint32_t
2019 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)
2020 {
2021 uint32_t status, rescount;
2022 asl_file_list_t *files, *n;
2023 aslmsg m;
2024 struct timeval now, finish;
2025
2026 if (list == NULL) return ASL_STATUS_INVALID_ARG;
2027 if (res == NULL) return ASL_STATUS_INVALID_ARG;
2028 if (last_id == NULL) return ASL_STATUS_INVALID_ARG;
2029
2030 files = NULL;
2031
2032 for (n = list; n != NULL; n = n->next)
2033 {
2034 /* init file for the search */
2035 status = asl_file_match_start(n->file, start_id, direction);
2036 if (status != ASL_STATUS_OK) continue;
2037 if (n->file->cursor_xid == 0) continue;
2038
2039 files = asl_file_list_insert(files, n->file, direction);
2040 }
2041
2042 if (files == NULL)
2043 {
2044 asl_file_list_free(files);
2045 return ASL_STATUS_OK;
2046 }
2047
2048 /* start the timer if a timeout was specified */
2049 memset(&finish, 0, sizeof(struct timeval));
2050 if (usec != 0)
2051 {
2052 if (gettimeofday(&finish, NULL) == 0)
2053 {
2054 finish.tv_sec += (usec / MILLION);
2055 finish.tv_usec += (usec % MILLION);
2056 if (finish.tv_usec > MILLION)
2057 {
2058 finish.tv_usec -= MILLION;
2059 finish.tv_sec += 1;
2060 }
2061 }
2062 else
2063 {
2064 /* shouldn't happen, but if gettimeofday failed we just run without a timeout */
2065 memset(&finish, 0, sizeof(struct timeval));
2066 }
2067 }
2068
2069 rescount = 0;
2070 while ((files != NULL) && ((rescount < count) || (count == 0)))
2071 {
2072 m = NULL;
2073 status = asl_file_match_next(files->file, query, &m, last_id, direction);
2074 if (m != NULL)
2075 {
2076 if (*res == NULL) *res = (aslresponse)calloc(1, sizeof(asl_search_result_t));
2077 if (*res == NULL)
2078 {
2079 asl_file_list_free(files);
2080 return ASL_STATUS_NO_MEMORY;
2081 }
2082
2083 if ((*res)->msg == NULL) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg));
2084 else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg));
2085 if ((*res)->msg == NULL)
2086 {
2087 free(*res);
2088 *res = NULL;
2089 asl_file_list_free(files);
2090 return ASL_STATUS_NO_MEMORY;
2091 }
2092
2093 (*res)->msg[(*res)->count] = (asl_msg_t *)m;
2094 (*res)->count++;
2095 rescount++;
2096 }
2097
2098 if (files->file->cursor_xid == 0)
2099 {
2100 n = files->next;
2101 free(files);
2102 files = n;
2103 }
2104
2105 if (files != NULL)
2106 {
2107 n = files->next;
2108 if (n != NULL)
2109 {
2110 if (((direction < 0) && (files->file->cursor_xid <= n->file->cursor_xid)) || ((direction >= 0) && (files->file->cursor_xid > n->file->cursor_xid)))
2111 {
2112 n = files;
2113 files = files->next;
2114 n->next = NULL;
2115 files = asl_file_list_insert(files, n->file, direction);
2116 free(n);
2117 }
2118 }
2119 }
2120
2121 /* check the timer */
2122 if ((finish.tv_sec != 0) && (gettimeofday(&now, NULL) == 0))
2123 {
2124 if ((now.tv_sec > finish.tv_sec) || ((now.tv_sec == finish.tv_sec) && (now.tv_usec > finish.tv_usec))) break;
2125 }
2126 }
2127
2128 asl_file_list_free(files);
2129 return ASL_STATUS_OK;
2130 }
2131
2132 uint32_t
2133 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)
2134 {
2135 return asl_file_list_match_timeout(list, query, res, last_id, start_id, count, direction, 0);
2136 }