18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *   fs/cifs/link.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *   Copyright (C) International Business Machines  Corp., 2002,2008
58c2ecf20Sopenharmony_ci *   Author(s): Steve French (sfrench@us.ibm.com)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
88c2ecf20Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as published
98c2ecf20Sopenharmony_ci *   by the Free Software Foundation; either version 2.1 of the License, or
108c2ecf20Sopenharmony_ci *   (at your option) any later version.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *   This library is distributed in the hope that it will be useful,
138c2ecf20Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
148c2ecf20Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
158c2ecf20Sopenharmony_ci *   the GNU Lesser General Public License for more details.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public License
188c2ecf20Sopenharmony_ci *   along with this library; if not, write to the Free Software
198c2ecf20Sopenharmony_ci *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci#include <linux/fs.h>
228c2ecf20Sopenharmony_ci#include <linux/stat.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci#include <linux/namei.h>
258c2ecf20Sopenharmony_ci#include "cifsfs.h"
268c2ecf20Sopenharmony_ci#include "cifspdu.h"
278c2ecf20Sopenharmony_ci#include "cifsglob.h"
288c2ecf20Sopenharmony_ci#include "cifsproto.h"
298c2ecf20Sopenharmony_ci#include "cifs_debug.h"
308c2ecf20Sopenharmony_ci#include "cifs_fs_sb.h"
318c2ecf20Sopenharmony_ci#include "cifs_unicode.h"
328c2ecf20Sopenharmony_ci#include "smb2proto.h"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/*
358c2ecf20Sopenharmony_ci * M-F Symlink Functions - Begin
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1)
398c2ecf20Sopenharmony_ci#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1))
408c2ecf20Sopenharmony_ci#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1))
418c2ecf20Sopenharmony_ci#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024)
428c2ecf20Sopenharmony_ci#define CIFS_MF_SYMLINK_FILE_SIZE \
438c2ecf20Sopenharmony_ci	(CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN)
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n"
468c2ecf20Sopenharmony_ci#define CIFS_MF_SYMLINK_MD5_FORMAT "%16phN\n"
478c2ecf20Sopenharmony_ci#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) md5_hash
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic int
508c2ecf20Sopenharmony_cisymlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	int rc;
538c2ecf20Sopenharmony_ci	struct crypto_shash *md5 = NULL;
548c2ecf20Sopenharmony_ci	struct sdesc *sdescmd5 = NULL;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	rc = cifs_alloc_hash("md5", &md5, &sdescmd5);
578c2ecf20Sopenharmony_ci	if (rc)
588c2ecf20Sopenharmony_ci		goto symlink_hash_err;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	rc = crypto_shash_init(&sdescmd5->shash);
618c2ecf20Sopenharmony_ci	if (rc) {
628c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__);
638c2ecf20Sopenharmony_ci		goto symlink_hash_err;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci	rc = crypto_shash_update(&sdescmd5->shash, link_str, link_len);
668c2ecf20Sopenharmony_ci	if (rc) {
678c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__);
688c2ecf20Sopenharmony_ci		goto symlink_hash_err;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	rc = crypto_shash_final(&sdescmd5->shash, md5_hash);
718c2ecf20Sopenharmony_ci	if (rc)
728c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cisymlink_hash_err:
758c2ecf20Sopenharmony_ci	cifs_free_hash(&md5, &sdescmd5);
768c2ecf20Sopenharmony_ci	return rc;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int
808c2ecf20Sopenharmony_ciparse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len,
818c2ecf20Sopenharmony_ci		 char **_link_str)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	int rc;
848c2ecf20Sopenharmony_ci	unsigned int link_len;
858c2ecf20Sopenharmony_ci	const char *md5_str1;
868c2ecf20Sopenharmony_ci	const char *link_str;
878c2ecf20Sopenharmony_ci	u8 md5_hash[16];
888c2ecf20Sopenharmony_ci	char md5_str2[34];
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
918c2ecf20Sopenharmony_ci		return -EINVAL;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET];
948c2ecf20Sopenharmony_ci	link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET];
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len);
978c2ecf20Sopenharmony_ci	if (rc != 1)
988c2ecf20Sopenharmony_ci		return -EINVAL;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
1018c2ecf20Sopenharmony_ci		return -EINVAL;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	rc = symlink_hash(link_len, link_str, md5_hash);
1048c2ecf20Sopenharmony_ci	if (rc) {
1058c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);
1068c2ecf20Sopenharmony_ci		return rc;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	scnprintf(md5_str2, sizeof(md5_str2),
1108c2ecf20Sopenharmony_ci		  CIFS_MF_SYMLINK_MD5_FORMAT,
1118c2ecf20Sopenharmony_ci		  CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (strncmp(md5_str1, md5_str2, 17) != 0)
1148c2ecf20Sopenharmony_ci		return -EINVAL;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (_link_str) {
1178c2ecf20Sopenharmony_ci		*_link_str = kstrndup(link_str, link_len, GFP_KERNEL);
1188c2ecf20Sopenharmony_ci		if (!*_link_str)
1198c2ecf20Sopenharmony_ci			return -ENOMEM;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	*_link_len = link_len;
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int
1278c2ecf20Sopenharmony_ciformat_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	int rc;
1308c2ecf20Sopenharmony_ci	unsigned int link_len;
1318c2ecf20Sopenharmony_ci	unsigned int ofs;
1328c2ecf20Sopenharmony_ci	u8 md5_hash[16];
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
1358c2ecf20Sopenharmony_ci		return -EINVAL;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	link_len = strlen(link_str);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
1408c2ecf20Sopenharmony_ci		return -ENAMETOOLONG;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	rc = symlink_hash(link_len, link_str, md5_hash);
1438c2ecf20Sopenharmony_ci	if (rc) {
1448c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "%s: MD5 hash failure: %d\n", __func__, rc);
1458c2ecf20Sopenharmony_ci		return rc;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	scnprintf(buf, buf_len,
1498c2ecf20Sopenharmony_ci		  CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
1508c2ecf20Sopenharmony_ci		  link_len,
1518c2ecf20Sopenharmony_ci		  CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ofs = CIFS_MF_SYMLINK_LINK_OFFSET;
1548c2ecf20Sopenharmony_ci	memcpy(buf + ofs, link_str, link_len);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ofs += link_len;
1578c2ecf20Sopenharmony_ci	if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
1588c2ecf20Sopenharmony_ci		buf[ofs] = '\n';
1598c2ecf20Sopenharmony_ci		ofs++;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
1638c2ecf20Sopenharmony_ci		buf[ofs] = ' ';
1648c2ecf20Sopenharmony_ci		ofs++;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cibool
1718c2ecf20Sopenharmony_cicouldbe_mf_symlink(const struct cifs_fattr *fattr)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	if (!S_ISREG(fattr->cf_mode))
1748c2ecf20Sopenharmony_ci		/* it's not a symlink */
1758c2ecf20Sopenharmony_ci		return false;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE)
1788c2ecf20Sopenharmony_ci		/* it's not a symlink */
1798c2ecf20Sopenharmony_ci		return false;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return true;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic int
1858c2ecf20Sopenharmony_cicreate_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
1868c2ecf20Sopenharmony_ci		  struct cifs_sb_info *cifs_sb, const char *fromName,
1878c2ecf20Sopenharmony_ci		  const char *toName)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	int rc;
1908c2ecf20Sopenharmony_ci	u8 *buf;
1918c2ecf20Sopenharmony_ci	unsigned int bytes_written = 0;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
1948c2ecf20Sopenharmony_ci	if (!buf)
1958c2ecf20Sopenharmony_ci		return -ENOMEM;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName);
1988c2ecf20Sopenharmony_ci	if (rc)
1998c2ecf20Sopenharmony_ci		goto out;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (tcon->ses->server->ops->create_mf_symlink)
2028c2ecf20Sopenharmony_ci		rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon,
2038c2ecf20Sopenharmony_ci					cifs_sb, fromName, buf, &bytes_written);
2048c2ecf20Sopenharmony_ci	else
2058c2ecf20Sopenharmony_ci		rc = -EOPNOTSUPP;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (rc)
2088c2ecf20Sopenharmony_ci		goto out;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE)
2118c2ecf20Sopenharmony_ci		rc = -EIO;
2128c2ecf20Sopenharmony_ciout:
2138c2ecf20Sopenharmony_ci	kfree(buf);
2148c2ecf20Sopenharmony_ci	return rc;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int
2188c2ecf20Sopenharmony_ciquery_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon,
2198c2ecf20Sopenharmony_ci		 struct cifs_sb_info *cifs_sb, const unsigned char *path,
2208c2ecf20Sopenharmony_ci		 char **symlinkinfo)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	int rc;
2238c2ecf20Sopenharmony_ci	u8 *buf = NULL;
2248c2ecf20Sopenharmony_ci	unsigned int link_len = 0;
2258c2ecf20Sopenharmony_ci	unsigned int bytes_read = 0;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
2288c2ecf20Sopenharmony_ci	if (!buf)
2298c2ecf20Sopenharmony_ci		return -ENOMEM;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (tcon->ses->server->ops->query_mf_symlink)
2328c2ecf20Sopenharmony_ci		rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,
2338c2ecf20Sopenharmony_ci					      cifs_sb, path, buf, &bytes_read);
2348c2ecf20Sopenharmony_ci	else
2358c2ecf20Sopenharmony_ci		rc = -ENOSYS;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (rc)
2388c2ecf20Sopenharmony_ci		goto out;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (bytes_read == 0) { /* not a symlink */
2418c2ecf20Sopenharmony_ci		rc = -EINVAL;
2428c2ecf20Sopenharmony_ci		goto out;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	rc = parse_mf_symlink(buf, bytes_read, &link_len, symlinkinfo);
2468c2ecf20Sopenharmony_ciout:
2478c2ecf20Sopenharmony_ci	kfree(buf);
2488c2ecf20Sopenharmony_ci	return rc;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ciint
2528c2ecf20Sopenharmony_cicheck_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
2538c2ecf20Sopenharmony_ci		 struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
2548c2ecf20Sopenharmony_ci		 const unsigned char *path)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	int rc;
2578c2ecf20Sopenharmony_ci	u8 *buf = NULL;
2588c2ecf20Sopenharmony_ci	unsigned int link_len = 0;
2598c2ecf20Sopenharmony_ci	unsigned int bytes_read = 0;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (!couldbe_mf_symlink(fattr))
2628c2ecf20Sopenharmony_ci		/* it's not a symlink */
2638c2ecf20Sopenharmony_ci		return 0;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
2668c2ecf20Sopenharmony_ci	if (!buf)
2678c2ecf20Sopenharmony_ci		return -ENOMEM;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (tcon->ses->server->ops->query_mf_symlink)
2708c2ecf20Sopenharmony_ci		rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon,
2718c2ecf20Sopenharmony_ci					      cifs_sb, path, buf, &bytes_read);
2728c2ecf20Sopenharmony_ci	else
2738c2ecf20Sopenharmony_ci		rc = -ENOSYS;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (rc)
2768c2ecf20Sopenharmony_ci		goto out;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (bytes_read == 0) /* not a symlink */
2798c2ecf20Sopenharmony_ci		goto out;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	rc = parse_mf_symlink(buf, bytes_read, &link_len, NULL);
2828c2ecf20Sopenharmony_ci	if (rc == -EINVAL) {
2838c2ecf20Sopenharmony_ci		/* it's not a symlink */
2848c2ecf20Sopenharmony_ci		rc = 0;
2858c2ecf20Sopenharmony_ci		goto out;
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (rc != 0)
2898c2ecf20Sopenharmony_ci		goto out;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* it is a symlink */
2928c2ecf20Sopenharmony_ci	fattr->cf_eof = link_len;
2938c2ecf20Sopenharmony_ci	fattr->cf_mode &= ~S_IFMT;
2948c2ecf20Sopenharmony_ci	fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
2958c2ecf20Sopenharmony_ci	fattr->cf_dtype = DT_LNK;
2968c2ecf20Sopenharmony_ciout:
2978c2ecf20Sopenharmony_ci	kfree(buf);
2988c2ecf20Sopenharmony_ci	return rc;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci/*
3028c2ecf20Sopenharmony_ci * SMB 1.0 Protocol specific functions
3038c2ecf20Sopenharmony_ci */
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ciint
3068c2ecf20Sopenharmony_cicifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
3078c2ecf20Sopenharmony_ci		      struct cifs_sb_info *cifs_sb, const unsigned char *path,
3088c2ecf20Sopenharmony_ci		      char *pbuf, unsigned int *pbytes_read)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	int rc;
3118c2ecf20Sopenharmony_ci	int oplock = 0;
3128c2ecf20Sopenharmony_ci	struct cifs_fid fid;
3138c2ecf20Sopenharmony_ci	struct cifs_open_parms oparms;
3148c2ecf20Sopenharmony_ci	struct cifs_io_parms io_parms = {0};
3158c2ecf20Sopenharmony_ci	int buf_type = CIFS_NO_BUFFER;
3168c2ecf20Sopenharmony_ci	FILE_ALL_INFO file_info;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	oparms.tcon = tcon;
3198c2ecf20Sopenharmony_ci	oparms.cifs_sb = cifs_sb;
3208c2ecf20Sopenharmony_ci	oparms.desired_access = GENERIC_READ;
3218c2ecf20Sopenharmony_ci	oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR);
3228c2ecf20Sopenharmony_ci	oparms.disposition = FILE_OPEN;
3238c2ecf20Sopenharmony_ci	oparms.path = path;
3248c2ecf20Sopenharmony_ci	oparms.fid = &fid;
3258c2ecf20Sopenharmony_ci	oparms.reconnect = false;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	rc = CIFS_open(xid, &oparms, &oplock, &file_info);
3288c2ecf20Sopenharmony_ci	if (rc)
3298c2ecf20Sopenharmony_ci		return rc;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
3328c2ecf20Sopenharmony_ci		rc = -ENOENT;
3338c2ecf20Sopenharmony_ci		/* it's not a symlink */
3348c2ecf20Sopenharmony_ci		goto out;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	io_parms.netfid = fid.netfid;
3388c2ecf20Sopenharmony_ci	io_parms.pid = current->tgid;
3398c2ecf20Sopenharmony_ci	io_parms.tcon = tcon;
3408c2ecf20Sopenharmony_ci	io_parms.offset = 0;
3418c2ecf20Sopenharmony_ci	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
3448c2ecf20Sopenharmony_ciout:
3458c2ecf20Sopenharmony_ci	CIFSSMBClose(xid, tcon, fid.netfid);
3468c2ecf20Sopenharmony_ci	return rc;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ciint
3508c2ecf20Sopenharmony_cicifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
3518c2ecf20Sopenharmony_ci		       struct cifs_sb_info *cifs_sb, const unsigned char *path,
3528c2ecf20Sopenharmony_ci		       char *pbuf, unsigned int *pbytes_written)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	int rc;
3558c2ecf20Sopenharmony_ci	int oplock = 0;
3568c2ecf20Sopenharmony_ci	struct cifs_fid fid;
3578c2ecf20Sopenharmony_ci	struct cifs_open_parms oparms;
3588c2ecf20Sopenharmony_ci	struct cifs_io_parms io_parms = {0};
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	oparms.tcon = tcon;
3618c2ecf20Sopenharmony_ci	oparms.cifs_sb = cifs_sb;
3628c2ecf20Sopenharmony_ci	oparms.desired_access = GENERIC_WRITE;
3638c2ecf20Sopenharmony_ci	oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR);
3648c2ecf20Sopenharmony_ci	oparms.disposition = FILE_CREATE;
3658c2ecf20Sopenharmony_ci	oparms.path = path;
3668c2ecf20Sopenharmony_ci	oparms.fid = &fid;
3678c2ecf20Sopenharmony_ci	oparms.reconnect = false;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	rc = CIFS_open(xid, &oparms, &oplock, NULL);
3708c2ecf20Sopenharmony_ci	if (rc)
3718c2ecf20Sopenharmony_ci		return rc;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	io_parms.netfid = fid.netfid;
3748c2ecf20Sopenharmony_ci	io_parms.pid = current->tgid;
3758c2ecf20Sopenharmony_ci	io_parms.tcon = tcon;
3768c2ecf20Sopenharmony_ci	io_parms.offset = 0;
3778c2ecf20Sopenharmony_ci	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf);
3808c2ecf20Sopenharmony_ci	CIFSSMBClose(xid, tcon, fid.netfid);
3818c2ecf20Sopenharmony_ci	return rc;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci/*
3858c2ecf20Sopenharmony_ci * SMB 2.1/SMB3 Protocol specific functions
3868c2ecf20Sopenharmony_ci */
3878c2ecf20Sopenharmony_ciint
3888c2ecf20Sopenharmony_cismb3_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
3898c2ecf20Sopenharmony_ci		      struct cifs_sb_info *cifs_sb, const unsigned char *path,
3908c2ecf20Sopenharmony_ci		      char *pbuf, unsigned int *pbytes_read)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	int rc;
3938c2ecf20Sopenharmony_ci	struct cifs_fid fid;
3948c2ecf20Sopenharmony_ci	struct cifs_open_parms oparms;
3958c2ecf20Sopenharmony_ci	struct cifs_io_parms io_parms = {0};
3968c2ecf20Sopenharmony_ci	int buf_type = CIFS_NO_BUFFER;
3978c2ecf20Sopenharmony_ci	__le16 *utf16_path;
3988c2ecf20Sopenharmony_ci	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
3998c2ecf20Sopenharmony_ci	struct smb2_file_all_info *pfile_info = NULL;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	oparms.tcon = tcon;
4028c2ecf20Sopenharmony_ci	oparms.cifs_sb = cifs_sb;
4038c2ecf20Sopenharmony_ci	oparms.desired_access = GENERIC_READ;
4048c2ecf20Sopenharmony_ci	oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR);
4058c2ecf20Sopenharmony_ci	oparms.disposition = FILE_OPEN;
4068c2ecf20Sopenharmony_ci	oparms.fid = &fid;
4078c2ecf20Sopenharmony_ci	oparms.reconnect = false;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
4108c2ecf20Sopenharmony_ci	if (utf16_path == NULL)
4118c2ecf20Sopenharmony_ci		return -ENOMEM;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	pfile_info = kzalloc(sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
4148c2ecf20Sopenharmony_ci			     GFP_KERNEL);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (pfile_info == NULL) {
4178c2ecf20Sopenharmony_ci		kfree(utf16_path);
4188c2ecf20Sopenharmony_ci		return  -ENOMEM;
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, pfile_info, NULL,
4228c2ecf20Sopenharmony_ci		       NULL, NULL);
4238c2ecf20Sopenharmony_ci	if (rc)
4248c2ecf20Sopenharmony_ci		goto qmf_out_open_fail;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (pfile_info->EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
4278c2ecf20Sopenharmony_ci		/* it's not a symlink */
4288c2ecf20Sopenharmony_ci		rc = -ENOENT; /* Is there a better rc to return? */
4298c2ecf20Sopenharmony_ci		goto qmf_out;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	io_parms.netfid = fid.netfid;
4338c2ecf20Sopenharmony_ci	io_parms.pid = current->tgid;
4348c2ecf20Sopenharmony_ci	io_parms.tcon = tcon;
4358c2ecf20Sopenharmony_ci	io_parms.offset = 0;
4368c2ecf20Sopenharmony_ci	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
4378c2ecf20Sopenharmony_ci	io_parms.persistent_fid = fid.persistent_fid;
4388c2ecf20Sopenharmony_ci	io_parms.volatile_fid = fid.volatile_fid;
4398c2ecf20Sopenharmony_ci	rc = SMB2_read(xid, &io_parms, pbytes_read, &pbuf, &buf_type);
4408c2ecf20Sopenharmony_ciqmf_out:
4418c2ecf20Sopenharmony_ci	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
4428c2ecf20Sopenharmony_ciqmf_out_open_fail:
4438c2ecf20Sopenharmony_ci	kfree(utf16_path);
4448c2ecf20Sopenharmony_ci	kfree(pfile_info);
4458c2ecf20Sopenharmony_ci	return rc;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ciint
4498c2ecf20Sopenharmony_cismb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
4508c2ecf20Sopenharmony_ci		       struct cifs_sb_info *cifs_sb, const unsigned char *path,
4518c2ecf20Sopenharmony_ci		       char *pbuf, unsigned int *pbytes_written)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	int rc;
4548c2ecf20Sopenharmony_ci	struct cifs_fid fid;
4558c2ecf20Sopenharmony_ci	struct cifs_open_parms oparms;
4568c2ecf20Sopenharmony_ci	struct cifs_io_parms io_parms = {0};
4578c2ecf20Sopenharmony_ci	__le16 *utf16_path;
4588c2ecf20Sopenharmony_ci	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
4598c2ecf20Sopenharmony_ci	struct kvec iov[2];
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: path: %s\n", __func__, path);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
4648c2ecf20Sopenharmony_ci	if (!utf16_path)
4658c2ecf20Sopenharmony_ci		return -ENOMEM;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	oparms.tcon = tcon;
4688c2ecf20Sopenharmony_ci	oparms.cifs_sb = cifs_sb;
4698c2ecf20Sopenharmony_ci	oparms.desired_access = GENERIC_WRITE;
4708c2ecf20Sopenharmony_ci	oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR);
4718c2ecf20Sopenharmony_ci	oparms.disposition = FILE_CREATE;
4728c2ecf20Sopenharmony_ci	oparms.fid = &fid;
4738c2ecf20Sopenharmony_ci	oparms.reconnect = false;
4748c2ecf20Sopenharmony_ci	oparms.mode = 0644;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,
4778c2ecf20Sopenharmony_ci		       NULL, NULL);
4788c2ecf20Sopenharmony_ci	if (rc) {
4798c2ecf20Sopenharmony_ci		kfree(utf16_path);
4808c2ecf20Sopenharmony_ci		return rc;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	io_parms.netfid = fid.netfid;
4848c2ecf20Sopenharmony_ci	io_parms.pid = current->tgid;
4858c2ecf20Sopenharmony_ci	io_parms.tcon = tcon;
4868c2ecf20Sopenharmony_ci	io_parms.offset = 0;
4878c2ecf20Sopenharmony_ci	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
4888c2ecf20Sopenharmony_ci	io_parms.persistent_fid = fid.persistent_fid;
4898c2ecf20Sopenharmony_ci	io_parms.volatile_fid = fid.volatile_fid;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* iov[0] is reserved for smb header */
4928c2ecf20Sopenharmony_ci	iov[1].iov_base = pbuf;
4938c2ecf20Sopenharmony_ci	iov[1].iov_len = CIFS_MF_SYMLINK_FILE_SIZE;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	rc = SMB2_write(xid, &io_parms, pbytes_written, iov, 1);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	/* Make sure we wrote all of the symlink data */
4988c2ecf20Sopenharmony_ci	if ((rc == 0) && (*pbytes_written != CIFS_MF_SYMLINK_FILE_SIZE))
4998c2ecf20Sopenharmony_ci		rc = -EIO;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	kfree(utf16_path);
5048c2ecf20Sopenharmony_ci	return rc;
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci/*
5088c2ecf20Sopenharmony_ci * M-F Symlink Functions - End
5098c2ecf20Sopenharmony_ci */
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ciint
5128c2ecf20Sopenharmony_cicifs_hardlink(struct dentry *old_file, struct inode *inode,
5138c2ecf20Sopenharmony_ci	      struct dentry *direntry)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	int rc = -EACCES;
5168c2ecf20Sopenharmony_ci	unsigned int xid;
5178c2ecf20Sopenharmony_ci	char *from_name = NULL;
5188c2ecf20Sopenharmony_ci	char *to_name = NULL;
5198c2ecf20Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
5208c2ecf20Sopenharmony_ci	struct tcon_link *tlink;
5218c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon;
5228c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server;
5238c2ecf20Sopenharmony_ci	struct cifsInodeInfo *cifsInode;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
5268c2ecf20Sopenharmony_ci	if (IS_ERR(tlink))
5278c2ecf20Sopenharmony_ci		return PTR_ERR(tlink);
5288c2ecf20Sopenharmony_ci	tcon = tlink_tcon(tlink);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	xid = get_xid();
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	from_name = build_path_from_dentry(old_file);
5338c2ecf20Sopenharmony_ci	to_name = build_path_from_dentry(direntry);
5348c2ecf20Sopenharmony_ci	if ((from_name == NULL) || (to_name == NULL)) {
5358c2ecf20Sopenharmony_ci		rc = -ENOMEM;
5368c2ecf20Sopenharmony_ci		goto cifs_hl_exit;
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	if (tcon->unix_ext)
5408c2ecf20Sopenharmony_ci		rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name,
5418c2ecf20Sopenharmony_ci					    cifs_sb->local_nls,
5428c2ecf20Sopenharmony_ci					    cifs_remap(cifs_sb));
5438c2ecf20Sopenharmony_ci	else {
5448c2ecf20Sopenharmony_ci		server = tcon->ses->server;
5458c2ecf20Sopenharmony_ci		if (!server->ops->create_hardlink) {
5468c2ecf20Sopenharmony_ci			rc = -ENOSYS;
5478c2ecf20Sopenharmony_ci			goto cifs_hl_exit;
5488c2ecf20Sopenharmony_ci		}
5498c2ecf20Sopenharmony_ci		rc = server->ops->create_hardlink(xid, tcon, from_name, to_name,
5508c2ecf20Sopenharmony_ci						  cifs_sb);
5518c2ecf20Sopenharmony_ci		if ((rc == -EIO) || (rc == -EINVAL))
5528c2ecf20Sopenharmony_ci			rc = -EOPNOTSUPP;
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	d_drop(direntry);	/* force new lookup from server of target */
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/*
5588c2ecf20Sopenharmony_ci	 * if source file is cached (oplocked) revalidate will not go to server
5598c2ecf20Sopenharmony_ci	 * until the file is closed or oplock broken so update nlinks locally
5608c2ecf20Sopenharmony_ci	 */
5618c2ecf20Sopenharmony_ci	if (d_really_is_positive(old_file)) {
5628c2ecf20Sopenharmony_ci		cifsInode = CIFS_I(d_inode(old_file));
5638c2ecf20Sopenharmony_ci		if (rc == 0) {
5648c2ecf20Sopenharmony_ci			spin_lock(&d_inode(old_file)->i_lock);
5658c2ecf20Sopenharmony_ci			inc_nlink(d_inode(old_file));
5668c2ecf20Sopenharmony_ci			spin_unlock(&d_inode(old_file)->i_lock);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci			/*
5698c2ecf20Sopenharmony_ci			 * parent dir timestamps will update from srv within a
5708c2ecf20Sopenharmony_ci			 * second, would it really be worth it to set the parent
5718c2ecf20Sopenharmony_ci			 * dir cifs inode time to zero to force revalidate
5728c2ecf20Sopenharmony_ci			 * (faster) for it too?
5738c2ecf20Sopenharmony_ci			 */
5748c2ecf20Sopenharmony_ci		}
5758c2ecf20Sopenharmony_ci		/*
5768c2ecf20Sopenharmony_ci		 * if not oplocked will force revalidate to get info on source
5778c2ecf20Sopenharmony_ci		 * file from srv.  Note Samba server prior to 4.2 has bug -
5788c2ecf20Sopenharmony_ci		 * not updating src file ctime on hardlinks but Windows servers
5798c2ecf20Sopenharmony_ci		 * handle it properly
5808c2ecf20Sopenharmony_ci		 */
5818c2ecf20Sopenharmony_ci		cifsInode->time = 0;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci		/*
5848c2ecf20Sopenharmony_ci		 * Will update parent dir timestamps from srv within a second.
5858c2ecf20Sopenharmony_ci		 * Would it really be worth it to set the parent dir (cifs
5868c2ecf20Sopenharmony_ci		 * inode) time field to zero to force revalidate on parent
5878c2ecf20Sopenharmony_ci		 * directory faster ie
5888c2ecf20Sopenharmony_ci		 *
5898c2ecf20Sopenharmony_ci		 * CIFS_I(inode)->time = 0;
5908c2ecf20Sopenharmony_ci		 */
5918c2ecf20Sopenharmony_ci	}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_cicifs_hl_exit:
5948c2ecf20Sopenharmony_ci	kfree(from_name);
5958c2ecf20Sopenharmony_ci	kfree(to_name);
5968c2ecf20Sopenharmony_ci	free_xid(xid);
5978c2ecf20Sopenharmony_ci	cifs_put_tlink(tlink);
5988c2ecf20Sopenharmony_ci	return rc;
5998c2ecf20Sopenharmony_ci}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ciconst char *
6028c2ecf20Sopenharmony_cicifs_get_link(struct dentry *direntry, struct inode *inode,
6038c2ecf20Sopenharmony_ci	      struct delayed_call *done)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	int rc = -ENOMEM;
6068c2ecf20Sopenharmony_ci	unsigned int xid;
6078c2ecf20Sopenharmony_ci	char *full_path = NULL;
6088c2ecf20Sopenharmony_ci	char *target_path = NULL;
6098c2ecf20Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
6108c2ecf20Sopenharmony_ci	struct tcon_link *tlink = NULL;
6118c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon;
6128c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	if (!direntry)
6158c2ecf20Sopenharmony_ci		return ERR_PTR(-ECHILD);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	xid = get_xid();
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
6208c2ecf20Sopenharmony_ci	if (IS_ERR(tlink)) {
6218c2ecf20Sopenharmony_ci		free_xid(xid);
6228c2ecf20Sopenharmony_ci		return ERR_CAST(tlink);
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci	tcon = tlink_tcon(tlink);
6258c2ecf20Sopenharmony_ci	server = tcon->ses->server;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	full_path = build_path_from_dentry(direntry);
6288c2ecf20Sopenharmony_ci	if (!full_path) {
6298c2ecf20Sopenharmony_ci		free_xid(xid);
6308c2ecf20Sopenharmony_ci		cifs_put_tlink(tlink);
6318c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
6328c2ecf20Sopenharmony_ci	}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, inode);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	rc = -EACCES;
6378c2ecf20Sopenharmony_ci	/*
6388c2ecf20Sopenharmony_ci	 * First try Minshall+French Symlinks, if configured
6398c2ecf20Sopenharmony_ci	 * and fallback to UNIX Extensions Symlinks.
6408c2ecf20Sopenharmony_ci	 */
6418c2ecf20Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
6428c2ecf20Sopenharmony_ci		rc = query_mf_symlink(xid, tcon, cifs_sb, full_path,
6438c2ecf20Sopenharmony_ci				      &target_path);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (rc != 0 && server->ops->query_symlink) {
6468c2ecf20Sopenharmony_ci		struct cifsInodeInfo *cifsi = CIFS_I(inode);
6478c2ecf20Sopenharmony_ci		bool reparse_point = false;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci		if (cifsi->cifsAttrs & ATTR_REPARSE)
6508c2ecf20Sopenharmony_ci			reparse_point = true;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci		rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
6538c2ecf20Sopenharmony_ci						&target_path, reparse_point);
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	kfree(full_path);
6578c2ecf20Sopenharmony_ci	free_xid(xid);
6588c2ecf20Sopenharmony_ci	cifs_put_tlink(tlink);
6598c2ecf20Sopenharmony_ci	if (rc != 0) {
6608c2ecf20Sopenharmony_ci		kfree(target_path);
6618c2ecf20Sopenharmony_ci		return ERR_PTR(rc);
6628c2ecf20Sopenharmony_ci	}
6638c2ecf20Sopenharmony_ci	set_delayed_call(done, kfree_link, target_path);
6648c2ecf20Sopenharmony_ci	return target_path;
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ciint
6688c2ecf20Sopenharmony_cicifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	int rc = -EOPNOTSUPP;
6718c2ecf20Sopenharmony_ci	unsigned int xid;
6728c2ecf20Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
6738c2ecf20Sopenharmony_ci	struct tcon_link *tlink;
6748c2ecf20Sopenharmony_ci	struct cifs_tcon *pTcon;
6758c2ecf20Sopenharmony_ci	char *full_path = NULL;
6768c2ecf20Sopenharmony_ci	struct inode *newinode = NULL;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	xid = get_xid();
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
6818c2ecf20Sopenharmony_ci	if (IS_ERR(tlink)) {
6828c2ecf20Sopenharmony_ci		rc = PTR_ERR(tlink);
6838c2ecf20Sopenharmony_ci		goto symlink_exit;
6848c2ecf20Sopenharmony_ci	}
6858c2ecf20Sopenharmony_ci	pTcon = tlink_tcon(tlink);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	full_path = build_path_from_dentry(direntry);
6888c2ecf20Sopenharmony_ci	if (full_path == NULL) {
6898c2ecf20Sopenharmony_ci		rc = -ENOMEM;
6908c2ecf20Sopenharmony_ci		goto symlink_exit;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "Full path: %s\n", full_path);
6948c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "symname is %s\n", symname);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	/* BB what if DFS and this volume is on different share? BB */
6978c2ecf20Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
6988c2ecf20Sopenharmony_ci		rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);
6998c2ecf20Sopenharmony_ci	else if (pTcon->unix_ext)
7008c2ecf20Sopenharmony_ci		rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
7018c2ecf20Sopenharmony_ci					   cifs_sb->local_nls,
7028c2ecf20Sopenharmony_ci					   cifs_remap(cifs_sb));
7038c2ecf20Sopenharmony_ci	/* else
7048c2ecf20Sopenharmony_ci	   rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
7058c2ecf20Sopenharmony_ci					cifs_sb_target->local_nls); */
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	if (rc == 0) {
7088c2ecf20Sopenharmony_ci		if (pTcon->posix_extensions)
7098c2ecf20Sopenharmony_ci			rc = smb311_posix_get_inode_info(&newinode, full_path, inode->i_sb, xid);
7108c2ecf20Sopenharmony_ci		else if (pTcon->unix_ext)
7118c2ecf20Sopenharmony_ci			rc = cifs_get_inode_info_unix(&newinode, full_path,
7128c2ecf20Sopenharmony_ci						      inode->i_sb, xid);
7138c2ecf20Sopenharmony_ci		else
7148c2ecf20Sopenharmony_ci			rc = cifs_get_inode_info(&newinode, full_path, NULL,
7158c2ecf20Sopenharmony_ci						 inode->i_sb, xid, NULL);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		if (rc != 0) {
7188c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "Create symlink ok, getinodeinfo fail rc = %d\n",
7198c2ecf20Sopenharmony_ci				 rc);
7208c2ecf20Sopenharmony_ci		} else {
7218c2ecf20Sopenharmony_ci			d_instantiate(direntry, newinode);
7228c2ecf20Sopenharmony_ci		}
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_cisymlink_exit:
7258c2ecf20Sopenharmony_ci	kfree(full_path);
7268c2ecf20Sopenharmony_ci	cifs_put_tlink(tlink);
7278c2ecf20Sopenharmony_ci	free_xid(xid);
7288c2ecf20Sopenharmony_ci	return rc;
7298c2ecf20Sopenharmony_ci}
730