1149 lines · 28.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * fs/f2fs/dir.c * * Copyright (c) 2012 Samsung Electronics Co., Ltd. *             http://www.samsung.com/ */#include <linux/unaligned.h>#include <linux/fs.h>#include <linux/f2fs_fs.h>#include <linux/filelock.h>#include <linux/sched/signal.h>#include <linux/unicode.h>#include <linux/fserror.h>#include "f2fs.h"#include "node.h"#include "acl.h"#include "xattr.h"#include <trace/events/f2fs.h> static inline bool f2fs_should_fallback_to_linear(struct inode *dir){	struct f2fs_sb_info *sbi = F2FS_I_SB(dir); 	switch (F2FS_OPTION(sbi).lookup_mode) {	case LOOKUP_PERF:		return false;	case LOOKUP_COMPAT:		return true;	case LOOKUP_AUTO:		return !sb_no_casefold_compat_fallback(sbi->sb);	}	return false;} #if IS_ENABLED(CONFIG_UNICODE)extern struct kmem_cache *f2fs_cf_name_slab;#endif static unsigned long dir_blocks(struct inode *inode){	return ((unsigned long long) (i_size_read(inode) + PAGE_SIZE - 1))							>> PAGE_SHIFT;} static unsigned int dir_buckets(unsigned int level, int dir_level){	if (level + dir_level < MAX_DIR_HASH_DEPTH / 2)		return BIT(level + dir_level);	else		return MAX_DIR_BUCKETS;} static unsigned int bucket_blocks(unsigned int level){	if (level < MAX_DIR_HASH_DEPTH / 2)		return 2;	else		return 4;} #if IS_ENABLED(CONFIG_UNICODE)/* If @dir is casefolded, initialize @fname->cf_name from @fname->usr_fname. */int f2fs_init_casefolded_name(const struct inode *dir,			      struct f2fs_filename *fname){	struct super_block *sb = dir->i_sb;	unsigned char *buf;	int len; 	if (IS_CASEFOLDED(dir) &&	    !name_is_dot_dotdot(fname->usr_fname->name, fname->usr_fname->len)) {		buf = f2fs_kmem_cache_alloc(f2fs_cf_name_slab,					    GFP_NOFS, false, F2FS_SB(sb));		if (!buf)			return -ENOMEM; 		len = utf8_casefold(sb->s_encoding, fname->usr_fname,				    buf, F2FS_NAME_LEN);		if (len <= 0) {			kmem_cache_free(f2fs_cf_name_slab, buf);			if (sb_has_strict_encoding(sb))				return -EINVAL;			/* fall back to treating name as opaque byte sequence */			return 0;		}		fname->cf_name.name = buf;		fname->cf_name.len = len;	} 	return 0;} void f2fs_free_casefolded_name(struct f2fs_filename *fname){	unsigned char *buf = (unsigned char *)fname->cf_name.name; 	if (buf) {		kmem_cache_free(f2fs_cf_name_slab, buf);		fname->cf_name.name = NULL;	}}#endif /* CONFIG_UNICODE */ static int __f2fs_setup_filename(const struct inode *dir,				 const struct fscrypt_name *crypt_name,				 struct f2fs_filename *fname){	int err; 	memset(fname, 0, sizeof(*fname)); 	fname->usr_fname = crypt_name->usr_fname;	fname->disk_name = crypt_name->disk_name;#ifdef CONFIG_FS_ENCRYPTION	fname->crypto_buf = crypt_name->crypto_buf;#endif	if (crypt_name->is_nokey_name) {		/* hash was decoded from the no-key name */		fname->hash = cpu_to_le32(crypt_name->hash);	} else {		err = f2fs_init_casefolded_name(dir, fname);		if (err) {			f2fs_free_filename(fname);			return err;		}		f2fs_hash_filename(dir, fname);	}	return 0;} /* * Prepare to search for @iname in @dir.  This is similar to * fscrypt_setup_filename(), but this also handles computing the casefolded name * and the f2fs dirhash if needed, then packing all the information about this * filename up into a 'struct f2fs_filename'. */int f2fs_setup_filename(struct inode *dir, const struct qstr *iname,			int lookup, struct f2fs_filename *fname){	struct fscrypt_name crypt_name;	int err; 	err = fscrypt_setup_filename(dir, iname, lookup, &crypt_name);	if (err)		return err; 	return __f2fs_setup_filename(dir, &crypt_name, fname);} /* * Prepare to look up @dentry in @dir.  This is similar to * fscrypt_prepare_lookup(), but this also handles computing the casefolded name * and the f2fs dirhash if needed, then packing all the information about this * filename up into a 'struct f2fs_filename'. */int f2fs_prepare_lookup(struct inode *dir, struct dentry *dentry,			struct f2fs_filename *fname){	struct fscrypt_name crypt_name;	int err; 	err = fscrypt_prepare_lookup(dir, dentry, &crypt_name);	if (err)		return err; 	return __f2fs_setup_filename(dir, &crypt_name, fname);} void f2fs_free_filename(struct f2fs_filename *fname){#ifdef CONFIG_FS_ENCRYPTION	kfree(fname->crypto_buf.name);	fname->crypto_buf.name = NULL;#endif	f2fs_free_casefolded_name(fname);} static unsigned long dir_block_index(unsigned int level,				int dir_level, unsigned int idx){	unsigned long i;	unsigned long bidx = 0; 	for (i = 0; i < level; i++)		bidx += mul_u32_u32(dir_buckets(i, dir_level),				    bucket_blocks(i));	bidx += idx * bucket_blocks(level);	return bidx;} static struct f2fs_dir_entry *find_in_block(struct inode *dir,				struct folio *dentry_folio,				const struct f2fs_filename *fname,				int *max_slots,				bool use_hash){	struct f2fs_dentry_block *dentry_blk;	struct f2fs_dentry_ptr d; 	dentry_blk = folio_address(dentry_folio); 	make_dentry_ptr_block(dir, &d, dentry_blk);	return f2fs_find_target_dentry(&d, fname, max_slots, use_hash);} static inline int f2fs_match_name(const struct inode *dir,				   const struct f2fs_filename *fname,				   const u8 *de_name, u32 de_name_len){	struct fscrypt_name f; #if IS_ENABLED(CONFIG_UNICODE)	if (fname->cf_name.name)		return generic_ci_match(dir, fname->usr_fname,					&fname->cf_name,					de_name, de_name_len); #endif	f.usr_fname = fname->usr_fname;	f.disk_name = fname->disk_name;#ifdef CONFIG_FS_ENCRYPTION	f.crypto_buf = fname->crypto_buf;#endif	return fscrypt_match_name(&f, de_name, de_name_len);} struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d,			const struct f2fs_filename *fname, int *max_slots,			bool use_hash){	struct f2fs_dir_entry *de;	unsigned long bit_pos = 0;	int max_len = 0;	int res = 0; 	if (max_slots)		*max_slots = 0;	while (bit_pos < d->max) {		if (!test_bit_le(bit_pos, d->bitmap)) {			bit_pos++;			max_len++;			continue;		} 		de = &d->dentry[bit_pos]; 		if (unlikely(!de->name_len)) {			bit_pos++;			continue;		} 		if (unlikely(le16_to_cpu(de->name_len) > F2FS_NAME_LEN ||			     bit_pos + GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)) >			     d->max))			return ERR_PTR(-EFSCORRUPTED); 		if (!use_hash || de->hash_code == fname->hash) {			res = f2fs_match_name(d->inode, fname,					      d->filename[bit_pos],					      le16_to_cpu(de->name_len));			if (res < 0)				return ERR_PTR(res);			if (res)				goto found;		} 		if (max_slots && max_len > *max_slots)			*max_slots = max_len;		max_len = 0; 		bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));	} 	de = NULL;found:	if (max_slots && max_len > *max_slots)		*max_slots = max_len;	return de;} static struct f2fs_dir_entry *find_in_level(struct inode *dir,					unsigned int level,					const struct f2fs_filename *fname,					struct folio **res_folio,					bool use_hash){	int s = GET_DENTRY_SLOTS(fname->disk_name.len);	unsigned int nbucket, nblock;	unsigned int bidx, end_block, bucket_no;	struct f2fs_dir_entry *de = NULL;	pgoff_t next_pgofs;	bool room = false;	int max_slots; 	nbucket = dir_buckets(level, F2FS_I(dir)->i_dir_level);	nblock = bucket_blocks(level); 	bucket_no = use_hash ? le32_to_cpu(fname->hash) % nbucket : 0; start_find_bucket:	bidx = dir_block_index(level, F2FS_I(dir)->i_dir_level,			       bucket_no);	end_block = bidx + nblock; 	while (bidx < end_block) {		/* no need to allocate new dentry pages to all the indices */		struct folio *dentry_folio;		dentry_folio = f2fs_find_data_folio(dir, bidx, &next_pgofs);		if (IS_ERR(dentry_folio)) {			if (PTR_ERR(dentry_folio) == -ENOENT) {				room = true;				bidx = next_pgofs;				continue;			} else {				*res_folio = dentry_folio;				break;			}		} 		de = find_in_block(dir, dentry_folio, fname, &max_slots, use_hash);		if (IS_ERR(de)) {			f2fs_folio_put(dentry_folio, false);			*res_folio = ERR_CAST(de);			de = NULL;			break;		} else if (de) {			*res_folio = dentry_folio;			break;		} 		if (max_slots >= s)			room = true;		f2fs_folio_put(dentry_folio, false); 		bidx++;	} 	if (de)		return de; 	if (likely(use_hash)) {		if (room && F2FS_I(dir)->chash != fname->hash) {			F2FS_I(dir)->chash = fname->hash;			F2FS_I(dir)->clevel = level;		}	} else if (++bucket_no < nbucket) {		goto start_find_bucket;	}	return NULL;} struct f2fs_dir_entry *__f2fs_find_entry(struct inode *dir,					 const struct f2fs_filename *fname,					 struct folio **res_folio){	unsigned long npages = dir_blocks(dir);	struct f2fs_dir_entry *de = NULL;	unsigned int max_depth;	unsigned int level;	bool use_hash = true; 	*res_folio = NULL; #if IS_ENABLED(CONFIG_UNICODE)start_find_entry:#endif	if (f2fs_has_inline_dentry(dir)) {		de = f2fs_find_in_inline_dir(dir, fname, res_folio, use_hash);		goto out;	} 	if (npages == 0)		goto out; 	max_depth = F2FS_I(dir)->i_current_depth;	if (unlikely(max_depth > MAX_DIR_HASH_DEPTH)) {		f2fs_warn(F2FS_I_SB(dir), "Corrupted max_depth of %llu: %u",			  dir->i_ino, max_depth);		max_depth = MAX_DIR_HASH_DEPTH;		f2fs_i_depth_write(dir, max_depth);	} 	for (level = 0; level < max_depth; level++) {		de = find_in_level(dir, level, fname, res_folio, use_hash);		if (de || IS_ERR(*res_folio))			break;	} out:#if IS_ENABLED(CONFIG_UNICODE)	if (f2fs_should_fallback_to_linear(dir) &&		IS_CASEFOLDED(dir) && !de && use_hash) {		use_hash = false;		goto start_find_entry;	}#endif	/* This is to increase the speed of f2fs_create */	if (!de)		F2FS_I(dir)->task = current;	return de;} /* * Find an entry in the specified directory with the wanted name. * It returns the page where the entry was found (as a parameter - res_page), * and the entry itself. Page is returned mapped and unlocked. * Entry is guaranteed to be valid. */struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir,			const struct qstr *child, struct folio **res_folio){	struct f2fs_dir_entry *de = NULL;	struct f2fs_filename fname;	int err; 	err = f2fs_setup_filename(dir, child, 1, &fname);	if (err) {		if (err == -ENOENT)			*res_folio = NULL;		else			*res_folio = ERR_PTR(err);		return NULL;	} 	de = __f2fs_find_entry(dir, &fname, res_folio); 	f2fs_free_filename(&fname);	return de;} struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct folio **f){	return f2fs_find_entry(dir, &dotdot_name, f);} ino_t f2fs_inode_by_name(struct inode *dir, const struct qstr *qstr,							struct folio **folio){	ino_t res = 0;	struct f2fs_dir_entry *de; 	de = f2fs_find_entry(dir, qstr, folio);	if (de) {		res = le32_to_cpu(de->ino);		f2fs_folio_put(*folio, false);	} 	return res;} void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de,		struct folio *folio, struct inode *inode){	enum page_type type = f2fs_has_inline_dentry(dir) ? NODE : DATA; 	folio_lock(folio);	f2fs_folio_wait_writeback(folio, type, true, true);	de->ino = cpu_to_le32(inode->i_ino);	de->file_type = fs_umode_to_ftype(inode->i_mode);	folio_mark_dirty(folio); 	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));	f2fs_mark_inode_dirty_sync(dir, true);	f2fs_folio_put(folio, true);} static void init_dent_inode(struct inode *dir, struct inode *inode,			    const struct f2fs_filename *fname,			    struct folio *ifolio){	struct f2fs_inode *ri; 	if (!fname) /* tmpfile case? */		return; 	f2fs_folio_wait_writeback(ifolio, NODE, true, true); 	/* copy name info. to this inode folio */	ri = F2FS_INODE(ifolio);	ri->i_namelen = cpu_to_le32(fname->disk_name.len);	memcpy(ri->i_name, fname->disk_name.name, fname->disk_name.len);	if (IS_ENCRYPTED(dir)) {		file_set_enc_name(inode);		/*		 * Roll-forward recovery doesn't have encryption keys available,		 * so it can't compute the dirhash for encrypted+casefolded		 * filenames.  Append it to i_name if possible.  Else, disable		 * roll-forward recovery of the dentry (i.e., make fsync'ing the		 * file force a checkpoint) by setting LOST_PINO.		 */		if (IS_CASEFOLDED(dir)) {			if (fname->disk_name.len + sizeof(f2fs_hash_t) <=			    F2FS_NAME_LEN)				put_unaligned(fname->hash, (f2fs_hash_t *)					&ri->i_name[fname->disk_name.len]);			else				file_lost_pino(inode);		}	}

Lines 1–500 of 1149.