2 * Copyright (c) 2007-2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
33 #include <sys/errno.h>
36 #include <sys/types.h>
38 #include <membership.h>
41 #include <asl_private.h>
42 #include <asl_legacy1.h>
43 #include <TargetConditionals.h>
45 #define forever for(;;)
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.
51 #define RECORD_COMMON_LEN 6
52 #define RECORD_TYPE_LEN 2
53 #define BUFFER_OFFSET_KVCOUNT 56
55 #define SCRATCH_BUFFER_SIZE (MSG_RECORD_FIXED_LENGTH + (20 * sizeof(uint64_t)))
83 asl_file_list_t
*list
;
85 } asl_file_match_token_t
;
97 _asl_put_16(uint16_t i
, char *h
)
115 _asl_put_32(uint32_t i
, char *h
)
129 return asl_core_ntohq(x
);
133 _asl_put_64(uint64_t i
, char *h
)
137 x
= asl_core_htonq(i
);
142 asl_file_read_uint32(asl_file_t
*s
, off_t off
, uint32_t *out
)
144 uint32_t status
, val
;
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
;
150 status
= fseeko(s
->store
, off
, SEEK_SET
);
151 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
155 status
= fread(&val
, sizeof(uint32_t), 1, s
->store
);
156 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
158 if (out
!= NULL
) *out
= ntohl(val
);
159 return ASL_STATUS_OK
;
163 asl_file_read_uint64(asl_file_t
*s
, off_t off
, uint64_t *out
)
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
;
172 status
= fseeko(s
->store
, off
, SEEK_SET
);
173 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
177 status
= fread(&val
, sizeof(uint64_t), 1, s
->store
);
178 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
180 if (out
!= NULL
) *out
= asl_core_ntohq(val
);
181 return ASL_STATUS_OK
;
184 static file_string_t
*
185 file_string_create(asl_file_t
*s
)
187 if ((s
!= NULL
) && (s
->string_spare
!= NULL
))
189 file_string_t
*out
= s
->string_spare
;
190 s
->string_spare
= NULL
;
194 return (file_string_t
*)calloc(1, sizeof(file_string_t
));
198 file_string_dispose(asl_file_t
*s
, file_string_t
*x
)
200 if ((s
!= NULL
) && (s
->string_spare
== NULL
))
203 memset(s
->string_spare
, 0, sizeof(file_string_t
));
213 asl_file_retain(asl_file_t
*s
)
215 if (s
== NULL
) return NULL
;
216 asl_retain((asl_object_t
)s
);
221 asl_file_release(asl_file_t
*s
)
223 if (s
== NULL
) return;
224 asl_release((asl_object_t
)s
);
228 asl_file_close(asl_file_t
*s
)
230 if (s
== NULL
) return ASL_STATUS_OK
;
231 asl_release((asl_object_t
)s
);
232 return ASL_STATUS_OK
;
236 _asl_file_free_internal(asl_file_t
*s
)
240 if (s
== NULL
) return;
244 asl_legacy1_close((asl_legacy1_t
*)s
->legacy
);
248 while (s
->string_list
!= NULL
)
250 x
= s
->string_list
->next
;
251 free(s
->string_list
);
255 free(s
->string_spare
);
257 if (s
->store
!= NULL
) fclose(s
->store
);
258 if (s
->scratch
!= NULL
) free(s
->scratch
);
260 memset(s
, 0, sizeof(asl_file_t
));
264 __private_extern__ ASL_STATUS
265 asl_file_open_write_fd(int fd
, asl_file_t
**s
)
269 char buf
[DB_HEADER_LEN
];
272 if (fd
< 0) return ASL_STATUS_FAILED
;
273 if (s
== NULL
) return ASL_STATUS_FAILED
;
275 out
= (asl_file_t
*)calloc(1, sizeof(asl_file_t
));
276 if (out
== NULL
) return ASL_STATUS_NO_MEMORY
;
278 out
->asl_type
= ASL_TYPE_FILE
;
281 out
->store
= fdopen(fd
, "w+");
282 if (out
->store
== NULL
)
285 return ASL_STATUS_FAILED
;
288 memset(buf
, 0, sizeof(buf
));
289 memcpy(buf
, ASL_DB_COOKIE
, ASL_DB_COOKIE_LEN
);
291 _asl_put_32(DB_VERSION
, buf
+ DB_HEADER_VERS_OFFSET
);
295 _asl_put_64(out
->dob
, buf
+ DB_HEADER_TIME_OFFSET
);
297 _asl_put_32(CACHE_SIZE
, buf
+ DB_HEADER_CSIZE_OFFSET
);
299 status
= fwrite(buf
, sizeof(buf
), 1, out
->store
);
304 return ASL_STATUS_FAILED
;
310 out
->file_size
= sizeof(buf
);
312 /* scratch buffer for file writes (we test for NULL before using it) */
313 out
->scratch
= malloc(SCRATCH_BUFFER_SIZE
);
317 return ASL_STATUS_OK
;
320 __private_extern__
int
321 asl_file_create(const char *path
, uid_t uid
, gid_t gid
, mode_t mode
)
324 return open(path
, O_RDWR
| O_CREAT
| O_EXCL
, mode
);
333 /* -1 means don't set ACL for uid or gid */
334 if ((uid
== -1) && (gid
== -1))
336 return open(path
, O_RDWR
| O_CREAT
| O_EXCL
, mode
);
341 if ((gid
!= 0) && (gid
!= -1))
343 status
= mbr_gid_to_uuid(gid
, uuid
);
344 if (status
!= 0) goto asl_file_create_return
;
346 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
347 if (status
!= 0) goto asl_file_create_return
;
349 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
350 if (status
!= 0) goto asl_file_create_return
;
352 status
= acl_set_qualifier(entry
, &uuid
);
353 if (status
!= 0) goto asl_file_create_return
;
355 status
= acl_get_permset(entry
, &perms
);
356 if (status
!= 0) goto asl_file_create_return
;
358 status
= acl_add_perm(perms
, ACL_READ_DATA
);
359 if (status
!= 0) goto asl_file_create_return
;
362 if ((uid
!= 0) && (uid
!= -1))
364 status
= mbr_uid_to_uuid(uid
, uuid
);
365 if (status
!= 0) goto asl_file_create_return
;
367 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
368 if (status
!= 0) goto asl_file_create_return
;
370 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
371 if (status
!= 0) goto asl_file_create_return
;
373 status
= acl_set_qualifier(entry
, &uuid
);
374 if (status
!= 0) goto asl_file_create_return
;
376 status
= acl_get_permset(entry
, &perms
);
377 if (status
!= 0) goto asl_file_create_return
;
379 status
= acl_add_perm(perms
, ACL_READ_DATA
);
380 if (status
!= 0) goto asl_file_create_return
;
383 fd
= open(path
, O_RDWR
| O_CREAT
| O_EXCL
, mode
);
384 if (fd
< 0) goto asl_file_create_return
;
386 status
= acl_set_fd(fd
, acl
);
394 asl_file_create_return
:
402 asl_file_open_write(const char *path
, mode_t mode
, uid_t uid
, gid_t gid
, asl_file_t
**s
)
406 char buf
[DB_HEADER_LEN
];
408 uint32_t aslstatus
, vers
, last_len
;
411 memset(&sb
, 0, sizeof(struct stat
));
413 status
= stat(path
, &sb
);
416 /* must be a plain file */
417 if (!S_ISREG(sb
.st_mode
)) return ASL_STATUS_INVALID_STORE
;
420 * If the file exists, we go with the existing mode, uid, and gid.
425 fd
= open(path
, O_RDWR
| O_EXCL
, mode
);
426 if (fd
< 0) return ASL_STATUS_FAILED
;
428 return asl_file_open_write_fd(fd
, s
);
432 out
= (asl_file_t
*)calloc(1, sizeof(asl_file_t
));
433 if (out
== NULL
) return ASL_STATUS_NO_MEMORY
;
435 out
->asl_type
= ASL_TYPE_FILE
;
438 out
->store
= fopen(path
, "r+");
439 if (out
->store
== NULL
)
442 return ASL_STATUS_FAILED
;
445 i
= fread(buf
, DB_HEADER_LEN
, 1, out
->store
);
449 return ASL_STATUS_READ_FAILED
;
453 if (strncmp(buf
, ASL_DB_COOKIE
, ASL_DB_COOKIE_LEN
))
456 return ASL_STATUS_INVALID_STORE
;
460 vers
= _asl_get_32(buf
+ DB_HEADER_VERS_OFFSET
);
461 if (vers
!= DB_VERSION
)
464 return ASL_STATUS_INVALID_STORE
;
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
;
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.
479 /* records are at least MSG_RECORD_FIXED_LENGTH bytes */
480 if ((out
->last
+ MSG_RECORD_FIXED_LENGTH
) > out
->file_size
)
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
)
495 if ((out
->last
+ last_len
) > out
->file_size
) out
->last
= 0;
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
)
510 aslstatus
= asl_file_read_set_position(out
, ASL_FILE_POSITION_LAST
);
511 if (aslstatus
!= ASL_STATUS_OK
)
517 out
->prev
= out
->cursor
;
518 status
= fseeko(out
->store
, 0, SEEK_END
);
522 return ASL_STATUS_READ_FAILED
;
525 out
->file_size
= (size_t)ftello(out
->store
);
527 /* scratch buffer for file writes (we test for NULL before using it) */
528 out
->scratch
= malloc(SCRATCH_BUFFER_SIZE
);
532 return ASL_STATUS_OK
;
535 else if (errno
!= ENOENT
)
537 /* unexpected status */
538 return ASL_STATUS_FAILED
;
542 * If the file does not exist, we set the mode, uid, and gid.
545 fd
= asl_file_create(path
, uid
, gid
, mode
);
546 if (fd
< 0) return ASL_STATUS_FAILED
;
548 aslstatus
= asl_file_open_write_fd(fd
, s
);
549 if (aslstatus
!= ASL_STATUS_OK
) unlink(path
);
555 asl_file_compact(asl_file_t
*s
, const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
563 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
564 if (path
== NULL
) return ASL_STATUS_INVALID_ARG
;
566 if (s
->version
== 1) return ASL_STATUS_FAILED
;
568 memset(&sb
, 0, sizeof(struct stat
));
570 if (stat(path
, &sb
) == 0) return ASL_STATUS_FAILED
;
571 if (errno
!= ENOENT
) return ASL_STATUS_FAILED
;
573 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
574 if (status
!= ASL_STATUS_OK
) return status
;
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
;
581 while ((status
== ASL_STATUS_OK
) && (s
->cursor
!= 0))
584 status
= asl_file_fetch_next(s
, &m
);
585 if (status
!= ASL_STATUS_OK
) break;
588 status
= asl_file_save(new, m
, &xid
);
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
))
603 uint32_t status
, n
= 0;
604 uint32_t matchflag
= flags
& ASL_FILE_FILTER_FLAG_KEEP_MATCHES
;
606 if (dstcount
!= NULL
) *dstcount
= n
;
608 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
609 if (path
== NULL
) return ASL_STATUS_INVALID_ARG
;
611 if (s
->version
== 1) return ASL_STATUS_FAILED
;
613 memset(&sb
, 0, sizeof(struct stat
));
615 if (stat(path
, &sb
) == 0) return ASL_STATUS_FAILED
;
616 if (errno
!= ENOENT
) return ASL_STATUS_FAILED
;
618 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
619 if (status
!= ASL_STATUS_OK
) return status
;
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
;
626 while ((status
== ASL_STATUS_OK
) && (s
->cursor
!= 0))
629 status
= asl_file_fetch_next(s
, &m
);
630 if (status
!= ASL_STATUS_OK
) break;
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.
636 uint32_t msgmatch
= (asl_msg_cmp_list(m
, filter
) == 0) ? 0 : 1;
637 if (msgmatch
== matchflag
)
639 status
= asl_file_save(new, m
, &xid
);
640 if (status
== ASL_STATUS_OK
) n
++;
642 else if (aux_callback
!= NULL
)
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
);
653 if (dstcount
!= NULL
) *dstcount
= n
;
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
))
664 uint32_t status
, n
= 0;
665 uint8_t kmcur
, kmnew
;
667 if (dstcount
!= NULL
) *dstcount
= n
;
669 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
670 if (path
== NULL
) return ASL_STATUS_INVALID_ARG
;
672 if (s
->version
== 1) return ASL_STATUS_FAILED
;
674 memset(&sb
, 0, sizeof(struct stat
));
676 if (stat(path
, &sb
) == 0) return ASL_STATUS_FAILED
;
677 if (errno
!= ENOENT
) return ASL_STATUS_FAILED
;
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
);
686 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
687 if (status
!= ASL_STATUS_OK
) return status
;
690 status
= asl_file_open_write(path
, mode
, uid
, gid
, &new);
691 if (status
!= ASL_STATUS_OK
) return status
;
693 new->flags
= ASL_FILE_FLAG_UNLIMITED_CACHE
| ASL_FILE_FLAG_PRESERVE_MSG_ID
;
695 while ((status
== ASL_STATUS_OK
) && (s
->cursor
!= 0))
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
);
705 if (level_bit
& keep_mask
)
707 status
= asl_file_save(new, m
, &xid
);
708 if (status
== ASL_STATUS_OK
) n
++;
710 else if (aux_callback
!= NULL
)
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
);
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
);
726 if (dstcount
!= NULL
) *dstcount
= n
;
731 asl_file_string_encode(asl_file_t
*s
, const char *str
, uint64_t *out
)
733 uint32_t i
, hash
, len
, x32
;
734 file_string_t
*sp
, *sx
, *sl
;
741 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
742 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
743 if (str
== NULL
) return ASL_STATUS_INVALID_ARG
;
757 memcpy(p
+ 1, str
, len
);
758 *out
= asl_core_ntohq(x64
);
759 return ASL_STATUS_OK
;
762 /* cached strings include trailing nul */
765 if (len
<= CACHE_MAX_STRING_LEN
)
767 /* check the cache */
768 hash
= asl_core_string_hash(str
, len
);
771 for (sx
= s
->string_list
; sx
!= NULL
; sx
= sx
->next
)
773 if ((hash
== sx
->hash
) && (!strcmp(str
, sx
->str
)))
775 /* Move this string to the head of the list */
785 return ASL_STATUS_OK
;
792 off
= ftello(s
->store
);
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
;
799 /* Length (includes trailing nul) */
801 i
= fwrite(&x32
, sizeof(uint32_t), 1, s
->store
);
802 if (i
!= 1) return ASL_STATUS_WRITE_FAILED
;
804 /* String data (nul terminated) */
805 i
= fwrite(str
, len
, 1, s
->store
);
806 if (i
!= 1) return ASL_STATUS_WRITE_FAILED
;
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.
816 if (len
<= CACHE_MAX_STRING_LEN
)
818 sx
= file_string_create(s
);
819 if (sx
== NULL
) return ASL_STATUS_NO_MEMORY
;
823 sx
->next
= s
->string_list
;
825 /* includes trailing nul */
826 memcpy(sx
->str
, str
, len
);
830 if (((s
->flags
& ASL_FILE_FLAG_UNLIMITED_CACHE
) == 0) && (s
->string_cache_count
== CACHE_SIZE
))
832 /* drop last (lru) string from cache */
836 /* NB CACHE_SIZE must be > 1 */
837 while (sx
->next
!= NULL
)
844 file_string_dispose(s
, sx
);
848 s
->string_cache_count
++;
853 return ASL_STATUS_OK
;
857 * Encode an asl_msg_t *as a record structure.
858 * Creates and caches strings.
860 #define KVSTACK_SIZE 128
863 asl_file_save(asl_file_t
*s
, asl_msg_t
*in
, uint64_t *mid
)
866 uint32_t i
, len
, x
, status
;
869 uint64_t kvstack
[KVSTACK_SIZE
];
870 uint64_t *kvmalloc
= NULL
;
874 const char *key
, *val
;
876 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
877 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
878 if (in
== NULL
) return ASL_STATUS_INVALID_MESSAGE
;
880 if (s
->flags
& ASL_FILE_FLAG_READ
) return ASL_STATUS_READ_ONLY
;
882 msg
= (asl_msg_t
*)in
;
884 memset(&r
, 0, sizeof(file_record_t
));
887 if ((mid
!= NULL
) && (*mid
!= 0)) r
.mid
= *mid
;
890 r
.level
= ASL_LEVEL_DEBUG
;
904 for (x
= asl_msg_fetch(msg
, 0, &key
, &val
, NULL
); x
!= IndexNull
; x
= asl_msg_fetch(msg
, x
, &key
, &val
, NULL
))
910 else if (!strcmp(key
, ASL_KEY_TIME
))
912 if (val
!= NULL
) r
.time
= asl_core_parse_time(val
, NULL
);
914 else if (!strcmp(key
, ASL_KEY_TIME_NSEC
))
916 if (val
!= NULL
) r
.nano
= atoi(val
);
918 else if (!strcmp(key
, ASL_KEY_HOST
))
922 status
= asl_file_string_encode(s
, val
, &(r
.host
));
923 if (status
!= ASL_STATUS_OK
)
930 else if (!strcmp(key
, ASL_KEY_SENDER
))
934 status
= asl_file_string_encode(s
, val
, &(r
.sender
));
935 if (status
!= ASL_STATUS_OK
)
942 else if (!strcmp(key
, ASL_KEY_PID
))
944 if (val
!= NULL
) r
.pid
= atoi(val
);
946 else if (!strcmp(key
, ASL_KEY_REF_PID
))
948 if (val
!= NULL
) r
.refpid
= atoi(val
);
950 else if (!strcmp(key
, ASL_KEY_UID
))
952 if (val
!= NULL
) r
.uid
= atoi(val
);
954 else if (!strcmp(key
, ASL_KEY_GID
))
956 if (val
!= NULL
) r
.gid
= atoi(val
);
958 else if (!strcmp(key
, ASL_KEY_LEVEL
))
960 if (val
!= NULL
) r
.level
= atoi(val
);
962 else if (!strcmp(key
, ASL_KEY_MSG
))
966 status
= asl_file_string_encode(s
, val
, &(r
.message
));
967 if (status
!= ASL_STATUS_OK
)
974 else if (!strcmp(key
, ASL_KEY_FACILITY
))
978 status
= asl_file_string_encode(s
, val
, &(r
.facility
));
979 if (status
!= ASL_STATUS_OK
)
986 else if (!strcmp(key
, ASL_KEY_REF_PROC
))
990 status
= asl_file_string_encode(s
, val
, &(r
.refproc
));
991 if (status
!= ASL_STATUS_OK
)
998 else if (!strcmp(key
, ASL_KEY_SESSION
))
1002 status
= asl_file_string_encode(s
, val
, &(r
.session
));
1003 if (status
!= ASL_STATUS_OK
)
1010 else if (!strcmp(key
, ASL_KEY_READ_UID
))
1012 if (((r
.flags
& ASL_MSG_FLAG_READ_UID_SET
) == 0) && (val
!= NULL
))
1015 r
.flags
|= ASL_MSG_FLAG_READ_UID_SET
;
1018 else if (!strcmp(key
, ASL_KEY_READ_GID
))
1020 if (((r
.flags
& ASL_MSG_FLAG_READ_GID_SET
) == 0) && (val
!= NULL
))
1023 r
.flags
|= ASL_MSG_FLAG_READ_GID_SET
;
1026 else if (!strcmp(key
, ASL_KEY_MSG_ID
))
1028 if (s
->flags
& ASL_FILE_FLAG_PRESERVE_MSG_ID
)
1031 if (mid
!= NULL
) *mid
= r
.mid
;
1034 else if (!strcmp(key
, ASL_KEY_OPTION
))
1036 /* ignore - we don't save ASLOption */
1040 status
= asl_file_string_encode(s
, key
, &k
);
1041 if (status
!= ASL_STATUS_OK
)
1050 status
= asl_file_string_encode(s
, val
, &v
);
1051 if (status
!= ASL_STATUS_OK
)
1058 if (r
.kvcount
>= KVSTACK_SIZE
)
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
;
1066 if (r
.kvcount
== KVSTACK_SIZE
)
1068 /* copy kvstack to kvmalloc */
1069 for (i
= 0; i
< KVSTACK_SIZE
; i
++) kvmalloc
[i
] = kvstack
[i
];
1073 kvlist
[r
.kvcount
++] = k
;
1074 kvlist
[r
.kvcount
++] = v
;
1078 len
= MSG_RECORD_FIXED_LENGTH
+ (r
.kvcount
* sizeof(uint64_t));
1081 /* use the scratch buffer if it exists and is large enough */
1082 if ((s
->scratch
!= NULL
) && (len
<= SCRATCH_BUFFER_SIZE
))
1084 memset(s
->scratch
, 0, SCRATCH_BUFFER_SIZE
);
1089 buf
= calloc(1, len
);
1092 if (buf
== NULL
) return ASL_STATUS_NO_MEMORY
;
1094 if (r
.mid
== UINT64_MAX
)
1096 s
->last_mid
= s
->last_mid
+ 1;
1097 r
.mid
= s
->last_mid
;
1103 _asl_put_16(ASL_FILE_TYPE_MSG
, p
);
1104 p
+= sizeof(uint16_t);
1106 /* Length of message (excludes type and length fields) */
1107 _asl_put_32(len
- RECORD_COMMON_LEN
, p
);
1108 p
+= sizeof(uint32_t);
1110 /* Message data... */
1112 _asl_put_64(r
.next
, p
);
1113 p
+= sizeof(uint64_t);
1115 _asl_put_64(r
.mid
, p
);
1116 p
+= sizeof(uint64_t);
1118 _asl_put_64(r
.time
, p
);
1119 p
+= sizeof(uint64_t);
1121 _asl_put_32(r
.nano
, p
);
1122 p
+= sizeof(uint32_t);
1124 _asl_put_16(r
.level
, p
);
1125 p
+= sizeof(uint16_t);
1127 _asl_put_16(r
.flags
, p
);
1128 p
+= sizeof(uint16_t);
1130 _asl_put_32(r
.pid
, p
);
1131 p
+= sizeof(uint32_t);
1133 _asl_put_32(r
.uid
, p
);
1134 p
+= sizeof(uint32_t);
1136 _asl_put_32(r
.gid
, p
);
1137 p
+= sizeof(uint32_t);
1139 _asl_put_32(r
.ruid
, p
);
1140 p
+= sizeof(uint32_t);
1142 _asl_put_32(r
.rgid
, p
);
1143 p
+= sizeof(uint32_t);
1145 _asl_put_32(r
.refpid
, p
);
1146 p
+= sizeof(uint32_t);
1148 _asl_put_32(r
.kvcount
, p
);
1149 p
+= sizeof(uint32_t);
1151 _asl_put_64(r
.host
, p
);
1152 p
+= sizeof(uint64_t);
1154 _asl_put_64(r
.sender
, p
);
1155 p
+= sizeof(uint64_t);
1157 _asl_put_64(r
.facility
, p
);
1158 p
+= sizeof(uint64_t);
1160 _asl_put_64(r
.message
, p
);
1161 p
+= sizeof(uint64_t);
1163 _asl_put_64(r
.refproc
, p
);
1164 p
+= sizeof(uint64_t);
1166 _asl_put_64(r
.session
, p
);
1167 p
+= sizeof(uint64_t);
1169 for (i
= 0; i
< r
.kvcount
; i
++)
1171 _asl_put_64(kvlist
[i
], p
);
1172 p
+= sizeof(uint64_t);
1175 _asl_put_64(r
.prev
, p
);
1176 p
+= sizeof(uint64_t);
1180 /* write record at end of file */
1181 status
= fseeko(s
->store
, 0, SEEK_END
);
1182 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
1184 s
->last
= (uint64_t)ftello(s
->store
);
1186 v
= asl_core_htonq(s
->last
);
1188 status
= fwrite(buf
, len
, 1, s
->store
);
1191 /* free the buffer if it was allocated here */
1192 if (buf
!= s
->scratch
) free(buf
);
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
;
1198 status
= fseeko(s
->store
, off
, SEEK_SET
);
1199 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
1201 status
= fwrite(&v
, sizeof(uint64_t), 1, s
->store
);
1202 if (status
!= 1) return ASL_STATUS_WRITE_FAILED
;
1204 /* seek to DB_HEADER_LAST_OFFSET, write last record offset */
1205 off
= DB_HEADER_LAST_OFFSET
;
1207 status
= fseeko(s
->store
, off
, SEEK_SET
);
1208 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
1210 status
= fwrite(&v
, sizeof(uint64_t), 1, s
->store
);
1211 if (status
!= 1) return ASL_STATUS_WRITE_FAILED
;
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
;
1220 s
->file_size
= (size_t)ftello(s
->store
);
1224 return ASL_STATUS_OK
;
1228 asl_file_fetch_object(asl_file_t
*s
, uint16_t fetch_type
, uint64_t where
, char **out
, uint32_t *outlen
)
1239 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1240 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
1241 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
1242 if (outlen
== NULL
) return ASL_STATUS_INVALID_ARG
;
1243 if (where
== 0) return ASL_STATUS_INVALID_ARG
;
1249 x64
= asl_core_htonq(where
);
1250 memcpy(&inls
, &x64
, 1);
1253 if (fetch_type
!= ASL_FILE_TYPE_STR
) return ASL_STATUS_INVALID_STORE
;
1257 if (inls
> 7) return ASL_STATUS_INVALID_STORE
;
1259 p
= 1 + (char *)&x64
;
1260 memset(ils
, 0, sizeof(ils
));
1261 memcpy(ils
, p
, inls
);
1263 if (*out
== NULL
) return ASL_STATUS_NO_MEMORY
;
1266 return ASL_STATUS_OK
;
1269 if (fetch_type
== ASL_FILE_TYPE_STR
)
1271 /* check the string cache */
1272 file_string_t
*sx
, *sp
;
1275 for (sx
= s
->string_list
; sx
!= NULL
; sx
= sx
->next
)
1277 if (sx
->where
== where
)
1279 *out
= strdup(sx
->str
);
1280 if (*out
== NULL
) return ASL_STATUS_NO_MEMORY
;
1282 /* N.B. hash field is used to hold length when reading */
1285 /* Move this string to the head of the list */
1288 file_string_t
*sl
= s
->string_list
;
1289 sp
->next
= sx
->next
;
1291 s
->string_list
= sx
;
1294 return ASL_STATUS_OK
;
1302 if ((off
+ sizeof(uint16_t) + sizeof(uint32_t)) > s
->file_size
) return ASL_STATUS_READ_FAILED
;
1304 status
= fseeko(s
->store
, off
, SEEK_SET
);
1305 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
1308 status
= fread(&type
, sizeof(uint16_t), 1, s
->store
);
1309 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
1311 off
+= sizeof(uint16_t);
1313 if (type
!= fetch_type
) return ASL_STATUS_INVALID_STORE
;
1317 status
= fread(&len
, sizeof(uint32_t), 1, s
->store
);
1318 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
1319 off
+= sizeof(uint32_t);
1322 if ((off
+ len
) > s
->file_size
) return ASL_STATUS_READ_FAILED
;
1324 *out
= calloc(1, len
);
1325 if (*out
== NULL
) return ASL_STATUS_NO_MEMORY
;
1327 status
= fread(*out
, len
, 1, s
->store
);
1332 return ASL_STATUS_READ_FAILED
;
1337 if ((fetch_type
== ASL_FILE_TYPE_STR
) && (len
<= CACHE_MAX_STRING_LEN
))
1339 file_string_t
*sx
= file_string_create(s
);
1344 /* N.B. hash field is used to hold length when reading */
1346 sx
->next
= s
->string_list
;
1347 memcpy(sx
->str
, *out
, len
);
1349 s
->string_list
= sx
;
1351 if (((s
->flags
& ASL_FILE_FLAG_UNLIMITED_CACHE
) == 0) && (s
->string_cache_count
== CACHE_SIZE
))
1353 /* drop last (lru) string from cache */
1354 file_string_t
*sp
= s
->string_list
;
1357 /* NB CACHE_SIZE must be > 1 */
1358 while (sx
->next
!= NULL
)
1365 file_string_dispose(s
, sx
);
1369 s
->string_cache_count
++;
1374 return ASL_STATUS_OK
;
1378 asl_file_fetch_helper_16(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
)
1383 out
= _asl_get_16(*p
);
1384 *p
+= sizeof(uint16_t);
1386 if ((m
== NULL
) || (key
== NULL
)) return out
;
1388 snprintf(str
, sizeof(str
), "%hu", out
);
1389 asl_msg_set_key_val(m
, key
, str
);
1395 asl_file_fetch_helper_32(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
, int ignore
, uint32_t ignoreval
)
1400 out
= _asl_get_32(*p
);
1401 *p
+= sizeof(uint32_t);
1403 if ((m
== NULL
) || (key
== NULL
)) return out
;
1406 if ((ignore
!= 0) && (out
== ignoreval
)) doit
= 0;
1409 snprintf(str
, sizeof(str
), "%u", out
);
1410 asl_msg_set_key_val(m
, key
, str
);
1417 asl_file_fetch_helper_64(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
)
1422 out
= _asl_get_64(*p
);
1423 *p
+= sizeof(uint64_t);
1425 if ((m
== NULL
) || (key
== NULL
)) return out
;
1427 snprintf(str
, sizeof(str
), "%llu", out
);
1428 asl_msg_set_key_val(m
, key
, str
);
1434 asl_file_fetch_helper_str(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
, uint32_t *err
)
1438 uint32_t status
, len
;
1440 out
= _asl_get_64(*p
);
1441 *p
+= sizeof(uint64_t);
1445 status
= ASL_STATUS_OK
;
1446 if (out
!= 0) status
= asl_file_fetch_object(s
, ASL_FILE_TYPE_STR
, out
, &val
, &len
);
1448 if (err
!= NULL
) *err
= status
;
1449 if ((status
== ASL_STATUS_OK
) && (val
!= NULL
))
1451 asl_msg_set_key_val(m
, key
, val
);
1459 asl_file_fetch_pos(asl_file_t
*s
, uint64_t where
, int dir
, asl_msg_t
**msg
)
1461 char *buf
, *p
, *k
, *v
;
1463 uint32_t i
, status
, len
, buflen
, kvn
;
1468 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1469 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
1470 if (msg
== NULL
) return ASL_STATUS_INVALID_ARG
;
1471 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
1475 status
= asl_file_fetch_object(s
, ASL_FILE_TYPE_MSG
, where
, &buf
, &buflen
);
1476 if ((status
!= ASL_STATUS_OK
) || (buf
== NULL
))
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))))
1490 return ASL_STATUS_READ_FAILED
;
1493 out
= asl_msg_new(ASL_TYPE_MSG
);
1497 return ASL_STATUS_NO_MEMORY
;
1500 memset(&r
, 0, sizeof(file_record_t
));
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);
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 */
1525 if (status
!= ASL_STATUS_OK
)
1527 asl_msg_release(out
);
1534 kvn
= r
.kvcount
/ 2;
1536 for (i
= 0; i
< kvn
; i
++)
1542 kv
= _asl_get_64(p
);
1543 p
+= sizeof(uint64_t);
1545 status
= asl_file_fetch_object(s
, ASL_FILE_TYPE_STR
, kv
, &k
, &len
);
1546 if (status
!= ASL_STATUS_OK
)
1548 asl_msg_release(out
);
1555 kv
= _asl_get_64(p
);
1556 p
+= sizeof(uint64_t);
1561 status
= asl_file_fetch_object(s
, ASL_FILE_TYPE_STR
, kv
, &v
, &len
);
1562 if (status
!= ASL_STATUS_OK
)
1565 asl_msg_release(out
);
1573 if ((status
== ASL_STATUS_OK
) && (k
!= NULL
))
1575 asl_msg_set_key_val(out
, k
, v
);
1582 r
.prev
= asl_file_fetch_helper_64(s
, &p
, NULL
, NULL
); /* 116 */
1588 if ((r
.next
!= 0) && (r
.next
<= s
->cursor
))
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.
1597 return ASL_STATUS_OK
;
1604 if ((r
.prev
!= 0) && (r
.prev
>= s
->cursor
))
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.
1613 return ASL_STATUS_OK
;
1623 off
= s
->cursor
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1624 if (off
> s
->file_size
)
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.
1634 return ASL_STATUS_OK
;
1637 status
= fseeko(s
->store
, off
, SEEK_SET
);
1640 asl_msg_release(out
);
1643 return ASL_STATUS_READ_FAILED
;
1646 status
= fread(&x64
, sizeof(uint64_t), 1, s
->store
);
1649 asl_msg_release(out
);
1652 return ASL_STATUS_READ_FAILED
;
1655 s
->cursor_xid
= asl_core_ntohq(x64
);
1659 return ASL_STATUS_OK
;
1663 asl_file_open_read(const char *path
, asl_file_t
**s
)
1668 uint32_t status
, vers
, last_len
;
1669 char buf
[DB_HEADER_LEN
];
1671 asl_legacy1_t
*legacy
;
1674 memset(&sb
, 0, sizeof(struct stat
));
1675 if (stat(path
, &sb
) != 0) return ASL_STATUS_FAILED
;
1677 f
= fopen(path
, "r");
1680 if (errno
== EACCES
) return ASL_STATUS_ACCESS_DENIED
;
1681 return ASL_STATUS_FAILED
;
1684 i
= fread(buf
, DB_HEADER_LEN
, 1, f
);
1688 return ASL_STATUS_INVALID_STORE
;
1691 /* validate header */
1692 if (strncmp(buf
, ASL_DB_COOKIE
, ASL_DB_COOKIE_LEN
))
1695 return ASL_STATUS_INVALID_STORE
;
1700 vers
= _asl_get_32(buf
+ DB_HEADER_VERS_OFFSET
);
1701 if (vers
== DB_VERSION_LEGACY_1
)
1704 status
= asl_legacy1_open(path
, &legacy
);
1705 if (status
!= ASL_STATUS_OK
) return status
;
1708 out
= (asl_file_t
*)calloc(1, sizeof(asl_file_t
));
1712 return ASL_STATUS_NO_MEMORY
;
1715 out
->asl_type
= ASL_TYPE_FILE
;
1719 out
->flags
= ASL_FILE_FLAG_READ
;
1720 out
->version
= vers
;
1724 out
->flags
|= ASL_FILE_FLAG_LEGACY_STORE
;
1725 out
->legacy
= (void *)legacy
;
1728 return ASL_STATUS_OK
;
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
;
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.
1743 /* records are at least MSG_RECORD_FIXED_LENGTH bytes */
1744 if ((out
->last
+ MSG_RECORD_FIXED_LENGTH
) > out
->file_size
)
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
)
1760 if ((out
->last
+ last_len
) > out
->file_size
) out
->last
= 0;
1763 out
->cursor
= out
->first
;
1764 if (out
->cursor
!= 0)
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
)
1777 return ASL_STATUS_OK
;
1781 asl_file_read_set_position_first(asl_file_t
*s
)
1786 s
->cursor
= s
->first
;
1789 if (s
->cursor
== 0) return ASL_STATUS_OK
;
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
));
1798 asl_file_read_set_position_last(asl_file_t
*s
, int do_count
)
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.
1810 * Note that s->last may be zero if the file is empty.
1813 if ((s
->last
!= 0) && (do_count
== 0))
1815 s
->cursor
= s
->last
;
1816 off
= s
->last
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1818 /* read ID of the last record */
1819 status
= asl_file_read_uint64(s
, off
, &(s
->cursor_xid
));
1823 /* start at the first record and iterate */
1824 s
->cursor
= s
->first
;
1830 off
= s
->cursor
+ RECORD_COMMON_LEN
;
1833 /* read next offset */
1834 status
= asl_file_read_uint64(s
, off
, &next
);
1835 if (status
!= ASL_STATUS_OK
) return status
;
1837 /* detect bogus next pointer */
1838 if (((next
+ MSG_RECORD_FIXED_LENGTH
) > s
->file_size
) || (next
<= s
->cursor
)) next
= 0;
1844 if (s
->cursor
== 0) return ASL_STATUS_OK
;
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
;
1856 asl_file_read_set_position(asl_file_t
*s
, uint32_t pos
)
1859 uint32_t len
, status
;
1862 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1863 if (s
->version
== 1) return ASL_STATUS_FAILED
;
1865 if (pos
== ASL_FILE_POSITION_FIRST
) return asl_file_read_set_position_first(s
);
1866 if (pos
== ASL_FILE_POSITION_LAST
) return asl_file_read_set_position_last(s
, 0);
1870 if (pos
== ASL_FILE_POSITION_PREVIOUS
)
1872 if (s
->cursor
== s
->first
) return ASL_STATUS_NO_RECORDS
;
1873 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
1875 off
= s
->cursor
+ RECORD_TYPE_LEN
;
1876 status
= asl_file_read_uint32(s
, off
, &len
);
1877 if (status
!= ASL_STATUS_OK
) return status
;
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);
1882 else if (pos
== ASL_FILE_POSITION_NEXT
)
1884 if (s
->cursor
== s
->last
) return ASL_STATUS_NO_RECORDS
;
1885 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
1887 /* set offset to read the "next" field in the current record */
1888 off
= s
->cursor
+ RECORD_COMMON_LEN
;
1890 else return ASL_STATUS_INVALID_ARG
;
1895 * read offset of next / previous
1898 status
= asl_file_read_uint64(s
, off
, &next
);
1899 if (status
!= ASL_STATUS_OK
) return ASL_STATUS_READ_FAILED
;
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;
1907 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
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
));
1916 asl_file_fetch_next(asl_file_t
*s
, asl_msg_t
**msg
)
1918 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1919 if (s
->version
== 1) return ASL_STATUS_FAILED
;
1921 return asl_file_fetch_pos(s
, s
->cursor
, 1, msg
);
1925 asl_file_fetch_previous(asl_file_t
*s
, asl_msg_t
**msg
)
1927 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1928 if (s
->version
== 1) return ASL_STATUS_FAILED
;
1930 return asl_file_fetch_pos(s
, s
->cursor
, -1, msg
);
1934 asl_file_fetch(asl_file_t
*s
, uint64_t mid
, asl_msg_t
**msg
)
1938 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1939 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
1941 if (s
->version
== 1)
1943 if (msg
== NULL
) return ASL_STATUS_OK
;
1944 return asl_legacy1_fetch((asl_legacy1_t
*)s
->legacy
, mid
, msg
);
1947 if (s
->cursor_xid
== 0)
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
;
1954 while (s
->cursor_xid
< mid
)
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
;
1962 while (s
->cursor_xid
> mid
)
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
;
1970 if (s
->cursor_xid
!= mid
) return ASL_STATUS_INVALID_ID
;
1972 if (msg
== NULL
) return ASL_STATUS_OK
;
1973 return asl_file_fetch_pos(s
, s
->cursor
, 1, msg
);
1976 __private_extern__
uint64_t
1977 asl_file_cursor(asl_file_t
*s
)
1979 if (s
== NULL
) return 0;
1980 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return 0;
1981 if (s
->version
== 1) return 0;
1983 return s
->cursor_xid
;
1986 __private_extern__ ASL_STATUS
1987 asl_file_match_start(asl_file_t
*s
, uint64_t start
, int32_t direction
)
1991 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1992 if (s
->version
== 1) return ASL_STATUS_INVALID_STORE
;
1993 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
1995 d
= ASL_FILE_POSITION_NEXT
;
1996 if (direction
< 0) d
= ASL_FILE_POSITION_PREVIOUS
;
1999 * find starting point
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
;
2006 while ((status
== ASL_STATUS_OK
) && (((direction
>= 0) && (s
->cursor_xid
< start
)) || ((direction
< 0) && (s
->cursor_xid
> start
))))
2008 status
= asl_file_read_set_position(s
, d
);
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
)
2017 uint32_t status
, d
, i
, do_match
, did_match
;
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
;
2023 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
2024 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
2029 if (query
== NULL
) do_match
= 0;
2030 else if (query
->count
== 0) do_match
= 0;
2032 d
= ASL_FILE_POSITION_NEXT
;
2033 if (direction
< 0) d
= ASL_FILE_POSITION_PREVIOUS
;
2037 *last
= s
->cursor_xid
;
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
;
2050 for (i
= 0; (i
< query
->count
) && (did_match
== 0); i
++)
2052 did_match
= asl_msg_cmp(query
->msg
[i
], m
);
2059 return ASL_STATUS_OK
;
2064 return ASL_STATUS_MATCH_FAILED
;
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
)
2070 uint32_t status
, d
, i
, do_match
, did_match
, rescount
;
2072 struct timeval now
, finish
;
2073 asl_msg_list_t
*out
= NULL
;
2075 if (s
== NULL
) return NULL
;
2076 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return NULL
;
2078 if (s
->version
== 1)
2080 asl_legacy1_match((asl_legacy1_t
*)s
->legacy
, qlist
, &out
, last
, start
, count
, direction
);
2085 if (qlist
== NULL
) do_match
= 0;
2086 else if (qlist
->count
== 0) do_match
= 0;
2090 d
= ASL_FILE_POSITION_NEXT
;
2091 if (direction
< 0) d
= ASL_FILE_POSITION_PREVIOUS
;
2094 * find starting point
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
);
2099 if (status
!= ASL_STATUS_OK
) return NULL
;
2101 while ((status
== ASL_STATUS_OK
) && (((direction
>= 0) && (s
->cursor_xid
< start
)) || ((direction
< 0) && (s
->cursor_xid
> start
))))
2103 status
= asl_file_read_set_position(s
, d
);
2106 /* start the timer if a duration was specified */
2107 memset(&finish
, 0, sizeof(struct timeval
));
2110 if (gettimeofday(&finish
, NULL
) == 0)
2112 finish
.tv_sec
+= (duration
/ USEC_PER_SEC
);
2113 finish
.tv_usec
+= (duration
% USEC_PER_SEC
);
2114 if (finish
.tv_usec
> USEC_PER_SEC
)
2116 finish
.tv_usec
-= USEC_PER_SEC
;
2122 /* shouldn't happen, but if gettimeofday failed we just run without a timeout */
2123 memset(&finish
, 0, sizeof(struct timeval
));
2128 * loop through records
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;
2137 *last
= s
->cursor_xid
;
2145 for (i
= 0; (i
< qlist
->count
) && (did_match
== 0); i
++)
2147 did_match
= asl_msg_cmp(qlist
->msg
[i
], m
);
2153 /* append m to res */
2156 out
= asl_msg_list_new();
2157 if (out
== NULL
) return NULL
;
2160 asl_msg_list_append(out
, m
);
2162 if ((count
!= 0) && (rescount
>= count
)) break;
2164 /* check the timer */
2165 if ((finish
.tv_sec
!= 0) && (gettimeofday(&now
, NULL
) == 0))
2167 if ((now
.tv_sec
> finish
.tv_sec
) || ((now
.tv_sec
== finish
.tv_sec
) && (now
.tv_usec
> finish
.tv_usec
))) break;
2178 asl_file_size(asl_file_t
*s
)
2180 if (s
== NULL
) return 0;
2181 return s
->file_size
;
2185 asl_file_ctime(asl_file_t
*s
)
2187 if (s
== NULL
) return 0;
2192 asl_file_list_close(asl_file_list_t
*head
)
2194 asl_file_list_t
*next
;
2196 while (head
!= NULL
)
2199 asl_file_close(head
->file
);
2206 asl_file_list_free(asl_file_list_t
*head
)
2208 asl_file_list_t
*next
;
2210 while (head
!= NULL
)
2218 static asl_file_list_t
*
2219 asl_file_list_insert(asl_file_list_t
*list
, asl_file_t
*f
, int32_t dir
)
2221 asl_file_list_t
*a
, *b
, *tmp
;
2223 if (f
== NULL
) return list
;
2225 tmp
= (asl_file_list_t
*)calloc(1, sizeof(asl_file_list_t
));
2226 if (tmp
== NULL
) return NULL
;
2229 if (list
== NULL
) return tmp
;
2232 if (((dir
< 0) && (f
->cursor_xid
> a
->file
->cursor_xid
)) || ((dir
>= 0) && (f
->cursor_xid
< a
->file
->cursor_xid
)))
2241 if (((dir
< 0) && (f
->cursor_xid
> b
->file
->cursor_xid
)) || ((dir
>= 0) && (f
->cursor_xid
< b
->file
->cursor_xid
)))
2257 asl_file_list_add(asl_file_list_t
*list
, asl_file_t
*f
)
2259 asl_file_list_t
*tmp
;
2261 if (f
== NULL
) return list
;
2262 if (f
->version
== 1) return list
;
2264 tmp
= (asl_file_list_t
*)calloc(1, sizeof(asl_file_list_t
));
2265 if (tmp
== NULL
) return NULL
;
2273 asl_file_list_match_start(asl_file_list_t
*list
, uint64_t start
, int32_t direction
)
2277 asl_file_match_token_t
*out
;
2279 if (list
== NULL
) return NULL
;
2281 out
= (asl_file_match_token_t
*)calloc(1, sizeof(asl_file_match_token_t
));
2282 if (out
== NULL
) return NULL
;
2284 for (n
= list
; n
!= NULL
; n
= n
->next
)
2286 /* init file for the search */
2287 status
= asl_file_match_start(n
->file
, start
, direction
);
2288 if (status
!= ASL_STATUS_OK
) continue;
2289 if (n
->file
->cursor_xid
== 0) continue;
2291 out
->list
= asl_file_list_insert(out
->list
, n
->file
, direction
);
2294 out
->dir
= direction
;
2299 asl_file_list_match_next(void *token
, asl_msg_list_t
*qlist
, asl_msg_list_t
**res
, uint32_t count
)
2301 uint32_t status
, rescount
;
2304 asl_file_match_token_t
*work
;
2307 if (token
== NULL
) return ASL_STATUS_OK
;
2308 if (res
== NULL
) return ASL_STATUS_INVALID_ARG
;
2310 work
= (asl_file_match_token_t
*)token
;
2315 while ((work
->list
!= NULL
) && ((rescount
< count
) || (count
== 0)))
2318 status
= asl_file_match_next(work
->list
->file
, qlist
, &m
, &last
, work
->dir
);
2321 if (*res
== NULL
) *res
= asl_msg_list_new();
2324 asl_file_list_free(work
->list
);
2326 return ASL_STATUS_NO_MEMORY
;
2329 asl_msg_list_append(*res
, m
);
2334 if ((status
!= ASL_STATUS_OK
) || (work
->list
->file
->cursor_xid
== 0))
2336 n
= work
->list
->next
;
2341 if (work
->list
!= NULL
)
2343 n
= work
->list
->next
;
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
)))
2349 work
->list
= work
->list
->next
;
2351 work
->list
= asl_file_list_insert(work
->list
, n
->file
, work
->dir
);
2358 return ASL_STATUS_OK
;
2362 asl_file_list_match_end(void *token
)
2364 asl_file_match_token_t
*work
;
2366 if (token
== NULL
) return;
2368 work
= (asl_file_match_token_t
*)token
;
2369 asl_file_list_free(work
->list
);
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
)
2378 uint32_t status
, rescount
;
2379 asl_file_list_t
*files
, *n
;
2381 struct timeval now
, finish
;
2382 asl_msg_list_t
*out
= NULL
;
2384 if (list
== NULL
) return NULL
;
2385 if (last
== NULL
) return NULL
;
2389 for (n
= list
; n
!= NULL
; n
= n
->next
)
2391 /* init file for the search */
2392 status
= asl_file_match_start(n
->file
, start
, direction
);
2393 if (status
!= ASL_STATUS_OK
) continue;
2394 if (n
->file
->cursor_xid
== 0) continue;
2396 files
= asl_file_list_insert(files
, n
->file
, direction
);
2401 asl_file_list_free(files
);
2405 /* start the timer if a timeout was specified */
2406 memset(&finish
, 0, sizeof(struct timeval
));
2409 if (gettimeofday(&finish
, NULL
) == 0)
2411 finish
.tv_sec
+= (duration
/ USEC_PER_SEC
);
2412 finish
.tv_usec
+= (duration
% USEC_PER_SEC
);
2413 if (finish
.tv_usec
> USEC_PER_SEC
)
2415 finish
.tv_usec
-= USEC_PER_SEC
;
2421 /* shouldn't happen, but if gettimeofday failed we just run without a timeout */
2422 memset(&finish
, 0, sizeof(struct timeval
));
2427 while ((files
!= NULL
) && ((rescount
< count
) || (count
== 0)))
2430 status
= asl_file_match_next(files
->file
, qlist
, &m
, last
, direction
);
2433 if (out
== NULL
) out
= asl_msg_list_new();
2436 asl_file_list_free(files
);
2440 asl_msg_list_append(out
, m
);
2445 if (files
->file
->cursor_xid
== 0)
2457 if (((direction
< 0) && (files
->file
->cursor_xid
<= n
->file
->cursor_xid
)) || ((direction
>= 0) && (files
->file
->cursor_xid
> n
->file
->cursor_xid
)))
2460 files
= files
->next
;
2462 files
= asl_file_list_insert(files
, n
->file
, direction
);
2468 /* check the timer */
2469 if ((finish
.tv_sec
!= 0) && (gettimeofday(&now
, NULL
) == 0))
2471 if ((now
.tv_sec
> finish
.tv_sec
) || ((now
.tv_sec
== finish
.tv_sec
) && (now
.tv_usec
> finish
.tv_usec
))) break;
2475 asl_file_list_free(files
);
2480 #pragma mark asl_object support
2483 _jump_dealloc(asl_object_private_t
*obj
)
2485 _asl_file_free_internal((asl_file_t
*)obj
);
2489 _jump_count(asl_object_private_t
*obj
)
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;
2495 uint64_t cursor
= s
->cursor
;
2496 uint64_t cursor_xid
= s
->cursor_xid
;
2498 if (asl_file_read_set_position_last((asl_file_t
*)obj
, 1) != ASL_STATUS_OK
) return 0;
2501 s
->cursor_xid
= cursor_xid
;
2502 return s
->msg_count
;
2505 static asl_object_private_t
*
2506 _jump_next(asl_object_private_t
*obj
)
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
;
2513 static asl_object_private_t
*
2514 _jump_prev(asl_object_private_t
*obj
)
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
;
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
)
2525 asl_msg_t
*msg
= NULL
;
2527 if (asl_file_fetch((asl_file_t
*)obj
, mid
, &msg
) != ASL_STATUS_OK
) return NULL
;
2528 return (asl_object_private_t
*)msg
;
2532 _jump_set_iteration_index(asl_object_private_t
*obj
, size_t n
)
2534 asl_file_t
*s
= (asl_file_t
*)obj
;
2535 if (s
== NULL
) return;
2536 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return;
2540 asl_file_read_set_position_first(s
);
2542 else if (n
== SIZE_MAX
)
2544 asl_file_read_set_position_last(s
, 0);
2548 /* we don't really need to support this, but we are generous */
2549 asl_file_fetch(s
, n
, NULL
);
2554 _jump_append(asl_object_private_t
*obj
, asl_object_private_t
*newobj
, void *addr
)
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;
2562 if (type
== ASL_TYPE_LIST
)
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
)))
2568 if (asl_file_save(s
, msg
, &xid
) != ASL_STATUS_OK
) return;
2571 else if ((type
== ASL_TYPE_MSG
) || (type
== ASL_TYPE_QUERY
))
2573 asl_file_save(s
, (asl_msg_t
*)newobj
, &xid
);
2577 static asl_object_private_t
*
2578 _jump_search(asl_object_private_t
*obj
, asl_object_private_t
*query
)
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
;
2588 return (asl_object_private_t
*)asl_file_match(s
, NULL
, &last
, 0, 0, 0, 1);
2590 else if (type
== ASL_TYPE_LIST
)
2592 return (asl_object_private_t
*)asl_file_match(s
, (asl_msg_list_t
*)query
, &last
, 0, 0, 0, 1);
2594 else if ((type
== ASL_TYPE_MSG
) || (type
== ASL_TYPE_QUERY
))
2596 ql
= asl_msg_list_new();
2597 asl_msg_list_append(ql
, query
);
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
;
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
)
2611 asl_msg_list_t
*out
= asl_file_match((asl_file_t
*)obj
, (asl_msg_list_t
*)qlist
, &x
, start
, count
, duration
, dir
);
2613 return (asl_object_private_t
*)out
;
2616 __private_extern__
const asl_jump_table_t
*
2617 asl_file_jump_table()
2619 static const asl_jump_table_t jump
=
2622 .dealloc
= &_jump_dealloc
,
2623 .set_key_val_op
= 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
,
2635 .search
= &_jump_search
,
2636 .match
= &_jump_match