/src/c-blosc2/blosc/sframe.c
Line | Count | Source |
1 | | /********************************************************************* |
2 | | Blosc - Blocked Shuffling and Compression Library |
3 | | |
4 | | Copyright (c) 2021 Blosc Development Team <blosc@blosc.org> |
5 | | https://blosc.org |
6 | | License: BSD 3-Clause (see LICENSE.txt) |
7 | | |
8 | | See LICENSE.txt for details about copyright and rights to use. |
9 | | **********************************************************************/ |
10 | | |
11 | | #include "frame.h" |
12 | | #include "blosc2.h" |
13 | | |
14 | | #include <inttypes.h> |
15 | | #include <limits.h> |
16 | | #include <stdio.h> |
17 | | #include <stdint.h> |
18 | | #include <stdlib.h> |
19 | | #include <string.h> |
20 | | |
21 | | |
22 | | /* If C11 is supported, use it's built-in aligned allocation. */ |
23 | | #if __STDC_VERSION__ >= 201112L |
24 | | #include <stdalign.h> |
25 | | #endif |
26 | | |
27 | | |
28 | 0 | static char* sframe_make_index_path(const char* urlpath) { |
29 | 0 | size_t path_len = strlen(urlpath); |
30 | 0 | size_t suffix_len = strlen("/chunks.b2frame"); |
31 | 0 | if (path_len > SIZE_MAX - suffix_len - 1) { |
32 | 0 | BLOSC_TRACE_ERROR("Index path length overflows size limits"); |
33 | 0 | return NULL; |
34 | 0 | } |
35 | | |
36 | 0 | char* index_path = malloc(path_len + suffix_len + 1); |
37 | 0 | if (index_path == NULL) { |
38 | 0 | return NULL; |
39 | 0 | } |
40 | | |
41 | 0 | int written = snprintf(index_path, path_len + suffix_len + 1, "%s/chunks.b2frame", urlpath); |
42 | 0 | if (written < 0 || (size_t)written >= path_len + suffix_len + 1) { |
43 | 0 | BLOSC_TRACE_ERROR("Error building index path"); |
44 | 0 | free(index_path); |
45 | 0 | return NULL; |
46 | 0 | } |
47 | | |
48 | 0 | return index_path; |
49 | 0 | } |
50 | | |
51 | | |
52 | 0 | static char* sframe_make_chunk_path(const char* urlpath, int64_t nchunk) { |
53 | 0 | if (nchunk < 0 || (uint64_t)nchunk > UINT32_MAX) { |
54 | 0 | BLOSC_TRACE_ERROR("Chunk index (%" PRId64 ") is out of range for sframe filenames", nchunk); |
55 | 0 | return NULL; |
56 | 0 | } |
57 | | |
58 | 0 | size_t path_len = strlen(urlpath); |
59 | 0 | size_t suffix_len = strlen("/.chunk"); |
60 | 0 | size_t chunk_hex_len = 8; |
61 | 0 | if (path_len > SIZE_MAX - suffix_len - chunk_hex_len - 1) { |
62 | 0 | BLOSC_TRACE_ERROR("Chunk path length overflows size limits"); |
63 | 0 | return NULL; |
64 | 0 | } |
65 | | |
66 | 0 | size_t total_len = path_len + suffix_len + chunk_hex_len + 1; |
67 | 0 | char* chunk_path = malloc(total_len); |
68 | 0 | if (chunk_path == NULL) { |
69 | 0 | return NULL; |
70 | 0 | } |
71 | | |
72 | 0 | int written = snprintf(chunk_path, total_len, "%s/%08" PRIX32 ".chunk", urlpath, (uint32_t)nchunk); |
73 | 0 | if (written < 0 || (size_t)written >= total_len) { |
74 | 0 | BLOSC_TRACE_ERROR("Error building chunk path for chunk index (%" PRId64 ")", nchunk); |
75 | 0 | free(chunk_path); |
76 | 0 | return NULL; |
77 | 0 | } |
78 | | |
79 | 0 | return chunk_path; |
80 | 0 | } |
81 | | |
82 | | |
83 | | /* Open sparse frame index chunk */ |
84 | 0 | void* sframe_open_index(const char* urlpath, const char* mode, const blosc2_io *io) { |
85 | 0 | void* fp = NULL; |
86 | 0 | char* index_path = sframe_make_index_path(urlpath); |
87 | 0 | if (index_path) { |
88 | 0 | blosc2_io_cb *io_cb = blosc2_get_io_cb(io->id); |
89 | 0 | if (io_cb == NULL) { |
90 | 0 | BLOSC_TRACE_ERROR("Error getting the input/output API"); |
91 | 0 | free(index_path); |
92 | 0 | return NULL; |
93 | 0 | } |
94 | 0 | fp = io_cb->open(index_path, mode, io->params); |
95 | 0 | if (fp == NULL) |
96 | 0 | BLOSC_TRACE_ERROR("Error creating index path in: %s", index_path); |
97 | 0 | free(index_path); |
98 | 0 | } |
99 | 0 | return fp; |
100 | 0 | } |
101 | | |
102 | | /* Open directory/nchunk.chunk with 8 zeros of padding */ |
103 | 0 | void* sframe_open_chunk(const char* urlpath, int64_t nchunk, const char* mode, const blosc2_io *io) { |
104 | 0 | void* fp = NULL; |
105 | 0 | char* chunk_path = sframe_make_chunk_path(urlpath, nchunk); |
106 | 0 | if (chunk_path) { |
107 | 0 | blosc2_io_cb *io_cb = blosc2_get_io_cb(io->id); |
108 | 0 | if (io_cb == NULL) { |
109 | 0 | BLOSC_TRACE_ERROR("Error getting the input/output API"); |
110 | 0 | free(chunk_path); |
111 | 0 | return NULL; |
112 | 0 | } |
113 | 0 | fp = io_cb->open(chunk_path, mode, io->params); |
114 | 0 | if (fp == NULL) |
115 | 0 | BLOSC_TRACE_ERROR("Error opening chunk path in: %s", chunk_path); |
116 | 0 | free(chunk_path); |
117 | 0 | } |
118 | 0 | return fp; |
119 | 0 | } |
120 | | |
121 | | /* Append an existing chunk into a sparse frame. */ |
122 | 0 | void* sframe_create_chunk(blosc2_frame_s* frame, uint8_t* chunk, int64_t nchunk, int64_t cbytes) { |
123 | 0 | void* fpc = sframe_open_chunk(frame->urlpath, nchunk, "wb", frame->schunk->storage->io); |
124 | 0 | if (fpc == NULL) { |
125 | 0 | BLOSC_TRACE_ERROR("Cannot open the chunkfile."); |
126 | 0 | return NULL; |
127 | 0 | } |
128 | 0 | blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); |
129 | 0 | if (io_cb == NULL) { |
130 | 0 | BLOSC_TRACE_ERROR("Error getting the input/output API"); |
131 | 0 | return NULL; |
132 | 0 | } |
133 | 0 | int64_t io_pos = 0; |
134 | 0 | int64_t wbytes = io_cb->write(chunk, 1, cbytes, io_pos, fpc); |
135 | 0 | io_cb->close(fpc); |
136 | 0 | if (wbytes != cbytes) { |
137 | 0 | BLOSC_TRACE_ERROR("Cannot write the full chunk."); |
138 | 0 | return NULL; |
139 | 0 | } |
140 | | |
141 | 0 | return frame; |
142 | 0 | } |
143 | | |
144 | | /* Append an existing chunk into a sparse frame. */ |
145 | 0 | int sframe_delete_chunk(const char *urlpath, int64_t nchunk) { |
146 | 0 | char* chunk_path = sframe_make_chunk_path(urlpath, nchunk); |
147 | 0 | if (chunk_path) { |
148 | 0 | int rc = remove(chunk_path); |
149 | 0 | free(chunk_path); |
150 | 0 | return rc; |
151 | 0 | } |
152 | 0 | return BLOSC2_ERROR_FILE_REMOVE; |
153 | 0 | } |
154 | | |
155 | | /* Get chunk from sparse frame. */ |
156 | 0 | int32_t sframe_get_chunk(blosc2_frame_s* frame, int64_t nchunk, uint8_t** chunk, bool* needs_free){ |
157 | 0 | void *fpc = sframe_open_chunk(frame->urlpath, nchunk, "rb", frame->schunk->storage->io); |
158 | 0 | if(fpc == NULL){ |
159 | 0 | BLOSC_TRACE_ERROR("Cannot open the chunkfile."); |
160 | 0 | return BLOSC2_ERROR_FILE_OPEN; |
161 | 0 | } |
162 | | |
163 | 0 | blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); |
164 | 0 | if (io_cb == NULL) { |
165 | 0 | BLOSC_TRACE_ERROR("Error getting the input/output API"); |
166 | 0 | return BLOSC2_ERROR_PLUGIN_IO; |
167 | 0 | } |
168 | | |
169 | 0 | int64_t chunk_cbytes = io_cb->size(fpc); |
170 | 0 | if (chunk_cbytes < BLOSC_MIN_HEADER_LENGTH) { |
171 | | // A valid chunk file always starts with a Blosc header; a smaller (e.g. empty) |
172 | | // file means the on-disk state changed under us (another handle replaced this |
173 | | // chunk) or the file is corrupted. Fail the same way as frame_get_lazychunk(). |
174 | 0 | BLOSC_TRACE_ERROR("Chunkfile is too small to contain a valid chunk."); |
175 | 0 | io_cb->close(fpc); |
176 | 0 | return BLOSC2_ERROR_FILE_READ; |
177 | 0 | } |
178 | | |
179 | 0 | if (io_cb->is_allocation_necessary) { |
180 | 0 | *chunk = malloc((size_t)chunk_cbytes); |
181 | 0 | *needs_free = true; |
182 | 0 | } |
183 | 0 | else { |
184 | 0 | *needs_free = false; |
185 | 0 | } |
186 | |
|
187 | 0 | int64_t io_pos = 0; |
188 | 0 | int64_t rbytes = io_cb->read((void**)chunk, 1, chunk_cbytes, io_pos, fpc); |
189 | 0 | io_cb->close(fpc); |
190 | 0 | if (rbytes != chunk_cbytes) { |
191 | 0 | BLOSC_TRACE_ERROR("Cannot read the chunk out of the chunkfile."); |
192 | 0 | return BLOSC2_ERROR_FILE_READ; |
193 | 0 | } |
194 | | |
195 | 0 | return (int32_t)chunk_cbytes; |
196 | 0 | } |