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
handle_create(struct snapraid_handle * handle,struct snapraid_file * file,int mode)27 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 if (handle->file == file && handle->f != -1) {
34 return 0;
35 }
36
37 advise_init(&handle->advise, mode);
38 pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
39
40 ret = mkancestor(handle->path);
41 if (ret != 0) {
42 /* LCOV_EXCL_START */
43 return -1;
44 /* LCOV_EXCL_STOP */
45 }
46
47 /* initial values, changed later if required */
48 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 flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
54
55 /* open for read write */
56 handle->f = open(handle->path, flags | O_RDWR);
57
58 /* if failed for missing write permission */
59 if (handle->f == -1 && (errno == EACCES || errno == EROFS)) {
60 /* open for real-only */
61 handle->f = open(handle->path, flags | O_RDONLY);
62 }
63
64 /* if failed for missing file */
65 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 pathprint(path_from, sizeof(path_from), "%s.unrecoverable", handle->path);
70
71 if (rename(path_from, handle->path) == 0) {
72 /* open for read write */
73 handle->f = open(handle->path, flags | O_RDWR);
74 } else {
75 /* create it */
76 handle->f = open(handle->path, flags | O_RDWR | O_CREAT, 0600);
77 if (handle->f != -1) {
78 /* mark it as created if really done */
79 handle->created = 1;
80 }
81 }
82 }
83
84 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 handle->file = file;
98
99 /* get the stat info */
100 ret = fstat(handle->f, &handle->st);
101 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 handle->valid_size = handle->st.st_size;
110
111 ret = advise_open(&handle->advise, handle->f);
112 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 return 0;
120 }
121
handle_truncate(struct snapraid_handle * handle,struct snapraid_file * file)122 int handle_truncate(struct snapraid_handle* handle, struct snapraid_file* file)
123 {
124 int ret;
125
126 ret = ftruncate(handle->f, file->size);
127 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 handle->valid_size = file->size;
140
141 return 0;
142 }
143
handle_open(struct snapraid_handle * handle,struct snapraid_file * file,int mode,fptr * out,fptr * out_missing)144 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 if (!out_missing)
150 out_missing = out;
151
152 /* if already opened, nothing to do */
153 if (handle->file == file && handle->file != 0 && handle->f != -1) {
154 return 0;
155 }
156
157 advise_init(&handle->advise, mode);
158 pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
159
160 /* for sure not created */
161 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 flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
167
168 /* open for read */
169 handle->f = open_noatime(handle->path, flags | O_RDONLY);
170 if (handle->f == -1) {
171 /* invalidate for error */
172 handle->file = 0;
173 handle->f = -1;
174 handle->valid_size = 0;
175
176 if (errno == ENOENT)
177 out_missing("Missing file '%s'.\n", handle->path);
178 else
179 out("Error opening file '%s'. %s.\n", handle->path, strerror(errno));
180 return -1;
181 }
182
183 /* just opened */
184 handle->file = file;
185
186 /* get the stat info */
187 ret = fstat(handle->f, &handle->st);
188 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 handle->valid_size = handle->st.st_size;
197
198 ret = advise_open(&handle->advise, handle->f);
199 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 return 0;
207 }
208
handle_close(struct snapraid_handle * handle)209 int handle_close(struct snapraid_handle* handle)
210 {
211 int ret;
212
213 /* close if open */
214 if (handle->f != -1) {
215 ret = close(handle->f);
216 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 handle->file = 0;
231 handle->f = -1;
232 handle->valid_size = 0;
233
234 return 0;
235 }
236
handle_read(struct snapraid_handle * handle,block_off_t file_pos,unsigned char * block_buffer,unsigned block_size,fptr * out,fptr * out_missing)237 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 offset = file_pos * (data_off_t)block_size;
246
247 if (!out_missing)
248 out_missing = out;
249
250 /* check if we are going to read only not initialized data */
251 if (offset >= handle->valid_size) {
252 /* if the file is missing, it's at 0 size, or it's rebuilt while reading */
253 if (offset == handle->valid_size || handle->valid_size == 0)
254 out_missing("Reading data from missing file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
255 else
256 out("Reading missing data from file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
257 return -1;
258 }
259
260 read_size = file_block_size(handle->file, file_pos, block_size);
261
262 count = 0;
263 do {
264 /* read the full block to support O_DIRECT */
265 read_ret = pread(handle->f, block_buffer + count, block_size - count, offset + count);
266 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 if (read_ret == 0) {
273 out("Unexpected end of file '%s' at offset %" PRIu64 ". %s.\n", handle->path, offset, strerror(errno));
274 return -1;
275 }
276
277 count += read_ret;
278 } while (count < read_size);
279
280 /* pad with 0 */
281 if (read_size < block_size) {
282 memset(block_buffer + read_size, 0, block_size - read_size);
283 }
284
285 ret = advise_read(&handle->advise, handle->f, offset, block_size);
286 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 return read_size;
294 }
295
handle_write(struct snapraid_handle * handle,block_off_t file_pos,unsigned char * block_buffer,unsigned block_size)296 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 offset = file_pos * (data_off_t)block_size;
304
305 write_size = file_block_size(handle->file, file_pos, block_size);
306
307 write_ret = pwrite(handle->f, block_buffer, write_size, offset);
308 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 if (handle->valid_size < offset + write_size) {
317 handle->valid_size = offset + write_size;
318 }
319
320 ret = advise_write(&handle->advise, handle->f, offset, block_size);
321 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 return 0;
329 }
330
handle_utime(struct snapraid_handle * handle)331 int handle_utime(struct snapraid_handle* handle)
332 {
333 int ret;
334
335 /* do nothing if not opened */
336 if (handle->f == -1)
337 return 0;
338
339 ret = fmtime(handle->f, handle->file->mtime_sec, handle->file->mtime_nsec);
340
341 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 return 0;
349 }
350
handle_mapping(struct snapraid_state * state,unsigned * handlemax)351 struct snapraid_handle* handle_mapping(struct snapraid_state* state, unsigned* handlemax)
352 {
353 tommy_node* i;
354 unsigned j;
355 unsigned size = 0;
356 struct snapraid_handle* handle;
357
358 /* get the size of the mapping */
359 size = 0;
360 for (i = state->maplist; i != 0; i = i->next) {
361 struct snapraid_map* map = i->data;
362 if (map->position > size)
363 size = map->position;
364 }
365 ++size; /* size is one more than the max */
366
367 handle = malloc_nofail(size * sizeof(struct snapraid_handle));
368
369 for (j = 0; j < size; ++j) {
370 /* default for empty position */
371 handle[j].disk = 0;
372 handle[j].file = 0;
373 handle[j].f = -1;
374 handle[j].valid_size = 0;
375 }
376
377 /* set the vector */
378 for (i = state->disklist; i != 0; i = i->next) {
379 struct snapraid_map* map;
380 struct snapraid_disk* disk = i->data;
381 tommy_node* k;
382
383 /* search the mapping for this disk */
384 map = 0;
385 for (k = state->maplist; k != 0; k = k->next) {
386 map = k->data;
387 if (strcmp(disk->name, map->name) == 0)
388 break;
389 }
390 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 handle[map->position].disk = disk;
398 }
399
400 *handlemax = size;
401 return handle;
402 }
403
404