xref: /5.5.2/couchstore/src/couch_file_read.cc (revision ea89a543)
1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2#include "config.h"
3
4#include <fcntl.h>
5#include <platform/cb_malloc.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <snappy.h>
10
11#include "internal.h"
12#include "iobuffer.h"
13#include "bitfield.h"
14#include "crc32.h"
15#include "util.h"
16
17
18couchstore_error_t tree_file_open(tree_file* file,
19                                  const char *filename,
20                                  int openflags,
21                                  crc_mode_e crc_mode,
22                                  FileOpsInterface* ops,
23                                  tree_file_options file_options)
24{
25    if(filename == nullptr || file == nullptr || ops == nullptr) {
26        return COUCHSTORE_ERROR_INVALID_ARGUMENTS;
27    }
28
29    couchstore_error_t errcode = COUCHSTORE_SUCCESS;
30
31    memset(file, 0, sizeof(*file));
32
33    file->crc_mode = crc_mode;
34    file->options = file_options;
35
36    file->path = (const char *) cb_strdup(filename);
37    error_unless(file->path, COUCHSTORE_ERROR_ALLOC_FAIL);
38
39    if (file_options.buf_io_enabled) {
40        buffered_file_ops_params params((openflags == O_RDONLY),
41                                        file_options.buf_io_read_unit_size,
42                                        file_options.buf_io_read_buffers);
43
44        file->ops = couch_get_buffered_file_ops(&file->lastError, ops,
45                                                &file->handle, params);
46    } else {
47        file->ops = ops;
48        file->handle = file->ops->constructor(&file->lastError);
49    }
50
51    error_unless(file->ops, COUCHSTORE_ERROR_ALLOC_FAIL);
52
53    error_pass(file->ops->open(&file->lastError, &file->handle,
54                               filename, openflags));
55
56    if (file->options.periodic_sync_bytes != 0) {
57        error_pass(file->ops->set_periodic_sync(
58                file->handle, file->options.periodic_sync_bytes));
59    }
60
61cleanup:
62    if (errcode != COUCHSTORE_SUCCESS) {
63        cb_free((char *) file->path);
64        file->path = NULL;
65        if (file->ops) {
66            file->ops->destructor(file->handle);
67            file->ops = NULL;
68            file->handle = NULL;
69        }
70    }
71    return errcode;
72}
73
74couchstore_error_t tree_file_close(tree_file* file)
75{
76    couchstore_error_t errcode = COUCHSTORE_SUCCESS;
77    if (file->ops) {
78        errcode = file->ops->close(&file->lastError, file->handle);
79        file->ops->destructor(file->handle);
80    }
81    cb_free((char*)file->path);
82    return errcode;
83}
84
85/** Read bytes from the database file, skipping over the header-detection bytes at every block
86    boundary. */
87static couchstore_error_t read_skipping_prefixes(tree_file *file,
88                                                 cs_off_t *pos,
89                                                 ssize_t len,
90                                                 void *dst) {
91    if (*pos % COUCH_BLOCK_SIZE == 0) {
92        ++*pos;
93    }
94    while (len > 0) {
95        ssize_t read_size = COUCH_BLOCK_SIZE - (*pos % COUCH_BLOCK_SIZE);
96        if (read_size > len) {
97            read_size = len;
98        }
99        ssize_t got_bytes = file->ops->pread(&file->lastError, file->handle,
100                                             dst, read_size, *pos);
101        if (got_bytes < 0) {
102            return (couchstore_error_t) got_bytes;
103        } else if (got_bytes == 0) {
104            return COUCHSTORE_ERROR_READ;
105        }
106        *pos += got_bytes;
107        len -= got_bytes;
108        dst = (char*)dst + got_bytes;
109        if (*pos % COUCH_BLOCK_SIZE == 0) {
110            ++*pos;
111        }
112    }
113    return COUCHSTORE_SUCCESS;
114}
115
116/*
117 * Common subroutine of pread_bin, pread_compressed and pread_header.
118 * Parameters and return value are the same as for pread_bin,
119 * except the 'max_header_size' parameter which is greater than 0 if
120 * reading a header, 0 otherwise.
121 */
122static int pread_bin_internal(tree_file *file,
123                              cs_off_t pos,
124                              char **ret_ptr,
125                              uint32_t max_header_size)
126{
127    struct {
128        uint32_t chunk_len;
129        uint32_t crc32;
130    } info;
131
132    couchstore_error_t err = read_skipping_prefixes(file, &pos, sizeof(info), &info);
133    if (err < 0) {
134        return err;
135    }
136
137    info.chunk_len = ntohl(info.chunk_len) & ~0x80000000;
138    if (max_header_size) {
139        if (info.chunk_len < 4 || info.chunk_len > max_header_size)
140            return COUCHSTORE_ERROR_CORRUPT;
141        info.chunk_len -= 4;    //Header len includes CRC len.
142    }
143    info.crc32 = ntohl(info.crc32);
144
145    uint8_t* buf = static_cast<uint8_t*>(cb_malloc(info.chunk_len));
146    if (!buf) {
147        return COUCHSTORE_ERROR_ALLOC_FAIL;
148    }
149    err = read_skipping_prefixes(file, &pos, info.chunk_len, buf);
150
151    if (!err && !perform_integrity_check(buf, info.chunk_len, info.crc32, file->crc_mode)) {
152        err = COUCHSTORE_ERROR_CHECKSUM_FAIL;
153    }
154
155    if (err < 0) {
156        cb_free(buf);
157        return err;
158    }
159
160    *ret_ptr = reinterpret_cast<char*>(buf);
161    return info.chunk_len;
162}
163
164int pread_header(tree_file *file,
165                 cs_off_t pos,
166                 char **ret_ptr,
167                 uint32_t max_header_size)
168{
169    if (max_header_size == 0) {
170        return COUCHSTORE_ERROR_INVALID_ARGUMENTS;
171    }
172
173    ScopedFileTag tag(file->ops, file->handle, FileTag::FileHeader);
174    return pread_bin_internal(file, pos + 1, ret_ptr, max_header_size);
175}
176
177int pread_compressed(tree_file *file, cs_off_t pos, char **ret_ptr)
178{
179    char *compressed_buf;
180    char *new_buf;
181    int len = pread_bin_internal(file, pos, &compressed_buf, 0);
182    if (len < 0) {
183        return len;
184    }
185    size_t uncompressed_len;
186
187    if (!snappy::GetUncompressedLength(compressed_buf, len, &uncompressed_len)) {
188        //should be compressed but snappy doesn't see it as valid.
189        cb_free(compressed_buf);
190        return COUCHSTORE_ERROR_CORRUPT;
191    }
192
193    new_buf = static_cast<char *>(cb_malloc(uncompressed_len));
194    if (!new_buf) {
195        cb_free(compressed_buf);
196        return COUCHSTORE_ERROR_ALLOC_FAIL;
197    }
198
199    if (!snappy::RawUncompress(compressed_buf, len, new_buf)) {
200        cb_free(compressed_buf);
201        cb_free(new_buf);
202        return COUCHSTORE_ERROR_CORRUPT;
203    }
204
205    cb_free(compressed_buf);
206    *ret_ptr = new_buf;
207    return static_cast<int>(uncompressed_len);
208}
209
210int pread_bin(tree_file *file, cs_off_t pos, char **ret_ptr)
211{
212    return pread_bin_internal(file, pos, ret_ptr, 0);
213}
214