1522 lines · 39.7 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/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/writeback.h>#include <linux/buffer_head.h> /* sync_mapping_buffers */#include <linux/fs_context.h>#include <linux/pseudo_fs.h>#include <linux/fsnotify.h>#include <linux/unicode.h>#include <linux/fscrypt.h> #include <linux/uaccess.h> #include "internal.h" int simple_getattr(struct user_namespace *mnt_userns, const struct path *path,		   struct kstat *stat, u32 request_mask,		   unsigned int query_flags){	struct inode *inode = d_inode(path->dentry);	generic_fillattr(&init_user_ns, 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){	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); const struct dentry_operations simple_dentry_operations = {	.d_delete = always_delete_dentry,};EXPORT_SYMBOL(simple_dentry_operations); /* * 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_sb->s_d_op)		d_set_d_op(dentry, &simple_dentry_operations);	d_add(dentry, NULL);	return NULL;}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 list_head *p,					loff_t count,					struct dentry *last){	struct dentry *dentry = cursor->d_parent, *found = NULL; 	spin_lock(&dentry->d_lock);	while ((p = p->next) != &dentry->d_subdirs) {		struct dentry *d = list_entry(p, struct dentry, d_child);		// 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()) {			list_move(&cursor->d_child, p);			p = &cursor->d_child;			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_subdirs,					    offset - 2, NULL);		spin_lock(&dentry->d_lock);		if (to)			list_move(&cursor->d_child, &to->d_child);		else			list_del_init(&cursor->d_child);		spin_unlock(&dentry->d_lock);		dput(to); 		file->f_pos = offset; 		inode_unlock_shared(dentry->d_inode);	}	return offset;}EXPORT_SYMBOL(dcache_dir_lseek); /* Relationship between i_mode and the DT_xxx types */static inline unsigned char dt_type(struct inode *inode){	return (inode->i_mode >> 12) & 15;} /* * 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 list_head *anchor = &dentry->d_subdirs;	struct dentry *next = NULL;	struct list_head *p; 	if (!dir_emit_dots(file, ctx))		return 0; 	if (ctx->pos == 2)		p = anchor;	else if (!list_empty(&cursor->d_child))		p = &cursor->d_child;	else		return 0; 	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, dt_type(d_inode(next))))			break;		ctx->pos++;		p = &next->d_child;	}	spin_lock(&dentry->d_lock);	if (next)		list_move_tail(&cursor->d_child, &next->d_child);	else		list_del_init(&cursor->d_child);	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); static struct dentry *find_next_child(struct dentry *parent, struct dentry *prev){	struct dentry *child = NULL;	struct list_head *p = prev ? &prev->d_child : &parent->d_subdirs; 	spin_lock(&parent->d_lock);	while ((p = p->next) != &parent->d_subdirs) {		struct dentry *d = container_of(p, struct dentry, d_child);		if (simple_positive(d)) {			spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);			if (simple_positive(d))				child = dget_dlock(d);			spin_unlock(&d->d_lock);			if (likely(child))				break;		}	}	spin_unlock(&parent->d_lock);	dput(prev);	return child;} void simple_recursive_removal(struct dentry *dentry,                              void (*callback)(struct dentry *)){	struct dentry *this = dget(dentry);	while (true) {		struct dentry *victim = NULL, *child;		struct inode *inode = this->d_inode; 		inode_lock(inode);		if (d_is_dir(this))			inode->i_flags |= S_DEAD;		while ((child = find_next_child(this, victim)) == NULL) {			// kill and ascend			// update metadata while it's still locked			inode->i_ctime = current_time(inode);			clear_nlink(inode);			inode_unlock(inode);			victim = this;			this = this->d_parent;			inode = this->d_inode;			inode_lock(inode);			if (simple_positive(victim)) {				d_invalidate(victim);	// avoid lost mounts				if (d_is_dir(victim))					fsnotify_rmdir(inode, victim);				else					fsnotify_unlink(inode, victim);				if (callback)					callback(victim);				dput(victim);		// unpin it			}			if (victim == dentry) {				inode->i_ctime = inode->i_mtime =					current_time(inode);				if (d_is_dir(dentry))					drop_nlink(inode);				inode_unlock(inode);				dput(dentry);				return;			}		}		inode_unlock(inode);		this = child;	}}EXPORT_SYMBOL(simple_recursive_removal); static const struct super_operations simple_super_operations = {	.statfs		= simple_statfs,}; static int pseudo_fs_fill_super(struct super_block *s, struct fs_context *fc){	struct pseudo_fs_context *ctx = fc->fs_private;	struct inode *root; 	s->s_maxbytes = MAX_LFS_FILESIZE;	s->s_blocksize = PAGE_SIZE;	s->s_blocksize_bits = PAGE_SHIFT;	s->s_magic = ctx->magic;	s->s_op = ctx->ops ?: &simple_super_operations;	s->s_xattr = ctx->xattr;	s->s_time_gran = 1;	root = new_inode(s);	if (!root)		return -ENOMEM; 	/*	 * since this is the first inode, make it number 1. New inodes created	 * after this must take care not to collide with it (by passing	 * max_reserved of 1 to iunique).	 */	root->i_ino = 1;	root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;	root->i_atime = root->i_mtime = root->i_ctime = current_time(root);	s->s_root = d_make_root(root);	if (!s->s_root)		return -ENOMEM;	s->s_d_op = ctx->dops;	return 0;} static int pseudo_fs_get_tree(struct fs_context *fc){	return get_tree_nodev(fc, pseudo_fs_fill_super);} static void pseudo_fs_free(struct fs_context *fc){	kfree(fc->fs_private);} static const struct fs_context_operations pseudo_fs_context_ops = {	.free		= pseudo_fs_free,	.get_tree	= pseudo_fs_get_tree,}; /* * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that * will never be mountable) */struct pseudo_fs_context *init_pseudo(struct fs_context *fc,					unsigned long magic){	struct pseudo_fs_context *ctx; 	ctx = kzalloc(sizeof(struct pseudo_fs_context), GFP_KERNEL);	if (likely(ctx)) {		ctx->magic = magic;		fc->fs_private = ctx;		fc->ops = &pseudo_fs_context_ops;		fc->sb_flags |= SB_NOUSER;		fc->global = true;	}	return ctx;}EXPORT_SYMBOL(init_pseudo); int simple_open(struct inode *inode, struct file *file){	if (inode->i_private)		file->private_data = inode->i_private;	return 0;}EXPORT_SYMBOL(simple_open); int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry){	struct inode *inode = d_inode(old_dentry); 	inode->i_ctime = dir->i_ctime = dir->i_mtime = current_time(inode);	inc_nlink(inode);	ihold(inode);	dget(dentry);	d_instantiate(dentry, inode);	return 0;}EXPORT_SYMBOL(simple_link); int simple_empty(struct dentry *dentry){	struct dentry *child;	int ret = 0; 	spin_lock(&dentry->d_lock);	list_for_each_entry(child, &dentry->d_subdirs, d_child) {		spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);		if (simple_positive(child)) {			spin_unlock(&child->d_lock);			goto out;		}		spin_unlock(&child->d_lock);	}	ret = 1;out:	spin_unlock(&dentry->d_lock);	return ret;}EXPORT_SYMBOL(simple_empty); int simple_unlink(struct inode *dir, struct dentry *dentry){	struct inode *inode = d_inode(dentry); 	inode->i_ctime = dir->i_ctime = dir->i_mtime = current_time(inode);	drop_nlink(inode);	dput(dentry);	return 0;}EXPORT_SYMBOL(simple_unlink); int simple_rmdir(struct inode *dir, struct dentry *dentry){	if (!simple_empty(dentry))		return -ENOTEMPTY; 	drop_nlink(d_inode(dentry));	simple_unlink(dir, dentry);	drop_nlink(dir);	return 0;}EXPORT_SYMBOL(simple_rmdir); int simple_rename(struct user_namespace *mnt_userns, struct inode *old_dir,		  struct dentry *old_dentry, struct inode *new_dir,		  struct dentry *new_dentry, unsigned int flags){	struct inode *inode = d_inode(old_dentry);	int they_are_dirs = d_is_dir(old_dentry); 	if (flags & ~RENAME_NOREPLACE)		return -EINVAL; 	if (!simple_empty(new_dentry))		return -ENOTEMPTY; 	if (d_really_is_positive(new_dentry)) {		simple_unlink(new_dir, new_dentry);		if (they_are_dirs) {			drop_nlink(d_inode(new_dentry));			drop_nlink(old_dir);		}	} else if (they_are_dirs) {		drop_nlink(old_dir);		inc_nlink(new_dir);	} 	old_dir->i_ctime = old_dir->i_mtime = new_dir->i_ctime =		new_dir->i_mtime = inode->i_ctime = current_time(old_dir); 	return 0;}EXPORT_SYMBOL(simple_rename); /** * simple_setattr - setattr for simple filesystem * @mnt_userns: user namespace of the target mount * @dentry: dentry * @iattr: iattr structure * * Returns 0 on success, -error on failure. * * simple_setattr is a simple ->setattr implementation without a proper * implementation of size changes. * * It can either be used for in-memory filesystems or special files * on simple regular filesystems.  Anything that needs to change on-disk * or wire state on size changes needs its own setattr method. */int simple_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,		   struct iattr *iattr){	struct inode *inode = d_inode(dentry);

Lines 1–500 of 1522.