blob: 675041a6949c281d9c4819dbc84f9253900fed62 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/cifssmb.c
3 *
Steve Frenchf19159d2010-04-21 04:12:10 +00004 * Copyright (C) International Business Machines Corp., 2002,2010
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Author(s): Steve French (sfrench@us.ibm.com)
6 *
7 * Contains the routines for constructing the SMB PDUs themselves
8 *
9 * This library is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published
11 * by the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */
25 /* These are mostly routines that operate on a pathname, or on a tree id */
26 /* (mounted volume), but there are eight handle based routines which must be */
Steve French2dd29d32007-04-23 22:07:35 +000027 /* treated slightly differently for reconnection purposes since we never */
28 /* want to reuse a stale file handle and only the caller knows the file info */
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#include <linux/fs.h>
31#include <linux/kernel.h>
32#include <linux/vfs.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090033#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <linux/posix_acl_xattr.h>
35#include <asm/uaccess.h>
36#include "cifspdu.h"
37#include "cifsglob.h"
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +000038#include "cifsacl.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include "cifsproto.h"
40#include "cifs_unicode.h"
41#include "cifs_debug.h"
42
43#ifdef CONFIG_CIFS_POSIX
44static struct {
45 int index;
46 char *name;
47} protocols[] = {
Steve French39798772006-05-31 22:40:51 +000048#ifdef CONFIG_CIFS_WEAK_PW_HASH
49 {LANMAN_PROT, "\2LM1.2X002"},
Steve French9ac00b72006-09-30 04:13:17 +000050 {LANMAN2_PROT, "\2LANMAN2.1"},
Steve French39798772006-05-31 22:40:51 +000051#endif /* weak password hashing for legacy clients */
Steve French50c2f752007-07-13 00:33:32 +000052 {CIFS_PROT, "\2NT LM 0.12"},
Steve French39798772006-05-31 22:40:51 +000053 {POSIX_PROT, "\2POSIX 2"},
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 {BAD_PROT, "\2"}
55};
56#else
57static struct {
58 int index;
59 char *name;
60} protocols[] = {
Steve French39798772006-05-31 22:40:51 +000061#ifdef CONFIG_CIFS_WEAK_PW_HASH
62 {LANMAN_PROT, "\2LM1.2X002"},
Steve French18f75ca2006-10-01 03:13:01 +000063 {LANMAN2_PROT, "\2LANMAN2.1"},
Steve French39798772006-05-31 22:40:51 +000064#endif /* weak password hashing for legacy clients */
Steve French790fe572007-07-07 19:25:05 +000065 {CIFS_PROT, "\2NT LM 0.12"},
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 {BAD_PROT, "\2"}
67};
68#endif
69
Steve French39798772006-05-31 22:40:51 +000070/* define the number of elements in the cifs dialect array */
71#ifdef CONFIG_CIFS_POSIX
72#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French9ac00b72006-09-30 04:13:17 +000073#define CIFS_NUM_PROT 4
Steve French39798772006-05-31 22:40:51 +000074#else
75#define CIFS_NUM_PROT 2
76#endif /* CIFS_WEAK_PW_HASH */
77#else /* not posix */
78#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French9ac00b72006-09-30 04:13:17 +000079#define CIFS_NUM_PROT 3
Steve French39798772006-05-31 22:40:51 +000080#else
81#define CIFS_NUM_PROT 1
82#endif /* CONFIG_CIFS_WEAK_PW_HASH */
83#endif /* CIFS_POSIX */
84
Linus Torvalds1da177e2005-04-16 15:20:36 -070085/* Mark as invalid, all open files on tree connections since they
86 were closed when session to server was lost */
Steve French790fe572007-07-07 19:25:05 +000087static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088{
89 struct cifsFileInfo *open_file = NULL;
Steve French790fe572007-07-07 19:25:05 +000090 struct list_head *tmp;
91 struct list_head *tmp1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
93/* list all files open on tree connection and mark them invalid */
Jeff Layton44772882010-10-15 15:34:03 -040094 spin_lock(&cifs_file_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
Steve French790fe572007-07-07 19:25:05 +000096 open_file = list_entry(tmp, struct cifsFileInfo, tlist);
Steve Frenchad8b15f2008-08-08 21:10:16 +000097 open_file->invalidHandle = true;
Jeff Layton3bc303c2009-09-21 06:47:50 -040098 open_file->oplock_break_cancelled = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 }
Jeff Layton44772882010-10-15 15:34:03 -0400100 spin_unlock(&cifs_file_list_lock);
Steve French09d1db52005-04-28 22:41:08 -0700101 /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
102 to this tcon */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103}
104
Jeff Layton9162ab22009-09-03 12:07:17 -0400105/* reconnect the socket, tcon, and smb session if needed */
106static int
107cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)
108{
109 int rc = 0;
110 struct cifsSesInfo *ses;
111 struct TCP_Server_Info *server;
112 struct nls_table *nls_codepage;
113
114 /*
115 * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
116 * tcp and smb session status done differently for those three - in the
117 * calling routine
118 */
119 if (!tcon)
120 return 0;
121
122 ses = tcon->ses;
123 server = ses->server;
124
125 /*
126 * only tree disconnect, open, and write, (and ulogoff which does not
127 * have tcon) are allowed as we start force umount
128 */
129 if (tcon->tidStatus == CifsExiting) {
130 if (smb_command != SMB_COM_WRITE_ANDX &&
131 smb_command != SMB_COM_OPEN_ANDX &&
132 smb_command != SMB_COM_TREE_DISCONNECT) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000133 cFYI(1, "can not send cmd %d while umounting",
134 smb_command);
Jeff Layton9162ab22009-09-03 12:07:17 -0400135 return -ENODEV;
136 }
137 }
138
139 if (ses->status == CifsExiting)
140 return -EIO;
141
142 /*
143 * Give demultiplex thread up to 10 seconds to reconnect, should be
144 * greater than cifs socket timeout which is 7 seconds
145 */
146 while (server->tcpStatus == CifsNeedReconnect) {
147 wait_event_interruptible_timeout(server->response_q,
148 (server->tcpStatus == CifsGood), 10 * HZ);
149
150 /* is TCP session is reestablished now ?*/
151 if (server->tcpStatus != CifsNeedReconnect)
152 break;
153
154 /*
155 * on "soft" mounts we wait once. Hard mounts keep
156 * retrying until process is killed or server comes
157 * back on-line
158 */
159 if (!tcon->retry || ses->status == CifsExiting) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000160 cFYI(1, "gave up waiting on reconnect in smb_init");
Jeff Layton9162ab22009-09-03 12:07:17 -0400161 return -EHOSTDOWN;
162 }
163 }
164
165 if (!ses->need_reconnect && !tcon->need_reconnect)
166 return 0;
167
168 nls_codepage = load_nls_default();
169
170 /*
171 * need to prevent multiple threads trying to simultaneously
172 * reconnect the same SMB session
173 */
Steve Frenchd7b619c2010-02-25 05:36:46 +0000174 mutex_lock(&ses->session_mutex);
Jeff Layton198b5682010-04-24 07:57:48 -0400175 rc = cifs_negotiate_protocol(0, ses);
176 if (rc == 0 && ses->need_reconnect)
Jeff Layton9162ab22009-09-03 12:07:17 -0400177 rc = cifs_setup_session(0, ses, nls_codepage);
178
179 /* do we need to reconnect tcon? */
180 if (rc || !tcon->need_reconnect) {
Steve Frenchd7b619c2010-02-25 05:36:46 +0000181 mutex_unlock(&ses->session_mutex);
Jeff Layton9162ab22009-09-03 12:07:17 -0400182 goto out;
183 }
184
185 mark_open_files_invalid(tcon);
186 rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage);
Steve Frenchd7b619c2010-02-25 05:36:46 +0000187 mutex_unlock(&ses->session_mutex);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000188 cFYI(1, "reconnect tcon rc = %d", rc);
Jeff Layton9162ab22009-09-03 12:07:17 -0400189
190 if (rc)
191 goto out;
192
193 /*
194 * FIXME: check if wsize needs updated due to negotiated smb buffer
195 * size shrinking
196 */
197 atomic_inc(&tconInfoReconnectCount);
198
199 /* tell server Unix caps we support */
200 if (ses->capabilities & CAP_UNIX)
201 reset_cifs_unix_caps(0, tcon, NULL, NULL);
202
203 /*
204 * Removed call to reopen open files here. It is safer (and faster) to
205 * reopen files one at a time as needed in read and write.
206 *
207 * FIXME: what about file locks? don't we need to reclaim them ASAP?
208 */
209
210out:
211 /*
212 * Check if handle based operation so we know whether we can continue
213 * or not without returning to caller to reset file handle
214 */
215 switch (smb_command) {
216 case SMB_COM_READ_ANDX:
217 case SMB_COM_WRITE_ANDX:
218 case SMB_COM_CLOSE:
219 case SMB_COM_FIND_CLOSE2:
220 case SMB_COM_LOCKING_ANDX:
221 rc = -EAGAIN;
222 }
223
224 unload_nls(nls_codepage);
225 return rc;
226}
227
Steve Frenchad7a2922008-02-07 23:25:02 +0000228/* Allocate and return pointer to an SMB request buffer, and set basic
229 SMB information in the SMB header. If the return code is zero, this
230 function must have filled in request_buf pointer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231static int
232small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000233 void **request_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234{
Jeff Laytonf5695992010-09-29 15:27:08 -0400235 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
Jeff Layton9162ab22009-09-03 12:07:17 -0400237 rc = cifs_reconnect_tcon(tcon, smb_command);
Steve French790fe572007-07-07 19:25:05 +0000238 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 return rc;
240
241 *request_buf = cifs_small_buf_get();
242 if (*request_buf == NULL) {
243 /* BB should we add a retry in here if not a writepage? */
244 return -ENOMEM;
245 }
246
Steve French63135e02007-07-17 17:34:02 +0000247 header_assemble((struct smb_hdr *) *request_buf, smb_command,
Steve Frenchc18c8422007-07-18 23:21:09 +0000248 tcon, wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Steve French790fe572007-07-07 19:25:05 +0000250 if (tcon != NULL)
251 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700252
Jeff Laytonf5695992010-09-29 15:27:08 -0400253 return 0;
Steve French5815449d2006-02-14 01:36:20 +0000254}
255
Steve French12b3b8f2006-02-09 21:12:47 +0000256int
Steve French50c2f752007-07-13 00:33:32 +0000257small_smb_init_no_tc(const int smb_command, const int wct,
Steve French5815449d2006-02-14 01:36:20 +0000258 struct cifsSesInfo *ses, void **request_buf)
Steve French12b3b8f2006-02-09 21:12:47 +0000259{
260 int rc;
Steve French50c2f752007-07-13 00:33:32 +0000261 struct smb_hdr *buffer;
Steve French12b3b8f2006-02-09 21:12:47 +0000262
Steve French5815449d2006-02-14 01:36:20 +0000263 rc = small_smb_init(smb_command, wct, NULL, request_buf);
Steve French790fe572007-07-07 19:25:05 +0000264 if (rc)
Steve French12b3b8f2006-02-09 21:12:47 +0000265 return rc;
266
Steve French04fdabe2006-02-10 05:52:50 +0000267 buffer = (struct smb_hdr *)*request_buf;
Steve French12b3b8f2006-02-09 21:12:47 +0000268 buffer->Mid = GetNextMid(ses->server);
269 if (ses->capabilities & CAP_UNICODE)
270 buffer->Flags2 |= SMBFLG2_UNICODE;
Steve French04fdabe2006-02-10 05:52:50 +0000271 if (ses->capabilities & CAP_STATUS32)
Steve French12b3b8f2006-02-09 21:12:47 +0000272 buffer->Flags2 |= SMBFLG2_ERR_STATUS;
273
274 /* uid, tid can stay at zero as set in header assemble */
275
Steve French50c2f752007-07-13 00:33:32 +0000276 /* BB add support for turning on the signing when
Steve French12b3b8f2006-02-09 21:12:47 +0000277 this function is used after 1st of session setup requests */
278
279 return rc;
280}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
282/* If the return code is zero, this function must fill in request_buf pointer */
283static int
Jeff Laytonf5695992010-09-29 15:27:08 -0400284__smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
285 void **request_buf, void **response_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 *request_buf = cifs_buf_get();
288 if (*request_buf == NULL) {
289 /* BB should we add a retry in here if not a writepage? */
290 return -ENOMEM;
291 }
292 /* Although the original thought was we needed the response buf for */
293 /* potential retries of smb operations it turns out we can determine */
294 /* from the mid flags when the request buffer can be resent without */
295 /* having to use a second distinct buffer for the response */
Steve French790fe572007-07-07 19:25:05 +0000296 if (response_buf)
Steve French50c2f752007-07-13 00:33:32 +0000297 *response_buf = *request_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299 header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000300 wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
Steve French790fe572007-07-07 19:25:05 +0000302 if (tcon != NULL)
303 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700304
Jeff Laytonf5695992010-09-29 15:27:08 -0400305 return 0;
306}
307
308/* If the return code is zero, this function must fill in request_buf pointer */
309static int
310smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
311 void **request_buf, void **response_buf)
312{
313 int rc;
314
315 rc = cifs_reconnect_tcon(tcon, smb_command);
316 if (rc)
317 return rc;
318
319 return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
320}
321
322static int
323smb_init_no_reconnect(int smb_command, int wct, struct cifsTconInfo *tcon,
324 void **request_buf, void **response_buf)
325{
326 if (tcon->ses->need_reconnect || tcon->need_reconnect)
327 return -EHOSTDOWN;
328
329 return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330}
331
Steve French50c2f752007-07-13 00:33:32 +0000332static int validate_t2(struct smb_t2_rsp *pSMB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
Jeff Layton12df83c2011-01-20 13:36:51 -0500334 unsigned int total_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
Jeff Layton12df83c2011-01-20 13:36:51 -0500336 /* check for plausible wct */
337 if (pSMB->hdr.WordCount < 10)
338 goto vt2_err;
339
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 /* check for parm and data offset going beyond end of smb */
Jeff Layton12df83c2011-01-20 13:36:51 -0500341 if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 ||
342 get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024)
343 goto vt2_err;
344
345 /* check that bcc is at least as big as parms + data */
346 /* check that bcc is less than negotiated smb buffer */
347 total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount);
348 if (total_size >= 512)
349 goto vt2_err;
350
351 total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount);
352 if (total_size > get_bcc(&pSMB->hdr) ||
353 total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE)
354 goto vt2_err;
355
356 return 0;
357vt2_err:
Steve French50c2f752007-07-13 00:33:32 +0000358 cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 sizeof(struct smb_t2_rsp) + 16);
Jeff Layton12df83c2011-01-20 13:36:51 -0500360 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361}
Jeff Layton690c5222011-01-20 13:36:51 -0500362
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363int
364CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
365{
366 NEGOTIATE_REQ *pSMB;
367 NEGOTIATE_RSP *pSMBr;
368 int rc = 0;
369 int bytes_returned;
Steve French39798772006-05-31 22:40:51 +0000370 int i;
Steve French50c2f752007-07-13 00:33:32 +0000371 struct TCP_Server_Info *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 u16 count;
Steve French750d1152006-06-27 06:28:30 +0000373 unsigned int secFlags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
Steve French790fe572007-07-07 19:25:05 +0000375 if (ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 server = ses->server;
377 else {
378 rc = -EIO;
379 return rc;
380 }
381 rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
382 (void **) &pSMB, (void **) &pSMBr);
383 if (rc)
384 return rc;
Steve French750d1152006-06-27 06:28:30 +0000385
386 /* if any of auth flags (ie not sign or seal) are overriden use them */
Steve French790fe572007-07-07 19:25:05 +0000387 if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
Steve French762e5ab2007-06-28 18:41:42 +0000388 secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */
Steve French750d1152006-06-27 06:28:30 +0000389 else /* if override flags set only sign/seal OR them with global auth */
Jeff Layton04912d62010-04-24 07:57:45 -0400390 secFlags = global_secflags | ses->overrideSecFlg;
Steve French750d1152006-06-27 06:28:30 +0000391
Joe Perchesb6b38f72010-04-21 03:50:45 +0000392 cFYI(1, "secFlags 0x%x", secFlags);
Steve Frenchf40c5622006-06-28 00:13:38 +0000393
Steve French1982c342005-08-17 12:38:22 -0700394 pSMB->hdr.Mid = GetNextMid(server);
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000395 pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
Steve Frencha0136892007-10-04 20:05:09 +0000396
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000397 if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
Steve French254e55e2006-06-04 05:53:15 +0000398 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Steve Frencha0136892007-10-04 20:05:09 +0000399 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000400 cFYI(1, "Kerberos only mechanism, enable extended security");
Steve Frencha0136892007-10-04 20:05:09 +0000401 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Jeff Laytonb4d6fcf2011-01-07 11:30:28 -0500402 } else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
Steve Frenchac683922009-05-06 04:16:04 +0000403 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
404 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000405 cFYI(1, "NTLMSSP only mechanism, enable extended security");
Steve Frenchac683922009-05-06 04:16:04 +0000406 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
407 }
Steve French50c2f752007-07-13 00:33:32 +0000408
Steve French39798772006-05-31 22:40:51 +0000409 count = 0;
Steve French50c2f752007-07-13 00:33:32 +0000410 for (i = 0; i < CIFS_NUM_PROT; i++) {
Steve French39798772006-05-31 22:40:51 +0000411 strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
412 count += strlen(protocols[i].name) + 1;
413 /* null at end of source and target buffers anyway */
414 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 pSMB->hdr.smb_buf_length += count;
416 pSMB->ByteCount = cpu_to_le16(count);
417
418 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
419 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French50c2f752007-07-13 00:33:32 +0000420 if (rc != 0)
Steve French254e55e2006-06-04 05:53:15 +0000421 goto neg_err_exit;
422
Jeff Layton9bf67e52010-04-24 07:57:46 -0400423 server->dialect = le16_to_cpu(pSMBr->DialectIndex);
424 cFYI(1, "Dialect: %d", server->dialect);
Steve French254e55e2006-06-04 05:53:15 +0000425 /* Check wct = 1 error case */
Jeff Layton9bf67e52010-04-24 07:57:46 -0400426 if ((pSMBr->hdr.WordCount < 13) || (server->dialect == BAD_PROT)) {
Steve French254e55e2006-06-04 05:53:15 +0000427 /* core returns wct = 1, but we do not ask for core - otherwise
Steve French50c2f752007-07-13 00:33:32 +0000428 small wct just comes when dialect index is -1 indicating we
Steve French254e55e2006-06-04 05:53:15 +0000429 could not negotiate a common dialect */
430 rc = -EOPNOTSUPP;
431 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000432#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French790fe572007-07-07 19:25:05 +0000433 } else if ((pSMBr->hdr.WordCount == 13)
Jeff Layton9bf67e52010-04-24 07:57:46 -0400434 && ((server->dialect == LANMAN_PROT)
435 || (server->dialect == LANMAN2_PROT))) {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000436 __s16 tmp;
Steve French50c2f752007-07-13 00:33:32 +0000437 struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
Steve French254e55e2006-06-04 05:53:15 +0000438
Steve French790fe572007-07-07 19:25:05 +0000439 if ((secFlags & CIFSSEC_MAY_LANMAN) ||
Steve French750d1152006-06-27 06:28:30 +0000440 (secFlags & CIFSSEC_MAY_PLNTXT))
Steve French254e55e2006-06-04 05:53:15 +0000441 server->secType = LANMAN;
442 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000443 cERROR(1, "mount failed weak security disabled"
444 " in /proc/fs/cifs/SecurityFlags");
Steve French39798772006-05-31 22:40:51 +0000445 rc = -EOPNOTSUPP;
446 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000447 }
Steve French254e55e2006-06-04 05:53:15 +0000448 server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
449 server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
450 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
Steve French39798772006-05-31 22:40:51 +0000451 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000452 server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
Steve French254e55e2006-06-04 05:53:15 +0000453 /* even though we do not use raw we might as well set this
454 accurately, in case we ever find a need for it */
Steve French790fe572007-07-07 19:25:05 +0000455 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
Steve Frencheca6acf2009-02-20 05:43:09 +0000456 server->max_rw = 0xFF00;
Steve French254e55e2006-06-04 05:53:15 +0000457 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
458 } else {
Steve Frencheca6acf2009-02-20 05:43:09 +0000459 server->max_rw = 0;/* do not need to use raw anyway */
Steve French254e55e2006-06-04 05:53:15 +0000460 server->capabilities = CAP_MPX_MODE;
461 }
Steve Frenchb815f1e52006-10-02 05:53:29 +0000462 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
Steve French1a70d652006-10-02 05:59:18 +0000463 if (tmp == -1) {
Steve French25ee4a92006-09-30 00:54:23 +0000464 /* OS/2 often does not set timezone therefore
465 * we must use server time to calc time zone.
Steve Frenchb815f1e52006-10-02 05:53:29 +0000466 * Could deviate slightly from the right zone.
467 * Smallest defined timezone difference is 15 minutes
468 * (i.e. Nepal). Rounding up/down is done to match
469 * this requirement.
Steve French25ee4a92006-09-30 00:54:23 +0000470 */
Steve Frenchb815f1e52006-10-02 05:53:29 +0000471 int val, seconds, remain, result;
Steve French25ee4a92006-09-30 00:54:23 +0000472 struct timespec ts, utc;
473 utc = CURRENT_TIME;
Jeff Laytonc4a2c082009-05-27 09:37:33 -0400474 ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
475 rsp->SrvTime.Time, 0);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000476 cFYI(1, "SrvTime %d sec since 1970 (utc: %d) diff: %d",
Steve French50c2f752007-07-13 00:33:32 +0000477 (int)ts.tv_sec, (int)utc.tv_sec,
Joe Perchesb6b38f72010-04-21 03:50:45 +0000478 (int)(utc.tv_sec - ts.tv_sec));
Steve Frenchb815f1e52006-10-02 05:53:29 +0000479 val = (int)(utc.tv_sec - ts.tv_sec);
Andre Haupt8594c152007-08-30 20:18:41 +0000480 seconds = abs(val);
Steve French947a5062006-10-02 05:55:25 +0000481 result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000482 remain = seconds % MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000483 if (remain >= (MIN_TZ_ADJ / 2))
Steve Frenchb815f1e52006-10-02 05:53:29 +0000484 result += MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000485 if (val < 0)
Steve Frenchad7a2922008-02-07 23:25:02 +0000486 result = -result;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000487 server->timeAdj = result;
Steve French25ee4a92006-09-30 00:54:23 +0000488 } else {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000489 server->timeAdj = (int)tmp;
490 server->timeAdj *= 60; /* also in seconds */
Steve French25ee4a92006-09-30 00:54:23 +0000491 }
Joe Perchesb6b38f72010-04-21 03:50:45 +0000492 cFYI(1, "server->timeAdj: %d seconds", server->timeAdj);
Steve French25ee4a92006-09-30 00:54:23 +0000493
Steve French39798772006-05-31 22:40:51 +0000494
Steve French254e55e2006-06-04 05:53:15 +0000495 /* BB get server time for time conversions and add
Steve French50c2f752007-07-13 00:33:32 +0000496 code to use it and timezone since this is not UTC */
Steve French39798772006-05-31 22:40:51 +0000497
Steve French50c2f752007-07-13 00:33:32 +0000498 if (rsp->EncryptionKeyLength ==
Steve French25ee4a92006-09-30 00:54:23 +0000499 cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
Shirish Pargaonkard3ba50b2010-10-27 15:20:36 -0500500 memcpy(ses->server->cryptkey, rsp->EncryptionKey,
Steve French254e55e2006-06-04 05:53:15 +0000501 CIFS_CRYPTO_KEY_SIZE);
502 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
503 rc = -EIO; /* need cryptkey unless plain text */
504 goto neg_err_exit;
505 }
Steve French39798772006-05-31 22:40:51 +0000506
Steve Frenchf19159d2010-04-21 04:12:10 +0000507 cFYI(1, "LANMAN negotiated");
Steve French254e55e2006-06-04 05:53:15 +0000508 /* we will not end up setting signing flags - as no signing
509 was in LANMAN and server did not return the flags on */
510 goto signing_check;
Steve French7c7b25b2006-06-01 19:20:10 +0000511#else /* weak security disabled */
Steve French790fe572007-07-07 19:25:05 +0000512 } else if (pSMBr->hdr.WordCount == 13) {
Steve Frenchf19159d2010-04-21 04:12:10 +0000513 cERROR(1, "mount failed, cifs module not built "
514 "with CIFS_WEAK_PW_HASH support");
Dan Carpenter8212cf72010-03-15 11:22:26 +0300515 rc = -EOPNOTSUPP;
Steve French7c7b25b2006-06-01 19:20:10 +0000516#endif /* WEAK_PW_HASH */
Steve French254e55e2006-06-04 05:53:15 +0000517 goto neg_err_exit;
Steve French790fe572007-07-07 19:25:05 +0000518 } else if (pSMBr->hdr.WordCount != 17) {
Steve French254e55e2006-06-04 05:53:15 +0000519 /* unknown wct */
520 rc = -EOPNOTSUPP;
521 goto neg_err_exit;
522 }
523 /* else wct == 17 NTLM */
524 server->secMode = pSMBr->SecurityMode;
Steve French790fe572007-07-07 19:25:05 +0000525 if ((server->secMode & SECMODE_USER) == 0)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000526 cFYI(1, "share mode security");
Steve French39798772006-05-31 22:40:51 +0000527
Steve French790fe572007-07-07 19:25:05 +0000528 if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000529#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French750d1152006-06-27 06:28:30 +0000530 if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000531#endif /* CIFS_WEAK_PW_HASH */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000532 cERROR(1, "Server requests plain text password"
533 " but client support disabled");
Steve French9312f672006-06-04 22:21:07 +0000534
Steve French790fe572007-07-07 19:25:05 +0000535 if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
Steve French254e55e2006-06-04 05:53:15 +0000536 server->secType = NTLMv2;
Steve French790fe572007-07-07 19:25:05 +0000537 else if (secFlags & CIFSSEC_MAY_NTLM)
Steve French254e55e2006-06-04 05:53:15 +0000538 server->secType = NTLM;
Steve French790fe572007-07-07 19:25:05 +0000539 else if (secFlags & CIFSSEC_MAY_NTLMV2)
Steve Frenchf40c5622006-06-28 00:13:38 +0000540 server->secType = NTLMv2;
Steve Frencha0136892007-10-04 20:05:09 +0000541 else if (secFlags & CIFSSEC_MAY_KRB5)
542 server->secType = Kerberos;
Steve Frenchac683922009-05-06 04:16:04 +0000543 else if (secFlags & CIFSSEC_MAY_NTLMSSP)
Steve Frenchf46c7232009-06-25 03:04:20 +0000544 server->secType = RawNTLMSSP;
Steve Frencha0136892007-10-04 20:05:09 +0000545 else if (secFlags & CIFSSEC_MAY_LANMAN)
546 server->secType = LANMAN;
547/* #ifdef CONFIG_CIFS_EXPERIMENTAL
548 else if (secFlags & CIFSSEC_MAY_PLNTXT)
549 server->secType = ??
550#endif */
551 else {
552 rc = -EOPNOTSUPP;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000553 cERROR(1, "Invalid security type");
Steve Frencha0136892007-10-04 20:05:09 +0000554 goto neg_err_exit;
555 }
556 /* else ... any others ...? */
Steve French7c7b25b2006-06-01 19:20:10 +0000557
Steve French254e55e2006-06-04 05:53:15 +0000558 /* one byte, so no need to convert this or EncryptionKeyLen from
559 little endian */
560 server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
561 /* probably no need to store and check maxvcs */
562 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000564 server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000565 cFYI(DBG2, "Max buf = %d", ses->server->maxBuf);
Steve French254e55e2006-06-04 05:53:15 +0000566 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
Steve Frenchb815f1e52006-10-02 05:53:29 +0000567 server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
568 server->timeAdj *= 60;
Steve French254e55e2006-06-04 05:53:15 +0000569 if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
Shirish Pargaonkard3ba50b2010-10-27 15:20:36 -0500570 memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey,
Steve French254e55e2006-06-04 05:53:15 +0000571 CIFS_CRYPTO_KEY_SIZE);
572 } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
573 && (pSMBr->EncryptionKeyLength == 0)) {
574 /* decode security blob */
575 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
576 rc = -EIO; /* no crypt key only if plain text pwd */
577 goto neg_err_exit;
578 }
579
580 /* BB might be helpful to save off the domain of server here */
581
Steve French50c2f752007-07-13 00:33:32 +0000582 if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
Steve French254e55e2006-06-04 05:53:15 +0000583 (server->capabilities & CAP_EXTENDED_SECURITY)) {
584 count = pSMBr->ByteCount;
Jeff Laytone187e442007-10-16 17:10:44 +0000585 if (count < 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 rc = -EIO;
Jeff Laytone187e442007-10-16 17:10:44 +0000587 goto neg_err_exit;
588 }
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530589 spin_lock(&cifs_tcp_ses_lock);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500590 if (server->srv_count > 1) {
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530591 spin_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000592 if (memcmp(server->server_GUID,
593 pSMBr->u.extended_response.
594 GUID, 16) != 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000595 cFYI(1, "server UID changed");
Steve French254e55e2006-06-04 05:53:15 +0000596 memcpy(server->server_GUID,
Jeff Laytone187e442007-10-16 17:10:44 +0000597 pSMBr->u.extended_response.GUID,
598 16);
599 }
Jeff Laytone7ddee92008-11-14 13:44:38 -0500600 } else {
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530601 spin_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000602 memcpy(server->server_GUID,
603 pSMBr->u.extended_response.GUID, 16);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500604 }
Jeff Laytone187e442007-10-16 17:10:44 +0000605
606 if (count == 16) {
607 server->secType = RawNTLMSSP;
Steve French254e55e2006-06-04 05:53:15 +0000608 } else {
609 rc = decode_negTokenInit(pSMBr->u.extended_response.
Jeff Layton26efa0b2010-04-24 07:57:49 -0400610 SecurityBlob, count - 16,
611 server);
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000612 if (rc == 1)
Jeff Laytone5459372007-11-03 05:11:06 +0000613 rc = 0;
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000614 else
Steve French254e55e2006-06-04 05:53:15 +0000615 rc = -EINVAL;
Shirish Pargaonkar2b149f12010-09-18 22:02:18 -0500616 if (server->secType == Kerberos) {
617 if (!server->sec_kerberos &&
618 !server->sec_mskerberos)
619 rc = -EOPNOTSUPP;
620 } else if (server->secType == RawNTLMSSP) {
621 if (!server->sec_ntlmssp)
622 rc = -EOPNOTSUPP;
623 } else
624 rc = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 }
Steve French254e55e2006-06-04 05:53:15 +0000626 } else
627 server->capabilities &= ~CAP_EXTENDED_SECURITY;
628
Steve French6344a422006-06-12 04:18:35 +0000629#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French254e55e2006-06-04 05:53:15 +0000630signing_check:
Steve French6344a422006-06-12 04:18:35 +0000631#endif
Steve French762e5ab2007-06-28 18:41:42 +0000632 if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
633 /* MUST_SIGN already includes the MAY_SIGN FLAG
634 so if this is zero it means that signing is disabled */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000635 cFYI(1, "Signing disabled");
Steve Frenchabb63d62007-10-18 02:58:40 +0000636 if (server->secMode & SECMODE_SIGN_REQUIRED) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000637 cERROR(1, "Server requires "
Jeff Layton7111d212007-10-16 16:50:25 +0000638 "packet signing to be enabled in "
Joe Perchesb6b38f72010-04-21 03:50:45 +0000639 "/proc/fs/cifs/SecurityFlags.");
Steve Frenchabb63d62007-10-18 02:58:40 +0000640 rc = -EOPNOTSUPP;
641 }
Steve French50c2f752007-07-13 00:33:32 +0000642 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000643 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Steve French762e5ab2007-06-28 18:41:42 +0000644 } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
645 /* signing required */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000646 cFYI(1, "Must sign - secFlags 0x%x", secFlags);
Steve French762e5ab2007-06-28 18:41:42 +0000647 if ((server->secMode &
648 (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000649 cERROR(1, "signing required but server lacks support");
Jeff38c10a12007-07-06 21:10:07 +0000650 rc = -EOPNOTSUPP;
Steve French762e5ab2007-06-28 18:41:42 +0000651 } else
652 server->secMode |= SECMODE_SIGN_REQUIRED;
653 } else {
654 /* signing optional ie CIFSSEC_MAY_SIGN */
Steve French790fe572007-07-07 19:25:05 +0000655 if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
Steve French50c2f752007-07-13 00:33:32 +0000656 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000657 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 }
Steve French50c2f752007-07-13 00:33:32 +0000659
660neg_err_exit:
Steve French4a6d87f2005-08-13 08:15:54 -0700661 cifs_buf_release(pSMB);
Steve French254e55e2006-06-04 05:53:15 +0000662
Joe Perchesb6b38f72010-04-21 03:50:45 +0000663 cFYI(1, "negprot rc %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 return rc;
665}
666
667int
668CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
669{
670 struct smb_hdr *smb_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
Joe Perchesb6b38f72010-04-21 03:50:45 +0000673 cFYI(1, "In tree disconnect");
Jeff Laytonf1987b42008-11-15 11:12:47 -0500674
675 /* BB: do we need to check this? These should never be NULL. */
676 if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
677 return -EIO;
678
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 /*
Jeff Laytonf1987b42008-11-15 11:12:47 -0500680 * No need to return error on this operation if tid invalidated and
681 * closed on server already e.g. due to tcp session crashing. Also,
682 * the tcon is no longer on the list, so no need to take lock before
683 * checking this.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 */
Steve French268875b2009-06-25 00:29:21 +0000685 if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
Steve French50c2f752007-07-13 00:33:32 +0000686 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687
Steve French50c2f752007-07-13 00:33:32 +0000688 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
Steve French09d1db52005-04-28 22:41:08 -0700689 (void **)&smb_buffer);
Jeff Laytonf1987b42008-11-15 11:12:47 -0500690 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 return rc;
Steve French133672e2007-11-13 22:41:37 +0000692
693 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000695 cFYI(1, "Tree disconnect failed %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
Steve French50c2f752007-07-13 00:33:32 +0000697 /* No need to return error on this operation if tid invalidated and
Jeff Laytonf1987b42008-11-15 11:12:47 -0500698 closed on server already e.g. due to tcp session crashing */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 if (rc == -EAGAIN)
700 rc = 0;
701
702 return rc;
703}
704
Jeff Layton766fdbb2011-01-11 07:24:21 -0500705/*
706 * This is a no-op for now. We're not really interested in the reply, but
707 * rather in the fact that the server sent one and that server->lstrp
708 * gets updated.
709 *
710 * FIXME: maybe we should consider checking that the reply matches request?
711 */
712static void
713cifs_echo_callback(struct mid_q_entry *mid)
714{
715 struct TCP_Server_Info *server = mid->callback_data;
716
717 DeleteMidQEntry(mid);
718 atomic_dec(&server->inFlight);
719 wake_up(&server->request_q);
720}
721
722int
723CIFSSMBEcho(struct TCP_Server_Info *server)
724{
725 ECHO_REQ *smb;
726 int rc = 0;
727
728 cFYI(1, "In echo request");
729
730 rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb);
731 if (rc)
732 return rc;
733
734 /* set up echo request */
735 smb->hdr.Tid = cpu_to_le16(0xffff);
736 smb->hdr.WordCount = cpu_to_le16(1);
737 smb->EchoCount = cpu_to_le16(1);
738 smb->ByteCount = cpu_to_le16(1);
739 smb->Data[0] = 'a';
740 smb->hdr.smb_buf_length += 3;
741
742 rc = cifs_call_async(server, (struct smb_hdr *)smb,
743 cifs_echo_callback, server);
744 if (rc)
745 cFYI(1, "Echo request failed: %d", rc);
746
747 cifs_small_buf_release(smb);
748
749 return rc;
750}
751
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752int
753CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
754{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 LOGOFF_ANDX_REQ *pSMB;
756 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
Joe Perchesb6b38f72010-04-21 03:50:45 +0000758 cFYI(1, "In SMBLogoff for session disconnect");
Jeff Layton14fbf502008-11-14 13:53:46 -0500759
760 /*
761 * BB: do we need to check validity of ses and server? They should
762 * always be valid since we have an active reference. If not, that
763 * should probably be a BUG()
764 */
765 if (!ses || !ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 return -EIO;
767
Steve Frenchd7b619c2010-02-25 05:36:46 +0000768 mutex_lock(&ses->session_mutex);
Steve French3b795212008-11-13 19:45:32 +0000769 if (ses->need_reconnect)
770 goto session_already_dead; /* no need to send SMBlogoff if uid
771 already closed due to reconnect */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
773 if (rc) {
Steve Frenchd7b619c2010-02-25 05:36:46 +0000774 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 return rc;
776 }
777
Steve French3b795212008-11-13 19:45:32 +0000778 pSMB->hdr.Mid = GetNextMid(ses->server);
Steve French1982c342005-08-17 12:38:22 -0700779
Steve French3b795212008-11-13 19:45:32 +0000780 if (ses->server->secMode &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
782 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783
784 pSMB->hdr.Uid = ses->Suid;
785
786 pSMB->AndXCommand = 0xFF;
Steve French133672e2007-11-13 22:41:37 +0000787 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
Steve French3b795212008-11-13 19:45:32 +0000788session_already_dead:
Steve Frenchd7b619c2010-02-25 05:36:46 +0000789 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
791 /* if session dead then we do not need to do ulogoff,
Steve French50c2f752007-07-13 00:33:32 +0000792 since server closed smb session, no sense reporting
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 error */
794 if (rc == -EAGAIN)
795 rc = 0;
796 return rc;
797}
798
799int
Steve French2d785a52007-07-15 01:48:57 +0000800CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
801 __u16 type, const struct nls_table *nls_codepage, int remap)
802{
803 TRANSACTION2_SPI_REQ *pSMB = NULL;
804 TRANSACTION2_SPI_RSP *pSMBr = NULL;
805 struct unlink_psx_rq *pRqD;
806 int name_len;
807 int rc = 0;
808 int bytes_returned = 0;
809 __u16 params, param_offset, offset, byte_count;
810
Joe Perchesb6b38f72010-04-21 03:50:45 +0000811 cFYI(1, "In POSIX delete");
Steve French2d785a52007-07-15 01:48:57 +0000812PsxDelete:
813 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
814 (void **) &pSMBr);
815 if (rc)
816 return rc;
817
818 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
819 name_len =
820 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
821 PATH_MAX, nls_codepage, remap);
822 name_len++; /* trailing null */
823 name_len *= 2;
824 } else { /* BB add path length overrun check */
825 name_len = strnlen(fileName, PATH_MAX);
826 name_len++; /* trailing null */
827 strncpy(pSMB->FileName, fileName, name_len);
828 }
829
830 params = 6 + name_len;
831 pSMB->MaxParameterCount = cpu_to_le16(2);
832 pSMB->MaxDataCount = 0; /* BB double check this with jra */
833 pSMB->MaxSetupCount = 0;
834 pSMB->Reserved = 0;
835 pSMB->Flags = 0;
836 pSMB->Timeout = 0;
837 pSMB->Reserved2 = 0;
838 param_offset = offsetof(struct smb_com_transaction2_spi_req,
839 InformationLevel) - 4;
840 offset = param_offset + params;
841
842 /* Setup pointer to Request Data (inode type) */
843 pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
844 pRqD->type = cpu_to_le16(type);
845 pSMB->ParameterOffset = cpu_to_le16(param_offset);
846 pSMB->DataOffset = cpu_to_le16(offset);
847 pSMB->SetupCount = 1;
848 pSMB->Reserved3 = 0;
849 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
850 byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
851
852 pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
853 pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
854 pSMB->ParameterCount = cpu_to_le16(params);
855 pSMB->TotalParameterCount = pSMB->ParameterCount;
856 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
857 pSMB->Reserved4 = 0;
858 pSMB->hdr.smb_buf_length += byte_count;
859 pSMB->ByteCount = cpu_to_le16(byte_count);
860 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
861 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +0000862 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000863 cFYI(1, "Posix delete returned %d", rc);
Steve French2d785a52007-07-15 01:48:57 +0000864 cifs_buf_release(pSMB);
865
866 cifs_stats_inc(&tcon->num_deletes);
867
868 if (rc == -EAGAIN)
869 goto PsxDelete;
870
871 return rc;
872}
873
874int
Steve French737b7582005-04-28 22:41:06 -0700875CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
876 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877{
878 DELETE_FILE_REQ *pSMB = NULL;
879 DELETE_FILE_RSP *pSMBr = NULL;
880 int rc = 0;
881 int bytes_returned;
882 int name_len;
883
884DelFileRetry:
885 rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
886 (void **) &pSMBr);
887 if (rc)
888 return rc;
889
890 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
891 name_len =
Steve French50c2f752007-07-13 00:33:32 +0000892 cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
Steve French737b7582005-04-28 22:41:06 -0700893 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 name_len++; /* trailing null */
895 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700896 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 name_len = strnlen(fileName, PATH_MAX);
898 name_len++; /* trailing null */
899 strncpy(pSMB->fileName, fileName, name_len);
900 }
901 pSMB->SearchAttributes =
902 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
903 pSMB->BufferFormat = 0x04;
904 pSMB->hdr.smb_buf_length += name_len + 1;
905 pSMB->ByteCount = cpu_to_le16(name_len + 1);
906 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
907 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700908 cifs_stats_inc(&tcon->num_deletes);
Steve Frenchad7a2922008-02-07 23:25:02 +0000909 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000910 cFYI(1, "Error in RMFile = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
912 cifs_buf_release(pSMB);
913 if (rc == -EAGAIN)
914 goto DelFileRetry;
915
916 return rc;
917}
918
919int
Steve French50c2f752007-07-13 00:33:32 +0000920CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
Steve French737b7582005-04-28 22:41:06 -0700921 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922{
923 DELETE_DIRECTORY_REQ *pSMB = NULL;
924 DELETE_DIRECTORY_RSP *pSMBr = NULL;
925 int rc = 0;
926 int bytes_returned;
927 int name_len;
928
Joe Perchesb6b38f72010-04-21 03:50:45 +0000929 cFYI(1, "In CIFSSMBRmDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930RmDirRetry:
931 rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
932 (void **) &pSMBr);
933 if (rc)
934 return rc;
935
936 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French737b7582005-04-28 22:41:06 -0700937 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
938 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 name_len++; /* trailing null */
940 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700941 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 name_len = strnlen(dirName, PATH_MAX);
943 name_len++; /* trailing null */
944 strncpy(pSMB->DirName, dirName, name_len);
945 }
946
947 pSMB->BufferFormat = 0x04;
948 pSMB->hdr.smb_buf_length += name_len + 1;
949 pSMB->ByteCount = cpu_to_le16(name_len + 1);
950 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
951 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700952 cifs_stats_inc(&tcon->num_rmdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000953 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000954 cFYI(1, "Error in RMDir = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
956 cifs_buf_release(pSMB);
957 if (rc == -EAGAIN)
958 goto RmDirRetry;
959 return rc;
960}
961
962int
963CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -0700964 const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965{
966 int rc = 0;
967 CREATE_DIRECTORY_REQ *pSMB = NULL;
968 CREATE_DIRECTORY_RSP *pSMBr = NULL;
969 int bytes_returned;
970 int name_len;
971
Joe Perchesb6b38f72010-04-21 03:50:45 +0000972 cFYI(1, "In CIFSSMBMkDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973MkDirRetry:
974 rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
975 (void **) &pSMBr);
976 if (rc)
977 return rc;
978
979 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +0000980 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
Steve French737b7582005-04-28 22:41:06 -0700981 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 name_len++; /* trailing null */
983 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700984 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 name_len = strnlen(name, PATH_MAX);
986 name_len++; /* trailing null */
987 strncpy(pSMB->DirName, name, name_len);
988 }
989
990 pSMB->BufferFormat = 0x04;
991 pSMB->hdr.smb_buf_length += name_len + 1;
992 pSMB->ByteCount = cpu_to_le16(name_len + 1);
993 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
994 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700995 cifs_stats_inc(&tcon->num_mkdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000996 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000997 cFYI(1, "Error in Mkdir = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -0700998
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 cifs_buf_release(pSMB);
1000 if (rc == -EAGAIN)
1001 goto MkDirRetry;
1002 return rc;
1003}
1004
Steve French2dd29d32007-04-23 22:07:35 +00001005int
1006CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
Steve Frenchad7a2922008-02-07 23:25:02 +00001007 __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
Steve French50c2f752007-07-13 00:33:32 +00001008 __u32 *pOplock, const char *name,
Steve French2dd29d32007-04-23 22:07:35 +00001009 const struct nls_table *nls_codepage, int remap)
1010{
1011 TRANSACTION2_SPI_REQ *pSMB = NULL;
1012 TRANSACTION2_SPI_RSP *pSMBr = NULL;
1013 int name_len;
1014 int rc = 0;
1015 int bytes_returned = 0;
Steve French2dd29d32007-04-23 22:07:35 +00001016 __u16 params, param_offset, offset, byte_count, count;
Steve Frenchad7a2922008-02-07 23:25:02 +00001017 OPEN_PSX_REQ *pdata;
1018 OPEN_PSX_RSP *psx_rsp;
Steve French2dd29d32007-04-23 22:07:35 +00001019
Joe Perchesb6b38f72010-04-21 03:50:45 +00001020 cFYI(1, "In POSIX Create");
Steve French2dd29d32007-04-23 22:07:35 +00001021PsxCreat:
1022 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
1023 (void **) &pSMBr);
1024 if (rc)
1025 return rc;
1026
1027 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1028 name_len =
1029 cifsConvertToUCS((__le16 *) pSMB->FileName, name,
1030 PATH_MAX, nls_codepage, remap);
1031 name_len++; /* trailing null */
1032 name_len *= 2;
1033 } else { /* BB improve the check for buffer overruns BB */
1034 name_len = strnlen(name, PATH_MAX);
1035 name_len++; /* trailing null */
1036 strncpy(pSMB->FileName, name, name_len);
1037 }
1038
1039 params = 6 + name_len;
1040 count = sizeof(OPEN_PSX_REQ);
1041 pSMB->MaxParameterCount = cpu_to_le16(2);
1042 pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
1043 pSMB->MaxSetupCount = 0;
1044 pSMB->Reserved = 0;
1045 pSMB->Flags = 0;
1046 pSMB->Timeout = 0;
1047 pSMB->Reserved2 = 0;
1048 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00001049 InformationLevel) - 4;
Steve French2dd29d32007-04-23 22:07:35 +00001050 offset = param_offset + params;
Steve French2dd29d32007-04-23 22:07:35 +00001051 pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001052 pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
Steve French2dd29d32007-04-23 22:07:35 +00001053 pdata->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00001054 pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
Steve French2dd29d32007-04-23 22:07:35 +00001055 pdata->OpenFlags = cpu_to_le32(*pOplock);
1056 pSMB->ParameterOffset = cpu_to_le16(param_offset);
1057 pSMB->DataOffset = cpu_to_le16(offset);
1058 pSMB->SetupCount = 1;
1059 pSMB->Reserved3 = 0;
1060 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
1061 byte_count = 3 /* pad */ + params + count;
1062
1063 pSMB->DataCount = cpu_to_le16(count);
1064 pSMB->ParameterCount = cpu_to_le16(params);
1065 pSMB->TotalDataCount = pSMB->DataCount;
1066 pSMB->TotalParameterCount = pSMB->ParameterCount;
1067 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1068 pSMB->Reserved4 = 0;
Steve French50c2f752007-07-13 00:33:32 +00001069 pSMB->hdr.smb_buf_length += byte_count;
Steve French2dd29d32007-04-23 22:07:35 +00001070 pSMB->ByteCount = cpu_to_le16(byte_count);
1071 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1072 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1073 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001074 cFYI(1, "Posix create returned %d", rc);
Steve French2dd29d32007-04-23 22:07:35 +00001075 goto psx_create_err;
1076 }
1077
Joe Perchesb6b38f72010-04-21 03:50:45 +00001078 cFYI(1, "copying inode info");
Steve French2dd29d32007-04-23 22:07:35 +00001079 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1080
1081 if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
1082 rc = -EIO; /* bad smb */
1083 goto psx_create_err;
1084 }
1085
1086 /* copy return information to pRetData */
Steve French50c2f752007-07-13 00:33:32 +00001087 psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
Steve French2dd29d32007-04-23 22:07:35 +00001088 + le16_to_cpu(pSMBr->t2.DataOffset));
Steve French50c2f752007-07-13 00:33:32 +00001089
Steve French2dd29d32007-04-23 22:07:35 +00001090 *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
Steve French790fe572007-07-07 19:25:05 +00001091 if (netfid)
Steve French2dd29d32007-04-23 22:07:35 +00001092 *netfid = psx_rsp->Fid; /* cifs fid stays in le */
1093 /* Let caller know file was created so we can set the mode. */
1094 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001095 if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
Steve French2dd29d32007-04-23 22:07:35 +00001096 *pOplock |= CIFS_CREATE_ACTION;
1097 /* check to make sure response data is there */
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001098 if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1099 pRetData->Type = cpu_to_le32(-1); /* unknown */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001100 cFYI(DBG2, "unknown type");
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001101 } else {
Steve French790fe572007-07-07 19:25:05 +00001102 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001103 + sizeof(FILE_UNIX_BASIC_INFO)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001104 cERROR(1, "Open response data too small");
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001105 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001106 goto psx_create_err;
1107 }
Steve French50c2f752007-07-13 00:33:32 +00001108 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001109 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001110 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001111 }
Steve French2dd29d32007-04-23 22:07:35 +00001112
1113psx_create_err:
1114 cifs_buf_release(pSMB);
1115
Steve French65bc98b2009-07-10 15:27:25 +00001116 if (posix_flags & SMB_O_DIRECTORY)
1117 cifs_stats_inc(&tcon->num_posixmkdirs);
1118 else
1119 cifs_stats_inc(&tcon->num_posixopens);
Steve French2dd29d32007-04-23 22:07:35 +00001120
1121 if (rc == -EAGAIN)
1122 goto PsxCreat;
1123
Steve French50c2f752007-07-13 00:33:32 +00001124 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001125}
1126
Steve Frencha9d02ad2005-08-24 23:06:05 -07001127static __u16 convert_disposition(int disposition)
1128{
1129 __u16 ofun = 0;
1130
1131 switch (disposition) {
1132 case FILE_SUPERSEDE:
1133 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1134 break;
1135 case FILE_OPEN:
1136 ofun = SMBOPEN_OAPPEND;
1137 break;
1138 case FILE_CREATE:
1139 ofun = SMBOPEN_OCREATE;
1140 break;
1141 case FILE_OPEN_IF:
1142 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1143 break;
1144 case FILE_OVERWRITE:
1145 ofun = SMBOPEN_OTRUNC;
1146 break;
1147 case FILE_OVERWRITE_IF:
1148 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1149 break;
1150 default:
Joe Perchesb6b38f72010-04-21 03:50:45 +00001151 cFYI(1, "unknown disposition %d", disposition);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001152 ofun = SMBOPEN_OAPPEND; /* regular open */
1153 }
1154 return ofun;
1155}
1156
Jeff Layton35fc37d2008-05-14 10:22:03 -07001157static int
1158access_flags_to_smbopen_mode(const int access_flags)
1159{
1160 int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
1161
1162 if (masked_flags == GENERIC_READ)
1163 return SMBOPEN_READ;
1164 else if (masked_flags == GENERIC_WRITE)
1165 return SMBOPEN_WRITE;
1166
1167 /* just go for read/write */
1168 return SMBOPEN_READWRITE;
1169}
1170
Steve Frencha9d02ad2005-08-24 23:06:05 -07001171int
1172SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1173 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001174 const int access_flags, const int create_options, __u16 *netfid,
1175 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001176 const struct nls_table *nls_codepage, int remap)
1177{
1178 int rc = -EACCES;
1179 OPENX_REQ *pSMB = NULL;
1180 OPENX_RSP *pSMBr = NULL;
1181 int bytes_returned;
1182 int name_len;
1183 __u16 count;
1184
1185OldOpenRetry:
1186 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1187 (void **) &pSMBr);
1188 if (rc)
1189 return rc;
1190
1191 pSMB->AndXCommand = 0xFF; /* none */
1192
1193 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1194 count = 1; /* account for one byte pad to word boundary */
1195 name_len =
1196 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1197 fileName, PATH_MAX, nls_codepage, remap);
1198 name_len++; /* trailing null */
1199 name_len *= 2;
1200 } else { /* BB improve check for buffer overruns BB */
1201 count = 0; /* no pad */
1202 name_len = strnlen(fileName, PATH_MAX);
1203 name_len++; /* trailing null */
1204 strncpy(pSMB->fileName, fileName, name_len);
1205 }
1206 if (*pOplock & REQ_OPLOCK)
1207 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001208 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001209 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001210
Steve Frencha9d02ad2005-08-24 23:06:05 -07001211 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
Jeff Layton35fc37d2008-05-14 10:22:03 -07001212 pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001213 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1214 /* set file as system file if special file such
1215 as fifo and server expecting SFU style and
1216 no Unix extensions */
1217
Steve French790fe572007-07-07 19:25:05 +00001218 if (create_options & CREATE_OPTION_SPECIAL)
1219 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001220 else /* BB FIXME BB */
1221 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001222
Jeff Layton67750fb2008-05-09 22:28:02 +00001223 if (create_options & CREATE_OPTION_READONLY)
1224 pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001225
1226 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001227/* pSMB->CreateOptions = cpu_to_le32(create_options &
1228 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001229 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001230
1231 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001232 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001233 count += name_len;
1234 pSMB->hdr.smb_buf_length += count;
1235
1236 pSMB->ByteCount = cpu_to_le16(count);
1237 /* long_op set to 1 to allow for oplock break timeouts */
1238 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Jeff Layton77499812011-01-11 07:24:23 -05001239 (struct smb_hdr *)pSMBr, &bytes_returned, 0);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001240 cifs_stats_inc(&tcon->num_opens);
1241 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001242 cFYI(1, "Error in Open = %d", rc);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001243 } else {
1244 /* BB verify if wct == 15 */
1245
Steve French582d21e2008-05-13 04:54:12 +00001246/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
Steve Frencha9d02ad2005-08-24 23:06:05 -07001247
1248 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1249 /* Let caller know file was created so we can set the mode. */
1250 /* Do we care about the CreateAction in any other cases? */
1251 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001252/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001253 *pOplock |= CIFS_CREATE_ACTION; */
1254 /* BB FIXME END */
1255
Steve French790fe572007-07-07 19:25:05 +00001256 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001257 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1258 pfile_info->LastAccessTime = 0; /* BB fixme */
1259 pfile_info->LastWriteTime = 0; /* BB fixme */
1260 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001261 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001262 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001263 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001264 pfile_info->AllocationSize =
1265 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1266 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001267 pfile_info->NumberOfLinks = cpu_to_le32(1);
Jeff Layton9a8165f2008-10-17 21:03:20 -04001268 pfile_info->DeletePending = 0;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001269 }
1270 }
1271
1272 cifs_buf_release(pSMB);
1273 if (rc == -EAGAIN)
1274 goto OldOpenRetry;
1275 return rc;
1276}
1277
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278int
1279CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1280 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001281 const int access_flags, const int create_options, __u16 *netfid,
1282 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001283 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284{
1285 int rc = -EACCES;
1286 OPEN_REQ *pSMB = NULL;
1287 OPEN_RSP *pSMBr = NULL;
1288 int bytes_returned;
1289 int name_len;
1290 __u16 count;
1291
1292openRetry:
1293 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1294 (void **) &pSMBr);
1295 if (rc)
1296 return rc;
1297
1298 pSMB->AndXCommand = 0xFF; /* none */
1299
1300 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1301 count = 1; /* account for one byte pad to word boundary */
1302 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001303 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001304 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 name_len++; /* trailing null */
1306 name_len *= 2;
1307 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001308 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 count = 0; /* no pad */
1310 name_len = strnlen(fileName, PATH_MAX);
1311 name_len++; /* trailing null */
1312 pSMB->NameLength = cpu_to_le16(name_len);
1313 strncpy(pSMB->fileName, fileName, name_len);
1314 }
1315 if (*pOplock & REQ_OPLOCK)
1316 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001317 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1320 pSMB->AllocationSize = 0;
Steve Frencheda3c0292005-07-21 15:20:28 -07001321 /* set file as system file if special file such
1322 as fifo and server expecting SFU style and
1323 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001324 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c0292005-07-21 15:20:28 -07001325 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1326 else
1327 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Jeff Layton67750fb2008-05-09 22:28:02 +00001328
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 /* XP does not handle ATTR_POSIX_SEMANTICS */
1330 /* but it helps speed up case sensitive checks for other
1331 servers such as Samba */
1332 if (tcon->ses->capabilities & CAP_UNIX)
1333 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1334
Jeff Layton67750fb2008-05-09 22:28:02 +00001335 if (create_options & CREATE_OPTION_READONLY)
1336 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);
1337
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1339 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c0292005-07-21 15:20:28 -07001340 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001341 /* BB Expirement with various impersonation levels and verify */
1342 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 pSMB->SecurityFlags =
1344 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1345
1346 count += name_len;
1347 pSMB->hdr.smb_buf_length += count;
1348
1349 pSMB->ByteCount = cpu_to_le16(count);
1350 /* long_op set to 1 to allow for oplock break timeouts */
1351 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Jeff Layton77499812011-01-11 07:24:23 -05001352 (struct smb_hdr *)pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001353 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001355 cFYI(1, "Error in Open = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356 } else {
Steve French09d1db52005-04-28 22:41:08 -07001357 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1359 /* Let caller know file was created so we can set the mode. */
1360 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001361 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001362 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001363 if (pfile_info) {
Steve French61e74802008-12-03 00:57:54 +00001364 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
1365 36 /* CreationTime to Attributes */);
1366 /* the file_info buf is endian converted by caller */
1367 pfile_info->AllocationSize = pSMBr->AllocationSize;
1368 pfile_info->EndOfFile = pSMBr->EndOfFile;
1369 pfile_info->NumberOfLinks = cpu_to_le32(1);
1370 pfile_info->DeletePending = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001373
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 cifs_buf_release(pSMB);
1375 if (rc == -EAGAIN)
1376 goto openRetry;
1377 return rc;
1378}
1379
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380int
Steve French50c2f752007-07-13 00:33:32 +00001381CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1382 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1383 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384{
1385 int rc = -EACCES;
1386 READ_REQ *pSMB = NULL;
1387 READ_RSP *pSMBr = NULL;
1388 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001389 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001390 int resp_buf_type = 0;
1391 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392
Joe Perchesb6b38f72010-04-21 03:50:45 +00001393 cFYI(1, "Reading %d bytes on fid %d", count, netfid);
Steve French790fe572007-07-07 19:25:05 +00001394 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001395 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001396 else {
Steve Frenchbfa0d752005-08-31 21:50:37 -07001397 wct = 10; /* old style read */
Steve French4c3130e2008-12-09 00:28:16 +00001398 if ((lseek >> 32) > 0) {
1399 /* can not handle this big offset for old */
1400 return -EIO;
1401 }
1402 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403
1404 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001405 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 if (rc)
1407 return rc;
1408
1409 /* tcon and ses pointer are checked in smb_init */
1410 if (tcon->ses->server == NULL)
1411 return -ECONNABORTED;
1412
Steve Frenchec637e32005-12-12 20:53:18 -08001413 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 pSMB->Fid = netfid;
1415 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001416 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001417 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve Frenchbfa0d752005-08-31 21:50:37 -07001418
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 pSMB->Remaining = 0;
1420 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1421 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001422 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001423 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1424 else {
1425 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001426 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001427 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001428 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001429 }
Steve Frenchec637e32005-12-12 20:53:18 -08001430
1431 iov[0].iov_base = (char *)pSMB;
1432 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001433 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Jeff Layton77499812011-01-11 07:24:23 -05001434 &resp_buf_type, CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001435 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001436 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001438 cERROR(1, "Send error in read = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 } else {
1440 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1441 data_length = data_length << 16;
1442 data_length += le16_to_cpu(pSMBr->DataLength);
1443 *nbytes = data_length;
1444
1445 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001446 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 || (data_length > count)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001448 cFYI(1, "bad length %d for count %d",
1449 data_length, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 rc = -EIO;
1451 *nbytes = 0;
1452 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001453 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001454 le16_to_cpu(pSMBr->DataOffset);
1455/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001456 cERROR(1, "Faulting on read rc = %d",rc);
Steve French50c2f752007-07-13 00:33:32 +00001457 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001458 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001459 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001460 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 }
1462 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463
Steve French4b8f9302006-02-26 16:41:18 +00001464/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001465 if (*buf) {
1466 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001467 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001468 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001469 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001470 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001471 /* return buffer to caller to free */
1472 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001473 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001474 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001475 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001476 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001477 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001478
1479 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 since file handle passed in no longer valid */
1481 return rc;
1482}
1483
Steve Frenchec637e32005-12-12 20:53:18 -08001484
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485int
1486CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1487 const int netfid, const unsigned int count,
1488 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001489 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490{
1491 int rc = -EACCES;
1492 WRITE_REQ *pSMB = NULL;
1493 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001494 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 __u32 bytes_sent;
1496 __u16 byte_count;
1497
Steve Frencha24e2d72010-04-03 17:20:21 +00001498 *nbytes = 0;
1499
Joe Perchesb6b38f72010-04-21 03:50:45 +00001500 /* cFYI(1, "write at %lld %d bytes", offset, count);*/
Steve French790fe572007-07-07 19:25:05 +00001501 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001502 return -ECONNABORTED;
1503
Steve French790fe572007-07-07 19:25:05 +00001504 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001505 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001506 else {
Steve French1c955182005-08-30 20:58:07 -07001507 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001508 if ((offset >> 32) > 0) {
1509 /* can not handle big offset for old srv */
1510 return -EIO;
1511 }
1512 }
Steve French1c955182005-08-30 20:58:07 -07001513
1514 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 (void **) &pSMBr);
1516 if (rc)
1517 return rc;
1518 /* tcon and ses pointer are checked in smb_init */
1519 if (tcon->ses->server == NULL)
1520 return -ECONNABORTED;
1521
1522 pSMB->AndXCommand = 0xFF; /* none */
1523 pSMB->Fid = netfid;
1524 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001525 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001526 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French50c2f752007-07-13 00:33:32 +00001527
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 pSMB->Reserved = 0xFFFFFFFF;
1529 pSMB->WriteMode = 0;
1530 pSMB->Remaining = 0;
1531
Steve French50c2f752007-07-13 00:33:32 +00001532 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 can send more if LARGE_WRITE_X capability returned by the server and if
1534 our buffer is big enough or if we convert to iovecs on socket writes
1535 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001536 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1538 } else {
1539 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1540 & ~0xFF;
1541 }
1542
1543 if (bytes_sent > count)
1544 bytes_sent = count;
1545 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001546 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001547 if (buf)
Steve French61e74802008-12-03 00:57:54 +00001548 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001549 else if (ubuf) {
1550 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 cifs_buf_release(pSMB);
1552 return -EFAULT;
1553 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001554 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555 /* No buffer */
1556 cifs_buf_release(pSMB);
1557 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001558 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001559 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001560 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001561 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001562 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001563
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1565 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001566 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001567
Steve French790fe572007-07-07 19:25:05 +00001568 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001569 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001570 else { /* old style write has byte count 4 bytes earlier
1571 so 4 bytes pad */
1572 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001573 (struct smb_com_writex_req *)pSMB;
1574 pSMBW->ByteCount = cpu_to_le16(byte_count);
1575 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576
1577 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1578 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001579 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00001581 cFYI(1, "Send error in write = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582 } else {
1583 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1584 *nbytes = (*nbytes) << 16;
1585 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301586
1587 /*
1588 * Mask off high 16 bits when bytes written as returned by the
1589 * server is greater than bytes requested by the client. Some
1590 * OS/2 servers are known to set incorrect CountHigh values.
1591 */
1592 if (*nbytes > count)
1593 *nbytes &= 0xFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594 }
1595
1596 cifs_buf_release(pSMB);
1597
Steve French50c2f752007-07-13 00:33:32 +00001598 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 since file handle passed in no longer valid */
1600
1601 return rc;
1602}
1603
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001604int
1605CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001607 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1608 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609{
1610 int rc = -EACCES;
1611 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001612 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001613 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001614 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615
Jeff Laytonfbec9ab2009-04-03 13:44:00 -04001616 *nbytes = 0;
1617
Joe Perchesb6b38f72010-04-21 03:50:45 +00001618 cFYI(1, "write2 at %lld %d bytes", (long long)offset, count);
Steve Frenchff7feac2005-11-15 16:45:16 -08001619
Steve French4c3130e2008-12-09 00:28:16 +00001620 if (tcon->ses->capabilities & CAP_LARGE_FILES) {
Steve French8cc64c62005-10-03 13:49:43 -07001621 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001622 } else {
Steve French8cc64c62005-10-03 13:49:43 -07001623 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001624 if ((offset >> 32) > 0) {
1625 /* can not handle big offset for old srv */
1626 return -EIO;
1627 }
1628 }
Steve French8cc64c62005-10-03 13:49:43 -07001629 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630 if (rc)
1631 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 /* tcon and ses pointer are checked in smb_init */
1633 if (tcon->ses->server == NULL)
1634 return -ECONNABORTED;
1635
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001636 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637 pSMB->Fid = netfid;
1638 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001639 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001640 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 pSMB->Reserved = 0xFFFFFFFF;
1642 pSMB->WriteMode = 0;
1643 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001644
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001646 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647
Steve French3e844692005-10-03 13:37:24 -07001648 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1649 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001650 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001651 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001652 pSMB->hdr.smb_buf_length += count+1;
1653 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001654 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1655 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001656 pSMB->ByteCount = cpu_to_le16(count + 1);
1657 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001658 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001659 (struct smb_com_writex_req *)pSMB;
1660 pSMBW->ByteCount = cpu_to_le16(count + 5);
1661 }
Steve French3e844692005-10-03 13:37:24 -07001662 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001663 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001664 iov[0].iov_len = smb_hdr_len + 4;
1665 else /* wct == 12 pad bigger by four bytes */
1666 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001667
Steve French3e844692005-10-03 13:37:24 -07001668
Steve Frenchec637e32005-12-12 20:53:18 -08001669 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001670 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001671 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001673 cFYI(1, "Send error Write2 = %d", rc);
Steve French790fe572007-07-07 19:25:05 +00001674 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001675 /* presumably this can not happen, but best to be safe */
1676 rc = -EIO;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001677 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001678 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001679 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1680 *nbytes = (*nbytes) << 16;
1681 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301682
1683 /*
1684 * Mask off high 16 bits when bytes written as returned by the
1685 * server is greater than bytes requested by the client. OS/2
1686 * servers are known to set incorrect CountHigh values.
1687 */
1688 if (*nbytes > count)
1689 *nbytes &= 0xFFFF;
Steve French50c2f752007-07-13 00:33:32 +00001690 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691
Steve French4b8f9302006-02-26 16:41:18 +00001692/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001693 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001694 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001695 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001696 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697
Steve French50c2f752007-07-13 00:33:32 +00001698 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 since file handle passed in no longer valid */
1700
1701 return rc;
1702}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001703
1704
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705int
1706CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1707 const __u16 smb_file_id, const __u64 len,
1708 const __u64 offset, const __u32 numUnlock,
Pavel Shilovsky12fed002011-01-17 20:15:44 +03001709 const __u32 numLock, const __u8 lockType,
1710 const bool waitFlag, const __u8 oplock_level)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711{
1712 int rc = 0;
1713 LOCK_REQ *pSMB = NULL;
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001714/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715 int bytes_returned;
1716 int timeout = 0;
1717 __u16 count;
1718
Joe Perchesb6b38f72010-04-21 03:50:45 +00001719 cFYI(1, "CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock);
Steve French46810cb2005-04-28 22:41:09 -07001720 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1721
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 if (rc)
1723 return rc;
1724
Steve French790fe572007-07-07 19:25:05 +00001725 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001726 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 pSMB->Timeout = 0;
Steve French4b18f2a2008-04-29 00:06:05 +00001728 } else if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001729 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1731 } else {
1732 pSMB->Timeout = 0;
1733 }
1734
1735 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1736 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1737 pSMB->LockType = lockType;
Pavel Shilovsky12fed002011-01-17 20:15:44 +03001738 pSMB->OplockLevel = oplock_level;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 pSMB->AndXCommand = 0xFF; /* none */
1740 pSMB->Fid = smb_file_id; /* netfid stays le */
1741
Steve French790fe572007-07-07 19:25:05 +00001742 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1744 /* BB where to store pid high? */
1745 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1746 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1747 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1748 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1749 count = sizeof(LOCKING_ANDX_RANGE);
1750 } else {
1751 /* oplock break */
1752 count = 0;
1753 }
1754 pSMB->hdr.smb_buf_length += count;
1755 pSMB->ByteCount = cpu_to_le16(count);
1756
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001757 if (waitFlag) {
1758 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001759 (struct smb_hdr *) pSMB, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001760 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001761 } else {
Steve French133672e2007-11-13 22:41:37 +00001762 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1763 timeout);
1764 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001765 }
Steve Frencha4544342005-08-24 13:59:35 -07001766 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001767 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001768 cFYI(1, "Send error in Lock = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769
Steve French50c2f752007-07-13 00:33:32 +00001770 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 since file handle passed in no longer valid */
1772 return rc;
1773}
1774
1775int
Steve French08547b02006-02-28 22:39:25 +00001776CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1777 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001778 struct file_lock *pLockData, const __u16 lock_type,
Steve French4b18f2a2008-04-29 00:06:05 +00001779 const bool waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001780{
1781 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1782 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001783 struct cifs_posix_lock *parm_data;
1784 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001785 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001786 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001787 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001788 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001789 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001790
Joe Perchesb6b38f72010-04-21 03:50:45 +00001791 cFYI(1, "Posix Lock");
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001792
Steve French790fe572007-07-07 19:25:05 +00001793 if (pLockData == NULL)
Marcin Slusarzed5f0372008-05-13 04:01:01 +00001794 return -EINVAL;
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001795
Steve French08547b02006-02-28 22:39:25 +00001796 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1797
1798 if (rc)
1799 return rc;
1800
1801 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1802
Steve French50c2f752007-07-13 00:33:32 +00001803 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001804 pSMB->MaxSetupCount = 0;
1805 pSMB->Reserved = 0;
1806 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001807 pSMB->Reserved2 = 0;
1808 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1809 offset = param_offset + params;
1810
Steve French08547b02006-02-28 22:39:25 +00001811 count = sizeof(struct cifs_posix_lock);
1812 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001813 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001814 pSMB->SetupCount = 1;
1815 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001816 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001817 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1818 else
1819 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1820 byte_count = 3 /* pad */ + params + count;
1821 pSMB->DataCount = cpu_to_le16(count);
1822 pSMB->ParameterCount = cpu_to_le16(params);
1823 pSMB->TotalDataCount = pSMB->DataCount;
1824 pSMB->TotalParameterCount = pSMB->ParameterCount;
1825 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001826 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001827 (((char *) &pSMB->hdr.Protocol) + offset);
1828
1829 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001830 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001831 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001832 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001833 pSMB->Timeout = cpu_to_le32(-1);
1834 } else
1835 pSMB->Timeout = 0;
1836
Steve French08547b02006-02-28 22:39:25 +00001837 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001838 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001839 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001840
1841 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001842 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001843 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1844 pSMB->Reserved4 = 0;
1845 pSMB->hdr.smb_buf_length += byte_count;
1846 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001847 if (waitFlag) {
1848 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1849 (struct smb_hdr *) pSMBr, &bytes_returned);
1850 } else {
Steve French133672e2007-11-13 22:41:37 +00001851 iov[0].iov_base = (char *)pSMB;
1852 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1853 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1854 &resp_buf_type, timeout);
1855 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1856 not try to free it twice below on exit */
1857 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001858 }
1859
Steve French08547b02006-02-28 22:39:25 +00001860 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001861 cFYI(1, "Send error in Posix Lock = %d", rc);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001862 } else if (get_flag) {
1863 /* lock structure can be returned on get */
1864 __u16 data_offset;
1865 __u16 data_count;
1866 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001867
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001868 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1869 rc = -EIO; /* bad smb */
1870 goto plk_err_exit;
1871 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001872 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1873 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001874 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001875 rc = -EIO;
1876 goto plk_err_exit;
1877 }
1878 parm_data = (struct cifs_posix_lock *)
1879 ((char *)&pSMBr->hdr.Protocol + data_offset);
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001880 if (parm_data->lock_type == __constant_cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001881 pLockData->fl_type = F_UNLCK;
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001882 else {
1883 if (parm_data->lock_type ==
1884 __constant_cpu_to_le16(CIFS_RDLCK))
1885 pLockData->fl_type = F_RDLCK;
1886 else if (parm_data->lock_type ==
1887 __constant_cpu_to_le16(CIFS_WRLCK))
1888 pLockData->fl_type = F_WRLCK;
1889
1890 pLockData->fl_start = parm_data->start;
1891 pLockData->fl_end = parm_data->start +
1892 parm_data->length - 1;
1893 pLockData->fl_pid = parm_data->pid;
1894 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001895 }
Steve French50c2f752007-07-13 00:33:32 +00001896
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001897plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001898 if (pSMB)
1899 cifs_small_buf_release(pSMB);
1900
Steve French133672e2007-11-13 22:41:37 +00001901 if (resp_buf_type == CIFS_SMALL_BUFFER)
1902 cifs_small_buf_release(iov[0].iov_base);
1903 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1904 cifs_buf_release(iov[0].iov_base);
1905
Steve French08547b02006-02-28 22:39:25 +00001906 /* Note: On -EAGAIN error only caller can retry on handle based calls
1907 since file handle passed in no longer valid */
1908
1909 return rc;
1910}
1911
1912
1913int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1915{
1916 int rc = 0;
1917 CLOSE_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001918 cFYI(1, "In CIFSSMBClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919
1920/* do not retry on dead session on close */
1921 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001922 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 return 0;
1924 if (rc)
1925 return rc;
1926
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e52006-10-02 05:53:29 +00001928 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001930 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001931 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001933 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 /* EINTR is expected when user ctl-c to kill app */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001935 cERROR(1, "Send error in Close = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 }
1937 }
1938
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001940 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 rc = 0;
1942
1943 return rc;
1944}
1945
1946int
Steve Frenchb298f222009-02-21 21:17:43 +00001947CIFSSMBFlush(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1948{
1949 int rc = 0;
1950 FLUSH_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001951 cFYI(1, "In CIFSSMBFlush");
Steve Frenchb298f222009-02-21 21:17:43 +00001952
1953 rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB);
1954 if (rc)
1955 return rc;
1956
1957 pSMB->FileID = (__u16) smb_file_id;
1958 pSMB->ByteCount = 0;
1959 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
1960 cifs_stats_inc(&tcon->num_flushes);
1961 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001962 cERROR(1, "Send error in Flush = %d", rc);
Steve Frenchb298f222009-02-21 21:17:43 +00001963
1964 return rc;
1965}
1966
1967int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1969 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001970 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971{
1972 int rc = 0;
1973 RENAME_REQ *pSMB = NULL;
1974 RENAME_RSP *pSMBr = NULL;
1975 int bytes_returned;
1976 int name_len, name_len2;
1977 __u16 count;
1978
Joe Perchesb6b38f72010-04-21 03:50:45 +00001979 cFYI(1, "In CIFSSMBRename");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980renameRetry:
1981 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1982 (void **) &pSMBr);
1983 if (rc)
1984 return rc;
1985
1986 pSMB->BufferFormat = 0x04;
1987 pSMB->SearchAttributes =
1988 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1989 ATTR_DIRECTORY);
1990
1991 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1992 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001993 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001994 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 name_len++; /* trailing null */
1996 name_len *= 2;
1997 pSMB->OldFileName[name_len] = 0x04; /* pad */
1998 /* protocol requires ASCII signature byte on Unicode string */
1999 pSMB->OldFileName[name_len + 1] = 0x00;
2000 name_len2 =
Steve French582d21e2008-05-13 04:54:12 +00002001 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002002 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2004 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002005 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 name_len = strnlen(fromName, PATH_MAX);
2007 name_len++; /* trailing null */
2008 strncpy(pSMB->OldFileName, fromName, name_len);
2009 name_len2 = strnlen(toName, PATH_MAX);
2010 name_len2++; /* trailing null */
2011 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2012 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2013 name_len2++; /* trailing null */
2014 name_len2++; /* signature byte */
2015 }
2016
2017 count = 1 /* 1st signature byte */ + name_len + name_len2;
2018 pSMB->hdr.smb_buf_length += count;
2019 pSMB->ByteCount = cpu_to_le16(count);
2020
2021 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2022 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002023 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002024 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002025 cFYI(1, "Send error in rename = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027 cifs_buf_release(pSMB);
2028
2029 if (rc == -EAGAIN)
2030 goto renameRetry;
2031
2032 return rc;
2033}
2034
Steve French50c2f752007-07-13 00:33:32 +00002035int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
Jeff Layton391e5752008-09-24 11:32:59 -04002036 int netfid, const char *target_name,
Steve French50c2f752007-07-13 00:33:32 +00002037 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038{
2039 struct smb_com_transaction2_sfi_req *pSMB = NULL;
2040 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00002041 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 char *data_offset;
2043 char dummy_string[30];
2044 int rc = 0;
2045 int bytes_returned = 0;
2046 int len_of_str;
2047 __u16 params, param_offset, offset, count, byte_count;
2048
Joe Perchesb6b38f72010-04-21 03:50:45 +00002049 cFYI(1, "Rename to File by handle");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
2051 (void **) &pSMBr);
2052 if (rc)
2053 return rc;
2054
2055 params = 6;
2056 pSMB->MaxSetupCount = 0;
2057 pSMB->Reserved = 0;
2058 pSMB->Flags = 0;
2059 pSMB->Timeout = 0;
2060 pSMB->Reserved2 = 0;
2061 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2062 offset = param_offset + params;
2063
2064 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2065 rename_info = (struct set_file_rename *) data_offset;
2066 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002067 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 pSMB->SetupCount = 1;
2069 pSMB->Reserved3 = 0;
2070 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2071 byte_count = 3 /* pad */ + params;
2072 pSMB->ParameterCount = cpu_to_le16(params);
2073 pSMB->TotalParameterCount = pSMB->ParameterCount;
2074 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2075 pSMB->DataOffset = cpu_to_le16(offset);
2076 /* construct random name ".cifs_tmp<inodenum><mid>" */
2077 rename_info->overwrite = cpu_to_le32(1);
2078 rename_info->root_fid = 0;
2079 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002080 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002081 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2082 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002083 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002085 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002086 target_name, PATH_MAX, nls_codepage,
2087 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 }
2089 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
Jeff Layton391e5752008-09-24 11:32:59 -04002090 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091 byte_count += count;
2092 pSMB->DataCount = cpu_to_le16(count);
2093 pSMB->TotalDataCount = pSMB->DataCount;
2094 pSMB->Fid = netfid;
2095 pSMB->InformationLevel =
2096 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2097 pSMB->Reserved4 = 0;
2098 pSMB->hdr.smb_buf_length += byte_count;
2099 pSMB->ByteCount = cpu_to_le16(byte_count);
2100 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002101 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002102 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002103 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002104 cFYI(1, "Send error in Rename (by file handle) = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -07002105
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 cifs_buf_release(pSMB);
2107
2108 /* Note: On -EAGAIN error only caller can retry on handle based calls
2109 since file handle passed in no longer valid */
2110
2111 return rc;
2112}
2113
2114int
Steve French50c2f752007-07-13 00:33:32 +00002115CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2116 const __u16 target_tid, const char *toName, const int flags,
2117 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118{
2119 int rc = 0;
2120 COPY_REQ *pSMB = NULL;
2121 COPY_RSP *pSMBr = NULL;
2122 int bytes_returned;
2123 int name_len, name_len2;
2124 __u16 count;
2125
Joe Perchesb6b38f72010-04-21 03:50:45 +00002126 cFYI(1, "In CIFSSMBCopy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127copyRetry:
2128 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2129 (void **) &pSMBr);
2130 if (rc)
2131 return rc;
2132
2133 pSMB->BufferFormat = 0x04;
2134 pSMB->Tid2 = target_tid;
2135
2136 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2137
2138 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002139 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002140 fromName, PATH_MAX, nls_codepage,
2141 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 name_len++; /* trailing null */
2143 name_len *= 2;
2144 pSMB->OldFileName[name_len] = 0x04; /* pad */
2145 /* protocol requires ASCII signature byte on Unicode string */
2146 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002147 name_len2 =
2148 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002149 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2151 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002152 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153 name_len = strnlen(fromName, PATH_MAX);
2154 name_len++; /* trailing null */
2155 strncpy(pSMB->OldFileName, fromName, name_len);
2156 name_len2 = strnlen(toName, PATH_MAX);
2157 name_len2++; /* trailing null */
2158 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2159 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2160 name_len2++; /* trailing null */
2161 name_len2++; /* signature byte */
2162 }
2163
2164 count = 1 /* 1st signature byte */ + name_len + name_len2;
2165 pSMB->hdr.smb_buf_length += count;
2166 pSMB->ByteCount = cpu_to_le16(count);
2167
2168 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2169 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2170 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002171 cFYI(1, "Send error in copy = %d with %d files copied",
2172 rc, le16_to_cpu(pSMBr->CopyCount));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 }
Steve French0d817bc2008-05-22 02:02:03 +00002174 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175
2176 if (rc == -EAGAIN)
2177 goto copyRetry;
2178
2179 return rc;
2180}
2181
2182int
2183CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2184 const char *fromName, const char *toName,
2185 const struct nls_table *nls_codepage)
2186{
2187 TRANSACTION2_SPI_REQ *pSMB = NULL;
2188 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2189 char *data_offset;
2190 int name_len;
2191 int name_len_target;
2192 int rc = 0;
2193 int bytes_returned = 0;
2194 __u16 params, param_offset, offset, byte_count;
2195
Joe Perchesb6b38f72010-04-21 03:50:45 +00002196 cFYI(1, "In Symlink Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197createSymLinkRetry:
2198 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2199 (void **) &pSMBr);
2200 if (rc)
2201 return rc;
2202
2203 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2204 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002205 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206 /* find define for this maxpathcomponent */
2207 , nls_codepage);
2208 name_len++; /* trailing null */
2209 name_len *= 2;
2210
Steve French50c2f752007-07-13 00:33:32 +00002211 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 name_len = strnlen(fromName, PATH_MAX);
2213 name_len++; /* trailing null */
2214 strncpy(pSMB->FileName, fromName, name_len);
2215 }
2216 params = 6 + name_len;
2217 pSMB->MaxSetupCount = 0;
2218 pSMB->Reserved = 0;
2219 pSMB->Flags = 0;
2220 pSMB->Timeout = 0;
2221 pSMB->Reserved2 = 0;
2222 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002223 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224 offset = param_offset + params;
2225
2226 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2227 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2228 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002229 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 /* find define for this maxpathcomponent */
2231 , nls_codepage);
2232 name_len_target++; /* trailing null */
2233 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002234 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235 name_len_target = strnlen(toName, PATH_MAX);
2236 name_len_target++; /* trailing null */
2237 strncpy(data_offset, toName, name_len_target);
2238 }
2239
2240 pSMB->MaxParameterCount = cpu_to_le16(2);
2241 /* BB find exact max on data count below from sess */
2242 pSMB->MaxDataCount = cpu_to_le16(1000);
2243 pSMB->SetupCount = 1;
2244 pSMB->Reserved3 = 0;
2245 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2246 byte_count = 3 /* pad */ + params + name_len_target;
2247 pSMB->DataCount = cpu_to_le16(name_len_target);
2248 pSMB->ParameterCount = cpu_to_le16(params);
2249 pSMB->TotalDataCount = pSMB->DataCount;
2250 pSMB->TotalParameterCount = pSMB->ParameterCount;
2251 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2252 pSMB->DataOffset = cpu_to_le16(offset);
2253 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2254 pSMB->Reserved4 = 0;
2255 pSMB->hdr.smb_buf_length += byte_count;
2256 pSMB->ByteCount = cpu_to_le16(byte_count);
2257 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2258 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002259 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002260 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002261 cFYI(1, "Send error in SetPathInfo create symlink = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262
Steve French0d817bc2008-05-22 02:02:03 +00002263 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264
2265 if (rc == -EAGAIN)
2266 goto createSymLinkRetry;
2267
2268 return rc;
2269}
2270
2271int
2272CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2273 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002274 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275{
2276 TRANSACTION2_SPI_REQ *pSMB = NULL;
2277 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2278 char *data_offset;
2279 int name_len;
2280 int name_len_target;
2281 int rc = 0;
2282 int bytes_returned = 0;
2283 __u16 params, param_offset, offset, byte_count;
2284
Joe Perchesb6b38f72010-04-21 03:50:45 +00002285 cFYI(1, "In Create Hard link Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286createHardLinkRetry:
2287 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2288 (void **) &pSMBr);
2289 if (rc)
2290 return rc;
2291
2292 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002293 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002294 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 name_len++; /* trailing null */
2296 name_len *= 2;
2297
Steve French50c2f752007-07-13 00:33:32 +00002298 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299 name_len = strnlen(toName, PATH_MAX);
2300 name_len++; /* trailing null */
2301 strncpy(pSMB->FileName, toName, name_len);
2302 }
2303 params = 6 + name_len;
2304 pSMB->MaxSetupCount = 0;
2305 pSMB->Reserved = 0;
2306 pSMB->Flags = 0;
2307 pSMB->Timeout = 0;
2308 pSMB->Reserved2 = 0;
2309 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002310 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311 offset = param_offset + params;
2312
2313 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2314 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2315 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002316 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002317 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318 name_len_target++; /* trailing null */
2319 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002320 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 name_len_target = strnlen(fromName, PATH_MAX);
2322 name_len_target++; /* trailing null */
2323 strncpy(data_offset, fromName, name_len_target);
2324 }
2325
2326 pSMB->MaxParameterCount = cpu_to_le16(2);
2327 /* BB find exact max on data count below from sess*/
2328 pSMB->MaxDataCount = cpu_to_le16(1000);
2329 pSMB->SetupCount = 1;
2330 pSMB->Reserved3 = 0;
2331 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2332 byte_count = 3 /* pad */ + params + name_len_target;
2333 pSMB->ParameterCount = cpu_to_le16(params);
2334 pSMB->TotalParameterCount = pSMB->ParameterCount;
2335 pSMB->DataCount = cpu_to_le16(name_len_target);
2336 pSMB->TotalDataCount = pSMB->DataCount;
2337 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2338 pSMB->DataOffset = cpu_to_le16(offset);
2339 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2340 pSMB->Reserved4 = 0;
2341 pSMB->hdr.smb_buf_length += byte_count;
2342 pSMB->ByteCount = cpu_to_le16(byte_count);
2343 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2344 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002345 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002346 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002347 cFYI(1, "Send error in SetPathInfo (hard link) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348
2349 cifs_buf_release(pSMB);
2350 if (rc == -EAGAIN)
2351 goto createHardLinkRetry;
2352
2353 return rc;
2354}
2355
2356int
2357CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2358 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002359 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360{
2361 int rc = 0;
2362 NT_RENAME_REQ *pSMB = NULL;
2363 RENAME_RSP *pSMBr = NULL;
2364 int bytes_returned;
2365 int name_len, name_len2;
2366 __u16 count;
2367
Joe Perchesb6b38f72010-04-21 03:50:45 +00002368 cFYI(1, "In CIFSCreateHardLink");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369winCreateHardLinkRetry:
2370
2371 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2372 (void **) &pSMBr);
2373 if (rc)
2374 return rc;
2375
2376 pSMB->SearchAttributes =
2377 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2378 ATTR_DIRECTORY);
2379 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2380 pSMB->ClusterCount = 0;
2381
2382 pSMB->BufferFormat = 0x04;
2383
2384 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2385 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002386 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002387 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388 name_len++; /* trailing null */
2389 name_len *= 2;
Jeff Laytonfcc7c092009-02-28 12:59:03 -05002390
2391 /* protocol specifies ASCII buffer format (0x04) for unicode */
2392 pSMB->OldFileName[name_len] = 0x04;
2393 pSMB->OldFileName[name_len + 1] = 0x00; /* pad */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002395 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002396 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2398 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002399 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 name_len = strnlen(fromName, PATH_MAX);
2401 name_len++; /* trailing null */
2402 strncpy(pSMB->OldFileName, fromName, name_len);
2403 name_len2 = strnlen(toName, PATH_MAX);
2404 name_len2++; /* trailing null */
2405 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2406 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2407 name_len2++; /* trailing null */
2408 name_len2++; /* signature byte */
2409 }
2410
2411 count = 1 /* string type byte */ + name_len + name_len2;
2412 pSMB->hdr.smb_buf_length += count;
2413 pSMB->ByteCount = cpu_to_le16(count);
2414
2415 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2416 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002417 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002418 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002419 cFYI(1, "Send error in hard link (NT rename) = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00002420
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421 cifs_buf_release(pSMB);
2422 if (rc == -EAGAIN)
2423 goto winCreateHardLinkRetry;
2424
2425 return rc;
2426}
2427
2428int
2429CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
Jeff Layton460b9692009-04-30 07:17:56 -04002430 const unsigned char *searchName, char **symlinkinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431 const struct nls_table *nls_codepage)
2432{
2433/* SMB_QUERY_FILE_UNIX_LINK */
2434 TRANSACTION2_QPI_REQ *pSMB = NULL;
2435 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2436 int rc = 0;
2437 int bytes_returned;
2438 int name_len;
2439 __u16 params, byte_count;
Jeff Layton460b9692009-04-30 07:17:56 -04002440 char *data_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441
Joe Perchesb6b38f72010-04-21 03:50:45 +00002442 cFYI(1, "In QPathSymLinkInfo (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443
2444querySymLinkRetry:
2445 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2446 (void **) &pSMBr);
2447 if (rc)
2448 return rc;
2449
2450 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2451 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002452 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2453 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454 name_len++; /* trailing null */
2455 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002456 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457 name_len = strnlen(searchName, PATH_MAX);
2458 name_len++; /* trailing null */
2459 strncpy(pSMB->FileName, searchName, name_len);
2460 }
2461
2462 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2463 pSMB->TotalDataCount = 0;
2464 pSMB->MaxParameterCount = cpu_to_le16(2);
Jeff Layton46a75742009-05-24 18:45:17 -04002465 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466 pSMB->MaxSetupCount = 0;
2467 pSMB->Reserved = 0;
2468 pSMB->Flags = 0;
2469 pSMB->Timeout = 0;
2470 pSMB->Reserved2 = 0;
2471 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002472 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473 pSMB->DataCount = 0;
2474 pSMB->DataOffset = 0;
2475 pSMB->SetupCount = 1;
2476 pSMB->Reserved3 = 0;
2477 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2478 byte_count = params + 1 /* pad */ ;
2479 pSMB->TotalParameterCount = cpu_to_le16(params);
2480 pSMB->ParameterCount = pSMB->TotalParameterCount;
2481 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2482 pSMB->Reserved4 = 0;
2483 pSMB->hdr.smb_buf_length += byte_count;
2484 pSMB->ByteCount = cpu_to_le16(byte_count);
2485
2486 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2487 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2488 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002489 cFYI(1, "Send error in QuerySymLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490 } else {
2491 /* decode response */
2492
2493 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494 /* BB also check enough total bytes returned */
Jeff Layton460b9692009-04-30 07:17:56 -04002495 if (rc || (pSMBr->ByteCount < 2))
2496 rc = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497 else {
Steve French0e0d2cf2009-05-01 05:27:32 +00002498 bool is_unicode;
Jeff Layton460b9692009-04-30 07:17:56 -04002499 u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500
Jeff Layton460b9692009-04-30 07:17:56 -04002501 data_start = ((char *) &pSMBr->hdr.Protocol) +
2502 le16_to_cpu(pSMBr->t2.DataOffset);
2503
Steve French0e0d2cf2009-05-01 05:27:32 +00002504 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
2505 is_unicode = true;
2506 else
2507 is_unicode = false;
2508
Steve French737b7582005-04-28 22:41:06 -07002509 /* BB FIXME investigate remapping reserved chars here */
Steve Frenchd185cda2009-04-30 17:45:10 +00002510 *symlinkinfo = cifs_strndup_from_ucs(data_start, count,
Steve French0e0d2cf2009-05-01 05:27:32 +00002511 is_unicode, nls_codepage);
Jeff Layton8b6427a2009-05-19 09:57:03 -04002512 if (!*symlinkinfo)
Jeff Layton460b9692009-04-30 07:17:56 -04002513 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514 }
2515 }
2516 cifs_buf_release(pSMB);
2517 if (rc == -EAGAIN)
2518 goto querySymLinkRetry;
2519 return rc;
2520}
2521
Parag Warudkarc9489772007-10-23 18:09:48 +00002522#ifdef CONFIG_CIFS_EXPERIMENTAL
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523int
2524CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2525 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002526 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527 const struct nls_table *nls_codepage)
2528{
2529 int rc = 0;
2530 int bytes_returned;
Steve French50c2f752007-07-13 00:33:32 +00002531 struct smb_com_transaction_ioctl_req *pSMB;
2532 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533
Joe Perchesb6b38f72010-04-21 03:50:45 +00002534 cFYI(1, "In Windows reparse style QueryLink for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2536 (void **) &pSMBr);
2537 if (rc)
2538 return rc;
2539
2540 pSMB->TotalParameterCount = 0 ;
2541 pSMB->TotalDataCount = 0;
2542 pSMB->MaxParameterCount = cpu_to_le32(2);
2543 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002544 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2545 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 pSMB->MaxSetupCount = 4;
2547 pSMB->Reserved = 0;
2548 pSMB->ParameterOffset = 0;
2549 pSMB->DataCount = 0;
2550 pSMB->DataOffset = 0;
2551 pSMB->SetupCount = 4;
2552 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2553 pSMB->ParameterCount = pSMB->TotalParameterCount;
2554 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2555 pSMB->IsFsctl = 1; /* FSCTL */
2556 pSMB->IsRootFlag = 0;
2557 pSMB->Fid = fid; /* file handle always le */
2558 pSMB->ByteCount = 0;
2559
2560 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2561 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2562 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002563 cFYI(1, "Send error in QueryReparseLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564 } else { /* decode response */
2565 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2566 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
Steve Frenchafe48c32009-05-02 05:25:46 +00002567 if ((pSMBr->ByteCount < 2) || (data_offset > 512)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568 /* BB also check enough total bytes returned */
2569 rc = -EIO; /* bad smb */
Steve Frenchafe48c32009-05-02 05:25:46 +00002570 goto qreparse_out;
2571 }
2572 if (data_count && (data_count < 2048)) {
2573 char *end_of_smb = 2 /* sizeof byte count */ +
2574 pSMBr->ByteCount + (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575
Steve Frenchafe48c32009-05-02 05:25:46 +00002576 struct reparse_data *reparse_buf =
Steve French50c2f752007-07-13 00:33:32 +00002577 (struct reparse_data *)
2578 ((char *)&pSMBr->hdr.Protocol
2579 + data_offset);
Steve Frenchafe48c32009-05-02 05:25:46 +00002580 if ((char *)reparse_buf >= end_of_smb) {
2581 rc = -EIO;
2582 goto qreparse_out;
2583 }
2584 if ((reparse_buf->LinkNamesBuf +
2585 reparse_buf->TargetNameOffset +
2586 reparse_buf->TargetNameLen) > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002587 cFYI(1, "reparse buf beyond SMB");
Steve Frenchafe48c32009-05-02 05:25:46 +00002588 rc = -EIO;
2589 goto qreparse_out;
2590 }
Steve French50c2f752007-07-13 00:33:32 +00002591
Steve Frenchafe48c32009-05-02 05:25:46 +00002592 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2593 cifs_from_ucs2(symlinkinfo, (__le16 *)
Steve French50c2f752007-07-13 00:33:32 +00002594 (reparse_buf->LinkNamesBuf +
2595 reparse_buf->TargetNameOffset),
Steve Frenchafe48c32009-05-02 05:25:46 +00002596 buflen,
2597 reparse_buf->TargetNameLen,
2598 nls_codepage, 0);
2599 } else { /* ASCII names */
2600 strncpy(symlinkinfo,
2601 reparse_buf->LinkNamesBuf +
2602 reparse_buf->TargetNameOffset,
2603 min_t(const int, buflen,
2604 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002606 } else {
2607 rc = -EIO;
Joe Perchesb6b38f72010-04-21 03:50:45 +00002608 cFYI(1, "Invalid return data count on "
2609 "get reparse info ioctl");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002610 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002611 symlinkinfo[buflen] = 0; /* just in case so the caller
2612 does not go off the end of the buffer */
Joe Perchesb6b38f72010-04-21 03:50:45 +00002613 cFYI(1, "readlink result - %s", symlinkinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614 }
Steve French989c7e52009-05-02 05:32:20 +00002615
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002617 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002618
2619 /* Note: On -EAGAIN error only caller can retry on handle based calls
2620 since file handle passed in no longer valid */
2621
2622 return rc;
2623}
Steve Frenchafe48c32009-05-02 05:25:46 +00002624#endif /* CIFS_EXPERIMENTAL */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625
2626#ifdef CONFIG_CIFS_POSIX
2627
2628/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002629static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2630 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631{
2632 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002633 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2634 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2635 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002636 /* cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637
2638 return;
2639}
2640
2641/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002642static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2643 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644{
2645 int size = 0;
2646 int i;
2647 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002648 struct cifs_posix_ace *pACE;
2649 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2650 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651
2652 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2653 return -EOPNOTSUPP;
2654
Steve French790fe572007-07-07 19:25:05 +00002655 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656 count = le16_to_cpu(cifs_acl->access_entry_count);
2657 pACE = &cifs_acl->ace_array[0];
2658 size = sizeof(struct cifs_posix_acl);
2659 size += sizeof(struct cifs_posix_ace) * count;
2660 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002661 if (size_of_data_area < size) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002662 cFYI(1, "bad CIFS POSIX ACL size %d vs. %d",
2663 size_of_data_area, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664 return -EINVAL;
2665 }
Steve French790fe572007-07-07 19:25:05 +00002666 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667 count = le16_to_cpu(cifs_acl->access_entry_count);
2668 size = sizeof(struct cifs_posix_acl);
2669 size += sizeof(struct cifs_posix_ace) * count;
2670/* skip past access ACEs to get to default ACEs */
2671 pACE = &cifs_acl->ace_array[count];
2672 count = le16_to_cpu(cifs_acl->default_entry_count);
2673 size += sizeof(struct cifs_posix_ace) * count;
2674 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002675 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676 return -EINVAL;
2677 } else {
2678 /* illegal type */
2679 return -EINVAL;
2680 }
2681
2682 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002683 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002684 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002685 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686 return -ERANGE;
2687 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002688 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002689 for (i = 0; i < count ; i++) {
2690 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2691 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692 }
2693 }
2694 return size;
2695}
2696
Steve French50c2f752007-07-13 00:33:32 +00002697static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2698 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699{
2700 __u16 rc = 0; /* 0 = ACL converted ok */
2701
Steve Frenchff7feac2005-11-15 16:45:16 -08002702 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2703 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002705 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706 /* Probably no need to le convert -1 on any arch but can not hurt */
2707 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002708 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002709 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002710 /*cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id);*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711 return rc;
2712}
2713
2714/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002715static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2716 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717{
2718 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002719 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2720 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721 int count;
2722 int i;
2723
Steve French790fe572007-07-07 19:25:05 +00002724 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725 return 0;
2726
2727 count = posix_acl_xattr_count((size_t)buflen);
Joe Perchesb6b38f72010-04-21 03:50:45 +00002728 cFYI(1, "setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002729 "version of %d",
Joe Perchesb6b38f72010-04-21 03:50:45 +00002730 count, buflen, le32_to_cpu(local_acl->a_version));
Steve French790fe572007-07-07 19:25:05 +00002731 if (le32_to_cpu(local_acl->a_version) != 2) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002732 cFYI(1, "unknown POSIX ACL version %d",
2733 le32_to_cpu(local_acl->a_version));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734 return 0;
2735 }
2736 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002737 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002738 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002739 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002740 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002742 cFYI(1, "unknown ACL type %d", acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743 return 0;
2744 }
Steve French50c2f752007-07-13 00:33:32 +00002745 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2747 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002748 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749 /* ACE not converted */
2750 break;
2751 }
2752 }
Steve French790fe572007-07-07 19:25:05 +00002753 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2755 rc += sizeof(struct cifs_posix_acl);
2756 /* BB add check to make sure ACL does not overflow SMB */
2757 }
2758 return rc;
2759}
2760
2761int
2762CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002763 const unsigned char *searchName,
2764 char *acl_inf, const int buflen, const int acl_type,
2765 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766{
2767/* SMB_QUERY_POSIX_ACL */
2768 TRANSACTION2_QPI_REQ *pSMB = NULL;
2769 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2770 int rc = 0;
2771 int bytes_returned;
2772 int name_len;
2773 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002774
Joe Perchesb6b38f72010-04-21 03:50:45 +00002775 cFYI(1, "In GetPosixACL (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776
2777queryAclRetry:
2778 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2779 (void **) &pSMBr);
2780 if (rc)
2781 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002782
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2784 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002785 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002786 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 name_len++; /* trailing null */
2788 name_len *= 2;
2789 pSMB->FileName[name_len] = 0;
2790 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002791 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 name_len = strnlen(searchName, PATH_MAX);
2793 name_len++; /* trailing null */
2794 strncpy(pSMB->FileName, searchName, name_len);
2795 }
2796
2797 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2798 pSMB->TotalDataCount = 0;
2799 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002800 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 pSMB->MaxDataCount = cpu_to_le16(4000);
2802 pSMB->MaxSetupCount = 0;
2803 pSMB->Reserved = 0;
2804 pSMB->Flags = 0;
2805 pSMB->Timeout = 0;
2806 pSMB->Reserved2 = 0;
2807 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002808 offsetof(struct smb_com_transaction2_qpi_req,
2809 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810 pSMB->DataCount = 0;
2811 pSMB->DataOffset = 0;
2812 pSMB->SetupCount = 1;
2813 pSMB->Reserved3 = 0;
2814 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2815 byte_count = params + 1 /* pad */ ;
2816 pSMB->TotalParameterCount = cpu_to_le16(params);
2817 pSMB->ParameterCount = pSMB->TotalParameterCount;
2818 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2819 pSMB->Reserved4 = 0;
2820 pSMB->hdr.smb_buf_length += byte_count;
2821 pSMB->ByteCount = cpu_to_le16(byte_count);
2822
2823 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2824 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002825 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002827 cFYI(1, "Send error in Query POSIX ACL = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 } else {
2829 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002830
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2832 if (rc || (pSMBr->ByteCount < 2))
2833 /* BB also check enough total bytes returned */
2834 rc = -EIO; /* bad smb */
2835 else {
2836 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2837 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2838 rc = cifs_copy_posix_acl(acl_inf,
2839 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002840 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841 }
2842 }
2843 cifs_buf_release(pSMB);
2844 if (rc == -EAGAIN)
2845 goto queryAclRetry;
2846 return rc;
2847}
2848
2849int
2850CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002851 const unsigned char *fileName,
2852 const char *local_acl, const int buflen,
2853 const int acl_type,
2854 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855{
2856 struct smb_com_transaction2_spi_req *pSMB = NULL;
2857 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2858 char *parm_data;
2859 int name_len;
2860 int rc = 0;
2861 int bytes_returned = 0;
2862 __u16 params, byte_count, data_count, param_offset, offset;
2863
Joe Perchesb6b38f72010-04-21 03:50:45 +00002864 cFYI(1, "In SetPosixACL (Unix) for path %s", fileName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865setAclRetry:
2866 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002867 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868 if (rc)
2869 return rc;
2870 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2871 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002872 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002873 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874 name_len++; /* trailing null */
2875 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002876 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877 name_len = strnlen(fileName, PATH_MAX);
2878 name_len++; /* trailing null */
2879 strncpy(pSMB->FileName, fileName, name_len);
2880 }
2881 params = 6 + name_len;
2882 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00002883 /* BB find max SMB size from sess */
2884 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002885 pSMB->MaxSetupCount = 0;
2886 pSMB->Reserved = 0;
2887 pSMB->Flags = 0;
2888 pSMB->Timeout = 0;
2889 pSMB->Reserved2 = 0;
2890 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002891 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002892 offset = param_offset + params;
2893 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2894 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2895
2896 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002897 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002898
Steve French790fe572007-07-07 19:25:05 +00002899 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900 rc = -EOPNOTSUPP;
2901 goto setACLerrorExit;
2902 }
2903 pSMB->DataOffset = cpu_to_le16(offset);
2904 pSMB->SetupCount = 1;
2905 pSMB->Reserved3 = 0;
2906 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2907 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2908 byte_count = 3 /* pad */ + params + data_count;
2909 pSMB->DataCount = cpu_to_le16(data_count);
2910 pSMB->TotalDataCount = pSMB->DataCount;
2911 pSMB->ParameterCount = cpu_to_le16(params);
2912 pSMB->TotalParameterCount = pSMB->ParameterCount;
2913 pSMB->Reserved4 = 0;
2914 pSMB->hdr.smb_buf_length += byte_count;
2915 pSMB->ByteCount = cpu_to_le16(byte_count);
2916 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002917 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00002918 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002919 cFYI(1, "Set POSIX ACL returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002920
2921setACLerrorExit:
2922 cifs_buf_release(pSMB);
2923 if (rc == -EAGAIN)
2924 goto setAclRetry;
2925 return rc;
2926}
2927
Steve Frenchf654bac2005-04-28 22:41:04 -07002928/* BB fix tabs in this function FIXME BB */
2929int
2930CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00002931 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07002932{
Steve French50c2f752007-07-13 00:33:32 +00002933 int rc = 0;
2934 struct smb_t2_qfi_req *pSMB = NULL;
2935 struct smb_t2_qfi_rsp *pSMBr = NULL;
2936 int bytes_returned;
2937 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07002938
Joe Perchesb6b38f72010-04-21 03:50:45 +00002939 cFYI(1, "In GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00002940 if (tcon == NULL)
2941 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07002942
2943GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00002944 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2945 (void **) &pSMBr);
2946 if (rc)
2947 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07002948
Steve Frenchad7a2922008-02-07 23:25:02 +00002949 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00002950 pSMB->t2.TotalDataCount = 0;
2951 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
2952 /* BB find exact max data count below from sess structure BB */
2953 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
2954 pSMB->t2.MaxSetupCount = 0;
2955 pSMB->t2.Reserved = 0;
2956 pSMB->t2.Flags = 0;
2957 pSMB->t2.Timeout = 0;
2958 pSMB->t2.Reserved2 = 0;
2959 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
2960 Fid) - 4);
2961 pSMB->t2.DataCount = 0;
2962 pSMB->t2.DataOffset = 0;
2963 pSMB->t2.SetupCount = 1;
2964 pSMB->t2.Reserved3 = 0;
2965 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
2966 byte_count = params + 1 /* pad */ ;
2967 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
2968 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
2969 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
2970 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07002971 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00002972 pSMB->hdr.smb_buf_length += byte_count;
2973 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07002974
Steve French790fe572007-07-07 19:25:05 +00002975 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2976 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2977 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002978 cFYI(1, "error %d in GetExtAttr", rc);
Steve French790fe572007-07-07 19:25:05 +00002979 } else {
2980 /* decode response */
2981 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2982 if (rc || (pSMBr->ByteCount < 2))
2983 /* BB also check enough total bytes returned */
2984 /* If rc should we check for EOPNOSUPP and
2985 disable the srvino flag? or in caller? */
2986 rc = -EIO; /* bad smb */
2987 else {
2988 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2989 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2990 struct file_chattr_info *pfinfo;
2991 /* BB Do we need a cast or hash here ? */
2992 if (count != 16) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002993 cFYI(1, "Illegal size ret in GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00002994 rc = -EIO;
2995 goto GetExtAttrOut;
2996 }
2997 pfinfo = (struct file_chattr_info *)
2998 (data_offset + (char *) &pSMBr->hdr.Protocol);
2999 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07003000 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00003001 }
3002 }
Steve Frenchf654bac2005-04-28 22:41:04 -07003003GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00003004 cifs_buf_release(pSMB);
3005 if (rc == -EAGAIN)
3006 goto GetExtAttrRetry;
3007 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003008}
3009
Steve Frenchf654bac2005-04-28 22:41:04 -07003010#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003011
Jeff Layton79df1ba2010-12-06 12:52:08 -05003012#ifdef CONFIG_CIFS_ACL
3013/*
3014 * Initialize NT TRANSACT SMB into small smb request buffer. This assumes that
3015 * all NT TRANSACTS that we init here have total parm and data under about 400
3016 * bytes (to fit in small cifs buffer size), which is the case so far, it
3017 * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of
3018 * returned setup area) and MaxParameterCount (returned parms size) must be set
3019 * by caller
3020 */
3021static int
3022smb_init_nttransact(const __u16 sub_command, const int setup_count,
3023 const int parm_len, struct cifsTconInfo *tcon,
3024 void **ret_buf)
3025{
3026 int rc;
3027 __u32 temp_offset;
3028 struct smb_com_ntransact_req *pSMB;
3029
3030 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
3031 (void **)&pSMB);
3032 if (rc)
3033 return rc;
3034 *ret_buf = (void *)pSMB;
3035 pSMB->Reserved = 0;
3036 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
3037 pSMB->TotalDataCount = 0;
3038 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
3039 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3040 pSMB->ParameterCount = pSMB->TotalParameterCount;
3041 pSMB->DataCount = pSMB->TotalDataCount;
3042 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
3043 (setup_count * 2) - 4 /* for rfc1001 length itself */;
3044 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
3045 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
3046 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
3047 pSMB->SubCommand = cpu_to_le16(sub_command);
3048 return 0;
3049}
3050
3051static int
3052validate_ntransact(char *buf, char **ppparm, char **ppdata,
3053 __u32 *pparmlen, __u32 *pdatalen)
3054{
3055 char *end_of_smb;
3056 __u32 data_count, data_offset, parm_count, parm_offset;
3057 struct smb_com_ntransact_rsp *pSMBr;
3058
3059 *pdatalen = 0;
3060 *pparmlen = 0;
3061
3062 if (buf == NULL)
3063 return -EINVAL;
3064
3065 pSMBr = (struct smb_com_ntransact_rsp *)buf;
3066
3067 /* ByteCount was converted from little endian in SendReceive */
3068 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
3069 (char *)&pSMBr->ByteCount;
3070
3071 data_offset = le32_to_cpu(pSMBr->DataOffset);
3072 data_count = le32_to_cpu(pSMBr->DataCount);
3073 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
3074 parm_count = le32_to_cpu(pSMBr->ParameterCount);
3075
3076 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
3077 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
3078
3079 /* should we also check that parm and data areas do not overlap? */
3080 if (*ppparm > end_of_smb) {
3081 cFYI(1, "parms start after end of smb");
3082 return -EINVAL;
3083 } else if (parm_count + *ppparm > end_of_smb) {
3084 cFYI(1, "parm end after end of smb");
3085 return -EINVAL;
3086 } else if (*ppdata > end_of_smb) {
3087 cFYI(1, "data starts after end of smb");
3088 return -EINVAL;
3089 } else if (data_count + *ppdata > end_of_smb) {
3090 cFYI(1, "data %p + count %d (%p) past smb end %p start %p",
3091 *ppdata, data_count, (data_count + *ppdata),
3092 end_of_smb, pSMBr);
3093 return -EINVAL;
3094 } else if (parm_count + data_count > pSMBr->ByteCount) {
3095 cFYI(1, "parm count and data count larger than SMB");
3096 return -EINVAL;
3097 }
3098 *pdatalen = data_count;
3099 *pparmlen = parm_count;
3100 return 0;
3101}
3102
Steve French0a4b92c2006-01-12 15:44:21 -08003103/* Get Security Descriptor (by handle) from remote server for a file or dir */
3104int
3105CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f0c2007-10-25 21:17:17 +00003106 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003107{
3108 int rc = 0;
3109 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003110 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003111 struct kvec iov[1];
3112
Joe Perchesb6b38f72010-04-21 03:50:45 +00003113 cFYI(1, "GetCifsACL");
Steve French0a4b92c2006-01-12 15:44:21 -08003114
Steve French630f3f0c2007-10-25 21:17:17 +00003115 *pbuflen = 0;
3116 *acl_inf = NULL;
3117
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003118 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003119 8 /* parm len */, tcon, (void **) &pSMB);
3120 if (rc)
3121 return rc;
3122
3123 pSMB->MaxParameterCount = cpu_to_le32(4);
3124 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3125 pSMB->MaxSetupCount = 0;
3126 pSMB->Fid = fid; /* file handle always le */
3127 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3128 CIFS_ACL_DACL);
3129 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3130 pSMB->hdr.smb_buf_length += 11;
3131 iov[0].iov_base = (char *)pSMB;
3132 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3133
Steve Frencha761ac52007-10-18 21:45:27 +00003134 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Jeff Layton77499812011-01-11 07:24:23 -05003135 0);
Steve French0a4b92c2006-01-12 15:44:21 -08003136 cifs_stats_inc(&tcon->num_acl_get);
3137 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003138 cFYI(1, "Send error in QuerySecDesc = %d", rc);
Steve French0a4b92c2006-01-12 15:44:21 -08003139 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003140 __le32 *parm;
Steve French630f3f0c2007-10-25 21:17:17 +00003141 __u32 parm_len;
3142 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003143 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f0c2007-10-25 21:17:17 +00003144 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003145
3146/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003147 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f0c2007-10-25 21:17:17 +00003148 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003149 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003150 goto qsec_out;
3151 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3152
Joe Perchesb6b38f72010-04-21 03:50:45 +00003153 cFYI(1, "smb %p parm %p data %p", pSMBr, parm, *acl_inf);
Steve French0a4b92c2006-01-12 15:44:21 -08003154
3155 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3156 rc = -EIO; /* bad smb */
Steve French630f3f0c2007-10-25 21:17:17 +00003157 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003158 goto qsec_out;
3159 }
3160
3161/* BB check that data area is minimum length and as big as acl_len */
3162
Steve Frenchaf6f4612007-10-16 18:40:37 +00003163 acl_len = le32_to_cpu(*parm);
Steve French630f3f0c2007-10-25 21:17:17 +00003164 if (acl_len != *pbuflen) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003165 cERROR(1, "acl length %d does not match %d",
3166 acl_len, *pbuflen);
Steve French630f3f0c2007-10-25 21:17:17 +00003167 if (*pbuflen > acl_len)
3168 *pbuflen = acl_len;
3169 }
Steve French0a4b92c2006-01-12 15:44:21 -08003170
Steve French630f3f0c2007-10-25 21:17:17 +00003171 /* check if buffer is big enough for the acl
3172 header followed by the smallest SID */
3173 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3174 (*pbuflen >= 64 * 1024)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003175 cERROR(1, "bad acl length %d", *pbuflen);
Steve French630f3f0c2007-10-25 21:17:17 +00003176 rc = -EINVAL;
3177 *pbuflen = 0;
3178 } else {
3179 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3180 if (*acl_inf == NULL) {
3181 *pbuflen = 0;
3182 rc = -ENOMEM;
3183 }
3184 memcpy(*acl_inf, pdata, *pbuflen);
3185 }
Steve French0a4b92c2006-01-12 15:44:21 -08003186 }
3187qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003188 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003189 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003190 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003191 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003192/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003193 return rc;
3194}
Steve French97837582007-12-31 07:47:21 +00003195
3196int
3197CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3198 struct cifs_ntsd *pntsd, __u32 acllen)
3199{
3200 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3201 int rc = 0;
3202 int bytes_returned = 0;
3203 SET_SEC_DESC_REQ *pSMB = NULL;
3204 NTRANSACT_RSP *pSMBr = NULL;
3205
3206setCifsAclRetry:
3207 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3208 (void **) &pSMBr);
3209 if (rc)
3210 return (rc);
3211
3212 pSMB->MaxSetupCount = 0;
3213 pSMB->Reserved = 0;
3214
3215 param_count = 8;
3216 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3217 data_count = acllen;
3218 data_offset = param_offset + param_count;
3219 byte_count = 3 /* pad */ + param_count;
3220
3221 pSMB->DataCount = cpu_to_le32(data_count);
3222 pSMB->TotalDataCount = pSMB->DataCount;
3223 pSMB->MaxParameterCount = cpu_to_le32(4);
3224 pSMB->MaxDataCount = cpu_to_le32(16384);
3225 pSMB->ParameterCount = cpu_to_le32(param_count);
3226 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3227 pSMB->TotalParameterCount = pSMB->ParameterCount;
3228 pSMB->DataOffset = cpu_to_le32(data_offset);
3229 pSMB->SetupCount = 0;
3230 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3231 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3232
3233 pSMB->Fid = fid; /* file handle always le */
3234 pSMB->Reserved2 = 0;
3235 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3236
3237 if (pntsd && acllen) {
3238 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3239 (char *) pntsd,
3240 acllen);
3241 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3242
3243 } else
3244 pSMB->hdr.smb_buf_length += byte_count;
3245
3246 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3247 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3248
Joe Perchesb6b38f72010-04-21 03:50:45 +00003249 cFYI(1, "SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc);
Steve French97837582007-12-31 07:47:21 +00003250 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003251 cFYI(1, "Set CIFS ACL returned %d", rc);
Steve French97837582007-12-31 07:47:21 +00003252 cifs_buf_release(pSMB);
3253
3254 if (rc == -EAGAIN)
3255 goto setCifsAclRetry;
3256
3257 return (rc);
3258}
3259
Jeff Layton79df1ba2010-12-06 12:52:08 -05003260#endif /* CONFIG_CIFS_ACL */
Steve French0a4b92c2006-01-12 15:44:21 -08003261
Steve French6b8edfe2005-08-23 20:26:03 -07003262/* Legacy Query Path Information call for lookup to old servers such
3263 as Win9x/WinME */
3264int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003265 const unsigned char *searchName,
3266 FILE_ALL_INFO *pFinfo,
3267 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003268{
Steve Frenchad7a2922008-02-07 23:25:02 +00003269 QUERY_INFORMATION_REQ *pSMB;
3270 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003271 int rc = 0;
3272 int bytes_returned;
3273 int name_len;
3274
Joe Perchesb6b38f72010-04-21 03:50:45 +00003275 cFYI(1, "In SMBQPath path %s", searchName);
Steve French6b8edfe2005-08-23 20:26:03 -07003276QInfRetry:
3277 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003278 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003279 if (rc)
3280 return rc;
3281
3282 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3283 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003284 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3285 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003286 name_len++; /* trailing null */
3287 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003288 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003289 name_len = strnlen(searchName, PATH_MAX);
3290 name_len++; /* trailing null */
3291 strncpy(pSMB->FileName, searchName, name_len);
3292 }
3293 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003294 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003295 pSMB->hdr.smb_buf_length += (__u16) name_len;
3296 pSMB->ByteCount = cpu_to_le16(name_len);
3297
3298 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003299 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003300 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003301 cFYI(1, "Send error in QueryInfo = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003302 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003303 struct timespec ts;
3304 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003305
3306 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003307 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003308 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003309 ts.tv_nsec = 0;
3310 ts.tv_sec = time;
3311 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003312 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003313 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3314 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003315 pFinfo->AllocationSize =
3316 cpu_to_le64(le32_to_cpu(pSMBr->size));
3317 pFinfo->EndOfFile = pFinfo->AllocationSize;
3318 pFinfo->Attributes =
3319 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003320 } else
3321 rc = -EIO; /* bad buffer passed in */
3322
3323 cifs_buf_release(pSMB);
3324
3325 if (rc == -EAGAIN)
3326 goto QInfRetry;
3327
3328 return rc;
3329}
3330
Jeff Laytonbcd53572010-02-12 07:44:16 -05003331int
3332CIFSSMBQFileInfo(const int xid, struct cifsTconInfo *tcon,
3333 u16 netfid, FILE_ALL_INFO *pFindData)
3334{
3335 struct smb_t2_qfi_req *pSMB = NULL;
3336 struct smb_t2_qfi_rsp *pSMBr = NULL;
3337 int rc = 0;
3338 int bytes_returned;
3339 __u16 params, byte_count;
Steve French6b8edfe2005-08-23 20:26:03 -07003340
Jeff Laytonbcd53572010-02-12 07:44:16 -05003341QFileInfoRetry:
3342 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3343 (void **) &pSMBr);
3344 if (rc)
3345 return rc;
Steve French6b8edfe2005-08-23 20:26:03 -07003346
Jeff Laytonbcd53572010-02-12 07:44:16 -05003347 params = 2 /* level */ + 2 /* fid */;
3348 pSMB->t2.TotalDataCount = 0;
3349 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3350 /* BB find exact max data count below from sess structure BB */
3351 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3352 pSMB->t2.MaxSetupCount = 0;
3353 pSMB->t2.Reserved = 0;
3354 pSMB->t2.Flags = 0;
3355 pSMB->t2.Timeout = 0;
3356 pSMB->t2.Reserved2 = 0;
3357 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3358 Fid) - 4);
3359 pSMB->t2.DataCount = 0;
3360 pSMB->t2.DataOffset = 0;
3361 pSMB->t2.SetupCount = 1;
3362 pSMB->t2.Reserved3 = 0;
3363 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3364 byte_count = params + 1 /* pad */ ;
3365 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3366 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3367 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
3368 pSMB->Pad = 0;
3369 pSMB->Fid = netfid;
3370 pSMB->hdr.smb_buf_length += byte_count;
3371
3372 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3373 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3374 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003375 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonbcd53572010-02-12 07:44:16 -05003376 } else { /* decode response */
3377 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3378
3379 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3380 rc = -EIO;
3381 else if (pSMBr->ByteCount < 40)
3382 rc = -EIO; /* bad smb */
3383 else if (pFindData) {
3384 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3385 memcpy((char *) pFindData,
3386 (char *) &pSMBr->hdr.Protocol +
3387 data_offset, sizeof(FILE_ALL_INFO));
3388 } else
3389 rc = -ENOMEM;
3390 }
3391 cifs_buf_release(pSMB);
3392 if (rc == -EAGAIN)
3393 goto QFileInfoRetry;
3394
3395 return rc;
3396}
Steve French6b8edfe2005-08-23 20:26:03 -07003397
Linus Torvalds1da177e2005-04-16 15:20:36 -07003398int
3399CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3400 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003401 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003402 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003403 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003404{
3405/* level 263 SMB_QUERY_FILE_ALL_INFO */
3406 TRANSACTION2_QPI_REQ *pSMB = NULL;
3407 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3408 int rc = 0;
3409 int bytes_returned;
3410 int name_len;
3411 __u16 params, byte_count;
3412
Joe Perchesb6b38f72010-04-21 03:50:45 +00003413/* cFYI(1, "In QPathInfo path %s", searchName); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003414QPathInfoRetry:
3415 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3416 (void **) &pSMBr);
3417 if (rc)
3418 return rc;
3419
3420 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3421 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003422 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003423 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003424 name_len++; /* trailing null */
3425 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003426 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003427 name_len = strnlen(searchName, PATH_MAX);
3428 name_len++; /* trailing null */
3429 strncpy(pSMB->FileName, searchName, name_len);
3430 }
3431
Steve French50c2f752007-07-13 00:33:32 +00003432 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003433 pSMB->TotalDataCount = 0;
3434 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00003435 /* BB find exact max SMB PDU from sess structure BB */
3436 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003437 pSMB->MaxSetupCount = 0;
3438 pSMB->Reserved = 0;
3439 pSMB->Flags = 0;
3440 pSMB->Timeout = 0;
3441 pSMB->Reserved2 = 0;
3442 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003443 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003444 pSMB->DataCount = 0;
3445 pSMB->DataOffset = 0;
3446 pSMB->SetupCount = 1;
3447 pSMB->Reserved3 = 0;
3448 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3449 byte_count = params + 1 /* pad */ ;
3450 pSMB->TotalParameterCount = cpu_to_le16(params);
3451 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003452 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003453 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3454 else
3455 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003456 pSMB->Reserved4 = 0;
3457 pSMB->hdr.smb_buf_length += byte_count;
3458 pSMB->ByteCount = cpu_to_le16(byte_count);
3459
3460 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3461 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3462 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003463 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003464 } else { /* decode response */
3465 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3466
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003467 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3468 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003469 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003470 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003471 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003472 rc = -EIO; /* 24 or 26 expected but we do not read
3473 last field */
3474 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003475 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003476 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003477
3478 /* On legacy responses we do not read the last field,
3479 EAsize, fortunately since it varies by subdialect and
3480 also note it differs on Set vs. Get, ie two bytes or 4
3481 bytes depending but we don't care here */
3482 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003483 size = sizeof(FILE_INFO_STANDARD);
3484 else
3485 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003486 memcpy((char *) pFindData,
3487 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003488 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003489 } else
3490 rc = -ENOMEM;
3491 }
3492 cifs_buf_release(pSMB);
3493 if (rc == -EAGAIN)
3494 goto QPathInfoRetry;
3495
3496 return rc;
3497}
3498
3499int
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003500CIFSSMBUnixQFileInfo(const int xid, struct cifsTconInfo *tcon,
3501 u16 netfid, FILE_UNIX_BASIC_INFO *pFindData)
3502{
3503 struct smb_t2_qfi_req *pSMB = NULL;
3504 struct smb_t2_qfi_rsp *pSMBr = NULL;
3505 int rc = 0;
3506 int bytes_returned;
3507 __u16 params, byte_count;
3508
3509UnixQFileInfoRetry:
3510 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3511 (void **) &pSMBr);
3512 if (rc)
3513 return rc;
3514
3515 params = 2 /* level */ + 2 /* fid */;
3516 pSMB->t2.TotalDataCount = 0;
3517 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3518 /* BB find exact max data count below from sess structure BB */
3519 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3520 pSMB->t2.MaxSetupCount = 0;
3521 pSMB->t2.Reserved = 0;
3522 pSMB->t2.Flags = 0;
3523 pSMB->t2.Timeout = 0;
3524 pSMB->t2.Reserved2 = 0;
3525 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3526 Fid) - 4);
3527 pSMB->t2.DataCount = 0;
3528 pSMB->t2.DataOffset = 0;
3529 pSMB->t2.SetupCount = 1;
3530 pSMB->t2.Reserved3 = 0;
3531 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3532 byte_count = params + 1 /* pad */ ;
3533 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3534 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3535 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3536 pSMB->Pad = 0;
3537 pSMB->Fid = netfid;
3538 pSMB->hdr.smb_buf_length += byte_count;
3539
3540 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3541 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3542 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003543 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003544 } else { /* decode response */
3545 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3546
3547 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003548 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003549 "Unix Extensions can be disabled on mount "
Steve Frenchf19159d2010-04-21 04:12:10 +00003550 "by specifying the nosfu mount option.");
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003551 rc = -EIO; /* bad smb */
3552 } else {
3553 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3554 memcpy((char *) pFindData,
3555 (char *) &pSMBr->hdr.Protocol +
3556 data_offset,
3557 sizeof(FILE_UNIX_BASIC_INFO));
3558 }
3559 }
3560
3561 cifs_buf_release(pSMB);
3562 if (rc == -EAGAIN)
3563 goto UnixQFileInfoRetry;
3564
3565 return rc;
3566}
3567
3568int
Linus Torvalds1da177e2005-04-16 15:20:36 -07003569CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3570 const unsigned char *searchName,
Steve French582d21e2008-05-13 04:54:12 +00003571 FILE_UNIX_BASIC_INFO *pFindData,
Steve French737b7582005-04-28 22:41:06 -07003572 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003573{
3574/* SMB_QUERY_FILE_UNIX_BASIC */
3575 TRANSACTION2_QPI_REQ *pSMB = NULL;
3576 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3577 int rc = 0;
3578 int bytes_returned = 0;
3579 int name_len;
3580 __u16 params, byte_count;
3581
Joe Perchesb6b38f72010-04-21 03:50:45 +00003582 cFYI(1, "In QPathInfo (Unix) the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003583UnixQPathInfoRetry:
3584 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3585 (void **) &pSMBr);
3586 if (rc)
3587 return rc;
3588
3589 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3590 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003591 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003592 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003593 name_len++; /* trailing null */
3594 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003595 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003596 name_len = strnlen(searchName, PATH_MAX);
3597 name_len++; /* trailing null */
3598 strncpy(pSMB->FileName, searchName, name_len);
3599 }
3600
Steve French50c2f752007-07-13 00:33:32 +00003601 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003602 pSMB->TotalDataCount = 0;
3603 pSMB->MaxParameterCount = cpu_to_le16(2);
3604 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003605 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003606 pSMB->MaxSetupCount = 0;
3607 pSMB->Reserved = 0;
3608 pSMB->Flags = 0;
3609 pSMB->Timeout = 0;
3610 pSMB->Reserved2 = 0;
3611 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003612 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003613 pSMB->DataCount = 0;
3614 pSMB->DataOffset = 0;
3615 pSMB->SetupCount = 1;
3616 pSMB->Reserved3 = 0;
3617 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3618 byte_count = params + 1 /* pad */ ;
3619 pSMB->TotalParameterCount = cpu_to_le16(params);
3620 pSMB->ParameterCount = pSMB->TotalParameterCount;
3621 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3622 pSMB->Reserved4 = 0;
3623 pSMB->hdr.smb_buf_length += byte_count;
3624 pSMB->ByteCount = cpu_to_le16(byte_count);
3625
3626 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3627 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3628 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003629 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003630 } else { /* decode response */
3631 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3632
3633 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003634 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Steve French1e71f252007-09-20 15:30:07 +00003635 "Unix Extensions can be disabled on mount "
Joe Perchesb6b38f72010-04-21 03:50:45 +00003636 "by specifying the nosfu mount option.");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003637 rc = -EIO; /* bad smb */
3638 } else {
3639 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3640 memcpy((char *) pFindData,
3641 (char *) &pSMBr->hdr.Protocol +
3642 data_offset,
Steve French630f3f0c2007-10-25 21:17:17 +00003643 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003644 }
3645 }
3646 cifs_buf_release(pSMB);
3647 if (rc == -EAGAIN)
3648 goto UnixQPathInfoRetry;
3649
3650 return rc;
3651}
3652
Linus Torvalds1da177e2005-04-16 15:20:36 -07003653/* xid, tcon, searchName and codepage are input parms, rest are returned */
3654int
3655CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003656 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003657 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003658 __u16 *pnetfid,
3659 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003660{
3661/* level 257 SMB_ */
3662 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3663 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003664 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003665 int rc = 0;
3666 int bytes_returned = 0;
3667 int name_len;
3668 __u16 params, byte_count;
3669
Joe Perchesb6b38f72010-04-21 03:50:45 +00003670 cFYI(1, "In FindFirst for %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003671
3672findFirstRetry:
3673 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3674 (void **) &pSMBr);
3675 if (rc)
3676 return rc;
3677
3678 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3679 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003680 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003681 PATH_MAX, nls_codepage, remap);
3682 /* We can not add the asterik earlier in case
3683 it got remapped to 0xF03A as if it were part of the
3684 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003685 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003686 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003687 pSMB->FileName[name_len+1] = 0;
3688 pSMB->FileName[name_len+2] = '*';
3689 pSMB->FileName[name_len+3] = 0;
3690 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003691 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3692 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003693 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003694 } else { /* BB add check for overrun of SMB buf BB */
3695 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003696/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003697 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003698 free buffer exit; BB */
3699 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003700 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003701 pSMB->FileName[name_len+1] = '*';
3702 pSMB->FileName[name_len+2] = 0;
3703 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003704 }
3705
3706 params = 12 + name_len /* includes null */ ;
3707 pSMB->TotalDataCount = 0; /* no EAs */
3708 pSMB->MaxParameterCount = cpu_to_le16(10);
3709 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3710 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3711 pSMB->MaxSetupCount = 0;
3712 pSMB->Reserved = 0;
3713 pSMB->Flags = 0;
3714 pSMB->Timeout = 0;
3715 pSMB->Reserved2 = 0;
3716 byte_count = params + 1 /* pad */ ;
3717 pSMB->TotalParameterCount = cpu_to_le16(params);
3718 pSMB->ParameterCount = pSMB->TotalParameterCount;
3719 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003720 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3721 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003722 pSMB->DataCount = 0;
3723 pSMB->DataOffset = 0;
3724 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3725 pSMB->Reserved3 = 0;
3726 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3727 pSMB->SearchAttributes =
3728 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3729 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003730 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3731 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003732 CIFS_SEARCH_RETURN_RESUME);
3733 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3734
3735 /* BB what should we set StorageType to? Does it matter? BB */
3736 pSMB->SearchStorageType = 0;
3737 pSMB->hdr.smb_buf_length += byte_count;
3738 pSMB->ByteCount = cpu_to_le16(byte_count);
3739
3740 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3741 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003742 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003743
Steve French88274812006-03-09 22:21:45 +00003744 if (rc) {/* BB add logic to retry regular search if Unix search
3745 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003746 /* BB Add code to handle unsupported level rc */
Joe Perchesb6b38f72010-04-21 03:50:45 +00003747 cFYI(1, "Error in FindFirst = %d", rc);
Steve French1982c342005-08-17 12:38:22 -07003748
Steve French88274812006-03-09 22:21:45 +00003749 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003750
3751 /* BB eventually could optimize out free and realloc of buf */
3752 /* for this case */
3753 if (rc == -EAGAIN)
3754 goto findFirstRetry;
3755 } else { /* decode response */
3756 /* BB remember to free buffer if error BB */
3757 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003758 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003759 unsigned int lnoff;
3760
Linus Torvalds1da177e2005-04-16 15:20:36 -07003761 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003762 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003763 else
Steve French4b18f2a2008-04-29 00:06:05 +00003764 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003765
3766 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003767 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003768 psrch_inf->srch_entries_start =
3769 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003770 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003771 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3772 le16_to_cpu(pSMBr->t2.ParameterOffset));
3773
Steve French790fe572007-07-07 19:25:05 +00003774 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003775 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003776 else
Steve French4b18f2a2008-04-29 00:06:05 +00003777 psrch_inf->endOfSearch = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003778
Steve French50c2f752007-07-13 00:33:32 +00003779 psrch_inf->entries_in_buffer =
3780 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003781 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003782 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003783 lnoff = le16_to_cpu(parms->LastNameOffset);
3784 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3785 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003786 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003787 psrch_inf->last_entry = NULL;
3788 return rc;
3789 }
3790
Steve French0752f152008-10-07 20:03:33 +00003791 psrch_inf->last_entry = psrch_inf->srch_entries_start +
Steve Frenchb77d7532008-10-08 19:13:46 +00003792 lnoff;
3793
Linus Torvalds1da177e2005-04-16 15:20:36 -07003794 *pnetfid = parms->SearchHandle;
3795 } else {
3796 cifs_buf_release(pSMB);
3797 }
3798 }
3799
3800 return rc;
3801}
3802
3803int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003804 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003805{
3806 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3807 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003808 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003809 char *response_data;
3810 int rc = 0;
3811 int bytes_returned, name_len;
3812 __u16 params, byte_count;
3813
Joe Perchesb6b38f72010-04-21 03:50:45 +00003814 cFYI(1, "In FindNext");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003815
Steve French4b18f2a2008-04-29 00:06:05 +00003816 if (psrch_inf->endOfSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003817 return -ENOENT;
3818
3819 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3820 (void **) &pSMBr);
3821 if (rc)
3822 return rc;
3823
Steve French50c2f752007-07-13 00:33:32 +00003824 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003825 byte_count = 0;
3826 pSMB->TotalDataCount = 0; /* no EAs */
3827 pSMB->MaxParameterCount = cpu_to_le16(8);
3828 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003829 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3830 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003831 pSMB->MaxSetupCount = 0;
3832 pSMB->Reserved = 0;
3833 pSMB->Flags = 0;
3834 pSMB->Timeout = 0;
3835 pSMB->Reserved2 = 0;
3836 pSMB->ParameterOffset = cpu_to_le16(
3837 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3838 pSMB->DataCount = 0;
3839 pSMB->DataOffset = 0;
3840 pSMB->SetupCount = 1;
3841 pSMB->Reserved3 = 0;
3842 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3843 pSMB->SearchHandle = searchHandle; /* always kept as le */
3844 pSMB->SearchCount =
Steve French630f3f0c2007-10-25 21:17:17 +00003845 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003846 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3847 pSMB->ResumeKey = psrch_inf->resume_key;
3848 pSMB->SearchFlags =
3849 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3850
3851 name_len = psrch_inf->resume_name_len;
3852 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003853 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003854 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3855 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003856 /* 14 byte parm len above enough for 2 byte null terminator */
3857 pSMB->ResumeFileName[name_len] = 0;
3858 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003859 } else {
3860 rc = -EINVAL;
3861 goto FNext2_err_exit;
3862 }
3863 byte_count = params + 1 /* pad */ ;
3864 pSMB->TotalParameterCount = cpu_to_le16(params);
3865 pSMB->ParameterCount = pSMB->TotalParameterCount;
3866 pSMB->hdr.smb_buf_length += byte_count;
3867 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003868
Linus Torvalds1da177e2005-04-16 15:20:36 -07003869 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3870 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003871 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003872 if (rc) {
3873 if (rc == -EBADF) {
Steve French4b18f2a2008-04-29 00:06:05 +00003874 psrch_inf->endOfSearch = true;
Jeff Layton63534502008-05-12 19:56:05 -07003875 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00003876 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003877 } else
Joe Perchesb6b38f72010-04-21 03:50:45 +00003878 cFYI(1, "FindNext returned = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003879 } else { /* decode response */
3880 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003881
Steve French790fe572007-07-07 19:25:05 +00003882 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003883 unsigned int lnoff;
3884
Linus Torvalds1da177e2005-04-16 15:20:36 -07003885 /* BB fixme add lock for file (srch_info) struct here */
3886 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003887 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003888 else
Steve French4b18f2a2008-04-29 00:06:05 +00003889 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003890 response_data = (char *) &pSMBr->hdr.Protocol +
3891 le16_to_cpu(pSMBr->t2.ParameterOffset);
3892 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3893 response_data = (char *)&pSMBr->hdr.Protocol +
3894 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003895 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003896 cifs_small_buf_release(
3897 psrch_inf->ntwrk_buf_start);
3898 else
3899 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003900 psrch_inf->srch_entries_start = response_data;
3901 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003902 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003903 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003904 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003905 else
Steve French4b18f2a2008-04-29 00:06:05 +00003906 psrch_inf->endOfSearch = false;
Steve French50c2f752007-07-13 00:33:32 +00003907 psrch_inf->entries_in_buffer =
3908 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003909 psrch_inf->index_of_last_entry +=
3910 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003911 lnoff = le16_to_cpu(parms->LastNameOffset);
3912 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3913 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003914 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003915 psrch_inf->last_entry = NULL;
3916 return rc;
3917 } else
3918 psrch_inf->last_entry =
3919 psrch_inf->srch_entries_start + lnoff;
3920
Joe Perchesb6b38f72010-04-21 03:50:45 +00003921/* cFYI(1, "fnxt2 entries in buf %d index_of_last %d",
3922 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003923
3924 /* BB fixme add unlock here */
3925 }
3926
3927 }
3928
3929 /* BB On error, should we leave previous search buf (and count and
3930 last entry fields) intact or free the previous one? */
3931
3932 /* Note: On -EAGAIN error only caller can retry on handle based calls
3933 since file handle passed in no longer valid */
3934FNext2_err_exit:
3935 if (rc != 0)
3936 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003937 return rc;
3938}
3939
3940int
Steve French50c2f752007-07-13 00:33:32 +00003941CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3942 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003943{
3944 int rc = 0;
3945 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003946
Joe Perchesb6b38f72010-04-21 03:50:45 +00003947 cFYI(1, "In CIFSSMBFindClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003948 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3949
3950 /* no sense returning error if session restarted
3951 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003952 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003953 return 0;
3954 if (rc)
3955 return rc;
3956
Linus Torvalds1da177e2005-04-16 15:20:36 -07003957 pSMB->FileID = searchHandle;
3958 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003959 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003960 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003961 cERROR(1, "Send error in FindClose = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003962
Steve Frencha4544342005-08-24 13:59:35 -07003963 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003964
3965 /* Since session is dead, search handle closed on server already */
3966 if (rc == -EAGAIN)
3967 rc = 0;
3968
3969 return rc;
3970}
3971
Linus Torvalds1da177e2005-04-16 15:20:36 -07003972int
3973CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003974 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003975 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003976 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003977{
3978 int rc = 0;
3979 TRANSACTION2_QPI_REQ *pSMB = NULL;
3980 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3981 int name_len, bytes_returned;
3982 __u16 params, byte_count;
3983
Joe Perchesb6b38f72010-04-21 03:50:45 +00003984 cFYI(1, "In GetSrvInodeNum for %s", searchName);
Steve French790fe572007-07-07 19:25:05 +00003985 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003986 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003987
3988GetInodeNumberRetry:
3989 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003990 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003991 if (rc)
3992 return rc;
3993
Linus Torvalds1da177e2005-04-16 15:20:36 -07003994 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3995 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003996 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003997 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003998 name_len++; /* trailing null */
3999 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004000 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004001 name_len = strnlen(searchName, PATH_MAX);
4002 name_len++; /* trailing null */
4003 strncpy(pSMB->FileName, searchName, name_len);
4004 }
4005
4006 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
4007 pSMB->TotalDataCount = 0;
4008 pSMB->MaxParameterCount = cpu_to_le16(2);
4009 /* BB find exact max data count below from sess structure BB */
4010 pSMB->MaxDataCount = cpu_to_le16(4000);
4011 pSMB->MaxSetupCount = 0;
4012 pSMB->Reserved = 0;
4013 pSMB->Flags = 0;
4014 pSMB->Timeout = 0;
4015 pSMB->Reserved2 = 0;
4016 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004017 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004018 pSMB->DataCount = 0;
4019 pSMB->DataOffset = 0;
4020 pSMB->SetupCount = 1;
4021 pSMB->Reserved3 = 0;
4022 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
4023 byte_count = params + 1 /* pad */ ;
4024 pSMB->TotalParameterCount = cpu_to_le16(params);
4025 pSMB->ParameterCount = pSMB->TotalParameterCount;
4026 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
4027 pSMB->Reserved4 = 0;
4028 pSMB->hdr.smb_buf_length += byte_count;
4029 pSMB->ByteCount = cpu_to_le16(byte_count);
4030
4031 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4032 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4033 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004034 cFYI(1, "error %d in QueryInternalInfo", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004035 } else {
4036 /* decode response */
4037 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4038 if (rc || (pSMBr->ByteCount < 2))
4039 /* BB also check enough total bytes returned */
4040 /* If rc should we check for EOPNOSUPP and
4041 disable the srvino flag? or in caller? */
4042 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00004043 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004044 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4045 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00004046 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004047 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00004048 if (count < 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004049 cFYI(1, "Illegal size ret in QryIntrnlInf");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004050 rc = -EIO;
4051 goto GetInodeNumOut;
4052 }
4053 pfinfo = (struct file_internal_info *)
4054 (data_offset + (char *) &pSMBr->hdr.Protocol);
Steve French85a6dac2009-04-01 05:22:00 +00004055 *inode_number = le64_to_cpu(pfinfo->UniqueId);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004056 }
4057 }
4058GetInodeNumOut:
4059 cifs_buf_release(pSMB);
4060 if (rc == -EAGAIN)
4061 goto GetInodeNumberRetry;
4062 return rc;
4063}
Linus Torvalds1da177e2005-04-16 15:20:36 -07004064
Igor Mammedovfec45852008-05-16 13:06:30 +04004065/* parses DFS refferal V3 structure
4066 * caller is responsible for freeing target_nodes
4067 * returns:
4068 * on success - 0
4069 * on failure - errno
4070 */
4071static int
Steve Frencha1fe78f2008-05-16 18:48:38 +00004072parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
Igor Mammedovfec45852008-05-16 13:06:30 +04004073 unsigned int *num_of_nodes,
4074 struct dfs_info3_param **target_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004075 const struct nls_table *nls_codepage, int remap,
4076 const char *searchName)
Igor Mammedovfec45852008-05-16 13:06:30 +04004077{
4078 int i, rc = 0;
4079 char *data_end;
4080 bool is_unicode;
4081 struct dfs_referral_level_3 *ref;
4082
Harvey Harrison5ca33c62008-07-23 17:45:58 -07004083 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
4084 is_unicode = true;
4085 else
4086 is_unicode = false;
Igor Mammedovfec45852008-05-16 13:06:30 +04004087 *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
4088
4089 if (*num_of_nodes < 1) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004090 cERROR(1, "num_referrals: must be at least > 0,"
4091 "but we get num_referrals = %d\n", *num_of_nodes);
Igor Mammedovfec45852008-05-16 13:06:30 +04004092 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004093 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004094 }
4095
4096 ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
Al Viro1d92cfd2008-06-02 10:59:02 +01004097 if (ref->VersionNumber != cpu_to_le16(3)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004098 cERROR(1, "Referrals of V%d version are not supported,"
4099 "should be V3", le16_to_cpu(ref->VersionNumber));
Igor Mammedovfec45852008-05-16 13:06:30 +04004100 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004101 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004102 }
4103
4104 /* get the upper boundary of the resp buffer */
4105 data_end = (char *)(&(pSMBr->PathConsumed)) +
4106 le16_to_cpu(pSMBr->t2.DataCount);
4107
Steve Frenchf19159d2010-04-21 04:12:10 +00004108 cFYI(1, "num_referrals: %d dfs flags: 0x%x ...\n",
Igor Mammedovfec45852008-05-16 13:06:30 +04004109 *num_of_nodes,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004110 le32_to_cpu(pSMBr->DFSFlags));
Igor Mammedovfec45852008-05-16 13:06:30 +04004111
4112 *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
4113 *num_of_nodes, GFP_KERNEL);
4114 if (*target_nodes == NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004115 cERROR(1, "Failed to allocate buffer for target_nodes\n");
Igor Mammedovfec45852008-05-16 13:06:30 +04004116 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004117 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004118 }
4119
Daniel Mack3ad2f3f2010-02-03 08:01:28 +08004120 /* collect necessary data from referrals */
Igor Mammedovfec45852008-05-16 13:06:30 +04004121 for (i = 0; i < *num_of_nodes; i++) {
4122 char *temp;
4123 int max_len;
4124 struct dfs_info3_param *node = (*target_nodes)+i;
4125
Steve French0e0d2cf2009-05-01 05:27:32 +00004126 node->flags = le32_to_cpu(pSMBr->DFSFlags);
Igor Mammedov2c556082008-10-23 13:58:42 +04004127 if (is_unicode) {
Jeff Layton331c3132008-12-17 06:31:53 -05004128 __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
4129 GFP_KERNEL);
Steve French2920ee22009-08-31 15:27:26 +00004130 if (tmp == NULL) {
4131 rc = -ENOMEM;
4132 goto parse_DFS_referrals_exit;
4133 }
Igor Mammedov2c556082008-10-23 13:58:42 +04004134 cifsConvertToUCS((__le16 *) tmp, searchName,
4135 PATH_MAX, nls_codepage, remap);
Jeff Layton69f801f2009-04-30 06:46:32 -04004136 node->path_consumed = cifs_ucs2_bytes(tmp,
4137 le16_to_cpu(pSMBr->PathConsumed),
Igor Mammedov2c556082008-10-23 13:58:42 +04004138 nls_codepage);
4139 kfree(tmp);
4140 } else
4141 node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
4142
Igor Mammedovfec45852008-05-16 13:06:30 +04004143 node->server_type = le16_to_cpu(ref->ServerType);
4144 node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
4145
4146 /* copy DfsPath */
4147 temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
4148 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004149 node->path_name = cifs_strndup_from_ucs(temp, max_len,
4150 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004151 if (!node->path_name) {
4152 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004153 goto parse_DFS_referrals_exit;
Jeff Layton066ce682009-04-30 07:16:14 -04004154 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004155
4156 /* copy link target UNC */
4157 temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
4158 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004159 node->node_name = cifs_strndup_from_ucs(temp, max_len,
4160 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004161 if (!node->node_name)
4162 rc = -ENOMEM;
Igor Mammedovfec45852008-05-16 13:06:30 +04004163 }
4164
Steve Frencha1fe78f2008-05-16 18:48:38 +00004165parse_DFS_referrals_exit:
Igor Mammedovfec45852008-05-16 13:06:30 +04004166 if (rc) {
4167 free_dfs_info_array(*target_nodes, *num_of_nodes);
4168 *target_nodes = NULL;
4169 *num_of_nodes = 0;
4170 }
4171 return rc;
4172}
4173
Linus Torvalds1da177e2005-04-16 15:20:36 -07004174int
4175CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
4176 const unsigned char *searchName,
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004177 struct dfs_info3_param **target_nodes,
4178 unsigned int *num_of_nodes,
Steve French737b7582005-04-28 22:41:06 -07004179 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004180{
4181/* TRANS2_GET_DFS_REFERRAL */
4182 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
4183 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004184 int rc = 0;
4185 int bytes_returned;
4186 int name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004187 __u16 params, byte_count;
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004188 *num_of_nodes = 0;
4189 *target_nodes = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004190
Joe Perchesb6b38f72010-04-21 03:50:45 +00004191 cFYI(1, "In GetDFSRefer the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004192 if (ses == NULL)
4193 return -ENODEV;
4194getDFSRetry:
4195 rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
4196 (void **) &pSMBr);
4197 if (rc)
4198 return rc;
Steve French50c2f752007-07-13 00:33:32 +00004199
4200 /* server pointer checked in called function,
Steve French1982c342005-08-17 12:38:22 -07004201 but should never be null here anyway */
4202 pSMB->hdr.Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004203 pSMB->hdr.Tid = ses->ipc_tid;
4204 pSMB->hdr.Uid = ses->Suid;
Steve French26f57362007-08-30 22:09:15 +00004205 if (ses->capabilities & CAP_STATUS32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004206 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
Steve French26f57362007-08-30 22:09:15 +00004207 if (ses->capabilities & CAP_DFS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004208 pSMB->hdr.Flags2 |= SMBFLG2_DFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004209
4210 if (ses->capabilities & CAP_UNICODE) {
4211 pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
4212 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004213 cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
Steve French737b7582005-04-28 22:41:06 -07004214 searchName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004215 name_len++; /* trailing null */
4216 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004217 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004218 name_len = strnlen(searchName, PATH_MAX);
4219 name_len++; /* trailing null */
4220 strncpy(pSMB->RequestFileName, searchName, name_len);
4221 }
4222
Steve French790fe572007-07-07 19:25:05 +00004223 if (ses->server) {
4224 if (ses->server->secMode &
Steve French1a4e15a2006-10-12 21:33:51 +00004225 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
4226 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
4227 }
4228
Steve French50c2f752007-07-13 00:33:32 +00004229 pSMB->hdr.Uid = ses->Suid;
Steve French1a4e15a2006-10-12 21:33:51 +00004230
Linus Torvalds1da177e2005-04-16 15:20:36 -07004231 params = 2 /* level */ + name_len /*includes null */ ;
4232 pSMB->TotalDataCount = 0;
4233 pSMB->DataCount = 0;
4234 pSMB->DataOffset = 0;
4235 pSMB->MaxParameterCount = 0;
Steve French582d21e2008-05-13 04:54:12 +00004236 /* BB find exact max SMB PDU from sess structure BB */
4237 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004238 pSMB->MaxSetupCount = 0;
4239 pSMB->Reserved = 0;
4240 pSMB->Flags = 0;
4241 pSMB->Timeout = 0;
4242 pSMB->Reserved2 = 0;
4243 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004244 struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004245 pSMB->SetupCount = 1;
4246 pSMB->Reserved3 = 0;
4247 pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
4248 byte_count = params + 3 /* pad */ ;
4249 pSMB->ParameterCount = cpu_to_le16(params);
4250 pSMB->TotalParameterCount = pSMB->ParameterCount;
4251 pSMB->MaxReferralLevel = cpu_to_le16(3);
4252 pSMB->hdr.smb_buf_length += byte_count;
4253 pSMB->ByteCount = cpu_to_le16(byte_count);
4254
4255 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
4256 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4257 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004258 cFYI(1, "Send error in GetDFSRefer = %d", rc);
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004259 goto GetDFSRefExit;
4260 }
4261 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004262
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004263 /* BB Also check if enough total bytes returned? */
Igor Mammedovfec45852008-05-16 13:06:30 +04004264 if (rc || (pSMBr->ByteCount < 17)) {
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004265 rc = -EIO; /* bad smb */
Igor Mammedovfec45852008-05-16 13:06:30 +04004266 goto GetDFSRefExit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004267 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004268
Joe Perchesb6b38f72010-04-21 03:50:45 +00004269 cFYI(1, "Decoding GetDFSRefer response BCC: %d Offset %d",
Igor Mammedovfec45852008-05-16 13:06:30 +04004270 pSMBr->ByteCount,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004271 le16_to_cpu(pSMBr->t2.DataOffset));
Igor Mammedovfec45852008-05-16 13:06:30 +04004272
4273 /* parse returned result into more usable form */
Steve Frencha1fe78f2008-05-16 18:48:38 +00004274 rc = parse_DFS_referrals(pSMBr, num_of_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004275 target_nodes, nls_codepage, remap,
4276 searchName);
Igor Mammedovfec45852008-05-16 13:06:30 +04004277
Linus Torvalds1da177e2005-04-16 15:20:36 -07004278GetDFSRefExit:
Steve French0d817bc2008-05-22 02:02:03 +00004279 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004280
4281 if (rc == -EAGAIN)
4282 goto getDFSRetry;
4283
4284 return rc;
4285}
4286
Steve French20962432005-09-21 22:05:57 -07004287/* Query File System Info such as free space to old servers such as Win 9x */
4288int
4289SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4290{
4291/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4292 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4293 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4294 FILE_SYSTEM_ALLOC_INFO *response_data;
4295 int rc = 0;
4296 int bytes_returned = 0;
4297 __u16 params, byte_count;
4298
Joe Perchesb6b38f72010-04-21 03:50:45 +00004299 cFYI(1, "OldQFSInfo");
Steve French20962432005-09-21 22:05:57 -07004300oldQFSInfoRetry:
4301 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4302 (void **) &pSMBr);
4303 if (rc)
4304 return rc;
Steve French20962432005-09-21 22:05:57 -07004305
4306 params = 2; /* level */
4307 pSMB->TotalDataCount = 0;
4308 pSMB->MaxParameterCount = cpu_to_le16(2);
4309 pSMB->MaxDataCount = cpu_to_le16(1000);
4310 pSMB->MaxSetupCount = 0;
4311 pSMB->Reserved = 0;
4312 pSMB->Flags = 0;
4313 pSMB->Timeout = 0;
4314 pSMB->Reserved2 = 0;
4315 byte_count = params + 1 /* pad */ ;
4316 pSMB->TotalParameterCount = cpu_to_le16(params);
4317 pSMB->ParameterCount = pSMB->TotalParameterCount;
4318 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4319 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4320 pSMB->DataCount = 0;
4321 pSMB->DataOffset = 0;
4322 pSMB->SetupCount = 1;
4323 pSMB->Reserved3 = 0;
4324 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4325 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4326 pSMB->hdr.smb_buf_length += byte_count;
4327 pSMB->ByteCount = cpu_to_le16(byte_count);
4328
4329 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4330 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4331 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004332 cFYI(1, "Send error in QFSInfo = %d", rc);
Steve French20962432005-09-21 22:05:57 -07004333 } else { /* decode response */
4334 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4335
4336 if (rc || (pSMBr->ByteCount < 18))
4337 rc = -EIO; /* bad smb */
4338 else {
4339 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004340 cFYI(1, "qfsinf resp BCC: %d Offset %d",
4341 pSMBr->ByteCount, data_offset);
Steve French20962432005-09-21 22:05:57 -07004342
Steve French50c2f752007-07-13 00:33:32 +00004343 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004344 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4345 FSData->f_bsize =
4346 le16_to_cpu(response_data->BytesPerSector) *
4347 le32_to_cpu(response_data->
4348 SectorsPerAllocationUnit);
4349 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004350 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004351 FSData->f_bfree = FSData->f_bavail =
4352 le32_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004353 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4354 (unsigned long long)FSData->f_blocks,
4355 (unsigned long long)FSData->f_bfree,
4356 FSData->f_bsize);
Steve French20962432005-09-21 22:05:57 -07004357 }
4358 }
4359 cifs_buf_release(pSMB);
4360
4361 if (rc == -EAGAIN)
4362 goto oldQFSInfoRetry;
4363
4364 return rc;
4365}
4366
Linus Torvalds1da177e2005-04-16 15:20:36 -07004367int
Steve French737b7582005-04-28 22:41:06 -07004368CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004369{
4370/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4371 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4372 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4373 FILE_SYSTEM_INFO *response_data;
4374 int rc = 0;
4375 int bytes_returned = 0;
4376 __u16 params, byte_count;
4377
Joe Perchesb6b38f72010-04-21 03:50:45 +00004378 cFYI(1, "In QFSInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004379QFSInfoRetry:
4380 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4381 (void **) &pSMBr);
4382 if (rc)
4383 return rc;
4384
4385 params = 2; /* level */
4386 pSMB->TotalDataCount = 0;
4387 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004388 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004389 pSMB->MaxSetupCount = 0;
4390 pSMB->Reserved = 0;
4391 pSMB->Flags = 0;
4392 pSMB->Timeout = 0;
4393 pSMB->Reserved2 = 0;
4394 byte_count = params + 1 /* pad */ ;
4395 pSMB->TotalParameterCount = cpu_to_le16(params);
4396 pSMB->ParameterCount = pSMB->TotalParameterCount;
4397 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004398 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004399 pSMB->DataCount = 0;
4400 pSMB->DataOffset = 0;
4401 pSMB->SetupCount = 1;
4402 pSMB->Reserved3 = 0;
4403 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4404 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4405 pSMB->hdr.smb_buf_length += byte_count;
4406 pSMB->ByteCount = cpu_to_le16(byte_count);
4407
4408 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4409 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4410 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004411 cFYI(1, "Send error in QFSInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004412 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004413 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004414
Steve French20962432005-09-21 22:05:57 -07004415 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004416 rc = -EIO; /* bad smb */
4417 else {
4418 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004419
4420 response_data =
4421 (FILE_SYSTEM_INFO
4422 *) (((char *) &pSMBr->hdr.Protocol) +
4423 data_offset);
4424 FSData->f_bsize =
4425 le32_to_cpu(response_data->BytesPerSector) *
4426 le32_to_cpu(response_data->
4427 SectorsPerAllocationUnit);
4428 FSData->f_blocks =
4429 le64_to_cpu(response_data->TotalAllocationUnits);
4430 FSData->f_bfree = FSData->f_bavail =
4431 le64_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004432 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4433 (unsigned long long)FSData->f_blocks,
4434 (unsigned long long)FSData->f_bfree,
4435 FSData->f_bsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004436 }
4437 }
4438 cifs_buf_release(pSMB);
4439
4440 if (rc == -EAGAIN)
4441 goto QFSInfoRetry;
4442
4443 return rc;
4444}
4445
4446int
Steve French737b7582005-04-28 22:41:06 -07004447CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004448{
4449/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4450 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4451 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4452 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4453 int rc = 0;
4454 int bytes_returned = 0;
4455 __u16 params, byte_count;
4456
Joe Perchesb6b38f72010-04-21 03:50:45 +00004457 cFYI(1, "In QFSAttributeInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004458QFSAttributeRetry:
4459 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4460 (void **) &pSMBr);
4461 if (rc)
4462 return rc;
4463
4464 params = 2; /* level */
4465 pSMB->TotalDataCount = 0;
4466 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004467 /* BB find exact max SMB PDU from sess structure BB */
4468 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004469 pSMB->MaxSetupCount = 0;
4470 pSMB->Reserved = 0;
4471 pSMB->Flags = 0;
4472 pSMB->Timeout = 0;
4473 pSMB->Reserved2 = 0;
4474 byte_count = params + 1 /* pad */ ;
4475 pSMB->TotalParameterCount = cpu_to_le16(params);
4476 pSMB->ParameterCount = pSMB->TotalParameterCount;
4477 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004478 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004479 pSMB->DataCount = 0;
4480 pSMB->DataOffset = 0;
4481 pSMB->SetupCount = 1;
4482 pSMB->Reserved3 = 0;
4483 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4484 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4485 pSMB->hdr.smb_buf_length += byte_count;
4486 pSMB->ByteCount = cpu_to_le16(byte_count);
4487
4488 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4489 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4490 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004491 cERROR(1, "Send error in QFSAttributeInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004492 } else { /* decode response */
4493 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4494
Steve French50c2f752007-07-13 00:33:32 +00004495 if (rc || (pSMBr->ByteCount < 13)) {
4496 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004497 rc = -EIO; /* bad smb */
4498 } else {
4499 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4500 response_data =
4501 (FILE_SYSTEM_ATTRIBUTE_INFO
4502 *) (((char *) &pSMBr->hdr.Protocol) +
4503 data_offset);
4504 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004505 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004506 }
4507 }
4508 cifs_buf_release(pSMB);
4509
4510 if (rc == -EAGAIN)
4511 goto QFSAttributeRetry;
4512
4513 return rc;
4514}
4515
4516int
Steve French737b7582005-04-28 22:41:06 -07004517CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004518{
4519/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4520 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4521 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4522 FILE_SYSTEM_DEVICE_INFO *response_data;
4523 int rc = 0;
4524 int bytes_returned = 0;
4525 __u16 params, byte_count;
4526
Joe Perchesb6b38f72010-04-21 03:50:45 +00004527 cFYI(1, "In QFSDeviceInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004528QFSDeviceRetry:
4529 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4530 (void **) &pSMBr);
4531 if (rc)
4532 return rc;
4533
4534 params = 2; /* level */
4535 pSMB->TotalDataCount = 0;
4536 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004537 /* BB find exact max SMB PDU from sess structure BB */
4538 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004539 pSMB->MaxSetupCount = 0;
4540 pSMB->Reserved = 0;
4541 pSMB->Flags = 0;
4542 pSMB->Timeout = 0;
4543 pSMB->Reserved2 = 0;
4544 byte_count = params + 1 /* pad */ ;
4545 pSMB->TotalParameterCount = cpu_to_le16(params);
4546 pSMB->ParameterCount = pSMB->TotalParameterCount;
4547 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004548 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004549
4550 pSMB->DataCount = 0;
4551 pSMB->DataOffset = 0;
4552 pSMB->SetupCount = 1;
4553 pSMB->Reserved3 = 0;
4554 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4555 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4556 pSMB->hdr.smb_buf_length += byte_count;
4557 pSMB->ByteCount = cpu_to_le16(byte_count);
4558
4559 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4560 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4561 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004562 cFYI(1, "Send error in QFSDeviceInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004563 } else { /* decode response */
4564 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4565
Steve French630f3f0c2007-10-25 21:17:17 +00004566 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004567 rc = -EIO; /* bad smb */
4568 else {
4569 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4570 response_data =
Steve French737b7582005-04-28 22:41:06 -07004571 (FILE_SYSTEM_DEVICE_INFO *)
4572 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004573 data_offset);
4574 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004575 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004576 }
4577 }
4578 cifs_buf_release(pSMB);
4579
4580 if (rc == -EAGAIN)
4581 goto QFSDeviceRetry;
4582
4583 return rc;
4584}
4585
4586int
Steve French737b7582005-04-28 22:41:06 -07004587CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004588{
4589/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4590 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4591 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4592 FILE_SYSTEM_UNIX_INFO *response_data;
4593 int rc = 0;
4594 int bytes_returned = 0;
4595 __u16 params, byte_count;
4596
Joe Perchesb6b38f72010-04-21 03:50:45 +00004597 cFYI(1, "In QFSUnixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004598QFSUnixRetry:
Jeff Laytonf5695992010-09-29 15:27:08 -04004599 rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
4600 (void **) &pSMB, (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004601 if (rc)
4602 return rc;
4603
4604 params = 2; /* level */
4605 pSMB->TotalDataCount = 0;
4606 pSMB->DataCount = 0;
4607 pSMB->DataOffset = 0;
4608 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004609 /* BB find exact max SMB PDU from sess structure BB */
4610 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004611 pSMB->MaxSetupCount = 0;
4612 pSMB->Reserved = 0;
4613 pSMB->Flags = 0;
4614 pSMB->Timeout = 0;
4615 pSMB->Reserved2 = 0;
4616 byte_count = params + 1 /* pad */ ;
4617 pSMB->ParameterCount = cpu_to_le16(params);
4618 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004619 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4620 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004621 pSMB->SetupCount = 1;
4622 pSMB->Reserved3 = 0;
4623 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4624 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4625 pSMB->hdr.smb_buf_length += byte_count;
4626 pSMB->ByteCount = cpu_to_le16(byte_count);
4627
4628 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4629 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4630 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004631 cERROR(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004632 } else { /* decode response */
4633 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4634
4635 if (rc || (pSMBr->ByteCount < 13)) {
4636 rc = -EIO; /* bad smb */
4637 } else {
4638 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4639 response_data =
4640 (FILE_SYSTEM_UNIX_INFO
4641 *) (((char *) &pSMBr->hdr.Protocol) +
4642 data_offset);
4643 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004644 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004645 }
4646 }
4647 cifs_buf_release(pSMB);
4648
4649 if (rc == -EAGAIN)
4650 goto QFSUnixRetry;
4651
4652
4653 return rc;
4654}
4655
Jeremy Allisonac670552005-06-22 17:26:35 -07004656int
Steve French45abc6e2005-06-23 13:42:03 -05004657CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004658{
4659/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4660 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4661 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4662 int rc = 0;
4663 int bytes_returned = 0;
4664 __u16 params, param_offset, offset, byte_count;
4665
Joe Perchesb6b38f72010-04-21 03:50:45 +00004666 cFYI(1, "In SETFSUnixInfo");
Jeremy Allisonac670552005-06-22 17:26:35 -07004667SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004668 /* BB switch to small buf init to save memory */
Jeff Laytonf5695992010-09-29 15:27:08 -04004669 rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
4670 (void **) &pSMB, (void **) &pSMBr);
Jeremy Allisonac670552005-06-22 17:26:35 -07004671 if (rc)
4672 return rc;
4673
4674 params = 4; /* 2 bytes zero followed by info level. */
4675 pSMB->MaxSetupCount = 0;
4676 pSMB->Reserved = 0;
4677 pSMB->Flags = 0;
4678 pSMB->Timeout = 0;
4679 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004680 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4681 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004682 offset = param_offset + params;
4683
4684 pSMB->MaxParameterCount = cpu_to_le16(4);
Steve French582d21e2008-05-13 04:54:12 +00004685 /* BB find exact max SMB PDU from sess structure BB */
4686 pSMB->MaxDataCount = cpu_to_le16(100);
Jeremy Allisonac670552005-06-22 17:26:35 -07004687 pSMB->SetupCount = 1;
4688 pSMB->Reserved3 = 0;
4689 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4690 byte_count = 1 /* pad */ + params + 12;
4691
4692 pSMB->DataCount = cpu_to_le16(12);
4693 pSMB->ParameterCount = cpu_to_le16(params);
4694 pSMB->TotalDataCount = pSMB->DataCount;
4695 pSMB->TotalParameterCount = pSMB->ParameterCount;
4696 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4697 pSMB->DataOffset = cpu_to_le16(offset);
4698
4699 /* Params. */
4700 pSMB->FileNum = 0;
4701 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4702
4703 /* Data. */
4704 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4705 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4706 pSMB->ClientUnixCap = cpu_to_le64(cap);
4707
4708 pSMB->hdr.smb_buf_length += byte_count;
4709 pSMB->ByteCount = cpu_to_le16(byte_count);
4710
4711 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4712 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4713 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004714 cERROR(1, "Send error in SETFSUnixInfo = %d", rc);
Jeremy Allisonac670552005-06-22 17:26:35 -07004715 } else { /* decode response */
4716 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004717 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004718 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004719 }
4720 cifs_buf_release(pSMB);
4721
4722 if (rc == -EAGAIN)
4723 goto SETFSUnixRetry;
4724
4725 return rc;
4726}
4727
4728
Linus Torvalds1da177e2005-04-16 15:20:36 -07004729
4730int
4731CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004732 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004733{
4734/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4735 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4736 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4737 FILE_SYSTEM_POSIX_INFO *response_data;
4738 int rc = 0;
4739 int bytes_returned = 0;
4740 __u16 params, byte_count;
4741
Joe Perchesb6b38f72010-04-21 03:50:45 +00004742 cFYI(1, "In QFSPosixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004743QFSPosixRetry:
4744 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4745 (void **) &pSMBr);
4746 if (rc)
4747 return rc;
4748
4749 params = 2; /* level */
4750 pSMB->TotalDataCount = 0;
4751 pSMB->DataCount = 0;
4752 pSMB->DataOffset = 0;
4753 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004754 /* BB find exact max SMB PDU from sess structure BB */
4755 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004756 pSMB->MaxSetupCount = 0;
4757 pSMB->Reserved = 0;
4758 pSMB->Flags = 0;
4759 pSMB->Timeout = 0;
4760 pSMB->Reserved2 = 0;
4761 byte_count = params + 1 /* pad */ ;
4762 pSMB->ParameterCount = cpu_to_le16(params);
4763 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004764 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4765 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004766 pSMB->SetupCount = 1;
4767 pSMB->Reserved3 = 0;
4768 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4769 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4770 pSMB->hdr.smb_buf_length += byte_count;
4771 pSMB->ByteCount = cpu_to_le16(byte_count);
4772
4773 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4774 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4775 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004776 cFYI(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004777 } else { /* decode response */
4778 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4779
4780 if (rc || (pSMBr->ByteCount < 13)) {
4781 rc = -EIO; /* bad smb */
4782 } else {
4783 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4784 response_data =
4785 (FILE_SYSTEM_POSIX_INFO
4786 *) (((char *) &pSMBr->hdr.Protocol) +
4787 data_offset);
4788 FSData->f_bsize =
4789 le32_to_cpu(response_data->BlockSize);
4790 FSData->f_blocks =
4791 le64_to_cpu(response_data->TotalBlocks);
4792 FSData->f_bfree =
4793 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004794 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004795 FSData->f_bavail = FSData->f_bfree;
4796 } else {
4797 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004798 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004799 }
Steve French790fe572007-07-07 19:25:05 +00004800 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004801 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004802 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004803 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004804 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004805 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004806 }
4807 }
4808 cifs_buf_release(pSMB);
4809
4810 if (rc == -EAGAIN)
4811 goto QFSPosixRetry;
4812
4813 return rc;
4814}
4815
4816
Steve French50c2f752007-07-13 00:33:32 +00004817/* We can not use write of zero bytes trick to
4818 set file size due to need for large file support. Also note that
4819 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004820 routine which is only needed to work around a sharing violation bug
4821 in Samba which this routine can run into */
4822
4823int
4824CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French4b18f2a2008-04-29 00:06:05 +00004825 __u64 size, bool SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004826 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004827{
4828 struct smb_com_transaction2_spi_req *pSMB = NULL;
4829 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4830 struct file_end_of_file_info *parm_data;
4831 int name_len;
4832 int rc = 0;
4833 int bytes_returned = 0;
4834 __u16 params, byte_count, data_count, param_offset, offset;
4835
Joe Perchesb6b38f72010-04-21 03:50:45 +00004836 cFYI(1, "In SetEOF");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004837SetEOFRetry:
4838 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4839 (void **) &pSMBr);
4840 if (rc)
4841 return rc;
4842
4843 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4844 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004845 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004846 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004847 name_len++; /* trailing null */
4848 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004849 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004850 name_len = strnlen(fileName, PATH_MAX);
4851 name_len++; /* trailing null */
4852 strncpy(pSMB->FileName, fileName, name_len);
4853 }
4854 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004855 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004856 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004857 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004858 pSMB->MaxSetupCount = 0;
4859 pSMB->Reserved = 0;
4860 pSMB->Flags = 0;
4861 pSMB->Timeout = 0;
4862 pSMB->Reserved2 = 0;
4863 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004864 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004865 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004866 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004867 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4868 pSMB->InformationLevel =
4869 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4870 else
4871 pSMB->InformationLevel =
4872 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4873 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004874 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4875 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004876 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004877 else
4878 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004879 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004880 }
4881
4882 parm_data =
4883 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4884 offset);
4885 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4886 pSMB->DataOffset = cpu_to_le16(offset);
4887 pSMB->SetupCount = 1;
4888 pSMB->Reserved3 = 0;
4889 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4890 byte_count = 3 /* pad */ + params + data_count;
4891 pSMB->DataCount = cpu_to_le16(data_count);
4892 pSMB->TotalDataCount = pSMB->DataCount;
4893 pSMB->ParameterCount = cpu_to_le16(params);
4894 pSMB->TotalParameterCount = pSMB->ParameterCount;
4895 pSMB->Reserved4 = 0;
4896 pSMB->hdr.smb_buf_length += byte_count;
4897 parm_data->FileSize = cpu_to_le64(size);
4898 pSMB->ByteCount = cpu_to_le16(byte_count);
4899 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4900 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004901 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00004902 cFYI(1, "SetPathInfo (file size) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004903
4904 cifs_buf_release(pSMB);
4905
4906 if (rc == -EAGAIN)
4907 goto SetEOFRetry;
4908
4909 return rc;
4910}
4911
4912int
Steve French50c2f752007-07-13 00:33:32 +00004913CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
Steve French4b18f2a2008-04-29 00:06:05 +00004914 __u16 fid, __u32 pid_of_opener, bool SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004915{
4916 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004917 char *data_offset;
4918 struct file_end_of_file_info *parm_data;
4919 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004920 __u16 params, param_offset, offset, byte_count, count;
4921
Joe Perchesb6b38f72010-04-21 03:50:45 +00004922 cFYI(1, "SetFileSize (via SetFileInfo) %lld",
4923 (long long)size);
Steve Frenchcd634992005-04-28 22:41:10 -07004924 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4925
Linus Torvalds1da177e2005-04-16 15:20:36 -07004926 if (rc)
4927 return rc;
4928
4929 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4930 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004931
Linus Torvalds1da177e2005-04-16 15:20:36 -07004932 params = 6;
4933 pSMB->MaxSetupCount = 0;
4934 pSMB->Reserved = 0;
4935 pSMB->Flags = 0;
4936 pSMB->Timeout = 0;
4937 pSMB->Reserved2 = 0;
4938 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4939 offset = param_offset + params;
4940
Steve French50c2f752007-07-13 00:33:32 +00004941 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004942
4943 count = sizeof(struct file_end_of_file_info);
4944 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004945 /* BB find exact max SMB PDU from sess structure BB */
4946 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004947 pSMB->SetupCount = 1;
4948 pSMB->Reserved3 = 0;
4949 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4950 byte_count = 3 /* pad */ + params + count;
4951 pSMB->DataCount = cpu_to_le16(count);
4952 pSMB->ParameterCount = cpu_to_le16(params);
4953 pSMB->TotalDataCount = pSMB->DataCount;
4954 pSMB->TotalParameterCount = pSMB->ParameterCount;
4955 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4956 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004957 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4958 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004959 pSMB->DataOffset = cpu_to_le16(offset);
4960 parm_data->FileSize = cpu_to_le64(size);
4961 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004962 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004963 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4964 pSMB->InformationLevel =
4965 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4966 else
4967 pSMB->InformationLevel =
4968 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004969 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004970 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4971 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004972 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004973 else
4974 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004975 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004976 }
4977 pSMB->Reserved4 = 0;
4978 pSMB->hdr.smb_buf_length += byte_count;
4979 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004980 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004981 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004982 cFYI(1, "Send error in SetFileInfo (SetFileSize) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004983 }
4984
Steve French50c2f752007-07-13 00:33:32 +00004985 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004986 since file handle passed in no longer valid */
4987
4988 return rc;
4989}
4990
Steve French50c2f752007-07-13 00:33:32 +00004991/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004992 an open handle, rather than by pathname - this is awkward due to
4993 potential access conflicts on the open, but it is unavoidable for these
4994 old servers since the only other choice is to go from 100 nanosecond DCE
4995 time and resort to the original setpathinfo level which takes the ancient
4996 DOS time format with 2 second granularity */
4997int
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004998CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
4999 const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005000{
5001 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005002 char *data_offset;
5003 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005004 __u16 params, param_offset, offset, byte_count, count;
5005
Joe Perchesb6b38f72010-04-21 03:50:45 +00005006 cFYI(1, "Set Times (via SetFileInfo)");
Steve Frenchcd634992005-04-28 22:41:10 -07005007 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5008
Linus Torvalds1da177e2005-04-16 15:20:36 -07005009 if (rc)
5010 return rc;
5011
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04005012 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5013 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00005014
Linus Torvalds1da177e2005-04-16 15:20:36 -07005015 params = 6;
5016 pSMB->MaxSetupCount = 0;
5017 pSMB->Reserved = 0;
5018 pSMB->Flags = 0;
5019 pSMB->Timeout = 0;
5020 pSMB->Reserved2 = 0;
5021 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5022 offset = param_offset + params;
5023
Steve French50c2f752007-07-13 00:33:32 +00005024 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005025
Steve French26f57362007-08-30 22:09:15 +00005026 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005027 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005028 /* BB find max SMB PDU from sess */
5029 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005030 pSMB->SetupCount = 1;
5031 pSMB->Reserved3 = 0;
5032 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5033 byte_count = 3 /* pad */ + params + count;
5034 pSMB->DataCount = cpu_to_le16(count);
5035 pSMB->ParameterCount = cpu_to_le16(params);
5036 pSMB->TotalDataCount = pSMB->DataCount;
5037 pSMB->TotalParameterCount = pSMB->ParameterCount;
5038 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5039 pSMB->DataOffset = cpu_to_le16(offset);
5040 pSMB->Fid = fid;
5041 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5042 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5043 else
5044 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5045 pSMB->Reserved4 = 0;
5046 pSMB->hdr.smb_buf_length += byte_count;
5047 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00005048 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00005049 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005050 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005051 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005052
Steve French50c2f752007-07-13 00:33:32 +00005053 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07005054 since file handle passed in no longer valid */
5055
5056 return rc;
5057}
5058
Jeff Layton6d22f092008-09-23 11:48:35 -04005059int
5060CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
5061 bool delete_file, __u16 fid, __u32 pid_of_opener)
5062{
5063 struct smb_com_transaction2_sfi_req *pSMB = NULL;
5064 char *data_offset;
5065 int rc = 0;
5066 __u16 params, param_offset, offset, byte_count, count;
5067
Joe Perchesb6b38f72010-04-21 03:50:45 +00005068 cFYI(1, "Set File Disposition (via SetFileInfo)");
Jeff Layton6d22f092008-09-23 11:48:35 -04005069 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5070
5071 if (rc)
5072 return rc;
5073
5074 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5075 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5076
5077 params = 6;
5078 pSMB->MaxSetupCount = 0;
5079 pSMB->Reserved = 0;
5080 pSMB->Flags = 0;
5081 pSMB->Timeout = 0;
5082 pSMB->Reserved2 = 0;
5083 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5084 offset = param_offset + params;
5085
5086 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5087
5088 count = 1;
5089 pSMB->MaxParameterCount = cpu_to_le16(2);
5090 /* BB find max SMB PDU from sess */
5091 pSMB->MaxDataCount = cpu_to_le16(1000);
5092 pSMB->SetupCount = 1;
5093 pSMB->Reserved3 = 0;
5094 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5095 byte_count = 3 /* pad */ + params + count;
5096 pSMB->DataCount = cpu_to_le16(count);
5097 pSMB->ParameterCount = cpu_to_le16(params);
5098 pSMB->TotalDataCount = pSMB->DataCount;
5099 pSMB->TotalParameterCount = pSMB->ParameterCount;
5100 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5101 pSMB->DataOffset = cpu_to_le16(offset);
5102 pSMB->Fid = fid;
5103 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
5104 pSMB->Reserved4 = 0;
5105 pSMB->hdr.smb_buf_length += byte_count;
5106 pSMB->ByteCount = cpu_to_le16(byte_count);
5107 *data_offset = delete_file ? 1 : 0;
5108 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5109 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005110 cFYI(1, "Send error in SetFileDisposition = %d", rc);
Jeff Layton6d22f092008-09-23 11:48:35 -04005111
5112 return rc;
5113}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005114
5115int
Jeff Layton6fc000e2008-08-02 07:26:12 -04005116CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
5117 const char *fileName, const FILE_BASIC_INFO *data,
5118 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005119{
5120 TRANSACTION2_SPI_REQ *pSMB = NULL;
5121 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5122 int name_len;
5123 int rc = 0;
5124 int bytes_returned = 0;
5125 char *data_offset;
5126 __u16 params, param_offset, offset, byte_count, count;
5127
Joe Perchesb6b38f72010-04-21 03:50:45 +00005128 cFYI(1, "In SetTimes");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005129
5130SetTimesRetry:
5131 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5132 (void **) &pSMBr);
5133 if (rc)
5134 return rc;
5135
5136 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5137 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05005138 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005139 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005140 name_len++; /* trailing null */
5141 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005142 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005143 name_len = strnlen(fileName, PATH_MAX);
5144 name_len++; /* trailing null */
5145 strncpy(pSMB->FileName, fileName, name_len);
5146 }
5147
5148 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005149 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005150 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005151 /* BB find max SMB PDU from sess structure BB */
5152 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005153 pSMB->MaxSetupCount = 0;
5154 pSMB->Reserved = 0;
5155 pSMB->Flags = 0;
5156 pSMB->Timeout = 0;
5157 pSMB->Reserved2 = 0;
5158 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005159 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005160 offset = param_offset + params;
5161 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5162 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5163 pSMB->DataOffset = cpu_to_le16(offset);
5164 pSMB->SetupCount = 1;
5165 pSMB->Reserved3 = 0;
5166 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5167 byte_count = 3 /* pad */ + params + count;
5168
5169 pSMB->DataCount = cpu_to_le16(count);
5170 pSMB->ParameterCount = cpu_to_le16(params);
5171 pSMB->TotalDataCount = pSMB->DataCount;
5172 pSMB->TotalParameterCount = pSMB->ParameterCount;
5173 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5174 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5175 else
5176 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5177 pSMB->Reserved4 = 0;
5178 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00005179 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005180 pSMB->ByteCount = cpu_to_le16(byte_count);
5181 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5182 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005183 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005184 cFYI(1, "SetPathInfo (times) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005185
5186 cifs_buf_release(pSMB);
5187
5188 if (rc == -EAGAIN)
5189 goto SetTimesRetry;
5190
5191 return rc;
5192}
5193
5194/* Can not be used to set time stamps yet (due to old DOS time format) */
5195/* Can be used to set attributes */
5196#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
5197 handling it anyway and NT4 was what we thought it would be needed for
5198 Do not delete it until we prove whether needed for Win9x though */
5199int
5200CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
5201 __u16 dos_attrs, const struct nls_table *nls_codepage)
5202{
5203 SETATTR_REQ *pSMB = NULL;
5204 SETATTR_RSP *pSMBr = NULL;
5205 int rc = 0;
5206 int bytes_returned;
5207 int name_len;
5208
Joe Perchesb6b38f72010-04-21 03:50:45 +00005209 cFYI(1, "In SetAttrLegacy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005210
5211SetAttrLgcyRetry:
5212 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
5213 (void **) &pSMBr);
5214 if (rc)
5215 return rc;
5216
5217 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5218 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005219 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005220 PATH_MAX, nls_codepage);
5221 name_len++; /* trailing null */
5222 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005223 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005224 name_len = strnlen(fileName, PATH_MAX);
5225 name_len++; /* trailing null */
5226 strncpy(pSMB->fileName, fileName, name_len);
5227 }
5228 pSMB->attr = cpu_to_le16(dos_attrs);
5229 pSMB->BufferFormat = 0x04;
5230 pSMB->hdr.smb_buf_length += name_len + 1;
5231 pSMB->ByteCount = cpu_to_le16(name_len + 1);
5232 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5233 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005234 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005235 cFYI(1, "Error in LegacySetAttr = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005236
5237 cifs_buf_release(pSMB);
5238
5239 if (rc == -EAGAIN)
5240 goto SetAttrLgcyRetry;
5241
5242 return rc;
5243}
5244#endif /* temporarily unneeded SetAttr legacy function */
5245
Jeff Layton654cf142009-07-09 20:02:49 -04005246static void
5247cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,
5248 const struct cifs_unix_set_info_args *args)
5249{
5250 u64 mode = args->mode;
5251
5252 /*
5253 * Samba server ignores set of file size to zero due to bugs in some
5254 * older clients, but we should be precise - we use SetFileSize to
5255 * set file size and do not want to truncate file size to zero
5256 * accidently as happened on one Samba server beta by putting
5257 * zero instead of -1 here
5258 */
5259 data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
5260 data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
5261 data_offset->LastStatusChange = cpu_to_le64(args->ctime);
5262 data_offset->LastAccessTime = cpu_to_le64(args->atime);
5263 data_offset->LastModificationTime = cpu_to_le64(args->mtime);
5264 data_offset->Uid = cpu_to_le64(args->uid);
5265 data_offset->Gid = cpu_to_le64(args->gid);
5266 /* better to leave device as zero when it is */
5267 data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
5268 data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
5269 data_offset->Permissions = cpu_to_le64(mode);
5270
5271 if (S_ISREG(mode))
5272 data_offset->Type = cpu_to_le32(UNIX_FILE);
5273 else if (S_ISDIR(mode))
5274 data_offset->Type = cpu_to_le32(UNIX_DIR);
5275 else if (S_ISLNK(mode))
5276 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
5277 else if (S_ISCHR(mode))
5278 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
5279 else if (S_ISBLK(mode))
5280 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
5281 else if (S_ISFIFO(mode))
5282 data_offset->Type = cpu_to_le32(UNIX_FIFO);
5283 else if (S_ISSOCK(mode))
5284 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5285}
5286
Linus Torvalds1da177e2005-04-16 15:20:36 -07005287int
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005288CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon,
5289 const struct cifs_unix_set_info_args *args,
5290 u16 fid, u32 pid_of_opener)
5291{
5292 struct smb_com_transaction2_sfi_req *pSMB = NULL;
5293 FILE_UNIX_BASIC_INFO *data_offset;
5294 int rc = 0;
5295 u16 params, param_offset, offset, byte_count, count;
5296
Joe Perchesb6b38f72010-04-21 03:50:45 +00005297 cFYI(1, "Set Unix Info (via SetFileInfo)");
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005298 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5299
5300 if (rc)
5301 return rc;
5302
5303 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5304 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5305
5306 params = 6;
5307 pSMB->MaxSetupCount = 0;
5308 pSMB->Reserved = 0;
5309 pSMB->Flags = 0;
5310 pSMB->Timeout = 0;
5311 pSMB->Reserved2 = 0;
5312 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5313 offset = param_offset + params;
5314
5315 data_offset = (FILE_UNIX_BASIC_INFO *)
5316 ((char *)(&pSMB->hdr.Protocol) + offset);
5317 count = sizeof(FILE_UNIX_BASIC_INFO);
5318
5319 pSMB->MaxParameterCount = cpu_to_le16(2);
5320 /* BB find max SMB PDU from sess */
5321 pSMB->MaxDataCount = cpu_to_le16(1000);
5322 pSMB->SetupCount = 1;
5323 pSMB->Reserved3 = 0;
5324 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5325 byte_count = 3 /* pad */ + params + count;
5326 pSMB->DataCount = cpu_to_le16(count);
5327 pSMB->ParameterCount = cpu_to_le16(params);
5328 pSMB->TotalDataCount = pSMB->DataCount;
5329 pSMB->TotalParameterCount = pSMB->ParameterCount;
5330 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5331 pSMB->DataOffset = cpu_to_le16(offset);
5332 pSMB->Fid = fid;
5333 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5334 pSMB->Reserved4 = 0;
5335 pSMB->hdr.smb_buf_length += byte_count;
5336 pSMB->ByteCount = cpu_to_le16(byte_count);
5337
5338 cifs_fill_unix_set_info(data_offset, args);
5339
5340 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5341 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005342 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005343
5344 /* Note: On -EAGAIN error only caller can retry on handle based calls
5345 since file handle passed in no longer valid */
5346
5347 return rc;
5348}
5349
5350int
Jeff Layton01ea95e2009-07-09 20:02:49 -04005351CIFSSMBUnixSetPathInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,
5352 const struct cifs_unix_set_info_args *args,
5353 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005354{
5355 TRANSACTION2_SPI_REQ *pSMB = NULL;
5356 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5357 int name_len;
5358 int rc = 0;
5359 int bytes_returned = 0;
5360 FILE_UNIX_BASIC_INFO *data_offset;
5361 __u16 params, param_offset, offset, count, byte_count;
5362
Joe Perchesb6b38f72010-04-21 03:50:45 +00005363 cFYI(1, "In SetUID/GID/Mode");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005364setPermsRetry:
5365 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5366 (void **) &pSMBr);
5367 if (rc)
5368 return rc;
5369
5370 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5371 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005372 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005373 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005374 name_len++; /* trailing null */
5375 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07005376 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005377 name_len = strnlen(fileName, PATH_MAX);
5378 name_len++; /* trailing null */
5379 strncpy(pSMB->FileName, fileName, name_len);
5380 }
5381
5382 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005383 count = sizeof(FILE_UNIX_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005384 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005385 /* BB find max SMB PDU from sess structure BB */
5386 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005387 pSMB->MaxSetupCount = 0;
5388 pSMB->Reserved = 0;
5389 pSMB->Flags = 0;
5390 pSMB->Timeout = 0;
5391 pSMB->Reserved2 = 0;
5392 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005393 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005394 offset = param_offset + params;
5395 data_offset =
5396 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5397 offset);
5398 memset(data_offset, 0, count);
5399 pSMB->DataOffset = cpu_to_le16(offset);
5400 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5401 pSMB->SetupCount = 1;
5402 pSMB->Reserved3 = 0;
5403 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5404 byte_count = 3 /* pad */ + params + count;
5405 pSMB->ParameterCount = cpu_to_le16(params);
5406 pSMB->DataCount = cpu_to_le16(count);
5407 pSMB->TotalParameterCount = pSMB->ParameterCount;
5408 pSMB->TotalDataCount = pSMB->DataCount;
5409 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5410 pSMB->Reserved4 = 0;
5411 pSMB->hdr.smb_buf_length += byte_count;
Steve French50c2f752007-07-13 00:33:32 +00005412
Jeff Layton654cf142009-07-09 20:02:49 -04005413 cifs_fill_unix_set_info(data_offset, args);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005414
5415 pSMB->ByteCount = cpu_to_le16(byte_count);
5416 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5417 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005418 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005419 cFYI(1, "SetPathInfo (perms) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005420
Steve French0d817bc2008-05-22 02:02:03 +00005421 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005422 if (rc == -EAGAIN)
5423 goto setPermsRetry;
5424 return rc;
5425}
5426
Steve French50c2f752007-07-13 00:33:32 +00005427int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005428 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005429 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005430 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005431{
5432 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005433 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5434 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005435 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005436 int bytes_returned;
5437
Joe Perchesb6b38f72010-04-21 03:50:45 +00005438 cFYI(1, "In CIFSSMBNotify for file handle %d", (int)netfid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005439 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005440 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005441 if (rc)
5442 return rc;
5443
5444 pSMB->TotalParameterCount = 0 ;
5445 pSMB->TotalDataCount = 0;
5446 pSMB->MaxParameterCount = cpu_to_le32(2);
5447 /* BB find exact data count max from sess structure BB */
5448 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005449/* BB VERIFY verify which is correct for above BB */
5450 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5451 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5452
Linus Torvalds1da177e2005-04-16 15:20:36 -07005453 pSMB->MaxSetupCount = 4;
5454 pSMB->Reserved = 0;
5455 pSMB->ParameterOffset = 0;
5456 pSMB->DataCount = 0;
5457 pSMB->DataOffset = 0;
5458 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5459 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5460 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005461 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005462 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5463 pSMB->Reserved2 = 0;
5464 pSMB->CompletionFilter = cpu_to_le32(filter);
5465 pSMB->Fid = netfid; /* file handle always le */
5466 pSMB->ByteCount = 0;
5467
5468 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005469 (struct smb_hdr *)pSMBr, &bytes_returned,
5470 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005471 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005472 cFYI(1, "Error in Notify = %d", rc);
Steve Frenchff5dbd92005-08-24 17:10:36 -07005473 } else {
5474 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005475 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005476 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005477 sizeof(struct dir_notify_req),
5478 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005479 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005480 dnotify_req->Pid = pSMB->hdr.Pid;
5481 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5482 dnotify_req->Mid = pSMB->hdr.Mid;
5483 dnotify_req->Tid = pSMB->hdr.Tid;
5484 dnotify_req->Uid = pSMB->hdr.Uid;
5485 dnotify_req->netfid = netfid;
5486 dnotify_req->pfile = pfile;
5487 dnotify_req->filter = filter;
5488 dnotify_req->multishot = multishot;
5489 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005490 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005491 &GlobalDnotifyReqList);
5492 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005493 } else
Steve French47c786e2005-10-11 20:03:18 -07005494 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005495 }
5496 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005497 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005498}
Jeff Layton31c05192010-02-10 16:18:26 -05005499
Linus Torvalds1da177e2005-04-16 15:20:36 -07005500#ifdef CONFIG_CIFS_XATTR
Jeff Layton31c05192010-02-10 16:18:26 -05005501/*
5502 * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common
5503 * function used by listxattr and getxattr type calls. When ea_name is set,
5504 * it looks for that attribute name and stuffs that value into the EAData
5505 * buffer. When ea_name is NULL, it stuffs a list of attribute names into the
5506 * buffer. In both cases, the return value is either the length of the
5507 * resulting data or a negative error code. If EAData is a NULL pointer then
5508 * the data isn't copied to it, but the length is returned.
5509 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005510ssize_t
5511CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
Jeff Layton31c05192010-02-10 16:18:26 -05005512 const unsigned char *searchName, const unsigned char *ea_name,
5513 char *EAData, size_t buf_size,
5514 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005515{
5516 /* BB assumes one setup word */
5517 TRANSACTION2_QPI_REQ *pSMB = NULL;
5518 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5519 int rc = 0;
5520 int bytes_returned;
Jeff Layton6e462b92010-02-10 16:18:26 -05005521 int list_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005522 struct fealist *ea_response_data;
Steve French50c2f752007-07-13 00:33:32 +00005523 struct fea *temp_fea;
5524 char *temp_ptr;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005525 char *end_of_smb;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005526 __u16 params, byte_count, data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005527
Joe Perchesb6b38f72010-04-21 03:50:45 +00005528 cFYI(1, "In Query All EAs path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005529QAllEAsRetry:
5530 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5531 (void **) &pSMBr);
5532 if (rc)
5533 return rc;
5534
5535 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Jeff Layton6e462b92010-02-10 16:18:26 -05005536 list_len =
Steve French50c2f752007-07-13 00:33:32 +00005537 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005538 PATH_MAX, nls_codepage, remap);
Jeff Layton6e462b92010-02-10 16:18:26 -05005539 list_len++; /* trailing null */
5540 list_len *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005541 } else { /* BB improve the check for buffer overruns BB */
Jeff Layton6e462b92010-02-10 16:18:26 -05005542 list_len = strnlen(searchName, PATH_MAX);
5543 list_len++; /* trailing null */
5544 strncpy(pSMB->FileName, searchName, list_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005545 }
5546
Jeff Layton6e462b92010-02-10 16:18:26 -05005547 params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005548 pSMB->TotalDataCount = 0;
5549 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005550 /* BB find exact max SMB PDU from sess structure BB */
Jeff Laytone5296142010-02-10 16:18:26 -05005551 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005552 pSMB->MaxSetupCount = 0;
5553 pSMB->Reserved = 0;
5554 pSMB->Flags = 0;
5555 pSMB->Timeout = 0;
5556 pSMB->Reserved2 = 0;
5557 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005558 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005559 pSMB->DataCount = 0;
5560 pSMB->DataOffset = 0;
5561 pSMB->SetupCount = 1;
5562 pSMB->Reserved3 = 0;
5563 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5564 byte_count = params + 1 /* pad */ ;
5565 pSMB->TotalParameterCount = cpu_to_le16(params);
5566 pSMB->ParameterCount = pSMB->TotalParameterCount;
5567 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5568 pSMB->Reserved4 = 0;
5569 pSMB->hdr.smb_buf_length += byte_count;
5570 pSMB->ByteCount = cpu_to_le16(byte_count);
5571
5572 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5573 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5574 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005575 cFYI(1, "Send error in QueryAllEAs = %d", rc);
Jeff Laytonf0d38682010-02-10 16:18:26 -05005576 goto QAllEAsOut;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005577 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005578
5579
5580 /* BB also check enough total bytes returned */
5581 /* BB we need to improve the validity checking
5582 of these trans2 responses */
5583
5584 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5585 if (rc || (pSMBr->ByteCount < 4)) {
5586 rc = -EIO; /* bad smb */
5587 goto QAllEAsOut;
5588 }
5589
5590 /* check that length of list is not more than bcc */
5591 /* check that each entry does not go beyond length
5592 of list */
5593 /* check that each element of each entry does not
5594 go beyond end of list */
5595 /* validate_trans2_offsets() */
5596 /* BB check if start of smb + data_offset > &bcc+ bcc */
5597
5598 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5599 ea_response_data = (struct fealist *)
5600 (((char *) &pSMBr->hdr.Protocol) + data_offset);
5601
Jeff Layton6e462b92010-02-10 16:18:26 -05005602 list_len = le32_to_cpu(ea_response_data->list_len);
Joe Perchesb6b38f72010-04-21 03:50:45 +00005603 cFYI(1, "ea length %d", list_len);
Jeff Layton6e462b92010-02-10 16:18:26 -05005604 if (list_len <= 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005605 cFYI(1, "empty EA list returned from server");
Jeff Laytonf0d38682010-02-10 16:18:26 -05005606 goto QAllEAsOut;
5607 }
5608
Jeff Layton0cd126b2010-02-10 16:18:26 -05005609 /* make sure list_len doesn't go past end of SMB */
Jeff Layton690c5222011-01-20 13:36:51 -05005610 end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr);
Jeff Layton0cd126b2010-02-10 16:18:26 -05005611 if ((char *)ea_response_data + list_len > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005612 cFYI(1, "EA list appears to go beyond SMB");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005613 rc = -EIO;
5614 goto QAllEAsOut;
5615 }
5616
Jeff Laytonf0d38682010-02-10 16:18:26 -05005617 /* account for ea list len */
Jeff Layton6e462b92010-02-10 16:18:26 -05005618 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005619 temp_fea = ea_response_data->list;
5620 temp_ptr = (char *)temp_fea;
Jeff Layton6e462b92010-02-10 16:18:26 -05005621 while (list_len > 0) {
Steve French122ca002010-02-24 21:56:48 +00005622 unsigned int name_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005623 __u16 value_len;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005624
Jeff Layton6e462b92010-02-10 16:18:26 -05005625 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005626 temp_ptr += 4;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005627 /* make sure we can read name_len and value_len */
5628 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005629 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005630 rc = -EIO;
5631 goto QAllEAsOut;
5632 }
5633
5634 name_len = temp_fea->name_len;
5635 value_len = le16_to_cpu(temp_fea->value_len);
5636 list_len -= name_len + 1 + value_len;
5637 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005638 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005639 rc = -EIO;
5640 goto QAllEAsOut;
5641 }
5642
Jeff Layton31c05192010-02-10 16:18:26 -05005643 if (ea_name) {
5644 if (strncmp(ea_name, temp_ptr, name_len) == 0) {
5645 temp_ptr += name_len + 1;
5646 rc = value_len;
5647 if (buf_size == 0)
5648 goto QAllEAsOut;
5649 if ((size_t)value_len > buf_size) {
5650 rc = -ERANGE;
5651 goto QAllEAsOut;
5652 }
5653 memcpy(EAData, temp_ptr, value_len);
5654 goto QAllEAsOut;
5655 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005656 } else {
Jeff Layton31c05192010-02-10 16:18:26 -05005657 /* account for prefix user. and trailing null */
5658 rc += (5 + 1 + name_len);
5659 if (rc < (int) buf_size) {
5660 memcpy(EAData, "user.", 5);
5661 EAData += 5;
5662 memcpy(EAData, temp_ptr, name_len);
5663 EAData += name_len;
5664 /* null terminate name */
5665 *EAData = 0;
5666 ++EAData;
5667 } else if (buf_size == 0) {
5668 /* skip copy - calc size only */
5669 } else {
5670 /* stop before overrun buffer */
5671 rc = -ERANGE;
5672 break;
5673 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005674 }
Jeff Layton0cd126b2010-02-10 16:18:26 -05005675 temp_ptr += name_len + 1 + value_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005676 temp_fea = (struct fea *)temp_ptr;
5677 }
5678
Jeff Layton31c05192010-02-10 16:18:26 -05005679 /* didn't find the named attribute */
5680 if (ea_name)
5681 rc = -ENODATA;
5682
Jeff Laytonf0d38682010-02-10 16:18:26 -05005683QAllEAsOut:
Steve French0d817bc2008-05-22 02:02:03 +00005684 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005685 if (rc == -EAGAIN)
5686 goto QAllEAsRetry;
5687
5688 return (ssize_t)rc;
5689}
5690
Linus Torvalds1da177e2005-04-16 15:20:36 -07005691int
5692CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005693 const char *ea_name, const void *ea_value,
5694 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5695 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005696{
5697 struct smb_com_transaction2_spi_req *pSMB = NULL;
5698 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5699 struct fealist *parm_data;
5700 int name_len;
5701 int rc = 0;
5702 int bytes_returned = 0;
5703 __u16 params, param_offset, byte_count, offset, count;
5704
Joe Perchesb6b38f72010-04-21 03:50:45 +00005705 cFYI(1, "In SetEA");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005706SetEARetry:
5707 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5708 (void **) &pSMBr);
5709 if (rc)
5710 return rc;
5711
5712 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5713 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005714 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005715 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005716 name_len++; /* trailing null */
5717 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005718 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005719 name_len = strnlen(fileName, PATH_MAX);
5720 name_len++; /* trailing null */
5721 strncpy(pSMB->FileName, fileName, name_len);
5722 }
5723
5724 params = 6 + name_len;
5725
5726 /* done calculating parms using name_len of file name,
5727 now use name_len to calculate length of ea name
5728 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005729 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005730 name_len = 0;
5731 else
Steve French50c2f752007-07-13 00:33:32 +00005732 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005733
Steve Frenchdae5dbdb2007-12-30 23:49:57 +00005734 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005735 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005736 /* BB find max SMB PDU from sess */
5737 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005738 pSMB->MaxSetupCount = 0;
5739 pSMB->Reserved = 0;
5740 pSMB->Flags = 0;
5741 pSMB->Timeout = 0;
5742 pSMB->Reserved2 = 0;
5743 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005744 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005745 offset = param_offset + params;
5746 pSMB->InformationLevel =
5747 cpu_to_le16(SMB_SET_FILE_EA);
5748
5749 parm_data =
5750 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5751 offset);
5752 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5753 pSMB->DataOffset = cpu_to_le16(offset);
5754 pSMB->SetupCount = 1;
5755 pSMB->Reserved3 = 0;
5756 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5757 byte_count = 3 /* pad */ + params + count;
5758 pSMB->DataCount = cpu_to_le16(count);
5759 parm_data->list_len = cpu_to_le32(count);
5760 parm_data->list[0].EA_flags = 0;
5761 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005762 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005763 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005764 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005765 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005766 parm_data->list[0].name[name_len] = 0;
5767 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5768 /* caller ensures that ea_value_len is less than 64K but
5769 we need to ensure that it fits within the smb */
5770
Steve French50c2f752007-07-13 00:33:32 +00005771 /*BB add length check to see if it would fit in
5772 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005773 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5774 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005775 memcpy(parm_data->list[0].name+name_len+1,
5776 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005777
5778 pSMB->TotalDataCount = pSMB->DataCount;
5779 pSMB->ParameterCount = cpu_to_le16(params);
5780 pSMB->TotalParameterCount = pSMB->ParameterCount;
5781 pSMB->Reserved4 = 0;
5782 pSMB->hdr.smb_buf_length += byte_count;
5783 pSMB->ByteCount = cpu_to_le16(byte_count);
5784 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5785 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005786 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005787 cFYI(1, "SetPathInfo (EA) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005788
5789 cifs_buf_release(pSMB);
5790
5791 if (rc == -EAGAIN)
5792 goto SetEARetry;
5793
5794 return rc;
5795}
5796
5797#endif