2008 lines · 48.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org> * Copyright (C) 2019 Samsung Electronics Co., Ltd. */ #include <linux/fs.h>#include <linux/filelock.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/kthread.h>#include <linux/freezer.h>#include <linux/dcache.h> #include "glob.h"#include "vfs_cache.h"#include "oplock.h"#include "vfs.h"#include "connection.h"#include "misc.h"#include "mgmt/tree_connect.h"#include "mgmt/user_session.h"#include "mgmt/user_config.h"#include "smb_common.h"#include "server.h"#include "smb2pdu.h" #define S_DEL_PENDING			1#define S_DEL_ON_CLS			2#define S_DEL_ON_CLS_STREAM		8 static unsigned int inode_hash_mask __read_mostly;static unsigned int inode_hash_shift __read_mostly;static struct hlist_head *inode_hashtable __read_mostly;static DEFINE_RWLOCK(inode_hash_lock); static struct ksmbd_file_table global_ft;static atomic_long_t fd_limit;static struct kmem_cache *filp_cache; static int ksmbd_mark_fp_closed(struct ksmbd_file *fp); #define OPLOCK_NONE      0#define OPLOCK_EXCLUSIVE 1#define OPLOCK_BATCH     2#define OPLOCK_READ      3  /* level 2 oplock */ #ifdef CONFIG_PROC_FS static const struct ksmbd_const_name ksmbd_lease_const_names[] = {	{le32_to_cpu(SMB2_LEASE_NONE_LE), "LEASE_NONE"},	{le32_to_cpu(SMB2_LEASE_READ_CACHING_LE), "LEASE_R"},	{le32_to_cpu(SMB2_LEASE_HANDLE_CACHING_LE), "LEASE_H"},	{le32_to_cpu(SMB2_LEASE_WRITE_CACHING_LE), "LEASE_W"},	{le32_to_cpu(SMB2_LEASE_READ_CACHING_LE |		     SMB2_LEASE_HANDLE_CACHING_LE), "LEASE_RH"},	{le32_to_cpu(SMB2_LEASE_READ_CACHING_LE |		     SMB2_LEASE_WRITE_CACHING_LE), "LEASE_RW"},	{le32_to_cpu(SMB2_LEASE_HANDLE_CACHING_LE |		     SMB2_LEASE_WRITE_CACHING_LE), "LEASE_WH"},	{le32_to_cpu(SMB2_LEASE_READ_CACHING_LE |		     SMB2_LEASE_HANDLE_CACHING_LE |		     SMB2_LEASE_WRITE_CACHING_LE), "LEASE_RWH"},}; static const struct ksmbd_const_name ksmbd_oplock_const_names[] = {	{SMB2_OPLOCK_LEVEL_NONE, "OPLOCK_NONE"},	{SMB2_OPLOCK_LEVEL_II, "OPLOCK_II"},	{SMB2_OPLOCK_LEVEL_EXCLUSIVE, "OPLOCK_EXCLUSIVE"},	{SMB2_OPLOCK_LEVEL_BATCH, "OPLOCK_BATCH"},}; static const struct ksmbd_const_name ksmbd_file_state_names[] = {	{FP_NEW, "new"},	{FP_INITED, "open"},	{FP_CLOSED, "closed"},}; #define KSMBD_PROC_FILE_DURABLE		BIT(0)#define KSMBD_PROC_FILE_PERSISTENT	BIT(1)#define KSMBD_PROC_FILE_RESILIENT	BIT(2)#define KSMBD_PROC_FILE_DELETE_ON_CLOSE	BIT(3)#define KSMBD_PROC_FILE_STREAM		BIT(4)#define KSMBD_PROC_FILE_POSIX		BIT(5)#define KSMBD_PROC_FILE_ATTRIB_ONLY	BIT(6) static const struct ksmbd_const_name ksmbd_file_flag_names[] = {	{KSMBD_PROC_FILE_DURABLE, "durable"},	{KSMBD_PROC_FILE_PERSISTENT, "persistent"},	{KSMBD_PROC_FILE_RESILIENT, "resilient"},	{KSMBD_PROC_FILE_DELETE_ON_CLOSE, "delete-on-close"},	{KSMBD_PROC_FILE_STREAM, "stream"},	{KSMBD_PROC_FILE_POSIX, "posix"},	{KSMBD_PROC_FILE_ATTRIB_ONLY, "attrib-only"},}; static unsigned int ksmbd_proc_file_flags(struct ksmbd_file *fp){	unsigned int flags = 0; 	if (fp->is_durable)		flags |= KSMBD_PROC_FILE_DURABLE;	if (fp->is_persistent)		flags |= KSMBD_PROC_FILE_PERSISTENT;	if (fp->is_resilient)		flags |= KSMBD_PROC_FILE_RESILIENT;	if (fp->coption & FILE_DELETE_ON_CLOSE_LE)		flags |= KSMBD_PROC_FILE_DELETE_ON_CLOSE;	if (fp->stream.name)		flags |= KSMBD_PROC_FILE_STREAM;	if (fp->is_posix_ctxt)		flags |= KSMBD_PROC_FILE_POSIX;	if (fp->attrib_only)		flags |= KSMBD_PROC_FILE_ATTRIB_ONLY;	return flags;} static int proc_show_files(struct seq_file *m, void *v){	struct ksmbd_file *fp = NULL;	unsigned int id;	struct oplock_info *opinfo; 	read_lock(&global_ft.lock);	idr_for_each_entry(global_ft.idr, fp, id) {		seq_printf(m, "tree_id:\t0x%x\n", fp->tcon ? fp->tcon->id : 0);		seq_printf(m, "persistent_id:\t0x%llx\n", fp->persistent_id);		seq_printf(m, "volatile_id:\t0x%llx\n", fp->volatile_id);		seq_printf(m, "refcount:\t%d\n", atomic_read(&fp->refcount)); 		rcu_read_lock();		opinfo = rcu_dereference(fp->f_opinfo);		if (opinfo) {			const struct ksmbd_const_name *const_names;			const char *name;			int count;			unsigned int level; 			if (opinfo->is_lease) {				const_names = ksmbd_lease_const_names;				count = ARRAY_SIZE(ksmbd_lease_const_names);				level = le32_to_cpu(opinfo->o_lease->state);			} else {				const_names = ksmbd_oplock_const_names;				count = ARRAY_SIZE(ksmbd_oplock_const_names);				level = opinfo->level;			}			rcu_read_unlock();			name = ksmbd_proc_const_name(const_names, count, level);			if (name)				seq_printf(m, "oplock:\t%s\n", name);			else				seq_printf(m, "oplock:\t0x%x\n", level);		} else {			rcu_read_unlock();			seq_puts(m, "oplock:\tnone\n");		} 		seq_printf(m, "state:\t%s\n",			   ksmbd_proc_const_name(ksmbd_file_state_names,						 ARRAY_SIZE(ksmbd_file_state_names),						 fp->f_state));		seq_printf(m, "durable_timeout:\t%u\n", fp->durable_timeout);		seq_printf(m, "create_options:\t0x%08x\n",			   le32_to_cpu(fp->coption));		seq_printf(m, "desired_access:\t0x%08x\n",			   le32_to_cpu(fp->daccess));		seq_printf(m, "share_access:\t0x%08x\n",			   le32_to_cpu(fp->saccess));		seq_puts(m, "flags:\t");		ksmbd_proc_show_flag_names(m, ksmbd_file_flag_names,					   ARRAY_SIZE(ksmbd_file_flag_names),					   ksmbd_proc_file_flags(fp));		seq_printf(m, "\nname:\t%s\n\n",			   fp->filp->f_path.dentry->d_name.name);	}	read_unlock(&global_ft.lock);	return 0;} static int create_proc_files(void){	if (!ksmbd_proc_create("files", proc_show_files, NULL))		return -ENOMEM;	return 0;}#elsestatic int create_proc_files(void) { return 0; }#endif static bool durable_scavenger_running;static DEFINE_MUTEX(durable_scavenger_lock);static wait_queue_head_t dh_wq; bool ksmbd_durable_scavenger_active(void){	bool active; 	mutex_lock(&durable_scavenger_lock);	active = durable_scavenger_running;	mutex_unlock(&durable_scavenger_lock);	return active;} void ksmbd_set_fd_limit(unsigned long limit){	limit = min(limit, get_max_files());	atomic_long_set(&fd_limit, limit);} static bool fd_limit_depleted(void){	long v = atomic_long_dec_return(&fd_limit); 	if (v >= 0)		return false;	atomic_long_inc(&fd_limit);	return true;} static void fd_limit_close(void){	atomic_long_inc(&fd_limit);} /* * INODE hash */ static unsigned long inode_hash(struct super_block *sb, unsigned long hashval){	unsigned long tmp; 	tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) /		L1_CACHE_BYTES;	tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> inode_hash_shift);	return tmp & inode_hash_mask;} static struct ksmbd_inode *__ksmbd_inode_lookup(struct dentry *de){	struct hlist_head *head = inode_hashtable +		inode_hash(d_inode(de)->i_sb, (unsigned long)de);	struct ksmbd_inode *ci = NULL, *ret_ci = NULL; 	hlist_for_each_entry(ci, head, m_hash) {		if (ci->m_de == de) {			if (atomic_inc_not_zero(&ci->m_count))				ret_ci = ci;			break;		}	}	return ret_ci;} static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp){	return __ksmbd_inode_lookup(fp->filp->f_path.dentry);} struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d){	struct ksmbd_inode *ci; 	read_lock(&inode_hash_lock);	ci = __ksmbd_inode_lookup(d);	read_unlock(&inode_hash_lock); 	return ci;} int ksmbd_query_inode_status(struct dentry *dentry){	struct ksmbd_inode *ci;	int ret = KSMBD_INODE_STATUS_UNKNOWN; 	read_lock(&inode_hash_lock);	ci = __ksmbd_inode_lookup(dentry);	read_unlock(&inode_hash_lock);	if (!ci)		return ret; 	down_read(&ci->m_lock);	if (ci->m_flags & S_DEL_PENDING)		ret = KSMBD_INODE_STATUS_PENDING_DELETE;	else		ret = KSMBD_INODE_STATUS_OK;	up_read(&ci->m_lock); 	ksmbd_inode_put(ci);	return ret;} bool ksmbd_inode_pending_delete(struct ksmbd_file *fp){	struct ksmbd_inode *ci = fp->f_ci;	int ret; 	down_read(&ci->m_lock);	ret = (ci->m_flags & S_DEL_PENDING);	up_read(&ci->m_lock);	if (ret || !ksmbd_stream_fd(fp))		return ret; 	spin_lock(&fp->f_lock);	ret = fp->stream_del_pending;	spin_unlock(&fp->f_lock); 	return ret;} void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp){	struct ksmbd_inode *ci = fp->f_ci; 	down_write(&ci->m_lock);	ci->m_flags |= S_DEL_PENDING;	up_write(&ci->m_lock);} void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp){	struct ksmbd_inode *ci = fp->f_ci; 	down_write(&ci->m_lock);	ci->m_flags &= ~S_DEL_PENDING;	up_write(&ci->m_lock);} bool ksmbd_has_stream_without_delete_share(struct ksmbd_file *fp){	struct ksmbd_file *prev_fp;	struct ksmbd_inode *ci = fp->f_ci;	bool ret = false; 	if (ksmbd_stream_fd(fp))		return false; 	down_read(&ci->m_lock);	list_for_each_entry(prev_fp, &ci->m_fp_list, node) {		if (prev_fp == fp || !ksmbd_stream_fd(prev_fp))			continue; 		if (file_inode(fp->filp) != file_inode(prev_fp->filp))			continue; 		if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE)) {			ret = true;			break;		}	}	up_read(&ci->m_lock); 	return ret;} void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,				  int file_info){	struct ksmbd_inode *ci = fp->f_ci; 	down_write(&ci->m_lock);	if (ksmbd_stream_fd(fp))		ci->m_flags |= S_DEL_ON_CLS_STREAM;	else		ci->m_flags |= S_DEL_ON_CLS;	up_write(&ci->m_lock);} /* * FileDispositionInformation (SET_INFO) on a stream handle must only * mark the stream for deletion, not the whole file -- otherwise * deleting a single alternate data stream (e.g. AFP_AfpInfo) deletes * the entire file's data along with it. * * This is tracked on fp itself (stream_del_pending), not the shared * ksmbd_inode: the inode-wide S_DEL_ON_CLS_STREAM flag used by * ksmbd_fd_set_delete_on_close() can't record *which* stream should be * deleted, so if a different stream handle on the same file closed * first, it would delete the wrong stream. */void ksmbd_fd_set_delete_pending(struct ksmbd_file *fp){	if (ksmbd_stream_fd(fp)) {		spin_lock(&fp->f_lock);		fp->stream_del_pending = true;		spin_unlock(&fp->f_lock);	} else {		ksmbd_set_inode_pending_delete(fp);	}} void ksmbd_fd_clear_delete_pending(struct ksmbd_file *fp){	if (ksmbd_stream_fd(fp)) {		spin_lock(&fp->f_lock);		fp->stream_del_pending = false;		spin_unlock(&fp->f_lock);	} else {		ksmbd_clear_inode_pending_delete(fp);	}} static void ksmbd_inode_hash(struct ksmbd_inode *ci){	struct hlist_head *b = inode_hashtable +		inode_hash(d_inode(ci->m_de)->i_sb, (unsigned long)ci->m_de); 	hlist_add_head(&ci->m_hash, b);} static void ksmbd_inode_unhash(struct ksmbd_inode *ci){	write_lock(&inode_hash_lock);	hlist_del_init(&ci->m_hash);	write_unlock(&inode_hash_lock);} static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp){	atomic_set(&ci->m_count, 1);	atomic_set(&ci->op_count, 0);	atomic_set(&ci->sop_count, 0);	ci->m_flags = 0;	ci->m_fattr = 0;	INIT_LIST_HEAD(&ci->m_fp_list);	INIT_LIST_HEAD(&ci->m_op_list);	init_rwsem(&ci->m_lock);	ci->m_de = fp->filp->f_path.dentry;	return 0;} static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp){	struct ksmbd_inode *ci, *tmpci;	int rc; 	read_lock(&inode_hash_lock);	ci = ksmbd_inode_lookup(fp);	read_unlock(&inode_hash_lock);	if (ci)		return ci; 	ci = kmalloc_obj(struct ksmbd_inode, KSMBD_DEFAULT_GFP);	if (!ci)		return NULL; 	rc = ksmbd_inode_init(ci, fp);	if (rc) {		pr_err("inode initialized failed\n");		kfree(ci);		return NULL;	} 	write_lock(&inode_hash_lock);	tmpci = ksmbd_inode_lookup(fp);	if (!tmpci) {		ksmbd_inode_hash(ci);	} else {		kfree(ci);		ci = tmpci;	}	write_unlock(&inode_hash_lock);	return ci;} static void ksmbd_inode_free(struct ksmbd_inode *ci){	ksmbd_inode_unhash(ci);	kfree(ci);} void ksmbd_inode_put(struct ksmbd_inode *ci){	if (atomic_dec_and_test(&ci->m_count))		ksmbd_inode_free(ci);} int __init ksmbd_inode_hash_init(void){	unsigned int loop;	unsigned long numentries = 16384;	unsigned long bucketsize = sizeof(struct hlist_head);	unsigned long size; 	inode_hash_shift = ilog2(numentries);	inode_hash_mask = (1 << inode_hash_shift) - 1; 	size = bucketsize << inode_hash_shift; 	/* init master fp hash table */	inode_hashtable = vmalloc(size);	if (!inode_hashtable)		return -ENOMEM; 	for (loop = 0; loop < (1U << inode_hash_shift); loop++)		INIT_HLIST_HEAD(&inode_hashtable[loop]);	return 0;} 

Lines 1–500 of 2008.