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
sparse_import_segment(void * UNUSED (priv),const void * data,int len,unsigned int block,unsigned int nr_blocks)117 static int sparse_import_segment(void *UNUSED(priv), const void *data, int len,
118 unsigned int block, unsigned int nr_blocks)
119 {
120 /* Ignore chunk headers, only write the data */
121 if (!nr_blocks || len % F2FS_BLKSIZE)
122 return 0;
123
124 return sparse_write_blk(block, nr_blocks, data);
125 }
126
sparse_merge_blocks(uint64_t start,uint64_t num)127 static int sparse_merge_blocks(uint64_t start, uint64_t num)
128 {
129 char *buf;
130 uint64_t i;
131
132 buf = calloc(num, F2FS_BLKSIZE);
133 if (!buf) {
134 fprintf(stderr, "failed to alloc %llu\n",
135 (unsigned long long)num * F2FS_BLKSIZE);
136 return -ENOMEM;
137 }
138
139 for (i = 0; i < num; i++) {
140 memcpy(buf + i * F2FS_BLKSIZE, blocks[start + i], F2FS_BLKSIZE);
141 free(blocks[start + i]);
142 blocks[start + i] = NULL;
143 }
144
145 /* free_sparse_blocks will release this buf. */
146 blocks[start] = buf;
147
148 return sparse_file_add_data(f2fs_sparse_file, blocks[start],
149 F2FS_BLKSIZE * num, start);
150 }
151 #else
sparse_read_blk(__u64 block,int count,void * buf)152 static int sparse_read_blk(__u64 block, int count, void *buf) { return 0; }
sparse_write_blk(__u64 block,int count,const void * buf)153 static int sparse_write_blk(__u64 block, int count, const void *buf) { return 0; }
154 #endif
155
dev_read(void * buf,__u64 offset,size_t len)156 int dev_read(void *buf, __u64 offset, size_t len)
157 {
158 int fd;
159
160 if (c.sparse_mode)
161 return sparse_read_blk(offset / F2FS_BLKSIZE,
162 len / F2FS_BLKSIZE, buf);
163
164 fd = __get_device_fd(&offset);
165 if (fd < 0)
166 return fd;
167
168 if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
169 return -1;
170 if (read(fd, buf, len) < 0)
171 return -1;
172 return 0;
173 }
174
175 #ifdef POSIX_FADV_WILLNEED
dev_readahead(__u64 offset,size_t len)176 int dev_readahead(__u64 offset, size_t len)
177 #else
178 int dev_readahead(__u64 offset, size_t UNUSED(len))
179 #endif
180 {
181 int fd = __get_device_fd(&offset);
182
183 if (fd < 0)
184 return fd;
185 #ifdef POSIX_FADV_WILLNEED
186 return posix_fadvise(fd, offset, len, POSIX_FADV_WILLNEED);
187 #else
188 return 0;
189 #endif
190 }
191
dev_write(void * buf,__u64 offset,size_t len)192 int dev_write(void *buf, __u64 offset, size_t len)
193 {
194 int fd;
195
196 if (c.dry_run)
197 return 0;
198
199 if (c.sparse_mode)
200 return sparse_write_blk(offset / F2FS_BLKSIZE,
201 len / F2FS_BLKSIZE, buf);
202
203 fd = __get_device_fd(&offset);
204 if (fd < 0)
205 return fd;
206
207 if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
208 return -1;
209 if (write(fd, buf, len) < 0)
210 return -1;
211 return 0;
212 }
213
dev_write_block(void * buf,__u64 blk_addr)214 int dev_write_block(void *buf, __u64 blk_addr)
215 {
216 return dev_write(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
217 }
218
dev_write_dump(void * buf,__u64 offset,size_t len)219 int dev_write_dump(void *buf, __u64 offset, size_t len)
220 {
221 if (lseek64(c.dump_fd, (off64_t)offset, SEEK_SET) < 0)
222 return -1;
223 if (write(c.dump_fd, buf, len) < 0)
224 return -1;
225 return 0;
226 }
227
dev_fill(void * buf,__u64 offset,size_t len)228 int dev_fill(void *buf, __u64 offset, size_t len)
229 {
230 int fd;
231
232 if (c.sparse_mode)
233 return 0;
234
235 fd = __get_device_fd(&offset);
236 if (fd < 0)
237 return fd;
238
239 /* Only allow fill to zero */
240 if (*((__u8*)buf))
241 return -1;
242 if (lseek64(fd, (off64_t)offset, SEEK_SET) < 0)
243 return -1;
244 if (write(fd, buf, len) < 0)
245 return -1;
246 return 0;
247 }
248
dev_fill_block(void * buf,__u64 blk_addr)249 int dev_fill_block(void *buf, __u64 blk_addr)
250 {
251 return dev_fill(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
252 }
253
dev_read_block(void * buf,__u64 blk_addr)254 int dev_read_block(void *buf, __u64 blk_addr)
255 {
256 return dev_read(buf, blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
257 }
258
dev_reada_block(__u64 blk_addr)259 int dev_reada_block(__u64 blk_addr)
260 {
261 return dev_readahead(blk_addr << F2FS_BLKSIZE_BITS, F2FS_BLKSIZE);
262 }
263
f2fs_fsync_device(void)264 int f2fs_fsync_device(void)
265 {
266 #ifndef ANDROID_WINDOWS_HOST
267 int i;
268
269 for (i = 0; i < c.ndevs; i++) {
270 if (fsync(c.devices[i].fd) < 0) {
271 MSG(0, "\tError: Could not conduct fsync!!!\n");
272 return -1;
273 }
274 }
275 #endif
276 return 0;
277 }
278
f2fs_init_sparse_file(void)279 int f2fs_init_sparse_file(void)
280 {
281 #ifdef WITH_ANDROID
282 if (c.func == MKFS) {
283 f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE, c.device_size);
284 } else {
285 f2fs_sparse_file = sparse_file_import(c.devices[0].fd,
286 true, false);
287 if (!f2fs_sparse_file)
288 return -1;
289
290 c.device_size = sparse_file_len(f2fs_sparse_file, 0, 0);
291 c.device_size &= (~((u_int64_t)(F2FS_BLKSIZE - 1)));
292 }
293
294 if (sparse_file_block_size(f2fs_sparse_file) != F2FS_BLKSIZE) {
295 MSG(0, "\tError: Corrupted sparse file\n");
296 return -1;
297 }
298 blocks_count = c.device_size / F2FS_BLKSIZE;
299 blocks = calloc(blocks_count, sizeof(char *));
300
301 return sparse_file_foreach_chunk(f2fs_sparse_file, true, false,
302 sparse_import_segment, NULL);
303 #else
304 MSG(0, "\tError: Sparse mode is only supported for android\n");
305 return -1;
306 #endif
307 }
308
f2fs_finalize_device(void)309 int f2fs_finalize_device(void)
310 {
311 int i;
312 int ret = 0;
313
314 #ifdef WITH_ANDROID
315 if (c.sparse_mode) {
316 int64_t chunk_start = (blocks[0] == NULL) ? -1 : 0;
317 uint64_t j;
318
319 if (c.func != MKFS) {
320 sparse_file_destroy(f2fs_sparse_file);
321 ret = ftruncate(c.devices[0].fd, 0);
322 ASSERT(!ret);
323 lseek(c.devices[0].fd, 0, SEEK_SET);
324 f2fs_sparse_file = sparse_file_new(F2FS_BLKSIZE,
325 c.device_size);
326 }
327
328 for (j = 0; j < blocks_count; ++j) {
329 if (!blocks[j] && chunk_start != -1) {
330 ret = sparse_merge_blocks(chunk_start,
331 j - chunk_start);
332 chunk_start = -1;
333 } else if (blocks[j] && chunk_start == -1) {
334 chunk_start = j;
335 }
336 ASSERT(!ret);
337 }
338 if (chunk_start != -1) {
339 ret = sparse_merge_blocks(chunk_start,
340 blocks_count - chunk_start);
341 ASSERT(!ret);
342 }
343
344 sparse_file_write(f2fs_sparse_file, c.devices[0].fd,
345 /*gzip*/0, /*sparse*/1, /*crc*/0);
346
347 sparse_file_destroy(f2fs_sparse_file);
348 for (j = 0; j < blocks_count; j++)
349 free(blocks[j]);
350 free(blocks);
351 blocks = NULL;
352 f2fs_sparse_file = NULL;
353 }
354 #endif
355 /*
356 * We should call fsync() to flush out all the dirty pages
357 * in the block device page cache.
358 */
359 for (i = 0; i < c.ndevs; i++) {
360 #ifndef ANDROID_WINDOWS_HOST
361 ret = fsync(c.devices[i].fd);
362 if (ret < 0) {
363 MSG(0, "\tError: Could not conduct fsync!!!\n");
364 break;
365 }
366 #endif
367 ret = close(c.devices[i].fd);
368 if (ret < 0) {
369 MSG(0, "\tError: Failed to close device file!!!\n");
370 break;
371 }
372 }
373 close(c.kd);
374
375 return ret;
376 }
377