xref: /OK3568_Linux_fs/kernel/fs/cifs/cifssmb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  *   fs/cifs/cifssmb.c
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  *   Copyright (C) International Business Machines  Corp., 2002,2010
5*4882a593Smuzhiyun  *   Author(s): Steve French (sfrench@us.ibm.com)
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *   Contains the routines for constructing the SMB PDUs themselves
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  *   This library is free software; you can redistribute it and/or modify
10*4882a593Smuzhiyun  *   it under the terms of the GNU Lesser General Public License as published
11*4882a593Smuzhiyun  *   by the Free Software Foundation; either version 2.1 of the License, or
12*4882a593Smuzhiyun  *   (at your option) any later version.
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  *   This library is distributed in the hope that it will be useful,
15*4882a593Smuzhiyun  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16*4882a593Smuzhiyun  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
17*4882a593Smuzhiyun  *   the GNU Lesser General Public License for more details.
18*4882a593Smuzhiyun  *
19*4882a593Smuzhiyun  *   You should have received a copy of the GNU Lesser General Public License
20*4882a593Smuzhiyun  *   along with this library; if not, write to the Free Software
21*4882a593Smuzhiyun  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22*4882a593Smuzhiyun  */
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun  /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c   */
25*4882a593Smuzhiyun  /* These are mostly routines that operate on a pathname, or on a tree id     */
26*4882a593Smuzhiyun  /* (mounted volume), but there are eight handle based routines which must be */
27*4882a593Smuzhiyun  /* treated slightly differently for reconnection purposes since we never     */
28*4882a593Smuzhiyun  /* want to reuse a stale file handle and only the caller knows the file info */
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #include <linux/fs.h>
31*4882a593Smuzhiyun #include <linux/kernel.h>
32*4882a593Smuzhiyun #include <linux/vfs.h>
33*4882a593Smuzhiyun #include <linux/slab.h>
34*4882a593Smuzhiyun #include <linux/posix_acl_xattr.h>
35*4882a593Smuzhiyun #include <linux/pagemap.h>
36*4882a593Smuzhiyun #include <linux/swap.h>
37*4882a593Smuzhiyun #include <linux/task_io_accounting_ops.h>
38*4882a593Smuzhiyun #include <linux/uaccess.h>
39*4882a593Smuzhiyun #include "cifspdu.h"
40*4882a593Smuzhiyun #include "cifsglob.h"
41*4882a593Smuzhiyun #include "cifsacl.h"
42*4882a593Smuzhiyun #include "cifsproto.h"
43*4882a593Smuzhiyun #include "cifs_unicode.h"
44*4882a593Smuzhiyun #include "cifs_debug.h"
45*4882a593Smuzhiyun #include "smb2proto.h"
46*4882a593Smuzhiyun #include "fscache.h"
47*4882a593Smuzhiyun #include "smbdirect.h"
48*4882a593Smuzhiyun #ifdef CONFIG_CIFS_DFS_UPCALL
49*4882a593Smuzhiyun #include "dfs_cache.h"
50*4882a593Smuzhiyun #endif
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun #ifdef CONFIG_CIFS_POSIX
53*4882a593Smuzhiyun static struct {
54*4882a593Smuzhiyun 	int index;
55*4882a593Smuzhiyun 	char *name;
56*4882a593Smuzhiyun } protocols[] = {
57*4882a593Smuzhiyun #ifdef CONFIG_CIFS_WEAK_PW_HASH
58*4882a593Smuzhiyun 	{LANMAN_PROT, "\2LM1.2X002"},
59*4882a593Smuzhiyun 	{LANMAN2_PROT, "\2LANMAN2.1"},
60*4882a593Smuzhiyun #endif /* weak password hashing for legacy clients */
61*4882a593Smuzhiyun 	{CIFS_PROT, "\2NT LM 0.12"},
62*4882a593Smuzhiyun 	{POSIX_PROT, "\2POSIX 2"},
63*4882a593Smuzhiyun 	{BAD_PROT, "\2"}
64*4882a593Smuzhiyun };
65*4882a593Smuzhiyun #else
66*4882a593Smuzhiyun static struct {
67*4882a593Smuzhiyun 	int index;
68*4882a593Smuzhiyun 	char *name;
69*4882a593Smuzhiyun } protocols[] = {
70*4882a593Smuzhiyun #ifdef CONFIG_CIFS_WEAK_PW_HASH
71*4882a593Smuzhiyun 	{LANMAN_PROT, "\2LM1.2X002"},
72*4882a593Smuzhiyun 	{LANMAN2_PROT, "\2LANMAN2.1"},
73*4882a593Smuzhiyun #endif /* weak password hashing for legacy clients */
74*4882a593Smuzhiyun 	{CIFS_PROT, "\2NT LM 0.12"},
75*4882a593Smuzhiyun 	{BAD_PROT, "\2"}
76*4882a593Smuzhiyun };
77*4882a593Smuzhiyun #endif
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun /* define the number of elements in the cifs dialect array */
80*4882a593Smuzhiyun #ifdef CONFIG_CIFS_POSIX
81*4882a593Smuzhiyun #ifdef CONFIG_CIFS_WEAK_PW_HASH
82*4882a593Smuzhiyun #define CIFS_NUM_PROT 4
83*4882a593Smuzhiyun #else
84*4882a593Smuzhiyun #define CIFS_NUM_PROT 2
85*4882a593Smuzhiyun #endif /* CIFS_WEAK_PW_HASH */
86*4882a593Smuzhiyun #else /* not posix */
87*4882a593Smuzhiyun #ifdef CONFIG_CIFS_WEAK_PW_HASH
88*4882a593Smuzhiyun #define CIFS_NUM_PROT 3
89*4882a593Smuzhiyun #else
90*4882a593Smuzhiyun #define CIFS_NUM_PROT 1
91*4882a593Smuzhiyun #endif /* CONFIG_CIFS_WEAK_PW_HASH */
92*4882a593Smuzhiyun #endif /* CIFS_POSIX */
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun /*
95*4882a593Smuzhiyun  * Mark as invalid, all open files on tree connections since they
96*4882a593Smuzhiyun  * were closed when session to server was lost.
97*4882a593Smuzhiyun  */
98*4882a593Smuzhiyun void
cifs_mark_open_files_invalid(struct cifs_tcon * tcon)99*4882a593Smuzhiyun cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	struct cifsFileInfo *open_file = NULL;
102*4882a593Smuzhiyun 	struct list_head *tmp;
103*4882a593Smuzhiyun 	struct list_head *tmp1;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	/* list all files open on tree connection and mark them invalid */
106*4882a593Smuzhiyun 	spin_lock(&tcon->open_file_lock);
107*4882a593Smuzhiyun 	list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
108*4882a593Smuzhiyun 		open_file = list_entry(tmp, struct cifsFileInfo, tlist);
109*4882a593Smuzhiyun 		open_file->invalidHandle = true;
110*4882a593Smuzhiyun 		open_file->oplock_break_cancelled = true;
111*4882a593Smuzhiyun 	}
112*4882a593Smuzhiyun 	spin_unlock(&tcon->open_file_lock);
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	mutex_lock(&tcon->crfid.fid_mutex);
115*4882a593Smuzhiyun 	tcon->crfid.is_valid = false;
116*4882a593Smuzhiyun 	/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
117*4882a593Smuzhiyun 	close_shroot_lease_locked(&tcon->crfid);
118*4882a593Smuzhiyun 	memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
119*4882a593Smuzhiyun 	mutex_unlock(&tcon->crfid.fid_mutex);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	/*
122*4882a593Smuzhiyun 	 * BB Add call to invalidate_inodes(sb) for all superblocks mounted
123*4882a593Smuzhiyun 	 * to this tcon.
124*4882a593Smuzhiyun 	 */
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun /* reconnect the socket, tcon, and smb session if needed */
128*4882a593Smuzhiyun static int
cifs_reconnect_tcon(struct cifs_tcon * tcon,int smb_command)129*4882a593Smuzhiyun cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun 	int rc;
132*4882a593Smuzhiyun 	struct cifs_ses *ses;
133*4882a593Smuzhiyun 	struct TCP_Server_Info *server;
134*4882a593Smuzhiyun 	struct nls_table *nls_codepage;
135*4882a593Smuzhiyun 	int retries;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	/*
138*4882a593Smuzhiyun 	 * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
139*4882a593Smuzhiyun 	 * tcp and smb session status done differently for those three - in the
140*4882a593Smuzhiyun 	 * calling routine
141*4882a593Smuzhiyun 	 */
142*4882a593Smuzhiyun 	if (!tcon)
143*4882a593Smuzhiyun 		return 0;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	ses = tcon->ses;
146*4882a593Smuzhiyun 	server = ses->server;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 	/*
149*4882a593Smuzhiyun 	 * only tree disconnect, open, and write, (and ulogoff which does not
150*4882a593Smuzhiyun 	 * have tcon) are allowed as we start force umount
151*4882a593Smuzhiyun 	 */
152*4882a593Smuzhiyun 	if (tcon->tidStatus == CifsExiting) {
153*4882a593Smuzhiyun 		if (smb_command != SMB_COM_WRITE_ANDX &&
154*4882a593Smuzhiyun 		    smb_command != SMB_COM_OPEN_ANDX &&
155*4882a593Smuzhiyun 		    smb_command != SMB_COM_TREE_DISCONNECT) {
156*4882a593Smuzhiyun 			cifs_dbg(FYI, "can not send cmd %d while umounting\n",
157*4882a593Smuzhiyun 				 smb_command);
158*4882a593Smuzhiyun 			return -ENODEV;
159*4882a593Smuzhiyun 		}
160*4882a593Smuzhiyun 	}
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	retries = server->nr_targets;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	/*
165*4882a593Smuzhiyun 	 * Give demultiplex thread up to 10 seconds to each target available for
166*4882a593Smuzhiyun 	 * reconnect -- should be greater than cifs socket timeout which is 7
167*4882a593Smuzhiyun 	 * seconds.
168*4882a593Smuzhiyun 	 */
169*4882a593Smuzhiyun 	while (server->tcpStatus == CifsNeedReconnect) {
170*4882a593Smuzhiyun 		rc = wait_event_interruptible_timeout(server->response_q,
171*4882a593Smuzhiyun 						      (server->tcpStatus != CifsNeedReconnect),
172*4882a593Smuzhiyun 						      10 * HZ);
173*4882a593Smuzhiyun 		if (rc < 0) {
174*4882a593Smuzhiyun 			cifs_dbg(FYI, "%s: aborting reconnect due to a received signal by the process\n",
175*4882a593Smuzhiyun 				 __func__);
176*4882a593Smuzhiyun 			return -ERESTARTSYS;
177*4882a593Smuzhiyun 		}
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 		/* are we still trying to reconnect? */
180*4882a593Smuzhiyun 		if (server->tcpStatus != CifsNeedReconnect)
181*4882a593Smuzhiyun 			break;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 		if (retries && --retries)
184*4882a593Smuzhiyun 			continue;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 		/*
187*4882a593Smuzhiyun 		 * on "soft" mounts we wait once. Hard mounts keep
188*4882a593Smuzhiyun 		 * retrying until process is killed or server comes
189*4882a593Smuzhiyun 		 * back on-line
190*4882a593Smuzhiyun 		 */
191*4882a593Smuzhiyun 		if (!tcon->retry) {
192*4882a593Smuzhiyun 			cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
193*4882a593Smuzhiyun 			return -EHOSTDOWN;
194*4882a593Smuzhiyun 		}
195*4882a593Smuzhiyun 		retries = server->nr_targets;
196*4882a593Smuzhiyun 	}
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	if (!ses->need_reconnect && !tcon->need_reconnect)
199*4882a593Smuzhiyun 		return 0;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	nls_codepage = load_nls_default();
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	/*
204*4882a593Smuzhiyun 	 * need to prevent multiple threads trying to simultaneously
205*4882a593Smuzhiyun 	 * reconnect the same SMB session
206*4882a593Smuzhiyun 	 */
207*4882a593Smuzhiyun 	mutex_lock(&ses->session_mutex);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	/*
210*4882a593Smuzhiyun 	 * Recheck after acquire mutex. If another thread is negotiating
211*4882a593Smuzhiyun 	 * and the server never sends an answer the socket will be closed
212*4882a593Smuzhiyun 	 * and tcpStatus set to reconnect.
213*4882a593Smuzhiyun 	 */
214*4882a593Smuzhiyun 	if (server->tcpStatus == CifsNeedReconnect) {
215*4882a593Smuzhiyun 		rc = -EHOSTDOWN;
216*4882a593Smuzhiyun 		mutex_unlock(&ses->session_mutex);
217*4882a593Smuzhiyun 		goto out;
218*4882a593Smuzhiyun 	}
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	rc = cifs_negotiate_protocol(0, ses);
221*4882a593Smuzhiyun 	if (rc == 0 && ses->need_reconnect)
222*4882a593Smuzhiyun 		rc = cifs_setup_session(0, ses, nls_codepage);
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	/* do we need to reconnect tcon? */
225*4882a593Smuzhiyun 	if (rc || !tcon->need_reconnect) {
226*4882a593Smuzhiyun 		mutex_unlock(&ses->session_mutex);
227*4882a593Smuzhiyun 		goto out;
228*4882a593Smuzhiyun 	}
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	cifs_mark_open_files_invalid(tcon);
231*4882a593Smuzhiyun 	rc = cifs_tree_connect(0, tcon, nls_codepage);
232*4882a593Smuzhiyun 	mutex_unlock(&ses->session_mutex);
233*4882a593Smuzhiyun 	cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	if (rc) {
236*4882a593Smuzhiyun 		pr_warn_once("reconnect tcon failed rc = %d\n", rc);
237*4882a593Smuzhiyun 		goto out;
238*4882a593Smuzhiyun 	}
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	atomic_inc(&tconInfoReconnectCount);
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	/* tell server Unix caps we support */
243*4882a593Smuzhiyun 	if (cap_unix(ses))
244*4882a593Smuzhiyun 		reset_cifs_unix_caps(0, tcon, NULL, NULL);
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	/*
247*4882a593Smuzhiyun 	 * Removed call to reopen open files here. It is safer (and faster) to
248*4882a593Smuzhiyun 	 * reopen files one at a time as needed in read and write.
249*4882a593Smuzhiyun 	 *
250*4882a593Smuzhiyun 	 * FIXME: what about file locks? don't we need to reclaim them ASAP?
251*4882a593Smuzhiyun 	 */
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun out:
254*4882a593Smuzhiyun 	/*
255*4882a593Smuzhiyun 	 * Check if handle based operation so we know whether we can continue
256*4882a593Smuzhiyun 	 * or not without returning to caller to reset file handle
257*4882a593Smuzhiyun 	 */
258*4882a593Smuzhiyun 	switch (smb_command) {
259*4882a593Smuzhiyun 	case SMB_COM_READ_ANDX:
260*4882a593Smuzhiyun 	case SMB_COM_WRITE_ANDX:
261*4882a593Smuzhiyun 	case SMB_COM_CLOSE:
262*4882a593Smuzhiyun 	case SMB_COM_FIND_CLOSE2:
263*4882a593Smuzhiyun 	case SMB_COM_LOCKING_ANDX:
264*4882a593Smuzhiyun 		rc = -EAGAIN;
265*4882a593Smuzhiyun 	}
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	unload_nls(nls_codepage);
268*4882a593Smuzhiyun 	return rc;
269*4882a593Smuzhiyun }
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun /* Allocate and return pointer to an SMB request buffer, and set basic
272*4882a593Smuzhiyun    SMB information in the SMB header.  If the return code is zero, this
273*4882a593Smuzhiyun    function must have filled in request_buf pointer */
274*4882a593Smuzhiyun static int
small_smb_init(int smb_command,int wct,struct cifs_tcon * tcon,void ** request_buf)275*4882a593Smuzhiyun small_smb_init(int smb_command, int wct, struct cifs_tcon *tcon,
276*4882a593Smuzhiyun 		void **request_buf)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun 	int rc;
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	rc = cifs_reconnect_tcon(tcon, smb_command);
281*4882a593Smuzhiyun 	if (rc)
282*4882a593Smuzhiyun 		return rc;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	*request_buf = cifs_small_buf_get();
285*4882a593Smuzhiyun 	if (*request_buf == NULL) {
286*4882a593Smuzhiyun 		/* BB should we add a retry in here if not a writepage? */
287*4882a593Smuzhiyun 		return -ENOMEM;
288*4882a593Smuzhiyun 	}
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	header_assemble((struct smb_hdr *) *request_buf, smb_command,
291*4882a593Smuzhiyun 			tcon, wct);
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	if (tcon != NULL)
294*4882a593Smuzhiyun 		cifs_stats_inc(&tcon->num_smbs_sent);
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	return 0;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun int
small_smb_init_no_tc(const int smb_command,const int wct,struct cifs_ses * ses,void ** request_buf)300*4882a593Smuzhiyun small_smb_init_no_tc(const int smb_command, const int wct,
301*4882a593Smuzhiyun 		     struct cifs_ses *ses, void **request_buf)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun 	int rc;
304*4882a593Smuzhiyun 	struct smb_hdr *buffer;
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	rc = small_smb_init(smb_command, wct, NULL, request_buf);
307*4882a593Smuzhiyun 	if (rc)
308*4882a593Smuzhiyun 		return rc;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	buffer = (struct smb_hdr *)*request_buf;
311*4882a593Smuzhiyun 	buffer->Mid = get_next_mid(ses->server);
312*4882a593Smuzhiyun 	if (ses->capabilities & CAP_UNICODE)
313*4882a593Smuzhiyun 		buffer->Flags2 |= SMBFLG2_UNICODE;
314*4882a593Smuzhiyun 	if (ses->capabilities & CAP_STATUS32)
315*4882a593Smuzhiyun 		buffer->Flags2 |= SMBFLG2_ERR_STATUS;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	/* uid, tid can stay at zero as set in header assemble */
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	/* BB add support for turning on the signing when
320*4882a593Smuzhiyun 	this function is used after 1st of session setup requests */
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	return rc;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun /* If the return code is zero, this function must fill in request_buf pointer */
326*4882a593Smuzhiyun static int
__smb_init(int smb_command,int wct,struct cifs_tcon * tcon,void ** request_buf,void ** response_buf)327*4882a593Smuzhiyun __smb_init(int smb_command, int wct, struct cifs_tcon *tcon,
328*4882a593Smuzhiyun 			void **request_buf, void **response_buf)
329*4882a593Smuzhiyun {
330*4882a593Smuzhiyun 	*request_buf = cifs_buf_get();
331*4882a593Smuzhiyun 	if (*request_buf == NULL) {
332*4882a593Smuzhiyun 		/* BB should we add a retry in here if not a writepage? */
333*4882a593Smuzhiyun 		return -ENOMEM;
334*4882a593Smuzhiyun 	}
335*4882a593Smuzhiyun     /* Although the original thought was we needed the response buf for  */
336*4882a593Smuzhiyun     /* potential retries of smb operations it turns out we can determine */
337*4882a593Smuzhiyun     /* from the mid flags when the request buffer can be resent without  */
338*4882a593Smuzhiyun     /* having to use a second distinct buffer for the response */
339*4882a593Smuzhiyun 	if (response_buf)
340*4882a593Smuzhiyun 		*response_buf = *request_buf;
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun 	header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
343*4882a593Smuzhiyun 			wct);
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	if (tcon != NULL)
346*4882a593Smuzhiyun 		cifs_stats_inc(&tcon->num_smbs_sent);
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	return 0;
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun /* If the return code is zero, this function must fill in request_buf pointer */
352*4882a593Smuzhiyun static int
smb_init(int smb_command,int wct,struct cifs_tcon * tcon,void ** request_buf,void ** response_buf)353*4882a593Smuzhiyun smb_init(int smb_command, int wct, struct cifs_tcon *tcon,
354*4882a593Smuzhiyun 	 void **request_buf, void **response_buf)
355*4882a593Smuzhiyun {
356*4882a593Smuzhiyun 	int rc;
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	rc = cifs_reconnect_tcon(tcon, smb_command);
359*4882a593Smuzhiyun 	if (rc)
360*4882a593Smuzhiyun 		return rc;
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
363*4882a593Smuzhiyun }
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun static int
smb_init_no_reconnect(int smb_command,int wct,struct cifs_tcon * tcon,void ** request_buf,void ** response_buf)366*4882a593Smuzhiyun smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon,
367*4882a593Smuzhiyun 			void **request_buf, void **response_buf)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun 	if (tcon->ses->need_reconnect || tcon->need_reconnect)
370*4882a593Smuzhiyun 		return -EHOSTDOWN;
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
373*4882a593Smuzhiyun }
374*4882a593Smuzhiyun 
validate_t2(struct smb_t2_rsp * pSMB)375*4882a593Smuzhiyun static int validate_t2(struct smb_t2_rsp *pSMB)
376*4882a593Smuzhiyun {
377*4882a593Smuzhiyun 	unsigned int total_size;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	/* check for plausible wct */
380*4882a593Smuzhiyun 	if (pSMB->hdr.WordCount < 10)
381*4882a593Smuzhiyun 		goto vt2_err;
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	/* check for parm and data offset going beyond end of smb */
384*4882a593Smuzhiyun 	if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 ||
385*4882a593Smuzhiyun 	    get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024)
386*4882a593Smuzhiyun 		goto vt2_err;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount);
389*4882a593Smuzhiyun 	if (total_size >= 512)
390*4882a593Smuzhiyun 		goto vt2_err;
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 	/* check that bcc is at least as big as parms + data, and that it is
393*4882a593Smuzhiyun 	 * less than negotiated smb buffer
394*4882a593Smuzhiyun 	 */
395*4882a593Smuzhiyun 	total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount);
396*4882a593Smuzhiyun 	if (total_size > get_bcc(&pSMB->hdr) ||
397*4882a593Smuzhiyun 	    total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE)
398*4882a593Smuzhiyun 		goto vt2_err;
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun 	return 0;
401*4882a593Smuzhiyun vt2_err:
402*4882a593Smuzhiyun 	cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
403*4882a593Smuzhiyun 		sizeof(struct smb_t2_rsp) + 16);
404*4882a593Smuzhiyun 	return -EINVAL;
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun static int
decode_ext_sec_blob(struct cifs_ses * ses,NEGOTIATE_RSP * pSMBr)408*4882a593Smuzhiyun decode_ext_sec_blob(struct cifs_ses *ses, NEGOTIATE_RSP *pSMBr)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun 	int	rc = 0;
411*4882a593Smuzhiyun 	u16	count;
412*4882a593Smuzhiyun 	char	*guid = pSMBr->u.extended_response.GUID;
413*4882a593Smuzhiyun 	struct TCP_Server_Info *server = ses->server;
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	count = get_bcc(&pSMBr->hdr);
416*4882a593Smuzhiyun 	if (count < SMB1_CLIENT_GUID_SIZE)
417*4882a593Smuzhiyun 		return -EIO;
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 	spin_lock(&cifs_tcp_ses_lock);
420*4882a593Smuzhiyun 	if (server->srv_count > 1) {
421*4882a593Smuzhiyun 		spin_unlock(&cifs_tcp_ses_lock);
422*4882a593Smuzhiyun 		if (memcmp(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE) != 0) {
423*4882a593Smuzhiyun 			cifs_dbg(FYI, "server UID changed\n");
424*4882a593Smuzhiyun 			memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE);
425*4882a593Smuzhiyun 		}
426*4882a593Smuzhiyun 	} else {
427*4882a593Smuzhiyun 		spin_unlock(&cifs_tcp_ses_lock);
428*4882a593Smuzhiyun 		memcpy(server->server_GUID, guid, SMB1_CLIENT_GUID_SIZE);
429*4882a593Smuzhiyun 	}
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	if (count == SMB1_CLIENT_GUID_SIZE) {
432*4882a593Smuzhiyun 		server->sec_ntlmssp = true;
433*4882a593Smuzhiyun 	} else {
434*4882a593Smuzhiyun 		count -= SMB1_CLIENT_GUID_SIZE;
435*4882a593Smuzhiyun 		rc = decode_negTokenInit(
436*4882a593Smuzhiyun 			pSMBr->u.extended_response.SecurityBlob, count, server);
437*4882a593Smuzhiyun 		if (rc != 1)
438*4882a593Smuzhiyun 			return -EINVAL;
439*4882a593Smuzhiyun 	}
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	return 0;
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun int
cifs_enable_signing(struct TCP_Server_Info * server,bool mnt_sign_required)445*4882a593Smuzhiyun cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required)
446*4882a593Smuzhiyun {
447*4882a593Smuzhiyun 	bool srv_sign_required = server->sec_mode & server->vals->signing_required;
448*4882a593Smuzhiyun 	bool srv_sign_enabled = server->sec_mode & server->vals->signing_enabled;
449*4882a593Smuzhiyun 	bool mnt_sign_enabled = global_secflags & CIFSSEC_MAY_SIGN;
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun 	/*
452*4882a593Smuzhiyun 	 * Is signing required by mnt options? If not then check
453*4882a593Smuzhiyun 	 * global_secflags to see if it is there.
454*4882a593Smuzhiyun 	 */
455*4882a593Smuzhiyun 	if (!mnt_sign_required)
456*4882a593Smuzhiyun 		mnt_sign_required = ((global_secflags & CIFSSEC_MUST_SIGN) ==
457*4882a593Smuzhiyun 						CIFSSEC_MUST_SIGN);
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 	/*
460*4882a593Smuzhiyun 	 * If signing is required then it's automatically enabled too,
461*4882a593Smuzhiyun 	 * otherwise, check to see if the secflags allow it.
462*4882a593Smuzhiyun 	 */
463*4882a593Smuzhiyun 	mnt_sign_enabled = mnt_sign_required ? mnt_sign_required :
464*4882a593Smuzhiyun 				(global_secflags & CIFSSEC_MAY_SIGN);
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	/* If server requires signing, does client allow it? */
467*4882a593Smuzhiyun 	if (srv_sign_required) {
468*4882a593Smuzhiyun 		if (!mnt_sign_enabled) {
469*4882a593Smuzhiyun 			cifs_dbg(VFS, "Server requires signing, but it's disabled in SecurityFlags!\n");
470*4882a593Smuzhiyun 			return -ENOTSUPP;
471*4882a593Smuzhiyun 		}
472*4882a593Smuzhiyun 		server->sign = true;
473*4882a593Smuzhiyun 	}
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 	/* If client requires signing, does server allow it? */
476*4882a593Smuzhiyun 	if (mnt_sign_required) {
477*4882a593Smuzhiyun 		if (!srv_sign_enabled) {
478*4882a593Smuzhiyun 			cifs_dbg(VFS, "Server does not support signing!\n");
479*4882a593Smuzhiyun 			return -ENOTSUPP;
480*4882a593Smuzhiyun 		}
481*4882a593Smuzhiyun 		server->sign = true;
482*4882a593Smuzhiyun 	}
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	if (cifs_rdma_enabled(server) && server->sign)
485*4882a593Smuzhiyun 		cifs_dbg(VFS, "Signing is enabled, and RDMA read/write will be disabled\n");
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 	return 0;
488*4882a593Smuzhiyun }
489*4882a593Smuzhiyun 
490*4882a593Smuzhiyun #ifdef CONFIG_CIFS_WEAK_PW_HASH
491*4882a593Smuzhiyun static int
decode_lanman_negprot_rsp(struct TCP_Server_Info * server,NEGOTIATE_RSP * pSMBr)492*4882a593Smuzhiyun decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
493*4882a593Smuzhiyun {
494*4882a593Smuzhiyun 	__s16 tmp;
495*4882a593Smuzhiyun 	struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun 	if (server->dialect != LANMAN_PROT && server->dialect != LANMAN2_PROT)
498*4882a593Smuzhiyun 		return -EOPNOTSUPP;
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun 	server->sec_mode = le16_to_cpu(rsp->SecurityMode);
501*4882a593Smuzhiyun 	server->maxReq = min_t(unsigned int,
502*4882a593Smuzhiyun 			       le16_to_cpu(rsp->MaxMpxCount),
503*4882a593Smuzhiyun 			       cifs_max_pending);
504*4882a593Smuzhiyun 	set_credits(server, server->maxReq);
505*4882a593Smuzhiyun 	server->maxBuf = le16_to_cpu(rsp->MaxBufSize);
506*4882a593Smuzhiyun 	/* set up max_read for readpages check */
507*4882a593Smuzhiyun 	server->max_read = server->maxBuf;
508*4882a593Smuzhiyun 	/* even though we do not use raw we might as well set this
509*4882a593Smuzhiyun 	accurately, in case we ever find a need for it */
510*4882a593Smuzhiyun 	if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
511*4882a593Smuzhiyun 		server->max_rw = 0xFF00;
512*4882a593Smuzhiyun 		server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
513*4882a593Smuzhiyun 	} else {
514*4882a593Smuzhiyun 		server->max_rw = 0;/* do not need to use raw anyway */
515*4882a593Smuzhiyun 		server->capabilities = CAP_MPX_MODE;
516*4882a593Smuzhiyun 	}
517*4882a593Smuzhiyun 	tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
518*4882a593Smuzhiyun 	if (tmp == -1) {
519*4882a593Smuzhiyun 		/* OS/2 often does not set timezone therefore
520*4882a593Smuzhiyun 		 * we must use server time to calc time zone.
521*4882a593Smuzhiyun 		 * Could deviate slightly from the right zone.
522*4882a593Smuzhiyun 		 * Smallest defined timezone difference is 15 minutes
523*4882a593Smuzhiyun 		 * (i.e. Nepal).  Rounding up/down is done to match
524*4882a593Smuzhiyun 		 * this requirement.
525*4882a593Smuzhiyun 		 */
526*4882a593Smuzhiyun 		int val, seconds, remain, result;
527*4882a593Smuzhiyun 		struct timespec64 ts;
528*4882a593Smuzhiyun 		time64_t utc = ktime_get_real_seconds();
529*4882a593Smuzhiyun 		ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
530*4882a593Smuzhiyun 				    rsp->SrvTime.Time, 0);
531*4882a593Smuzhiyun 		cifs_dbg(FYI, "SrvTime %lld sec since 1970 (utc: %lld) diff: %lld\n",
532*4882a593Smuzhiyun 			 ts.tv_sec, utc,
533*4882a593Smuzhiyun 			 utc - ts.tv_sec);
534*4882a593Smuzhiyun 		val = (int)(utc - ts.tv_sec);
535*4882a593Smuzhiyun 		seconds = abs(val);
536*4882a593Smuzhiyun 		result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
537*4882a593Smuzhiyun 		remain = seconds % MIN_TZ_ADJ;
538*4882a593Smuzhiyun 		if (remain >= (MIN_TZ_ADJ / 2))
539*4882a593Smuzhiyun 			result += MIN_TZ_ADJ;
540*4882a593Smuzhiyun 		if (val < 0)
541*4882a593Smuzhiyun 			result = -result;
542*4882a593Smuzhiyun 		server->timeAdj = result;
543*4882a593Smuzhiyun 	} else {
544*4882a593Smuzhiyun 		server->timeAdj = (int)tmp;
545*4882a593Smuzhiyun 		server->timeAdj *= 60; /* also in seconds */
546*4882a593Smuzhiyun 	}
547*4882a593Smuzhiyun 	cifs_dbg(FYI, "server->timeAdj: %d seconds\n", server->timeAdj);
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	/* BB get server time for time conversions and add
551*4882a593Smuzhiyun 	code to use it and timezone since this is not UTC */
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	if (rsp->EncryptionKeyLength ==
554*4882a593Smuzhiyun 			cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
555*4882a593Smuzhiyun 		memcpy(server->cryptkey, rsp->EncryptionKey,
556*4882a593Smuzhiyun 			CIFS_CRYPTO_KEY_SIZE);
557*4882a593Smuzhiyun 	} else if (server->sec_mode & SECMODE_PW_ENCRYPT) {
558*4882a593Smuzhiyun 		return -EIO; /* need cryptkey unless plain text */
559*4882a593Smuzhiyun 	}
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 	cifs_dbg(FYI, "LANMAN negotiated\n");
562*4882a593Smuzhiyun 	return 0;
563*4882a593Smuzhiyun }
564*4882a593Smuzhiyun #else
565*4882a593Smuzhiyun static inline int
decode_lanman_negprot_rsp(struct TCP_Server_Info * server,NEGOTIATE_RSP * pSMBr)566*4882a593Smuzhiyun decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)
567*4882a593Smuzhiyun {
568*4882a593Smuzhiyun 	cifs_dbg(VFS, "mount failed, cifs module not built with CIFS_WEAK_PW_HASH support\n");
569*4882a593Smuzhiyun 	return -EOPNOTSUPP;
570*4882a593Smuzhiyun }
571*4882a593Smuzhiyun #endif
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun static bool
should_set_ext_sec_flag(enum securityEnum sectype)574*4882a593Smuzhiyun should_set_ext_sec_flag(enum securityEnum sectype)
575*4882a593Smuzhiyun {
576*4882a593Smuzhiyun 	switch (sectype) {
577*4882a593Smuzhiyun 	case RawNTLMSSP:
578*4882a593Smuzhiyun 	case Kerberos:
579*4882a593Smuzhiyun 		return true;
580*4882a593Smuzhiyun 	case Unspecified:
581*4882a593Smuzhiyun 		if (global_secflags &
582*4882a593Smuzhiyun 		    (CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP))
583*4882a593Smuzhiyun 			return true;
584*4882a593Smuzhiyun 		fallthrough;
585*4882a593Smuzhiyun 	default:
586*4882a593Smuzhiyun 		return false;
587*4882a593Smuzhiyun 	}
588*4882a593Smuzhiyun }
589*4882a593Smuzhiyun 
590*4882a593Smuzhiyun int
CIFSSMBNegotiate(const unsigned int xid,struct cifs_ses * ses)591*4882a593Smuzhiyun CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
592*4882a593Smuzhiyun {
593*4882a593Smuzhiyun 	NEGOTIATE_REQ *pSMB;
594*4882a593Smuzhiyun 	NEGOTIATE_RSP *pSMBr;
595*4882a593Smuzhiyun 	int rc = 0;
596*4882a593Smuzhiyun 	int bytes_returned;
597*4882a593Smuzhiyun 	int i;
598*4882a593Smuzhiyun 	struct TCP_Server_Info *server = ses->server;
599*4882a593Smuzhiyun 	u16 count;
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	if (!server) {
602*4882a593Smuzhiyun 		WARN(1, "%s: server is NULL!\n", __func__);
603*4882a593Smuzhiyun 		return -EIO;
604*4882a593Smuzhiyun 	}
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
607*4882a593Smuzhiyun 		      (void **) &pSMB, (void **) &pSMBr);
608*4882a593Smuzhiyun 	if (rc)
609*4882a593Smuzhiyun 		return rc;
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 	pSMB->hdr.Mid = get_next_mid(server);
612*4882a593Smuzhiyun 	pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
613*4882a593Smuzhiyun 
614*4882a593Smuzhiyun 	if (should_set_ext_sec_flag(ses->sectype)) {
615*4882a593Smuzhiyun 		cifs_dbg(FYI, "Requesting extended security\n");
616*4882a593Smuzhiyun 		pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
617*4882a593Smuzhiyun 	}
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	count = 0;
620*4882a593Smuzhiyun 	/*
621*4882a593Smuzhiyun 	 * We know that all the name entries in the protocols array
622*4882a593Smuzhiyun 	 * are short (< 16 bytes anyway) and are NUL terminated.
623*4882a593Smuzhiyun 	 */
624*4882a593Smuzhiyun 	for (i = 0; i < CIFS_NUM_PROT; i++) {
625*4882a593Smuzhiyun 		size_t len = strlen(protocols[i].name) + 1;
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 		memcpy(pSMB->DialectsArray+count, protocols[i].name, len);
628*4882a593Smuzhiyun 		count += len;
629*4882a593Smuzhiyun 	}
630*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, count);
631*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(count);
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
634*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
635*4882a593Smuzhiyun 	if (rc != 0)
636*4882a593Smuzhiyun 		goto neg_err_exit;
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun 	server->dialect = le16_to_cpu(pSMBr->DialectIndex);
639*4882a593Smuzhiyun 	cifs_dbg(FYI, "Dialect: %d\n", server->dialect);
640*4882a593Smuzhiyun 	/* Check wct = 1 error case */
641*4882a593Smuzhiyun 	if ((pSMBr->hdr.WordCount < 13) || (server->dialect == BAD_PROT)) {
642*4882a593Smuzhiyun 		/* core returns wct = 1, but we do not ask for core - otherwise
643*4882a593Smuzhiyun 		small wct just comes when dialect index is -1 indicating we
644*4882a593Smuzhiyun 		could not negotiate a common dialect */
645*4882a593Smuzhiyun 		rc = -EOPNOTSUPP;
646*4882a593Smuzhiyun 		goto neg_err_exit;
647*4882a593Smuzhiyun 	} else if (pSMBr->hdr.WordCount == 13) {
648*4882a593Smuzhiyun 		server->negflavor = CIFS_NEGFLAVOR_LANMAN;
649*4882a593Smuzhiyun 		rc = decode_lanman_negprot_rsp(server, pSMBr);
650*4882a593Smuzhiyun 		goto signing_check;
651*4882a593Smuzhiyun 	} else if (pSMBr->hdr.WordCount != 17) {
652*4882a593Smuzhiyun 		/* unknown wct */
653*4882a593Smuzhiyun 		rc = -EOPNOTSUPP;
654*4882a593Smuzhiyun 		goto neg_err_exit;
655*4882a593Smuzhiyun 	}
656*4882a593Smuzhiyun 	/* else wct == 17, NTLM or better */
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 	server->sec_mode = pSMBr->SecurityMode;
659*4882a593Smuzhiyun 	if ((server->sec_mode & SECMODE_USER) == 0)
660*4882a593Smuzhiyun 		cifs_dbg(FYI, "share mode security\n");
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun 	/* one byte, so no need to convert this or EncryptionKeyLen from
663*4882a593Smuzhiyun 	   little endian */
664*4882a593Smuzhiyun 	server->maxReq = min_t(unsigned int, le16_to_cpu(pSMBr->MaxMpxCount),
665*4882a593Smuzhiyun 			       cifs_max_pending);
666*4882a593Smuzhiyun 	set_credits(server, server->maxReq);
667*4882a593Smuzhiyun 	/* probably no need to store and check maxvcs */
668*4882a593Smuzhiyun 	server->maxBuf = le32_to_cpu(pSMBr->MaxBufferSize);
669*4882a593Smuzhiyun 	/* set up max_read for readpages check */
670*4882a593Smuzhiyun 	server->max_read = server->maxBuf;
671*4882a593Smuzhiyun 	server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
672*4882a593Smuzhiyun 	cifs_dbg(NOISY, "Max buf = %d\n", ses->server->maxBuf);
673*4882a593Smuzhiyun 	server->capabilities = le32_to_cpu(pSMBr->Capabilities);
674*4882a593Smuzhiyun 	server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
675*4882a593Smuzhiyun 	server->timeAdj *= 60;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
678*4882a593Smuzhiyun 		server->negflavor = CIFS_NEGFLAVOR_UNENCAP;
679*4882a593Smuzhiyun 		memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey,
680*4882a593Smuzhiyun 		       CIFS_CRYPTO_KEY_SIZE);
681*4882a593Smuzhiyun 	} else if (pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC ||
682*4882a593Smuzhiyun 			server->capabilities & CAP_EXTENDED_SECURITY) {
683*4882a593Smuzhiyun 		server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
684*4882a593Smuzhiyun 		rc = decode_ext_sec_blob(ses, pSMBr);
685*4882a593Smuzhiyun 	} else if (server->sec_mode & SECMODE_PW_ENCRYPT) {
686*4882a593Smuzhiyun 		rc = -EIO; /* no crypt key only if plain text pwd */
687*4882a593Smuzhiyun 	} else {
688*4882a593Smuzhiyun 		server->negflavor = CIFS_NEGFLAVOR_UNENCAP;
689*4882a593Smuzhiyun 		server->capabilities &= ~CAP_EXTENDED_SECURITY;
690*4882a593Smuzhiyun 	}
691*4882a593Smuzhiyun 
692*4882a593Smuzhiyun signing_check:
693*4882a593Smuzhiyun 	if (!rc)
694*4882a593Smuzhiyun 		rc = cifs_enable_signing(server, ses->sign);
695*4882a593Smuzhiyun neg_err_exit:
696*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	cifs_dbg(FYI, "negprot rc %d\n", rc);
699*4882a593Smuzhiyun 	return rc;
700*4882a593Smuzhiyun }
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun int
CIFSSMBTDis(const unsigned int xid,struct cifs_tcon * tcon)703*4882a593Smuzhiyun CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon)
704*4882a593Smuzhiyun {
705*4882a593Smuzhiyun 	struct smb_hdr *smb_buffer;
706*4882a593Smuzhiyun 	int rc = 0;
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun 	cifs_dbg(FYI, "In tree disconnect\n");
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 	/* BB: do we need to check this? These should never be NULL. */
711*4882a593Smuzhiyun 	if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
712*4882a593Smuzhiyun 		return -EIO;
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 	/*
715*4882a593Smuzhiyun 	 * No need to return error on this operation if tid invalidated and
716*4882a593Smuzhiyun 	 * closed on server already e.g. due to tcp session crashing. Also,
717*4882a593Smuzhiyun 	 * the tcon is no longer on the list, so no need to take lock before
718*4882a593Smuzhiyun 	 * checking this.
719*4882a593Smuzhiyun 	 */
720*4882a593Smuzhiyun 	if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
721*4882a593Smuzhiyun 		return 0;
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
724*4882a593Smuzhiyun 			    (void **)&smb_buffer);
725*4882a593Smuzhiyun 	if (rc)
726*4882a593Smuzhiyun 		return rc;
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun 	rc = SendReceiveNoRsp(xid, tcon->ses, (char *)smb_buffer, 0);
729*4882a593Smuzhiyun 	cifs_small_buf_release(smb_buffer);
730*4882a593Smuzhiyun 	if (rc)
731*4882a593Smuzhiyun 		cifs_dbg(FYI, "Tree disconnect failed %d\n", rc);
732*4882a593Smuzhiyun 
733*4882a593Smuzhiyun 	/* No need to return error on this operation if tid invalidated and
734*4882a593Smuzhiyun 	   closed on server already e.g. due to tcp session crashing */
735*4882a593Smuzhiyun 	if (rc == -EAGAIN)
736*4882a593Smuzhiyun 		rc = 0;
737*4882a593Smuzhiyun 
738*4882a593Smuzhiyun 	return rc;
739*4882a593Smuzhiyun }
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun /*
742*4882a593Smuzhiyun  * This is a no-op for now. We're not really interested in the reply, but
743*4882a593Smuzhiyun  * rather in the fact that the server sent one and that server->lstrp
744*4882a593Smuzhiyun  * gets updated.
745*4882a593Smuzhiyun  *
746*4882a593Smuzhiyun  * FIXME: maybe we should consider checking that the reply matches request?
747*4882a593Smuzhiyun  */
748*4882a593Smuzhiyun static void
cifs_echo_callback(struct mid_q_entry * mid)749*4882a593Smuzhiyun cifs_echo_callback(struct mid_q_entry *mid)
750*4882a593Smuzhiyun {
751*4882a593Smuzhiyun 	struct TCP_Server_Info *server = mid->callback_data;
752*4882a593Smuzhiyun 	struct cifs_credits credits = { .value = 1, .instance = 0 };
753*4882a593Smuzhiyun 
754*4882a593Smuzhiyun 	DeleteMidQEntry(mid);
755*4882a593Smuzhiyun 	add_credits(server, &credits, CIFS_ECHO_OP);
756*4882a593Smuzhiyun }
757*4882a593Smuzhiyun 
758*4882a593Smuzhiyun int
CIFSSMBEcho(struct TCP_Server_Info * server)759*4882a593Smuzhiyun CIFSSMBEcho(struct TCP_Server_Info *server)
760*4882a593Smuzhiyun {
761*4882a593Smuzhiyun 	ECHO_REQ *smb;
762*4882a593Smuzhiyun 	int rc = 0;
763*4882a593Smuzhiyun 	struct kvec iov[2];
764*4882a593Smuzhiyun 	struct smb_rqst rqst = { .rq_iov = iov,
765*4882a593Smuzhiyun 				 .rq_nvec = 2 };
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun 	cifs_dbg(FYI, "In echo request\n");
768*4882a593Smuzhiyun 
769*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb);
770*4882a593Smuzhiyun 	if (rc)
771*4882a593Smuzhiyun 		return rc;
772*4882a593Smuzhiyun 
773*4882a593Smuzhiyun 	if (server->capabilities & CAP_UNICODE)
774*4882a593Smuzhiyun 		smb->hdr.Flags2 |= SMBFLG2_UNICODE;
775*4882a593Smuzhiyun 
776*4882a593Smuzhiyun 	/* set up echo request */
777*4882a593Smuzhiyun 	smb->hdr.Tid = 0xffff;
778*4882a593Smuzhiyun 	smb->hdr.WordCount = 1;
779*4882a593Smuzhiyun 	put_unaligned_le16(1, &smb->EchoCount);
780*4882a593Smuzhiyun 	put_bcc(1, &smb->hdr);
781*4882a593Smuzhiyun 	smb->Data[0] = 'a';
782*4882a593Smuzhiyun 	inc_rfc1001_len(smb, 3);
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun 	iov[0].iov_len = 4;
785*4882a593Smuzhiyun 	iov[0].iov_base = smb;
786*4882a593Smuzhiyun 	iov[1].iov_len = get_rfc1002_length(smb);
787*4882a593Smuzhiyun 	iov[1].iov_base = (char *)smb + 4;
788*4882a593Smuzhiyun 
789*4882a593Smuzhiyun 	rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL,
790*4882a593Smuzhiyun 			     server, CIFS_NON_BLOCKING | CIFS_ECHO_OP, NULL);
791*4882a593Smuzhiyun 	if (rc)
792*4882a593Smuzhiyun 		cifs_dbg(FYI, "Echo request failed: %d\n", rc);
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 	cifs_small_buf_release(smb);
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun 	return rc;
797*4882a593Smuzhiyun }
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun int
CIFSSMBLogoff(const unsigned int xid,struct cifs_ses * ses)800*4882a593Smuzhiyun CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses)
801*4882a593Smuzhiyun {
802*4882a593Smuzhiyun 	LOGOFF_ANDX_REQ *pSMB;
803*4882a593Smuzhiyun 	int rc = 0;
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	cifs_dbg(FYI, "In SMBLogoff for session disconnect\n");
806*4882a593Smuzhiyun 
807*4882a593Smuzhiyun 	/*
808*4882a593Smuzhiyun 	 * BB: do we need to check validity of ses and server? They should
809*4882a593Smuzhiyun 	 * always be valid since we have an active reference. If not, that
810*4882a593Smuzhiyun 	 * should probably be a BUG()
811*4882a593Smuzhiyun 	 */
812*4882a593Smuzhiyun 	if (!ses || !ses->server)
813*4882a593Smuzhiyun 		return -EIO;
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 	mutex_lock(&ses->session_mutex);
816*4882a593Smuzhiyun 	if (ses->need_reconnect)
817*4882a593Smuzhiyun 		goto session_already_dead; /* no need to send SMBlogoff if uid
818*4882a593Smuzhiyun 					      already closed due to reconnect */
819*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
820*4882a593Smuzhiyun 	if (rc) {
821*4882a593Smuzhiyun 		mutex_unlock(&ses->session_mutex);
822*4882a593Smuzhiyun 		return rc;
823*4882a593Smuzhiyun 	}
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	pSMB->hdr.Mid = get_next_mid(ses->server);
826*4882a593Smuzhiyun 
827*4882a593Smuzhiyun 	if (ses->server->sign)
828*4882a593Smuzhiyun 		pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 	pSMB->hdr.Uid = ses->Suid;
831*4882a593Smuzhiyun 
832*4882a593Smuzhiyun 	pSMB->AndXCommand = 0xFF;
833*4882a593Smuzhiyun 	rc = SendReceiveNoRsp(xid, ses, (char *) pSMB, 0);
834*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
835*4882a593Smuzhiyun session_already_dead:
836*4882a593Smuzhiyun 	mutex_unlock(&ses->session_mutex);
837*4882a593Smuzhiyun 
838*4882a593Smuzhiyun 	/* if session dead then we do not need to do ulogoff,
839*4882a593Smuzhiyun 		since server closed smb session, no sense reporting
840*4882a593Smuzhiyun 		error */
841*4882a593Smuzhiyun 	if (rc == -EAGAIN)
842*4882a593Smuzhiyun 		rc = 0;
843*4882a593Smuzhiyun 	return rc;
844*4882a593Smuzhiyun }
845*4882a593Smuzhiyun 
846*4882a593Smuzhiyun int
CIFSPOSIXDelFile(const unsigned int xid,struct cifs_tcon * tcon,const char * fileName,__u16 type,const struct nls_table * nls_codepage,int remap)847*4882a593Smuzhiyun CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
848*4882a593Smuzhiyun 		 const char *fileName, __u16 type,
849*4882a593Smuzhiyun 		 const struct nls_table *nls_codepage, int remap)
850*4882a593Smuzhiyun {
851*4882a593Smuzhiyun 	TRANSACTION2_SPI_REQ *pSMB = NULL;
852*4882a593Smuzhiyun 	TRANSACTION2_SPI_RSP *pSMBr = NULL;
853*4882a593Smuzhiyun 	struct unlink_psx_rq *pRqD;
854*4882a593Smuzhiyun 	int name_len;
855*4882a593Smuzhiyun 	int rc = 0;
856*4882a593Smuzhiyun 	int bytes_returned = 0;
857*4882a593Smuzhiyun 	__u16 params, param_offset, offset, byte_count;
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun 	cifs_dbg(FYI, "In POSIX delete\n");
860*4882a593Smuzhiyun PsxDelete:
861*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
862*4882a593Smuzhiyun 		      (void **) &pSMBr);
863*4882a593Smuzhiyun 	if (rc)
864*4882a593Smuzhiyun 		return rc;
865*4882a593Smuzhiyun 
866*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
867*4882a593Smuzhiyun 		name_len =
868*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
869*4882a593Smuzhiyun 				       PATH_MAX, nls_codepage, remap);
870*4882a593Smuzhiyun 		name_len++;	/* trailing null */
871*4882a593Smuzhiyun 		name_len *= 2;
872*4882a593Smuzhiyun 	} else {
873*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, fileName);
874*4882a593Smuzhiyun 	}
875*4882a593Smuzhiyun 
876*4882a593Smuzhiyun 	params = 6 + name_len;
877*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
878*4882a593Smuzhiyun 	pSMB->MaxDataCount = 0; /* BB double check this with jra */
879*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
880*4882a593Smuzhiyun 	pSMB->Reserved = 0;
881*4882a593Smuzhiyun 	pSMB->Flags = 0;
882*4882a593Smuzhiyun 	pSMB->Timeout = 0;
883*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
884*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_spi_req,
885*4882a593Smuzhiyun 				InformationLevel) - 4;
886*4882a593Smuzhiyun 	offset = param_offset + params;
887*4882a593Smuzhiyun 
888*4882a593Smuzhiyun 	/* Setup pointer to Request Data (inode type) */
889*4882a593Smuzhiyun 	pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
890*4882a593Smuzhiyun 	pRqD->type = cpu_to_le16(type);
891*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
892*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
893*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
894*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
895*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
896*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + sizeof(struct unlink_psx_rq);
897*4882a593Smuzhiyun 
898*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
899*4882a593Smuzhiyun 	pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
900*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
901*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
902*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
903*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
904*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
905*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
906*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
907*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
908*4882a593Smuzhiyun 	if (rc)
909*4882a593Smuzhiyun 		cifs_dbg(FYI, "Posix delete returned %d\n", rc);
910*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
911*4882a593Smuzhiyun 
912*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes);
913*4882a593Smuzhiyun 
914*4882a593Smuzhiyun 	if (rc == -EAGAIN)
915*4882a593Smuzhiyun 		goto PsxDelete;
916*4882a593Smuzhiyun 
917*4882a593Smuzhiyun 	return rc;
918*4882a593Smuzhiyun }
919*4882a593Smuzhiyun 
920*4882a593Smuzhiyun int
CIFSSMBDelFile(const unsigned int xid,struct cifs_tcon * tcon,const char * name,struct cifs_sb_info * cifs_sb)921*4882a593Smuzhiyun CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
922*4882a593Smuzhiyun 	       struct cifs_sb_info *cifs_sb)
923*4882a593Smuzhiyun {
924*4882a593Smuzhiyun 	DELETE_FILE_REQ *pSMB = NULL;
925*4882a593Smuzhiyun 	DELETE_FILE_RSP *pSMBr = NULL;
926*4882a593Smuzhiyun 	int rc = 0;
927*4882a593Smuzhiyun 	int bytes_returned;
928*4882a593Smuzhiyun 	int name_len;
929*4882a593Smuzhiyun 	int remap = cifs_remap(cifs_sb);
930*4882a593Smuzhiyun 
931*4882a593Smuzhiyun DelFileRetry:
932*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
933*4882a593Smuzhiyun 		      (void **) &pSMBr);
934*4882a593Smuzhiyun 	if (rc)
935*4882a593Smuzhiyun 		return rc;
936*4882a593Smuzhiyun 
937*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
938*4882a593Smuzhiyun 		name_len = cifsConvertToUTF16((__le16 *) pSMB->fileName, name,
939*4882a593Smuzhiyun 					      PATH_MAX, cifs_sb->local_nls,
940*4882a593Smuzhiyun 					      remap);
941*4882a593Smuzhiyun 		name_len++;	/* trailing null */
942*4882a593Smuzhiyun 		name_len *= 2;
943*4882a593Smuzhiyun 	} else {
944*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->fileName, name);
945*4882a593Smuzhiyun 	}
946*4882a593Smuzhiyun 	pSMB->SearchAttributes =
947*4882a593Smuzhiyun 	    cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
948*4882a593Smuzhiyun 	pSMB->BufferFormat = 0x04;
949*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, name_len + 1);
950*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(name_len + 1);
951*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
952*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
953*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_deletes);
954*4882a593Smuzhiyun 	if (rc)
955*4882a593Smuzhiyun 		cifs_dbg(FYI, "Error in RMFile = %d\n", rc);
956*4882a593Smuzhiyun 
957*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
958*4882a593Smuzhiyun 	if (rc == -EAGAIN)
959*4882a593Smuzhiyun 		goto DelFileRetry;
960*4882a593Smuzhiyun 
961*4882a593Smuzhiyun 	return rc;
962*4882a593Smuzhiyun }
963*4882a593Smuzhiyun 
964*4882a593Smuzhiyun int
CIFSSMBRmDir(const unsigned int xid,struct cifs_tcon * tcon,const char * name,struct cifs_sb_info * cifs_sb)965*4882a593Smuzhiyun CIFSSMBRmDir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
966*4882a593Smuzhiyun 	     struct cifs_sb_info *cifs_sb)
967*4882a593Smuzhiyun {
968*4882a593Smuzhiyun 	DELETE_DIRECTORY_REQ *pSMB = NULL;
969*4882a593Smuzhiyun 	DELETE_DIRECTORY_RSP *pSMBr = NULL;
970*4882a593Smuzhiyun 	int rc = 0;
971*4882a593Smuzhiyun 	int bytes_returned;
972*4882a593Smuzhiyun 	int name_len;
973*4882a593Smuzhiyun 	int remap = cifs_remap(cifs_sb);
974*4882a593Smuzhiyun 
975*4882a593Smuzhiyun 	cifs_dbg(FYI, "In CIFSSMBRmDir\n");
976*4882a593Smuzhiyun RmDirRetry:
977*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
978*4882a593Smuzhiyun 		      (void **) &pSMBr);
979*4882a593Smuzhiyun 	if (rc)
980*4882a593Smuzhiyun 		return rc;
981*4882a593Smuzhiyun 
982*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
983*4882a593Smuzhiyun 		name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name,
984*4882a593Smuzhiyun 					      PATH_MAX, cifs_sb->local_nls,
985*4882a593Smuzhiyun 					      remap);
986*4882a593Smuzhiyun 		name_len++;	/* trailing null */
987*4882a593Smuzhiyun 		name_len *= 2;
988*4882a593Smuzhiyun 	} else {
989*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->DirName, name);
990*4882a593Smuzhiyun 	}
991*4882a593Smuzhiyun 
992*4882a593Smuzhiyun 	pSMB->BufferFormat = 0x04;
993*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, name_len + 1);
994*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(name_len + 1);
995*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
996*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
997*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_rmdirs);
998*4882a593Smuzhiyun 	if (rc)
999*4882a593Smuzhiyun 		cifs_dbg(FYI, "Error in RMDir = %d\n", rc);
1000*4882a593Smuzhiyun 
1001*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
1002*4882a593Smuzhiyun 	if (rc == -EAGAIN)
1003*4882a593Smuzhiyun 		goto RmDirRetry;
1004*4882a593Smuzhiyun 	return rc;
1005*4882a593Smuzhiyun }
1006*4882a593Smuzhiyun 
1007*4882a593Smuzhiyun int
CIFSSMBMkDir(const unsigned int xid,struct inode * inode,umode_t mode,struct cifs_tcon * tcon,const char * name,struct cifs_sb_info * cifs_sb)1008*4882a593Smuzhiyun CIFSSMBMkDir(const unsigned int xid, struct inode *inode, umode_t mode,
1009*4882a593Smuzhiyun 	     struct cifs_tcon *tcon, const char *name,
1010*4882a593Smuzhiyun 	     struct cifs_sb_info *cifs_sb)
1011*4882a593Smuzhiyun {
1012*4882a593Smuzhiyun 	int rc = 0;
1013*4882a593Smuzhiyun 	CREATE_DIRECTORY_REQ *pSMB = NULL;
1014*4882a593Smuzhiyun 	CREATE_DIRECTORY_RSP *pSMBr = NULL;
1015*4882a593Smuzhiyun 	int bytes_returned;
1016*4882a593Smuzhiyun 	int name_len;
1017*4882a593Smuzhiyun 	int remap = cifs_remap(cifs_sb);
1018*4882a593Smuzhiyun 
1019*4882a593Smuzhiyun 	cifs_dbg(FYI, "In CIFSSMBMkDir\n");
1020*4882a593Smuzhiyun MkDirRetry:
1021*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
1022*4882a593Smuzhiyun 		      (void **) &pSMBr);
1023*4882a593Smuzhiyun 	if (rc)
1024*4882a593Smuzhiyun 		return rc;
1025*4882a593Smuzhiyun 
1026*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1027*4882a593Smuzhiyun 		name_len = cifsConvertToUTF16((__le16 *) pSMB->DirName, name,
1028*4882a593Smuzhiyun 					      PATH_MAX, cifs_sb->local_nls,
1029*4882a593Smuzhiyun 					      remap);
1030*4882a593Smuzhiyun 		name_len++;	/* trailing null */
1031*4882a593Smuzhiyun 		name_len *= 2;
1032*4882a593Smuzhiyun 	} else {
1033*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->DirName, name);
1034*4882a593Smuzhiyun 	}
1035*4882a593Smuzhiyun 
1036*4882a593Smuzhiyun 	pSMB->BufferFormat = 0x04;
1037*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, name_len + 1);
1038*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(name_len + 1);
1039*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1040*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1041*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_mkdirs);
1042*4882a593Smuzhiyun 	if (rc)
1043*4882a593Smuzhiyun 		cifs_dbg(FYI, "Error in Mkdir = %d\n", rc);
1044*4882a593Smuzhiyun 
1045*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
1046*4882a593Smuzhiyun 	if (rc == -EAGAIN)
1047*4882a593Smuzhiyun 		goto MkDirRetry;
1048*4882a593Smuzhiyun 	return rc;
1049*4882a593Smuzhiyun }
1050*4882a593Smuzhiyun 
1051*4882a593Smuzhiyun int
CIFSPOSIXCreate(const unsigned int xid,struct cifs_tcon * tcon,__u32 posix_flags,__u64 mode,__u16 * netfid,FILE_UNIX_BASIC_INFO * pRetData,__u32 * pOplock,const char * name,const struct nls_table * nls_codepage,int remap)1052*4882a593Smuzhiyun CIFSPOSIXCreate(const unsigned int xid, struct cifs_tcon *tcon,
1053*4882a593Smuzhiyun 		__u32 posix_flags, __u64 mode, __u16 *netfid,
1054*4882a593Smuzhiyun 		FILE_UNIX_BASIC_INFO *pRetData, __u32 *pOplock,
1055*4882a593Smuzhiyun 		const char *name, const struct nls_table *nls_codepage,
1056*4882a593Smuzhiyun 		int remap)
1057*4882a593Smuzhiyun {
1058*4882a593Smuzhiyun 	TRANSACTION2_SPI_REQ *pSMB = NULL;
1059*4882a593Smuzhiyun 	TRANSACTION2_SPI_RSP *pSMBr = NULL;
1060*4882a593Smuzhiyun 	int name_len;
1061*4882a593Smuzhiyun 	int rc = 0;
1062*4882a593Smuzhiyun 	int bytes_returned = 0;
1063*4882a593Smuzhiyun 	__u16 params, param_offset, offset, byte_count, count;
1064*4882a593Smuzhiyun 	OPEN_PSX_REQ *pdata;
1065*4882a593Smuzhiyun 	OPEN_PSX_RSP *psx_rsp;
1066*4882a593Smuzhiyun 
1067*4882a593Smuzhiyun 	cifs_dbg(FYI, "In POSIX Create\n");
1068*4882a593Smuzhiyun PsxCreat:
1069*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
1070*4882a593Smuzhiyun 		      (void **) &pSMBr);
1071*4882a593Smuzhiyun 	if (rc)
1072*4882a593Smuzhiyun 		return rc;
1073*4882a593Smuzhiyun 
1074*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1075*4882a593Smuzhiyun 		name_len =
1076*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->FileName, name,
1077*4882a593Smuzhiyun 				       PATH_MAX, nls_codepage, remap);
1078*4882a593Smuzhiyun 		name_len++;	/* trailing null */
1079*4882a593Smuzhiyun 		name_len *= 2;
1080*4882a593Smuzhiyun 	} else {
1081*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, name);
1082*4882a593Smuzhiyun 	}
1083*4882a593Smuzhiyun 
1084*4882a593Smuzhiyun 	params = 6 + name_len;
1085*4882a593Smuzhiyun 	count = sizeof(OPEN_PSX_REQ);
1086*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
1087*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);	/* large enough */
1088*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
1089*4882a593Smuzhiyun 	pSMB->Reserved = 0;
1090*4882a593Smuzhiyun 	pSMB->Flags = 0;
1091*4882a593Smuzhiyun 	pSMB->Timeout = 0;
1092*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
1093*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_spi_req,
1094*4882a593Smuzhiyun 				InformationLevel) - 4;
1095*4882a593Smuzhiyun 	offset = param_offset + params;
1096*4882a593Smuzhiyun 	pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
1097*4882a593Smuzhiyun 	pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
1098*4882a593Smuzhiyun 	pdata->Permissions = cpu_to_le64(mode);
1099*4882a593Smuzhiyun 	pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
1100*4882a593Smuzhiyun 	pdata->OpenFlags =  cpu_to_le32(*pOplock);
1101*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
1102*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
1103*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
1104*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
1105*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
1106*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + count;
1107*4882a593Smuzhiyun 
1108*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(count);
1109*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
1110*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
1111*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
1112*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1113*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
1114*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
1115*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
1116*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1117*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1118*4882a593Smuzhiyun 	if (rc) {
1119*4882a593Smuzhiyun 		cifs_dbg(FYI, "Posix create returned %d\n", rc);
1120*4882a593Smuzhiyun 		goto psx_create_err;
1121*4882a593Smuzhiyun 	}
1122*4882a593Smuzhiyun 
1123*4882a593Smuzhiyun 	cifs_dbg(FYI, "copying inode info\n");
1124*4882a593Smuzhiyun 	rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1125*4882a593Smuzhiyun 
1126*4882a593Smuzhiyun 	if (rc || get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)) {
1127*4882a593Smuzhiyun 		rc = -EIO;	/* bad smb */
1128*4882a593Smuzhiyun 		goto psx_create_err;
1129*4882a593Smuzhiyun 	}
1130*4882a593Smuzhiyun 
1131*4882a593Smuzhiyun 	/* copy return information to pRetData */
1132*4882a593Smuzhiyun 	psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
1133*4882a593Smuzhiyun 			+ le16_to_cpu(pSMBr->t2.DataOffset));
1134*4882a593Smuzhiyun 
1135*4882a593Smuzhiyun 	*pOplock = le16_to_cpu(psx_rsp->OplockFlags);
1136*4882a593Smuzhiyun 	if (netfid)
1137*4882a593Smuzhiyun 		*netfid = psx_rsp->Fid;   /* cifs fid stays in le */
1138*4882a593Smuzhiyun 	/* Let caller know file was created so we can set the mode. */
1139*4882a593Smuzhiyun 	/* Do we care about the CreateAction in any other cases? */
1140*4882a593Smuzhiyun 	if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
1141*4882a593Smuzhiyun 		*pOplock |= CIFS_CREATE_ACTION;
1142*4882a593Smuzhiyun 	/* check to make sure response data is there */
1143*4882a593Smuzhiyun 	if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1144*4882a593Smuzhiyun 		pRetData->Type = cpu_to_le32(-1); /* unknown */
1145*4882a593Smuzhiyun 		cifs_dbg(NOISY, "unknown type\n");
1146*4882a593Smuzhiyun 	} else {
1147*4882a593Smuzhiyun 		if (get_bcc(&pSMBr->hdr) < sizeof(OPEN_PSX_RSP)
1148*4882a593Smuzhiyun 					+ sizeof(FILE_UNIX_BASIC_INFO)) {
1149*4882a593Smuzhiyun 			cifs_dbg(VFS, "Open response data too small\n");
1150*4882a593Smuzhiyun 			pRetData->Type = cpu_to_le32(-1);
1151*4882a593Smuzhiyun 			goto psx_create_err;
1152*4882a593Smuzhiyun 		}
1153*4882a593Smuzhiyun 		memcpy((char *) pRetData,
1154*4882a593Smuzhiyun 			(char *)psx_rsp + sizeof(OPEN_PSX_RSP),
1155*4882a593Smuzhiyun 			sizeof(FILE_UNIX_BASIC_INFO));
1156*4882a593Smuzhiyun 	}
1157*4882a593Smuzhiyun 
1158*4882a593Smuzhiyun psx_create_err:
1159*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
1160*4882a593Smuzhiyun 
1161*4882a593Smuzhiyun 	if (posix_flags & SMB_O_DIRECTORY)
1162*4882a593Smuzhiyun 		cifs_stats_inc(&tcon->stats.cifs_stats.num_posixmkdirs);
1163*4882a593Smuzhiyun 	else
1164*4882a593Smuzhiyun 		cifs_stats_inc(&tcon->stats.cifs_stats.num_posixopens);
1165*4882a593Smuzhiyun 
1166*4882a593Smuzhiyun 	if (rc == -EAGAIN)
1167*4882a593Smuzhiyun 		goto PsxCreat;
1168*4882a593Smuzhiyun 
1169*4882a593Smuzhiyun 	return rc;
1170*4882a593Smuzhiyun }
1171*4882a593Smuzhiyun 
convert_disposition(int disposition)1172*4882a593Smuzhiyun static __u16 convert_disposition(int disposition)
1173*4882a593Smuzhiyun {
1174*4882a593Smuzhiyun 	__u16 ofun = 0;
1175*4882a593Smuzhiyun 
1176*4882a593Smuzhiyun 	switch (disposition) {
1177*4882a593Smuzhiyun 		case FILE_SUPERSEDE:
1178*4882a593Smuzhiyun 			ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1179*4882a593Smuzhiyun 			break;
1180*4882a593Smuzhiyun 		case FILE_OPEN:
1181*4882a593Smuzhiyun 			ofun = SMBOPEN_OAPPEND;
1182*4882a593Smuzhiyun 			break;
1183*4882a593Smuzhiyun 		case FILE_CREATE:
1184*4882a593Smuzhiyun 			ofun = SMBOPEN_OCREATE;
1185*4882a593Smuzhiyun 			break;
1186*4882a593Smuzhiyun 		case FILE_OPEN_IF:
1187*4882a593Smuzhiyun 			ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1188*4882a593Smuzhiyun 			break;
1189*4882a593Smuzhiyun 		case FILE_OVERWRITE:
1190*4882a593Smuzhiyun 			ofun = SMBOPEN_OTRUNC;
1191*4882a593Smuzhiyun 			break;
1192*4882a593Smuzhiyun 		case FILE_OVERWRITE_IF:
1193*4882a593Smuzhiyun 			ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1194*4882a593Smuzhiyun 			break;
1195*4882a593Smuzhiyun 		default:
1196*4882a593Smuzhiyun 			cifs_dbg(FYI, "unknown disposition %d\n", disposition);
1197*4882a593Smuzhiyun 			ofun =  SMBOPEN_OAPPEND; /* regular open */
1198*4882a593Smuzhiyun 	}
1199*4882a593Smuzhiyun 	return ofun;
1200*4882a593Smuzhiyun }
1201*4882a593Smuzhiyun 
1202*4882a593Smuzhiyun static int
access_flags_to_smbopen_mode(const int access_flags)1203*4882a593Smuzhiyun access_flags_to_smbopen_mode(const int access_flags)
1204*4882a593Smuzhiyun {
1205*4882a593Smuzhiyun 	int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
1206*4882a593Smuzhiyun 
1207*4882a593Smuzhiyun 	if (masked_flags == GENERIC_READ)
1208*4882a593Smuzhiyun 		return SMBOPEN_READ;
1209*4882a593Smuzhiyun 	else if (masked_flags == GENERIC_WRITE)
1210*4882a593Smuzhiyun 		return SMBOPEN_WRITE;
1211*4882a593Smuzhiyun 
1212*4882a593Smuzhiyun 	/* just go for read/write */
1213*4882a593Smuzhiyun 	return SMBOPEN_READWRITE;
1214*4882a593Smuzhiyun }
1215*4882a593Smuzhiyun 
1216*4882a593Smuzhiyun int
SMBLegacyOpen(const unsigned int xid,struct cifs_tcon * tcon,const char * fileName,const int openDisposition,const int access_flags,const int create_options,__u16 * netfid,int * pOplock,FILE_ALL_INFO * pfile_info,const struct nls_table * nls_codepage,int remap)1217*4882a593Smuzhiyun SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon,
1218*4882a593Smuzhiyun 	    const char *fileName, const int openDisposition,
1219*4882a593Smuzhiyun 	    const int access_flags, const int create_options, __u16 *netfid,
1220*4882a593Smuzhiyun 	    int *pOplock, FILE_ALL_INFO *pfile_info,
1221*4882a593Smuzhiyun 	    const struct nls_table *nls_codepage, int remap)
1222*4882a593Smuzhiyun {
1223*4882a593Smuzhiyun 	int rc = -EACCES;
1224*4882a593Smuzhiyun 	OPENX_REQ *pSMB = NULL;
1225*4882a593Smuzhiyun 	OPENX_RSP *pSMBr = NULL;
1226*4882a593Smuzhiyun 	int bytes_returned;
1227*4882a593Smuzhiyun 	int name_len;
1228*4882a593Smuzhiyun 	__u16 count;
1229*4882a593Smuzhiyun 
1230*4882a593Smuzhiyun OldOpenRetry:
1231*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1232*4882a593Smuzhiyun 		      (void **) &pSMBr);
1233*4882a593Smuzhiyun 	if (rc)
1234*4882a593Smuzhiyun 		return rc;
1235*4882a593Smuzhiyun 
1236*4882a593Smuzhiyun 	pSMB->AndXCommand = 0xFF;       /* none */
1237*4882a593Smuzhiyun 
1238*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1239*4882a593Smuzhiyun 		count = 1;      /* account for one byte pad to word boundary */
1240*4882a593Smuzhiyun 		name_len =
1241*4882a593Smuzhiyun 		   cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1),
1242*4882a593Smuzhiyun 				      fileName, PATH_MAX, nls_codepage, remap);
1243*4882a593Smuzhiyun 		name_len++;     /* trailing null */
1244*4882a593Smuzhiyun 		name_len *= 2;
1245*4882a593Smuzhiyun 	} else {
1246*4882a593Smuzhiyun 		count = 0;      /* no pad */
1247*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->fileName, fileName);
1248*4882a593Smuzhiyun 	}
1249*4882a593Smuzhiyun 	if (*pOplock & REQ_OPLOCK)
1250*4882a593Smuzhiyun 		pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
1251*4882a593Smuzhiyun 	else if (*pOplock & REQ_BATCHOPLOCK)
1252*4882a593Smuzhiyun 		pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
1253*4882a593Smuzhiyun 
1254*4882a593Smuzhiyun 	pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
1255*4882a593Smuzhiyun 	pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
1256*4882a593Smuzhiyun 	pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1257*4882a593Smuzhiyun 	/* set file as system file if special file such
1258*4882a593Smuzhiyun 	   as fifo and server expecting SFU style and
1259*4882a593Smuzhiyun 	   no Unix extensions */
1260*4882a593Smuzhiyun 
1261*4882a593Smuzhiyun 	if (create_options & CREATE_OPTION_SPECIAL)
1262*4882a593Smuzhiyun 		pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
1263*4882a593Smuzhiyun 	else /* BB FIXME BB */
1264*4882a593Smuzhiyun 		pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
1265*4882a593Smuzhiyun 
1266*4882a593Smuzhiyun 	if (create_options & CREATE_OPTION_READONLY)
1267*4882a593Smuzhiyun 		pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
1268*4882a593Smuzhiyun 
1269*4882a593Smuzhiyun 	/* BB FIXME BB */
1270*4882a593Smuzhiyun /*	pSMB->CreateOptions = cpu_to_le32(create_options &
1271*4882a593Smuzhiyun 						 CREATE_OPTIONS_MASK); */
1272*4882a593Smuzhiyun 	/* BB FIXME END BB */
1273*4882a593Smuzhiyun 
1274*4882a593Smuzhiyun 	pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
1275*4882a593Smuzhiyun 	pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
1276*4882a593Smuzhiyun 	count += name_len;
1277*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, count);
1278*4882a593Smuzhiyun 
1279*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(count);
1280*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1281*4882a593Smuzhiyun 			(struct smb_hdr *)pSMBr, &bytes_returned, 0);
1282*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_opens);
1283*4882a593Smuzhiyun 	if (rc) {
1284*4882a593Smuzhiyun 		cifs_dbg(FYI, "Error in Open = %d\n", rc);
1285*4882a593Smuzhiyun 	} else {
1286*4882a593Smuzhiyun 	/* BB verify if wct == 15 */
1287*4882a593Smuzhiyun 
1288*4882a593Smuzhiyun /*		*pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
1289*4882a593Smuzhiyun 
1290*4882a593Smuzhiyun 		*netfid = pSMBr->Fid;   /* cifs fid stays in le */
1291*4882a593Smuzhiyun 		/* Let caller know file was created so we can set the mode. */
1292*4882a593Smuzhiyun 		/* Do we care about the CreateAction in any other cases? */
1293*4882a593Smuzhiyun 	/* BB FIXME BB */
1294*4882a593Smuzhiyun /*		if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
1295*4882a593Smuzhiyun 			*pOplock |= CIFS_CREATE_ACTION; */
1296*4882a593Smuzhiyun 	/* BB FIXME END */
1297*4882a593Smuzhiyun 
1298*4882a593Smuzhiyun 		if (pfile_info) {
1299*4882a593Smuzhiyun 			pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1300*4882a593Smuzhiyun 			pfile_info->LastAccessTime = 0; /* BB fixme */
1301*4882a593Smuzhiyun 			pfile_info->LastWriteTime = 0; /* BB fixme */
1302*4882a593Smuzhiyun 			pfile_info->ChangeTime = 0;  /* BB fixme */
1303*4882a593Smuzhiyun 			pfile_info->Attributes =
1304*4882a593Smuzhiyun 				cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
1305*4882a593Smuzhiyun 			/* the file_info buf is endian converted by caller */
1306*4882a593Smuzhiyun 			pfile_info->AllocationSize =
1307*4882a593Smuzhiyun 				cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1308*4882a593Smuzhiyun 			pfile_info->EndOfFile = pfile_info->AllocationSize;
1309*4882a593Smuzhiyun 			pfile_info->NumberOfLinks = cpu_to_le32(1);
1310*4882a593Smuzhiyun 			pfile_info->DeletePending = 0;
1311*4882a593Smuzhiyun 		}
1312*4882a593Smuzhiyun 	}
1313*4882a593Smuzhiyun 
1314*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
1315*4882a593Smuzhiyun 	if (rc == -EAGAIN)
1316*4882a593Smuzhiyun 		goto OldOpenRetry;
1317*4882a593Smuzhiyun 	return rc;
1318*4882a593Smuzhiyun }
1319*4882a593Smuzhiyun 
1320*4882a593Smuzhiyun int
CIFS_open(const unsigned int xid,struct cifs_open_parms * oparms,int * oplock,FILE_ALL_INFO * buf)1321*4882a593Smuzhiyun CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock,
1322*4882a593Smuzhiyun 	  FILE_ALL_INFO *buf)
1323*4882a593Smuzhiyun {
1324*4882a593Smuzhiyun 	int rc;
1325*4882a593Smuzhiyun 	OPEN_REQ *req = NULL;
1326*4882a593Smuzhiyun 	OPEN_RSP *rsp = NULL;
1327*4882a593Smuzhiyun 	int bytes_returned;
1328*4882a593Smuzhiyun 	int name_len;
1329*4882a593Smuzhiyun 	__u16 count;
1330*4882a593Smuzhiyun 	struct cifs_sb_info *cifs_sb = oparms->cifs_sb;
1331*4882a593Smuzhiyun 	struct cifs_tcon *tcon = oparms->tcon;
1332*4882a593Smuzhiyun 	int remap = cifs_remap(cifs_sb);
1333*4882a593Smuzhiyun 	const struct nls_table *nls = cifs_sb->local_nls;
1334*4882a593Smuzhiyun 	int create_options = oparms->create_options;
1335*4882a593Smuzhiyun 	int desired_access = oparms->desired_access;
1336*4882a593Smuzhiyun 	int disposition = oparms->disposition;
1337*4882a593Smuzhiyun 	const char *path = oparms->path;
1338*4882a593Smuzhiyun 
1339*4882a593Smuzhiyun openRetry:
1340*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **)&req,
1341*4882a593Smuzhiyun 		      (void **)&rsp);
1342*4882a593Smuzhiyun 	if (rc)
1343*4882a593Smuzhiyun 		return rc;
1344*4882a593Smuzhiyun 
1345*4882a593Smuzhiyun 	/* no commands go after this */
1346*4882a593Smuzhiyun 	req->AndXCommand = 0xFF;
1347*4882a593Smuzhiyun 
1348*4882a593Smuzhiyun 	if (req->hdr.Flags2 & SMBFLG2_UNICODE) {
1349*4882a593Smuzhiyun 		/* account for one byte pad to word boundary */
1350*4882a593Smuzhiyun 		count = 1;
1351*4882a593Smuzhiyun 		name_len = cifsConvertToUTF16((__le16 *)(req->fileName + 1),
1352*4882a593Smuzhiyun 					      path, PATH_MAX, nls, remap);
1353*4882a593Smuzhiyun 		/* trailing null */
1354*4882a593Smuzhiyun 		name_len++;
1355*4882a593Smuzhiyun 		name_len *= 2;
1356*4882a593Smuzhiyun 		req->NameLength = cpu_to_le16(name_len);
1357*4882a593Smuzhiyun 	} else {
1358*4882a593Smuzhiyun 		/* BB improve check for buffer overruns BB */
1359*4882a593Smuzhiyun 		/* no pad */
1360*4882a593Smuzhiyun 		count = 0;
1361*4882a593Smuzhiyun 		name_len = copy_path_name(req->fileName, path);
1362*4882a593Smuzhiyun 		req->NameLength = cpu_to_le16(name_len);
1363*4882a593Smuzhiyun 	}
1364*4882a593Smuzhiyun 
1365*4882a593Smuzhiyun 	if (*oplock & REQ_OPLOCK)
1366*4882a593Smuzhiyun 		req->OpenFlags = cpu_to_le32(REQ_OPLOCK);
1367*4882a593Smuzhiyun 	else if (*oplock & REQ_BATCHOPLOCK)
1368*4882a593Smuzhiyun 		req->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
1369*4882a593Smuzhiyun 
1370*4882a593Smuzhiyun 	req->DesiredAccess = cpu_to_le32(desired_access);
1371*4882a593Smuzhiyun 	req->AllocationSize = 0;
1372*4882a593Smuzhiyun 
1373*4882a593Smuzhiyun 	/*
1374*4882a593Smuzhiyun 	 * Set file as system file if special file such as fifo and server
1375*4882a593Smuzhiyun 	 * expecting SFU style and no Unix extensions.
1376*4882a593Smuzhiyun 	 */
1377*4882a593Smuzhiyun 	if (create_options & CREATE_OPTION_SPECIAL)
1378*4882a593Smuzhiyun 		req->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1379*4882a593Smuzhiyun 	else
1380*4882a593Smuzhiyun 		req->FileAttributes = cpu_to_le32(ATTR_NORMAL);
1381*4882a593Smuzhiyun 
1382*4882a593Smuzhiyun 	/*
1383*4882a593Smuzhiyun 	 * XP does not handle ATTR_POSIX_SEMANTICS but it helps speed up case
1384*4882a593Smuzhiyun 	 * sensitive checks for other servers such as Samba.
1385*4882a593Smuzhiyun 	 */
1386*4882a593Smuzhiyun 	if (tcon->ses->capabilities & CAP_UNIX)
1387*4882a593Smuzhiyun 		req->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1388*4882a593Smuzhiyun 
1389*4882a593Smuzhiyun 	if (create_options & CREATE_OPTION_READONLY)
1390*4882a593Smuzhiyun 		req->FileAttributes |= cpu_to_le32(ATTR_READONLY);
1391*4882a593Smuzhiyun 
1392*4882a593Smuzhiyun 	req->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1393*4882a593Smuzhiyun 	req->CreateDisposition = cpu_to_le32(disposition);
1394*4882a593Smuzhiyun 	req->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
1395*4882a593Smuzhiyun 
1396*4882a593Smuzhiyun 	/* BB Expirement with various impersonation levels and verify */
1397*4882a593Smuzhiyun 	req->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
1398*4882a593Smuzhiyun 	req->SecurityFlags = SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY;
1399*4882a593Smuzhiyun 
1400*4882a593Smuzhiyun 	count += name_len;
1401*4882a593Smuzhiyun 	inc_rfc1001_len(req, count);
1402*4882a593Smuzhiyun 
1403*4882a593Smuzhiyun 	req->ByteCount = cpu_to_le16(count);
1404*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req,
1405*4882a593Smuzhiyun 			 (struct smb_hdr *)rsp, &bytes_returned, 0);
1406*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_opens);
1407*4882a593Smuzhiyun 	if (rc) {
1408*4882a593Smuzhiyun 		cifs_dbg(FYI, "Error in Open = %d\n", rc);
1409*4882a593Smuzhiyun 		cifs_buf_release(req);
1410*4882a593Smuzhiyun 		if (rc == -EAGAIN)
1411*4882a593Smuzhiyun 			goto openRetry;
1412*4882a593Smuzhiyun 		return rc;
1413*4882a593Smuzhiyun 	}
1414*4882a593Smuzhiyun 
1415*4882a593Smuzhiyun 	/* 1 byte no need to le_to_cpu */
1416*4882a593Smuzhiyun 	*oplock = rsp->OplockLevel;
1417*4882a593Smuzhiyun 	/* cifs fid stays in le */
1418*4882a593Smuzhiyun 	oparms->fid->netfid = rsp->Fid;
1419*4882a593Smuzhiyun 	oparms->fid->access = desired_access;
1420*4882a593Smuzhiyun 
1421*4882a593Smuzhiyun 	/* Let caller know file was created so we can set the mode. */
1422*4882a593Smuzhiyun 	/* Do we care about the CreateAction in any other cases? */
1423*4882a593Smuzhiyun 	if (cpu_to_le32(FILE_CREATE) == rsp->CreateAction)
1424*4882a593Smuzhiyun 		*oplock |= CIFS_CREATE_ACTION;
1425*4882a593Smuzhiyun 
1426*4882a593Smuzhiyun 	if (buf) {
1427*4882a593Smuzhiyun 		/* copy from CreationTime to Attributes */
1428*4882a593Smuzhiyun 		memcpy((char *)buf, (char *)&rsp->CreationTime, 36);
1429*4882a593Smuzhiyun 		/* the file_info buf is endian converted by caller */
1430*4882a593Smuzhiyun 		buf->AllocationSize = rsp->AllocationSize;
1431*4882a593Smuzhiyun 		buf->EndOfFile = rsp->EndOfFile;
1432*4882a593Smuzhiyun 		buf->NumberOfLinks = cpu_to_le32(1);
1433*4882a593Smuzhiyun 		buf->DeletePending = 0;
1434*4882a593Smuzhiyun 	}
1435*4882a593Smuzhiyun 
1436*4882a593Smuzhiyun 	cifs_buf_release(req);
1437*4882a593Smuzhiyun 	return rc;
1438*4882a593Smuzhiyun }
1439*4882a593Smuzhiyun 
1440*4882a593Smuzhiyun /*
1441*4882a593Smuzhiyun  * Discard any remaining data in the current SMB. To do this, we borrow the
1442*4882a593Smuzhiyun  * current bigbuf.
1443*4882a593Smuzhiyun  */
1444*4882a593Smuzhiyun int
cifs_discard_remaining_data(struct TCP_Server_Info * server)1445*4882a593Smuzhiyun cifs_discard_remaining_data(struct TCP_Server_Info *server)
1446*4882a593Smuzhiyun {
1447*4882a593Smuzhiyun 	unsigned int rfclen = server->pdu_size;
1448*4882a593Smuzhiyun 	int remaining = rfclen + server->vals->header_preamble_size -
1449*4882a593Smuzhiyun 		server->total_read;
1450*4882a593Smuzhiyun 
1451*4882a593Smuzhiyun 	while (remaining > 0) {
1452*4882a593Smuzhiyun 		int length;
1453*4882a593Smuzhiyun 
1454*4882a593Smuzhiyun 		length = cifs_discard_from_socket(server,
1455*4882a593Smuzhiyun 				min_t(size_t, remaining,
1456*4882a593Smuzhiyun 				      CIFSMaxBufSize + MAX_HEADER_SIZE(server)));
1457*4882a593Smuzhiyun 		if (length < 0)
1458*4882a593Smuzhiyun 			return length;
1459*4882a593Smuzhiyun 		server->total_read += length;
1460*4882a593Smuzhiyun 		remaining -= length;
1461*4882a593Smuzhiyun 	}
1462*4882a593Smuzhiyun 
1463*4882a593Smuzhiyun 	return 0;
1464*4882a593Smuzhiyun }
1465*4882a593Smuzhiyun 
1466*4882a593Smuzhiyun static int
__cifs_readv_discard(struct TCP_Server_Info * server,struct mid_q_entry * mid,bool malformed)1467*4882a593Smuzhiyun __cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid,
1468*4882a593Smuzhiyun 		     bool malformed)
1469*4882a593Smuzhiyun {
1470*4882a593Smuzhiyun 	int length;
1471*4882a593Smuzhiyun 
1472*4882a593Smuzhiyun 	length = cifs_discard_remaining_data(server);
1473*4882a593Smuzhiyun 	dequeue_mid(mid, malformed);
1474*4882a593Smuzhiyun 	mid->resp_buf = server->smallbuf;
1475*4882a593Smuzhiyun 	server->smallbuf = NULL;
1476*4882a593Smuzhiyun 	return length;
1477*4882a593Smuzhiyun }
1478*4882a593Smuzhiyun 
1479*4882a593Smuzhiyun static int
cifs_readv_discard(struct TCP_Server_Info * server,struct mid_q_entry * mid)1480*4882a593Smuzhiyun cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
1481*4882a593Smuzhiyun {
1482*4882a593Smuzhiyun 	struct cifs_readdata *rdata = mid->callback_data;
1483*4882a593Smuzhiyun 
1484*4882a593Smuzhiyun 	return  __cifs_readv_discard(server, mid, rdata->result);
1485*4882a593Smuzhiyun }
1486*4882a593Smuzhiyun 
1487*4882a593Smuzhiyun int
cifs_readv_receive(struct TCP_Server_Info * server,struct mid_q_entry * mid)1488*4882a593Smuzhiyun cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
1489*4882a593Smuzhiyun {
1490*4882a593Smuzhiyun 	int length, len;
1491*4882a593Smuzhiyun 	unsigned int data_offset, data_len;
1492*4882a593Smuzhiyun 	struct cifs_readdata *rdata = mid->callback_data;
1493*4882a593Smuzhiyun 	char *buf = server->smallbuf;
1494*4882a593Smuzhiyun 	unsigned int buflen = server->pdu_size +
1495*4882a593Smuzhiyun 		server->vals->header_preamble_size;
1496*4882a593Smuzhiyun 	bool use_rdma_mr = false;
1497*4882a593Smuzhiyun 
1498*4882a593Smuzhiyun 	cifs_dbg(FYI, "%s: mid=%llu offset=%llu bytes=%u\n",
1499*4882a593Smuzhiyun 		 __func__, mid->mid, rdata->offset, rdata->bytes);
1500*4882a593Smuzhiyun 
1501*4882a593Smuzhiyun 	/*
1502*4882a593Smuzhiyun 	 * read the rest of READ_RSP header (sans Data array), or whatever we
1503*4882a593Smuzhiyun 	 * can if there's not enough data. At this point, we've read down to
1504*4882a593Smuzhiyun 	 * the Mid.
1505*4882a593Smuzhiyun 	 */
1506*4882a593Smuzhiyun 	len = min_t(unsigned int, buflen, server->vals->read_rsp_size) -
1507*4882a593Smuzhiyun 							HEADER_SIZE(server) + 1;
1508*4882a593Smuzhiyun 
1509*4882a593Smuzhiyun 	length = cifs_read_from_socket(server,
1510*4882a593Smuzhiyun 				       buf + HEADER_SIZE(server) - 1, len);
1511*4882a593Smuzhiyun 	if (length < 0)
1512*4882a593Smuzhiyun 		return length;
1513*4882a593Smuzhiyun 	server->total_read += length;
1514*4882a593Smuzhiyun 
1515*4882a593Smuzhiyun 	if (server->ops->is_session_expired &&
1516*4882a593Smuzhiyun 	    server->ops->is_session_expired(buf)) {
1517*4882a593Smuzhiyun 		cifs_reconnect(server);
1518*4882a593Smuzhiyun 		return -1;
1519*4882a593Smuzhiyun 	}
1520*4882a593Smuzhiyun 
1521*4882a593Smuzhiyun 	if (server->ops->is_status_pending &&
1522*4882a593Smuzhiyun 	    server->ops->is_status_pending(buf, server)) {
1523*4882a593Smuzhiyun 		cifs_discard_remaining_data(server);
1524*4882a593Smuzhiyun 		return -1;
1525*4882a593Smuzhiyun 	}
1526*4882a593Smuzhiyun 
1527*4882a593Smuzhiyun 	/* set up first two iov for signature check and to get credits */
1528*4882a593Smuzhiyun 	rdata->iov[0].iov_base = buf;
1529*4882a593Smuzhiyun 	rdata->iov[0].iov_len = server->vals->header_preamble_size;
1530*4882a593Smuzhiyun 	rdata->iov[1].iov_base = buf + server->vals->header_preamble_size;
1531*4882a593Smuzhiyun 	rdata->iov[1].iov_len =
1532*4882a593Smuzhiyun 		server->total_read - server->vals->header_preamble_size;
1533*4882a593Smuzhiyun 	cifs_dbg(FYI, "0: iov_base=%p iov_len=%zu\n",
1534*4882a593Smuzhiyun 		 rdata->iov[0].iov_base, rdata->iov[0].iov_len);
1535*4882a593Smuzhiyun 	cifs_dbg(FYI, "1: iov_base=%p iov_len=%zu\n",
1536*4882a593Smuzhiyun 		 rdata->iov[1].iov_base, rdata->iov[1].iov_len);
1537*4882a593Smuzhiyun 
1538*4882a593Smuzhiyun 	/* Was the SMB read successful? */
1539*4882a593Smuzhiyun 	rdata->result = server->ops->map_error(buf, false);
1540*4882a593Smuzhiyun 	if (rdata->result != 0) {
1541*4882a593Smuzhiyun 		cifs_dbg(FYI, "%s: server returned error %d\n",
1542*4882a593Smuzhiyun 			 __func__, rdata->result);
1543*4882a593Smuzhiyun 		/* normal error on read response */
1544*4882a593Smuzhiyun 		return __cifs_readv_discard(server, mid, false);
1545*4882a593Smuzhiyun 	}
1546*4882a593Smuzhiyun 
1547*4882a593Smuzhiyun 	/* Is there enough to get to the rest of the READ_RSP header? */
1548*4882a593Smuzhiyun 	if (server->total_read < server->vals->read_rsp_size) {
1549*4882a593Smuzhiyun 		cifs_dbg(FYI, "%s: server returned short header. got=%u expected=%zu\n",
1550*4882a593Smuzhiyun 			 __func__, server->total_read,
1551*4882a593Smuzhiyun 			 server->vals->read_rsp_size);
1552*4882a593Smuzhiyun 		rdata->result = -EIO;
1553*4882a593Smuzhiyun 		return cifs_readv_discard(server, mid);
1554*4882a593Smuzhiyun 	}
1555*4882a593Smuzhiyun 
1556*4882a593Smuzhiyun 	data_offset = server->ops->read_data_offset(buf) +
1557*4882a593Smuzhiyun 		server->vals->header_preamble_size;
1558*4882a593Smuzhiyun 	if (data_offset < server->total_read) {
1559*4882a593Smuzhiyun 		/*
1560*4882a593Smuzhiyun 		 * win2k8 sometimes sends an offset of 0 when the read
1561*4882a593Smuzhiyun 		 * is beyond the EOF. Treat it as if the data starts just after
1562*4882a593Smuzhiyun 		 * the header.
1563*4882a593Smuzhiyun 		 */
1564*4882a593Smuzhiyun 		cifs_dbg(FYI, "%s: data offset (%u) inside read response header\n",
1565*4882a593Smuzhiyun 			 __func__, data_offset);
1566*4882a593Smuzhiyun 		data_offset = server->total_read;
1567*4882a593Smuzhiyun 	} else if (data_offset > MAX_CIFS_SMALL_BUFFER_SIZE) {
1568*4882a593Smuzhiyun 		/* data_offset is beyond the end of smallbuf */
1569*4882a593Smuzhiyun 		cifs_dbg(FYI, "%s: data offset (%u) beyond end of smallbuf\n",
1570*4882a593Smuzhiyun 			 __func__, data_offset);
1571*4882a593Smuzhiyun 		rdata->result = -EIO;
1572*4882a593Smuzhiyun 		return cifs_readv_discard(server, mid);
1573*4882a593Smuzhiyun 	}
1574*4882a593Smuzhiyun 
1575*4882a593Smuzhiyun 	cifs_dbg(FYI, "%s: total_read=%u data_offset=%u\n",
1576*4882a593Smuzhiyun 		 __func__, server->total_read, data_offset);
1577*4882a593Smuzhiyun 
1578*4882a593Smuzhiyun 	len = data_offset - server->total_read;
1579*4882a593Smuzhiyun 	if (len > 0) {
1580*4882a593Smuzhiyun 		/* read any junk before data into the rest of smallbuf */
1581*4882a593Smuzhiyun 		length = cifs_read_from_socket(server,
1582*4882a593Smuzhiyun 					       buf + server->total_read, len);
1583*4882a593Smuzhiyun 		if (length < 0)
1584*4882a593Smuzhiyun 			return length;
1585*4882a593Smuzhiyun 		server->total_read += length;
1586*4882a593Smuzhiyun 	}
1587*4882a593Smuzhiyun 
1588*4882a593Smuzhiyun 	/* how much data is in the response? */
1589*4882a593Smuzhiyun #ifdef CONFIG_CIFS_SMB_DIRECT
1590*4882a593Smuzhiyun 	use_rdma_mr = rdata->mr;
1591*4882a593Smuzhiyun #endif
1592*4882a593Smuzhiyun 	data_len = server->ops->read_data_length(buf, use_rdma_mr);
1593*4882a593Smuzhiyun 	if (!use_rdma_mr && (data_offset + data_len > buflen)) {
1594*4882a593Smuzhiyun 		/* data_len is corrupt -- discard frame */
1595*4882a593Smuzhiyun 		rdata->result = -EIO;
1596*4882a593Smuzhiyun 		return cifs_readv_discard(server, mid);
1597*4882a593Smuzhiyun 	}
1598*4882a593Smuzhiyun 
1599*4882a593Smuzhiyun 	length = rdata->read_into_pages(server, rdata, data_len);
1600*4882a593Smuzhiyun 	if (length < 0)
1601*4882a593Smuzhiyun 		return length;
1602*4882a593Smuzhiyun 
1603*4882a593Smuzhiyun 	server->total_read += length;
1604*4882a593Smuzhiyun 
1605*4882a593Smuzhiyun 	cifs_dbg(FYI, "total_read=%u buflen=%u remaining=%u\n",
1606*4882a593Smuzhiyun 		 server->total_read, buflen, data_len);
1607*4882a593Smuzhiyun 
1608*4882a593Smuzhiyun 	/* discard anything left over */
1609*4882a593Smuzhiyun 	if (server->total_read < buflen)
1610*4882a593Smuzhiyun 		return cifs_readv_discard(server, mid);
1611*4882a593Smuzhiyun 
1612*4882a593Smuzhiyun 	dequeue_mid(mid, false);
1613*4882a593Smuzhiyun 	mid->resp_buf = server->smallbuf;
1614*4882a593Smuzhiyun 	server->smallbuf = NULL;
1615*4882a593Smuzhiyun 	return length;
1616*4882a593Smuzhiyun }
1617*4882a593Smuzhiyun 
1618*4882a593Smuzhiyun static void
cifs_readv_callback(struct mid_q_entry * mid)1619*4882a593Smuzhiyun cifs_readv_callback(struct mid_q_entry *mid)
1620*4882a593Smuzhiyun {
1621*4882a593Smuzhiyun 	struct cifs_readdata *rdata = mid->callback_data;
1622*4882a593Smuzhiyun 	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
1623*4882a593Smuzhiyun 	struct TCP_Server_Info *server = tcon->ses->server;
1624*4882a593Smuzhiyun 	struct smb_rqst rqst = { .rq_iov = rdata->iov,
1625*4882a593Smuzhiyun 				 .rq_nvec = 2,
1626*4882a593Smuzhiyun 				 .rq_pages = rdata->pages,
1627*4882a593Smuzhiyun 				 .rq_offset = rdata->page_offset,
1628*4882a593Smuzhiyun 				 .rq_npages = rdata->nr_pages,
1629*4882a593Smuzhiyun 				 .rq_pagesz = rdata->pagesz,
1630*4882a593Smuzhiyun 				 .rq_tailsz = rdata->tailsz };
1631*4882a593Smuzhiyun 	struct cifs_credits credits = { .value = 1, .instance = 0 };
1632*4882a593Smuzhiyun 
1633*4882a593Smuzhiyun 	cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n",
1634*4882a593Smuzhiyun 		 __func__, mid->mid, mid->mid_state, rdata->result,
1635*4882a593Smuzhiyun 		 rdata->bytes);
1636*4882a593Smuzhiyun 
1637*4882a593Smuzhiyun 	switch (mid->mid_state) {
1638*4882a593Smuzhiyun 	case MID_RESPONSE_RECEIVED:
1639*4882a593Smuzhiyun 		/* result already set, check signature */
1640*4882a593Smuzhiyun 		if (server->sign) {
1641*4882a593Smuzhiyun 			int rc = 0;
1642*4882a593Smuzhiyun 
1643*4882a593Smuzhiyun 			rc = cifs_verify_signature(&rqst, server,
1644*4882a593Smuzhiyun 						  mid->sequence_number);
1645*4882a593Smuzhiyun 			if (rc)
1646*4882a593Smuzhiyun 				cifs_dbg(VFS, "SMB signature verification returned error = %d\n",
1647*4882a593Smuzhiyun 					 rc);
1648*4882a593Smuzhiyun 		}
1649*4882a593Smuzhiyun 		/* FIXME: should this be counted toward the initiating task? */
1650*4882a593Smuzhiyun 		task_io_account_read(rdata->got_bytes);
1651*4882a593Smuzhiyun 		cifs_stats_bytes_read(tcon, rdata->got_bytes);
1652*4882a593Smuzhiyun 		break;
1653*4882a593Smuzhiyun 	case MID_REQUEST_SUBMITTED:
1654*4882a593Smuzhiyun 	case MID_RETRY_NEEDED:
1655*4882a593Smuzhiyun 		rdata->result = -EAGAIN;
1656*4882a593Smuzhiyun 		if (server->sign && rdata->got_bytes)
1657*4882a593Smuzhiyun 			/* reset bytes number since we can not check a sign */
1658*4882a593Smuzhiyun 			rdata->got_bytes = 0;
1659*4882a593Smuzhiyun 		/* FIXME: should this be counted toward the initiating task? */
1660*4882a593Smuzhiyun 		task_io_account_read(rdata->got_bytes);
1661*4882a593Smuzhiyun 		cifs_stats_bytes_read(tcon, rdata->got_bytes);
1662*4882a593Smuzhiyun 		break;
1663*4882a593Smuzhiyun 	default:
1664*4882a593Smuzhiyun 		rdata->result = -EIO;
1665*4882a593Smuzhiyun 	}
1666*4882a593Smuzhiyun 
1667*4882a593Smuzhiyun 	queue_work(cifsiod_wq, &rdata->work);
1668*4882a593Smuzhiyun 	DeleteMidQEntry(mid);
1669*4882a593Smuzhiyun 	add_credits(server, &credits, 0);
1670*4882a593Smuzhiyun }
1671*4882a593Smuzhiyun 
1672*4882a593Smuzhiyun /* cifs_async_readv - send an async write, and set up mid to handle result */
1673*4882a593Smuzhiyun int
cifs_async_readv(struct cifs_readdata * rdata)1674*4882a593Smuzhiyun cifs_async_readv(struct cifs_readdata *rdata)
1675*4882a593Smuzhiyun {
1676*4882a593Smuzhiyun 	int rc;
1677*4882a593Smuzhiyun 	READ_REQ *smb = NULL;
1678*4882a593Smuzhiyun 	int wct;
1679*4882a593Smuzhiyun 	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
1680*4882a593Smuzhiyun 	struct smb_rqst rqst = { .rq_iov = rdata->iov,
1681*4882a593Smuzhiyun 				 .rq_nvec = 2 };
1682*4882a593Smuzhiyun 
1683*4882a593Smuzhiyun 	cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
1684*4882a593Smuzhiyun 		 __func__, rdata->offset, rdata->bytes);
1685*4882a593Smuzhiyun 
1686*4882a593Smuzhiyun 	if (tcon->ses->capabilities & CAP_LARGE_FILES)
1687*4882a593Smuzhiyun 		wct = 12;
1688*4882a593Smuzhiyun 	else {
1689*4882a593Smuzhiyun 		wct = 10; /* old style read */
1690*4882a593Smuzhiyun 		if ((rdata->offset >> 32) > 0)  {
1691*4882a593Smuzhiyun 			/* can not handle this big offset for old */
1692*4882a593Smuzhiyun 			return -EIO;
1693*4882a593Smuzhiyun 		}
1694*4882a593Smuzhiyun 	}
1695*4882a593Smuzhiyun 
1696*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **)&smb);
1697*4882a593Smuzhiyun 	if (rc)
1698*4882a593Smuzhiyun 		return rc;
1699*4882a593Smuzhiyun 
1700*4882a593Smuzhiyun 	smb->hdr.Pid = cpu_to_le16((__u16)rdata->pid);
1701*4882a593Smuzhiyun 	smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16));
1702*4882a593Smuzhiyun 
1703*4882a593Smuzhiyun 	smb->AndXCommand = 0xFF;	/* none */
1704*4882a593Smuzhiyun 	smb->Fid = rdata->cfile->fid.netfid;
1705*4882a593Smuzhiyun 	smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF);
1706*4882a593Smuzhiyun 	if (wct == 12)
1707*4882a593Smuzhiyun 		smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32);
1708*4882a593Smuzhiyun 	smb->Remaining = 0;
1709*4882a593Smuzhiyun 	smb->MaxCount = cpu_to_le16(rdata->bytes & 0xFFFF);
1710*4882a593Smuzhiyun 	smb->MaxCountHigh = cpu_to_le32(rdata->bytes >> 16);
1711*4882a593Smuzhiyun 	if (wct == 12)
1712*4882a593Smuzhiyun 		smb->ByteCount = 0;
1713*4882a593Smuzhiyun 	else {
1714*4882a593Smuzhiyun 		/* old style read */
1715*4882a593Smuzhiyun 		struct smb_com_readx_req *smbr =
1716*4882a593Smuzhiyun 			(struct smb_com_readx_req *)smb;
1717*4882a593Smuzhiyun 		smbr->ByteCount = 0;
1718*4882a593Smuzhiyun 	}
1719*4882a593Smuzhiyun 
1720*4882a593Smuzhiyun 	/* 4 for RFC1001 length + 1 for BCC */
1721*4882a593Smuzhiyun 	rdata->iov[0].iov_base = smb;
1722*4882a593Smuzhiyun 	rdata->iov[0].iov_len = 4;
1723*4882a593Smuzhiyun 	rdata->iov[1].iov_base = (char *)smb + 4;
1724*4882a593Smuzhiyun 	rdata->iov[1].iov_len = get_rfc1002_length(smb);
1725*4882a593Smuzhiyun 
1726*4882a593Smuzhiyun 	kref_get(&rdata->refcount);
1727*4882a593Smuzhiyun 	rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive,
1728*4882a593Smuzhiyun 			     cifs_readv_callback, NULL, rdata, 0, NULL);
1729*4882a593Smuzhiyun 
1730*4882a593Smuzhiyun 	if (rc == 0)
1731*4882a593Smuzhiyun 		cifs_stats_inc(&tcon->stats.cifs_stats.num_reads);
1732*4882a593Smuzhiyun 	else
1733*4882a593Smuzhiyun 		kref_put(&rdata->refcount, cifs_readdata_release);
1734*4882a593Smuzhiyun 
1735*4882a593Smuzhiyun 	cifs_small_buf_release(smb);
1736*4882a593Smuzhiyun 	return rc;
1737*4882a593Smuzhiyun }
1738*4882a593Smuzhiyun 
1739*4882a593Smuzhiyun int
CIFSSMBRead(const unsigned int xid,struct cifs_io_parms * io_parms,unsigned int * nbytes,char ** buf,int * pbuf_type)1740*4882a593Smuzhiyun CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
1741*4882a593Smuzhiyun 	    unsigned int *nbytes, char **buf, int *pbuf_type)
1742*4882a593Smuzhiyun {
1743*4882a593Smuzhiyun 	int rc = -EACCES;
1744*4882a593Smuzhiyun 	READ_REQ *pSMB = NULL;
1745*4882a593Smuzhiyun 	READ_RSP *pSMBr = NULL;
1746*4882a593Smuzhiyun 	char *pReadData = NULL;
1747*4882a593Smuzhiyun 	int wct;
1748*4882a593Smuzhiyun 	int resp_buf_type = 0;
1749*4882a593Smuzhiyun 	struct kvec iov[1];
1750*4882a593Smuzhiyun 	struct kvec rsp_iov;
1751*4882a593Smuzhiyun 	__u32 pid = io_parms->pid;
1752*4882a593Smuzhiyun 	__u16 netfid = io_parms->netfid;
1753*4882a593Smuzhiyun 	__u64 offset = io_parms->offset;
1754*4882a593Smuzhiyun 	struct cifs_tcon *tcon = io_parms->tcon;
1755*4882a593Smuzhiyun 	unsigned int count = io_parms->length;
1756*4882a593Smuzhiyun 
1757*4882a593Smuzhiyun 	cifs_dbg(FYI, "Reading %d bytes on fid %d\n", count, netfid);
1758*4882a593Smuzhiyun 	if (tcon->ses->capabilities & CAP_LARGE_FILES)
1759*4882a593Smuzhiyun 		wct = 12;
1760*4882a593Smuzhiyun 	else {
1761*4882a593Smuzhiyun 		wct = 10; /* old style read */
1762*4882a593Smuzhiyun 		if ((offset >> 32) > 0)  {
1763*4882a593Smuzhiyun 			/* can not handle this big offset for old */
1764*4882a593Smuzhiyun 			return -EIO;
1765*4882a593Smuzhiyun 		}
1766*4882a593Smuzhiyun 	}
1767*4882a593Smuzhiyun 
1768*4882a593Smuzhiyun 	*nbytes = 0;
1769*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
1770*4882a593Smuzhiyun 	if (rc)
1771*4882a593Smuzhiyun 		return rc;
1772*4882a593Smuzhiyun 
1773*4882a593Smuzhiyun 	pSMB->hdr.Pid = cpu_to_le16((__u16)pid);
1774*4882a593Smuzhiyun 	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
1775*4882a593Smuzhiyun 
1776*4882a593Smuzhiyun 	/* tcon and ses pointer are checked in smb_init */
1777*4882a593Smuzhiyun 	if (tcon->ses->server == NULL)
1778*4882a593Smuzhiyun 		return -ECONNABORTED;
1779*4882a593Smuzhiyun 
1780*4882a593Smuzhiyun 	pSMB->AndXCommand = 0xFF;       /* none */
1781*4882a593Smuzhiyun 	pSMB->Fid = netfid;
1782*4882a593Smuzhiyun 	pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
1783*4882a593Smuzhiyun 	if (wct == 12)
1784*4882a593Smuzhiyun 		pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
1785*4882a593Smuzhiyun 
1786*4882a593Smuzhiyun 	pSMB->Remaining = 0;
1787*4882a593Smuzhiyun 	pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1788*4882a593Smuzhiyun 	pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
1789*4882a593Smuzhiyun 	if (wct == 12)
1790*4882a593Smuzhiyun 		pSMB->ByteCount = 0;  /* no need to do le conversion since 0 */
1791*4882a593Smuzhiyun 	else {
1792*4882a593Smuzhiyun 		/* old style read */
1793*4882a593Smuzhiyun 		struct smb_com_readx_req *pSMBW =
1794*4882a593Smuzhiyun 			(struct smb_com_readx_req *)pSMB;
1795*4882a593Smuzhiyun 		pSMBW->ByteCount = 0;
1796*4882a593Smuzhiyun 	}
1797*4882a593Smuzhiyun 
1798*4882a593Smuzhiyun 	iov[0].iov_base = (char *)pSMB;
1799*4882a593Smuzhiyun 	iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;
1800*4882a593Smuzhiyun 	rc = SendReceive2(xid, tcon->ses, iov, 1, &resp_buf_type,
1801*4882a593Smuzhiyun 			  CIFS_LOG_ERROR, &rsp_iov);
1802*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
1803*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_reads);
1804*4882a593Smuzhiyun 	pSMBr = (READ_RSP *)rsp_iov.iov_base;
1805*4882a593Smuzhiyun 	if (rc) {
1806*4882a593Smuzhiyun 		cifs_dbg(VFS, "Send error in read = %d\n", rc);
1807*4882a593Smuzhiyun 	} else {
1808*4882a593Smuzhiyun 		int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1809*4882a593Smuzhiyun 		data_length = data_length << 16;
1810*4882a593Smuzhiyun 		data_length += le16_to_cpu(pSMBr->DataLength);
1811*4882a593Smuzhiyun 		*nbytes = data_length;
1812*4882a593Smuzhiyun 
1813*4882a593Smuzhiyun 		/*check that DataLength would not go beyond end of SMB */
1814*4882a593Smuzhiyun 		if ((data_length > CIFSMaxBufSize)
1815*4882a593Smuzhiyun 				|| (data_length > count)) {
1816*4882a593Smuzhiyun 			cifs_dbg(FYI, "bad length %d for count %d\n",
1817*4882a593Smuzhiyun 				 data_length, count);
1818*4882a593Smuzhiyun 			rc = -EIO;
1819*4882a593Smuzhiyun 			*nbytes = 0;
1820*4882a593Smuzhiyun 		} else {
1821*4882a593Smuzhiyun 			pReadData = (char *) (&pSMBr->hdr.Protocol) +
1822*4882a593Smuzhiyun 					le16_to_cpu(pSMBr->DataOffset);
1823*4882a593Smuzhiyun /*			if (rc = copy_to_user(buf, pReadData, data_length)) {
1824*4882a593Smuzhiyun 				cifs_dbg(VFS, "Faulting on read rc = %d\n",rc);
1825*4882a593Smuzhiyun 				rc = -EFAULT;
1826*4882a593Smuzhiyun 			}*/ /* can not use copy_to_user when using page cache*/
1827*4882a593Smuzhiyun 			if (*buf)
1828*4882a593Smuzhiyun 				memcpy(*buf, pReadData, data_length);
1829*4882a593Smuzhiyun 		}
1830*4882a593Smuzhiyun 	}
1831*4882a593Smuzhiyun 
1832*4882a593Smuzhiyun 	if (*buf) {
1833*4882a593Smuzhiyun 		free_rsp_buf(resp_buf_type, rsp_iov.iov_base);
1834*4882a593Smuzhiyun 	} else if (resp_buf_type != CIFS_NO_BUFFER) {
1835*4882a593Smuzhiyun 		/* return buffer to caller to free */
1836*4882a593Smuzhiyun 		*buf = rsp_iov.iov_base;
1837*4882a593Smuzhiyun 		if (resp_buf_type == CIFS_SMALL_BUFFER)
1838*4882a593Smuzhiyun 			*pbuf_type = CIFS_SMALL_BUFFER;
1839*4882a593Smuzhiyun 		else if (resp_buf_type == CIFS_LARGE_BUFFER)
1840*4882a593Smuzhiyun 			*pbuf_type = CIFS_LARGE_BUFFER;
1841*4882a593Smuzhiyun 	} /* else no valid buffer on return - leave as null */
1842*4882a593Smuzhiyun 
1843*4882a593Smuzhiyun 	/* Note: On -EAGAIN error only caller can retry on handle based calls
1844*4882a593Smuzhiyun 		since file handle passed in no longer valid */
1845*4882a593Smuzhiyun 	return rc;
1846*4882a593Smuzhiyun }
1847*4882a593Smuzhiyun 
1848*4882a593Smuzhiyun 
1849*4882a593Smuzhiyun int
CIFSSMBWrite(const unsigned int xid,struct cifs_io_parms * io_parms,unsigned int * nbytes,const char * buf)1850*4882a593Smuzhiyun CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
1851*4882a593Smuzhiyun 	     unsigned int *nbytes, const char *buf)
1852*4882a593Smuzhiyun {
1853*4882a593Smuzhiyun 	int rc = -EACCES;
1854*4882a593Smuzhiyun 	WRITE_REQ *pSMB = NULL;
1855*4882a593Smuzhiyun 	WRITE_RSP *pSMBr = NULL;
1856*4882a593Smuzhiyun 	int bytes_returned, wct;
1857*4882a593Smuzhiyun 	__u32 bytes_sent;
1858*4882a593Smuzhiyun 	__u16 byte_count;
1859*4882a593Smuzhiyun 	__u32 pid = io_parms->pid;
1860*4882a593Smuzhiyun 	__u16 netfid = io_parms->netfid;
1861*4882a593Smuzhiyun 	__u64 offset = io_parms->offset;
1862*4882a593Smuzhiyun 	struct cifs_tcon *tcon = io_parms->tcon;
1863*4882a593Smuzhiyun 	unsigned int count = io_parms->length;
1864*4882a593Smuzhiyun 
1865*4882a593Smuzhiyun 	*nbytes = 0;
1866*4882a593Smuzhiyun 
1867*4882a593Smuzhiyun 	/* cifs_dbg(FYI, "write at %lld %d bytes\n", offset, count);*/
1868*4882a593Smuzhiyun 	if (tcon->ses == NULL)
1869*4882a593Smuzhiyun 		return -ECONNABORTED;
1870*4882a593Smuzhiyun 
1871*4882a593Smuzhiyun 	if (tcon->ses->capabilities & CAP_LARGE_FILES)
1872*4882a593Smuzhiyun 		wct = 14;
1873*4882a593Smuzhiyun 	else {
1874*4882a593Smuzhiyun 		wct = 12;
1875*4882a593Smuzhiyun 		if ((offset >> 32) > 0) {
1876*4882a593Smuzhiyun 			/* can not handle big offset for old srv */
1877*4882a593Smuzhiyun 			return -EIO;
1878*4882a593Smuzhiyun 		}
1879*4882a593Smuzhiyun 	}
1880*4882a593Smuzhiyun 
1881*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
1882*4882a593Smuzhiyun 		      (void **) &pSMBr);
1883*4882a593Smuzhiyun 	if (rc)
1884*4882a593Smuzhiyun 		return rc;
1885*4882a593Smuzhiyun 
1886*4882a593Smuzhiyun 	pSMB->hdr.Pid = cpu_to_le16((__u16)pid);
1887*4882a593Smuzhiyun 	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
1888*4882a593Smuzhiyun 
1889*4882a593Smuzhiyun 	/* tcon and ses pointer are checked in smb_init */
1890*4882a593Smuzhiyun 	if (tcon->ses->server == NULL)
1891*4882a593Smuzhiyun 		return -ECONNABORTED;
1892*4882a593Smuzhiyun 
1893*4882a593Smuzhiyun 	pSMB->AndXCommand = 0xFF;	/* none */
1894*4882a593Smuzhiyun 	pSMB->Fid = netfid;
1895*4882a593Smuzhiyun 	pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
1896*4882a593Smuzhiyun 	if (wct == 14)
1897*4882a593Smuzhiyun 		pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
1898*4882a593Smuzhiyun 
1899*4882a593Smuzhiyun 	pSMB->Reserved = 0xFFFFFFFF;
1900*4882a593Smuzhiyun 	pSMB->WriteMode = 0;
1901*4882a593Smuzhiyun 	pSMB->Remaining = 0;
1902*4882a593Smuzhiyun 
1903*4882a593Smuzhiyun 	/* Can increase buffer size if buffer is big enough in some cases ie we
1904*4882a593Smuzhiyun 	can send more if LARGE_WRITE_X capability returned by the server and if
1905*4882a593Smuzhiyun 	our buffer is big enough or if we convert to iovecs on socket writes
1906*4882a593Smuzhiyun 	and eliminate the copy to the CIFS buffer */
1907*4882a593Smuzhiyun 	if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
1908*4882a593Smuzhiyun 		bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1909*4882a593Smuzhiyun 	} else {
1910*4882a593Smuzhiyun 		bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1911*4882a593Smuzhiyun 			 & ~0xFF;
1912*4882a593Smuzhiyun 	}
1913*4882a593Smuzhiyun 
1914*4882a593Smuzhiyun 	if (bytes_sent > count)
1915*4882a593Smuzhiyun 		bytes_sent = count;
1916*4882a593Smuzhiyun 	pSMB->DataOffset =
1917*4882a593Smuzhiyun 		cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
1918*4882a593Smuzhiyun 	if (buf)
1919*4882a593Smuzhiyun 		memcpy(pSMB->Data, buf, bytes_sent);
1920*4882a593Smuzhiyun 	else if (count != 0) {
1921*4882a593Smuzhiyun 		/* No buffer */
1922*4882a593Smuzhiyun 		cifs_buf_release(pSMB);
1923*4882a593Smuzhiyun 		return -EINVAL;
1924*4882a593Smuzhiyun 	} /* else setting file size with write of zero bytes */
1925*4882a593Smuzhiyun 	if (wct == 14)
1926*4882a593Smuzhiyun 		byte_count = bytes_sent + 1; /* pad */
1927*4882a593Smuzhiyun 	else /* wct == 12 */
1928*4882a593Smuzhiyun 		byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
1929*4882a593Smuzhiyun 
1930*4882a593Smuzhiyun 	pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1931*4882a593Smuzhiyun 	pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
1932*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
1933*4882a593Smuzhiyun 
1934*4882a593Smuzhiyun 	if (wct == 14)
1935*4882a593Smuzhiyun 		pSMB->ByteCount = cpu_to_le16(byte_count);
1936*4882a593Smuzhiyun 	else { /* old style write has byte count 4 bytes earlier
1937*4882a593Smuzhiyun 		  so 4 bytes pad  */
1938*4882a593Smuzhiyun 		struct smb_com_writex_req *pSMBW =
1939*4882a593Smuzhiyun 			(struct smb_com_writex_req *)pSMB;
1940*4882a593Smuzhiyun 		pSMBW->ByteCount = cpu_to_le16(byte_count);
1941*4882a593Smuzhiyun 	}
1942*4882a593Smuzhiyun 
1943*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1944*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1945*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
1946*4882a593Smuzhiyun 	if (rc) {
1947*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in write = %d\n", rc);
1948*4882a593Smuzhiyun 	} else {
1949*4882a593Smuzhiyun 		*nbytes = le16_to_cpu(pSMBr->CountHigh);
1950*4882a593Smuzhiyun 		*nbytes = (*nbytes) << 16;
1951*4882a593Smuzhiyun 		*nbytes += le16_to_cpu(pSMBr->Count);
1952*4882a593Smuzhiyun 
1953*4882a593Smuzhiyun 		/*
1954*4882a593Smuzhiyun 		 * Mask off high 16 bits when bytes written as returned by the
1955*4882a593Smuzhiyun 		 * server is greater than bytes requested by the client. Some
1956*4882a593Smuzhiyun 		 * OS/2 servers are known to set incorrect CountHigh values.
1957*4882a593Smuzhiyun 		 */
1958*4882a593Smuzhiyun 		if (*nbytes > count)
1959*4882a593Smuzhiyun 			*nbytes &= 0xFFFF;
1960*4882a593Smuzhiyun 	}
1961*4882a593Smuzhiyun 
1962*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
1963*4882a593Smuzhiyun 
1964*4882a593Smuzhiyun 	/* Note: On -EAGAIN error only caller can retry on handle based calls
1965*4882a593Smuzhiyun 		since file handle passed in no longer valid */
1966*4882a593Smuzhiyun 
1967*4882a593Smuzhiyun 	return rc;
1968*4882a593Smuzhiyun }
1969*4882a593Smuzhiyun 
1970*4882a593Smuzhiyun void
cifs_writedata_release(struct kref * refcount)1971*4882a593Smuzhiyun cifs_writedata_release(struct kref *refcount)
1972*4882a593Smuzhiyun {
1973*4882a593Smuzhiyun 	struct cifs_writedata *wdata = container_of(refcount,
1974*4882a593Smuzhiyun 					struct cifs_writedata, refcount);
1975*4882a593Smuzhiyun #ifdef CONFIG_CIFS_SMB_DIRECT
1976*4882a593Smuzhiyun 	if (wdata->mr) {
1977*4882a593Smuzhiyun 		smbd_deregister_mr(wdata->mr);
1978*4882a593Smuzhiyun 		wdata->mr = NULL;
1979*4882a593Smuzhiyun 	}
1980*4882a593Smuzhiyun #endif
1981*4882a593Smuzhiyun 
1982*4882a593Smuzhiyun 	if (wdata->cfile)
1983*4882a593Smuzhiyun 		cifsFileInfo_put(wdata->cfile);
1984*4882a593Smuzhiyun 
1985*4882a593Smuzhiyun 	kvfree(wdata->pages);
1986*4882a593Smuzhiyun 	kfree(wdata);
1987*4882a593Smuzhiyun }
1988*4882a593Smuzhiyun 
1989*4882a593Smuzhiyun /*
1990*4882a593Smuzhiyun  * Write failed with a retryable error. Resend the write request. It's also
1991*4882a593Smuzhiyun  * possible that the page was redirtied so re-clean the page.
1992*4882a593Smuzhiyun  */
1993*4882a593Smuzhiyun static void
cifs_writev_requeue(struct cifs_writedata * wdata)1994*4882a593Smuzhiyun cifs_writev_requeue(struct cifs_writedata *wdata)
1995*4882a593Smuzhiyun {
1996*4882a593Smuzhiyun 	int i, rc = 0;
1997*4882a593Smuzhiyun 	struct inode *inode = d_inode(wdata->cfile->dentry);
1998*4882a593Smuzhiyun 	struct TCP_Server_Info *server;
1999*4882a593Smuzhiyun 	unsigned int rest_len;
2000*4882a593Smuzhiyun 
2001*4882a593Smuzhiyun 	server = tlink_tcon(wdata->cfile->tlink)->ses->server;
2002*4882a593Smuzhiyun 	i = 0;
2003*4882a593Smuzhiyun 	rest_len = wdata->bytes;
2004*4882a593Smuzhiyun 	do {
2005*4882a593Smuzhiyun 		struct cifs_writedata *wdata2;
2006*4882a593Smuzhiyun 		unsigned int j, nr_pages, wsize, tailsz, cur_len;
2007*4882a593Smuzhiyun 
2008*4882a593Smuzhiyun 		wsize = server->ops->wp_retry_size(inode);
2009*4882a593Smuzhiyun 		if (wsize < rest_len) {
2010*4882a593Smuzhiyun 			nr_pages = wsize / PAGE_SIZE;
2011*4882a593Smuzhiyun 			if (!nr_pages) {
2012*4882a593Smuzhiyun 				rc = -ENOTSUPP;
2013*4882a593Smuzhiyun 				break;
2014*4882a593Smuzhiyun 			}
2015*4882a593Smuzhiyun 			cur_len = nr_pages * PAGE_SIZE;
2016*4882a593Smuzhiyun 			tailsz = PAGE_SIZE;
2017*4882a593Smuzhiyun 		} else {
2018*4882a593Smuzhiyun 			nr_pages = DIV_ROUND_UP(rest_len, PAGE_SIZE);
2019*4882a593Smuzhiyun 			cur_len = rest_len;
2020*4882a593Smuzhiyun 			tailsz = rest_len - (nr_pages - 1) * PAGE_SIZE;
2021*4882a593Smuzhiyun 		}
2022*4882a593Smuzhiyun 
2023*4882a593Smuzhiyun 		wdata2 = cifs_writedata_alloc(nr_pages, cifs_writev_complete);
2024*4882a593Smuzhiyun 		if (!wdata2) {
2025*4882a593Smuzhiyun 			rc = -ENOMEM;
2026*4882a593Smuzhiyun 			break;
2027*4882a593Smuzhiyun 		}
2028*4882a593Smuzhiyun 
2029*4882a593Smuzhiyun 		for (j = 0; j < nr_pages; j++) {
2030*4882a593Smuzhiyun 			wdata2->pages[j] = wdata->pages[i + j];
2031*4882a593Smuzhiyun 			lock_page(wdata2->pages[j]);
2032*4882a593Smuzhiyun 			clear_page_dirty_for_io(wdata2->pages[j]);
2033*4882a593Smuzhiyun 		}
2034*4882a593Smuzhiyun 
2035*4882a593Smuzhiyun 		wdata2->sync_mode = wdata->sync_mode;
2036*4882a593Smuzhiyun 		wdata2->nr_pages = nr_pages;
2037*4882a593Smuzhiyun 		wdata2->offset = page_offset(wdata2->pages[0]);
2038*4882a593Smuzhiyun 		wdata2->pagesz = PAGE_SIZE;
2039*4882a593Smuzhiyun 		wdata2->tailsz = tailsz;
2040*4882a593Smuzhiyun 		wdata2->bytes = cur_len;
2041*4882a593Smuzhiyun 
2042*4882a593Smuzhiyun 		rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY,
2043*4882a593Smuzhiyun 					    &wdata2->cfile);
2044*4882a593Smuzhiyun 		if (!wdata2->cfile) {
2045*4882a593Smuzhiyun 			cifs_dbg(VFS, "No writable handle to retry writepages rc=%d\n",
2046*4882a593Smuzhiyun 				 rc);
2047*4882a593Smuzhiyun 			if (!is_retryable_error(rc))
2048*4882a593Smuzhiyun 				rc = -EBADF;
2049*4882a593Smuzhiyun 		} else {
2050*4882a593Smuzhiyun 			wdata2->pid = wdata2->cfile->pid;
2051*4882a593Smuzhiyun 			rc = server->ops->async_writev(wdata2,
2052*4882a593Smuzhiyun 						       cifs_writedata_release);
2053*4882a593Smuzhiyun 		}
2054*4882a593Smuzhiyun 
2055*4882a593Smuzhiyun 		for (j = 0; j < nr_pages; j++) {
2056*4882a593Smuzhiyun 			unlock_page(wdata2->pages[j]);
2057*4882a593Smuzhiyun 			if (rc != 0 && !is_retryable_error(rc)) {
2058*4882a593Smuzhiyun 				SetPageError(wdata2->pages[j]);
2059*4882a593Smuzhiyun 				end_page_writeback(wdata2->pages[j]);
2060*4882a593Smuzhiyun 				put_page(wdata2->pages[j]);
2061*4882a593Smuzhiyun 			}
2062*4882a593Smuzhiyun 		}
2063*4882a593Smuzhiyun 
2064*4882a593Smuzhiyun 		kref_put(&wdata2->refcount, cifs_writedata_release);
2065*4882a593Smuzhiyun 		if (rc) {
2066*4882a593Smuzhiyun 			if (is_retryable_error(rc))
2067*4882a593Smuzhiyun 				continue;
2068*4882a593Smuzhiyun 			i += nr_pages;
2069*4882a593Smuzhiyun 			break;
2070*4882a593Smuzhiyun 		}
2071*4882a593Smuzhiyun 
2072*4882a593Smuzhiyun 		rest_len -= cur_len;
2073*4882a593Smuzhiyun 		i += nr_pages;
2074*4882a593Smuzhiyun 	} while (i < wdata->nr_pages);
2075*4882a593Smuzhiyun 
2076*4882a593Smuzhiyun 	/* cleanup remaining pages from the original wdata */
2077*4882a593Smuzhiyun 	for (; i < wdata->nr_pages; i++) {
2078*4882a593Smuzhiyun 		SetPageError(wdata->pages[i]);
2079*4882a593Smuzhiyun 		end_page_writeback(wdata->pages[i]);
2080*4882a593Smuzhiyun 		put_page(wdata->pages[i]);
2081*4882a593Smuzhiyun 	}
2082*4882a593Smuzhiyun 
2083*4882a593Smuzhiyun 	if (rc != 0 && !is_retryable_error(rc))
2084*4882a593Smuzhiyun 		mapping_set_error(inode->i_mapping, rc);
2085*4882a593Smuzhiyun 	kref_put(&wdata->refcount, cifs_writedata_release);
2086*4882a593Smuzhiyun }
2087*4882a593Smuzhiyun 
2088*4882a593Smuzhiyun void
cifs_writev_complete(struct work_struct * work)2089*4882a593Smuzhiyun cifs_writev_complete(struct work_struct *work)
2090*4882a593Smuzhiyun {
2091*4882a593Smuzhiyun 	struct cifs_writedata *wdata = container_of(work,
2092*4882a593Smuzhiyun 						struct cifs_writedata, work);
2093*4882a593Smuzhiyun 	struct inode *inode = d_inode(wdata->cfile->dentry);
2094*4882a593Smuzhiyun 	int i = 0;
2095*4882a593Smuzhiyun 
2096*4882a593Smuzhiyun 	if (wdata->result == 0) {
2097*4882a593Smuzhiyun 		spin_lock(&inode->i_lock);
2098*4882a593Smuzhiyun 		cifs_update_eof(CIFS_I(inode), wdata->offset, wdata->bytes);
2099*4882a593Smuzhiyun 		spin_unlock(&inode->i_lock);
2100*4882a593Smuzhiyun 		cifs_stats_bytes_written(tlink_tcon(wdata->cfile->tlink),
2101*4882a593Smuzhiyun 					 wdata->bytes);
2102*4882a593Smuzhiyun 	} else if (wdata->sync_mode == WB_SYNC_ALL && wdata->result == -EAGAIN)
2103*4882a593Smuzhiyun 		return cifs_writev_requeue(wdata);
2104*4882a593Smuzhiyun 
2105*4882a593Smuzhiyun 	for (i = 0; i < wdata->nr_pages; i++) {
2106*4882a593Smuzhiyun 		struct page *page = wdata->pages[i];
2107*4882a593Smuzhiyun 		if (wdata->result == -EAGAIN)
2108*4882a593Smuzhiyun 			__set_page_dirty_nobuffers(page);
2109*4882a593Smuzhiyun 		else if (wdata->result < 0)
2110*4882a593Smuzhiyun 			SetPageError(page);
2111*4882a593Smuzhiyun 		end_page_writeback(page);
2112*4882a593Smuzhiyun 		put_page(page);
2113*4882a593Smuzhiyun 	}
2114*4882a593Smuzhiyun 	if (wdata->result != -EAGAIN)
2115*4882a593Smuzhiyun 		mapping_set_error(inode->i_mapping, wdata->result);
2116*4882a593Smuzhiyun 	kref_put(&wdata->refcount, cifs_writedata_release);
2117*4882a593Smuzhiyun }
2118*4882a593Smuzhiyun 
2119*4882a593Smuzhiyun struct cifs_writedata *
cifs_writedata_alloc(unsigned int nr_pages,work_func_t complete)2120*4882a593Smuzhiyun cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete)
2121*4882a593Smuzhiyun {
2122*4882a593Smuzhiyun 	struct page **pages =
2123*4882a593Smuzhiyun 		kcalloc(nr_pages, sizeof(struct page *), GFP_NOFS);
2124*4882a593Smuzhiyun 	if (pages)
2125*4882a593Smuzhiyun 		return cifs_writedata_direct_alloc(pages, complete);
2126*4882a593Smuzhiyun 
2127*4882a593Smuzhiyun 	return NULL;
2128*4882a593Smuzhiyun }
2129*4882a593Smuzhiyun 
2130*4882a593Smuzhiyun struct cifs_writedata *
cifs_writedata_direct_alloc(struct page ** pages,work_func_t complete)2131*4882a593Smuzhiyun cifs_writedata_direct_alloc(struct page **pages, work_func_t complete)
2132*4882a593Smuzhiyun {
2133*4882a593Smuzhiyun 	struct cifs_writedata *wdata;
2134*4882a593Smuzhiyun 
2135*4882a593Smuzhiyun 	wdata = kzalloc(sizeof(*wdata), GFP_NOFS);
2136*4882a593Smuzhiyun 	if (wdata != NULL) {
2137*4882a593Smuzhiyun 		wdata->pages = pages;
2138*4882a593Smuzhiyun 		kref_init(&wdata->refcount);
2139*4882a593Smuzhiyun 		INIT_LIST_HEAD(&wdata->list);
2140*4882a593Smuzhiyun 		init_completion(&wdata->done);
2141*4882a593Smuzhiyun 		INIT_WORK(&wdata->work, complete);
2142*4882a593Smuzhiyun 	}
2143*4882a593Smuzhiyun 	return wdata;
2144*4882a593Smuzhiyun }
2145*4882a593Smuzhiyun 
2146*4882a593Smuzhiyun /*
2147*4882a593Smuzhiyun  * Check the mid_state and signature on received buffer (if any), and queue the
2148*4882a593Smuzhiyun  * workqueue completion task.
2149*4882a593Smuzhiyun  */
2150*4882a593Smuzhiyun static void
cifs_writev_callback(struct mid_q_entry * mid)2151*4882a593Smuzhiyun cifs_writev_callback(struct mid_q_entry *mid)
2152*4882a593Smuzhiyun {
2153*4882a593Smuzhiyun 	struct cifs_writedata *wdata = mid->callback_data;
2154*4882a593Smuzhiyun 	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
2155*4882a593Smuzhiyun 	unsigned int written;
2156*4882a593Smuzhiyun 	WRITE_RSP *smb = (WRITE_RSP *)mid->resp_buf;
2157*4882a593Smuzhiyun 	struct cifs_credits credits = { .value = 1, .instance = 0 };
2158*4882a593Smuzhiyun 
2159*4882a593Smuzhiyun 	switch (mid->mid_state) {
2160*4882a593Smuzhiyun 	case MID_RESPONSE_RECEIVED:
2161*4882a593Smuzhiyun 		wdata->result = cifs_check_receive(mid, tcon->ses->server, 0);
2162*4882a593Smuzhiyun 		if (wdata->result != 0)
2163*4882a593Smuzhiyun 			break;
2164*4882a593Smuzhiyun 
2165*4882a593Smuzhiyun 		written = le16_to_cpu(smb->CountHigh);
2166*4882a593Smuzhiyun 		written <<= 16;
2167*4882a593Smuzhiyun 		written += le16_to_cpu(smb->Count);
2168*4882a593Smuzhiyun 		/*
2169*4882a593Smuzhiyun 		 * Mask off high 16 bits when bytes written as returned
2170*4882a593Smuzhiyun 		 * by the server is greater than bytes requested by the
2171*4882a593Smuzhiyun 		 * client. OS/2 servers are known to set incorrect
2172*4882a593Smuzhiyun 		 * CountHigh values.
2173*4882a593Smuzhiyun 		 */
2174*4882a593Smuzhiyun 		if (written > wdata->bytes)
2175*4882a593Smuzhiyun 			written &= 0xFFFF;
2176*4882a593Smuzhiyun 
2177*4882a593Smuzhiyun 		if (written < wdata->bytes)
2178*4882a593Smuzhiyun 			wdata->result = -ENOSPC;
2179*4882a593Smuzhiyun 		else
2180*4882a593Smuzhiyun 			wdata->bytes = written;
2181*4882a593Smuzhiyun 		break;
2182*4882a593Smuzhiyun 	case MID_REQUEST_SUBMITTED:
2183*4882a593Smuzhiyun 	case MID_RETRY_NEEDED:
2184*4882a593Smuzhiyun 		wdata->result = -EAGAIN;
2185*4882a593Smuzhiyun 		break;
2186*4882a593Smuzhiyun 	default:
2187*4882a593Smuzhiyun 		wdata->result = -EIO;
2188*4882a593Smuzhiyun 		break;
2189*4882a593Smuzhiyun 	}
2190*4882a593Smuzhiyun 
2191*4882a593Smuzhiyun 	queue_work(cifsiod_wq, &wdata->work);
2192*4882a593Smuzhiyun 	DeleteMidQEntry(mid);
2193*4882a593Smuzhiyun 	add_credits(tcon->ses->server, &credits, 0);
2194*4882a593Smuzhiyun }
2195*4882a593Smuzhiyun 
2196*4882a593Smuzhiyun /* cifs_async_writev - send an async write, and set up mid to handle result */
2197*4882a593Smuzhiyun int
cifs_async_writev(struct cifs_writedata * wdata,void (* release)(struct kref * kref))2198*4882a593Smuzhiyun cifs_async_writev(struct cifs_writedata *wdata,
2199*4882a593Smuzhiyun 		  void (*release)(struct kref *kref))
2200*4882a593Smuzhiyun {
2201*4882a593Smuzhiyun 	int rc = -EACCES;
2202*4882a593Smuzhiyun 	WRITE_REQ *smb = NULL;
2203*4882a593Smuzhiyun 	int wct;
2204*4882a593Smuzhiyun 	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
2205*4882a593Smuzhiyun 	struct kvec iov[2];
2206*4882a593Smuzhiyun 	struct smb_rqst rqst = { };
2207*4882a593Smuzhiyun 
2208*4882a593Smuzhiyun 	if (tcon->ses->capabilities & CAP_LARGE_FILES) {
2209*4882a593Smuzhiyun 		wct = 14;
2210*4882a593Smuzhiyun 	} else {
2211*4882a593Smuzhiyun 		wct = 12;
2212*4882a593Smuzhiyun 		if (wdata->offset >> 32 > 0) {
2213*4882a593Smuzhiyun 			/* can not handle big offset for old srv */
2214*4882a593Smuzhiyun 			return -EIO;
2215*4882a593Smuzhiyun 		}
2216*4882a593Smuzhiyun 	}
2217*4882a593Smuzhiyun 
2218*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **)&smb);
2219*4882a593Smuzhiyun 	if (rc)
2220*4882a593Smuzhiyun 		goto async_writev_out;
2221*4882a593Smuzhiyun 
2222*4882a593Smuzhiyun 	smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid);
2223*4882a593Smuzhiyun 	smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16));
2224*4882a593Smuzhiyun 
2225*4882a593Smuzhiyun 	smb->AndXCommand = 0xFF;	/* none */
2226*4882a593Smuzhiyun 	smb->Fid = wdata->cfile->fid.netfid;
2227*4882a593Smuzhiyun 	smb->OffsetLow = cpu_to_le32(wdata->offset & 0xFFFFFFFF);
2228*4882a593Smuzhiyun 	if (wct == 14)
2229*4882a593Smuzhiyun 		smb->OffsetHigh = cpu_to_le32(wdata->offset >> 32);
2230*4882a593Smuzhiyun 	smb->Reserved = 0xFFFFFFFF;
2231*4882a593Smuzhiyun 	smb->WriteMode = 0;
2232*4882a593Smuzhiyun 	smb->Remaining = 0;
2233*4882a593Smuzhiyun 
2234*4882a593Smuzhiyun 	smb->DataOffset =
2235*4882a593Smuzhiyun 	    cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
2236*4882a593Smuzhiyun 
2237*4882a593Smuzhiyun 	/* 4 for RFC1001 length + 1 for BCC */
2238*4882a593Smuzhiyun 	iov[0].iov_len = 4;
2239*4882a593Smuzhiyun 	iov[0].iov_base = smb;
2240*4882a593Smuzhiyun 	iov[1].iov_len = get_rfc1002_length(smb) + 1;
2241*4882a593Smuzhiyun 	iov[1].iov_base = (char *)smb + 4;
2242*4882a593Smuzhiyun 
2243*4882a593Smuzhiyun 	rqst.rq_iov = iov;
2244*4882a593Smuzhiyun 	rqst.rq_nvec = 2;
2245*4882a593Smuzhiyun 	rqst.rq_pages = wdata->pages;
2246*4882a593Smuzhiyun 	rqst.rq_offset = wdata->page_offset;
2247*4882a593Smuzhiyun 	rqst.rq_npages = wdata->nr_pages;
2248*4882a593Smuzhiyun 	rqst.rq_pagesz = wdata->pagesz;
2249*4882a593Smuzhiyun 	rqst.rq_tailsz = wdata->tailsz;
2250*4882a593Smuzhiyun 
2251*4882a593Smuzhiyun 	cifs_dbg(FYI, "async write at %llu %u bytes\n",
2252*4882a593Smuzhiyun 		 wdata->offset, wdata->bytes);
2253*4882a593Smuzhiyun 
2254*4882a593Smuzhiyun 	smb->DataLengthLow = cpu_to_le16(wdata->bytes & 0xFFFF);
2255*4882a593Smuzhiyun 	smb->DataLengthHigh = cpu_to_le16(wdata->bytes >> 16);
2256*4882a593Smuzhiyun 
2257*4882a593Smuzhiyun 	if (wct == 14) {
2258*4882a593Smuzhiyun 		inc_rfc1001_len(&smb->hdr, wdata->bytes + 1);
2259*4882a593Smuzhiyun 		put_bcc(wdata->bytes + 1, &smb->hdr);
2260*4882a593Smuzhiyun 	} else {
2261*4882a593Smuzhiyun 		/* wct == 12 */
2262*4882a593Smuzhiyun 		struct smb_com_writex_req *smbw =
2263*4882a593Smuzhiyun 				(struct smb_com_writex_req *)smb;
2264*4882a593Smuzhiyun 		inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5);
2265*4882a593Smuzhiyun 		put_bcc(wdata->bytes + 5, &smbw->hdr);
2266*4882a593Smuzhiyun 		iov[1].iov_len += 4; /* pad bigger by four bytes */
2267*4882a593Smuzhiyun 	}
2268*4882a593Smuzhiyun 
2269*4882a593Smuzhiyun 	kref_get(&wdata->refcount);
2270*4882a593Smuzhiyun 	rc = cifs_call_async(tcon->ses->server, &rqst, NULL,
2271*4882a593Smuzhiyun 			     cifs_writev_callback, NULL, wdata, 0, NULL);
2272*4882a593Smuzhiyun 
2273*4882a593Smuzhiyun 	if (rc == 0)
2274*4882a593Smuzhiyun 		cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
2275*4882a593Smuzhiyun 	else
2276*4882a593Smuzhiyun 		kref_put(&wdata->refcount, release);
2277*4882a593Smuzhiyun 
2278*4882a593Smuzhiyun async_writev_out:
2279*4882a593Smuzhiyun 	cifs_small_buf_release(smb);
2280*4882a593Smuzhiyun 	return rc;
2281*4882a593Smuzhiyun }
2282*4882a593Smuzhiyun 
2283*4882a593Smuzhiyun int
CIFSSMBWrite2(const unsigned int xid,struct cifs_io_parms * io_parms,unsigned int * nbytes,struct kvec * iov,int n_vec)2284*4882a593Smuzhiyun CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
2285*4882a593Smuzhiyun 	      unsigned int *nbytes, struct kvec *iov, int n_vec)
2286*4882a593Smuzhiyun {
2287*4882a593Smuzhiyun 	int rc;
2288*4882a593Smuzhiyun 	WRITE_REQ *pSMB = NULL;
2289*4882a593Smuzhiyun 	int wct;
2290*4882a593Smuzhiyun 	int smb_hdr_len;
2291*4882a593Smuzhiyun 	int resp_buf_type = 0;
2292*4882a593Smuzhiyun 	__u32 pid = io_parms->pid;
2293*4882a593Smuzhiyun 	__u16 netfid = io_parms->netfid;
2294*4882a593Smuzhiyun 	__u64 offset = io_parms->offset;
2295*4882a593Smuzhiyun 	struct cifs_tcon *tcon = io_parms->tcon;
2296*4882a593Smuzhiyun 	unsigned int count = io_parms->length;
2297*4882a593Smuzhiyun 	struct kvec rsp_iov;
2298*4882a593Smuzhiyun 
2299*4882a593Smuzhiyun 	*nbytes = 0;
2300*4882a593Smuzhiyun 
2301*4882a593Smuzhiyun 	cifs_dbg(FYI, "write2 at %lld %d bytes\n", (long long)offset, count);
2302*4882a593Smuzhiyun 
2303*4882a593Smuzhiyun 	if (tcon->ses->capabilities & CAP_LARGE_FILES) {
2304*4882a593Smuzhiyun 		wct = 14;
2305*4882a593Smuzhiyun 	} else {
2306*4882a593Smuzhiyun 		wct = 12;
2307*4882a593Smuzhiyun 		if ((offset >> 32) > 0) {
2308*4882a593Smuzhiyun 			/* can not handle big offset for old srv */
2309*4882a593Smuzhiyun 			return -EIO;
2310*4882a593Smuzhiyun 		}
2311*4882a593Smuzhiyun 	}
2312*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
2313*4882a593Smuzhiyun 	if (rc)
2314*4882a593Smuzhiyun 		return rc;
2315*4882a593Smuzhiyun 
2316*4882a593Smuzhiyun 	pSMB->hdr.Pid = cpu_to_le16((__u16)pid);
2317*4882a593Smuzhiyun 	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
2318*4882a593Smuzhiyun 
2319*4882a593Smuzhiyun 	/* tcon and ses pointer are checked in smb_init */
2320*4882a593Smuzhiyun 	if (tcon->ses->server == NULL)
2321*4882a593Smuzhiyun 		return -ECONNABORTED;
2322*4882a593Smuzhiyun 
2323*4882a593Smuzhiyun 	pSMB->AndXCommand = 0xFF;	/* none */
2324*4882a593Smuzhiyun 	pSMB->Fid = netfid;
2325*4882a593Smuzhiyun 	pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
2326*4882a593Smuzhiyun 	if (wct == 14)
2327*4882a593Smuzhiyun 		pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
2328*4882a593Smuzhiyun 	pSMB->Reserved = 0xFFFFFFFF;
2329*4882a593Smuzhiyun 	pSMB->WriteMode = 0;
2330*4882a593Smuzhiyun 	pSMB->Remaining = 0;
2331*4882a593Smuzhiyun 
2332*4882a593Smuzhiyun 	pSMB->DataOffset =
2333*4882a593Smuzhiyun 	    cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
2334*4882a593Smuzhiyun 
2335*4882a593Smuzhiyun 	pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
2336*4882a593Smuzhiyun 	pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
2337*4882a593Smuzhiyun 	/* header + 1 byte pad */
2338*4882a593Smuzhiyun 	smb_hdr_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 1;
2339*4882a593Smuzhiyun 	if (wct == 14)
2340*4882a593Smuzhiyun 		inc_rfc1001_len(pSMB, count + 1);
2341*4882a593Smuzhiyun 	else /* wct == 12 */
2342*4882a593Smuzhiyun 		inc_rfc1001_len(pSMB, count + 5); /* smb data starts later */
2343*4882a593Smuzhiyun 	if (wct == 14)
2344*4882a593Smuzhiyun 		pSMB->ByteCount = cpu_to_le16(count + 1);
2345*4882a593Smuzhiyun 	else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
2346*4882a593Smuzhiyun 		struct smb_com_writex_req *pSMBW =
2347*4882a593Smuzhiyun 				(struct smb_com_writex_req *)pSMB;
2348*4882a593Smuzhiyun 		pSMBW->ByteCount = cpu_to_le16(count + 5);
2349*4882a593Smuzhiyun 	}
2350*4882a593Smuzhiyun 	iov[0].iov_base = pSMB;
2351*4882a593Smuzhiyun 	if (wct == 14)
2352*4882a593Smuzhiyun 		iov[0].iov_len = smb_hdr_len + 4;
2353*4882a593Smuzhiyun 	else /* wct == 12 pad bigger by four bytes */
2354*4882a593Smuzhiyun 		iov[0].iov_len = smb_hdr_len + 8;
2355*4882a593Smuzhiyun 
2356*4882a593Smuzhiyun 	rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0,
2357*4882a593Smuzhiyun 			  &rsp_iov);
2358*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
2359*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
2360*4882a593Smuzhiyun 	if (rc) {
2361*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error Write2 = %d\n", rc);
2362*4882a593Smuzhiyun 	} else if (resp_buf_type == 0) {
2363*4882a593Smuzhiyun 		/* presumably this can not happen, but best to be safe */
2364*4882a593Smuzhiyun 		rc = -EIO;
2365*4882a593Smuzhiyun 	} else {
2366*4882a593Smuzhiyun 		WRITE_RSP *pSMBr = (WRITE_RSP *)rsp_iov.iov_base;
2367*4882a593Smuzhiyun 		*nbytes = le16_to_cpu(pSMBr->CountHigh);
2368*4882a593Smuzhiyun 		*nbytes = (*nbytes) << 16;
2369*4882a593Smuzhiyun 		*nbytes += le16_to_cpu(pSMBr->Count);
2370*4882a593Smuzhiyun 
2371*4882a593Smuzhiyun 		/*
2372*4882a593Smuzhiyun 		 * Mask off high 16 bits when bytes written as returned by the
2373*4882a593Smuzhiyun 		 * server is greater than bytes requested by the client. OS/2
2374*4882a593Smuzhiyun 		 * servers are known to set incorrect CountHigh values.
2375*4882a593Smuzhiyun 		 */
2376*4882a593Smuzhiyun 		if (*nbytes > count)
2377*4882a593Smuzhiyun 			*nbytes &= 0xFFFF;
2378*4882a593Smuzhiyun 	}
2379*4882a593Smuzhiyun 
2380*4882a593Smuzhiyun 	free_rsp_buf(resp_buf_type, rsp_iov.iov_base);
2381*4882a593Smuzhiyun 
2382*4882a593Smuzhiyun 	/* Note: On -EAGAIN error only caller can retry on handle based calls
2383*4882a593Smuzhiyun 		since file handle passed in no longer valid */
2384*4882a593Smuzhiyun 
2385*4882a593Smuzhiyun 	return rc;
2386*4882a593Smuzhiyun }
2387*4882a593Smuzhiyun 
cifs_lockv(const unsigned int xid,struct cifs_tcon * tcon,const __u16 netfid,const __u8 lock_type,const __u32 num_unlock,const __u32 num_lock,LOCKING_ANDX_RANGE * buf)2388*4882a593Smuzhiyun int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon,
2389*4882a593Smuzhiyun 	       const __u16 netfid, const __u8 lock_type, const __u32 num_unlock,
2390*4882a593Smuzhiyun 	       const __u32 num_lock, LOCKING_ANDX_RANGE *buf)
2391*4882a593Smuzhiyun {
2392*4882a593Smuzhiyun 	int rc = 0;
2393*4882a593Smuzhiyun 	LOCK_REQ *pSMB = NULL;
2394*4882a593Smuzhiyun 	struct kvec iov[2];
2395*4882a593Smuzhiyun 	struct kvec rsp_iov;
2396*4882a593Smuzhiyun 	int resp_buf_type;
2397*4882a593Smuzhiyun 	__u16 count;
2398*4882a593Smuzhiyun 
2399*4882a593Smuzhiyun 	cifs_dbg(FYI, "cifs_lockv num lock %d num unlock %d\n",
2400*4882a593Smuzhiyun 		 num_lock, num_unlock);
2401*4882a593Smuzhiyun 
2402*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
2403*4882a593Smuzhiyun 	if (rc)
2404*4882a593Smuzhiyun 		return rc;
2405*4882a593Smuzhiyun 
2406*4882a593Smuzhiyun 	pSMB->Timeout = 0;
2407*4882a593Smuzhiyun 	pSMB->NumberOfLocks = cpu_to_le16(num_lock);
2408*4882a593Smuzhiyun 	pSMB->NumberOfUnlocks = cpu_to_le16(num_unlock);
2409*4882a593Smuzhiyun 	pSMB->LockType = lock_type;
2410*4882a593Smuzhiyun 	pSMB->AndXCommand = 0xFF; /* none */
2411*4882a593Smuzhiyun 	pSMB->Fid = netfid; /* netfid stays le */
2412*4882a593Smuzhiyun 
2413*4882a593Smuzhiyun 	count = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
2414*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, count);
2415*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(count);
2416*4882a593Smuzhiyun 
2417*4882a593Smuzhiyun 	iov[0].iov_base = (char *)pSMB;
2418*4882a593Smuzhiyun 	iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4 -
2419*4882a593Smuzhiyun 			 (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
2420*4882a593Smuzhiyun 	iov[1].iov_base = (char *)buf;
2421*4882a593Smuzhiyun 	iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE);
2422*4882a593Smuzhiyun 
2423*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
2424*4882a593Smuzhiyun 	rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type,
2425*4882a593Smuzhiyun 			  CIFS_NO_RSP_BUF, &rsp_iov);
2426*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
2427*4882a593Smuzhiyun 	if (rc)
2428*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in cifs_lockv = %d\n", rc);
2429*4882a593Smuzhiyun 
2430*4882a593Smuzhiyun 	return rc;
2431*4882a593Smuzhiyun }
2432*4882a593Smuzhiyun 
2433*4882a593Smuzhiyun int
CIFSSMBLock(const unsigned int xid,struct cifs_tcon * tcon,const __u16 smb_file_id,const __u32 netpid,const __u64 len,const __u64 offset,const __u32 numUnlock,const __u32 numLock,const __u8 lockType,const bool waitFlag,const __u8 oplock_level)2434*4882a593Smuzhiyun CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon,
2435*4882a593Smuzhiyun 	    const __u16 smb_file_id, const __u32 netpid, const __u64 len,
2436*4882a593Smuzhiyun 	    const __u64 offset, const __u32 numUnlock,
2437*4882a593Smuzhiyun 	    const __u32 numLock, const __u8 lockType,
2438*4882a593Smuzhiyun 	    const bool waitFlag, const __u8 oplock_level)
2439*4882a593Smuzhiyun {
2440*4882a593Smuzhiyun 	int rc = 0;
2441*4882a593Smuzhiyun 	LOCK_REQ *pSMB = NULL;
2442*4882a593Smuzhiyun /*	LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
2443*4882a593Smuzhiyun 	int bytes_returned;
2444*4882a593Smuzhiyun 	int flags = 0;
2445*4882a593Smuzhiyun 	__u16 count;
2446*4882a593Smuzhiyun 
2447*4882a593Smuzhiyun 	cifs_dbg(FYI, "CIFSSMBLock timeout %d numLock %d\n",
2448*4882a593Smuzhiyun 		 (int)waitFlag, numLock);
2449*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
2450*4882a593Smuzhiyun 
2451*4882a593Smuzhiyun 	if (rc)
2452*4882a593Smuzhiyun 		return rc;
2453*4882a593Smuzhiyun 
2454*4882a593Smuzhiyun 	if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
2455*4882a593Smuzhiyun 		/* no response expected */
2456*4882a593Smuzhiyun 		flags = CIFS_NO_SRV_RSP | CIFS_NON_BLOCKING | CIFS_OBREAK_OP;
2457*4882a593Smuzhiyun 		pSMB->Timeout = 0;
2458*4882a593Smuzhiyun 	} else if (waitFlag) {
2459*4882a593Smuzhiyun 		flags = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
2460*4882a593Smuzhiyun 		pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
2461*4882a593Smuzhiyun 	} else {
2462*4882a593Smuzhiyun 		pSMB->Timeout = 0;
2463*4882a593Smuzhiyun 	}
2464*4882a593Smuzhiyun 
2465*4882a593Smuzhiyun 	pSMB->NumberOfLocks = cpu_to_le16(numLock);
2466*4882a593Smuzhiyun 	pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
2467*4882a593Smuzhiyun 	pSMB->LockType = lockType;
2468*4882a593Smuzhiyun 	pSMB->OplockLevel = oplock_level;
2469*4882a593Smuzhiyun 	pSMB->AndXCommand = 0xFF;	/* none */
2470*4882a593Smuzhiyun 	pSMB->Fid = smb_file_id; /* netfid stays le */
2471*4882a593Smuzhiyun 
2472*4882a593Smuzhiyun 	if ((numLock != 0) || (numUnlock != 0)) {
2473*4882a593Smuzhiyun 		pSMB->Locks[0].Pid = cpu_to_le16(netpid);
2474*4882a593Smuzhiyun 		/* BB where to store pid high? */
2475*4882a593Smuzhiyun 		pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
2476*4882a593Smuzhiyun 		pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
2477*4882a593Smuzhiyun 		pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
2478*4882a593Smuzhiyun 		pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
2479*4882a593Smuzhiyun 		count = sizeof(LOCKING_ANDX_RANGE);
2480*4882a593Smuzhiyun 	} else {
2481*4882a593Smuzhiyun 		/* oplock break */
2482*4882a593Smuzhiyun 		count = 0;
2483*4882a593Smuzhiyun 	}
2484*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, count);
2485*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(count);
2486*4882a593Smuzhiyun 
2487*4882a593Smuzhiyun 	if (waitFlag)
2488*4882a593Smuzhiyun 		rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
2489*4882a593Smuzhiyun 			(struct smb_hdr *) pSMB, &bytes_returned);
2490*4882a593Smuzhiyun 	else
2491*4882a593Smuzhiyun 		rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, flags);
2492*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
2493*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
2494*4882a593Smuzhiyun 	if (rc)
2495*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in Lock = %d\n", rc);
2496*4882a593Smuzhiyun 
2497*4882a593Smuzhiyun 	/* Note: On -EAGAIN error only caller can retry on handle based calls
2498*4882a593Smuzhiyun 	since file handle passed in no longer valid */
2499*4882a593Smuzhiyun 	return rc;
2500*4882a593Smuzhiyun }
2501*4882a593Smuzhiyun 
2502*4882a593Smuzhiyun int
CIFSSMBPosixLock(const unsigned int xid,struct cifs_tcon * tcon,const __u16 smb_file_id,const __u32 netpid,const loff_t start_offset,const __u64 len,struct file_lock * pLockData,const __u16 lock_type,const bool waitFlag)2503*4882a593Smuzhiyun CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
2504*4882a593Smuzhiyun 		const __u16 smb_file_id, const __u32 netpid,
2505*4882a593Smuzhiyun 		const loff_t start_offset, const __u64 len,
2506*4882a593Smuzhiyun 		struct file_lock *pLockData, const __u16 lock_type,
2507*4882a593Smuzhiyun 		const bool waitFlag)
2508*4882a593Smuzhiyun {
2509*4882a593Smuzhiyun 	struct smb_com_transaction2_sfi_req *pSMB  = NULL;
2510*4882a593Smuzhiyun 	struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
2511*4882a593Smuzhiyun 	struct cifs_posix_lock *parm_data;
2512*4882a593Smuzhiyun 	int rc = 0;
2513*4882a593Smuzhiyun 	int timeout = 0;
2514*4882a593Smuzhiyun 	int bytes_returned = 0;
2515*4882a593Smuzhiyun 	int resp_buf_type = 0;
2516*4882a593Smuzhiyun 	__u16 params, param_offset, offset, byte_count, count;
2517*4882a593Smuzhiyun 	struct kvec iov[1];
2518*4882a593Smuzhiyun 	struct kvec rsp_iov;
2519*4882a593Smuzhiyun 
2520*4882a593Smuzhiyun 	cifs_dbg(FYI, "Posix Lock\n");
2521*4882a593Smuzhiyun 
2522*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
2523*4882a593Smuzhiyun 
2524*4882a593Smuzhiyun 	if (rc)
2525*4882a593Smuzhiyun 		return rc;
2526*4882a593Smuzhiyun 
2527*4882a593Smuzhiyun 	pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
2528*4882a593Smuzhiyun 
2529*4882a593Smuzhiyun 	params = 6;
2530*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
2531*4882a593Smuzhiyun 	pSMB->Reserved = 0;
2532*4882a593Smuzhiyun 	pSMB->Flags = 0;
2533*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
2534*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2535*4882a593Smuzhiyun 	offset = param_offset + params;
2536*4882a593Smuzhiyun 
2537*4882a593Smuzhiyun 	count = sizeof(struct cifs_posix_lock);
2538*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
2539*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
2540*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
2541*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
2542*4882a593Smuzhiyun 	if (pLockData)
2543*4882a593Smuzhiyun 		pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
2544*4882a593Smuzhiyun 	else
2545*4882a593Smuzhiyun 		pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2546*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + count;
2547*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(count);
2548*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
2549*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
2550*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
2551*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
2552*4882a593Smuzhiyun 	parm_data = (struct cifs_posix_lock *)
2553*4882a593Smuzhiyun 			(((char *) &pSMB->hdr.Protocol) + offset);
2554*4882a593Smuzhiyun 
2555*4882a593Smuzhiyun 	parm_data->lock_type = cpu_to_le16(lock_type);
2556*4882a593Smuzhiyun 	if (waitFlag) {
2557*4882a593Smuzhiyun 		timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
2558*4882a593Smuzhiyun 		parm_data->lock_flags = cpu_to_le16(1);
2559*4882a593Smuzhiyun 		pSMB->Timeout = cpu_to_le32(-1);
2560*4882a593Smuzhiyun 	} else
2561*4882a593Smuzhiyun 		pSMB->Timeout = 0;
2562*4882a593Smuzhiyun 
2563*4882a593Smuzhiyun 	parm_data->pid = cpu_to_le32(netpid);
2564*4882a593Smuzhiyun 	parm_data->start = cpu_to_le64(start_offset);
2565*4882a593Smuzhiyun 	parm_data->length = cpu_to_le64(len);  /* normalize negative numbers */
2566*4882a593Smuzhiyun 
2567*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
2568*4882a593Smuzhiyun 	pSMB->Fid = smb_file_id;
2569*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
2570*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
2571*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
2572*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
2573*4882a593Smuzhiyun 	if (waitFlag) {
2574*4882a593Smuzhiyun 		rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
2575*4882a593Smuzhiyun 			(struct smb_hdr *) pSMBr, &bytes_returned);
2576*4882a593Smuzhiyun 	} else {
2577*4882a593Smuzhiyun 		iov[0].iov_base = (char *)pSMB;
2578*4882a593Smuzhiyun 		iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;
2579*4882a593Smuzhiyun 		rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
2580*4882a593Smuzhiyun 				&resp_buf_type, timeout, &rsp_iov);
2581*4882a593Smuzhiyun 		pSMBr = (struct smb_com_transaction2_sfi_rsp *)rsp_iov.iov_base;
2582*4882a593Smuzhiyun 	}
2583*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
2584*4882a593Smuzhiyun 
2585*4882a593Smuzhiyun 	if (rc) {
2586*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in Posix Lock = %d\n", rc);
2587*4882a593Smuzhiyun 	} else if (pLockData) {
2588*4882a593Smuzhiyun 		/* lock structure can be returned on get */
2589*4882a593Smuzhiyun 		__u16 data_offset;
2590*4882a593Smuzhiyun 		__u16 data_count;
2591*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2592*4882a593Smuzhiyun 
2593*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < sizeof(*parm_data)) {
2594*4882a593Smuzhiyun 			rc = -EIO;      /* bad smb */
2595*4882a593Smuzhiyun 			goto plk_err_exit;
2596*4882a593Smuzhiyun 		}
2597*4882a593Smuzhiyun 		data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2598*4882a593Smuzhiyun 		data_count  = le16_to_cpu(pSMBr->t2.DataCount);
2599*4882a593Smuzhiyun 		if (data_count < sizeof(struct cifs_posix_lock)) {
2600*4882a593Smuzhiyun 			rc = -EIO;
2601*4882a593Smuzhiyun 			goto plk_err_exit;
2602*4882a593Smuzhiyun 		}
2603*4882a593Smuzhiyun 		parm_data = (struct cifs_posix_lock *)
2604*4882a593Smuzhiyun 			((char *)&pSMBr->hdr.Protocol + data_offset);
2605*4882a593Smuzhiyun 		if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK))
2606*4882a593Smuzhiyun 			pLockData->fl_type = F_UNLCK;
2607*4882a593Smuzhiyun 		else {
2608*4882a593Smuzhiyun 			if (parm_data->lock_type ==
2609*4882a593Smuzhiyun 					cpu_to_le16(CIFS_RDLCK))
2610*4882a593Smuzhiyun 				pLockData->fl_type = F_RDLCK;
2611*4882a593Smuzhiyun 			else if (parm_data->lock_type ==
2612*4882a593Smuzhiyun 					cpu_to_le16(CIFS_WRLCK))
2613*4882a593Smuzhiyun 				pLockData->fl_type = F_WRLCK;
2614*4882a593Smuzhiyun 
2615*4882a593Smuzhiyun 			pLockData->fl_start = le64_to_cpu(parm_data->start);
2616*4882a593Smuzhiyun 			pLockData->fl_end = pLockData->fl_start +
2617*4882a593Smuzhiyun 					le64_to_cpu(parm_data->length) - 1;
2618*4882a593Smuzhiyun 			pLockData->fl_pid = -le32_to_cpu(parm_data->pid);
2619*4882a593Smuzhiyun 		}
2620*4882a593Smuzhiyun 	}
2621*4882a593Smuzhiyun 
2622*4882a593Smuzhiyun plk_err_exit:
2623*4882a593Smuzhiyun 	free_rsp_buf(resp_buf_type, rsp_iov.iov_base);
2624*4882a593Smuzhiyun 
2625*4882a593Smuzhiyun 	/* Note: On -EAGAIN error only caller can retry on handle based calls
2626*4882a593Smuzhiyun 	   since file handle passed in no longer valid */
2627*4882a593Smuzhiyun 
2628*4882a593Smuzhiyun 	return rc;
2629*4882a593Smuzhiyun }
2630*4882a593Smuzhiyun 
2631*4882a593Smuzhiyun 
2632*4882a593Smuzhiyun int
CIFSSMBClose(const unsigned int xid,struct cifs_tcon * tcon,int smb_file_id)2633*4882a593Smuzhiyun CIFSSMBClose(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)
2634*4882a593Smuzhiyun {
2635*4882a593Smuzhiyun 	int rc = 0;
2636*4882a593Smuzhiyun 	CLOSE_REQ *pSMB = NULL;
2637*4882a593Smuzhiyun 	cifs_dbg(FYI, "In CIFSSMBClose\n");
2638*4882a593Smuzhiyun 
2639*4882a593Smuzhiyun /* do not retry on dead session on close */
2640*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
2641*4882a593Smuzhiyun 	if (rc == -EAGAIN)
2642*4882a593Smuzhiyun 		return 0;
2643*4882a593Smuzhiyun 	if (rc)
2644*4882a593Smuzhiyun 		return rc;
2645*4882a593Smuzhiyun 
2646*4882a593Smuzhiyun 	pSMB->FileID = (__u16) smb_file_id;
2647*4882a593Smuzhiyun 	pSMB->LastWriteTime = 0xFFFFFFFF;
2648*4882a593Smuzhiyun 	pSMB->ByteCount = 0;
2649*4882a593Smuzhiyun 	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
2650*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
2651*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_closes);
2652*4882a593Smuzhiyun 	if (rc) {
2653*4882a593Smuzhiyun 		if (rc != -EINTR) {
2654*4882a593Smuzhiyun 			/* EINTR is expected when user ctl-c to kill app */
2655*4882a593Smuzhiyun 			cifs_dbg(VFS, "Send error in Close = %d\n", rc);
2656*4882a593Smuzhiyun 		}
2657*4882a593Smuzhiyun 	}
2658*4882a593Smuzhiyun 
2659*4882a593Smuzhiyun 	/* Since session is dead, file will be closed on server already */
2660*4882a593Smuzhiyun 	if (rc == -EAGAIN)
2661*4882a593Smuzhiyun 		rc = 0;
2662*4882a593Smuzhiyun 
2663*4882a593Smuzhiyun 	return rc;
2664*4882a593Smuzhiyun }
2665*4882a593Smuzhiyun 
2666*4882a593Smuzhiyun int
CIFSSMBFlush(const unsigned int xid,struct cifs_tcon * tcon,int smb_file_id)2667*4882a593Smuzhiyun CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)
2668*4882a593Smuzhiyun {
2669*4882a593Smuzhiyun 	int rc = 0;
2670*4882a593Smuzhiyun 	FLUSH_REQ *pSMB = NULL;
2671*4882a593Smuzhiyun 	cifs_dbg(FYI, "In CIFSSMBFlush\n");
2672*4882a593Smuzhiyun 
2673*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB);
2674*4882a593Smuzhiyun 	if (rc)
2675*4882a593Smuzhiyun 		return rc;
2676*4882a593Smuzhiyun 
2677*4882a593Smuzhiyun 	pSMB->FileID = (__u16) smb_file_id;
2678*4882a593Smuzhiyun 	pSMB->ByteCount = 0;
2679*4882a593Smuzhiyun 	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
2680*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
2681*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_flushes);
2682*4882a593Smuzhiyun 	if (rc)
2683*4882a593Smuzhiyun 		cifs_dbg(VFS, "Send error in Flush = %d\n", rc);
2684*4882a593Smuzhiyun 
2685*4882a593Smuzhiyun 	return rc;
2686*4882a593Smuzhiyun }
2687*4882a593Smuzhiyun 
2688*4882a593Smuzhiyun int
CIFSSMBRename(const unsigned int xid,struct cifs_tcon * tcon,const char * from_name,const char * to_name,struct cifs_sb_info * cifs_sb)2689*4882a593Smuzhiyun CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
2690*4882a593Smuzhiyun 	      const char *from_name, const char *to_name,
2691*4882a593Smuzhiyun 	      struct cifs_sb_info *cifs_sb)
2692*4882a593Smuzhiyun {
2693*4882a593Smuzhiyun 	int rc = 0;
2694*4882a593Smuzhiyun 	RENAME_REQ *pSMB = NULL;
2695*4882a593Smuzhiyun 	RENAME_RSP *pSMBr = NULL;
2696*4882a593Smuzhiyun 	int bytes_returned;
2697*4882a593Smuzhiyun 	int name_len, name_len2;
2698*4882a593Smuzhiyun 	__u16 count;
2699*4882a593Smuzhiyun 	int remap = cifs_remap(cifs_sb);
2700*4882a593Smuzhiyun 
2701*4882a593Smuzhiyun 	cifs_dbg(FYI, "In CIFSSMBRename\n");
2702*4882a593Smuzhiyun renameRetry:
2703*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
2704*4882a593Smuzhiyun 		      (void **) &pSMBr);
2705*4882a593Smuzhiyun 	if (rc)
2706*4882a593Smuzhiyun 		return rc;
2707*4882a593Smuzhiyun 
2708*4882a593Smuzhiyun 	pSMB->BufferFormat = 0x04;
2709*4882a593Smuzhiyun 	pSMB->SearchAttributes =
2710*4882a593Smuzhiyun 	    cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2711*4882a593Smuzhiyun 			ATTR_DIRECTORY);
2712*4882a593Smuzhiyun 
2713*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2714*4882a593Smuzhiyun 		name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName,
2715*4882a593Smuzhiyun 					      from_name, PATH_MAX,
2716*4882a593Smuzhiyun 					      cifs_sb->local_nls, remap);
2717*4882a593Smuzhiyun 		name_len++;	/* trailing null */
2718*4882a593Smuzhiyun 		name_len *= 2;
2719*4882a593Smuzhiyun 		pSMB->OldFileName[name_len] = 0x04;	/* pad */
2720*4882a593Smuzhiyun 	/* protocol requires ASCII signature byte on Unicode string */
2721*4882a593Smuzhiyun 		pSMB->OldFileName[name_len + 1] = 0x00;
2722*4882a593Smuzhiyun 		name_len2 =
2723*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
2724*4882a593Smuzhiyun 				       to_name, PATH_MAX, cifs_sb->local_nls,
2725*4882a593Smuzhiyun 				       remap);
2726*4882a593Smuzhiyun 		name_len2 += 1 /* trailing null */  + 1 /* Signature word */ ;
2727*4882a593Smuzhiyun 		name_len2 *= 2;	/* convert to bytes */
2728*4882a593Smuzhiyun 	} else {
2729*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->OldFileName, from_name);
2730*4882a593Smuzhiyun 		name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, to_name);
2731*4882a593Smuzhiyun 		pSMB->OldFileName[name_len] = 0x04;  /* 2nd buffer format */
2732*4882a593Smuzhiyun 		name_len2++;	/* signature byte */
2733*4882a593Smuzhiyun 	}
2734*4882a593Smuzhiyun 
2735*4882a593Smuzhiyun 	count = 1 /* 1st signature byte */  + name_len + name_len2;
2736*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, count);
2737*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(count);
2738*4882a593Smuzhiyun 
2739*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2740*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2741*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_renames);
2742*4882a593Smuzhiyun 	if (rc)
2743*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in rename = %d\n", rc);
2744*4882a593Smuzhiyun 
2745*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
2746*4882a593Smuzhiyun 
2747*4882a593Smuzhiyun 	if (rc == -EAGAIN)
2748*4882a593Smuzhiyun 		goto renameRetry;
2749*4882a593Smuzhiyun 
2750*4882a593Smuzhiyun 	return rc;
2751*4882a593Smuzhiyun }
2752*4882a593Smuzhiyun 
CIFSSMBRenameOpenFile(const unsigned int xid,struct cifs_tcon * pTcon,int netfid,const char * target_name,const struct nls_table * nls_codepage,int remap)2753*4882a593Smuzhiyun int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *pTcon,
2754*4882a593Smuzhiyun 		int netfid, const char *target_name,
2755*4882a593Smuzhiyun 		const struct nls_table *nls_codepage, int remap)
2756*4882a593Smuzhiyun {
2757*4882a593Smuzhiyun 	struct smb_com_transaction2_sfi_req *pSMB  = NULL;
2758*4882a593Smuzhiyun 	struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
2759*4882a593Smuzhiyun 	struct set_file_rename *rename_info;
2760*4882a593Smuzhiyun 	char *data_offset;
2761*4882a593Smuzhiyun 	char dummy_string[30];
2762*4882a593Smuzhiyun 	int rc = 0;
2763*4882a593Smuzhiyun 	int bytes_returned = 0;
2764*4882a593Smuzhiyun 	int len_of_str;
2765*4882a593Smuzhiyun 	__u16 params, param_offset, offset, count, byte_count;
2766*4882a593Smuzhiyun 
2767*4882a593Smuzhiyun 	cifs_dbg(FYI, "Rename to File by handle\n");
2768*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
2769*4882a593Smuzhiyun 			(void **) &pSMBr);
2770*4882a593Smuzhiyun 	if (rc)
2771*4882a593Smuzhiyun 		return rc;
2772*4882a593Smuzhiyun 
2773*4882a593Smuzhiyun 	params = 6;
2774*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
2775*4882a593Smuzhiyun 	pSMB->Reserved = 0;
2776*4882a593Smuzhiyun 	pSMB->Flags = 0;
2777*4882a593Smuzhiyun 	pSMB->Timeout = 0;
2778*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
2779*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2780*4882a593Smuzhiyun 	offset = param_offset + params;
2781*4882a593Smuzhiyun 
2782*4882a593Smuzhiyun 	data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2783*4882a593Smuzhiyun 	rename_info = (struct set_file_rename *) data_offset;
2784*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
2785*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
2786*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
2787*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
2788*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2789*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params;
2790*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
2791*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
2792*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
2793*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
2794*4882a593Smuzhiyun 	/* construct random name ".cifs_tmp<inodenum><mid>" */
2795*4882a593Smuzhiyun 	rename_info->overwrite = cpu_to_le32(1);
2796*4882a593Smuzhiyun 	rename_info->root_fid  = 0;
2797*4882a593Smuzhiyun 	/* unicode only call */
2798*4882a593Smuzhiyun 	if (target_name == NULL) {
2799*4882a593Smuzhiyun 		sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2800*4882a593Smuzhiyun 		len_of_str =
2801*4882a593Smuzhiyun 			cifsConvertToUTF16((__le16 *)rename_info->target_name,
2802*4882a593Smuzhiyun 					dummy_string, 24, nls_codepage, remap);
2803*4882a593Smuzhiyun 	} else {
2804*4882a593Smuzhiyun 		len_of_str =
2805*4882a593Smuzhiyun 			cifsConvertToUTF16((__le16 *)rename_info->target_name,
2806*4882a593Smuzhiyun 					target_name, PATH_MAX, nls_codepage,
2807*4882a593Smuzhiyun 					remap);
2808*4882a593Smuzhiyun 	}
2809*4882a593Smuzhiyun 	rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
2810*4882a593Smuzhiyun 	count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
2811*4882a593Smuzhiyun 	byte_count += count;
2812*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(count);
2813*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
2814*4882a593Smuzhiyun 	pSMB->Fid = netfid;
2815*4882a593Smuzhiyun 	pSMB->InformationLevel =
2816*4882a593Smuzhiyun 		cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2817*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
2818*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
2819*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
2820*4882a593Smuzhiyun 	rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
2821*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2822*4882a593Smuzhiyun 	cifs_stats_inc(&pTcon->stats.cifs_stats.num_t2renames);
2823*4882a593Smuzhiyun 	if (rc)
2824*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in Rename (by file handle) = %d\n",
2825*4882a593Smuzhiyun 			 rc);
2826*4882a593Smuzhiyun 
2827*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
2828*4882a593Smuzhiyun 
2829*4882a593Smuzhiyun 	/* Note: On -EAGAIN error only caller can retry on handle based calls
2830*4882a593Smuzhiyun 		since file handle passed in no longer valid */
2831*4882a593Smuzhiyun 
2832*4882a593Smuzhiyun 	return rc;
2833*4882a593Smuzhiyun }
2834*4882a593Smuzhiyun 
2835*4882a593Smuzhiyun int
CIFSSMBCopy(const unsigned int xid,struct cifs_tcon * tcon,const char * fromName,const __u16 target_tid,const char * toName,const int flags,const struct nls_table * nls_codepage,int remap)2836*4882a593Smuzhiyun CIFSSMBCopy(const unsigned int xid, struct cifs_tcon *tcon,
2837*4882a593Smuzhiyun 	    const char *fromName, const __u16 target_tid, const char *toName,
2838*4882a593Smuzhiyun 	    const int flags, const struct nls_table *nls_codepage, int remap)
2839*4882a593Smuzhiyun {
2840*4882a593Smuzhiyun 	int rc = 0;
2841*4882a593Smuzhiyun 	COPY_REQ *pSMB = NULL;
2842*4882a593Smuzhiyun 	COPY_RSP *pSMBr = NULL;
2843*4882a593Smuzhiyun 	int bytes_returned;
2844*4882a593Smuzhiyun 	int name_len, name_len2;
2845*4882a593Smuzhiyun 	__u16 count;
2846*4882a593Smuzhiyun 
2847*4882a593Smuzhiyun 	cifs_dbg(FYI, "In CIFSSMBCopy\n");
2848*4882a593Smuzhiyun copyRetry:
2849*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2850*4882a593Smuzhiyun 			(void **) &pSMBr);
2851*4882a593Smuzhiyun 	if (rc)
2852*4882a593Smuzhiyun 		return rc;
2853*4882a593Smuzhiyun 
2854*4882a593Smuzhiyun 	pSMB->BufferFormat = 0x04;
2855*4882a593Smuzhiyun 	pSMB->Tid2 = target_tid;
2856*4882a593Smuzhiyun 
2857*4882a593Smuzhiyun 	pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2858*4882a593Smuzhiyun 
2859*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2860*4882a593Smuzhiyun 		name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName,
2861*4882a593Smuzhiyun 					      fromName, PATH_MAX, nls_codepage,
2862*4882a593Smuzhiyun 					      remap);
2863*4882a593Smuzhiyun 		name_len++;     /* trailing null */
2864*4882a593Smuzhiyun 		name_len *= 2;
2865*4882a593Smuzhiyun 		pSMB->OldFileName[name_len] = 0x04;     /* pad */
2866*4882a593Smuzhiyun 		/* protocol requires ASCII signature byte on Unicode string */
2867*4882a593Smuzhiyun 		pSMB->OldFileName[name_len + 1] = 0x00;
2868*4882a593Smuzhiyun 		name_len2 =
2869*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
2870*4882a593Smuzhiyun 				       toName, PATH_MAX, nls_codepage, remap);
2871*4882a593Smuzhiyun 		name_len2 += 1 /* trailing null */  + 1 /* Signature word */ ;
2872*4882a593Smuzhiyun 		name_len2 *= 2; /* convert to bytes */
2873*4882a593Smuzhiyun 	} else {
2874*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->OldFileName, fromName);
2875*4882a593Smuzhiyun 		pSMB->OldFileName[name_len] = 0x04;  /* 2nd buffer format */
2876*4882a593Smuzhiyun 		name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, toName);
2877*4882a593Smuzhiyun 		name_len2++;    /* signature byte */
2878*4882a593Smuzhiyun 	}
2879*4882a593Smuzhiyun 
2880*4882a593Smuzhiyun 	count = 1 /* 1st signature byte */  + name_len + name_len2;
2881*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, count);
2882*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(count);
2883*4882a593Smuzhiyun 
2884*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2885*4882a593Smuzhiyun 		(struct smb_hdr *) pSMBr, &bytes_returned, 0);
2886*4882a593Smuzhiyun 	if (rc) {
2887*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in copy = %d with %d files copied\n",
2888*4882a593Smuzhiyun 			 rc, le16_to_cpu(pSMBr->CopyCount));
2889*4882a593Smuzhiyun 	}
2890*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
2891*4882a593Smuzhiyun 
2892*4882a593Smuzhiyun 	if (rc == -EAGAIN)
2893*4882a593Smuzhiyun 		goto copyRetry;
2894*4882a593Smuzhiyun 
2895*4882a593Smuzhiyun 	return rc;
2896*4882a593Smuzhiyun }
2897*4882a593Smuzhiyun 
2898*4882a593Smuzhiyun int
CIFSUnixCreateSymLink(const unsigned int xid,struct cifs_tcon * tcon,const char * fromName,const char * toName,const struct nls_table * nls_codepage,int remap)2899*4882a593Smuzhiyun CIFSUnixCreateSymLink(const unsigned int xid, struct cifs_tcon *tcon,
2900*4882a593Smuzhiyun 		      const char *fromName, const char *toName,
2901*4882a593Smuzhiyun 		      const struct nls_table *nls_codepage, int remap)
2902*4882a593Smuzhiyun {
2903*4882a593Smuzhiyun 	TRANSACTION2_SPI_REQ *pSMB = NULL;
2904*4882a593Smuzhiyun 	TRANSACTION2_SPI_RSP *pSMBr = NULL;
2905*4882a593Smuzhiyun 	char *data_offset;
2906*4882a593Smuzhiyun 	int name_len;
2907*4882a593Smuzhiyun 	int name_len_target;
2908*4882a593Smuzhiyun 	int rc = 0;
2909*4882a593Smuzhiyun 	int bytes_returned = 0;
2910*4882a593Smuzhiyun 	__u16 params, param_offset, offset, byte_count;
2911*4882a593Smuzhiyun 
2912*4882a593Smuzhiyun 	cifs_dbg(FYI, "In Symlink Unix style\n");
2913*4882a593Smuzhiyun createSymLinkRetry:
2914*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2915*4882a593Smuzhiyun 		      (void **) &pSMBr);
2916*4882a593Smuzhiyun 	if (rc)
2917*4882a593Smuzhiyun 		return rc;
2918*4882a593Smuzhiyun 
2919*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2920*4882a593Smuzhiyun 		name_len =
2921*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->FileName, fromName,
2922*4882a593Smuzhiyun 				/* find define for this maxpathcomponent */
2923*4882a593Smuzhiyun 					PATH_MAX, nls_codepage, remap);
2924*4882a593Smuzhiyun 		name_len++;	/* trailing null */
2925*4882a593Smuzhiyun 		name_len *= 2;
2926*4882a593Smuzhiyun 
2927*4882a593Smuzhiyun 	} else {
2928*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, fromName);
2929*4882a593Smuzhiyun 	}
2930*4882a593Smuzhiyun 	params = 6 + name_len;
2931*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
2932*4882a593Smuzhiyun 	pSMB->Reserved = 0;
2933*4882a593Smuzhiyun 	pSMB->Flags = 0;
2934*4882a593Smuzhiyun 	pSMB->Timeout = 0;
2935*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
2936*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_spi_req,
2937*4882a593Smuzhiyun 				InformationLevel) - 4;
2938*4882a593Smuzhiyun 	offset = param_offset + params;
2939*4882a593Smuzhiyun 
2940*4882a593Smuzhiyun 	data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2941*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2942*4882a593Smuzhiyun 		name_len_target =
2943*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) data_offset, toName,
2944*4882a593Smuzhiyun 				/* find define for this maxpathcomponent */
2945*4882a593Smuzhiyun 					PATH_MAX, nls_codepage, remap);
2946*4882a593Smuzhiyun 		name_len_target++;	/* trailing null */
2947*4882a593Smuzhiyun 		name_len_target *= 2;
2948*4882a593Smuzhiyun 	} else {
2949*4882a593Smuzhiyun 		name_len_target = copy_path_name(data_offset, toName);
2950*4882a593Smuzhiyun 	}
2951*4882a593Smuzhiyun 
2952*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
2953*4882a593Smuzhiyun 	/* BB find exact max on data count below from sess */
2954*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
2955*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
2956*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
2957*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2958*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + name_len_target;
2959*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(name_len_target);
2960*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
2961*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
2962*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
2963*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
2964*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
2965*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2966*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
2967*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
2968*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
2969*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2970*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2971*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_symlinks);
2972*4882a593Smuzhiyun 	if (rc)
2973*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in SetPathInfo create symlink = %d\n",
2974*4882a593Smuzhiyun 			 rc);
2975*4882a593Smuzhiyun 
2976*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
2977*4882a593Smuzhiyun 
2978*4882a593Smuzhiyun 	if (rc == -EAGAIN)
2979*4882a593Smuzhiyun 		goto createSymLinkRetry;
2980*4882a593Smuzhiyun 
2981*4882a593Smuzhiyun 	return rc;
2982*4882a593Smuzhiyun }
2983*4882a593Smuzhiyun 
2984*4882a593Smuzhiyun int
CIFSUnixCreateHardLink(const unsigned int xid,struct cifs_tcon * tcon,const char * fromName,const char * toName,const struct nls_table * nls_codepage,int remap)2985*4882a593Smuzhiyun CIFSUnixCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
2986*4882a593Smuzhiyun 		       const char *fromName, const char *toName,
2987*4882a593Smuzhiyun 		       const struct nls_table *nls_codepage, int remap)
2988*4882a593Smuzhiyun {
2989*4882a593Smuzhiyun 	TRANSACTION2_SPI_REQ *pSMB = NULL;
2990*4882a593Smuzhiyun 	TRANSACTION2_SPI_RSP *pSMBr = NULL;
2991*4882a593Smuzhiyun 	char *data_offset;
2992*4882a593Smuzhiyun 	int name_len;
2993*4882a593Smuzhiyun 	int name_len_target;
2994*4882a593Smuzhiyun 	int rc = 0;
2995*4882a593Smuzhiyun 	int bytes_returned = 0;
2996*4882a593Smuzhiyun 	__u16 params, param_offset, offset, byte_count;
2997*4882a593Smuzhiyun 
2998*4882a593Smuzhiyun 	cifs_dbg(FYI, "In Create Hard link Unix style\n");
2999*4882a593Smuzhiyun createHardLinkRetry:
3000*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3001*4882a593Smuzhiyun 		      (void **) &pSMBr);
3002*4882a593Smuzhiyun 	if (rc)
3003*4882a593Smuzhiyun 		return rc;
3004*4882a593Smuzhiyun 
3005*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3006*4882a593Smuzhiyun 		name_len = cifsConvertToUTF16((__le16 *) pSMB->FileName, toName,
3007*4882a593Smuzhiyun 					      PATH_MAX, nls_codepage, remap);
3008*4882a593Smuzhiyun 		name_len++;	/* trailing null */
3009*4882a593Smuzhiyun 		name_len *= 2;
3010*4882a593Smuzhiyun 
3011*4882a593Smuzhiyun 	} else {
3012*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, toName);
3013*4882a593Smuzhiyun 	}
3014*4882a593Smuzhiyun 	params = 6 + name_len;
3015*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
3016*4882a593Smuzhiyun 	pSMB->Reserved = 0;
3017*4882a593Smuzhiyun 	pSMB->Flags = 0;
3018*4882a593Smuzhiyun 	pSMB->Timeout = 0;
3019*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
3020*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_spi_req,
3021*4882a593Smuzhiyun 				InformationLevel) - 4;
3022*4882a593Smuzhiyun 	offset = param_offset + params;
3023*4882a593Smuzhiyun 
3024*4882a593Smuzhiyun 	data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
3025*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3026*4882a593Smuzhiyun 		name_len_target =
3027*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) data_offset, fromName,
3028*4882a593Smuzhiyun 				       PATH_MAX, nls_codepage, remap);
3029*4882a593Smuzhiyun 		name_len_target++;	/* trailing null */
3030*4882a593Smuzhiyun 		name_len_target *= 2;
3031*4882a593Smuzhiyun 	} else {
3032*4882a593Smuzhiyun 		name_len_target = copy_path_name(data_offset, fromName);
3033*4882a593Smuzhiyun 	}
3034*4882a593Smuzhiyun 
3035*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
3036*4882a593Smuzhiyun 	/* BB find exact max on data count below from sess*/
3037*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
3038*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
3039*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
3040*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
3041*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + name_len_target;
3042*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
3043*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
3044*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(name_len_target);
3045*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
3046*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
3047*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
3048*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
3049*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
3050*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
3051*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
3052*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3053*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3054*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks);
3055*4882a593Smuzhiyun 	if (rc)
3056*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in SetPathInfo (hard link) = %d\n",
3057*4882a593Smuzhiyun 			 rc);
3058*4882a593Smuzhiyun 
3059*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
3060*4882a593Smuzhiyun 	if (rc == -EAGAIN)
3061*4882a593Smuzhiyun 		goto createHardLinkRetry;
3062*4882a593Smuzhiyun 
3063*4882a593Smuzhiyun 	return rc;
3064*4882a593Smuzhiyun }
3065*4882a593Smuzhiyun 
3066*4882a593Smuzhiyun int
CIFSCreateHardLink(const unsigned int xid,struct cifs_tcon * tcon,const char * from_name,const char * to_name,struct cifs_sb_info * cifs_sb)3067*4882a593Smuzhiyun CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
3068*4882a593Smuzhiyun 		   const char *from_name, const char *to_name,
3069*4882a593Smuzhiyun 		   struct cifs_sb_info *cifs_sb)
3070*4882a593Smuzhiyun {
3071*4882a593Smuzhiyun 	int rc = 0;
3072*4882a593Smuzhiyun 	NT_RENAME_REQ *pSMB = NULL;
3073*4882a593Smuzhiyun 	RENAME_RSP *pSMBr = NULL;
3074*4882a593Smuzhiyun 	int bytes_returned;
3075*4882a593Smuzhiyun 	int name_len, name_len2;
3076*4882a593Smuzhiyun 	__u16 count;
3077*4882a593Smuzhiyun 	int remap = cifs_remap(cifs_sb);
3078*4882a593Smuzhiyun 
3079*4882a593Smuzhiyun 	cifs_dbg(FYI, "In CIFSCreateHardLink\n");
3080*4882a593Smuzhiyun winCreateHardLinkRetry:
3081*4882a593Smuzhiyun 
3082*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
3083*4882a593Smuzhiyun 		      (void **) &pSMBr);
3084*4882a593Smuzhiyun 	if (rc)
3085*4882a593Smuzhiyun 		return rc;
3086*4882a593Smuzhiyun 
3087*4882a593Smuzhiyun 	pSMB->SearchAttributes =
3088*4882a593Smuzhiyun 	    cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3089*4882a593Smuzhiyun 			ATTR_DIRECTORY);
3090*4882a593Smuzhiyun 	pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
3091*4882a593Smuzhiyun 	pSMB->ClusterCount = 0;
3092*4882a593Smuzhiyun 
3093*4882a593Smuzhiyun 	pSMB->BufferFormat = 0x04;
3094*4882a593Smuzhiyun 
3095*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3096*4882a593Smuzhiyun 		name_len =
3097*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->OldFileName, from_name,
3098*4882a593Smuzhiyun 				       PATH_MAX, cifs_sb->local_nls, remap);
3099*4882a593Smuzhiyun 		name_len++;	/* trailing null */
3100*4882a593Smuzhiyun 		name_len *= 2;
3101*4882a593Smuzhiyun 
3102*4882a593Smuzhiyun 		/* protocol specifies ASCII buffer format (0x04) for unicode */
3103*4882a593Smuzhiyun 		pSMB->OldFileName[name_len] = 0x04;
3104*4882a593Smuzhiyun 		pSMB->OldFileName[name_len + 1] = 0x00; /* pad */
3105*4882a593Smuzhiyun 		name_len2 =
3106*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
3107*4882a593Smuzhiyun 				       to_name, PATH_MAX, cifs_sb->local_nls,
3108*4882a593Smuzhiyun 				       remap);
3109*4882a593Smuzhiyun 		name_len2 += 1 /* trailing null */  + 1 /* Signature word */ ;
3110*4882a593Smuzhiyun 		name_len2 *= 2;	/* convert to bytes */
3111*4882a593Smuzhiyun 	} else {
3112*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->OldFileName, from_name);
3113*4882a593Smuzhiyun 		pSMB->OldFileName[name_len] = 0x04;	/* 2nd buffer format */
3114*4882a593Smuzhiyun 		name_len2 = copy_path_name(pSMB->OldFileName+name_len+1, to_name);
3115*4882a593Smuzhiyun 		name_len2++;	/* signature byte */
3116*4882a593Smuzhiyun 	}
3117*4882a593Smuzhiyun 
3118*4882a593Smuzhiyun 	count = 1 /* string type byte */  + name_len + name_len2;
3119*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, count);
3120*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(count);
3121*4882a593Smuzhiyun 
3122*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3123*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3124*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_hardlinks);
3125*4882a593Smuzhiyun 	if (rc)
3126*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in hard link (NT rename) = %d\n", rc);
3127*4882a593Smuzhiyun 
3128*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
3129*4882a593Smuzhiyun 	if (rc == -EAGAIN)
3130*4882a593Smuzhiyun 		goto winCreateHardLinkRetry;
3131*4882a593Smuzhiyun 
3132*4882a593Smuzhiyun 	return rc;
3133*4882a593Smuzhiyun }
3134*4882a593Smuzhiyun 
3135*4882a593Smuzhiyun int
CIFSSMBUnixQuerySymLink(const unsigned int xid,struct cifs_tcon * tcon,const unsigned char * searchName,char ** symlinkinfo,const struct nls_table * nls_codepage,int remap)3136*4882a593Smuzhiyun CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
3137*4882a593Smuzhiyun 			const unsigned char *searchName, char **symlinkinfo,
3138*4882a593Smuzhiyun 			const struct nls_table *nls_codepage, int remap)
3139*4882a593Smuzhiyun {
3140*4882a593Smuzhiyun /* SMB_QUERY_FILE_UNIX_LINK */
3141*4882a593Smuzhiyun 	TRANSACTION2_QPI_REQ *pSMB = NULL;
3142*4882a593Smuzhiyun 	TRANSACTION2_QPI_RSP *pSMBr = NULL;
3143*4882a593Smuzhiyun 	int rc = 0;
3144*4882a593Smuzhiyun 	int bytes_returned;
3145*4882a593Smuzhiyun 	int name_len;
3146*4882a593Smuzhiyun 	__u16 params, byte_count;
3147*4882a593Smuzhiyun 	char *data_start;
3148*4882a593Smuzhiyun 
3149*4882a593Smuzhiyun 	cifs_dbg(FYI, "In QPathSymLinkInfo (Unix) for path %s\n", searchName);
3150*4882a593Smuzhiyun 
3151*4882a593Smuzhiyun querySymLinkRetry:
3152*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3153*4882a593Smuzhiyun 		      (void **) &pSMBr);
3154*4882a593Smuzhiyun 	if (rc)
3155*4882a593Smuzhiyun 		return rc;
3156*4882a593Smuzhiyun 
3157*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3158*4882a593Smuzhiyun 		name_len =
3159*4882a593Smuzhiyun 			cifsConvertToUTF16((__le16 *) pSMB->FileName,
3160*4882a593Smuzhiyun 					   searchName, PATH_MAX, nls_codepage,
3161*4882a593Smuzhiyun 					   remap);
3162*4882a593Smuzhiyun 		name_len++;	/* trailing null */
3163*4882a593Smuzhiyun 		name_len *= 2;
3164*4882a593Smuzhiyun 	} else {
3165*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, searchName);
3166*4882a593Smuzhiyun 	}
3167*4882a593Smuzhiyun 
3168*4882a593Smuzhiyun 	params = 2 /* level */  + 4 /* rsrvd */  + name_len /* incl null */ ;
3169*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
3170*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
3171*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3172*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
3173*4882a593Smuzhiyun 	pSMB->Reserved = 0;
3174*4882a593Smuzhiyun 	pSMB->Flags = 0;
3175*4882a593Smuzhiyun 	pSMB->Timeout = 0;
3176*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
3177*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(
3178*4882a593Smuzhiyun 	struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
3179*4882a593Smuzhiyun 	pSMB->DataCount = 0;
3180*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
3181*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
3182*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
3183*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3184*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
3185*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
3186*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
3187*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
3188*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
3189*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
3190*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
3191*4882a593Smuzhiyun 
3192*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3193*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3194*4882a593Smuzhiyun 	if (rc) {
3195*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in QuerySymLinkInfo = %d\n", rc);
3196*4882a593Smuzhiyun 	} else {
3197*4882a593Smuzhiyun 		/* decode response */
3198*4882a593Smuzhiyun 
3199*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3200*4882a593Smuzhiyun 		/* BB also check enough total bytes returned */
3201*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < 2)
3202*4882a593Smuzhiyun 			rc = -EIO;
3203*4882a593Smuzhiyun 		else {
3204*4882a593Smuzhiyun 			bool is_unicode;
3205*4882a593Smuzhiyun 			u16 count = le16_to_cpu(pSMBr->t2.DataCount);
3206*4882a593Smuzhiyun 
3207*4882a593Smuzhiyun 			data_start = ((char *) &pSMBr->hdr.Protocol) +
3208*4882a593Smuzhiyun 					   le16_to_cpu(pSMBr->t2.DataOffset);
3209*4882a593Smuzhiyun 
3210*4882a593Smuzhiyun 			if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
3211*4882a593Smuzhiyun 				is_unicode = true;
3212*4882a593Smuzhiyun 			else
3213*4882a593Smuzhiyun 				is_unicode = false;
3214*4882a593Smuzhiyun 
3215*4882a593Smuzhiyun 			/* BB FIXME investigate remapping reserved chars here */
3216*4882a593Smuzhiyun 			*symlinkinfo = cifs_strndup_from_utf16(data_start,
3217*4882a593Smuzhiyun 					count, is_unicode, nls_codepage);
3218*4882a593Smuzhiyun 			if (!*symlinkinfo)
3219*4882a593Smuzhiyun 				rc = -ENOMEM;
3220*4882a593Smuzhiyun 		}
3221*4882a593Smuzhiyun 	}
3222*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
3223*4882a593Smuzhiyun 	if (rc == -EAGAIN)
3224*4882a593Smuzhiyun 		goto querySymLinkRetry;
3225*4882a593Smuzhiyun 	return rc;
3226*4882a593Smuzhiyun }
3227*4882a593Smuzhiyun 
3228*4882a593Smuzhiyun /*
3229*4882a593Smuzhiyun  *	Recent Windows versions now create symlinks more frequently
3230*4882a593Smuzhiyun  *	and they use the "reparse point" mechanism below.  We can of course
3231*4882a593Smuzhiyun  *	do symlinks nicely to Samba and other servers which support the
3232*4882a593Smuzhiyun  *	CIFS Unix Extensions and we can also do SFU symlinks and "client only"
3233*4882a593Smuzhiyun  *	"MF" symlinks optionally, but for recent Windows we really need to
3234*4882a593Smuzhiyun  *	reenable the code below and fix the cifs_symlink callers to handle this.
3235*4882a593Smuzhiyun  *	In the interim this code has been moved to its own config option so
3236*4882a593Smuzhiyun  *	it is not compiled in by default until callers fixed up and more tested.
3237*4882a593Smuzhiyun  */
3238*4882a593Smuzhiyun int
CIFSSMBQuerySymLink(const unsigned int xid,struct cifs_tcon * tcon,__u16 fid,char ** symlinkinfo,const struct nls_table * nls_codepage)3239*4882a593Smuzhiyun CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
3240*4882a593Smuzhiyun 		    __u16 fid, char **symlinkinfo,
3241*4882a593Smuzhiyun 		    const struct nls_table *nls_codepage)
3242*4882a593Smuzhiyun {
3243*4882a593Smuzhiyun 	int rc = 0;
3244*4882a593Smuzhiyun 	int bytes_returned;
3245*4882a593Smuzhiyun 	struct smb_com_transaction_ioctl_req *pSMB;
3246*4882a593Smuzhiyun 	struct smb_com_transaction_ioctl_rsp *pSMBr;
3247*4882a593Smuzhiyun 	bool is_unicode;
3248*4882a593Smuzhiyun 	unsigned int sub_len;
3249*4882a593Smuzhiyun 	char *sub_start;
3250*4882a593Smuzhiyun 	struct reparse_symlink_data *reparse_buf;
3251*4882a593Smuzhiyun 	struct reparse_posix_data *posix_buf;
3252*4882a593Smuzhiyun 	__u32 data_offset, data_count;
3253*4882a593Smuzhiyun 	char *end_of_smb;
3254*4882a593Smuzhiyun 
3255*4882a593Smuzhiyun 	cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid);
3256*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
3257*4882a593Smuzhiyun 		      (void **) &pSMBr);
3258*4882a593Smuzhiyun 	if (rc)
3259*4882a593Smuzhiyun 		return rc;
3260*4882a593Smuzhiyun 
3261*4882a593Smuzhiyun 	pSMB->TotalParameterCount = 0 ;
3262*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
3263*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le32(2);
3264*4882a593Smuzhiyun 	/* BB find exact data count max from sess structure BB */
3265*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
3266*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 4;
3267*4882a593Smuzhiyun 	pSMB->Reserved = 0;
3268*4882a593Smuzhiyun 	pSMB->ParameterOffset = 0;
3269*4882a593Smuzhiyun 	pSMB->DataCount = 0;
3270*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
3271*4882a593Smuzhiyun 	pSMB->SetupCount = 4;
3272*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
3273*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
3274*4882a593Smuzhiyun 	pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
3275*4882a593Smuzhiyun 	pSMB->IsFsctl = 1; /* FSCTL */
3276*4882a593Smuzhiyun 	pSMB->IsRootFlag = 0;
3277*4882a593Smuzhiyun 	pSMB->Fid = fid; /* file handle always le */
3278*4882a593Smuzhiyun 	pSMB->ByteCount = 0;
3279*4882a593Smuzhiyun 
3280*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3281*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3282*4882a593Smuzhiyun 	if (rc) {
3283*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc);
3284*4882a593Smuzhiyun 		goto qreparse_out;
3285*4882a593Smuzhiyun 	}
3286*4882a593Smuzhiyun 
3287*4882a593Smuzhiyun 	data_offset = le32_to_cpu(pSMBr->DataOffset);
3288*4882a593Smuzhiyun 	data_count = le32_to_cpu(pSMBr->DataCount);
3289*4882a593Smuzhiyun 	if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) {
3290*4882a593Smuzhiyun 		/* BB also check enough total bytes returned */
3291*4882a593Smuzhiyun 		rc = -EIO;	/* bad smb */
3292*4882a593Smuzhiyun 		goto qreparse_out;
3293*4882a593Smuzhiyun 	}
3294*4882a593Smuzhiyun 	if (!data_count || (data_count > 2048)) {
3295*4882a593Smuzhiyun 		rc = -EIO;
3296*4882a593Smuzhiyun 		cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n");
3297*4882a593Smuzhiyun 		goto qreparse_out;
3298*4882a593Smuzhiyun 	}
3299*4882a593Smuzhiyun 	end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount;
3300*4882a593Smuzhiyun 	reparse_buf = (struct reparse_symlink_data *)
3301*4882a593Smuzhiyun 				((char *)&pSMBr->hdr.Protocol + data_offset);
3302*4882a593Smuzhiyun 	if ((char *)reparse_buf >= end_of_smb) {
3303*4882a593Smuzhiyun 		rc = -EIO;
3304*4882a593Smuzhiyun 		goto qreparse_out;
3305*4882a593Smuzhiyun 	}
3306*4882a593Smuzhiyun 	if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) {
3307*4882a593Smuzhiyun 		cifs_dbg(FYI, "NFS style reparse tag\n");
3308*4882a593Smuzhiyun 		posix_buf =  (struct reparse_posix_data *)reparse_buf;
3309*4882a593Smuzhiyun 
3310*4882a593Smuzhiyun 		if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) {
3311*4882a593Smuzhiyun 			cifs_dbg(FYI, "unsupported file type 0x%llx\n",
3312*4882a593Smuzhiyun 				 le64_to_cpu(posix_buf->InodeType));
3313*4882a593Smuzhiyun 			rc = -EOPNOTSUPP;
3314*4882a593Smuzhiyun 			goto qreparse_out;
3315*4882a593Smuzhiyun 		}
3316*4882a593Smuzhiyun 		is_unicode = true;
3317*4882a593Smuzhiyun 		sub_len = le16_to_cpu(reparse_buf->ReparseDataLength);
3318*4882a593Smuzhiyun 		if (posix_buf->PathBuffer + sub_len > end_of_smb) {
3319*4882a593Smuzhiyun 			cifs_dbg(FYI, "reparse buf beyond SMB\n");
3320*4882a593Smuzhiyun 			rc = -EIO;
3321*4882a593Smuzhiyun 			goto qreparse_out;
3322*4882a593Smuzhiyun 		}
3323*4882a593Smuzhiyun 		*symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer,
3324*4882a593Smuzhiyun 				sub_len, is_unicode, nls_codepage);
3325*4882a593Smuzhiyun 		goto qreparse_out;
3326*4882a593Smuzhiyun 	} else if (reparse_buf->ReparseTag !=
3327*4882a593Smuzhiyun 			cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) {
3328*4882a593Smuzhiyun 		rc = -EOPNOTSUPP;
3329*4882a593Smuzhiyun 		goto qreparse_out;
3330*4882a593Smuzhiyun 	}
3331*4882a593Smuzhiyun 
3332*4882a593Smuzhiyun 	/* Reparse tag is NTFS symlink */
3333*4882a593Smuzhiyun 	sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) +
3334*4882a593Smuzhiyun 				reparse_buf->PathBuffer;
3335*4882a593Smuzhiyun 	sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength);
3336*4882a593Smuzhiyun 	if (sub_start + sub_len > end_of_smb) {
3337*4882a593Smuzhiyun 		cifs_dbg(FYI, "reparse buf beyond SMB\n");
3338*4882a593Smuzhiyun 		rc = -EIO;
3339*4882a593Smuzhiyun 		goto qreparse_out;
3340*4882a593Smuzhiyun 	}
3341*4882a593Smuzhiyun 	if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
3342*4882a593Smuzhiyun 		is_unicode = true;
3343*4882a593Smuzhiyun 	else
3344*4882a593Smuzhiyun 		is_unicode = false;
3345*4882a593Smuzhiyun 
3346*4882a593Smuzhiyun 	/* BB FIXME investigate remapping reserved chars here */
3347*4882a593Smuzhiyun 	*symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode,
3348*4882a593Smuzhiyun 					       nls_codepage);
3349*4882a593Smuzhiyun 	if (!*symlinkinfo)
3350*4882a593Smuzhiyun 		rc = -ENOMEM;
3351*4882a593Smuzhiyun qreparse_out:
3352*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
3353*4882a593Smuzhiyun 
3354*4882a593Smuzhiyun 	/*
3355*4882a593Smuzhiyun 	 * Note: On -EAGAIN error only caller can retry on handle based calls
3356*4882a593Smuzhiyun 	 * since file handle passed in no longer valid.
3357*4882a593Smuzhiyun 	 */
3358*4882a593Smuzhiyun 	return rc;
3359*4882a593Smuzhiyun }
3360*4882a593Smuzhiyun 
3361*4882a593Smuzhiyun int
CIFSSMB_set_compression(const unsigned int xid,struct cifs_tcon * tcon,__u16 fid)3362*4882a593Smuzhiyun CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
3363*4882a593Smuzhiyun 		    __u16 fid)
3364*4882a593Smuzhiyun {
3365*4882a593Smuzhiyun 	int rc = 0;
3366*4882a593Smuzhiyun 	int bytes_returned;
3367*4882a593Smuzhiyun 	struct smb_com_transaction_compr_ioctl_req *pSMB;
3368*4882a593Smuzhiyun 	struct smb_com_transaction_ioctl_rsp *pSMBr;
3369*4882a593Smuzhiyun 
3370*4882a593Smuzhiyun 	cifs_dbg(FYI, "Set compression for %u\n", fid);
3371*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
3372*4882a593Smuzhiyun 		      (void **) &pSMBr);
3373*4882a593Smuzhiyun 	if (rc)
3374*4882a593Smuzhiyun 		return rc;
3375*4882a593Smuzhiyun 
3376*4882a593Smuzhiyun 	pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
3377*4882a593Smuzhiyun 
3378*4882a593Smuzhiyun 	pSMB->TotalParameterCount = 0;
3379*4882a593Smuzhiyun 	pSMB->TotalDataCount = cpu_to_le32(2);
3380*4882a593Smuzhiyun 	pSMB->MaxParameterCount = 0;
3381*4882a593Smuzhiyun 	pSMB->MaxDataCount = 0;
3382*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 4;
3383*4882a593Smuzhiyun 	pSMB->Reserved = 0;
3384*4882a593Smuzhiyun 	pSMB->ParameterOffset = 0;
3385*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le32(2);
3386*4882a593Smuzhiyun 	pSMB->DataOffset =
3387*4882a593Smuzhiyun 		cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req,
3388*4882a593Smuzhiyun 				compression_state) - 4);  /* 84 */
3389*4882a593Smuzhiyun 	pSMB->SetupCount = 4;
3390*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
3391*4882a593Smuzhiyun 	pSMB->ParameterCount = 0;
3392*4882a593Smuzhiyun 	pSMB->FunctionCode = cpu_to_le32(FSCTL_SET_COMPRESSION);
3393*4882a593Smuzhiyun 	pSMB->IsFsctl = 1; /* FSCTL */
3394*4882a593Smuzhiyun 	pSMB->IsRootFlag = 0;
3395*4882a593Smuzhiyun 	pSMB->Fid = fid; /* file handle always le */
3396*4882a593Smuzhiyun 	/* 3 byte pad, followed by 2 byte compress state */
3397*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(5);
3398*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, 5);
3399*4882a593Smuzhiyun 
3400*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3401*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3402*4882a593Smuzhiyun 	if (rc)
3403*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc);
3404*4882a593Smuzhiyun 
3405*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
3406*4882a593Smuzhiyun 
3407*4882a593Smuzhiyun 	/*
3408*4882a593Smuzhiyun 	 * Note: On -EAGAIN error only caller can retry on handle based calls
3409*4882a593Smuzhiyun 	 * since file handle passed in no longer valid.
3410*4882a593Smuzhiyun 	 */
3411*4882a593Smuzhiyun 	return rc;
3412*4882a593Smuzhiyun }
3413*4882a593Smuzhiyun 
3414*4882a593Smuzhiyun 
3415*4882a593Smuzhiyun #ifdef CONFIG_CIFS_POSIX
3416*4882a593Smuzhiyun 
3417*4882a593Smuzhiyun /*Convert an Access Control Entry from wire format to local POSIX xattr format*/
cifs_convert_ace(struct posix_acl_xattr_entry * ace,struct cifs_posix_ace * cifs_ace)3418*4882a593Smuzhiyun static void cifs_convert_ace(struct posix_acl_xattr_entry *ace,
3419*4882a593Smuzhiyun 			     struct cifs_posix_ace *cifs_ace)
3420*4882a593Smuzhiyun {
3421*4882a593Smuzhiyun 	/* u8 cifs fields do not need le conversion */
3422*4882a593Smuzhiyun 	ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
3423*4882a593Smuzhiyun 	ace->e_tag  = cpu_to_le16(cifs_ace->cifs_e_tag);
3424*4882a593Smuzhiyun 	ace->e_id   = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
3425*4882a593Smuzhiyun /*
3426*4882a593Smuzhiyun 	cifs_dbg(FYI, "perm %d tag %d id %d\n",
3427*4882a593Smuzhiyun 		 ace->e_perm, ace->e_tag, ace->e_id);
3428*4882a593Smuzhiyun */
3429*4882a593Smuzhiyun 
3430*4882a593Smuzhiyun 	return;
3431*4882a593Smuzhiyun }
3432*4882a593Smuzhiyun 
3433*4882a593Smuzhiyun /* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
cifs_copy_posix_acl(char * trgt,char * src,const int buflen,const int acl_type,const int size_of_data_area)3434*4882a593Smuzhiyun static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
3435*4882a593Smuzhiyun 			       const int acl_type, const int size_of_data_area)
3436*4882a593Smuzhiyun {
3437*4882a593Smuzhiyun 	int size =  0;
3438*4882a593Smuzhiyun 	int i;
3439*4882a593Smuzhiyun 	__u16 count;
3440*4882a593Smuzhiyun 	struct cifs_posix_ace *pACE;
3441*4882a593Smuzhiyun 	struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
3442*4882a593Smuzhiyun 	struct posix_acl_xattr_header *local_acl = (void *)trgt;
3443*4882a593Smuzhiyun 
3444*4882a593Smuzhiyun 	if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
3445*4882a593Smuzhiyun 		return -EOPNOTSUPP;
3446*4882a593Smuzhiyun 
3447*4882a593Smuzhiyun 	if (acl_type == ACL_TYPE_ACCESS) {
3448*4882a593Smuzhiyun 		count = le16_to_cpu(cifs_acl->access_entry_count);
3449*4882a593Smuzhiyun 		pACE = &cifs_acl->ace_array[0];
3450*4882a593Smuzhiyun 		size = sizeof(struct cifs_posix_acl);
3451*4882a593Smuzhiyun 		size += sizeof(struct cifs_posix_ace) * count;
3452*4882a593Smuzhiyun 		/* check if we would go beyond end of SMB */
3453*4882a593Smuzhiyun 		if (size_of_data_area < size) {
3454*4882a593Smuzhiyun 			cifs_dbg(FYI, "bad CIFS POSIX ACL size %d vs. %d\n",
3455*4882a593Smuzhiyun 				 size_of_data_area, size);
3456*4882a593Smuzhiyun 			return -EINVAL;
3457*4882a593Smuzhiyun 		}
3458*4882a593Smuzhiyun 	} else if (acl_type == ACL_TYPE_DEFAULT) {
3459*4882a593Smuzhiyun 		count = le16_to_cpu(cifs_acl->access_entry_count);
3460*4882a593Smuzhiyun 		size = sizeof(struct cifs_posix_acl);
3461*4882a593Smuzhiyun 		size += sizeof(struct cifs_posix_ace) * count;
3462*4882a593Smuzhiyun /* skip past access ACEs to get to default ACEs */
3463*4882a593Smuzhiyun 		pACE = &cifs_acl->ace_array[count];
3464*4882a593Smuzhiyun 		count = le16_to_cpu(cifs_acl->default_entry_count);
3465*4882a593Smuzhiyun 		size += sizeof(struct cifs_posix_ace) * count;
3466*4882a593Smuzhiyun 		/* check if we would go beyond end of SMB */
3467*4882a593Smuzhiyun 		if (size_of_data_area < size)
3468*4882a593Smuzhiyun 			return -EINVAL;
3469*4882a593Smuzhiyun 	} else {
3470*4882a593Smuzhiyun 		/* illegal type */
3471*4882a593Smuzhiyun 		return -EINVAL;
3472*4882a593Smuzhiyun 	}
3473*4882a593Smuzhiyun 
3474*4882a593Smuzhiyun 	size = posix_acl_xattr_size(count);
3475*4882a593Smuzhiyun 	if ((buflen == 0) || (local_acl == NULL)) {
3476*4882a593Smuzhiyun 		/* used to query ACL EA size */
3477*4882a593Smuzhiyun 	} else if (size > buflen) {
3478*4882a593Smuzhiyun 		return -ERANGE;
3479*4882a593Smuzhiyun 	} else /* buffer big enough */ {
3480*4882a593Smuzhiyun 		struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1);
3481*4882a593Smuzhiyun 
3482*4882a593Smuzhiyun 		local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
3483*4882a593Smuzhiyun 		for (i = 0; i < count ; i++) {
3484*4882a593Smuzhiyun 			cifs_convert_ace(&ace[i], pACE);
3485*4882a593Smuzhiyun 			pACE++;
3486*4882a593Smuzhiyun 		}
3487*4882a593Smuzhiyun 	}
3488*4882a593Smuzhiyun 	return size;
3489*4882a593Smuzhiyun }
3490*4882a593Smuzhiyun 
convert_ace_to_cifs_ace(struct cifs_posix_ace * cifs_ace,const struct posix_acl_xattr_entry * local_ace)3491*4882a593Smuzhiyun static void convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
3492*4882a593Smuzhiyun 				     const struct posix_acl_xattr_entry *local_ace)
3493*4882a593Smuzhiyun {
3494*4882a593Smuzhiyun 	cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
3495*4882a593Smuzhiyun 	cifs_ace->cifs_e_tag =  le16_to_cpu(local_ace->e_tag);
3496*4882a593Smuzhiyun 	/* BB is there a better way to handle the large uid? */
3497*4882a593Smuzhiyun 	if (local_ace->e_id == cpu_to_le32(-1)) {
3498*4882a593Smuzhiyun 	/* Probably no need to le convert -1 on any arch but can not hurt */
3499*4882a593Smuzhiyun 		cifs_ace->cifs_uid = cpu_to_le64(-1);
3500*4882a593Smuzhiyun 	} else
3501*4882a593Smuzhiyun 		cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
3502*4882a593Smuzhiyun /*
3503*4882a593Smuzhiyun 	cifs_dbg(FYI, "perm %d tag %d id %d\n",
3504*4882a593Smuzhiyun 		 ace->e_perm, ace->e_tag, ace->e_id);
3505*4882a593Smuzhiyun */
3506*4882a593Smuzhiyun }
3507*4882a593Smuzhiyun 
3508*4882a593Smuzhiyun /* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
ACL_to_cifs_posix(char * parm_data,const char * pACL,const int buflen,const int acl_type)3509*4882a593Smuzhiyun static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
3510*4882a593Smuzhiyun 			       const int buflen, const int acl_type)
3511*4882a593Smuzhiyun {
3512*4882a593Smuzhiyun 	__u16 rc = 0;
3513*4882a593Smuzhiyun 	struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
3514*4882a593Smuzhiyun 	struct posix_acl_xattr_header *local_acl = (void *)pACL;
3515*4882a593Smuzhiyun 	struct posix_acl_xattr_entry *ace = (void *)(local_acl + 1);
3516*4882a593Smuzhiyun 	int count;
3517*4882a593Smuzhiyun 	int i;
3518*4882a593Smuzhiyun 
3519*4882a593Smuzhiyun 	if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
3520*4882a593Smuzhiyun 		return 0;
3521*4882a593Smuzhiyun 
3522*4882a593Smuzhiyun 	count = posix_acl_xattr_count((size_t)buflen);
3523*4882a593Smuzhiyun 	cifs_dbg(FYI, "setting acl with %d entries from buf of length %d and version of %d\n",
3524*4882a593Smuzhiyun 		 count, buflen, le32_to_cpu(local_acl->a_version));
3525*4882a593Smuzhiyun 	if (le32_to_cpu(local_acl->a_version) != 2) {
3526*4882a593Smuzhiyun 		cifs_dbg(FYI, "unknown POSIX ACL version %d\n",
3527*4882a593Smuzhiyun 			 le32_to_cpu(local_acl->a_version));
3528*4882a593Smuzhiyun 		return 0;
3529*4882a593Smuzhiyun 	}
3530*4882a593Smuzhiyun 	cifs_acl->version = cpu_to_le16(1);
3531*4882a593Smuzhiyun 	if (acl_type == ACL_TYPE_ACCESS) {
3532*4882a593Smuzhiyun 		cifs_acl->access_entry_count = cpu_to_le16(count);
3533*4882a593Smuzhiyun 		cifs_acl->default_entry_count = cpu_to_le16(0xFFFF);
3534*4882a593Smuzhiyun 	} else if (acl_type == ACL_TYPE_DEFAULT) {
3535*4882a593Smuzhiyun 		cifs_acl->default_entry_count = cpu_to_le16(count);
3536*4882a593Smuzhiyun 		cifs_acl->access_entry_count = cpu_to_le16(0xFFFF);
3537*4882a593Smuzhiyun 	} else {
3538*4882a593Smuzhiyun 		cifs_dbg(FYI, "unknown ACL type %d\n", acl_type);
3539*4882a593Smuzhiyun 		return 0;
3540*4882a593Smuzhiyun 	}
3541*4882a593Smuzhiyun 	for (i = 0; i < count; i++)
3542*4882a593Smuzhiyun 		convert_ace_to_cifs_ace(&cifs_acl->ace_array[i], &ace[i]);
3543*4882a593Smuzhiyun 	if (rc == 0) {
3544*4882a593Smuzhiyun 		rc = (__u16)(count * sizeof(struct cifs_posix_ace));
3545*4882a593Smuzhiyun 		rc += sizeof(struct cifs_posix_acl);
3546*4882a593Smuzhiyun 		/* BB add check to make sure ACL does not overflow SMB */
3547*4882a593Smuzhiyun 	}
3548*4882a593Smuzhiyun 	return rc;
3549*4882a593Smuzhiyun }
3550*4882a593Smuzhiyun 
3551*4882a593Smuzhiyun int
CIFSSMBGetPosixACL(const unsigned int xid,struct cifs_tcon * tcon,const unsigned char * searchName,char * acl_inf,const int buflen,const int acl_type,const struct nls_table * nls_codepage,int remap)3552*4882a593Smuzhiyun CIFSSMBGetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
3553*4882a593Smuzhiyun 		   const unsigned char *searchName,
3554*4882a593Smuzhiyun 		   char *acl_inf, const int buflen, const int acl_type,
3555*4882a593Smuzhiyun 		   const struct nls_table *nls_codepage, int remap)
3556*4882a593Smuzhiyun {
3557*4882a593Smuzhiyun /* SMB_QUERY_POSIX_ACL */
3558*4882a593Smuzhiyun 	TRANSACTION2_QPI_REQ *pSMB = NULL;
3559*4882a593Smuzhiyun 	TRANSACTION2_QPI_RSP *pSMBr = NULL;
3560*4882a593Smuzhiyun 	int rc = 0;
3561*4882a593Smuzhiyun 	int bytes_returned;
3562*4882a593Smuzhiyun 	int name_len;
3563*4882a593Smuzhiyun 	__u16 params, byte_count;
3564*4882a593Smuzhiyun 
3565*4882a593Smuzhiyun 	cifs_dbg(FYI, "In GetPosixACL (Unix) for path %s\n", searchName);
3566*4882a593Smuzhiyun 
3567*4882a593Smuzhiyun queryAclRetry:
3568*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3569*4882a593Smuzhiyun 		(void **) &pSMBr);
3570*4882a593Smuzhiyun 	if (rc)
3571*4882a593Smuzhiyun 		return rc;
3572*4882a593Smuzhiyun 
3573*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3574*4882a593Smuzhiyun 		name_len =
3575*4882a593Smuzhiyun 			cifsConvertToUTF16((__le16 *) pSMB->FileName,
3576*4882a593Smuzhiyun 					   searchName, PATH_MAX, nls_codepage,
3577*4882a593Smuzhiyun 					   remap);
3578*4882a593Smuzhiyun 		name_len++;     /* trailing null */
3579*4882a593Smuzhiyun 		name_len *= 2;
3580*4882a593Smuzhiyun 		pSMB->FileName[name_len] = 0;
3581*4882a593Smuzhiyun 		pSMB->FileName[name_len+1] = 0;
3582*4882a593Smuzhiyun 	} else {
3583*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, searchName);
3584*4882a593Smuzhiyun 	}
3585*4882a593Smuzhiyun 
3586*4882a593Smuzhiyun 	params = 2 /* level */  + 4 /* rsrvd */  + name_len /* incl null */ ;
3587*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
3588*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
3589*4882a593Smuzhiyun 	/* BB find exact max data count below from sess structure BB */
3590*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(4000);
3591*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
3592*4882a593Smuzhiyun 	pSMB->Reserved = 0;
3593*4882a593Smuzhiyun 	pSMB->Flags = 0;
3594*4882a593Smuzhiyun 	pSMB->Timeout = 0;
3595*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
3596*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(
3597*4882a593Smuzhiyun 		offsetof(struct smb_com_transaction2_qpi_req,
3598*4882a593Smuzhiyun 			 InformationLevel) - 4);
3599*4882a593Smuzhiyun 	pSMB->DataCount = 0;
3600*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
3601*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
3602*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
3603*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3604*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
3605*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
3606*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
3607*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
3608*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
3609*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
3610*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
3611*4882a593Smuzhiyun 
3612*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3613*4882a593Smuzhiyun 		(struct smb_hdr *) pSMBr, &bytes_returned, 0);
3614*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get);
3615*4882a593Smuzhiyun 	if (rc) {
3616*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in Query POSIX ACL = %d\n", rc);
3617*4882a593Smuzhiyun 	} else {
3618*4882a593Smuzhiyun 		/* decode response */
3619*4882a593Smuzhiyun 
3620*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3621*4882a593Smuzhiyun 		/* BB also check enough total bytes returned */
3622*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < 2)
3623*4882a593Smuzhiyun 			rc = -EIO;      /* bad smb */
3624*4882a593Smuzhiyun 		else {
3625*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3626*4882a593Smuzhiyun 			__u16 count = le16_to_cpu(pSMBr->t2.DataCount);
3627*4882a593Smuzhiyun 			rc = cifs_copy_posix_acl(acl_inf,
3628*4882a593Smuzhiyun 				(char *)&pSMBr->hdr.Protocol+data_offset,
3629*4882a593Smuzhiyun 				buflen, acl_type, count);
3630*4882a593Smuzhiyun 		}
3631*4882a593Smuzhiyun 	}
3632*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
3633*4882a593Smuzhiyun 	if (rc == -EAGAIN)
3634*4882a593Smuzhiyun 		goto queryAclRetry;
3635*4882a593Smuzhiyun 	return rc;
3636*4882a593Smuzhiyun }
3637*4882a593Smuzhiyun 
3638*4882a593Smuzhiyun int
CIFSSMBSetPosixACL(const unsigned int xid,struct cifs_tcon * tcon,const unsigned char * fileName,const char * local_acl,const int buflen,const int acl_type,const struct nls_table * nls_codepage,int remap)3639*4882a593Smuzhiyun CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,
3640*4882a593Smuzhiyun 		   const unsigned char *fileName,
3641*4882a593Smuzhiyun 		   const char *local_acl, const int buflen,
3642*4882a593Smuzhiyun 		   const int acl_type,
3643*4882a593Smuzhiyun 		   const struct nls_table *nls_codepage, int remap)
3644*4882a593Smuzhiyun {
3645*4882a593Smuzhiyun 	struct smb_com_transaction2_spi_req *pSMB = NULL;
3646*4882a593Smuzhiyun 	struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
3647*4882a593Smuzhiyun 	char *parm_data;
3648*4882a593Smuzhiyun 	int name_len;
3649*4882a593Smuzhiyun 	int rc = 0;
3650*4882a593Smuzhiyun 	int bytes_returned = 0;
3651*4882a593Smuzhiyun 	__u16 params, byte_count, data_count, param_offset, offset;
3652*4882a593Smuzhiyun 
3653*4882a593Smuzhiyun 	cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
3654*4882a593Smuzhiyun setAclRetry:
3655*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3656*4882a593Smuzhiyun 		      (void **) &pSMBr);
3657*4882a593Smuzhiyun 	if (rc)
3658*4882a593Smuzhiyun 		return rc;
3659*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3660*4882a593Smuzhiyun 		name_len =
3661*4882a593Smuzhiyun 			cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
3662*4882a593Smuzhiyun 					   PATH_MAX, nls_codepage, remap);
3663*4882a593Smuzhiyun 		name_len++;     /* trailing null */
3664*4882a593Smuzhiyun 		name_len *= 2;
3665*4882a593Smuzhiyun 	} else {
3666*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, fileName);
3667*4882a593Smuzhiyun 	}
3668*4882a593Smuzhiyun 	params = 6 + name_len;
3669*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
3670*4882a593Smuzhiyun 	/* BB find max SMB size from sess */
3671*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
3672*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
3673*4882a593Smuzhiyun 	pSMB->Reserved = 0;
3674*4882a593Smuzhiyun 	pSMB->Flags = 0;
3675*4882a593Smuzhiyun 	pSMB->Timeout = 0;
3676*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
3677*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_spi_req,
3678*4882a593Smuzhiyun 				InformationLevel) - 4;
3679*4882a593Smuzhiyun 	offset = param_offset + params;
3680*4882a593Smuzhiyun 	parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
3681*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
3682*4882a593Smuzhiyun 
3683*4882a593Smuzhiyun 	/* convert to on the wire format for POSIX ACL */
3684*4882a593Smuzhiyun 	data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
3685*4882a593Smuzhiyun 
3686*4882a593Smuzhiyun 	if (data_count == 0) {
3687*4882a593Smuzhiyun 		rc = -EOPNOTSUPP;
3688*4882a593Smuzhiyun 		goto setACLerrorExit;
3689*4882a593Smuzhiyun 	}
3690*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
3691*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
3692*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
3693*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
3694*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
3695*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + data_count;
3696*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(data_count);
3697*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
3698*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
3699*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
3700*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
3701*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
3702*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
3703*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3704*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3705*4882a593Smuzhiyun 	if (rc)
3706*4882a593Smuzhiyun 		cifs_dbg(FYI, "Set POSIX ACL returned %d\n", rc);
3707*4882a593Smuzhiyun 
3708*4882a593Smuzhiyun setACLerrorExit:
3709*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
3710*4882a593Smuzhiyun 	if (rc == -EAGAIN)
3711*4882a593Smuzhiyun 		goto setAclRetry;
3712*4882a593Smuzhiyun 	return rc;
3713*4882a593Smuzhiyun }
3714*4882a593Smuzhiyun 
3715*4882a593Smuzhiyun /* BB fix tabs in this function FIXME BB */
3716*4882a593Smuzhiyun int
CIFSGetExtAttr(const unsigned int xid,struct cifs_tcon * tcon,const int netfid,__u64 * pExtAttrBits,__u64 * pMask)3717*4882a593Smuzhiyun CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *tcon,
3718*4882a593Smuzhiyun 	       const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
3719*4882a593Smuzhiyun {
3720*4882a593Smuzhiyun 	int rc = 0;
3721*4882a593Smuzhiyun 	struct smb_t2_qfi_req *pSMB = NULL;
3722*4882a593Smuzhiyun 	struct smb_t2_qfi_rsp *pSMBr = NULL;
3723*4882a593Smuzhiyun 	int bytes_returned;
3724*4882a593Smuzhiyun 	__u16 params, byte_count;
3725*4882a593Smuzhiyun 
3726*4882a593Smuzhiyun 	cifs_dbg(FYI, "In GetExtAttr\n");
3727*4882a593Smuzhiyun 	if (tcon == NULL)
3728*4882a593Smuzhiyun 		return -ENODEV;
3729*4882a593Smuzhiyun 
3730*4882a593Smuzhiyun GetExtAttrRetry:
3731*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3732*4882a593Smuzhiyun 			(void **) &pSMBr);
3733*4882a593Smuzhiyun 	if (rc)
3734*4882a593Smuzhiyun 		return rc;
3735*4882a593Smuzhiyun 
3736*4882a593Smuzhiyun 	params = 2 /* level */ + 2 /* fid */;
3737*4882a593Smuzhiyun 	pSMB->t2.TotalDataCount = 0;
3738*4882a593Smuzhiyun 	pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3739*4882a593Smuzhiyun 	/* BB find exact max data count below from sess structure BB */
3740*4882a593Smuzhiyun 	pSMB->t2.MaxDataCount = cpu_to_le16(4000);
3741*4882a593Smuzhiyun 	pSMB->t2.MaxSetupCount = 0;
3742*4882a593Smuzhiyun 	pSMB->t2.Reserved = 0;
3743*4882a593Smuzhiyun 	pSMB->t2.Flags = 0;
3744*4882a593Smuzhiyun 	pSMB->t2.Timeout = 0;
3745*4882a593Smuzhiyun 	pSMB->t2.Reserved2 = 0;
3746*4882a593Smuzhiyun 	pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3747*4882a593Smuzhiyun 					       Fid) - 4);
3748*4882a593Smuzhiyun 	pSMB->t2.DataCount = 0;
3749*4882a593Smuzhiyun 	pSMB->t2.DataOffset = 0;
3750*4882a593Smuzhiyun 	pSMB->t2.SetupCount = 1;
3751*4882a593Smuzhiyun 	pSMB->t2.Reserved3 = 0;
3752*4882a593Smuzhiyun 	pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3753*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
3754*4882a593Smuzhiyun 	pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3755*4882a593Smuzhiyun 	pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3756*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
3757*4882a593Smuzhiyun 	pSMB->Pad = 0;
3758*4882a593Smuzhiyun 	pSMB->Fid = netfid;
3759*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
3760*4882a593Smuzhiyun 	pSMB->t2.ByteCount = cpu_to_le16(byte_count);
3761*4882a593Smuzhiyun 
3762*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3763*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3764*4882a593Smuzhiyun 	if (rc) {
3765*4882a593Smuzhiyun 		cifs_dbg(FYI, "error %d in GetExtAttr\n", rc);
3766*4882a593Smuzhiyun 	} else {
3767*4882a593Smuzhiyun 		/* decode response */
3768*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3769*4882a593Smuzhiyun 		/* BB also check enough total bytes returned */
3770*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < 2)
3771*4882a593Smuzhiyun 			/* If rc should we check for EOPNOSUPP and
3772*4882a593Smuzhiyun 			   disable the srvino flag? or in caller? */
3773*4882a593Smuzhiyun 			rc = -EIO;      /* bad smb */
3774*4882a593Smuzhiyun 		else {
3775*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3776*4882a593Smuzhiyun 			__u16 count = le16_to_cpu(pSMBr->t2.DataCount);
3777*4882a593Smuzhiyun 			struct file_chattr_info *pfinfo;
3778*4882a593Smuzhiyun 			/* BB Do we need a cast or hash here ? */
3779*4882a593Smuzhiyun 			if (count != 16) {
3780*4882a593Smuzhiyun 				cifs_dbg(FYI, "Invalid size ret in GetExtAttr\n");
3781*4882a593Smuzhiyun 				rc = -EIO;
3782*4882a593Smuzhiyun 				goto GetExtAttrOut;
3783*4882a593Smuzhiyun 			}
3784*4882a593Smuzhiyun 			pfinfo = (struct file_chattr_info *)
3785*4882a593Smuzhiyun 				 (data_offset + (char *) &pSMBr->hdr.Protocol);
3786*4882a593Smuzhiyun 			*pExtAttrBits = le64_to_cpu(pfinfo->mode);
3787*4882a593Smuzhiyun 			*pMask = le64_to_cpu(pfinfo->mask);
3788*4882a593Smuzhiyun 		}
3789*4882a593Smuzhiyun 	}
3790*4882a593Smuzhiyun GetExtAttrOut:
3791*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
3792*4882a593Smuzhiyun 	if (rc == -EAGAIN)
3793*4882a593Smuzhiyun 		goto GetExtAttrRetry;
3794*4882a593Smuzhiyun 	return rc;
3795*4882a593Smuzhiyun }
3796*4882a593Smuzhiyun 
3797*4882a593Smuzhiyun #endif /* CONFIG_POSIX */
3798*4882a593Smuzhiyun 
3799*4882a593Smuzhiyun /*
3800*4882a593Smuzhiyun  * Initialize NT TRANSACT SMB into small smb request buffer.  This assumes that
3801*4882a593Smuzhiyun  * all NT TRANSACTS that we init here have total parm and data under about 400
3802*4882a593Smuzhiyun  * bytes (to fit in small cifs buffer size), which is the case so far, it
3803*4882a593Smuzhiyun  * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of
3804*4882a593Smuzhiyun  * returned setup area) and MaxParameterCount (returned parms size) must be set
3805*4882a593Smuzhiyun  * by caller
3806*4882a593Smuzhiyun  */
3807*4882a593Smuzhiyun static int
smb_init_nttransact(const __u16 sub_command,const int setup_count,const int parm_len,struct cifs_tcon * tcon,void ** ret_buf)3808*4882a593Smuzhiyun smb_init_nttransact(const __u16 sub_command, const int setup_count,
3809*4882a593Smuzhiyun 		   const int parm_len, struct cifs_tcon *tcon,
3810*4882a593Smuzhiyun 		   void **ret_buf)
3811*4882a593Smuzhiyun {
3812*4882a593Smuzhiyun 	int rc;
3813*4882a593Smuzhiyun 	__u32 temp_offset;
3814*4882a593Smuzhiyun 	struct smb_com_ntransact_req *pSMB;
3815*4882a593Smuzhiyun 
3816*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
3817*4882a593Smuzhiyun 				(void **)&pSMB);
3818*4882a593Smuzhiyun 	if (rc)
3819*4882a593Smuzhiyun 		return rc;
3820*4882a593Smuzhiyun 	*ret_buf = (void *)pSMB;
3821*4882a593Smuzhiyun 	pSMB->Reserved = 0;
3822*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le32(parm_len);
3823*4882a593Smuzhiyun 	pSMB->TotalDataCount  = 0;
3824*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
3825*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
3826*4882a593Smuzhiyun 	pSMB->DataCount  = pSMB->TotalDataCount;
3827*4882a593Smuzhiyun 	temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
3828*4882a593Smuzhiyun 			(setup_count * 2) - 4 /* for rfc1001 length itself */;
3829*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le32(temp_offset);
3830*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
3831*4882a593Smuzhiyun 	pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
3832*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(sub_command);
3833*4882a593Smuzhiyun 	return 0;
3834*4882a593Smuzhiyun }
3835*4882a593Smuzhiyun 
3836*4882a593Smuzhiyun static int
validate_ntransact(char * buf,char ** ppparm,char ** ppdata,__u32 * pparmlen,__u32 * pdatalen)3837*4882a593Smuzhiyun validate_ntransact(char *buf, char **ppparm, char **ppdata,
3838*4882a593Smuzhiyun 		   __u32 *pparmlen, __u32 *pdatalen)
3839*4882a593Smuzhiyun {
3840*4882a593Smuzhiyun 	char *end_of_smb;
3841*4882a593Smuzhiyun 	__u32 data_count, data_offset, parm_count, parm_offset;
3842*4882a593Smuzhiyun 	struct smb_com_ntransact_rsp *pSMBr;
3843*4882a593Smuzhiyun 	u16 bcc;
3844*4882a593Smuzhiyun 
3845*4882a593Smuzhiyun 	*pdatalen = 0;
3846*4882a593Smuzhiyun 	*pparmlen = 0;
3847*4882a593Smuzhiyun 
3848*4882a593Smuzhiyun 	if (buf == NULL)
3849*4882a593Smuzhiyun 		return -EINVAL;
3850*4882a593Smuzhiyun 
3851*4882a593Smuzhiyun 	pSMBr = (struct smb_com_ntransact_rsp *)buf;
3852*4882a593Smuzhiyun 
3853*4882a593Smuzhiyun 	bcc = get_bcc(&pSMBr->hdr);
3854*4882a593Smuzhiyun 	end_of_smb = 2 /* sizeof byte count */ + bcc +
3855*4882a593Smuzhiyun 			(char *)&pSMBr->ByteCount;
3856*4882a593Smuzhiyun 
3857*4882a593Smuzhiyun 	data_offset = le32_to_cpu(pSMBr->DataOffset);
3858*4882a593Smuzhiyun 	data_count = le32_to_cpu(pSMBr->DataCount);
3859*4882a593Smuzhiyun 	parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
3860*4882a593Smuzhiyun 	parm_count = le32_to_cpu(pSMBr->ParameterCount);
3861*4882a593Smuzhiyun 
3862*4882a593Smuzhiyun 	*ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
3863*4882a593Smuzhiyun 	*ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
3864*4882a593Smuzhiyun 
3865*4882a593Smuzhiyun 	/* should we also check that parm and data areas do not overlap? */
3866*4882a593Smuzhiyun 	if (*ppparm > end_of_smb) {
3867*4882a593Smuzhiyun 		cifs_dbg(FYI, "parms start after end of smb\n");
3868*4882a593Smuzhiyun 		return -EINVAL;
3869*4882a593Smuzhiyun 	} else if (parm_count + *ppparm > end_of_smb) {
3870*4882a593Smuzhiyun 		cifs_dbg(FYI, "parm end after end of smb\n");
3871*4882a593Smuzhiyun 		return -EINVAL;
3872*4882a593Smuzhiyun 	} else if (*ppdata > end_of_smb) {
3873*4882a593Smuzhiyun 		cifs_dbg(FYI, "data starts after end of smb\n");
3874*4882a593Smuzhiyun 		return -EINVAL;
3875*4882a593Smuzhiyun 	} else if (data_count + *ppdata > end_of_smb) {
3876*4882a593Smuzhiyun 		cifs_dbg(FYI, "data %p + count %d (%p) past smb end %p start %p\n",
3877*4882a593Smuzhiyun 			 *ppdata, data_count, (data_count + *ppdata),
3878*4882a593Smuzhiyun 			 end_of_smb, pSMBr);
3879*4882a593Smuzhiyun 		return -EINVAL;
3880*4882a593Smuzhiyun 	} else if (parm_count + data_count > bcc) {
3881*4882a593Smuzhiyun 		cifs_dbg(FYI, "parm count and data count larger than SMB\n");
3882*4882a593Smuzhiyun 		return -EINVAL;
3883*4882a593Smuzhiyun 	}
3884*4882a593Smuzhiyun 	*pdatalen = data_count;
3885*4882a593Smuzhiyun 	*pparmlen = parm_count;
3886*4882a593Smuzhiyun 	return 0;
3887*4882a593Smuzhiyun }
3888*4882a593Smuzhiyun 
3889*4882a593Smuzhiyun /* Get Security Descriptor (by handle) from remote server for a file or dir */
3890*4882a593Smuzhiyun int
CIFSSMBGetCIFSACL(const unsigned int xid,struct cifs_tcon * tcon,__u16 fid,struct cifs_ntsd ** acl_inf,__u32 * pbuflen)3891*4882a593Smuzhiyun CIFSSMBGetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid,
3892*4882a593Smuzhiyun 		  struct cifs_ntsd **acl_inf, __u32 *pbuflen)
3893*4882a593Smuzhiyun {
3894*4882a593Smuzhiyun 	int rc = 0;
3895*4882a593Smuzhiyun 	int buf_type = 0;
3896*4882a593Smuzhiyun 	QUERY_SEC_DESC_REQ *pSMB;
3897*4882a593Smuzhiyun 	struct kvec iov[1];
3898*4882a593Smuzhiyun 	struct kvec rsp_iov;
3899*4882a593Smuzhiyun 
3900*4882a593Smuzhiyun 	cifs_dbg(FYI, "GetCifsACL\n");
3901*4882a593Smuzhiyun 
3902*4882a593Smuzhiyun 	*pbuflen = 0;
3903*4882a593Smuzhiyun 	*acl_inf = NULL;
3904*4882a593Smuzhiyun 
3905*4882a593Smuzhiyun 	rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
3906*4882a593Smuzhiyun 			8 /* parm len */, tcon, (void **) &pSMB);
3907*4882a593Smuzhiyun 	if (rc)
3908*4882a593Smuzhiyun 		return rc;
3909*4882a593Smuzhiyun 
3910*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le32(4);
3911*4882a593Smuzhiyun 	/* BB TEST with big acls that might need to be e.g. larger than 16K */
3912*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
3913*4882a593Smuzhiyun 	pSMB->Fid = fid; /* file handle always le */
3914*4882a593Smuzhiyun 	pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3915*4882a593Smuzhiyun 				     CIFS_ACL_DACL);
3916*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3917*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, 11);
3918*4882a593Smuzhiyun 	iov[0].iov_base = (char *)pSMB;
3919*4882a593Smuzhiyun 	iov[0].iov_len = be32_to_cpu(pSMB->hdr.smb_buf_length) + 4;
3920*4882a593Smuzhiyun 
3921*4882a593Smuzhiyun 	rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
3922*4882a593Smuzhiyun 			  0, &rsp_iov);
3923*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
3924*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_acl_get);
3925*4882a593Smuzhiyun 	if (rc) {
3926*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in QuerySecDesc = %d\n", rc);
3927*4882a593Smuzhiyun 	} else {                /* decode response */
3928*4882a593Smuzhiyun 		__le32 *parm;
3929*4882a593Smuzhiyun 		__u32 parm_len;
3930*4882a593Smuzhiyun 		__u32 acl_len;
3931*4882a593Smuzhiyun 		struct smb_com_ntransact_rsp *pSMBr;
3932*4882a593Smuzhiyun 		char *pdata;
3933*4882a593Smuzhiyun 
3934*4882a593Smuzhiyun /* validate_nttransact */
3935*4882a593Smuzhiyun 		rc = validate_ntransact(rsp_iov.iov_base, (char **)&parm,
3936*4882a593Smuzhiyun 					&pdata, &parm_len, pbuflen);
3937*4882a593Smuzhiyun 		if (rc)
3938*4882a593Smuzhiyun 			goto qsec_out;
3939*4882a593Smuzhiyun 		pSMBr = (struct smb_com_ntransact_rsp *)rsp_iov.iov_base;
3940*4882a593Smuzhiyun 
3941*4882a593Smuzhiyun 		cifs_dbg(FYI, "smb %p parm %p data %p\n",
3942*4882a593Smuzhiyun 			 pSMBr, parm, *acl_inf);
3943*4882a593Smuzhiyun 
3944*4882a593Smuzhiyun 		if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3945*4882a593Smuzhiyun 			rc = -EIO;      /* bad smb */
3946*4882a593Smuzhiyun 			*pbuflen = 0;
3947*4882a593Smuzhiyun 			goto qsec_out;
3948*4882a593Smuzhiyun 		}
3949*4882a593Smuzhiyun 
3950*4882a593Smuzhiyun /* BB check that data area is minimum length and as big as acl_len */
3951*4882a593Smuzhiyun 
3952*4882a593Smuzhiyun 		acl_len = le32_to_cpu(*parm);
3953*4882a593Smuzhiyun 		if (acl_len != *pbuflen) {
3954*4882a593Smuzhiyun 			cifs_dbg(VFS, "acl length %d does not match %d\n",
3955*4882a593Smuzhiyun 				 acl_len, *pbuflen);
3956*4882a593Smuzhiyun 			if (*pbuflen > acl_len)
3957*4882a593Smuzhiyun 				*pbuflen = acl_len;
3958*4882a593Smuzhiyun 		}
3959*4882a593Smuzhiyun 
3960*4882a593Smuzhiyun 		/* check if buffer is big enough for the acl
3961*4882a593Smuzhiyun 		   header followed by the smallest SID */
3962*4882a593Smuzhiyun 		if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3963*4882a593Smuzhiyun 		    (*pbuflen >= 64 * 1024)) {
3964*4882a593Smuzhiyun 			cifs_dbg(VFS, "bad acl length %d\n", *pbuflen);
3965*4882a593Smuzhiyun 			rc = -EINVAL;
3966*4882a593Smuzhiyun 			*pbuflen = 0;
3967*4882a593Smuzhiyun 		} else {
3968*4882a593Smuzhiyun 			*acl_inf = kmemdup(pdata, *pbuflen, GFP_KERNEL);
3969*4882a593Smuzhiyun 			if (*acl_inf == NULL) {
3970*4882a593Smuzhiyun 				*pbuflen = 0;
3971*4882a593Smuzhiyun 				rc = -ENOMEM;
3972*4882a593Smuzhiyun 			}
3973*4882a593Smuzhiyun 		}
3974*4882a593Smuzhiyun 	}
3975*4882a593Smuzhiyun qsec_out:
3976*4882a593Smuzhiyun 	free_rsp_buf(buf_type, rsp_iov.iov_base);
3977*4882a593Smuzhiyun 	return rc;
3978*4882a593Smuzhiyun }
3979*4882a593Smuzhiyun 
3980*4882a593Smuzhiyun int
CIFSSMBSetCIFSACL(const unsigned int xid,struct cifs_tcon * tcon,__u16 fid,struct cifs_ntsd * pntsd,__u32 acllen,int aclflag)3981*4882a593Smuzhiyun CIFSSMBSetCIFSACL(const unsigned int xid, struct cifs_tcon *tcon, __u16 fid,
3982*4882a593Smuzhiyun 			struct cifs_ntsd *pntsd, __u32 acllen, int aclflag)
3983*4882a593Smuzhiyun {
3984*4882a593Smuzhiyun 	__u16 byte_count, param_count, data_count, param_offset, data_offset;
3985*4882a593Smuzhiyun 	int rc = 0;
3986*4882a593Smuzhiyun 	int bytes_returned = 0;
3987*4882a593Smuzhiyun 	SET_SEC_DESC_REQ *pSMB = NULL;
3988*4882a593Smuzhiyun 	void *pSMBr;
3989*4882a593Smuzhiyun 
3990*4882a593Smuzhiyun setCifsAclRetry:
3991*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, &pSMBr);
3992*4882a593Smuzhiyun 	if (rc)
3993*4882a593Smuzhiyun 		return rc;
3994*4882a593Smuzhiyun 
3995*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
3996*4882a593Smuzhiyun 	pSMB->Reserved = 0;
3997*4882a593Smuzhiyun 
3998*4882a593Smuzhiyun 	param_count = 8;
3999*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
4000*4882a593Smuzhiyun 	data_count = acllen;
4001*4882a593Smuzhiyun 	data_offset = param_offset + param_count;
4002*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + param_count;
4003*4882a593Smuzhiyun 
4004*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le32(data_count);
4005*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
4006*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le32(4);
4007*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le32(16384);
4008*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le32(param_count);
4009*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le32(param_offset);
4010*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
4011*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le32(data_offset);
4012*4882a593Smuzhiyun 	pSMB->SetupCount = 0;
4013*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
4014*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
4015*4882a593Smuzhiyun 
4016*4882a593Smuzhiyun 	pSMB->Fid = fid; /* file handle always le */
4017*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
4018*4882a593Smuzhiyun 	pSMB->AclFlags = cpu_to_le32(aclflag);
4019*4882a593Smuzhiyun 
4020*4882a593Smuzhiyun 	if (pntsd && acllen) {
4021*4882a593Smuzhiyun 		memcpy((char *)pSMBr + offsetof(struct smb_hdr, Protocol) +
4022*4882a593Smuzhiyun 				data_offset, pntsd, acllen);
4023*4882a593Smuzhiyun 		inc_rfc1001_len(pSMB, byte_count + data_count);
4024*4882a593Smuzhiyun 	} else
4025*4882a593Smuzhiyun 		inc_rfc1001_len(pSMB, byte_count);
4026*4882a593Smuzhiyun 
4027*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4028*4882a593Smuzhiyun 		(struct smb_hdr *) pSMBr, &bytes_returned, 0);
4029*4882a593Smuzhiyun 
4030*4882a593Smuzhiyun 	cifs_dbg(FYI, "SetCIFSACL bytes_returned: %d, rc: %d\n",
4031*4882a593Smuzhiyun 		 bytes_returned, rc);
4032*4882a593Smuzhiyun 	if (rc)
4033*4882a593Smuzhiyun 		cifs_dbg(FYI, "Set CIFS ACL returned %d\n", rc);
4034*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
4035*4882a593Smuzhiyun 
4036*4882a593Smuzhiyun 	if (rc == -EAGAIN)
4037*4882a593Smuzhiyun 		goto setCifsAclRetry;
4038*4882a593Smuzhiyun 
4039*4882a593Smuzhiyun 	return (rc);
4040*4882a593Smuzhiyun }
4041*4882a593Smuzhiyun 
4042*4882a593Smuzhiyun 
4043*4882a593Smuzhiyun /* Legacy Query Path Information call for lookup to old servers such
4044*4882a593Smuzhiyun    as Win9x/WinME */
4045*4882a593Smuzhiyun int
SMBQueryInformation(const unsigned int xid,struct cifs_tcon * tcon,const char * search_name,FILE_ALL_INFO * data,const struct nls_table * nls_codepage,int remap)4046*4882a593Smuzhiyun SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon,
4047*4882a593Smuzhiyun 		    const char *search_name, FILE_ALL_INFO *data,
4048*4882a593Smuzhiyun 		    const struct nls_table *nls_codepage, int remap)
4049*4882a593Smuzhiyun {
4050*4882a593Smuzhiyun 	QUERY_INFORMATION_REQ *pSMB;
4051*4882a593Smuzhiyun 	QUERY_INFORMATION_RSP *pSMBr;
4052*4882a593Smuzhiyun 	int rc = 0;
4053*4882a593Smuzhiyun 	int bytes_returned;
4054*4882a593Smuzhiyun 	int name_len;
4055*4882a593Smuzhiyun 
4056*4882a593Smuzhiyun 	cifs_dbg(FYI, "In SMBQPath path %s\n", search_name);
4057*4882a593Smuzhiyun QInfRetry:
4058*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
4059*4882a593Smuzhiyun 		      (void **) &pSMBr);
4060*4882a593Smuzhiyun 	if (rc)
4061*4882a593Smuzhiyun 		return rc;
4062*4882a593Smuzhiyun 
4063*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4064*4882a593Smuzhiyun 		name_len =
4065*4882a593Smuzhiyun 			cifsConvertToUTF16((__le16 *) pSMB->FileName,
4066*4882a593Smuzhiyun 					   search_name, PATH_MAX, nls_codepage,
4067*4882a593Smuzhiyun 					   remap);
4068*4882a593Smuzhiyun 		name_len++;     /* trailing null */
4069*4882a593Smuzhiyun 		name_len *= 2;
4070*4882a593Smuzhiyun 	} else {
4071*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, search_name);
4072*4882a593Smuzhiyun 	}
4073*4882a593Smuzhiyun 	pSMB->BufferFormat = 0x04;
4074*4882a593Smuzhiyun 	name_len++; /* account for buffer type byte */
4075*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, (__u16)name_len);
4076*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(name_len);
4077*4882a593Smuzhiyun 
4078*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4079*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4080*4882a593Smuzhiyun 	if (rc) {
4081*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in QueryInfo = %d\n", rc);
4082*4882a593Smuzhiyun 	} else if (data) {
4083*4882a593Smuzhiyun 		struct timespec64 ts;
4084*4882a593Smuzhiyun 		__u32 time = le32_to_cpu(pSMBr->last_write_time);
4085*4882a593Smuzhiyun 
4086*4882a593Smuzhiyun 		/* decode response */
4087*4882a593Smuzhiyun 		/* BB FIXME - add time zone adjustment BB */
4088*4882a593Smuzhiyun 		memset(data, 0, sizeof(FILE_ALL_INFO));
4089*4882a593Smuzhiyun 		ts.tv_nsec = 0;
4090*4882a593Smuzhiyun 		ts.tv_sec = time;
4091*4882a593Smuzhiyun 		/* decode time fields */
4092*4882a593Smuzhiyun 		data->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
4093*4882a593Smuzhiyun 		data->LastWriteTime = data->ChangeTime;
4094*4882a593Smuzhiyun 		data->LastAccessTime = 0;
4095*4882a593Smuzhiyun 		data->AllocationSize =
4096*4882a593Smuzhiyun 			cpu_to_le64(le32_to_cpu(pSMBr->size));
4097*4882a593Smuzhiyun 		data->EndOfFile = data->AllocationSize;
4098*4882a593Smuzhiyun 		data->Attributes =
4099*4882a593Smuzhiyun 			cpu_to_le32(le16_to_cpu(pSMBr->attr));
4100*4882a593Smuzhiyun 	} else
4101*4882a593Smuzhiyun 		rc = -EIO; /* bad buffer passed in */
4102*4882a593Smuzhiyun 
4103*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
4104*4882a593Smuzhiyun 
4105*4882a593Smuzhiyun 	if (rc == -EAGAIN)
4106*4882a593Smuzhiyun 		goto QInfRetry;
4107*4882a593Smuzhiyun 
4108*4882a593Smuzhiyun 	return rc;
4109*4882a593Smuzhiyun }
4110*4882a593Smuzhiyun 
4111*4882a593Smuzhiyun int
CIFSSMBQFileInfo(const unsigned int xid,struct cifs_tcon * tcon,u16 netfid,FILE_ALL_INFO * pFindData)4112*4882a593Smuzhiyun CIFSSMBQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
4113*4882a593Smuzhiyun 		 u16 netfid, FILE_ALL_INFO *pFindData)
4114*4882a593Smuzhiyun {
4115*4882a593Smuzhiyun 	struct smb_t2_qfi_req *pSMB = NULL;
4116*4882a593Smuzhiyun 	struct smb_t2_qfi_rsp *pSMBr = NULL;
4117*4882a593Smuzhiyun 	int rc = 0;
4118*4882a593Smuzhiyun 	int bytes_returned;
4119*4882a593Smuzhiyun 	__u16 params, byte_count;
4120*4882a593Smuzhiyun 
4121*4882a593Smuzhiyun QFileInfoRetry:
4122*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4123*4882a593Smuzhiyun 		      (void **) &pSMBr);
4124*4882a593Smuzhiyun 	if (rc)
4125*4882a593Smuzhiyun 		return rc;
4126*4882a593Smuzhiyun 
4127*4882a593Smuzhiyun 	params = 2 /* level */ + 2 /* fid */;
4128*4882a593Smuzhiyun 	pSMB->t2.TotalDataCount = 0;
4129*4882a593Smuzhiyun 	pSMB->t2.MaxParameterCount = cpu_to_le16(4);
4130*4882a593Smuzhiyun 	/* BB find exact max data count below from sess structure BB */
4131*4882a593Smuzhiyun 	pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
4132*4882a593Smuzhiyun 	pSMB->t2.MaxSetupCount = 0;
4133*4882a593Smuzhiyun 	pSMB->t2.Reserved = 0;
4134*4882a593Smuzhiyun 	pSMB->t2.Flags = 0;
4135*4882a593Smuzhiyun 	pSMB->t2.Timeout = 0;
4136*4882a593Smuzhiyun 	pSMB->t2.Reserved2 = 0;
4137*4882a593Smuzhiyun 	pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
4138*4882a593Smuzhiyun 					       Fid) - 4);
4139*4882a593Smuzhiyun 	pSMB->t2.DataCount = 0;
4140*4882a593Smuzhiyun 	pSMB->t2.DataOffset = 0;
4141*4882a593Smuzhiyun 	pSMB->t2.SetupCount = 1;
4142*4882a593Smuzhiyun 	pSMB->t2.Reserved3 = 0;
4143*4882a593Smuzhiyun 	pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
4144*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
4145*4882a593Smuzhiyun 	pSMB->t2.TotalParameterCount = cpu_to_le16(params);
4146*4882a593Smuzhiyun 	pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
4147*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
4148*4882a593Smuzhiyun 	pSMB->Pad = 0;
4149*4882a593Smuzhiyun 	pSMB->Fid = netfid;
4150*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
4151*4882a593Smuzhiyun 	pSMB->t2.ByteCount = cpu_to_le16(byte_count);
4152*4882a593Smuzhiyun 
4153*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4154*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4155*4882a593Smuzhiyun 	if (rc) {
4156*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in QFileInfo = %d\n", rc);
4157*4882a593Smuzhiyun 	} else {		/* decode response */
4158*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4159*4882a593Smuzhiyun 
4160*4882a593Smuzhiyun 		if (rc) /* BB add auto retry on EOPNOTSUPP? */
4161*4882a593Smuzhiyun 			rc = -EIO;
4162*4882a593Smuzhiyun 		else if (get_bcc(&pSMBr->hdr) < 40)
4163*4882a593Smuzhiyun 			rc = -EIO;	/* bad smb */
4164*4882a593Smuzhiyun 		else if (pFindData) {
4165*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4166*4882a593Smuzhiyun 			memcpy((char *) pFindData,
4167*4882a593Smuzhiyun 			       (char *) &pSMBr->hdr.Protocol +
4168*4882a593Smuzhiyun 			       data_offset, sizeof(FILE_ALL_INFO));
4169*4882a593Smuzhiyun 		} else
4170*4882a593Smuzhiyun 		    rc = -ENOMEM;
4171*4882a593Smuzhiyun 	}
4172*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
4173*4882a593Smuzhiyun 	if (rc == -EAGAIN)
4174*4882a593Smuzhiyun 		goto QFileInfoRetry;
4175*4882a593Smuzhiyun 
4176*4882a593Smuzhiyun 	return rc;
4177*4882a593Smuzhiyun }
4178*4882a593Smuzhiyun 
4179*4882a593Smuzhiyun int
CIFSSMBQPathInfo(const unsigned int xid,struct cifs_tcon * tcon,const char * search_name,FILE_ALL_INFO * data,int legacy,const struct nls_table * nls_codepage,int remap)4180*4882a593Smuzhiyun CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
4181*4882a593Smuzhiyun 		 const char *search_name, FILE_ALL_INFO *data,
4182*4882a593Smuzhiyun 		 int legacy /* old style infolevel */,
4183*4882a593Smuzhiyun 		 const struct nls_table *nls_codepage, int remap)
4184*4882a593Smuzhiyun {
4185*4882a593Smuzhiyun 	/* level 263 SMB_QUERY_FILE_ALL_INFO */
4186*4882a593Smuzhiyun 	TRANSACTION2_QPI_REQ *pSMB = NULL;
4187*4882a593Smuzhiyun 	TRANSACTION2_QPI_RSP *pSMBr = NULL;
4188*4882a593Smuzhiyun 	int rc = 0;
4189*4882a593Smuzhiyun 	int bytes_returned;
4190*4882a593Smuzhiyun 	int name_len;
4191*4882a593Smuzhiyun 	__u16 params, byte_count;
4192*4882a593Smuzhiyun 
4193*4882a593Smuzhiyun 	/* cifs_dbg(FYI, "In QPathInfo path %s\n", search_name); */
4194*4882a593Smuzhiyun QPathInfoRetry:
4195*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4196*4882a593Smuzhiyun 		      (void **) &pSMBr);
4197*4882a593Smuzhiyun 	if (rc)
4198*4882a593Smuzhiyun 		return rc;
4199*4882a593Smuzhiyun 
4200*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4201*4882a593Smuzhiyun 		name_len =
4202*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->FileName, search_name,
4203*4882a593Smuzhiyun 				       PATH_MAX, nls_codepage, remap);
4204*4882a593Smuzhiyun 		name_len++;	/* trailing null */
4205*4882a593Smuzhiyun 		name_len *= 2;
4206*4882a593Smuzhiyun 	} else {
4207*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, search_name);
4208*4882a593Smuzhiyun 	}
4209*4882a593Smuzhiyun 
4210*4882a593Smuzhiyun 	params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
4211*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
4212*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
4213*4882a593Smuzhiyun 	/* BB find exact max SMB PDU from sess structure BB */
4214*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(4000);
4215*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
4216*4882a593Smuzhiyun 	pSMB->Reserved = 0;
4217*4882a593Smuzhiyun 	pSMB->Flags = 0;
4218*4882a593Smuzhiyun 	pSMB->Timeout = 0;
4219*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
4220*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(
4221*4882a593Smuzhiyun 	struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
4222*4882a593Smuzhiyun 	pSMB->DataCount = 0;
4223*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
4224*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
4225*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
4226*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
4227*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
4228*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
4229*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
4230*4882a593Smuzhiyun 	if (legacy)
4231*4882a593Smuzhiyun 		pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
4232*4882a593Smuzhiyun 	else
4233*4882a593Smuzhiyun 		pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
4234*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
4235*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
4236*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
4237*4882a593Smuzhiyun 
4238*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4239*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4240*4882a593Smuzhiyun 	if (rc) {
4241*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc);
4242*4882a593Smuzhiyun 	} else {		/* decode response */
4243*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4244*4882a593Smuzhiyun 
4245*4882a593Smuzhiyun 		if (rc) /* BB add auto retry on EOPNOTSUPP? */
4246*4882a593Smuzhiyun 			rc = -EIO;
4247*4882a593Smuzhiyun 		else if (!legacy && get_bcc(&pSMBr->hdr) < 40)
4248*4882a593Smuzhiyun 			rc = -EIO;	/* bad smb */
4249*4882a593Smuzhiyun 		else if (legacy && get_bcc(&pSMBr->hdr) < 24)
4250*4882a593Smuzhiyun 			rc = -EIO;  /* 24 or 26 expected but we do not read
4251*4882a593Smuzhiyun 					last field */
4252*4882a593Smuzhiyun 		else if (data) {
4253*4882a593Smuzhiyun 			int size;
4254*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4255*4882a593Smuzhiyun 
4256*4882a593Smuzhiyun 			/*
4257*4882a593Smuzhiyun 			 * On legacy responses we do not read the last field,
4258*4882a593Smuzhiyun 			 * EAsize, fortunately since it varies by subdialect and
4259*4882a593Smuzhiyun 			 * also note it differs on Set vs Get, ie two bytes or 4
4260*4882a593Smuzhiyun 			 * bytes depending but we don't care here.
4261*4882a593Smuzhiyun 			 */
4262*4882a593Smuzhiyun 			if (legacy)
4263*4882a593Smuzhiyun 				size = sizeof(FILE_INFO_STANDARD);
4264*4882a593Smuzhiyun 			else
4265*4882a593Smuzhiyun 				size = sizeof(FILE_ALL_INFO);
4266*4882a593Smuzhiyun 			memcpy((char *) data, (char *) &pSMBr->hdr.Protocol +
4267*4882a593Smuzhiyun 			       data_offset, size);
4268*4882a593Smuzhiyun 		} else
4269*4882a593Smuzhiyun 		    rc = -ENOMEM;
4270*4882a593Smuzhiyun 	}
4271*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
4272*4882a593Smuzhiyun 	if (rc == -EAGAIN)
4273*4882a593Smuzhiyun 		goto QPathInfoRetry;
4274*4882a593Smuzhiyun 
4275*4882a593Smuzhiyun 	return rc;
4276*4882a593Smuzhiyun }
4277*4882a593Smuzhiyun 
4278*4882a593Smuzhiyun int
CIFSSMBUnixQFileInfo(const unsigned int xid,struct cifs_tcon * tcon,u16 netfid,FILE_UNIX_BASIC_INFO * pFindData)4279*4882a593Smuzhiyun CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
4280*4882a593Smuzhiyun 		 u16 netfid, FILE_UNIX_BASIC_INFO *pFindData)
4281*4882a593Smuzhiyun {
4282*4882a593Smuzhiyun 	struct smb_t2_qfi_req *pSMB = NULL;
4283*4882a593Smuzhiyun 	struct smb_t2_qfi_rsp *pSMBr = NULL;
4284*4882a593Smuzhiyun 	int rc = 0;
4285*4882a593Smuzhiyun 	int bytes_returned;
4286*4882a593Smuzhiyun 	__u16 params, byte_count;
4287*4882a593Smuzhiyun 
4288*4882a593Smuzhiyun UnixQFileInfoRetry:
4289*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4290*4882a593Smuzhiyun 		      (void **) &pSMBr);
4291*4882a593Smuzhiyun 	if (rc)
4292*4882a593Smuzhiyun 		return rc;
4293*4882a593Smuzhiyun 
4294*4882a593Smuzhiyun 	params = 2 /* level */ + 2 /* fid */;
4295*4882a593Smuzhiyun 	pSMB->t2.TotalDataCount = 0;
4296*4882a593Smuzhiyun 	pSMB->t2.MaxParameterCount = cpu_to_le16(4);
4297*4882a593Smuzhiyun 	/* BB find exact max data count below from sess structure BB */
4298*4882a593Smuzhiyun 	pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
4299*4882a593Smuzhiyun 	pSMB->t2.MaxSetupCount = 0;
4300*4882a593Smuzhiyun 	pSMB->t2.Reserved = 0;
4301*4882a593Smuzhiyun 	pSMB->t2.Flags = 0;
4302*4882a593Smuzhiyun 	pSMB->t2.Timeout = 0;
4303*4882a593Smuzhiyun 	pSMB->t2.Reserved2 = 0;
4304*4882a593Smuzhiyun 	pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
4305*4882a593Smuzhiyun 					       Fid) - 4);
4306*4882a593Smuzhiyun 	pSMB->t2.DataCount = 0;
4307*4882a593Smuzhiyun 	pSMB->t2.DataOffset = 0;
4308*4882a593Smuzhiyun 	pSMB->t2.SetupCount = 1;
4309*4882a593Smuzhiyun 	pSMB->t2.Reserved3 = 0;
4310*4882a593Smuzhiyun 	pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
4311*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
4312*4882a593Smuzhiyun 	pSMB->t2.TotalParameterCount = cpu_to_le16(params);
4313*4882a593Smuzhiyun 	pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
4314*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
4315*4882a593Smuzhiyun 	pSMB->Pad = 0;
4316*4882a593Smuzhiyun 	pSMB->Fid = netfid;
4317*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
4318*4882a593Smuzhiyun 	pSMB->t2.ByteCount = cpu_to_le16(byte_count);
4319*4882a593Smuzhiyun 
4320*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4321*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4322*4882a593Smuzhiyun 	if (rc) {
4323*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in UnixQFileInfo = %d\n", rc);
4324*4882a593Smuzhiyun 	} else {		/* decode response */
4325*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4326*4882a593Smuzhiyun 
4327*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) {
4328*4882a593Smuzhiyun 			cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n");
4329*4882a593Smuzhiyun 			rc = -EIO;	/* bad smb */
4330*4882a593Smuzhiyun 		} else {
4331*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4332*4882a593Smuzhiyun 			memcpy((char *) pFindData,
4333*4882a593Smuzhiyun 			       (char *) &pSMBr->hdr.Protocol +
4334*4882a593Smuzhiyun 			       data_offset,
4335*4882a593Smuzhiyun 			       sizeof(FILE_UNIX_BASIC_INFO));
4336*4882a593Smuzhiyun 		}
4337*4882a593Smuzhiyun 	}
4338*4882a593Smuzhiyun 
4339*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
4340*4882a593Smuzhiyun 	if (rc == -EAGAIN)
4341*4882a593Smuzhiyun 		goto UnixQFileInfoRetry;
4342*4882a593Smuzhiyun 
4343*4882a593Smuzhiyun 	return rc;
4344*4882a593Smuzhiyun }
4345*4882a593Smuzhiyun 
4346*4882a593Smuzhiyun int
CIFSSMBUnixQPathInfo(const unsigned int xid,struct cifs_tcon * tcon,const unsigned char * searchName,FILE_UNIX_BASIC_INFO * pFindData,const struct nls_table * nls_codepage,int remap)4347*4882a593Smuzhiyun CIFSSMBUnixQPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
4348*4882a593Smuzhiyun 		     const unsigned char *searchName,
4349*4882a593Smuzhiyun 		     FILE_UNIX_BASIC_INFO *pFindData,
4350*4882a593Smuzhiyun 		     const struct nls_table *nls_codepage, int remap)
4351*4882a593Smuzhiyun {
4352*4882a593Smuzhiyun /* SMB_QUERY_FILE_UNIX_BASIC */
4353*4882a593Smuzhiyun 	TRANSACTION2_QPI_REQ *pSMB = NULL;
4354*4882a593Smuzhiyun 	TRANSACTION2_QPI_RSP *pSMBr = NULL;
4355*4882a593Smuzhiyun 	int rc = 0;
4356*4882a593Smuzhiyun 	int bytes_returned = 0;
4357*4882a593Smuzhiyun 	int name_len;
4358*4882a593Smuzhiyun 	__u16 params, byte_count;
4359*4882a593Smuzhiyun 
4360*4882a593Smuzhiyun 	cifs_dbg(FYI, "In QPathInfo (Unix) the path %s\n", searchName);
4361*4882a593Smuzhiyun UnixQPathInfoRetry:
4362*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4363*4882a593Smuzhiyun 		      (void **) &pSMBr);
4364*4882a593Smuzhiyun 	if (rc)
4365*4882a593Smuzhiyun 		return rc;
4366*4882a593Smuzhiyun 
4367*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4368*4882a593Smuzhiyun 		name_len =
4369*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName,
4370*4882a593Smuzhiyun 				       PATH_MAX, nls_codepage, remap);
4371*4882a593Smuzhiyun 		name_len++;	/* trailing null */
4372*4882a593Smuzhiyun 		name_len *= 2;
4373*4882a593Smuzhiyun 	} else {
4374*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, searchName);
4375*4882a593Smuzhiyun 	}
4376*4882a593Smuzhiyun 
4377*4882a593Smuzhiyun 	params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
4378*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
4379*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
4380*4882a593Smuzhiyun 	/* BB find exact max SMB PDU from sess structure BB */
4381*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(4000);
4382*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
4383*4882a593Smuzhiyun 	pSMB->Reserved = 0;
4384*4882a593Smuzhiyun 	pSMB->Flags = 0;
4385*4882a593Smuzhiyun 	pSMB->Timeout = 0;
4386*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
4387*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(
4388*4882a593Smuzhiyun 	struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
4389*4882a593Smuzhiyun 	pSMB->DataCount = 0;
4390*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
4391*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
4392*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
4393*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
4394*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
4395*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
4396*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
4397*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
4398*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
4399*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
4400*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
4401*4882a593Smuzhiyun 
4402*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4403*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4404*4882a593Smuzhiyun 	if (rc) {
4405*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in UnixQPathInfo = %d\n", rc);
4406*4882a593Smuzhiyun 	} else {		/* decode response */
4407*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4408*4882a593Smuzhiyun 
4409*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < sizeof(FILE_UNIX_BASIC_INFO)) {
4410*4882a593Smuzhiyun 			cifs_dbg(VFS, "Malformed FILE_UNIX_BASIC_INFO response. Unix Extensions can be disabled on mount by specifying the nosfu mount option.\n");
4411*4882a593Smuzhiyun 			rc = -EIO;	/* bad smb */
4412*4882a593Smuzhiyun 		} else {
4413*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4414*4882a593Smuzhiyun 			memcpy((char *) pFindData,
4415*4882a593Smuzhiyun 			       (char *) &pSMBr->hdr.Protocol +
4416*4882a593Smuzhiyun 			       data_offset,
4417*4882a593Smuzhiyun 			       sizeof(FILE_UNIX_BASIC_INFO));
4418*4882a593Smuzhiyun 		}
4419*4882a593Smuzhiyun 	}
4420*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
4421*4882a593Smuzhiyun 	if (rc == -EAGAIN)
4422*4882a593Smuzhiyun 		goto UnixQPathInfoRetry;
4423*4882a593Smuzhiyun 
4424*4882a593Smuzhiyun 	return rc;
4425*4882a593Smuzhiyun }
4426*4882a593Smuzhiyun 
4427*4882a593Smuzhiyun /* xid, tcon, searchName and codepage are input parms, rest are returned */
4428*4882a593Smuzhiyun int
CIFSFindFirst(const unsigned int xid,struct cifs_tcon * tcon,const char * searchName,struct cifs_sb_info * cifs_sb,__u16 * pnetfid,__u16 search_flags,struct cifs_search_info * psrch_inf,bool msearch)4429*4882a593Smuzhiyun CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
4430*4882a593Smuzhiyun 	      const char *searchName, struct cifs_sb_info *cifs_sb,
4431*4882a593Smuzhiyun 	      __u16 *pnetfid, __u16 search_flags,
4432*4882a593Smuzhiyun 	      struct cifs_search_info *psrch_inf, bool msearch)
4433*4882a593Smuzhiyun {
4434*4882a593Smuzhiyun /* level 257 SMB_ */
4435*4882a593Smuzhiyun 	TRANSACTION2_FFIRST_REQ *pSMB = NULL;
4436*4882a593Smuzhiyun 	TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
4437*4882a593Smuzhiyun 	T2_FFIRST_RSP_PARMS *parms;
4438*4882a593Smuzhiyun 	int rc = 0;
4439*4882a593Smuzhiyun 	int bytes_returned = 0;
4440*4882a593Smuzhiyun 	int name_len, remap;
4441*4882a593Smuzhiyun 	__u16 params, byte_count;
4442*4882a593Smuzhiyun 	struct nls_table *nls_codepage;
4443*4882a593Smuzhiyun 
4444*4882a593Smuzhiyun 	cifs_dbg(FYI, "In FindFirst for %s\n", searchName);
4445*4882a593Smuzhiyun 
4446*4882a593Smuzhiyun findFirstRetry:
4447*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4448*4882a593Smuzhiyun 		      (void **) &pSMBr);
4449*4882a593Smuzhiyun 	if (rc)
4450*4882a593Smuzhiyun 		return rc;
4451*4882a593Smuzhiyun 
4452*4882a593Smuzhiyun 	nls_codepage = cifs_sb->local_nls;
4453*4882a593Smuzhiyun 	remap = cifs_remap(cifs_sb);
4454*4882a593Smuzhiyun 
4455*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4456*4882a593Smuzhiyun 		name_len =
4457*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName,
4458*4882a593Smuzhiyun 				       PATH_MAX, nls_codepage, remap);
4459*4882a593Smuzhiyun 		/* We can not add the asterik earlier in case
4460*4882a593Smuzhiyun 		it got remapped to 0xF03A as if it were part of the
4461*4882a593Smuzhiyun 		directory name instead of a wildcard */
4462*4882a593Smuzhiyun 		name_len *= 2;
4463*4882a593Smuzhiyun 		if (msearch) {
4464*4882a593Smuzhiyun 			pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb);
4465*4882a593Smuzhiyun 			pSMB->FileName[name_len+1] = 0;
4466*4882a593Smuzhiyun 			pSMB->FileName[name_len+2] = '*';
4467*4882a593Smuzhiyun 			pSMB->FileName[name_len+3] = 0;
4468*4882a593Smuzhiyun 			name_len += 4; /* now the trailing null */
4469*4882a593Smuzhiyun 			/* null terminate just in case */
4470*4882a593Smuzhiyun 			pSMB->FileName[name_len] = 0;
4471*4882a593Smuzhiyun 			pSMB->FileName[name_len+1] = 0;
4472*4882a593Smuzhiyun 			name_len += 2;
4473*4882a593Smuzhiyun 		}
4474*4882a593Smuzhiyun 	} else {
4475*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, searchName);
4476*4882a593Smuzhiyun 		if (msearch) {
4477*4882a593Smuzhiyun 			if (WARN_ON_ONCE(name_len > PATH_MAX-2))
4478*4882a593Smuzhiyun 				name_len = PATH_MAX-2;
4479*4882a593Smuzhiyun 			/* overwrite nul byte */
4480*4882a593Smuzhiyun 			pSMB->FileName[name_len-1] = CIFS_DIR_SEP(cifs_sb);
4481*4882a593Smuzhiyun 			pSMB->FileName[name_len] = '*';
4482*4882a593Smuzhiyun 			pSMB->FileName[name_len+1] = 0;
4483*4882a593Smuzhiyun 			name_len += 2;
4484*4882a593Smuzhiyun 		}
4485*4882a593Smuzhiyun 	}
4486*4882a593Smuzhiyun 
4487*4882a593Smuzhiyun 	params = 12 + name_len /* includes null */ ;
4488*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;	/* no EAs */
4489*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(10);
4490*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00);
4491*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
4492*4882a593Smuzhiyun 	pSMB->Reserved = 0;
4493*4882a593Smuzhiyun 	pSMB->Flags = 0;
4494*4882a593Smuzhiyun 	pSMB->Timeout = 0;
4495*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
4496*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
4497*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
4498*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
4499*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(
4500*4882a593Smuzhiyun 	      offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
4501*4882a593Smuzhiyun 		- 4);
4502*4882a593Smuzhiyun 	pSMB->DataCount = 0;
4503*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
4504*4882a593Smuzhiyun 	pSMB->SetupCount = 1;	/* one byte, no need to make endian neutral */
4505*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
4506*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
4507*4882a593Smuzhiyun 	pSMB->SearchAttributes =
4508*4882a593Smuzhiyun 	    cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
4509*4882a593Smuzhiyun 			ATTR_DIRECTORY);
4510*4882a593Smuzhiyun 	pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
4511*4882a593Smuzhiyun 	pSMB->SearchFlags = cpu_to_le16(search_flags);
4512*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
4513*4882a593Smuzhiyun 
4514*4882a593Smuzhiyun 	/* BB what should we set StorageType to? Does it matter? BB */
4515*4882a593Smuzhiyun 	pSMB->SearchStorageType = 0;
4516*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
4517*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
4518*4882a593Smuzhiyun 
4519*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4520*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4521*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_ffirst);
4522*4882a593Smuzhiyun 
4523*4882a593Smuzhiyun 	if (rc) {/* BB add logic to retry regular search if Unix search
4524*4882a593Smuzhiyun 			rejected unexpectedly by server */
4525*4882a593Smuzhiyun 		/* BB Add code to handle unsupported level rc */
4526*4882a593Smuzhiyun 		cifs_dbg(FYI, "Error in FindFirst = %d\n", rc);
4527*4882a593Smuzhiyun 
4528*4882a593Smuzhiyun 		cifs_buf_release(pSMB);
4529*4882a593Smuzhiyun 
4530*4882a593Smuzhiyun 		/* BB eventually could optimize out free and realloc of buf */
4531*4882a593Smuzhiyun 		/*    for this case */
4532*4882a593Smuzhiyun 		if (rc == -EAGAIN)
4533*4882a593Smuzhiyun 			goto findFirstRetry;
4534*4882a593Smuzhiyun 	} else { /* decode response */
4535*4882a593Smuzhiyun 		/* BB remember to free buffer if error BB */
4536*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4537*4882a593Smuzhiyun 		if (rc == 0) {
4538*4882a593Smuzhiyun 			unsigned int lnoff;
4539*4882a593Smuzhiyun 
4540*4882a593Smuzhiyun 			if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
4541*4882a593Smuzhiyun 				psrch_inf->unicode = true;
4542*4882a593Smuzhiyun 			else
4543*4882a593Smuzhiyun 				psrch_inf->unicode = false;
4544*4882a593Smuzhiyun 
4545*4882a593Smuzhiyun 			psrch_inf->ntwrk_buf_start = (char *)pSMBr;
4546*4882a593Smuzhiyun 			psrch_inf->smallBuf = false;
4547*4882a593Smuzhiyun 			psrch_inf->srch_entries_start =
4548*4882a593Smuzhiyun 				(char *) &pSMBr->hdr.Protocol +
4549*4882a593Smuzhiyun 					le16_to_cpu(pSMBr->t2.DataOffset);
4550*4882a593Smuzhiyun 			parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
4551*4882a593Smuzhiyun 			       le16_to_cpu(pSMBr->t2.ParameterOffset));
4552*4882a593Smuzhiyun 
4553*4882a593Smuzhiyun 			if (parms->EndofSearch)
4554*4882a593Smuzhiyun 				psrch_inf->endOfSearch = true;
4555*4882a593Smuzhiyun 			else
4556*4882a593Smuzhiyun 				psrch_inf->endOfSearch = false;
4557*4882a593Smuzhiyun 
4558*4882a593Smuzhiyun 			psrch_inf->entries_in_buffer =
4559*4882a593Smuzhiyun 					le16_to_cpu(parms->SearchCount);
4560*4882a593Smuzhiyun 			psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
4561*4882a593Smuzhiyun 				psrch_inf->entries_in_buffer;
4562*4882a593Smuzhiyun 			lnoff = le16_to_cpu(parms->LastNameOffset);
4563*4882a593Smuzhiyun 			if (CIFSMaxBufSize < lnoff) {
4564*4882a593Smuzhiyun 				cifs_dbg(VFS, "ignoring corrupt resume name\n");
4565*4882a593Smuzhiyun 				psrch_inf->last_entry = NULL;
4566*4882a593Smuzhiyun 				return rc;
4567*4882a593Smuzhiyun 			}
4568*4882a593Smuzhiyun 
4569*4882a593Smuzhiyun 			psrch_inf->last_entry = psrch_inf->srch_entries_start +
4570*4882a593Smuzhiyun 							lnoff;
4571*4882a593Smuzhiyun 
4572*4882a593Smuzhiyun 			if (pnetfid)
4573*4882a593Smuzhiyun 				*pnetfid = parms->SearchHandle;
4574*4882a593Smuzhiyun 		} else {
4575*4882a593Smuzhiyun 			cifs_buf_release(pSMB);
4576*4882a593Smuzhiyun 		}
4577*4882a593Smuzhiyun 	}
4578*4882a593Smuzhiyun 
4579*4882a593Smuzhiyun 	return rc;
4580*4882a593Smuzhiyun }
4581*4882a593Smuzhiyun 
CIFSFindNext(const unsigned int xid,struct cifs_tcon * tcon,__u16 searchHandle,__u16 search_flags,struct cifs_search_info * psrch_inf)4582*4882a593Smuzhiyun int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon,
4583*4882a593Smuzhiyun 		 __u16 searchHandle, __u16 search_flags,
4584*4882a593Smuzhiyun 		 struct cifs_search_info *psrch_inf)
4585*4882a593Smuzhiyun {
4586*4882a593Smuzhiyun 	TRANSACTION2_FNEXT_REQ *pSMB = NULL;
4587*4882a593Smuzhiyun 	TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
4588*4882a593Smuzhiyun 	T2_FNEXT_RSP_PARMS *parms;
4589*4882a593Smuzhiyun 	char *response_data;
4590*4882a593Smuzhiyun 	int rc = 0;
4591*4882a593Smuzhiyun 	int bytes_returned;
4592*4882a593Smuzhiyun 	unsigned int name_len;
4593*4882a593Smuzhiyun 	__u16 params, byte_count;
4594*4882a593Smuzhiyun 
4595*4882a593Smuzhiyun 	cifs_dbg(FYI, "In FindNext\n");
4596*4882a593Smuzhiyun 
4597*4882a593Smuzhiyun 	if (psrch_inf->endOfSearch)
4598*4882a593Smuzhiyun 		return -ENOENT;
4599*4882a593Smuzhiyun 
4600*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4601*4882a593Smuzhiyun 		(void **) &pSMBr);
4602*4882a593Smuzhiyun 	if (rc)
4603*4882a593Smuzhiyun 		return rc;
4604*4882a593Smuzhiyun 
4605*4882a593Smuzhiyun 	params = 14; /* includes 2 bytes of null string, converted to LE below*/
4606*4882a593Smuzhiyun 	byte_count = 0;
4607*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;       /* no EAs */
4608*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(8);
4609*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize & 0xFFFFFF00);
4610*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
4611*4882a593Smuzhiyun 	pSMB->Reserved = 0;
4612*4882a593Smuzhiyun 	pSMB->Flags = 0;
4613*4882a593Smuzhiyun 	pSMB->Timeout = 0;
4614*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
4615*4882a593Smuzhiyun 	pSMB->ParameterOffset =  cpu_to_le16(
4616*4882a593Smuzhiyun 	      offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
4617*4882a593Smuzhiyun 	pSMB->DataCount = 0;
4618*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
4619*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
4620*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
4621*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
4622*4882a593Smuzhiyun 	pSMB->SearchHandle = searchHandle;      /* always kept as le */
4623*4882a593Smuzhiyun 	pSMB->SearchCount =
4624*4882a593Smuzhiyun 		cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
4625*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
4626*4882a593Smuzhiyun 	pSMB->ResumeKey = psrch_inf->resume_key;
4627*4882a593Smuzhiyun 	pSMB->SearchFlags = cpu_to_le16(search_flags);
4628*4882a593Smuzhiyun 
4629*4882a593Smuzhiyun 	name_len = psrch_inf->resume_name_len;
4630*4882a593Smuzhiyun 	params += name_len;
4631*4882a593Smuzhiyun 	if (name_len < PATH_MAX) {
4632*4882a593Smuzhiyun 		memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
4633*4882a593Smuzhiyun 		byte_count += name_len;
4634*4882a593Smuzhiyun 		/* 14 byte parm len above enough for 2 byte null terminator */
4635*4882a593Smuzhiyun 		pSMB->ResumeFileName[name_len] = 0;
4636*4882a593Smuzhiyun 		pSMB->ResumeFileName[name_len+1] = 0;
4637*4882a593Smuzhiyun 	} else {
4638*4882a593Smuzhiyun 		rc = -EINVAL;
4639*4882a593Smuzhiyun 		goto FNext2_err_exit;
4640*4882a593Smuzhiyun 	}
4641*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
4642*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
4643*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
4644*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
4645*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
4646*4882a593Smuzhiyun 
4647*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4648*4882a593Smuzhiyun 			(struct smb_hdr *) pSMBr, &bytes_returned, 0);
4649*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_fnext);
4650*4882a593Smuzhiyun 	if (rc) {
4651*4882a593Smuzhiyun 		if (rc == -EBADF) {
4652*4882a593Smuzhiyun 			psrch_inf->endOfSearch = true;
4653*4882a593Smuzhiyun 			cifs_buf_release(pSMB);
4654*4882a593Smuzhiyun 			rc = 0; /* search probably was closed at end of search*/
4655*4882a593Smuzhiyun 		} else
4656*4882a593Smuzhiyun 			cifs_dbg(FYI, "FindNext returned = %d\n", rc);
4657*4882a593Smuzhiyun 	} else {                /* decode response */
4658*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4659*4882a593Smuzhiyun 
4660*4882a593Smuzhiyun 		if (rc == 0) {
4661*4882a593Smuzhiyun 			unsigned int lnoff;
4662*4882a593Smuzhiyun 
4663*4882a593Smuzhiyun 			/* BB fixme add lock for file (srch_info) struct here */
4664*4882a593Smuzhiyun 			if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
4665*4882a593Smuzhiyun 				psrch_inf->unicode = true;
4666*4882a593Smuzhiyun 			else
4667*4882a593Smuzhiyun 				psrch_inf->unicode = false;
4668*4882a593Smuzhiyun 			response_data = (char *) &pSMBr->hdr.Protocol +
4669*4882a593Smuzhiyun 			       le16_to_cpu(pSMBr->t2.ParameterOffset);
4670*4882a593Smuzhiyun 			parms = (T2_FNEXT_RSP_PARMS *)response_data;
4671*4882a593Smuzhiyun 			response_data = (char *)&pSMBr->hdr.Protocol +
4672*4882a593Smuzhiyun 				le16_to_cpu(pSMBr->t2.DataOffset);
4673*4882a593Smuzhiyun 			if (psrch_inf->smallBuf)
4674*4882a593Smuzhiyun 				cifs_small_buf_release(
4675*4882a593Smuzhiyun 					psrch_inf->ntwrk_buf_start);
4676*4882a593Smuzhiyun 			else
4677*4882a593Smuzhiyun 				cifs_buf_release(psrch_inf->ntwrk_buf_start);
4678*4882a593Smuzhiyun 			psrch_inf->srch_entries_start = response_data;
4679*4882a593Smuzhiyun 			psrch_inf->ntwrk_buf_start = (char *)pSMB;
4680*4882a593Smuzhiyun 			psrch_inf->smallBuf = false;
4681*4882a593Smuzhiyun 			if (parms->EndofSearch)
4682*4882a593Smuzhiyun 				psrch_inf->endOfSearch = true;
4683*4882a593Smuzhiyun 			else
4684*4882a593Smuzhiyun 				psrch_inf->endOfSearch = false;
4685*4882a593Smuzhiyun 			psrch_inf->entries_in_buffer =
4686*4882a593Smuzhiyun 						le16_to_cpu(parms->SearchCount);
4687*4882a593Smuzhiyun 			psrch_inf->index_of_last_entry +=
4688*4882a593Smuzhiyun 				psrch_inf->entries_in_buffer;
4689*4882a593Smuzhiyun 			lnoff = le16_to_cpu(parms->LastNameOffset);
4690*4882a593Smuzhiyun 			if (CIFSMaxBufSize < lnoff) {
4691*4882a593Smuzhiyun 				cifs_dbg(VFS, "ignoring corrupt resume name\n");
4692*4882a593Smuzhiyun 				psrch_inf->last_entry = NULL;
4693*4882a593Smuzhiyun 				return rc;
4694*4882a593Smuzhiyun 			} else
4695*4882a593Smuzhiyun 				psrch_inf->last_entry =
4696*4882a593Smuzhiyun 					psrch_inf->srch_entries_start + lnoff;
4697*4882a593Smuzhiyun 
4698*4882a593Smuzhiyun /*  cifs_dbg(FYI, "fnxt2 entries in buf %d index_of_last %d\n",
4699*4882a593Smuzhiyun     psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */
4700*4882a593Smuzhiyun 
4701*4882a593Smuzhiyun 			/* BB fixme add unlock here */
4702*4882a593Smuzhiyun 		}
4703*4882a593Smuzhiyun 
4704*4882a593Smuzhiyun 	}
4705*4882a593Smuzhiyun 
4706*4882a593Smuzhiyun 	/* BB On error, should we leave previous search buf (and count and
4707*4882a593Smuzhiyun 	last entry fields) intact or free the previous one? */
4708*4882a593Smuzhiyun 
4709*4882a593Smuzhiyun 	/* Note: On -EAGAIN error only caller can retry on handle based calls
4710*4882a593Smuzhiyun 	since file handle passed in no longer valid */
4711*4882a593Smuzhiyun FNext2_err_exit:
4712*4882a593Smuzhiyun 	if (rc != 0)
4713*4882a593Smuzhiyun 		cifs_buf_release(pSMB);
4714*4882a593Smuzhiyun 	return rc;
4715*4882a593Smuzhiyun }
4716*4882a593Smuzhiyun 
4717*4882a593Smuzhiyun int
CIFSFindClose(const unsigned int xid,struct cifs_tcon * tcon,const __u16 searchHandle)4718*4882a593Smuzhiyun CIFSFindClose(const unsigned int xid, struct cifs_tcon *tcon,
4719*4882a593Smuzhiyun 	      const __u16 searchHandle)
4720*4882a593Smuzhiyun {
4721*4882a593Smuzhiyun 	int rc = 0;
4722*4882a593Smuzhiyun 	FINDCLOSE_REQ *pSMB = NULL;
4723*4882a593Smuzhiyun 
4724*4882a593Smuzhiyun 	cifs_dbg(FYI, "In CIFSSMBFindClose\n");
4725*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
4726*4882a593Smuzhiyun 
4727*4882a593Smuzhiyun 	/* no sense returning error if session restarted
4728*4882a593Smuzhiyun 		as file handle has been closed */
4729*4882a593Smuzhiyun 	if (rc == -EAGAIN)
4730*4882a593Smuzhiyun 		return 0;
4731*4882a593Smuzhiyun 	if (rc)
4732*4882a593Smuzhiyun 		return rc;
4733*4882a593Smuzhiyun 
4734*4882a593Smuzhiyun 	pSMB->FileID = searchHandle;
4735*4882a593Smuzhiyun 	pSMB->ByteCount = 0;
4736*4882a593Smuzhiyun 	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
4737*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
4738*4882a593Smuzhiyun 	if (rc)
4739*4882a593Smuzhiyun 		cifs_dbg(VFS, "Send error in FindClose = %d\n", rc);
4740*4882a593Smuzhiyun 
4741*4882a593Smuzhiyun 	cifs_stats_inc(&tcon->stats.cifs_stats.num_fclose);
4742*4882a593Smuzhiyun 
4743*4882a593Smuzhiyun 	/* Since session is dead, search handle closed on server already */
4744*4882a593Smuzhiyun 	if (rc == -EAGAIN)
4745*4882a593Smuzhiyun 		rc = 0;
4746*4882a593Smuzhiyun 
4747*4882a593Smuzhiyun 	return rc;
4748*4882a593Smuzhiyun }
4749*4882a593Smuzhiyun 
4750*4882a593Smuzhiyun int
CIFSGetSrvInodeNumber(const unsigned int xid,struct cifs_tcon * tcon,const char * search_name,__u64 * inode_number,const struct nls_table * nls_codepage,int remap)4751*4882a593Smuzhiyun CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon,
4752*4882a593Smuzhiyun 		      const char *search_name, __u64 *inode_number,
4753*4882a593Smuzhiyun 		      const struct nls_table *nls_codepage, int remap)
4754*4882a593Smuzhiyun {
4755*4882a593Smuzhiyun 	int rc = 0;
4756*4882a593Smuzhiyun 	TRANSACTION2_QPI_REQ *pSMB = NULL;
4757*4882a593Smuzhiyun 	TRANSACTION2_QPI_RSP *pSMBr = NULL;
4758*4882a593Smuzhiyun 	int name_len, bytes_returned;
4759*4882a593Smuzhiyun 	__u16 params, byte_count;
4760*4882a593Smuzhiyun 
4761*4882a593Smuzhiyun 	cifs_dbg(FYI, "In GetSrvInodeNum for %s\n", search_name);
4762*4882a593Smuzhiyun 	if (tcon == NULL)
4763*4882a593Smuzhiyun 		return -ENODEV;
4764*4882a593Smuzhiyun 
4765*4882a593Smuzhiyun GetInodeNumberRetry:
4766*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4767*4882a593Smuzhiyun 		      (void **) &pSMBr);
4768*4882a593Smuzhiyun 	if (rc)
4769*4882a593Smuzhiyun 		return rc;
4770*4882a593Smuzhiyun 
4771*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4772*4882a593Smuzhiyun 		name_len =
4773*4882a593Smuzhiyun 			cifsConvertToUTF16((__le16 *) pSMB->FileName,
4774*4882a593Smuzhiyun 					   search_name, PATH_MAX, nls_codepage,
4775*4882a593Smuzhiyun 					   remap);
4776*4882a593Smuzhiyun 		name_len++;     /* trailing null */
4777*4882a593Smuzhiyun 		name_len *= 2;
4778*4882a593Smuzhiyun 	} else {
4779*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, search_name);
4780*4882a593Smuzhiyun 	}
4781*4882a593Smuzhiyun 
4782*4882a593Smuzhiyun 	params = 2 /* level */  + 4 /* rsrvd */  + name_len /* incl null */ ;
4783*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
4784*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
4785*4882a593Smuzhiyun 	/* BB find exact max data count below from sess structure BB */
4786*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(4000);
4787*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
4788*4882a593Smuzhiyun 	pSMB->Reserved = 0;
4789*4882a593Smuzhiyun 	pSMB->Flags = 0;
4790*4882a593Smuzhiyun 	pSMB->Timeout = 0;
4791*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
4792*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(
4793*4882a593Smuzhiyun 		struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
4794*4882a593Smuzhiyun 	pSMB->DataCount = 0;
4795*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
4796*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
4797*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
4798*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
4799*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
4800*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
4801*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
4802*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
4803*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
4804*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
4805*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
4806*4882a593Smuzhiyun 
4807*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4808*4882a593Smuzhiyun 		(struct smb_hdr *) pSMBr, &bytes_returned, 0);
4809*4882a593Smuzhiyun 	if (rc) {
4810*4882a593Smuzhiyun 		cifs_dbg(FYI, "error %d in QueryInternalInfo\n", rc);
4811*4882a593Smuzhiyun 	} else {
4812*4882a593Smuzhiyun 		/* decode response */
4813*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4814*4882a593Smuzhiyun 		/* BB also check enough total bytes returned */
4815*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < 2)
4816*4882a593Smuzhiyun 			/* If rc should we check for EOPNOSUPP and
4817*4882a593Smuzhiyun 			disable the srvino flag? or in caller? */
4818*4882a593Smuzhiyun 			rc = -EIO;      /* bad smb */
4819*4882a593Smuzhiyun 		else {
4820*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4821*4882a593Smuzhiyun 			__u16 count = le16_to_cpu(pSMBr->t2.DataCount);
4822*4882a593Smuzhiyun 			struct file_internal_info *pfinfo;
4823*4882a593Smuzhiyun 			/* BB Do we need a cast or hash here ? */
4824*4882a593Smuzhiyun 			if (count < 8) {
4825*4882a593Smuzhiyun 				cifs_dbg(FYI, "Invalid size ret in QryIntrnlInf\n");
4826*4882a593Smuzhiyun 				rc = -EIO;
4827*4882a593Smuzhiyun 				goto GetInodeNumOut;
4828*4882a593Smuzhiyun 			}
4829*4882a593Smuzhiyun 			pfinfo = (struct file_internal_info *)
4830*4882a593Smuzhiyun 				(data_offset + (char *) &pSMBr->hdr.Protocol);
4831*4882a593Smuzhiyun 			*inode_number = le64_to_cpu(pfinfo->UniqueId);
4832*4882a593Smuzhiyun 		}
4833*4882a593Smuzhiyun 	}
4834*4882a593Smuzhiyun GetInodeNumOut:
4835*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
4836*4882a593Smuzhiyun 	if (rc == -EAGAIN)
4837*4882a593Smuzhiyun 		goto GetInodeNumberRetry;
4838*4882a593Smuzhiyun 	return rc;
4839*4882a593Smuzhiyun }
4840*4882a593Smuzhiyun 
4841*4882a593Smuzhiyun int
CIFSGetDFSRefer(const unsigned int xid,struct cifs_ses * ses,const char * search_name,struct dfs_info3_param ** target_nodes,unsigned int * num_of_nodes,const struct nls_table * nls_codepage,int remap)4842*4882a593Smuzhiyun CIFSGetDFSRefer(const unsigned int xid, struct cifs_ses *ses,
4843*4882a593Smuzhiyun 		const char *search_name, struct dfs_info3_param **target_nodes,
4844*4882a593Smuzhiyun 		unsigned int *num_of_nodes,
4845*4882a593Smuzhiyun 		const struct nls_table *nls_codepage, int remap)
4846*4882a593Smuzhiyun {
4847*4882a593Smuzhiyun /* TRANS2_GET_DFS_REFERRAL */
4848*4882a593Smuzhiyun 	TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
4849*4882a593Smuzhiyun 	TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
4850*4882a593Smuzhiyun 	int rc = 0;
4851*4882a593Smuzhiyun 	int bytes_returned;
4852*4882a593Smuzhiyun 	int name_len;
4853*4882a593Smuzhiyun 	__u16 params, byte_count;
4854*4882a593Smuzhiyun 	*num_of_nodes = 0;
4855*4882a593Smuzhiyun 	*target_nodes = NULL;
4856*4882a593Smuzhiyun 
4857*4882a593Smuzhiyun 	cifs_dbg(FYI, "In GetDFSRefer the path %s\n", search_name);
4858*4882a593Smuzhiyun 	if (ses == NULL || ses->tcon_ipc == NULL)
4859*4882a593Smuzhiyun 		return -ENODEV;
4860*4882a593Smuzhiyun 
4861*4882a593Smuzhiyun getDFSRetry:
4862*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, ses->tcon_ipc, (void **) &pSMB,
4863*4882a593Smuzhiyun 		      (void **) &pSMBr);
4864*4882a593Smuzhiyun 	if (rc)
4865*4882a593Smuzhiyun 		return rc;
4866*4882a593Smuzhiyun 
4867*4882a593Smuzhiyun 	/* server pointer checked in called function,
4868*4882a593Smuzhiyun 	but should never be null here anyway */
4869*4882a593Smuzhiyun 	pSMB->hdr.Mid = get_next_mid(ses->server);
4870*4882a593Smuzhiyun 	pSMB->hdr.Tid = ses->tcon_ipc->tid;
4871*4882a593Smuzhiyun 	pSMB->hdr.Uid = ses->Suid;
4872*4882a593Smuzhiyun 	if (ses->capabilities & CAP_STATUS32)
4873*4882a593Smuzhiyun 		pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
4874*4882a593Smuzhiyun 	if (ses->capabilities & CAP_DFS)
4875*4882a593Smuzhiyun 		pSMB->hdr.Flags2 |= SMBFLG2_DFS;
4876*4882a593Smuzhiyun 
4877*4882a593Smuzhiyun 	if (ses->capabilities & CAP_UNICODE) {
4878*4882a593Smuzhiyun 		pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
4879*4882a593Smuzhiyun 		name_len =
4880*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->RequestFileName,
4881*4882a593Smuzhiyun 				       search_name, PATH_MAX, nls_codepage,
4882*4882a593Smuzhiyun 				       remap);
4883*4882a593Smuzhiyun 		name_len++;	/* trailing null */
4884*4882a593Smuzhiyun 		name_len *= 2;
4885*4882a593Smuzhiyun 	} else {	/* BB improve the check for buffer overruns BB */
4886*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->RequestFileName, search_name);
4887*4882a593Smuzhiyun 	}
4888*4882a593Smuzhiyun 
4889*4882a593Smuzhiyun 	if (ses->server->sign)
4890*4882a593Smuzhiyun 		pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
4891*4882a593Smuzhiyun 
4892*4882a593Smuzhiyun 	pSMB->hdr.Uid = ses->Suid;
4893*4882a593Smuzhiyun 
4894*4882a593Smuzhiyun 	params = 2 /* level */  + name_len /*includes null */ ;
4895*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
4896*4882a593Smuzhiyun 	pSMB->DataCount = 0;
4897*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
4898*4882a593Smuzhiyun 	pSMB->MaxParameterCount = 0;
4899*4882a593Smuzhiyun 	/* BB find exact max SMB PDU from sess structure BB */
4900*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(4000);
4901*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
4902*4882a593Smuzhiyun 	pSMB->Reserved = 0;
4903*4882a593Smuzhiyun 	pSMB->Flags = 0;
4904*4882a593Smuzhiyun 	pSMB->Timeout = 0;
4905*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
4906*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(
4907*4882a593Smuzhiyun 	  struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
4908*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
4909*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
4910*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
4911*4882a593Smuzhiyun 	byte_count = params + 3 /* pad */ ;
4912*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
4913*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
4914*4882a593Smuzhiyun 	pSMB->MaxReferralLevel = cpu_to_le16(3);
4915*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
4916*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
4917*4882a593Smuzhiyun 
4918*4882a593Smuzhiyun 	rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
4919*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4920*4882a593Smuzhiyun 	if (rc) {
4921*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in GetDFSRefer = %d\n", rc);
4922*4882a593Smuzhiyun 		goto GetDFSRefExit;
4923*4882a593Smuzhiyun 	}
4924*4882a593Smuzhiyun 	rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4925*4882a593Smuzhiyun 
4926*4882a593Smuzhiyun 	/* BB Also check if enough total bytes returned? */
4927*4882a593Smuzhiyun 	if (rc || get_bcc(&pSMBr->hdr) < 17) {
4928*4882a593Smuzhiyun 		rc = -EIO;      /* bad smb */
4929*4882a593Smuzhiyun 		goto GetDFSRefExit;
4930*4882a593Smuzhiyun 	}
4931*4882a593Smuzhiyun 
4932*4882a593Smuzhiyun 	cifs_dbg(FYI, "Decoding GetDFSRefer response BCC: %d  Offset %d\n",
4933*4882a593Smuzhiyun 		 get_bcc(&pSMBr->hdr), le16_to_cpu(pSMBr->t2.DataOffset));
4934*4882a593Smuzhiyun 
4935*4882a593Smuzhiyun 	/* parse returned result into more usable form */
4936*4882a593Smuzhiyun 	rc = parse_dfs_referrals(&pSMBr->dfs_data,
4937*4882a593Smuzhiyun 				 le16_to_cpu(pSMBr->t2.DataCount),
4938*4882a593Smuzhiyun 				 num_of_nodes, target_nodes, nls_codepage,
4939*4882a593Smuzhiyun 				 remap, search_name,
4940*4882a593Smuzhiyun 				 (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) != 0);
4941*4882a593Smuzhiyun 
4942*4882a593Smuzhiyun GetDFSRefExit:
4943*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
4944*4882a593Smuzhiyun 
4945*4882a593Smuzhiyun 	if (rc == -EAGAIN)
4946*4882a593Smuzhiyun 		goto getDFSRetry;
4947*4882a593Smuzhiyun 
4948*4882a593Smuzhiyun 	return rc;
4949*4882a593Smuzhiyun }
4950*4882a593Smuzhiyun 
4951*4882a593Smuzhiyun /* Query File System Info such as free space to old servers such as Win 9x */
4952*4882a593Smuzhiyun int
SMBOldQFSInfo(const unsigned int xid,struct cifs_tcon * tcon,struct kstatfs * FSData)4953*4882a593Smuzhiyun SMBOldQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
4954*4882a593Smuzhiyun 	      struct kstatfs *FSData)
4955*4882a593Smuzhiyun {
4956*4882a593Smuzhiyun /* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4957*4882a593Smuzhiyun 	TRANSACTION2_QFSI_REQ *pSMB = NULL;
4958*4882a593Smuzhiyun 	TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4959*4882a593Smuzhiyun 	FILE_SYSTEM_ALLOC_INFO *response_data;
4960*4882a593Smuzhiyun 	int rc = 0;
4961*4882a593Smuzhiyun 	int bytes_returned = 0;
4962*4882a593Smuzhiyun 	__u16 params, byte_count;
4963*4882a593Smuzhiyun 
4964*4882a593Smuzhiyun 	cifs_dbg(FYI, "OldQFSInfo\n");
4965*4882a593Smuzhiyun oldQFSInfoRetry:
4966*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4967*4882a593Smuzhiyun 		(void **) &pSMBr);
4968*4882a593Smuzhiyun 	if (rc)
4969*4882a593Smuzhiyun 		return rc;
4970*4882a593Smuzhiyun 
4971*4882a593Smuzhiyun 	params = 2;     /* level */
4972*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
4973*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
4974*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
4975*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
4976*4882a593Smuzhiyun 	pSMB->Reserved = 0;
4977*4882a593Smuzhiyun 	pSMB->Flags = 0;
4978*4882a593Smuzhiyun 	pSMB->Timeout = 0;
4979*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
4980*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
4981*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
4982*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
4983*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(
4984*4882a593Smuzhiyun 	struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4985*4882a593Smuzhiyun 	pSMB->DataCount = 0;
4986*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
4987*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
4988*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
4989*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4990*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4991*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
4992*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
4993*4882a593Smuzhiyun 
4994*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4995*4882a593Smuzhiyun 		(struct smb_hdr *) pSMBr, &bytes_returned, 0);
4996*4882a593Smuzhiyun 	if (rc) {
4997*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc);
4998*4882a593Smuzhiyun 	} else {                /* decode response */
4999*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5000*4882a593Smuzhiyun 
5001*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < 18)
5002*4882a593Smuzhiyun 			rc = -EIO;      /* bad smb */
5003*4882a593Smuzhiyun 		else {
5004*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5005*4882a593Smuzhiyun 			cifs_dbg(FYI, "qfsinf resp BCC: %d  Offset %d\n",
5006*4882a593Smuzhiyun 				 get_bcc(&pSMBr->hdr), data_offset);
5007*4882a593Smuzhiyun 
5008*4882a593Smuzhiyun 			response_data = (FILE_SYSTEM_ALLOC_INFO *)
5009*4882a593Smuzhiyun 				(((char *) &pSMBr->hdr.Protocol) + data_offset);
5010*4882a593Smuzhiyun 			FSData->f_bsize =
5011*4882a593Smuzhiyun 				le16_to_cpu(response_data->BytesPerSector) *
5012*4882a593Smuzhiyun 				le32_to_cpu(response_data->
5013*4882a593Smuzhiyun 					SectorsPerAllocationUnit);
5014*4882a593Smuzhiyun 			/*
5015*4882a593Smuzhiyun 			 * much prefer larger but if server doesn't report
5016*4882a593Smuzhiyun 			 * a valid size than 4K is a reasonable minimum
5017*4882a593Smuzhiyun 			 */
5018*4882a593Smuzhiyun 			if (FSData->f_bsize < 512)
5019*4882a593Smuzhiyun 				FSData->f_bsize = 4096;
5020*4882a593Smuzhiyun 
5021*4882a593Smuzhiyun 			FSData->f_blocks =
5022*4882a593Smuzhiyun 			       le32_to_cpu(response_data->TotalAllocationUnits);
5023*4882a593Smuzhiyun 			FSData->f_bfree = FSData->f_bavail =
5024*4882a593Smuzhiyun 				le32_to_cpu(response_data->FreeAllocationUnits);
5025*4882a593Smuzhiyun 			cifs_dbg(FYI, "Blocks: %lld  Free: %lld Block size %ld\n",
5026*4882a593Smuzhiyun 				 (unsigned long long)FSData->f_blocks,
5027*4882a593Smuzhiyun 				 (unsigned long long)FSData->f_bfree,
5028*4882a593Smuzhiyun 				 FSData->f_bsize);
5029*4882a593Smuzhiyun 		}
5030*4882a593Smuzhiyun 	}
5031*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
5032*4882a593Smuzhiyun 
5033*4882a593Smuzhiyun 	if (rc == -EAGAIN)
5034*4882a593Smuzhiyun 		goto oldQFSInfoRetry;
5035*4882a593Smuzhiyun 
5036*4882a593Smuzhiyun 	return rc;
5037*4882a593Smuzhiyun }
5038*4882a593Smuzhiyun 
5039*4882a593Smuzhiyun int
CIFSSMBQFSInfo(const unsigned int xid,struct cifs_tcon * tcon,struct kstatfs * FSData)5040*4882a593Smuzhiyun CIFSSMBQFSInfo(const unsigned int xid, struct cifs_tcon *tcon,
5041*4882a593Smuzhiyun 	       struct kstatfs *FSData)
5042*4882a593Smuzhiyun {
5043*4882a593Smuzhiyun /* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
5044*4882a593Smuzhiyun 	TRANSACTION2_QFSI_REQ *pSMB = NULL;
5045*4882a593Smuzhiyun 	TRANSACTION2_QFSI_RSP *pSMBr = NULL;
5046*4882a593Smuzhiyun 	FILE_SYSTEM_INFO *response_data;
5047*4882a593Smuzhiyun 	int rc = 0;
5048*4882a593Smuzhiyun 	int bytes_returned = 0;
5049*4882a593Smuzhiyun 	__u16 params, byte_count;
5050*4882a593Smuzhiyun 
5051*4882a593Smuzhiyun 	cifs_dbg(FYI, "In QFSInfo\n");
5052*4882a593Smuzhiyun QFSInfoRetry:
5053*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5054*4882a593Smuzhiyun 		      (void **) &pSMBr);
5055*4882a593Smuzhiyun 	if (rc)
5056*4882a593Smuzhiyun 		return rc;
5057*4882a593Smuzhiyun 
5058*4882a593Smuzhiyun 	params = 2;	/* level */
5059*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
5060*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
5061*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
5062*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
5063*4882a593Smuzhiyun 	pSMB->Reserved = 0;
5064*4882a593Smuzhiyun 	pSMB->Flags = 0;
5065*4882a593Smuzhiyun 	pSMB->Timeout = 0;
5066*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
5067*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
5068*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
5069*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
5070*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(
5071*4882a593Smuzhiyun 		struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
5072*4882a593Smuzhiyun 	pSMB->DataCount = 0;
5073*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
5074*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
5075*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
5076*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
5077*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
5078*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
5079*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
5080*4882a593Smuzhiyun 
5081*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5082*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5083*4882a593Smuzhiyun 	if (rc) {
5084*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in QFSInfo = %d\n", rc);
5085*4882a593Smuzhiyun 	} else {		/* decode response */
5086*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5087*4882a593Smuzhiyun 
5088*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < 24)
5089*4882a593Smuzhiyun 			rc = -EIO;	/* bad smb */
5090*4882a593Smuzhiyun 		else {
5091*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5092*4882a593Smuzhiyun 
5093*4882a593Smuzhiyun 			response_data =
5094*4882a593Smuzhiyun 			    (FILE_SYSTEM_INFO
5095*4882a593Smuzhiyun 			     *) (((char *) &pSMBr->hdr.Protocol) +
5096*4882a593Smuzhiyun 				 data_offset);
5097*4882a593Smuzhiyun 			FSData->f_bsize =
5098*4882a593Smuzhiyun 			    le32_to_cpu(response_data->BytesPerSector) *
5099*4882a593Smuzhiyun 			    le32_to_cpu(response_data->
5100*4882a593Smuzhiyun 					SectorsPerAllocationUnit);
5101*4882a593Smuzhiyun 			/*
5102*4882a593Smuzhiyun 			 * much prefer larger but if server doesn't report
5103*4882a593Smuzhiyun 			 * a valid size than 4K is a reasonable minimum
5104*4882a593Smuzhiyun 			 */
5105*4882a593Smuzhiyun 			if (FSData->f_bsize < 512)
5106*4882a593Smuzhiyun 				FSData->f_bsize = 4096;
5107*4882a593Smuzhiyun 
5108*4882a593Smuzhiyun 			FSData->f_blocks =
5109*4882a593Smuzhiyun 			    le64_to_cpu(response_data->TotalAllocationUnits);
5110*4882a593Smuzhiyun 			FSData->f_bfree = FSData->f_bavail =
5111*4882a593Smuzhiyun 			    le64_to_cpu(response_data->FreeAllocationUnits);
5112*4882a593Smuzhiyun 			cifs_dbg(FYI, "Blocks: %lld  Free: %lld Block size %ld\n",
5113*4882a593Smuzhiyun 				 (unsigned long long)FSData->f_blocks,
5114*4882a593Smuzhiyun 				 (unsigned long long)FSData->f_bfree,
5115*4882a593Smuzhiyun 				 FSData->f_bsize);
5116*4882a593Smuzhiyun 		}
5117*4882a593Smuzhiyun 	}
5118*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
5119*4882a593Smuzhiyun 
5120*4882a593Smuzhiyun 	if (rc == -EAGAIN)
5121*4882a593Smuzhiyun 		goto QFSInfoRetry;
5122*4882a593Smuzhiyun 
5123*4882a593Smuzhiyun 	return rc;
5124*4882a593Smuzhiyun }
5125*4882a593Smuzhiyun 
5126*4882a593Smuzhiyun int
CIFSSMBQFSAttributeInfo(const unsigned int xid,struct cifs_tcon * tcon)5127*4882a593Smuzhiyun CIFSSMBQFSAttributeInfo(const unsigned int xid, struct cifs_tcon *tcon)
5128*4882a593Smuzhiyun {
5129*4882a593Smuzhiyun /* level 0x105  SMB_QUERY_FILE_SYSTEM_INFO */
5130*4882a593Smuzhiyun 	TRANSACTION2_QFSI_REQ *pSMB = NULL;
5131*4882a593Smuzhiyun 	TRANSACTION2_QFSI_RSP *pSMBr = NULL;
5132*4882a593Smuzhiyun 	FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
5133*4882a593Smuzhiyun 	int rc = 0;
5134*4882a593Smuzhiyun 	int bytes_returned = 0;
5135*4882a593Smuzhiyun 	__u16 params, byte_count;
5136*4882a593Smuzhiyun 
5137*4882a593Smuzhiyun 	cifs_dbg(FYI, "In QFSAttributeInfo\n");
5138*4882a593Smuzhiyun QFSAttributeRetry:
5139*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5140*4882a593Smuzhiyun 		      (void **) &pSMBr);
5141*4882a593Smuzhiyun 	if (rc)
5142*4882a593Smuzhiyun 		return rc;
5143*4882a593Smuzhiyun 
5144*4882a593Smuzhiyun 	params = 2;	/* level */
5145*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
5146*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
5147*4882a593Smuzhiyun 	/* BB find exact max SMB PDU from sess structure BB */
5148*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
5149*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
5150*4882a593Smuzhiyun 	pSMB->Reserved = 0;
5151*4882a593Smuzhiyun 	pSMB->Flags = 0;
5152*4882a593Smuzhiyun 	pSMB->Timeout = 0;
5153*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
5154*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
5155*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
5156*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
5157*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(
5158*4882a593Smuzhiyun 		struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
5159*4882a593Smuzhiyun 	pSMB->DataCount = 0;
5160*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
5161*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
5162*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
5163*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
5164*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
5165*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
5166*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
5167*4882a593Smuzhiyun 
5168*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5169*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5170*4882a593Smuzhiyun 	if (rc) {
5171*4882a593Smuzhiyun 		cifs_dbg(VFS, "Send error in QFSAttributeInfo = %d\n", rc);
5172*4882a593Smuzhiyun 	} else {		/* decode response */
5173*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5174*4882a593Smuzhiyun 
5175*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < 13) {
5176*4882a593Smuzhiyun 			/* BB also check if enough bytes returned */
5177*4882a593Smuzhiyun 			rc = -EIO;	/* bad smb */
5178*4882a593Smuzhiyun 		} else {
5179*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5180*4882a593Smuzhiyun 			response_data =
5181*4882a593Smuzhiyun 			    (FILE_SYSTEM_ATTRIBUTE_INFO
5182*4882a593Smuzhiyun 			     *) (((char *) &pSMBr->hdr.Protocol) +
5183*4882a593Smuzhiyun 				 data_offset);
5184*4882a593Smuzhiyun 			memcpy(&tcon->fsAttrInfo, response_data,
5185*4882a593Smuzhiyun 			       sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
5186*4882a593Smuzhiyun 		}
5187*4882a593Smuzhiyun 	}
5188*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
5189*4882a593Smuzhiyun 
5190*4882a593Smuzhiyun 	if (rc == -EAGAIN)
5191*4882a593Smuzhiyun 		goto QFSAttributeRetry;
5192*4882a593Smuzhiyun 
5193*4882a593Smuzhiyun 	return rc;
5194*4882a593Smuzhiyun }
5195*4882a593Smuzhiyun 
5196*4882a593Smuzhiyun int
CIFSSMBQFSDeviceInfo(const unsigned int xid,struct cifs_tcon * tcon)5197*4882a593Smuzhiyun CIFSSMBQFSDeviceInfo(const unsigned int xid, struct cifs_tcon *tcon)
5198*4882a593Smuzhiyun {
5199*4882a593Smuzhiyun /* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
5200*4882a593Smuzhiyun 	TRANSACTION2_QFSI_REQ *pSMB = NULL;
5201*4882a593Smuzhiyun 	TRANSACTION2_QFSI_RSP *pSMBr = NULL;
5202*4882a593Smuzhiyun 	FILE_SYSTEM_DEVICE_INFO *response_data;
5203*4882a593Smuzhiyun 	int rc = 0;
5204*4882a593Smuzhiyun 	int bytes_returned = 0;
5205*4882a593Smuzhiyun 	__u16 params, byte_count;
5206*4882a593Smuzhiyun 
5207*4882a593Smuzhiyun 	cifs_dbg(FYI, "In QFSDeviceInfo\n");
5208*4882a593Smuzhiyun QFSDeviceRetry:
5209*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5210*4882a593Smuzhiyun 		      (void **) &pSMBr);
5211*4882a593Smuzhiyun 	if (rc)
5212*4882a593Smuzhiyun 		return rc;
5213*4882a593Smuzhiyun 
5214*4882a593Smuzhiyun 	params = 2;	/* level */
5215*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
5216*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
5217*4882a593Smuzhiyun 	/* BB find exact max SMB PDU from sess structure BB */
5218*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
5219*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
5220*4882a593Smuzhiyun 	pSMB->Reserved = 0;
5221*4882a593Smuzhiyun 	pSMB->Flags = 0;
5222*4882a593Smuzhiyun 	pSMB->Timeout = 0;
5223*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
5224*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
5225*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
5226*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
5227*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(
5228*4882a593Smuzhiyun 		struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
5229*4882a593Smuzhiyun 
5230*4882a593Smuzhiyun 	pSMB->DataCount = 0;
5231*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
5232*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
5233*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
5234*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
5235*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
5236*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
5237*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
5238*4882a593Smuzhiyun 
5239*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5240*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5241*4882a593Smuzhiyun 	if (rc) {
5242*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in QFSDeviceInfo = %d\n", rc);
5243*4882a593Smuzhiyun 	} else {		/* decode response */
5244*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5245*4882a593Smuzhiyun 
5246*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) <
5247*4882a593Smuzhiyun 			  sizeof(FILE_SYSTEM_DEVICE_INFO))
5248*4882a593Smuzhiyun 			rc = -EIO;	/* bad smb */
5249*4882a593Smuzhiyun 		else {
5250*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5251*4882a593Smuzhiyun 			response_data =
5252*4882a593Smuzhiyun 			    (FILE_SYSTEM_DEVICE_INFO *)
5253*4882a593Smuzhiyun 				(((char *) &pSMBr->hdr.Protocol) +
5254*4882a593Smuzhiyun 				 data_offset);
5255*4882a593Smuzhiyun 			memcpy(&tcon->fsDevInfo, response_data,
5256*4882a593Smuzhiyun 			       sizeof(FILE_SYSTEM_DEVICE_INFO));
5257*4882a593Smuzhiyun 		}
5258*4882a593Smuzhiyun 	}
5259*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
5260*4882a593Smuzhiyun 
5261*4882a593Smuzhiyun 	if (rc == -EAGAIN)
5262*4882a593Smuzhiyun 		goto QFSDeviceRetry;
5263*4882a593Smuzhiyun 
5264*4882a593Smuzhiyun 	return rc;
5265*4882a593Smuzhiyun }
5266*4882a593Smuzhiyun 
5267*4882a593Smuzhiyun int
CIFSSMBQFSUnixInfo(const unsigned int xid,struct cifs_tcon * tcon)5268*4882a593Smuzhiyun CIFSSMBQFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon)
5269*4882a593Smuzhiyun {
5270*4882a593Smuzhiyun /* level 0x200  SMB_QUERY_CIFS_UNIX_INFO */
5271*4882a593Smuzhiyun 	TRANSACTION2_QFSI_REQ *pSMB = NULL;
5272*4882a593Smuzhiyun 	TRANSACTION2_QFSI_RSP *pSMBr = NULL;
5273*4882a593Smuzhiyun 	FILE_SYSTEM_UNIX_INFO *response_data;
5274*4882a593Smuzhiyun 	int rc = 0;
5275*4882a593Smuzhiyun 	int bytes_returned = 0;
5276*4882a593Smuzhiyun 	__u16 params, byte_count;
5277*4882a593Smuzhiyun 
5278*4882a593Smuzhiyun 	cifs_dbg(FYI, "In QFSUnixInfo\n");
5279*4882a593Smuzhiyun QFSUnixRetry:
5280*4882a593Smuzhiyun 	rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
5281*4882a593Smuzhiyun 				   (void **) &pSMB, (void **) &pSMBr);
5282*4882a593Smuzhiyun 	if (rc)
5283*4882a593Smuzhiyun 		return rc;
5284*4882a593Smuzhiyun 
5285*4882a593Smuzhiyun 	params = 2;	/* level */
5286*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
5287*4882a593Smuzhiyun 	pSMB->DataCount = 0;
5288*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
5289*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
5290*4882a593Smuzhiyun 	/* BB find exact max SMB PDU from sess structure BB */
5291*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(100);
5292*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
5293*4882a593Smuzhiyun 	pSMB->Reserved = 0;
5294*4882a593Smuzhiyun 	pSMB->Flags = 0;
5295*4882a593Smuzhiyun 	pSMB->Timeout = 0;
5296*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
5297*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
5298*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
5299*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
5300*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
5301*4882a593Smuzhiyun 			smb_com_transaction2_qfsi_req, InformationLevel) - 4);
5302*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
5303*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
5304*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
5305*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
5306*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
5307*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
5308*4882a593Smuzhiyun 
5309*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5310*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5311*4882a593Smuzhiyun 	if (rc) {
5312*4882a593Smuzhiyun 		cifs_dbg(VFS, "Send error in QFSUnixInfo = %d\n", rc);
5313*4882a593Smuzhiyun 	} else {		/* decode response */
5314*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5315*4882a593Smuzhiyun 
5316*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < 13) {
5317*4882a593Smuzhiyun 			rc = -EIO;	/* bad smb */
5318*4882a593Smuzhiyun 		} else {
5319*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5320*4882a593Smuzhiyun 			response_data =
5321*4882a593Smuzhiyun 			    (FILE_SYSTEM_UNIX_INFO
5322*4882a593Smuzhiyun 			     *) (((char *) &pSMBr->hdr.Protocol) +
5323*4882a593Smuzhiyun 				 data_offset);
5324*4882a593Smuzhiyun 			memcpy(&tcon->fsUnixInfo, response_data,
5325*4882a593Smuzhiyun 			       sizeof(FILE_SYSTEM_UNIX_INFO));
5326*4882a593Smuzhiyun 		}
5327*4882a593Smuzhiyun 	}
5328*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
5329*4882a593Smuzhiyun 
5330*4882a593Smuzhiyun 	if (rc == -EAGAIN)
5331*4882a593Smuzhiyun 		goto QFSUnixRetry;
5332*4882a593Smuzhiyun 
5333*4882a593Smuzhiyun 
5334*4882a593Smuzhiyun 	return rc;
5335*4882a593Smuzhiyun }
5336*4882a593Smuzhiyun 
5337*4882a593Smuzhiyun int
CIFSSMBSetFSUnixInfo(const unsigned int xid,struct cifs_tcon * tcon,__u64 cap)5338*4882a593Smuzhiyun CIFSSMBSetFSUnixInfo(const unsigned int xid, struct cifs_tcon *tcon, __u64 cap)
5339*4882a593Smuzhiyun {
5340*4882a593Smuzhiyun /* level 0x200  SMB_SET_CIFS_UNIX_INFO */
5341*4882a593Smuzhiyun 	TRANSACTION2_SETFSI_REQ *pSMB = NULL;
5342*4882a593Smuzhiyun 	TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
5343*4882a593Smuzhiyun 	int rc = 0;
5344*4882a593Smuzhiyun 	int bytes_returned = 0;
5345*4882a593Smuzhiyun 	__u16 params, param_offset, offset, byte_count;
5346*4882a593Smuzhiyun 
5347*4882a593Smuzhiyun 	cifs_dbg(FYI, "In SETFSUnixInfo\n");
5348*4882a593Smuzhiyun SETFSUnixRetry:
5349*4882a593Smuzhiyun 	/* BB switch to small buf init to save memory */
5350*4882a593Smuzhiyun 	rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
5351*4882a593Smuzhiyun 					(void **) &pSMB, (void **) &pSMBr);
5352*4882a593Smuzhiyun 	if (rc)
5353*4882a593Smuzhiyun 		return rc;
5354*4882a593Smuzhiyun 
5355*4882a593Smuzhiyun 	params = 4;	/* 2 bytes zero followed by info level. */
5356*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
5357*4882a593Smuzhiyun 	pSMB->Reserved = 0;
5358*4882a593Smuzhiyun 	pSMB->Flags = 0;
5359*4882a593Smuzhiyun 	pSMB->Timeout = 0;
5360*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
5361*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
5362*4882a593Smuzhiyun 				- 4;
5363*4882a593Smuzhiyun 	offset = param_offset + params;
5364*4882a593Smuzhiyun 
5365*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(4);
5366*4882a593Smuzhiyun 	/* BB find exact max SMB PDU from sess structure BB */
5367*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(100);
5368*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
5369*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
5370*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
5371*4882a593Smuzhiyun 	byte_count = 1 /* pad */ + params + 12;
5372*4882a593Smuzhiyun 
5373*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(12);
5374*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
5375*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
5376*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
5377*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
5378*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
5379*4882a593Smuzhiyun 
5380*4882a593Smuzhiyun 	/* Params. */
5381*4882a593Smuzhiyun 	pSMB->FileNum = 0;
5382*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
5383*4882a593Smuzhiyun 
5384*4882a593Smuzhiyun 	/* Data. */
5385*4882a593Smuzhiyun 	pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
5386*4882a593Smuzhiyun 	pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
5387*4882a593Smuzhiyun 	pSMB->ClientUnixCap = cpu_to_le64(cap);
5388*4882a593Smuzhiyun 
5389*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
5390*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
5391*4882a593Smuzhiyun 
5392*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5393*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5394*4882a593Smuzhiyun 	if (rc) {
5395*4882a593Smuzhiyun 		cifs_dbg(VFS, "Send error in SETFSUnixInfo = %d\n", rc);
5396*4882a593Smuzhiyun 	} else {		/* decode response */
5397*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5398*4882a593Smuzhiyun 		if (rc)
5399*4882a593Smuzhiyun 			rc = -EIO;	/* bad smb */
5400*4882a593Smuzhiyun 	}
5401*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
5402*4882a593Smuzhiyun 
5403*4882a593Smuzhiyun 	if (rc == -EAGAIN)
5404*4882a593Smuzhiyun 		goto SETFSUnixRetry;
5405*4882a593Smuzhiyun 
5406*4882a593Smuzhiyun 	return rc;
5407*4882a593Smuzhiyun }
5408*4882a593Smuzhiyun 
5409*4882a593Smuzhiyun 
5410*4882a593Smuzhiyun 
5411*4882a593Smuzhiyun int
CIFSSMBQFSPosixInfo(const unsigned int xid,struct cifs_tcon * tcon,struct kstatfs * FSData)5412*4882a593Smuzhiyun CIFSSMBQFSPosixInfo(const unsigned int xid, struct cifs_tcon *tcon,
5413*4882a593Smuzhiyun 		   struct kstatfs *FSData)
5414*4882a593Smuzhiyun {
5415*4882a593Smuzhiyun /* level 0x201  SMB_QUERY_CIFS_POSIX_INFO */
5416*4882a593Smuzhiyun 	TRANSACTION2_QFSI_REQ *pSMB = NULL;
5417*4882a593Smuzhiyun 	TRANSACTION2_QFSI_RSP *pSMBr = NULL;
5418*4882a593Smuzhiyun 	FILE_SYSTEM_POSIX_INFO *response_data;
5419*4882a593Smuzhiyun 	int rc = 0;
5420*4882a593Smuzhiyun 	int bytes_returned = 0;
5421*4882a593Smuzhiyun 	__u16 params, byte_count;
5422*4882a593Smuzhiyun 
5423*4882a593Smuzhiyun 	cifs_dbg(FYI, "In QFSPosixInfo\n");
5424*4882a593Smuzhiyun QFSPosixRetry:
5425*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5426*4882a593Smuzhiyun 		      (void **) &pSMBr);
5427*4882a593Smuzhiyun 	if (rc)
5428*4882a593Smuzhiyun 		return rc;
5429*4882a593Smuzhiyun 
5430*4882a593Smuzhiyun 	params = 2;	/* level */
5431*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
5432*4882a593Smuzhiyun 	pSMB->DataCount = 0;
5433*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
5434*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
5435*4882a593Smuzhiyun 	/* BB find exact max SMB PDU from sess structure BB */
5436*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(100);
5437*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
5438*4882a593Smuzhiyun 	pSMB->Reserved = 0;
5439*4882a593Smuzhiyun 	pSMB->Flags = 0;
5440*4882a593Smuzhiyun 	pSMB->Timeout = 0;
5441*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
5442*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
5443*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
5444*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
5445*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
5446*4882a593Smuzhiyun 			smb_com_transaction2_qfsi_req, InformationLevel) - 4);
5447*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
5448*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
5449*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
5450*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
5451*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
5452*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
5453*4882a593Smuzhiyun 
5454*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5455*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5456*4882a593Smuzhiyun 	if (rc) {
5457*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in QFSUnixInfo = %d\n", rc);
5458*4882a593Smuzhiyun 	} else {		/* decode response */
5459*4882a593Smuzhiyun 		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5460*4882a593Smuzhiyun 
5461*4882a593Smuzhiyun 		if (rc || get_bcc(&pSMBr->hdr) < 13) {
5462*4882a593Smuzhiyun 			rc = -EIO;	/* bad smb */
5463*4882a593Smuzhiyun 		} else {
5464*4882a593Smuzhiyun 			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5465*4882a593Smuzhiyun 			response_data =
5466*4882a593Smuzhiyun 			    (FILE_SYSTEM_POSIX_INFO
5467*4882a593Smuzhiyun 			     *) (((char *) &pSMBr->hdr.Protocol) +
5468*4882a593Smuzhiyun 				 data_offset);
5469*4882a593Smuzhiyun 			FSData->f_bsize =
5470*4882a593Smuzhiyun 					le32_to_cpu(response_data->BlockSize);
5471*4882a593Smuzhiyun 			/*
5472*4882a593Smuzhiyun 			 * much prefer larger but if server doesn't report
5473*4882a593Smuzhiyun 			 * a valid size than 4K is a reasonable minimum
5474*4882a593Smuzhiyun 			 */
5475*4882a593Smuzhiyun 			if (FSData->f_bsize < 512)
5476*4882a593Smuzhiyun 				FSData->f_bsize = 4096;
5477*4882a593Smuzhiyun 
5478*4882a593Smuzhiyun 			FSData->f_blocks =
5479*4882a593Smuzhiyun 					le64_to_cpu(response_data->TotalBlocks);
5480*4882a593Smuzhiyun 			FSData->f_bfree =
5481*4882a593Smuzhiyun 			    le64_to_cpu(response_data->BlocksAvail);
5482*4882a593Smuzhiyun 			if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
5483*4882a593Smuzhiyun 				FSData->f_bavail = FSData->f_bfree;
5484*4882a593Smuzhiyun 			} else {
5485*4882a593Smuzhiyun 				FSData->f_bavail =
5486*4882a593Smuzhiyun 				    le64_to_cpu(response_data->UserBlocksAvail);
5487*4882a593Smuzhiyun 			}
5488*4882a593Smuzhiyun 			if (response_data->TotalFileNodes != cpu_to_le64(-1))
5489*4882a593Smuzhiyun 				FSData->f_files =
5490*4882a593Smuzhiyun 				     le64_to_cpu(response_data->TotalFileNodes);
5491*4882a593Smuzhiyun 			if (response_data->FreeFileNodes != cpu_to_le64(-1))
5492*4882a593Smuzhiyun 				FSData->f_ffree =
5493*4882a593Smuzhiyun 				      le64_to_cpu(response_data->FreeFileNodes);
5494*4882a593Smuzhiyun 		}
5495*4882a593Smuzhiyun 	}
5496*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
5497*4882a593Smuzhiyun 
5498*4882a593Smuzhiyun 	if (rc == -EAGAIN)
5499*4882a593Smuzhiyun 		goto QFSPosixRetry;
5500*4882a593Smuzhiyun 
5501*4882a593Smuzhiyun 	return rc;
5502*4882a593Smuzhiyun }
5503*4882a593Smuzhiyun 
5504*4882a593Smuzhiyun 
5505*4882a593Smuzhiyun /*
5506*4882a593Smuzhiyun  * We can not use write of zero bytes trick to set file size due to need for
5507*4882a593Smuzhiyun  * large file support. Also note that this SetPathInfo is preferred to
5508*4882a593Smuzhiyun  * SetFileInfo based method in next routine which is only needed to work around
5509*4882a593Smuzhiyun  * a sharing violation bugin Samba which this routine can run into.
5510*4882a593Smuzhiyun  */
5511*4882a593Smuzhiyun int
CIFSSMBSetEOF(const unsigned int xid,struct cifs_tcon * tcon,const char * file_name,__u64 size,struct cifs_sb_info * cifs_sb,bool set_allocation)5512*4882a593Smuzhiyun CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
5513*4882a593Smuzhiyun 	      const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb,
5514*4882a593Smuzhiyun 	      bool set_allocation)
5515*4882a593Smuzhiyun {
5516*4882a593Smuzhiyun 	struct smb_com_transaction2_spi_req *pSMB = NULL;
5517*4882a593Smuzhiyun 	struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5518*4882a593Smuzhiyun 	struct file_end_of_file_info *parm_data;
5519*4882a593Smuzhiyun 	int name_len;
5520*4882a593Smuzhiyun 	int rc = 0;
5521*4882a593Smuzhiyun 	int bytes_returned = 0;
5522*4882a593Smuzhiyun 	int remap = cifs_remap(cifs_sb);
5523*4882a593Smuzhiyun 
5524*4882a593Smuzhiyun 	__u16 params, byte_count, data_count, param_offset, offset;
5525*4882a593Smuzhiyun 
5526*4882a593Smuzhiyun 	cifs_dbg(FYI, "In SetEOF\n");
5527*4882a593Smuzhiyun SetEOFRetry:
5528*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5529*4882a593Smuzhiyun 		      (void **) &pSMBr);
5530*4882a593Smuzhiyun 	if (rc)
5531*4882a593Smuzhiyun 		return rc;
5532*4882a593Smuzhiyun 
5533*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5534*4882a593Smuzhiyun 		name_len =
5535*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name,
5536*4882a593Smuzhiyun 				       PATH_MAX, cifs_sb->local_nls, remap);
5537*4882a593Smuzhiyun 		name_len++;	/* trailing null */
5538*4882a593Smuzhiyun 		name_len *= 2;
5539*4882a593Smuzhiyun 	} else {
5540*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, file_name);
5541*4882a593Smuzhiyun 	}
5542*4882a593Smuzhiyun 	params = 6 + name_len;
5543*4882a593Smuzhiyun 	data_count = sizeof(struct file_end_of_file_info);
5544*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
5545*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(4100);
5546*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
5547*4882a593Smuzhiyun 	pSMB->Reserved = 0;
5548*4882a593Smuzhiyun 	pSMB->Flags = 0;
5549*4882a593Smuzhiyun 	pSMB->Timeout = 0;
5550*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
5551*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_spi_req,
5552*4882a593Smuzhiyun 				InformationLevel) - 4;
5553*4882a593Smuzhiyun 	offset = param_offset + params;
5554*4882a593Smuzhiyun 	if (set_allocation) {
5555*4882a593Smuzhiyun 		if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5556*4882a593Smuzhiyun 			pSMB->InformationLevel =
5557*4882a593Smuzhiyun 				cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
5558*4882a593Smuzhiyun 		else
5559*4882a593Smuzhiyun 			pSMB->InformationLevel =
5560*4882a593Smuzhiyun 				cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
5561*4882a593Smuzhiyun 	} else /* Set File Size */  {
5562*4882a593Smuzhiyun 	    if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5563*4882a593Smuzhiyun 		    pSMB->InformationLevel =
5564*4882a593Smuzhiyun 				cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
5565*4882a593Smuzhiyun 	    else
5566*4882a593Smuzhiyun 		    pSMB->InformationLevel =
5567*4882a593Smuzhiyun 				cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
5568*4882a593Smuzhiyun 	}
5569*4882a593Smuzhiyun 
5570*4882a593Smuzhiyun 	parm_data =
5571*4882a593Smuzhiyun 	    (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
5572*4882a593Smuzhiyun 				       offset);
5573*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
5574*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
5575*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
5576*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
5577*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5578*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + data_count;
5579*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(data_count);
5580*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
5581*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
5582*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
5583*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
5584*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
5585*4882a593Smuzhiyun 	parm_data->FileSize = cpu_to_le64(size);
5586*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
5587*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5588*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5589*4882a593Smuzhiyun 	if (rc)
5590*4882a593Smuzhiyun 		cifs_dbg(FYI, "SetPathInfo (file size) returned %d\n", rc);
5591*4882a593Smuzhiyun 
5592*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
5593*4882a593Smuzhiyun 
5594*4882a593Smuzhiyun 	if (rc == -EAGAIN)
5595*4882a593Smuzhiyun 		goto SetEOFRetry;
5596*4882a593Smuzhiyun 
5597*4882a593Smuzhiyun 	return rc;
5598*4882a593Smuzhiyun }
5599*4882a593Smuzhiyun 
5600*4882a593Smuzhiyun int
CIFSSMBSetFileSize(const unsigned int xid,struct cifs_tcon * tcon,struct cifsFileInfo * cfile,__u64 size,bool set_allocation)5601*4882a593Smuzhiyun CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
5602*4882a593Smuzhiyun 		   struct cifsFileInfo *cfile, __u64 size, bool set_allocation)
5603*4882a593Smuzhiyun {
5604*4882a593Smuzhiyun 	struct smb_com_transaction2_sfi_req *pSMB  = NULL;
5605*4882a593Smuzhiyun 	struct file_end_of_file_info *parm_data;
5606*4882a593Smuzhiyun 	int rc = 0;
5607*4882a593Smuzhiyun 	__u16 params, param_offset, offset, byte_count, count;
5608*4882a593Smuzhiyun 
5609*4882a593Smuzhiyun 	cifs_dbg(FYI, "SetFileSize (via SetFileInfo) %lld\n",
5610*4882a593Smuzhiyun 		 (long long)size);
5611*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5612*4882a593Smuzhiyun 
5613*4882a593Smuzhiyun 	if (rc)
5614*4882a593Smuzhiyun 		return rc;
5615*4882a593Smuzhiyun 
5616*4882a593Smuzhiyun 	pSMB->hdr.Pid = cpu_to_le16((__u16)cfile->pid);
5617*4882a593Smuzhiyun 	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(cfile->pid >> 16));
5618*4882a593Smuzhiyun 
5619*4882a593Smuzhiyun 	params = 6;
5620*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
5621*4882a593Smuzhiyun 	pSMB->Reserved = 0;
5622*4882a593Smuzhiyun 	pSMB->Flags = 0;
5623*4882a593Smuzhiyun 	pSMB->Timeout = 0;
5624*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
5625*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5626*4882a593Smuzhiyun 	offset = param_offset + params;
5627*4882a593Smuzhiyun 
5628*4882a593Smuzhiyun 	count = sizeof(struct file_end_of_file_info);
5629*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
5630*4882a593Smuzhiyun 	/* BB find exact max SMB PDU from sess structure BB */
5631*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
5632*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
5633*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
5634*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5635*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + count;
5636*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(count);
5637*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
5638*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
5639*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
5640*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
5641*4882a593Smuzhiyun 	parm_data =
5642*4882a593Smuzhiyun 		(struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
5643*4882a593Smuzhiyun 				+ offset);
5644*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
5645*4882a593Smuzhiyun 	parm_data->FileSize = cpu_to_le64(size);
5646*4882a593Smuzhiyun 	pSMB->Fid = cfile->fid.netfid;
5647*4882a593Smuzhiyun 	if (set_allocation) {
5648*4882a593Smuzhiyun 		if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5649*4882a593Smuzhiyun 			pSMB->InformationLevel =
5650*4882a593Smuzhiyun 				cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
5651*4882a593Smuzhiyun 		else
5652*4882a593Smuzhiyun 			pSMB->InformationLevel =
5653*4882a593Smuzhiyun 				cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
5654*4882a593Smuzhiyun 	} else /* Set File Size */  {
5655*4882a593Smuzhiyun 	    if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5656*4882a593Smuzhiyun 		    pSMB->InformationLevel =
5657*4882a593Smuzhiyun 				cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
5658*4882a593Smuzhiyun 	    else
5659*4882a593Smuzhiyun 		    pSMB->InformationLevel =
5660*4882a593Smuzhiyun 				cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
5661*4882a593Smuzhiyun 	}
5662*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
5663*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
5664*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
5665*4882a593Smuzhiyun 	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
5666*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
5667*4882a593Smuzhiyun 	if (rc) {
5668*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in SetFileInfo (SetFileSize) = %d\n",
5669*4882a593Smuzhiyun 			 rc);
5670*4882a593Smuzhiyun 	}
5671*4882a593Smuzhiyun 
5672*4882a593Smuzhiyun 	/* Note: On -EAGAIN error only caller can retry on handle based calls
5673*4882a593Smuzhiyun 		since file handle passed in no longer valid */
5674*4882a593Smuzhiyun 
5675*4882a593Smuzhiyun 	return rc;
5676*4882a593Smuzhiyun }
5677*4882a593Smuzhiyun 
5678*4882a593Smuzhiyun /* Some legacy servers such as NT4 require that the file times be set on
5679*4882a593Smuzhiyun    an open handle, rather than by pathname - this is awkward due to
5680*4882a593Smuzhiyun    potential access conflicts on the open, but it is unavoidable for these
5681*4882a593Smuzhiyun    old servers since the only other choice is to go from 100 nanosecond DCE
5682*4882a593Smuzhiyun    time and resort to the original setpathinfo level which takes the ancient
5683*4882a593Smuzhiyun    DOS time format with 2 second granularity */
5684*4882a593Smuzhiyun int
CIFSSMBSetFileInfo(const unsigned int xid,struct cifs_tcon * tcon,const FILE_BASIC_INFO * data,__u16 fid,__u32 pid_of_opener)5685*4882a593Smuzhiyun CIFSSMBSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
5686*4882a593Smuzhiyun 		    const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
5687*4882a593Smuzhiyun {
5688*4882a593Smuzhiyun 	struct smb_com_transaction2_sfi_req *pSMB  = NULL;
5689*4882a593Smuzhiyun 	char *data_offset;
5690*4882a593Smuzhiyun 	int rc = 0;
5691*4882a593Smuzhiyun 	__u16 params, param_offset, offset, byte_count, count;
5692*4882a593Smuzhiyun 
5693*4882a593Smuzhiyun 	cifs_dbg(FYI, "Set Times (via SetFileInfo)\n");
5694*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5695*4882a593Smuzhiyun 
5696*4882a593Smuzhiyun 	if (rc)
5697*4882a593Smuzhiyun 		return rc;
5698*4882a593Smuzhiyun 
5699*4882a593Smuzhiyun 	pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5700*4882a593Smuzhiyun 	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5701*4882a593Smuzhiyun 
5702*4882a593Smuzhiyun 	params = 6;
5703*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
5704*4882a593Smuzhiyun 	pSMB->Reserved = 0;
5705*4882a593Smuzhiyun 	pSMB->Flags = 0;
5706*4882a593Smuzhiyun 	pSMB->Timeout = 0;
5707*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
5708*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5709*4882a593Smuzhiyun 	offset = param_offset + params;
5710*4882a593Smuzhiyun 
5711*4882a593Smuzhiyun 	data_offset = (char *)pSMB +
5712*4882a593Smuzhiyun 			offsetof(struct smb_hdr, Protocol) + offset;
5713*4882a593Smuzhiyun 
5714*4882a593Smuzhiyun 	count = sizeof(FILE_BASIC_INFO);
5715*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
5716*4882a593Smuzhiyun 	/* BB find max SMB PDU from sess */
5717*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
5718*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
5719*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
5720*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5721*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + count;
5722*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(count);
5723*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
5724*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
5725*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
5726*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
5727*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
5728*4882a593Smuzhiyun 	pSMB->Fid = fid;
5729*4882a593Smuzhiyun 	if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5730*4882a593Smuzhiyun 		pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5731*4882a593Smuzhiyun 	else
5732*4882a593Smuzhiyun 		pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5733*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
5734*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
5735*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
5736*4882a593Smuzhiyun 	memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
5737*4882a593Smuzhiyun 	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
5738*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
5739*4882a593Smuzhiyun 	if (rc)
5740*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n",
5741*4882a593Smuzhiyun 			 rc);
5742*4882a593Smuzhiyun 
5743*4882a593Smuzhiyun 	/* Note: On -EAGAIN error only caller can retry on handle based calls
5744*4882a593Smuzhiyun 		since file handle passed in no longer valid */
5745*4882a593Smuzhiyun 
5746*4882a593Smuzhiyun 	return rc;
5747*4882a593Smuzhiyun }
5748*4882a593Smuzhiyun 
5749*4882a593Smuzhiyun int
CIFSSMBSetFileDisposition(const unsigned int xid,struct cifs_tcon * tcon,bool delete_file,__u16 fid,__u32 pid_of_opener)5750*4882a593Smuzhiyun CIFSSMBSetFileDisposition(const unsigned int xid, struct cifs_tcon *tcon,
5751*4882a593Smuzhiyun 			  bool delete_file, __u16 fid, __u32 pid_of_opener)
5752*4882a593Smuzhiyun {
5753*4882a593Smuzhiyun 	struct smb_com_transaction2_sfi_req *pSMB  = NULL;
5754*4882a593Smuzhiyun 	char *data_offset;
5755*4882a593Smuzhiyun 	int rc = 0;
5756*4882a593Smuzhiyun 	__u16 params, param_offset, offset, byte_count, count;
5757*4882a593Smuzhiyun 
5758*4882a593Smuzhiyun 	cifs_dbg(FYI, "Set File Disposition (via SetFileInfo)\n");
5759*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5760*4882a593Smuzhiyun 
5761*4882a593Smuzhiyun 	if (rc)
5762*4882a593Smuzhiyun 		return rc;
5763*4882a593Smuzhiyun 
5764*4882a593Smuzhiyun 	pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5765*4882a593Smuzhiyun 	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5766*4882a593Smuzhiyun 
5767*4882a593Smuzhiyun 	params = 6;
5768*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
5769*4882a593Smuzhiyun 	pSMB->Reserved = 0;
5770*4882a593Smuzhiyun 	pSMB->Flags = 0;
5771*4882a593Smuzhiyun 	pSMB->Timeout = 0;
5772*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
5773*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5774*4882a593Smuzhiyun 	offset = param_offset + params;
5775*4882a593Smuzhiyun 
5776*4882a593Smuzhiyun 	data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5777*4882a593Smuzhiyun 
5778*4882a593Smuzhiyun 	count = 1;
5779*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
5780*4882a593Smuzhiyun 	/* BB find max SMB PDU from sess */
5781*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
5782*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
5783*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
5784*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5785*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + count;
5786*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(count);
5787*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
5788*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
5789*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
5790*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
5791*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
5792*4882a593Smuzhiyun 	pSMB->Fid = fid;
5793*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
5794*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
5795*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
5796*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
5797*4882a593Smuzhiyun 	*data_offset = delete_file ? 1 : 0;
5798*4882a593Smuzhiyun 	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
5799*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
5800*4882a593Smuzhiyun 	if (rc)
5801*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in SetFileDisposition = %d\n", rc);
5802*4882a593Smuzhiyun 
5803*4882a593Smuzhiyun 	return rc;
5804*4882a593Smuzhiyun }
5805*4882a593Smuzhiyun 
5806*4882a593Smuzhiyun static int
CIFSSMBSetPathInfoFB(const unsigned int xid,struct cifs_tcon * tcon,const char * fileName,const FILE_BASIC_INFO * data,const struct nls_table * nls_codepage,struct cifs_sb_info * cifs_sb)5807*4882a593Smuzhiyun CIFSSMBSetPathInfoFB(const unsigned int xid, struct cifs_tcon *tcon,
5808*4882a593Smuzhiyun 		     const char *fileName, const FILE_BASIC_INFO *data,
5809*4882a593Smuzhiyun 		     const struct nls_table *nls_codepage,
5810*4882a593Smuzhiyun 		     struct cifs_sb_info *cifs_sb)
5811*4882a593Smuzhiyun {
5812*4882a593Smuzhiyun 	int oplock = 0;
5813*4882a593Smuzhiyun 	struct cifs_open_parms oparms;
5814*4882a593Smuzhiyun 	struct cifs_fid fid;
5815*4882a593Smuzhiyun 	int rc;
5816*4882a593Smuzhiyun 
5817*4882a593Smuzhiyun 	oparms.tcon = tcon;
5818*4882a593Smuzhiyun 	oparms.cifs_sb = cifs_sb;
5819*4882a593Smuzhiyun 	oparms.desired_access = GENERIC_WRITE;
5820*4882a593Smuzhiyun 	oparms.create_options = cifs_create_options(cifs_sb, 0);
5821*4882a593Smuzhiyun 	oparms.disposition = FILE_OPEN;
5822*4882a593Smuzhiyun 	oparms.path = fileName;
5823*4882a593Smuzhiyun 	oparms.fid = &fid;
5824*4882a593Smuzhiyun 	oparms.reconnect = false;
5825*4882a593Smuzhiyun 
5826*4882a593Smuzhiyun 	rc = CIFS_open(xid, &oparms, &oplock, NULL);
5827*4882a593Smuzhiyun 	if (rc)
5828*4882a593Smuzhiyun 		goto out;
5829*4882a593Smuzhiyun 
5830*4882a593Smuzhiyun 	rc = CIFSSMBSetFileInfo(xid, tcon, data, fid.netfid, current->tgid);
5831*4882a593Smuzhiyun 	CIFSSMBClose(xid, tcon, fid.netfid);
5832*4882a593Smuzhiyun out:
5833*4882a593Smuzhiyun 
5834*4882a593Smuzhiyun 	return rc;
5835*4882a593Smuzhiyun }
5836*4882a593Smuzhiyun 
5837*4882a593Smuzhiyun int
CIFSSMBSetPathInfo(const unsigned int xid,struct cifs_tcon * tcon,const char * fileName,const FILE_BASIC_INFO * data,const struct nls_table * nls_codepage,struct cifs_sb_info * cifs_sb)5838*4882a593Smuzhiyun CIFSSMBSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
5839*4882a593Smuzhiyun 		   const char *fileName, const FILE_BASIC_INFO *data,
5840*4882a593Smuzhiyun 		   const struct nls_table *nls_codepage,
5841*4882a593Smuzhiyun 		     struct cifs_sb_info *cifs_sb)
5842*4882a593Smuzhiyun {
5843*4882a593Smuzhiyun 	TRANSACTION2_SPI_REQ *pSMB = NULL;
5844*4882a593Smuzhiyun 	TRANSACTION2_SPI_RSP *pSMBr = NULL;
5845*4882a593Smuzhiyun 	int name_len;
5846*4882a593Smuzhiyun 	int rc = 0;
5847*4882a593Smuzhiyun 	int bytes_returned = 0;
5848*4882a593Smuzhiyun 	char *data_offset;
5849*4882a593Smuzhiyun 	__u16 params, param_offset, offset, byte_count, count;
5850*4882a593Smuzhiyun 	int remap = cifs_remap(cifs_sb);
5851*4882a593Smuzhiyun 
5852*4882a593Smuzhiyun 	cifs_dbg(FYI, "In SetTimes\n");
5853*4882a593Smuzhiyun 
5854*4882a593Smuzhiyun SetTimesRetry:
5855*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5856*4882a593Smuzhiyun 		      (void **) &pSMBr);
5857*4882a593Smuzhiyun 	if (rc)
5858*4882a593Smuzhiyun 		return rc;
5859*4882a593Smuzhiyun 
5860*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5861*4882a593Smuzhiyun 		name_len =
5862*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
5863*4882a593Smuzhiyun 				       PATH_MAX, nls_codepage, remap);
5864*4882a593Smuzhiyun 		name_len++;	/* trailing null */
5865*4882a593Smuzhiyun 		name_len *= 2;
5866*4882a593Smuzhiyun 	} else {
5867*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, fileName);
5868*4882a593Smuzhiyun 	}
5869*4882a593Smuzhiyun 
5870*4882a593Smuzhiyun 	params = 6 + name_len;
5871*4882a593Smuzhiyun 	count = sizeof(FILE_BASIC_INFO);
5872*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
5873*4882a593Smuzhiyun 	/* BB find max SMB PDU from sess structure BB */
5874*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
5875*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
5876*4882a593Smuzhiyun 	pSMB->Reserved = 0;
5877*4882a593Smuzhiyun 	pSMB->Flags = 0;
5878*4882a593Smuzhiyun 	pSMB->Timeout = 0;
5879*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
5880*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_spi_req,
5881*4882a593Smuzhiyun 				InformationLevel) - 4;
5882*4882a593Smuzhiyun 	offset = param_offset + params;
5883*4882a593Smuzhiyun 	data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5884*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
5885*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
5886*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
5887*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
5888*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5889*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + count;
5890*4882a593Smuzhiyun 
5891*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(count);
5892*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
5893*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
5894*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
5895*4882a593Smuzhiyun 	if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5896*4882a593Smuzhiyun 		pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5897*4882a593Smuzhiyun 	else
5898*4882a593Smuzhiyun 		pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5899*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
5900*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
5901*4882a593Smuzhiyun 	memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
5902*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
5903*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5904*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5905*4882a593Smuzhiyun 	if (rc)
5906*4882a593Smuzhiyun 		cifs_dbg(FYI, "SetPathInfo (times) returned %d\n", rc);
5907*4882a593Smuzhiyun 
5908*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
5909*4882a593Smuzhiyun 
5910*4882a593Smuzhiyun 	if (rc == -EAGAIN)
5911*4882a593Smuzhiyun 		goto SetTimesRetry;
5912*4882a593Smuzhiyun 
5913*4882a593Smuzhiyun 	if (rc == -EOPNOTSUPP)
5914*4882a593Smuzhiyun 		return CIFSSMBSetPathInfoFB(xid, tcon, fileName, data,
5915*4882a593Smuzhiyun 					    nls_codepage, cifs_sb);
5916*4882a593Smuzhiyun 
5917*4882a593Smuzhiyun 	return rc;
5918*4882a593Smuzhiyun }
5919*4882a593Smuzhiyun 
5920*4882a593Smuzhiyun /* Can not be used to set time stamps yet (due to old DOS time format) */
5921*4882a593Smuzhiyun /* Can be used to set attributes */
5922*4882a593Smuzhiyun #if 0  /* Possibly not needed - since it turns out that strangely NT4 has a bug
5923*4882a593Smuzhiyun 	  handling it anyway and NT4 was what we thought it would be needed for
5924*4882a593Smuzhiyun 	  Do not delete it until we prove whether needed for Win9x though */
5925*4882a593Smuzhiyun int
5926*4882a593Smuzhiyun CIFSSMBSetAttrLegacy(unsigned int xid, struct cifs_tcon *tcon, char *fileName,
5927*4882a593Smuzhiyun 		__u16 dos_attrs, const struct nls_table *nls_codepage)
5928*4882a593Smuzhiyun {
5929*4882a593Smuzhiyun 	SETATTR_REQ *pSMB = NULL;
5930*4882a593Smuzhiyun 	SETATTR_RSP *pSMBr = NULL;
5931*4882a593Smuzhiyun 	int rc = 0;
5932*4882a593Smuzhiyun 	int bytes_returned;
5933*4882a593Smuzhiyun 	int name_len;
5934*4882a593Smuzhiyun 
5935*4882a593Smuzhiyun 	cifs_dbg(FYI, "In SetAttrLegacy\n");
5936*4882a593Smuzhiyun 
5937*4882a593Smuzhiyun SetAttrLgcyRetry:
5938*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
5939*4882a593Smuzhiyun 		      (void **) &pSMBr);
5940*4882a593Smuzhiyun 	if (rc)
5941*4882a593Smuzhiyun 		return rc;
5942*4882a593Smuzhiyun 
5943*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5944*4882a593Smuzhiyun 		name_len =
5945*4882a593Smuzhiyun 			ConvertToUTF16((__le16 *) pSMB->fileName, fileName,
5946*4882a593Smuzhiyun 				       PATH_MAX, nls_codepage);
5947*4882a593Smuzhiyun 		name_len++;     /* trailing null */
5948*4882a593Smuzhiyun 		name_len *= 2;
5949*4882a593Smuzhiyun 	} else {
5950*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->fileName, fileName);
5951*4882a593Smuzhiyun 	}
5952*4882a593Smuzhiyun 	pSMB->attr = cpu_to_le16(dos_attrs);
5953*4882a593Smuzhiyun 	pSMB->BufferFormat = 0x04;
5954*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, name_len + 1);
5955*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(name_len + 1);
5956*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5957*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5958*4882a593Smuzhiyun 	if (rc)
5959*4882a593Smuzhiyun 		cifs_dbg(FYI, "Error in LegacySetAttr = %d\n", rc);
5960*4882a593Smuzhiyun 
5961*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
5962*4882a593Smuzhiyun 
5963*4882a593Smuzhiyun 	if (rc == -EAGAIN)
5964*4882a593Smuzhiyun 		goto SetAttrLgcyRetry;
5965*4882a593Smuzhiyun 
5966*4882a593Smuzhiyun 	return rc;
5967*4882a593Smuzhiyun }
5968*4882a593Smuzhiyun #endif /* temporarily unneeded SetAttr legacy function */
5969*4882a593Smuzhiyun 
5970*4882a593Smuzhiyun static void
cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO * data_offset,const struct cifs_unix_set_info_args * args)5971*4882a593Smuzhiyun cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,
5972*4882a593Smuzhiyun 			const struct cifs_unix_set_info_args *args)
5973*4882a593Smuzhiyun {
5974*4882a593Smuzhiyun 	u64 uid = NO_CHANGE_64, gid = NO_CHANGE_64;
5975*4882a593Smuzhiyun 	u64 mode = args->mode;
5976*4882a593Smuzhiyun 
5977*4882a593Smuzhiyun 	if (uid_valid(args->uid))
5978*4882a593Smuzhiyun 		uid = from_kuid(&init_user_ns, args->uid);
5979*4882a593Smuzhiyun 	if (gid_valid(args->gid))
5980*4882a593Smuzhiyun 		gid = from_kgid(&init_user_ns, args->gid);
5981*4882a593Smuzhiyun 
5982*4882a593Smuzhiyun 	/*
5983*4882a593Smuzhiyun 	 * Samba server ignores set of file size to zero due to bugs in some
5984*4882a593Smuzhiyun 	 * older clients, but we should be precise - we use SetFileSize to
5985*4882a593Smuzhiyun 	 * set file size and do not want to truncate file size to zero
5986*4882a593Smuzhiyun 	 * accidentally as happened on one Samba server beta by putting
5987*4882a593Smuzhiyun 	 * zero instead of -1 here
5988*4882a593Smuzhiyun 	 */
5989*4882a593Smuzhiyun 	data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
5990*4882a593Smuzhiyun 	data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
5991*4882a593Smuzhiyun 	data_offset->LastStatusChange = cpu_to_le64(args->ctime);
5992*4882a593Smuzhiyun 	data_offset->LastAccessTime = cpu_to_le64(args->atime);
5993*4882a593Smuzhiyun 	data_offset->LastModificationTime = cpu_to_le64(args->mtime);
5994*4882a593Smuzhiyun 	data_offset->Uid = cpu_to_le64(uid);
5995*4882a593Smuzhiyun 	data_offset->Gid = cpu_to_le64(gid);
5996*4882a593Smuzhiyun 	/* better to leave device as zero when it is  */
5997*4882a593Smuzhiyun 	data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
5998*4882a593Smuzhiyun 	data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
5999*4882a593Smuzhiyun 	data_offset->Permissions = cpu_to_le64(mode);
6000*4882a593Smuzhiyun 
6001*4882a593Smuzhiyun 	if (S_ISREG(mode))
6002*4882a593Smuzhiyun 		data_offset->Type = cpu_to_le32(UNIX_FILE);
6003*4882a593Smuzhiyun 	else if (S_ISDIR(mode))
6004*4882a593Smuzhiyun 		data_offset->Type = cpu_to_le32(UNIX_DIR);
6005*4882a593Smuzhiyun 	else if (S_ISLNK(mode))
6006*4882a593Smuzhiyun 		data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
6007*4882a593Smuzhiyun 	else if (S_ISCHR(mode))
6008*4882a593Smuzhiyun 		data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
6009*4882a593Smuzhiyun 	else if (S_ISBLK(mode))
6010*4882a593Smuzhiyun 		data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
6011*4882a593Smuzhiyun 	else if (S_ISFIFO(mode))
6012*4882a593Smuzhiyun 		data_offset->Type = cpu_to_le32(UNIX_FIFO);
6013*4882a593Smuzhiyun 	else if (S_ISSOCK(mode))
6014*4882a593Smuzhiyun 		data_offset->Type = cpu_to_le32(UNIX_SOCKET);
6015*4882a593Smuzhiyun }
6016*4882a593Smuzhiyun 
6017*4882a593Smuzhiyun int
CIFSSMBUnixSetFileInfo(const unsigned int xid,struct cifs_tcon * tcon,const struct cifs_unix_set_info_args * args,u16 fid,u32 pid_of_opener)6018*4882a593Smuzhiyun CIFSSMBUnixSetFileInfo(const unsigned int xid, struct cifs_tcon *tcon,
6019*4882a593Smuzhiyun 		       const struct cifs_unix_set_info_args *args,
6020*4882a593Smuzhiyun 		       u16 fid, u32 pid_of_opener)
6021*4882a593Smuzhiyun {
6022*4882a593Smuzhiyun 	struct smb_com_transaction2_sfi_req *pSMB  = NULL;
6023*4882a593Smuzhiyun 	char *data_offset;
6024*4882a593Smuzhiyun 	int rc = 0;
6025*4882a593Smuzhiyun 	u16 params, param_offset, offset, byte_count, count;
6026*4882a593Smuzhiyun 
6027*4882a593Smuzhiyun 	cifs_dbg(FYI, "Set Unix Info (via SetFileInfo)\n");
6028*4882a593Smuzhiyun 	rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
6029*4882a593Smuzhiyun 
6030*4882a593Smuzhiyun 	if (rc)
6031*4882a593Smuzhiyun 		return rc;
6032*4882a593Smuzhiyun 
6033*4882a593Smuzhiyun 	pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
6034*4882a593Smuzhiyun 	pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
6035*4882a593Smuzhiyun 
6036*4882a593Smuzhiyun 	params = 6;
6037*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
6038*4882a593Smuzhiyun 	pSMB->Reserved = 0;
6039*4882a593Smuzhiyun 	pSMB->Flags = 0;
6040*4882a593Smuzhiyun 	pSMB->Timeout = 0;
6041*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
6042*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
6043*4882a593Smuzhiyun 	offset = param_offset + params;
6044*4882a593Smuzhiyun 
6045*4882a593Smuzhiyun 	data_offset = (char *)pSMB +
6046*4882a593Smuzhiyun 			offsetof(struct smb_hdr, Protocol) + offset;
6047*4882a593Smuzhiyun 
6048*4882a593Smuzhiyun 	count = sizeof(FILE_UNIX_BASIC_INFO);
6049*4882a593Smuzhiyun 
6050*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
6051*4882a593Smuzhiyun 	/* BB find max SMB PDU from sess */
6052*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
6053*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
6054*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
6055*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
6056*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + count;
6057*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(count);
6058*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
6059*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
6060*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
6061*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
6062*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
6063*4882a593Smuzhiyun 	pSMB->Fid = fid;
6064*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
6065*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
6066*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
6067*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
6068*4882a593Smuzhiyun 
6069*4882a593Smuzhiyun 	cifs_fill_unix_set_info((FILE_UNIX_BASIC_INFO *)data_offset, args);
6070*4882a593Smuzhiyun 
6071*4882a593Smuzhiyun 	rc = SendReceiveNoRsp(xid, tcon->ses, (char *) pSMB, 0);
6072*4882a593Smuzhiyun 	cifs_small_buf_release(pSMB);
6073*4882a593Smuzhiyun 	if (rc)
6074*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in Set Time (SetFileInfo) = %d\n",
6075*4882a593Smuzhiyun 			 rc);
6076*4882a593Smuzhiyun 
6077*4882a593Smuzhiyun 	/* Note: On -EAGAIN error only caller can retry on handle based calls
6078*4882a593Smuzhiyun 		since file handle passed in no longer valid */
6079*4882a593Smuzhiyun 
6080*4882a593Smuzhiyun 	return rc;
6081*4882a593Smuzhiyun }
6082*4882a593Smuzhiyun 
6083*4882a593Smuzhiyun int
CIFSSMBUnixSetPathInfo(const unsigned int xid,struct cifs_tcon * tcon,const char * file_name,const struct cifs_unix_set_info_args * args,const struct nls_table * nls_codepage,int remap)6084*4882a593Smuzhiyun CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
6085*4882a593Smuzhiyun 		       const char *file_name,
6086*4882a593Smuzhiyun 		       const struct cifs_unix_set_info_args *args,
6087*4882a593Smuzhiyun 		       const struct nls_table *nls_codepage, int remap)
6088*4882a593Smuzhiyun {
6089*4882a593Smuzhiyun 	TRANSACTION2_SPI_REQ *pSMB = NULL;
6090*4882a593Smuzhiyun 	TRANSACTION2_SPI_RSP *pSMBr = NULL;
6091*4882a593Smuzhiyun 	int name_len;
6092*4882a593Smuzhiyun 	int rc = 0;
6093*4882a593Smuzhiyun 	int bytes_returned = 0;
6094*4882a593Smuzhiyun 	FILE_UNIX_BASIC_INFO *data_offset;
6095*4882a593Smuzhiyun 	__u16 params, param_offset, offset, count, byte_count;
6096*4882a593Smuzhiyun 
6097*4882a593Smuzhiyun 	cifs_dbg(FYI, "In SetUID/GID/Mode\n");
6098*4882a593Smuzhiyun setPermsRetry:
6099*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
6100*4882a593Smuzhiyun 		      (void **) &pSMBr);
6101*4882a593Smuzhiyun 	if (rc)
6102*4882a593Smuzhiyun 		return rc;
6103*4882a593Smuzhiyun 
6104*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
6105*4882a593Smuzhiyun 		name_len =
6106*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name,
6107*4882a593Smuzhiyun 				       PATH_MAX, nls_codepage, remap);
6108*4882a593Smuzhiyun 		name_len++;	/* trailing null */
6109*4882a593Smuzhiyun 		name_len *= 2;
6110*4882a593Smuzhiyun 	} else {
6111*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, file_name);
6112*4882a593Smuzhiyun 	}
6113*4882a593Smuzhiyun 
6114*4882a593Smuzhiyun 	params = 6 + name_len;
6115*4882a593Smuzhiyun 	count = sizeof(FILE_UNIX_BASIC_INFO);
6116*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
6117*4882a593Smuzhiyun 	/* BB find max SMB PDU from sess structure BB */
6118*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
6119*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
6120*4882a593Smuzhiyun 	pSMB->Reserved = 0;
6121*4882a593Smuzhiyun 	pSMB->Flags = 0;
6122*4882a593Smuzhiyun 	pSMB->Timeout = 0;
6123*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
6124*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_spi_req,
6125*4882a593Smuzhiyun 				InformationLevel) - 4;
6126*4882a593Smuzhiyun 	offset = param_offset + params;
6127*4882a593Smuzhiyun 	data_offset =
6128*4882a593Smuzhiyun 	    (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
6129*4882a593Smuzhiyun 				      offset);
6130*4882a593Smuzhiyun 	memset(data_offset, 0, count);
6131*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
6132*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
6133*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
6134*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
6135*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
6136*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + count;
6137*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
6138*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(count);
6139*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
6140*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
6141*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
6142*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
6143*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
6144*4882a593Smuzhiyun 
6145*4882a593Smuzhiyun 	cifs_fill_unix_set_info(data_offset, args);
6146*4882a593Smuzhiyun 
6147*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
6148*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
6149*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
6150*4882a593Smuzhiyun 	if (rc)
6151*4882a593Smuzhiyun 		cifs_dbg(FYI, "SetPathInfo (perms) returned %d\n", rc);
6152*4882a593Smuzhiyun 
6153*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
6154*4882a593Smuzhiyun 	if (rc == -EAGAIN)
6155*4882a593Smuzhiyun 		goto setPermsRetry;
6156*4882a593Smuzhiyun 	return rc;
6157*4882a593Smuzhiyun }
6158*4882a593Smuzhiyun 
6159*4882a593Smuzhiyun #ifdef CONFIG_CIFS_XATTR
6160*4882a593Smuzhiyun /*
6161*4882a593Smuzhiyun  * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common
6162*4882a593Smuzhiyun  * function used by listxattr and getxattr type calls. When ea_name is set,
6163*4882a593Smuzhiyun  * it looks for that attribute name and stuffs that value into the EAData
6164*4882a593Smuzhiyun  * buffer. When ea_name is NULL, it stuffs a list of attribute names into the
6165*4882a593Smuzhiyun  * buffer. In both cases, the return value is either the length of the
6166*4882a593Smuzhiyun  * resulting data or a negative error code. If EAData is a NULL pointer then
6167*4882a593Smuzhiyun  * the data isn't copied to it, but the length is returned.
6168*4882a593Smuzhiyun  */
6169*4882a593Smuzhiyun ssize_t
CIFSSMBQAllEAs(const unsigned int xid,struct cifs_tcon * tcon,const unsigned char * searchName,const unsigned char * ea_name,char * EAData,size_t buf_size,struct cifs_sb_info * cifs_sb)6170*4882a593Smuzhiyun CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
6171*4882a593Smuzhiyun 		const unsigned char *searchName, const unsigned char *ea_name,
6172*4882a593Smuzhiyun 		char *EAData, size_t buf_size,
6173*4882a593Smuzhiyun 		struct cifs_sb_info *cifs_sb)
6174*4882a593Smuzhiyun {
6175*4882a593Smuzhiyun 		/* BB assumes one setup word */
6176*4882a593Smuzhiyun 	TRANSACTION2_QPI_REQ *pSMB = NULL;
6177*4882a593Smuzhiyun 	TRANSACTION2_QPI_RSP *pSMBr = NULL;
6178*4882a593Smuzhiyun 	int remap = cifs_remap(cifs_sb);
6179*4882a593Smuzhiyun 	struct nls_table *nls_codepage = cifs_sb->local_nls;
6180*4882a593Smuzhiyun 	int rc = 0;
6181*4882a593Smuzhiyun 	int bytes_returned;
6182*4882a593Smuzhiyun 	int list_len;
6183*4882a593Smuzhiyun 	struct fealist *ea_response_data;
6184*4882a593Smuzhiyun 	struct fea *temp_fea;
6185*4882a593Smuzhiyun 	char *temp_ptr;
6186*4882a593Smuzhiyun 	char *end_of_smb;
6187*4882a593Smuzhiyun 	__u16 params, byte_count, data_offset;
6188*4882a593Smuzhiyun 	unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
6189*4882a593Smuzhiyun 
6190*4882a593Smuzhiyun 	cifs_dbg(FYI, "In Query All EAs path %s\n", searchName);
6191*4882a593Smuzhiyun QAllEAsRetry:
6192*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
6193*4882a593Smuzhiyun 		      (void **) &pSMBr);
6194*4882a593Smuzhiyun 	if (rc)
6195*4882a593Smuzhiyun 		return rc;
6196*4882a593Smuzhiyun 
6197*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
6198*4882a593Smuzhiyun 		list_len =
6199*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName,
6200*4882a593Smuzhiyun 				       PATH_MAX, nls_codepage, remap);
6201*4882a593Smuzhiyun 		list_len++;	/* trailing null */
6202*4882a593Smuzhiyun 		list_len *= 2;
6203*4882a593Smuzhiyun 	} else {
6204*4882a593Smuzhiyun 		list_len = copy_path_name(pSMB->FileName, searchName);
6205*4882a593Smuzhiyun 	}
6206*4882a593Smuzhiyun 
6207*4882a593Smuzhiyun 	params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */;
6208*4882a593Smuzhiyun 	pSMB->TotalDataCount = 0;
6209*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
6210*4882a593Smuzhiyun 	/* BB find exact max SMB PDU from sess structure BB */
6211*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
6212*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
6213*4882a593Smuzhiyun 	pSMB->Reserved = 0;
6214*4882a593Smuzhiyun 	pSMB->Flags = 0;
6215*4882a593Smuzhiyun 	pSMB->Timeout = 0;
6216*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
6217*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(offsetof(
6218*4882a593Smuzhiyun 	struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
6219*4882a593Smuzhiyun 	pSMB->DataCount = 0;
6220*4882a593Smuzhiyun 	pSMB->DataOffset = 0;
6221*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
6222*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
6223*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
6224*4882a593Smuzhiyun 	byte_count = params + 1 /* pad */ ;
6225*4882a593Smuzhiyun 	pSMB->TotalParameterCount = cpu_to_le16(params);
6226*4882a593Smuzhiyun 	pSMB->ParameterCount = pSMB->TotalParameterCount;
6227*4882a593Smuzhiyun 	pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
6228*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
6229*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
6230*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
6231*4882a593Smuzhiyun 
6232*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
6233*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
6234*4882a593Smuzhiyun 	if (rc) {
6235*4882a593Smuzhiyun 		cifs_dbg(FYI, "Send error in QueryAllEAs = %d\n", rc);
6236*4882a593Smuzhiyun 		goto QAllEAsOut;
6237*4882a593Smuzhiyun 	}
6238*4882a593Smuzhiyun 
6239*4882a593Smuzhiyun 
6240*4882a593Smuzhiyun 	/* BB also check enough total bytes returned */
6241*4882a593Smuzhiyun 	/* BB we need to improve the validity checking
6242*4882a593Smuzhiyun 	of these trans2 responses */
6243*4882a593Smuzhiyun 
6244*4882a593Smuzhiyun 	rc = validate_t2((struct smb_t2_rsp *)pSMBr);
6245*4882a593Smuzhiyun 	if (rc || get_bcc(&pSMBr->hdr) < 4) {
6246*4882a593Smuzhiyun 		rc = -EIO;	/* bad smb */
6247*4882a593Smuzhiyun 		goto QAllEAsOut;
6248*4882a593Smuzhiyun 	}
6249*4882a593Smuzhiyun 
6250*4882a593Smuzhiyun 	/* check that length of list is not more than bcc */
6251*4882a593Smuzhiyun 	/* check that each entry does not go beyond length
6252*4882a593Smuzhiyun 	   of list */
6253*4882a593Smuzhiyun 	/* check that each element of each entry does not
6254*4882a593Smuzhiyun 	   go beyond end of list */
6255*4882a593Smuzhiyun 	/* validate_trans2_offsets() */
6256*4882a593Smuzhiyun 	/* BB check if start of smb + data_offset > &bcc+ bcc */
6257*4882a593Smuzhiyun 
6258*4882a593Smuzhiyun 	data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
6259*4882a593Smuzhiyun 	ea_response_data = (struct fealist *)
6260*4882a593Smuzhiyun 				(((char *) &pSMBr->hdr.Protocol) + data_offset);
6261*4882a593Smuzhiyun 
6262*4882a593Smuzhiyun 	list_len = le32_to_cpu(ea_response_data->list_len);
6263*4882a593Smuzhiyun 	cifs_dbg(FYI, "ea length %d\n", list_len);
6264*4882a593Smuzhiyun 	if (list_len <= 8) {
6265*4882a593Smuzhiyun 		cifs_dbg(FYI, "empty EA list returned from server\n");
6266*4882a593Smuzhiyun 		/* didn't find the named attribute */
6267*4882a593Smuzhiyun 		if (ea_name)
6268*4882a593Smuzhiyun 			rc = -ENODATA;
6269*4882a593Smuzhiyun 		goto QAllEAsOut;
6270*4882a593Smuzhiyun 	}
6271*4882a593Smuzhiyun 
6272*4882a593Smuzhiyun 	/* make sure list_len doesn't go past end of SMB */
6273*4882a593Smuzhiyun 	end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr);
6274*4882a593Smuzhiyun 	if ((char *)ea_response_data + list_len > end_of_smb) {
6275*4882a593Smuzhiyun 		cifs_dbg(FYI, "EA list appears to go beyond SMB\n");
6276*4882a593Smuzhiyun 		rc = -EIO;
6277*4882a593Smuzhiyun 		goto QAllEAsOut;
6278*4882a593Smuzhiyun 	}
6279*4882a593Smuzhiyun 
6280*4882a593Smuzhiyun 	/* account for ea list len */
6281*4882a593Smuzhiyun 	list_len -= 4;
6282*4882a593Smuzhiyun 	temp_fea = ea_response_data->list;
6283*4882a593Smuzhiyun 	temp_ptr = (char *)temp_fea;
6284*4882a593Smuzhiyun 	while (list_len > 0) {
6285*4882a593Smuzhiyun 		unsigned int name_len;
6286*4882a593Smuzhiyun 		__u16 value_len;
6287*4882a593Smuzhiyun 
6288*4882a593Smuzhiyun 		list_len -= 4;
6289*4882a593Smuzhiyun 		temp_ptr += 4;
6290*4882a593Smuzhiyun 		/* make sure we can read name_len and value_len */
6291*4882a593Smuzhiyun 		if (list_len < 0) {
6292*4882a593Smuzhiyun 			cifs_dbg(FYI, "EA entry goes beyond length of list\n");
6293*4882a593Smuzhiyun 			rc = -EIO;
6294*4882a593Smuzhiyun 			goto QAllEAsOut;
6295*4882a593Smuzhiyun 		}
6296*4882a593Smuzhiyun 
6297*4882a593Smuzhiyun 		name_len = temp_fea->name_len;
6298*4882a593Smuzhiyun 		value_len = le16_to_cpu(temp_fea->value_len);
6299*4882a593Smuzhiyun 		list_len -= name_len + 1 + value_len;
6300*4882a593Smuzhiyun 		if (list_len < 0) {
6301*4882a593Smuzhiyun 			cifs_dbg(FYI, "EA entry goes beyond length of list\n");
6302*4882a593Smuzhiyun 			rc = -EIO;
6303*4882a593Smuzhiyun 			goto QAllEAsOut;
6304*4882a593Smuzhiyun 		}
6305*4882a593Smuzhiyun 
6306*4882a593Smuzhiyun 		if (ea_name) {
6307*4882a593Smuzhiyun 			if (ea_name_len == name_len &&
6308*4882a593Smuzhiyun 			    memcmp(ea_name, temp_ptr, name_len) == 0) {
6309*4882a593Smuzhiyun 				temp_ptr += name_len + 1;
6310*4882a593Smuzhiyun 				rc = value_len;
6311*4882a593Smuzhiyun 				if (buf_size == 0)
6312*4882a593Smuzhiyun 					goto QAllEAsOut;
6313*4882a593Smuzhiyun 				if ((size_t)value_len > buf_size) {
6314*4882a593Smuzhiyun 					rc = -ERANGE;
6315*4882a593Smuzhiyun 					goto QAllEAsOut;
6316*4882a593Smuzhiyun 				}
6317*4882a593Smuzhiyun 				memcpy(EAData, temp_ptr, value_len);
6318*4882a593Smuzhiyun 				goto QAllEAsOut;
6319*4882a593Smuzhiyun 			}
6320*4882a593Smuzhiyun 		} else {
6321*4882a593Smuzhiyun 			/* account for prefix user. and trailing null */
6322*4882a593Smuzhiyun 			rc += (5 + 1 + name_len);
6323*4882a593Smuzhiyun 			if (rc < (int) buf_size) {
6324*4882a593Smuzhiyun 				memcpy(EAData, "user.", 5);
6325*4882a593Smuzhiyun 				EAData += 5;
6326*4882a593Smuzhiyun 				memcpy(EAData, temp_ptr, name_len);
6327*4882a593Smuzhiyun 				EAData += name_len;
6328*4882a593Smuzhiyun 				/* null terminate name */
6329*4882a593Smuzhiyun 				*EAData = 0;
6330*4882a593Smuzhiyun 				++EAData;
6331*4882a593Smuzhiyun 			} else if (buf_size == 0) {
6332*4882a593Smuzhiyun 				/* skip copy - calc size only */
6333*4882a593Smuzhiyun 			} else {
6334*4882a593Smuzhiyun 				/* stop before overrun buffer */
6335*4882a593Smuzhiyun 				rc = -ERANGE;
6336*4882a593Smuzhiyun 				break;
6337*4882a593Smuzhiyun 			}
6338*4882a593Smuzhiyun 		}
6339*4882a593Smuzhiyun 		temp_ptr += name_len + 1 + value_len;
6340*4882a593Smuzhiyun 		temp_fea = (struct fea *)temp_ptr;
6341*4882a593Smuzhiyun 	}
6342*4882a593Smuzhiyun 
6343*4882a593Smuzhiyun 	/* didn't find the named attribute */
6344*4882a593Smuzhiyun 	if (ea_name)
6345*4882a593Smuzhiyun 		rc = -ENODATA;
6346*4882a593Smuzhiyun 
6347*4882a593Smuzhiyun QAllEAsOut:
6348*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
6349*4882a593Smuzhiyun 	if (rc == -EAGAIN)
6350*4882a593Smuzhiyun 		goto QAllEAsRetry;
6351*4882a593Smuzhiyun 
6352*4882a593Smuzhiyun 	return (ssize_t)rc;
6353*4882a593Smuzhiyun }
6354*4882a593Smuzhiyun 
6355*4882a593Smuzhiyun int
CIFSSMBSetEA(const unsigned int xid,struct cifs_tcon * tcon,const char * fileName,const char * ea_name,const void * ea_value,const __u16 ea_value_len,const struct nls_table * nls_codepage,struct cifs_sb_info * cifs_sb)6356*4882a593Smuzhiyun CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
6357*4882a593Smuzhiyun 	     const char *fileName, const char *ea_name, const void *ea_value,
6358*4882a593Smuzhiyun 	     const __u16 ea_value_len, const struct nls_table *nls_codepage,
6359*4882a593Smuzhiyun 	     struct cifs_sb_info *cifs_sb)
6360*4882a593Smuzhiyun {
6361*4882a593Smuzhiyun 	struct smb_com_transaction2_spi_req *pSMB = NULL;
6362*4882a593Smuzhiyun 	struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
6363*4882a593Smuzhiyun 	struct fealist *parm_data;
6364*4882a593Smuzhiyun 	int name_len;
6365*4882a593Smuzhiyun 	int rc = 0;
6366*4882a593Smuzhiyun 	int bytes_returned = 0;
6367*4882a593Smuzhiyun 	__u16 params, param_offset, byte_count, offset, count;
6368*4882a593Smuzhiyun 	int remap = cifs_remap(cifs_sb);
6369*4882a593Smuzhiyun 
6370*4882a593Smuzhiyun 	cifs_dbg(FYI, "In SetEA\n");
6371*4882a593Smuzhiyun SetEARetry:
6372*4882a593Smuzhiyun 	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
6373*4882a593Smuzhiyun 		      (void **) &pSMBr);
6374*4882a593Smuzhiyun 	if (rc)
6375*4882a593Smuzhiyun 		return rc;
6376*4882a593Smuzhiyun 
6377*4882a593Smuzhiyun 	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
6378*4882a593Smuzhiyun 		name_len =
6379*4882a593Smuzhiyun 		    cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
6380*4882a593Smuzhiyun 				       PATH_MAX, nls_codepage, remap);
6381*4882a593Smuzhiyun 		name_len++;	/* trailing null */
6382*4882a593Smuzhiyun 		name_len *= 2;
6383*4882a593Smuzhiyun 	} else {
6384*4882a593Smuzhiyun 		name_len = copy_path_name(pSMB->FileName, fileName);
6385*4882a593Smuzhiyun 	}
6386*4882a593Smuzhiyun 
6387*4882a593Smuzhiyun 	params = 6 + name_len;
6388*4882a593Smuzhiyun 
6389*4882a593Smuzhiyun 	/* done calculating parms using name_len of file name,
6390*4882a593Smuzhiyun 	now use name_len to calculate length of ea name
6391*4882a593Smuzhiyun 	we are going to create in the inode xattrs */
6392*4882a593Smuzhiyun 	if (ea_name == NULL)
6393*4882a593Smuzhiyun 		name_len = 0;
6394*4882a593Smuzhiyun 	else
6395*4882a593Smuzhiyun 		name_len = strnlen(ea_name, 255);
6396*4882a593Smuzhiyun 
6397*4882a593Smuzhiyun 	count = sizeof(*parm_data) + ea_value_len + name_len;
6398*4882a593Smuzhiyun 	pSMB->MaxParameterCount = cpu_to_le16(2);
6399*4882a593Smuzhiyun 	/* BB find max SMB PDU from sess */
6400*4882a593Smuzhiyun 	pSMB->MaxDataCount = cpu_to_le16(1000);
6401*4882a593Smuzhiyun 	pSMB->MaxSetupCount = 0;
6402*4882a593Smuzhiyun 	pSMB->Reserved = 0;
6403*4882a593Smuzhiyun 	pSMB->Flags = 0;
6404*4882a593Smuzhiyun 	pSMB->Timeout = 0;
6405*4882a593Smuzhiyun 	pSMB->Reserved2 = 0;
6406*4882a593Smuzhiyun 	param_offset = offsetof(struct smb_com_transaction2_spi_req,
6407*4882a593Smuzhiyun 				InformationLevel) - 4;
6408*4882a593Smuzhiyun 	offset = param_offset + params;
6409*4882a593Smuzhiyun 	pSMB->InformationLevel =
6410*4882a593Smuzhiyun 		cpu_to_le16(SMB_SET_FILE_EA);
6411*4882a593Smuzhiyun 
6412*4882a593Smuzhiyun 	parm_data = (void *)pSMB + offsetof(struct smb_hdr, Protocol) + offset;
6413*4882a593Smuzhiyun 	pSMB->ParameterOffset = cpu_to_le16(param_offset);
6414*4882a593Smuzhiyun 	pSMB->DataOffset = cpu_to_le16(offset);
6415*4882a593Smuzhiyun 	pSMB->SetupCount = 1;
6416*4882a593Smuzhiyun 	pSMB->Reserved3 = 0;
6417*4882a593Smuzhiyun 	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
6418*4882a593Smuzhiyun 	byte_count = 3 /* pad */  + params + count;
6419*4882a593Smuzhiyun 	pSMB->DataCount = cpu_to_le16(count);
6420*4882a593Smuzhiyun 	parm_data->list_len = cpu_to_le32(count);
6421*4882a593Smuzhiyun 	parm_data->list[0].EA_flags = 0;
6422*4882a593Smuzhiyun 	/* we checked above that name len is less than 255 */
6423*4882a593Smuzhiyun 	parm_data->list[0].name_len = (__u8)name_len;
6424*4882a593Smuzhiyun 	/* EA names are always ASCII */
6425*4882a593Smuzhiyun 	if (ea_name)
6426*4882a593Smuzhiyun 		strncpy(parm_data->list[0].name, ea_name, name_len);
6427*4882a593Smuzhiyun 	parm_data->list[0].name[name_len] = 0;
6428*4882a593Smuzhiyun 	parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
6429*4882a593Smuzhiyun 	/* caller ensures that ea_value_len is less than 64K but
6430*4882a593Smuzhiyun 	we need to ensure that it fits within the smb */
6431*4882a593Smuzhiyun 
6432*4882a593Smuzhiyun 	/*BB add length check to see if it would fit in
6433*4882a593Smuzhiyun 	     negotiated SMB buffer size BB */
6434*4882a593Smuzhiyun 	/* if (ea_value_len > buffer_size - 512 (enough for header)) */
6435*4882a593Smuzhiyun 	if (ea_value_len)
6436*4882a593Smuzhiyun 		memcpy(parm_data->list[0].name+name_len+1,
6437*4882a593Smuzhiyun 		       ea_value, ea_value_len);
6438*4882a593Smuzhiyun 
6439*4882a593Smuzhiyun 	pSMB->TotalDataCount = pSMB->DataCount;
6440*4882a593Smuzhiyun 	pSMB->ParameterCount = cpu_to_le16(params);
6441*4882a593Smuzhiyun 	pSMB->TotalParameterCount = pSMB->ParameterCount;
6442*4882a593Smuzhiyun 	pSMB->Reserved4 = 0;
6443*4882a593Smuzhiyun 	inc_rfc1001_len(pSMB, byte_count);
6444*4882a593Smuzhiyun 	pSMB->ByteCount = cpu_to_le16(byte_count);
6445*4882a593Smuzhiyun 	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
6446*4882a593Smuzhiyun 			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
6447*4882a593Smuzhiyun 	if (rc)
6448*4882a593Smuzhiyun 		cifs_dbg(FYI, "SetPathInfo (EA) returned %d\n", rc);
6449*4882a593Smuzhiyun 
6450*4882a593Smuzhiyun 	cifs_buf_release(pSMB);
6451*4882a593Smuzhiyun 
6452*4882a593Smuzhiyun 	if (rc == -EAGAIN)
6453*4882a593Smuzhiyun 		goto SetEARetry;
6454*4882a593Smuzhiyun 
6455*4882a593Smuzhiyun 	return rc;
6456*4882a593Smuzhiyun }
6457*4882a593Smuzhiyun #endif
6458