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