/* * Copyright (c) Likewise Software. All rights Reserved. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the license, or (at * your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. You should have received a copy * of the GNU Lesser General Public License along with this program. If * not, see . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Module Name: * * archive-format.c * * Abstract: * * Archive API * Data file format functions * * Authors: Brian Koropoff (bkoropoff@likewisesoftware.com) * */ #include "archive-private.h" #include "util-private.h" #include "convert.h" #include #include #include static LWMsgStatus lwmsg_archive_write_fd( LWMsgArchive* archive, const void* data, size_t size ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; const char* cursor = (const char*) data; size_t remaining = size; ssize_t written = 0; while (remaining) { do { written = write(archive->fd, cursor, remaining); } while (written < 0 && (errno == EINTR || errno == EAGAIN)); if (written < 0) { BAIL_ON_ERROR(status = lwmsg_error_raise_errno(&archive->base.context.error, errno)); } remaining -= written; cursor += written; archive->offset += written; } error: return status; } static LWMsgStatus lwmsg_archive_read_fd( LWMsgArchive* archive, void* buffer, size_t size, size_t* bytes_read ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; char* cursor = (char*) buffer; size_t remaining = size; ssize_t count = 0; size_t total = 0; while (remaining) { do { count = read(archive->fd, cursor, remaining); } while (count < 0 && (errno == EINTR || errno == EAGAIN)); if (count < 0) { BAIL_ON_ERROR(status = lwmsg_error_raise_errno(&archive->base.context.error, errno)); } else if (count == 0) { if (remaining == size) { /* We didn't read anything */ BAIL_ON_ERROR(status = LWMSG_STATUS_EOF); } else { /* Short but successful read */ break; } } remaining -= count; cursor += count; archive->offset += count; total += count; } *bytes_read = total; error: return status; } static LWMsgStatus lwmsg_archive_seek_fd( LWMsgArchive* archive, off_t position ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; off_t new_position = 0; if ((new_position = lseek(archive->fd, position, SEEK_SET)) == (off_t) -1) { BAIL_ON_ERROR(status = lwmsg_error_raise_errno(&archive->base.context.error, errno)); } archive->offset = new_position; error: return status; } static void lwmsg_archive_populate_header( LWMsgArchive* archive, ArchiveHeader* header ) { memcpy(header->magic, "LWMA", sizeof(header->magic)); header->version_flags = 0; if (archive->byte_order == LWMSG_BIG_ENDIAN) { header->version_flags |= ARCHIVE_VERSION_FLAG_BIG_ENDIAN; } header->version_major = ARCHIVE_VERSION_MAJOR; header->version_minor = ARCHIVE_VERSION_MINOR; header->version_micro = ARCHIVE_VERSION_MICRO; header->format_flags = 0; memset(header->protocol_id, 0, sizeof(header->protocol_id)); } LWMsgStatus lwmsg_archive_write_header_fd( LWMsgArchive* archive ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; ArchiveHeader header; lwmsg_archive_populate_header(archive, &header); BAIL_ON_ERROR(status = lwmsg_archive_write_fd(archive, &header, sizeof(header))); error: return status; } static void lwmsg_archive_populate_message_header( LWMsgArchive* archive, LWMsgMessage* message, size_t data_size, ArchiveMessage* header ) { header->flags = LWMSG_SWAP16((uint16_t) message->flags, LWMSG_NATIVE_ENDIAN, archive->byte_order); header->status = LWMSG_SWAP32((uint32_t) message->status, LWMSG_NATIVE_ENDIAN, archive->byte_order); header->cookie = LWMSG_SWAP16((uint16_t) message->cookie, LWMSG_NATIVE_ENDIAN, archive->byte_order); header->tag = LWMSG_SWAP16((int16_t) message->tag, LWMSG_NATIVE_ENDIAN, archive->byte_order); header->size = LWMSG_SWAP32((uint32_t) data_size, LWMSG_NATIVE_ENDIAN, archive->byte_order); } static LWMsgStatus lwmsg_archive_write_message_wrap_fd( LWMsgBuffer* buffer, size_t needed ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; LWMsgArchive* archive = buffer->data; /* Flush data in the buffer to file */ BAIL_ON_ERROR(status = lwmsg_archive_write_fd(archive, buffer->base, (size_t) (buffer->cursor - buffer->base))); buffer->cursor = buffer->base; error: return status; } LWMsgStatus lwmsg_archive_write_message_fd( LWMsgArchive* archive, LWMsgMessage* message ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; unsigned char data[2048]; ArchiveMessage header = {0}; LWMsgBuffer buffer = {0}; LWMsgTypeSpec* type = NULL; off_t header_offset = archive->offset; off_t end_offset = 0; BAIL_ON_ERROR(status = lwmsg_protocol_get_message_type(archive->base.prot, message->tag, &type)); buffer.base = data; buffer.end = data + sizeof(data); buffer.cursor = buffer.base; buffer.wrap = lwmsg_archive_write_message_wrap_fd; buffer.data = archive; /* Leave room to write the header in later */ BAIL_ON_ERROR(status = lwmsg_archive_seek_fd(archive, header_offset + sizeof(header))); /* Write the marshaled data payload */ BAIL_ON_ERROR(status = lwmsg_data_marshal(archive->data_context, type, message->data, &buffer)); end_offset = archive->offset; /* Go back and write the header */ BAIL_ON_ERROR(status = lwmsg_archive_seek_fd(archive, header_offset)); lwmsg_archive_populate_message_header(archive, message, end_offset - header_offset - sizeof(header), &header); BAIL_ON_ERROR(status = lwmsg_archive_write_fd(archive, &header, sizeof(header))); /* Seek back to end of message stream */ BAIL_ON_ERROR(status = lwmsg_archive_seek_fd(archive, end_offset)); error: return status; } LWMsgStatus lwmsg_archive_read_header_fd( LWMsgArchive* archive ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; ArchiveHeader header; size_t count = 0; BAIL_ON_ERROR(status = lwmsg_archive_read_fd(archive, &header, sizeof(header), &count)); if (count < sizeof(header) || memcmp(header.magic, "LWMA", 4) != 0) { BAIL_ON_ERROR(status = LWMSG_STATUS_MALFORMED); } if (header.version_major > ARCHIVE_VERSION_MAJOR || header.version_minor > ARCHIVE_VERSION_MINOR) { BAIL_ON_ERROR(status = LWMSG_STATUS_MALFORMED); } archive->version_major = header.version_major; archive->version_minor = header.version_minor; if (header.version_flags & ARCHIVE_VERSION_FLAG_BIG_ENDIAN) { archive->byte_order = LWMSG_BIG_ENDIAN; } else { archive->byte_order = LWMSG_LITTLE_ENDIAN; } lwmsg_data_context_set_byte_order(archive->data_context, archive->byte_order); error: return status; } typedef struct readinfo { LWMsgArchive* archive; unsigned char data[2048]; size_t remaining; } readinfo; static LWMsgStatus lwmsg_archive_read_message_wrap_fd ( LWMsgBuffer* buffer, size_t needed ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; readinfo* info = buffer->data; LWMsgArchive* archive = info->archive; size_t count = 0; if (needed) { /* Read next block of data from file */ if (info->remaining == 0) { /* The message was longer than the length specified in the header */ BAIL_ON_ERROR(status = LWMSG_STATUS_MALFORMED); } else { BAIL_ON_ERROR(status = lwmsg_archive_read_fd( archive, info->data, info->remaining > sizeof(info->data) ? sizeof(info->data) : info->remaining, &count)); info->remaining -= count; buffer->end = buffer->base + count; buffer->cursor = buffer->base; } } else { if (info->remaining != 0) { /* The message was shorter than the length specified in the header */ BAIL_ON_ERROR(status = LWMSG_STATUS_MALFORMED); } } error: return status; } static LWMsgStatus lwmsg_archive_read_message_header( LWMsgArchive* archive, LWMsgMessage* message, size_t* size ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; ArchiveMessage_v0 header_v0 = {0}; ArchiveMessage header = {0}; size_t count = 0; switch(archive->version_major) { case 0: BAIL_ON_ERROR(status = lwmsg_archive_read_fd(archive, &header_v0, sizeof(header_v0), &count)); if (count < sizeof(header)) { BAIL_ON_ERROR(status = LWMSG_STATUS_MALFORMED); } message->flags = 0; message->status = LWMSG_SWAP32(header_v0.status, archive->byte_order, LWMSG_NATIVE_ENDIAN); message->cookie = (uint16_t) LWMSG_SWAP32(header_v0.cookie, archive->byte_order, LWMSG_NATIVE_ENDIAN); message->tag = (int16_t) LWMSG_SWAP32(header_v0.tag, archive->byte_order, LWMSG_NATIVE_ENDIAN); *size = LWMSG_SWAP32(header_v0.size, archive->byte_order, LWMSG_NATIVE_ENDIAN); break; case ARCHIVE_VERSION_MAJOR: BAIL_ON_ERROR(status = lwmsg_archive_read_fd(archive, &header, sizeof(header), &count)); if (count < sizeof(header)) { BAIL_ON_ERROR(status = LWMSG_STATUS_MALFORMED); } message->flags = LWMSG_SWAP16(header.flags, archive->byte_order, LWMSG_NATIVE_ENDIAN); message->status = LWMSG_SWAP32(header.status, archive->byte_order, LWMSG_NATIVE_ENDIAN); message->cookie = LWMSG_SWAP16(header.cookie, archive->byte_order, LWMSG_NATIVE_ENDIAN); message->tag = LWMSG_SWAP16(header.tag, archive->byte_order, LWMSG_NATIVE_ENDIAN); *size = LWMSG_SWAP32(header.size, archive->byte_order, LWMSG_NATIVE_ENDIAN); break; } error: return status; } LWMsgStatus lwmsg_archive_read_message_fd( LWMsgArchive* archive, LWMsgMessage* message ) { LWMsgStatus status = LWMSG_STATUS_SUCCESS; readinfo info; LWMsgBuffer buffer = {0}; LWMsgTypeSpec* type = NULL; size_t message_size = 0; BAIL_ON_ERROR(status = lwmsg_archive_read_message_header(archive, message, &message_size)); BAIL_ON_ERROR(status = lwmsg_protocol_get_message_type(archive->base.prot, message->tag, &type)); info.archive = archive; info.remaining = message_size; buffer.base = info.data; buffer.end = buffer.base; buffer.cursor = buffer.base; buffer.wrap = lwmsg_archive_read_message_wrap_fd; buffer.data = &info; /* Unmarshal the message payload */ BAIL_ON_ERROR(status = lwmsg_data_unmarshal(archive->data_context, type, &buffer, &message->data)); error: return status; }