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