/* * nilfs2.c - New Implementation of Log filesystem * * Written by Jiro SEKIBA * * Copyright (C) 2003,2004,2005,2007,2008,2010 Free Software Foundation, Inc. * * VAS_EBOOT is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VAS_EBOOT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VAS_EBOOT. If not, see . */ /* Filetype information as used in inodes. */ #define FILETYPE_INO_MASK 0170000 #define FILETYPE_INO_REG 0100000 #define FILETYPE_INO_DIRECTORY 0040000 #define FILETYPE_INO_SYMLINK 0120000 #include #include #include #include #include #include #include #include #include VAS_EBOOT_MOD_LICENSE ("GPLv3+"); #define NILFS_INODE_BMAP_SIZE 7 #define NILFS_SUPORT_REV 2 /* Magic value used to identify an nilfs2 filesystem. */ #define NILFS2_SUPER_MAGIC 0x3434 /* nilfs btree node flag. */ #define NILFS_BTREE_NODE_ROOT 0x01 /* nilfs btree node level. */ #define NILFS_BTREE_LEVEL_DATA 0 #define NILFS_BTREE_LEVEL_NODE_MIN (NILFS_BTREE_LEVEL_DATA + 1) /* nilfs 1st super block posission from beginning of the partition in 512 block size */ #define NILFS_1ST_SUPER_BLOCK 2 /* nilfs 2nd super block posission from beginning of the partition in 512 block size */ #define NILFS_2ND_SUPER_BLOCK(devsize) (((devsize >> 3) - 1) << 3) #define LOG_INODE_SIZE 7 struct VasEBoot_nilfs2_inode { VasEBoot_uint64_t i_blocks; VasEBoot_uint64_t i_size; VasEBoot_uint64_t i_ctime; VasEBoot_uint64_t i_mtime; VasEBoot_uint32_t i_ctime_nsec; VasEBoot_uint32_t i_mtime_nsec; VasEBoot_uint32_t i_uid; VasEBoot_uint32_t i_gid; VasEBoot_uint16_t i_mode; VasEBoot_uint16_t i_links_count; VasEBoot_uint32_t i_flags; VasEBoot_uint64_t i_bmap[NILFS_INODE_BMAP_SIZE]; #define i_device_code i_bmap[0] VasEBoot_uint64_t i_xattr; VasEBoot_uint32_t i_generation; VasEBoot_uint32_t i_pad; }; struct VasEBoot_nilfs2_super_root { VasEBoot_uint32_t sr_sum; VasEBoot_uint16_t sr_bytes; VasEBoot_uint16_t sr_flags; VasEBoot_uint64_t sr_nongc_ctime; struct VasEBoot_nilfs2_inode sr_dat; struct VasEBoot_nilfs2_inode sr_cpfile; struct VasEBoot_nilfs2_inode sr_sufile; }; struct VasEBoot_nilfs2_super_block { VasEBoot_uint32_t s_rev_level; VasEBoot_uint16_t s_minor_rev_level; VasEBoot_uint16_t s_magic; VasEBoot_uint16_t s_bytes; VasEBoot_uint16_t s_flags; VasEBoot_uint32_t s_crc_seed; VasEBoot_uint32_t s_sum; VasEBoot_uint32_t s_log_block_size; VasEBoot_uint64_t s_nsegments; VasEBoot_uint64_t s_dev_size; VasEBoot_uint64_t s_first_data_block; VasEBoot_uint32_t s_blocks_per_segment; VasEBoot_uint32_t s_r_segments_percentage; VasEBoot_uint64_t s_last_cno; VasEBoot_uint64_t s_last_pseg; VasEBoot_uint64_t s_last_seq; VasEBoot_uint64_t s_free_blocks_count; VasEBoot_uint64_t s_ctime; VasEBoot_uint64_t s_mtime; VasEBoot_uint64_t s_wtime; VasEBoot_uint16_t s_mnt_count; VasEBoot_uint16_t s_max_mnt_count; VasEBoot_uint16_t s_state; VasEBoot_uint16_t s_errors; VasEBoot_uint64_t s_lastcheck; VasEBoot_uint32_t s_checkinterval; VasEBoot_uint32_t s_creator_os; VasEBoot_uint16_t s_def_resuid; VasEBoot_uint16_t s_def_resgid; VasEBoot_uint32_t s_first_ino; VasEBoot_uint16_t s_inode_size; VasEBoot_uint16_t s_dat_entry_size; VasEBoot_uint16_t s_checkpoint_size; VasEBoot_uint16_t s_segment_usage_size; VasEBoot_uint8_t s_uuid[16]; char s_volume_name[80]; VasEBoot_uint32_t s_c_interval; VasEBoot_uint32_t s_c_block_max; VasEBoot_uint32_t s_reserved[192]; }; struct VasEBoot_nilfs2_dir_entry { VasEBoot_uint64_t inode; VasEBoot_uint16_t rec_len; #define MAX_NAMELEN 255 VasEBoot_uint8_t name_len; VasEBoot_uint8_t file_type; #if 0 /* followed by file name. */ char name[NILFS_NAME_LEN]; char pad; #endif } VAS_EBOOT_PACKED; enum { NILFS_FT_UNKNOWN, NILFS_FT_REG_FILE, NILFS_FT_DIR, NILFS_FT_CHRDEV, NILFS_FT_BLKDEV, NILFS_FT_FIFO, NILFS_FT_SOCK, NILFS_FT_SYMLINK, NILFS_FT_MAX }; struct VasEBoot_nilfs2_finfo { VasEBoot_uint64_t fi_ino; VasEBoot_uint64_t fi_cno; VasEBoot_uint32_t fi_nblocks; VasEBoot_uint32_t fi_ndatablk; }; struct VasEBoot_nilfs2_binfo_v { VasEBoot_uint64_t bi_vblocknr; VasEBoot_uint64_t bi_blkoff; }; struct VasEBoot_nilfs2_binfo_dat { VasEBoot_uint64_t bi_blkoff; VasEBoot_uint8_t bi_level; VasEBoot_uint8_t bi_pad[7]; }; union VasEBoot_nilfs2_binfo { struct VasEBoot_nilfs2_binfo_v bi_v; struct VasEBoot_nilfs2_binfo_dat bi_dat; }; struct VasEBoot_nilfs2_segment_summary { VasEBoot_uint32_t ss_datasum; VasEBoot_uint32_t ss_sumsum; VasEBoot_uint32_t ss_magic; VasEBoot_uint16_t ss_bytes; VasEBoot_uint16_t ss_flags; VasEBoot_uint64_t ss_seq; VasEBoot_uint64_t ss_create; VasEBoot_uint64_t ss_next; VasEBoot_uint32_t ss_nblocks; VasEBoot_uint32_t ss_nfinfo; VasEBoot_uint32_t ss_sumbytes; VasEBoot_uint32_t ss_pad; }; struct VasEBoot_nilfs2_btree_node { VasEBoot_uint8_t bn_flags; VasEBoot_uint8_t bn_level; VasEBoot_uint16_t bn_nchildren; VasEBoot_uint32_t bn_pad; VasEBoot_uint64_t keys[0]; }; struct VasEBoot_nilfs2_palloc_group_desc { VasEBoot_uint32_t pg_nfrees; }; #define LOG_SIZE_GROUP_DESC 2 #define LOG_NILFS_DAT_ENTRY_SIZE 5 struct VasEBoot_nilfs2_dat_entry { VasEBoot_uint64_t de_blocknr; VasEBoot_uint64_t de_start; VasEBoot_uint64_t de_end; VasEBoot_uint64_t de_rsv; }; struct VasEBoot_nilfs2_snapshot_list { VasEBoot_uint64_t ssl_next; VasEBoot_uint64_t ssl_prev; }; struct VasEBoot_nilfs2_cpfile_header { VasEBoot_uint64_t ch_ncheckpoints; VasEBoot_uint64_t ch_nsnapshots; struct VasEBoot_nilfs2_snapshot_list ch_snapshot_list; }; struct VasEBoot_nilfs2_checkpoint { VasEBoot_uint32_t cp_flags; VasEBoot_uint32_t cp_checkpoints_count; struct VasEBoot_nilfs2_snapshot_list cp_snapshot_list; VasEBoot_uint64_t cp_cno; VasEBoot_uint64_t cp_create; VasEBoot_uint64_t cp_nblk_inc; VasEBoot_uint64_t cp_inodes_count; VasEBoot_uint64_t cp_blocks_count; struct VasEBoot_nilfs2_inode cp_ifile_inode; }; #define NILFS_BMAP_LARGE 0x1 #define NILFS_BMAP_SIZE (NILFS_INODE_BMAP_SIZE * sizeof(VasEBoot_uint64_t)) /* nilfs extra padding for nonroot btree node. */ #define NILFS_BTREE_NODE_EXTRA_PAD_SIZE (sizeof(VasEBoot_uint64_t)) #define NILFS_BTREE_ROOT_SIZE NILFS_BMAP_SIZE #define NILFS_BTREE_ROOT_NCHILDREN_MAX \ ((NILFS_BTREE_ROOT_SIZE - sizeof(struct nilfs_btree_node)) / \ (sizeof(VasEBoot_uint64_t) + sizeof(VasEBoot_uint64_t)) ) struct VasEBoot_fshelp_node { struct VasEBoot_nilfs2_data *data; struct VasEBoot_nilfs2_inode inode; VasEBoot_uint64_t ino; int inode_read; }; struct VasEBoot_nilfs2_data { struct VasEBoot_nilfs2_super_block sblock; struct VasEBoot_nilfs2_super_root sroot; struct VasEBoot_nilfs2_inode ifile; VasEBoot_disk_t disk; struct VasEBoot_nilfs2_inode *inode; struct VasEBoot_fshelp_node diropen; }; /* Log2 size of nilfs2 block in 512 blocks. */ #define LOG2_NILFS2_BLOCK_SIZE(data) \ (VasEBoot_le_to_cpu32 (data->sblock.s_log_block_size) + 1) /* Log2 size of nilfs2 block in bytes. */ #define LOG2_BLOCK_SIZE(data) \ (VasEBoot_le_to_cpu32 (data->sblock.s_log_block_size) + 10) /* The size of an nilfs2 block in bytes. */ #define NILFS2_BLOCK_SIZE(data) (1 << LOG2_BLOCK_SIZE (data)) static VasEBoot_uint64_t VasEBoot_nilfs2_dat_translate (struct VasEBoot_nilfs2_data *data, VasEBoot_uint64_t key); static VasEBoot_dl_t my_mod; static inline unsigned long VasEBoot_nilfs2_log_palloc_entries_per_group (struct VasEBoot_nilfs2_data *data) { return LOG2_BLOCK_SIZE (data) + 3; } static inline VasEBoot_uint64_t VasEBoot_nilfs2_palloc_group (struct VasEBoot_nilfs2_data *data, VasEBoot_uint64_t nr, VasEBoot_uint64_t * offset) { *offset = nr & ((1 << VasEBoot_nilfs2_log_palloc_entries_per_group (data)) - 1); return nr >> VasEBoot_nilfs2_log_palloc_entries_per_group (data); } static inline VasEBoot_uint32_t VasEBoot_nilfs2_palloc_log_groups_per_desc_block (struct VasEBoot_nilfs2_data *data) { return LOG2_BLOCK_SIZE (data) - LOG_SIZE_GROUP_DESC; COMPILE_TIME_ASSERT (sizeof (struct VasEBoot_nilfs2_palloc_group_desc) == (1 << LOG_SIZE_GROUP_DESC)); } static inline VasEBoot_uint32_t VasEBoot_nilfs2_log_entries_per_block_log (struct VasEBoot_nilfs2_data *data, unsigned long log_entry_size) { return LOG2_BLOCK_SIZE (data) - log_entry_size; } static inline VasEBoot_uint32_t VasEBoot_nilfs2_blocks_per_group_log (struct VasEBoot_nilfs2_data *data, unsigned long log_entry_size) { return (1 << (VasEBoot_nilfs2_log_palloc_entries_per_group (data) - VasEBoot_nilfs2_log_entries_per_block_log (data, log_entry_size))) + 1; } static inline VasEBoot_uint32_t VasEBoot_nilfs2_blocks_per_desc_block_log (struct VasEBoot_nilfs2_data *data, unsigned long log_entry_size) { return(VasEBoot_nilfs2_blocks_per_group_log (data, log_entry_size) << VasEBoot_nilfs2_palloc_log_groups_per_desc_block (data)) + 1; } static inline VasEBoot_uint32_t VasEBoot_nilfs2_palloc_desc_block_offset_log (struct VasEBoot_nilfs2_data *data, unsigned long group, unsigned long log_entry_size) { VasEBoot_uint32_t desc_block = group >> VasEBoot_nilfs2_palloc_log_groups_per_desc_block (data); return desc_block * VasEBoot_nilfs2_blocks_per_desc_block_log (data, log_entry_size); } static inline VasEBoot_uint32_t VasEBoot_nilfs2_palloc_bitmap_block_offset (struct VasEBoot_nilfs2_data *data, unsigned long group, unsigned long log_entry_size) { unsigned long desc_offset = group & ((1 << VasEBoot_nilfs2_palloc_log_groups_per_desc_block (data)) - 1); return VasEBoot_nilfs2_palloc_desc_block_offset_log (data, group, log_entry_size) + 1 + desc_offset * VasEBoot_nilfs2_blocks_per_group_log (data, log_entry_size); } static inline VasEBoot_uint32_t VasEBoot_nilfs2_palloc_entry_offset_log (struct VasEBoot_nilfs2_data *data, VasEBoot_uint64_t nr, unsigned long log_entry_size) { unsigned long group; VasEBoot_uint64_t group_offset; group = VasEBoot_nilfs2_palloc_group (data, nr, &group_offset); return VasEBoot_nilfs2_palloc_bitmap_block_offset (data, group, log_entry_size) + 1 + (group_offset >> VasEBoot_nilfs2_log_entries_per_block_log (data, log_entry_size)); } static inline struct VasEBoot_nilfs2_btree_node * VasEBoot_nilfs2_btree_get_root (struct VasEBoot_nilfs2_inode *inode) { return (struct VasEBoot_nilfs2_btree_node *) &inode->i_bmap[0]; } static inline int VasEBoot_nilfs2_btree_get_level (struct VasEBoot_nilfs2_btree_node *node) { return node->bn_level; } static inline VasEBoot_uint64_t * VasEBoot_nilfs2_btree_node_dkeys (struct VasEBoot_nilfs2_btree_node *node) { return (node->keys + ((node->bn_flags & NILFS_BTREE_NODE_ROOT) ? 0 : (NILFS_BTREE_NODE_EXTRA_PAD_SIZE / sizeof (VasEBoot_uint64_t)))); } static inline VasEBoot_uint64_t VasEBoot_nilfs2_btree_node_get_key (struct VasEBoot_nilfs2_btree_node *node, int index) { return VasEBoot_le_to_cpu64 (*(VasEBoot_nilfs2_btree_node_dkeys (node) + index)); } static inline int VasEBoot_nilfs2_btree_node_nchildren_max (struct VasEBoot_nilfs2_data *data, struct VasEBoot_nilfs2_btree_node *node) { int node_children_max = ((NILFS2_BLOCK_SIZE (data) - sizeof (struct VasEBoot_nilfs2_btree_node) - NILFS_BTREE_NODE_EXTRA_PAD_SIZE) / (sizeof (VasEBoot_uint64_t) + sizeof (VasEBoot_uint64_t))); return (node->bn_flags & NILFS_BTREE_NODE_ROOT) ? 3 : node_children_max; } static inline int VasEBoot_nilfs2_btree_node_lookup (struct VasEBoot_nilfs2_data *data, struct VasEBoot_nilfs2_btree_node *node, VasEBoot_uint64_t key, int *indexp) { VasEBoot_uint64_t nkey; int index = 0, low, high, s; low = 0; high = VasEBoot_le_to_cpu16 (node->bn_nchildren) - 1; if (high >= VasEBoot_nilfs2_btree_node_nchildren_max (data, node)) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "too many children"); *indexp = index; return 0; } s = 0; while (low <= high) { index = (low + high) / 2; nkey = VasEBoot_nilfs2_btree_node_get_key (node, index); if (nkey == key) { *indexp = index; return 1; } else if (nkey < key) { low = index + 1; s = -1; } else { high = index - 1; s = 1; } } if (node->bn_level > NILFS_BTREE_LEVEL_NODE_MIN) { if (s > 0 && index > 0) index--; } else if (s < 0) index++; *indexp = index; return s == 0; } static inline VasEBoot_uint64_t * VasEBoot_nilfs2_btree_node_dptrs (struct VasEBoot_nilfs2_data *data, struct VasEBoot_nilfs2_btree_node *node) { return (VasEBoot_uint64_t *) (VasEBoot_nilfs2_btree_node_dkeys (node) + VasEBoot_nilfs2_btree_node_nchildren_max (data, node)); } static inline VasEBoot_uint64_t VasEBoot_nilfs2_btree_node_get_ptr (struct VasEBoot_nilfs2_data *data, struct VasEBoot_nilfs2_btree_node *node, int index) { return VasEBoot_le_to_cpu64 (*(VasEBoot_nilfs2_btree_node_dptrs (data, node) + index)); } static inline int VasEBoot_nilfs2_btree_get_nonroot_node (struct VasEBoot_nilfs2_data *data, VasEBoot_uint64_t ptr, void *block) { VasEBoot_disk_t disk = data->disk; unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); return VasEBoot_disk_read (disk, ptr * nilfs2_block_count, 0, NILFS2_BLOCK_SIZE (data), block); } static VasEBoot_uint64_t VasEBoot_nilfs2_btree_lookup (struct VasEBoot_nilfs2_data *data, struct VasEBoot_nilfs2_inode *inode, VasEBoot_uint64_t key, int need_translate) { struct VasEBoot_nilfs2_btree_node *node; void *block; VasEBoot_uint64_t ptr; int level, found = 0, index; block = VasEBoot_malloc (NILFS2_BLOCK_SIZE (data)); if (!block) return -1; node = VasEBoot_nilfs2_btree_get_root (inode); level = VasEBoot_nilfs2_btree_get_level (node); found = VasEBoot_nilfs2_btree_node_lookup (data, node, key, &index); if (VasEBoot_errno != VAS_EBOOT_ERR_NONE) goto fail; ptr = VasEBoot_nilfs2_btree_node_get_ptr (data, node, index); if (need_translate) ptr = VasEBoot_nilfs2_dat_translate (data, ptr); for (level--; level >= NILFS_BTREE_LEVEL_NODE_MIN; level--) { VasEBoot_nilfs2_btree_get_nonroot_node (data, ptr, block); if (VasEBoot_errno) { goto fail; } node = (struct VasEBoot_nilfs2_btree_node *) block; if (node->bn_level != level) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "btree level mismatch\n"); goto fail; } if (!found) found = VasEBoot_nilfs2_btree_node_lookup (data, node, key, &index); else index = 0; if (index < VasEBoot_nilfs2_btree_node_nchildren_max (data, node) && VasEBoot_errno == VAS_EBOOT_ERR_NONE) { ptr = VasEBoot_nilfs2_btree_node_get_ptr (data, node, index); if (need_translate) ptr = VasEBoot_nilfs2_dat_translate (data, ptr); } else { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "btree corruption\n"); goto fail; } } VasEBoot_free (block); if (!found) return -1; return ptr; fail: VasEBoot_free (block); return -1; } static inline VasEBoot_uint64_t VasEBoot_nilfs2_direct_lookup (struct VasEBoot_nilfs2_inode *inode, VasEBoot_uint64_t key) { if (1 + key > 6) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "key is too large"); return 0xffffffffffffffff; } return VasEBoot_le_to_cpu64 (inode->i_bmap[1 + key]); } static inline VasEBoot_uint64_t VasEBoot_nilfs2_bmap_lookup (struct VasEBoot_nilfs2_data *data, struct VasEBoot_nilfs2_inode *inode, VasEBoot_uint64_t key, int need_translate) { struct VasEBoot_nilfs2_btree_node *root = VasEBoot_nilfs2_btree_get_root (inode); if (root->bn_flags & NILFS_BMAP_LARGE) return VasEBoot_nilfs2_btree_lookup (data, inode, key, need_translate); else { VasEBoot_uint64_t ptr; ptr = VasEBoot_nilfs2_direct_lookup (inode, key); if (ptr != ((VasEBoot_uint64_t) 0xffffffffffffffff) && need_translate) ptr = VasEBoot_nilfs2_dat_translate (data, ptr); return ptr; } } static VasEBoot_uint64_t VasEBoot_nilfs2_dat_translate (struct VasEBoot_nilfs2_data *data, VasEBoot_uint64_t key) { struct VasEBoot_nilfs2_dat_entry entry; VasEBoot_disk_t disk = data->disk; VasEBoot_uint64_t pptr; VasEBoot_uint64_t blockno, offset; unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); blockno = VasEBoot_nilfs2_palloc_entry_offset_log (data, key, LOG_NILFS_DAT_ENTRY_SIZE); offset = ((key * sizeof (struct VasEBoot_nilfs2_dat_entry)) & ((1 << LOG2_BLOCK_SIZE (data)) - 1)); pptr = VasEBoot_nilfs2_bmap_lookup (data, &data->sroot.sr_dat, blockno, 0); if (pptr == (VasEBoot_uint64_t) - 1) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "btree lookup failure"); return -1; } VasEBoot_disk_read (disk, pptr * nilfs2_block_count, offset, sizeof (struct VasEBoot_nilfs2_dat_entry), &entry); return VasEBoot_le_to_cpu64 (entry.de_blocknr); } static VasEBoot_disk_addr_t VasEBoot_nilfs2_read_block (VasEBoot_fshelp_node_t node, VasEBoot_disk_addr_t fileblock) { struct VasEBoot_nilfs2_data *data = node->data; struct VasEBoot_nilfs2_inode *inode = &node->inode; VasEBoot_uint64_t pptr = -1; pptr = VasEBoot_nilfs2_bmap_lookup (data, inode, fileblock, 1); if (pptr == (VasEBoot_uint64_t) - 1) { VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "btree lookup failure"); return -1; } return pptr; } /* Read LEN bytes from the file described by DATA starting with byte POS. Return the amount of read bytes in READ. */ static VasEBoot_ssize_t VasEBoot_nilfs2_read_file (VasEBoot_fshelp_node_t node, VasEBoot_disk_read_hook_t read_hook, void *read_hook_data, VasEBoot_off_t pos, VasEBoot_size_t len, char *buf) { return VasEBoot_fshelp_read_file (node->data->disk, node, read_hook, read_hook_data, pos, len, buf, VasEBoot_nilfs2_read_block, VasEBoot_le_to_cpu64 (node->inode.i_size), LOG2_NILFS2_BLOCK_SIZE (node->data), 0); } static VasEBoot_err_t VasEBoot_nilfs2_read_checkpoint (struct VasEBoot_nilfs2_data *data, VasEBoot_uint64_t cpno, struct VasEBoot_nilfs2_checkpoint *cpp) { VasEBoot_uint64_t blockno; VasEBoot_uint64_t offset; VasEBoot_uint64_t pptr; VasEBoot_disk_t disk = data->disk; unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); /* Assume sizeof(struct VasEBoot_nilfs2_cpfile_header) < sizeof(struct VasEBoot_nilfs2_checkpoint). */ blockno = VasEBoot_divmod64 (cpno, NILFS2_BLOCK_SIZE (data) / sizeof (struct VasEBoot_nilfs2_checkpoint), &offset); pptr = VasEBoot_nilfs2_bmap_lookup (data, &data->sroot.sr_cpfile, blockno, 1); if (pptr == (VasEBoot_uint64_t) - 1) { return VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "btree lookup failure"); } return VasEBoot_disk_read (disk, pptr * nilfs2_block_count, offset * sizeof (struct VasEBoot_nilfs2_checkpoint), sizeof (struct VasEBoot_nilfs2_checkpoint), cpp); } static inline VasEBoot_err_t VasEBoot_nilfs2_read_last_checkpoint (struct VasEBoot_nilfs2_data *data, struct VasEBoot_nilfs2_checkpoint *cpp) { return VasEBoot_nilfs2_read_checkpoint (data, VasEBoot_le_to_cpu64 (data-> sblock.s_last_cno), cpp); } /* Read the inode INO for the file described by DATA into INODE. */ static VasEBoot_err_t VasEBoot_nilfs2_read_inode (struct VasEBoot_nilfs2_data *data, VasEBoot_uint64_t ino, struct VasEBoot_nilfs2_inode *inodep) { VasEBoot_uint64_t blockno; VasEBoot_uint64_t offset; VasEBoot_uint64_t pptr; VasEBoot_disk_t disk = data->disk; unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); blockno = VasEBoot_nilfs2_palloc_entry_offset_log (data, ino, LOG_INODE_SIZE); offset = ((sizeof (struct VasEBoot_nilfs2_inode) * ino) & ((1 << LOG2_BLOCK_SIZE (data)) - 1)); pptr = VasEBoot_nilfs2_bmap_lookup (data, &data->ifile, blockno, 1); if (pptr == (VasEBoot_uint64_t) - 1) { return VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "btree lookup failure"); } return VasEBoot_disk_read (disk, pptr * nilfs2_block_count, offset, sizeof (struct VasEBoot_nilfs2_inode), inodep); } static int VasEBoot_nilfs2_valid_sb (struct VasEBoot_nilfs2_super_block *sbp) { if (VasEBoot_le_to_cpu16 (sbp->s_magic) != NILFS2_SUPER_MAGIC) return 0; if (VasEBoot_le_to_cpu32 (sbp->s_rev_level) != NILFS_SUPORT_REV) return 0; /* 20 already means 1GiB blocks. We don't want to deal with blocks overflowing int32. */ if (VasEBoot_le_to_cpu32 (sbp->s_log_block_size) > 20) return 0; return 1; } static VasEBoot_err_t VasEBoot_nilfs2_load_sb (struct VasEBoot_nilfs2_data *data) { VasEBoot_disk_t disk = data->disk; struct VasEBoot_nilfs2_super_block sb2; VasEBoot_uint64_t partition_size; int valid[2]; int swp = 0; VasEBoot_err_t err; /* Read first super block. */ err = VasEBoot_disk_read (disk, NILFS_1ST_SUPER_BLOCK, 0, sizeof (struct VasEBoot_nilfs2_super_block), &data->sblock); if (err) return err; /* Make sure if 1st super block is valid. */ valid[0] = VasEBoot_nilfs2_valid_sb (&data->sblock); if (valid[0]) partition_size = (VasEBoot_le_to_cpu64 (data->sblock.s_dev_size) >> VAS_EBOOT_DISK_SECTOR_BITS); else partition_size = VasEBoot_disk_native_sectors (disk); if (partition_size != VAS_EBOOT_DISK_SIZE_UNKNOWN) { /* Read second super block. */ err = VasEBoot_disk_read (disk, NILFS_2ND_SUPER_BLOCK (partition_size), 0, sizeof (struct VasEBoot_nilfs2_super_block), &sb2); if (err) { valid[1] = 0; VasEBoot_errno = VAS_EBOOT_ERR_NONE; } else /* Make sure if 2nd super block is valid. */ valid[1] = VasEBoot_nilfs2_valid_sb (&sb2); } else /* 2nd super block may not exist, so it's invalid. */ valid[1] = 0; if (!valid[0] && !valid[1]) return VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "not a nilfs2 filesystem"); swp = valid[1] && (!valid[0] || VasEBoot_le_to_cpu64 (data->sblock.s_last_cno) < VasEBoot_le_to_cpu64 (sb2.s_last_cno)); /* swap if first super block is invalid or older than second one. */ if (swp) VasEBoot_memcpy (&data->sblock, &sb2, sizeof (struct VasEBoot_nilfs2_super_block)); return VAS_EBOOT_ERR_NONE; } static struct VasEBoot_nilfs2_data * VasEBoot_nilfs2_mount (VasEBoot_disk_t disk) { struct VasEBoot_nilfs2_data *data; struct VasEBoot_nilfs2_segment_summary ss; struct VasEBoot_nilfs2_checkpoint last_checkpoint; VasEBoot_uint64_t last_pseg; VasEBoot_uint32_t nblocks; unsigned int nilfs2_block_count; data = VasEBoot_malloc (sizeof (struct VasEBoot_nilfs2_data)); if (!data) return 0; data->disk = disk; /* Read the superblock. */ VasEBoot_nilfs2_load_sb (data); if (VasEBoot_errno) goto fail; nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); /* Read the last segment summary. */ last_pseg = VasEBoot_le_to_cpu64 (data->sblock.s_last_pseg); VasEBoot_disk_read (disk, last_pseg * nilfs2_block_count, 0, sizeof (struct VasEBoot_nilfs2_segment_summary), &ss); if (VasEBoot_errno) goto fail; /* Read the super root block. */ nblocks = VasEBoot_le_to_cpu32 (ss.ss_nblocks); VasEBoot_disk_read (disk, (last_pseg + (nblocks - 1)) * nilfs2_block_count, 0, sizeof (struct VasEBoot_nilfs2_super_root), &data->sroot); if (VasEBoot_errno) goto fail; VasEBoot_nilfs2_read_last_checkpoint (data, &last_checkpoint); if (VasEBoot_errno) goto fail; VasEBoot_memcpy (&data->ifile, &last_checkpoint.cp_ifile_inode, sizeof (struct VasEBoot_nilfs2_inode)); data->diropen.data = data; data->diropen.ino = 2; data->diropen.inode_read = 1; data->inode = &data->diropen.inode; VasEBoot_nilfs2_read_inode (data, 2, data->inode); return data; fail: if (VasEBoot_errno == VAS_EBOOT_ERR_OUT_OF_RANGE) VasEBoot_error (VAS_EBOOT_ERR_BAD_FS, "not a nilfs2 filesystem"); VasEBoot_free (data); return 0; } static char * VasEBoot_nilfs2_read_symlink (VasEBoot_fshelp_node_t node) { char *symlink; struct VasEBoot_fshelp_node *diro = node; if (!diro->inode_read) { VasEBoot_nilfs2_read_inode (diro->data, diro->ino, &diro->inode); if (VasEBoot_errno) return 0; } symlink = VasEBoot_malloc (VasEBoot_le_to_cpu64 (diro->inode.i_size) + 1); if (!symlink) return 0; VasEBoot_nilfs2_read_file (diro, 0, 0, 0, VasEBoot_le_to_cpu64 (diro->inode.i_size), symlink); if (VasEBoot_errno) { VasEBoot_free (symlink); return 0; } symlink[VasEBoot_le_to_cpu64 (diro->inode.i_size)] = '\0'; return symlink; } static int VasEBoot_nilfs2_iterate_dir (VasEBoot_fshelp_node_t dir, VasEBoot_fshelp_iterate_dir_hook_t hook, void *hook_data) { VasEBoot_off_t fpos = 0; struct VasEBoot_fshelp_node *diro = (struct VasEBoot_fshelp_node *) dir; if (!diro->inode_read) { VasEBoot_nilfs2_read_inode (diro->data, diro->ino, &diro->inode); if (VasEBoot_errno) return 0; } /* Iterate files. */ while (fpos < VasEBoot_le_to_cpu64 (diro->inode.i_size)) { struct VasEBoot_nilfs2_dir_entry dirent; VasEBoot_nilfs2_read_file (diro, 0, 0, fpos, sizeof (struct VasEBoot_nilfs2_dir_entry), (char *) &dirent); if (VasEBoot_errno) return 0; if (dirent.rec_len == 0) return 0; if (dirent.name_len != 0) { char filename[MAX_NAMELEN + 1]; struct VasEBoot_fshelp_node *fdiro; enum VasEBoot_fshelp_filetype type = VAS_EBOOT_FSHELP_UNKNOWN; VasEBoot_nilfs2_read_file (diro, 0, 0, fpos + sizeof (struct VasEBoot_nilfs2_dir_entry), dirent.name_len, filename); if (VasEBoot_errno) return 0; fdiro = VasEBoot_malloc (sizeof (struct VasEBoot_fshelp_node)); if (!fdiro) return 0; fdiro->data = diro->data; fdiro->ino = VasEBoot_le_to_cpu64 (dirent.inode); filename[dirent.name_len] = '\0'; if (dirent.file_type != NILFS_FT_UNKNOWN) { fdiro->inode_read = 0; if (dirent.file_type == NILFS_FT_DIR) type = VAS_EBOOT_FSHELP_DIR; else if (dirent.file_type == NILFS_FT_SYMLINK) type = VAS_EBOOT_FSHELP_SYMLINK; else if (dirent.file_type == NILFS_FT_REG_FILE) type = VAS_EBOOT_FSHELP_REG; } else { /* The filetype can not be read from the dirent, read the inode to get more information. */ VasEBoot_nilfs2_read_inode (diro->data, VasEBoot_le_to_cpu64 (dirent.inode), &fdiro->inode); if (VasEBoot_errno) { VasEBoot_free (fdiro); return 0; } fdiro->inode_read = 1; if ((VasEBoot_le_to_cpu16 (fdiro->inode.i_mode) & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY) type = VAS_EBOOT_FSHELP_DIR; else if ((VasEBoot_le_to_cpu16 (fdiro->inode.i_mode) & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK) type = VAS_EBOOT_FSHELP_SYMLINK; else if ((VasEBoot_le_to_cpu16 (fdiro->inode.i_mode) & FILETYPE_INO_MASK) == FILETYPE_INO_REG) type = VAS_EBOOT_FSHELP_REG; } if (hook (filename, type, fdiro, hook_data)) return 1; } fpos += VasEBoot_le_to_cpu16 (dirent.rec_len); } return 0; } /* Open a file named NAME and initialize FILE. */ static VasEBoot_err_t VasEBoot_nilfs2_open (struct VasEBoot_file *file, const char *name) { struct VasEBoot_nilfs2_data *data = NULL; struct VasEBoot_fshelp_node *fdiro = 0; VasEBoot_dl_ref (my_mod); data = VasEBoot_nilfs2_mount (file->device->disk); if (!data) goto fail; VasEBoot_fshelp_find_file (name, &data->diropen, &fdiro, VasEBoot_nilfs2_iterate_dir, VasEBoot_nilfs2_read_symlink, VAS_EBOOT_FSHELP_REG); if (VasEBoot_errno) goto fail; if (!fdiro->inode_read) { VasEBoot_nilfs2_read_inode (data, fdiro->ino, &fdiro->inode); if (VasEBoot_errno) goto fail; } VasEBoot_memcpy (data->inode, &fdiro->inode, sizeof (struct VasEBoot_nilfs2_inode)); VasEBoot_free (fdiro); file->size = VasEBoot_le_to_cpu64 (data->inode->i_size); file->data = data; file->offset = 0; return 0; fail: if (fdiro != &data->diropen) VasEBoot_free (fdiro); VasEBoot_free (data); VasEBoot_dl_unref (my_mod); return VasEBoot_errno; } static VasEBoot_err_t VasEBoot_nilfs2_close (VasEBoot_file_t file) { VasEBoot_free (file->data); VasEBoot_dl_unref (my_mod); return VAS_EBOOT_ERR_NONE; } /* Read LEN bytes data from FILE into BUF. */ static VasEBoot_ssize_t VasEBoot_nilfs2_read (VasEBoot_file_t file, char *buf, VasEBoot_size_t len) { struct VasEBoot_nilfs2_data *data = (struct VasEBoot_nilfs2_data *) file->data; return VasEBoot_nilfs2_read_file (&data->diropen, file->read_hook, file->read_hook_data, file->offset, len, buf); } /* Context for VasEBoot_nilfs2_dir. */ struct VasEBoot_nilfs2_dir_ctx { VasEBoot_fs_dir_hook_t hook; void *hook_data; struct VasEBoot_nilfs2_data *data; }; /* Helper for VasEBoot_nilfs2_dir. */ static int VasEBoot_nilfs2_dir_iter (const char *filename, enum VasEBoot_fshelp_filetype filetype, VasEBoot_fshelp_node_t node, void *data) { struct VasEBoot_nilfs2_dir_ctx *ctx = data; struct VasEBoot_dirhook_info info; VasEBoot_memset (&info, 0, sizeof (info)); if (!node->inode_read) { VasEBoot_nilfs2_read_inode (ctx->data, node->ino, &node->inode); if (!VasEBoot_errno) node->inode_read = 1; VasEBoot_errno = VAS_EBOOT_ERR_NONE; } if (node->inode_read) { info.mtimeset = 1; info.mtime = VasEBoot_le_to_cpu64 (node->inode.i_mtime); } info.dir = ((filetype & VAS_EBOOT_FSHELP_TYPE_MASK) == VAS_EBOOT_FSHELP_DIR); VasEBoot_free (node); return ctx->hook (filename, &info, ctx->hook_data); } static VasEBoot_err_t VasEBoot_nilfs2_dir (VasEBoot_device_t device, const char *path, VasEBoot_fs_dir_hook_t hook, void *hook_data) { struct VasEBoot_nilfs2_dir_ctx ctx = { .hook = hook, .hook_data = hook_data }; struct VasEBoot_fshelp_node *fdiro = 0; VasEBoot_dl_ref (my_mod); ctx.data = VasEBoot_nilfs2_mount (device->disk); if (!ctx.data) goto fail; VasEBoot_fshelp_find_file (path, &ctx.data->diropen, &fdiro, VasEBoot_nilfs2_iterate_dir, VasEBoot_nilfs2_read_symlink, VAS_EBOOT_FSHELP_DIR); if (VasEBoot_errno) goto fail; VasEBoot_nilfs2_iterate_dir (fdiro, VasEBoot_nilfs2_dir_iter, &ctx); fail: if (fdiro != &ctx.data->diropen) VasEBoot_free (fdiro); VasEBoot_free (ctx.data); VasEBoot_dl_unref (my_mod); return VasEBoot_errno; } static VasEBoot_err_t VasEBoot_nilfs2_label (VasEBoot_device_t device, char **label) { struct VasEBoot_nilfs2_data *data; VasEBoot_disk_t disk = device->disk; VasEBoot_dl_ref (my_mod); data = VasEBoot_nilfs2_mount (disk); if (data) *label = VasEBoot_strndup (data->sblock.s_volume_name, sizeof (data->sblock.s_volume_name)); else *label = NULL; VasEBoot_dl_unref (my_mod); VasEBoot_free (data); return VasEBoot_errno; } static VasEBoot_err_t VasEBoot_nilfs2_uuid (VasEBoot_device_t device, char **uuid) { struct VasEBoot_nilfs2_data *data; VasEBoot_disk_t disk = device->disk; VasEBoot_dl_ref (my_mod); data = VasEBoot_nilfs2_mount (disk); if (data) { *uuid = VasEBoot_xasprintf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", data->sblock.s_uuid[0], data->sblock.s_uuid[1], data->sblock.s_uuid[2], data->sblock.s_uuid[3], data->sblock.s_uuid[4], data->sblock.s_uuid[5], data->sblock.s_uuid[6], data->sblock.s_uuid[7], data->sblock.s_uuid[8], data->sblock.s_uuid[9], data->sblock.s_uuid[10], data->sblock.s_uuid[11], data->sblock.s_uuid[12], data->sblock.s_uuid[13], data->sblock.s_uuid[14], data->sblock.s_uuid[15]); } else *uuid = NULL; VasEBoot_dl_unref (my_mod); VasEBoot_free (data); return VasEBoot_errno; } /* Get mtime. */ static VasEBoot_err_t VasEBoot_nilfs2_mtime (VasEBoot_device_t device, VasEBoot_int64_t * tm) { struct VasEBoot_nilfs2_data *data; VasEBoot_disk_t disk = device->disk; VasEBoot_dl_ref (my_mod); data = VasEBoot_nilfs2_mount (disk); if (!data) *tm = 0; else *tm = (VasEBoot_int32_t) VasEBoot_le_to_cpu64 (data->sblock.s_wtime); VasEBoot_dl_unref (my_mod); VasEBoot_free (data); return VasEBoot_errno; } static struct VasEBoot_fs VasEBoot_nilfs2_fs = { .name = "nilfs2", .fs_dir = VasEBoot_nilfs2_dir, .fs_open = VasEBoot_nilfs2_open, .fs_read = VasEBoot_nilfs2_read, .fs_close = VasEBoot_nilfs2_close, .fs_label = VasEBoot_nilfs2_label, .fs_uuid = VasEBoot_nilfs2_uuid, .fs_mtime = VasEBoot_nilfs2_mtime, #ifdef VAS_EBOOT_UTIL .reserved_first_sector = 1, .blocklist_install = 0, #endif .next = 0 }; VAS_EBOOT_MOD_INIT (nilfs2) { COMPILE_TIME_ASSERT ((1 << LOG_NILFS_DAT_ENTRY_SIZE) == sizeof (struct VasEBoot_nilfs2_dat_entry)); COMPILE_TIME_ASSERT (1 << LOG_INODE_SIZE == sizeof (struct VasEBoot_nilfs2_inode)); if (!VasEBoot_is_lockdown ()) { VasEBoot_nilfs2_fs.mod = mod; VasEBoot_fs_register (&VasEBoot_nilfs2_fs); } my_mod = mod; } VAS_EBOOT_MOD_FINI (nilfs2) { if (!VasEBoot_is_lockdown ()) VasEBoot_fs_unregister (&VasEBoot_nilfs2_fs); }