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 771326 : 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 771326 : if (handle->file == file && handle->f != -1) {
34 0 : return 0;
35 : }
36 :
37 771326 : advise_init(&handle->advise, mode);
38 771326 : pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
39 :
40 771326 : ret = mkancestor(handle->path);
41 771326 : if (ret != 0) {
42 : /* LCOV_EXCL_START */
43 : return -1;
44 : /* LCOV_EXCL_STOP */
45 : }
46 :
47 : /* initial values, changed later if required */
48 771326 : 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 771326 : flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
54 :
55 : /* open for read write */
56 771326 : handle->f = open(handle->path, flags | O_RDWR);
57 :
58 : /* if failed for missing write permission */
59 771326 : 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 771326 : 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 176347 : pathprint(path_from, sizeof(path_from), "%s.unrecoverable", handle->path);
70 :
71 176347 : if (rename(path_from, handle->path) == 0) {
72 : /* open for read write */
73 63292 : handle->f = open(handle->path, flags | O_RDWR);
74 : } else {
75 : /* create it */
76 113055 : handle->f = open(handle->path, flags | O_RDWR | O_CREAT, 0600);
77 113055 : if (handle->f != -1) {
78 : /* mark it as created if really done */
79 113055 : handle->created = 1;
80 : }
81 : }
82 : }
83 :
84 771326 : 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 771326 : handle->file = file;
98 :
99 : /* get the stat info */
100 771326 : ret = fstat(handle->f, &handle->st);
101 771326 : 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 771326 : handle->valid_size = handle->st.st_size;
110 :
111 771326 : ret = advise_open(&handle->advise, handle->f);
112 771326 : 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 771326 : return 0;
120 : }
121 :
122 3197 : int handle_truncate(struct snapraid_handle* handle, struct snapraid_file* file)
123 : {
124 : int ret;
125 :
126 3197 : ret = ftruncate(handle->f, file->size);
127 3197 : 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 3197 : handle->valid_size = file->size;
140 :
141 3197 : return 0;
142 : }
143 :
144 2175789 : 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 2175789 : if (!out_missing)
150 2175293 : out_missing = out;
151 :
152 : /* if already opened, nothing to do */
153 2175789 : if (handle->file == file && handle->file != 0 && handle->f != -1) {
154 460789 : return 0;
155 : }
156 :
157 1715000 : advise_init(&handle->advise, mode);
158 1715000 : pathprint(handle->path, sizeof(handle->path), "%s%s", handle->disk->dir, file->sub);
159 :
160 : /* for sure not created */
161 1715000 : 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 1715000 : flags = O_BINARY | O_NOFOLLOW | advise_flags(&handle->advise);
167 :
168 : /* open for read */
169 1715000 : handle->f = open_noatime(handle->path, flags | O_RDONLY);
170 1715000 : if (handle->f == -1) {
171 : /* invalidate for error */
172 78656 : handle->file = 0;
173 78656 : handle->f = -1;
174 78656 : handle->valid_size = 0;
175 :
176 78656 : if (errno == ENOENT)
177 78644 : out_missing("Missing file '%s'.\n", handle->path);
178 : else
179 12 : out("Error opening file '%s'. %s.\n", handle->path, strerror(errno));
180 78656 : return -1;
181 : }
182 :
183 : /* just opened */
184 1636344 : handle->file = file;
185 :
186 : /* get the stat info */
187 1636344 : ret = fstat(handle->f, &handle->st);
188 1636344 : 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 1636344 : handle->valid_size = handle->st.st_size;
197 :
198 1636344 : ret = advise_open(&handle->advise, handle->f);
199 1636344 : 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 1636344 : return 0;
207 : }
208 :
209 4655251 : int handle_close(struct snapraid_handle* handle)
210 : {
211 : int ret;
212 :
213 : /* close if open */
214 4655251 : if (handle->f != -1) {
215 2407670 : ret = close(handle->f);
216 2407670 : 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 4655251 : handle->file = 0;
231 4655251 : handle->f = -1;
232 4655251 : handle->valid_size = 0;
233 :
234 4655251 : return 0;
235 : }
236 :
237 5859021 : 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 5859021 : offset = file_pos * (data_off_t)block_size;
246 :
247 5859021 : if (!out_missing)
248 5846130 : out_missing = out;
249 :
250 : /* check if we are going to read only not initialized data */
251 5859021 : if (offset >= handle->valid_size) {
252 : /* if the file is missing, it's at 0 size, or it's rebuilt while reading */
253 433267 : if (offset == handle->valid_size || handle->valid_size == 0)
254 431972 : out_missing("Reading data from missing file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
255 : else
256 1295 : out("Reading missing data from file '%s' at offset %" PRIu64 ".\n", handle->path, offset);
257 433267 : return -1;
258 : }
259 :
260 5425754 : read_size = file_block_size(handle->file, file_pos, block_size);
261 :
262 5425754 : count = 0;
263 : do {
264 5434662 : bw_limit(handle->bw, block_size - count);
265 :
266 : /* read the full block to support O_DIRECT */
267 5434662 : read_ret = pread(handle->f, block_buffer + count, block_size - count, offset + count);
268 5434662 : if (read_ret < 0) {
269 : /* LCOV_EXCL_START */
270 : out("Error reading file '%s' at offset %" PRIu64 " for size %u. %s.\n", handle->path, offset + count, block_size - count, strerror(errno));
271 : return -1;
272 : /* LCOV_EXCL_STOP */
273 : }
274 5434662 : if (read_ret == 0) {
275 8908 : out("Unexpected end of file '%s' at offset %" PRIu64 ". %s.\n", handle->path, offset, strerror(errno));
276 8908 : return -1;
277 : }
278 :
279 5425754 : count += read_ret;
280 5425754 : } while (count < read_size);
281 :
282 : /* pad with 0 */
283 5416846 : if (read_size < block_size) {
284 2189817 : memset(block_buffer + read_size, 0, block_size - read_size);
285 : }
286 :
287 5416846 : ret = advise_read(&handle->advise, handle->f, offset, block_size);
288 5416846 : if (ret != 0) {
289 : /* LCOV_EXCL_START */
290 : out("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
291 : return -1;
292 : /* LCOV_EXCL_STOP */
293 : }
294 :
295 5416846 : return read_size;
296 : }
297 :
298 288437 : int handle_write(struct snapraid_handle* handle, block_off_t file_pos, unsigned char* block_buffer, unsigned block_size)
299 : {
300 : ssize_t write_ret;
301 : data_off_t offset;
302 : unsigned write_size;
303 : int ret;
304 :
305 288437 : offset = file_pos * (data_off_t)block_size;
306 :
307 288437 : write_size = file_block_size(handle->file, file_pos, block_size);
308 :
309 288437 : write_ret = pwrite(handle->f, block_buffer, write_size, offset);
310 288437 : if (write_ret != (ssize_t)write_size) { /* conversion is safe because block_size is always small */
311 : /* LCOV_EXCL_START */
312 : log_fatal("Error writing file '%s'. %s.\n", handle->path, strerror(errno));
313 : return -1;
314 : /* LCOV_EXCL_STOP */
315 : }
316 :
317 : /* adjust the size of the valid data */
318 288437 : if (handle->valid_size < offset + write_size) {
319 282354 : handle->valid_size = offset + write_size;
320 : }
321 :
322 288437 : ret = advise_write(&handle->advise, handle->f, offset, block_size);
323 288437 : if (ret != 0) {
324 : /* LCOV_EXCL_START */
325 : log_fatal("Error advising file '%s'. %s.\n", handle->path, strerror(errno));
326 : return -1;
327 : /* LCOV_EXCL_STOP */
328 : }
329 :
330 288437 : return 0;
331 : }
332 :
333 121230 : int handle_utime(struct snapraid_handle* handle)
334 : {
335 : int ret;
336 :
337 : /* do nothing if not opened */
338 121230 : if (handle->f == -1)
339 0 : return 0;
340 :
341 121230 : ret = fmtime(handle->f, handle->file->mtime_sec, handle->file->mtime_nsec);
342 :
343 121230 : if (ret != 0) {
344 : /* LCOV_EXCL_START */
345 : log_fatal("Error timing file '%s'. %s.\n", handle->file->sub, strerror(errno));
346 : return -1;
347 : /* LCOV_EXCL_STOP */
348 : }
349 :
350 121230 : return 0;
351 : }
352 :
353 222 : struct snapraid_handle* handle_mapping(struct snapraid_state* state, unsigned* handlemax)
354 : {
355 : tommy_node* i;
356 : unsigned j;
357 222 : unsigned size = 0;
358 : struct snapraid_handle* handle;
359 :
360 : /* get the size of the mapping */
361 222 : size = 0;
362 1549 : for (i = state->maplist; i != 0; i = i->next) {
363 1327 : struct snapraid_map* map = i->data;
364 1327 : if (map->position > size)
365 946 : size = map->position;
366 : }
367 222 : ++size; /* size is one more than the max */
368 :
369 222 : handle = malloc_nofail(size * sizeof(struct snapraid_handle));
370 :
371 1554 : for (j = 0; j < size; ++j) {
372 : /* default for empty position */
373 1332 : handle[j].disk = 0;
374 1332 : handle[j].file = 0;
375 1332 : handle[j].f = -1;
376 1332 : handle[j].valid_size = 0;
377 1332 : handle[j].bw = 0;
378 : }
379 :
380 : /* set the vector */
381 1549 : for (i = state->disklist; i != 0; i = i->next) {
382 : struct snapraid_map* map;
383 1327 : struct snapraid_disk* disk = i->data;
384 : tommy_node* k;
385 :
386 : /* search the mapping for this disk */
387 1327 : map = 0;
388 4632 : for (k = state->maplist; k != 0; k = k->next) {
389 4632 : map = k->data;
390 4632 : if (strcmp(disk->name, map->name) == 0)
391 1327 : break;
392 : }
393 1327 : if (!map) {
394 : /* LCOV_EXCL_START */
395 : log_fatal("Internal error for inconsistent disk mapping.\n");
396 : os_abort();
397 : /* LCOV_EXCL_STOP */
398 : }
399 :
400 1327 : handle[map->position].disk = disk;
401 : }
402 :
403 222 : *handlemax = size;
404 222 : return handle;
405 : }
406 :
|