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 786781 : 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 786781 : if (handle->file == file && handle->f != -1) {
34 0 : return 0;
35 : }
36 :
37 786781 : advise_init(&handle->advise, mode);
38 786781 : pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
39 :
40 786781 : ret = mkancestor(handle->path);
41 786781 : if (ret != 0) {
42 : /* LCOV_EXCL_START */
43 : return -1;
44 : /* LCOV_EXCL_STOP */
45 : }
46 :
47 : /* initial values, changed later if required */
48 786781 : 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 786781 : flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
54 :
55 : /* open for read write */
56 786781 : handle->f = open(handle->path, flags | O_RDWR);
57 :
58 : /* if failed for missing write permission */
59 786781 : if (handle->f == -1 && (errno == EACCES || errno == EROFS)) {
60 : /* open for read-only */
61 0 : handle->f = open(handle->path, flags | O_RDONLY);
62 : }
63 :
64 : /* if failed for missing file */
65 786781 : 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 183815 : pathprint(path_from, sizeof(path_from), "%s.unrecoverable", handle->path);
70 :
71 183815 : if (rename(path_from, handle->path) == 0) {
72 : /* open for read write */
73 62951 : handle->f = open(handle->path, flags | O_RDWR);
74 : } else {
75 : /* create it */
76 120864 : handle->f = open(handle->path, flags | O_RDWR | O_CREAT, 0600);
77 120864 : if (handle->f != -1) {
78 : /* mark it as created if really done */
79 120864 : handle->created = 1;
80 : }
81 : }
82 : }
83 :
84 786781 : 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(errno, "Error opening file '%s'. %s.\n", handle->path, strerror(errno));
92 : return -1;
93 : /* LCOV_EXCL_STOP */
94 : }
95 :
96 : /* just opened */
97 786781 : handle->file = file;
98 :
99 : /* get the stat info */
100 786781 : ret = fstat(handle->f, &handle->st);
101 786781 : if (ret != 0) {
102 : /* LCOV_EXCL_START */
103 : log_fatal(errno, "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 786781 : handle->valid_size = handle->st.st_size;
110 :
111 786781 : ret = advise_open(&handle->advise, handle->f);
112 786781 : if (ret != 0) {
113 : /* LCOV_EXCL_START */
114 : log_fatal(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
115 : return -1;
116 : /* LCOV_EXCL_STOP */
117 : }
118 :
119 786781 : return 0;
120 : }
121 :
122 3181 : int handle_truncate(struct snapraid_handle* handle, struct snapraid_file* file)
123 : {
124 : int ret;
125 :
126 3181 : ret = ftruncate(handle->f, file->size);
127 3181 : if (ret != 0) {
128 : /* LCOV_EXCL_START */
129 : if (errno == EACCES) {
130 : log_fatal(errno, "Failed to truncate file '%s' for missing write permission.\n", handle->path);
131 : } else {
132 : log_fatal(errno, "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 3181 : handle->valid_size = file->size;
140 :
141 3181 : return 0;
142 : }
143 :
144 2283455 : int handle_open(struct snapraid_handle* handle, struct snapraid_file* file, int mode, log_ptr* out, log_ptr* out_missing)
145 : {
146 : int ret;
147 : int flags;
148 :
149 2283455 : if (!out_missing)
150 1624093 : out_missing = out;
151 :
152 : /* if already opened, nothing to do */
153 2283455 : if (handle->file == file && handle->file != 0 && handle->f != -1) {
154 499414 : return 0;
155 : }
156 :
157 1784041 : advise_init(&handle->advise, mode);
158 1784041 : pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
159 :
160 : /* for sure not created */
161 1784041 : 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 1784041 : flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
167 :
168 : /* open for read */
169 1784041 : handle->f = open_noatime(handle->path, flags | O_RDONLY);
170 1784041 : if (handle->f == -1) {
171 : /* invalidate for error */
172 86490 : handle->file = 0;
173 86490 : handle->f = -1;
174 86490 : handle->valid_size = 0;
175 :
176 86490 : if (errno == ENOENT)
177 86478 : out_missing(errno, "Missing file '%s'.\n", handle->path);
178 12 : else if (errno == EACCES)
179 12 : out(errno, "Permission denied for file '%s'.\n", handle->path);
180 : else
181 0 : out(errno, "Error opening file '%s'. %s.\n", handle->path, strerror(errno));
182 86490 : return -1;
183 : }
184 :
185 : /* just opened */
186 1697551 : handle->file = file;
187 :
188 : /* get the stat info */
189 1697551 : ret = fstat(handle->f, &handle->st);
190 1697551 : if (ret != 0) {
191 : /* LCOV_EXCL_START */
192 : out(errno, "Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
193 : return -1;
194 : /* LCOV_EXCL_STOP */
195 : }
196 :
197 : /* get the size of the existing data */
198 1697551 : handle->valid_size = handle->st.st_size;
199 :
200 1697551 : ret = advise_open(&handle->advise, handle->f);
201 1697551 : if (ret != 0) {
202 : /* LCOV_EXCL_START */
203 : out(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
204 : return -1;
205 : /* LCOV_EXCL_STOP */
206 : }
207 :
208 1697551 : return 0;
209 : }
210 :
211 4802848 : int handle_close(struct snapraid_handle* handle)
212 : {
213 : int ret;
214 :
215 : /* close if open */
216 4802848 : if (handle->f != -1) {
217 2484332 : ret = close(handle->f);
218 2484332 : if (ret != 0) {
219 : /* LCOV_EXCL_START */
220 : log_fatal(errno, "Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
221 :
222 : /* invalidate for error */
223 : handle->file = 0;
224 : handle->f = -1;
225 : handle->valid_size = 0;
226 : return -1;
227 : /* LCOV_EXCL_STOP */
228 : }
229 : }
230 :
231 : /* reset the descriptor */
232 4802848 : handle->file = 0;
233 4802848 : handle->f = -1;
234 4802848 : handle->valid_size = 0;
235 :
236 4802848 : return 0;
237 : }
238 :
239 5783636 : int handle_read(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size, log_ptr* out, log_ptr* out_missing)
240 : {
241 : ssize_t read_ret;
242 : data_off_t offset;
243 : unsigned read_size;
244 : unsigned count;
245 : int ret;
246 :
247 5783636 : offset = file_pos * (data_off_t)block_size;
248 :
249 5783636 : if (!out_missing)
250 5777053 : out_missing = out;
251 :
252 : /* check if we are going to read only not initialized data */
253 5783636 : if (offset >= handle->valid_size) {
254 : /* if the file is missing, it's at 0 size, or it's rebuilt while reading */
255 184168 : if (offset == handle->valid_size || handle->valid_size == 0) {
256 183162 : errno = ENOENT;
257 183162 : if (offset == 0) {
258 182244 : out_missing(errno, "Missing file '%s'.\n", handle->path);
259 : } else {
260 918 : out_missing(errno, "Missing data in file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
261 : }
262 : } else {
263 1006 : errno = ENXIO;
264 1006 : out(errno, "Reading over the end from file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
265 : }
266 184168 : return -1;
267 : }
268 :
269 5599468 : read_size = file_block_size(handle->file, file_pos, block_size);
270 :
271 5599468 : count = 0;
272 5599468 : errno = 0;
273 : do {
274 5608418 : bw_limit(handle->bw, block_size - count);
275 :
276 5608418 : read_ret = pread(handle->f, block_buffer + count, block_size - count, offset + count);
277 5608418 : if (read_ret == -1) {
278 : /* LCOV_EXCL_START */
279 : out(errno, "Error reading file '%s' at offset %" PRIu64 " for size %u. %s.\n", handle->path, offset + count, block_size - count, strerror(errno));
280 : return -1;
281 : /* LCOV_EXCL_STOP */
282 : }
283 5608418 : if (read_ret == 0) {
284 : /* LCOV_EXCL_START */
285 : if (errno == 0)
286 : errno = ENXIO;
287 : out(errno, "Unexpected end of file '%s' at offset %" PRIu64 ". %s.\n", handle->path, offset, strerror(errno));
288 : return -1;
289 : /* LCOV_EXCL_STOP */
290 : }
291 :
292 5599468 : count += read_ret;
293 5599468 : } while (count < read_size);
294 :
295 : /* pad with 0 */
296 5590518 : if (read_size < block_size) {
297 2257811 : memset(block_buffer + read_size, 0, block_size - read_size);
298 : }
299 :
300 5590518 : ret = advise_read(&handle->advise, handle->f, offset, block_size);
301 5590518 : if (ret != 0) {
302 : /* LCOV_EXCL_START */
303 : out(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
304 : return -1;
305 : /* LCOV_EXCL_STOP */
306 : }
307 :
308 5590518 : return read_size;
309 : }
310 :
311 307391 : int handle_write(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size)
312 : {
313 : ssize_t write_ret;
314 : data_off_t offset;
315 : unsigned write_size;
316 : unsigned count;
317 : int ret;
318 :
319 307391 : offset = file_pos * (data_off_t)block_size;
320 :
321 307391 : write_size = file_block_size(handle->file, file_pos, block_size);
322 :
323 307391 : count = 0;
324 : do {
325 307391 : bw_limit(handle->bw, write_size - count);
326 :
327 307391 : write_ret = pwrite(handle->f, block_buffer + count, write_size - count, offset + count);
328 307391 : if (write_ret == -1) {
329 : /* LCOV_EXCL_START */
330 : log_fatal(errno, "Error writing file '%s'. %s.\n", handle->path, strerror(errno));
331 : return -1;
332 : /* LCOV_EXCL_STOP */
333 : }
334 307391 : if (write_ret == 0) {
335 : /* LCOV_EXCL_START */
336 : errno = ENXIO;
337 : log_fatal(errno, "Unexpected 0 write to file '%s'. %s.\n", handle->path, strerror(errno));
338 : return -1;
339 : /* LCOV_EXCL_STOP */
340 : }
341 :
342 307391 : count += write_ret;
343 307391 : } while (count < write_size);
344 :
345 : /* adjust the size of the valid data */
346 307391 : if (handle->valid_size < offset + write_size) {
347 301251 : handle->valid_size = offset + write_size;
348 : }
349 :
350 307391 : ret = advise_write(&handle->advise, handle->f, offset, block_size);
351 307391 : if (ret != 0) {
352 : /* LCOV_EXCL_START */
353 : log_fatal(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
354 : return -1;
355 : /* LCOV_EXCL_STOP */
356 : }
357 :
358 307391 : return 0;
359 : }
360 :
361 129038 : int handle_utime(struct snapraid_handle* handle)
362 : {
363 : int ret;
364 :
365 : /* do nothing if not opened */
366 129038 : if (handle->f == -1)
367 0 : return 0;
368 :
369 129038 : ret = fmtime(handle->f, handle->file->mtime_sec, handle->file->mtime_nsec);
370 :
371 129038 : if (ret != 0) {
372 : /* LCOV_EXCL_START */
373 : log_fatal(errno, "Error timing file '%s'. %s.\n", handle->file->sub, strerror(errno));
374 : return -1;
375 : /* LCOV_EXCL_STOP */
376 : }
377 :
378 129038 : return 0;
379 : }
380 :
381 235 : struct snapraid_handle* handle_mapping(struct snapraid_state* state, unsigned* handlemax)
382 : {
383 : tommy_node* i;
384 : unsigned j;
385 235 : unsigned size = 0;
386 : struct snapraid_handle* handle;
387 :
388 : /* get the size of the mapping */
389 235 : size = 0;
390 1640 : for (i = state->maplist; i != 0; i = i->next) {
391 1405 : struct snapraid_map* map = i->data;
392 1405 : if (map->position > size)
393 1005 : size = map->position;
394 : }
395 235 : ++size; /* size is one more than the max */
396 :
397 235 : handle = malloc_nofail(size * sizeof(struct snapraid_handle));
398 :
399 1645 : for (j = 0; j < size; ++j) {
400 : /* default for empty position */
401 1410 : handle[j].disk = 0;
402 1410 : handle[j].file = 0;
403 1410 : handle[j].f = -1;
404 1410 : handle[j].valid_size = 0;
405 1410 : handle[j].bw = 0;
406 : }
407 :
408 : /* set the vector */
409 1640 : for (i = state->disklist; i != 0; i = i->next) {
410 : struct snapraid_map* map;
411 1405 : struct snapraid_disk* disk = i->data;
412 : tommy_node* k;
413 :
414 : /* search the mapping for this disk */
415 1405 : map = 0;
416 4905 : for (k = state->maplist; k != 0; k = k->next) {
417 4905 : map = k->data;
418 4905 : if (strcmp(disk->name, map->name) == 0)
419 1405 : break;
420 : }
421 1405 : if (!map) {
422 : /* LCOV_EXCL_START */
423 : log_fatal(EINTERNAL, "Internal error for inconsistent disk mapping.\n");
424 : os_abort();
425 : /* LCOV_EXCL_STOP */
426 : }
427 :
428 1405 : handle[map->position].disk = disk;
429 : }
430 :
431 235 : *handlemax = size;
432 235 : return handle;
433 : }
434 :
|