Home | History | Annotate | Download | only in lib
      1 /**
      2  * libf2fs.c
      3  *
      4  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
      5  *             http://www.samsung.com/
      6  *
      7  * Dual licensed under the GPL or LGPL version 2 licenses.
      8  */
      9 #define _LARGEFILE64_SOURCE
     10 
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <errno.h>
     15 #include <unistd.h>
     16 #include <fcntl.h>
     17 #ifdef HAVE_MNTENT_H
     18 #include <mntent.h>
     19 #endif
     20 #include <time.h>
     21 #ifndef ANDROID_WINDOWS_HOST
     22 #include <sys/stat.h>
     23 #include <sys/mount.h>
     24 #include <sys/ioctl.h>
     25 #endif
     26 #ifdef HAVE_LINUX_HDREG_H
     27 #include <linux/hdreg.h>
     28 #endif
     29 
     30 #include <f2fs_fs.h>
     31 
     32 struct f2fs_configuration c;
     33 
     34 #ifdef WITH_ANDROID
     35 #include <sparse/sparse.h>
     36 struct sparse_file *f2fs_sparse_file;
     37 static char **blocks;
     38 u_int64_t blocks_count;
     39 #endif
     40 
     41 static int __get_device_fd(__u64 *offset)
     42 {
     43 	__u64 blk_addr = *offset >> F2FS_BLKSIZE_BITS;
     44 	int i;
     45 
     46 	for (i = 0; i < c.ndevs; i++) {
     47 		if (c.devices[i].start_blkaddr <= blk_addr &&
     48 				c.devices[i].end_blkaddr >= blk_addr) {
     49 			*offset -=
     50 				c.devices[i].start_blkaddr << F2FS_BLKSIZE_BITS;
     51 			return c.devices[i].fd;
     52 		}
     53 	}
     54 	return -1;
     55 }
     56 
     57 #ifndef HAVE_LSEEK64
     58 typedef off_t	off64_t;
     59 
     60 static inline off64_t lseek64(int fd, __u64 offset, int set)
     61 {
     62 	return lseek(fd, offset, set);
     63 }
     64 #endif
     65 
     66 /*
     67  * IO interfaces
     68  */
     69 int dev_read_version(void *buf, __u64 offset, size_t len)
     70 {
     71 	if (c.sparse_mode)
     72 		return 0;
     73 	if (lseek64(c.kd, (off64_t)offset, SEEK_SET) < 0)
     74 		return -1;
     75 	if (read(c.kd, buf, len) < 0)
     76 		return -1;
     77 	return 0;
     78 }
     79 
     80 #ifdef WITH_ANDROID
     81 static int sparse_read_blk(__u64 block, int count, void *buf)
     82 {
     83 	int i;
     84 	char *out = buf;
     85 	__u64 cur_block;
     86 
     87 	for (i = 0; i < count; ++i) {
     88 		cur_block = block + i;
     89 		if (blocks[cur_block])
     90 			memcpy(out + (i * F2FS_BLKSIZE),
     91 					blocks[cur_block], F2FS_BLKSIZE);
     92 		else if (blocks)
     93 			memset(out + (i * F2FS_BLKSIZE), 0, F2FS_BLKSIZE);
     94 	}
     95 	return 0;
     96 }
     97 
     98 static int sparse_write_blk(__u64 block, int count, const void *buf)
     99 {
    100 	int i;
    101 	__u64 cur_block;
    102 	const char *in = buf;
    103 
    104 	for (i = 0; i < count; ++i) {
    105 		cur_block = block + i;
    106 		if (!blocks[cur_block]) {
    107 			blocks[cur_block] = calloc(1, F2FS_BLKSIZE);
    108 			if (!blocks[cur_block])
    109 				return -ENOMEM;
    110 		}
    111 		memcpy(blocks[cur_block], in + (i * F2FS_BLKSIZE),
    112 				F2FS_BLKSIZE);
    113 	}
    114 	return 0;
    115 }
    116 
    117 #ifdef SPARSE_CALLBACK_USES_SIZE_T
    118 static int sparse_import_segment(void *UNUSED(priv), const void *data,
    119 		size_t len, unsigned int block, unsigned int nr_blocks)
    120 #else
    121 static int sparse_import_segment(void *UNUSED(priv), const void *data, int len,
    122 		unsigned int block, unsigned int nr_blocks)
    123 #endif
    124 {
    125 	/* Ignore chunk headers, only write the data */
    126 	if (!nr_blocks || len % F2FS_BLKSIZE)
    127 		return 0;
    128 
    129 	return sparse_write_blk(block, nr_blocks, data);
    130 }
    131 
    132 static int sparse_merge_blocks(uint64_t start, uint64_t num)
    133 {
    134 	char *buf;
    135 	uint64_t i;
    136 
    137 	buf = calloc(num, F2FS_BLKSIZE);
    138 	if (!buf) {
    139 		fprintf(stderr, "failed to alloc %llu\n",
    140 			(unsigned long long)num * F2FS_BLKSIZE);
    141 		return -ENOMEM;
    142 	}
    143 
    144 	for (i = 0; i < num; i++) {
    145 		memcpy(buf + i * F2FS_BLKSIZE, blocks[start + i], F2FS_BLKSIZE);
    146 		free(blocks[start + i]);
    147 		blocks[start + i] = NULL;
    148 	}
    149 
    150 	/* free_sparse_blocks will release this buf. */
    151 	blocks[start] = buf;
    152 
    153 	return sparse_file_add_data(f2fs_sparse_file, blocks[start],
    154 					F2FS_BLKSIZE * num, start);
    155 }
    156 #else
    157 static int sparse_read_blk(__u64 block, int count, void *buf) { return 0; }
    158 static int sparse_write_blk(__u64 block, int count, const void *buf) { return 0; }
    159 #endif
    160 
    161 int dev_read(void *buf, __u64 offset, size_t len)
    162 {
    163 	int fd;
    164 
    165 	if (c.sparse_mode)
    166 		return sparse_read_blk(offset / F2FS_BLKSIZE,
    167 					len / F2FS_BLKSIZE, buf);
    168 
    169 	fd = __get_device_fd(&offset);
    170 	if (fd < 0)
    171 		return fd;
    172 
    173 	if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
    174 		return -1;
    175 	if (read(fd, buf, len) < 0)
    176 		return -1;
    177 	return 0;
    178 }
    179 
    180 #ifdef POSIX_FADV_WILLNEED
    181 int dev_readahead(__u64 offset, size_t len)
    182 #else
    183 int dev_readahead(__u64 offset, size_t UNUSED(len))
    184 #endif
    185 {
    186 	int fd = __get_device_fd(&offset);
    187 
    188 	if (fd < 0)
    189 		return fd;
    190 #ifdef POSIX_FADV_WILLNEED
    191 	return posix_fadvise(fd, offset, len, POSIX_FADV_WILLNEED);
    192 #else
    193 	return 0;
    194 #endif
    195 }
    196 
    197 int dev_write(void *buf, __u64 offset, size_t len)
    198 {
    199 	int fd;
    200 
    201 	if (c.dry_run)
    202 		return 0;
    203 
    204 	if (c.sparse_mode)
    205 		return sparse_write_blk(offset / F2FS_BLKSIZE,
    206 					len / F2FS_BLKSIZE, buf);
    207 
    208 	fd = __get_device_fd(&offset);
    209 	if (fd < 0)
    210 		return fd;
    211 
    212 	if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
    213 		return -1;
    214 	if (write(fd, buf, len) < 0)
    215 		return -1;
    216 	return 0;
    217 }
    218 
    219 int dev_write_block(void *buf, __u64 blk_addr)
    220 {
    221 	return dev_write(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
    222 }
    223 
    224 int dev_write_dump(void *buf, __u64 offset, size_t len)
    225 {
    226 	if (lseek64(c.dump_fd, (off64_t)offset, SEEK_SET) < 0)
    227 		return -1;
    228 	if (write(c.dump_fd, buf, len) < 0)
    229 		return -1;
    230 	return 0;
    231 }
    232 
    233 int dev_fill(void *buf, __u64 offset, size_t len)
    234 {
    235 	int fd;
    236 
    237 	if (c.sparse_mode)
    238 		return 0;
    239 
    240 	fd = __get_device_fd(&offset);
    241 	if (fd < 0)
    242 		return fd;
    243 
    244 	/* Only allow fill to zero */
    245 	if (*((__u8*)buf))
    246 		return -1;
    247 	if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
    248 		return -1;
    249 	if (write(fd, buf, len) < 0)
    250 		return -1;
    251 	return 0;
    252 }
    253 
    254 int dev_fill_block(void *buf, __u64 blk_addr)
    255 {
    256 	return dev_fill(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
    257 }
    258 
    259 int dev_read_block(void *buf, __u64 blk_addr)
    260 {
    261 	return dev_read(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
    262 }
    263 
    264 int dev_reada_block(__u64 blk_addr)
    265 {
    266 	return dev_readahead(blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
    267 }
    268 
    269 int f2fs_fsync_device(void)
    270 {
    271 #ifndef ANDROID_WINDOWS_HOST
    272 	int i;
    273 
    274 	for (i = 0; i < c.ndevs; i++) {
    275 		if (fsync(c.devices[i].fd) < 0) {
    276 			MSG(0, "\tError: Could not conduct fsync!!!\n");
    277 			return -1;
    278 		}
    279 	}
    280 #endif
    281 	return 0;
    282 }
    283 
    284 int f2fs_init_sparse_file(void)
    285 {
    286 #ifdef WITH_ANDROID
    287 	if (c.func == MKFS) {
    288 		f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, c.device_size);
    289 	} else {
    290 		f2fs_sparse_file = sparse_file_import(c.devices[0].fd,
    291 							true, false);
    292 		if (!f2fs_sparse_file)
    293 			return -1;
    294 
    295 		c.device_size = sparse_file_len(f2fs_sparse_file, 0, 0);
    296 		c.device_size &= (~((u_int64_t)(F2FS_BLKSIZE - 1)));
    297 	}
    298 
    299 	if (sparse_file_block_size(f2fs_sparse_file) != F2FS_BLKSIZE) {
    300 		MSG(0, "\tError: Corrupted sparse file\n");
    301 		return -1;
    302 	}
    303 	blocks_count = c.device_size / F2FS_BLKSIZE;
    304 	blocks = calloc(blocks_count, sizeof(char *));
    305 	if (!blocks) {
    306 		MSG(0, "\tError: Calloc Failed for blocks!!!\n");
    307 		return -1;
    308 	}
    309 
    310 	return sparse_file_foreach_chunk(f2fs_sparse_file, true, false,
    311 				sparse_import_segment, NULL);
    312 #else
    313 	MSG(0, "\tError: Sparse mode is only supported for android\n");
    314 	return -1;
    315 #endif
    316 }
    317 
    318 #define MAX_CHUNK_SIZE (1 * 1024 * 1024 * 1024ULL)
    319 int f2fs_finalize_device(void)
    320 {
    321 	int i;
    322 	int ret = 0;
    323 
    324 #ifdef WITH_ANDROID
    325 	if (c.sparse_mode) {
    326 		int64_t chunk_start = (blocks[0] == NULL) ? -1 : 0;
    327 		uint64_t j;
    328 
    329 		if (c.func != MKFS) {
    330 			sparse_file_destroy(f2fs_sparse_file);
    331 			ret = ftruncate(c.devices[0].fd, 0);
    332 			ASSERT(!ret);
    333 			lseek(c.devices[0].fd, 0, SEEK_SET);
    334 			f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE,
    335 							c.device_size);
    336 		}
    337 
    338 		for (j = 0; j < blocks_count; ++j) {
    339 			if (!blocks[j] && chunk_start != -1) {
    340 				ret = sparse_merge_blocks(chunk_start,
    341 							j - chunk_start);
    342 				chunk_start = -1;
    343 			} else if (blocks[j] && chunk_start == -1) {
    344 				chunk_start = j;
    345 			} else if (blocks[j] && (chunk_start != -1) &&
    346 				 (j + 1 - chunk_start >=
    347 					(MAX_CHUNK_SIZE / F2FS_BLKSIZE))) {
    348 				ret = sparse_merge_blocks(chunk_start,
    349 							  j + 1 - chunk_start);
    350 				chunk_start = -1;
    351 			}
    352 			ASSERT(!ret);
    353 		}
    354 		if (chunk_start != -1) {
    355 			ret = sparse_merge_blocks(chunk_start,
    356 						blocks_count - chunk_start);
    357 			ASSERT(!ret);
    358 		}
    359 
    360 		sparse_file_write(f2fs_sparse_file, c.devices[0].fd,
    361 				/*gzip*/0, /*sparse*/1, /*crc*/0);
    362 
    363 		sparse_file_destroy(f2fs_sparse_file);
    364 		for (j = 0; j < blocks_count; j++)
    365 			free(blocks[j]);
    366 		free(blocks);
    367 		blocks = NULL;
    368 		f2fs_sparse_file = NULL;
    369 	}
    370 #endif
    371 	/*
    372 	 * We should call fsync() to flush out all the dirty pages
    373 	 * in the block device page cache.
    374 	 */
    375 	for (i = 0; i < c.ndevs; i++) {
    376 #ifndef ANDROID_WINDOWS_HOST
    377 		ret = fsync(c.devices[i].fd);
    378 		if (ret < 0) {
    379 			MSG(0, "\tError: Could not conduct fsync!!!\n");
    380 			break;
    381 		}
    382 #endif
    383 		ret = close(c.devices[i].fd);
    384 		if (ret < 0) {
    385 			MSG(0, "\tError: Failed to close device file!!!\n");
    386 			break;
    387 		}
    388 		free(c.devices[i].path);
    389 	}
    390 	close(c.kd);
    391 
    392 	return ret;
    393 }
    394