brintos

brintos / linux-shallow public Read only

0
0
Text · 15.3 KiB · 47ddeb7 Raw
650 lines · c
1// SPDX-License-Identifier: LGPL-2.12/*3 *4 *   Copyright (C) International Business Machines  Corp., 2002,20085 *   Author(s): Steve French (sfrench@us.ibm.com)6 *7 */8#include <linux/fs.h>9#include <linux/stat.h>10#include <linux/slab.h>11#include <linux/namei.h>12#include "cifsfs.h"13#include "cifspdu.h"14#include "cifsglob.h"15#include "cifsproto.h"16#include "cifs_debug.h"17#include "cifs_fs_sb.h"18#include "cifs_unicode.h"19#include "smb2proto.h"20#include "cifs_ioctl.h"21 22/*23 * M-F Symlink Functions - Begin24 */25 26#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1)27#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1))28#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1))29#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024)30#define CIFS_MF_SYMLINK_FILE_SIZE \31	(CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN)32 33#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n"34#define CIFS_MF_SYMLINK_MD5_FORMAT "%16phN\n"35#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) md5_hash36 37static int38symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)39{40	int rc;41	struct shash_desc *md5 = NULL;42 43	rc = cifs_alloc_hash("md5", &md5);44	if (rc)45		return rc;46 47	rc = crypto_shash_digest(md5, link_str, link_len, md5_hash);48	if (rc)49		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);50	cifs_free_hash(&md5);51	return rc;52}53 54static int55parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len,56		 char **_link_str)57{58	int rc;59	unsigned int link_len;60	const char *md5_str1;61	const char *link_str;62	u8 md5_hash[16];63	char md5_str2[34];64 65	if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)66		return -EINVAL;67 68	md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET];69	link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET];70 71	rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len);72	if (rc != 1)73		return -EINVAL;74 75	if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)76		return -EINVAL;77 78	rc = symlink_hash(link_len, link_str, md5_hash);79	if (rc) {80		cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);81		return rc;82	}83 84	scnprintf(md5_str2, sizeof(md5_str2),85		  CIFS_MF_SYMLINK_MD5_FORMAT,86		  CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));87 88	if (strncmp(md5_str1, md5_str2, 17) != 0)89		return -EINVAL;90 91	if (_link_str) {92		*_link_str = kstrndup(link_str, link_len, GFP_KERNEL);93		if (!*_link_str)94			return -ENOMEM;95	}96 97	*_link_len = link_len;98	return 0;99}100 101static int102format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)103{104	int rc;105	unsigned int link_len;106	unsigned int ofs;107	u8 md5_hash[16];108 109	if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)110		return -EINVAL;111 112	link_len = strlen(link_str);113 114	if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)115		return -ENAMETOOLONG;116 117	rc = symlink_hash(link_len, link_str, md5_hash);118	if (rc) {119		cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);120		return rc;121	}122 123	scnprintf(buf, buf_len,124		  CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,125		  link_len,126		  CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));127 128	ofs = CIFS_MF_SYMLINK_LINK_OFFSET;129	memcpy(buf + ofs, link_str, link_len);130 131	ofs += link_len;132	if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {133		buf[ofs] = '\n';134		ofs++;135	}136 137	while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {138		buf[ofs] = ' ';139		ofs++;140	}141 142	return 0;143}144 145bool146couldbe_mf_symlink(const struct cifs_fattr *fattr)147{148	if (!S_ISREG(fattr->cf_mode))149		/* it's not a symlink */150		return false;151 152	if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE)153		/* it's not a symlink */154		return false;155 156	return true;157}158 159static int160create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,161		  struct cifs_sb_info *cifs_sb, const char *fromName,162		  const char *toName)163{164	int rc;165	u8 *buf;166	unsigned int bytes_written = 0;167 168	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);169	if (!buf)170		return -ENOMEM;171 172	rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName);173	if (rc)174		goto out;175 176	if (tcon->ses->server->ops->create_mf_symlink)177		rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon,178					cifs_sb, fromName, buf, &bytes_written);179	else180		rc = -EOPNOTSUPP;181 182	if (rc)183		goto out;184 185	if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE)186		rc = -EIO;187out:188	kfree(buf);189	return rc;190}191 192int193check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,194		 struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,195		 const unsigned char *path)196{197	int rc;198	u8 *buf = NULL;199	unsigned int link_len = 0;200	unsigned int bytes_read = 0;201	char *symlink = NULL;202 203	if (!couldbe_mf_symlink(fattr))204		/* it's not a symlink */205		return 0;206 207	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);208	if (!buf)209		return -ENOMEM;210 211	if (tcon->ses->server->ops->query_mf_symlink)212		rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,213					      cifs_sb, path, buf, &bytes_read);214	else215		rc = -ENOSYS;216 217	if (rc)218		goto out;219 220	if (bytes_read == 0) /* not a symlink */221		goto out;222 223	rc = parse_mf_symlink(buf, bytes_read, &link_len, &symlink);224	if (rc == -EINVAL) {225		/* it's not a symlink */226		rc = 0;227		goto out;228	}229 230	if (rc != 0)231		goto out;232 233	/* it is a symlink */234	fattr->cf_eof = link_len;235	fattr->cf_mode &= ~S_IFMT;236	fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;237	fattr->cf_dtype = DT_LNK;238	fattr->cf_symlink_target = symlink;239out:240	kfree(buf);241	return rc;242}243 244#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY245/*246 * SMB 1.0 Protocol specific functions247 */248 249int250cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,251		      struct cifs_sb_info *cifs_sb, const unsigned char *path,252		      char *pbuf, unsigned int *pbytes_read)253{254	int rc;255	int oplock = 0;256	struct cifs_fid fid;257	struct cifs_open_parms oparms;258	struct cifs_io_parms io_parms = {0};259	int buf_type = CIFS_NO_BUFFER;260	FILE_ALL_INFO file_info;261 262	oparms = (struct cifs_open_parms) {263		.tcon = tcon,264		.cifs_sb = cifs_sb,265		.desired_access = GENERIC_READ,266		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),267		.disposition = FILE_OPEN,268		.path = path,269		.fid = &fid,270	};271 272	rc = CIFS_open(xid, &oparms, &oplock, &file_info);273	if (rc)274		return rc;275 276	if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {277		rc = -ENOENT;278		/* it's not a symlink */279		goto out;280	}281 282	io_parms.netfid = fid.netfid;283	io_parms.pid = current->tgid;284	io_parms.tcon = tcon;285	io_parms.offset = 0;286	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;287 288	rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type);289out:290	CIFSSMBClose(xid, tcon, fid.netfid);291	return rc;292}293 294int295cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,296		       struct cifs_sb_info *cifs_sb, const unsigned char *path,297		       char *pbuf, unsigned int *pbytes_written)298{299	int rc;300	int oplock = 0;301	struct cifs_fid fid;302	struct cifs_open_parms oparms;303	struct cifs_io_parms io_parms = {0};304 305	oparms = (struct cifs_open_parms) {306		.tcon = tcon,307		.cifs_sb = cifs_sb,308		.desired_access = GENERIC_WRITE,309		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),310		.disposition = FILE_CREATE,311		.path = path,312		.fid = &fid,313	};314 315	rc = CIFS_open(xid, &oparms, &oplock, NULL);316	if (rc)317		return rc;318 319	io_parms.netfid = fid.netfid;320	io_parms.pid = current->tgid;321	io_parms.tcon = tcon;322	io_parms.offset = 0;323	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;324 325	rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf);326	CIFSSMBClose(xid, tcon, fid.netfid);327	return rc;328}329#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */330 331/*332 * SMB 2.1/SMB3 Protocol specific functions333 */334int335smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,336		      struct cifs_sb_info *cifs_sb, const unsigned char *path,337		      char *pbuf, unsigned int *pbytes_read)338{339	int rc;340	struct cifs_fid fid;341	struct cifs_open_parms oparms;342	struct cifs_io_parms io_parms = {0};343	int buf_type = CIFS_NO_BUFFER;344	__le16 *utf16_path;345	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;346	struct smb2_file_all_info *pfile_info = NULL;347 348	oparms = (struct cifs_open_parms) {349		.tcon = tcon,350		.cifs_sb = cifs_sb,351		.path = path,352		.desired_access = GENERIC_READ,353		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),354		.disposition = FILE_OPEN,355		.fid = &fid,356	};357 358	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);359	if (utf16_path == NULL)360		return -ENOMEM;361 362	pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,363			     GFP_KERNEL);364 365	if (pfile_info == NULL) {366		kfree(utf16_path);367		return  -ENOMEM;368	}369 370	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL,371		       NULL, NULL);372	if (rc)373		goto qmf_out_open_fail;374 375	if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {376		/* it's not a symlink */377		rc = -ENOENT; /* Is there a better rc to return? */378		goto qmf_out;379	}380 381	io_parms.netfid = fid.netfid;382	io_parms.pid = current->tgid;383	io_parms.tcon = tcon;384	io_parms.offset = 0;385	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;386	io_parms.persistent_fid = fid.persistent_fid;387	io_parms.volatile_fid = fid.volatile_fid;388	rc = SMB2_read(xid, &io_parms, pbytes_read, &pbuf, &buf_type);389qmf_out:390	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);391qmf_out_open_fail:392	kfree(utf16_path);393	kfree(pfile_info);394	return rc;395}396 397int398smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,399		       struct cifs_sb_info *cifs_sb, const unsigned char *path,400		       char *pbuf, unsigned int *pbytes_written)401{402	int rc;403	struct cifs_fid fid;404	struct cifs_open_parms oparms;405	struct cifs_io_parms io_parms = {0};406	__le16 *utf16_path;407	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;408	struct kvec iov[2];409 410	cifs_dbg(FYI, "%s: path: %s\n", __func__, path);411 412	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);413	if (!utf16_path)414		return -ENOMEM;415 416	oparms = (struct cifs_open_parms) {417		.tcon = tcon,418		.cifs_sb = cifs_sb,419		.path = path,420		.desired_access = GENERIC_WRITE,421		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),422		.disposition = FILE_CREATE,423		.fid = &fid,424		.mode = 0644,425	};426 427	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,428		       NULL, NULL);429	if (rc) {430		kfree(utf16_path);431		return rc;432	}433 434	io_parms.netfid = fid.netfid;435	io_parms.pid = current->tgid;436	io_parms.tcon = tcon;437	io_parms.offset = 0;438	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;439	io_parms.persistent_fid = fid.persistent_fid;440	io_parms.volatile_fid = fid.volatile_fid;441 442	/* iov[0] is reserved for smb header */443	iov[1].iov_base = pbuf;444	iov[1].iov_len = CIFS_MF_SYMLINK_FILE_SIZE;445 446	rc = SMB2_write(xid, &io_parms, pbytes_written, iov, 1);447 448	/* Make sure we wrote all of the symlink data */449	if ((rc == 0) && (*pbytes_written != CIFS_MF_SYMLINK_FILE_SIZE))450		rc = -EIO;451 452	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);453 454	kfree(utf16_path);455	return rc;456}457 458/*459 * M-F Symlink Functions - End460 */461 462int463cifs_hardlink(struct dentry *old_file, struct inode *inode,464	      struct dentry *direntry)465{466	int rc = -EACCES;467	unsigned int xid;468	const char *from_name, *to_name;469	void *page1, *page2;470	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);471	struct tcon_link *tlink;472	struct cifs_tcon *tcon;473	struct TCP_Server_Info *server;474	struct cifsInodeInfo *cifsInode;475 476	if (unlikely(cifs_forced_shutdown(cifs_sb)))477		return -EIO;478 479	tlink = cifs_sb_tlink(cifs_sb);480	if (IS_ERR(tlink))481		return PTR_ERR(tlink);482	tcon = tlink_tcon(tlink);483 484	xid = get_xid();485	page1 = alloc_dentry_path();486	page2 = alloc_dentry_path();487 488	from_name = build_path_from_dentry(old_file, page1);489	if (IS_ERR(from_name)) {490		rc = PTR_ERR(from_name);491		goto cifs_hl_exit;492	}493	to_name = build_path_from_dentry(direntry, page2);494	if (IS_ERR(to_name)) {495		rc = PTR_ERR(to_name);496		goto cifs_hl_exit;497	}498 499#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY500	if (tcon->unix_ext)501		rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name,502					    cifs_sb->local_nls,503					    cifs_remap(cifs_sb));504	else {505#else506	{507#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */508		server = tcon->ses->server;509		if (!server->ops->create_hardlink) {510			rc = -ENOSYS;511			goto cifs_hl_exit;512		}513		rc = server->ops->create_hardlink(xid, tcon, old_file,514						  from_name, to_name, cifs_sb);515		if ((rc == -EIO) || (rc == -EINVAL))516			rc = -EOPNOTSUPP;517	}518 519	d_drop(direntry);	/* force new lookup from server of target */520 521	/*522	 * if source file is cached (oplocked) revalidate will not go to server523	 * until the file is closed or oplock broken so update nlinks locally524	 */525	if (d_really_is_positive(old_file)) {526		cifsInode = CIFS_I(d_inode(old_file));527		if (rc == 0) {528			spin_lock(&d_inode(old_file)->i_lock);529			inc_nlink(d_inode(old_file));530			spin_unlock(&d_inode(old_file)->i_lock);531 532			/*533			 * parent dir timestamps will update from srv within a534			 * second, would it really be worth it to set the parent535			 * dir cifs inode time to zero to force revalidate536			 * (faster) for it too?537			 */538		}539		/*540		 * if not oplocked will force revalidate to get info on source541		 * file from srv.  Note Samba server prior to 4.2 has bug -542		 * not updating src file ctime on hardlinks but Windows servers543		 * handle it properly544		 */545		cifsInode->time = 0;546 547		/*548		 * Will update parent dir timestamps from srv within a second.549		 * Would it really be worth it to set the parent dir (cifs550		 * inode) time field to zero to force revalidate on parent551		 * directory faster ie552		 *553		 * CIFS_I(inode)->time = 0;554		 */555	}556 557cifs_hl_exit:558	free_dentry_path(page1);559	free_dentry_path(page2);560	free_xid(xid);561	cifs_put_tlink(tlink);562	return rc;563}564 565int566cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,567	     struct dentry *direntry, const char *symname)568{569	int rc = -EOPNOTSUPP;570	unsigned int xid;571	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);572	struct TCP_Server_Info *server;573	struct tcon_link *tlink;574	struct cifs_tcon *pTcon;575	const char *full_path;576	void *page;577	struct inode *newinode = NULL;578 579	if (unlikely(cifs_forced_shutdown(cifs_sb)))580		return -EIO;581 582	page = alloc_dentry_path();583	if (!page)584		return -ENOMEM;585 586	xid = get_xid();587 588	tlink = cifs_sb_tlink(cifs_sb);589	if (IS_ERR(tlink)) {590		rc = PTR_ERR(tlink);591		/* BB could be clearer if skipped put_tlink on error here, but harmless */592		goto symlink_exit;593	}594	pTcon = tlink_tcon(tlink);595	server = cifs_pick_channel(pTcon->ses);596 597	full_path = build_path_from_dentry(direntry, page);598	if (IS_ERR(full_path)) {599		rc = PTR_ERR(full_path);600		goto symlink_exit;601	}602 603	cifs_dbg(FYI, "Full path: %s\n", full_path);604	cifs_dbg(FYI, "symname is %s\n", symname);605 606	/* BB what if DFS and this volume is on different share? BB */607	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {608		rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);609	} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {610		rc = __cifs_sfu_make_node(xid, inode, direntry, pTcon,611					  full_path, S_IFLNK, 0, symname);612#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY613	} else if (pTcon->unix_ext) {614		rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,615					   cifs_sb->local_nls,616					   cifs_remap(cifs_sb));617#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */618	} else if (server->ops->create_reparse_symlink) {619		rc =  server->ops->create_reparse_symlink(xid, inode, direntry,620							  pTcon, full_path,621							  symname);622		goto symlink_exit;623	}624 625	if (rc == 0) {626		if (pTcon->posix_extensions) {627			rc = smb311_posix_get_inode_info(&newinode, full_path,628							 NULL, inode->i_sb, xid);629		} else if (pTcon->unix_ext) {630			rc = cifs_get_inode_info_unix(&newinode, full_path,631						      inode->i_sb, xid);632		} else {633			rc = cifs_get_inode_info(&newinode, full_path, NULL,634						 inode->i_sb, xid, NULL);635		}636 637		if (rc != 0) {638			cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n",639				 rc);640		} else {641			d_instantiate(direntry, newinode);642		}643	}644symlink_exit:645	free_dentry_path(page);646	cifs_put_tlink(tlink);647	free_xid(xid);648	return rc;649}650