Line data Source code
1 : /*
2 : * Copyright (C) 2011 Andrea Mazzoleni
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "portable.h"
19 :
20 : #include "elem.h"
21 : #include "support.h"
22 : #include "handle.h"
23 :
24 : /****************************************************************************/
25 : /* handle */
26 :
27 771262 : int handle_create(struct snapraid_handle* handle, struct snapraid_file* file, int mode)
28 : {
29 : int ret;
30 : int flags;
31 :
32 : /* if it's the same file, and already opened, nothing to do */
33 771262 : if (handle->file == file && handle->f != -1) {
34 0 : return 0;
35 : }
36 :
37 771262 : advise_init(&handle->advise, mode);
38 771262 : pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
39 :
40 771262 : ret = mkancestor(handle->path);
41 771262 : if (ret != 0) {
42 : /* LCOV_EXCL_START */
43 : return -1;
44 : /* LCOV_EXCL_STOP */
45 : }
46 :
47 : /* initial values, changed later if required */
48 771262 : handle->created = 0;
49 :
50 : /* flags for opening */
51 : /* O_BINARY: open as binary file (Windows only) */
52 : /* O_NOFOLLOW: do not follow links to ensure to open the real file */
53 771262 : flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
54 :
55 : /* open for read write */
56 771262 : handle->f = open(handle->path, flags | O_RDWR);
57 :
58 : /* if failed for missing write permission */
59 771262 : if (handle->f == -1 && (errno == EACCES || errno == EROFS)) {
60 : /* open for real-only */
61 0 : handle->f = open(handle->path, flags | O_RDONLY);
62 : }
63 :
64 : /* if failed for missing file */
65 771262 : if (handle->f == -1 && errno == ENOENT) {
66 : char path_from[PATH_MAX];
67 :
68 : /* check if exists a .unrecoverable copy, and rename to the real one */
69 176483 : pathprint(path_from, sizeof(path_from), "%s.unrecoverable", handle->path);
70 :
71 176483 : if (rename(path_from, handle->path) == 0) {
72 : /* open for read write */
73 63315 : handle->f = open(handle->path, flags | O_RDWR);
74 : } else {
75 : /* create it */
76 113168 : handle->f = open(handle->path, flags | O_RDWR | O_CREAT, 0600);
77 113168 : if (handle->f != -1) {
78 : /* mark it as created if really done */
79 113168 : handle->created = 1;
80 : }
81 : }
82 : }
83 :
84 771262 : if (handle->f == -1) {
85 : /* LCOV_EXCL_START */
86 : /* invalidate for error */
87 : handle->file = 0;
88 : handle->f = -1;
89 : handle->valid_size = 0;
90 :
91 : log_fatal("Error opening file '%s'. %s.\n", handle->path, strerror(errno));
92 : return -1;
93 : /* LCOV_EXCL_STOP */
94 : }
95 :
96 : /* just opened */
97 771262 : handle->file = file;
98 :
99 : /* get the stat info */
100 771262 : ret = fstat(handle->f, &handle->st);
101 771262 : if (ret != 0) {
102 : /* LCOV_EXCL_START */
103 : log_fatal("Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
104 : return -1;
105 : /* LCOV_EXCL_STOP */
106 : }
107 :
108 : /* get the size of the existing data */
109 771262 : handle->valid_size = handle->st.st_size;
110 :
111 771262 : ret = advise_open(&handle->advise, handle->f);
112 771262 : if (ret != 0) {
113 : /* LCOV_EXCL_START */
114 : log_fatal("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
115 : return -1;
116 : /* LCOV_EXCL_STOP */
117 : }
118 :
119 771262 : return 0;
120 : }
121 :
122 3186 : int handle_truncate(struct snapraid_handle* handle, struct snapraid_file* file)
123 : {
124 : int ret;
125 :
126 3186 : ret = ftruncate(handle->f, file->size);
127 3186 : if (ret != 0) {
128 : /* LCOV_EXCL_START */
129 : if (errno == EACCES) {
130 : log_fatal("Failed to truncate file '%s' for missing write permission.\n", handle->path);
131 : } else {
132 : log_fatal("Error truncating file '%s'. %s.\n", handle->path, strerror(errno));
133 : }
134 : return -1;
135 : /* LCOV_EXCL_STOP */
136 : }
137 :
138 : /* adjust the size to the truncated size */
139 3186 : handle->valid_size = file->size;
140 :
141 3186 : return 0;
142 : }
143 :
144 2181235 : int handle_open(struct snapraid_handle* handle, struct snapraid_file* file, int mode, fptr* out, fptr* out_missing)
145 : {
146 : int ret;
147 : int flags;
148 :
149 2181235 : if (!out_missing)
150 2182560 : out_missing = out;
151 :
152 : /* if already opened, nothing to do */
153 2181235 : if (handle->file == file && handle->file != 0 && handle->f != -1) {
154 468203 : return 0;
155 : }
156 :
157 1713032 : advise_init(&handle->advise, mode);
158 1715621 : pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
159 :
160 : /* for sure not created */
161 1716756 : handle->created = 0;
162 :
163 : /* flags for opening */
164 : /* O_BINARY: open as binary file (Windows only) */
165 : /* O_NOFOLLOW: do not follow links to ensure to open the real file */
166 1716756 : flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
167 :
168 : /* open for read */
169 1717051 : handle->f = open_noatime(handle->path, flags | O_RDONLY);
170 1712928 : if (handle->f == -1) {
171 : /* invalidate for error */
172 78934 : handle->file = 0;
173 78934 : handle->f = -1;
174 78934 : handle->valid_size = 0;
175 :
176 78934 : if (errno == ENOENT)
177 78922 : out_missing("Missing file '%s'.\n", handle->path);
178 : else
179 12 : out("Error opening file '%s'. %s.\n", handle->path, strerror(errno));
180 78934 : return -1;
181 : }
182 :
183 : /* just opened */
184 1633994 : handle->file = file;
185 :
186 : /* get the stat info */
187 1633994 : ret = fstat(handle->f, &handle->st);
188 1630006 : if (ret != 0) {
189 : /* LCOV_EXCL_START */
190 : out("Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
191 : return -1;
192 : /* LCOV_EXCL_STOP */
193 : }
194 :
195 : /* get the size of the existing data */
196 1630006 : handle->valid_size = handle->st.st_size;
197 :
198 1630006 : ret = advise_open(&handle->advise, handle->f);
199 1629129 : if (ret != 0) {
200 : /* LCOV_EXCL_START */
201 : out("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
202 : return -1;
203 : /* LCOV_EXCL_STOP */
204 : }
205 :
206 1629129 : return 0;
207 : }
208 :
209 4644944 : int handle_close(struct snapraid_handle* handle)
210 : {
211 : int ret;
212 :
213 : /* close if open */
214 4644944 : if (handle->f != -1) {
215 2409673 : ret = close(handle->f);
216 2409346 : if (ret != 0) {
217 : /* LCOV_EXCL_START */
218 : log_fatal("Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
219 :
220 : /* invalidate for error */
221 : handle->file = 0;
222 : handle->f = -1;
223 : handle->valid_size = 0;
224 : return -1;
225 : /* LCOV_EXCL_STOP */
226 : }
227 : }
228 :
229 : /* reset the descriptor */
230 4644617 : handle->file = 0;
231 4644617 : handle->f = -1;
232 4644617 : handle->valid_size = 0;
233 :
234 4644617 : return 0;
235 : }
236 :
237 5850799 : int handle_read(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size, fptr* out, fptr* out_missing)
238 : {
239 : ssize_t read_ret;
240 : data_off_t offset;
241 : unsigned read_size;
242 : unsigned count;
243 : int ret;
244 :
245 5850799 : offset = file_pos * (data_off_t)block_size;
246 :
247 5850799 : if (!out_missing)
248 5834263 : out_missing = out;
249 :
250 : /* check if we are going to read only not initialized data */
251 5850799 : if (offset >= handle->valid_size) {
252 : /* if the file is missing, it's at 0 size, or it's rebuilt while reading */
253 433534 : if (offset == handle->valid_size || handle->valid_size == 0)
254 432265 : out_missing("Reading data from missing file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
255 : else
256 1269 : out("Reading missing data from file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
257 433534 : return -1;
258 : }
259 :
260 5417265 : read_size = file_block_size(handle->file, file_pos, block_size);
261 :
262 5413909 : count = 0;
263 : do {
264 : /* read the full block to support O_DIRECT */
265 5423657 : read_ret = pread(handle->f, block_buffer + count, block_size - count, offset + count);
266 5427744 : if (read_ret < 0) {
267 : /* LCOV_EXCL_START */
268 : out("Error reading file '%s' at offset %" PRIu64 " for size %u. %s.\n", handle->path, offset + count, block_size - count, strerror(errno));
269 : return -1;
270 : /* LCOV_EXCL_STOP */
271 : }
272 5427744 : if (read_ret == 0) {
273 8602 : out("Unexpected end of file '%s' at offset %" PRIu64 ". %s.\n", handle->path, offset, strerror(errno));
274 8602 : return -1;
275 : }
276 :
277 5419142 : count += read_ret;
278 5419142 : } while (count < read_size);
279 :
280 : /* pad with 0 */
281 5409394 : if (read_size < block_size) {
282 2187944 : memset(block_buffer + read_size, 0, block_size - read_size);
283 : }
284 :
285 5409394 : ret = advise_read(&handle->advise, handle->f, offset, block_size);
286 5408496 : if (ret != 0) {
287 : /* LCOV_EXCL_START */
288 : out("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
289 : return -1;
290 : /* LCOV_EXCL_STOP */
291 : }
292 :
293 5408496 : return read_size;
294 : }
295 :
296 288647 : int handle_write(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size)
297 : {
298 : ssize_t write_ret;
299 : data_off_t offset;
300 : unsigned write_size;
301 : int ret;
302 :
303 288647 : offset = file_pos * (data_off_t)block_size;
304 :
305 288647 : write_size = file_block_size(handle->file, file_pos, block_size);
306 :
307 288647 : write_ret = pwrite(handle->f, block_buffer, write_size, offset);
308 288647 : if (write_ret != (ssize_t)write_size) { /* conversion is safe because block_size is always small */
309 : /* LCOV_EXCL_START */
310 : log_fatal("Error writing file '%s'. %s.\n", handle->path, strerror(errno));
311 : return -1;
312 : /* LCOV_EXCL_STOP */
313 : }
314 :
315 : /* adjust the size of the valid data */
316 288647 : if (handle->valid_size < offset + write_size) {
317 282551 : handle->valid_size = offset + write_size;
318 : }
319 :
320 288647 : ret = advise_write(&handle->advise, handle->f, offset, block_size);
321 288647 : if (ret != 0) {
322 : /* LCOV_EXCL_START */
323 : log_fatal("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
324 : return -1;
325 : /* LCOV_EXCL_STOP */
326 : }
327 :
328 288647 : return 0;
329 : }
330 :
331 119462 : int handle_utime(struct snapraid_handle* handle)
332 : {
333 : int ret;
334 :
335 : /* do nothing if not opened */
336 119462 : if (handle->f == -1)
337 0 : return 0;
338 :
339 119462 : ret = fmtime(handle->f, handle->file->mtime_sec, handle->file->mtime_nsec);
340 :
341 119462 : if (ret != 0) {
342 : /* LCOV_EXCL_START */
343 : log_fatal("Error timing file '%s'. %s.\n", handle->file->sub, strerror(errno));
344 : return -1;
345 : /* LCOV_EXCL_STOP */
346 : }
347 :
348 119462 : return 0;
349 : }
350 :
351 221 : struct snapraid_handle* handle_mapping(struct snapraid_state* state, unsigned* handlemax)
352 : {
353 : tommy_node* i;
354 : unsigned j;
355 221 : unsigned size = 0;
356 : struct snapraid_handle* handle;
357 :
358 : /* get the size of the mapping */
359 221 : size = 0;
360 1542 : for (i = state->maplist; i != 0; i = i->next) {
361 1321 : struct snapraid_map* map = i->data;
362 1321 : if (map->position > size)
363 941 : size = map->position;
364 : }
365 221 : ++size; /* size is one more than the max */
366 :
367 221 : handle = malloc_nofail(size * sizeof(struct snapraid_handle));
368 :
369 1547 : for (j = 0; j < size; ++j) {
370 : /* default for empty position */
371 1326 : handle[j].disk = 0;
372 1326 : handle[j].file = 0;
373 1326 : handle[j].f = -1;
374 1326 : handle[j].valid_size = 0;
375 : }
376 :
377 : /* set the vector */
378 1542 : for (i = state->disklist; i != 0; i = i->next) {
379 : struct snapraid_map* map;
380 1321 : struct snapraid_disk* disk = i->data;
381 : tommy_node* k;
382 :
383 : /* search the mapping for this disk */
384 1321 : map = 0;
385 4611 : for (k = state->maplist; k != 0; k = k->next) {
386 4611 : map = k->data;
387 4611 : if (strcmp(disk->name, map->name) == 0)
388 1321 : break;
389 : }
390 1321 : if (!map) {
391 : /* LCOV_EXCL_START */
392 : log_fatal("Internal error for inconsistent disk mapping.\n");
393 : os_abort();
394 : /* LCOV_EXCL_STOP */
395 : }
396 :
397 1321 : handle[map->position].disk = disk;
398 : }
399 :
400 221 : *handlemax = size;
401 221 : return handle;
402 : }
403 :
|