Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d244bf2d authored by Pavel Shilovsky's avatar Pavel Shilovsky Committed by Steve French
Browse files

CIFS: Implement follow_link for nounix CIFS mounts



by using a query reparse ioctl request.

Acked-by: default avatarJeff Layton <jlayton@redhat.com>
Signed-off-by: default avatarPavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
parent b42bf888
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -1495,11 +1495,12 @@ struct reparse_data {
	__u32	ReparseTag;
	__u16	ReparseDataLength;
	__u16	Reserved;
	__u16	AltNameOffset;
	__u16	AltNameLen;
	__u16	TargetNameOffset;
	__u16	TargetNameLen;
	char	LinkNamesBuf[1];
	__u16	SubstituteNameOffset;
	__u16	SubstituteNameLength;
	__u16	PrintNameOffset;
	__u16	PrintNameLength;
	__u32	Flags;
	char	PathBuffer[0];
} __attribute__((packed));

struct cifs_quota_data {
+3 −7
Original line number Diff line number Diff line
@@ -357,13 +357,9 @@ extern int CIFSSMBUnixQuerySymLink(const unsigned int xid,
			struct cifs_tcon *tcon,
			const unsigned char *searchName, char **syminfo,
			const struct nls_table *nls_codepage);
#ifdef CONFIG_CIFS_SYMLINK_EXPERIMENTAL
extern int CIFSSMBQueryReparseLinkInfo(const unsigned int xid,
			struct cifs_tcon *tcon,
			const unsigned char *searchName,
			char *symlinkinfo, const int buflen, __u16 fid,
extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
			       __u16 fid, char **symlinkinfo,
			       const struct nls_table *nls_codepage);
#endif /* temporarily unused until cifs_symlink fixed */
extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon,
			const char *fileName, const int disposition,
			const int access_flags, const int omode,
+51 −59
Original line number Diff line number Diff line
@@ -3067,7 +3067,6 @@ CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
	return rc;
}

#ifdef CONFIG_CIFS_SYMLINK_EXPERIMENTAL
/*
 *	Recent Windows versions now create symlinks more frequently
 *	and they use the "reparse point" mechanism below.  We can of course
@@ -3079,18 +3078,22 @@ CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
 *	it is not compiled in by default until callers fixed up and more tested.
 */
int
CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon,
			const unsigned char *searchName,
			char *symlinkinfo, const int buflen, __u16 fid,
CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
		    __u16 fid, char **symlinkinfo,
		    const struct nls_table *nls_codepage)
{
	int rc = 0;
	int bytes_returned;
	struct smb_com_transaction_ioctl_req *pSMB;
	struct smb_com_transaction_ioctl_rsp *pSMBr;
	bool is_unicode;
	unsigned int sub_len;
	char *sub_start;
	struct reparse_data *reparse_buf;
	__u32 data_offset, data_count;
	char *end_of_smb;

	cifs_dbg(FYI, "In Windows reparse style QueryLink for path %s\n",
		 searchName);
	cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid);
	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
@@ -3119,66 +3122,55 @@ CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
	if (rc) {
		cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc);
	} else {		/* decode response */
		__u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
		__u32 data_count = le32_to_cpu(pSMBr->DataCount);
		goto qreparse_out;
	}

	data_offset = le32_to_cpu(pSMBr->DataOffset);
	data_count = le32_to_cpu(pSMBr->DataCount);
	if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) {
		/* BB also check enough total bytes returned */
		rc = -EIO;	/* bad smb */
		goto qreparse_out;
	}
		if (data_count && (data_count < 2048)) {
			char *end_of_smb = 2 /* sizeof byte count */ +
			       get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount;

			struct reparse_data *reparse_buf =
						(struct reparse_data *)
						((char *)&pSMBr->hdr.Protocol
								 + data_offset);
			if ((char *)reparse_buf >= end_of_smb) {
	if (!data_count || (data_count > 2048)) {
		rc = -EIO;
		cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n");
		goto qreparse_out;
	}
			if ((reparse_buf->LinkNamesBuf +
				reparse_buf->TargetNameOffset +
				reparse_buf->TargetNameLen) > end_of_smb) {
				cifs_dbg(FYI, "reparse buf beyond SMB\n");
	end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount;
	reparse_buf = (struct reparse_data *)
				((char *)&pSMBr->hdr.Protocol + data_offset);
	if ((char *)reparse_buf >= end_of_smb) {
		rc = -EIO;
		goto qreparse_out;
	}

			if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
				cifs_from_ucs2(symlinkinfo, (__le16 *)
						(reparse_buf->LinkNamesBuf +
						reparse_buf->TargetNameOffset),
						buflen,
						reparse_buf->TargetNameLen,
						nls_codepage, 0);
			} else { /* ASCII names */
				strncpy(symlinkinfo,
					reparse_buf->LinkNamesBuf +
					reparse_buf->TargetNameOffset,
					min_t(const int, buflen,
					   reparse_buf->TargetNameLen));
			}
		} else {
	if ((reparse_buf->PathBuffer + reparse_buf->PrintNameOffset +
				reparse_buf->PrintNameLength) > end_of_smb) {
		cifs_dbg(FYI, "reparse buf beyond SMB\n");
		rc = -EIO;
			cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n");
		}
		symlinkinfo[buflen] = 0; /* just in case so the caller
					does not go off the end of the buffer */
		cifs_dbg(FYI, "readlink result - %s\n", symlinkinfo);
		goto qreparse_out;
	}
	sub_start = reparse_buf->SubstituteNameOffset + reparse_buf->PathBuffer;
	sub_len = reparse_buf->SubstituteNameLength;
	if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
		is_unicode = true;
	else
		is_unicode = false;

	/* BB FIXME investigate remapping reserved chars here */
	*symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode,
					       nls_codepage);
	if (!*symlinkinfo)
		rc = -ENOMEM;
qreparse_out:
	cifs_buf_release(pSMB);

	/* Note: On -EAGAIN error only caller can retry on handle based calls
		since file handle passed in no longer valid */

	/*
	 * Note: On -EAGAIN error only caller can retry on handle based calls
	 * since file handle passed in no longer valid.
	 */
	return rc;
}
#endif /* CIFS_SYMLINK_EXPERIMENTAL */ /* BB temporarily unused */

#ifdef CONFIG_CIFS_POSIX

+32 −0
Original line number Diff line number Diff line
@@ -881,6 +881,37 @@ cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
			   (__u8)type, wait, 0);
}

static int
cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
		   const char *full_path, char **target_path,
		   struct cifs_sb_info *cifs_sb)
{
	int rc;
	int oplock = 0;
	__u16 netfid;

	cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);

	rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
			 FILE_READ_ATTRIBUTES, OPEN_REPARSE_POINT, &netfid,
			 &oplock, NULL, cifs_sb->local_nls,
			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
	if (rc)
		return rc;

	rc = CIFSSMBQuerySymLink(xid, tcon, netfid, target_path,
				 cifs_sb->local_nls);
	if (rc) {
		CIFSSMBClose(xid, tcon, netfid);
		return rc;
	}

	convert_delimiter(*target_path, '/');
	CIFSSMBClose(xid, tcon, netfid);
	cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
	return rc;
}

struct smb_version_operations smb1_operations = {
	.send_cancel = send_nt_cancel,
	.compare_fids = cifs_compare_fids,
@@ -927,6 +958,7 @@ struct smb_version_operations smb1_operations = {
	.rename_pending_delete = cifs_rename_pending_delete,
	.rename = CIFSSMBRename,
	.create_hardlink = CIFSCreateHardLink,
	.query_symlink = cifs_query_symlink,
	.open = cifs_open_file,
	.set_fid = cifs_set_fid,
	.close = cifs_close_file,