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