• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
__get_device_fd(__u64 * offset)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 
lseek64(int fd,__u64 offset,int set)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  */
dev_read_version(void * buf,__u64 offset,size_t len)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
sparse_read_blk(__u64 block,int count,void * buf)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 
sparse_write_blk(__u64 block,int count,const void * buf)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
sparse_import_segment(void * UNUSED (priv),const void * data,size_t len,unsigned int block,unsigned int nr_blocks)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 
sparse_merge_blocks(uint64_t start,uint64_t num)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
sparse_read_blk(__u64 block,int count,void * buf)157 static int sparse_read_blk(__u64 block, int count, void *buf) { return 0; }
sparse_write_blk(__u64 block,int count,const void * buf)158 static int sparse_write_blk(__u64 block, int count, const void *buf) { return 0; }
159 #endif
160 
dev_read(void * buf,__u64 offset,size_t len)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
dev_readahead(__u64 offset,size_t len)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 
dev_write(void * buf,__u64 offset,size_t len)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 
dev_write_block(void * buf,__u64 blk_addr)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 
dev_write_dump(void * buf,__u64 offset,size_t len)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 
dev_fill(void * buf,__u64 offset,size_t len)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 
dev_fill_block(void * buf,__u64 blk_addr)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 
dev_read_block(void * buf,__u64 blk_addr)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 
dev_reada_block(__u64 blk_addr)264 int dev_reada_block(__u64 blk_addr)
265 {
266 	return dev_readahead(blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
267 }
268 
f2fs_fsync_device(void)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 
f2fs_init_sparse_file(void)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)
f2fs_finalize_device(void)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