xref: /openbmc/linux/fs/smb/client/link.c (revision 00ecebcb)
138c8a9a5SSteve French // SPDX-License-Identifier: LGPL-2.1
238c8a9a5SSteve French /*
338c8a9a5SSteve French  *
438c8a9a5SSteve French  *   Copyright (C) International Business Machines  Corp., 2002,2008
538c8a9a5SSteve French  *   Author(s): Steve French (sfrench@us.ibm.com)
638c8a9a5SSteve French  *
738c8a9a5SSteve French  */
838c8a9a5SSteve French #include <linux/fs.h>
938c8a9a5SSteve French #include <linux/stat.h>
1038c8a9a5SSteve French #include <linux/slab.h>
1138c8a9a5SSteve French #include <linux/namei.h>
1238c8a9a5SSteve French #include "cifsfs.h"
1338c8a9a5SSteve French #include "cifspdu.h"
1438c8a9a5SSteve French #include "cifsglob.h"
1538c8a9a5SSteve French #include "cifsproto.h"
1638c8a9a5SSteve French #include "cifs_debug.h"
1738c8a9a5SSteve French #include "cifs_fs_sb.h"
1838c8a9a5SSteve French #include "cifs_unicode.h"
1938c8a9a5SSteve French #include "smb2proto.h"
2038c8a9a5SSteve French #include "cifs_ioctl.h"
2138c8a9a5SSteve French 
2238c8a9a5SSteve French /*
2338c8a9a5SSteve French  * M-F Symlink Functions - Begin
2438c8a9a5SSteve French  */
2538c8a9a5SSteve French 
2638c8a9a5SSteve French #define CIFS_MF_SYMLINK_LEN_OFFSET (4+1)
2738c8a9a5SSteve French #define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1))
2838c8a9a5SSteve French #define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1))
2938c8a9a5SSteve French #define CIFS_MF_SYMLINK_LINK_MAXLEN (1024)
3038c8a9a5SSteve French #define CIFS_MF_SYMLINK_FILE_SIZE \
3138c8a9a5SSteve French 	(CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN)
3238c8a9a5SSteve French 
3338c8a9a5SSteve French #define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n"
3438c8a9a5SSteve French #define CIFS_MF_SYMLINK_MD5_FORMAT "%16phN\n"
3538c8a9a5SSteve French #define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) md5_hash
3638c8a9a5SSteve French 
3738c8a9a5SSteve French static int
symlink_hash(unsigned int link_len,const char * link_str,u8 * md5_hash)3838c8a9a5SSteve French symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
3938c8a9a5SSteve French {
4038c8a9a5SSteve French 	int rc;
4138c8a9a5SSteve French 	struct shash_desc *md5 = NULL;
4238c8a9a5SSteve French 
4338c8a9a5SSteve French 	rc = cifs_alloc_hash("md5", &md5);
4438c8a9a5SSteve French 	if (rc)
4569a1669aSEric Biggers 		return rc;
4638c8a9a5SSteve French 
4769a1669aSEric Biggers 	rc = crypto_shash_digest(md5, link_str, link_len, md5_hash);
4838c8a9a5SSteve French 	if (rc)
4938c8a9a5SSteve French 		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
5038c8a9a5SSteve French 	cifs_free_hash(&md5);
5138c8a9a5SSteve French 	return rc;
5238c8a9a5SSteve French }
5338c8a9a5SSteve French 
5438c8a9a5SSteve French static int
parse_mf_symlink(const u8 * buf,unsigned int buf_len,unsigned int * _link_len,char ** _link_str)5538c8a9a5SSteve French parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len,
5638c8a9a5SSteve French 		 char **_link_str)
5738c8a9a5SSteve French {
5838c8a9a5SSteve French 	int rc;
5938c8a9a5SSteve French 	unsigned int link_len;
6038c8a9a5SSteve French 	const char *md5_str1;
6138c8a9a5SSteve French 	const char *link_str;
6238c8a9a5SSteve French 	u8 md5_hash[16];
6338c8a9a5SSteve French 	char md5_str2[34];
6438c8a9a5SSteve French 
6538c8a9a5SSteve French 	if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
6638c8a9a5SSteve French 		return -EINVAL;
6738c8a9a5SSteve French 
6838c8a9a5SSteve French 	md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET];
6938c8a9a5SSteve French 	link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET];
7038c8a9a5SSteve French 
7138c8a9a5SSteve French 	rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len);
7238c8a9a5SSteve French 	if (rc != 1)
7338c8a9a5SSteve French 		return -EINVAL;
7438c8a9a5SSteve French 
7538c8a9a5SSteve French 	if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
7638c8a9a5SSteve French 		return -EINVAL;
7738c8a9a5SSteve French 
7838c8a9a5SSteve French 	rc = symlink_hash(link_len, link_str, md5_hash);
7938c8a9a5SSteve French 	if (rc) {
8038c8a9a5SSteve French 		cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);
8138c8a9a5SSteve French 		return rc;
8238c8a9a5SSteve French 	}
8338c8a9a5SSteve French 
8438c8a9a5SSteve French 	scnprintf(md5_str2, sizeof(md5_str2),
8538c8a9a5SSteve French 		  CIFS_MF_SYMLINK_MD5_FORMAT,
8638c8a9a5SSteve French 		  CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
8738c8a9a5SSteve French 
8838c8a9a5SSteve French 	if (strncmp(md5_str1, md5_str2, 17) != 0)
8938c8a9a5SSteve French 		return -EINVAL;
9038c8a9a5SSteve French 
9138c8a9a5SSteve French 	if (_link_str) {
9238c8a9a5SSteve French 		*_link_str = kstrndup(link_str, link_len, GFP_KERNEL);
9338c8a9a5SSteve French 		if (!*_link_str)
9438c8a9a5SSteve French 			return -ENOMEM;
9538c8a9a5SSteve French 	}
9638c8a9a5SSteve French 
9738c8a9a5SSteve French 	*_link_len = link_len;
9838c8a9a5SSteve French 	return 0;
9938c8a9a5SSteve French }
10038c8a9a5SSteve French 
10138c8a9a5SSteve French static int
format_mf_symlink(u8 * buf,unsigned int buf_len,const char * link_str)10238c8a9a5SSteve French format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)
10338c8a9a5SSteve French {
10438c8a9a5SSteve French 	int rc;
10538c8a9a5SSteve French 	unsigned int link_len;
10638c8a9a5SSteve French 	unsigned int ofs;
10738c8a9a5SSteve French 	u8 md5_hash[16];
10838c8a9a5SSteve French 
10938c8a9a5SSteve French 	if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
11038c8a9a5SSteve French 		return -EINVAL;
11138c8a9a5SSteve French 
11238c8a9a5SSteve French 	link_len = strlen(link_str);
11338c8a9a5SSteve French 
11438c8a9a5SSteve French 	if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
11538c8a9a5SSteve French 		return -ENAMETOOLONG;
11638c8a9a5SSteve French 
11738c8a9a5SSteve French 	rc = symlink_hash(link_len, link_str, md5_hash);
11838c8a9a5SSteve French 	if (rc) {
11938c8a9a5SSteve French 		cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);
12038c8a9a5SSteve French 		return rc;
12138c8a9a5SSteve French 	}
12238c8a9a5SSteve French 
12338c8a9a5SSteve French 	scnprintf(buf, buf_len,
12438c8a9a5SSteve French 		  CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
12538c8a9a5SSteve French 		  link_len,
12638c8a9a5SSteve French 		  CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
12738c8a9a5SSteve French 
12838c8a9a5SSteve French 	ofs = CIFS_MF_SYMLINK_LINK_OFFSET;
12938c8a9a5SSteve French 	memcpy(buf + ofs, link_str, link_len);
13038c8a9a5SSteve French 
13138c8a9a5SSteve French 	ofs += link_len;
13238c8a9a5SSteve French 	if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
13338c8a9a5SSteve French 		buf[ofs] = '\n';
13438c8a9a5SSteve French 		ofs++;
13538c8a9a5SSteve French 	}
13638c8a9a5SSteve French 
13738c8a9a5SSteve French 	while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
13838c8a9a5SSteve French 		buf[ofs] = ' ';
13938c8a9a5SSteve French 		ofs++;
14038c8a9a5SSteve French 	}
14138c8a9a5SSteve French 
14238c8a9a5SSteve French 	return 0;
14338c8a9a5SSteve French }
14438c8a9a5SSteve French 
14538c8a9a5SSteve French bool
couldbe_mf_symlink(const struct cifs_fattr * fattr)14638c8a9a5SSteve French couldbe_mf_symlink(const struct cifs_fattr *fattr)
14738c8a9a5SSteve French {
14838c8a9a5SSteve French 	if (!S_ISREG(fattr->cf_mode))
14938c8a9a5SSteve French 		/* it's not a symlink */
15038c8a9a5SSteve French 		return false;
15138c8a9a5SSteve French 
15238c8a9a5SSteve French 	if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE)
15338c8a9a5SSteve French 		/* it's not a symlink */
15438c8a9a5SSteve French 		return false;
15538c8a9a5SSteve French 
15638c8a9a5SSteve French 	return true;
15738c8a9a5SSteve French }
15838c8a9a5SSteve French 
15938c8a9a5SSteve French static int
create_mf_symlink(const unsigned int xid,struct cifs_tcon * tcon,struct cifs_sb_info * cifs_sb,const char * fromName,const char * toName)16038c8a9a5SSteve French create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
16138c8a9a5SSteve French 		  struct cifs_sb_info *cifs_sb, const char *fromName,
16238c8a9a5SSteve French 		  const char *toName)
16338c8a9a5SSteve French {
16438c8a9a5SSteve French 	int rc;
16538c8a9a5SSteve French 	u8 *buf;
16638c8a9a5SSteve French 	unsigned int bytes_written = 0;
16738c8a9a5SSteve French 
16838c8a9a5SSteve French 	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
16938c8a9a5SSteve French 	if (!buf)
17038c8a9a5SSteve French 		return -ENOMEM;
17138c8a9a5SSteve French 
17238c8a9a5SSteve French 	rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName);
17338c8a9a5SSteve French 	if (rc)
17438c8a9a5SSteve French 		goto out;
17538c8a9a5SSteve French 
17638c8a9a5SSteve French 	if (tcon->ses->server->ops->create_mf_symlink)
17738c8a9a5SSteve French 		rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon,
17838c8a9a5SSteve French 					cifs_sb, fromName, buf, &bytes_written);
17938c8a9a5SSteve French 	else
18038c8a9a5SSteve French 		rc = -EOPNOTSUPP;
18138c8a9a5SSteve French 
18238c8a9a5SSteve French 	if (rc)
18338c8a9a5SSteve French 		goto out;
18438c8a9a5SSteve French 
18538c8a9a5SSteve French 	if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE)
18638c8a9a5SSteve French 		rc = -EIO;
18738c8a9a5SSteve French out:
18838c8a9a5SSteve French 	kfree(buf);
18938c8a9a5SSteve French 	return rc;
19038c8a9a5SSteve French }
19138c8a9a5SSteve French 
19238c8a9a5SSteve French int
check_mf_symlink(unsigned int xid,struct cifs_tcon * tcon,struct cifs_sb_info * cifs_sb,struct cifs_fattr * fattr,const unsigned char * path)19338c8a9a5SSteve French check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
19438c8a9a5SSteve French 		 struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
19538c8a9a5SSteve French 		 const unsigned char *path)
19638c8a9a5SSteve French {
19738c8a9a5SSteve French 	int rc;
19838c8a9a5SSteve French 	u8 *buf = NULL;
19938c8a9a5SSteve French 	unsigned int link_len = 0;
20038c8a9a5SSteve French 	unsigned int bytes_read = 0;
20138c8a9a5SSteve French 	char *symlink = NULL;
20238c8a9a5SSteve French 
20338c8a9a5SSteve French 	if (!couldbe_mf_symlink(fattr))
20438c8a9a5SSteve French 		/* it's not a symlink */
20538c8a9a5SSteve French 		return 0;
20638c8a9a5SSteve French 
20738c8a9a5SSteve French 	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
20838c8a9a5SSteve French 	if (!buf)
20938c8a9a5SSteve French 		return -ENOMEM;
21038c8a9a5SSteve French 
21138c8a9a5SSteve French 	if (tcon->ses->server->ops->query_mf_symlink)
21238c8a9a5SSteve French 		rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,
21338c8a9a5SSteve French 					      cifs_sb, path, buf, &bytes_read);
21438c8a9a5SSteve French 	else
21538c8a9a5SSteve French 		rc = -ENOSYS;
21638c8a9a5SSteve French 
21738c8a9a5SSteve French 	if (rc)
21838c8a9a5SSteve French 		goto out;
21938c8a9a5SSteve French 
22038c8a9a5SSteve French 	if (bytes_read == 0) /* not a symlink */
22138c8a9a5SSteve French 		goto out;
22238c8a9a5SSteve French 
22338c8a9a5SSteve French 	rc = parse_mf_symlink(buf, bytes_read, &link_len, &symlink);
22438c8a9a5SSteve French 	if (rc == -EINVAL) {
22538c8a9a5SSteve French 		/* it's not a symlink */
22638c8a9a5SSteve French 		rc = 0;
22738c8a9a5SSteve French 		goto out;
22838c8a9a5SSteve French 	}
22938c8a9a5SSteve French 
23038c8a9a5SSteve French 	if (rc != 0)
23138c8a9a5SSteve French 		goto out;
23238c8a9a5SSteve French 
23338c8a9a5SSteve French 	/* it is a symlink */
23438c8a9a5SSteve French 	fattr->cf_eof = link_len;
23538c8a9a5SSteve French 	fattr->cf_mode &= ~S_IFMT;
23638c8a9a5SSteve French 	fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
23738c8a9a5SSteve French 	fattr->cf_dtype = DT_LNK;
23838c8a9a5SSteve French 	fattr->cf_symlink_target = symlink;
23938c8a9a5SSteve French out:
24038c8a9a5SSteve French 	kfree(buf);
24138c8a9a5SSteve French 	return rc;
24238c8a9a5SSteve French }
24338c8a9a5SSteve French 
24438c8a9a5SSteve French #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
24538c8a9a5SSteve French /*
24638c8a9a5SSteve French  * SMB 1.0 Protocol specific functions
24738c8a9a5SSteve French  */
24838c8a9a5SSteve French 
24938c8a9a5SSteve French int
cifs_query_mf_symlink(unsigned int xid,struct cifs_tcon * tcon,struct cifs_sb_info * cifs_sb,const unsigned char * path,char * pbuf,unsigned int * pbytes_read)25038c8a9a5SSteve French cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
25138c8a9a5SSteve French 		      struct cifs_sb_info *cifs_sb, const unsigned char *path,
25238c8a9a5SSteve French 		      char *pbuf, unsigned int *pbytes_read)
25338c8a9a5SSteve French {
25438c8a9a5SSteve French 	int rc;
25538c8a9a5SSteve French 	int oplock = 0;
25638c8a9a5SSteve French 	struct cifs_fid fid;
25738c8a9a5SSteve French 	struct cifs_open_parms oparms;
25838c8a9a5SSteve French 	struct cifs_io_parms io_parms = {0};
25938c8a9a5SSteve French 	int buf_type = CIFS_NO_BUFFER;
26038c8a9a5SSteve French 	FILE_ALL_INFO file_info;
26138c8a9a5SSteve French 
26238c8a9a5SSteve French 	oparms = (struct cifs_open_parms) {
26338c8a9a5SSteve French 		.tcon = tcon,
26438c8a9a5SSteve French 		.cifs_sb = cifs_sb,
26538c8a9a5SSteve French 		.desired_access = GENERIC_READ,
26638c8a9a5SSteve French 		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
26738c8a9a5SSteve French 		.disposition = FILE_OPEN,
26838c8a9a5SSteve French 		.path = path,
26938c8a9a5SSteve French 		.fid = &fid,
27038c8a9a5SSteve French 	};
27138c8a9a5SSteve French 
27238c8a9a5SSteve French 	rc = CIFS_open(xid, &oparms, &oplock, &file_info);
27338c8a9a5SSteve French 	if (rc)
27438c8a9a5SSteve French 		return rc;
27538c8a9a5SSteve French 
27638c8a9a5SSteve French 	if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
27738c8a9a5SSteve French 		rc = -ENOENT;
27838c8a9a5SSteve French 		/* it's not a symlink */
27938c8a9a5SSteve French 		goto out;
28038c8a9a5SSteve French 	}
28138c8a9a5SSteve French 
28238c8a9a5SSteve French 	io_parms.netfid = fid.netfid;
28338c8a9a5SSteve French 	io_parms.pid = current->tgid;
28438c8a9a5SSteve French 	io_parms.tcon = tcon;
28538c8a9a5SSteve French 	io_parms.offset = 0;
28638c8a9a5SSteve French 	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
28738c8a9a5SSteve French 
28838c8a9a5SSteve French 	rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
28938c8a9a5SSteve French out:
29038c8a9a5SSteve French 	CIFSSMBClose(xid, tcon, fid.netfid);
29138c8a9a5SSteve French 	return rc;
29238c8a9a5SSteve French }
29338c8a9a5SSteve French 
29438c8a9a5SSteve French int
cifs_create_mf_symlink(unsigned int xid,struct cifs_tcon * tcon,struct cifs_sb_info * cifs_sb,const unsigned char * path,char * pbuf,unsigned int * pbytes_written)29538c8a9a5SSteve French cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
29638c8a9a5SSteve French 		       struct cifs_sb_info *cifs_sb, const unsigned char *path,
29738c8a9a5SSteve French 		       char *pbuf, unsigned int *pbytes_written)
29838c8a9a5SSteve French {
29938c8a9a5SSteve French 	int rc;
30038c8a9a5SSteve French 	int oplock = 0;
30138c8a9a5SSteve French 	struct cifs_fid fid;
30238c8a9a5SSteve French 	struct cifs_open_parms oparms;
30338c8a9a5SSteve French 	struct cifs_io_parms io_parms = {0};
30438c8a9a5SSteve French 
30538c8a9a5SSteve French 	oparms = (struct cifs_open_parms) {
30638c8a9a5SSteve French 		.tcon = tcon,
30738c8a9a5SSteve French 		.cifs_sb = cifs_sb,
30838c8a9a5SSteve French 		.desired_access = GENERIC_WRITE,
30938c8a9a5SSteve French 		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
31038c8a9a5SSteve French 		.disposition = FILE_CREATE,
31138c8a9a5SSteve French 		.path = path,
31238c8a9a5SSteve French 		.fid = &fid,
31338c8a9a5SSteve French 	};
31438c8a9a5SSteve French 
31538c8a9a5SSteve French 	rc = CIFS_open(xid, &oparms, &oplock, NULL);
31638c8a9a5SSteve French 	if (rc)
31738c8a9a5SSteve French 		return rc;
31838c8a9a5SSteve French 
31938c8a9a5SSteve French 	io_parms.netfid = fid.netfid;
32038c8a9a5SSteve French 	io_parms.pid = current->tgid;
32138c8a9a5SSteve French 	io_parms.tcon = tcon;
32238c8a9a5SSteve French 	io_parms.offset = 0;
32338c8a9a5SSteve French 	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
32438c8a9a5SSteve French 
32538c8a9a5SSteve French 	rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf);
32638c8a9a5SSteve French 	CIFSSMBClose(xid, tcon, fid.netfid);
32738c8a9a5SSteve French 	return rc;
32838c8a9a5SSteve French }
32938c8a9a5SSteve French #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
33038c8a9a5SSteve French 
33138c8a9a5SSteve French /*
33238c8a9a5SSteve French  * SMB 2.1/SMB3 Protocol specific functions
33338c8a9a5SSteve French  */
33438c8a9a5SSteve French int
smb3_query_mf_symlink(unsigned int xid,struct cifs_tcon * tcon,struct cifs_sb_info * cifs_sb,const unsigned char * path,char * pbuf,unsigned int * pbytes_read)33538c8a9a5SSteve French smb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
33638c8a9a5SSteve French 		      struct cifs_sb_info *cifs_sb, const unsigned char *path,
33738c8a9a5SSteve French 		      char *pbuf, unsigned int *pbytes_read)
33838c8a9a5SSteve French {
33938c8a9a5SSteve French 	int rc;
34038c8a9a5SSteve French 	struct cifs_fid fid;
34138c8a9a5SSteve French 	struct cifs_open_parms oparms;
34238c8a9a5SSteve French 	struct cifs_io_parms io_parms = {0};
34338c8a9a5SSteve French 	int buf_type = CIFS_NO_BUFFER;
34438c8a9a5SSteve French 	__le16 *utf16_path;
34538c8a9a5SSteve French 	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
34638c8a9a5SSteve French 	struct smb2_file_all_info *pfile_info = NULL;
34738c8a9a5SSteve French 
34838c8a9a5SSteve French 	oparms = (struct cifs_open_parms) {
34938c8a9a5SSteve French 		.tcon = tcon,
35038c8a9a5SSteve French 		.cifs_sb = cifs_sb,
35138c8a9a5SSteve French 		.path = path,
35238c8a9a5SSteve French 		.desired_access = GENERIC_READ,
35338c8a9a5SSteve French 		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
35438c8a9a5SSteve French 		.disposition = FILE_OPEN,
35538c8a9a5SSteve French 		.fid = &fid,
35638c8a9a5SSteve French 	};
35738c8a9a5SSteve French 
35838c8a9a5SSteve French 	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
35938c8a9a5SSteve French 	if (utf16_path == NULL)
36038c8a9a5SSteve French 		return -ENOMEM;
36138c8a9a5SSteve French 
36238c8a9a5SSteve French 	pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
36338c8a9a5SSteve French 			     GFP_KERNEL);
36438c8a9a5SSteve French 
36538c8a9a5SSteve French 	if (pfile_info == NULL) {
36638c8a9a5SSteve French 		kfree(utf16_path);
36738c8a9a5SSteve French 		return  -ENOMEM;
36838c8a9a5SSteve French 	}
36938c8a9a5SSteve French 
37038c8a9a5SSteve French 	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL,
37138c8a9a5SSteve French 		       NULL, NULL);
37238c8a9a5SSteve French 	if (rc)
37338c8a9a5SSteve French 		goto qmf_out_open_fail;
37438c8a9a5SSteve French 
37538c8a9a5SSteve French 	if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
37638c8a9a5SSteve French 		/* it's not a symlink */
37738c8a9a5SSteve French 		rc = -ENOENT; /* Is there a better rc to return? */
37838c8a9a5SSteve French 		goto qmf_out;
37938c8a9a5SSteve French 	}
38038c8a9a5SSteve French 
38138c8a9a5SSteve French 	io_parms.netfid = fid.netfid;
38238c8a9a5SSteve French 	io_parms.pid = current->tgid;
38338c8a9a5SSteve French 	io_parms.tcon = tcon;
38438c8a9a5SSteve French 	io_parms.offset = 0;
38538c8a9a5SSteve French 	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
38638c8a9a5SSteve French 	io_parms.persistent_fid = fid.persistent_fid;
38738c8a9a5SSteve French 	io_parms.volatile_fid = fid.volatile_fid;
38838c8a9a5SSteve French 	rc = SMB2_read(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
38938c8a9a5SSteve French qmf_out:
39038c8a9a5SSteve French 	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
39138c8a9a5SSteve French qmf_out_open_fail:
39238c8a9a5SSteve French 	kfree(utf16_path);
39338c8a9a5SSteve French 	kfree(pfile_info);
39438c8a9a5SSteve French 	return rc;
39538c8a9a5SSteve French }
39638c8a9a5SSteve French 
39738c8a9a5SSteve French int
smb3_create_mf_symlink(unsigned int xid,struct cifs_tcon * tcon,struct cifs_sb_info * cifs_sb,const unsigned char * path,char * pbuf,unsigned int * pbytes_written)39838c8a9a5SSteve French smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
39938c8a9a5SSteve French 		       struct cifs_sb_info *cifs_sb, const unsigned char *path,
40038c8a9a5SSteve French 		       char *pbuf, unsigned int *pbytes_written)
40138c8a9a5SSteve French {
40238c8a9a5SSteve French 	int rc;
40338c8a9a5SSteve French 	struct cifs_fid fid;
40438c8a9a5SSteve French 	struct cifs_open_parms oparms;
40538c8a9a5SSteve French 	struct cifs_io_parms io_parms = {0};
40638c8a9a5SSteve French 	__le16 *utf16_path;
40738c8a9a5SSteve French 	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
40838c8a9a5SSteve French 	struct kvec iov[2];
40938c8a9a5SSteve French 
41038c8a9a5SSteve French 	cifs_dbg(FYI, "%s: path: %s\n", __func__, path);
41138c8a9a5SSteve French 
41238c8a9a5SSteve French 	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
41338c8a9a5SSteve French 	if (!utf16_path)
41438c8a9a5SSteve French 		return -ENOMEM;
41538c8a9a5SSteve French 
41638c8a9a5SSteve French 	oparms = (struct cifs_open_parms) {
41738c8a9a5SSteve French 		.tcon = tcon,
41838c8a9a5SSteve French 		.cifs_sb = cifs_sb,
41938c8a9a5SSteve French 		.path = path,
42038c8a9a5SSteve French 		.desired_access = GENERIC_WRITE,
42138c8a9a5SSteve French 		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
42238c8a9a5SSteve French 		.disposition = FILE_CREATE,
42338c8a9a5SSteve French 		.fid = &fid,
42438c8a9a5SSteve French 		.mode = 0644,
42538c8a9a5SSteve French 	};
42638c8a9a5SSteve French 
42738c8a9a5SSteve French 	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,
42838c8a9a5SSteve French 		       NULL, NULL);
42938c8a9a5SSteve French 	if (rc) {
43038c8a9a5SSteve French 		kfree(utf16_path);
43138c8a9a5SSteve French 		return rc;
43238c8a9a5SSteve French 	}
43338c8a9a5SSteve French 
43438c8a9a5SSteve French 	io_parms.netfid = fid.netfid;
43538c8a9a5SSteve French 	io_parms.pid = current->tgid;
43638c8a9a5SSteve French 	io_parms.tcon = tcon;
43738c8a9a5SSteve French 	io_parms.offset = 0;
43838c8a9a5SSteve French 	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
43938c8a9a5SSteve French 	io_parms.persistent_fid = fid.persistent_fid;
44038c8a9a5SSteve French 	io_parms.volatile_fid = fid.volatile_fid;
44138c8a9a5SSteve French 
44238c8a9a5SSteve French 	/* iov[0] is reserved for smb header */
44338c8a9a5SSteve French 	iov[1].iov_base = pbuf;
44438c8a9a5SSteve French 	iov[1].iov_len = CIFS_MF_SYMLINK_FILE_SIZE;
44538c8a9a5SSteve French 
44638c8a9a5SSteve French 	rc = SMB2_write(xid, &io_parms, pbytes_written, iov, 1);
44738c8a9a5SSteve French 
44838c8a9a5SSteve French 	/* Make sure we wrote all of the symlink data */
44938c8a9a5SSteve French 	if ((rc == 0) && (*pbytes_written != CIFS_MF_SYMLINK_FILE_SIZE))
45038c8a9a5SSteve French 		rc = -EIO;
45138c8a9a5SSteve French 
45238c8a9a5SSteve French 	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
45338c8a9a5SSteve French 
45438c8a9a5SSteve French 	kfree(utf16_path);
45538c8a9a5SSteve French 	return rc;
45638c8a9a5SSteve French }
45738c8a9a5SSteve French 
45838c8a9a5SSteve French /*
45938c8a9a5SSteve French  * M-F Symlink Functions - End
46038c8a9a5SSteve French  */
46138c8a9a5SSteve French 
46238c8a9a5SSteve French int
cifs_hardlink(struct dentry * old_file,struct inode * inode,struct dentry * direntry)46338c8a9a5SSteve French cifs_hardlink(struct dentry *old_file, struct inode *inode,
46438c8a9a5SSteve French 	      struct dentry *direntry)
46538c8a9a5SSteve French {
46638c8a9a5SSteve French 	int rc = -EACCES;
46738c8a9a5SSteve French 	unsigned int xid;
46838c8a9a5SSteve French 	const char *from_name, *to_name;
46938c8a9a5SSteve French 	void *page1, *page2;
47038c8a9a5SSteve French 	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
47138c8a9a5SSteve French 	struct tcon_link *tlink;
47238c8a9a5SSteve French 	struct cifs_tcon *tcon;
47338c8a9a5SSteve French 	struct TCP_Server_Info *server;
47438c8a9a5SSteve French 	struct cifsInodeInfo *cifsInode;
47538c8a9a5SSteve French 
47638c8a9a5SSteve French 	if (unlikely(cifs_forced_shutdown(cifs_sb)))
47738c8a9a5SSteve French 		return -EIO;
47838c8a9a5SSteve French 
47938c8a9a5SSteve French 	tlink = cifs_sb_tlink(cifs_sb);
48038c8a9a5SSteve French 	if (IS_ERR(tlink))
48138c8a9a5SSteve French 		return PTR_ERR(tlink);
48238c8a9a5SSteve French 	tcon = tlink_tcon(tlink);
48338c8a9a5SSteve French 
48438c8a9a5SSteve French 	xid = get_xid();
48538c8a9a5SSteve French 	page1 = alloc_dentry_path();
48638c8a9a5SSteve French 	page2 = alloc_dentry_path();
48738c8a9a5SSteve French 
48838c8a9a5SSteve French 	from_name = build_path_from_dentry(old_file, page1);
48938c8a9a5SSteve French 	if (IS_ERR(from_name)) {
49038c8a9a5SSteve French 		rc = PTR_ERR(from_name);
49138c8a9a5SSteve French 		goto cifs_hl_exit;
49238c8a9a5SSteve French 	}
49338c8a9a5SSteve French 	to_name = build_path_from_dentry(direntry, page2);
49438c8a9a5SSteve French 	if (IS_ERR(to_name)) {
49538c8a9a5SSteve French 		rc = PTR_ERR(to_name);
49638c8a9a5SSteve French 		goto cifs_hl_exit;
49738c8a9a5SSteve French 	}
49838c8a9a5SSteve French 
49938c8a9a5SSteve French #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
50038c8a9a5SSteve French 	if (tcon->unix_ext)
50138c8a9a5SSteve French 		rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name,
50238c8a9a5SSteve French 					    cifs_sb->local_nls,
50338c8a9a5SSteve French 					    cifs_remap(cifs_sb));
50438c8a9a5SSteve French 	else {
50538c8a9a5SSteve French #else
50638c8a9a5SSteve French 	{
50738c8a9a5SSteve French #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
50838c8a9a5SSteve French 		server = tcon->ses->server;
50938c8a9a5SSteve French 		if (!server->ops->create_hardlink) {
51038c8a9a5SSteve French 			rc = -ENOSYS;
51138c8a9a5SSteve French 			goto cifs_hl_exit;
51238c8a9a5SSteve French 		}
5138c944f8aSPaulo Alcantara 		rc = server->ops->create_hardlink(xid, tcon, old_file,
5148c944f8aSPaulo Alcantara 						  from_name, to_name, cifs_sb);
51538c8a9a5SSteve French 		if ((rc == -EIO) || (rc == -EINVAL))
51638c8a9a5SSteve French 			rc = -EOPNOTSUPP;
51738c8a9a5SSteve French 	}
51838c8a9a5SSteve French 
51938c8a9a5SSteve French 	d_drop(direntry);	/* force new lookup from server of target */
52038c8a9a5SSteve French 
52138c8a9a5SSteve French 	/*
52238c8a9a5SSteve French 	 * if source file is cached (oplocked) revalidate will not go to server
52338c8a9a5SSteve French 	 * until the file is closed or oplock broken so update nlinks locally
52438c8a9a5SSteve French 	 */
52538c8a9a5SSteve French 	if (d_really_is_positive(old_file)) {
52638c8a9a5SSteve French 		cifsInode = CIFS_I(d_inode(old_file));
52738c8a9a5SSteve French 		if (rc == 0) {
52838c8a9a5SSteve French 			spin_lock(&d_inode(old_file)->i_lock);
52938c8a9a5SSteve French 			inc_nlink(d_inode(old_file));
53038c8a9a5SSteve French 			spin_unlock(&d_inode(old_file)->i_lock);
53138c8a9a5SSteve French 
53238c8a9a5SSteve French 			/*
53338c8a9a5SSteve French 			 * parent dir timestamps will update from srv within a
53438c8a9a5SSteve French 			 * second, would it really be worth it to set the parent
53538c8a9a5SSteve French 			 * dir cifs inode time to zero to force revalidate
53638c8a9a5SSteve French 			 * (faster) for it too?
53738c8a9a5SSteve French 			 */
53838c8a9a5SSteve French 		}
53938c8a9a5SSteve French 		/*
54038c8a9a5SSteve French 		 * if not oplocked will force revalidate to get info on source
54138c8a9a5SSteve French 		 * file from srv.  Note Samba server prior to 4.2 has bug -
54238c8a9a5SSteve French 		 * not updating src file ctime on hardlinks but Windows servers
54338c8a9a5SSteve French 		 * handle it properly
54438c8a9a5SSteve French 		 */
54538c8a9a5SSteve French 		cifsInode->time = 0;
54638c8a9a5SSteve French 
54738c8a9a5SSteve French 		/*
54838c8a9a5SSteve French 		 * Will update parent dir timestamps from srv within a second.
54938c8a9a5SSteve French 		 * Would it really be worth it to set the parent dir (cifs
55038c8a9a5SSteve French 		 * inode) time field to zero to force revalidate on parent
55138c8a9a5SSteve French 		 * directory faster ie
55238c8a9a5SSteve French 		 *
55338c8a9a5SSteve French 		 * CIFS_I(inode)->time = 0;
55438c8a9a5SSteve French 		 */
55538c8a9a5SSteve French 	}
55638c8a9a5SSteve French 
55738c8a9a5SSteve French cifs_hl_exit:
55838c8a9a5SSteve French 	free_dentry_path(page1);
55938c8a9a5SSteve French 	free_dentry_path(page2);
56038c8a9a5SSteve French 	free_xid(xid);
56138c8a9a5SSteve French 	cifs_put_tlink(tlink);
56238c8a9a5SSteve French 	return rc;
56338c8a9a5SSteve French }
56438c8a9a5SSteve French 
56538c8a9a5SSteve French int
56638c8a9a5SSteve French cifs_symlink(struct mnt_idmap *idmap, struct inode *inode,
56738c8a9a5SSteve French 	     struct dentry *direntry, const char *symname)
56838c8a9a5SSteve French {
56938c8a9a5SSteve French 	int rc = -EOPNOTSUPP;
57038c8a9a5SSteve French 	unsigned int xid;
57138c8a9a5SSteve French 	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
57200ecebcbSPaulo Alcantara 	struct TCP_Server_Info *server;
57338c8a9a5SSteve French 	struct tcon_link *tlink;
57438c8a9a5SSteve French 	struct cifs_tcon *pTcon;
57538c8a9a5SSteve French 	const char *full_path;
57638c8a9a5SSteve French 	void *page;
57738c8a9a5SSteve French 	struct inode *newinode = NULL;
57838c8a9a5SSteve French 
57938c8a9a5SSteve French 	if (unlikely(cifs_forced_shutdown(cifs_sb)))
58038c8a9a5SSteve French 		return -EIO;
58138c8a9a5SSteve French 
58238c8a9a5SSteve French 	page = alloc_dentry_path();
58338c8a9a5SSteve French 	if (!page)
58438c8a9a5SSteve French 		return -ENOMEM;
58538c8a9a5SSteve French 
58638c8a9a5SSteve French 	xid = get_xid();
58738c8a9a5SSteve French 
58838c8a9a5SSteve French 	tlink = cifs_sb_tlink(cifs_sb);
58938c8a9a5SSteve French 	if (IS_ERR(tlink)) {
59038c8a9a5SSteve French 		rc = PTR_ERR(tlink);
59138c8a9a5SSteve French 		goto symlink_exit;
59238c8a9a5SSteve French 	}
59338c8a9a5SSteve French 	pTcon = tlink_tcon(tlink);
59400ecebcbSPaulo Alcantara 	server = cifs_pick_channel(pTcon->ses);
59538c8a9a5SSteve French 
59638c8a9a5SSteve French 	full_path = build_path_from_dentry(direntry, page);
59738c8a9a5SSteve French 	if (IS_ERR(full_path)) {
59838c8a9a5SSteve French 		rc = PTR_ERR(full_path);
59938c8a9a5SSteve French 		goto symlink_exit;
60038c8a9a5SSteve French 	}
60138c8a9a5SSteve French 
60238c8a9a5SSteve French 	cifs_dbg(FYI, "Full path: %s\n", full_path);
60338c8a9a5SSteve French 	cifs_dbg(FYI, "symname is %s\n", symname);
60438c8a9a5SSteve French 
60538c8a9a5SSteve French 	/* BB what if DFS and this volume is on different share? BB */
60600ecebcbSPaulo Alcantara 	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
60738c8a9a5SSteve French 		rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
60838c8a9a5SSteve French #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
60900ecebcbSPaulo Alcantara 	} else if (pTcon->unix_ext) {
61038c8a9a5SSteve French 		rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
61138c8a9a5SSteve French 					   cifs_sb->local_nls,
61238c8a9a5SSteve French 					   cifs_remap(cifs_sb));
61338c8a9a5SSteve French #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
61400ecebcbSPaulo Alcantara 	} else if (server->ops->create_reparse_symlink) {
61500ecebcbSPaulo Alcantara 		rc =  server->ops->create_reparse_symlink(xid, inode, direntry,
61600ecebcbSPaulo Alcantara 							  pTcon, full_path,
61700ecebcbSPaulo Alcantara 							  symname);
61800ecebcbSPaulo Alcantara 		goto symlink_exit;
61900ecebcbSPaulo Alcantara 	}
62038c8a9a5SSteve French 
62138c8a9a5SSteve French 	if (rc == 0) {
62202bcf865SSteve French 		if (pTcon->posix_extensions) {
62302bcf865SSteve French 			rc = smb311_posix_get_inode_info(&newinode, full_path,
62402bcf865SSteve French 							 NULL, inode->i_sb, xid);
62502bcf865SSteve French 		} else if (pTcon->unix_ext) {
62638c8a9a5SSteve French 			rc = cifs_get_inode_info_unix(&newinode, full_path,
62738c8a9a5SSteve French 						      inode->i_sb, xid);
62802bcf865SSteve French 		} else {
62938c8a9a5SSteve French 			rc = cifs_get_inode_info(&newinode, full_path, NULL,
63038c8a9a5SSteve French 						 inode->i_sb, xid, NULL);
63102bcf865SSteve French 		}
63238c8a9a5SSteve French 
63338c8a9a5SSteve French 		if (rc != 0) {
63438c8a9a5SSteve French 			cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n",
63538c8a9a5SSteve French 				 rc);
63638c8a9a5SSteve French 		} else {
63738c8a9a5SSteve French 			d_instantiate(direntry, newinode);
63838c8a9a5SSteve French 		}
63938c8a9a5SSteve French 	}
64038c8a9a5SSteve French symlink_exit:
64138c8a9a5SSteve French 	free_dentry_path(page);
64238c8a9a5SSteve French 	cifs_put_tlink(tlink);
64338c8a9a5SSteve French 	free_xid(xid);
64438c8a9a5SSteve French 	return rc;
64538c8a9a5SSteve French }
646