2314 lines · 61.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *	fs/libfs.c *	Library for filesystems writers. */ #include <linux/blkdev.h>#include <linux/export.h>#include <linux/filelock.h>#include <linux/pagemap.h>#include <linux/slab.h>#include <linux/cred.h>#include <linux/mount.h>#include <linux/vfs.h>#include <linux/quotaops.h>#include <linux/mutex.h>#include <linux/namei.h>#include <linux/exportfs.h>#include <linux/iversion.h>#include <linux/writeback.h>#include <linux/fs_context.h>#include <linux/pseudo_fs.h>#include <linux/fsnotify.h>#include <linux/unicode.h>#include <linux/fscrypt.h>#include <linux/pidfs.h> #include <linux/uaccess.h> #include "internal.h" int simple_getattr(struct mnt_idmap *idmap, const struct path *path,		   struct kstat *stat, u32 request_mask,		   unsigned int query_flags){	struct inode *inode = d_inode(path->dentry);	generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);	stat->blocks = inode->i_mapping->nrpages << (PAGE_SHIFT - 9);	return 0;}EXPORT_SYMBOL(simple_getattr); int simple_statfs(struct dentry *dentry, struct kstatfs *buf){	u64 id = huge_encode_dev(dentry->d_sb->s_dev); 	buf->f_fsid = u64_to_fsid(id);	buf->f_type = dentry->d_sb->s_magic;	buf->f_bsize = PAGE_SIZE;	buf->f_namelen = NAME_MAX;	return 0;}EXPORT_SYMBOL(simple_statfs); /* * Retaining negative dentries for an in-memory filesystem just wastes * memory and lookup time: arrange for them to be deleted immediately. */int always_delete_dentry(const struct dentry *dentry){	return 1;}EXPORT_SYMBOL(always_delete_dentry); /* * Lookup the data. This is trivial - if the dentry didn't already * exist, we know it is negative.  Set d_op to delete negative dentries. */struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags){	if (dentry->d_name.len > NAME_MAX)		return ERR_PTR(-ENAMETOOLONG);	if (!dentry->d_op && !(dentry->d_flags & DCACHE_DONTCACHE)) {		spin_lock(&dentry->d_lock);		dentry->d_flags |= DCACHE_DONTCACHE;		spin_unlock(&dentry->d_lock);	}	if (IS_ENABLED(CONFIG_UNICODE) && IS_CASEFOLDED(dir))		return NULL; 	return d_splice_alias(NULL, dentry);}EXPORT_SYMBOL(simple_lookup); int dcache_dir_open(struct inode *inode, struct file *file){	file->private_data = d_alloc_cursor(file->f_path.dentry); 	return file->private_data ? 0 : -ENOMEM;}EXPORT_SYMBOL(dcache_dir_open); int dcache_dir_close(struct inode *inode, struct file *file){	dput(file->private_data);	return 0;}EXPORT_SYMBOL(dcache_dir_close); /* parent is locked at least shared *//* * Returns an element of siblings' list. * We are looking for <count>th positive after <p>; if * found, dentry is grabbed and returned to caller. * If no such element exists, NULL is returned. */static struct dentry *scan_positives(struct dentry *cursor,					struct hlist_node **p,					loff_t count,					struct dentry *last){	struct dentry *dentry = cursor->d_parent, *found = NULL; 	spin_lock(&dentry->d_lock);	while (*p) {		struct dentry *d = hlist_entry(*p, struct dentry, d_sib);		p = &d->d_sib.next;		// we must at least skip cursors, to avoid livelocks		if (d->d_flags & DCACHE_DENTRY_CURSOR)			continue;		if (simple_positive(d) && !--count) {			spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);			if (simple_positive(d))				found = dget_dlock(d);			spin_unlock(&d->d_lock);			if (likely(found))				break;			count = 1;		}		if (need_resched()) {			if (!hlist_unhashed(&cursor->d_sib))				__hlist_del(&cursor->d_sib);			hlist_add_behind(&cursor->d_sib, &d->d_sib);			p = &cursor->d_sib.next;			spin_unlock(&dentry->d_lock);			cond_resched();			spin_lock(&dentry->d_lock);		}	}	spin_unlock(&dentry->d_lock);	dput(last);	return found;} loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence){	struct dentry *dentry = file->f_path.dentry;	switch (whence) {		case 1:			offset += file->f_pos;			fallthrough;		case 0:			if (offset >= 0)				break;			fallthrough;		default:			return -EINVAL;	}	if (offset != file->f_pos) {		struct dentry *cursor = file->private_data;		struct dentry *to = NULL; 		inode_lock_shared(dentry->d_inode); 		if (offset > 2)			to = scan_positives(cursor, &dentry->d_children.first,					    offset - 2, NULL);		spin_lock(&dentry->d_lock);		hlist_del_init(&cursor->d_sib);		if (to)			hlist_add_behind(&cursor->d_sib, &to->d_sib);		spin_unlock(&dentry->d_lock);		dput(to); 		file->f_pos = offset; 		inode_unlock_shared(dentry->d_inode);	}	return offset;}EXPORT_SYMBOL(dcache_dir_lseek); /* * Directory is locked and all positive dentries in it are safe, since * for ramfs-type trees they can't go away without unlink() or rmdir(), * both impossible due to the lock on directory. */ int dcache_readdir(struct file *file, struct dir_context *ctx){	struct dentry *dentry = file->f_path.dentry;	struct dentry *cursor = file->private_data;	struct dentry *next = NULL;	struct hlist_node **p; 	if (!dir_emit_dots(file, ctx))		return 0; 	if (ctx->pos == 2)		p = &dentry->d_children.first;	else		p = &cursor->d_sib.next; 	while ((next = scan_positives(cursor, p, 1, next)) != NULL) {		if (!dir_emit(ctx, next->d_name.name, next->d_name.len,			      d_inode(next)->i_ino,			      fs_umode_to_dtype(d_inode(next)->i_mode)))			break;		ctx->pos++;		p = &next->d_sib.next;	}	spin_lock(&dentry->d_lock);	hlist_del_init(&cursor->d_sib);	if (next)		hlist_add_before(&cursor->d_sib, &next->d_sib);	spin_unlock(&dentry->d_lock);	dput(next); 	return 0;}EXPORT_SYMBOL(dcache_readdir); ssize_t generic_read_dir(struct file *filp, char __user *buf, size_t siz, loff_t *ppos){	return -EISDIR;}EXPORT_SYMBOL(generic_read_dir); const struct file_operations simple_dir_operations = {	.open		= dcache_dir_open,	.release	= dcache_dir_close,	.llseek		= dcache_dir_lseek,	.read		= generic_read_dir,	.iterate_shared	= dcache_readdir,	.fsync		= noop_fsync,};EXPORT_SYMBOL(simple_dir_operations); const struct inode_operations simple_dir_inode_operations = {	.lookup		= simple_lookup,};EXPORT_SYMBOL(simple_dir_inode_operations); /* simple_offset_add() never assigns these to a dentry */enum {	DIR_OFFSET_FIRST	= 2,		/* Find first real entry */	DIR_OFFSET_EOD		= S32_MAX,}; /* simple_offset_add() allocation range */enum {	DIR_OFFSET_MIN		= DIR_OFFSET_FIRST + 1,	DIR_OFFSET_MAX		= DIR_OFFSET_EOD - 1,}; static void offset_set(struct dentry *dentry, long offset){	dentry->d_fsdata = (void *)offset;} static long dentry2offset(struct dentry *dentry){	return (long)dentry->d_fsdata;} static struct lock_class_key simple_offset_lock_class; /** * simple_offset_init - initialize an offset_ctx * @octx: directory offset map to be initialized * */void simple_offset_init(struct offset_ctx *octx){	mt_init_flags(&octx->mt, MT_FLAGS_ALLOC_RANGE);	lockdep_set_class(&octx->mt.ma_lock, &simple_offset_lock_class);	octx->next_offset = DIR_OFFSET_MIN;} /** * simple_offset_add - Add an entry to a directory's offset map * @octx: directory offset ctx to be updated * @dentry: new dentry being added * * Returns zero on success. @octx and the dentry's offset are updated. * Otherwise, a negative errno value is returned. */int simple_offset_add(struct offset_ctx *octx, struct dentry *dentry){	unsigned long offset;	int ret; 	if (dentry2offset(dentry) != 0)		return -EBUSY; 	ret = mtree_alloc_cyclic(&octx->mt, &offset, dentry, DIR_OFFSET_MIN,				 DIR_OFFSET_MAX, &octx->next_offset,				 GFP_KERNEL);	if (unlikely(ret < 0))		return ret == -EBUSY ? -ENOSPC : ret; 	offset_set(dentry, offset);	return 0;} static int simple_offset_replace(struct offset_ctx *octx, struct dentry *dentry,				 long offset){	int ret; 	ret = mtree_store(&octx->mt, offset, dentry, GFP_KERNEL);	if (ret)		return ret;	offset_set(dentry, offset);	return 0;} /** * simple_offset_remove - Remove an entry to a directory's offset map * @octx: directory offset ctx to be updated * @dentry: dentry being removed * */void simple_offset_remove(struct offset_ctx *octx, struct dentry *dentry){	long offset; 	offset = dentry2offset(dentry);	if (offset == 0)		return; 	mtree_erase(&octx->mt, offset);	offset_set(dentry, 0);} /** * simple_offset_rename - handle directory offsets for rename * @old_dir: parent directory of source entry * @old_dentry: dentry of source entry * @new_dir: parent_directory of destination entry * @new_dentry: dentry of destination * * Caller provides appropriate serialization. * * User space expects the directory offset value of the replaced * (new) directory entry to be unchanged after a rename. * * Caller must have grabbed a slot for new_dentry in the maple_tree * associated with new_dir, even if dentry is negative. */void simple_offset_rename(struct inode *old_dir, struct dentry *old_dentry,			  struct inode *new_dir, struct dentry *new_dentry){	struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);	struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);	long new_offset = dentry2offset(new_dentry); 	if (WARN_ON(!new_offset))		return; 	simple_offset_remove(old_ctx, old_dentry);	offset_set(new_dentry, 0);	WARN_ON(simple_offset_replace(new_ctx, old_dentry, new_offset));} /** * simple_offset_rename_exchange - exchange rename with directory offsets * @old_dir: parent of dentry being moved * @old_dentry: dentry being moved * @new_dir: destination parent * @new_dentry: destination dentry * * This API preserves the directory offset values. Caller provides * appropriate serialization. * * Returns zero on success. Otherwise a negative errno is returned and the * rename is rolled back. */int simple_offset_rename_exchange(struct inode *old_dir,				  struct dentry *old_dentry,				  struct inode *new_dir,				  struct dentry *new_dentry){	struct offset_ctx *old_ctx = old_dir->i_op->get_offset_ctx(old_dir);	struct offset_ctx *new_ctx = new_dir->i_op->get_offset_ctx(new_dir);	long old_index = dentry2offset(old_dentry);	long new_index = dentry2offset(new_dentry);	int ret; 	if (WARN_ON(!old_index || !new_index))		return -EINVAL; 	ret = mtree_store(&new_ctx->mt, new_index, old_dentry, GFP_KERNEL);	if (WARN_ON(ret))		return ret; 	ret = mtree_store(&old_ctx->mt, old_index, new_dentry, GFP_KERNEL);	if (WARN_ON(ret)) {		mtree_store(&new_ctx->mt, new_index, new_dentry, GFP_KERNEL);		return ret;	} 	offset_set(old_dentry, new_index);	offset_set(new_dentry, old_index);	simple_rename_exchange(old_dir, old_dentry, new_dir, new_dentry);	return 0;} /** * simple_offset_destroy - Release offset map * @octx: directory offset ctx that is about to be destroyed * * During fs teardown (eg. umount), a directory's offset map might still * contain entries. xa_destroy() cleans out anything that remains. */void simple_offset_destroy(struct offset_ctx *octx){	mtree_destroy(&octx->mt);} /** * offset_dir_llseek - Advance the read position of a directory descriptor * @file: an open directory whose position is to be updated * @offset: a byte offset * @whence: enumerator describing the starting position for this update * * SEEK_END, SEEK_DATA, and SEEK_HOLE are not supported for directories. * * Returns the updated read position if successful; otherwise a * negative errno is returned and the read position remains unchanged. */static loff_t offset_dir_llseek(struct file *file, loff_t offset, int whence){	switch (whence) {	case SEEK_CUR:		offset += file->f_pos;		fallthrough;	case SEEK_SET:		if (offset >= 0)			break;		fallthrough;	default:		return -EINVAL;	} 	return vfs_setpos(file, offset, LONG_MAX);} static struct dentry *find_positive_dentry(struct dentry *parent,					   struct dentry *dentry,					   bool next){	struct dentry *found = NULL; 	spin_lock(&parent->d_lock);	if (next)		dentry = d_next_sibling(dentry);	else if (!dentry)		dentry = d_first_child(parent);	hlist_for_each_entry_from(dentry, d_sib) {		if (!simple_positive(dentry))			continue;		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);		if (simple_positive(dentry))			found = dget_dlock(dentry);		spin_unlock(&dentry->d_lock);		if (likely(found))			break;	}	spin_unlock(&parent->d_lock);	return found;} static noinline_for_stack struct dentry *offset_dir_lookup(struct dentry *parent, loff_t offset){	struct inode *inode = d_inode(parent);	struct offset_ctx *octx = inode->i_op->get_offset_ctx(inode);	struct dentry *child, *found = NULL; 	MA_STATE(mas, &octx->mt, offset, offset); 	if (offset == DIR_OFFSET_FIRST)		found = find_positive_dentry(parent, NULL, false);	else {		rcu_read_lock();		child = mas_find_rev(&mas, DIR_OFFSET_MIN);		found = find_positive_dentry(parent, child, false);		rcu_read_unlock();	}	return found;} static bool offset_dir_emit(struct dir_context *ctx, struct dentry *dentry){	struct inode *inode = d_inode(dentry); 	return dir_emit(ctx, dentry->d_name.name, dentry->d_name.len,			inode->i_ino, fs_umode_to_dtype(inode->i_mode));}

Lines 1–500 of 2314.