18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/ext4/namei.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1992, 1993, 1994, 1995 68c2ecf20Sopenharmony_ci * Remy Card (card@masi.ibp.fr) 78c2ecf20Sopenharmony_ci * Laboratoire MASI - Institut Blaise Pascal 88c2ecf20Sopenharmony_ci * Universite Pierre et Marie Curie (Paris VI) 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * from 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * linux/fs/minix/namei.c 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Big-endian to little-endian byte-swapping/bitmaps by 178c2ecf20Sopenharmony_ci * David S. Miller (davem@caip.rutgers.edu), 1995 188c2ecf20Sopenharmony_ci * Directory entry file type support and forward compatibility hooks 198c2ecf20Sopenharmony_ci * for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998 208c2ecf20Sopenharmony_ci * Hash Tree Directory indexing (c) 218c2ecf20Sopenharmony_ci * Daniel Phillips, 2001 228c2ecf20Sopenharmony_ci * Hash Tree Directory indexing porting 238c2ecf20Sopenharmony_ci * Christopher Li, 2002 248c2ecf20Sopenharmony_ci * Hash Tree Directory indexing cleanup 258c2ecf20Sopenharmony_ci * Theodore Ts'o, 2002 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/fs.h> 298c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 308c2ecf20Sopenharmony_ci#include <linux/time.h> 318c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 328c2ecf20Sopenharmony_ci#include <linux/stat.h> 338c2ecf20Sopenharmony_ci#include <linux/string.h> 348c2ecf20Sopenharmony_ci#include <linux/quotaops.h> 358c2ecf20Sopenharmony_ci#include <linux/buffer_head.h> 368c2ecf20Sopenharmony_ci#include <linux/bio.h> 378c2ecf20Sopenharmony_ci#include <linux/iversion.h> 388c2ecf20Sopenharmony_ci#include <linux/unicode.h> 398c2ecf20Sopenharmony_ci#include "ext4.h" 408c2ecf20Sopenharmony_ci#include "ext4_jbd2.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include "xattr.h" 438c2ecf20Sopenharmony_ci#include "acl.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include <trace/events/ext4.h> 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * define how far ahead to read directories while searching them. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_ci#define NAMEI_RA_CHUNKS 2 508c2ecf20Sopenharmony_ci#define NAMEI_RA_BLOCKS 4 518c2ecf20Sopenharmony_ci#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic struct buffer_head *ext4_append(handle_t *handle, 548c2ecf20Sopenharmony_ci struct inode *inode, 558c2ecf20Sopenharmony_ci ext4_lblk_t *block) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci struct ext4_map_blocks map; 588c2ecf20Sopenharmony_ci struct buffer_head *bh; 598c2ecf20Sopenharmony_ci int err; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (unlikely(EXT4_SB(inode->i_sb)->s_max_dir_size_kb && 628c2ecf20Sopenharmony_ci ((inode->i_size >> 10) >= 638c2ecf20Sopenharmony_ci EXT4_SB(inode->i_sb)->s_max_dir_size_kb))) 648c2ecf20Sopenharmony_ci return ERR_PTR(-ENOSPC); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci *block = inode->i_size >> inode->i_sb->s_blocksize_bits; 678c2ecf20Sopenharmony_ci map.m_lblk = *block; 688c2ecf20Sopenharmony_ci map.m_len = 1; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* 718c2ecf20Sopenharmony_ci * We're appending new directory block. Make sure the block is not 728c2ecf20Sopenharmony_ci * allocated yet, otherwise we will end up corrupting the 738c2ecf20Sopenharmony_ci * directory. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci err = ext4_map_blocks(NULL, inode, &map, 0); 768c2ecf20Sopenharmony_ci if (err < 0) 778c2ecf20Sopenharmony_ci return ERR_PTR(err); 788c2ecf20Sopenharmony_ci if (err) { 798c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "Logical block already allocated"); 808c2ecf20Sopenharmony_ci return ERR_PTR(-EFSCORRUPTED); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci bh = ext4_bread(handle, inode, *block, EXT4_GET_BLOCKS_CREATE); 848c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 858c2ecf20Sopenharmony_ci return bh; 868c2ecf20Sopenharmony_ci inode->i_size += inode->i_sb->s_blocksize; 878c2ecf20Sopenharmony_ci EXT4_I(inode)->i_disksize = inode->i_size; 888c2ecf20Sopenharmony_ci BUFFER_TRACE(bh, "get_write_access"); 898c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, bh); 908c2ecf20Sopenharmony_ci if (err) { 918c2ecf20Sopenharmony_ci brelse(bh); 928c2ecf20Sopenharmony_ci ext4_std_error(inode->i_sb, err); 938c2ecf20Sopenharmony_ci return ERR_PTR(err); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci return bh; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int ext4_dx_csum_verify(struct inode *inode, 998c2ecf20Sopenharmony_ci struct ext4_dir_entry *dirent); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* 1028c2ecf20Sopenharmony_ci * Hints to ext4_read_dirblock regarding whether we expect a directory 1038c2ecf20Sopenharmony_ci * block being read to be an index block, or a block containing 1048c2ecf20Sopenharmony_ci * directory entries (and if the latter, whether it was found via a 1058c2ecf20Sopenharmony_ci * logical block in an htree index block). This is used to control 1068c2ecf20Sopenharmony_ci * what sort of sanity checkinig ext4_read_dirblock() will do on the 1078c2ecf20Sopenharmony_ci * directory block read from the storage device. EITHER will means 1088c2ecf20Sopenharmony_ci * the caller doesn't know what kind of directory block will be read, 1098c2ecf20Sopenharmony_ci * so no specific verification will be done. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_citypedef enum { 1128c2ecf20Sopenharmony_ci EITHER, INDEX, DIRENT, DIRENT_HTREE 1138c2ecf20Sopenharmony_ci} dirblock_type_t; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#define ext4_read_dirblock(inode, block, type) \ 1168c2ecf20Sopenharmony_ci __ext4_read_dirblock((inode), (block), (type), __func__, __LINE__) 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic struct buffer_head *__ext4_read_dirblock(struct inode *inode, 1198c2ecf20Sopenharmony_ci ext4_lblk_t block, 1208c2ecf20Sopenharmony_ci dirblock_type_t type, 1218c2ecf20Sopenharmony_ci const char *func, 1228c2ecf20Sopenharmony_ci unsigned int line) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct buffer_head *bh; 1258c2ecf20Sopenharmony_ci struct ext4_dir_entry *dirent; 1268c2ecf20Sopenharmony_ci int is_dx_block = 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (block >= inode->i_size >> inode->i_blkbits) { 1298c2ecf20Sopenharmony_ci ext4_error_inode(inode, func, line, block, 1308c2ecf20Sopenharmony_ci "Attempting to read directory block (%u) that is past i_size (%llu)", 1318c2ecf20Sopenharmony_ci block, inode->i_size); 1328c2ecf20Sopenharmony_ci return ERR_PTR(-EFSCORRUPTED); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_EIO)) 1368c2ecf20Sopenharmony_ci bh = ERR_PTR(-EIO); 1378c2ecf20Sopenharmony_ci else 1388c2ecf20Sopenharmony_ci bh = ext4_bread(NULL, inode, block, 0); 1398c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 1408c2ecf20Sopenharmony_ci __ext4_warning(inode->i_sb, func, line, 1418c2ecf20Sopenharmony_ci "inode #%lu: lblock %lu: comm %s: " 1428c2ecf20Sopenharmony_ci "error %ld reading directory block", 1438c2ecf20Sopenharmony_ci inode->i_ino, (unsigned long)block, 1448c2ecf20Sopenharmony_ci current->comm, PTR_ERR(bh)); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return bh; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci /* The first directory block must not be a hole. */ 1498c2ecf20Sopenharmony_ci if (!bh && (type == INDEX || type == DIRENT_HTREE || block == 0)) { 1508c2ecf20Sopenharmony_ci ext4_error_inode(inode, func, line, block, 1518c2ecf20Sopenharmony_ci "Directory hole found for htree %s block %u", 1528c2ecf20Sopenharmony_ci (type == INDEX) ? "index" : "leaf", block); 1538c2ecf20Sopenharmony_ci return ERR_PTR(-EFSCORRUPTED); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci if (!bh) 1568c2ecf20Sopenharmony_ci return NULL; 1578c2ecf20Sopenharmony_ci dirent = (struct ext4_dir_entry *) bh->b_data; 1588c2ecf20Sopenharmony_ci /* Determine whether or not we have an index block */ 1598c2ecf20Sopenharmony_ci if (is_dx(inode)) { 1608c2ecf20Sopenharmony_ci if (block == 0) 1618c2ecf20Sopenharmony_ci is_dx_block = 1; 1628c2ecf20Sopenharmony_ci else if (ext4_rec_len_from_disk(dirent->rec_len, 1638c2ecf20Sopenharmony_ci inode->i_sb->s_blocksize) == 1648c2ecf20Sopenharmony_ci inode->i_sb->s_blocksize) 1658c2ecf20Sopenharmony_ci is_dx_block = 1; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci if (!is_dx_block && type == INDEX) { 1688c2ecf20Sopenharmony_ci ext4_error_inode(inode, func, line, block, 1698c2ecf20Sopenharmony_ci "directory leaf block found instead of index block"); 1708c2ecf20Sopenharmony_ci brelse(bh); 1718c2ecf20Sopenharmony_ci return ERR_PTR(-EFSCORRUPTED); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci if (!ext4_has_metadata_csum(inode->i_sb) || 1748c2ecf20Sopenharmony_ci buffer_verified(bh)) 1758c2ecf20Sopenharmony_ci return bh; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* 1788c2ecf20Sopenharmony_ci * An empty leaf block can get mistaken for a index block; for 1798c2ecf20Sopenharmony_ci * this reason, we can only check the index checksum when the 1808c2ecf20Sopenharmony_ci * caller is sure it should be an index block. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci if (is_dx_block && type == INDEX) { 1838c2ecf20Sopenharmony_ci if (ext4_dx_csum_verify(inode, dirent) && 1848c2ecf20Sopenharmony_ci !ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_CRC)) 1858c2ecf20Sopenharmony_ci set_buffer_verified(bh); 1868c2ecf20Sopenharmony_ci else { 1878c2ecf20Sopenharmony_ci ext4_error_inode_err(inode, func, line, block, 1888c2ecf20Sopenharmony_ci EFSBADCRC, 1898c2ecf20Sopenharmony_ci "Directory index failed checksum"); 1908c2ecf20Sopenharmony_ci brelse(bh); 1918c2ecf20Sopenharmony_ci return ERR_PTR(-EFSBADCRC); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci if (!is_dx_block) { 1958c2ecf20Sopenharmony_ci if (ext4_dirblock_csum_verify(inode, bh) && 1968c2ecf20Sopenharmony_ci !ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_CRC)) 1978c2ecf20Sopenharmony_ci set_buffer_verified(bh); 1988c2ecf20Sopenharmony_ci else { 1998c2ecf20Sopenharmony_ci ext4_error_inode_err(inode, func, line, block, 2008c2ecf20Sopenharmony_ci EFSBADCRC, 2018c2ecf20Sopenharmony_ci "Directory block failed checksum"); 2028c2ecf20Sopenharmony_ci brelse(bh); 2038c2ecf20Sopenharmony_ci return ERR_PTR(-EFSBADCRC); 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci return bh; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci#ifndef assert 2108c2ecf20Sopenharmony_ci#define assert(test) J_ASSERT(test) 2118c2ecf20Sopenharmony_ci#endif 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci#ifdef DX_DEBUG 2148c2ecf20Sopenharmony_ci#define dxtrace(command) command 2158c2ecf20Sopenharmony_ci#else 2168c2ecf20Sopenharmony_ci#define dxtrace(command) 2178c2ecf20Sopenharmony_ci#endif 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistruct fake_dirent 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci __le32 inode; 2228c2ecf20Sopenharmony_ci __le16 rec_len; 2238c2ecf20Sopenharmony_ci u8 name_len; 2248c2ecf20Sopenharmony_ci u8 file_type; 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistruct dx_countlimit 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci __le16 limit; 2308c2ecf20Sopenharmony_ci __le16 count; 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistruct dx_entry 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci __le32 hash; 2368c2ecf20Sopenharmony_ci __le32 block; 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* 2408c2ecf20Sopenharmony_ci * dx_root_info is laid out so that if it should somehow get overlaid by a 2418c2ecf20Sopenharmony_ci * dirent the two low bits of the hash version will be zero. Therefore, the 2428c2ecf20Sopenharmony_ci * hash version mod 4 should never be 0. Sincerely, the paranoia department. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistruct dx_root 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct fake_dirent dot; 2488c2ecf20Sopenharmony_ci char dot_name[4]; 2498c2ecf20Sopenharmony_ci struct fake_dirent dotdot; 2508c2ecf20Sopenharmony_ci char dotdot_name[4]; 2518c2ecf20Sopenharmony_ci struct dx_root_info 2528c2ecf20Sopenharmony_ci { 2538c2ecf20Sopenharmony_ci __le32 reserved_zero; 2548c2ecf20Sopenharmony_ci u8 hash_version; 2558c2ecf20Sopenharmony_ci u8 info_length; /* 8 */ 2568c2ecf20Sopenharmony_ci u8 indirect_levels; 2578c2ecf20Sopenharmony_ci u8 unused_flags; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci info; 2608c2ecf20Sopenharmony_ci struct dx_entry entries[]; 2618c2ecf20Sopenharmony_ci}; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistruct dx_node 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct fake_dirent fake; 2668c2ecf20Sopenharmony_ci struct dx_entry entries[]; 2678c2ecf20Sopenharmony_ci}; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistruct dx_frame 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci struct buffer_head *bh; 2738c2ecf20Sopenharmony_ci struct dx_entry *entries; 2748c2ecf20Sopenharmony_ci struct dx_entry *at; 2758c2ecf20Sopenharmony_ci}; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistruct dx_map_entry 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci u32 hash; 2808c2ecf20Sopenharmony_ci u16 offs; 2818c2ecf20Sopenharmony_ci u16 size; 2828c2ecf20Sopenharmony_ci}; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* 2858c2ecf20Sopenharmony_ci * This goes at the end of each htree block. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_cistruct dx_tail { 2888c2ecf20Sopenharmony_ci u32 dt_reserved; 2898c2ecf20Sopenharmony_ci __le32 dt_checksum; /* crc32c(uuid+inum+dirblock) */ 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic inline ext4_lblk_t dx_get_block(struct dx_entry *entry); 2938c2ecf20Sopenharmony_cistatic void dx_set_block(struct dx_entry *entry, ext4_lblk_t value); 2948c2ecf20Sopenharmony_cistatic inline unsigned dx_get_hash(struct dx_entry *entry); 2958c2ecf20Sopenharmony_cistatic void dx_set_hash(struct dx_entry *entry, unsigned value); 2968c2ecf20Sopenharmony_cistatic unsigned dx_get_count(struct dx_entry *entries); 2978c2ecf20Sopenharmony_cistatic unsigned dx_get_limit(struct dx_entry *entries); 2988c2ecf20Sopenharmony_cistatic void dx_set_count(struct dx_entry *entries, unsigned value); 2998c2ecf20Sopenharmony_cistatic void dx_set_limit(struct dx_entry *entries, unsigned value); 3008c2ecf20Sopenharmony_cistatic unsigned dx_root_limit(struct inode *dir, unsigned infosize); 3018c2ecf20Sopenharmony_cistatic unsigned dx_node_limit(struct inode *dir); 3028c2ecf20Sopenharmony_cistatic struct dx_frame *dx_probe(struct ext4_filename *fname, 3038c2ecf20Sopenharmony_ci struct inode *dir, 3048c2ecf20Sopenharmony_ci struct dx_hash_info *hinfo, 3058c2ecf20Sopenharmony_ci struct dx_frame *frame); 3068c2ecf20Sopenharmony_cistatic void dx_release(struct dx_frame *frames); 3078c2ecf20Sopenharmony_cistatic int dx_make_map(struct inode *dir, struct buffer_head *bh, 3088c2ecf20Sopenharmony_ci struct dx_hash_info *hinfo, 3098c2ecf20Sopenharmony_ci struct dx_map_entry *map_tail); 3108c2ecf20Sopenharmony_cistatic void dx_sort_map(struct dx_map_entry *map, unsigned count); 3118c2ecf20Sopenharmony_cistatic struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to, 3128c2ecf20Sopenharmony_ci struct dx_map_entry *offsets, int count, unsigned blocksize); 3138c2ecf20Sopenharmony_cistatic struct ext4_dir_entry_2* dx_pack_dirents(char *base, unsigned blocksize); 3148c2ecf20Sopenharmony_cistatic void dx_insert_block(struct dx_frame *frame, 3158c2ecf20Sopenharmony_ci u32 hash, ext4_lblk_t block); 3168c2ecf20Sopenharmony_cistatic int ext4_htree_next_block(struct inode *dir, __u32 hash, 3178c2ecf20Sopenharmony_ci struct dx_frame *frame, 3188c2ecf20Sopenharmony_ci struct dx_frame *frames, 3198c2ecf20Sopenharmony_ci __u32 *start_hash); 3208c2ecf20Sopenharmony_cistatic struct buffer_head * ext4_dx_find_entry(struct inode *dir, 3218c2ecf20Sopenharmony_ci struct ext4_filename *fname, 3228c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 **res_dir); 3238c2ecf20Sopenharmony_cistatic int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname, 3248c2ecf20Sopenharmony_ci struct inode *dir, struct inode *inode); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/* checksumming functions */ 3278c2ecf20Sopenharmony_civoid ext4_initialize_dirent_tail(struct buffer_head *bh, 3288c2ecf20Sopenharmony_ci unsigned int blocksize) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct ext4_dir_entry_tail *t = EXT4_DIRENT_TAIL(bh->b_data, blocksize); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci memset(t, 0, sizeof(struct ext4_dir_entry_tail)); 3338c2ecf20Sopenharmony_ci t->det_rec_len = ext4_rec_len_to_disk( 3348c2ecf20Sopenharmony_ci sizeof(struct ext4_dir_entry_tail), blocksize); 3358c2ecf20Sopenharmony_ci t->det_reserved_ft = EXT4_FT_DIR_CSUM; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/* Walk through a dirent block to find a checksum "dirent" at the tail */ 3398c2ecf20Sopenharmony_cistatic struct ext4_dir_entry_tail *get_dirent_tail(struct inode *inode, 3408c2ecf20Sopenharmony_ci struct buffer_head *bh) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct ext4_dir_entry_tail *t; 3438c2ecf20Sopenharmony_ci int blocksize = EXT4_BLOCK_SIZE(inode->i_sb); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci#ifdef PARANOID 3468c2ecf20Sopenharmony_ci struct ext4_dir_entry *d, *top; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci d = (struct ext4_dir_entry *)bh->b_data; 3498c2ecf20Sopenharmony_ci top = (struct ext4_dir_entry *)(bh->b_data + 3508c2ecf20Sopenharmony_ci (blocksize - sizeof(struct ext4_dir_entry_tail))); 3518c2ecf20Sopenharmony_ci while (d < top && ext4_rec_len_from_disk(d->rec_len, blocksize)) 3528c2ecf20Sopenharmony_ci d = (struct ext4_dir_entry *)(((void *)d) + 3538c2ecf20Sopenharmony_ci ext4_rec_len_from_disk(d->rec_len, blocksize)); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (d != top) 3568c2ecf20Sopenharmony_ci return NULL; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci t = (struct ext4_dir_entry_tail *)d; 3598c2ecf20Sopenharmony_ci#else 3608c2ecf20Sopenharmony_ci t = EXT4_DIRENT_TAIL(bh->b_data, EXT4_BLOCK_SIZE(inode->i_sb)); 3618c2ecf20Sopenharmony_ci#endif 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (t->det_reserved_zero1 || 3648c2ecf20Sopenharmony_ci (ext4_rec_len_from_disk(t->det_rec_len, blocksize) != 3658c2ecf20Sopenharmony_ci sizeof(struct ext4_dir_entry_tail)) || 3668c2ecf20Sopenharmony_ci t->det_reserved_zero2 || 3678c2ecf20Sopenharmony_ci t->det_reserved_ft != EXT4_FT_DIR_CSUM) 3688c2ecf20Sopenharmony_ci return NULL; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci return t; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic __le32 ext4_dirblock_csum(struct inode *inode, void *dirent, int size) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); 3768c2ecf20Sopenharmony_ci struct ext4_inode_info *ei = EXT4_I(inode); 3778c2ecf20Sopenharmony_ci __u32 csum; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size); 3808c2ecf20Sopenharmony_ci return cpu_to_le32(csum); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci#define warn_no_space_for_csum(inode) \ 3848c2ecf20Sopenharmony_ci __warn_no_space_for_csum((inode), __func__, __LINE__) 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic void __warn_no_space_for_csum(struct inode *inode, const char *func, 3878c2ecf20Sopenharmony_ci unsigned int line) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci __ext4_warning_inode(inode, func, line, 3908c2ecf20Sopenharmony_ci "No space for directory leaf checksum. Please run e2fsck -D."); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ciint ext4_dirblock_csum_verify(struct inode *inode, struct buffer_head *bh) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct ext4_dir_entry_tail *t; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (!ext4_has_metadata_csum(inode->i_sb)) 3988c2ecf20Sopenharmony_ci return 1; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci t = get_dirent_tail(inode, bh); 4018c2ecf20Sopenharmony_ci if (!t) { 4028c2ecf20Sopenharmony_ci warn_no_space_for_csum(inode); 4038c2ecf20Sopenharmony_ci return 0; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (t->det_checksum != ext4_dirblock_csum(inode, bh->b_data, 4078c2ecf20Sopenharmony_ci (char *)t - bh->b_data)) 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return 1; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic void ext4_dirblock_csum_set(struct inode *inode, 4148c2ecf20Sopenharmony_ci struct buffer_head *bh) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct ext4_dir_entry_tail *t; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (!ext4_has_metadata_csum(inode->i_sb)) 4198c2ecf20Sopenharmony_ci return; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci t = get_dirent_tail(inode, bh); 4228c2ecf20Sopenharmony_ci if (!t) { 4238c2ecf20Sopenharmony_ci warn_no_space_for_csum(inode); 4248c2ecf20Sopenharmony_ci return; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci t->det_checksum = ext4_dirblock_csum(inode, bh->b_data, 4288c2ecf20Sopenharmony_ci (char *)t - bh->b_data); 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ciint ext4_handle_dirty_dirblock(handle_t *handle, 4328c2ecf20Sopenharmony_ci struct inode *inode, 4338c2ecf20Sopenharmony_ci struct buffer_head *bh) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci ext4_dirblock_csum_set(inode, bh); 4368c2ecf20Sopenharmony_ci return ext4_handle_dirty_metadata(handle, inode, bh); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic struct dx_countlimit *get_dx_countlimit(struct inode *inode, 4408c2ecf20Sopenharmony_ci struct ext4_dir_entry *dirent, 4418c2ecf20Sopenharmony_ci int *offset) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct ext4_dir_entry *dp; 4448c2ecf20Sopenharmony_ci struct dx_root_info *root; 4458c2ecf20Sopenharmony_ci int count_offset; 4468c2ecf20Sopenharmony_ci int blocksize = EXT4_BLOCK_SIZE(inode->i_sb); 4478c2ecf20Sopenharmony_ci unsigned int rlen = ext4_rec_len_from_disk(dirent->rec_len, blocksize); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (rlen == blocksize) 4508c2ecf20Sopenharmony_ci count_offset = 8; 4518c2ecf20Sopenharmony_ci else if (rlen == 12) { 4528c2ecf20Sopenharmony_ci dp = (struct ext4_dir_entry *)(((void *)dirent) + 12); 4538c2ecf20Sopenharmony_ci if (ext4_rec_len_from_disk(dp->rec_len, blocksize) != blocksize - 12) 4548c2ecf20Sopenharmony_ci return NULL; 4558c2ecf20Sopenharmony_ci root = (struct dx_root_info *)(((void *)dp + 12)); 4568c2ecf20Sopenharmony_ci if (root->reserved_zero || 4578c2ecf20Sopenharmony_ci root->info_length != sizeof(struct dx_root_info)) 4588c2ecf20Sopenharmony_ci return NULL; 4598c2ecf20Sopenharmony_ci count_offset = 32; 4608c2ecf20Sopenharmony_ci } else 4618c2ecf20Sopenharmony_ci return NULL; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (offset) 4648c2ecf20Sopenharmony_ci *offset = count_offset; 4658c2ecf20Sopenharmony_ci return (struct dx_countlimit *)(((void *)dirent) + count_offset); 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic __le32 ext4_dx_csum(struct inode *inode, struct ext4_dir_entry *dirent, 4698c2ecf20Sopenharmony_ci int count_offset, int count, struct dx_tail *t) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); 4728c2ecf20Sopenharmony_ci struct ext4_inode_info *ei = EXT4_I(inode); 4738c2ecf20Sopenharmony_ci __u32 csum; 4748c2ecf20Sopenharmony_ci int size; 4758c2ecf20Sopenharmony_ci __u32 dummy_csum = 0; 4768c2ecf20Sopenharmony_ci int offset = offsetof(struct dx_tail, dt_checksum); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci size = count_offset + (count * sizeof(struct dx_entry)); 4798c2ecf20Sopenharmony_ci csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size); 4808c2ecf20Sopenharmony_ci csum = ext4_chksum(sbi, csum, (__u8 *)t, offset); 4818c2ecf20Sopenharmony_ci csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum)); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci return cpu_to_le32(csum); 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_cistatic int ext4_dx_csum_verify(struct inode *inode, 4878c2ecf20Sopenharmony_ci struct ext4_dir_entry *dirent) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct dx_countlimit *c; 4908c2ecf20Sopenharmony_ci struct dx_tail *t; 4918c2ecf20Sopenharmony_ci int count_offset, limit, count; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (!ext4_has_metadata_csum(inode->i_sb)) 4948c2ecf20Sopenharmony_ci return 1; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci c = get_dx_countlimit(inode, dirent, &count_offset); 4978c2ecf20Sopenharmony_ci if (!c) { 4988c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D."); 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci limit = le16_to_cpu(c->limit); 5028c2ecf20Sopenharmony_ci count = le16_to_cpu(c->count); 5038c2ecf20Sopenharmony_ci if (count_offset + (limit * sizeof(struct dx_entry)) > 5048c2ecf20Sopenharmony_ci EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) { 5058c2ecf20Sopenharmony_ci warn_no_space_for_csum(inode); 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci t = (struct dx_tail *)(((struct dx_entry *)c) + limit); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (t->dt_checksum != ext4_dx_csum(inode, dirent, count_offset, 5118c2ecf20Sopenharmony_ci count, t)) 5128c2ecf20Sopenharmony_ci return 0; 5138c2ecf20Sopenharmony_ci return 1; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic void ext4_dx_csum_set(struct inode *inode, struct ext4_dir_entry *dirent) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct dx_countlimit *c; 5198c2ecf20Sopenharmony_ci struct dx_tail *t; 5208c2ecf20Sopenharmony_ci int count_offset, limit, count; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (!ext4_has_metadata_csum(inode->i_sb)) 5238c2ecf20Sopenharmony_ci return; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci c = get_dx_countlimit(inode, dirent, &count_offset); 5268c2ecf20Sopenharmony_ci if (!c) { 5278c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "dir seems corrupt? Run e2fsck -D."); 5288c2ecf20Sopenharmony_ci return; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci limit = le16_to_cpu(c->limit); 5318c2ecf20Sopenharmony_ci count = le16_to_cpu(c->count); 5328c2ecf20Sopenharmony_ci if (count_offset + (limit * sizeof(struct dx_entry)) > 5338c2ecf20Sopenharmony_ci EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) { 5348c2ecf20Sopenharmony_ci warn_no_space_for_csum(inode); 5358c2ecf20Sopenharmony_ci return; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci t = (struct dx_tail *)(((struct dx_entry *)c) + limit); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci t->dt_checksum = ext4_dx_csum(inode, dirent, count_offset, count, t); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic inline int ext4_handle_dirty_dx_node(handle_t *handle, 5438c2ecf20Sopenharmony_ci struct inode *inode, 5448c2ecf20Sopenharmony_ci struct buffer_head *bh) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci ext4_dx_csum_set(inode, (struct ext4_dir_entry *)bh->b_data); 5478c2ecf20Sopenharmony_ci return ext4_handle_dirty_metadata(handle, inode, bh); 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci/* 5518c2ecf20Sopenharmony_ci * p is at least 6 bytes before the end of page 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_cistatic inline struct ext4_dir_entry_2 * 5548c2ecf20Sopenharmony_ciext4_next_entry(struct ext4_dir_entry_2 *p, unsigned long blocksize) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci return (struct ext4_dir_entry_2 *)((char *)p + 5578c2ecf20Sopenharmony_ci ext4_rec_len_from_disk(p->rec_len, blocksize)); 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci/* 5618c2ecf20Sopenharmony_ci * Future: use high four bits of block for coalesce-on-delete flags 5628c2ecf20Sopenharmony_ci * Mask them off for now. 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic inline ext4_lblk_t dx_get_block(struct dx_entry *entry) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci return le32_to_cpu(entry->block) & 0x0fffffff; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic inline void dx_set_block(struct dx_entry *entry, ext4_lblk_t value) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci entry->block = cpu_to_le32(value); 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic inline unsigned dx_get_hash(struct dx_entry *entry) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci return le32_to_cpu(entry->hash); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic inline void dx_set_hash(struct dx_entry *entry, unsigned value) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci entry->hash = cpu_to_le32(value); 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic inline unsigned dx_get_count(struct dx_entry *entries) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci return le16_to_cpu(((struct dx_countlimit *) entries)->count); 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic inline unsigned dx_get_limit(struct dx_entry *entries) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci return le16_to_cpu(((struct dx_countlimit *) entries)->limit); 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic inline void dx_set_count(struct dx_entry *entries, unsigned value) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci ((struct dx_countlimit *) entries)->count = cpu_to_le16(value); 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic inline void dx_set_limit(struct dx_entry *entries, unsigned value) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci ((struct dx_countlimit *) entries)->limit = cpu_to_le16(value); 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_cistatic inline unsigned dx_root_limit(struct inode *dir, unsigned infosize) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(1) - 6088c2ecf20Sopenharmony_ci EXT4_DIR_REC_LEN(2) - infosize; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(dir->i_sb)) 6118c2ecf20Sopenharmony_ci entry_space -= sizeof(struct dx_tail); 6128c2ecf20Sopenharmony_ci return entry_space / sizeof(struct dx_entry); 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic inline unsigned dx_node_limit(struct inode *dir) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci unsigned entry_space = dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(dir->i_sb)) 6208c2ecf20Sopenharmony_ci entry_space -= sizeof(struct dx_tail); 6218c2ecf20Sopenharmony_ci return entry_space / sizeof(struct dx_entry); 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci/* 6258c2ecf20Sopenharmony_ci * Debug 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_ci#ifdef DX_DEBUG 6288c2ecf20Sopenharmony_cistatic void dx_show_index(char * label, struct dx_entry *entries) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci int i, n = dx_get_count (entries); 6318c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s index", label); 6328c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 6338c2ecf20Sopenharmony_ci printk(KERN_CONT " %x->%lu", 6348c2ecf20Sopenharmony_ci i ? dx_get_hash(entries + i) : 0, 6358c2ecf20Sopenharmony_ci (unsigned long)dx_get_block(entries + i)); 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci printk(KERN_CONT "\n"); 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistruct stats 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci unsigned names; 6438c2ecf20Sopenharmony_ci unsigned space; 6448c2ecf20Sopenharmony_ci unsigned bcount; 6458c2ecf20Sopenharmony_ci}; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic struct stats dx_show_leaf(struct inode *dir, 6488c2ecf20Sopenharmony_ci struct dx_hash_info *hinfo, 6498c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de, 6508c2ecf20Sopenharmony_ci int size, int show_names) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci unsigned names = 0, space = 0; 6538c2ecf20Sopenharmony_ci char *base = (char *) de; 6548c2ecf20Sopenharmony_ci struct dx_hash_info h = *hinfo; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci printk("names: "); 6578c2ecf20Sopenharmony_ci while ((char *) de < base + size) 6588c2ecf20Sopenharmony_ci { 6598c2ecf20Sopenharmony_ci if (de->inode) 6608c2ecf20Sopenharmony_ci { 6618c2ecf20Sopenharmony_ci if (show_names) 6628c2ecf20Sopenharmony_ci { 6638c2ecf20Sopenharmony_ci#ifdef CONFIG_FS_ENCRYPTION 6648c2ecf20Sopenharmony_ci int len; 6658c2ecf20Sopenharmony_ci char *name; 6668c2ecf20Sopenharmony_ci struct fscrypt_str fname_crypto_str = 6678c2ecf20Sopenharmony_ci FSTR_INIT(NULL, 0); 6688c2ecf20Sopenharmony_ci int res = 0; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci name = de->name; 6718c2ecf20Sopenharmony_ci len = de->name_len; 6728c2ecf20Sopenharmony_ci if (IS_ENCRYPTED(dir)) 6738c2ecf20Sopenharmony_ci res = fscrypt_get_encryption_info(dir); 6748c2ecf20Sopenharmony_ci if (res) { 6758c2ecf20Sopenharmony_ci printk(KERN_WARNING "Error setting up" 6768c2ecf20Sopenharmony_ci " fname crypto: %d\n", res); 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci if (!fscrypt_has_encryption_key(dir)) { 6798c2ecf20Sopenharmony_ci /* Directory is not encrypted */ 6808c2ecf20Sopenharmony_ci ext4fs_dirhash(dir, de->name, 6818c2ecf20Sopenharmony_ci de->name_len, &h); 6828c2ecf20Sopenharmony_ci printk("%*.s:(U)%x.%u ", len, 6838c2ecf20Sopenharmony_ci name, h.hash, 6848c2ecf20Sopenharmony_ci (unsigned) ((char *) de 6858c2ecf20Sopenharmony_ci - base)); 6868c2ecf20Sopenharmony_ci } else { 6878c2ecf20Sopenharmony_ci struct fscrypt_str de_name = 6888c2ecf20Sopenharmony_ci FSTR_INIT(name, len); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* Directory is encrypted */ 6918c2ecf20Sopenharmony_ci res = fscrypt_fname_alloc_buffer( 6928c2ecf20Sopenharmony_ci len, &fname_crypto_str); 6938c2ecf20Sopenharmony_ci if (res) 6948c2ecf20Sopenharmony_ci printk(KERN_WARNING "Error " 6958c2ecf20Sopenharmony_ci "allocating crypto " 6968c2ecf20Sopenharmony_ci "buffer--skipping " 6978c2ecf20Sopenharmony_ci "crypto\n"); 6988c2ecf20Sopenharmony_ci res = fscrypt_fname_disk_to_usr(dir, 6998c2ecf20Sopenharmony_ci 0, 0, &de_name, 7008c2ecf20Sopenharmony_ci &fname_crypto_str); 7018c2ecf20Sopenharmony_ci if (res) { 7028c2ecf20Sopenharmony_ci printk(KERN_WARNING "Error " 7038c2ecf20Sopenharmony_ci "converting filename " 7048c2ecf20Sopenharmony_ci "from disk to usr" 7058c2ecf20Sopenharmony_ci "\n"); 7068c2ecf20Sopenharmony_ci name = "??"; 7078c2ecf20Sopenharmony_ci len = 2; 7088c2ecf20Sopenharmony_ci } else { 7098c2ecf20Sopenharmony_ci name = fname_crypto_str.name; 7108c2ecf20Sopenharmony_ci len = fname_crypto_str.len; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci ext4fs_dirhash(dir, de->name, 7138c2ecf20Sopenharmony_ci de->name_len, &h); 7148c2ecf20Sopenharmony_ci printk("%*.s:(E)%x.%u ", len, name, 7158c2ecf20Sopenharmony_ci h.hash, (unsigned) ((char *) de 7168c2ecf20Sopenharmony_ci - base)); 7178c2ecf20Sopenharmony_ci fscrypt_fname_free_buffer( 7188c2ecf20Sopenharmony_ci &fname_crypto_str); 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci#else 7218c2ecf20Sopenharmony_ci int len = de->name_len; 7228c2ecf20Sopenharmony_ci char *name = de->name; 7238c2ecf20Sopenharmony_ci ext4fs_dirhash(dir, de->name, de->name_len, &h); 7248c2ecf20Sopenharmony_ci printk("%*.s:%x.%u ", len, name, h.hash, 7258c2ecf20Sopenharmony_ci (unsigned) ((char *) de - base)); 7268c2ecf20Sopenharmony_ci#endif 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci space += EXT4_DIR_REC_LEN(de->name_len); 7298c2ecf20Sopenharmony_ci names++; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci de = ext4_next_entry(de, size); 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci printk(KERN_CONT "(%i)\n", names); 7348c2ecf20Sopenharmony_ci return (struct stats) { names, space, 1 }; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistruct stats dx_show_entries(struct dx_hash_info *hinfo, struct inode *dir, 7388c2ecf20Sopenharmony_ci struct dx_entry *entries, int levels) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci unsigned blocksize = dir->i_sb->s_blocksize; 7418c2ecf20Sopenharmony_ci unsigned count = dx_get_count(entries), names = 0, space = 0, i; 7428c2ecf20Sopenharmony_ci unsigned bcount = 0; 7438c2ecf20Sopenharmony_ci struct buffer_head *bh; 7448c2ecf20Sopenharmony_ci printk("%i indexed blocks...\n", count); 7458c2ecf20Sopenharmony_ci for (i = 0; i < count; i++, entries++) 7468c2ecf20Sopenharmony_ci { 7478c2ecf20Sopenharmony_ci ext4_lblk_t block = dx_get_block(entries); 7488c2ecf20Sopenharmony_ci ext4_lblk_t hash = i ? dx_get_hash(entries): 0; 7498c2ecf20Sopenharmony_ci u32 range = i < count - 1? (dx_get_hash(entries + 1) - hash): ~hash; 7508c2ecf20Sopenharmony_ci struct stats stats; 7518c2ecf20Sopenharmony_ci printk("%s%3u:%03u hash %8x/%8x ",levels?"":" ", i, block, hash, range); 7528c2ecf20Sopenharmony_ci bh = ext4_bread(NULL,dir, block, 0); 7538c2ecf20Sopenharmony_ci if (!bh || IS_ERR(bh)) 7548c2ecf20Sopenharmony_ci continue; 7558c2ecf20Sopenharmony_ci stats = levels? 7568c2ecf20Sopenharmony_ci dx_show_entries(hinfo, dir, ((struct dx_node *) bh->b_data)->entries, levels - 1): 7578c2ecf20Sopenharmony_ci dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *) 7588c2ecf20Sopenharmony_ci bh->b_data, blocksize, 0); 7598c2ecf20Sopenharmony_ci names += stats.names; 7608c2ecf20Sopenharmony_ci space += stats.space; 7618c2ecf20Sopenharmony_ci bcount += stats.bcount; 7628c2ecf20Sopenharmony_ci brelse(bh); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci if (bcount) 7658c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%snames %u, fullness %u (%u%%)\n", 7668c2ecf20Sopenharmony_ci levels ? "" : " ", names, space/bcount, 7678c2ecf20Sopenharmony_ci (space/bcount)*100/blocksize); 7688c2ecf20Sopenharmony_ci return (struct stats) { names, space, bcount}; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci#endif /* DX_DEBUG */ 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/* 7738c2ecf20Sopenharmony_ci * Probe for a directory leaf block to search. 7748c2ecf20Sopenharmony_ci * 7758c2ecf20Sopenharmony_ci * dx_probe can return ERR_BAD_DX_DIR, which means there was a format 7768c2ecf20Sopenharmony_ci * error in the directory index, and the caller should fall back to 7778c2ecf20Sopenharmony_ci * searching the directory normally. The callers of dx_probe **MUST** 7788c2ecf20Sopenharmony_ci * check for this error code, and make sure it never gets reflected 7798c2ecf20Sopenharmony_ci * back to userspace. 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_cistatic struct dx_frame * 7828c2ecf20Sopenharmony_cidx_probe(struct ext4_filename *fname, struct inode *dir, 7838c2ecf20Sopenharmony_ci struct dx_hash_info *hinfo, struct dx_frame *frame_in) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci unsigned count, indirect, level, i; 7868c2ecf20Sopenharmony_ci struct dx_entry *at, *entries, *p, *q, *m; 7878c2ecf20Sopenharmony_ci struct dx_root *root; 7888c2ecf20Sopenharmony_ci struct dx_frame *frame = frame_in; 7898c2ecf20Sopenharmony_ci struct dx_frame *ret_err = ERR_PTR(ERR_BAD_DX_DIR); 7908c2ecf20Sopenharmony_ci u32 hash; 7918c2ecf20Sopenharmony_ci ext4_lblk_t block; 7928c2ecf20Sopenharmony_ci ext4_lblk_t blocks[EXT4_HTREE_LEVEL]; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci memset(frame_in, 0, EXT4_HTREE_LEVEL * sizeof(frame_in[0])); 7958c2ecf20Sopenharmony_ci frame->bh = ext4_read_dirblock(dir, 0, INDEX); 7968c2ecf20Sopenharmony_ci if (IS_ERR(frame->bh)) 7978c2ecf20Sopenharmony_ci return (struct dx_frame *) frame->bh; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci root = (struct dx_root *) frame->bh->b_data; 8008c2ecf20Sopenharmony_ci if (root->info.hash_version != DX_HASH_TEA && 8018c2ecf20Sopenharmony_ci root->info.hash_version != DX_HASH_HALF_MD4 && 8028c2ecf20Sopenharmony_ci root->info.hash_version != DX_HASH_LEGACY) { 8038c2ecf20Sopenharmony_ci ext4_warning_inode(dir, "Unrecognised inode hash code %u", 8048c2ecf20Sopenharmony_ci root->info.hash_version); 8058c2ecf20Sopenharmony_ci goto fail; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci if (fname) 8088c2ecf20Sopenharmony_ci hinfo = &fname->hinfo; 8098c2ecf20Sopenharmony_ci hinfo->hash_version = root->info.hash_version; 8108c2ecf20Sopenharmony_ci if (hinfo->hash_version <= DX_HASH_TEA) 8118c2ecf20Sopenharmony_ci hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; 8128c2ecf20Sopenharmony_ci hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed; 8138c2ecf20Sopenharmony_ci if (fname && fname_name(fname)) 8148c2ecf20Sopenharmony_ci ext4fs_dirhash(dir, fname_name(fname), fname_len(fname), hinfo); 8158c2ecf20Sopenharmony_ci hash = hinfo->hash; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (root->info.unused_flags & 1) { 8188c2ecf20Sopenharmony_ci ext4_warning_inode(dir, "Unimplemented hash flags: %#06x", 8198c2ecf20Sopenharmony_ci root->info.unused_flags); 8208c2ecf20Sopenharmony_ci goto fail; 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci indirect = root->info.indirect_levels; 8248c2ecf20Sopenharmony_ci if (indirect >= ext4_dir_htree_level(dir->i_sb)) { 8258c2ecf20Sopenharmony_ci ext4_warning(dir->i_sb, 8268c2ecf20Sopenharmony_ci "Directory (ino: %lu) htree depth %#06x exceed" 8278c2ecf20Sopenharmony_ci "supported value", dir->i_ino, 8288c2ecf20Sopenharmony_ci ext4_dir_htree_level(dir->i_sb)); 8298c2ecf20Sopenharmony_ci if (ext4_dir_htree_level(dir->i_sb) < EXT4_HTREE_LEVEL) { 8308c2ecf20Sopenharmony_ci ext4_warning(dir->i_sb, "Enable large directory " 8318c2ecf20Sopenharmony_ci "feature to access it"); 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci goto fail; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci entries = (struct dx_entry *)(((char *)&root->info) + 8378c2ecf20Sopenharmony_ci root->info.info_length); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (dx_get_limit(entries) != dx_root_limit(dir, 8408c2ecf20Sopenharmony_ci root->info.info_length)) { 8418c2ecf20Sopenharmony_ci ext4_warning_inode(dir, "dx entry: limit %u != root limit %u", 8428c2ecf20Sopenharmony_ci dx_get_limit(entries), 8438c2ecf20Sopenharmony_ci dx_root_limit(dir, root->info.info_length)); 8448c2ecf20Sopenharmony_ci goto fail; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci dxtrace(printk("Look up %x", hash)); 8488c2ecf20Sopenharmony_ci level = 0; 8498c2ecf20Sopenharmony_ci blocks[0] = 0; 8508c2ecf20Sopenharmony_ci while (1) { 8518c2ecf20Sopenharmony_ci count = dx_get_count(entries); 8528c2ecf20Sopenharmony_ci if (!count || count > dx_get_limit(entries)) { 8538c2ecf20Sopenharmony_ci ext4_warning_inode(dir, 8548c2ecf20Sopenharmony_ci "dx entry: count %u beyond limit %u", 8558c2ecf20Sopenharmony_ci count, dx_get_limit(entries)); 8568c2ecf20Sopenharmony_ci goto fail; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci p = entries + 1; 8608c2ecf20Sopenharmony_ci q = entries + count - 1; 8618c2ecf20Sopenharmony_ci while (p <= q) { 8628c2ecf20Sopenharmony_ci m = p + (q - p) / 2; 8638c2ecf20Sopenharmony_ci dxtrace(printk(KERN_CONT ".")); 8648c2ecf20Sopenharmony_ci if (dx_get_hash(m) > hash) 8658c2ecf20Sopenharmony_ci q = m - 1; 8668c2ecf20Sopenharmony_ci else 8678c2ecf20Sopenharmony_ci p = m + 1; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci if (0) { // linear search cross check 8718c2ecf20Sopenharmony_ci unsigned n = count - 1; 8728c2ecf20Sopenharmony_ci at = entries; 8738c2ecf20Sopenharmony_ci while (n--) 8748c2ecf20Sopenharmony_ci { 8758c2ecf20Sopenharmony_ci dxtrace(printk(KERN_CONT ",")); 8768c2ecf20Sopenharmony_ci if (dx_get_hash(++at) > hash) 8778c2ecf20Sopenharmony_ci { 8788c2ecf20Sopenharmony_ci at--; 8798c2ecf20Sopenharmony_ci break; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci assert (at == p - 1); 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci at = p - 1; 8868c2ecf20Sopenharmony_ci dxtrace(printk(KERN_CONT " %x->%u\n", 8878c2ecf20Sopenharmony_ci at == entries ? 0 : dx_get_hash(at), 8888c2ecf20Sopenharmony_ci dx_get_block(at))); 8898c2ecf20Sopenharmony_ci frame->entries = entries; 8908c2ecf20Sopenharmony_ci frame->at = at; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci block = dx_get_block(at); 8938c2ecf20Sopenharmony_ci for (i = 0; i <= level; i++) { 8948c2ecf20Sopenharmony_ci if (blocks[i] == block) { 8958c2ecf20Sopenharmony_ci ext4_warning_inode(dir, 8968c2ecf20Sopenharmony_ci "dx entry: tree cycle block %u points back to block %u", 8978c2ecf20Sopenharmony_ci blocks[level], block); 8988c2ecf20Sopenharmony_ci goto fail; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci if (++level > indirect) 9028c2ecf20Sopenharmony_ci return frame; 9038c2ecf20Sopenharmony_ci blocks[level] = block; 9048c2ecf20Sopenharmony_ci frame++; 9058c2ecf20Sopenharmony_ci frame->bh = ext4_read_dirblock(dir, block, INDEX); 9068c2ecf20Sopenharmony_ci if (IS_ERR(frame->bh)) { 9078c2ecf20Sopenharmony_ci ret_err = (struct dx_frame *) frame->bh; 9088c2ecf20Sopenharmony_ci frame->bh = NULL; 9098c2ecf20Sopenharmony_ci goto fail; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci entries = ((struct dx_node *) frame->bh->b_data)->entries; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (dx_get_limit(entries) != dx_node_limit(dir)) { 9158c2ecf20Sopenharmony_ci ext4_warning_inode(dir, 9168c2ecf20Sopenharmony_ci "dx entry: limit %u != node limit %u", 9178c2ecf20Sopenharmony_ci dx_get_limit(entries), dx_node_limit(dir)); 9188c2ecf20Sopenharmony_ci goto fail; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_cifail: 9228c2ecf20Sopenharmony_ci while (frame >= frame_in) { 9238c2ecf20Sopenharmony_ci brelse(frame->bh); 9248c2ecf20Sopenharmony_ci frame--; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci if (ret_err == ERR_PTR(ERR_BAD_DX_DIR)) 9288c2ecf20Sopenharmony_ci ext4_warning_inode(dir, 9298c2ecf20Sopenharmony_ci "Corrupt directory, running e2fsck is recommended"); 9308c2ecf20Sopenharmony_ci return ret_err; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cistatic void dx_release(struct dx_frame *frames) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct dx_root_info *info; 9368c2ecf20Sopenharmony_ci int i; 9378c2ecf20Sopenharmony_ci unsigned int indirect_levels; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (frames[0].bh == NULL) 9408c2ecf20Sopenharmony_ci return; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci info = &((struct dx_root *)frames[0].bh->b_data)->info; 9438c2ecf20Sopenharmony_ci /* save local copy, "info" may be freed after brelse() */ 9448c2ecf20Sopenharmony_ci indirect_levels = info->indirect_levels; 9458c2ecf20Sopenharmony_ci for (i = 0; i <= indirect_levels; i++) { 9468c2ecf20Sopenharmony_ci if (frames[i].bh == NULL) 9478c2ecf20Sopenharmony_ci break; 9488c2ecf20Sopenharmony_ci brelse(frames[i].bh); 9498c2ecf20Sopenharmony_ci frames[i].bh = NULL; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci} 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci/* 9548c2ecf20Sopenharmony_ci * This function increments the frame pointer to search the next leaf 9558c2ecf20Sopenharmony_ci * block, and reads in the necessary intervening nodes if the search 9568c2ecf20Sopenharmony_ci * should be necessary. Whether or not the search is necessary is 9578c2ecf20Sopenharmony_ci * controlled by the hash parameter. If the hash value is even, then 9588c2ecf20Sopenharmony_ci * the search is only continued if the next block starts with that 9598c2ecf20Sopenharmony_ci * hash value. This is used if we are searching for a specific file. 9608c2ecf20Sopenharmony_ci * 9618c2ecf20Sopenharmony_ci * If the hash value is HASH_NB_ALWAYS, then always go to the next block. 9628c2ecf20Sopenharmony_ci * 9638c2ecf20Sopenharmony_ci * This function returns 1 if the caller should continue to search, 9648c2ecf20Sopenharmony_ci * or 0 if it should not. If there is an error reading one of the 9658c2ecf20Sopenharmony_ci * index blocks, it will a negative error code. 9668c2ecf20Sopenharmony_ci * 9678c2ecf20Sopenharmony_ci * If start_hash is non-null, it will be filled in with the starting 9688c2ecf20Sopenharmony_ci * hash of the next page. 9698c2ecf20Sopenharmony_ci */ 9708c2ecf20Sopenharmony_cistatic int ext4_htree_next_block(struct inode *dir, __u32 hash, 9718c2ecf20Sopenharmony_ci struct dx_frame *frame, 9728c2ecf20Sopenharmony_ci struct dx_frame *frames, 9738c2ecf20Sopenharmony_ci __u32 *start_hash) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci struct dx_frame *p; 9768c2ecf20Sopenharmony_ci struct buffer_head *bh; 9778c2ecf20Sopenharmony_ci int num_frames = 0; 9788c2ecf20Sopenharmony_ci __u32 bhash; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci p = frame; 9818c2ecf20Sopenharmony_ci /* 9828c2ecf20Sopenharmony_ci * Find the next leaf page by incrementing the frame pointer. 9838c2ecf20Sopenharmony_ci * If we run out of entries in the interior node, loop around and 9848c2ecf20Sopenharmony_ci * increment pointer in the parent node. When we break out of 9858c2ecf20Sopenharmony_ci * this loop, num_frames indicates the number of interior 9868c2ecf20Sopenharmony_ci * nodes need to be read. 9878c2ecf20Sopenharmony_ci */ 9888c2ecf20Sopenharmony_ci while (1) { 9898c2ecf20Sopenharmony_ci if (++(p->at) < p->entries + dx_get_count(p->entries)) 9908c2ecf20Sopenharmony_ci break; 9918c2ecf20Sopenharmony_ci if (p == frames) 9928c2ecf20Sopenharmony_ci return 0; 9938c2ecf20Sopenharmony_ci num_frames++; 9948c2ecf20Sopenharmony_ci p--; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci /* 9988c2ecf20Sopenharmony_ci * If the hash is 1, then continue only if the next page has a 9998c2ecf20Sopenharmony_ci * continuation hash of any value. This is used for readdir 10008c2ecf20Sopenharmony_ci * handling. Otherwise, check to see if the hash matches the 10018c2ecf20Sopenharmony_ci * desired continuation hash. If it doesn't, return since 10028c2ecf20Sopenharmony_ci * there's no point to read in the successive index pages. 10038c2ecf20Sopenharmony_ci */ 10048c2ecf20Sopenharmony_ci bhash = dx_get_hash(p->at); 10058c2ecf20Sopenharmony_ci if (start_hash) 10068c2ecf20Sopenharmony_ci *start_hash = bhash; 10078c2ecf20Sopenharmony_ci if ((hash & 1) == 0) { 10088c2ecf20Sopenharmony_ci if ((bhash & ~1) != hash) 10098c2ecf20Sopenharmony_ci return 0; 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci /* 10128c2ecf20Sopenharmony_ci * If the hash is HASH_NB_ALWAYS, we always go to the next 10138c2ecf20Sopenharmony_ci * block so no check is necessary 10148c2ecf20Sopenharmony_ci */ 10158c2ecf20Sopenharmony_ci while (num_frames--) { 10168c2ecf20Sopenharmony_ci bh = ext4_read_dirblock(dir, dx_get_block(p->at), INDEX); 10178c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 10188c2ecf20Sopenharmony_ci return PTR_ERR(bh); 10198c2ecf20Sopenharmony_ci p++; 10208c2ecf20Sopenharmony_ci brelse(p->bh); 10218c2ecf20Sopenharmony_ci p->bh = bh; 10228c2ecf20Sopenharmony_ci p->at = p->entries = ((struct dx_node *) bh->b_data)->entries; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci return 1; 10258c2ecf20Sopenharmony_ci} 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci/* 10298c2ecf20Sopenharmony_ci * This function fills a red-black tree with information from a 10308c2ecf20Sopenharmony_ci * directory block. It returns the number directory entries loaded 10318c2ecf20Sopenharmony_ci * into the tree. If there is an error it is returned in err. 10328c2ecf20Sopenharmony_ci */ 10338c2ecf20Sopenharmony_cistatic int htree_dirblock_to_tree(struct file *dir_file, 10348c2ecf20Sopenharmony_ci struct inode *dir, ext4_lblk_t block, 10358c2ecf20Sopenharmony_ci struct dx_hash_info *hinfo, 10368c2ecf20Sopenharmony_ci __u32 start_hash, __u32 start_minor_hash) 10378c2ecf20Sopenharmony_ci{ 10388c2ecf20Sopenharmony_ci struct buffer_head *bh; 10398c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de, *top; 10408c2ecf20Sopenharmony_ci int err = 0, count = 0; 10418c2ecf20Sopenharmony_ci struct fscrypt_str fname_crypto_str = FSTR_INIT(NULL, 0), tmp_str; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n", 10448c2ecf20Sopenharmony_ci (unsigned long)block)); 10458c2ecf20Sopenharmony_ci bh = ext4_read_dirblock(dir, block, DIRENT_HTREE); 10468c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 10478c2ecf20Sopenharmony_ci return PTR_ERR(bh); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *) bh->b_data; 10508c2ecf20Sopenharmony_ci top = (struct ext4_dir_entry_2 *) ((char *) de + 10518c2ecf20Sopenharmony_ci dir->i_sb->s_blocksize - 10528c2ecf20Sopenharmony_ci EXT4_DIR_REC_LEN(0)); 10538c2ecf20Sopenharmony_ci /* Check if the directory is encrypted */ 10548c2ecf20Sopenharmony_ci if (IS_ENCRYPTED(dir)) { 10558c2ecf20Sopenharmony_ci err = fscrypt_get_encryption_info(dir); 10568c2ecf20Sopenharmony_ci if (err < 0) { 10578c2ecf20Sopenharmony_ci brelse(bh); 10588c2ecf20Sopenharmony_ci return err; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci err = fscrypt_fname_alloc_buffer(EXT4_NAME_LEN, 10618c2ecf20Sopenharmony_ci &fname_crypto_str); 10628c2ecf20Sopenharmony_ci if (err < 0) { 10638c2ecf20Sopenharmony_ci brelse(bh); 10648c2ecf20Sopenharmony_ci return err; 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) { 10698c2ecf20Sopenharmony_ci if (ext4_check_dir_entry(dir, NULL, de, bh, 10708c2ecf20Sopenharmony_ci bh->b_data, bh->b_size, 10718c2ecf20Sopenharmony_ci (block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb)) 10728c2ecf20Sopenharmony_ci + ((char *)de - bh->b_data))) { 10738c2ecf20Sopenharmony_ci /* silently ignore the rest of the block */ 10748c2ecf20Sopenharmony_ci break; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci ext4fs_dirhash(dir, de->name, de->name_len, hinfo); 10778c2ecf20Sopenharmony_ci if ((hinfo->hash < start_hash) || 10788c2ecf20Sopenharmony_ci ((hinfo->hash == start_hash) && 10798c2ecf20Sopenharmony_ci (hinfo->minor_hash < start_minor_hash))) 10808c2ecf20Sopenharmony_ci continue; 10818c2ecf20Sopenharmony_ci if (de->inode == 0) 10828c2ecf20Sopenharmony_ci continue; 10838c2ecf20Sopenharmony_ci if (!IS_ENCRYPTED(dir)) { 10848c2ecf20Sopenharmony_ci tmp_str.name = de->name; 10858c2ecf20Sopenharmony_ci tmp_str.len = de->name_len; 10868c2ecf20Sopenharmony_ci err = ext4_htree_store_dirent(dir_file, 10878c2ecf20Sopenharmony_ci hinfo->hash, hinfo->minor_hash, de, 10888c2ecf20Sopenharmony_ci &tmp_str); 10898c2ecf20Sopenharmony_ci } else { 10908c2ecf20Sopenharmony_ci int save_len = fname_crypto_str.len; 10918c2ecf20Sopenharmony_ci struct fscrypt_str de_name = FSTR_INIT(de->name, 10928c2ecf20Sopenharmony_ci de->name_len); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci /* Directory is encrypted */ 10958c2ecf20Sopenharmony_ci err = fscrypt_fname_disk_to_usr(dir, hinfo->hash, 10968c2ecf20Sopenharmony_ci hinfo->minor_hash, &de_name, 10978c2ecf20Sopenharmony_ci &fname_crypto_str); 10988c2ecf20Sopenharmony_ci if (err) { 10998c2ecf20Sopenharmony_ci count = err; 11008c2ecf20Sopenharmony_ci goto errout; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci err = ext4_htree_store_dirent(dir_file, 11038c2ecf20Sopenharmony_ci hinfo->hash, hinfo->minor_hash, de, 11048c2ecf20Sopenharmony_ci &fname_crypto_str); 11058c2ecf20Sopenharmony_ci fname_crypto_str.len = save_len; 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci if (err != 0) { 11088c2ecf20Sopenharmony_ci count = err; 11098c2ecf20Sopenharmony_ci goto errout; 11108c2ecf20Sopenharmony_ci } 11118c2ecf20Sopenharmony_ci count++; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_cierrout: 11148c2ecf20Sopenharmony_ci brelse(bh); 11158c2ecf20Sopenharmony_ci fscrypt_fname_free_buffer(&fname_crypto_str); 11168c2ecf20Sopenharmony_ci return count; 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci/* 11218c2ecf20Sopenharmony_ci * This function fills a red-black tree with information from a 11228c2ecf20Sopenharmony_ci * directory. We start scanning the directory in hash order, starting 11238c2ecf20Sopenharmony_ci * at start_hash and start_minor_hash. 11248c2ecf20Sopenharmony_ci * 11258c2ecf20Sopenharmony_ci * This function returns the number of entries inserted into the tree, 11268c2ecf20Sopenharmony_ci * or a negative error code. 11278c2ecf20Sopenharmony_ci */ 11288c2ecf20Sopenharmony_ciint ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash, 11298c2ecf20Sopenharmony_ci __u32 start_minor_hash, __u32 *next_hash) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci struct dx_hash_info hinfo; 11328c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 11338c2ecf20Sopenharmony_ci struct dx_frame frames[EXT4_HTREE_LEVEL], *frame; 11348c2ecf20Sopenharmony_ci struct inode *dir; 11358c2ecf20Sopenharmony_ci ext4_lblk_t block; 11368c2ecf20Sopenharmony_ci int count = 0; 11378c2ecf20Sopenharmony_ci int ret, err; 11388c2ecf20Sopenharmony_ci __u32 hashval; 11398c2ecf20Sopenharmony_ci struct fscrypt_str tmp_str; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci dxtrace(printk(KERN_DEBUG "In htree_fill_tree, start hash: %x:%x\n", 11428c2ecf20Sopenharmony_ci start_hash, start_minor_hash)); 11438c2ecf20Sopenharmony_ci dir = file_inode(dir_file); 11448c2ecf20Sopenharmony_ci if (!(ext4_test_inode_flag(dir, EXT4_INODE_INDEX))) { 11458c2ecf20Sopenharmony_ci hinfo.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version; 11468c2ecf20Sopenharmony_ci if (hinfo.hash_version <= DX_HASH_TEA) 11478c2ecf20Sopenharmony_ci hinfo.hash_version += 11488c2ecf20Sopenharmony_ci EXT4_SB(dir->i_sb)->s_hash_unsigned; 11498c2ecf20Sopenharmony_ci hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; 11508c2ecf20Sopenharmony_ci if (ext4_has_inline_data(dir)) { 11518c2ecf20Sopenharmony_ci int has_inline_data = 1; 11528c2ecf20Sopenharmony_ci count = ext4_inlinedir_to_tree(dir_file, dir, 0, 11538c2ecf20Sopenharmony_ci &hinfo, start_hash, 11548c2ecf20Sopenharmony_ci start_minor_hash, 11558c2ecf20Sopenharmony_ci &has_inline_data); 11568c2ecf20Sopenharmony_ci if (has_inline_data) { 11578c2ecf20Sopenharmony_ci *next_hash = ~0; 11588c2ecf20Sopenharmony_ci return count; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci count = htree_dirblock_to_tree(dir_file, dir, 0, &hinfo, 11628c2ecf20Sopenharmony_ci start_hash, start_minor_hash); 11638c2ecf20Sopenharmony_ci *next_hash = ~0; 11648c2ecf20Sopenharmony_ci return count; 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci hinfo.hash = start_hash; 11678c2ecf20Sopenharmony_ci hinfo.minor_hash = 0; 11688c2ecf20Sopenharmony_ci frame = dx_probe(NULL, dir, &hinfo, frames); 11698c2ecf20Sopenharmony_ci if (IS_ERR(frame)) 11708c2ecf20Sopenharmony_ci return PTR_ERR(frame); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci /* Add '.' and '..' from the htree header */ 11738c2ecf20Sopenharmony_ci if (!start_hash && !start_minor_hash) { 11748c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *) frames[0].bh->b_data; 11758c2ecf20Sopenharmony_ci tmp_str.name = de->name; 11768c2ecf20Sopenharmony_ci tmp_str.len = de->name_len; 11778c2ecf20Sopenharmony_ci err = ext4_htree_store_dirent(dir_file, 0, 0, 11788c2ecf20Sopenharmony_ci de, &tmp_str); 11798c2ecf20Sopenharmony_ci if (err != 0) 11808c2ecf20Sopenharmony_ci goto errout; 11818c2ecf20Sopenharmony_ci count++; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci if (start_hash < 2 || (start_hash ==2 && start_minor_hash==0)) { 11848c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *) frames[0].bh->b_data; 11858c2ecf20Sopenharmony_ci de = ext4_next_entry(de, dir->i_sb->s_blocksize); 11868c2ecf20Sopenharmony_ci tmp_str.name = de->name; 11878c2ecf20Sopenharmony_ci tmp_str.len = de->name_len; 11888c2ecf20Sopenharmony_ci err = ext4_htree_store_dirent(dir_file, 2, 0, 11898c2ecf20Sopenharmony_ci de, &tmp_str); 11908c2ecf20Sopenharmony_ci if (err != 0) 11918c2ecf20Sopenharmony_ci goto errout; 11928c2ecf20Sopenharmony_ci count++; 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci while (1) { 11968c2ecf20Sopenharmony_ci if (fatal_signal_pending(current)) { 11978c2ecf20Sopenharmony_ci err = -ERESTARTSYS; 11988c2ecf20Sopenharmony_ci goto errout; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci cond_resched(); 12018c2ecf20Sopenharmony_ci block = dx_get_block(frame->at); 12028c2ecf20Sopenharmony_ci ret = htree_dirblock_to_tree(dir_file, dir, block, &hinfo, 12038c2ecf20Sopenharmony_ci start_hash, start_minor_hash); 12048c2ecf20Sopenharmony_ci if (ret < 0) { 12058c2ecf20Sopenharmony_ci err = ret; 12068c2ecf20Sopenharmony_ci goto errout; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci count += ret; 12098c2ecf20Sopenharmony_ci hashval = ~0; 12108c2ecf20Sopenharmony_ci ret = ext4_htree_next_block(dir, HASH_NB_ALWAYS, 12118c2ecf20Sopenharmony_ci frame, frames, &hashval); 12128c2ecf20Sopenharmony_ci *next_hash = hashval; 12138c2ecf20Sopenharmony_ci if (ret < 0) { 12148c2ecf20Sopenharmony_ci err = ret; 12158c2ecf20Sopenharmony_ci goto errout; 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci /* 12188c2ecf20Sopenharmony_ci * Stop if: (a) there are no more entries, or 12198c2ecf20Sopenharmony_ci * (b) we have inserted at least one entry and the 12208c2ecf20Sopenharmony_ci * next hash value is not a continuation 12218c2ecf20Sopenharmony_ci */ 12228c2ecf20Sopenharmony_ci if ((ret == 0) || 12238c2ecf20Sopenharmony_ci (count && ((hashval & 1) == 0))) 12248c2ecf20Sopenharmony_ci break; 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci dx_release(frames); 12278c2ecf20Sopenharmony_ci dxtrace(printk(KERN_DEBUG "Fill tree: returned %d entries, " 12288c2ecf20Sopenharmony_ci "next hash: %x\n", count, *next_hash)); 12298c2ecf20Sopenharmony_ci return count; 12308c2ecf20Sopenharmony_cierrout: 12318c2ecf20Sopenharmony_ci dx_release(frames); 12328c2ecf20Sopenharmony_ci return (err); 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic inline int search_dirblock(struct buffer_head *bh, 12368c2ecf20Sopenharmony_ci struct inode *dir, 12378c2ecf20Sopenharmony_ci struct ext4_filename *fname, 12388c2ecf20Sopenharmony_ci unsigned int offset, 12398c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 **res_dir) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci return ext4_search_dir(bh, bh->b_data, dir->i_sb->s_blocksize, dir, 12428c2ecf20Sopenharmony_ci fname, offset, res_dir); 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci/* 12468c2ecf20Sopenharmony_ci * Directory block splitting, compacting 12478c2ecf20Sopenharmony_ci */ 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci/* 12508c2ecf20Sopenharmony_ci * Create map of hash values, offsets, and sizes, stored at end of block. 12518c2ecf20Sopenharmony_ci * Returns number of entries mapped. 12528c2ecf20Sopenharmony_ci */ 12538c2ecf20Sopenharmony_cistatic int dx_make_map(struct inode *dir, struct buffer_head *bh, 12548c2ecf20Sopenharmony_ci struct dx_hash_info *hinfo, 12558c2ecf20Sopenharmony_ci struct dx_map_entry *map_tail) 12568c2ecf20Sopenharmony_ci{ 12578c2ecf20Sopenharmony_ci int count = 0; 12588c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *)bh->b_data; 12598c2ecf20Sopenharmony_ci unsigned int buflen = bh->b_size; 12608c2ecf20Sopenharmony_ci char *base = bh->b_data; 12618c2ecf20Sopenharmony_ci struct dx_hash_info h = *hinfo; 12628c2ecf20Sopenharmony_ci int blocksize = EXT4_BLOCK_SIZE(dir->i_sb); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(dir->i_sb)) 12658c2ecf20Sopenharmony_ci buflen -= sizeof(struct ext4_dir_entry_tail); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci while ((char *) de < base + buflen) { 12688c2ecf20Sopenharmony_ci if (ext4_check_dir_entry(dir, NULL, de, bh, base, buflen, 12698c2ecf20Sopenharmony_ci ((char *)de) - base)) 12708c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 12718c2ecf20Sopenharmony_ci if (de->name_len && de->inode) { 12728c2ecf20Sopenharmony_ci ext4fs_dirhash(dir, de->name, de->name_len, &h); 12738c2ecf20Sopenharmony_ci map_tail--; 12748c2ecf20Sopenharmony_ci map_tail->hash = h.hash; 12758c2ecf20Sopenharmony_ci map_tail->offs = ((char *) de - base)>>2; 12768c2ecf20Sopenharmony_ci map_tail->size = ext4_rec_len_from_disk(de->rec_len, 12778c2ecf20Sopenharmony_ci blocksize); 12788c2ecf20Sopenharmony_ci count++; 12798c2ecf20Sopenharmony_ci cond_resched(); 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci de = ext4_next_entry(de, blocksize); 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci return count; 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci/* Sort map by hash value */ 12878c2ecf20Sopenharmony_cistatic void dx_sort_map (struct dx_map_entry *map, unsigned count) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci struct dx_map_entry *p, *q, *top = map + count - 1; 12908c2ecf20Sopenharmony_ci int more; 12918c2ecf20Sopenharmony_ci /* Combsort until bubble sort doesn't suck */ 12928c2ecf20Sopenharmony_ci while (count > 2) { 12938c2ecf20Sopenharmony_ci count = count*10/13; 12948c2ecf20Sopenharmony_ci if (count - 9 < 2) /* 9, 10 -> 11 */ 12958c2ecf20Sopenharmony_ci count = 11; 12968c2ecf20Sopenharmony_ci for (p = top, q = p - count; q >= map; p--, q--) 12978c2ecf20Sopenharmony_ci if (p->hash < q->hash) 12988c2ecf20Sopenharmony_ci swap(*p, *q); 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci /* Garden variety bubble sort */ 13018c2ecf20Sopenharmony_ci do { 13028c2ecf20Sopenharmony_ci more = 0; 13038c2ecf20Sopenharmony_ci q = top; 13048c2ecf20Sopenharmony_ci while (q-- > map) { 13058c2ecf20Sopenharmony_ci if (q[1].hash >= q[0].hash) 13068c2ecf20Sopenharmony_ci continue; 13078c2ecf20Sopenharmony_ci swap(*(q+1), *q); 13088c2ecf20Sopenharmony_ci more = 1; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci } while(more); 13118c2ecf20Sopenharmony_ci} 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_cistatic void dx_insert_block(struct dx_frame *frame, u32 hash, ext4_lblk_t block) 13148c2ecf20Sopenharmony_ci{ 13158c2ecf20Sopenharmony_ci struct dx_entry *entries = frame->entries; 13168c2ecf20Sopenharmony_ci struct dx_entry *old = frame->at, *new = old + 1; 13178c2ecf20Sopenharmony_ci int count = dx_get_count(entries); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci assert(count < dx_get_limit(entries)); 13208c2ecf20Sopenharmony_ci assert(old < entries + count); 13218c2ecf20Sopenharmony_ci memmove(new + 1, new, (char *)(entries + count) - (char *)(new)); 13228c2ecf20Sopenharmony_ci dx_set_hash(new, hash); 13238c2ecf20Sopenharmony_ci dx_set_block(new, block); 13248c2ecf20Sopenharmony_ci dx_set_count(entries, count + 1); 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci#ifdef CONFIG_UNICODE 13288c2ecf20Sopenharmony_ci/* 13298c2ecf20Sopenharmony_ci * Test whether a case-insensitive directory entry matches the filename 13308c2ecf20Sopenharmony_ci * being searched for. If quick is set, assume the name being looked up 13318c2ecf20Sopenharmony_ci * is already in the casefolded form. 13328c2ecf20Sopenharmony_ci * 13338c2ecf20Sopenharmony_ci * Returns: 0 if the directory entry matches, more than 0 if it 13348c2ecf20Sopenharmony_ci * doesn't match or less than zero on error. 13358c2ecf20Sopenharmony_ci */ 13368c2ecf20Sopenharmony_ciint ext4_ci_compare(const struct inode *parent, const struct qstr *name, 13378c2ecf20Sopenharmony_ci const struct qstr *entry, bool quick) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci const struct super_block *sb = parent->i_sb; 13408c2ecf20Sopenharmony_ci const struct unicode_map *um = sb->s_encoding; 13418c2ecf20Sopenharmony_ci int ret; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci if (quick) 13448c2ecf20Sopenharmony_ci ret = utf8_strncasecmp_folded(um, name, entry); 13458c2ecf20Sopenharmony_ci else 13468c2ecf20Sopenharmony_ci ret = utf8_strncasecmp(um, name, entry); 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci if (ret < 0) { 13498c2ecf20Sopenharmony_ci /* Handle invalid character sequence as either an error 13508c2ecf20Sopenharmony_ci * or as an opaque byte sequence. 13518c2ecf20Sopenharmony_ci */ 13528c2ecf20Sopenharmony_ci if (sb_has_strict_encoding(sb)) 13538c2ecf20Sopenharmony_ci return -EINVAL; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci if (name->len != entry->len) 13568c2ecf20Sopenharmony_ci return 1; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci return !!memcmp(name->name, entry->name, name->len); 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci return ret; 13628c2ecf20Sopenharmony_ci} 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_civoid ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname, 13658c2ecf20Sopenharmony_ci struct fscrypt_str *cf_name) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci int len; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci if (!IS_CASEFOLDED(dir) || !dir->i_sb->s_encoding) { 13708c2ecf20Sopenharmony_ci cf_name->name = NULL; 13718c2ecf20Sopenharmony_ci return; 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci cf_name->name = kmalloc(EXT4_NAME_LEN, GFP_NOFS); 13758c2ecf20Sopenharmony_ci if (!cf_name->name) 13768c2ecf20Sopenharmony_ci return; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci len = utf8_casefold(dir->i_sb->s_encoding, 13798c2ecf20Sopenharmony_ci iname, cf_name->name, 13808c2ecf20Sopenharmony_ci EXT4_NAME_LEN); 13818c2ecf20Sopenharmony_ci if (len <= 0) { 13828c2ecf20Sopenharmony_ci kfree(cf_name->name); 13838c2ecf20Sopenharmony_ci cf_name->name = NULL; 13848c2ecf20Sopenharmony_ci return; 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci cf_name->len = (unsigned) len; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci#endif 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci/* 13928c2ecf20Sopenharmony_ci * Test whether a directory entry matches the filename being searched for. 13938c2ecf20Sopenharmony_ci * 13948c2ecf20Sopenharmony_ci * Return: %true if the directory entry matches, otherwise %false. 13958c2ecf20Sopenharmony_ci */ 13968c2ecf20Sopenharmony_cistatic inline bool ext4_match(const struct inode *parent, 13978c2ecf20Sopenharmony_ci const struct ext4_filename *fname, 13988c2ecf20Sopenharmony_ci const struct ext4_dir_entry_2 *de) 13998c2ecf20Sopenharmony_ci{ 14008c2ecf20Sopenharmony_ci struct fscrypt_name f; 14018c2ecf20Sopenharmony_ci#ifdef CONFIG_UNICODE 14028c2ecf20Sopenharmony_ci const struct qstr entry = {.name = de->name, .len = de->name_len}; 14038c2ecf20Sopenharmony_ci#endif 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci if (!de->inode) 14068c2ecf20Sopenharmony_ci return false; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci f.usr_fname = fname->usr_fname; 14098c2ecf20Sopenharmony_ci f.disk_name = fname->disk_name; 14108c2ecf20Sopenharmony_ci#ifdef CONFIG_FS_ENCRYPTION 14118c2ecf20Sopenharmony_ci f.crypto_buf = fname->crypto_buf; 14128c2ecf20Sopenharmony_ci#endif 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci#ifdef CONFIG_UNICODE 14158c2ecf20Sopenharmony_ci if (parent->i_sb->s_encoding && IS_CASEFOLDED(parent)) { 14168c2ecf20Sopenharmony_ci if (fname->cf_name.name) { 14178c2ecf20Sopenharmony_ci struct qstr cf = {.name = fname->cf_name.name, 14188c2ecf20Sopenharmony_ci .len = fname->cf_name.len}; 14198c2ecf20Sopenharmony_ci return !ext4_ci_compare(parent, &cf, &entry, true); 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci return !ext4_ci_compare(parent, fname->usr_fname, &entry, 14228c2ecf20Sopenharmony_ci false); 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci#endif 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci return fscrypt_match_name(&f, de->name, de->name_len); 14278c2ecf20Sopenharmony_ci} 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci/* 14308c2ecf20Sopenharmony_ci * Returns 0 if not found, -1 on failure, and 1 on success 14318c2ecf20Sopenharmony_ci */ 14328c2ecf20Sopenharmony_ciint ext4_search_dir(struct buffer_head *bh, char *search_buf, int buf_size, 14338c2ecf20Sopenharmony_ci struct inode *dir, struct ext4_filename *fname, 14348c2ecf20Sopenharmony_ci unsigned int offset, struct ext4_dir_entry_2 **res_dir) 14358c2ecf20Sopenharmony_ci{ 14368c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 * de; 14378c2ecf20Sopenharmony_ci char * dlimit; 14388c2ecf20Sopenharmony_ci int de_len; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *)search_buf; 14418c2ecf20Sopenharmony_ci dlimit = search_buf + buf_size; 14428c2ecf20Sopenharmony_ci while ((char *) de < dlimit - EXT4_BASE_DIR_LEN) { 14438c2ecf20Sopenharmony_ci /* this code is executed quadratically often */ 14448c2ecf20Sopenharmony_ci /* do minimal checking `by hand' */ 14458c2ecf20Sopenharmony_ci if (de->name + de->name_len <= dlimit && 14468c2ecf20Sopenharmony_ci ext4_match(dir, fname, de)) { 14478c2ecf20Sopenharmony_ci /* found a match - just to be sure, do 14488c2ecf20Sopenharmony_ci * a full check */ 14498c2ecf20Sopenharmony_ci if (ext4_check_dir_entry(dir, NULL, de, bh, search_buf, 14508c2ecf20Sopenharmony_ci buf_size, offset)) 14518c2ecf20Sopenharmony_ci return -1; 14528c2ecf20Sopenharmony_ci *res_dir = de; 14538c2ecf20Sopenharmony_ci return 1; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci /* prevent looping on a bad block */ 14568c2ecf20Sopenharmony_ci de_len = ext4_rec_len_from_disk(de->rec_len, 14578c2ecf20Sopenharmony_ci dir->i_sb->s_blocksize); 14588c2ecf20Sopenharmony_ci if (de_len <= 0) 14598c2ecf20Sopenharmony_ci return -1; 14608c2ecf20Sopenharmony_ci offset += de_len; 14618c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *) ((char *) de + de_len); 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci return 0; 14648c2ecf20Sopenharmony_ci} 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_cistatic int is_dx_internal_node(struct inode *dir, ext4_lblk_t block, 14678c2ecf20Sopenharmony_ci struct ext4_dir_entry *de) 14688c2ecf20Sopenharmony_ci{ 14698c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (!is_dx(dir)) 14728c2ecf20Sopenharmony_ci return 0; 14738c2ecf20Sopenharmony_ci if (block == 0) 14748c2ecf20Sopenharmony_ci return 1; 14758c2ecf20Sopenharmony_ci if (de->inode == 0 && 14768c2ecf20Sopenharmony_ci ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize) == 14778c2ecf20Sopenharmony_ci sb->s_blocksize) 14788c2ecf20Sopenharmony_ci return 1; 14798c2ecf20Sopenharmony_ci return 0; 14808c2ecf20Sopenharmony_ci} 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci/* 14838c2ecf20Sopenharmony_ci * __ext4_find_entry() 14848c2ecf20Sopenharmony_ci * 14858c2ecf20Sopenharmony_ci * finds an entry in the specified directory with the wanted name. It 14868c2ecf20Sopenharmony_ci * returns the cache buffer in which the entry was found, and the entry 14878c2ecf20Sopenharmony_ci * itself (as a parameter - res_dir). It does NOT read the inode of the 14888c2ecf20Sopenharmony_ci * entry - you'll have to do that yourself if you want to. 14898c2ecf20Sopenharmony_ci * 14908c2ecf20Sopenharmony_ci * The returned buffer_head has ->b_count elevated. The caller is expected 14918c2ecf20Sopenharmony_ci * to brelse() it when appropriate. 14928c2ecf20Sopenharmony_ci */ 14938c2ecf20Sopenharmony_cistatic struct buffer_head *__ext4_find_entry(struct inode *dir, 14948c2ecf20Sopenharmony_ci struct ext4_filename *fname, 14958c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 **res_dir, 14968c2ecf20Sopenharmony_ci int *inlined) 14978c2ecf20Sopenharmony_ci{ 14988c2ecf20Sopenharmony_ci struct super_block *sb; 14998c2ecf20Sopenharmony_ci struct buffer_head *bh_use[NAMEI_RA_SIZE]; 15008c2ecf20Sopenharmony_ci struct buffer_head *bh, *ret = NULL; 15018c2ecf20Sopenharmony_ci ext4_lblk_t start, block; 15028c2ecf20Sopenharmony_ci const u8 *name = fname->usr_fname->name; 15038c2ecf20Sopenharmony_ci size_t ra_max = 0; /* Number of bh's in the readahead 15048c2ecf20Sopenharmony_ci buffer, bh_use[] */ 15058c2ecf20Sopenharmony_ci size_t ra_ptr = 0; /* Current index into readahead 15068c2ecf20Sopenharmony_ci buffer */ 15078c2ecf20Sopenharmony_ci ext4_lblk_t nblocks; 15088c2ecf20Sopenharmony_ci int i, namelen, retval; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci *res_dir = NULL; 15118c2ecf20Sopenharmony_ci sb = dir->i_sb; 15128c2ecf20Sopenharmony_ci namelen = fname->usr_fname->len; 15138c2ecf20Sopenharmony_ci if (namelen > EXT4_NAME_LEN) 15148c2ecf20Sopenharmony_ci return NULL; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (ext4_has_inline_data(dir)) { 15178c2ecf20Sopenharmony_ci int has_inline_data = 1; 15188c2ecf20Sopenharmony_ci ret = ext4_find_inline_entry(dir, fname, res_dir, 15198c2ecf20Sopenharmony_ci &has_inline_data); 15208c2ecf20Sopenharmony_ci if (inlined) 15218c2ecf20Sopenharmony_ci *inlined = has_inline_data; 15228c2ecf20Sopenharmony_ci if (has_inline_data) 15238c2ecf20Sopenharmony_ci goto cleanup_and_exit; 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci if ((namelen <= 2) && (name[0] == '.') && 15278c2ecf20Sopenharmony_ci (name[1] == '.' || name[1] == '\0')) { 15288c2ecf20Sopenharmony_ci /* 15298c2ecf20Sopenharmony_ci * "." or ".." will only be in the first block 15308c2ecf20Sopenharmony_ci * NFS may look up ".."; "." should be handled by the VFS 15318c2ecf20Sopenharmony_ci */ 15328c2ecf20Sopenharmony_ci block = start = 0; 15338c2ecf20Sopenharmony_ci nblocks = 1; 15348c2ecf20Sopenharmony_ci goto restart; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci if (is_dx(dir)) { 15378c2ecf20Sopenharmony_ci ret = ext4_dx_find_entry(dir, fname, res_dir); 15388c2ecf20Sopenharmony_ci /* 15398c2ecf20Sopenharmony_ci * On success, or if the error was file not found, 15408c2ecf20Sopenharmony_ci * return. Otherwise, fall back to doing a search the 15418c2ecf20Sopenharmony_ci * old fashioned way. 15428c2ecf20Sopenharmony_ci */ 15438c2ecf20Sopenharmony_ci if (!IS_ERR(ret) || PTR_ERR(ret) != ERR_BAD_DX_DIR) 15448c2ecf20Sopenharmony_ci goto cleanup_and_exit; 15458c2ecf20Sopenharmony_ci dxtrace(printk(KERN_DEBUG "ext4_find_entry: dx failed, " 15468c2ecf20Sopenharmony_ci "falling back\n")); 15478c2ecf20Sopenharmony_ci ret = NULL; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci nblocks = dir->i_size >> EXT4_BLOCK_SIZE_BITS(sb); 15508c2ecf20Sopenharmony_ci if (!nblocks) { 15518c2ecf20Sopenharmony_ci ret = NULL; 15528c2ecf20Sopenharmony_ci goto cleanup_and_exit; 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci start = EXT4_I(dir)->i_dir_start_lookup; 15558c2ecf20Sopenharmony_ci if (start >= nblocks) 15568c2ecf20Sopenharmony_ci start = 0; 15578c2ecf20Sopenharmony_ci block = start; 15588c2ecf20Sopenharmony_cirestart: 15598c2ecf20Sopenharmony_ci do { 15608c2ecf20Sopenharmony_ci /* 15618c2ecf20Sopenharmony_ci * We deal with the read-ahead logic here. 15628c2ecf20Sopenharmony_ci */ 15638c2ecf20Sopenharmony_ci cond_resched(); 15648c2ecf20Sopenharmony_ci if (ra_ptr >= ra_max) { 15658c2ecf20Sopenharmony_ci /* Refill the readahead buffer */ 15668c2ecf20Sopenharmony_ci ra_ptr = 0; 15678c2ecf20Sopenharmony_ci if (block < start) 15688c2ecf20Sopenharmony_ci ra_max = start - block; 15698c2ecf20Sopenharmony_ci else 15708c2ecf20Sopenharmony_ci ra_max = nblocks - block; 15718c2ecf20Sopenharmony_ci ra_max = min(ra_max, ARRAY_SIZE(bh_use)); 15728c2ecf20Sopenharmony_ci retval = ext4_bread_batch(dir, block, ra_max, 15738c2ecf20Sopenharmony_ci false /* wait */, bh_use); 15748c2ecf20Sopenharmony_ci if (retval) { 15758c2ecf20Sopenharmony_ci ret = ERR_PTR(retval); 15768c2ecf20Sopenharmony_ci ra_max = 0; 15778c2ecf20Sopenharmony_ci goto cleanup_and_exit; 15788c2ecf20Sopenharmony_ci } 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci if ((bh = bh_use[ra_ptr++]) == NULL) 15818c2ecf20Sopenharmony_ci goto next; 15828c2ecf20Sopenharmony_ci wait_on_buffer(bh); 15838c2ecf20Sopenharmony_ci if (!buffer_uptodate(bh)) { 15848c2ecf20Sopenharmony_ci EXT4_ERROR_INODE_ERR(dir, EIO, 15858c2ecf20Sopenharmony_ci "reading directory lblock %lu", 15868c2ecf20Sopenharmony_ci (unsigned long) block); 15878c2ecf20Sopenharmony_ci brelse(bh); 15888c2ecf20Sopenharmony_ci ret = ERR_PTR(-EIO); 15898c2ecf20Sopenharmony_ci goto cleanup_and_exit; 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci if (!buffer_verified(bh) && 15928c2ecf20Sopenharmony_ci !is_dx_internal_node(dir, block, 15938c2ecf20Sopenharmony_ci (struct ext4_dir_entry *)bh->b_data) && 15948c2ecf20Sopenharmony_ci !ext4_dirblock_csum_verify(dir, bh)) { 15958c2ecf20Sopenharmony_ci EXT4_ERROR_INODE_ERR(dir, EFSBADCRC, 15968c2ecf20Sopenharmony_ci "checksumming directory " 15978c2ecf20Sopenharmony_ci "block %lu", (unsigned long)block); 15988c2ecf20Sopenharmony_ci brelse(bh); 15998c2ecf20Sopenharmony_ci ret = ERR_PTR(-EFSBADCRC); 16008c2ecf20Sopenharmony_ci goto cleanup_and_exit; 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci set_buffer_verified(bh); 16038c2ecf20Sopenharmony_ci i = search_dirblock(bh, dir, fname, 16048c2ecf20Sopenharmony_ci block << EXT4_BLOCK_SIZE_BITS(sb), res_dir); 16058c2ecf20Sopenharmony_ci if (i == 1) { 16068c2ecf20Sopenharmony_ci EXT4_I(dir)->i_dir_start_lookup = block; 16078c2ecf20Sopenharmony_ci ret = bh; 16088c2ecf20Sopenharmony_ci goto cleanup_and_exit; 16098c2ecf20Sopenharmony_ci } else { 16108c2ecf20Sopenharmony_ci brelse(bh); 16118c2ecf20Sopenharmony_ci if (i < 0) 16128c2ecf20Sopenharmony_ci goto cleanup_and_exit; 16138c2ecf20Sopenharmony_ci } 16148c2ecf20Sopenharmony_ci next: 16158c2ecf20Sopenharmony_ci if (++block >= nblocks) 16168c2ecf20Sopenharmony_ci block = 0; 16178c2ecf20Sopenharmony_ci } while (block != start); 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci /* 16208c2ecf20Sopenharmony_ci * If the directory has grown while we were searching, then 16218c2ecf20Sopenharmony_ci * search the last part of the directory before giving up. 16228c2ecf20Sopenharmony_ci */ 16238c2ecf20Sopenharmony_ci block = nblocks; 16248c2ecf20Sopenharmony_ci nblocks = dir->i_size >> EXT4_BLOCK_SIZE_BITS(sb); 16258c2ecf20Sopenharmony_ci if (block < nblocks) { 16268c2ecf20Sopenharmony_ci start = 0; 16278c2ecf20Sopenharmony_ci goto restart; 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_cicleanup_and_exit: 16318c2ecf20Sopenharmony_ci /* Clean up the read-ahead blocks */ 16328c2ecf20Sopenharmony_ci for (; ra_ptr < ra_max; ra_ptr++) 16338c2ecf20Sopenharmony_ci brelse(bh_use[ra_ptr]); 16348c2ecf20Sopenharmony_ci return ret; 16358c2ecf20Sopenharmony_ci} 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_cistatic struct buffer_head *ext4_find_entry(struct inode *dir, 16388c2ecf20Sopenharmony_ci const struct qstr *d_name, 16398c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 **res_dir, 16408c2ecf20Sopenharmony_ci int *inlined) 16418c2ecf20Sopenharmony_ci{ 16428c2ecf20Sopenharmony_ci int err; 16438c2ecf20Sopenharmony_ci struct ext4_filename fname; 16448c2ecf20Sopenharmony_ci struct buffer_head *bh; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci err = ext4_fname_setup_filename(dir, d_name, 1, &fname); 16478c2ecf20Sopenharmony_ci if (err == -ENOENT) 16488c2ecf20Sopenharmony_ci return NULL; 16498c2ecf20Sopenharmony_ci if (err) 16508c2ecf20Sopenharmony_ci return ERR_PTR(err); 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci bh = __ext4_find_entry(dir, &fname, res_dir, inlined); 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci ext4_fname_free_filename(&fname); 16558c2ecf20Sopenharmony_ci return bh; 16568c2ecf20Sopenharmony_ci} 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_cistatic struct buffer_head *ext4_lookup_entry(struct inode *dir, 16598c2ecf20Sopenharmony_ci struct dentry *dentry, 16608c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 **res_dir) 16618c2ecf20Sopenharmony_ci{ 16628c2ecf20Sopenharmony_ci int err; 16638c2ecf20Sopenharmony_ci struct ext4_filename fname; 16648c2ecf20Sopenharmony_ci struct buffer_head *bh; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci err = ext4_fname_prepare_lookup(dir, dentry, &fname); 16678c2ecf20Sopenharmony_ci if (err == -ENOENT) 16688c2ecf20Sopenharmony_ci return NULL; 16698c2ecf20Sopenharmony_ci if (err) 16708c2ecf20Sopenharmony_ci return ERR_PTR(err); 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci bh = __ext4_find_entry(dir, &fname, res_dir, NULL); 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci ext4_fname_free_filename(&fname); 16758c2ecf20Sopenharmony_ci return bh; 16768c2ecf20Sopenharmony_ci} 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_cistatic struct buffer_head * ext4_dx_find_entry(struct inode *dir, 16798c2ecf20Sopenharmony_ci struct ext4_filename *fname, 16808c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 **res_dir) 16818c2ecf20Sopenharmony_ci{ 16828c2ecf20Sopenharmony_ci struct super_block * sb = dir->i_sb; 16838c2ecf20Sopenharmony_ci struct dx_frame frames[EXT4_HTREE_LEVEL], *frame; 16848c2ecf20Sopenharmony_ci struct buffer_head *bh; 16858c2ecf20Sopenharmony_ci ext4_lblk_t block; 16868c2ecf20Sopenharmony_ci int retval; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci#ifdef CONFIG_FS_ENCRYPTION 16898c2ecf20Sopenharmony_ci *res_dir = NULL; 16908c2ecf20Sopenharmony_ci#endif 16918c2ecf20Sopenharmony_ci frame = dx_probe(fname, dir, NULL, frames); 16928c2ecf20Sopenharmony_ci if (IS_ERR(frame)) 16938c2ecf20Sopenharmony_ci return (struct buffer_head *) frame; 16948c2ecf20Sopenharmony_ci do { 16958c2ecf20Sopenharmony_ci block = dx_get_block(frame->at); 16968c2ecf20Sopenharmony_ci bh = ext4_read_dirblock(dir, block, DIRENT_HTREE); 16978c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 16988c2ecf20Sopenharmony_ci goto errout; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci retval = search_dirblock(bh, dir, fname, 17018c2ecf20Sopenharmony_ci block << EXT4_BLOCK_SIZE_BITS(sb), 17028c2ecf20Sopenharmony_ci res_dir); 17038c2ecf20Sopenharmony_ci if (retval == 1) 17048c2ecf20Sopenharmony_ci goto success; 17058c2ecf20Sopenharmony_ci brelse(bh); 17068c2ecf20Sopenharmony_ci if (retval == -1) { 17078c2ecf20Sopenharmony_ci bh = ERR_PTR(ERR_BAD_DX_DIR); 17088c2ecf20Sopenharmony_ci goto errout; 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci /* Check to see if we should continue to search */ 17128c2ecf20Sopenharmony_ci retval = ext4_htree_next_block(dir, fname->hinfo.hash, frame, 17138c2ecf20Sopenharmony_ci frames, NULL); 17148c2ecf20Sopenharmony_ci if (retval < 0) { 17158c2ecf20Sopenharmony_ci ext4_warning_inode(dir, 17168c2ecf20Sopenharmony_ci "error %d reading directory index block", 17178c2ecf20Sopenharmony_ci retval); 17188c2ecf20Sopenharmony_ci bh = ERR_PTR(retval); 17198c2ecf20Sopenharmony_ci goto errout; 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci } while (retval == 1); 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci bh = NULL; 17248c2ecf20Sopenharmony_cierrout: 17258c2ecf20Sopenharmony_ci dxtrace(printk(KERN_DEBUG "%s not found\n", fname->usr_fname->name)); 17268c2ecf20Sopenharmony_cisuccess: 17278c2ecf20Sopenharmony_ci dx_release(frames); 17288c2ecf20Sopenharmony_ci return bh; 17298c2ecf20Sopenharmony_ci} 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_cistatic struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 17328c2ecf20Sopenharmony_ci{ 17338c2ecf20Sopenharmony_ci struct inode *inode; 17348c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 17358c2ecf20Sopenharmony_ci struct buffer_head *bh; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci if (dentry->d_name.len > EXT4_NAME_LEN) 17388c2ecf20Sopenharmony_ci return ERR_PTR(-ENAMETOOLONG); 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci bh = ext4_lookup_entry(dir, dentry, &de); 17418c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 17428c2ecf20Sopenharmony_ci return ERR_CAST(bh); 17438c2ecf20Sopenharmony_ci inode = NULL; 17448c2ecf20Sopenharmony_ci if (bh) { 17458c2ecf20Sopenharmony_ci __u32 ino = le32_to_cpu(de->inode); 17468c2ecf20Sopenharmony_ci brelse(bh); 17478c2ecf20Sopenharmony_ci if (!ext4_valid_inum(dir->i_sb, ino)) { 17488c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(dir, "bad inode number: %u", ino); 17498c2ecf20Sopenharmony_ci return ERR_PTR(-EFSCORRUPTED); 17508c2ecf20Sopenharmony_ci } 17518c2ecf20Sopenharmony_ci if (unlikely(ino == dir->i_ino)) { 17528c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(dir, "'%pd' linked to parent dir", 17538c2ecf20Sopenharmony_ci dentry); 17548c2ecf20Sopenharmony_ci return ERR_PTR(-EFSCORRUPTED); 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci inode = ext4_iget(dir->i_sb, ino, EXT4_IGET_NORMAL); 17578c2ecf20Sopenharmony_ci if (inode == ERR_PTR(-ESTALE)) { 17588c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(dir, 17598c2ecf20Sopenharmony_ci "deleted inode referenced: %u", 17608c2ecf20Sopenharmony_ci ino); 17618c2ecf20Sopenharmony_ci return ERR_PTR(-EFSCORRUPTED); 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci if (!IS_ERR(inode) && IS_ENCRYPTED(dir) && 17648c2ecf20Sopenharmony_ci (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && 17658c2ecf20Sopenharmony_ci !fscrypt_has_permitted_context(dir, inode)) { 17668c2ecf20Sopenharmony_ci ext4_warning(inode->i_sb, 17678c2ecf20Sopenharmony_ci "Inconsistent encryption contexts: %lu/%lu", 17688c2ecf20Sopenharmony_ci dir->i_ino, inode->i_ino); 17698c2ecf20Sopenharmony_ci iput(inode); 17708c2ecf20Sopenharmony_ci return ERR_PTR(-EPERM); 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci } 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci#ifdef CONFIG_UNICODE 17758c2ecf20Sopenharmony_ci if (!inode && IS_CASEFOLDED(dir)) { 17768c2ecf20Sopenharmony_ci /* Eventually we want to call d_add_ci(dentry, NULL) 17778c2ecf20Sopenharmony_ci * for negative dentries in the encoding case as 17788c2ecf20Sopenharmony_ci * well. For now, prevent the negative dentry 17798c2ecf20Sopenharmony_ci * from being cached. 17808c2ecf20Sopenharmony_ci */ 17818c2ecf20Sopenharmony_ci return NULL; 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ci#endif 17848c2ecf20Sopenharmony_ci return d_splice_alias(inode, dentry); 17858c2ecf20Sopenharmony_ci} 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_cistruct dentry *ext4_get_parent(struct dentry *child) 17898c2ecf20Sopenharmony_ci{ 17908c2ecf20Sopenharmony_ci __u32 ino; 17918c2ecf20Sopenharmony_ci static const struct qstr dotdot = QSTR_INIT("..", 2); 17928c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 * de; 17938c2ecf20Sopenharmony_ci struct buffer_head *bh; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci bh = ext4_find_entry(d_inode(child), &dotdot, &de, NULL); 17968c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 17978c2ecf20Sopenharmony_ci return ERR_CAST(bh); 17988c2ecf20Sopenharmony_ci if (!bh) 17998c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 18008c2ecf20Sopenharmony_ci ino = le32_to_cpu(de->inode); 18018c2ecf20Sopenharmony_ci brelse(bh); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci if (!ext4_valid_inum(child->d_sb, ino)) { 18048c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(d_inode(child), 18058c2ecf20Sopenharmony_ci "bad parent inode number: %u", ino); 18068c2ecf20Sopenharmony_ci return ERR_PTR(-EFSCORRUPTED); 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci return d_obtain_alias(ext4_iget(child->d_sb, ino, EXT4_IGET_NORMAL)); 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci/* 18138c2ecf20Sopenharmony_ci * Move count entries from end of map between two memory locations. 18148c2ecf20Sopenharmony_ci * Returns pointer to last entry moved. 18158c2ecf20Sopenharmony_ci */ 18168c2ecf20Sopenharmony_cistatic struct ext4_dir_entry_2 * 18178c2ecf20Sopenharmony_cidx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count, 18188c2ecf20Sopenharmony_ci unsigned blocksize) 18198c2ecf20Sopenharmony_ci{ 18208c2ecf20Sopenharmony_ci unsigned rec_len = 0; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci while (count--) { 18238c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *) 18248c2ecf20Sopenharmony_ci (from + (map->offs<<2)); 18258c2ecf20Sopenharmony_ci rec_len = EXT4_DIR_REC_LEN(de->name_len); 18268c2ecf20Sopenharmony_ci memcpy (to, de, rec_len); 18278c2ecf20Sopenharmony_ci ((struct ext4_dir_entry_2 *) to)->rec_len = 18288c2ecf20Sopenharmony_ci ext4_rec_len_to_disk(rec_len, blocksize); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci /* wipe dir_entry excluding the rec_len field */ 18318c2ecf20Sopenharmony_ci de->inode = 0; 18328c2ecf20Sopenharmony_ci memset(&de->name_len, 0, ext4_rec_len_from_disk(de->rec_len, 18338c2ecf20Sopenharmony_ci blocksize) - 18348c2ecf20Sopenharmony_ci offsetof(struct ext4_dir_entry_2, 18358c2ecf20Sopenharmony_ci name_len)); 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci map++; 18388c2ecf20Sopenharmony_ci to += rec_len; 18398c2ecf20Sopenharmony_ci } 18408c2ecf20Sopenharmony_ci return (struct ext4_dir_entry_2 *) (to - rec_len); 18418c2ecf20Sopenharmony_ci} 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci/* 18448c2ecf20Sopenharmony_ci * Compact each dir entry in the range to the minimal rec_len. 18458c2ecf20Sopenharmony_ci * Returns pointer to last entry in range. 18468c2ecf20Sopenharmony_ci */ 18478c2ecf20Sopenharmony_cistatic struct ext4_dir_entry_2* dx_pack_dirents(char *base, unsigned blocksize) 18488c2ecf20Sopenharmony_ci{ 18498c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *next, *to, *prev, *de = (struct ext4_dir_entry_2 *) base; 18508c2ecf20Sopenharmony_ci unsigned rec_len = 0; 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci prev = to = de; 18538c2ecf20Sopenharmony_ci while ((char*)de < base + blocksize) { 18548c2ecf20Sopenharmony_ci next = ext4_next_entry(de, blocksize); 18558c2ecf20Sopenharmony_ci if (de->inode && de->name_len) { 18568c2ecf20Sopenharmony_ci rec_len = EXT4_DIR_REC_LEN(de->name_len); 18578c2ecf20Sopenharmony_ci if (de > to) 18588c2ecf20Sopenharmony_ci memmove(to, de, rec_len); 18598c2ecf20Sopenharmony_ci to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize); 18608c2ecf20Sopenharmony_ci prev = to; 18618c2ecf20Sopenharmony_ci to = (struct ext4_dir_entry_2 *) (((char *) to) + rec_len); 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci de = next; 18648c2ecf20Sopenharmony_ci } 18658c2ecf20Sopenharmony_ci return prev; 18668c2ecf20Sopenharmony_ci} 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci/* 18698c2ecf20Sopenharmony_ci * Split a full leaf block to make room for a new dir entry. 18708c2ecf20Sopenharmony_ci * Allocate a new block, and move entries so that they are approx. equally full. 18718c2ecf20Sopenharmony_ci * Returns pointer to de in block into which the new entry will be inserted. 18728c2ecf20Sopenharmony_ci */ 18738c2ecf20Sopenharmony_cistatic struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, 18748c2ecf20Sopenharmony_ci struct buffer_head **bh,struct dx_frame *frame, 18758c2ecf20Sopenharmony_ci struct dx_hash_info *hinfo) 18768c2ecf20Sopenharmony_ci{ 18778c2ecf20Sopenharmony_ci unsigned blocksize = dir->i_sb->s_blocksize; 18788c2ecf20Sopenharmony_ci unsigned continued; 18798c2ecf20Sopenharmony_ci int count; 18808c2ecf20Sopenharmony_ci struct buffer_head *bh2; 18818c2ecf20Sopenharmony_ci ext4_lblk_t newblock; 18828c2ecf20Sopenharmony_ci u32 hash2; 18838c2ecf20Sopenharmony_ci struct dx_map_entry *map; 18848c2ecf20Sopenharmony_ci char *data1 = (*bh)->b_data, *data2; 18858c2ecf20Sopenharmony_ci unsigned split, move, size; 18868c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de = NULL, *de2; 18878c2ecf20Sopenharmony_ci int csum_size = 0; 18888c2ecf20Sopenharmony_ci int err = 0, i; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(dir->i_sb)) 18918c2ecf20Sopenharmony_ci csum_size = sizeof(struct ext4_dir_entry_tail); 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci bh2 = ext4_append(handle, dir, &newblock); 18948c2ecf20Sopenharmony_ci if (IS_ERR(bh2)) { 18958c2ecf20Sopenharmony_ci brelse(*bh); 18968c2ecf20Sopenharmony_ci *bh = NULL; 18978c2ecf20Sopenharmony_ci return (struct ext4_dir_entry_2 *) bh2; 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci BUFFER_TRACE(*bh, "get_write_access"); 19018c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, *bh); 19028c2ecf20Sopenharmony_ci if (err) 19038c2ecf20Sopenharmony_ci goto journal_error; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci BUFFER_TRACE(frame->bh, "get_write_access"); 19068c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, frame->bh); 19078c2ecf20Sopenharmony_ci if (err) 19088c2ecf20Sopenharmony_ci goto journal_error; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci data2 = bh2->b_data; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci /* create map in the end of data2 block */ 19138c2ecf20Sopenharmony_ci map = (struct dx_map_entry *) (data2 + blocksize); 19148c2ecf20Sopenharmony_ci count = dx_make_map(dir, *bh, hinfo, map); 19158c2ecf20Sopenharmony_ci if (count < 0) { 19168c2ecf20Sopenharmony_ci err = count; 19178c2ecf20Sopenharmony_ci goto journal_error; 19188c2ecf20Sopenharmony_ci } 19198c2ecf20Sopenharmony_ci map -= count; 19208c2ecf20Sopenharmony_ci dx_sort_map(map, count); 19218c2ecf20Sopenharmony_ci /* Ensure that neither split block is over half full */ 19228c2ecf20Sopenharmony_ci size = 0; 19238c2ecf20Sopenharmony_ci move = 0; 19248c2ecf20Sopenharmony_ci for (i = count-1; i >= 0; i--) { 19258c2ecf20Sopenharmony_ci /* is more than half of this entry in 2nd half of the block? */ 19268c2ecf20Sopenharmony_ci if (size + map[i].size/2 > blocksize/2) 19278c2ecf20Sopenharmony_ci break; 19288c2ecf20Sopenharmony_ci size += map[i].size; 19298c2ecf20Sopenharmony_ci move++; 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci /* 19328c2ecf20Sopenharmony_ci * map index at which we will split 19338c2ecf20Sopenharmony_ci * 19348c2ecf20Sopenharmony_ci * If the sum of active entries didn't exceed half the block size, just 19358c2ecf20Sopenharmony_ci * split it in half by count; each resulting block will have at least 19368c2ecf20Sopenharmony_ci * half the space free. 19378c2ecf20Sopenharmony_ci */ 19388c2ecf20Sopenharmony_ci if (i > 0) 19398c2ecf20Sopenharmony_ci split = count - move; 19408c2ecf20Sopenharmony_ci else 19418c2ecf20Sopenharmony_ci split = count/2; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci hash2 = map[split].hash; 19448c2ecf20Sopenharmony_ci continued = hash2 == map[split - 1].hash; 19458c2ecf20Sopenharmony_ci dxtrace(printk(KERN_INFO "Split block %lu at %x, %i/%i\n", 19468c2ecf20Sopenharmony_ci (unsigned long)dx_get_block(frame->at), 19478c2ecf20Sopenharmony_ci hash2, split, count-split)); 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci /* Fancy dance to stay within two buffers */ 19508c2ecf20Sopenharmony_ci de2 = dx_move_dirents(data1, data2, map + split, count - split, 19518c2ecf20Sopenharmony_ci blocksize); 19528c2ecf20Sopenharmony_ci de = dx_pack_dirents(data1, blocksize); 19538c2ecf20Sopenharmony_ci de->rec_len = ext4_rec_len_to_disk(data1 + (blocksize - csum_size) - 19548c2ecf20Sopenharmony_ci (char *) de, 19558c2ecf20Sopenharmony_ci blocksize); 19568c2ecf20Sopenharmony_ci de2->rec_len = ext4_rec_len_to_disk(data2 + (blocksize - csum_size) - 19578c2ecf20Sopenharmony_ci (char *) de2, 19588c2ecf20Sopenharmony_ci blocksize); 19598c2ecf20Sopenharmony_ci if (csum_size) { 19608c2ecf20Sopenharmony_ci ext4_initialize_dirent_tail(*bh, blocksize); 19618c2ecf20Sopenharmony_ci ext4_initialize_dirent_tail(bh2, blocksize); 19628c2ecf20Sopenharmony_ci } 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci dxtrace(dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *) data1, 19658c2ecf20Sopenharmony_ci blocksize, 1)); 19668c2ecf20Sopenharmony_ci dxtrace(dx_show_leaf(dir, hinfo, (struct ext4_dir_entry_2 *) data2, 19678c2ecf20Sopenharmony_ci blocksize, 1)); 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci /* Which block gets the new entry? */ 19708c2ecf20Sopenharmony_ci if (hinfo->hash >= hash2) { 19718c2ecf20Sopenharmony_ci swap(*bh, bh2); 19728c2ecf20Sopenharmony_ci de = de2; 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci dx_insert_block(frame, hash2 + continued, newblock); 19758c2ecf20Sopenharmony_ci err = ext4_handle_dirty_dirblock(handle, dir, bh2); 19768c2ecf20Sopenharmony_ci if (err) 19778c2ecf20Sopenharmony_ci goto journal_error; 19788c2ecf20Sopenharmony_ci err = ext4_handle_dirty_dx_node(handle, dir, frame->bh); 19798c2ecf20Sopenharmony_ci if (err) 19808c2ecf20Sopenharmony_ci goto journal_error; 19818c2ecf20Sopenharmony_ci brelse(bh2); 19828c2ecf20Sopenharmony_ci dxtrace(dx_show_index("frame", frame->entries)); 19838c2ecf20Sopenharmony_ci return de; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_cijournal_error: 19868c2ecf20Sopenharmony_ci brelse(*bh); 19878c2ecf20Sopenharmony_ci brelse(bh2); 19888c2ecf20Sopenharmony_ci *bh = NULL; 19898c2ecf20Sopenharmony_ci ext4_std_error(dir->i_sb, err); 19908c2ecf20Sopenharmony_ci return ERR_PTR(err); 19918c2ecf20Sopenharmony_ci} 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ciint ext4_find_dest_de(struct inode *dir, struct inode *inode, 19948c2ecf20Sopenharmony_ci struct buffer_head *bh, 19958c2ecf20Sopenharmony_ci void *buf, int buf_size, 19968c2ecf20Sopenharmony_ci struct ext4_filename *fname, 19978c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 **dest_de) 19988c2ecf20Sopenharmony_ci{ 19998c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 20008c2ecf20Sopenharmony_ci unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)); 20018c2ecf20Sopenharmony_ci int nlen, rlen; 20028c2ecf20Sopenharmony_ci unsigned int offset = 0; 20038c2ecf20Sopenharmony_ci char *top; 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *)buf; 20068c2ecf20Sopenharmony_ci top = buf + buf_size - reclen; 20078c2ecf20Sopenharmony_ci while ((char *) de <= top) { 20088c2ecf20Sopenharmony_ci if (ext4_check_dir_entry(dir, NULL, de, bh, 20098c2ecf20Sopenharmony_ci buf, buf_size, offset)) 20108c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 20118c2ecf20Sopenharmony_ci if (ext4_match(dir, fname, de)) 20128c2ecf20Sopenharmony_ci return -EEXIST; 20138c2ecf20Sopenharmony_ci nlen = EXT4_DIR_REC_LEN(de->name_len); 20148c2ecf20Sopenharmony_ci rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); 20158c2ecf20Sopenharmony_ci if ((de->inode ? rlen - nlen : rlen) >= reclen) 20168c2ecf20Sopenharmony_ci break; 20178c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *)((char *)de + rlen); 20188c2ecf20Sopenharmony_ci offset += rlen; 20198c2ecf20Sopenharmony_ci } 20208c2ecf20Sopenharmony_ci if ((char *) de > top) 20218c2ecf20Sopenharmony_ci return -ENOSPC; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci *dest_de = de; 20248c2ecf20Sopenharmony_ci return 0; 20258c2ecf20Sopenharmony_ci} 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_civoid ext4_insert_dentry(struct inode *inode, 20288c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de, 20298c2ecf20Sopenharmony_ci int buf_size, 20308c2ecf20Sopenharmony_ci struct ext4_filename *fname) 20318c2ecf20Sopenharmony_ci{ 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci int nlen, rlen; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci nlen = EXT4_DIR_REC_LEN(de->name_len); 20368c2ecf20Sopenharmony_ci rlen = ext4_rec_len_from_disk(de->rec_len, buf_size); 20378c2ecf20Sopenharmony_ci if (de->inode) { 20388c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de1 = 20398c2ecf20Sopenharmony_ci (struct ext4_dir_entry_2 *)((char *)de + nlen); 20408c2ecf20Sopenharmony_ci de1->rec_len = ext4_rec_len_to_disk(rlen - nlen, buf_size); 20418c2ecf20Sopenharmony_ci de->rec_len = ext4_rec_len_to_disk(nlen, buf_size); 20428c2ecf20Sopenharmony_ci de = de1; 20438c2ecf20Sopenharmony_ci } 20448c2ecf20Sopenharmony_ci de->file_type = EXT4_FT_UNKNOWN; 20458c2ecf20Sopenharmony_ci de->inode = cpu_to_le32(inode->i_ino); 20468c2ecf20Sopenharmony_ci ext4_set_de_type(inode->i_sb, de, inode->i_mode); 20478c2ecf20Sopenharmony_ci de->name_len = fname_len(fname); 20488c2ecf20Sopenharmony_ci memcpy(de->name, fname_name(fname), fname_len(fname)); 20498c2ecf20Sopenharmony_ci} 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci/* 20528c2ecf20Sopenharmony_ci * Add a new entry into a directory (leaf) block. If de is non-NULL, 20538c2ecf20Sopenharmony_ci * it points to a directory entry which is guaranteed to be large 20548c2ecf20Sopenharmony_ci * enough for new directory entry. If de is NULL, then 20558c2ecf20Sopenharmony_ci * add_dirent_to_buf will attempt search the directory block for 20568c2ecf20Sopenharmony_ci * space. It will return -ENOSPC if no space is available, and -EIO 20578c2ecf20Sopenharmony_ci * and -EEXIST if directory entry already exists. 20588c2ecf20Sopenharmony_ci */ 20598c2ecf20Sopenharmony_cistatic int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname, 20608c2ecf20Sopenharmony_ci struct inode *dir, 20618c2ecf20Sopenharmony_ci struct inode *inode, struct ext4_dir_entry_2 *de, 20628c2ecf20Sopenharmony_ci struct buffer_head *bh) 20638c2ecf20Sopenharmony_ci{ 20648c2ecf20Sopenharmony_ci unsigned int blocksize = dir->i_sb->s_blocksize; 20658c2ecf20Sopenharmony_ci int csum_size = 0; 20668c2ecf20Sopenharmony_ci int err, err2; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(inode->i_sb)) 20698c2ecf20Sopenharmony_ci csum_size = sizeof(struct ext4_dir_entry_tail); 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci if (!de) { 20728c2ecf20Sopenharmony_ci err = ext4_find_dest_de(dir, inode, bh, bh->b_data, 20738c2ecf20Sopenharmony_ci blocksize - csum_size, fname, &de); 20748c2ecf20Sopenharmony_ci if (err) 20758c2ecf20Sopenharmony_ci return err; 20768c2ecf20Sopenharmony_ci } 20778c2ecf20Sopenharmony_ci BUFFER_TRACE(bh, "get_write_access"); 20788c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, bh); 20798c2ecf20Sopenharmony_ci if (err) { 20808c2ecf20Sopenharmony_ci ext4_std_error(dir->i_sb, err); 20818c2ecf20Sopenharmony_ci return err; 20828c2ecf20Sopenharmony_ci } 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci /* By now the buffer is marked for journaling */ 20858c2ecf20Sopenharmony_ci ext4_insert_dentry(inode, de, blocksize, fname); 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci /* 20888c2ecf20Sopenharmony_ci * XXX shouldn't update any times until successful 20898c2ecf20Sopenharmony_ci * completion of syscall, but too many callers depend 20908c2ecf20Sopenharmony_ci * on this. 20918c2ecf20Sopenharmony_ci * 20928c2ecf20Sopenharmony_ci * XXX similarly, too many callers depend on 20938c2ecf20Sopenharmony_ci * ext4_new_inode() setting the times, but error 20948c2ecf20Sopenharmony_ci * recovery deletes the inode, so the worst that can 20958c2ecf20Sopenharmony_ci * happen is that the times are slightly out of date 20968c2ecf20Sopenharmony_ci * and/or different from the directory change time. 20978c2ecf20Sopenharmony_ci */ 20988c2ecf20Sopenharmony_ci dir->i_mtime = dir->i_ctime = current_time(dir); 20998c2ecf20Sopenharmony_ci ext4_update_dx_flag(dir); 21008c2ecf20Sopenharmony_ci inode_inc_iversion(dir); 21018c2ecf20Sopenharmony_ci err2 = ext4_mark_inode_dirty(handle, dir); 21028c2ecf20Sopenharmony_ci BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); 21038c2ecf20Sopenharmony_ci err = ext4_handle_dirty_dirblock(handle, dir, bh); 21048c2ecf20Sopenharmony_ci if (err) 21058c2ecf20Sopenharmony_ci ext4_std_error(dir->i_sb, err); 21068c2ecf20Sopenharmony_ci return err ? err : err2; 21078c2ecf20Sopenharmony_ci} 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_cistatic bool ext4_check_dx_root(struct inode *dir, struct dx_root *root) 21108c2ecf20Sopenharmony_ci{ 21118c2ecf20Sopenharmony_ci struct fake_dirent *fde; 21128c2ecf20Sopenharmony_ci const char *error_msg; 21138c2ecf20Sopenharmony_ci unsigned int rlen; 21148c2ecf20Sopenharmony_ci unsigned int blocksize = dir->i_sb->s_blocksize; 21158c2ecf20Sopenharmony_ci char *blockend = (char *)root + dir->i_sb->s_blocksize; 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci fde = &root->dot; 21188c2ecf20Sopenharmony_ci if (unlikely(fde->name_len != 1)) { 21198c2ecf20Sopenharmony_ci error_msg = "invalid name_len for '.'"; 21208c2ecf20Sopenharmony_ci goto corrupted; 21218c2ecf20Sopenharmony_ci } 21228c2ecf20Sopenharmony_ci if (unlikely(strncmp(root->dot_name, ".", fde->name_len))) { 21238c2ecf20Sopenharmony_ci error_msg = "invalid name for '.'"; 21248c2ecf20Sopenharmony_ci goto corrupted; 21258c2ecf20Sopenharmony_ci } 21268c2ecf20Sopenharmony_ci rlen = ext4_rec_len_from_disk(fde->rec_len, blocksize); 21278c2ecf20Sopenharmony_ci if (unlikely((char *)fde + rlen >= blockend)) { 21288c2ecf20Sopenharmony_ci error_msg = "invalid rec_len for '.'"; 21298c2ecf20Sopenharmony_ci goto corrupted; 21308c2ecf20Sopenharmony_ci } 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci fde = &root->dotdot; 21338c2ecf20Sopenharmony_ci if (unlikely(fde->name_len != 2)) { 21348c2ecf20Sopenharmony_ci error_msg = "invalid name_len for '..'"; 21358c2ecf20Sopenharmony_ci goto corrupted; 21368c2ecf20Sopenharmony_ci } 21378c2ecf20Sopenharmony_ci if (unlikely(strncmp(root->dotdot_name, "..", fde->name_len))) { 21388c2ecf20Sopenharmony_ci error_msg = "invalid name for '..'"; 21398c2ecf20Sopenharmony_ci goto corrupted; 21408c2ecf20Sopenharmony_ci } 21418c2ecf20Sopenharmony_ci rlen = ext4_rec_len_from_disk(fde->rec_len, blocksize); 21428c2ecf20Sopenharmony_ci if (unlikely((char *)fde + rlen >= blockend)) { 21438c2ecf20Sopenharmony_ci error_msg = "invalid rec_len for '..'"; 21448c2ecf20Sopenharmony_ci goto corrupted; 21458c2ecf20Sopenharmony_ci } 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci return true; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_cicorrupted: 21508c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(dir, "Corrupt dir, %s, running e2fsck is recommended", 21518c2ecf20Sopenharmony_ci error_msg); 21528c2ecf20Sopenharmony_ci return false; 21538c2ecf20Sopenharmony_ci} 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci/* 21568c2ecf20Sopenharmony_ci * This converts a one block unindexed directory to a 3 block indexed 21578c2ecf20Sopenharmony_ci * directory, and adds the dentry to the indexed directory. 21588c2ecf20Sopenharmony_ci */ 21598c2ecf20Sopenharmony_cistatic int make_indexed_dir(handle_t *handle, struct ext4_filename *fname, 21608c2ecf20Sopenharmony_ci struct inode *dir, 21618c2ecf20Sopenharmony_ci struct inode *inode, struct buffer_head *bh) 21628c2ecf20Sopenharmony_ci{ 21638c2ecf20Sopenharmony_ci struct buffer_head *bh2; 21648c2ecf20Sopenharmony_ci struct dx_root *root; 21658c2ecf20Sopenharmony_ci struct dx_frame frames[EXT4_HTREE_LEVEL], *frame; 21668c2ecf20Sopenharmony_ci struct dx_entry *entries; 21678c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de, *de2; 21688c2ecf20Sopenharmony_ci char *data2, *top; 21698c2ecf20Sopenharmony_ci unsigned len; 21708c2ecf20Sopenharmony_ci int retval; 21718c2ecf20Sopenharmony_ci unsigned blocksize; 21728c2ecf20Sopenharmony_ci ext4_lblk_t block; 21738c2ecf20Sopenharmony_ci struct fake_dirent *fde; 21748c2ecf20Sopenharmony_ci int csum_size = 0; 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(inode->i_sb)) 21778c2ecf20Sopenharmony_ci csum_size = sizeof(struct ext4_dir_entry_tail); 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci blocksize = dir->i_sb->s_blocksize; 21808c2ecf20Sopenharmony_ci dxtrace(printk(KERN_DEBUG "Creating index: inode %lu\n", dir->i_ino)); 21818c2ecf20Sopenharmony_ci BUFFER_TRACE(bh, "get_write_access"); 21828c2ecf20Sopenharmony_ci retval = ext4_journal_get_write_access(handle, bh); 21838c2ecf20Sopenharmony_ci if (retval) { 21848c2ecf20Sopenharmony_ci ext4_std_error(dir->i_sb, retval); 21858c2ecf20Sopenharmony_ci brelse(bh); 21868c2ecf20Sopenharmony_ci return retval; 21878c2ecf20Sopenharmony_ci } 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci root = (struct dx_root *) bh->b_data; 21908c2ecf20Sopenharmony_ci if (!ext4_check_dx_root(dir, root)) { 21918c2ecf20Sopenharmony_ci brelse(bh); 21928c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 21938c2ecf20Sopenharmony_ci } 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci /* The 0th block becomes the root, move the dirents out */ 21968c2ecf20Sopenharmony_ci fde = &root->dotdot; 21978c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *)((char *)fde + 21988c2ecf20Sopenharmony_ci ext4_rec_len_from_disk(fde->rec_len, blocksize)); 21998c2ecf20Sopenharmony_ci len = ((char *) root) + (blocksize - csum_size) - (char *) de; 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci /* Allocate new block for the 0th block's dirents */ 22028c2ecf20Sopenharmony_ci bh2 = ext4_append(handle, dir, &block); 22038c2ecf20Sopenharmony_ci if (IS_ERR(bh2)) { 22048c2ecf20Sopenharmony_ci brelse(bh); 22058c2ecf20Sopenharmony_ci return PTR_ERR(bh2); 22068c2ecf20Sopenharmony_ci } 22078c2ecf20Sopenharmony_ci ext4_set_inode_flag(dir, EXT4_INODE_INDEX); 22088c2ecf20Sopenharmony_ci data2 = bh2->b_data; 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci memcpy(data2, de, len); 22118c2ecf20Sopenharmony_ci memset(de, 0, len); /* wipe old data */ 22128c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *) data2; 22138c2ecf20Sopenharmony_ci top = data2 + len; 22148c2ecf20Sopenharmony_ci while ((char *)(de2 = ext4_next_entry(de, blocksize)) < top) { 22158c2ecf20Sopenharmony_ci if (ext4_check_dir_entry(dir, NULL, de, bh2, data2, len, 22168c2ecf20Sopenharmony_ci (data2 + (blocksize - csum_size) - 22178c2ecf20Sopenharmony_ci (char *) de))) { 22188c2ecf20Sopenharmony_ci brelse(bh2); 22198c2ecf20Sopenharmony_ci brelse(bh); 22208c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 22218c2ecf20Sopenharmony_ci } 22228c2ecf20Sopenharmony_ci de = de2; 22238c2ecf20Sopenharmony_ci } 22248c2ecf20Sopenharmony_ci de->rec_len = ext4_rec_len_to_disk(data2 + (blocksize - csum_size) - 22258c2ecf20Sopenharmony_ci (char *) de, blocksize); 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci if (csum_size) 22288c2ecf20Sopenharmony_ci ext4_initialize_dirent_tail(bh2, blocksize); 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci /* Initialize the root; the dot dirents already exist */ 22318c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *) (&root->dotdot); 22328c2ecf20Sopenharmony_ci de->rec_len = ext4_rec_len_to_disk(blocksize - EXT4_DIR_REC_LEN(2), 22338c2ecf20Sopenharmony_ci blocksize); 22348c2ecf20Sopenharmony_ci memset (&root->info, 0, sizeof(root->info)); 22358c2ecf20Sopenharmony_ci root->info.info_length = sizeof(root->info); 22368c2ecf20Sopenharmony_ci root->info.hash_version = EXT4_SB(dir->i_sb)->s_def_hash_version; 22378c2ecf20Sopenharmony_ci entries = root->entries; 22388c2ecf20Sopenharmony_ci dx_set_block(entries, 1); 22398c2ecf20Sopenharmony_ci dx_set_count(entries, 1); 22408c2ecf20Sopenharmony_ci dx_set_limit(entries, dx_root_limit(dir, sizeof(root->info))); 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci /* Initialize as for dx_probe */ 22438c2ecf20Sopenharmony_ci fname->hinfo.hash_version = root->info.hash_version; 22448c2ecf20Sopenharmony_ci if (fname->hinfo.hash_version <= DX_HASH_TEA) 22458c2ecf20Sopenharmony_ci fname->hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; 22468c2ecf20Sopenharmony_ci fname->hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; 22478c2ecf20Sopenharmony_ci ext4fs_dirhash(dir, fname_name(fname), fname_len(fname), &fname->hinfo); 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci memset(frames, 0, sizeof(frames)); 22508c2ecf20Sopenharmony_ci frame = frames; 22518c2ecf20Sopenharmony_ci frame->entries = entries; 22528c2ecf20Sopenharmony_ci frame->at = entries; 22538c2ecf20Sopenharmony_ci frame->bh = bh; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci retval = ext4_handle_dirty_dx_node(handle, dir, frame->bh); 22568c2ecf20Sopenharmony_ci if (retval) 22578c2ecf20Sopenharmony_ci goto out_frames; 22588c2ecf20Sopenharmony_ci retval = ext4_handle_dirty_dirblock(handle, dir, bh2); 22598c2ecf20Sopenharmony_ci if (retval) 22608c2ecf20Sopenharmony_ci goto out_frames; 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci de = do_split(handle,dir, &bh2, frame, &fname->hinfo); 22638c2ecf20Sopenharmony_ci if (IS_ERR(de)) { 22648c2ecf20Sopenharmony_ci retval = PTR_ERR(de); 22658c2ecf20Sopenharmony_ci goto out_frames; 22668c2ecf20Sopenharmony_ci } 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci retval = add_dirent_to_buf(handle, fname, dir, inode, de, bh2); 22698c2ecf20Sopenharmony_ciout_frames: 22708c2ecf20Sopenharmony_ci /* 22718c2ecf20Sopenharmony_ci * Even if the block split failed, we have to properly write 22728c2ecf20Sopenharmony_ci * out all the changes we did so far. Otherwise we can end up 22738c2ecf20Sopenharmony_ci * with corrupted filesystem. 22748c2ecf20Sopenharmony_ci */ 22758c2ecf20Sopenharmony_ci if (retval) 22768c2ecf20Sopenharmony_ci ext4_mark_inode_dirty(handle, dir); 22778c2ecf20Sopenharmony_ci dx_release(frames); 22788c2ecf20Sopenharmony_ci brelse(bh2); 22798c2ecf20Sopenharmony_ci return retval; 22808c2ecf20Sopenharmony_ci} 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci/* 22838c2ecf20Sopenharmony_ci * ext4_add_entry() 22848c2ecf20Sopenharmony_ci * 22858c2ecf20Sopenharmony_ci * adds a file entry to the specified directory, using the same 22868c2ecf20Sopenharmony_ci * semantics as ext4_find_entry(). It returns NULL if it failed. 22878c2ecf20Sopenharmony_ci * 22888c2ecf20Sopenharmony_ci * NOTE!! The inode part of 'de' is left at 0 - which means you 22898c2ecf20Sopenharmony_ci * may not sleep between calling this and putting something into 22908c2ecf20Sopenharmony_ci * the entry, as someone else might have used it while you slept. 22918c2ecf20Sopenharmony_ci */ 22928c2ecf20Sopenharmony_cistatic int ext4_add_entry(handle_t *handle, struct dentry *dentry, 22938c2ecf20Sopenharmony_ci struct inode *inode) 22948c2ecf20Sopenharmony_ci{ 22958c2ecf20Sopenharmony_ci struct inode *dir = d_inode(dentry->d_parent); 22968c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 22978c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 22988c2ecf20Sopenharmony_ci struct super_block *sb; 22998c2ecf20Sopenharmony_ci struct ext4_filename fname; 23008c2ecf20Sopenharmony_ci int retval; 23018c2ecf20Sopenharmony_ci int dx_fallback=0; 23028c2ecf20Sopenharmony_ci unsigned blocksize; 23038c2ecf20Sopenharmony_ci ext4_lblk_t block, blocks; 23048c2ecf20Sopenharmony_ci int csum_size = 0; 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(inode->i_sb)) 23078c2ecf20Sopenharmony_ci csum_size = sizeof(struct ext4_dir_entry_tail); 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci sb = dir->i_sb; 23108c2ecf20Sopenharmony_ci blocksize = sb->s_blocksize; 23118c2ecf20Sopenharmony_ci if (!dentry->d_name.len) 23128c2ecf20Sopenharmony_ci return -EINVAL; 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci if (fscrypt_is_nokey_name(dentry)) 23158c2ecf20Sopenharmony_ci return -ENOKEY; 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci#ifdef CONFIG_UNICODE 23188c2ecf20Sopenharmony_ci if (sb_has_strict_encoding(sb) && IS_CASEFOLDED(dir) && 23198c2ecf20Sopenharmony_ci sb->s_encoding && utf8_validate(sb->s_encoding, &dentry->d_name)) 23208c2ecf20Sopenharmony_ci return -EINVAL; 23218c2ecf20Sopenharmony_ci#endif 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci retval = ext4_fname_setup_filename(dir, &dentry->d_name, 0, &fname); 23248c2ecf20Sopenharmony_ci if (retval) 23258c2ecf20Sopenharmony_ci return retval; 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci if (ext4_has_inline_data(dir)) { 23288c2ecf20Sopenharmony_ci retval = ext4_try_add_inline_entry(handle, &fname, dir, inode); 23298c2ecf20Sopenharmony_ci if (retval < 0) 23308c2ecf20Sopenharmony_ci goto out; 23318c2ecf20Sopenharmony_ci if (retval == 1) { 23328c2ecf20Sopenharmony_ci retval = 0; 23338c2ecf20Sopenharmony_ci goto out; 23348c2ecf20Sopenharmony_ci } 23358c2ecf20Sopenharmony_ci } 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci if (is_dx(dir)) { 23388c2ecf20Sopenharmony_ci retval = ext4_dx_add_entry(handle, &fname, dir, inode); 23398c2ecf20Sopenharmony_ci if (!retval || (retval != ERR_BAD_DX_DIR)) 23408c2ecf20Sopenharmony_ci goto out; 23418c2ecf20Sopenharmony_ci /* Can we just ignore htree data? */ 23428c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(sb)) { 23438c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(dir, 23448c2ecf20Sopenharmony_ci "Directory has corrupted htree index."); 23458c2ecf20Sopenharmony_ci retval = -EFSCORRUPTED; 23468c2ecf20Sopenharmony_ci goto out; 23478c2ecf20Sopenharmony_ci } 23488c2ecf20Sopenharmony_ci ext4_clear_inode_flag(dir, EXT4_INODE_INDEX); 23498c2ecf20Sopenharmony_ci dx_fallback++; 23508c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, dir); 23518c2ecf20Sopenharmony_ci if (unlikely(retval)) 23528c2ecf20Sopenharmony_ci goto out; 23538c2ecf20Sopenharmony_ci } 23548c2ecf20Sopenharmony_ci blocks = dir->i_size >> sb->s_blocksize_bits; 23558c2ecf20Sopenharmony_ci for (block = 0; block < blocks; block++) { 23568c2ecf20Sopenharmony_ci bh = ext4_read_dirblock(dir, block, DIRENT); 23578c2ecf20Sopenharmony_ci if (bh == NULL) { 23588c2ecf20Sopenharmony_ci bh = ext4_bread(handle, dir, block, 23598c2ecf20Sopenharmony_ci EXT4_GET_BLOCKS_CREATE); 23608c2ecf20Sopenharmony_ci goto add_to_new_block; 23618c2ecf20Sopenharmony_ci } 23628c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 23638c2ecf20Sopenharmony_ci retval = PTR_ERR(bh); 23648c2ecf20Sopenharmony_ci bh = NULL; 23658c2ecf20Sopenharmony_ci goto out; 23668c2ecf20Sopenharmony_ci } 23678c2ecf20Sopenharmony_ci retval = add_dirent_to_buf(handle, &fname, dir, inode, 23688c2ecf20Sopenharmony_ci NULL, bh); 23698c2ecf20Sopenharmony_ci if (retval != -ENOSPC) 23708c2ecf20Sopenharmony_ci goto out; 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci if (blocks == 1 && !dx_fallback && 23738c2ecf20Sopenharmony_ci ext4_has_feature_dir_index(sb)) { 23748c2ecf20Sopenharmony_ci retval = make_indexed_dir(handle, &fname, dir, 23758c2ecf20Sopenharmony_ci inode, bh); 23768c2ecf20Sopenharmony_ci bh = NULL; /* make_indexed_dir releases bh */ 23778c2ecf20Sopenharmony_ci goto out; 23788c2ecf20Sopenharmony_ci } 23798c2ecf20Sopenharmony_ci brelse(bh); 23808c2ecf20Sopenharmony_ci } 23818c2ecf20Sopenharmony_ci bh = ext4_append(handle, dir, &block); 23828c2ecf20Sopenharmony_ciadd_to_new_block: 23838c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 23848c2ecf20Sopenharmony_ci retval = PTR_ERR(bh); 23858c2ecf20Sopenharmony_ci bh = NULL; 23868c2ecf20Sopenharmony_ci goto out; 23878c2ecf20Sopenharmony_ci } 23888c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *) bh->b_data; 23898c2ecf20Sopenharmony_ci de->inode = 0; 23908c2ecf20Sopenharmony_ci de->rec_len = ext4_rec_len_to_disk(blocksize - csum_size, blocksize); 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci if (csum_size) 23938c2ecf20Sopenharmony_ci ext4_initialize_dirent_tail(bh, blocksize); 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci retval = add_dirent_to_buf(handle, &fname, dir, inode, de, bh); 23968c2ecf20Sopenharmony_ciout: 23978c2ecf20Sopenharmony_ci ext4_fname_free_filename(&fname); 23988c2ecf20Sopenharmony_ci brelse(bh); 23998c2ecf20Sopenharmony_ci if (retval == 0) 24008c2ecf20Sopenharmony_ci ext4_set_inode_state(inode, EXT4_STATE_NEWENTRY); 24018c2ecf20Sopenharmony_ci return retval; 24028c2ecf20Sopenharmony_ci} 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci/* 24058c2ecf20Sopenharmony_ci * Returns 0 for success, or a negative error value 24068c2ecf20Sopenharmony_ci */ 24078c2ecf20Sopenharmony_cistatic int ext4_dx_add_entry(handle_t *handle, struct ext4_filename *fname, 24088c2ecf20Sopenharmony_ci struct inode *dir, struct inode *inode) 24098c2ecf20Sopenharmony_ci{ 24108c2ecf20Sopenharmony_ci struct dx_frame frames[EXT4_HTREE_LEVEL], *frame; 24118c2ecf20Sopenharmony_ci struct dx_entry *entries, *at; 24128c2ecf20Sopenharmony_ci struct buffer_head *bh; 24138c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 24148c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 24158c2ecf20Sopenharmony_ci int restart; 24168c2ecf20Sopenharmony_ci int err; 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ciagain: 24198c2ecf20Sopenharmony_ci restart = 0; 24208c2ecf20Sopenharmony_ci frame = dx_probe(fname, dir, NULL, frames); 24218c2ecf20Sopenharmony_ci if (IS_ERR(frame)) 24228c2ecf20Sopenharmony_ci return PTR_ERR(frame); 24238c2ecf20Sopenharmony_ci entries = frame->entries; 24248c2ecf20Sopenharmony_ci at = frame->at; 24258c2ecf20Sopenharmony_ci bh = ext4_read_dirblock(dir, dx_get_block(frame->at), DIRENT_HTREE); 24268c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 24278c2ecf20Sopenharmony_ci err = PTR_ERR(bh); 24288c2ecf20Sopenharmony_ci bh = NULL; 24298c2ecf20Sopenharmony_ci goto cleanup; 24308c2ecf20Sopenharmony_ci } 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci BUFFER_TRACE(bh, "get_write_access"); 24338c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, bh); 24348c2ecf20Sopenharmony_ci if (err) 24358c2ecf20Sopenharmony_ci goto journal_error; 24368c2ecf20Sopenharmony_ci 24378c2ecf20Sopenharmony_ci err = add_dirent_to_buf(handle, fname, dir, inode, NULL, bh); 24388c2ecf20Sopenharmony_ci if (err != -ENOSPC) 24398c2ecf20Sopenharmony_ci goto cleanup; 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci err = 0; 24428c2ecf20Sopenharmony_ci /* Block full, should compress but for now just split */ 24438c2ecf20Sopenharmony_ci dxtrace(printk(KERN_DEBUG "using %u of %u node entries\n", 24448c2ecf20Sopenharmony_ci dx_get_count(entries), dx_get_limit(entries))); 24458c2ecf20Sopenharmony_ci /* Need to split index? */ 24468c2ecf20Sopenharmony_ci if (dx_get_count(entries) == dx_get_limit(entries)) { 24478c2ecf20Sopenharmony_ci ext4_lblk_t newblock; 24488c2ecf20Sopenharmony_ci int levels = frame - frames + 1; 24498c2ecf20Sopenharmony_ci unsigned int icount; 24508c2ecf20Sopenharmony_ci int add_level = 1; 24518c2ecf20Sopenharmony_ci struct dx_entry *entries2; 24528c2ecf20Sopenharmony_ci struct dx_node *node2; 24538c2ecf20Sopenharmony_ci struct buffer_head *bh2; 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci while (frame > frames) { 24568c2ecf20Sopenharmony_ci if (dx_get_count((frame - 1)->entries) < 24578c2ecf20Sopenharmony_ci dx_get_limit((frame - 1)->entries)) { 24588c2ecf20Sopenharmony_ci add_level = 0; 24598c2ecf20Sopenharmony_ci break; 24608c2ecf20Sopenharmony_ci } 24618c2ecf20Sopenharmony_ci frame--; /* split higher index block */ 24628c2ecf20Sopenharmony_ci at = frame->at; 24638c2ecf20Sopenharmony_ci entries = frame->entries; 24648c2ecf20Sopenharmony_ci restart = 1; 24658c2ecf20Sopenharmony_ci } 24668c2ecf20Sopenharmony_ci if (add_level && levels == ext4_dir_htree_level(sb)) { 24678c2ecf20Sopenharmony_ci ext4_warning(sb, "Directory (ino: %lu) index full, " 24688c2ecf20Sopenharmony_ci "reach max htree level :%d", 24698c2ecf20Sopenharmony_ci dir->i_ino, levels); 24708c2ecf20Sopenharmony_ci if (ext4_dir_htree_level(sb) < EXT4_HTREE_LEVEL) { 24718c2ecf20Sopenharmony_ci ext4_warning(sb, "Large directory feature is " 24728c2ecf20Sopenharmony_ci "not enabled on this " 24738c2ecf20Sopenharmony_ci "filesystem"); 24748c2ecf20Sopenharmony_ci } 24758c2ecf20Sopenharmony_ci err = -ENOSPC; 24768c2ecf20Sopenharmony_ci goto cleanup; 24778c2ecf20Sopenharmony_ci } 24788c2ecf20Sopenharmony_ci icount = dx_get_count(entries); 24798c2ecf20Sopenharmony_ci bh2 = ext4_append(handle, dir, &newblock); 24808c2ecf20Sopenharmony_ci if (IS_ERR(bh2)) { 24818c2ecf20Sopenharmony_ci err = PTR_ERR(bh2); 24828c2ecf20Sopenharmony_ci goto cleanup; 24838c2ecf20Sopenharmony_ci } 24848c2ecf20Sopenharmony_ci node2 = (struct dx_node *)(bh2->b_data); 24858c2ecf20Sopenharmony_ci entries2 = node2->entries; 24868c2ecf20Sopenharmony_ci memset(&node2->fake, 0, sizeof(struct fake_dirent)); 24878c2ecf20Sopenharmony_ci node2->fake.rec_len = ext4_rec_len_to_disk(sb->s_blocksize, 24888c2ecf20Sopenharmony_ci sb->s_blocksize); 24898c2ecf20Sopenharmony_ci BUFFER_TRACE(frame->bh, "get_write_access"); 24908c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, frame->bh); 24918c2ecf20Sopenharmony_ci if (err) 24928c2ecf20Sopenharmony_ci goto journal_error; 24938c2ecf20Sopenharmony_ci if (!add_level) { 24948c2ecf20Sopenharmony_ci unsigned icount1 = icount/2, icount2 = icount - icount1; 24958c2ecf20Sopenharmony_ci unsigned hash2 = dx_get_hash(entries + icount1); 24968c2ecf20Sopenharmony_ci dxtrace(printk(KERN_DEBUG "Split index %i/%i\n", 24978c2ecf20Sopenharmony_ci icount1, icount2)); 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci BUFFER_TRACE(frame->bh, "get_write_access"); /* index root */ 25008c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, 25018c2ecf20Sopenharmony_ci (frame - 1)->bh); 25028c2ecf20Sopenharmony_ci if (err) 25038c2ecf20Sopenharmony_ci goto journal_error; 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci memcpy((char *) entries2, (char *) (entries + icount1), 25068c2ecf20Sopenharmony_ci icount2 * sizeof(struct dx_entry)); 25078c2ecf20Sopenharmony_ci dx_set_count(entries, icount1); 25088c2ecf20Sopenharmony_ci dx_set_count(entries2, icount2); 25098c2ecf20Sopenharmony_ci dx_set_limit(entries2, dx_node_limit(dir)); 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci /* Which index block gets the new entry? */ 25128c2ecf20Sopenharmony_ci if (at - entries >= icount1) { 25138c2ecf20Sopenharmony_ci frame->at = at = at - entries - icount1 + entries2; 25148c2ecf20Sopenharmony_ci frame->entries = entries = entries2; 25158c2ecf20Sopenharmony_ci swap(frame->bh, bh2); 25168c2ecf20Sopenharmony_ci } 25178c2ecf20Sopenharmony_ci dx_insert_block((frame - 1), hash2, newblock); 25188c2ecf20Sopenharmony_ci dxtrace(dx_show_index("node", frame->entries)); 25198c2ecf20Sopenharmony_ci dxtrace(dx_show_index("node", 25208c2ecf20Sopenharmony_ci ((struct dx_node *) bh2->b_data)->entries)); 25218c2ecf20Sopenharmony_ci err = ext4_handle_dirty_dx_node(handle, dir, bh2); 25228c2ecf20Sopenharmony_ci if (err) 25238c2ecf20Sopenharmony_ci goto journal_error; 25248c2ecf20Sopenharmony_ci brelse (bh2); 25258c2ecf20Sopenharmony_ci err = ext4_handle_dirty_dx_node(handle, dir, 25268c2ecf20Sopenharmony_ci (frame - 1)->bh); 25278c2ecf20Sopenharmony_ci if (err) 25288c2ecf20Sopenharmony_ci goto journal_error; 25298c2ecf20Sopenharmony_ci err = ext4_handle_dirty_dx_node(handle, dir, 25308c2ecf20Sopenharmony_ci frame->bh); 25318c2ecf20Sopenharmony_ci if (restart || err) 25328c2ecf20Sopenharmony_ci goto journal_error; 25338c2ecf20Sopenharmony_ci } else { 25348c2ecf20Sopenharmony_ci struct dx_root *dxroot; 25358c2ecf20Sopenharmony_ci memcpy((char *) entries2, (char *) entries, 25368c2ecf20Sopenharmony_ci icount * sizeof(struct dx_entry)); 25378c2ecf20Sopenharmony_ci dx_set_limit(entries2, dx_node_limit(dir)); 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_ci /* Set up root */ 25408c2ecf20Sopenharmony_ci dx_set_count(entries, 1); 25418c2ecf20Sopenharmony_ci dx_set_block(entries + 0, newblock); 25428c2ecf20Sopenharmony_ci dxroot = (struct dx_root *)frames[0].bh->b_data; 25438c2ecf20Sopenharmony_ci dxroot->info.indirect_levels += 1; 25448c2ecf20Sopenharmony_ci dxtrace(printk(KERN_DEBUG 25458c2ecf20Sopenharmony_ci "Creating %d level index...\n", 25468c2ecf20Sopenharmony_ci dxroot->info.indirect_levels)); 25478c2ecf20Sopenharmony_ci err = ext4_handle_dirty_dx_node(handle, dir, frame->bh); 25488c2ecf20Sopenharmony_ci if (err) 25498c2ecf20Sopenharmony_ci goto journal_error; 25508c2ecf20Sopenharmony_ci err = ext4_handle_dirty_dx_node(handle, dir, bh2); 25518c2ecf20Sopenharmony_ci brelse(bh2); 25528c2ecf20Sopenharmony_ci restart = 1; 25538c2ecf20Sopenharmony_ci goto journal_error; 25548c2ecf20Sopenharmony_ci } 25558c2ecf20Sopenharmony_ci } 25568c2ecf20Sopenharmony_ci de = do_split(handle, dir, &bh, frame, &fname->hinfo); 25578c2ecf20Sopenharmony_ci if (IS_ERR(de)) { 25588c2ecf20Sopenharmony_ci err = PTR_ERR(de); 25598c2ecf20Sopenharmony_ci goto cleanup; 25608c2ecf20Sopenharmony_ci } 25618c2ecf20Sopenharmony_ci err = add_dirent_to_buf(handle, fname, dir, inode, de, bh); 25628c2ecf20Sopenharmony_ci goto cleanup; 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_cijournal_error: 25658c2ecf20Sopenharmony_ci ext4_std_error(dir->i_sb, err); /* this is a no-op if err == 0 */ 25668c2ecf20Sopenharmony_cicleanup: 25678c2ecf20Sopenharmony_ci brelse(bh); 25688c2ecf20Sopenharmony_ci dx_release(frames); 25698c2ecf20Sopenharmony_ci /* @restart is true means htree-path has been changed, we need to 25708c2ecf20Sopenharmony_ci * repeat dx_probe() to find out valid htree-path 25718c2ecf20Sopenharmony_ci */ 25728c2ecf20Sopenharmony_ci if (restart && err == 0) 25738c2ecf20Sopenharmony_ci goto again; 25748c2ecf20Sopenharmony_ci return err; 25758c2ecf20Sopenharmony_ci} 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci/* 25788c2ecf20Sopenharmony_ci * ext4_generic_delete_entry deletes a directory entry by merging it 25798c2ecf20Sopenharmony_ci * with the previous entry 25808c2ecf20Sopenharmony_ci */ 25818c2ecf20Sopenharmony_ciint ext4_generic_delete_entry(struct inode *dir, 25828c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de_del, 25838c2ecf20Sopenharmony_ci struct buffer_head *bh, 25848c2ecf20Sopenharmony_ci void *entry_buf, 25858c2ecf20Sopenharmony_ci int buf_size, 25868c2ecf20Sopenharmony_ci int csum_size) 25878c2ecf20Sopenharmony_ci{ 25888c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de, *pde; 25898c2ecf20Sopenharmony_ci unsigned int blocksize = dir->i_sb->s_blocksize; 25908c2ecf20Sopenharmony_ci int i; 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci i = 0; 25938c2ecf20Sopenharmony_ci pde = NULL; 25948c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *)entry_buf; 25958c2ecf20Sopenharmony_ci while (i < buf_size - csum_size) { 25968c2ecf20Sopenharmony_ci if (ext4_check_dir_entry(dir, NULL, de, bh, 25978c2ecf20Sopenharmony_ci entry_buf, buf_size, i)) 25988c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 25998c2ecf20Sopenharmony_ci if (de == de_del) { 26008c2ecf20Sopenharmony_ci if (pde) { 26018c2ecf20Sopenharmony_ci pde->rec_len = ext4_rec_len_to_disk( 26028c2ecf20Sopenharmony_ci ext4_rec_len_from_disk(pde->rec_len, 26038c2ecf20Sopenharmony_ci blocksize) + 26048c2ecf20Sopenharmony_ci ext4_rec_len_from_disk(de->rec_len, 26058c2ecf20Sopenharmony_ci blocksize), 26068c2ecf20Sopenharmony_ci blocksize); 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci /* wipe entire dir_entry */ 26098c2ecf20Sopenharmony_ci memset(de, 0, ext4_rec_len_from_disk(de->rec_len, 26108c2ecf20Sopenharmony_ci blocksize)); 26118c2ecf20Sopenharmony_ci } else { 26128c2ecf20Sopenharmony_ci /* wipe dir_entry excluding the rec_len field */ 26138c2ecf20Sopenharmony_ci de->inode = 0; 26148c2ecf20Sopenharmony_ci memset(&de->name_len, 0, 26158c2ecf20Sopenharmony_ci ext4_rec_len_from_disk(de->rec_len, 26168c2ecf20Sopenharmony_ci blocksize) - 26178c2ecf20Sopenharmony_ci offsetof(struct ext4_dir_entry_2, 26188c2ecf20Sopenharmony_ci name_len)); 26198c2ecf20Sopenharmony_ci } 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_ci inode_inc_iversion(dir); 26228c2ecf20Sopenharmony_ci return 0; 26238c2ecf20Sopenharmony_ci } 26248c2ecf20Sopenharmony_ci i += ext4_rec_len_from_disk(de->rec_len, blocksize); 26258c2ecf20Sopenharmony_ci pde = de; 26268c2ecf20Sopenharmony_ci de = ext4_next_entry(de, blocksize); 26278c2ecf20Sopenharmony_ci } 26288c2ecf20Sopenharmony_ci return -ENOENT; 26298c2ecf20Sopenharmony_ci} 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_cistatic int ext4_delete_entry(handle_t *handle, 26328c2ecf20Sopenharmony_ci struct inode *dir, 26338c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de_del, 26348c2ecf20Sopenharmony_ci struct buffer_head *bh) 26358c2ecf20Sopenharmony_ci{ 26368c2ecf20Sopenharmony_ci int err, csum_size = 0; 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci if (ext4_has_inline_data(dir)) { 26398c2ecf20Sopenharmony_ci int has_inline_data = 1; 26408c2ecf20Sopenharmony_ci err = ext4_delete_inline_entry(handle, dir, de_del, bh, 26418c2ecf20Sopenharmony_ci &has_inline_data); 26428c2ecf20Sopenharmony_ci if (has_inline_data) 26438c2ecf20Sopenharmony_ci return err; 26448c2ecf20Sopenharmony_ci } 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(dir->i_sb)) 26478c2ecf20Sopenharmony_ci csum_size = sizeof(struct ext4_dir_entry_tail); 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci BUFFER_TRACE(bh, "get_write_access"); 26508c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, bh); 26518c2ecf20Sopenharmony_ci if (unlikely(err)) 26528c2ecf20Sopenharmony_ci goto out; 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci err = ext4_generic_delete_entry(dir, de_del, bh, bh->b_data, 26558c2ecf20Sopenharmony_ci dir->i_sb->s_blocksize, csum_size); 26568c2ecf20Sopenharmony_ci if (err) 26578c2ecf20Sopenharmony_ci goto out; 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata"); 26608c2ecf20Sopenharmony_ci err = ext4_handle_dirty_dirblock(handle, dir, bh); 26618c2ecf20Sopenharmony_ci if (unlikely(err)) 26628c2ecf20Sopenharmony_ci goto out; 26638c2ecf20Sopenharmony_ci 26648c2ecf20Sopenharmony_ci return 0; 26658c2ecf20Sopenharmony_ciout: 26668c2ecf20Sopenharmony_ci if (err != -ENOENT) 26678c2ecf20Sopenharmony_ci ext4_std_error(dir->i_sb, err); 26688c2ecf20Sopenharmony_ci return err; 26698c2ecf20Sopenharmony_ci} 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci/* 26728c2ecf20Sopenharmony_ci * Set directory link count to 1 if nlinks > EXT4_LINK_MAX, or if nlinks == 2 26738c2ecf20Sopenharmony_ci * since this indicates that nlinks count was previously 1 to avoid overflowing 26748c2ecf20Sopenharmony_ci * the 16-bit i_links_count field on disk. Directories with i_nlink == 1 mean 26758c2ecf20Sopenharmony_ci * that subdirectory link counts are not being maintained accurately. 26768c2ecf20Sopenharmony_ci * 26778c2ecf20Sopenharmony_ci * The caller has already checked for i_nlink overflow in case the DIR_LINK 26788c2ecf20Sopenharmony_ci * feature is not enabled and returned -EMLINK. The is_dx() check is a proxy 26798c2ecf20Sopenharmony_ci * for checking S_ISDIR(inode) (since the INODE_INDEX feature will not be set 26808c2ecf20Sopenharmony_ci * on regular files) and to avoid creating huge/slow non-HTREE directories. 26818c2ecf20Sopenharmony_ci */ 26828c2ecf20Sopenharmony_cistatic void ext4_inc_count(struct inode *inode) 26838c2ecf20Sopenharmony_ci{ 26848c2ecf20Sopenharmony_ci inc_nlink(inode); 26858c2ecf20Sopenharmony_ci if (is_dx(inode) && 26868c2ecf20Sopenharmony_ci (inode->i_nlink > EXT4_LINK_MAX || inode->i_nlink == 2)) 26878c2ecf20Sopenharmony_ci set_nlink(inode, 1); 26888c2ecf20Sopenharmony_ci} 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci/* 26918c2ecf20Sopenharmony_ci * If a directory had nlink == 1, then we should let it be 1. This indicates 26928c2ecf20Sopenharmony_ci * directory has >EXT4_LINK_MAX subdirs. 26938c2ecf20Sopenharmony_ci */ 26948c2ecf20Sopenharmony_cistatic void ext4_dec_count(struct inode *inode) 26958c2ecf20Sopenharmony_ci{ 26968c2ecf20Sopenharmony_ci if (!S_ISDIR(inode->i_mode) || inode->i_nlink > 2) 26978c2ecf20Sopenharmony_ci drop_nlink(inode); 26988c2ecf20Sopenharmony_ci} 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_ci/* 27028c2ecf20Sopenharmony_ci * Add non-directory inode to a directory. On success, the inode reference is 27038c2ecf20Sopenharmony_ci * consumed by dentry is instantiation. This is also indicated by clearing of 27048c2ecf20Sopenharmony_ci * *inodep pointer. On failure, the caller is responsible for dropping the 27058c2ecf20Sopenharmony_ci * inode reference in the safe context. 27068c2ecf20Sopenharmony_ci */ 27078c2ecf20Sopenharmony_cistatic int ext4_add_nondir(handle_t *handle, 27088c2ecf20Sopenharmony_ci struct dentry *dentry, struct inode **inodep) 27098c2ecf20Sopenharmony_ci{ 27108c2ecf20Sopenharmony_ci struct inode *dir = d_inode(dentry->d_parent); 27118c2ecf20Sopenharmony_ci struct inode *inode = *inodep; 27128c2ecf20Sopenharmony_ci int err = ext4_add_entry(handle, dentry, inode); 27138c2ecf20Sopenharmony_ci if (!err) { 27148c2ecf20Sopenharmony_ci err = ext4_mark_inode_dirty(handle, inode); 27158c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) 27168c2ecf20Sopenharmony_ci ext4_handle_sync(handle); 27178c2ecf20Sopenharmony_ci d_instantiate_new(dentry, inode); 27188c2ecf20Sopenharmony_ci *inodep = NULL; 27198c2ecf20Sopenharmony_ci return err; 27208c2ecf20Sopenharmony_ci } 27218c2ecf20Sopenharmony_ci drop_nlink(inode); 27228c2ecf20Sopenharmony_ci ext4_orphan_add(handle, inode); 27238c2ecf20Sopenharmony_ci unlock_new_inode(inode); 27248c2ecf20Sopenharmony_ci return err; 27258c2ecf20Sopenharmony_ci} 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci/* 27288c2ecf20Sopenharmony_ci * By the time this is called, we already have created 27298c2ecf20Sopenharmony_ci * the directory cache entry for the new file, but it 27308c2ecf20Sopenharmony_ci * is so far negative - it has no inode. 27318c2ecf20Sopenharmony_ci * 27328c2ecf20Sopenharmony_ci * If the create succeeds, we fill in the inode information 27338c2ecf20Sopenharmony_ci * with d_instantiate(). 27348c2ecf20Sopenharmony_ci */ 27358c2ecf20Sopenharmony_cistatic int ext4_create(struct inode *dir, struct dentry *dentry, umode_t mode, 27368c2ecf20Sopenharmony_ci bool excl) 27378c2ecf20Sopenharmony_ci{ 27388c2ecf20Sopenharmony_ci handle_t *handle; 27398c2ecf20Sopenharmony_ci struct inode *inode; 27408c2ecf20Sopenharmony_ci int err, credits, retries = 0; 27418c2ecf20Sopenharmony_ci 27428c2ecf20Sopenharmony_ci err = dquot_initialize(dir); 27438c2ecf20Sopenharmony_ci if (err) 27448c2ecf20Sopenharmony_ci return err; 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + 27478c2ecf20Sopenharmony_ci EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3); 27488c2ecf20Sopenharmony_ciretry: 27498c2ecf20Sopenharmony_ci inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0, 27508c2ecf20Sopenharmony_ci NULL, EXT4_HT_DIR, credits); 27518c2ecf20Sopenharmony_ci handle = ext4_journal_current_handle(); 27528c2ecf20Sopenharmony_ci err = PTR_ERR(inode); 27538c2ecf20Sopenharmony_ci if (!IS_ERR(inode)) { 27548c2ecf20Sopenharmony_ci inode->i_op = &ext4_file_inode_operations; 27558c2ecf20Sopenharmony_ci inode->i_fop = &ext4_file_operations; 27568c2ecf20Sopenharmony_ci ext4_set_aops(inode); 27578c2ecf20Sopenharmony_ci err = ext4_add_nondir(handle, dentry, &inode); 27588c2ecf20Sopenharmony_ci if (!err) 27598c2ecf20Sopenharmony_ci ext4_fc_track_create(handle, dentry); 27608c2ecf20Sopenharmony_ci } 27618c2ecf20Sopenharmony_ci if (handle) 27628c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 27638c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(inode)) 27648c2ecf20Sopenharmony_ci iput(inode); 27658c2ecf20Sopenharmony_ci if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) 27668c2ecf20Sopenharmony_ci goto retry; 27678c2ecf20Sopenharmony_ci return err; 27688c2ecf20Sopenharmony_ci} 27698c2ecf20Sopenharmony_ci 27708c2ecf20Sopenharmony_cistatic int ext4_mknod(struct inode *dir, struct dentry *dentry, 27718c2ecf20Sopenharmony_ci umode_t mode, dev_t rdev) 27728c2ecf20Sopenharmony_ci{ 27738c2ecf20Sopenharmony_ci handle_t *handle; 27748c2ecf20Sopenharmony_ci struct inode *inode; 27758c2ecf20Sopenharmony_ci int err, credits, retries = 0; 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci err = dquot_initialize(dir); 27788c2ecf20Sopenharmony_ci if (err) 27798c2ecf20Sopenharmony_ci return err; 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + 27828c2ecf20Sopenharmony_ci EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3); 27838c2ecf20Sopenharmony_ciretry: 27848c2ecf20Sopenharmony_ci inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0, 27858c2ecf20Sopenharmony_ci NULL, EXT4_HT_DIR, credits); 27868c2ecf20Sopenharmony_ci handle = ext4_journal_current_handle(); 27878c2ecf20Sopenharmony_ci err = PTR_ERR(inode); 27888c2ecf20Sopenharmony_ci if (!IS_ERR(inode)) { 27898c2ecf20Sopenharmony_ci init_special_inode(inode, inode->i_mode, rdev); 27908c2ecf20Sopenharmony_ci inode->i_op = &ext4_special_inode_operations; 27918c2ecf20Sopenharmony_ci err = ext4_add_nondir(handle, dentry, &inode); 27928c2ecf20Sopenharmony_ci if (!err) 27938c2ecf20Sopenharmony_ci ext4_fc_track_create(handle, dentry); 27948c2ecf20Sopenharmony_ci } 27958c2ecf20Sopenharmony_ci if (handle) 27968c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 27978c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(inode)) 27988c2ecf20Sopenharmony_ci iput(inode); 27998c2ecf20Sopenharmony_ci if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) 28008c2ecf20Sopenharmony_ci goto retry; 28018c2ecf20Sopenharmony_ci return err; 28028c2ecf20Sopenharmony_ci} 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_cistatic int ext4_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) 28058c2ecf20Sopenharmony_ci{ 28068c2ecf20Sopenharmony_ci handle_t *handle; 28078c2ecf20Sopenharmony_ci struct inode *inode; 28088c2ecf20Sopenharmony_ci int err, retries = 0; 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci err = dquot_initialize(dir); 28118c2ecf20Sopenharmony_ci if (err) 28128c2ecf20Sopenharmony_ci return err; 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ciretry: 28158c2ecf20Sopenharmony_ci inode = ext4_new_inode_start_handle(dir, mode, 28168c2ecf20Sopenharmony_ci NULL, 0, NULL, 28178c2ecf20Sopenharmony_ci EXT4_HT_DIR, 28188c2ecf20Sopenharmony_ci EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) + 28198c2ecf20Sopenharmony_ci 4 + EXT4_XATTR_TRANS_BLOCKS); 28208c2ecf20Sopenharmony_ci handle = ext4_journal_current_handle(); 28218c2ecf20Sopenharmony_ci err = PTR_ERR(inode); 28228c2ecf20Sopenharmony_ci if (!IS_ERR(inode)) { 28238c2ecf20Sopenharmony_ci inode->i_op = &ext4_file_inode_operations; 28248c2ecf20Sopenharmony_ci inode->i_fop = &ext4_file_operations; 28258c2ecf20Sopenharmony_ci ext4_set_aops(inode); 28268c2ecf20Sopenharmony_ci d_tmpfile(dentry, inode); 28278c2ecf20Sopenharmony_ci err = ext4_orphan_add(handle, inode); 28288c2ecf20Sopenharmony_ci if (err) 28298c2ecf20Sopenharmony_ci goto err_unlock_inode; 28308c2ecf20Sopenharmony_ci mark_inode_dirty(inode); 28318c2ecf20Sopenharmony_ci unlock_new_inode(inode); 28328c2ecf20Sopenharmony_ci } 28338c2ecf20Sopenharmony_ci if (handle) 28348c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 28358c2ecf20Sopenharmony_ci if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) 28368c2ecf20Sopenharmony_ci goto retry; 28378c2ecf20Sopenharmony_ci return err; 28388c2ecf20Sopenharmony_cierr_unlock_inode: 28398c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 28408c2ecf20Sopenharmony_ci unlock_new_inode(inode); 28418c2ecf20Sopenharmony_ci return err; 28428c2ecf20Sopenharmony_ci} 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_cistruct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode, 28458c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de, 28468c2ecf20Sopenharmony_ci int blocksize, int csum_size, 28478c2ecf20Sopenharmony_ci unsigned int parent_ino, int dotdot_real_len) 28488c2ecf20Sopenharmony_ci{ 28498c2ecf20Sopenharmony_ci de->inode = cpu_to_le32(inode->i_ino); 28508c2ecf20Sopenharmony_ci de->name_len = 1; 28518c2ecf20Sopenharmony_ci de->rec_len = ext4_rec_len_to_disk(EXT4_DIR_REC_LEN(de->name_len), 28528c2ecf20Sopenharmony_ci blocksize); 28538c2ecf20Sopenharmony_ci strcpy(de->name, "."); 28548c2ecf20Sopenharmony_ci ext4_set_de_type(inode->i_sb, de, S_IFDIR); 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_ci de = ext4_next_entry(de, blocksize); 28578c2ecf20Sopenharmony_ci de->inode = cpu_to_le32(parent_ino); 28588c2ecf20Sopenharmony_ci de->name_len = 2; 28598c2ecf20Sopenharmony_ci if (!dotdot_real_len) 28608c2ecf20Sopenharmony_ci de->rec_len = ext4_rec_len_to_disk(blocksize - 28618c2ecf20Sopenharmony_ci (csum_size + EXT4_DIR_REC_LEN(1)), 28628c2ecf20Sopenharmony_ci blocksize); 28638c2ecf20Sopenharmony_ci else 28648c2ecf20Sopenharmony_ci de->rec_len = ext4_rec_len_to_disk( 28658c2ecf20Sopenharmony_ci EXT4_DIR_REC_LEN(de->name_len), blocksize); 28668c2ecf20Sopenharmony_ci strcpy(de->name, ".."); 28678c2ecf20Sopenharmony_ci ext4_set_de_type(inode->i_sb, de, S_IFDIR); 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_ci return ext4_next_entry(de, blocksize); 28708c2ecf20Sopenharmony_ci} 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_ciint ext4_init_new_dir(handle_t *handle, struct inode *dir, 28738c2ecf20Sopenharmony_ci struct inode *inode) 28748c2ecf20Sopenharmony_ci{ 28758c2ecf20Sopenharmony_ci struct buffer_head *dir_block = NULL; 28768c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 28778c2ecf20Sopenharmony_ci ext4_lblk_t block = 0; 28788c2ecf20Sopenharmony_ci unsigned int blocksize = dir->i_sb->s_blocksize; 28798c2ecf20Sopenharmony_ci int csum_size = 0; 28808c2ecf20Sopenharmony_ci int err; 28818c2ecf20Sopenharmony_ci 28828c2ecf20Sopenharmony_ci if (ext4_has_metadata_csum(dir->i_sb)) 28838c2ecf20Sopenharmony_ci csum_size = sizeof(struct ext4_dir_entry_tail); 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) { 28868c2ecf20Sopenharmony_ci err = ext4_try_create_inline_dir(handle, dir, inode); 28878c2ecf20Sopenharmony_ci if (err < 0 && err != -ENOSPC) 28888c2ecf20Sopenharmony_ci goto out; 28898c2ecf20Sopenharmony_ci if (!err) 28908c2ecf20Sopenharmony_ci goto out; 28918c2ecf20Sopenharmony_ci } 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci inode->i_size = 0; 28948c2ecf20Sopenharmony_ci dir_block = ext4_append(handle, inode, &block); 28958c2ecf20Sopenharmony_ci if (IS_ERR(dir_block)) 28968c2ecf20Sopenharmony_ci return PTR_ERR(dir_block); 28978c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *)dir_block->b_data; 28988c2ecf20Sopenharmony_ci ext4_init_dot_dotdot(inode, de, blocksize, csum_size, dir->i_ino, 0); 28998c2ecf20Sopenharmony_ci set_nlink(inode, 2); 29008c2ecf20Sopenharmony_ci if (csum_size) 29018c2ecf20Sopenharmony_ci ext4_initialize_dirent_tail(dir_block, blocksize); 29028c2ecf20Sopenharmony_ci 29038c2ecf20Sopenharmony_ci BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata"); 29048c2ecf20Sopenharmony_ci err = ext4_handle_dirty_dirblock(handle, inode, dir_block); 29058c2ecf20Sopenharmony_ci if (err) 29068c2ecf20Sopenharmony_ci goto out; 29078c2ecf20Sopenharmony_ci set_buffer_verified(dir_block); 29088c2ecf20Sopenharmony_ciout: 29098c2ecf20Sopenharmony_ci brelse(dir_block); 29108c2ecf20Sopenharmony_ci return err; 29118c2ecf20Sopenharmony_ci} 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_cistatic int ext4_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) 29148c2ecf20Sopenharmony_ci{ 29158c2ecf20Sopenharmony_ci handle_t *handle; 29168c2ecf20Sopenharmony_ci struct inode *inode; 29178c2ecf20Sopenharmony_ci int err, err2 = 0, credits, retries = 0; 29188c2ecf20Sopenharmony_ci 29198c2ecf20Sopenharmony_ci if (EXT4_DIR_LINK_MAX(dir)) 29208c2ecf20Sopenharmony_ci return -EMLINK; 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci err = dquot_initialize(dir); 29238c2ecf20Sopenharmony_ci if (err) 29248c2ecf20Sopenharmony_ci return err; 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + 29278c2ecf20Sopenharmony_ci EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3); 29288c2ecf20Sopenharmony_ciretry: 29298c2ecf20Sopenharmony_ci inode = ext4_new_inode_start_handle(dir, S_IFDIR | mode, 29308c2ecf20Sopenharmony_ci &dentry->d_name, 29318c2ecf20Sopenharmony_ci 0, NULL, EXT4_HT_DIR, credits); 29328c2ecf20Sopenharmony_ci handle = ext4_journal_current_handle(); 29338c2ecf20Sopenharmony_ci err = PTR_ERR(inode); 29348c2ecf20Sopenharmony_ci if (IS_ERR(inode)) 29358c2ecf20Sopenharmony_ci goto out_stop; 29368c2ecf20Sopenharmony_ci 29378c2ecf20Sopenharmony_ci inode->i_op = &ext4_dir_inode_operations; 29388c2ecf20Sopenharmony_ci inode->i_fop = &ext4_dir_operations; 29398c2ecf20Sopenharmony_ci err = ext4_init_new_dir(handle, dir, inode); 29408c2ecf20Sopenharmony_ci if (err) 29418c2ecf20Sopenharmony_ci goto out_clear_inode; 29428c2ecf20Sopenharmony_ci err = ext4_mark_inode_dirty(handle, inode); 29438c2ecf20Sopenharmony_ci if (!err) 29448c2ecf20Sopenharmony_ci err = ext4_add_entry(handle, dentry, inode); 29458c2ecf20Sopenharmony_ci if (err) { 29468c2ecf20Sopenharmony_ciout_clear_inode: 29478c2ecf20Sopenharmony_ci clear_nlink(inode); 29488c2ecf20Sopenharmony_ci ext4_orphan_add(handle, inode); 29498c2ecf20Sopenharmony_ci unlock_new_inode(inode); 29508c2ecf20Sopenharmony_ci err2 = ext4_mark_inode_dirty(handle, inode); 29518c2ecf20Sopenharmony_ci if (unlikely(err2)) 29528c2ecf20Sopenharmony_ci err = err2; 29538c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 29548c2ecf20Sopenharmony_ci iput(inode); 29558c2ecf20Sopenharmony_ci goto out_retry; 29568c2ecf20Sopenharmony_ci } 29578c2ecf20Sopenharmony_ci ext4_inc_count(dir); 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_ci ext4_update_dx_flag(dir); 29608c2ecf20Sopenharmony_ci err = ext4_mark_inode_dirty(handle, dir); 29618c2ecf20Sopenharmony_ci if (err) 29628c2ecf20Sopenharmony_ci goto out_clear_inode; 29638c2ecf20Sopenharmony_ci d_instantiate_new(dentry, inode); 29648c2ecf20Sopenharmony_ci ext4_fc_track_create(handle, dentry); 29658c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) 29668c2ecf20Sopenharmony_ci ext4_handle_sync(handle); 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_ciout_stop: 29698c2ecf20Sopenharmony_ci if (handle) 29708c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 29718c2ecf20Sopenharmony_ciout_retry: 29728c2ecf20Sopenharmony_ci if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) 29738c2ecf20Sopenharmony_ci goto retry; 29748c2ecf20Sopenharmony_ci return err; 29758c2ecf20Sopenharmony_ci} 29768c2ecf20Sopenharmony_ci 29778c2ecf20Sopenharmony_ci/* 29788c2ecf20Sopenharmony_ci * routine to check that the specified directory is empty (for rmdir) 29798c2ecf20Sopenharmony_ci */ 29808c2ecf20Sopenharmony_cibool ext4_empty_dir(struct inode *inode) 29818c2ecf20Sopenharmony_ci{ 29828c2ecf20Sopenharmony_ci unsigned int offset; 29838c2ecf20Sopenharmony_ci struct buffer_head *bh; 29848c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 29858c2ecf20Sopenharmony_ci struct super_block *sb; 29868c2ecf20Sopenharmony_ci 29878c2ecf20Sopenharmony_ci if (ext4_has_inline_data(inode)) { 29888c2ecf20Sopenharmony_ci int has_inline_data = 1; 29898c2ecf20Sopenharmony_ci int ret; 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci ret = empty_inline_dir(inode, &has_inline_data); 29928c2ecf20Sopenharmony_ci if (has_inline_data) 29938c2ecf20Sopenharmony_ci return ret; 29948c2ecf20Sopenharmony_ci } 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_ci sb = inode->i_sb; 29978c2ecf20Sopenharmony_ci if (inode->i_size < EXT4_DIR_REC_LEN(1) + EXT4_DIR_REC_LEN(2)) { 29988c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "invalid size"); 29998c2ecf20Sopenharmony_ci return false; 30008c2ecf20Sopenharmony_ci } 30018c2ecf20Sopenharmony_ci bh = ext4_read_dirblock(inode, 0, EITHER); 30028c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 30038c2ecf20Sopenharmony_ci return false; 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *) bh->b_data; 30068c2ecf20Sopenharmony_ci if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size, 30078c2ecf20Sopenharmony_ci 0) || 30088c2ecf20Sopenharmony_ci le32_to_cpu(de->inode) != inode->i_ino || strcmp(".", de->name)) { 30098c2ecf20Sopenharmony_ci ext4_warning_inode(inode, "directory missing '.'"); 30108c2ecf20Sopenharmony_ci brelse(bh); 30118c2ecf20Sopenharmony_ci return false; 30128c2ecf20Sopenharmony_ci } 30138c2ecf20Sopenharmony_ci offset = ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize); 30148c2ecf20Sopenharmony_ci de = ext4_next_entry(de, sb->s_blocksize); 30158c2ecf20Sopenharmony_ci if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size, 30168c2ecf20Sopenharmony_ci offset) || 30178c2ecf20Sopenharmony_ci le32_to_cpu(de->inode) == 0 || strcmp("..", de->name)) { 30188c2ecf20Sopenharmony_ci ext4_warning_inode(inode, "directory missing '..'"); 30198c2ecf20Sopenharmony_ci brelse(bh); 30208c2ecf20Sopenharmony_ci return false; 30218c2ecf20Sopenharmony_ci } 30228c2ecf20Sopenharmony_ci offset += ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize); 30238c2ecf20Sopenharmony_ci while (offset < inode->i_size) { 30248c2ecf20Sopenharmony_ci if (!(offset & (sb->s_blocksize - 1))) { 30258c2ecf20Sopenharmony_ci unsigned int lblock; 30268c2ecf20Sopenharmony_ci brelse(bh); 30278c2ecf20Sopenharmony_ci lblock = offset >> EXT4_BLOCK_SIZE_BITS(sb); 30288c2ecf20Sopenharmony_ci bh = ext4_read_dirblock(inode, lblock, EITHER); 30298c2ecf20Sopenharmony_ci if (bh == NULL) { 30308c2ecf20Sopenharmony_ci offset += sb->s_blocksize; 30318c2ecf20Sopenharmony_ci continue; 30328c2ecf20Sopenharmony_ci } 30338c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 30348c2ecf20Sopenharmony_ci return false; 30358c2ecf20Sopenharmony_ci } 30368c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *) (bh->b_data + 30378c2ecf20Sopenharmony_ci (offset & (sb->s_blocksize - 1))); 30388c2ecf20Sopenharmony_ci if (ext4_check_dir_entry(inode, NULL, de, bh, 30398c2ecf20Sopenharmony_ci bh->b_data, bh->b_size, offset) || 30408c2ecf20Sopenharmony_ci le32_to_cpu(de->inode)) { 30418c2ecf20Sopenharmony_ci brelse(bh); 30428c2ecf20Sopenharmony_ci return false; 30438c2ecf20Sopenharmony_ci } 30448c2ecf20Sopenharmony_ci offset += ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize); 30458c2ecf20Sopenharmony_ci } 30468c2ecf20Sopenharmony_ci brelse(bh); 30478c2ecf20Sopenharmony_ci return true; 30488c2ecf20Sopenharmony_ci} 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci/* 30518c2ecf20Sopenharmony_ci * ext4_orphan_add() links an unlinked or truncated inode into a list of 30528c2ecf20Sopenharmony_ci * such inodes, starting at the superblock, in case we crash before the 30538c2ecf20Sopenharmony_ci * file is closed/deleted, or in case the inode truncate spans multiple 30548c2ecf20Sopenharmony_ci * transactions and the last transaction is not recovered after a crash. 30558c2ecf20Sopenharmony_ci * 30568c2ecf20Sopenharmony_ci * At filesystem recovery time, we walk this list deleting unlinked 30578c2ecf20Sopenharmony_ci * inodes and truncating linked inodes in ext4_orphan_cleanup(). 30588c2ecf20Sopenharmony_ci * 30598c2ecf20Sopenharmony_ci * Orphan list manipulation functions must be called under i_mutex unless 30608c2ecf20Sopenharmony_ci * we are just creating the inode or deleting it. 30618c2ecf20Sopenharmony_ci */ 30628c2ecf20Sopenharmony_ciint ext4_orphan_add(handle_t *handle, struct inode *inode) 30638c2ecf20Sopenharmony_ci{ 30648c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 30658c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 30668c2ecf20Sopenharmony_ci struct ext4_iloc iloc; 30678c2ecf20Sopenharmony_ci int err = 0, rc; 30688c2ecf20Sopenharmony_ci bool dirty = false; 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_ci if (!sbi->s_journal || is_bad_inode(inode)) 30718c2ecf20Sopenharmony_ci return 0; 30728c2ecf20Sopenharmony_ci 30738c2ecf20Sopenharmony_ci WARN_ON_ONCE(!(inode->i_state & (I_NEW | I_FREEING)) && 30748c2ecf20Sopenharmony_ci !inode_is_locked(inode)); 30758c2ecf20Sopenharmony_ci /* 30768c2ecf20Sopenharmony_ci * Exit early if inode already is on orphan list. This is a big speedup 30778c2ecf20Sopenharmony_ci * since we don't have to contend on the global s_orphan_lock. 30788c2ecf20Sopenharmony_ci */ 30798c2ecf20Sopenharmony_ci if (!list_empty(&EXT4_I(inode)->i_orphan)) 30808c2ecf20Sopenharmony_ci return 0; 30818c2ecf20Sopenharmony_ci 30828c2ecf20Sopenharmony_ci /* 30838c2ecf20Sopenharmony_ci * Orphan handling is only valid for files with data blocks 30848c2ecf20Sopenharmony_ci * being truncated, or files being unlinked. Note that we either 30858c2ecf20Sopenharmony_ci * hold i_mutex, or the inode can not be referenced from outside, 30868c2ecf20Sopenharmony_ci * so i_nlink should not be bumped due to race 30878c2ecf20Sopenharmony_ci */ 30888c2ecf20Sopenharmony_ci J_ASSERT((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || 30898c2ecf20Sopenharmony_ci S_ISLNK(inode->i_mode)) || inode->i_nlink == 0); 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_ci BUFFER_TRACE(sbi->s_sbh, "get_write_access"); 30928c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, sbi->s_sbh); 30938c2ecf20Sopenharmony_ci if (err) 30948c2ecf20Sopenharmony_ci goto out; 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci err = ext4_reserve_inode_write(handle, inode, &iloc); 30978c2ecf20Sopenharmony_ci if (err) 30988c2ecf20Sopenharmony_ci goto out; 30998c2ecf20Sopenharmony_ci 31008c2ecf20Sopenharmony_ci mutex_lock(&sbi->s_orphan_lock); 31018c2ecf20Sopenharmony_ci /* 31028c2ecf20Sopenharmony_ci * Due to previous errors inode may be already a part of on-disk 31038c2ecf20Sopenharmony_ci * orphan list. If so skip on-disk list modification. 31048c2ecf20Sopenharmony_ci */ 31058c2ecf20Sopenharmony_ci if (!NEXT_ORPHAN(inode) || NEXT_ORPHAN(inode) > 31068c2ecf20Sopenharmony_ci (le32_to_cpu(sbi->s_es->s_inodes_count))) { 31078c2ecf20Sopenharmony_ci /* Insert this inode at the head of the on-disk orphan list */ 31088c2ecf20Sopenharmony_ci NEXT_ORPHAN(inode) = le32_to_cpu(sbi->s_es->s_last_orphan); 31098c2ecf20Sopenharmony_ci lock_buffer(sbi->s_sbh); 31108c2ecf20Sopenharmony_ci sbi->s_es->s_last_orphan = cpu_to_le32(inode->i_ino); 31118c2ecf20Sopenharmony_ci ext4_superblock_csum_set(sb); 31128c2ecf20Sopenharmony_ci unlock_buffer(sbi->s_sbh); 31138c2ecf20Sopenharmony_ci dirty = true; 31148c2ecf20Sopenharmony_ci } 31158c2ecf20Sopenharmony_ci list_add(&EXT4_I(inode)->i_orphan, &sbi->s_orphan); 31168c2ecf20Sopenharmony_ci mutex_unlock(&sbi->s_orphan_lock); 31178c2ecf20Sopenharmony_ci 31188c2ecf20Sopenharmony_ci if (dirty) { 31198c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh); 31208c2ecf20Sopenharmony_ci rc = ext4_mark_iloc_dirty(handle, inode, &iloc); 31218c2ecf20Sopenharmony_ci if (!err) 31228c2ecf20Sopenharmony_ci err = rc; 31238c2ecf20Sopenharmony_ci if (err) { 31248c2ecf20Sopenharmony_ci /* 31258c2ecf20Sopenharmony_ci * We have to remove inode from in-memory list if 31268c2ecf20Sopenharmony_ci * addition to on disk orphan list failed. Stray orphan 31278c2ecf20Sopenharmony_ci * list entries can cause panics at unmount time. 31288c2ecf20Sopenharmony_ci */ 31298c2ecf20Sopenharmony_ci mutex_lock(&sbi->s_orphan_lock); 31308c2ecf20Sopenharmony_ci list_del_init(&EXT4_I(inode)->i_orphan); 31318c2ecf20Sopenharmony_ci mutex_unlock(&sbi->s_orphan_lock); 31328c2ecf20Sopenharmony_ci } 31338c2ecf20Sopenharmony_ci } else 31348c2ecf20Sopenharmony_ci brelse(iloc.bh); 31358c2ecf20Sopenharmony_ci 31368c2ecf20Sopenharmony_ci jbd_debug(4, "superblock will point to %lu\n", inode->i_ino); 31378c2ecf20Sopenharmony_ci jbd_debug(4, "orphan inode %lu will point to %d\n", 31388c2ecf20Sopenharmony_ci inode->i_ino, NEXT_ORPHAN(inode)); 31398c2ecf20Sopenharmony_ciout: 31408c2ecf20Sopenharmony_ci ext4_std_error(sb, err); 31418c2ecf20Sopenharmony_ci return err; 31428c2ecf20Sopenharmony_ci} 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci/* 31458c2ecf20Sopenharmony_ci * ext4_orphan_del() removes an unlinked or truncated inode from the list 31468c2ecf20Sopenharmony_ci * of such inodes stored on disk, because it is finally being cleaned up. 31478c2ecf20Sopenharmony_ci */ 31488c2ecf20Sopenharmony_ciint ext4_orphan_del(handle_t *handle, struct inode *inode) 31498c2ecf20Sopenharmony_ci{ 31508c2ecf20Sopenharmony_ci struct list_head *prev; 31518c2ecf20Sopenharmony_ci struct ext4_inode_info *ei = EXT4_I(inode); 31528c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); 31538c2ecf20Sopenharmony_ci __u32 ino_next; 31548c2ecf20Sopenharmony_ci struct ext4_iloc iloc; 31558c2ecf20Sopenharmony_ci int err = 0; 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci if (!sbi->s_journal && !(sbi->s_mount_state & EXT4_ORPHAN_FS)) 31588c2ecf20Sopenharmony_ci return 0; 31598c2ecf20Sopenharmony_ci 31608c2ecf20Sopenharmony_ci WARN_ON_ONCE(!(inode->i_state & (I_NEW | I_FREEING)) && 31618c2ecf20Sopenharmony_ci !inode_is_locked(inode)); 31628c2ecf20Sopenharmony_ci /* Do this quick check before taking global s_orphan_lock. */ 31638c2ecf20Sopenharmony_ci if (list_empty(&ei->i_orphan)) 31648c2ecf20Sopenharmony_ci return 0; 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci if (handle) { 31678c2ecf20Sopenharmony_ci /* Grab inode buffer early before taking global s_orphan_lock */ 31688c2ecf20Sopenharmony_ci err = ext4_reserve_inode_write(handle, inode, &iloc); 31698c2ecf20Sopenharmony_ci } 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_ci mutex_lock(&sbi->s_orphan_lock); 31728c2ecf20Sopenharmony_ci jbd_debug(4, "remove inode %lu from orphan list\n", inode->i_ino); 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci prev = ei->i_orphan.prev; 31758c2ecf20Sopenharmony_ci list_del_init(&ei->i_orphan); 31768c2ecf20Sopenharmony_ci 31778c2ecf20Sopenharmony_ci /* If we're on an error path, we may not have a valid 31788c2ecf20Sopenharmony_ci * transaction handle with which to update the orphan list on 31798c2ecf20Sopenharmony_ci * disk, but we still need to remove the inode from the linked 31808c2ecf20Sopenharmony_ci * list in memory. */ 31818c2ecf20Sopenharmony_ci if (!handle || err) { 31828c2ecf20Sopenharmony_ci mutex_unlock(&sbi->s_orphan_lock); 31838c2ecf20Sopenharmony_ci goto out_err; 31848c2ecf20Sopenharmony_ci } 31858c2ecf20Sopenharmony_ci 31868c2ecf20Sopenharmony_ci ino_next = NEXT_ORPHAN(inode); 31878c2ecf20Sopenharmony_ci if (prev == &sbi->s_orphan) { 31888c2ecf20Sopenharmony_ci jbd_debug(4, "superblock will point to %u\n", ino_next); 31898c2ecf20Sopenharmony_ci BUFFER_TRACE(sbi->s_sbh, "get_write_access"); 31908c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, sbi->s_sbh); 31918c2ecf20Sopenharmony_ci if (err) { 31928c2ecf20Sopenharmony_ci mutex_unlock(&sbi->s_orphan_lock); 31938c2ecf20Sopenharmony_ci goto out_brelse; 31948c2ecf20Sopenharmony_ci } 31958c2ecf20Sopenharmony_ci lock_buffer(sbi->s_sbh); 31968c2ecf20Sopenharmony_ci sbi->s_es->s_last_orphan = cpu_to_le32(ino_next); 31978c2ecf20Sopenharmony_ci ext4_superblock_csum_set(inode->i_sb); 31988c2ecf20Sopenharmony_ci unlock_buffer(sbi->s_sbh); 31998c2ecf20Sopenharmony_ci mutex_unlock(&sbi->s_orphan_lock); 32008c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh); 32018c2ecf20Sopenharmony_ci } else { 32028c2ecf20Sopenharmony_ci struct ext4_iloc iloc2; 32038c2ecf20Sopenharmony_ci struct inode *i_prev = 32048c2ecf20Sopenharmony_ci &list_entry(prev, struct ext4_inode_info, i_orphan)->vfs_inode; 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_ci jbd_debug(4, "orphan inode %lu will point to %u\n", 32078c2ecf20Sopenharmony_ci i_prev->i_ino, ino_next); 32088c2ecf20Sopenharmony_ci err = ext4_reserve_inode_write(handle, i_prev, &iloc2); 32098c2ecf20Sopenharmony_ci if (err) { 32108c2ecf20Sopenharmony_ci mutex_unlock(&sbi->s_orphan_lock); 32118c2ecf20Sopenharmony_ci goto out_brelse; 32128c2ecf20Sopenharmony_ci } 32138c2ecf20Sopenharmony_ci NEXT_ORPHAN(i_prev) = ino_next; 32148c2ecf20Sopenharmony_ci err = ext4_mark_iloc_dirty(handle, i_prev, &iloc2); 32158c2ecf20Sopenharmony_ci mutex_unlock(&sbi->s_orphan_lock); 32168c2ecf20Sopenharmony_ci } 32178c2ecf20Sopenharmony_ci if (err) 32188c2ecf20Sopenharmony_ci goto out_brelse; 32198c2ecf20Sopenharmony_ci NEXT_ORPHAN(inode) = 0; 32208c2ecf20Sopenharmony_ci err = ext4_mark_iloc_dirty(handle, inode, &iloc); 32218c2ecf20Sopenharmony_ciout_err: 32228c2ecf20Sopenharmony_ci ext4_std_error(inode->i_sb, err); 32238c2ecf20Sopenharmony_ci return err; 32248c2ecf20Sopenharmony_ci 32258c2ecf20Sopenharmony_ciout_brelse: 32268c2ecf20Sopenharmony_ci brelse(iloc.bh); 32278c2ecf20Sopenharmony_ci goto out_err; 32288c2ecf20Sopenharmony_ci} 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_cistatic int ext4_rmdir(struct inode *dir, struct dentry *dentry) 32318c2ecf20Sopenharmony_ci{ 32328c2ecf20Sopenharmony_ci int retval; 32338c2ecf20Sopenharmony_ci struct inode *inode; 32348c2ecf20Sopenharmony_ci struct buffer_head *bh; 32358c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 32368c2ecf20Sopenharmony_ci handle_t *handle = NULL; 32378c2ecf20Sopenharmony_ci 32388c2ecf20Sopenharmony_ci if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb)))) 32398c2ecf20Sopenharmony_ci return -EIO; 32408c2ecf20Sopenharmony_ci 32418c2ecf20Sopenharmony_ci /* Initialize quotas before so that eventual writes go in 32428c2ecf20Sopenharmony_ci * separate transaction */ 32438c2ecf20Sopenharmony_ci retval = dquot_initialize(dir); 32448c2ecf20Sopenharmony_ci if (retval) 32458c2ecf20Sopenharmony_ci return retval; 32468c2ecf20Sopenharmony_ci retval = dquot_initialize(d_inode(dentry)); 32478c2ecf20Sopenharmony_ci if (retval) 32488c2ecf20Sopenharmony_ci return retval; 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_ci retval = -ENOENT; 32518c2ecf20Sopenharmony_ci bh = ext4_find_entry(dir, &dentry->d_name, &de, NULL); 32528c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 32538c2ecf20Sopenharmony_ci return PTR_ERR(bh); 32548c2ecf20Sopenharmony_ci if (!bh) 32558c2ecf20Sopenharmony_ci goto end_rmdir; 32568c2ecf20Sopenharmony_ci 32578c2ecf20Sopenharmony_ci inode = d_inode(dentry); 32588c2ecf20Sopenharmony_ci 32598c2ecf20Sopenharmony_ci retval = -EFSCORRUPTED; 32608c2ecf20Sopenharmony_ci if (le32_to_cpu(de->inode) != inode->i_ino) 32618c2ecf20Sopenharmony_ci goto end_rmdir; 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_ci retval = -ENOTEMPTY; 32648c2ecf20Sopenharmony_ci if (!ext4_empty_dir(inode)) 32658c2ecf20Sopenharmony_ci goto end_rmdir; 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_ci handle = ext4_journal_start(dir, EXT4_HT_DIR, 32688c2ecf20Sopenharmony_ci EXT4_DATA_TRANS_BLOCKS(dir->i_sb)); 32698c2ecf20Sopenharmony_ci if (IS_ERR(handle)) { 32708c2ecf20Sopenharmony_ci retval = PTR_ERR(handle); 32718c2ecf20Sopenharmony_ci handle = NULL; 32728c2ecf20Sopenharmony_ci goto end_rmdir; 32738c2ecf20Sopenharmony_ci } 32748c2ecf20Sopenharmony_ci 32758c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) 32768c2ecf20Sopenharmony_ci ext4_handle_sync(handle); 32778c2ecf20Sopenharmony_ci 32788c2ecf20Sopenharmony_ci retval = ext4_delete_entry(handle, dir, de, bh); 32798c2ecf20Sopenharmony_ci if (retval) 32808c2ecf20Sopenharmony_ci goto end_rmdir; 32818c2ecf20Sopenharmony_ci if (!EXT4_DIR_LINK_EMPTY(inode)) 32828c2ecf20Sopenharmony_ci ext4_warning_inode(inode, 32838c2ecf20Sopenharmony_ci "empty directory '%.*s' has too many links (%u)", 32848c2ecf20Sopenharmony_ci dentry->d_name.len, dentry->d_name.name, 32858c2ecf20Sopenharmony_ci inode->i_nlink); 32868c2ecf20Sopenharmony_ci inode_inc_iversion(inode); 32878c2ecf20Sopenharmony_ci clear_nlink(inode); 32888c2ecf20Sopenharmony_ci /* There's no need to set i_disksize: the fact that i_nlink is 32898c2ecf20Sopenharmony_ci * zero will ensure that the right thing happens during any 32908c2ecf20Sopenharmony_ci * recovery. */ 32918c2ecf20Sopenharmony_ci inode->i_size = 0; 32928c2ecf20Sopenharmony_ci ext4_orphan_add(handle, inode); 32938c2ecf20Sopenharmony_ci inode->i_ctime = dir->i_ctime = dir->i_mtime = current_time(inode); 32948c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, inode); 32958c2ecf20Sopenharmony_ci if (retval) 32968c2ecf20Sopenharmony_ci goto end_rmdir; 32978c2ecf20Sopenharmony_ci ext4_dec_count(dir); 32988c2ecf20Sopenharmony_ci ext4_update_dx_flag(dir); 32998c2ecf20Sopenharmony_ci ext4_fc_track_unlink(handle, dentry); 33008c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, dir); 33018c2ecf20Sopenharmony_ci 33028c2ecf20Sopenharmony_ci#ifdef CONFIG_UNICODE 33038c2ecf20Sopenharmony_ci /* VFS negative dentries are incompatible with Encoding and 33048c2ecf20Sopenharmony_ci * Case-insensitiveness. Eventually we'll want avoid 33058c2ecf20Sopenharmony_ci * invalidating the dentries here, alongside with returning the 33068c2ecf20Sopenharmony_ci * negative dentries at ext4_lookup(), when it is better 33078c2ecf20Sopenharmony_ci * supported by the VFS for the CI case. 33088c2ecf20Sopenharmony_ci */ 33098c2ecf20Sopenharmony_ci if (IS_CASEFOLDED(dir)) 33108c2ecf20Sopenharmony_ci d_invalidate(dentry); 33118c2ecf20Sopenharmony_ci#endif 33128c2ecf20Sopenharmony_ci 33138c2ecf20Sopenharmony_ciend_rmdir: 33148c2ecf20Sopenharmony_ci brelse(bh); 33158c2ecf20Sopenharmony_ci if (handle) 33168c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 33178c2ecf20Sopenharmony_ci return retval; 33188c2ecf20Sopenharmony_ci} 33198c2ecf20Sopenharmony_ci 33208c2ecf20Sopenharmony_ciint __ext4_unlink(struct inode *dir, const struct qstr *d_name, 33218c2ecf20Sopenharmony_ci struct inode *inode, 33228c2ecf20Sopenharmony_ci struct dentry *dentry /* NULL during fast_commit recovery */) 33238c2ecf20Sopenharmony_ci{ 33248c2ecf20Sopenharmony_ci int retval = -ENOENT; 33258c2ecf20Sopenharmony_ci struct buffer_head *bh; 33268c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 33278c2ecf20Sopenharmony_ci handle_t *handle; 33288c2ecf20Sopenharmony_ci int skip_remove_dentry = 0; 33298c2ecf20Sopenharmony_ci 33308c2ecf20Sopenharmony_ci /* 33318c2ecf20Sopenharmony_ci * Keep this outside the transaction; it may have to set up the 33328c2ecf20Sopenharmony_ci * directory's encryption key, which isn't GFP_NOFS-safe. 33338c2ecf20Sopenharmony_ci */ 33348c2ecf20Sopenharmony_ci bh = ext4_find_entry(dir, d_name, &de, NULL); 33358c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 33368c2ecf20Sopenharmony_ci return PTR_ERR(bh); 33378c2ecf20Sopenharmony_ci 33388c2ecf20Sopenharmony_ci if (!bh) 33398c2ecf20Sopenharmony_ci return -ENOENT; 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_ci if (le32_to_cpu(de->inode) != inode->i_ino) { 33428c2ecf20Sopenharmony_ci /* 33438c2ecf20Sopenharmony_ci * It's okay if we find dont find dentry which matches 33448c2ecf20Sopenharmony_ci * the inode. That's because it might have gotten 33458c2ecf20Sopenharmony_ci * renamed to a different inode number 33468c2ecf20Sopenharmony_ci */ 33478c2ecf20Sopenharmony_ci if (EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY) 33488c2ecf20Sopenharmony_ci skip_remove_dentry = 1; 33498c2ecf20Sopenharmony_ci else 33508c2ecf20Sopenharmony_ci goto out_bh; 33518c2ecf20Sopenharmony_ci } 33528c2ecf20Sopenharmony_ci 33538c2ecf20Sopenharmony_ci handle = ext4_journal_start(dir, EXT4_HT_DIR, 33548c2ecf20Sopenharmony_ci EXT4_DATA_TRANS_BLOCKS(dir->i_sb)); 33558c2ecf20Sopenharmony_ci if (IS_ERR(handle)) { 33568c2ecf20Sopenharmony_ci retval = PTR_ERR(handle); 33578c2ecf20Sopenharmony_ci goto out_bh; 33588c2ecf20Sopenharmony_ci } 33598c2ecf20Sopenharmony_ci 33608c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) 33618c2ecf20Sopenharmony_ci ext4_handle_sync(handle); 33628c2ecf20Sopenharmony_ci 33638c2ecf20Sopenharmony_ci if (!skip_remove_dentry) { 33648c2ecf20Sopenharmony_ci retval = ext4_delete_entry(handle, dir, de, bh); 33658c2ecf20Sopenharmony_ci if (retval) 33668c2ecf20Sopenharmony_ci goto out_handle; 33678c2ecf20Sopenharmony_ci dir->i_ctime = dir->i_mtime = current_time(dir); 33688c2ecf20Sopenharmony_ci ext4_update_dx_flag(dir); 33698c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, dir); 33708c2ecf20Sopenharmony_ci if (retval) 33718c2ecf20Sopenharmony_ci goto out_handle; 33728c2ecf20Sopenharmony_ci } else { 33738c2ecf20Sopenharmony_ci retval = 0; 33748c2ecf20Sopenharmony_ci } 33758c2ecf20Sopenharmony_ci if (inode->i_nlink == 0) 33768c2ecf20Sopenharmony_ci ext4_warning_inode(inode, "Deleting file '%.*s' with no links", 33778c2ecf20Sopenharmony_ci d_name->len, d_name->name); 33788c2ecf20Sopenharmony_ci else 33798c2ecf20Sopenharmony_ci drop_nlink(inode); 33808c2ecf20Sopenharmony_ci if (!inode->i_nlink) 33818c2ecf20Sopenharmony_ci ext4_orphan_add(handle, inode); 33828c2ecf20Sopenharmony_ci inode->i_ctime = current_time(inode); 33838c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, inode); 33848c2ecf20Sopenharmony_ci if (dentry && !retval) 33858c2ecf20Sopenharmony_ci ext4_fc_track_unlink(handle, dentry); 33868c2ecf20Sopenharmony_ciout_handle: 33878c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 33888c2ecf20Sopenharmony_ciout_bh: 33898c2ecf20Sopenharmony_ci brelse(bh); 33908c2ecf20Sopenharmony_ci return retval; 33918c2ecf20Sopenharmony_ci} 33928c2ecf20Sopenharmony_ci 33938c2ecf20Sopenharmony_cistatic int ext4_unlink(struct inode *dir, struct dentry *dentry) 33948c2ecf20Sopenharmony_ci{ 33958c2ecf20Sopenharmony_ci int retval; 33968c2ecf20Sopenharmony_ci 33978c2ecf20Sopenharmony_ci if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb)))) 33988c2ecf20Sopenharmony_ci return -EIO; 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_ci trace_ext4_unlink_enter(dir, dentry); 34018c2ecf20Sopenharmony_ci /* 34028c2ecf20Sopenharmony_ci * Initialize quotas before so that eventual writes go 34038c2ecf20Sopenharmony_ci * in separate transaction 34048c2ecf20Sopenharmony_ci */ 34058c2ecf20Sopenharmony_ci retval = dquot_initialize(dir); 34068c2ecf20Sopenharmony_ci if (retval) 34078c2ecf20Sopenharmony_ci goto out_trace; 34088c2ecf20Sopenharmony_ci retval = dquot_initialize(d_inode(dentry)); 34098c2ecf20Sopenharmony_ci if (retval) 34108c2ecf20Sopenharmony_ci goto out_trace; 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci retval = __ext4_unlink(dir, &dentry->d_name, d_inode(dentry), dentry); 34138c2ecf20Sopenharmony_ci#ifdef CONFIG_UNICODE 34148c2ecf20Sopenharmony_ci /* VFS negative dentries are incompatible with Encoding and 34158c2ecf20Sopenharmony_ci * Case-insensitiveness. Eventually we'll want avoid 34168c2ecf20Sopenharmony_ci * invalidating the dentries here, alongside with returning the 34178c2ecf20Sopenharmony_ci * negative dentries at ext4_lookup(), when it is better 34188c2ecf20Sopenharmony_ci * supported by the VFS for the CI case. 34198c2ecf20Sopenharmony_ci */ 34208c2ecf20Sopenharmony_ci if (IS_CASEFOLDED(dir)) 34218c2ecf20Sopenharmony_ci d_invalidate(dentry); 34228c2ecf20Sopenharmony_ci#endif 34238c2ecf20Sopenharmony_ci 34248c2ecf20Sopenharmony_ciout_trace: 34258c2ecf20Sopenharmony_ci trace_ext4_unlink_exit(dentry, retval); 34268c2ecf20Sopenharmony_ci return retval; 34278c2ecf20Sopenharmony_ci} 34288c2ecf20Sopenharmony_ci 34298c2ecf20Sopenharmony_cistatic int ext4_symlink(struct inode *dir, 34308c2ecf20Sopenharmony_ci struct dentry *dentry, const char *symname) 34318c2ecf20Sopenharmony_ci{ 34328c2ecf20Sopenharmony_ci handle_t *handle; 34338c2ecf20Sopenharmony_ci struct inode *inode; 34348c2ecf20Sopenharmony_ci int err, len = strlen(symname); 34358c2ecf20Sopenharmony_ci int credits; 34368c2ecf20Sopenharmony_ci struct fscrypt_str disk_link; 34378c2ecf20Sopenharmony_ci 34388c2ecf20Sopenharmony_ci if (unlikely(ext4_forced_shutdown(EXT4_SB(dir->i_sb)))) 34398c2ecf20Sopenharmony_ci return -EIO; 34408c2ecf20Sopenharmony_ci 34418c2ecf20Sopenharmony_ci err = fscrypt_prepare_symlink(dir, symname, len, dir->i_sb->s_blocksize, 34428c2ecf20Sopenharmony_ci &disk_link); 34438c2ecf20Sopenharmony_ci if (err) 34448c2ecf20Sopenharmony_ci return err; 34458c2ecf20Sopenharmony_ci 34468c2ecf20Sopenharmony_ci err = dquot_initialize(dir); 34478c2ecf20Sopenharmony_ci if (err) 34488c2ecf20Sopenharmony_ci return err; 34498c2ecf20Sopenharmony_ci 34508c2ecf20Sopenharmony_ci if ((disk_link.len > EXT4_N_BLOCKS * 4)) { 34518c2ecf20Sopenharmony_ci /* 34528c2ecf20Sopenharmony_ci * For non-fast symlinks, we just allocate inode and put it on 34538c2ecf20Sopenharmony_ci * orphan list in the first transaction => we need bitmap, 34548c2ecf20Sopenharmony_ci * group descriptor, sb, inode block, quota blocks, and 34558c2ecf20Sopenharmony_ci * possibly selinux xattr blocks. 34568c2ecf20Sopenharmony_ci */ 34578c2ecf20Sopenharmony_ci credits = 4 + EXT4_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) + 34588c2ecf20Sopenharmony_ci EXT4_XATTR_TRANS_BLOCKS; 34598c2ecf20Sopenharmony_ci } else { 34608c2ecf20Sopenharmony_ci /* 34618c2ecf20Sopenharmony_ci * Fast symlink. We have to add entry to directory 34628c2ecf20Sopenharmony_ci * (EXT4_DATA_TRANS_BLOCKS + EXT4_INDEX_EXTRA_TRANS_BLOCKS), 34638c2ecf20Sopenharmony_ci * allocate new inode (bitmap, group descriptor, inode block, 34648c2ecf20Sopenharmony_ci * quota blocks, sb is already counted in previous macros). 34658c2ecf20Sopenharmony_ci */ 34668c2ecf20Sopenharmony_ci credits = EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + 34678c2ecf20Sopenharmony_ci EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3; 34688c2ecf20Sopenharmony_ci } 34698c2ecf20Sopenharmony_ci 34708c2ecf20Sopenharmony_ci inode = ext4_new_inode_start_handle(dir, S_IFLNK|S_IRWXUGO, 34718c2ecf20Sopenharmony_ci &dentry->d_name, 0, NULL, 34728c2ecf20Sopenharmony_ci EXT4_HT_DIR, credits); 34738c2ecf20Sopenharmony_ci handle = ext4_journal_current_handle(); 34748c2ecf20Sopenharmony_ci if (IS_ERR(inode)) { 34758c2ecf20Sopenharmony_ci if (handle) 34768c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 34778c2ecf20Sopenharmony_ci return PTR_ERR(inode); 34788c2ecf20Sopenharmony_ci } 34798c2ecf20Sopenharmony_ci 34808c2ecf20Sopenharmony_ci if (IS_ENCRYPTED(inode)) { 34818c2ecf20Sopenharmony_ci err = fscrypt_encrypt_symlink(inode, symname, len, &disk_link); 34828c2ecf20Sopenharmony_ci if (err) 34838c2ecf20Sopenharmony_ci goto err_drop_inode; 34848c2ecf20Sopenharmony_ci inode->i_op = &ext4_encrypted_symlink_inode_operations; 34858c2ecf20Sopenharmony_ci } 34868c2ecf20Sopenharmony_ci 34878c2ecf20Sopenharmony_ci if ((disk_link.len > EXT4_N_BLOCKS * 4)) { 34888c2ecf20Sopenharmony_ci if (!IS_ENCRYPTED(inode)) 34898c2ecf20Sopenharmony_ci inode->i_op = &ext4_symlink_inode_operations; 34908c2ecf20Sopenharmony_ci inode_nohighmem(inode); 34918c2ecf20Sopenharmony_ci ext4_set_aops(inode); 34928c2ecf20Sopenharmony_ci /* 34938c2ecf20Sopenharmony_ci * We cannot call page_symlink() with transaction started 34948c2ecf20Sopenharmony_ci * because it calls into ext4_write_begin() which can wait 34958c2ecf20Sopenharmony_ci * for transaction commit if we are running out of space 34968c2ecf20Sopenharmony_ci * and thus we deadlock. So we have to stop transaction now 34978c2ecf20Sopenharmony_ci * and restart it when symlink contents is written. 34988c2ecf20Sopenharmony_ci * 34998c2ecf20Sopenharmony_ci * To keep fs consistent in case of crash, we have to put inode 35008c2ecf20Sopenharmony_ci * to orphan list in the mean time. 35018c2ecf20Sopenharmony_ci */ 35028c2ecf20Sopenharmony_ci drop_nlink(inode); 35038c2ecf20Sopenharmony_ci err = ext4_orphan_add(handle, inode); 35048c2ecf20Sopenharmony_ci if (handle) 35058c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 35068c2ecf20Sopenharmony_ci handle = NULL; 35078c2ecf20Sopenharmony_ci if (err) 35088c2ecf20Sopenharmony_ci goto err_drop_inode; 35098c2ecf20Sopenharmony_ci err = __page_symlink(inode, disk_link.name, disk_link.len, 1); 35108c2ecf20Sopenharmony_ci if (err) 35118c2ecf20Sopenharmony_ci goto err_drop_inode; 35128c2ecf20Sopenharmony_ci /* 35138c2ecf20Sopenharmony_ci * Now inode is being linked into dir (EXT4_DATA_TRANS_BLOCKS 35148c2ecf20Sopenharmony_ci * + EXT4_INDEX_EXTRA_TRANS_BLOCKS), inode is also modified 35158c2ecf20Sopenharmony_ci */ 35168c2ecf20Sopenharmony_ci handle = ext4_journal_start(dir, EXT4_HT_DIR, 35178c2ecf20Sopenharmony_ci EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + 35188c2ecf20Sopenharmony_ci EXT4_INDEX_EXTRA_TRANS_BLOCKS + 1); 35198c2ecf20Sopenharmony_ci if (IS_ERR(handle)) { 35208c2ecf20Sopenharmony_ci err = PTR_ERR(handle); 35218c2ecf20Sopenharmony_ci handle = NULL; 35228c2ecf20Sopenharmony_ci goto err_drop_inode; 35238c2ecf20Sopenharmony_ci } 35248c2ecf20Sopenharmony_ci set_nlink(inode, 1); 35258c2ecf20Sopenharmony_ci err = ext4_orphan_del(handle, inode); 35268c2ecf20Sopenharmony_ci if (err) 35278c2ecf20Sopenharmony_ci goto err_drop_inode; 35288c2ecf20Sopenharmony_ci } else { 35298c2ecf20Sopenharmony_ci /* clear the extent format for fast symlink */ 35308c2ecf20Sopenharmony_ci ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS); 35318c2ecf20Sopenharmony_ci if (!IS_ENCRYPTED(inode)) { 35328c2ecf20Sopenharmony_ci inode->i_op = &ext4_fast_symlink_inode_operations; 35338c2ecf20Sopenharmony_ci inode->i_link = (char *)&EXT4_I(inode)->i_data; 35348c2ecf20Sopenharmony_ci } 35358c2ecf20Sopenharmony_ci memcpy((char *)&EXT4_I(inode)->i_data, disk_link.name, 35368c2ecf20Sopenharmony_ci disk_link.len); 35378c2ecf20Sopenharmony_ci inode->i_size = disk_link.len - 1; 35388c2ecf20Sopenharmony_ci } 35398c2ecf20Sopenharmony_ci EXT4_I(inode)->i_disksize = inode->i_size; 35408c2ecf20Sopenharmony_ci err = ext4_add_nondir(handle, dentry, &inode); 35418c2ecf20Sopenharmony_ci if (handle) 35428c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 35438c2ecf20Sopenharmony_ci if (inode) 35448c2ecf20Sopenharmony_ci iput(inode); 35458c2ecf20Sopenharmony_ci goto out_free_encrypted_link; 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_cierr_drop_inode: 35488c2ecf20Sopenharmony_ci if (handle) 35498c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 35508c2ecf20Sopenharmony_ci clear_nlink(inode); 35518c2ecf20Sopenharmony_ci unlock_new_inode(inode); 35528c2ecf20Sopenharmony_ci iput(inode); 35538c2ecf20Sopenharmony_ciout_free_encrypted_link: 35548c2ecf20Sopenharmony_ci if (disk_link.name != (unsigned char *)symname) 35558c2ecf20Sopenharmony_ci kfree(disk_link.name); 35568c2ecf20Sopenharmony_ci return err; 35578c2ecf20Sopenharmony_ci} 35588c2ecf20Sopenharmony_ci 35598c2ecf20Sopenharmony_ciint __ext4_link(struct inode *dir, struct inode *inode, struct dentry *dentry) 35608c2ecf20Sopenharmony_ci{ 35618c2ecf20Sopenharmony_ci handle_t *handle; 35628c2ecf20Sopenharmony_ci int err, retries = 0; 35638c2ecf20Sopenharmony_ciretry: 35648c2ecf20Sopenharmony_ci handle = ext4_journal_start(dir, EXT4_HT_DIR, 35658c2ecf20Sopenharmony_ci (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + 35668c2ecf20Sopenharmony_ci EXT4_INDEX_EXTRA_TRANS_BLOCKS) + 1); 35678c2ecf20Sopenharmony_ci if (IS_ERR(handle)) 35688c2ecf20Sopenharmony_ci return PTR_ERR(handle); 35698c2ecf20Sopenharmony_ci 35708c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) 35718c2ecf20Sopenharmony_ci ext4_handle_sync(handle); 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_ci inode->i_ctime = current_time(inode); 35748c2ecf20Sopenharmony_ci ext4_inc_count(inode); 35758c2ecf20Sopenharmony_ci ihold(inode); 35768c2ecf20Sopenharmony_ci 35778c2ecf20Sopenharmony_ci err = ext4_add_entry(handle, dentry, inode); 35788c2ecf20Sopenharmony_ci if (!err) { 35798c2ecf20Sopenharmony_ci err = ext4_mark_inode_dirty(handle, inode); 35808c2ecf20Sopenharmony_ci /* this can happen only for tmpfile being 35818c2ecf20Sopenharmony_ci * linked the first time 35828c2ecf20Sopenharmony_ci */ 35838c2ecf20Sopenharmony_ci if (inode->i_nlink == 1) 35848c2ecf20Sopenharmony_ci ext4_orphan_del(handle, inode); 35858c2ecf20Sopenharmony_ci d_instantiate(dentry, inode); 35868c2ecf20Sopenharmony_ci ext4_fc_track_link(handle, dentry); 35878c2ecf20Sopenharmony_ci } else { 35888c2ecf20Sopenharmony_ci drop_nlink(inode); 35898c2ecf20Sopenharmony_ci iput(inode); 35908c2ecf20Sopenharmony_ci } 35918c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 35928c2ecf20Sopenharmony_ci if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) 35938c2ecf20Sopenharmony_ci goto retry; 35948c2ecf20Sopenharmony_ci return err; 35958c2ecf20Sopenharmony_ci} 35968c2ecf20Sopenharmony_ci 35978c2ecf20Sopenharmony_cistatic int ext4_link(struct dentry *old_dentry, 35988c2ecf20Sopenharmony_ci struct inode *dir, struct dentry *dentry) 35998c2ecf20Sopenharmony_ci{ 36008c2ecf20Sopenharmony_ci struct inode *inode = d_inode(old_dentry); 36018c2ecf20Sopenharmony_ci int err; 36028c2ecf20Sopenharmony_ci 36038c2ecf20Sopenharmony_ci if (inode->i_nlink >= EXT4_LINK_MAX) 36048c2ecf20Sopenharmony_ci return -EMLINK; 36058c2ecf20Sopenharmony_ci 36068c2ecf20Sopenharmony_ci err = fscrypt_prepare_link(old_dentry, dir, dentry); 36078c2ecf20Sopenharmony_ci if (err) 36088c2ecf20Sopenharmony_ci return err; 36098c2ecf20Sopenharmony_ci 36108c2ecf20Sopenharmony_ci if ((ext4_test_inode_flag(dir, EXT4_INODE_PROJINHERIT)) && 36118c2ecf20Sopenharmony_ci (!projid_eq(EXT4_I(dir)->i_projid, 36128c2ecf20Sopenharmony_ci EXT4_I(old_dentry->d_inode)->i_projid))) 36138c2ecf20Sopenharmony_ci return -EXDEV; 36148c2ecf20Sopenharmony_ci 36158c2ecf20Sopenharmony_ci err = dquot_initialize(dir); 36168c2ecf20Sopenharmony_ci if (err) 36178c2ecf20Sopenharmony_ci return err; 36188c2ecf20Sopenharmony_ci return __ext4_link(dir, inode, dentry); 36198c2ecf20Sopenharmony_ci} 36208c2ecf20Sopenharmony_ci 36218c2ecf20Sopenharmony_ci/* 36228c2ecf20Sopenharmony_ci * Try to find buffer head where contains the parent block. 36238c2ecf20Sopenharmony_ci * It should be the inode block if it is inlined or the 1st block 36248c2ecf20Sopenharmony_ci * if it is a normal dir. 36258c2ecf20Sopenharmony_ci */ 36268c2ecf20Sopenharmony_cistatic struct buffer_head *ext4_get_first_dir_block(handle_t *handle, 36278c2ecf20Sopenharmony_ci struct inode *inode, 36288c2ecf20Sopenharmony_ci int *retval, 36298c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 **parent_de, 36308c2ecf20Sopenharmony_ci int *inlined) 36318c2ecf20Sopenharmony_ci{ 36328c2ecf20Sopenharmony_ci struct buffer_head *bh; 36338c2ecf20Sopenharmony_ci 36348c2ecf20Sopenharmony_ci if (!ext4_has_inline_data(inode)) { 36358c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 36368c2ecf20Sopenharmony_ci unsigned int offset; 36378c2ecf20Sopenharmony_ci 36388c2ecf20Sopenharmony_ci bh = ext4_read_dirblock(inode, 0, EITHER); 36398c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 36408c2ecf20Sopenharmony_ci *retval = PTR_ERR(bh); 36418c2ecf20Sopenharmony_ci return NULL; 36428c2ecf20Sopenharmony_ci } 36438c2ecf20Sopenharmony_ci 36448c2ecf20Sopenharmony_ci de = (struct ext4_dir_entry_2 *) bh->b_data; 36458c2ecf20Sopenharmony_ci if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, 36468c2ecf20Sopenharmony_ci bh->b_size, 0) || 36478c2ecf20Sopenharmony_ci le32_to_cpu(de->inode) != inode->i_ino || 36488c2ecf20Sopenharmony_ci strcmp(".", de->name)) { 36498c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "directory missing '.'"); 36508c2ecf20Sopenharmony_ci brelse(bh); 36518c2ecf20Sopenharmony_ci *retval = -EFSCORRUPTED; 36528c2ecf20Sopenharmony_ci return NULL; 36538c2ecf20Sopenharmony_ci } 36548c2ecf20Sopenharmony_ci offset = ext4_rec_len_from_disk(de->rec_len, 36558c2ecf20Sopenharmony_ci inode->i_sb->s_blocksize); 36568c2ecf20Sopenharmony_ci de = ext4_next_entry(de, inode->i_sb->s_blocksize); 36578c2ecf20Sopenharmony_ci if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, 36588c2ecf20Sopenharmony_ci bh->b_size, offset) || 36598c2ecf20Sopenharmony_ci le32_to_cpu(de->inode) == 0 || strcmp("..", de->name)) { 36608c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(inode, "directory missing '..'"); 36618c2ecf20Sopenharmony_ci brelse(bh); 36628c2ecf20Sopenharmony_ci *retval = -EFSCORRUPTED; 36638c2ecf20Sopenharmony_ci return NULL; 36648c2ecf20Sopenharmony_ci } 36658c2ecf20Sopenharmony_ci *parent_de = de; 36668c2ecf20Sopenharmony_ci 36678c2ecf20Sopenharmony_ci return bh; 36688c2ecf20Sopenharmony_ci } 36698c2ecf20Sopenharmony_ci 36708c2ecf20Sopenharmony_ci *inlined = 1; 36718c2ecf20Sopenharmony_ci return ext4_get_first_inline_block(inode, parent_de, retval); 36728c2ecf20Sopenharmony_ci} 36738c2ecf20Sopenharmony_ci 36748c2ecf20Sopenharmony_cistruct ext4_renament { 36758c2ecf20Sopenharmony_ci struct inode *dir; 36768c2ecf20Sopenharmony_ci struct dentry *dentry; 36778c2ecf20Sopenharmony_ci struct inode *inode; 36788c2ecf20Sopenharmony_ci bool is_dir; 36798c2ecf20Sopenharmony_ci int dir_nlink_delta; 36808c2ecf20Sopenharmony_ci 36818c2ecf20Sopenharmony_ci /* entry for "dentry" */ 36828c2ecf20Sopenharmony_ci struct buffer_head *bh; 36838c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 36848c2ecf20Sopenharmony_ci int inlined; 36858c2ecf20Sopenharmony_ci 36868c2ecf20Sopenharmony_ci /* entry for ".." in inode if it's a directory */ 36878c2ecf20Sopenharmony_ci struct buffer_head *dir_bh; 36888c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *parent_de; 36898c2ecf20Sopenharmony_ci int dir_inlined; 36908c2ecf20Sopenharmony_ci}; 36918c2ecf20Sopenharmony_ci 36928c2ecf20Sopenharmony_cistatic int ext4_rename_dir_prepare(handle_t *handle, struct ext4_renament *ent) 36938c2ecf20Sopenharmony_ci{ 36948c2ecf20Sopenharmony_ci int retval; 36958c2ecf20Sopenharmony_ci 36968c2ecf20Sopenharmony_ci ent->dir_bh = ext4_get_first_dir_block(handle, ent->inode, 36978c2ecf20Sopenharmony_ci &retval, &ent->parent_de, 36988c2ecf20Sopenharmony_ci &ent->dir_inlined); 36998c2ecf20Sopenharmony_ci if (!ent->dir_bh) 37008c2ecf20Sopenharmony_ci return retval; 37018c2ecf20Sopenharmony_ci if (le32_to_cpu(ent->parent_de->inode) != ent->dir->i_ino) 37028c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 37038c2ecf20Sopenharmony_ci BUFFER_TRACE(ent->dir_bh, "get_write_access"); 37048c2ecf20Sopenharmony_ci return ext4_journal_get_write_access(handle, ent->dir_bh); 37058c2ecf20Sopenharmony_ci} 37068c2ecf20Sopenharmony_ci 37078c2ecf20Sopenharmony_cistatic int ext4_rename_dir_finish(handle_t *handle, struct ext4_renament *ent, 37088c2ecf20Sopenharmony_ci unsigned dir_ino) 37098c2ecf20Sopenharmony_ci{ 37108c2ecf20Sopenharmony_ci int retval; 37118c2ecf20Sopenharmony_ci 37128c2ecf20Sopenharmony_ci ent->parent_de->inode = cpu_to_le32(dir_ino); 37138c2ecf20Sopenharmony_ci BUFFER_TRACE(ent->dir_bh, "call ext4_handle_dirty_metadata"); 37148c2ecf20Sopenharmony_ci if (!ent->dir_inlined) { 37158c2ecf20Sopenharmony_ci if (is_dx(ent->inode)) { 37168c2ecf20Sopenharmony_ci retval = ext4_handle_dirty_dx_node(handle, 37178c2ecf20Sopenharmony_ci ent->inode, 37188c2ecf20Sopenharmony_ci ent->dir_bh); 37198c2ecf20Sopenharmony_ci } else { 37208c2ecf20Sopenharmony_ci retval = ext4_handle_dirty_dirblock(handle, ent->inode, 37218c2ecf20Sopenharmony_ci ent->dir_bh); 37228c2ecf20Sopenharmony_ci } 37238c2ecf20Sopenharmony_ci } else { 37248c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, ent->inode); 37258c2ecf20Sopenharmony_ci } 37268c2ecf20Sopenharmony_ci if (retval) { 37278c2ecf20Sopenharmony_ci ext4_std_error(ent->dir->i_sb, retval); 37288c2ecf20Sopenharmony_ci return retval; 37298c2ecf20Sopenharmony_ci } 37308c2ecf20Sopenharmony_ci return 0; 37318c2ecf20Sopenharmony_ci} 37328c2ecf20Sopenharmony_ci 37338c2ecf20Sopenharmony_cistatic int ext4_setent(handle_t *handle, struct ext4_renament *ent, 37348c2ecf20Sopenharmony_ci unsigned ino, unsigned file_type) 37358c2ecf20Sopenharmony_ci{ 37368c2ecf20Sopenharmony_ci int retval, retval2; 37378c2ecf20Sopenharmony_ci 37388c2ecf20Sopenharmony_ci BUFFER_TRACE(ent->bh, "get write access"); 37398c2ecf20Sopenharmony_ci retval = ext4_journal_get_write_access(handle, ent->bh); 37408c2ecf20Sopenharmony_ci if (retval) 37418c2ecf20Sopenharmony_ci return retval; 37428c2ecf20Sopenharmony_ci ent->de->inode = cpu_to_le32(ino); 37438c2ecf20Sopenharmony_ci if (ext4_has_feature_filetype(ent->dir->i_sb)) 37448c2ecf20Sopenharmony_ci ent->de->file_type = file_type; 37458c2ecf20Sopenharmony_ci inode_inc_iversion(ent->dir); 37468c2ecf20Sopenharmony_ci ent->dir->i_ctime = ent->dir->i_mtime = 37478c2ecf20Sopenharmony_ci current_time(ent->dir); 37488c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, ent->dir); 37498c2ecf20Sopenharmony_ci BUFFER_TRACE(ent->bh, "call ext4_handle_dirty_metadata"); 37508c2ecf20Sopenharmony_ci if (!ent->inlined) { 37518c2ecf20Sopenharmony_ci retval2 = ext4_handle_dirty_dirblock(handle, ent->dir, ent->bh); 37528c2ecf20Sopenharmony_ci if (unlikely(retval2)) { 37538c2ecf20Sopenharmony_ci ext4_std_error(ent->dir->i_sb, retval2); 37548c2ecf20Sopenharmony_ci return retval2; 37558c2ecf20Sopenharmony_ci } 37568c2ecf20Sopenharmony_ci } 37578c2ecf20Sopenharmony_ci return retval; 37588c2ecf20Sopenharmony_ci} 37598c2ecf20Sopenharmony_ci 37608c2ecf20Sopenharmony_cistatic void ext4_resetent(handle_t *handle, struct ext4_renament *ent, 37618c2ecf20Sopenharmony_ci unsigned ino, unsigned file_type) 37628c2ecf20Sopenharmony_ci{ 37638c2ecf20Sopenharmony_ci struct ext4_renament old = *ent; 37648c2ecf20Sopenharmony_ci int retval = 0; 37658c2ecf20Sopenharmony_ci 37668c2ecf20Sopenharmony_ci /* 37678c2ecf20Sopenharmony_ci * old->de could have moved from under us during make indexed dir, 37688c2ecf20Sopenharmony_ci * so the old->de may no longer valid and need to find it again 37698c2ecf20Sopenharmony_ci * before reset old inode info. 37708c2ecf20Sopenharmony_ci */ 37718c2ecf20Sopenharmony_ci old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, 37728c2ecf20Sopenharmony_ci &old.inlined); 37738c2ecf20Sopenharmony_ci if (IS_ERR(old.bh)) 37748c2ecf20Sopenharmony_ci retval = PTR_ERR(old.bh); 37758c2ecf20Sopenharmony_ci if (!old.bh) 37768c2ecf20Sopenharmony_ci retval = -ENOENT; 37778c2ecf20Sopenharmony_ci if (retval) { 37788c2ecf20Sopenharmony_ci ext4_std_error(old.dir->i_sb, retval); 37798c2ecf20Sopenharmony_ci return; 37808c2ecf20Sopenharmony_ci } 37818c2ecf20Sopenharmony_ci 37828c2ecf20Sopenharmony_ci ext4_setent(handle, &old, ino, file_type); 37838c2ecf20Sopenharmony_ci brelse(old.bh); 37848c2ecf20Sopenharmony_ci} 37858c2ecf20Sopenharmony_ci 37868c2ecf20Sopenharmony_cistatic int ext4_find_delete_entry(handle_t *handle, struct inode *dir, 37878c2ecf20Sopenharmony_ci const struct qstr *d_name) 37888c2ecf20Sopenharmony_ci{ 37898c2ecf20Sopenharmony_ci int retval = -ENOENT; 37908c2ecf20Sopenharmony_ci struct buffer_head *bh; 37918c2ecf20Sopenharmony_ci struct ext4_dir_entry_2 *de; 37928c2ecf20Sopenharmony_ci 37938c2ecf20Sopenharmony_ci bh = ext4_find_entry(dir, d_name, &de, NULL); 37948c2ecf20Sopenharmony_ci if (IS_ERR(bh)) 37958c2ecf20Sopenharmony_ci return PTR_ERR(bh); 37968c2ecf20Sopenharmony_ci if (bh) { 37978c2ecf20Sopenharmony_ci retval = ext4_delete_entry(handle, dir, de, bh); 37988c2ecf20Sopenharmony_ci brelse(bh); 37998c2ecf20Sopenharmony_ci } 38008c2ecf20Sopenharmony_ci return retval; 38018c2ecf20Sopenharmony_ci} 38028c2ecf20Sopenharmony_ci 38038c2ecf20Sopenharmony_cistatic void ext4_rename_delete(handle_t *handle, struct ext4_renament *ent, 38048c2ecf20Sopenharmony_ci int force_reread) 38058c2ecf20Sopenharmony_ci{ 38068c2ecf20Sopenharmony_ci int retval; 38078c2ecf20Sopenharmony_ci /* 38088c2ecf20Sopenharmony_ci * ent->de could have moved from under us during htree split, so make 38098c2ecf20Sopenharmony_ci * sure that we are deleting the right entry. We might also be pointing 38108c2ecf20Sopenharmony_ci * to a stale entry in the unused part of ent->bh so just checking inum 38118c2ecf20Sopenharmony_ci * and the name isn't enough. 38128c2ecf20Sopenharmony_ci */ 38138c2ecf20Sopenharmony_ci if (le32_to_cpu(ent->de->inode) != ent->inode->i_ino || 38148c2ecf20Sopenharmony_ci ent->de->name_len != ent->dentry->d_name.len || 38158c2ecf20Sopenharmony_ci strncmp(ent->de->name, ent->dentry->d_name.name, 38168c2ecf20Sopenharmony_ci ent->de->name_len) || 38178c2ecf20Sopenharmony_ci force_reread) { 38188c2ecf20Sopenharmony_ci retval = ext4_find_delete_entry(handle, ent->dir, 38198c2ecf20Sopenharmony_ci &ent->dentry->d_name); 38208c2ecf20Sopenharmony_ci } else { 38218c2ecf20Sopenharmony_ci retval = ext4_delete_entry(handle, ent->dir, ent->de, ent->bh); 38228c2ecf20Sopenharmony_ci if (retval == -ENOENT) { 38238c2ecf20Sopenharmony_ci retval = ext4_find_delete_entry(handle, ent->dir, 38248c2ecf20Sopenharmony_ci &ent->dentry->d_name); 38258c2ecf20Sopenharmony_ci } 38268c2ecf20Sopenharmony_ci } 38278c2ecf20Sopenharmony_ci 38288c2ecf20Sopenharmony_ci if (retval) { 38298c2ecf20Sopenharmony_ci ext4_warning_inode(ent->dir, 38308c2ecf20Sopenharmony_ci "Deleting old file: nlink %d, error=%d", 38318c2ecf20Sopenharmony_ci ent->dir->i_nlink, retval); 38328c2ecf20Sopenharmony_ci } 38338c2ecf20Sopenharmony_ci} 38348c2ecf20Sopenharmony_ci 38358c2ecf20Sopenharmony_cistatic void ext4_update_dir_count(handle_t *handle, struct ext4_renament *ent) 38368c2ecf20Sopenharmony_ci{ 38378c2ecf20Sopenharmony_ci if (ent->dir_nlink_delta) { 38388c2ecf20Sopenharmony_ci if (ent->dir_nlink_delta == -1) 38398c2ecf20Sopenharmony_ci ext4_dec_count(ent->dir); 38408c2ecf20Sopenharmony_ci else 38418c2ecf20Sopenharmony_ci ext4_inc_count(ent->dir); 38428c2ecf20Sopenharmony_ci ext4_mark_inode_dirty(handle, ent->dir); 38438c2ecf20Sopenharmony_ci } 38448c2ecf20Sopenharmony_ci} 38458c2ecf20Sopenharmony_ci 38468c2ecf20Sopenharmony_cistatic struct inode *ext4_whiteout_for_rename(struct ext4_renament *ent, 38478c2ecf20Sopenharmony_ci int credits, handle_t **h) 38488c2ecf20Sopenharmony_ci{ 38498c2ecf20Sopenharmony_ci struct inode *wh; 38508c2ecf20Sopenharmony_ci handle_t *handle; 38518c2ecf20Sopenharmony_ci int retries = 0; 38528c2ecf20Sopenharmony_ci 38538c2ecf20Sopenharmony_ci /* 38548c2ecf20Sopenharmony_ci * for inode block, sb block, group summaries, 38558c2ecf20Sopenharmony_ci * and inode bitmap 38568c2ecf20Sopenharmony_ci */ 38578c2ecf20Sopenharmony_ci credits += (EXT4_MAXQUOTAS_TRANS_BLOCKS(ent->dir->i_sb) + 38588c2ecf20Sopenharmony_ci EXT4_XATTR_TRANS_BLOCKS + 4); 38598c2ecf20Sopenharmony_ciretry: 38608c2ecf20Sopenharmony_ci wh = ext4_new_inode_start_handle(ent->dir, S_IFCHR | WHITEOUT_MODE, 38618c2ecf20Sopenharmony_ci &ent->dentry->d_name, 0, NULL, 38628c2ecf20Sopenharmony_ci EXT4_HT_DIR, credits); 38638c2ecf20Sopenharmony_ci 38648c2ecf20Sopenharmony_ci handle = ext4_journal_current_handle(); 38658c2ecf20Sopenharmony_ci if (IS_ERR(wh)) { 38668c2ecf20Sopenharmony_ci if (handle) 38678c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 38688c2ecf20Sopenharmony_ci if (PTR_ERR(wh) == -ENOSPC && 38698c2ecf20Sopenharmony_ci ext4_should_retry_alloc(ent->dir->i_sb, &retries)) 38708c2ecf20Sopenharmony_ci goto retry; 38718c2ecf20Sopenharmony_ci } else { 38728c2ecf20Sopenharmony_ci *h = handle; 38738c2ecf20Sopenharmony_ci init_special_inode(wh, wh->i_mode, WHITEOUT_DEV); 38748c2ecf20Sopenharmony_ci wh->i_op = &ext4_special_inode_operations; 38758c2ecf20Sopenharmony_ci } 38768c2ecf20Sopenharmony_ci return wh; 38778c2ecf20Sopenharmony_ci} 38788c2ecf20Sopenharmony_ci 38798c2ecf20Sopenharmony_ci/* 38808c2ecf20Sopenharmony_ci * Anybody can rename anything with this: the permission checks are left to the 38818c2ecf20Sopenharmony_ci * higher-level routines. 38828c2ecf20Sopenharmony_ci * 38838c2ecf20Sopenharmony_ci * n.b. old_{dentry,inode) refers to the source dentry/inode 38848c2ecf20Sopenharmony_ci * while new_{dentry,inode) refers to the destination dentry/inode 38858c2ecf20Sopenharmony_ci * This comes from rename(const char *oldpath, const char *newpath) 38868c2ecf20Sopenharmony_ci */ 38878c2ecf20Sopenharmony_cistatic int ext4_rename(struct inode *old_dir, struct dentry *old_dentry, 38888c2ecf20Sopenharmony_ci struct inode *new_dir, struct dentry *new_dentry, 38898c2ecf20Sopenharmony_ci unsigned int flags) 38908c2ecf20Sopenharmony_ci{ 38918c2ecf20Sopenharmony_ci handle_t *handle = NULL; 38928c2ecf20Sopenharmony_ci struct ext4_renament old = { 38938c2ecf20Sopenharmony_ci .dir = old_dir, 38948c2ecf20Sopenharmony_ci .dentry = old_dentry, 38958c2ecf20Sopenharmony_ci .inode = d_inode(old_dentry), 38968c2ecf20Sopenharmony_ci }; 38978c2ecf20Sopenharmony_ci struct ext4_renament new = { 38988c2ecf20Sopenharmony_ci .dir = new_dir, 38998c2ecf20Sopenharmony_ci .dentry = new_dentry, 39008c2ecf20Sopenharmony_ci .inode = d_inode(new_dentry), 39018c2ecf20Sopenharmony_ci }; 39028c2ecf20Sopenharmony_ci int force_reread; 39038c2ecf20Sopenharmony_ci int retval; 39048c2ecf20Sopenharmony_ci struct inode *whiteout = NULL; 39058c2ecf20Sopenharmony_ci int credits; 39068c2ecf20Sopenharmony_ci u8 old_file_type; 39078c2ecf20Sopenharmony_ci 39088c2ecf20Sopenharmony_ci if (new.inode && new.inode->i_nlink == 0) { 39098c2ecf20Sopenharmony_ci EXT4_ERROR_INODE(new.inode, 39108c2ecf20Sopenharmony_ci "target of rename is already freed"); 39118c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 39128c2ecf20Sopenharmony_ci } 39138c2ecf20Sopenharmony_ci 39148c2ecf20Sopenharmony_ci if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT)) && 39158c2ecf20Sopenharmony_ci (!projid_eq(EXT4_I(new_dir)->i_projid, 39168c2ecf20Sopenharmony_ci EXT4_I(old_dentry->d_inode)->i_projid))) 39178c2ecf20Sopenharmony_ci return -EXDEV; 39188c2ecf20Sopenharmony_ci 39198c2ecf20Sopenharmony_ci retval = dquot_initialize(old.dir); 39208c2ecf20Sopenharmony_ci if (retval) 39218c2ecf20Sopenharmony_ci return retval; 39228c2ecf20Sopenharmony_ci retval = dquot_initialize(old.inode); 39238c2ecf20Sopenharmony_ci if (retval) 39248c2ecf20Sopenharmony_ci return retval; 39258c2ecf20Sopenharmony_ci retval = dquot_initialize(new.dir); 39268c2ecf20Sopenharmony_ci if (retval) 39278c2ecf20Sopenharmony_ci return retval; 39288c2ecf20Sopenharmony_ci 39298c2ecf20Sopenharmony_ci /* Initialize quotas before so that eventual writes go 39308c2ecf20Sopenharmony_ci * in separate transaction */ 39318c2ecf20Sopenharmony_ci if (new.inode) { 39328c2ecf20Sopenharmony_ci retval = dquot_initialize(new.inode); 39338c2ecf20Sopenharmony_ci if (retval) 39348c2ecf20Sopenharmony_ci return retval; 39358c2ecf20Sopenharmony_ci } 39368c2ecf20Sopenharmony_ci 39378c2ecf20Sopenharmony_ci old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, 39388c2ecf20Sopenharmony_ci &old.inlined); 39398c2ecf20Sopenharmony_ci if (IS_ERR(old.bh)) 39408c2ecf20Sopenharmony_ci return PTR_ERR(old.bh); 39418c2ecf20Sopenharmony_ci 39428c2ecf20Sopenharmony_ci /* 39438c2ecf20Sopenharmony_ci * Check for inode number is _not_ due to possible IO errors. 39448c2ecf20Sopenharmony_ci * We might rmdir the source, keep it as pwd of some process 39458c2ecf20Sopenharmony_ci * and merrily kill the link to whatever was created under the 39468c2ecf20Sopenharmony_ci * same name. Goodbye sticky bit ;-< 39478c2ecf20Sopenharmony_ci */ 39488c2ecf20Sopenharmony_ci retval = -ENOENT; 39498c2ecf20Sopenharmony_ci if (!old.bh || le32_to_cpu(old.de->inode) != old.inode->i_ino) 39508c2ecf20Sopenharmony_ci goto release_bh; 39518c2ecf20Sopenharmony_ci 39528c2ecf20Sopenharmony_ci new.bh = ext4_find_entry(new.dir, &new.dentry->d_name, 39538c2ecf20Sopenharmony_ci &new.de, &new.inlined); 39548c2ecf20Sopenharmony_ci if (IS_ERR(new.bh)) { 39558c2ecf20Sopenharmony_ci retval = PTR_ERR(new.bh); 39568c2ecf20Sopenharmony_ci new.bh = NULL; 39578c2ecf20Sopenharmony_ci goto release_bh; 39588c2ecf20Sopenharmony_ci } 39598c2ecf20Sopenharmony_ci if (new.bh) { 39608c2ecf20Sopenharmony_ci if (!new.inode) { 39618c2ecf20Sopenharmony_ci brelse(new.bh); 39628c2ecf20Sopenharmony_ci new.bh = NULL; 39638c2ecf20Sopenharmony_ci } 39648c2ecf20Sopenharmony_ci } 39658c2ecf20Sopenharmony_ci if (new.inode && !test_opt(new.dir->i_sb, NO_AUTO_DA_ALLOC)) 39668c2ecf20Sopenharmony_ci ext4_alloc_da_blocks(old.inode); 39678c2ecf20Sopenharmony_ci 39688c2ecf20Sopenharmony_ci credits = (2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) + 39698c2ecf20Sopenharmony_ci EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2); 39708c2ecf20Sopenharmony_ci if (!(flags & RENAME_WHITEOUT)) { 39718c2ecf20Sopenharmony_ci handle = ext4_journal_start(old.dir, EXT4_HT_DIR, credits); 39728c2ecf20Sopenharmony_ci if (IS_ERR(handle)) { 39738c2ecf20Sopenharmony_ci retval = PTR_ERR(handle); 39748c2ecf20Sopenharmony_ci goto release_bh; 39758c2ecf20Sopenharmony_ci } 39768c2ecf20Sopenharmony_ci } else { 39778c2ecf20Sopenharmony_ci whiteout = ext4_whiteout_for_rename(&old, credits, &handle); 39788c2ecf20Sopenharmony_ci if (IS_ERR(whiteout)) { 39798c2ecf20Sopenharmony_ci retval = PTR_ERR(whiteout); 39808c2ecf20Sopenharmony_ci goto release_bh; 39818c2ecf20Sopenharmony_ci } 39828c2ecf20Sopenharmony_ci } 39838c2ecf20Sopenharmony_ci 39848c2ecf20Sopenharmony_ci old_file_type = old.de->file_type; 39858c2ecf20Sopenharmony_ci if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir)) 39868c2ecf20Sopenharmony_ci ext4_handle_sync(handle); 39878c2ecf20Sopenharmony_ci 39888c2ecf20Sopenharmony_ci if (S_ISDIR(old.inode->i_mode)) { 39898c2ecf20Sopenharmony_ci if (new.inode) { 39908c2ecf20Sopenharmony_ci retval = -ENOTEMPTY; 39918c2ecf20Sopenharmony_ci if (!ext4_empty_dir(new.inode)) 39928c2ecf20Sopenharmony_ci goto end_rename; 39938c2ecf20Sopenharmony_ci } else { 39948c2ecf20Sopenharmony_ci retval = -EMLINK; 39958c2ecf20Sopenharmony_ci if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir)) 39968c2ecf20Sopenharmony_ci goto end_rename; 39978c2ecf20Sopenharmony_ci } 39988c2ecf20Sopenharmony_ci retval = ext4_rename_dir_prepare(handle, &old); 39998c2ecf20Sopenharmony_ci if (retval) 40008c2ecf20Sopenharmony_ci goto end_rename; 40018c2ecf20Sopenharmony_ci } 40028c2ecf20Sopenharmony_ci /* 40038c2ecf20Sopenharmony_ci * If we're renaming a file within an inline_data dir and adding or 40048c2ecf20Sopenharmony_ci * setting the new dirent causes a conversion from inline_data to 40058c2ecf20Sopenharmony_ci * extents/blockmap, we need to force the dirent delete code to 40068c2ecf20Sopenharmony_ci * re-read the directory, or else we end up trying to delete a dirent 40078c2ecf20Sopenharmony_ci * from what is now the extent tree root (or a block map). 40088c2ecf20Sopenharmony_ci */ 40098c2ecf20Sopenharmony_ci force_reread = (new.dir->i_ino == old.dir->i_ino && 40108c2ecf20Sopenharmony_ci ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA)); 40118c2ecf20Sopenharmony_ci 40128c2ecf20Sopenharmony_ci if (whiteout) { 40138c2ecf20Sopenharmony_ci /* 40148c2ecf20Sopenharmony_ci * Do this before adding a new entry, so the old entry is sure 40158c2ecf20Sopenharmony_ci * to be still pointing to the valid old entry. 40168c2ecf20Sopenharmony_ci */ 40178c2ecf20Sopenharmony_ci retval = ext4_setent(handle, &old, whiteout->i_ino, 40188c2ecf20Sopenharmony_ci EXT4_FT_CHRDEV); 40198c2ecf20Sopenharmony_ci if (retval) 40208c2ecf20Sopenharmony_ci goto end_rename; 40218c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, whiteout); 40228c2ecf20Sopenharmony_ci if (unlikely(retval)) 40238c2ecf20Sopenharmony_ci goto end_rename; 40248c2ecf20Sopenharmony_ci 40258c2ecf20Sopenharmony_ci } 40268c2ecf20Sopenharmony_ci if (!new.bh) { 40278c2ecf20Sopenharmony_ci retval = ext4_add_entry(handle, new.dentry, old.inode); 40288c2ecf20Sopenharmony_ci if (retval) 40298c2ecf20Sopenharmony_ci goto end_rename; 40308c2ecf20Sopenharmony_ci } else { 40318c2ecf20Sopenharmony_ci retval = ext4_setent(handle, &new, 40328c2ecf20Sopenharmony_ci old.inode->i_ino, old_file_type); 40338c2ecf20Sopenharmony_ci if (retval) 40348c2ecf20Sopenharmony_ci goto end_rename; 40358c2ecf20Sopenharmony_ci } 40368c2ecf20Sopenharmony_ci if (force_reread) 40378c2ecf20Sopenharmony_ci force_reread = !ext4_test_inode_flag(new.dir, 40388c2ecf20Sopenharmony_ci EXT4_INODE_INLINE_DATA); 40398c2ecf20Sopenharmony_ci 40408c2ecf20Sopenharmony_ci /* 40418c2ecf20Sopenharmony_ci * Like most other Unix systems, set the ctime for inodes on a 40428c2ecf20Sopenharmony_ci * rename. 40438c2ecf20Sopenharmony_ci */ 40448c2ecf20Sopenharmony_ci old.inode->i_ctime = current_time(old.inode); 40458c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, old.inode); 40468c2ecf20Sopenharmony_ci if (unlikely(retval)) 40478c2ecf20Sopenharmony_ci goto end_rename; 40488c2ecf20Sopenharmony_ci 40498c2ecf20Sopenharmony_ci if (!whiteout) { 40508c2ecf20Sopenharmony_ci /* 40518c2ecf20Sopenharmony_ci * ok, that's it 40528c2ecf20Sopenharmony_ci */ 40538c2ecf20Sopenharmony_ci ext4_rename_delete(handle, &old, force_reread); 40548c2ecf20Sopenharmony_ci } 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_ci if (new.inode) { 40578c2ecf20Sopenharmony_ci ext4_dec_count(new.inode); 40588c2ecf20Sopenharmony_ci new.inode->i_ctime = current_time(new.inode); 40598c2ecf20Sopenharmony_ci } 40608c2ecf20Sopenharmony_ci old.dir->i_ctime = old.dir->i_mtime = current_time(old.dir); 40618c2ecf20Sopenharmony_ci ext4_update_dx_flag(old.dir); 40628c2ecf20Sopenharmony_ci if (old.dir_bh) { 40638c2ecf20Sopenharmony_ci retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino); 40648c2ecf20Sopenharmony_ci if (retval) 40658c2ecf20Sopenharmony_ci goto end_rename; 40668c2ecf20Sopenharmony_ci 40678c2ecf20Sopenharmony_ci ext4_dec_count(old.dir); 40688c2ecf20Sopenharmony_ci if (new.inode) { 40698c2ecf20Sopenharmony_ci /* checked ext4_empty_dir above, can't have another 40708c2ecf20Sopenharmony_ci * parent, ext4_dec_count() won't work for many-linked 40718c2ecf20Sopenharmony_ci * dirs */ 40728c2ecf20Sopenharmony_ci clear_nlink(new.inode); 40738c2ecf20Sopenharmony_ci } else { 40748c2ecf20Sopenharmony_ci ext4_inc_count(new.dir); 40758c2ecf20Sopenharmony_ci ext4_update_dx_flag(new.dir); 40768c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, new.dir); 40778c2ecf20Sopenharmony_ci if (unlikely(retval)) 40788c2ecf20Sopenharmony_ci goto end_rename; 40798c2ecf20Sopenharmony_ci } 40808c2ecf20Sopenharmony_ci } 40818c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, old.dir); 40828c2ecf20Sopenharmony_ci if (unlikely(retval)) 40838c2ecf20Sopenharmony_ci goto end_rename; 40848c2ecf20Sopenharmony_ci 40858c2ecf20Sopenharmony_ci if (S_ISDIR(old.inode->i_mode)) { 40868c2ecf20Sopenharmony_ci /* 40878c2ecf20Sopenharmony_ci * We disable fast commits here that's because the 40888c2ecf20Sopenharmony_ci * replay code is not yet capable of changing dot dot 40898c2ecf20Sopenharmony_ci * dirents in directories. 40908c2ecf20Sopenharmony_ci */ 40918c2ecf20Sopenharmony_ci ext4_fc_mark_ineligible(old.inode->i_sb, 40928c2ecf20Sopenharmony_ci EXT4_FC_REASON_RENAME_DIR); 40938c2ecf20Sopenharmony_ci } else { 40948c2ecf20Sopenharmony_ci if (new.inode) 40958c2ecf20Sopenharmony_ci ext4_fc_track_unlink(handle, new.dentry); 40968c2ecf20Sopenharmony_ci __ext4_fc_track_link(handle, old.inode, new.dentry); 40978c2ecf20Sopenharmony_ci __ext4_fc_track_unlink(handle, old.inode, old.dentry); 40988c2ecf20Sopenharmony_ci if (whiteout) 40998c2ecf20Sopenharmony_ci __ext4_fc_track_create(handle, whiteout, old.dentry); 41008c2ecf20Sopenharmony_ci } 41018c2ecf20Sopenharmony_ci 41028c2ecf20Sopenharmony_ci if (new.inode) { 41038c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, new.inode); 41048c2ecf20Sopenharmony_ci if (unlikely(retval)) 41058c2ecf20Sopenharmony_ci goto end_rename; 41068c2ecf20Sopenharmony_ci if (!new.inode->i_nlink) 41078c2ecf20Sopenharmony_ci ext4_orphan_add(handle, new.inode); 41088c2ecf20Sopenharmony_ci } 41098c2ecf20Sopenharmony_ci retval = 0; 41108c2ecf20Sopenharmony_ci 41118c2ecf20Sopenharmony_ciend_rename: 41128c2ecf20Sopenharmony_ci if (whiteout) { 41138c2ecf20Sopenharmony_ci if (retval) { 41148c2ecf20Sopenharmony_ci ext4_resetent(handle, &old, 41158c2ecf20Sopenharmony_ci old.inode->i_ino, old_file_type); 41168c2ecf20Sopenharmony_ci drop_nlink(whiteout); 41178c2ecf20Sopenharmony_ci ext4_orphan_add(handle, whiteout); 41188c2ecf20Sopenharmony_ci } 41198c2ecf20Sopenharmony_ci unlock_new_inode(whiteout); 41208c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 41218c2ecf20Sopenharmony_ci iput(whiteout); 41228c2ecf20Sopenharmony_ci } else { 41238c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 41248c2ecf20Sopenharmony_ci } 41258c2ecf20Sopenharmony_cirelease_bh: 41268c2ecf20Sopenharmony_ci brelse(old.dir_bh); 41278c2ecf20Sopenharmony_ci brelse(old.bh); 41288c2ecf20Sopenharmony_ci brelse(new.bh); 41298c2ecf20Sopenharmony_ci 41308c2ecf20Sopenharmony_ci return retval; 41318c2ecf20Sopenharmony_ci} 41328c2ecf20Sopenharmony_ci 41338c2ecf20Sopenharmony_cistatic int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry, 41348c2ecf20Sopenharmony_ci struct inode *new_dir, struct dentry *new_dentry) 41358c2ecf20Sopenharmony_ci{ 41368c2ecf20Sopenharmony_ci handle_t *handle = NULL; 41378c2ecf20Sopenharmony_ci struct ext4_renament old = { 41388c2ecf20Sopenharmony_ci .dir = old_dir, 41398c2ecf20Sopenharmony_ci .dentry = old_dentry, 41408c2ecf20Sopenharmony_ci .inode = d_inode(old_dentry), 41418c2ecf20Sopenharmony_ci }; 41428c2ecf20Sopenharmony_ci struct ext4_renament new = { 41438c2ecf20Sopenharmony_ci .dir = new_dir, 41448c2ecf20Sopenharmony_ci .dentry = new_dentry, 41458c2ecf20Sopenharmony_ci .inode = d_inode(new_dentry), 41468c2ecf20Sopenharmony_ci }; 41478c2ecf20Sopenharmony_ci u8 new_file_type; 41488c2ecf20Sopenharmony_ci int retval; 41498c2ecf20Sopenharmony_ci struct timespec64 ctime; 41508c2ecf20Sopenharmony_ci 41518c2ecf20Sopenharmony_ci if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT) && 41528c2ecf20Sopenharmony_ci !projid_eq(EXT4_I(new_dir)->i_projid, 41538c2ecf20Sopenharmony_ci EXT4_I(old_dentry->d_inode)->i_projid)) || 41548c2ecf20Sopenharmony_ci (ext4_test_inode_flag(old_dir, EXT4_INODE_PROJINHERIT) && 41558c2ecf20Sopenharmony_ci !projid_eq(EXT4_I(old_dir)->i_projid, 41568c2ecf20Sopenharmony_ci EXT4_I(new_dentry->d_inode)->i_projid))) 41578c2ecf20Sopenharmony_ci return -EXDEV; 41588c2ecf20Sopenharmony_ci 41598c2ecf20Sopenharmony_ci retval = dquot_initialize(old.dir); 41608c2ecf20Sopenharmony_ci if (retval) 41618c2ecf20Sopenharmony_ci return retval; 41628c2ecf20Sopenharmony_ci retval = dquot_initialize(new.dir); 41638c2ecf20Sopenharmony_ci if (retval) 41648c2ecf20Sopenharmony_ci return retval; 41658c2ecf20Sopenharmony_ci 41668c2ecf20Sopenharmony_ci old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, 41678c2ecf20Sopenharmony_ci &old.de, &old.inlined); 41688c2ecf20Sopenharmony_ci if (IS_ERR(old.bh)) 41698c2ecf20Sopenharmony_ci return PTR_ERR(old.bh); 41708c2ecf20Sopenharmony_ci /* 41718c2ecf20Sopenharmony_ci * Check for inode number is _not_ due to possible IO errors. 41728c2ecf20Sopenharmony_ci * We might rmdir the source, keep it as pwd of some process 41738c2ecf20Sopenharmony_ci * and merrily kill the link to whatever was created under the 41748c2ecf20Sopenharmony_ci * same name. Goodbye sticky bit ;-< 41758c2ecf20Sopenharmony_ci */ 41768c2ecf20Sopenharmony_ci retval = -ENOENT; 41778c2ecf20Sopenharmony_ci if (!old.bh || le32_to_cpu(old.de->inode) != old.inode->i_ino) 41788c2ecf20Sopenharmony_ci goto end_rename; 41798c2ecf20Sopenharmony_ci 41808c2ecf20Sopenharmony_ci new.bh = ext4_find_entry(new.dir, &new.dentry->d_name, 41818c2ecf20Sopenharmony_ci &new.de, &new.inlined); 41828c2ecf20Sopenharmony_ci if (IS_ERR(new.bh)) { 41838c2ecf20Sopenharmony_ci retval = PTR_ERR(new.bh); 41848c2ecf20Sopenharmony_ci new.bh = NULL; 41858c2ecf20Sopenharmony_ci goto end_rename; 41868c2ecf20Sopenharmony_ci } 41878c2ecf20Sopenharmony_ci 41888c2ecf20Sopenharmony_ci /* RENAME_EXCHANGE case: old *and* new must both exist */ 41898c2ecf20Sopenharmony_ci if (!new.bh || le32_to_cpu(new.de->inode) != new.inode->i_ino) 41908c2ecf20Sopenharmony_ci goto end_rename; 41918c2ecf20Sopenharmony_ci 41928c2ecf20Sopenharmony_ci handle = ext4_journal_start(old.dir, EXT4_HT_DIR, 41938c2ecf20Sopenharmony_ci (2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) + 41948c2ecf20Sopenharmony_ci 2 * EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2)); 41958c2ecf20Sopenharmony_ci if (IS_ERR(handle)) { 41968c2ecf20Sopenharmony_ci retval = PTR_ERR(handle); 41978c2ecf20Sopenharmony_ci handle = NULL; 41988c2ecf20Sopenharmony_ci goto end_rename; 41998c2ecf20Sopenharmony_ci } 42008c2ecf20Sopenharmony_ci 42018c2ecf20Sopenharmony_ci if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir)) 42028c2ecf20Sopenharmony_ci ext4_handle_sync(handle); 42038c2ecf20Sopenharmony_ci 42048c2ecf20Sopenharmony_ci if (S_ISDIR(old.inode->i_mode)) { 42058c2ecf20Sopenharmony_ci old.is_dir = true; 42068c2ecf20Sopenharmony_ci retval = ext4_rename_dir_prepare(handle, &old); 42078c2ecf20Sopenharmony_ci if (retval) 42088c2ecf20Sopenharmony_ci goto end_rename; 42098c2ecf20Sopenharmony_ci } 42108c2ecf20Sopenharmony_ci if (S_ISDIR(new.inode->i_mode)) { 42118c2ecf20Sopenharmony_ci new.is_dir = true; 42128c2ecf20Sopenharmony_ci retval = ext4_rename_dir_prepare(handle, &new); 42138c2ecf20Sopenharmony_ci if (retval) 42148c2ecf20Sopenharmony_ci goto end_rename; 42158c2ecf20Sopenharmony_ci } 42168c2ecf20Sopenharmony_ci 42178c2ecf20Sopenharmony_ci /* 42188c2ecf20Sopenharmony_ci * Other than the special case of overwriting a directory, parents' 42198c2ecf20Sopenharmony_ci * nlink only needs to be modified if this is a cross directory rename. 42208c2ecf20Sopenharmony_ci */ 42218c2ecf20Sopenharmony_ci if (old.dir != new.dir && old.is_dir != new.is_dir) { 42228c2ecf20Sopenharmony_ci old.dir_nlink_delta = old.is_dir ? -1 : 1; 42238c2ecf20Sopenharmony_ci new.dir_nlink_delta = -old.dir_nlink_delta; 42248c2ecf20Sopenharmony_ci retval = -EMLINK; 42258c2ecf20Sopenharmony_ci if ((old.dir_nlink_delta > 0 && EXT4_DIR_LINK_MAX(old.dir)) || 42268c2ecf20Sopenharmony_ci (new.dir_nlink_delta > 0 && EXT4_DIR_LINK_MAX(new.dir))) 42278c2ecf20Sopenharmony_ci goto end_rename; 42288c2ecf20Sopenharmony_ci } 42298c2ecf20Sopenharmony_ci 42308c2ecf20Sopenharmony_ci new_file_type = new.de->file_type; 42318c2ecf20Sopenharmony_ci retval = ext4_setent(handle, &new, old.inode->i_ino, old.de->file_type); 42328c2ecf20Sopenharmony_ci if (retval) 42338c2ecf20Sopenharmony_ci goto end_rename; 42348c2ecf20Sopenharmony_ci 42358c2ecf20Sopenharmony_ci retval = ext4_setent(handle, &old, new.inode->i_ino, new_file_type); 42368c2ecf20Sopenharmony_ci if (retval) 42378c2ecf20Sopenharmony_ci goto end_rename; 42388c2ecf20Sopenharmony_ci 42398c2ecf20Sopenharmony_ci /* 42408c2ecf20Sopenharmony_ci * Like most other Unix systems, set the ctime for inodes on a 42418c2ecf20Sopenharmony_ci * rename. 42428c2ecf20Sopenharmony_ci */ 42438c2ecf20Sopenharmony_ci ctime = current_time(old.inode); 42448c2ecf20Sopenharmony_ci old.inode->i_ctime = ctime; 42458c2ecf20Sopenharmony_ci new.inode->i_ctime = ctime; 42468c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, old.inode); 42478c2ecf20Sopenharmony_ci if (unlikely(retval)) 42488c2ecf20Sopenharmony_ci goto end_rename; 42498c2ecf20Sopenharmony_ci retval = ext4_mark_inode_dirty(handle, new.inode); 42508c2ecf20Sopenharmony_ci if (unlikely(retval)) 42518c2ecf20Sopenharmony_ci goto end_rename; 42528c2ecf20Sopenharmony_ci ext4_fc_mark_ineligible(new.inode->i_sb, 42538c2ecf20Sopenharmony_ci EXT4_FC_REASON_CROSS_RENAME); 42548c2ecf20Sopenharmony_ci if (old.dir_bh) { 42558c2ecf20Sopenharmony_ci retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino); 42568c2ecf20Sopenharmony_ci if (retval) 42578c2ecf20Sopenharmony_ci goto end_rename; 42588c2ecf20Sopenharmony_ci } 42598c2ecf20Sopenharmony_ci if (new.dir_bh) { 42608c2ecf20Sopenharmony_ci retval = ext4_rename_dir_finish(handle, &new, old.dir->i_ino); 42618c2ecf20Sopenharmony_ci if (retval) 42628c2ecf20Sopenharmony_ci goto end_rename; 42638c2ecf20Sopenharmony_ci } 42648c2ecf20Sopenharmony_ci ext4_update_dir_count(handle, &old); 42658c2ecf20Sopenharmony_ci ext4_update_dir_count(handle, &new); 42668c2ecf20Sopenharmony_ci retval = 0; 42678c2ecf20Sopenharmony_ci 42688c2ecf20Sopenharmony_ciend_rename: 42698c2ecf20Sopenharmony_ci brelse(old.dir_bh); 42708c2ecf20Sopenharmony_ci brelse(new.dir_bh); 42718c2ecf20Sopenharmony_ci brelse(old.bh); 42728c2ecf20Sopenharmony_ci brelse(new.bh); 42738c2ecf20Sopenharmony_ci if (handle) 42748c2ecf20Sopenharmony_ci ext4_journal_stop(handle); 42758c2ecf20Sopenharmony_ci return retval; 42768c2ecf20Sopenharmony_ci} 42778c2ecf20Sopenharmony_ci 42788c2ecf20Sopenharmony_cistatic int ext4_rename2(struct inode *old_dir, struct dentry *old_dentry, 42798c2ecf20Sopenharmony_ci struct inode *new_dir, struct dentry *new_dentry, 42808c2ecf20Sopenharmony_ci unsigned int flags) 42818c2ecf20Sopenharmony_ci{ 42828c2ecf20Sopenharmony_ci int err; 42838c2ecf20Sopenharmony_ci 42848c2ecf20Sopenharmony_ci if (unlikely(ext4_forced_shutdown(EXT4_SB(old_dir->i_sb)))) 42858c2ecf20Sopenharmony_ci return -EIO; 42868c2ecf20Sopenharmony_ci 42878c2ecf20Sopenharmony_ci if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT)) 42888c2ecf20Sopenharmony_ci return -EINVAL; 42898c2ecf20Sopenharmony_ci 42908c2ecf20Sopenharmony_ci err = fscrypt_prepare_rename(old_dir, old_dentry, new_dir, new_dentry, 42918c2ecf20Sopenharmony_ci flags); 42928c2ecf20Sopenharmony_ci if (err) 42938c2ecf20Sopenharmony_ci return err; 42948c2ecf20Sopenharmony_ci 42958c2ecf20Sopenharmony_ci if (flags & RENAME_EXCHANGE) { 42968c2ecf20Sopenharmony_ci return ext4_cross_rename(old_dir, old_dentry, 42978c2ecf20Sopenharmony_ci new_dir, new_dentry); 42988c2ecf20Sopenharmony_ci } 42998c2ecf20Sopenharmony_ci 43008c2ecf20Sopenharmony_ci return ext4_rename(old_dir, old_dentry, new_dir, new_dentry, flags); 43018c2ecf20Sopenharmony_ci} 43028c2ecf20Sopenharmony_ci 43038c2ecf20Sopenharmony_ci/* 43048c2ecf20Sopenharmony_ci * directories can handle most operations... 43058c2ecf20Sopenharmony_ci */ 43068c2ecf20Sopenharmony_ciconst struct inode_operations ext4_dir_inode_operations = { 43078c2ecf20Sopenharmony_ci .create = ext4_create, 43088c2ecf20Sopenharmony_ci .lookup = ext4_lookup, 43098c2ecf20Sopenharmony_ci .link = ext4_link, 43108c2ecf20Sopenharmony_ci .unlink = ext4_unlink, 43118c2ecf20Sopenharmony_ci .symlink = ext4_symlink, 43128c2ecf20Sopenharmony_ci .mkdir = ext4_mkdir, 43138c2ecf20Sopenharmony_ci .rmdir = ext4_rmdir, 43148c2ecf20Sopenharmony_ci .mknod = ext4_mknod, 43158c2ecf20Sopenharmony_ci .tmpfile = ext4_tmpfile, 43168c2ecf20Sopenharmony_ci .rename = ext4_rename2, 43178c2ecf20Sopenharmony_ci .setattr = ext4_setattr, 43188c2ecf20Sopenharmony_ci .getattr = ext4_getattr, 43198c2ecf20Sopenharmony_ci .listxattr = ext4_listxattr, 43208c2ecf20Sopenharmony_ci .get_acl = ext4_get_acl, 43218c2ecf20Sopenharmony_ci .set_acl = ext4_set_acl, 43228c2ecf20Sopenharmony_ci .fiemap = ext4_fiemap, 43238c2ecf20Sopenharmony_ci}; 43248c2ecf20Sopenharmony_ci 43258c2ecf20Sopenharmony_ciconst struct inode_operations ext4_special_inode_operations = { 43268c2ecf20Sopenharmony_ci .setattr = ext4_setattr, 43278c2ecf20Sopenharmony_ci .getattr = ext4_getattr, 43288c2ecf20Sopenharmony_ci .listxattr = ext4_listxattr, 43298c2ecf20Sopenharmony_ci .get_acl = ext4_get_acl, 43308c2ecf20Sopenharmony_ci .set_acl = ext4_set_acl, 43318c2ecf20Sopenharmony_ci}; 4332