xref: /6.6.0/couchstore/src/couch_file_write.cc (revision c2c458ff)
1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2#include "config.h"
3
4#include <libcouchstore/couch_db.h>
5#include <phosphor/phosphor.h>
6#include <platform/cb_malloc.h>
7#include <snappy.h>
8#include <stdint.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <sys/types.h>
12#include <libcouchstore/couch_db.h>
13#include <platform/compress.h>
14
15#include "internal.h"
16#include "crc32.h"
17#include "internal.h"
18#include "util.h"
19
20static ssize_t write_entire_buffer(tree_file *file, const void* buf,
21                                   size_t nbytes, cs_off_t offset) {
22    size_t left_to_write = nbytes;
23    const char* src = reinterpret_cast<const char*>(buf);
24
25    /* calculate CRC for the piece written and trace it. */
26    if (file->options.tracing_enabled) {
27        uint32_t crc32 = get_checksum(reinterpret_cast<const uint8_t*>(buf), nbytes, CRC32C);
28        TRACE_INSTANT2("couchstore_write",
29                       "write_entire_buffer",
30                       "offset",
31                       offset,
32                       "nbytes&CRC",
33                       nbytes << 32 | crc32);
34    }
35
36    while (left_to_write) {
37        ssize_t written = file->ops->pwrite(&file->lastError, file->handle,
38                                            src, nbytes, offset);
39        if (written < 0) {
40            return written;
41        }
42        left_to_write -= written;
43        src += written;
44        offset += written;
45    }
46    return (ssize_t)nbytes;
47}
48
49static ssize_t raw_write(tree_file *file, const sized_buf *buf, cs_off_t pos)
50{
51    cs_off_t write_pos = pos;
52    size_t buf_pos = 0;
53    char blockprefix = 0;
54    ssize_t written;
55    size_t block_remain;
56
57    /*
58    break up the write buffer into blocks adding the block prefix "0" as needed
59    */
60    while (buf_pos < buf->size) {
61        block_remain = COUCH_BLOCK_SIZE - (write_pos % COUCH_BLOCK_SIZE);
62        if (block_remain > (buf->size - buf_pos)) {
63            block_remain = buf->size - buf_pos;
64        }
65
66        if (write_pos % COUCH_BLOCK_SIZE == 0) {
67            written = write_entire_buffer(file, &blockprefix, 1, write_pos);
68            if (written < 0) {
69                return written;
70            }
71            write_pos += written;
72            continue;
73        }
74        written = write_entire_buffer(file, buf->buf + buf_pos, block_remain, write_pos);
75        if (written < 0) {
76            return written;
77        }
78        buf_pos += written;
79        write_pos += written;
80    }
81
82    return (ssize_t)(write_pos - pos);
83}
84
85couchstore_error_t write_header(tree_file *file, sized_buf *buf, cs_off_t *pos)
86{
87    cs_off_t write_pos = align_to_next_block(file->pos);
88    ssize_t written;
89    uint32_t size = htonl(buf->size + 4); //Len before header includes hash len.
90    uint32_t crc32 = htonl(get_checksum(reinterpret_cast<uint8_t*>(buf->buf),
91                                        buf->size,
92                                        file->crc_mode));
93    char headerbuf[1 + 4 + 4];
94
95    *pos = write_pos;
96
97    // Write the header's block header
98    headerbuf[0] = 1;
99    memcpy(&headerbuf[1], &size, 4);
100    memcpy(&headerbuf[5], &crc32, 4);
101
102    written = write_entire_buffer(file, &headerbuf, sizeof(headerbuf), write_pos);
103    if (written < 0) {
104        if (file->options.tracing_enabled) {
105            TRACE_INSTANT1(
106                    "couchstore_write", "write_header", "written", write_pos);
107        }
108        return (couchstore_error_t)written;
109    }
110    write_pos += written;
111
112    //Write actual header
113    written = raw_write(file, buf, write_pos);
114    if (written < 0) {
115        return (couchstore_error_t)written;
116    }
117    write_pos += written;
118    file->pos = write_pos;
119
120    return COUCHSTORE_SUCCESS;
121}
122
123int db_write_buf(tree_file *file, const sized_buf *buf, cs_off_t *pos, size_t *disk_size)
124{
125    cs_off_t write_pos = file->pos;
126    cs_off_t end_pos = write_pos;
127    ssize_t written;
128    uint32_t size = htonl(buf->size | 0x80000000);
129    uint32_t crc32 = htonl(get_checksum(reinterpret_cast<uint8_t*>(buf->buf),
130                                        buf->size,
131                                        file->crc_mode));
132    char headerbuf[4 + 4];
133
134    // Write the buffer's header:
135    memcpy(&headerbuf[0], &size, 4);
136    memcpy(&headerbuf[4], &crc32, 4);
137
138    if ((file->options.tracing_enabled) && (size == 0 && crc32 == 0)) {
139        TRACE_INSTANT2("couchstore_write",
140                       "Warning:db_write_buf",
141                       "size",
142                       size,
143                       "CRC",
144                       crc32);
145    }
146
147    sized_buf sized_headerbuf = { headerbuf, 8 };
148    written = raw_write(file, &sized_headerbuf, end_pos);
149    if (written < 0) {
150        return (int)written;
151    }
152    end_pos += written;
153
154    // Write actual buffer:
155    written = raw_write(file, buf, end_pos);
156    if (written < 0) {
157        return (int)written;
158    }
159    end_pos += written;
160
161    if (pos) {
162        *pos = write_pos;
163    }
164
165    file->pos = end_pos;
166    if (disk_size) {
167        *disk_size = (size_t) (end_pos - write_pos);
168    }
169
170    return 0;
171}
172
173couchstore_error_t db_write_buf_compressed(tree_file *file,
174                                           const sized_buf *buf,
175                                           cs_off_t *pos,
176                                           size_t *disk_size)
177{
178    cb::compression::Buffer buffer;
179    try {
180        using cb::compression::Algorithm;
181        if (!cb::compression::deflate(Algorithm::Snappy,
182                                      {buf->buf, buf->size},
183                                      buffer)) {
184            log_last_internal_error("Couchstore::db_write_buf_compressed() "
185                                    "Compression failed buffer size:%zu", buf->size);
186            return COUCHSTORE_ERROR_CORRUPT;
187        }
188    } catch (const std::bad_alloc&) {
189        return COUCHSTORE_ERROR_ALLOC_FAIL;
190    }
191
192    sized_buf to_write{};
193    to_write.buf = buffer.data();
194    to_write.size = buffer.size();
195
196    return static_cast<couchstore_error_t>(db_write_buf(file, &to_write, pos, disk_size));
197}
198