Line data Source code
1 : // SPDX-License-Identifier: GPL-3.0-or-later
2 : // Copyright (C) 2011 Andrea Mazzoleni
3 :
4 : #include "portable.h"
5 :
6 : #include "elem.h"
7 : #include "support.h"
8 : #include "handle.h"
9 :
10 : /****************************************************************************/
11 : /* handle */
12 :
13 790591 : int handle_create(struct snapraid_handle* handle, struct snapraid_file* file, int mode)
14 : {
15 : int ret;
16 : int flags;
17 :
18 : /* if it's the same file, and already opened, nothing to do */
19 790591 : if (handle->file == file && handle->f != -1) {
20 0 : return 0;
21 : }
22 :
23 790591 : advise_init(&handle->advise, mode);
24 790591 : pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
25 :
26 790591 : ret = mkancestor(handle->path);
27 790591 : if (ret != 0) {
28 : /* LCOV_EXCL_START */
29 : return -1;
30 : /* LCOV_EXCL_STOP */
31 : }
32 :
33 : /* initial values, changed later if required */
34 790591 : handle->created = 0;
35 :
36 : /*
37 : * Flags for opening
38 : * O_BINARY: open as binary file (Windows only)
39 : * O_NOFOLLOW: do not follow links to ensure to open the real file
40 : */
41 790591 : flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
42 :
43 : /* open for read write */
44 790591 : handle->f = open(handle->path, flags | O_RDWR);
45 :
46 : /* if failed for missing write permission */
47 790591 : if (handle->f == -1 && (errno == EACCES || errno == EROFS)) {
48 : /* open for read-only */
49 0 : handle->f = open(handle->path, flags | O_RDONLY);
50 : }
51 :
52 : /* if failed for missing file */
53 790591 : if (handle->f == -1 && errno == ENOENT) {
54 : char path_from[PATH_MAX];
55 :
56 : /* check if exists a .unrecoverable copy, and rename to the real one */
57 186769 : pathprint(path_from, sizeof(path_from), "%s.unrecoverable", handle->path);
58 :
59 186769 : if (rename(path_from, handle->path) == 0) {
60 : /* open for read write */
61 62986 : handle->f = open(handle->path, flags | O_RDWR);
62 : } else {
63 : /* create it */
64 123783 : handle->f = open(handle->path, flags | O_RDWR | O_CREAT, 0600);
65 123783 : if (handle->f != -1) {
66 : /* mark it as created if really done */
67 123783 : handle->created = 1;
68 : }
69 : }
70 : }
71 :
72 790591 : if (handle->f == -1) {
73 : /* LCOV_EXCL_START */
74 : log_fatal(errno, "Error opening file '%s'. %s.\n", handle->path, strerror(errno));
75 : handle_close(handle);
76 : return -1;
77 : /* LCOV_EXCL_STOP */
78 : }
79 :
80 : /* just opened */
81 790591 : handle->file = file;
82 :
83 : /* get the stat info */
84 790591 : ret = fstat(handle->f, &handle->st);
85 790591 : if (ret != 0) {
86 : /* LCOV_EXCL_START */
87 : log_fatal(errno, "Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
88 : handle_close(handle);
89 : return -1;
90 : /* LCOV_EXCL_STOP */
91 : }
92 :
93 : /* get the size of the existing data */
94 790591 : handle->valid_size = handle->st.st_size;
95 :
96 790591 : ret = advise_open(&handle->advise, handle->f);
97 790591 : if (ret != 0) {
98 : /* LCOV_EXCL_START */
99 : log_fatal(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
100 : handle_close(handle);
101 : return -1;
102 : /* LCOV_EXCL_STOP */
103 : }
104 :
105 790591 : return 0;
106 : }
107 :
108 3131 : int handle_truncate(struct snapraid_handle* handle, struct snapraid_file* file)
109 : {
110 : int ret;
111 :
112 3131 : ret = ftruncate(handle->f, file->size);
113 3131 : if (ret != 0) {
114 : /* LCOV_EXCL_START */
115 : if (errno == EACCES) {
116 : log_fatal(errno, "Failed to truncate file '%s' for missing write permission.\n", handle->path);
117 : } else {
118 : log_fatal(errno, "Error truncating file '%s'. %s.\n", handle->path, strerror(errno));
119 : }
120 : return -1;
121 : /* LCOV_EXCL_STOP */
122 : }
123 :
124 : /* adjust the size to the truncated size */
125 3131 : handle->valid_size = file->size;
126 :
127 3131 : return 0;
128 : }
129 :
130 2327527 : int handle_open(struct snapraid_handle* handle, struct snapraid_file* file, int mode, log_ptr* out, log_ptr* out_missing)
131 : {
132 : int ret;
133 : int flags;
134 :
135 2327527 : if (!out_missing)
136 1652166 : out_missing = out;
137 :
138 : /* if already opened, nothing to do */
139 2327527 : if (handle->file == file && handle->file != 0 && handle->f != -1) {
140 498867 : return 0;
141 : }
142 :
143 1828660 : advise_init(&handle->advise, mode);
144 1828660 : pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
145 :
146 : /* for sure not created */
147 1828660 : handle->created = 0;
148 :
149 : /*
150 : * Flags for opening
151 : * O_BINARY: open as binary file (Windows only)
152 : * O_NOFOLLOW: do not follow links to ensure to open the real file
153 : */
154 1828660 : flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
155 :
156 : /* open for read */
157 1828660 : handle->f = open_noatime(handle->path, flags | O_RDONLY);
158 1828660 : if (handle->f == -1) {
159 86637 : if (errno == ENOENT)
160 86625 : out_missing(errno, "Missing file '%s'.\n", handle->path);
161 12 : else if (errno == EACCES)
162 12 : out(errno, "Permission denied for file '%s'.\n", handle->path);
163 : else
164 0 : out(errno, "Error opening file '%s'. %s.\n", handle->path, strerror(errno));
165 86637 : handle_close(handle);
166 86637 : return -1;
167 : }
168 :
169 : /* just opened */
170 1742023 : handle->file = file;
171 :
172 : /* get the stat info */
173 1742023 : ret = fstat(handle->f, &handle->st);
174 1742023 : if (ret != 0) {
175 : /* LCOV_EXCL_START */
176 : out(errno, "Error accessing file '%s'. %s.\n", handle->path, strerror(errno));
177 : handle_close(handle);
178 : return -1;
179 : /* LCOV_EXCL_STOP */
180 : }
181 :
182 : /* get the size of the existing data */
183 1742023 : handle->valid_size = handle->st.st_size;
184 :
185 1742023 : ret = advise_open(&handle->advise, handle->f);
186 1742023 : if (ret != 0) {
187 : /* LCOV_EXCL_START */
188 : out(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
189 : handle_close(handle);
190 : return -1;
191 : /* LCOV_EXCL_STOP */
192 : }
193 :
194 1742023 : return 0;
195 : }
196 :
197 4967235 : int handle_close(struct snapraid_handle* handle)
198 : {
199 : int ret;
200 :
201 : /* close if open */
202 4967235 : if (handle->f != -1) {
203 2532561 : ret = close(handle->f);
204 2532561 : if (ret != 0) {
205 : /* LCOV_EXCL_START */
206 : log_fatal(errno, "Error closing file '%s'. %s.\n", handle->file->sub, strerror(errno));
207 :
208 : /* invalidate for error */
209 : handle->file = 0;
210 : handle->f = -1;
211 : handle->valid_size = 0;
212 : return -1;
213 : /* LCOV_EXCL_STOP */
214 : }
215 : }
216 :
217 : /* reset the descriptor */
218 4967235 : handle->file = 0;
219 4967235 : handle->f = -1;
220 4967235 : handle->valid_size = 0;
221 :
222 4967235 : return 0;
223 : }
224 :
225 5835406 : 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)
226 : {
227 : ssize_t read_ret;
228 : data_off_t offset;
229 : unsigned read_size;
230 : unsigned count;
231 : int ret;
232 :
233 5835406 : offset = file_pos * (data_off_t)block_size;
234 :
235 5835406 : if (!out_missing)
236 5828823 : out_missing = out;
237 :
238 : /* check if we are going to read only not initialized data */
239 5835406 : if (offset >= handle->valid_size) {
240 : /* if the file is missing, it's at 0 size, or it's rebuilt while reading */
241 187110 : if (offset == handle->valid_size || handle->valid_size == 0) {
242 186084 : errno = ENOENT;
243 186084 : if (offset == 0) {
244 185144 : out_missing(errno, "Missing file '%s'.\n", handle->path);
245 : } else {
246 940 : out_missing(errno, "Missing data in file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
247 : }
248 : } else {
249 1026 : errno = ENXIO;
250 1026 : out(errno, "Reading over the end from file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
251 : }
252 187110 : return -1;
253 : }
254 :
255 5648296 : read_size = file_block_size(handle->file, file_pos, block_size);
256 :
257 5648296 : count = 0;
258 5648296 : errno = 0;
259 : do {
260 5657114 : bw_limit(handle->bw, block_size - count);
261 :
262 5657114 : read_ret = pread(handle->f, block_buffer + count, block_size - count, offset + count);
263 5657114 : if (read_ret == -1) {
264 : /* LCOV_EXCL_START */
265 : out(errno, "Error reading file '%s' at offset %" PRIu64 " for size %u. %s.\n", handle->path, offset + count, block_size - count, strerror(errno));
266 : return -1;
267 : /* LCOV_EXCL_STOP */
268 : }
269 5657114 : if (read_ret == 0) {
270 : /* LCOV_EXCL_START */
271 : if (errno == 0)
272 : errno = ENXIO;
273 : out(errno, "Unexpected end of file '%s' at offset %" PRIu64 ". %s.\n", handle->path, offset, strerror(errno));
274 : return -1;
275 : /* LCOV_EXCL_STOP */
276 : }
277 :
278 5648296 : count += read_ret;
279 5648296 : } while (count < read_size);
280 :
281 : /* pad with 0 */
282 5639478 : if (read_size < block_size) {
283 2302942 : memset(block_buffer + read_size, 0, block_size - read_size);
284 : }
285 :
286 5639478 : ret = advise_read(&handle->advise, handle->f, offset, block_size);
287 5639478 : if (ret != 0) {
288 : /* LCOV_EXCL_START */
289 : out(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
290 : return -1;
291 : /* LCOV_EXCL_STOP */
292 : }
293 :
294 5639478 : return read_size;
295 : }
296 :
297 310483 : int handle_write(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size)
298 : {
299 : ssize_t write_ret;
300 : data_off_t offset;
301 : unsigned write_size;
302 : unsigned count;
303 : int ret;
304 :
305 310483 : offset = file_pos * (data_off_t)block_size;
306 :
307 310483 : write_size = file_block_size(handle->file, file_pos, block_size);
308 :
309 310483 : count = 0;
310 : do {
311 310483 : bw_limit(handle->bw, write_size - count);
312 :
313 310483 : write_ret = pwrite(handle->f, block_buffer + count, write_size - count, offset + count);
314 310483 : if (write_ret == -1) {
315 : /* LCOV_EXCL_START */
316 : log_fatal(errno, "Error writing file '%s'. %s.\n", handle->path, strerror(errno));
317 : return -1;
318 : /* LCOV_EXCL_STOP */
319 : }
320 310483 : if (write_ret == 0) {
321 : /* LCOV_EXCL_START */
322 : errno = ENXIO;
323 : log_fatal(errno, "Unexpected 0 write to file '%s'. %s.\n", handle->path, strerror(errno));
324 : return -1;
325 : /* LCOV_EXCL_STOP */
326 : }
327 :
328 310483 : count += write_ret;
329 310483 : } while (count < write_size);
330 :
331 : /* adjust the size of the valid data */
332 310483 : if (handle->valid_size < offset + write_size) {
333 304285 : handle->valid_size = offset + write_size;
334 : }
335 :
336 310483 : ret = advise_write(&handle->advise, handle->f, offset, block_size);
337 310483 : if (ret != 0) {
338 : /* LCOV_EXCL_START */
339 : log_fatal(errno, "Error advising file '%s'. %s.\n", handle->path, strerror(errno));
340 : return -1;
341 : /* LCOV_EXCL_STOP */
342 : }
343 :
344 310483 : return 0;
345 : }
346 :
347 131956 : int handle_utime(struct snapraid_handle* handle)
348 : {
349 : int ret;
350 :
351 : /* do nothing if not opened */
352 131956 : if (handle->f == -1)
353 0 : return 0;
354 :
355 131956 : ret = fmtime(handle->f, handle->file->mtime_sec, handle->file->mtime_nsec);
356 :
357 131956 : if (ret != 0) {
358 : /* LCOV_EXCL_START */
359 : log_fatal(errno, "Error timing file '%s'. %s.\n", handle->file->sub, strerror(errno));
360 : return -1;
361 : /* LCOV_EXCL_STOP */
362 : }
363 :
364 131956 : return 0;
365 : }
366 :
367 254 : struct snapraid_handle* handle_mapping(struct snapraid_state* state, unsigned* handlemax)
368 : {
369 : tommy_node* i;
370 : unsigned j;
371 254 : unsigned size = 0;
372 : struct snapraid_handle* handle;
373 :
374 : /* get the size of the mapping */
375 254 : size = 0;
376 1716 : for (i = state->maplist; i != 0; i = i->next) {
377 1462 : struct snapraid_map* map = i->data;
378 1462 : if (map->position > size)
379 1043 : size = map->position;
380 : }
381 254 : ++size; /* size is one more than the max */
382 :
383 254 : handle = malloc_nofail(size * sizeof(struct snapraid_handle));
384 :
385 1721 : for (j = 0; j < size; ++j) {
386 : /* default for empty position */
387 1467 : handle[j].disk = 0;
388 1467 : handle[j].file = 0;
389 1467 : handle[j].f = -1;
390 1467 : handle[j].valid_size = 0;
391 1467 : handle[j].bw = 0;
392 : }
393 :
394 : /* set the vector */
395 1716 : for (i = state->disklist; i != 0; i = i->next) {
396 : struct snapraid_map* map;
397 1462 : struct snapraid_disk* disk = i->data;
398 : tommy_node* k;
399 :
400 : /* search the mapping for this disk */
401 1462 : map = 0;
402 5019 : for (k = state->maplist; k != 0; k = k->next) {
403 5019 : map = k->data;
404 5019 : if (strcmp(disk->name, map->name) == 0)
405 1462 : break;
406 : }
407 1462 : if (!map) {
408 : /* LCOV_EXCL_START */
409 : log_fatal(EINTERNAL, "Internal error for inconsistent disk mapping.\n");
410 : os_abort();
411 : /* LCOV_EXCL_STOP */
412 : }
413 :
414 1462 : handle[map->position].disk = disk;
415 : }
416 :
417 254 : *handlemax = size;
418 254 : return handle;
419 : }
420 :
|