Whamcloud - gitweb
libext2fs: don't use O_DIRECT for files on tmpfs
[tools/e2fsprogs.git] / lib / ext2fs / sparse_io.c
1 #include "config.h"
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <stdint.h>
7 #include "ext2_fs.h"
8 #include "ext2fs.h"
9
10 #ifndef O_BINARY
11 #define O_BINARY 0
12 #endif
13
14 #if !defined(ENABLE_LIBSPARSE)
15 static errcode_t sparse_open(const char *name EXT2FS_ATTR((unused)),
16                              int flags EXT2FS_ATTR((unused)),
17                              io_channel *channel EXT2FS_ATTR((unused)))
18 {
19         return EXT2_ET_UNIMPLEMENTED;
20 }
21 static errcode_t sparse_close(io_channel channel EXT2FS_ATTR((unused)))
22 {
23         return EXT2_ET_UNIMPLEMENTED;
24 }
25 static struct struct_io_manager struct_sparse_manager = {
26         .magic                  = EXT2_ET_MAGIC_IO_MANAGER,
27         .name                   = "Android sparse I/O Manager",
28         .open                   = sparse_open,
29         .close                  = sparse_close,
30 };
31 static struct struct_io_manager struct_sparsefd_manager = {
32         .magic                  = EXT2_ET_MAGIC_IO_MANAGER,
33         .name                   = "Android sparse fd I/O Manager",
34         .open                   = sparse_open,
35         .close                  = sparse_close,
36 };
37 #else
38 #include <sparse/sparse.h>
39
40 struct sparse_map {
41         int                     fd;
42         char                    **blocks;
43         int                     block_size;
44         uint64_t                blocks_count;
45         char                    *file;
46         struct sparse_file      *sparse_file;
47         io_channel              channel;
48 };
49
50 struct sparse_io_params {
51         int                     fd;
52         char                    *file;
53         uint64_t                blocks_count;
54         unsigned int            block_size;
55 };
56
57 static errcode_t sparse_write_blk(io_channel channel, unsigned long block,
58                                   int count, const void *buf);
59
60 static void free_sparse_blocks(struct sparse_map *sm)
61 {
62         uint64_t i;
63
64         for (i = 0; i < sm->blocks_count; ++i)
65                 free(sm->blocks[i]);
66         free(sm->blocks);
67         sm->blocks = NULL;
68 }
69
70 static int sparse_import_segment(void *priv, const void *data, size_t len,
71                                  unsigned int block, unsigned int nr_blocks)
72 {
73         struct sparse_map *sm = priv;
74
75         /* Ignore chunk headers, only write the data */
76         if (!nr_blocks || len % sm->block_size)
77                 return 0;
78
79         return sparse_write_blk(sm->channel, block, nr_blocks, data);
80 }
81
82 static errcode_t io_manager_import_sparse(struct sparse_io_params *params,
83                                           struct sparse_map *sm, io_channel io)
84 {
85         int fd;
86         errcode_t retval;
87         struct sparse_file *sparse_file;
88
89         if (params->fd < 0) {
90                 fd = open(params->file, O_RDONLY);
91                 if (fd < 0) {
92                         retval = -1;
93                         goto err_open;
94                 }
95         } else
96                 fd = params->fd;
97         sparse_file = sparse_file_import(fd, false, false);
98         if (!sparse_file) {
99                 retval = -1;
100                 goto err_sparse;
101         }
102
103         sm->block_size = sparse_file_block_size(sparse_file);
104         sm->blocks_count = (sparse_file_len(sparse_file, 0, 0) - 1)
105                                 / sm->block_size + 1;
106         sm->blocks = calloc(sm->blocks_count, sizeof(char*));
107         if (!sm->blocks) {
108                 retval = -1;
109                 goto err_alloc;
110         }
111         io->block_size = sm->block_size;
112
113         retval = sparse_file_foreach_chunk(sparse_file, true, false,
114                                            sparse_import_segment, sm);
115
116         if (retval)
117                 free_sparse_blocks(sm);
118 err_alloc:
119         sparse_file_destroy(sparse_file);
120 err_sparse:
121         close(fd);
122 err_open:
123         return retval;
124 }
125
126 static errcode_t io_manager_configure(struct sparse_io_params *params,
127                                       int flags, io_channel io)
128 {
129         errcode_t retval;
130         uint64_t img_size;
131         struct sparse_map *sm = calloc(1, sizeof(*sm));
132         if (!sm)
133                 return EXT2_ET_NO_MEMORY;
134
135         sm->file = params->file;
136         sm->channel = io;
137         io->private_data = sm;
138         retval = io_manager_import_sparse(params, sm, io);
139         if (retval) {
140                 if (!params->block_size || !params->blocks_count) {
141                         retval = -EINVAL;
142                         goto err_params;
143                 }
144                 sm->block_size = params->block_size;
145                 sm->blocks_count = params->blocks_count;
146                 sm->blocks = calloc(params->blocks_count, sizeof(void*));
147                 if (!sm->blocks) {
148                         retval = EXT2_ET_NO_MEMORY;
149                         goto err_alloc;
150                 }
151         }
152         io->block_size = sm->block_size;
153         img_size = (uint64_t)sm->block_size * sm->blocks_count;
154
155         if (flags & IO_FLAG_RW) {
156                 sm->sparse_file = sparse_file_new(sm->block_size, img_size);
157                 if (!sm->sparse_file) {
158                         retval = EXT2_ET_NO_MEMORY;
159                         goto err_alloc;
160                 }
161                 if (params->fd < 0) {
162                         sm->fd = open(params->file, O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
163                                       0644);
164                         if (sm->fd < 0) {
165                                 retval = errno;
166                                 goto err_open;
167                         }
168                 } else
169                         sm->fd = params->fd;
170         } else {
171                 sm->fd = -1;
172                 sm->sparse_file = NULL;
173         }
174         return 0;
175
176 err_open:
177         sparse_file_destroy(sm->sparse_file);
178 err_alloc:
179         free_sparse_blocks(sm);
180 err_params:
181         free(sm);
182         return retval;
183 }
184
185 static errcode_t sparse_open_channel(struct sparse_io_params *sparse_params,
186                                      int flags, io_channel *channel)
187 {
188         errcode_t retval;
189         io_channel io;
190
191         io = calloc(1, sizeof(struct struct_io_channel));
192         io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
193         io->block_size = 0;
194         io->refcount = 1;
195
196         retval = io_manager_configure(sparse_params, flags, io);
197         if (retval) {
198                 free(io);
199                 return retval;
200         }
201
202         *channel = io;
203         return 0;
204 }
205
206 static errcode_t read_sparse_argv(const char *name, bool is_fd,
207                                   struct sparse_io_params *sparse_params)
208 {
209         int ret;
210         sparse_params->fd = -1;
211         sparse_params->block_size = 0;
212         sparse_params->blocks_count = 0;
213
214         sparse_params->file = malloc(strlen(name) + 1);
215         if (!sparse_params->file) {
216                 fprintf(stderr, "failed to alloc %zu\n", strlen(name) + 1);
217                 return EXT2_ET_NO_MEMORY;
218         }
219
220         if (is_fd) {
221                 ret = sscanf(name, "(%d):%llu:%u", &sparse_params->fd,
222                              (unsigned long long *)&sparse_params->blocks_count,
223                              &sparse_params->block_size);
224         } else {
225                 ret = sscanf(name, "(%[^)])%*[:]%llu%*[:]%u", sparse_params->file,
226                              (unsigned long long *)&sparse_params->blocks_count,
227                              &sparse_params->block_size);
228         }
229
230         if (ret < 1) {
231                 free(sparse_params->file);
232                 return -EINVAL;
233         }
234         return 0;
235 }
236
237 static errcode_t sparse_open(const char *name, int flags, io_channel *channel)
238 {
239         errcode_t retval;
240         struct sparse_io_params sparse_params;
241
242         retval = read_sparse_argv(name, false, &sparse_params);
243         if (retval)
244                 return EXT2_ET_BAD_DEVICE_NAME;
245
246         retval = sparse_open_channel(&sparse_params, flags, channel);
247         if (retval)
248                 return retval;
249         (*channel)->manager = sparse_io_manager;
250
251         return retval;
252 }
253
254 static errcode_t sparsefd_open(const char *name, int flags, io_channel *channel)
255 {
256         errcode_t retval;
257         struct sparse_io_params sparse_params;
258
259         retval = read_sparse_argv(name, true, &sparse_params);
260         if (retval)
261                 return EXT2_ET_BAD_DEVICE_NAME;
262
263         retval = sparse_open_channel(&sparse_params, flags, channel);
264         if (retval)
265                 return retval;
266         (*channel)->manager = sparsefd_io_manager;
267
268         return retval;
269 }
270
271 static errcode_t sparse_merge_blocks(struct sparse_map *sm, uint64_t start,
272                                         uint64_t num)
273 {
274         char *buf;
275         uint64_t i;
276         unsigned int block_size = sm->block_size;
277         errcode_t retval = 0;
278
279         buf = calloc(num, block_size);
280         if (!buf) {
281                 fprintf(stderr, "failed to alloc %llu\n",
282                         (unsigned long long)num * block_size);
283                 return EXT2_ET_NO_MEMORY;
284         }
285
286         for (i = 0; i < num; i++) {
287                 memcpy(buf + i * block_size, sm->blocks[start + i] , block_size);
288                 free(sm->blocks[start + i]);
289                 sm->blocks[start + i] = NULL;
290         }
291
292         /* free_sparse_blocks will release this buf. */
293         sm->blocks[start] = buf;
294
295         retval = sparse_file_add_data(sm->sparse_file, sm->blocks[start],
296                                         block_size * num, start);
297
298         return retval;
299 }
300
301 static errcode_t sparse_close_channel(io_channel channel)
302 {
303         uint64_t i;
304         errcode_t retval = 0;
305         struct sparse_map *sm = channel->private_data;
306
307         if (sm->sparse_file) {
308                 int64_t chunk_start = (sm->blocks[0] == NULL) ? -1 : 0;
309                 for (i = 0; i < sm->blocks_count; ++i) {
310                         if (!sm->blocks[i] && chunk_start != -1) {
311                                 retval = sparse_merge_blocks(sm, chunk_start, i - chunk_start);
312                                 chunk_start = -1;
313                         } else if (sm->blocks[i] && chunk_start == -1) {
314                                 chunk_start = i;
315                         }
316                         if (retval)
317                                 goto ret;
318                 }
319                 if (chunk_start != -1) {
320                         retval = sparse_merge_blocks(sm, chunk_start,
321                                                         sm->blocks_count - chunk_start);
322                         if (retval)
323                                 goto ret;
324                 }
325                 retval = sparse_file_write(sm->sparse_file, sm->fd,
326                                            /*gzip*/0, /*sparse*/1, /*crc*/0);
327         }
328
329 ret:
330         if (sm->sparse_file)
331                 sparse_file_destroy(sm->sparse_file);
332         free_sparse_blocks(sm);
333         free(sm->file);
334         free(sm);
335         free(channel);
336         return retval;
337 }
338
339 static errcode_t sparse_close(io_channel channel)
340 {
341         errcode_t retval;
342         struct sparse_map *sm = channel->private_data;
343         int fd = sm->fd;
344
345         retval = sparse_close_channel(channel);
346         if (fd >= 0)
347                 close(fd);
348
349         return retval;
350 }
351
352 static errcode_t sparse_set_blksize(io_channel channel, int blksize)
353 {
354         channel->block_size = blksize;
355         return 0;
356 }
357
358 static blk64_t block_to_sparse_block(blk64_t block, blk64_t *offset,
359                                io_channel channel, struct sparse_map *sm)
360 {
361         int ratio;
362         blk64_t ret = block;
363
364         ratio = sm->block_size / channel->block_size;
365         ret /= ratio;
366         *offset = (block % ratio) * channel->block_size;
367
368         return ret;
369 }
370
371 static errcode_t check_block_size(io_channel channel, struct sparse_map *sm)
372 {
373         if (sm->block_size >= channel->block_size)
374                 return 0;
375         return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
376 }
377
378 static errcode_t sparse_read_blk64(io_channel channel, blk64_t block,
379                                    int count, void *buf)
380 {
381         int i;
382         char *out = buf;
383         blk64_t offset = 0, cur_block;
384         struct sparse_map *sm = channel->private_data;
385
386         if (check_block_size(channel, sm))
387                 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
388
389         if (count < 0) { //partial read
390                 count = -count;
391                 cur_block = block_to_sparse_block(block, &offset, channel, sm);
392                 if (sm->blocks[cur_block])
393                         memcpy(out, (sm->blocks[cur_block]) + offset, count);
394                 else
395                         memset(out, 0, count);
396         } else {
397                 for (i = 0; i < count; ++i) {
398                         cur_block = block_to_sparse_block(block + i, &offset,
399                                                     channel, sm);
400                         if (sm->blocks[cur_block])
401                                 memcpy(out + (i * channel->block_size),
402                                        sm->blocks[cur_block] + offset,
403                                        channel->block_size);
404                         else if (sm->blocks)
405                                 memset(out + (i * channel->block_size), 0,
406                                        channel->block_size);
407                 }
408         }
409         return 0;
410 }
411
412 static errcode_t sparse_read_blk(io_channel channel, unsigned long block,
413                                  int count, void *buf)
414 {
415         return sparse_read_blk64(channel, block, count, buf);
416 }
417
418 static errcode_t sparse_write_blk64(io_channel channel, blk64_t block,
419                                     int count, const void *buf)
420 {
421         int i;
422         blk64_t offset = 0, cur_block;
423         const char *in = buf;
424         struct sparse_map *sm = channel->private_data;
425
426         if (check_block_size(channel, sm))
427                 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
428
429         if (count < 0) { //partial write
430                 count = -count;
431                 cur_block = block_to_sparse_block(block, &offset, channel,
432                                                   sm);
433                 if (!sm->blocks[cur_block]) {
434                         sm->blocks[cur_block] = calloc(1, sm->block_size);
435                         if (!sm->blocks[cur_block])
436                                 return EXT2_ET_NO_MEMORY;
437                 }
438                 memcpy(sm->blocks[cur_block] + offset, in, count);
439         } else {
440                 for (i = 0; i < count; ++i) {
441                         if (block + i >= sm->blocks_count)
442                                 return 0;
443                         cur_block = block_to_sparse_block(block + i, &offset,
444                                                     channel, sm);
445                         if (!sm->blocks[cur_block]) {
446                                 sm->blocks[cur_block] =
447                                         calloc(1, sm->block_size);
448                                 if (!sm->blocks[cur_block])
449                                         return EXT2_ET_NO_MEMORY;
450                         }
451                         memcpy(sm->blocks[cur_block] + offset,
452                                in + (i * channel->block_size),
453                                channel->block_size);
454                 }
455         }
456         return 0;
457 }
458
459 static errcode_t sparse_write_blk(io_channel channel, unsigned long block,
460                                   int count, const void *buf)
461 {
462         return sparse_write_blk64(channel, block, count, buf);
463 }
464
465 static errcode_t sparse_discard(io_channel channel __attribute__((unused)),
466                                 blk64_t blk, unsigned long long count)
467 {
468         blk64_t cur_block, offset;
469         struct sparse_map *sm = channel->private_data;
470
471         if (check_block_size(channel, sm))
472                 return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
473
474         for (unsigned long long i = 0; i < count; ++i) {
475                 if (blk + i >= sm->blocks_count)
476                         return 0;
477                 cur_block = block_to_sparse_block(blk + i, &offset, channel,
478                                                   sm);
479                 if (!sm->blocks[cur_block])
480                         continue;
481                 free(sm->blocks[cur_block]);
482                 sm->blocks[cur_block] = NULL;
483         }
484         return 0;
485 }
486
487 static errcode_t sparse_zeroout(io_channel channel, blk64_t blk,
488                                 unsigned long long count)
489 {
490         return sparse_discard(channel, blk, count);
491 }
492
493 static errcode_t sparse_flush(io_channel channel __attribute__((unused)))
494 {
495         return 0;
496 }
497
498 static errcode_t sparse_set_option(io_channel channel __attribute__((unused)),
499                                    const char *option __attribute__((unused)),
500                                    const char *arg __attribute__((unused)))
501 {
502         return 0;
503 }
504
505 static errcode_t sparse_cache_readahead(
506                         io_channel channel __attribute__((unused)),
507                         blk64_t blk __attribute__((unused)),
508                         unsigned long long count __attribute__((unused)))
509 {
510         return 0;
511 }
512
513 static struct struct_io_manager struct_sparse_manager = {
514         .magic                  = EXT2_ET_MAGIC_IO_MANAGER,
515         .name                   = "Android sparse I/O Manager",
516         .open                   = sparse_open,
517         .close                  = sparse_close,
518         .set_blksize            = sparse_set_blksize,
519         .read_blk               = sparse_read_blk,
520         .write_blk              = sparse_write_blk,
521         .flush                  = sparse_flush,
522         .write_byte             = NULL,
523         .set_option             = sparse_set_option,
524         .get_stats              = NULL,
525         .read_blk64             = sparse_read_blk64,
526         .write_blk64            = sparse_write_blk64,
527         .discard                = sparse_discard,
528         .cache_readahead        = sparse_cache_readahead,
529         .zeroout                = sparse_zeroout,
530 };
531
532 static struct struct_io_manager struct_sparsefd_manager = {
533         .magic                  = EXT2_ET_MAGIC_IO_MANAGER,
534         .name                   = "Android sparse fd I/O Manager",
535         .open                   = sparsefd_open,
536         .close                  = sparse_close,
537         .set_blksize            = sparse_set_blksize,
538         .read_blk               = sparse_read_blk,
539         .write_blk              = sparse_write_blk,
540         .flush                  = sparse_flush,
541         .write_byte             = NULL,
542         .set_option             = sparse_set_option,
543         .get_stats              = NULL,
544         .read_blk64             = sparse_read_blk64,
545         .write_blk64            = sparse_write_blk64,
546         .discard                = sparse_discard,
547         .cache_readahead        = sparse_cache_readahead,
548         .zeroout                = sparse_zeroout,
549 };
550
551 #endif
552
553 io_manager sparse_io_manager = &struct_sparse_manager;
554 io_manager sparsefd_io_manager = &struct_sparsefd_manager;