1 /**
2 * libf2fs.c
3 *
4 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
5 * http://www.samsung.com/
6 * Copyright (c) 2019 Google Inc.
7 * http://www.google.com/
8 *
9 * Dual licensed under the GPL or LGPL version 2 licenses.
10 */
11 #define _LARGEFILE64_SOURCE
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #ifdef HAVE_MNTENT_H
20 #include <mntent.h>
21 #endif
22 #include <time.h>
23 #ifndef ANDROID_WINDOWS_HOST
24 #include <sys/stat.h>
25 #include <sys/mount.h>
26 #include <sys/ioctl.h>
27 #endif
28 #ifdef HAVE_LINUX_HDREG_H
29 #include <linux/hdreg.h>
30 #endif
31
32 #include <stdbool.h>
33 #include <assert.h>
34 #include <inttypes.h>
35 #include "f2fs_fs.h"
36
37 struct f2fs_configuration c;
38
39 #ifdef WITH_ANDROID
40 #include <sparse/sparse.h>
41 struct sparse_file *f2fs_sparse_file;
42 static char **blocks;
43 u_int64_t blocks_count;
44 static char *zeroed_block;
45 #endif
46
__get_device_fd(__u64 * offset)47 static int __get_device_fd(__u64 *offset)
48 {
49 __u64 blk_addr = *offset >> F2FS_BLKSIZE_BITS;
50 int i;
51
52 for (i = 0; i < c.ndevs; i++) {
53 if (c.devices[i].start_blkaddr <= blk_addr &&
54 c.devices[i].end_blkaddr >= blk_addr) {
55 *offset -=
56 c.devices[i].start_blkaddr << F2FS_BLKSIZE_BITS;
57 return c.devices[i].fd;
58 }
59 }
60 return -1;
61 }
62
63 #ifndef HAVE_LSEEK64
64 typedef off_t off64_t;
65
lseek64(int fd,__u64 offset,int set)66 static inline off64_t lseek64(int fd, __u64 offset, int set)
67 {
68 return lseek(fd, offset, set);
69 }
70 #endif
71
72 /* ---------- dev_cache, Least Used First (LUF) policy ------------------- */
73 /*
74 * Least used block will be the first victim to be replaced when max hash
75 * collision exceeds
76 */
77 static bool *dcache_valid; /* is the cached block valid? */
78 static off64_t *dcache_blk; /* which block it cached */
79 static uint64_t *dcache_lastused; /* last used ticks for cache entries */
80 static char *dcache_buf; /* cached block data */
81 static uint64_t dcache_usetick; /* current use tick */
82
83 static uint64_t dcache_raccess;
84 static uint64_t dcache_rhit;
85 static uint64_t dcache_rmiss;
86 static uint64_t dcache_rreplace;
87
88 static bool dcache_exit_registered = false;
89
90 /*
91 * Shadow config:
92 *
93 * Active set of the configurations.
94 * Global configuration 'dcache_config' will be transferred here when
95 * when dcache_init() is called
96 */
97 static dev_cache_config_t dcache_config = {0, 16, 1};
98 static bool dcache_initialized = false;
99
100 #define MIN_NUM_CACHE_ENTRY 1024L
101 #define MAX_MAX_HASH_COLLISION 16
102
103 static long dcache_relocate_offset0[] = {
104 20, -20, 40, -40, 80, -80, 160, -160,
105 320, -320, 640, -640, 1280, -1280, 2560, -2560,
106 };
107 static int dcache_relocate_offset[16];
108
dcache_print_statistics(void)109 static void dcache_print_statistics(void)
110 {
111 long i;
112 long useCnt;
113
114 /* Number of used cache entries */
115 useCnt = 0;
116 for (i = 0; i < dcache_config.num_cache_entry; i++)
117 if (dcache_valid[i])
118 ++useCnt;
119
120 /*
121 * c: number of cache entries
122 * u: used entries
123 * RA: number of read access blocks
124 * CH: cache hit
125 * CM: cache miss
126 * Repl: read cache replaced
127 */
128 printf ("\nc, u, RA, CH, CM, Repl=\n");
129 printf ("%ld %ld %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
130 dcache_config.num_cache_entry,
131 useCnt,
132 dcache_raccess,
133 dcache_rhit,
134 dcache_rmiss,
135 dcache_rreplace);
136 }
137
dcache_release(void)138 void dcache_release(void)
139 {
140 if (!dcache_initialized)
141 return;
142
143 dcache_initialized = false;
144
145 if (c.cache_config.dbg_en)
146 dcache_print_statistics();
147
148 if (dcache_blk != NULL)
149 free(dcache_blk);
150 if (dcache_lastused != NULL)
151 free(dcache_lastused);
152 if (dcache_buf != NULL)
153 free(dcache_buf);
154 if (dcache_valid != NULL)
155 free(dcache_valid);
156 dcache_config.num_cache_entry = 0;
157 dcache_blk = NULL;
158 dcache_lastused = NULL;
159 dcache_buf = NULL;
160 dcache_valid = NULL;
161 }
162
163 // return 0 for success, error code for failure.
dcache_alloc_all(long n)164 static int dcache_alloc_all(long n)
165 {
166 if (n <= 0)
167 return -1;
168 if ((dcache_blk = (off64_t *) malloc(sizeof(off64_t) * n)) == NULL
169 || (dcache_lastused = (uint64_t *)
170 malloc(sizeof(uint64_t) * n)) == NULL
171 || (dcache_buf = (char *) malloc (F2FS_BLKSIZE * n)) == NULL
172 || (dcache_valid = (bool *) malloc(sizeof(bool) * n)) == NULL)
173 {
174 dcache_release();
175 return -1;
176 }
177 dcache_config.num_cache_entry = n;
178 return 0;
179 }
180
dcache_relocate_init(void)181 static void dcache_relocate_init(void)
182 {
183 int i;
184 int n0 = (sizeof(dcache_relocate_offset0)
185 / sizeof(dcache_relocate_offset0[0]));
186 int n = (sizeof(dcache_relocate_offset)
187 / sizeof(dcache_relocate_offset[0]));
188
189 ASSERT(n == n0);
190 for (i = 0; i < n && i < dcache_config.max_hash_collision; i++) {
191 if (labs(dcache_relocate_offset0[i])
192 > dcache_config.num_cache_entry / 2) {
193 dcache_config.max_hash_collision = i;
194 break;
195 }
196 dcache_relocate_offset[i] =
197 dcache_config.num_cache_entry
198 + dcache_relocate_offset0[i];
199 }
200 }
201
dcache_init(void)202 void dcache_init(void)
203 {
204 long n;
205
206 if (c.cache_config.num_cache_entry <= 0)
207 return;
208
209 /* release previous cache init, if any */
210 dcache_release();
211
212 dcache_blk = NULL;
213 dcache_lastused = NULL;
214 dcache_buf = NULL;
215 dcache_valid = NULL;
216
217 dcache_config = c.cache_config;
218
219 n = max(MIN_NUM_CACHE_ENTRY, dcache_config.num_cache_entry);
220
221 /* halve alloc size until alloc succeed, or min cache reached */
222 while (dcache_alloc_all(n) != 0 && n != MIN_NUM_CACHE_ENTRY)
223 n = max(MIN_NUM_CACHE_ENTRY, n/2);
224
225 /* must be the last: data dependent on num_cache_entry */
226 dcache_relocate_init();
227 dcache_initialized = true;
228
229 if (!dcache_exit_registered) {
230 dcache_exit_registered = true;
231 atexit(dcache_release); /* auto release */
232 }
233
234 dcache_raccess = 0;
235 dcache_rhit = 0;
236 dcache_rmiss = 0;
237 dcache_rreplace = 0;
238 }
239
dcache_addr(long entry)240 static inline char *dcache_addr(long entry)
241 {
242 return dcache_buf + F2FS_BLKSIZE * entry;
243 }
244
245 /* relocate on (n+1)-th collision */
dcache_relocate(long entry,int n)246 static inline long dcache_relocate(long entry, int n)
247 {
248 assert(dcache_config.num_cache_entry != 0);
249 return (entry + dcache_relocate_offset[n]) %
250 dcache_config.num_cache_entry;
251 }
252
dcache_find(off64_t blk)253 static long dcache_find(off64_t blk)
254 {
255 register long n = dcache_config.num_cache_entry;
256 register unsigned m = dcache_config.max_hash_collision;
257 long entry, least_used, target;
258 unsigned try;
259
260 assert(n > 0);
261 target = least_used = entry = blk % n; /* simple modulo hash */
262
263 for (try = 0; try < m; try++) {
264 if (!dcache_valid[target] || dcache_blk[target] == blk)
265 return target; /* found target or empty cache slot */
266 if (dcache_lastused[target] < dcache_lastused[least_used])
267 least_used = target;
268 target = dcache_relocate(entry, try); /* next target */
269 }
270 return least_used; /* max search reached, return least used slot */
271 }
272
273 /* Physical read into cache */
dcache_io_read(int fd,long entry,off64_t offset,off64_t blk)274 static int dcache_io_read(int fd, long entry, off64_t offset, off64_t blk)
275 {
276 if (lseek64(fd, offset, SEEK_SET) < 0) {
277 MSG(0, "\n lseek64 fail.\n");
278 return -1;
279 }
280 if (read(fd, dcache_buf + entry * F2FS_BLKSIZE, F2FS_BLKSIZE) < 0) {
281 MSG(0, "\n read() fail.\n");
282 return -1;
283 }
284 dcache_lastused[entry] = ++dcache_usetick;
285 dcache_valid[entry] = true;
286 dcache_blk[entry] = blk;
287 return 0;
288 }
289
290 /*
291 * - Note: Read/Write are not symmetric:
292 * For read, we need to do it block by block, due to the cache nature:
293 * some blocks may be cached, and others don't.
294 * For write, since we always do a write-thru, we can join all writes into one,
295 * and write it once at the caller. This function updates the cache for write, but
296 * not the do a physical write. The caller is responsible for the physical write.
297 * - Note: We concentrate read/write together, due to the fact of similar structure to find
298 * the relavant cache entries
299 * - Return values:
300 * 0: success
301 * 1: cache not available (uninitialized)
302 * -1: error
303 */
dcache_update_rw(int fd,void * buf,off64_t offset,size_t byte_count,bool is_write)304 static int dcache_update_rw(int fd, void *buf, off64_t offset,
305 size_t byte_count, bool is_write)
306 {
307 off64_t blk;
308 int addr_in_blk;
309 off64_t start;
310
311 if (!dcache_initialized)
312 dcache_init(); /* auto initialize */
313
314 if (!dcache_initialized)
315 return 1; /* not available */
316
317 blk = offset / F2FS_BLKSIZE;
318 addr_in_blk = offset % F2FS_BLKSIZE;
319 start = blk * F2FS_BLKSIZE;
320
321 while (byte_count != 0) {
322 size_t cur_size = min(byte_count,
323 (size_t)(F2FS_BLKSIZE - addr_in_blk));
324 long entry = dcache_find(blk);
325
326 if (!is_write)
327 ++dcache_raccess;
328
329 if (dcache_valid[entry] && dcache_blk[entry] == blk) {
330 /* cache hit */
331 if (is_write) /* write: update cache */
332 memcpy(dcache_addr(entry) + addr_in_blk,
333 buf, cur_size);
334 else
335 ++dcache_rhit;
336 } else {
337 /* cache miss */
338 if (!is_write) {
339 int err;
340 ++dcache_rmiss;
341 if (dcache_valid[entry])
342 ++dcache_rreplace;
343 /* read: physical I/O read into cache */
344 err = dcache_io_read(fd, entry, start, blk);
345 if (err)
346 return err;
347 }
348 }
349
350 /* read: copy data from cache */
351 /* write: nothing to do, since we don't do physical write. */
352 if (!is_write)
353 memcpy(buf, dcache_addr(entry) + addr_in_blk,
354 cur_size);
355
356 /* next block */
357 ++blk;
358 buf += cur_size;
359 start += F2FS_BLKSIZE;
360 byte_count -= cur_size;
361 addr_in_blk = 0;
362 }
363 return 0;
364 }
365
366 /*
367 * dcache_update_cache() just update cache, won't do physical I/O.
368 * Thus even no error, we need normal non-cache I/O for actual write
369 *
370 * return value: 1: cache not available
371 * 0: success, -1: I/O error
372 */
dcache_update_cache(int fd,void * buf,off64_t offset,size_t count)373 int dcache_update_cache(int fd, void *buf, off64_t offset, size_t count)
374 {
375 return dcache_update_rw(fd, buf, offset, count, true);
376 }
377
378 /* handles read into cache + read into buffer */
dcache_read(int fd,void * buf,off64_t offset,size_t count)379 int dcache_read(int fd, void *buf, off64_t offset, size_t count)
380 {
381 return dcache_update_rw(fd, buf, offset, count, false);
382 }
383
384 /*
385 * IO interfaces
386 */
dev_read_version(void * buf,__u64 offset,size_t len)387 int dev_read_version(void *buf, __u64 offset, size_t len)
388 {
389 if (c.sparse_mode)
390 return 0;
391 if (lseek64(c.kd, (off64_t)offset, SEEK_SET) < 0)
392 return -1;
393 if (read(c.kd, buf, len) < 0)
394 return -1;
395 return 0;
396 }
397
398 #ifdef WITH_ANDROID
sparse_read_blk(__u64 block,int count,void * buf)399 static int sparse_read_blk(__u64 block, int count, void *buf)
400 {
401 int i;
402 char *out = buf;
403 __u64 cur_block;
404
405 for (i = 0; i < count; ++i) {
406 cur_block = block + i;
407 if (blocks[cur_block])
408 memcpy(out + (i * F2FS_BLKSIZE),
409 blocks[cur_block], F2FS_BLKSIZE);
410 else if (blocks)
411 memset(out + (i * F2FS_BLKSIZE), 0, F2FS_BLKSIZE);
412 }
413 return 0;
414 }
415
sparse_write_blk(__u64 block,int count,const void * buf)416 static int sparse_write_blk(__u64 block, int count, const void *buf)
417 {
418 int i;
419 __u64 cur_block;
420 const char *in = buf;
421
422 for (i = 0; i < count; ++i) {
423 cur_block = block + i;
424 if (blocks[cur_block] == zeroed_block)
425 blocks[cur_block] = NULL;
426 if (!blocks[cur_block]) {
427 blocks[cur_block] = calloc(1, F2FS_BLKSIZE);
428 if (!blocks[cur_block])
429 return -ENOMEM;
430 }
431 memcpy(blocks[cur_block], in + (i * F2FS_BLKSIZE),
432 F2FS_BLKSIZE);
433 }
434 return 0;
435 }
436
sparse_write_zeroed_blk(__u64 block,int count)437 static int sparse_write_zeroed_blk(__u64 block, int count)
438 {
439 int i;
440 __u64 cur_block;
441
442 for (i = 0; i < count; ++i) {
443 cur_block = block + i;
444 if (blocks[cur_block])
445 continue;
446 blocks[cur_block] = zeroed_block;
447 }
448 return 0;
449 }
450
451 #ifdef SPARSE_CALLBACK_USES_SIZE_T
sparse_import_segment(void * UNUSED (priv),const void * data,size_t len,unsigned int block,unsigned int nr_blocks)452 static int sparse_import_segment(void *UNUSED(priv), const void *data,
453 size_t len, unsigned int block, unsigned int nr_blocks)
454 #else
455 static int sparse_import_segment(void *UNUSED(priv), const void *data, int len,
456 unsigned int block, unsigned int nr_blocks)
457 #endif
458 {
459 /* Ignore chunk headers, only write the data */
460 if (!nr_blocks || len % F2FS_BLKSIZE)
461 return 0;
462
463 return sparse_write_blk(block, nr_blocks, data);
464 }
465
sparse_merge_blocks(uint64_t start,uint64_t num,int zero)466 static int sparse_merge_blocks(uint64_t start, uint64_t num, int zero)
467 {
468 char *buf;
469 uint64_t i;
470
471 if (zero) {
472 blocks[start] = NULL;
473 return sparse_file_add_fill(f2fs_sparse_file, 0x0,
474 F2FS_BLKSIZE * num, start);
475 }
476
477 buf = calloc(num, F2FS_BLKSIZE);
478 if (!buf) {
479 fprintf(stderr, "failed to alloc %llu\n",
480 (unsigned long long)num * F2FS_BLKSIZE);
481 return -ENOMEM;
482 }
483
484 for (i = 0; i < num; i++) {
485 memcpy(buf + i * F2FS_BLKSIZE, blocks[start + i], F2FS_BLKSIZE);
486 free(blocks[start + i]);
487 blocks[start + i] = NULL;
488 }
489
490 /* free_sparse_blocks will release this buf. */
491 blocks[start] = buf;
492
493 return sparse_file_add_data(f2fs_sparse_file, blocks[start],
494 F2FS_BLKSIZE * num, start);
495 }
496 #else
sparse_read_blk(__u64 block,int count,void * buf)497 static int sparse_read_blk(__u64 block, int count, void *buf) { return 0; }
sparse_write_blk(__u64 block,int count,const void * buf)498 static int sparse_write_blk(__u64 block, int count, const void *buf) { return 0; }
sparse_write_zeroed_blk(__u64 block,int count)499 static int sparse_write_zeroed_blk(__u64 block, int count) { return 0; }
500 #endif
501
dev_read(void * buf,__u64 offset,size_t len)502 int dev_read(void *buf, __u64 offset, size_t len)
503 {
504 int fd;
505 int err;
506
507 if (c.sparse_mode)
508 return sparse_read_blk(offset / F2FS_BLKSIZE,
509 len / F2FS_BLKSIZE, buf);
510
511 fd = __get_device_fd(&offset);
512 if (fd < 0)
513 return fd;
514
515 /* err = 1: cache not available, fall back to non-cache R/W */
516 /* err = 0: success, err=-1: I/O error */
517 err = dcache_read(fd, buf, (off64_t)offset, len);
518 if (err <= 0)
519 return err;
520 if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
521 return -1;
522 if (read(fd, buf, len) < 0)
523 return -1;
524 return 0;
525 }
526
527 #ifdef POSIX_FADV_WILLNEED
dev_readahead(__u64 offset,size_t len)528 int dev_readahead(__u64 offset, size_t len)
529 #else
530 int dev_readahead(__u64 offset, size_t UNUSED(len))
531 #endif
532 {
533 int fd = __get_device_fd(&offset);
534
535 if (fd < 0)
536 return fd;
537 #ifdef POSIX_FADV_WILLNEED
538 return posix_fadvise(fd, offset, len, POSIX_FADV_WILLNEED);
539 #else
540 return 0;
541 #endif
542 }
543
dev_write(void * buf,__u64 offset,size_t len)544 int dev_write(void *buf, __u64 offset, size_t len)
545 {
546 int fd;
547
548 if (c.dry_run)
549 return 0;
550
551 if (c.sparse_mode)
552 return sparse_write_blk(offset / F2FS_BLKSIZE,
553 len / F2FS_BLKSIZE, buf);
554
555 fd = __get_device_fd(&offset);
556 if (fd < 0)
557 return fd;
558
559 /*
560 * dcache_update_cache() just update cache, won't do I/O.
561 * Thus even no error, we need normal non-cache I/O for actual write
562 */
563 if (dcache_update_cache(fd, buf, (off64_t)offset, len) < 0)
564 return -1;
565 if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
566 return -1;
567 if (write(fd, buf, len) < 0)
568 return -1;
569 return 0;
570 }
571
dev_write_block(void * buf,__u64 blk_addr)572 int dev_write_block(void *buf, __u64 blk_addr)
573 {
574 return dev_write(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
575 }
576
dev_write_dump(void * buf,__u64 offset,size_t len)577 int dev_write_dump(void *buf, __u64 offset, size_t len)
578 {
579 if (lseek64(c.dump_fd, (off64_t)offset, SEEK_SET) < 0)
580 return -1;
581 if (write(c.dump_fd, buf, len) < 0)
582 return -1;
583 return 0;
584 }
585
dev_fill(void * buf,__u64 offset,size_t len)586 int dev_fill(void *buf, __u64 offset, size_t len)
587 {
588 int fd;
589
590 if (c.sparse_mode)
591 return sparse_write_zeroed_blk(offset / F2FS_BLKSIZE,
592 len / F2FS_BLKSIZE);
593
594 fd = __get_device_fd(&offset);
595 if (fd < 0)
596 return fd;
597
598 /* Only allow fill to zero */
599 if (*((__u8*)buf))
600 return -1;
601 if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
602 return -1;
603 if (write(fd, buf, len) < 0)
604 return -1;
605 return 0;
606 }
607
dev_fill_block(void * buf,__u64 blk_addr)608 int dev_fill_block(void *buf, __u64 blk_addr)
609 {
610 return dev_fill(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
611 }
612
dev_read_block(void * buf,__u64 blk_addr)613 int dev_read_block(void *buf, __u64 blk_addr)
614 {
615 return dev_read(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
616 }
617
dev_reada_block(__u64 blk_addr)618 int dev_reada_block(__u64 blk_addr)
619 {
620 return dev_readahead(blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
621 }
622
f2fs_fsync_device(void)623 int f2fs_fsync_device(void)
624 {
625 #ifndef ANDROID_WINDOWS_HOST
626 int i;
627
628 for (i = 0; i < c.ndevs; i++) {
629 if (fsync(c.devices[i].fd) < 0) {
630 MSG(0, "\tError: Could not conduct fsync!!!\n");
631 return -1;
632 }
633 }
634 #endif
635 return 0;
636 }
637
f2fs_init_sparse_file(void)638 int f2fs_init_sparse_file(void)
639 {
640 #ifdef WITH_ANDROID
641 if (c.func == MKFS) {
642 f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, c.device_size);
643 if (!f2fs_sparse_file)
644 return -1;
645 } else {
646 f2fs_sparse_file = sparse_file_import(c.devices[0].fd,
647 true, false);
648 if (!f2fs_sparse_file)
649 return -1;
650
651 c.device_size = sparse_file_len(f2fs_sparse_file, 0, 0);
652 c.device_size &= (~((u_int64_t)(F2FS_BLKSIZE - 1)));
653 }
654
655 if (sparse_file_block_size(f2fs_sparse_file) != F2FS_BLKSIZE) {
656 MSG(0, "\tError: Corrupted sparse file\n");
657 return -1;
658 }
659 blocks_count = c.device_size / F2FS_BLKSIZE;
660 blocks = calloc(blocks_count, sizeof(char *));
661 if (!blocks) {
662 MSG(0, "\tError: Calloc Failed for blocks!!!\n");
663 return -1;
664 }
665
666 zeroed_block = calloc(1, F2FS_BLKSIZE);
667 if (!zeroed_block) {
668 MSG(0, "\tError: Calloc Failed for zeroed block!!!\n");
669 return -1;
670 }
671
672 return sparse_file_foreach_chunk(f2fs_sparse_file, true, false,
673 sparse_import_segment, NULL);
674 #else
675 MSG(0, "\tError: Sparse mode is only supported for android\n");
676 return -1;
677 #endif
678 }
679
f2fs_release_sparse_resource(void)680 void f2fs_release_sparse_resource(void)
681 {
682 #ifdef WITH_ANDROID
683 int j;
684
685 if (c.sparse_mode) {
686 if (f2fs_sparse_file != NULL) {
687 sparse_file_destroy(f2fs_sparse_file);
688 f2fs_sparse_file = NULL;
689 }
690 for (j = 0; j < blocks_count; j++)
691 free(blocks[j]);
692 free(blocks);
693 blocks = NULL;
694 free(zeroed_block);
695 zeroed_block = NULL;
696 }
697 #endif
698 }
699
700 #define MAX_CHUNK_SIZE (1 * 1024 * 1024 * 1024ULL)
701 #define MAX_CHUNK_COUNT (MAX_CHUNK_SIZE / F2FS_BLKSIZE)
f2fs_finalize_device(void)702 int f2fs_finalize_device(void)
703 {
704 int i;
705 int ret = 0;
706
707 #ifdef WITH_ANDROID
708 if (c.sparse_mode) {
709 int64_t chunk_start = (blocks[0] == NULL) ? -1 : 0;
710 uint64_t j;
711
712 if (c.func != MKFS) {
713 sparse_file_destroy(f2fs_sparse_file);
714 ret = ftruncate(c.devices[0].fd, 0);
715 ASSERT(!ret);
716 lseek(c.devices[0].fd, 0, SEEK_SET);
717 f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE,
718 c.device_size);
719 }
720
721 for (j = 0; j < blocks_count; ++j) {
722 if (chunk_start != -1) {
723 if (j - chunk_start >= MAX_CHUNK_COUNT) {
724 ret = sparse_merge_blocks(chunk_start,
725 j - chunk_start, 0);
726 ASSERT(!ret);
727 chunk_start = -1;
728 }
729 }
730
731 if (chunk_start == -1) {
732 if (!blocks[j])
733 continue;
734
735 if (blocks[j] == zeroed_block) {
736 ret = sparse_merge_blocks(j, 1, 1);
737 ASSERT(!ret);
738 } else {
739 chunk_start = j;
740 }
741 } else {
742 if (blocks[j] && blocks[j] != zeroed_block)
743 continue;
744
745 ret = sparse_merge_blocks(chunk_start,
746 j - chunk_start, 0);
747 ASSERT(!ret);
748
749 if (blocks[j] == zeroed_block) {
750 ret = sparse_merge_blocks(j, 1, 1);
751 ASSERT(!ret);
752 }
753
754 chunk_start = -1;
755 }
756 }
757 if (chunk_start != -1) {
758 ret = sparse_merge_blocks(chunk_start,
759 blocks_count - chunk_start, 0);
760 ASSERT(!ret);
761 }
762
763 sparse_file_write(f2fs_sparse_file, c.devices[0].fd,
764 /*gzip*/0, /*sparse*/1, /*crc*/0);
765
766 f2fs_release_sparse_resource();
767 }
768 #endif
769 /*
770 * We should call fsync() to flush out all the dirty pages
771 * in the block device page cache.
772 */
773 for (i = 0; i < c.ndevs; i++) {
774 #ifndef ANDROID_WINDOWS_HOST
775 ret = fsync(c.devices[i].fd);
776 if (ret < 0) {
777 MSG(0, "\tError: Could not conduct fsync!!!\n");
778 break;
779 }
780 #endif
781 ret = close(c.devices[i].fd);
782 if (ret < 0) {
783 MSG(0, "\tError: Failed to close device file!!!\n");
784 break;
785 }
786 free(c.devices[i].path);
787 }
788 close(c.kd);
789
790 return ret;
791 }
792