xref: /6.0.3/couchstore/src/couch_file_write.cc (revision 2b2ba193)
1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2#include "config.h"
3
4#include <platform/cb_malloc.h>
5#include <stdio.h>
6#include <stdint.h>
7#include <stdlib.h>
8#include <sys/types.h>
9#include <libcouchstore/couch_db.h>
10#include <platform/compress.h>
11
12#include "internal.h"
13#include "crc32.h"
14#include "util.h"
15
16static ssize_t raw_write(tree_file *file, const sized_buf *buf, cs_off_t pos)
17{
18    cs_off_t write_pos = pos;
19    size_t buf_pos = 0;
20    char blockprefix = 0;
21    ssize_t written;
22    size_t block_remain;
23    while (buf_pos < buf->size) {
24        block_remain = COUCH_BLOCK_SIZE - (write_pos % COUCH_BLOCK_SIZE);
25        if (block_remain > (buf->size - buf_pos)) {
26            block_remain = buf->size - buf_pos;
27        }
28
29        if (write_pos % COUCH_BLOCK_SIZE == 0) {
30            written = file->ops->pwrite(&file->lastError, file->handle,
31                                        &blockprefix, 1, write_pos);
32            if (written < 0) {
33                return written;
34            }
35            write_pos += 1;
36            continue;
37        }
38
39        written = file->ops->pwrite(&file->lastError, file->handle,
40                                    buf->buf + buf_pos, block_remain, write_pos);
41        if (written < 0) {
42            return written;
43        }
44        buf_pos += written;
45        write_pos += written;
46    }
47
48    return (ssize_t)(write_pos - pos);
49}
50
51couchstore_error_t write_header(tree_file *file, sized_buf *buf, cs_off_t *pos)
52{
53    cs_off_t write_pos = align_to_next_block(file->pos);
54    ssize_t written;
55    uint32_t size = htonl(buf->size + 4); //Len before header includes hash len.
56    uint32_t crc32 = htonl(get_checksum(reinterpret_cast<uint8_t*>(buf->buf),
57                                        buf->size,
58                                        file->crc_mode));
59    char headerbuf[1 + 4 + 4];
60
61    *pos = write_pos;
62
63    // Write the header's block header
64    headerbuf[0] = 1;
65    memcpy(&headerbuf[1], &size, 4);
66    memcpy(&headerbuf[5], &crc32, 4);
67
68    written = file->ops->pwrite(&file->lastError, file->handle,
69                                &headerbuf, sizeof(headerbuf), write_pos);
70    if (written < 0) {
71        return (couchstore_error_t)written;
72    }
73    write_pos += written;
74
75    //Write actual header
76    written = raw_write(file, buf, write_pos);
77    if (written < 0) {
78        return (couchstore_error_t)written;
79    }
80    write_pos += written;
81    file->pos = write_pos;
82
83    return COUCHSTORE_SUCCESS;
84}
85
86int db_write_buf(tree_file *file, const sized_buf *buf, cs_off_t *pos, size_t *disk_size)
87{
88    cs_off_t write_pos = file->pos;
89    cs_off_t end_pos = write_pos;
90    ssize_t written;
91    uint32_t size = htonl(buf->size | 0x80000000);
92    uint32_t crc32 = htonl(get_checksum(reinterpret_cast<uint8_t*>(buf->buf),
93                                        buf->size,
94                                        file->crc_mode));
95    char headerbuf[4 + 4];
96
97    // Write the buffer's header:
98    memcpy(&headerbuf[0], &size, 4);
99    memcpy(&headerbuf[4], &crc32, 4);
100
101    sized_buf sized_headerbuf = { headerbuf, 8 };
102    written = raw_write(file, &sized_headerbuf, end_pos);
103    if (written < 0) {
104        return (int)written;
105    }
106    end_pos += written;
107
108    // Write actual buffer:
109    written = raw_write(file, buf, end_pos);
110    if (written < 0) {
111        return (int)written;
112    }
113    end_pos += written;
114
115    if (pos) {
116        *pos = write_pos;
117    }
118
119    file->pos = end_pos;
120    if (disk_size) {
121        *disk_size = (size_t) (end_pos - write_pos);
122    }
123
124    return 0;
125}
126
127couchstore_error_t db_write_buf_compressed(tree_file *file,
128                                           const sized_buf *buf,
129                                           cs_off_t *pos,
130                                           size_t *disk_size)
131{
132    cb::compression::Buffer buffer;
133    try {
134        using cb::compression::Algorithm;
135        if (!cb::compression::deflate(Algorithm::Snappy,
136                                      {buf->buf, buf->size},
137                                      buffer)) {
138            log_last_internal_error("Couchstore::db_write_buf_compressed() "
139                                    "Compression failed buffer size:%zu", buf->size);
140            return COUCHSTORE_ERROR_CORRUPT;
141        }
142    } catch (const std::bad_alloc&) {
143        return COUCHSTORE_ERROR_ALLOC_FAIL;
144    }
145
146    sized_buf to_write{};
147    to_write.buf = buffer.data();
148    to_write.size = buffer.size();
149
150    return static_cast<couchstore_error_t>(db_write_buf(file, &to_write, pos, disk_size));
151}
152