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