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