newfs_nandfs.c revision 235537
1/*- 2 * Copyright (c) 2010-2012 Semihalf. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sbin/newfs_nandfs/newfs_nandfs.c 235537 2012-05-17 10:11:18Z gber $"); 29 30#include <sys/param.h> 31#include <sys/fdcio.h> 32#include <sys/disk.h> 33#include <sys/disklabel.h> 34#include <sys/mount.h> 35#include <sys/stat.h> 36#include <sys/time.h> 37#include <sys/endian.h> 38#include <sys/stddef.h> 39#include <sys/uuid.h> 40#include <sys/dirent.h> 41#include <sys/stat.h> 42 43#include <ctype.h> 44#include <err.h> 45#include <errno.h> 46#include <fcntl.h> 47#include <inttypes.h> 48#include <libgeom.h> 49#include <paths.h> 50#include <stdio.h> 51#include <stdlib.h> 52#include <string.h> 53#include <time.h> 54#include <unistd.h> 55 56#include <fs/nandfs/nandfs_fs.h> 57#include <dev/nand/nand_dev.h> 58 59#define DEBUG 60#undef DEBUG 61#ifdef DEBUG 62#define debug(fmt, args...) do { \ 63 printf("nandfs:" fmt "\n", ##args); } while (0) 64#else 65#define debug(fmt, args...) 66#endif 67 68#define NANDFS_FIRST_BLOCK nandfs_first_block() 69#define NANDFS_FIRST_CNO 1 70#define NANDFS_BLOCK_BAD 1 71#define NANDFS_BLOCK_GOOD 0 72 73struct file_info { 74 uint64_t ino; 75 const char *name; 76 uint32_t mode; 77 uint64_t size; 78 uint8_t nblocks; 79 uint32_t *blocks; 80 struct nandfs_inode *inode; 81}; 82 83struct file_info user_files[] = 84{ 85 {NANDFS_ROOT_INO, NULL, S_IFDIR | 0755, 0, 1, NULL, NULL}, 86}; 87 88struct file_info ifile = {NANDFS_IFILE_INO, NULL, 0, 0, -1, NULL, NULL}; 89struct file_info sufile = {NANDFS_SUFILE_INO, NULL, 0, 0, -1, NULL, NULL}; 90struct file_info cpfile = {NANDFS_CPFILE_INO, NULL, 0, 0, -1, NULL, NULL}; 91struct file_info datfile = {NANDFS_DAT_INO, NULL, 0, 0, -1, NULL, NULL}; 92 93struct nandfs_block { 94 LIST_ENTRY(nandfs_block) block_link; 95 uint32_t number; 96 uint64_t offset; 97 void *data; 98}; 99 100static LIST_HEAD(, nandfs_block) block_head = LIST_HEAD_INITIALIZER(&block_head); 101 102/* Storage geometry */ 103static off_t mediasize; 104static ssize_t sectorsize; 105static uint64_t nsegments; 106static uint64_t erasesize; 107static uint64_t segsize; 108 109struct nandfs_fsdata fsdata; 110struct nandfs_super_block super_block; 111 112static int is_nand; 113 114/* Nandfs parameters */ 115static size_t blocksize = NANDFS_DEF_BLOCKSIZE; 116static long blocks_per_segment; 117static long rsv_segment_percent = 5; 118static time_t nandfs_time; 119static uint32_t bad_segments_count = 0; 120static uint32_t *bad_segments = NULL; 121static uint8_t fsdata_blocks_state[NANDFS_NFSAREAS]; 122 123u_char *volumelabel = NULL; 124 125struct nandfs_super_root *sr; 126 127uint32_t nuserfiles; 128uint32_t seg_segsum_size; 129uint32_t seg_nblocks; 130uint32_t seg_endblock; 131 132#define SIZE_TO_BLOCK(size) (((size) + (blocksize - 1)) / blocksize) 133 134static uint32_t 135nandfs_first_block(void) 136{ 137 uint32_t i, first_free, start_bad_segments = 0; 138 139 for (i = 0; i < bad_segments_count; i++) { 140 if (i == bad_segments[i]) 141 start_bad_segments++; 142 else 143 break; 144 } 145 146 first_free = SIZE_TO_BLOCK(NANDFS_DATA_OFFSET_BYTES(erasesize) + 147 (start_bad_segments * segsize)); 148 149 if (first_free < (uint32_t)blocks_per_segment) 150 return (blocks_per_segment); 151 else 152 return (first_free); 153} 154 155static void 156usage(void) 157{ 158 159 fprintf(stderr, 160 "usage: newfs_nandfs [ -options ] device\n" 161 "where the options are:\n" 162 "\t-b block-size\n" 163 "\t-B blocks-per-segment\n" 164 "\t-L volume label\n" 165 "\t-m reserved-segments-percentage\n"); 166 exit(1); 167} 168 169static int 170nandfs_log2(unsigned n) 171{ 172 unsigned count; 173 174 /* 175 * N.B. this function will return 0 if supplied 0. 176 */ 177 for (count = 0; n/2; count++) 178 n /= 2; 179 return count; 180} 181 182/* from NetBSD's src/sys/net/if_ethersubr.c */ 183static uint32_t 184crc32_le(uint32_t crc, const uint8_t *buf, size_t len) 185{ 186 static const uint32_t crctab[] = { 187 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 188 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 189 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 190 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c 191 }; 192 size_t i; 193 194 crc = crc ^ ~0U; 195 196 for (i = 0; i < len; i++) { 197 crc ^= buf[i]; 198 crc = (crc >> 4) ^ crctab[crc & 0xf]; 199 crc = (crc >> 4) ^ crctab[crc & 0xf]; 200 } 201 202 return (crc ^ ~0U); 203} 204 205static void * 206get_block(uint32_t block_nr, uint64_t offset) 207{ 208 struct nandfs_block *block, *new_block; 209 210 LIST_FOREACH(block, &block_head, block_link) { 211 if (block->number == block_nr) 212 return block->data; 213 } 214 215 debug("allocating block %x\n", block_nr); 216 217 new_block = malloc(sizeof(*block)); 218 if (!new_block) 219 err(1, "cannot allocate block"); 220 221 new_block->number = block_nr; 222 new_block->offset = offset; 223 new_block->data = malloc(blocksize); 224 if (!new_block->data) 225 err(1, "cannot allocate block data"); 226 227 memset(new_block->data, 0, blocksize); 228 229 LIST_INSERT_HEAD(&block_head, new_block, block_link); 230 231 return (new_block->data); 232} 233 234static int 235nandfs_seg_usage_blk_offset(uint64_t seg, uint64_t *blk, uint64_t *offset) 236{ 237 uint64_t off; 238 uint16_t seg_size; 239 240 seg_size = sizeof(struct nandfs_segment_usage); 241 242 off = roundup(sizeof(struct nandfs_sufile_header), seg_size); 243 off += (seg * seg_size); 244 245 *blk = off / blocksize; 246 *offset = (off % blocksize) / seg_size; 247 return (0); 248} 249 250static uint32_t 251segment_size(void) 252{ 253 u_int size; 254 255 size = sizeof(struct nandfs_segment_summary ); 256 size += seg_nblocks * sizeof(struct nandfs_binfo_v); 257 258 if (size > blocksize) 259 err(1, "segsum info bigger that blocksize"); 260 261 return (size); 262} 263 264 265static void 266prepare_blockgrouped_file(uint32_t block) 267{ 268 struct nandfs_block_group_desc *desc; 269 uint32_t i, entries; 270 271 desc = (struct nandfs_block_group_desc *)get_block(block, 0); 272 entries = blocksize / sizeof(struct nandfs_block_group_desc); 273 for (i = 0; i < entries; i++) 274 desc[i].bg_nfrees = blocksize * 8; 275} 276 277static void 278alloc_blockgrouped_file(uint32_t block, uint32_t entry) 279{ 280 struct nandfs_block_group_desc *desc; 281 uint32_t desc_nr; 282 uint32_t *bitmap; 283 284 desc = (struct nandfs_block_group_desc *)get_block(block, 0); 285 bitmap = (uint32_t *)get_block(block + 1, 1); 286 287 bitmap += (entry >> 5); 288 if (*bitmap & (1 << (entry % 32))) { 289 printf("nandfs: blockgrouped entry %d already allocated\n", 290 entry); 291 } 292 *bitmap |= (1 << (entry % 32)); 293 294 desc_nr = entry / (blocksize * 8); 295 desc[desc_nr].bg_nfrees--; 296} 297 298 299static uint64_t 300count_su_blocks(void) 301{ 302 uint64_t maxblk, blk, offset, i; 303 304 maxblk = blk = 0; 305 306 for (i = 0; i < bad_segments_count; i++) { 307 nandfs_seg_usage_blk_offset(bad_segments[i], &blk, &offset); 308 debug("bad segment at block:%jx off: %jx", blk, offset); 309 if (blk > maxblk) 310 maxblk = blk; 311 } 312 313 debug("bad segment needs %#jx", blk); 314 if (blk >= NDADDR) { 315 printf("nandfs: file too big (%jd > %d)\n", blk, NDADDR); 316 exit(2); 317 } 318 319 sufile.size = (blk + 1) * blocksize; 320 return (blk + 1); 321} 322 323static void 324count_seg_blocks(void) 325{ 326 uint32_t i; 327 328 for (i = 0; i < nuserfiles; i++) 329 if (user_files[i].nblocks) { 330 seg_nblocks += user_files[i].nblocks; 331 user_files[i].blocks = malloc(user_files[i].nblocks * sizeof(uint32_t)); 332 } 333 334 ifile.nblocks = 2 + 335 SIZE_TO_BLOCK(sizeof(struct nandfs_inode) * (NANDFS_USER_INO + 1)); 336 ifile.blocks = malloc(ifile.nblocks * sizeof(uint32_t)); 337 seg_nblocks += ifile.nblocks; 338 339 cpfile.nblocks = 340 SIZE_TO_BLOCK((NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET + 1) * 341 sizeof(struct nandfs_checkpoint)); 342 cpfile.blocks = malloc(cpfile.nblocks * sizeof(uint32_t)); 343 seg_nblocks += cpfile.nblocks; 344 345 if (!bad_segments) { 346 sufile.nblocks = 347 SIZE_TO_BLOCK((NANDFS_SUFILE_FIRST_SEGMENT_USAGE_OFFSET + 1) * 348 sizeof(struct nandfs_segment_usage)); 349 } else { 350 debug("bad blocks found: extra space for sufile"); 351 sufile.nblocks = count_su_blocks(); 352 } 353 354 sufile.blocks = malloc(sufile.nblocks * sizeof(uint32_t)); 355 seg_nblocks += sufile.nblocks; 356 357 datfile.nblocks = 2 + 358 SIZE_TO_BLOCK((seg_nblocks) * sizeof(struct nandfs_dat_entry)); 359 datfile.blocks = malloc(datfile.nblocks * sizeof(uint32_t)); 360 seg_nblocks += datfile.nblocks; 361} 362 363static void 364assign_file_blocks(uint64_t start_block) 365{ 366 uint32_t i, j; 367 368 for (i = 0; i < nuserfiles; i++) 369 for (j = 0; j < user_files[i].nblocks; j++) { 370 debug("user file %d at block %d at %#jx", 371 i, j, (uintmax_t)start_block); 372 user_files[i].blocks[j] = start_block++; 373 } 374 375 for (j = 0; j < ifile.nblocks; j++) { 376 debug("ifile block %d at %#jx", j, (uintmax_t)start_block); 377 ifile.blocks[j] = start_block++; 378 } 379 380 for (j = 0; j < cpfile.nblocks; j++) { 381 debug("cpfile block %d at %#jx", j, (uintmax_t)start_block); 382 cpfile.blocks[j] = start_block++; 383 } 384 385 for (j = 0; j < sufile.nblocks; j++) { 386 debug("sufile block %d at %#jx", j, (uintmax_t)start_block); 387 sufile.blocks[j] = start_block++; 388 } 389 390 for (j = 0; j < datfile.nblocks; j++) { 391 debug("datfile block %d at %#jx", j, (uintmax_t)start_block); 392 datfile.blocks[j] = start_block++; 393 } 394 395 /* add one for superroot */ 396 debug("sr at block %#jx", (uintmax_t)start_block); 397 sr = (struct nandfs_super_root *)get_block(start_block++, 0); 398 seg_endblock = start_block; 399} 400 401static void 402save_datfile(void) 403{ 404 405 prepare_blockgrouped_file(datfile.blocks[0]); 406} 407 408static uint64_t 409update_datfile(uint64_t block) 410{ 411 struct nandfs_dat_entry *dat; 412 static uint64_t vblock = 0; 413 uint64_t allocated, i, off; 414 415 if (vblock == 0) { 416 alloc_blockgrouped_file(datfile.blocks[0], vblock); 417 vblock++; 418 } 419 allocated = vblock; 420 i = vblock / (blocksize / sizeof(*dat)); 421 off = vblock % (blocksize / sizeof(*dat)); 422 vblock++; 423 424 dat = (struct nandfs_dat_entry *)get_block(datfile.blocks[2 + i], 2 + i); 425 426 alloc_blockgrouped_file(datfile.blocks[0], allocated); 427 dat[off].de_blocknr = block; 428 dat[off].de_start = NANDFS_FIRST_CNO; 429 dat[off].de_end = UINTMAX_MAX; 430 431 return (allocated); 432} 433 434static union nandfs_binfo * 435update_block_info(union nandfs_binfo *binfo, struct file_info *file) 436{ 437 nandfs_daddr_t vblock; 438 uint32_t i; 439 440 for (i = 0; i < file->nblocks; i++) { 441 debug("%s: blk %x", __func__, i); 442 if (file->ino != NANDFS_DAT_INO) { 443 vblock = update_datfile(file->blocks[i]); 444 binfo->bi_v.bi_vblocknr = vblock; 445 binfo->bi_v.bi_blkoff = i; 446 binfo->bi_v.bi_ino = file->ino; 447 file->inode->i_db[i] = vblock; 448 } else { 449 binfo->bi_dat.bi_blkoff = i; 450 binfo->bi_dat.bi_ino = file->ino; 451 file->inode->i_db[i] = datfile.blocks[i]; 452 } 453 binfo++; 454 } 455 456 return (binfo); 457} 458 459static void 460save_segsum(struct nandfs_segment_summary *ss) 461{ 462 union nandfs_binfo *binfo; 463 struct nandfs_block *block; 464 uint32_t sum_bytes, i; 465 uint8_t crc_data, crc_skip; 466 467 sum_bytes = segment_size(); 468 ss->ss_magic = NANDFS_SEGSUM_MAGIC; 469 ss->ss_bytes = sizeof(struct nandfs_segment_summary); 470 ss->ss_flags = NANDFS_SS_LOGBGN | NANDFS_SS_LOGEND | NANDFS_SS_SR; 471 ss->ss_seq = 1; 472 ss->ss_create = nandfs_time; 473 474 ss->ss_next = nandfs_first_block() + blocks_per_segment; 475 /* nblocks = segment blocks + segsum block + superroot */ 476 ss->ss_nblocks = seg_nblocks + 2; 477 ss->ss_nbinfos = seg_nblocks; 478 ss->ss_sumbytes = sum_bytes; 479 480 crc_skip = sizeof(ss->ss_datasum) + sizeof(ss->ss_sumsum); 481 ss->ss_sumsum = crc32_le(0, (uint8_t *)ss + crc_skip, 482 sum_bytes - crc_skip); 483 crc_data = 0; 484 485 binfo = (union nandfs_binfo *)(ss + 1); 486 for (i = 0; i < nuserfiles; i++) { 487 if (user_files[i].nblocks) 488 binfo = update_block_info(binfo, &user_files[i]); 489 } 490 491 binfo = update_block_info(binfo, &ifile); 492 binfo = update_block_info(binfo, &cpfile); 493 binfo = update_block_info(binfo, &sufile); 494 update_block_info(binfo, &datfile); 495 496 /* save superroot crc */ 497 crc_skip = sizeof(sr->sr_sum); 498 sr->sr_sum = crc32_le(0, (uint8_t *)sr + crc_skip, 499 NANDFS_SR_BYTES - crc_skip); 500 501 /* segment checksup */ 502 crc_skip = sizeof(ss->ss_datasum); 503 LIST_FOREACH(block, &block_head, block_link) { 504 if (block->number < NANDFS_FIRST_BLOCK) 505 continue; 506 if (block->number == NANDFS_FIRST_BLOCK) 507 crc_data = crc32_le(0, 508 (uint8_t *)block->data + crc_skip, 509 blocksize - crc_skip); 510 else 511 crc_data = crc32_le(crc_data, (uint8_t *)block->data, 512 blocksize); 513 } 514 ss->ss_datasum = crc_data; 515} 516 517static void 518create_fsdata(void) 519{ 520 521 memset(&fsdata, 0, sizeof(struct nandfs_fsdata)); 522 523 fsdata.f_magic = NANDFS_FSDATA_MAGIC; 524 fsdata.f_nsegments = nsegments; 525 fsdata.f_erasesize = erasesize; 526 fsdata.f_first_data_block = NANDFS_FIRST_BLOCK; 527 fsdata.f_blocks_per_segment = blocks_per_segment; 528 fsdata.f_r_segments_percentage = rsv_segment_percent; 529 fsdata.f_rev_level = NANDFS_CURRENT_REV; 530 fsdata.f_sbbytes = NANDFS_SB_BYTES; 531 fsdata.f_bytes = NANDFS_FSDATA_CRC_BYTES; 532 fsdata.f_ctime = nandfs_time; 533 fsdata.f_log_block_size = nandfs_log2(blocksize) - 10; 534 fsdata.f_errors = 1; 535 fsdata.f_inode_size = sizeof(struct nandfs_inode); 536 fsdata.f_dat_entry_size = sizeof(struct nandfs_dat_entry); 537 fsdata.f_checkpoint_size = sizeof(struct nandfs_checkpoint); 538 fsdata.f_segment_usage_size = sizeof(struct nandfs_segment_usage); 539 540 uuidgen(&fsdata.f_uuid, 1); 541 542 if (volumelabel) 543 memcpy(fsdata.f_volume_name, volumelabel, 16); 544 545 fsdata.f_sum = crc32_le(0, (const uint8_t *)&fsdata, 546 NANDFS_FSDATA_CRC_BYTES); 547} 548 549static void 550save_fsdata(void *data) 551{ 552 553 memcpy(data, &fsdata, sizeof(fsdata)); 554} 555 556static void 557create_super_block(void) 558{ 559 560 memset(&super_block, 0, sizeof(struct nandfs_super_block)); 561 562 super_block.s_magic = NANDFS_SUPER_MAGIC; 563 super_block.s_last_cno = NANDFS_FIRST_CNO; 564 super_block.s_last_pseg = NANDFS_FIRST_BLOCK; 565 super_block.s_last_seq = 1; 566 super_block.s_free_blocks_count = 567 (nsegments - bad_segments_count) * blocks_per_segment; 568 super_block.s_mtime = 0; 569 super_block.s_wtime = nandfs_time; 570 super_block.s_state = NANDFS_VALID_FS; 571 572 super_block.s_sum = crc32_le(0, (const uint8_t *)&super_block, 573 NANDFS_SB_BYTES); 574} 575 576static void 577save_super_block(void *data) 578{ 579 580 memcpy(data, &super_block, sizeof(super_block)); 581} 582 583static void 584save_super_root(void) 585{ 586 587 sr->sr_bytes = NANDFS_SR_BYTES; 588 sr->sr_flags = 0; 589 sr->sr_nongc_ctime = nandfs_time; 590 datfile.inode = &sr->sr_dat; 591 cpfile.inode = &sr->sr_cpfile; 592 sufile.inode = &sr->sr_sufile; 593} 594 595static struct nandfs_dir_entry * 596add_de(void *block, struct nandfs_dir_entry *de, uint64_t ino, 597 const char *name, uint8_t type) 598{ 599 uint16_t reclen; 600 601 /* modify last de */ 602 de->rec_len = NANDFS_DIR_REC_LEN(de->name_len); 603 de = (void *)((uint8_t *)de + de->rec_len); 604 605 reclen = blocksize - ((uintptr_t)de - (uintptr_t)block); 606 if (reclen < NANDFS_DIR_REC_LEN(strlen(name))) { 607 printf("nandfs: too many dir entries for one block\n"); 608 return (NULL); 609 } 610 611 de->inode = ino; 612 de->rec_len = reclen; 613 de->name_len = strlen(name); 614 de->file_type = type; 615 memset(de->name, 0, 616 (strlen(name) + NANDFS_DIR_PAD - 1) & ~NANDFS_DIR_ROUND); 617 memcpy(de->name, name, strlen(name)); 618 619 return (de); 620} 621 622static struct nandfs_dir_entry * 623make_dir(void *block, uint64_t ino, uint64_t parent_ino) 624{ 625 struct nandfs_dir_entry *de = (struct nandfs_dir_entry *)block; 626 627 /* create '..' entry */ 628 de->inode = parent_ino; 629 de->rec_len = NANDFS_DIR_REC_LEN(2); 630 de->name_len = 2; 631 de->file_type = DT_DIR; 632 memset(de->name, 0, NANDFS_DIR_NAME_LEN(2)); 633 memcpy(de->name, "..", 2); 634 635 /* create '.' entry */ 636 de = (void *)((uint8_t *)block + NANDFS_DIR_REC_LEN(2)); 637 de->inode = ino; 638 de->rec_len = blocksize - NANDFS_DIR_REC_LEN(2); 639 de->name_len = 1; 640 de->file_type = DT_DIR; 641 memset(de->name, 0, NANDFS_DIR_NAME_LEN(1)); 642 memcpy(de->name, ".", 1); 643 644 return (de); 645} 646 647static void 648save_root_dir(void) 649{ 650 struct file_info *root = &user_files[0]; 651 struct nandfs_dir_entry *de; 652 uint32_t i; 653 void *block; 654 655 block = get_block(root->blocks[0], 0); 656 657 de = make_dir(block, root->ino, root->ino); 658 for (i = 1; i < nuserfiles; i++) 659 de = add_de(block, de, user_files[i].ino, user_files[i].name, 660 IFTODT(user_files[i].mode)); 661 662 root->size = ((uintptr_t)de - (uintptr_t)block) + 663 NANDFS_DIR_REC_LEN(de->name_len); 664} 665 666static void 667save_sufile(void) 668{ 669 struct nandfs_sufile_header *header; 670 struct nandfs_segment_usage *su; 671 uint64_t blk, i, off; 672 void *block; 673 int start; 674 675 /* 676 * At the beginning just zero-out everything 677 */ 678 for (i = 0; i < sufile.nblocks; i++) 679 get_block(sufile.blocks[i], 0); 680 681 start = 0; 682 683 block = get_block(sufile.blocks[start], 0); 684 header = (struct nandfs_sufile_header *)block; 685 header->sh_ncleansegs = nsegments - bad_segments_count - 1; 686 header->sh_ndirtysegs = 1; 687 header->sh_last_alloc = 1; 688 689 su = (struct nandfs_segment_usage *)header; 690 off = NANDFS_SUFILE_FIRST_SEGMENT_USAGE_OFFSET; 691 /* Allocate data segment */ 692 su[off].su_lastmod = nandfs_time; 693 /* nblocks = segment blocks + segsum block + superroot */ 694 su[off].su_nblocks = seg_nblocks + 2; 695 su[off].su_flags = NANDFS_SEGMENT_USAGE_DIRTY; 696 off++; 697 /* Allocate next segment */ 698 su[off].su_lastmod = nandfs_time; 699 su[off].su_nblocks = 0; 700 su[off].su_flags = NANDFS_SEGMENT_USAGE_DIRTY; 701 for (i = 0; i < bad_segments_count; i++) { 702 nandfs_seg_usage_blk_offset(bad_segments[i], &blk, &off); 703 debug("storing bad_segments[%jd]=%x at %jx off %jx\n", i, 704 bad_segments[i], blk, off); 705 block = get_block(sufile.blocks[blk], 706 off * sizeof(struct nandfs_segment_usage *)); 707 su = (struct nandfs_segment_usage *)block; 708 su[off].su_lastmod = nandfs_time; 709 su[off].su_nblocks = 0; 710 su[off].su_flags = NANDFS_SEGMENT_USAGE_ERROR; 711 } 712} 713 714static void 715save_cpfile(void) 716{ 717 struct nandfs_cpfile_header *header; 718 struct nandfs_checkpoint *cp, *initial_cp; 719 int i, entries = blocksize / sizeof(struct nandfs_checkpoint); 720 uint64_t cno; 721 722 header = (struct nandfs_cpfile_header *)get_block(cpfile.blocks[0], 0); 723 header->ch_ncheckpoints = 1; 724 header->ch_nsnapshots = 0; 725 726 cp = (struct nandfs_checkpoint *)header; 727 728 /* fill first checkpoint data*/ 729 initial_cp = &cp[NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET]; 730 initial_cp->cp_flags = 0; 731 initial_cp->cp_checkpoints_count = 0; 732 initial_cp->cp_cno = NANDFS_FIRST_CNO; 733 initial_cp->cp_create = nandfs_time; 734 initial_cp->cp_nblk_inc = seg_endblock - 1; 735 initial_cp->cp_blocks_count = seg_nblocks; 736 memset(&initial_cp->cp_snapshot_list, 0, 737 sizeof(struct nandfs_snapshot_list)); 738 739 ifile.inode = &initial_cp->cp_ifile_inode; 740 741 /* mark rest of cp as invalid */ 742 cno = NANDFS_FIRST_CNO + 1; 743 i = NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET + 1; 744 for (; i < entries; i++) { 745 cp[i].cp_cno = cno++; 746 cp[i].cp_flags = NANDFS_CHECKPOINT_INVALID; 747 } 748} 749 750static void 751init_inode(struct nandfs_inode *inode, struct file_info *file) 752{ 753 754 inode->i_blocks = file->nblocks; 755 inode->i_ctime = nandfs_time; 756 inode->i_mtime = nandfs_time; 757 inode->i_mode = file->mode & 0xffff; 758 inode->i_links_count = 1; 759 760 if (file->size > 0) 761 inode->i_size = file->size; 762 else 763 inode->i_size = 0; 764 765 if (file->ino == NANDFS_USER_INO) 766 inode->i_flags = SF_NOUNLINK|UF_NOUNLINK; 767 else 768 inode->i_flags = 0; 769} 770 771static void 772save_ifile(void) 773{ 774 struct nandfs_inode *inode; 775 struct file_info *file; 776 uint64_t ino, blk, off; 777 uint32_t i; 778 779 prepare_blockgrouped_file(ifile.blocks[0]); 780 for (i = 0; i <= NANDFS_USER_INO; i++) 781 alloc_blockgrouped_file(ifile.blocks[0], i); 782 783 for (i = 0; i < nuserfiles; i++) { 784 file = &user_files[i]; 785 ino = file->ino; 786 blk = ino / (blocksize / sizeof(*inode)); 787 off = ino % (blocksize / sizeof(*inode)); 788 inode = 789 (struct nandfs_inode *)get_block(ifile.blocks[2 + blk], 2 + blk); 790 file->inode = &inode[off]; 791 init_inode(file->inode, file); 792 } 793 794 init_inode(ifile.inode, &ifile); 795 init_inode(cpfile.inode, &cpfile); 796 init_inode(sufile.inode, &sufile); 797 init_inode(datfile.inode, &datfile); 798} 799 800static int 801create_fs(void) 802{ 803 uint64_t start_block; 804 uint32_t segsum_size; 805 char *data; 806 int i; 807 808 nuserfiles = (sizeof(user_files) / sizeof(user_files[0])); 809 810 /* Count and assign blocks */ 811 count_seg_blocks(); 812 segsum_size = segment_size(); 813 start_block = NANDFS_FIRST_BLOCK + SIZE_TO_BLOCK(segsum_size); 814 assign_file_blocks(start_block); 815 816 /* Create super root structure */ 817 save_super_root(); 818 819 /* Create root directory */ 820 save_root_dir(); 821 822 /* Fill in file contents */ 823 save_sufile(); 824 save_cpfile(); 825 save_ifile(); 826 save_datfile(); 827 828 /* Save fsdata and superblocks */ 829 create_fsdata(); 830 create_super_block(); 831 832 for (i = 0; i < NANDFS_NFSAREAS; i++) { 833 if (fsdata_blocks_state[i] != NANDFS_BLOCK_GOOD) 834 continue; 835 836 data = get_block((i * erasesize)/blocksize, 0); 837 save_fsdata(data); 838 839 data = get_block((i * erasesize + NANDFS_SBLOCK_OFFSET_BYTES) / 840 blocksize, 0); 841 if (blocksize > NANDFS_SBLOCK_OFFSET_BYTES) 842 data += NANDFS_SBLOCK_OFFSET_BYTES; 843 save_super_block(data); 844 memset(data + sizeof(struct nandfs_super_block), 0xff, 845 (blocksize - sizeof(struct nandfs_super_block) - 846 NANDFS_SBLOCK_OFFSET_BYTES)); 847 } 848 849 /* Save segment summary and CRCs */ 850 save_segsum(get_block(NANDFS_FIRST_BLOCK, 0)); 851 852 return (0); 853} 854 855static void 856write_fs(int fda) 857{ 858 struct nandfs_block *block; 859 char *data; 860 u_int ret; 861 862 /* Overwrite next block with ff if not nand device */ 863 if (!is_nand) { 864 data = get_block(seg_endblock, 0); 865 memset(data, 0xff, blocksize); 866 } 867 868 LIST_FOREACH(block, &block_head, block_link) { 869 lseek(fda, block->number * blocksize, SEEK_SET); 870 ret = write(fda, block->data, blocksize); 871 if (ret != blocksize) 872 err(1, "cannot write filesystem data"); 873 } 874} 875 876static void 877check_parameters(void) 878{ 879 int i; 880 881 /* check blocksize */ 882 if ((blocksize < NANDFS_MIN_BLOCKSIZE) || (blocksize > MAXBSIZE) || 883 ((blocksize - 1) & blocksize)) { 884 errx(1, "Bad blocksize (%zu). Must be in range [%u-%u] " 885 "and a power of two.", blocksize, NANDFS_MIN_BLOCKSIZE, 886 MAXBSIZE); 887 } 888 889 /* check blocks per segments */ 890 if ((blocks_per_segment < NANDFS_SEG_MIN_BLOCKS) || 891 ((blocksize - 1) & blocksize)) 892 errx(1, "Bad blocks per segment (%lu). Must be greater than " 893 "%u and a power of two.", blocks_per_segment, 894 NANDFS_SEG_MIN_BLOCKS); 895 896 /* check reserved segment percentage */ 897 if ((rsv_segment_percent < 1) && (rsv_segment_percent > 99)) 898 errx(1, "Bad reserved segment percentage. " 899 "Must in range 1..99."); 900 901 /* check volume label */ 902 i = 0; 903 if (volumelabel) { 904 while (isalnum(volumelabel[++i])) 905 ; 906 907 if (volumelabel[i] != '\0') { 908 errx(1, "bad volume label. " 909 "Valid characters are alphanumerics."); 910 } 911 912 if (strlen(volumelabel) >= 16) 913 errx(1, "Bad volume label. Length is longer than %d.", 914 16); 915 } 916 917 nandfs_time = time(NULL); 918} 919 920static void 921print_parameters(void) 922{ 923 924 printf("filesystem parameters:\n"); 925 printf("blocksize: %#zx sectorsize: %#zx\n", blocksize, sectorsize); 926 printf("erasesize: %#jx mediasize: %#jx\n", erasesize, mediasize); 927 printf("segment size: %#jx blocks per segment: %#x\n", segsize, 928 (uint32_t)blocks_per_segment); 929} 930 931/* 932 * Exit with error if file system is mounted. 933 */ 934static void 935check_mounted(const char *fname, mode_t mode) 936{ 937 struct statfs *mp; 938 const char *s1, *s2; 939 size_t len; 940 int n, r; 941 942 if (!(n = getmntinfo(&mp, MNT_NOWAIT))) 943 err(1, "getmntinfo"); 944 945 len = strlen(_PATH_DEV); 946 s1 = fname; 947 if (!strncmp(s1, _PATH_DEV, len)) 948 s1 += len; 949 950 r = S_ISCHR(mode) && s1 != fname && *s1 == 'r'; 951 952 for (; n--; mp++) { 953 s2 = mp->f_mntfromname; 954 955 if (!strncmp(s2, _PATH_DEV, len)) 956 s2 += len; 957 if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) || 958 !strcmp(s1, s2)) 959 errx(1, "%s is mounted on %s", fname, mp->f_mntonname); 960 } 961} 962 963static void 964calculate_geometry(int fd) 965{ 966 struct chip_param_io chip_params; 967 char ident[DISK_IDENT_SIZE]; 968 char medianame[MAXPATHLEN]; 969 970 /* Check storage type */ 971 g_get_ident(fd, ident, DISK_IDENT_SIZE); 972 g_get_name(ident, medianame, MAXPATHLEN); 973 debug("device name: %s", medianame); 974 975 is_nand = (strstr(medianame, "gnand") != NULL); 976 debug("is_nand = %d", is_nand); 977 978 sectorsize = g_sectorsize(fd); 979 debug("sectorsize: %#zx", sectorsize); 980 981 /* Get storage size */ 982 mediasize = g_mediasize(fd); 983 debug("mediasize: %#jx", mediasize); 984 985 /* Get storage erase unit size */ 986 if (!is_nand) 987 erasesize = NANDFS_DEF_ERASESIZE; 988 else if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) 989 errx(1, "Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); 990 else 991 erasesize = chip_params.page_size * chip_params.pages_per_block; 992 993 debug("erasesize: %#jx", (uintmax_t)erasesize); 994 995 if (blocks_per_segment == 0) { 996 if (erasesize >= NANDFS_MIN_SEGSIZE) 997 blocks_per_segment = erasesize / blocksize; 998 else 999 blocks_per_segment = NANDFS_MIN_SEGSIZE / blocksize; 1000 } 1001 1002 /* Calculate number of segments */ 1003 segsize = blocksize * blocks_per_segment; 1004 nsegments = ((mediasize - NANDFS_NFSAREAS * erasesize) / segsize) - 2; 1005 debug("segsize: %#jx", segsize); 1006 debug("nsegments: %#jx", nsegments); 1007} 1008 1009static void 1010erase_device(int fd) 1011{ 1012 int rest, failed; 1013 uint64_t i, nblocks; 1014 off_t offset; 1015 1016 failed = 0; 1017 for (i = 0; i < NANDFS_NFSAREAS; i++) { 1018 debug("Deleting %jx\n", i * erasesize); 1019 if (g_delete(fd, i * erasesize, erasesize)) { 1020 printf("cannot delete %jx\n", i * erasesize); 1021 fsdata_blocks_state[i] = NANDFS_BLOCK_BAD; 1022 failed++; 1023 } else 1024 fsdata_blocks_state[i] = NANDFS_BLOCK_GOOD; 1025 } 1026 1027 if (failed == NANDFS_NFSAREAS) { 1028 printf("%d first blocks not usable. Unable to create " 1029 "filesystem.\n", failed); 1030 exit(1); 1031 } 1032 1033 for (i = 0; i < nsegments; i++) { 1034 offset = NANDFS_NFSAREAS * erasesize + i * segsize; 1035 if (g_delete(fd, offset, segsize)) { 1036 printf("cannot delete segment %jx (offset %jd)\n", 1037 i, offset); 1038 bad_segments_count++; 1039 bad_segments = realloc(bad_segments, 1040 bad_segments_count * sizeof(uint32_t)); 1041 bad_segments[bad_segments_count - 1] = i; 1042 } 1043 } 1044 1045 if (bad_segments_count == nsegments) { 1046 printf("no valid segments\n"); 1047 exit(1); 1048 } 1049 1050 /* Delete remaining blocks at the end of device */ 1051 rest = mediasize % segsize; 1052 nblocks = rest / erasesize; 1053 for (i = 0; i < nblocks; i++) { 1054 offset = (segsize * nsegments) + (i * erasesize); 1055 if (g_delete(fd, offset, erasesize)) { 1056 printf("cannot delete space after last segment " 1057 "- probably a bad block\n"); 1058 } 1059 } 1060} 1061 1062static void 1063erase_initial(int fd) 1064{ 1065 char buf[512]; 1066 u_int i; 1067 1068 memset(buf, 0xff, sizeof(buf)); 1069 1070 lseek(fd, 0, SEEK_SET); 1071 for (i = 0; i < NANDFS_NFSAREAS * erasesize; i += sizeof(buf)) 1072 write(fd, buf, sizeof(buf)); 1073} 1074 1075static void 1076create_nandfs(int fd) 1077{ 1078 1079 create_fs(); 1080 1081 write_fs(fd); 1082} 1083 1084static void 1085print_summary(void) 1086{ 1087 1088 printf("filesystem created succesfully\n"); 1089 printf("total segments: %#jx valid segments: %#jx\n", nsegments, 1090 nsegments - bad_segments_count); 1091 printf("total space: %ju MB free: %ju MB\n", 1092 (nsegments * 1093 blocks_per_segment * blocksize) / (1024 * 1024), 1094 ((nsegments - bad_segments_count) * 1095 blocks_per_segment * blocksize) / (1024 * 1024)); 1096} 1097 1098int 1099main(int argc, char *argv[]) 1100{ 1101 struct stat sb; 1102 char buf[MAXPATHLEN]; 1103 const char opts[] = "b:B:L:m:"; 1104 const char *fname; 1105 int ch, fd; 1106 1107 while ((ch = getopt(argc, argv, opts)) != -1) { 1108 switch (ch) { 1109 case 'b': 1110 blocksize = strtol(optarg, (char **)NULL, 10); 1111 if (blocksize == 0) 1112 usage(); 1113 break; 1114 case 'B': 1115 blocks_per_segment = strtol(optarg, (char **)NULL, 10); 1116 if (blocks_per_segment == 0) 1117 usage(); 1118 break; 1119 case 'L': 1120 volumelabel = optarg; 1121 break; 1122 case 'm': 1123 rsv_segment_percent = strtol(optarg, (char **)NULL, 10); 1124 if (rsv_segment_percent == 0) 1125 usage(); 1126 break; 1127 default: 1128 usage(); 1129 } 1130 } 1131 1132 argc -= optind; 1133 argv += optind; 1134 if (argc < 1 || argc > 2) 1135 usage(); 1136 1137 /* construct proper device path */ 1138 fname = *argv++; 1139 if (!strchr(fname, '/')) { 1140 snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname); 1141 if (!(fname = strdup(buf))) 1142 err(1, NULL); 1143 } 1144 1145 fd = g_open(fname, 1); 1146 if (fd == -1) 1147 err(1, "Cannot open %s", fname); 1148 1149 if (fstat(fd, &sb) == -1) 1150 err(1, "Cannot stat %s", fname); 1151 if (!S_ISCHR(sb.st_mode)) 1152 warnx("%s is not a character device", fname); 1153 1154 check_mounted(fname, sb.st_mode); 1155 1156 calculate_geometry(fd); 1157 1158 check_parameters(); 1159 1160 print_parameters(); 1161 1162 if (is_nand) 1163 erase_device(fd); 1164 else 1165 erase_initial(fd); 1166 1167 create_nandfs(fd); 1168 1169 print_summary(); 1170 1171 g_close(fd); 1172 1173 return (0); 1174} 1175 1176 1177