+__private_extern__ int
+asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode)
+{
+ acl_t acl;
+ uuid_t uuid;
+ acl_entry_t entry;
+ acl_permset_t perms;
+ int status;
+ int fd = -1;
+
+ acl = acl_init(1);
+
+ if (gid != 0)
+ {
+ status = mbr_gid_to_uuid(gid, uuid);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_set_qualifier(entry, &uuid);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_get_permset(entry, &perms);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_add_perm(perms, ACL_READ_DATA);
+ if (status != 0) goto asl_file_create_return;
+ }
+
+ if (uid != 0)
+ {
+ status = mbr_uid_to_uuid(uid, uuid);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_set_qualifier(entry, &uuid);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_get_permset(entry, &perms);
+ if (status != 0) goto asl_file_create_return;
+
+ status = acl_add_perm(perms, ACL_READ_DATA);
+ if (status != 0) goto asl_file_create_return;
+ }
+
+ fd = open(path, O_RDWR | O_CREAT | O_EXCL, mode);
+ if (fd < 0) goto asl_file_create_return;
+
+ status = acl_set_fd(fd, acl);
+ if (status != 0)
+ {
+ close(fd);
+ fd = -1;
+ unlink(path);
+ }
+
+asl_file_create_return:
+
+ acl_free(acl);
+ return fd;
+}
+
+uint32_t
+asl_file_open_write(const char *path, mode_t mode, uid_t uid, gid_t gid, asl_file_t **s)
+{
+ int i, status, fd;
+ struct stat sb;
+ char buf[DB_HEADER_LEN];
+ asl_file_t *out;
+ uint32_t aslstatus, vers, last_len;
+ off_t off;
+
+ memset(&sb, 0, sizeof(struct stat));
+
+ status = stat(path, &sb);
+ if (status == 0)
+ {
+ /* must be a plain file */
+ if (!S_ISREG(sb.st_mode)) return ASL_STATUS_INVALID_STORE;
+
+ if (sb.st_size == 0)
+ {
+ fd = open(path, O_RDWR | O_EXCL, mode);
+ if (fd < 0) return ASL_STATUS_FAILED;
+
+ return asl_file_open_write_fd(fd, s);
+ }
+ else
+ {
+ /* XXX Check that mode, uid, and gid are correct */
+ out = (asl_file_t *)calloc(1, sizeof(asl_file_t));
+ if (out == NULL) return ASL_STATUS_NO_MEMORY;
+
+ out->store = fopen(path, "r+");
+ if (out->store == NULL)
+ {
+ free(out);
+ return ASL_STATUS_FAILED;
+ }
+
+ i = fread(buf, DB_HEADER_LEN, 1, out->store);
+ if (i < 1)
+ {
+ asl_file_close(out);
+ return ASL_STATUS_READ_FAILED;
+ }
+
+ /* check cookie */
+ if (strncmp(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN))
+ {
+ asl_file_close(out);
+ return ASL_STATUS_INVALID_STORE;
+ }
+
+ /* check version */
+ vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET);
+ if (vers != DB_VERSION)
+ {
+ asl_file_close(out);
+ return ASL_STATUS_INVALID_STORE;
+ }
+
+ out->dob = _asl_get_64(buf + DB_HEADER_TIME_OFFSET);
+ out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET);
+ out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET);
+ out->file_size = (size_t)sb.st_size;
+
+ /*
+ * Detect bogus last pointer and check for odd-sized files.
+ * Setting out->last to zero forces asl_file_read_set_position to
+ * follow the linked list of records in the file to the last record.
+ * It's slower, but it's better at preventing crashes in corrupt files.
+ */
+
+ /* records are at least MSG_RECORD_FIXED_LENGTH bytes */
+ if ((out->last + MSG_RECORD_FIXED_LENGTH) > out->file_size)
+ {
+ out->last = 0;
+ }
+ else
+ {
+ /* read last record length and make sure the file is at least that large */
+ off = out->last + RECORD_TYPE_LEN;
+ status = asl_file_read_uint32(out, off, &last_len);
+ if (status != ASL_STATUS_OK)
+ {
+ asl_file_close(out);
+ return status;
+ }
+
+ if ((out->last + last_len) > out->file_size) out->last = 0;
+ }
+
+ aslstatus = asl_file_read_set_position(out, ASL_FILE_POSITION_LAST);
+ if (aslstatus != ASL_STATUS_OK)
+ {
+ asl_file_close(out);
+ return aslstatus;
+ }
+
+ out->prev = out->cursor;
+ status = fseeko(out->store, 0, SEEK_END);
+ if (status != 0)
+ {
+ asl_file_close(out);
+ return ASL_STATUS_READ_FAILED;
+ }
+
+ out->file_size = (size_t)ftello(out->store);
+
+ /* scratch buffer for file writes (we test for NULL before using it) */
+ out->scratch = malloc(SCRATCH_BUFFER_SIZE);
+
+ *s = out;
+
+ return ASL_STATUS_OK;
+ }
+ }
+ else if (errno != ENOENT)
+ {
+ /* unexpected status */
+ return ASL_STATUS_FAILED;
+ }
+
+ /* the file does not exist */
+ fd = asl_file_create(path, uid, gid, mode);
+ if (fd < 0) return ASL_STATUS_FAILED;
+
+ aslstatus = asl_file_open_write_fd(fd, s);
+ if (aslstatus != ASL_STATUS_OK) unlink(path);
+
+ return aslstatus;
+}
+