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