brintos

brintos / linux-shallow public Read only

0
0
Text · 15.1 KiB · 2ce1936 Raw
600 lines · c
1// SPDX-License-Identifier: LGPL-2.12/*3 *4 *   vfs operations that deal with io control5 *6 *   Copyright (C) International Business Machines  Corp., 2005,20137 *   Author(s): Steve French (sfrench@us.ibm.com)8 *9 */10 11#include <linux/fs.h>12#include <linux/file.h>13#include <linux/mount.h>14#include <linux/mm.h>15#include <linux/pagemap.h>16#include "cifspdu.h"17#include "cifsglob.h"18#include "cifsproto.h"19#include "cifs_debug.h"20#include "cifsfs.h"21#include "cifs_ioctl.h"22#include "smb2proto.h"23#include "smb2glob.h"24#include <linux/btrfs.h>25 26static long cifs_ioctl_query_info(unsigned int xid, struct file *filep,27				  unsigned long p)28{29	struct inode *inode = file_inode(filep);30	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);31	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);32	struct dentry *dentry = filep->f_path.dentry;33	const unsigned char *path;34	void *page = alloc_dentry_path();35	__le16 *utf16_path = NULL, root_path;36	int rc = 0;37 38	path = build_path_from_dentry(dentry, page);39	if (IS_ERR(path)) {40		free_dentry_path(page);41		return PTR_ERR(path);42	}43 44	cifs_dbg(FYI, "%s %s\n", __func__, path);45 46	if (!path[0]) {47		root_path = 0;48		utf16_path = &root_path;49	} else {50		utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb);51		if (!utf16_path) {52			rc = -ENOMEM;53			goto ici_exit;54		}55	}56 57	if (tcon->ses->server->ops->ioctl_query_info)58		rc = tcon->ses->server->ops->ioctl_query_info(59				xid, tcon, cifs_sb, utf16_path,60				filep->private_data ? 0 : 1, p);61	else62		rc = -EOPNOTSUPP;63 64 ici_exit:65	if (utf16_path != &root_path)66		kfree(utf16_path);67	free_dentry_path(page);68	return rc;69}70 71static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,72			unsigned long srcfd)73{74	int rc;75	struct fd src_file;76	struct inode *src_inode;77 78	cifs_dbg(FYI, "ioctl copychunk range\n");79	/* the destination must be opened for writing */80	if (!(dst_file->f_mode & FMODE_WRITE)) {81		cifs_dbg(FYI, "file target not open for write\n");82		return -EINVAL;83	}84 85	/* check if target volume is readonly and take reference */86	rc = mnt_want_write_file(dst_file);87	if (rc) {88		cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);89		return rc;90	}91 92	src_file = fdget(srcfd);93	if (!fd_file(src_file)) {94		rc = -EBADF;95		goto out_drop_write;96	}97 98	if (fd_file(src_file)->f_op->unlocked_ioctl != cifs_ioctl) {99		rc = -EBADF;100		cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");101		goto out_fput;102	}103 104	src_inode = file_inode(fd_file(src_file));105	rc = -EINVAL;106	if (S_ISDIR(src_inode->i_mode))107		goto out_fput;108 109	rc = cifs_file_copychunk_range(xid, fd_file(src_file), 0, dst_file, 0,110					src_inode->i_size, 0);111	if (rc > 0)112		rc = 0;113out_fput:114	fdput(src_file);115out_drop_write:116	mnt_drop_write_file(dst_file);117	return rc;118}119 120static long smb_mnt_get_tcon_info(struct cifs_tcon *tcon, void __user *arg)121{122	int rc = 0;123	struct smb_mnt_tcon_info tcon_inf;124 125	tcon_inf.tid = tcon->tid;126	tcon_inf.session_id = tcon->ses->Suid;127 128	if (copy_to_user(arg, &tcon_inf, sizeof(struct smb_mnt_tcon_info)))129		rc = -EFAULT;130 131	return rc;132}133 134static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,135				void __user *arg)136{137	int rc = 0;138	struct smb_mnt_fs_info *fsinf;139 140	fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL);141	if (fsinf == NULL)142		return -ENOMEM;143 144	fsinf->version = 1;145	fsinf->protocol_id = tcon->ses->server->vals->protocol_id;146	fsinf->tcon_flags = tcon->Flags;147	fsinf->device_characteristics =148			le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics);149	fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);150	fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes);151	fsinf->max_path_component =152		le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength);153	fsinf->vol_serial_number = tcon->vol_serial_number;154	fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time);155	fsinf->share_flags = tcon->share_flags;156	fsinf->share_caps = le32_to_cpu(tcon->capabilities);157	fsinf->sector_flags = tcon->ss_flags;158	fsinf->optimal_sector_size = tcon->perf_sector_size;159	fsinf->max_bytes_chunk = tcon->max_bytes_chunk;160	fsinf->maximal_access = tcon->maximal_access;161	fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability);162 163	if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info)))164		rc = -EFAULT;165 166	kfree(fsinf);167	return rc;168}169 170static int cifs_shutdown(struct super_block *sb, unsigned long arg)171{172	struct cifs_sb_info *sbi = CIFS_SB(sb);173	struct tcon_link *tlink;174	struct cifs_tcon *tcon;175	__u32 flags;176	int rc;177 178	if (!capable(CAP_SYS_ADMIN))179		return -EPERM;180 181	if (get_user(flags, (__u32 __user *)arg))182		return -EFAULT;183 184	tlink = cifs_sb_tlink(sbi);185	if (IS_ERR(tlink))186		return PTR_ERR(tlink);187	tcon = tlink_tcon(tlink);188 189	trace_smb3_shutdown_enter(flags, tcon->tid);190	if (flags > CIFS_GOING_FLAGS_NOLOGFLUSH) {191		rc = -EINVAL;192		goto shutdown_out_err;193	}194 195	if (cifs_forced_shutdown(sbi))196		goto shutdown_good;197 198	cifs_dbg(VFS, "shut down requested (%d)", flags);199 200	/*201	 * see:202	 *   https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html203	 * for more information and description of original intent of the flags204	 */205	switch (flags) {206	/*207	 * We could add support later for default flag which requires:208	 *     "Flush all dirty data and metadata to disk"209	 * would need to call syncfs or equivalent to flush page cache for210	 * the mount and then issue fsync to server (if nostrictsync not set)211	 */212	case CIFS_GOING_FLAGS_DEFAULT:213		cifs_dbg(FYI, "shutdown with default flag not supported\n");214		rc = -EINVAL;215		goto shutdown_out_err;216	/*217	 * FLAGS_LOGFLUSH is easy since it asks to write out metadata (not218	 * data) but metadata writes are not cached on the client, so can treat219	 * it similarly to NOLOGFLUSH220	 */221	case CIFS_GOING_FLAGS_LOGFLUSH:222	case CIFS_GOING_FLAGS_NOLOGFLUSH:223		sbi->mnt_cifs_flags |= CIFS_MOUNT_SHUTDOWN;224		goto shutdown_good;225	default:226		rc = -EINVAL;227		goto shutdown_out_err;228	}229 230shutdown_good:231	trace_smb3_shutdown_done(flags, tcon->tid);232	cifs_put_tlink(tlink);233	return 0;234shutdown_out_err:235	trace_smb3_shutdown_err(rc, flags, tcon->tid);236	cifs_put_tlink(tlink);237	return rc;238}239 240static int cifs_dump_full_key(struct cifs_tcon *tcon, struct smb3_full_key_debug_info __user *in)241{242	struct smb3_full_key_debug_info out;243	struct cifs_ses *ses;244	int rc = 0;245	bool found = false;246	u8 __user *end;247 248	if (!smb3_encryption_required(tcon)) {249		rc = -EOPNOTSUPP;250		goto out;251	}252 253	/* copy user input into our output buffer */254	if (copy_from_user(&out, in, sizeof(out))) {255		rc = -EINVAL;256		goto out;257	}258 259	if (!out.session_id) {260		/* if ses id is 0, use current user session */261		ses = tcon->ses;262	} else {263		/* otherwise if a session id is given, look for it in all our sessions */264		struct cifs_ses *ses_it = NULL;265		struct TCP_Server_Info *server_it = NULL;266 267		spin_lock(&cifs_tcp_ses_lock);268		list_for_each_entry(server_it, &cifs_tcp_ses_list, tcp_ses_list) {269			list_for_each_entry(ses_it, &server_it->smb_ses_list, smb_ses_list) {270				spin_lock(&ses_it->ses_lock);271				if (ses_it->ses_status != SES_EXITING &&272				    ses_it->Suid == out.session_id) {273					ses = ses_it;274					/*275					 * since we are using the session outside the crit276					 * section, we need to make sure it won't be released277					 * so increment its refcount278					 */279					cifs_smb_ses_inc_refcount(ses);280					spin_unlock(&ses_it->ses_lock);281					found = true;282					goto search_end;283				}284				spin_unlock(&ses_it->ses_lock);285			}286		}287search_end:288		spin_unlock(&cifs_tcp_ses_lock);289		if (!found) {290			rc = -ENOENT;291			goto out;292		}293	}294 295	switch (ses->server->cipher_type) {296	case SMB2_ENCRYPTION_AES128_CCM:297	case SMB2_ENCRYPTION_AES128_GCM:298		out.session_key_length = CIFS_SESS_KEY_SIZE;299		out.server_in_key_length = out.server_out_key_length = SMB3_GCM128_CRYPTKEY_SIZE;300		break;301	case SMB2_ENCRYPTION_AES256_CCM:302	case SMB2_ENCRYPTION_AES256_GCM:303		out.session_key_length = CIFS_SESS_KEY_SIZE;304		out.server_in_key_length = out.server_out_key_length = SMB3_GCM256_CRYPTKEY_SIZE;305		break;306	default:307		rc = -EOPNOTSUPP;308		goto out;309	}310 311	/* check if user buffer is big enough to store all the keys */312	if (out.in_size < sizeof(out) + out.session_key_length + out.server_in_key_length313	    + out.server_out_key_length) {314		rc = -ENOBUFS;315		goto out;316	}317 318	out.session_id = ses->Suid;319	out.cipher_type = le16_to_cpu(ses->server->cipher_type);320 321	/* overwrite user input with our output */322	if (copy_to_user(in, &out, sizeof(out))) {323		rc = -EINVAL;324		goto out;325	}326 327	/* append all the keys at the end of the user buffer */328	end = in->data;329	if (copy_to_user(end, ses->auth_key.response, out.session_key_length)) {330		rc = -EINVAL;331		goto out;332	}333	end += out.session_key_length;334 335	if (copy_to_user(end, ses->smb3encryptionkey, out.server_in_key_length)) {336		rc = -EINVAL;337		goto out;338	}339	end += out.server_in_key_length;340 341	if (copy_to_user(end, ses->smb3decryptionkey, out.server_out_key_length)) {342		rc = -EINVAL;343		goto out;344	}345 346out:347	if (found)348		cifs_put_smb_ses(ses);349	return rc;350}351 352long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)353{354	struct inode *inode = file_inode(filep);355	struct smb3_key_debug_info pkey_inf;356	int rc = -ENOTTY; /* strange error - but the precedent */357	unsigned int xid;358	struct cifsFileInfo *pSMBFile = filep->private_data;359	struct cifs_tcon *tcon;360	struct tcon_link *tlink;361	struct cifs_sb_info *cifs_sb;362	__u64	ExtAttrBits = 0;363#ifdef CONFIG_CIFS_POSIX364#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY365	__u64   caps;366#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */367#endif /* CONFIG_CIFS_POSIX */368 369	xid = get_xid();370 371	cifs_dbg(FYI, "cifs ioctl 0x%x\n", command);372	if (pSMBFile == NULL)373		trace_smb3_ioctl(xid, 0, command);374	else375		trace_smb3_ioctl(xid, pSMBFile->fid.persistent_fid, command);376 377	switch (command) {378		case FS_IOC_GETFLAGS:379			if (pSMBFile == NULL)380				break;381			tcon = tlink_tcon(pSMBFile->tlink);382#ifdef CONFIG_CIFS_POSIX383#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY384			caps = le64_to_cpu(tcon->fsUnixInfo.Capability);385			if (CIFS_UNIX_EXTATTR_CAP & caps) {386				__u64	ExtAttrMask = 0;387				rc = CIFSGetExtAttr(xid, tcon,388						    pSMBFile->fid.netfid,389						    &ExtAttrBits, &ExtAttrMask);390				if (rc == 0)391					rc = put_user(ExtAttrBits &392						FS_FL_USER_VISIBLE,393						(int __user *)arg);394				if (rc != -EOPNOTSUPP)395					break;396			}397#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */398#endif /* CONFIG_CIFS_POSIX */399			rc = 0;400			if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) {401				/* add in the compressed bit */402				ExtAttrBits = FS_COMPR_FL;403				rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,404					      (int __user *)arg);405			}406			break;407		case FS_IOC_SETFLAGS:408			if (pSMBFile == NULL)409				break;410			tcon = tlink_tcon(pSMBFile->tlink);411			/* caps = le64_to_cpu(tcon->fsUnixInfo.Capability); */412 413			if (get_user(ExtAttrBits, (int __user *)arg)) {414				rc = -EFAULT;415				break;416			}417 418			/*419			 * if (CIFS_UNIX_EXTATTR_CAP & caps)420			 *	rc = CIFSSetExtAttr(xid, tcon,421			 *		       pSMBFile->fid.netfid,422			 *		       extAttrBits,423			 *		       &ExtAttrMask);424			 * if (rc != -EOPNOTSUPP)425			 *	break;426			 */427 428			/* Currently only flag we can set is compressed flag */429			if ((ExtAttrBits & FS_COMPR_FL) == 0)430				break;431 432			/* Try to set compress flag */433			if (tcon->ses->server->ops->set_compression) {434				rc = tcon->ses->server->ops->set_compression(435							xid, tcon, pSMBFile);436				cifs_dbg(FYI, "set compress flag rc %d\n", rc);437			}438			break;439		case CIFS_IOC_COPYCHUNK_FILE:440			rc = cifs_ioctl_copychunk(xid, filep, arg);441			break;442		case CIFS_QUERY_INFO:443			rc = cifs_ioctl_query_info(xid, filep, arg);444			break;445		case CIFS_IOC_SET_INTEGRITY:446			if (pSMBFile == NULL)447				break;448			tcon = tlink_tcon(pSMBFile->tlink);449			if (tcon->ses->server->ops->set_integrity)450				rc = tcon->ses->server->ops->set_integrity(xid,451						tcon, pSMBFile);452			else453				rc = -EOPNOTSUPP;454			break;455		case CIFS_IOC_GET_MNT_INFO:456			if (pSMBFile == NULL)457				break;458			tcon = tlink_tcon(pSMBFile->tlink);459			rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);460			break;461		case CIFS_IOC_GET_TCON_INFO:462			cifs_sb = CIFS_SB(inode->i_sb);463			tlink = cifs_sb_tlink(cifs_sb);464			if (IS_ERR(tlink)) {465				rc = PTR_ERR(tlink);466				break;467			}468			tcon = tlink_tcon(tlink);469			rc = smb_mnt_get_tcon_info(tcon, (void __user *)arg);470			cifs_put_tlink(tlink);471			break;472		case CIFS_ENUMERATE_SNAPSHOTS:473			if (pSMBFile == NULL)474				break;475			if (arg == 0) {476				rc = -EINVAL;477				goto cifs_ioc_exit;478			}479			tcon = tlink_tcon(pSMBFile->tlink);480			if (tcon->ses->server->ops->enum_snapshots)481				rc = tcon->ses->server->ops->enum_snapshots(xid, tcon,482						pSMBFile, (void __user *)arg);483			else484				rc = -EOPNOTSUPP;485			break;486		case CIFS_DUMP_KEY:487			/*488			 * Dump encryption keys. This is an old ioctl that only489			 * handles AES-128-{CCM,GCM}.490			 */491			if (!capable(CAP_SYS_ADMIN)) {492				rc = -EACCES;493				break;494			}495 496			cifs_sb = CIFS_SB(inode->i_sb);497			tlink = cifs_sb_tlink(cifs_sb);498			if (IS_ERR(tlink)) {499				rc = PTR_ERR(tlink);500				break;501			}502			tcon = tlink_tcon(tlink);503			if (!smb3_encryption_required(tcon)) {504				rc = -EOPNOTSUPP;505				cifs_put_tlink(tlink);506				break;507			}508			pkey_inf.cipher_type =509				le16_to_cpu(tcon->ses->server->cipher_type);510			pkey_inf.Suid = tcon->ses->Suid;511			memcpy(pkey_inf.auth_key, tcon->ses->auth_key.response,512					16 /* SMB2_NTLMV2_SESSKEY_SIZE */);513			memcpy(pkey_inf.smb3decryptionkey,514			      tcon->ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);515			memcpy(pkey_inf.smb3encryptionkey,516			      tcon->ses->smb3encryptionkey, SMB3_SIGN_KEY_SIZE);517			if (copy_to_user((void __user *)arg, &pkey_inf,518					sizeof(struct smb3_key_debug_info)))519				rc = -EFAULT;520			else521				rc = 0;522			cifs_put_tlink(tlink);523			break;524		case CIFS_DUMP_FULL_KEY:525			/*526			 * Dump encryption keys (handles any key sizes)527			 */528			if (pSMBFile == NULL)529				break;530			if (!capable(CAP_SYS_ADMIN)) {531				rc = -EACCES;532				break;533			}534			cifs_sb = CIFS_SB(inode->i_sb);535			tlink = cifs_sb_tlink(cifs_sb);536			if (IS_ERR(tlink)) {537				rc = PTR_ERR(tlink);538				break;539			}540 541			tcon = tlink_tcon(tlink);542			rc = cifs_dump_full_key(tcon, (void __user *)arg);543			cifs_put_tlink(tlink);544			break;545		case CIFS_IOC_NOTIFY:546			if (!S_ISDIR(inode->i_mode)) {547				/* Notify can only be done on directories */548				rc = -EOPNOTSUPP;549				break;550			}551			cifs_sb = CIFS_SB(inode->i_sb);552			tlink = cifs_sb_tlink(cifs_sb);553			if (IS_ERR(tlink)) {554				rc = PTR_ERR(tlink);555				break;556			}557			tcon = tlink_tcon(tlink);558			if (tcon && tcon->ses->server->ops->notify) {559				rc = tcon->ses->server->ops->notify(xid,560						filep, (void __user *)arg,561						false /* no ret data */);562				cifs_dbg(FYI, "ioctl notify rc %d\n", rc);563			} else564				rc = -EOPNOTSUPP;565			cifs_put_tlink(tlink);566			break;567		case CIFS_IOC_NOTIFY_INFO:568			if (!S_ISDIR(inode->i_mode)) {569				/* Notify can only be done on directories */570				rc = -EOPNOTSUPP;571				break;572			}573			cifs_sb = CIFS_SB(inode->i_sb);574			tlink = cifs_sb_tlink(cifs_sb);575			if (IS_ERR(tlink)) {576				rc = PTR_ERR(tlink);577				break;578			}579			tcon = tlink_tcon(tlink);580			if (tcon && tcon->ses->server->ops->notify) {581				rc = tcon->ses->server->ops->notify(xid,582						filep, (void __user *)arg,583						true /* return details */);584				cifs_dbg(FYI, "ioctl notify info rc %d\n", rc);585			} else586				rc = -EOPNOTSUPP;587			cifs_put_tlink(tlink);588			break;589		case CIFS_IOC_SHUTDOWN:590			rc = cifs_shutdown(inode->i_sb, arg);591			break;592		default:593			cifs_dbg(FYI, "unsupported ioctl\n");594			break;595	}596cifs_ioc_exit:597	free_xid(xid);598	return rc;599}600