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);
572*00ecebcbSPaulo 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);
594*00ecebcbSPaulo 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 */
606*00ecebcbSPaulo 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
609*00ecebcbSPaulo 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 */
614*00ecebcbSPaulo Alcantara } else if (server->ops->create_reparse_symlink) {
615*00ecebcbSPaulo Alcantara rc = server->ops->create_reparse_symlink(xid, inode, direntry,
616*00ecebcbSPaulo Alcantara pTcon, full_path,
617*00ecebcbSPaulo Alcantara symname);
618*00ecebcbSPaulo Alcantara goto symlink_exit;
619*00ecebcbSPaulo 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