blob: 3652cc60314ceea3f19ac3ec1714e93b94b85433 [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{
334 int rc = -EINVAL;
335 int total_size;
Steve French50c2f752007-07-13 00:33:32 +0000336 char *pBCC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338 /* check for plausible wct, bcc and t2 data and parm sizes */
339 /* check for parm and data offset going beyond end of smb */
Steve French790fe572007-07-07 19:25:05 +0000340 if (pSMB->hdr.WordCount >= 10) {
341 if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
343 /* check that bcc is at least as big as parms + data */
344 /* check that bcc is less than negotiated smb buffer */
345 total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
Steve French790fe572007-07-07 19:25:05 +0000346 if (total_size < 512) {
Steve Frenchc18c8422007-07-18 23:21:09 +0000347 total_size +=
Steve French63135e02007-07-17 17:34:02 +0000348 le16_to_cpu(pSMB->t2_rsp.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 /* BCC le converted in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +0000350 pBCC = (pSMB->hdr.WordCount * 2) +
Steve French09d1db52005-04-28 22:41:08 -0700351 sizeof(struct smb_hdr) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 (char *)pSMB;
Steve French790fe572007-07-07 19:25:05 +0000353 if ((total_size <= (*(u16 *)pBCC)) &&
Steve French50c2f752007-07-13 00:33:32 +0000354 (total_size <
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
356 return 0;
357 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 }
359 }
360 }
Steve French50c2f752007-07-13 00:33:32 +0000361 cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 sizeof(struct smb_t2_rsp) + 16);
363 return rc;
364}
365int
366CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
367{
368 NEGOTIATE_REQ *pSMB;
369 NEGOTIATE_RSP *pSMBr;
370 int rc = 0;
371 int bytes_returned;
Steve French39798772006-05-31 22:40:51 +0000372 int i;
Steve French50c2f752007-07-13 00:33:32 +0000373 struct TCP_Server_Info *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 u16 count;
Steve French750d1152006-06-27 06:28:30 +0000375 unsigned int secFlags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
Steve French790fe572007-07-07 19:25:05 +0000377 if (ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 server = ses->server;
379 else {
380 rc = -EIO;
381 return rc;
382 }
383 rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
384 (void **) &pSMB, (void **) &pSMBr);
385 if (rc)
386 return rc;
Steve French750d1152006-06-27 06:28:30 +0000387
388 /* if any of auth flags (ie not sign or seal) are overriden use them */
Steve French790fe572007-07-07 19:25:05 +0000389 if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
Steve French762e5ab2007-06-28 18:41:42 +0000390 secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */
Steve French750d1152006-06-27 06:28:30 +0000391 else /* if override flags set only sign/seal OR them with global auth */
Jeff Layton04912d62010-04-24 07:57:45 -0400392 secFlags = global_secflags | ses->overrideSecFlg;
Steve French750d1152006-06-27 06:28:30 +0000393
Joe Perchesb6b38f72010-04-21 03:50:45 +0000394 cFYI(1, "secFlags 0x%x", secFlags);
Steve Frenchf40c5622006-06-28 00:13:38 +0000395
Steve French1982c342005-08-17 12:38:22 -0700396 pSMB->hdr.Mid = GetNextMid(server);
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000397 pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
Steve Frencha0136892007-10-04 20:05:09 +0000398
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000399 if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
Steve French254e55e2006-06-04 05:53:15 +0000400 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Steve Frencha0136892007-10-04 20:05:09 +0000401 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000402 cFYI(1, "Kerberos only mechanism, enable extended security");
Steve Frencha0136892007-10-04 20:05:09 +0000403 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Jeff Laytonb4d6fcf2011-01-07 11:30:28 -0500404 } else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
Steve Frenchac683922009-05-06 04:16:04 +0000405 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
406 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000407 cFYI(1, "NTLMSSP only mechanism, enable extended security");
Steve Frenchac683922009-05-06 04:16:04 +0000408 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
409 }
Steve French50c2f752007-07-13 00:33:32 +0000410
Steve French39798772006-05-31 22:40:51 +0000411 count = 0;
Steve French50c2f752007-07-13 00:33:32 +0000412 for (i = 0; i < CIFS_NUM_PROT; i++) {
Steve French39798772006-05-31 22:40:51 +0000413 strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
414 count += strlen(protocols[i].name) + 1;
415 /* null at end of source and target buffers anyway */
416 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 pSMB->hdr.smb_buf_length += count;
418 pSMB->ByteCount = cpu_to_le16(count);
419
420 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
421 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French50c2f752007-07-13 00:33:32 +0000422 if (rc != 0)
Steve French254e55e2006-06-04 05:53:15 +0000423 goto neg_err_exit;
424
Jeff Layton9bf67e52010-04-24 07:57:46 -0400425 server->dialect = le16_to_cpu(pSMBr->DialectIndex);
426 cFYI(1, "Dialect: %d", server->dialect);
Steve French254e55e2006-06-04 05:53:15 +0000427 /* Check wct = 1 error case */
Jeff Layton9bf67e52010-04-24 07:57:46 -0400428 if ((pSMBr->hdr.WordCount < 13) || (server->dialect == BAD_PROT)) {
Steve French254e55e2006-06-04 05:53:15 +0000429 /* core returns wct = 1, but we do not ask for core - otherwise
Steve French50c2f752007-07-13 00:33:32 +0000430 small wct just comes when dialect index is -1 indicating we
Steve French254e55e2006-06-04 05:53:15 +0000431 could not negotiate a common dialect */
432 rc = -EOPNOTSUPP;
433 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000434#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French790fe572007-07-07 19:25:05 +0000435 } else if ((pSMBr->hdr.WordCount == 13)
Jeff Layton9bf67e52010-04-24 07:57:46 -0400436 && ((server->dialect == LANMAN_PROT)
437 || (server->dialect == LANMAN2_PROT))) {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000438 __s16 tmp;
Steve French50c2f752007-07-13 00:33:32 +0000439 struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
Steve French254e55e2006-06-04 05:53:15 +0000440
Steve French790fe572007-07-07 19:25:05 +0000441 if ((secFlags & CIFSSEC_MAY_LANMAN) ||
Steve French750d1152006-06-27 06:28:30 +0000442 (secFlags & CIFSSEC_MAY_PLNTXT))
Steve French254e55e2006-06-04 05:53:15 +0000443 server->secType = LANMAN;
444 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000445 cERROR(1, "mount failed weak security disabled"
446 " in /proc/fs/cifs/SecurityFlags");
Steve French39798772006-05-31 22:40:51 +0000447 rc = -EOPNOTSUPP;
448 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000449 }
Steve French254e55e2006-06-04 05:53:15 +0000450 server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
451 server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
452 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
Steve French39798772006-05-31 22:40:51 +0000453 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000454 server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
Steve French254e55e2006-06-04 05:53:15 +0000455 GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
456 /* even though we do not use raw we might as well set this
457 accurately, in case we ever find a need for it */
Steve French790fe572007-07-07 19:25:05 +0000458 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
Steve Frencheca6acf2009-02-20 05:43:09 +0000459 server->max_rw = 0xFF00;
Steve French254e55e2006-06-04 05:53:15 +0000460 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
461 } else {
Steve Frencheca6acf2009-02-20 05:43:09 +0000462 server->max_rw = 0;/* do not need to use raw anyway */
Steve French254e55e2006-06-04 05:53:15 +0000463 server->capabilities = CAP_MPX_MODE;
464 }
Steve Frenchb815f1e52006-10-02 05:53:29 +0000465 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
Steve French1a70d652006-10-02 05:59:18 +0000466 if (tmp == -1) {
Steve French25ee4a92006-09-30 00:54:23 +0000467 /* OS/2 often does not set timezone therefore
468 * we must use server time to calc time zone.
Steve Frenchb815f1e52006-10-02 05:53:29 +0000469 * Could deviate slightly from the right zone.
470 * Smallest defined timezone difference is 15 minutes
471 * (i.e. Nepal). Rounding up/down is done to match
472 * this requirement.
Steve French25ee4a92006-09-30 00:54:23 +0000473 */
Steve Frenchb815f1e52006-10-02 05:53:29 +0000474 int val, seconds, remain, result;
Steve French25ee4a92006-09-30 00:54:23 +0000475 struct timespec ts, utc;
476 utc = CURRENT_TIME;
Jeff Laytonc4a2c082009-05-27 09:37:33 -0400477 ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
478 rsp->SrvTime.Time, 0);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000479 cFYI(1, "SrvTime %d sec since 1970 (utc: %d) diff: %d",
Steve French50c2f752007-07-13 00:33:32 +0000480 (int)ts.tv_sec, (int)utc.tv_sec,
Joe Perchesb6b38f72010-04-21 03:50:45 +0000481 (int)(utc.tv_sec - ts.tv_sec));
Steve Frenchb815f1e52006-10-02 05:53:29 +0000482 val = (int)(utc.tv_sec - ts.tv_sec);
Andre Haupt8594c152007-08-30 20:18:41 +0000483 seconds = abs(val);
Steve French947a5062006-10-02 05:55:25 +0000484 result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000485 remain = seconds % MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000486 if (remain >= (MIN_TZ_ADJ / 2))
Steve Frenchb815f1e52006-10-02 05:53:29 +0000487 result += MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000488 if (val < 0)
Steve Frenchad7a2922008-02-07 23:25:02 +0000489 result = -result;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000490 server->timeAdj = result;
Steve French25ee4a92006-09-30 00:54:23 +0000491 } else {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000492 server->timeAdj = (int)tmp;
493 server->timeAdj *= 60; /* also in seconds */
Steve French25ee4a92006-09-30 00:54:23 +0000494 }
Joe Perchesb6b38f72010-04-21 03:50:45 +0000495 cFYI(1, "server->timeAdj: %d seconds", server->timeAdj);
Steve French25ee4a92006-09-30 00:54:23 +0000496
Steve French39798772006-05-31 22:40:51 +0000497
Steve French254e55e2006-06-04 05:53:15 +0000498 /* BB get server time for time conversions and add
Steve French50c2f752007-07-13 00:33:32 +0000499 code to use it and timezone since this is not UTC */
Steve French39798772006-05-31 22:40:51 +0000500
Steve French50c2f752007-07-13 00:33:32 +0000501 if (rsp->EncryptionKeyLength ==
Steve French25ee4a92006-09-30 00:54:23 +0000502 cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
Shirish Pargaonkard3ba50b2010-10-27 15:20:36 -0500503 memcpy(ses->server->cryptkey, rsp->EncryptionKey,
Steve French254e55e2006-06-04 05:53:15 +0000504 CIFS_CRYPTO_KEY_SIZE);
505 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
506 rc = -EIO; /* need cryptkey unless plain text */
507 goto neg_err_exit;
508 }
Steve French39798772006-05-31 22:40:51 +0000509
Steve Frenchf19159d2010-04-21 04:12:10 +0000510 cFYI(1, "LANMAN negotiated");
Steve French254e55e2006-06-04 05:53:15 +0000511 /* we will not end up setting signing flags - as no signing
512 was in LANMAN and server did not return the flags on */
513 goto signing_check;
Steve French7c7b25b2006-06-01 19:20:10 +0000514#else /* weak security disabled */
Steve French790fe572007-07-07 19:25:05 +0000515 } else if (pSMBr->hdr.WordCount == 13) {
Steve Frenchf19159d2010-04-21 04:12:10 +0000516 cERROR(1, "mount failed, cifs module not built "
517 "with CIFS_WEAK_PW_HASH support");
Dan Carpenter8212cf72010-03-15 11:22:26 +0300518 rc = -EOPNOTSUPP;
Steve French7c7b25b2006-06-01 19:20:10 +0000519#endif /* WEAK_PW_HASH */
Steve French254e55e2006-06-04 05:53:15 +0000520 goto neg_err_exit;
Steve French790fe572007-07-07 19:25:05 +0000521 } else if (pSMBr->hdr.WordCount != 17) {
Steve French254e55e2006-06-04 05:53:15 +0000522 /* unknown wct */
523 rc = -EOPNOTSUPP;
524 goto neg_err_exit;
525 }
526 /* else wct == 17 NTLM */
527 server->secMode = pSMBr->SecurityMode;
Steve French790fe572007-07-07 19:25:05 +0000528 if ((server->secMode & SECMODE_USER) == 0)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000529 cFYI(1, "share mode security");
Steve French39798772006-05-31 22:40:51 +0000530
Steve French790fe572007-07-07 19:25:05 +0000531 if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000532#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French750d1152006-06-27 06:28:30 +0000533 if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000534#endif /* CIFS_WEAK_PW_HASH */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000535 cERROR(1, "Server requests plain text password"
536 " but client support disabled");
Steve French9312f672006-06-04 22:21:07 +0000537
Steve French790fe572007-07-07 19:25:05 +0000538 if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
Steve French254e55e2006-06-04 05:53:15 +0000539 server->secType = NTLMv2;
Steve French790fe572007-07-07 19:25:05 +0000540 else if (secFlags & CIFSSEC_MAY_NTLM)
Steve French254e55e2006-06-04 05:53:15 +0000541 server->secType = NTLM;
Steve French790fe572007-07-07 19:25:05 +0000542 else if (secFlags & CIFSSEC_MAY_NTLMV2)
Steve Frenchf40c5622006-06-28 00:13:38 +0000543 server->secType = NTLMv2;
Steve Frencha0136892007-10-04 20:05:09 +0000544 else if (secFlags & CIFSSEC_MAY_KRB5)
545 server->secType = Kerberos;
Steve Frenchac683922009-05-06 04:16:04 +0000546 else if (secFlags & CIFSSEC_MAY_NTLMSSP)
Steve Frenchf46c7232009-06-25 03:04:20 +0000547 server->secType = RawNTLMSSP;
Steve Frencha0136892007-10-04 20:05:09 +0000548 else if (secFlags & CIFSSEC_MAY_LANMAN)
549 server->secType = LANMAN;
550/* #ifdef CONFIG_CIFS_EXPERIMENTAL
551 else if (secFlags & CIFSSEC_MAY_PLNTXT)
552 server->secType = ??
553#endif */
554 else {
555 rc = -EOPNOTSUPP;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000556 cERROR(1, "Invalid security type");
Steve Frencha0136892007-10-04 20:05:09 +0000557 goto neg_err_exit;
558 }
559 /* else ... any others ...? */
Steve French7c7b25b2006-06-01 19:20:10 +0000560
Steve French254e55e2006-06-04 05:53:15 +0000561 /* one byte, so no need to convert this or EncryptionKeyLen from
562 little endian */
563 server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
564 /* probably no need to store and check maxvcs */
565 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000567 server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000568 cFYI(DBG2, "Max buf = %d", ses->server->maxBuf);
Steve French254e55e2006-06-04 05:53:15 +0000569 GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
570 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
Steve Frenchb815f1e52006-10-02 05:53:29 +0000571 server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
572 server->timeAdj *= 60;
Steve French254e55e2006-06-04 05:53:15 +0000573 if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
Shirish Pargaonkard3ba50b2010-10-27 15:20:36 -0500574 memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey,
Steve French254e55e2006-06-04 05:53:15 +0000575 CIFS_CRYPTO_KEY_SIZE);
576 } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
577 && (pSMBr->EncryptionKeyLength == 0)) {
578 /* decode security blob */
579 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
580 rc = -EIO; /* no crypt key only if plain text pwd */
581 goto neg_err_exit;
582 }
583
584 /* BB might be helpful to save off the domain of server here */
585
Steve French50c2f752007-07-13 00:33:32 +0000586 if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
Steve French254e55e2006-06-04 05:53:15 +0000587 (server->capabilities & CAP_EXTENDED_SECURITY)) {
588 count = pSMBr->ByteCount;
Jeff Laytone187e442007-10-16 17:10:44 +0000589 if (count < 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 rc = -EIO;
Jeff Laytone187e442007-10-16 17:10:44 +0000591 goto neg_err_exit;
592 }
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530593 spin_lock(&cifs_tcp_ses_lock);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500594 if (server->srv_count > 1) {
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530595 spin_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000596 if (memcmp(server->server_GUID,
597 pSMBr->u.extended_response.
598 GUID, 16) != 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000599 cFYI(1, "server UID changed");
Steve French254e55e2006-06-04 05:53:15 +0000600 memcpy(server->server_GUID,
Jeff Laytone187e442007-10-16 17:10:44 +0000601 pSMBr->u.extended_response.GUID,
602 16);
603 }
Jeff Laytone7ddee92008-11-14 13:44:38 -0500604 } else {
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530605 spin_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000606 memcpy(server->server_GUID,
607 pSMBr->u.extended_response.GUID, 16);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500608 }
Jeff Laytone187e442007-10-16 17:10:44 +0000609
610 if (count == 16) {
611 server->secType = RawNTLMSSP;
Steve French254e55e2006-06-04 05:53:15 +0000612 } else {
613 rc = decode_negTokenInit(pSMBr->u.extended_response.
Jeff Layton26efa0b2010-04-24 07:57:49 -0400614 SecurityBlob, count - 16,
615 server);
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000616 if (rc == 1)
Jeff Laytone5459372007-11-03 05:11:06 +0000617 rc = 0;
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000618 else
Steve French254e55e2006-06-04 05:53:15 +0000619 rc = -EINVAL;
Shirish Pargaonkar2b149f12010-09-18 22:02:18 -0500620 if (server->secType == Kerberos) {
621 if (!server->sec_kerberos &&
622 !server->sec_mskerberos)
623 rc = -EOPNOTSUPP;
624 } else if (server->secType == RawNTLMSSP) {
625 if (!server->sec_ntlmssp)
626 rc = -EOPNOTSUPP;
627 } else
628 rc = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 }
Steve French254e55e2006-06-04 05:53:15 +0000630 } else
631 server->capabilities &= ~CAP_EXTENDED_SECURITY;
632
Steve French6344a422006-06-12 04:18:35 +0000633#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French254e55e2006-06-04 05:53:15 +0000634signing_check:
Steve French6344a422006-06-12 04:18:35 +0000635#endif
Steve French762e5ab2007-06-28 18:41:42 +0000636 if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
637 /* MUST_SIGN already includes the MAY_SIGN FLAG
638 so if this is zero it means that signing is disabled */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000639 cFYI(1, "Signing disabled");
Steve Frenchabb63d62007-10-18 02:58:40 +0000640 if (server->secMode & SECMODE_SIGN_REQUIRED) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000641 cERROR(1, "Server requires "
Jeff Layton7111d212007-10-16 16:50:25 +0000642 "packet signing to be enabled in "
Joe Perchesb6b38f72010-04-21 03:50:45 +0000643 "/proc/fs/cifs/SecurityFlags.");
Steve Frenchabb63d62007-10-18 02:58:40 +0000644 rc = -EOPNOTSUPP;
645 }
Steve French50c2f752007-07-13 00:33:32 +0000646 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000647 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Steve French762e5ab2007-06-28 18:41:42 +0000648 } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
649 /* signing required */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000650 cFYI(1, "Must sign - secFlags 0x%x", secFlags);
Steve French762e5ab2007-06-28 18:41:42 +0000651 if ((server->secMode &
652 (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000653 cERROR(1, "signing required but server lacks support");
Jeff38c10a12007-07-06 21:10:07 +0000654 rc = -EOPNOTSUPP;
Steve French762e5ab2007-06-28 18:41:42 +0000655 } else
656 server->secMode |= SECMODE_SIGN_REQUIRED;
657 } else {
658 /* signing optional ie CIFSSEC_MAY_SIGN */
Steve French790fe572007-07-07 19:25:05 +0000659 if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
Steve French50c2f752007-07-13 00:33:32 +0000660 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000661 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 }
Steve French50c2f752007-07-13 00:33:32 +0000663
664neg_err_exit:
Steve French4a6d87f2005-08-13 08:15:54 -0700665 cifs_buf_release(pSMB);
Steve French254e55e2006-06-04 05:53:15 +0000666
Joe Perchesb6b38f72010-04-21 03:50:45 +0000667 cFYI(1, "negprot rc %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 return rc;
669}
670
671int
672CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
673{
674 struct smb_hdr *smb_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
Joe Perchesb6b38f72010-04-21 03:50:45 +0000677 cFYI(1, "In tree disconnect");
Jeff Laytonf1987b42008-11-15 11:12:47 -0500678
679 /* BB: do we need to check this? These should never be NULL. */
680 if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
681 return -EIO;
682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 /*
Jeff Laytonf1987b42008-11-15 11:12:47 -0500684 * No need to return error on this operation if tid invalidated and
685 * closed on server already e.g. due to tcp session crashing. Also,
686 * the tcon is no longer on the list, so no need to take lock before
687 * checking this.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 */
Steve French268875b2009-06-25 00:29:21 +0000689 if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
Steve French50c2f752007-07-13 00:33:32 +0000690 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
Steve French50c2f752007-07-13 00:33:32 +0000692 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
Steve French09d1db52005-04-28 22:41:08 -0700693 (void **)&smb_buffer);
Jeff Laytonf1987b42008-11-15 11:12:47 -0500694 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 return rc;
Steve French133672e2007-11-13 22:41:37 +0000696
697 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000699 cFYI(1, "Tree disconnect failed %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700
Steve French50c2f752007-07-13 00:33:32 +0000701 /* No need to return error on this operation if tid invalidated and
Jeff Laytonf1987b42008-11-15 11:12:47 -0500702 closed on server already e.g. due to tcp session crashing */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 if (rc == -EAGAIN)
704 rc = 0;
705
706 return rc;
707}
708
709int
710CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
711{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 LOGOFF_ANDX_REQ *pSMB;
713 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Joe Perchesb6b38f72010-04-21 03:50:45 +0000715 cFYI(1, "In SMBLogoff for session disconnect");
Jeff Layton14fbf502008-11-14 13:53:46 -0500716
717 /*
718 * BB: do we need to check validity of ses and server? They should
719 * always be valid since we have an active reference. If not, that
720 * should probably be a BUG()
721 */
722 if (!ses || !ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 return -EIO;
724
Steve Frenchd7b619c2010-02-25 05:36:46 +0000725 mutex_lock(&ses->session_mutex);
Steve French3b795212008-11-13 19:45:32 +0000726 if (ses->need_reconnect)
727 goto session_already_dead; /* no need to send SMBlogoff if uid
728 already closed due to reconnect */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
730 if (rc) {
Steve Frenchd7b619c2010-02-25 05:36:46 +0000731 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 return rc;
733 }
734
Steve French3b795212008-11-13 19:45:32 +0000735 pSMB->hdr.Mid = GetNextMid(ses->server);
Steve French1982c342005-08-17 12:38:22 -0700736
Steve French3b795212008-11-13 19:45:32 +0000737 if (ses->server->secMode &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
739 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
741 pSMB->hdr.Uid = ses->Suid;
742
743 pSMB->AndXCommand = 0xFF;
Steve French133672e2007-11-13 22:41:37 +0000744 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
Steve French3b795212008-11-13 19:45:32 +0000745session_already_dead:
Steve Frenchd7b619c2010-02-25 05:36:46 +0000746 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
748 /* if session dead then we do not need to do ulogoff,
Steve French50c2f752007-07-13 00:33:32 +0000749 since server closed smb session, no sense reporting
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 error */
751 if (rc == -EAGAIN)
752 rc = 0;
753 return rc;
754}
755
756int
Steve French2d785a52007-07-15 01:48:57 +0000757CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
758 __u16 type, const struct nls_table *nls_codepage, int remap)
759{
760 TRANSACTION2_SPI_REQ *pSMB = NULL;
761 TRANSACTION2_SPI_RSP *pSMBr = NULL;
762 struct unlink_psx_rq *pRqD;
763 int name_len;
764 int rc = 0;
765 int bytes_returned = 0;
766 __u16 params, param_offset, offset, byte_count;
767
Joe Perchesb6b38f72010-04-21 03:50:45 +0000768 cFYI(1, "In POSIX delete");
Steve French2d785a52007-07-15 01:48:57 +0000769PsxDelete:
770 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
771 (void **) &pSMBr);
772 if (rc)
773 return rc;
774
775 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
776 name_len =
777 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
778 PATH_MAX, nls_codepage, remap);
779 name_len++; /* trailing null */
780 name_len *= 2;
781 } else { /* BB add path length overrun check */
782 name_len = strnlen(fileName, PATH_MAX);
783 name_len++; /* trailing null */
784 strncpy(pSMB->FileName, fileName, name_len);
785 }
786
787 params = 6 + name_len;
788 pSMB->MaxParameterCount = cpu_to_le16(2);
789 pSMB->MaxDataCount = 0; /* BB double check this with jra */
790 pSMB->MaxSetupCount = 0;
791 pSMB->Reserved = 0;
792 pSMB->Flags = 0;
793 pSMB->Timeout = 0;
794 pSMB->Reserved2 = 0;
795 param_offset = offsetof(struct smb_com_transaction2_spi_req,
796 InformationLevel) - 4;
797 offset = param_offset + params;
798
799 /* Setup pointer to Request Data (inode type) */
800 pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
801 pRqD->type = cpu_to_le16(type);
802 pSMB->ParameterOffset = cpu_to_le16(param_offset);
803 pSMB->DataOffset = cpu_to_le16(offset);
804 pSMB->SetupCount = 1;
805 pSMB->Reserved3 = 0;
806 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
807 byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
808
809 pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
810 pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
811 pSMB->ParameterCount = cpu_to_le16(params);
812 pSMB->TotalParameterCount = pSMB->ParameterCount;
813 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
814 pSMB->Reserved4 = 0;
815 pSMB->hdr.smb_buf_length += byte_count;
816 pSMB->ByteCount = cpu_to_le16(byte_count);
817 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
818 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +0000819 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000820 cFYI(1, "Posix delete returned %d", rc);
Steve French2d785a52007-07-15 01:48:57 +0000821 cifs_buf_release(pSMB);
822
823 cifs_stats_inc(&tcon->num_deletes);
824
825 if (rc == -EAGAIN)
826 goto PsxDelete;
827
828 return rc;
829}
830
831int
Steve French737b7582005-04-28 22:41:06 -0700832CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
833 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834{
835 DELETE_FILE_REQ *pSMB = NULL;
836 DELETE_FILE_RSP *pSMBr = NULL;
837 int rc = 0;
838 int bytes_returned;
839 int name_len;
840
841DelFileRetry:
842 rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
843 (void **) &pSMBr);
844 if (rc)
845 return rc;
846
847 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
848 name_len =
Steve French50c2f752007-07-13 00:33:32 +0000849 cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
Steve French737b7582005-04-28 22:41:06 -0700850 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 name_len++; /* trailing null */
852 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700853 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 name_len = strnlen(fileName, PATH_MAX);
855 name_len++; /* trailing null */
856 strncpy(pSMB->fileName, fileName, name_len);
857 }
858 pSMB->SearchAttributes =
859 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
860 pSMB->BufferFormat = 0x04;
861 pSMB->hdr.smb_buf_length += name_len + 1;
862 pSMB->ByteCount = cpu_to_le16(name_len + 1);
863 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
864 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700865 cifs_stats_inc(&tcon->num_deletes);
Steve Frenchad7a2922008-02-07 23:25:02 +0000866 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000867 cFYI(1, "Error in RMFile = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
869 cifs_buf_release(pSMB);
870 if (rc == -EAGAIN)
871 goto DelFileRetry;
872
873 return rc;
874}
875
876int
Steve French50c2f752007-07-13 00:33:32 +0000877CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
Steve French737b7582005-04-28 22:41:06 -0700878 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879{
880 DELETE_DIRECTORY_REQ *pSMB = NULL;
881 DELETE_DIRECTORY_RSP *pSMBr = NULL;
882 int rc = 0;
883 int bytes_returned;
884 int name_len;
885
Joe Perchesb6b38f72010-04-21 03:50:45 +0000886 cFYI(1, "In CIFSSMBRmDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887RmDirRetry:
888 rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
889 (void **) &pSMBr);
890 if (rc)
891 return rc;
892
893 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French737b7582005-04-28 22:41:06 -0700894 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
895 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 name_len++; /* trailing null */
897 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700898 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 name_len = strnlen(dirName, PATH_MAX);
900 name_len++; /* trailing null */
901 strncpy(pSMB->DirName, dirName, name_len);
902 }
903
904 pSMB->BufferFormat = 0x04;
905 pSMB->hdr.smb_buf_length += name_len + 1;
906 pSMB->ByteCount = cpu_to_le16(name_len + 1);
907 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
908 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700909 cifs_stats_inc(&tcon->num_rmdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000910 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000911 cFYI(1, "Error in RMDir = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912
913 cifs_buf_release(pSMB);
914 if (rc == -EAGAIN)
915 goto RmDirRetry;
916 return rc;
917}
918
919int
920CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -0700921 const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922{
923 int rc = 0;
924 CREATE_DIRECTORY_REQ *pSMB = NULL;
925 CREATE_DIRECTORY_RSP *pSMBr = NULL;
926 int bytes_returned;
927 int name_len;
928
Joe Perchesb6b38f72010-04-21 03:50:45 +0000929 cFYI(1, "In CIFSSMBMkDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930MkDirRetry:
931 rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
932 (void **) &pSMBr);
933 if (rc)
934 return rc;
935
936 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +0000937 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
Steve French737b7582005-04-28 22:41:06 -0700938 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(name, PATH_MAX);
943 name_len++; /* trailing null */
944 strncpy(pSMB->DirName, name, 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_mkdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000953 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000954 cFYI(1, "Error in Mkdir = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -0700955
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 cifs_buf_release(pSMB);
957 if (rc == -EAGAIN)
958 goto MkDirRetry;
959 return rc;
960}
961
Steve French2dd29d32007-04-23 22:07:35 +0000962int
963CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
Steve Frenchad7a2922008-02-07 23:25:02 +0000964 __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
Steve French50c2f752007-07-13 00:33:32 +0000965 __u32 *pOplock, const char *name,
Steve French2dd29d32007-04-23 22:07:35 +0000966 const struct nls_table *nls_codepage, int remap)
967{
968 TRANSACTION2_SPI_REQ *pSMB = NULL;
969 TRANSACTION2_SPI_RSP *pSMBr = NULL;
970 int name_len;
971 int rc = 0;
972 int bytes_returned = 0;
Steve French2dd29d32007-04-23 22:07:35 +0000973 __u16 params, param_offset, offset, byte_count, count;
Steve Frenchad7a2922008-02-07 23:25:02 +0000974 OPEN_PSX_REQ *pdata;
975 OPEN_PSX_RSP *psx_rsp;
Steve French2dd29d32007-04-23 22:07:35 +0000976
Joe Perchesb6b38f72010-04-21 03:50:45 +0000977 cFYI(1, "In POSIX Create");
Steve French2dd29d32007-04-23 22:07:35 +0000978PsxCreat:
979 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
980 (void **) &pSMBr);
981 if (rc)
982 return rc;
983
984 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
985 name_len =
986 cifsConvertToUCS((__le16 *) pSMB->FileName, name,
987 PATH_MAX, nls_codepage, remap);
988 name_len++; /* trailing null */
989 name_len *= 2;
990 } else { /* BB improve the check for buffer overruns BB */
991 name_len = strnlen(name, PATH_MAX);
992 name_len++; /* trailing null */
993 strncpy(pSMB->FileName, name, name_len);
994 }
995
996 params = 6 + name_len;
997 count = sizeof(OPEN_PSX_REQ);
998 pSMB->MaxParameterCount = cpu_to_le16(2);
999 pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
1000 pSMB->MaxSetupCount = 0;
1001 pSMB->Reserved = 0;
1002 pSMB->Flags = 0;
1003 pSMB->Timeout = 0;
1004 pSMB->Reserved2 = 0;
1005 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00001006 InformationLevel) - 4;
Steve French2dd29d32007-04-23 22:07:35 +00001007 offset = param_offset + params;
Steve French2dd29d32007-04-23 22:07:35 +00001008 pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001009 pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
Steve French2dd29d32007-04-23 22:07:35 +00001010 pdata->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00001011 pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
Steve French2dd29d32007-04-23 22:07:35 +00001012 pdata->OpenFlags = cpu_to_le32(*pOplock);
1013 pSMB->ParameterOffset = cpu_to_le16(param_offset);
1014 pSMB->DataOffset = cpu_to_le16(offset);
1015 pSMB->SetupCount = 1;
1016 pSMB->Reserved3 = 0;
1017 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
1018 byte_count = 3 /* pad */ + params + count;
1019
1020 pSMB->DataCount = cpu_to_le16(count);
1021 pSMB->ParameterCount = cpu_to_le16(params);
1022 pSMB->TotalDataCount = pSMB->DataCount;
1023 pSMB->TotalParameterCount = pSMB->ParameterCount;
1024 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1025 pSMB->Reserved4 = 0;
Steve French50c2f752007-07-13 00:33:32 +00001026 pSMB->hdr.smb_buf_length += byte_count;
Steve French2dd29d32007-04-23 22:07:35 +00001027 pSMB->ByteCount = cpu_to_le16(byte_count);
1028 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1029 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1030 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001031 cFYI(1, "Posix create returned %d", rc);
Steve French2dd29d32007-04-23 22:07:35 +00001032 goto psx_create_err;
1033 }
1034
Joe Perchesb6b38f72010-04-21 03:50:45 +00001035 cFYI(1, "copying inode info");
Steve French2dd29d32007-04-23 22:07:35 +00001036 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1037
1038 if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
1039 rc = -EIO; /* bad smb */
1040 goto psx_create_err;
1041 }
1042
1043 /* copy return information to pRetData */
Steve French50c2f752007-07-13 00:33:32 +00001044 psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
Steve French2dd29d32007-04-23 22:07:35 +00001045 + le16_to_cpu(pSMBr->t2.DataOffset));
Steve French50c2f752007-07-13 00:33:32 +00001046
Steve French2dd29d32007-04-23 22:07:35 +00001047 *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
Steve French790fe572007-07-07 19:25:05 +00001048 if (netfid)
Steve French2dd29d32007-04-23 22:07:35 +00001049 *netfid = psx_rsp->Fid; /* cifs fid stays in le */
1050 /* Let caller know file was created so we can set the mode. */
1051 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001052 if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
Steve French2dd29d32007-04-23 22:07:35 +00001053 *pOplock |= CIFS_CREATE_ACTION;
1054 /* check to make sure response data is there */
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001055 if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1056 pRetData->Type = cpu_to_le32(-1); /* unknown */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001057 cFYI(DBG2, "unknown type");
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001058 } else {
Steve French790fe572007-07-07 19:25:05 +00001059 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001060 + sizeof(FILE_UNIX_BASIC_INFO)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001061 cERROR(1, "Open response data too small");
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001062 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001063 goto psx_create_err;
1064 }
Steve French50c2f752007-07-13 00:33:32 +00001065 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001066 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001067 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001068 }
Steve French2dd29d32007-04-23 22:07:35 +00001069
1070psx_create_err:
1071 cifs_buf_release(pSMB);
1072
Steve French65bc98b2009-07-10 15:27:25 +00001073 if (posix_flags & SMB_O_DIRECTORY)
1074 cifs_stats_inc(&tcon->num_posixmkdirs);
1075 else
1076 cifs_stats_inc(&tcon->num_posixopens);
Steve French2dd29d32007-04-23 22:07:35 +00001077
1078 if (rc == -EAGAIN)
1079 goto PsxCreat;
1080
Steve French50c2f752007-07-13 00:33:32 +00001081 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001082}
1083
Steve Frencha9d02ad2005-08-24 23:06:05 -07001084static __u16 convert_disposition(int disposition)
1085{
1086 __u16 ofun = 0;
1087
1088 switch (disposition) {
1089 case FILE_SUPERSEDE:
1090 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1091 break;
1092 case FILE_OPEN:
1093 ofun = SMBOPEN_OAPPEND;
1094 break;
1095 case FILE_CREATE:
1096 ofun = SMBOPEN_OCREATE;
1097 break;
1098 case FILE_OPEN_IF:
1099 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1100 break;
1101 case FILE_OVERWRITE:
1102 ofun = SMBOPEN_OTRUNC;
1103 break;
1104 case FILE_OVERWRITE_IF:
1105 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1106 break;
1107 default:
Joe Perchesb6b38f72010-04-21 03:50:45 +00001108 cFYI(1, "unknown disposition %d", disposition);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001109 ofun = SMBOPEN_OAPPEND; /* regular open */
1110 }
1111 return ofun;
1112}
1113
Jeff Layton35fc37d2008-05-14 10:22:03 -07001114static int
1115access_flags_to_smbopen_mode(const int access_flags)
1116{
1117 int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
1118
1119 if (masked_flags == GENERIC_READ)
1120 return SMBOPEN_READ;
1121 else if (masked_flags == GENERIC_WRITE)
1122 return SMBOPEN_WRITE;
1123
1124 /* just go for read/write */
1125 return SMBOPEN_READWRITE;
1126}
1127
Steve Frencha9d02ad2005-08-24 23:06:05 -07001128int
1129SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1130 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001131 const int access_flags, const int create_options, __u16 *netfid,
1132 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001133 const struct nls_table *nls_codepage, int remap)
1134{
1135 int rc = -EACCES;
1136 OPENX_REQ *pSMB = NULL;
1137 OPENX_RSP *pSMBr = NULL;
1138 int bytes_returned;
1139 int name_len;
1140 __u16 count;
1141
1142OldOpenRetry:
1143 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1144 (void **) &pSMBr);
1145 if (rc)
1146 return rc;
1147
1148 pSMB->AndXCommand = 0xFF; /* none */
1149
1150 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1151 count = 1; /* account for one byte pad to word boundary */
1152 name_len =
1153 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1154 fileName, PATH_MAX, nls_codepage, remap);
1155 name_len++; /* trailing null */
1156 name_len *= 2;
1157 } else { /* BB improve check for buffer overruns BB */
1158 count = 0; /* no pad */
1159 name_len = strnlen(fileName, PATH_MAX);
1160 name_len++; /* trailing null */
1161 strncpy(pSMB->fileName, fileName, name_len);
1162 }
1163 if (*pOplock & REQ_OPLOCK)
1164 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001165 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001166 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001167
Steve Frencha9d02ad2005-08-24 23:06:05 -07001168 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
Jeff Layton35fc37d2008-05-14 10:22:03 -07001169 pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001170 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1171 /* set file as system file if special file such
1172 as fifo and server expecting SFU style and
1173 no Unix extensions */
1174
Steve French790fe572007-07-07 19:25:05 +00001175 if (create_options & CREATE_OPTION_SPECIAL)
1176 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001177 else /* BB FIXME BB */
1178 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001179
Jeff Layton67750fb2008-05-09 22:28:02 +00001180 if (create_options & CREATE_OPTION_READONLY)
1181 pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001182
1183 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001184/* pSMB->CreateOptions = cpu_to_le32(create_options &
1185 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001186 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001187
1188 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001189 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001190 count += name_len;
1191 pSMB->hdr.smb_buf_length += count;
1192
1193 pSMB->ByteCount = cpu_to_le16(count);
1194 /* long_op set to 1 to allow for oplock break timeouts */
1195 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001196 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001197 cifs_stats_inc(&tcon->num_opens);
1198 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001199 cFYI(1, "Error in Open = %d", rc);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001200 } else {
1201 /* BB verify if wct == 15 */
1202
Steve French582d21e2008-05-13 04:54:12 +00001203/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
Steve Frencha9d02ad2005-08-24 23:06:05 -07001204
1205 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1206 /* Let caller know file was created so we can set the mode. */
1207 /* Do we care about the CreateAction in any other cases? */
1208 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001209/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001210 *pOplock |= CIFS_CREATE_ACTION; */
1211 /* BB FIXME END */
1212
Steve French790fe572007-07-07 19:25:05 +00001213 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001214 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1215 pfile_info->LastAccessTime = 0; /* BB fixme */
1216 pfile_info->LastWriteTime = 0; /* BB fixme */
1217 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001218 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001219 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001220 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001221 pfile_info->AllocationSize =
1222 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1223 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001224 pfile_info->NumberOfLinks = cpu_to_le32(1);
Jeff Layton9a8165f2008-10-17 21:03:20 -04001225 pfile_info->DeletePending = 0;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001226 }
1227 }
1228
1229 cifs_buf_release(pSMB);
1230 if (rc == -EAGAIN)
1231 goto OldOpenRetry;
1232 return rc;
1233}
1234
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235int
1236CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1237 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001238 const int access_flags, const int create_options, __u16 *netfid,
1239 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001240 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241{
1242 int rc = -EACCES;
1243 OPEN_REQ *pSMB = NULL;
1244 OPEN_RSP *pSMBr = NULL;
1245 int bytes_returned;
1246 int name_len;
1247 __u16 count;
1248
1249openRetry:
1250 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1251 (void **) &pSMBr);
1252 if (rc)
1253 return rc;
1254
1255 pSMB->AndXCommand = 0xFF; /* none */
1256
1257 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1258 count = 1; /* account for one byte pad to word boundary */
1259 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001260 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001261 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 name_len++; /* trailing null */
1263 name_len *= 2;
1264 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001265 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 count = 0; /* no pad */
1267 name_len = strnlen(fileName, PATH_MAX);
1268 name_len++; /* trailing null */
1269 pSMB->NameLength = cpu_to_le16(name_len);
1270 strncpy(pSMB->fileName, fileName, name_len);
1271 }
1272 if (*pOplock & REQ_OPLOCK)
1273 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001274 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1277 pSMB->AllocationSize = 0;
Steve Frencheda3c0292005-07-21 15:20:28 -07001278 /* set file as system file if special file such
1279 as fifo and server expecting SFU style and
1280 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001281 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c0292005-07-21 15:20:28 -07001282 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1283 else
1284 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Jeff Layton67750fb2008-05-09 22:28:02 +00001285
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 /* XP does not handle ATTR_POSIX_SEMANTICS */
1287 /* but it helps speed up case sensitive checks for other
1288 servers such as Samba */
1289 if (tcon->ses->capabilities & CAP_UNIX)
1290 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1291
Jeff Layton67750fb2008-05-09 22:28:02 +00001292 if (create_options & CREATE_OPTION_READONLY)
1293 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);
1294
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1296 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c0292005-07-21 15:20:28 -07001297 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001298 /* BB Expirement with various impersonation levels and verify */
1299 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 pSMB->SecurityFlags =
1301 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1302
1303 count += name_len;
1304 pSMB->hdr.smb_buf_length += count;
1305
1306 pSMB->ByteCount = cpu_to_le16(count);
1307 /* long_op set to 1 to allow for oplock break timeouts */
1308 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001309 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha4544342005-08-24 13:59:35 -07001310 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001312 cFYI(1, "Error in Open = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 } else {
Steve French09d1db52005-04-28 22:41:08 -07001314 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1316 /* Let caller know file was created so we can set the mode. */
1317 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001318 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001319 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001320 if (pfile_info) {
Steve French61e74802008-12-03 00:57:54 +00001321 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
1322 36 /* CreationTime to Attributes */);
1323 /* the file_info buf is endian converted by caller */
1324 pfile_info->AllocationSize = pSMBr->AllocationSize;
1325 pfile_info->EndOfFile = pSMBr->EndOfFile;
1326 pfile_info->NumberOfLinks = cpu_to_le32(1);
1327 pfile_info->DeletePending = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001330
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 cifs_buf_release(pSMB);
1332 if (rc == -EAGAIN)
1333 goto openRetry;
1334 return rc;
1335}
1336
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337int
Steve French50c2f752007-07-13 00:33:32 +00001338CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1339 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1340 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341{
1342 int rc = -EACCES;
1343 READ_REQ *pSMB = NULL;
1344 READ_RSP *pSMBr = NULL;
1345 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001346 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001347 int resp_buf_type = 0;
1348 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
Joe Perchesb6b38f72010-04-21 03:50:45 +00001350 cFYI(1, "Reading %d bytes on fid %d", count, netfid);
Steve French790fe572007-07-07 19:25:05 +00001351 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001352 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001353 else {
Steve Frenchbfa0d752005-08-31 21:50:37 -07001354 wct = 10; /* old style read */
Steve French4c3130e2008-12-09 00:28:16 +00001355 if ((lseek >> 32) > 0) {
1356 /* can not handle this big offset for old */
1357 return -EIO;
1358 }
1359 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
1361 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001362 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 if (rc)
1364 return rc;
1365
1366 /* tcon and ses pointer are checked in smb_init */
1367 if (tcon->ses->server == NULL)
1368 return -ECONNABORTED;
1369
Steve Frenchec637e32005-12-12 20:53:18 -08001370 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 pSMB->Fid = netfid;
1372 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001373 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001374 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve Frenchbfa0d752005-08-31 21:50:37 -07001375
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 pSMB->Remaining = 0;
1377 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1378 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001379 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001380 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1381 else {
1382 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001383 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001384 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001385 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001386 }
Steve Frenchec637e32005-12-12 20:53:18 -08001387
1388 iov[0].iov_base = (char *)pSMB;
1389 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001390 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Steve French133672e2007-11-13 22:41:37 +00001391 &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001392 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001393 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001395 cERROR(1, "Send error in read = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 } else {
1397 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1398 data_length = data_length << 16;
1399 data_length += le16_to_cpu(pSMBr->DataLength);
1400 *nbytes = data_length;
1401
1402 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001403 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 || (data_length > count)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001405 cFYI(1, "bad length %d for count %d",
1406 data_length, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 rc = -EIO;
1408 *nbytes = 0;
1409 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001410 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001411 le16_to_cpu(pSMBr->DataOffset);
1412/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001413 cERROR(1, "Faulting on read rc = %d",rc);
Steve French50c2f752007-07-13 00:33:32 +00001414 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001415 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001416 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001417 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 }
1419 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420
Steve French4b8f9302006-02-26 16:41:18 +00001421/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001422 if (*buf) {
1423 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001424 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001425 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001426 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001427 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001428 /* return buffer to caller to free */
1429 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001430 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001431 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001432 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001433 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001434 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001435
1436 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 since file handle passed in no longer valid */
1438 return rc;
1439}
1440
Steve Frenchec637e32005-12-12 20:53:18 -08001441
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442int
1443CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1444 const int netfid, const unsigned int count,
1445 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001446 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447{
1448 int rc = -EACCES;
1449 WRITE_REQ *pSMB = NULL;
1450 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001451 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 __u32 bytes_sent;
1453 __u16 byte_count;
1454
Steve Frencha24e2d72010-04-03 17:20:21 +00001455 *nbytes = 0;
1456
Joe Perchesb6b38f72010-04-21 03:50:45 +00001457 /* cFYI(1, "write at %lld %d bytes", offset, count);*/
Steve French790fe572007-07-07 19:25:05 +00001458 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001459 return -ECONNABORTED;
1460
Steve French790fe572007-07-07 19:25:05 +00001461 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001462 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001463 else {
Steve French1c955182005-08-30 20:58:07 -07001464 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001465 if ((offset >> 32) > 0) {
1466 /* can not handle big offset for old srv */
1467 return -EIO;
1468 }
1469 }
Steve French1c955182005-08-30 20:58:07 -07001470
1471 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 (void **) &pSMBr);
1473 if (rc)
1474 return rc;
1475 /* tcon and ses pointer are checked in smb_init */
1476 if (tcon->ses->server == NULL)
1477 return -ECONNABORTED;
1478
1479 pSMB->AndXCommand = 0xFF; /* none */
1480 pSMB->Fid = netfid;
1481 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001482 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001483 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French50c2f752007-07-13 00:33:32 +00001484
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 pSMB->Reserved = 0xFFFFFFFF;
1486 pSMB->WriteMode = 0;
1487 pSMB->Remaining = 0;
1488
Steve French50c2f752007-07-13 00:33:32 +00001489 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 can send more if LARGE_WRITE_X capability returned by the server and if
1491 our buffer is big enough or if we convert to iovecs on socket writes
1492 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001493 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1495 } else {
1496 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1497 & ~0xFF;
1498 }
1499
1500 if (bytes_sent > count)
1501 bytes_sent = count;
1502 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001503 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001504 if (buf)
Steve French61e74802008-12-03 00:57:54 +00001505 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001506 else if (ubuf) {
1507 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 cifs_buf_release(pSMB);
1509 return -EFAULT;
1510 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001511 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 /* No buffer */
1513 cifs_buf_release(pSMB);
1514 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001515 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001516 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001517 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001518 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001519 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001520
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1522 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001523 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001524
Steve French790fe572007-07-07 19:25:05 +00001525 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001526 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001527 else { /* old style write has byte count 4 bytes earlier
1528 so 4 bytes pad */
1529 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001530 (struct smb_com_writex_req *)pSMB;
1531 pSMBW->ByteCount = cpu_to_le16(byte_count);
1532 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
1534 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1535 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001536 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00001538 cFYI(1, "Send error in write = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 } else {
1540 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1541 *nbytes = (*nbytes) << 16;
1542 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301543
1544 /*
1545 * Mask off high 16 bits when bytes written as returned by the
1546 * server is greater than bytes requested by the client. Some
1547 * OS/2 servers are known to set incorrect CountHigh values.
1548 */
1549 if (*nbytes > count)
1550 *nbytes &= 0xFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 }
1552
1553 cifs_buf_release(pSMB);
1554
Steve French50c2f752007-07-13 00:33:32 +00001555 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 since file handle passed in no longer valid */
1557
1558 return rc;
1559}
1560
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001561int
1562CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001564 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1565 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566{
1567 int rc = -EACCES;
1568 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001569 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001570 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001571 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572
Jeff Laytonfbec9ab2009-04-03 13:44:00 -04001573 *nbytes = 0;
1574
Joe Perchesb6b38f72010-04-21 03:50:45 +00001575 cFYI(1, "write2 at %lld %d bytes", (long long)offset, count);
Steve Frenchff7feac2005-11-15 16:45:16 -08001576
Steve French4c3130e2008-12-09 00:28:16 +00001577 if (tcon->ses->capabilities & CAP_LARGE_FILES) {
Steve French8cc64c62005-10-03 13:49:43 -07001578 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001579 } else {
Steve French8cc64c62005-10-03 13:49:43 -07001580 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001581 if ((offset >> 32) > 0) {
1582 /* can not handle big offset for old srv */
1583 return -EIO;
1584 }
1585 }
Steve French8cc64c62005-10-03 13:49:43 -07001586 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 if (rc)
1588 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 /* tcon and ses pointer are checked in smb_init */
1590 if (tcon->ses->server == NULL)
1591 return -ECONNABORTED;
1592
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001593 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594 pSMB->Fid = netfid;
1595 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001596 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001597 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 pSMB->Reserved = 0xFFFFFFFF;
1599 pSMB->WriteMode = 0;
1600 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001601
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001603 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
Steve French3e844692005-10-03 13:37:24 -07001605 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1606 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001607 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001608 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001609 pSMB->hdr.smb_buf_length += count+1;
1610 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001611 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1612 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001613 pSMB->ByteCount = cpu_to_le16(count + 1);
1614 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001615 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001616 (struct smb_com_writex_req *)pSMB;
1617 pSMBW->ByteCount = cpu_to_le16(count + 5);
1618 }
Steve French3e844692005-10-03 13:37:24 -07001619 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001620 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001621 iov[0].iov_len = smb_hdr_len + 4;
1622 else /* wct == 12 pad bigger by four bytes */
1623 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001624
Steve French3e844692005-10-03 13:37:24 -07001625
Steve Frenchec637e32005-12-12 20:53:18 -08001626 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001627 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001628 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001630 cFYI(1, "Send error Write2 = %d", rc);
Steve French790fe572007-07-07 19:25:05 +00001631 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001632 /* presumably this can not happen, but best to be safe */
1633 rc = -EIO;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001634 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001635 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001636 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1637 *nbytes = (*nbytes) << 16;
1638 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301639
1640 /*
1641 * Mask off high 16 bits when bytes written as returned by the
1642 * server is greater than bytes requested by the client. OS/2
1643 * servers are known to set incorrect CountHigh values.
1644 */
1645 if (*nbytes > count)
1646 *nbytes &= 0xFFFF;
Steve French50c2f752007-07-13 00:33:32 +00001647 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
Steve French4b8f9302006-02-26 16:41:18 +00001649/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001650 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001651 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001652 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001653 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654
Steve French50c2f752007-07-13 00:33:32 +00001655 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656 since file handle passed in no longer valid */
1657
1658 return rc;
1659}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001660
1661
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662int
1663CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1664 const __u16 smb_file_id, const __u64 len,
1665 const __u64 offset, const __u32 numUnlock,
Pavel Shilovsky12fed002011-01-17 20:15:44 +03001666 const __u32 numLock, const __u8 lockType,
1667 const bool waitFlag, const __u8 oplock_level)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668{
1669 int rc = 0;
1670 LOCK_REQ *pSMB = NULL;
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001671/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 int bytes_returned;
1673 int timeout = 0;
1674 __u16 count;
1675
Joe Perchesb6b38f72010-04-21 03:50:45 +00001676 cFYI(1, "CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock);
Steve French46810cb2005-04-28 22:41:09 -07001677 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1678
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679 if (rc)
1680 return rc;
1681
Steve French790fe572007-07-07 19:25:05 +00001682 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001683 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684 pSMB->Timeout = 0;
Steve French4b18f2a2008-04-29 00:06:05 +00001685 } else if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001686 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1688 } else {
1689 pSMB->Timeout = 0;
1690 }
1691
1692 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1693 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1694 pSMB->LockType = lockType;
Pavel Shilovsky12fed002011-01-17 20:15:44 +03001695 pSMB->OplockLevel = oplock_level;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 pSMB->AndXCommand = 0xFF; /* none */
1697 pSMB->Fid = smb_file_id; /* netfid stays le */
1698
Steve French790fe572007-07-07 19:25:05 +00001699 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1701 /* BB where to store pid high? */
1702 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1703 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1704 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1705 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1706 count = sizeof(LOCKING_ANDX_RANGE);
1707 } else {
1708 /* oplock break */
1709 count = 0;
1710 }
1711 pSMB->hdr.smb_buf_length += count;
1712 pSMB->ByteCount = cpu_to_le16(count);
1713
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001714 if (waitFlag) {
1715 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001716 (struct smb_hdr *) pSMB, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001717 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001718 } else {
Steve French133672e2007-11-13 22:41:37 +00001719 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1720 timeout);
1721 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001722 }
Steve Frencha4544342005-08-24 13:59:35 -07001723 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001724 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001725 cFYI(1, "Send error in Lock = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726
Steve French50c2f752007-07-13 00:33:32 +00001727 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 since file handle passed in no longer valid */
1729 return rc;
1730}
1731
1732int
Steve French08547b02006-02-28 22:39:25 +00001733CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1734 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001735 struct file_lock *pLockData, const __u16 lock_type,
Steve French4b18f2a2008-04-29 00:06:05 +00001736 const bool waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001737{
1738 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1739 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001740 struct cifs_posix_lock *parm_data;
1741 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001742 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001743 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001744 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001745 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001746 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001747
Joe Perchesb6b38f72010-04-21 03:50:45 +00001748 cFYI(1, "Posix Lock");
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001749
Steve French790fe572007-07-07 19:25:05 +00001750 if (pLockData == NULL)
Marcin Slusarzed5f0372008-05-13 04:01:01 +00001751 return -EINVAL;
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001752
Steve French08547b02006-02-28 22:39:25 +00001753 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1754
1755 if (rc)
1756 return rc;
1757
1758 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1759
Steve French50c2f752007-07-13 00:33:32 +00001760 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001761 pSMB->MaxSetupCount = 0;
1762 pSMB->Reserved = 0;
1763 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001764 pSMB->Reserved2 = 0;
1765 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1766 offset = param_offset + params;
1767
Steve French08547b02006-02-28 22:39:25 +00001768 count = sizeof(struct cifs_posix_lock);
1769 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001770 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001771 pSMB->SetupCount = 1;
1772 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001773 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001774 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1775 else
1776 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1777 byte_count = 3 /* pad */ + params + count;
1778 pSMB->DataCount = cpu_to_le16(count);
1779 pSMB->ParameterCount = cpu_to_le16(params);
1780 pSMB->TotalDataCount = pSMB->DataCount;
1781 pSMB->TotalParameterCount = pSMB->ParameterCount;
1782 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001783 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001784 (((char *) &pSMB->hdr.Protocol) + offset);
1785
1786 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001787 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001788 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001789 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001790 pSMB->Timeout = cpu_to_le32(-1);
1791 } else
1792 pSMB->Timeout = 0;
1793
Steve French08547b02006-02-28 22:39:25 +00001794 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001795 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001796 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001797
1798 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001799 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001800 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1801 pSMB->Reserved4 = 0;
1802 pSMB->hdr.smb_buf_length += byte_count;
1803 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001804 if (waitFlag) {
1805 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1806 (struct smb_hdr *) pSMBr, &bytes_returned);
1807 } else {
Steve French133672e2007-11-13 22:41:37 +00001808 iov[0].iov_base = (char *)pSMB;
1809 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1810 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1811 &resp_buf_type, timeout);
1812 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1813 not try to free it twice below on exit */
1814 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001815 }
1816
Steve French08547b02006-02-28 22:39:25 +00001817 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001818 cFYI(1, "Send error in Posix Lock = %d", rc);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001819 } else if (get_flag) {
1820 /* lock structure can be returned on get */
1821 __u16 data_offset;
1822 __u16 data_count;
1823 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001824
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001825 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1826 rc = -EIO; /* bad smb */
1827 goto plk_err_exit;
1828 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001829 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1830 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001831 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001832 rc = -EIO;
1833 goto plk_err_exit;
1834 }
1835 parm_data = (struct cifs_posix_lock *)
1836 ((char *)&pSMBr->hdr.Protocol + data_offset);
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001837 if (parm_data->lock_type == __constant_cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001838 pLockData->fl_type = F_UNLCK;
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001839 else {
1840 if (parm_data->lock_type ==
1841 __constant_cpu_to_le16(CIFS_RDLCK))
1842 pLockData->fl_type = F_RDLCK;
1843 else if (parm_data->lock_type ==
1844 __constant_cpu_to_le16(CIFS_WRLCK))
1845 pLockData->fl_type = F_WRLCK;
1846
1847 pLockData->fl_start = parm_data->start;
1848 pLockData->fl_end = parm_data->start +
1849 parm_data->length - 1;
1850 pLockData->fl_pid = parm_data->pid;
1851 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001852 }
Steve French50c2f752007-07-13 00:33:32 +00001853
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001854plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001855 if (pSMB)
1856 cifs_small_buf_release(pSMB);
1857
Steve French133672e2007-11-13 22:41:37 +00001858 if (resp_buf_type == CIFS_SMALL_BUFFER)
1859 cifs_small_buf_release(iov[0].iov_base);
1860 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1861 cifs_buf_release(iov[0].iov_base);
1862
Steve French08547b02006-02-28 22:39:25 +00001863 /* Note: On -EAGAIN error only caller can retry on handle based calls
1864 since file handle passed in no longer valid */
1865
1866 return rc;
1867}
1868
1869
1870int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1872{
1873 int rc = 0;
1874 CLOSE_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001875 cFYI(1, "In CIFSSMBClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876
1877/* do not retry on dead session on close */
1878 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001879 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880 return 0;
1881 if (rc)
1882 return rc;
1883
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e52006-10-02 05:53:29 +00001885 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001887 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001888 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001890 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 /* EINTR is expected when user ctl-c to kill app */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001892 cERROR(1, "Send error in Close = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 }
1894 }
1895
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001897 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898 rc = 0;
1899
1900 return rc;
1901}
1902
1903int
Steve Frenchb298f222009-02-21 21:17:43 +00001904CIFSSMBFlush(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1905{
1906 int rc = 0;
1907 FLUSH_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001908 cFYI(1, "In CIFSSMBFlush");
Steve Frenchb298f222009-02-21 21:17:43 +00001909
1910 rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB);
1911 if (rc)
1912 return rc;
1913
1914 pSMB->FileID = (__u16) smb_file_id;
1915 pSMB->ByteCount = 0;
1916 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
1917 cifs_stats_inc(&tcon->num_flushes);
1918 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001919 cERROR(1, "Send error in Flush = %d", rc);
Steve Frenchb298f222009-02-21 21:17:43 +00001920
1921 return rc;
1922}
1923
1924int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1926 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001927 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928{
1929 int rc = 0;
1930 RENAME_REQ *pSMB = NULL;
1931 RENAME_RSP *pSMBr = NULL;
1932 int bytes_returned;
1933 int name_len, name_len2;
1934 __u16 count;
1935
Joe Perchesb6b38f72010-04-21 03:50:45 +00001936 cFYI(1, "In CIFSSMBRename");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937renameRetry:
1938 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1939 (void **) &pSMBr);
1940 if (rc)
1941 return rc;
1942
1943 pSMB->BufferFormat = 0x04;
1944 pSMB->SearchAttributes =
1945 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1946 ATTR_DIRECTORY);
1947
1948 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1949 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001950 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001951 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 name_len++; /* trailing null */
1953 name_len *= 2;
1954 pSMB->OldFileName[name_len] = 0x04; /* pad */
1955 /* protocol requires ASCII signature byte on Unicode string */
1956 pSMB->OldFileName[name_len + 1] = 0x00;
1957 name_len2 =
Steve French582d21e2008-05-13 04:54:12 +00001958 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07001959 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
1961 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00001962 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963 name_len = strnlen(fromName, PATH_MAX);
1964 name_len++; /* trailing null */
1965 strncpy(pSMB->OldFileName, fromName, name_len);
1966 name_len2 = strnlen(toName, PATH_MAX);
1967 name_len2++; /* trailing null */
1968 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
1969 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
1970 name_len2++; /* trailing null */
1971 name_len2++; /* signature byte */
1972 }
1973
1974 count = 1 /* 1st signature byte */ + name_len + name_len2;
1975 pSMB->hdr.smb_buf_length += count;
1976 pSMB->ByteCount = cpu_to_le16(count);
1977
1978 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1979 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001980 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00001981 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001982 cFYI(1, "Send error in rename = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 cifs_buf_release(pSMB);
1985
1986 if (rc == -EAGAIN)
1987 goto renameRetry;
1988
1989 return rc;
1990}
1991
Steve French50c2f752007-07-13 00:33:32 +00001992int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
Jeff Layton391e5752008-09-24 11:32:59 -04001993 int netfid, const char *target_name,
Steve French50c2f752007-07-13 00:33:32 +00001994 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995{
1996 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1997 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00001998 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 char *data_offset;
2000 char dummy_string[30];
2001 int rc = 0;
2002 int bytes_returned = 0;
2003 int len_of_str;
2004 __u16 params, param_offset, offset, count, byte_count;
2005
Joe Perchesb6b38f72010-04-21 03:50:45 +00002006 cFYI(1, "Rename to File by handle");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
2008 (void **) &pSMBr);
2009 if (rc)
2010 return rc;
2011
2012 params = 6;
2013 pSMB->MaxSetupCount = 0;
2014 pSMB->Reserved = 0;
2015 pSMB->Flags = 0;
2016 pSMB->Timeout = 0;
2017 pSMB->Reserved2 = 0;
2018 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2019 offset = param_offset + params;
2020
2021 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2022 rename_info = (struct set_file_rename *) data_offset;
2023 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002024 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 pSMB->SetupCount = 1;
2026 pSMB->Reserved3 = 0;
2027 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2028 byte_count = 3 /* pad */ + params;
2029 pSMB->ParameterCount = cpu_to_le16(params);
2030 pSMB->TotalParameterCount = pSMB->ParameterCount;
2031 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2032 pSMB->DataOffset = cpu_to_le16(offset);
2033 /* construct random name ".cifs_tmp<inodenum><mid>" */
2034 rename_info->overwrite = cpu_to_le32(1);
2035 rename_info->root_fid = 0;
2036 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002037 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002038 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2039 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002040 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002042 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002043 target_name, PATH_MAX, nls_codepage,
2044 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045 }
2046 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
Jeff Layton391e5752008-09-24 11:32:59 -04002047 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048 byte_count += count;
2049 pSMB->DataCount = cpu_to_le16(count);
2050 pSMB->TotalDataCount = pSMB->DataCount;
2051 pSMB->Fid = netfid;
2052 pSMB->InformationLevel =
2053 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2054 pSMB->Reserved4 = 0;
2055 pSMB->hdr.smb_buf_length += byte_count;
2056 pSMB->ByteCount = cpu_to_le16(byte_count);
2057 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002058 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002059 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002060 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002061 cFYI(1, "Send error in Rename (by file handle) = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -07002062
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 cifs_buf_release(pSMB);
2064
2065 /* Note: On -EAGAIN error only caller can retry on handle based calls
2066 since file handle passed in no longer valid */
2067
2068 return rc;
2069}
2070
2071int
Steve French50c2f752007-07-13 00:33:32 +00002072CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2073 const __u16 target_tid, const char *toName, const int flags,
2074 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075{
2076 int rc = 0;
2077 COPY_REQ *pSMB = NULL;
2078 COPY_RSP *pSMBr = NULL;
2079 int bytes_returned;
2080 int name_len, name_len2;
2081 __u16 count;
2082
Joe Perchesb6b38f72010-04-21 03:50:45 +00002083 cFYI(1, "In CIFSSMBCopy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084copyRetry:
2085 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2086 (void **) &pSMBr);
2087 if (rc)
2088 return rc;
2089
2090 pSMB->BufferFormat = 0x04;
2091 pSMB->Tid2 = target_tid;
2092
2093 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2094
2095 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002096 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002097 fromName, PATH_MAX, nls_codepage,
2098 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 name_len++; /* trailing null */
2100 name_len *= 2;
2101 pSMB->OldFileName[name_len] = 0x04; /* pad */
2102 /* protocol requires ASCII signature byte on Unicode string */
2103 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002104 name_len2 =
2105 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002106 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2108 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002109 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 name_len = strnlen(fromName, PATH_MAX);
2111 name_len++; /* trailing null */
2112 strncpy(pSMB->OldFileName, fromName, name_len);
2113 name_len2 = strnlen(toName, PATH_MAX);
2114 name_len2++; /* trailing null */
2115 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2116 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2117 name_len2++; /* trailing null */
2118 name_len2++; /* signature byte */
2119 }
2120
2121 count = 1 /* 1st signature byte */ + name_len + name_len2;
2122 pSMB->hdr.smb_buf_length += count;
2123 pSMB->ByteCount = cpu_to_le16(count);
2124
2125 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2126 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2127 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002128 cFYI(1, "Send error in copy = %d with %d files copied",
2129 rc, le16_to_cpu(pSMBr->CopyCount));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 }
Steve French0d817bc2008-05-22 02:02:03 +00002131 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132
2133 if (rc == -EAGAIN)
2134 goto copyRetry;
2135
2136 return rc;
2137}
2138
2139int
2140CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2141 const char *fromName, const char *toName,
2142 const struct nls_table *nls_codepage)
2143{
2144 TRANSACTION2_SPI_REQ *pSMB = NULL;
2145 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2146 char *data_offset;
2147 int name_len;
2148 int name_len_target;
2149 int rc = 0;
2150 int bytes_returned = 0;
2151 __u16 params, param_offset, offset, byte_count;
2152
Joe Perchesb6b38f72010-04-21 03:50:45 +00002153 cFYI(1, "In Symlink Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154createSymLinkRetry:
2155 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2156 (void **) &pSMBr);
2157 if (rc)
2158 return rc;
2159
2160 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2161 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002162 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 /* find define for this maxpathcomponent */
2164 , nls_codepage);
2165 name_len++; /* trailing null */
2166 name_len *= 2;
2167
Steve French50c2f752007-07-13 00:33:32 +00002168 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169 name_len = strnlen(fromName, PATH_MAX);
2170 name_len++; /* trailing null */
2171 strncpy(pSMB->FileName, fromName, name_len);
2172 }
2173 params = 6 + name_len;
2174 pSMB->MaxSetupCount = 0;
2175 pSMB->Reserved = 0;
2176 pSMB->Flags = 0;
2177 pSMB->Timeout = 0;
2178 pSMB->Reserved2 = 0;
2179 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002180 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181 offset = param_offset + params;
2182
2183 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2184 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2185 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002186 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187 /* find define for this maxpathcomponent */
2188 , nls_codepage);
2189 name_len_target++; /* trailing null */
2190 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002191 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 name_len_target = strnlen(toName, PATH_MAX);
2193 name_len_target++; /* trailing null */
2194 strncpy(data_offset, toName, name_len_target);
2195 }
2196
2197 pSMB->MaxParameterCount = cpu_to_le16(2);
2198 /* BB find exact max on data count below from sess */
2199 pSMB->MaxDataCount = cpu_to_le16(1000);
2200 pSMB->SetupCount = 1;
2201 pSMB->Reserved3 = 0;
2202 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2203 byte_count = 3 /* pad */ + params + name_len_target;
2204 pSMB->DataCount = cpu_to_le16(name_len_target);
2205 pSMB->ParameterCount = cpu_to_le16(params);
2206 pSMB->TotalDataCount = pSMB->DataCount;
2207 pSMB->TotalParameterCount = pSMB->ParameterCount;
2208 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2209 pSMB->DataOffset = cpu_to_le16(offset);
2210 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2211 pSMB->Reserved4 = 0;
2212 pSMB->hdr.smb_buf_length += byte_count;
2213 pSMB->ByteCount = cpu_to_le16(byte_count);
2214 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2215 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002216 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002217 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002218 cFYI(1, "Send error in SetPathInfo create symlink = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219
Steve French0d817bc2008-05-22 02:02:03 +00002220 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221
2222 if (rc == -EAGAIN)
2223 goto createSymLinkRetry;
2224
2225 return rc;
2226}
2227
2228int
2229CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2230 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002231 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232{
2233 TRANSACTION2_SPI_REQ *pSMB = NULL;
2234 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2235 char *data_offset;
2236 int name_len;
2237 int name_len_target;
2238 int rc = 0;
2239 int bytes_returned = 0;
2240 __u16 params, param_offset, offset, byte_count;
2241
Joe Perchesb6b38f72010-04-21 03:50:45 +00002242 cFYI(1, "In Create Hard link Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243createHardLinkRetry:
2244 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2245 (void **) &pSMBr);
2246 if (rc)
2247 return rc;
2248
2249 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002250 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002251 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 name_len++; /* trailing null */
2253 name_len *= 2;
2254
Steve French50c2f752007-07-13 00:33:32 +00002255 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256 name_len = strnlen(toName, PATH_MAX);
2257 name_len++; /* trailing null */
2258 strncpy(pSMB->FileName, toName, name_len);
2259 }
2260 params = 6 + name_len;
2261 pSMB->MaxSetupCount = 0;
2262 pSMB->Reserved = 0;
2263 pSMB->Flags = 0;
2264 pSMB->Timeout = 0;
2265 pSMB->Reserved2 = 0;
2266 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002267 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268 offset = param_offset + params;
2269
2270 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2271 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2272 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002273 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002274 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275 name_len_target++; /* trailing null */
2276 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002277 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278 name_len_target = strnlen(fromName, PATH_MAX);
2279 name_len_target++; /* trailing null */
2280 strncpy(data_offset, fromName, name_len_target);
2281 }
2282
2283 pSMB->MaxParameterCount = cpu_to_le16(2);
2284 /* BB find exact max on data count below from sess*/
2285 pSMB->MaxDataCount = cpu_to_le16(1000);
2286 pSMB->SetupCount = 1;
2287 pSMB->Reserved3 = 0;
2288 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2289 byte_count = 3 /* pad */ + params + name_len_target;
2290 pSMB->ParameterCount = cpu_to_le16(params);
2291 pSMB->TotalParameterCount = pSMB->ParameterCount;
2292 pSMB->DataCount = cpu_to_le16(name_len_target);
2293 pSMB->TotalDataCount = pSMB->DataCount;
2294 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2295 pSMB->DataOffset = cpu_to_le16(offset);
2296 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2297 pSMB->Reserved4 = 0;
2298 pSMB->hdr.smb_buf_length += byte_count;
2299 pSMB->ByteCount = cpu_to_le16(byte_count);
2300 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2301 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002302 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002303 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002304 cFYI(1, "Send error in SetPathInfo (hard link) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305
2306 cifs_buf_release(pSMB);
2307 if (rc == -EAGAIN)
2308 goto createHardLinkRetry;
2309
2310 return rc;
2311}
2312
2313int
2314CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2315 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002316 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317{
2318 int rc = 0;
2319 NT_RENAME_REQ *pSMB = NULL;
2320 RENAME_RSP *pSMBr = NULL;
2321 int bytes_returned;
2322 int name_len, name_len2;
2323 __u16 count;
2324
Joe Perchesb6b38f72010-04-21 03:50:45 +00002325 cFYI(1, "In CIFSCreateHardLink");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326winCreateHardLinkRetry:
2327
2328 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2329 (void **) &pSMBr);
2330 if (rc)
2331 return rc;
2332
2333 pSMB->SearchAttributes =
2334 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2335 ATTR_DIRECTORY);
2336 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2337 pSMB->ClusterCount = 0;
2338
2339 pSMB->BufferFormat = 0x04;
2340
2341 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2342 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002343 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002344 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345 name_len++; /* trailing null */
2346 name_len *= 2;
Jeff Laytonfcc7c092009-02-28 12:59:03 -05002347
2348 /* protocol specifies ASCII buffer format (0x04) for unicode */
2349 pSMB->OldFileName[name_len] = 0x04;
2350 pSMB->OldFileName[name_len + 1] = 0x00; /* pad */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002352 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002353 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2355 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002356 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357 name_len = strnlen(fromName, PATH_MAX);
2358 name_len++; /* trailing null */
2359 strncpy(pSMB->OldFileName, fromName, name_len);
2360 name_len2 = strnlen(toName, PATH_MAX);
2361 name_len2++; /* trailing null */
2362 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2363 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2364 name_len2++; /* trailing null */
2365 name_len2++; /* signature byte */
2366 }
2367
2368 count = 1 /* string type byte */ + name_len + name_len2;
2369 pSMB->hdr.smb_buf_length += count;
2370 pSMB->ByteCount = cpu_to_le16(count);
2371
2372 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2373 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002374 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002375 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002376 cFYI(1, "Send error in hard link (NT rename) = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00002377
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378 cifs_buf_release(pSMB);
2379 if (rc == -EAGAIN)
2380 goto winCreateHardLinkRetry;
2381
2382 return rc;
2383}
2384
2385int
2386CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
Jeff Layton460b9692009-04-30 07:17:56 -04002387 const unsigned char *searchName, char **symlinkinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388 const struct nls_table *nls_codepage)
2389{
2390/* SMB_QUERY_FILE_UNIX_LINK */
2391 TRANSACTION2_QPI_REQ *pSMB = NULL;
2392 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2393 int rc = 0;
2394 int bytes_returned;
2395 int name_len;
2396 __u16 params, byte_count;
Jeff Layton460b9692009-04-30 07:17:56 -04002397 char *data_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398
Joe Perchesb6b38f72010-04-21 03:50:45 +00002399 cFYI(1, "In QPathSymLinkInfo (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400
2401querySymLinkRetry:
2402 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2403 (void **) &pSMBr);
2404 if (rc)
2405 return rc;
2406
2407 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2408 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002409 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2410 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411 name_len++; /* trailing null */
2412 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002413 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002414 name_len = strnlen(searchName, PATH_MAX);
2415 name_len++; /* trailing null */
2416 strncpy(pSMB->FileName, searchName, name_len);
2417 }
2418
2419 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2420 pSMB->TotalDataCount = 0;
2421 pSMB->MaxParameterCount = cpu_to_le16(2);
Jeff Layton46a75742009-05-24 18:45:17 -04002422 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 pSMB->MaxSetupCount = 0;
2424 pSMB->Reserved = 0;
2425 pSMB->Flags = 0;
2426 pSMB->Timeout = 0;
2427 pSMB->Reserved2 = 0;
2428 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002429 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002430 pSMB->DataCount = 0;
2431 pSMB->DataOffset = 0;
2432 pSMB->SetupCount = 1;
2433 pSMB->Reserved3 = 0;
2434 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2435 byte_count = params + 1 /* pad */ ;
2436 pSMB->TotalParameterCount = cpu_to_le16(params);
2437 pSMB->ParameterCount = pSMB->TotalParameterCount;
2438 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2439 pSMB->Reserved4 = 0;
2440 pSMB->hdr.smb_buf_length += byte_count;
2441 pSMB->ByteCount = cpu_to_le16(byte_count);
2442
2443 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2444 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2445 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002446 cFYI(1, "Send error in QuerySymLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 } else {
2448 /* decode response */
2449
2450 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451 /* BB also check enough total bytes returned */
Jeff Layton460b9692009-04-30 07:17:56 -04002452 if (rc || (pSMBr->ByteCount < 2))
2453 rc = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454 else {
Steve French0e0d2cf2009-05-01 05:27:32 +00002455 bool is_unicode;
Jeff Layton460b9692009-04-30 07:17:56 -04002456 u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457
Jeff Layton460b9692009-04-30 07:17:56 -04002458 data_start = ((char *) &pSMBr->hdr.Protocol) +
2459 le16_to_cpu(pSMBr->t2.DataOffset);
2460
Steve French0e0d2cf2009-05-01 05:27:32 +00002461 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
2462 is_unicode = true;
2463 else
2464 is_unicode = false;
2465
Steve French737b7582005-04-28 22:41:06 -07002466 /* BB FIXME investigate remapping reserved chars here */
Steve Frenchd185cda2009-04-30 17:45:10 +00002467 *symlinkinfo = cifs_strndup_from_ucs(data_start, count,
Steve French0e0d2cf2009-05-01 05:27:32 +00002468 is_unicode, nls_codepage);
Jeff Layton8b6427a2009-05-19 09:57:03 -04002469 if (!*symlinkinfo)
Jeff Layton460b9692009-04-30 07:17:56 -04002470 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471 }
2472 }
2473 cifs_buf_release(pSMB);
2474 if (rc == -EAGAIN)
2475 goto querySymLinkRetry;
2476 return rc;
2477}
2478
Parag Warudkarc9489772007-10-23 18:09:48 +00002479#ifdef CONFIG_CIFS_EXPERIMENTAL
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480int
2481CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2482 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002483 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484 const struct nls_table *nls_codepage)
2485{
2486 int rc = 0;
2487 int bytes_returned;
Steve French50c2f752007-07-13 00:33:32 +00002488 struct smb_com_transaction_ioctl_req *pSMB;
2489 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490
Joe Perchesb6b38f72010-04-21 03:50:45 +00002491 cFYI(1, "In Windows reparse style QueryLink for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2493 (void **) &pSMBr);
2494 if (rc)
2495 return rc;
2496
2497 pSMB->TotalParameterCount = 0 ;
2498 pSMB->TotalDataCount = 0;
2499 pSMB->MaxParameterCount = cpu_to_le32(2);
2500 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002501 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2502 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503 pSMB->MaxSetupCount = 4;
2504 pSMB->Reserved = 0;
2505 pSMB->ParameterOffset = 0;
2506 pSMB->DataCount = 0;
2507 pSMB->DataOffset = 0;
2508 pSMB->SetupCount = 4;
2509 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2510 pSMB->ParameterCount = pSMB->TotalParameterCount;
2511 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2512 pSMB->IsFsctl = 1; /* FSCTL */
2513 pSMB->IsRootFlag = 0;
2514 pSMB->Fid = fid; /* file handle always le */
2515 pSMB->ByteCount = 0;
2516
2517 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2518 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2519 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002520 cFYI(1, "Send error in QueryReparseLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521 } else { /* decode response */
2522 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2523 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
Steve Frenchafe48c32009-05-02 05:25:46 +00002524 if ((pSMBr->ByteCount < 2) || (data_offset > 512)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525 /* BB also check enough total bytes returned */
2526 rc = -EIO; /* bad smb */
Steve Frenchafe48c32009-05-02 05:25:46 +00002527 goto qreparse_out;
2528 }
2529 if (data_count && (data_count < 2048)) {
2530 char *end_of_smb = 2 /* sizeof byte count */ +
2531 pSMBr->ByteCount + (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532
Steve Frenchafe48c32009-05-02 05:25:46 +00002533 struct reparse_data *reparse_buf =
Steve French50c2f752007-07-13 00:33:32 +00002534 (struct reparse_data *)
2535 ((char *)&pSMBr->hdr.Protocol
2536 + data_offset);
Steve Frenchafe48c32009-05-02 05:25:46 +00002537 if ((char *)reparse_buf >= end_of_smb) {
2538 rc = -EIO;
2539 goto qreparse_out;
2540 }
2541 if ((reparse_buf->LinkNamesBuf +
2542 reparse_buf->TargetNameOffset +
2543 reparse_buf->TargetNameLen) > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002544 cFYI(1, "reparse buf beyond SMB");
Steve Frenchafe48c32009-05-02 05:25:46 +00002545 rc = -EIO;
2546 goto qreparse_out;
2547 }
Steve French50c2f752007-07-13 00:33:32 +00002548
Steve Frenchafe48c32009-05-02 05:25:46 +00002549 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2550 cifs_from_ucs2(symlinkinfo, (__le16 *)
Steve French50c2f752007-07-13 00:33:32 +00002551 (reparse_buf->LinkNamesBuf +
2552 reparse_buf->TargetNameOffset),
Steve Frenchafe48c32009-05-02 05:25:46 +00002553 buflen,
2554 reparse_buf->TargetNameLen,
2555 nls_codepage, 0);
2556 } else { /* ASCII names */
2557 strncpy(symlinkinfo,
2558 reparse_buf->LinkNamesBuf +
2559 reparse_buf->TargetNameOffset,
2560 min_t(const int, buflen,
2561 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002563 } else {
2564 rc = -EIO;
Joe Perchesb6b38f72010-04-21 03:50:45 +00002565 cFYI(1, "Invalid return data count on "
2566 "get reparse info ioctl");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002568 symlinkinfo[buflen] = 0; /* just in case so the caller
2569 does not go off the end of the buffer */
Joe Perchesb6b38f72010-04-21 03:50:45 +00002570 cFYI(1, "readlink result - %s", symlinkinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571 }
Steve French989c7e52009-05-02 05:32:20 +00002572
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002574 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575
2576 /* Note: On -EAGAIN error only caller can retry on handle based calls
2577 since file handle passed in no longer valid */
2578
2579 return rc;
2580}
Steve Frenchafe48c32009-05-02 05:25:46 +00002581#endif /* CIFS_EXPERIMENTAL */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582
2583#ifdef CONFIG_CIFS_POSIX
2584
2585/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002586static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2587 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588{
2589 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002590 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2591 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2592 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002593 /* cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594
2595 return;
2596}
2597
2598/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002599static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2600 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601{
2602 int size = 0;
2603 int i;
2604 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002605 struct cifs_posix_ace *pACE;
2606 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2607 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608
2609 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2610 return -EOPNOTSUPP;
2611
Steve French790fe572007-07-07 19:25:05 +00002612 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613 count = le16_to_cpu(cifs_acl->access_entry_count);
2614 pACE = &cifs_acl->ace_array[0];
2615 size = sizeof(struct cifs_posix_acl);
2616 size += sizeof(struct cifs_posix_ace) * count;
2617 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002618 if (size_of_data_area < size) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002619 cFYI(1, "bad CIFS POSIX ACL size %d vs. %d",
2620 size_of_data_area, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621 return -EINVAL;
2622 }
Steve French790fe572007-07-07 19:25:05 +00002623 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624 count = le16_to_cpu(cifs_acl->access_entry_count);
2625 size = sizeof(struct cifs_posix_acl);
2626 size += sizeof(struct cifs_posix_ace) * count;
2627/* skip past access ACEs to get to default ACEs */
2628 pACE = &cifs_acl->ace_array[count];
2629 count = le16_to_cpu(cifs_acl->default_entry_count);
2630 size += sizeof(struct cifs_posix_ace) * count;
2631 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002632 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633 return -EINVAL;
2634 } else {
2635 /* illegal type */
2636 return -EINVAL;
2637 }
2638
2639 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002640 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002641 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002642 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643 return -ERANGE;
2644 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002645 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002646 for (i = 0; i < count ; i++) {
2647 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2648 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002649 }
2650 }
2651 return size;
2652}
2653
Steve French50c2f752007-07-13 00:33:32 +00002654static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2655 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656{
2657 __u16 rc = 0; /* 0 = ACL converted ok */
2658
Steve Frenchff7feac2005-11-15 16:45:16 -08002659 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2660 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002662 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663 /* Probably no need to le convert -1 on any arch but can not hurt */
2664 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002665 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002666 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002667 /*cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id);*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668 return rc;
2669}
2670
2671/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002672static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2673 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002674{
2675 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002676 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2677 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678 int count;
2679 int i;
2680
Steve French790fe572007-07-07 19:25:05 +00002681 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682 return 0;
2683
2684 count = posix_acl_xattr_count((size_t)buflen);
Joe Perchesb6b38f72010-04-21 03:50:45 +00002685 cFYI(1, "setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002686 "version of %d",
Joe Perchesb6b38f72010-04-21 03:50:45 +00002687 count, buflen, le32_to_cpu(local_acl->a_version));
Steve French790fe572007-07-07 19:25:05 +00002688 if (le32_to_cpu(local_acl->a_version) != 2) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002689 cFYI(1, "unknown POSIX ACL version %d",
2690 le32_to_cpu(local_acl->a_version));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002691 return 0;
2692 }
2693 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002694 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002695 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002696 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002697 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002699 cFYI(1, "unknown ACL type %d", acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700 return 0;
2701 }
Steve French50c2f752007-07-13 00:33:32 +00002702 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2704 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002705 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706 /* ACE not converted */
2707 break;
2708 }
2709 }
Steve French790fe572007-07-07 19:25:05 +00002710 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2712 rc += sizeof(struct cifs_posix_acl);
2713 /* BB add check to make sure ACL does not overflow SMB */
2714 }
2715 return rc;
2716}
2717
2718int
2719CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002720 const unsigned char *searchName,
2721 char *acl_inf, const int buflen, const int acl_type,
2722 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723{
2724/* SMB_QUERY_POSIX_ACL */
2725 TRANSACTION2_QPI_REQ *pSMB = NULL;
2726 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2727 int rc = 0;
2728 int bytes_returned;
2729 int name_len;
2730 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002731
Joe Perchesb6b38f72010-04-21 03:50:45 +00002732 cFYI(1, "In GetPosixACL (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733
2734queryAclRetry:
2735 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2736 (void **) &pSMBr);
2737 if (rc)
2738 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002739
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2741 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002742 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002743 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002744 name_len++; /* trailing null */
2745 name_len *= 2;
2746 pSMB->FileName[name_len] = 0;
2747 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002748 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749 name_len = strnlen(searchName, PATH_MAX);
2750 name_len++; /* trailing null */
2751 strncpy(pSMB->FileName, searchName, name_len);
2752 }
2753
2754 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2755 pSMB->TotalDataCount = 0;
2756 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002757 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758 pSMB->MaxDataCount = cpu_to_le16(4000);
2759 pSMB->MaxSetupCount = 0;
2760 pSMB->Reserved = 0;
2761 pSMB->Flags = 0;
2762 pSMB->Timeout = 0;
2763 pSMB->Reserved2 = 0;
2764 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002765 offsetof(struct smb_com_transaction2_qpi_req,
2766 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 pSMB->DataCount = 0;
2768 pSMB->DataOffset = 0;
2769 pSMB->SetupCount = 1;
2770 pSMB->Reserved3 = 0;
2771 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2772 byte_count = params + 1 /* pad */ ;
2773 pSMB->TotalParameterCount = cpu_to_le16(params);
2774 pSMB->ParameterCount = pSMB->TotalParameterCount;
2775 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2776 pSMB->Reserved4 = 0;
2777 pSMB->hdr.smb_buf_length += byte_count;
2778 pSMB->ByteCount = cpu_to_le16(byte_count);
2779
2780 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2781 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002782 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002784 cFYI(1, "Send error in Query POSIX ACL = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 } else {
2786 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002787
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2789 if (rc || (pSMBr->ByteCount < 2))
2790 /* BB also check enough total bytes returned */
2791 rc = -EIO; /* bad smb */
2792 else {
2793 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2794 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2795 rc = cifs_copy_posix_acl(acl_inf,
2796 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002797 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 }
2799 }
2800 cifs_buf_release(pSMB);
2801 if (rc == -EAGAIN)
2802 goto queryAclRetry;
2803 return rc;
2804}
2805
2806int
2807CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002808 const unsigned char *fileName,
2809 const char *local_acl, const int buflen,
2810 const int acl_type,
2811 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812{
2813 struct smb_com_transaction2_spi_req *pSMB = NULL;
2814 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2815 char *parm_data;
2816 int name_len;
2817 int rc = 0;
2818 int bytes_returned = 0;
2819 __u16 params, byte_count, data_count, param_offset, offset;
2820
Joe Perchesb6b38f72010-04-21 03:50:45 +00002821 cFYI(1, "In SetPosixACL (Unix) for path %s", fileName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822setAclRetry:
2823 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002824 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 if (rc)
2826 return rc;
2827 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2828 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002829 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002830 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831 name_len++; /* trailing null */
2832 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002833 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834 name_len = strnlen(fileName, PATH_MAX);
2835 name_len++; /* trailing null */
2836 strncpy(pSMB->FileName, fileName, name_len);
2837 }
2838 params = 6 + name_len;
2839 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00002840 /* BB find max SMB size from sess */
2841 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842 pSMB->MaxSetupCount = 0;
2843 pSMB->Reserved = 0;
2844 pSMB->Flags = 0;
2845 pSMB->Timeout = 0;
2846 pSMB->Reserved2 = 0;
2847 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002848 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849 offset = param_offset + params;
2850 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2851 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2852
2853 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002854 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855
Steve French790fe572007-07-07 19:25:05 +00002856 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857 rc = -EOPNOTSUPP;
2858 goto setACLerrorExit;
2859 }
2860 pSMB->DataOffset = cpu_to_le16(offset);
2861 pSMB->SetupCount = 1;
2862 pSMB->Reserved3 = 0;
2863 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2864 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2865 byte_count = 3 /* pad */ + params + data_count;
2866 pSMB->DataCount = cpu_to_le16(data_count);
2867 pSMB->TotalDataCount = pSMB->DataCount;
2868 pSMB->ParameterCount = cpu_to_le16(params);
2869 pSMB->TotalParameterCount = pSMB->ParameterCount;
2870 pSMB->Reserved4 = 0;
2871 pSMB->hdr.smb_buf_length += byte_count;
2872 pSMB->ByteCount = cpu_to_le16(byte_count);
2873 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002874 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00002875 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002876 cFYI(1, "Set POSIX ACL returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877
2878setACLerrorExit:
2879 cifs_buf_release(pSMB);
2880 if (rc == -EAGAIN)
2881 goto setAclRetry;
2882 return rc;
2883}
2884
Steve Frenchf654bac2005-04-28 22:41:04 -07002885/* BB fix tabs in this function FIXME BB */
2886int
2887CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00002888 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07002889{
Steve French50c2f752007-07-13 00:33:32 +00002890 int rc = 0;
2891 struct smb_t2_qfi_req *pSMB = NULL;
2892 struct smb_t2_qfi_rsp *pSMBr = NULL;
2893 int bytes_returned;
2894 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07002895
Joe Perchesb6b38f72010-04-21 03:50:45 +00002896 cFYI(1, "In GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00002897 if (tcon == NULL)
2898 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07002899
2900GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00002901 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2902 (void **) &pSMBr);
2903 if (rc)
2904 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07002905
Steve Frenchad7a2922008-02-07 23:25:02 +00002906 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00002907 pSMB->t2.TotalDataCount = 0;
2908 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
2909 /* BB find exact max data count below from sess structure BB */
2910 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
2911 pSMB->t2.MaxSetupCount = 0;
2912 pSMB->t2.Reserved = 0;
2913 pSMB->t2.Flags = 0;
2914 pSMB->t2.Timeout = 0;
2915 pSMB->t2.Reserved2 = 0;
2916 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
2917 Fid) - 4);
2918 pSMB->t2.DataCount = 0;
2919 pSMB->t2.DataOffset = 0;
2920 pSMB->t2.SetupCount = 1;
2921 pSMB->t2.Reserved3 = 0;
2922 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
2923 byte_count = params + 1 /* pad */ ;
2924 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
2925 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
2926 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
2927 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07002928 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00002929 pSMB->hdr.smb_buf_length += byte_count;
2930 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07002931
Steve French790fe572007-07-07 19:25:05 +00002932 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2933 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2934 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002935 cFYI(1, "error %d in GetExtAttr", rc);
Steve French790fe572007-07-07 19:25:05 +00002936 } else {
2937 /* decode response */
2938 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2939 if (rc || (pSMBr->ByteCount < 2))
2940 /* BB also check enough total bytes returned */
2941 /* If rc should we check for EOPNOSUPP and
2942 disable the srvino flag? or in caller? */
2943 rc = -EIO; /* bad smb */
2944 else {
2945 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2946 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2947 struct file_chattr_info *pfinfo;
2948 /* BB Do we need a cast or hash here ? */
2949 if (count != 16) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002950 cFYI(1, "Illegal size ret in GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00002951 rc = -EIO;
2952 goto GetExtAttrOut;
2953 }
2954 pfinfo = (struct file_chattr_info *)
2955 (data_offset + (char *) &pSMBr->hdr.Protocol);
2956 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07002957 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00002958 }
2959 }
Steve Frenchf654bac2005-04-28 22:41:04 -07002960GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00002961 cifs_buf_release(pSMB);
2962 if (rc == -EAGAIN)
2963 goto GetExtAttrRetry;
2964 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07002965}
2966
Steve Frenchf654bac2005-04-28 22:41:04 -07002967#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968
Jeff Layton79df1ba2010-12-06 12:52:08 -05002969#ifdef CONFIG_CIFS_ACL
2970/*
2971 * Initialize NT TRANSACT SMB into small smb request buffer. This assumes that
2972 * all NT TRANSACTS that we init here have total parm and data under about 400
2973 * bytes (to fit in small cifs buffer size), which is the case so far, it
2974 * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of
2975 * returned setup area) and MaxParameterCount (returned parms size) must be set
2976 * by caller
2977 */
2978static int
2979smb_init_nttransact(const __u16 sub_command, const int setup_count,
2980 const int parm_len, struct cifsTconInfo *tcon,
2981 void **ret_buf)
2982{
2983 int rc;
2984 __u32 temp_offset;
2985 struct smb_com_ntransact_req *pSMB;
2986
2987 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
2988 (void **)&pSMB);
2989 if (rc)
2990 return rc;
2991 *ret_buf = (void *)pSMB;
2992 pSMB->Reserved = 0;
2993 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
2994 pSMB->TotalDataCount = 0;
2995 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2996 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
2997 pSMB->ParameterCount = pSMB->TotalParameterCount;
2998 pSMB->DataCount = pSMB->TotalDataCount;
2999 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
3000 (setup_count * 2) - 4 /* for rfc1001 length itself */;
3001 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
3002 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
3003 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
3004 pSMB->SubCommand = cpu_to_le16(sub_command);
3005 return 0;
3006}
3007
3008static int
3009validate_ntransact(char *buf, char **ppparm, char **ppdata,
3010 __u32 *pparmlen, __u32 *pdatalen)
3011{
3012 char *end_of_smb;
3013 __u32 data_count, data_offset, parm_count, parm_offset;
3014 struct smb_com_ntransact_rsp *pSMBr;
3015
3016 *pdatalen = 0;
3017 *pparmlen = 0;
3018
3019 if (buf == NULL)
3020 return -EINVAL;
3021
3022 pSMBr = (struct smb_com_ntransact_rsp *)buf;
3023
3024 /* ByteCount was converted from little endian in SendReceive */
3025 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
3026 (char *)&pSMBr->ByteCount;
3027
3028 data_offset = le32_to_cpu(pSMBr->DataOffset);
3029 data_count = le32_to_cpu(pSMBr->DataCount);
3030 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
3031 parm_count = le32_to_cpu(pSMBr->ParameterCount);
3032
3033 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
3034 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
3035
3036 /* should we also check that parm and data areas do not overlap? */
3037 if (*ppparm > end_of_smb) {
3038 cFYI(1, "parms start after end of smb");
3039 return -EINVAL;
3040 } else if (parm_count + *ppparm > end_of_smb) {
3041 cFYI(1, "parm end after end of smb");
3042 return -EINVAL;
3043 } else if (*ppdata > end_of_smb) {
3044 cFYI(1, "data starts after end of smb");
3045 return -EINVAL;
3046 } else if (data_count + *ppdata > end_of_smb) {
3047 cFYI(1, "data %p + count %d (%p) past smb end %p start %p",
3048 *ppdata, data_count, (data_count + *ppdata),
3049 end_of_smb, pSMBr);
3050 return -EINVAL;
3051 } else if (parm_count + data_count > pSMBr->ByteCount) {
3052 cFYI(1, "parm count and data count larger than SMB");
3053 return -EINVAL;
3054 }
3055 *pdatalen = data_count;
3056 *pparmlen = parm_count;
3057 return 0;
3058}
3059
Steve French0a4b92c2006-01-12 15:44:21 -08003060/* Get Security Descriptor (by handle) from remote server for a file or dir */
3061int
3062CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f0c2007-10-25 21:17:17 +00003063 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003064{
3065 int rc = 0;
3066 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003067 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003068 struct kvec iov[1];
3069
Joe Perchesb6b38f72010-04-21 03:50:45 +00003070 cFYI(1, "GetCifsACL");
Steve French0a4b92c2006-01-12 15:44:21 -08003071
Steve French630f3f0c2007-10-25 21:17:17 +00003072 *pbuflen = 0;
3073 *acl_inf = NULL;
3074
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003075 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003076 8 /* parm len */, tcon, (void **) &pSMB);
3077 if (rc)
3078 return rc;
3079
3080 pSMB->MaxParameterCount = cpu_to_le32(4);
3081 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3082 pSMB->MaxSetupCount = 0;
3083 pSMB->Fid = fid; /* file handle always le */
3084 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3085 CIFS_ACL_DACL);
3086 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3087 pSMB->hdr.smb_buf_length += 11;
3088 iov[0].iov_base = (char *)pSMB;
3089 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3090
Steve Frencha761ac52007-10-18 21:45:27 +00003091 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Steve French133672e2007-11-13 22:41:37 +00003092 CIFS_STD_OP);
Steve French0a4b92c2006-01-12 15:44:21 -08003093 cifs_stats_inc(&tcon->num_acl_get);
3094 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003095 cFYI(1, "Send error in QuerySecDesc = %d", rc);
Steve French0a4b92c2006-01-12 15:44:21 -08003096 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003097 __le32 *parm;
Steve French630f3f0c2007-10-25 21:17:17 +00003098 __u32 parm_len;
3099 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003100 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f0c2007-10-25 21:17:17 +00003101 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003102
3103/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003104 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f0c2007-10-25 21:17:17 +00003105 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003106 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003107 goto qsec_out;
3108 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3109
Joe Perchesb6b38f72010-04-21 03:50:45 +00003110 cFYI(1, "smb %p parm %p data %p", pSMBr, parm, *acl_inf);
Steve French0a4b92c2006-01-12 15:44:21 -08003111
3112 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3113 rc = -EIO; /* bad smb */
Steve French630f3f0c2007-10-25 21:17:17 +00003114 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003115 goto qsec_out;
3116 }
3117
3118/* BB check that data area is minimum length and as big as acl_len */
3119
Steve Frenchaf6f4612007-10-16 18:40:37 +00003120 acl_len = le32_to_cpu(*parm);
Steve French630f3f0c2007-10-25 21:17:17 +00003121 if (acl_len != *pbuflen) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003122 cERROR(1, "acl length %d does not match %d",
3123 acl_len, *pbuflen);
Steve French630f3f0c2007-10-25 21:17:17 +00003124 if (*pbuflen > acl_len)
3125 *pbuflen = acl_len;
3126 }
Steve French0a4b92c2006-01-12 15:44:21 -08003127
Steve French630f3f0c2007-10-25 21:17:17 +00003128 /* check if buffer is big enough for the acl
3129 header followed by the smallest SID */
3130 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3131 (*pbuflen >= 64 * 1024)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003132 cERROR(1, "bad acl length %d", *pbuflen);
Steve French630f3f0c2007-10-25 21:17:17 +00003133 rc = -EINVAL;
3134 *pbuflen = 0;
3135 } else {
3136 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3137 if (*acl_inf == NULL) {
3138 *pbuflen = 0;
3139 rc = -ENOMEM;
3140 }
3141 memcpy(*acl_inf, pdata, *pbuflen);
3142 }
Steve French0a4b92c2006-01-12 15:44:21 -08003143 }
3144qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003145 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003146 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003147 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003148 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003149/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003150 return rc;
3151}
Steve French97837582007-12-31 07:47:21 +00003152
3153int
3154CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3155 struct cifs_ntsd *pntsd, __u32 acllen)
3156{
3157 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3158 int rc = 0;
3159 int bytes_returned = 0;
3160 SET_SEC_DESC_REQ *pSMB = NULL;
3161 NTRANSACT_RSP *pSMBr = NULL;
3162
3163setCifsAclRetry:
3164 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3165 (void **) &pSMBr);
3166 if (rc)
3167 return (rc);
3168
3169 pSMB->MaxSetupCount = 0;
3170 pSMB->Reserved = 0;
3171
3172 param_count = 8;
3173 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3174 data_count = acllen;
3175 data_offset = param_offset + param_count;
3176 byte_count = 3 /* pad */ + param_count;
3177
3178 pSMB->DataCount = cpu_to_le32(data_count);
3179 pSMB->TotalDataCount = pSMB->DataCount;
3180 pSMB->MaxParameterCount = cpu_to_le32(4);
3181 pSMB->MaxDataCount = cpu_to_le32(16384);
3182 pSMB->ParameterCount = cpu_to_le32(param_count);
3183 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3184 pSMB->TotalParameterCount = pSMB->ParameterCount;
3185 pSMB->DataOffset = cpu_to_le32(data_offset);
3186 pSMB->SetupCount = 0;
3187 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3188 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3189
3190 pSMB->Fid = fid; /* file handle always le */
3191 pSMB->Reserved2 = 0;
3192 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3193
3194 if (pntsd && acllen) {
3195 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3196 (char *) pntsd,
3197 acllen);
3198 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3199
3200 } else
3201 pSMB->hdr.smb_buf_length += byte_count;
3202
3203 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3204 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3205
Joe Perchesb6b38f72010-04-21 03:50:45 +00003206 cFYI(1, "SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc);
Steve French97837582007-12-31 07:47:21 +00003207 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003208 cFYI(1, "Set CIFS ACL returned %d", rc);
Steve French97837582007-12-31 07:47:21 +00003209 cifs_buf_release(pSMB);
3210
3211 if (rc == -EAGAIN)
3212 goto setCifsAclRetry;
3213
3214 return (rc);
3215}
3216
Jeff Layton79df1ba2010-12-06 12:52:08 -05003217#endif /* CONFIG_CIFS_ACL */
Steve French0a4b92c2006-01-12 15:44:21 -08003218
Steve French6b8edfe2005-08-23 20:26:03 -07003219/* Legacy Query Path Information call for lookup to old servers such
3220 as Win9x/WinME */
3221int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003222 const unsigned char *searchName,
3223 FILE_ALL_INFO *pFinfo,
3224 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003225{
Steve Frenchad7a2922008-02-07 23:25:02 +00003226 QUERY_INFORMATION_REQ *pSMB;
3227 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003228 int rc = 0;
3229 int bytes_returned;
3230 int name_len;
3231
Joe Perchesb6b38f72010-04-21 03:50:45 +00003232 cFYI(1, "In SMBQPath path %s", searchName);
Steve French6b8edfe2005-08-23 20:26:03 -07003233QInfRetry:
3234 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003235 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003236 if (rc)
3237 return rc;
3238
3239 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3240 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003241 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3242 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003243 name_len++; /* trailing null */
3244 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003245 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003246 name_len = strnlen(searchName, PATH_MAX);
3247 name_len++; /* trailing null */
3248 strncpy(pSMB->FileName, searchName, name_len);
3249 }
3250 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003251 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003252 pSMB->hdr.smb_buf_length += (__u16) name_len;
3253 pSMB->ByteCount = cpu_to_le16(name_len);
3254
3255 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003256 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003257 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003258 cFYI(1, "Send error in QueryInfo = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003259 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003260 struct timespec ts;
3261 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003262
3263 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003264 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003265 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003266 ts.tv_nsec = 0;
3267 ts.tv_sec = time;
3268 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003269 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003270 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3271 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003272 pFinfo->AllocationSize =
3273 cpu_to_le64(le32_to_cpu(pSMBr->size));
3274 pFinfo->EndOfFile = pFinfo->AllocationSize;
3275 pFinfo->Attributes =
3276 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003277 } else
3278 rc = -EIO; /* bad buffer passed in */
3279
3280 cifs_buf_release(pSMB);
3281
3282 if (rc == -EAGAIN)
3283 goto QInfRetry;
3284
3285 return rc;
3286}
3287
Jeff Laytonbcd53572010-02-12 07:44:16 -05003288int
3289CIFSSMBQFileInfo(const int xid, struct cifsTconInfo *tcon,
3290 u16 netfid, FILE_ALL_INFO *pFindData)
3291{
3292 struct smb_t2_qfi_req *pSMB = NULL;
3293 struct smb_t2_qfi_rsp *pSMBr = NULL;
3294 int rc = 0;
3295 int bytes_returned;
3296 __u16 params, byte_count;
Steve French6b8edfe2005-08-23 20:26:03 -07003297
Jeff Laytonbcd53572010-02-12 07:44:16 -05003298QFileInfoRetry:
3299 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3300 (void **) &pSMBr);
3301 if (rc)
3302 return rc;
Steve French6b8edfe2005-08-23 20:26:03 -07003303
Jeff Laytonbcd53572010-02-12 07:44:16 -05003304 params = 2 /* level */ + 2 /* fid */;
3305 pSMB->t2.TotalDataCount = 0;
3306 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3307 /* BB find exact max data count below from sess structure BB */
3308 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3309 pSMB->t2.MaxSetupCount = 0;
3310 pSMB->t2.Reserved = 0;
3311 pSMB->t2.Flags = 0;
3312 pSMB->t2.Timeout = 0;
3313 pSMB->t2.Reserved2 = 0;
3314 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3315 Fid) - 4);
3316 pSMB->t2.DataCount = 0;
3317 pSMB->t2.DataOffset = 0;
3318 pSMB->t2.SetupCount = 1;
3319 pSMB->t2.Reserved3 = 0;
3320 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3321 byte_count = params + 1 /* pad */ ;
3322 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3323 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3324 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
3325 pSMB->Pad = 0;
3326 pSMB->Fid = netfid;
3327 pSMB->hdr.smb_buf_length += byte_count;
3328
3329 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3330 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3331 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003332 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonbcd53572010-02-12 07:44:16 -05003333 } else { /* decode response */
3334 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3335
3336 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3337 rc = -EIO;
3338 else if (pSMBr->ByteCount < 40)
3339 rc = -EIO; /* bad smb */
3340 else if (pFindData) {
3341 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3342 memcpy((char *) pFindData,
3343 (char *) &pSMBr->hdr.Protocol +
3344 data_offset, sizeof(FILE_ALL_INFO));
3345 } else
3346 rc = -ENOMEM;
3347 }
3348 cifs_buf_release(pSMB);
3349 if (rc == -EAGAIN)
3350 goto QFileInfoRetry;
3351
3352 return rc;
3353}
Steve French6b8edfe2005-08-23 20:26:03 -07003354
Linus Torvalds1da177e2005-04-16 15:20:36 -07003355int
3356CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3357 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003358 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003359 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003360 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003361{
3362/* level 263 SMB_QUERY_FILE_ALL_INFO */
3363 TRANSACTION2_QPI_REQ *pSMB = NULL;
3364 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3365 int rc = 0;
3366 int bytes_returned;
3367 int name_len;
3368 __u16 params, byte_count;
3369
Joe Perchesb6b38f72010-04-21 03:50:45 +00003370/* cFYI(1, "In QPathInfo path %s", searchName); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003371QPathInfoRetry:
3372 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3373 (void **) &pSMBr);
3374 if (rc)
3375 return rc;
3376
3377 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3378 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003379 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003380 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003381 name_len++; /* trailing null */
3382 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003383 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003384 name_len = strnlen(searchName, PATH_MAX);
3385 name_len++; /* trailing null */
3386 strncpy(pSMB->FileName, searchName, name_len);
3387 }
3388
Steve French50c2f752007-07-13 00:33:32 +00003389 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003390 pSMB->TotalDataCount = 0;
3391 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00003392 /* BB find exact max SMB PDU from sess structure BB */
3393 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003394 pSMB->MaxSetupCount = 0;
3395 pSMB->Reserved = 0;
3396 pSMB->Flags = 0;
3397 pSMB->Timeout = 0;
3398 pSMB->Reserved2 = 0;
3399 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003400 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003401 pSMB->DataCount = 0;
3402 pSMB->DataOffset = 0;
3403 pSMB->SetupCount = 1;
3404 pSMB->Reserved3 = 0;
3405 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3406 byte_count = params + 1 /* pad */ ;
3407 pSMB->TotalParameterCount = cpu_to_le16(params);
3408 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003409 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003410 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3411 else
3412 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003413 pSMB->Reserved4 = 0;
3414 pSMB->hdr.smb_buf_length += byte_count;
3415 pSMB->ByteCount = cpu_to_le16(byte_count);
3416
3417 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3418 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3419 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003420 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003421 } else { /* decode response */
3422 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3423
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003424 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3425 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003426 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003427 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003428 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003429 rc = -EIO; /* 24 or 26 expected but we do not read
3430 last field */
3431 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003432 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003433 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003434
3435 /* On legacy responses we do not read the last field,
3436 EAsize, fortunately since it varies by subdialect and
3437 also note it differs on Set vs. Get, ie two bytes or 4
3438 bytes depending but we don't care here */
3439 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003440 size = sizeof(FILE_INFO_STANDARD);
3441 else
3442 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003443 memcpy((char *) pFindData,
3444 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003445 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003446 } else
3447 rc = -ENOMEM;
3448 }
3449 cifs_buf_release(pSMB);
3450 if (rc == -EAGAIN)
3451 goto QPathInfoRetry;
3452
3453 return rc;
3454}
3455
3456int
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003457CIFSSMBUnixQFileInfo(const int xid, struct cifsTconInfo *tcon,
3458 u16 netfid, FILE_UNIX_BASIC_INFO *pFindData)
3459{
3460 struct smb_t2_qfi_req *pSMB = NULL;
3461 struct smb_t2_qfi_rsp *pSMBr = NULL;
3462 int rc = 0;
3463 int bytes_returned;
3464 __u16 params, byte_count;
3465
3466UnixQFileInfoRetry:
3467 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3468 (void **) &pSMBr);
3469 if (rc)
3470 return rc;
3471
3472 params = 2 /* level */ + 2 /* fid */;
3473 pSMB->t2.TotalDataCount = 0;
3474 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3475 /* BB find exact max data count below from sess structure BB */
3476 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3477 pSMB->t2.MaxSetupCount = 0;
3478 pSMB->t2.Reserved = 0;
3479 pSMB->t2.Flags = 0;
3480 pSMB->t2.Timeout = 0;
3481 pSMB->t2.Reserved2 = 0;
3482 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3483 Fid) - 4);
3484 pSMB->t2.DataCount = 0;
3485 pSMB->t2.DataOffset = 0;
3486 pSMB->t2.SetupCount = 1;
3487 pSMB->t2.Reserved3 = 0;
3488 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3489 byte_count = params + 1 /* pad */ ;
3490 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3491 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3492 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3493 pSMB->Pad = 0;
3494 pSMB->Fid = netfid;
3495 pSMB->hdr.smb_buf_length += byte_count;
3496
3497 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3498 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3499 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003500 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003501 } else { /* decode response */
3502 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3503
3504 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003505 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003506 "Unix Extensions can be disabled on mount "
Steve Frenchf19159d2010-04-21 04:12:10 +00003507 "by specifying the nosfu mount option.");
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003508 rc = -EIO; /* bad smb */
3509 } else {
3510 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3511 memcpy((char *) pFindData,
3512 (char *) &pSMBr->hdr.Protocol +
3513 data_offset,
3514 sizeof(FILE_UNIX_BASIC_INFO));
3515 }
3516 }
3517
3518 cifs_buf_release(pSMB);
3519 if (rc == -EAGAIN)
3520 goto UnixQFileInfoRetry;
3521
3522 return rc;
3523}
3524
3525int
Linus Torvalds1da177e2005-04-16 15:20:36 -07003526CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3527 const unsigned char *searchName,
Steve French582d21e2008-05-13 04:54:12 +00003528 FILE_UNIX_BASIC_INFO *pFindData,
Steve French737b7582005-04-28 22:41:06 -07003529 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003530{
3531/* SMB_QUERY_FILE_UNIX_BASIC */
3532 TRANSACTION2_QPI_REQ *pSMB = NULL;
3533 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3534 int rc = 0;
3535 int bytes_returned = 0;
3536 int name_len;
3537 __u16 params, byte_count;
3538
Joe Perchesb6b38f72010-04-21 03:50:45 +00003539 cFYI(1, "In QPathInfo (Unix) the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003540UnixQPathInfoRetry:
3541 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3542 (void **) &pSMBr);
3543 if (rc)
3544 return rc;
3545
3546 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3547 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003548 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003549 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003550 name_len++; /* trailing null */
3551 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003552 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003553 name_len = strnlen(searchName, PATH_MAX);
3554 name_len++; /* trailing null */
3555 strncpy(pSMB->FileName, searchName, name_len);
3556 }
3557
Steve French50c2f752007-07-13 00:33:32 +00003558 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003559 pSMB->TotalDataCount = 0;
3560 pSMB->MaxParameterCount = cpu_to_le16(2);
3561 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003562 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003563 pSMB->MaxSetupCount = 0;
3564 pSMB->Reserved = 0;
3565 pSMB->Flags = 0;
3566 pSMB->Timeout = 0;
3567 pSMB->Reserved2 = 0;
3568 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003569 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003570 pSMB->DataCount = 0;
3571 pSMB->DataOffset = 0;
3572 pSMB->SetupCount = 1;
3573 pSMB->Reserved3 = 0;
3574 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3575 byte_count = params + 1 /* pad */ ;
3576 pSMB->TotalParameterCount = cpu_to_le16(params);
3577 pSMB->ParameterCount = pSMB->TotalParameterCount;
3578 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3579 pSMB->Reserved4 = 0;
3580 pSMB->hdr.smb_buf_length += byte_count;
3581 pSMB->ByteCount = cpu_to_le16(byte_count);
3582
3583 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3584 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3585 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003586 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003587 } else { /* decode response */
3588 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3589
3590 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003591 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Steve French1e71f252007-09-20 15:30:07 +00003592 "Unix Extensions can be disabled on mount "
Joe Perchesb6b38f72010-04-21 03:50:45 +00003593 "by specifying the nosfu mount option.");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003594 rc = -EIO; /* bad smb */
3595 } else {
3596 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3597 memcpy((char *) pFindData,
3598 (char *) &pSMBr->hdr.Protocol +
3599 data_offset,
Steve French630f3f0c2007-10-25 21:17:17 +00003600 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003601 }
3602 }
3603 cifs_buf_release(pSMB);
3604 if (rc == -EAGAIN)
3605 goto UnixQPathInfoRetry;
3606
3607 return rc;
3608}
3609
Linus Torvalds1da177e2005-04-16 15:20:36 -07003610/* xid, tcon, searchName and codepage are input parms, rest are returned */
3611int
3612CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003613 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003614 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003615 __u16 *pnetfid,
3616 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003617{
3618/* level 257 SMB_ */
3619 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3620 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003621 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003622 int rc = 0;
3623 int bytes_returned = 0;
3624 int name_len;
3625 __u16 params, byte_count;
3626
Joe Perchesb6b38f72010-04-21 03:50:45 +00003627 cFYI(1, "In FindFirst for %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003628
3629findFirstRetry:
3630 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3631 (void **) &pSMBr);
3632 if (rc)
3633 return rc;
3634
3635 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3636 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003637 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003638 PATH_MAX, nls_codepage, remap);
3639 /* We can not add the asterik earlier in case
3640 it got remapped to 0xF03A as if it were part of the
3641 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003642 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003643 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003644 pSMB->FileName[name_len+1] = 0;
3645 pSMB->FileName[name_len+2] = '*';
3646 pSMB->FileName[name_len+3] = 0;
3647 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003648 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3649 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003650 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003651 } else { /* BB add check for overrun of SMB buf BB */
3652 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003653/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003654 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003655 free buffer exit; BB */
3656 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003657 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003658 pSMB->FileName[name_len+1] = '*';
3659 pSMB->FileName[name_len+2] = 0;
3660 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003661 }
3662
3663 params = 12 + name_len /* includes null */ ;
3664 pSMB->TotalDataCount = 0; /* no EAs */
3665 pSMB->MaxParameterCount = cpu_to_le16(10);
3666 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3667 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3668 pSMB->MaxSetupCount = 0;
3669 pSMB->Reserved = 0;
3670 pSMB->Flags = 0;
3671 pSMB->Timeout = 0;
3672 pSMB->Reserved2 = 0;
3673 byte_count = params + 1 /* pad */ ;
3674 pSMB->TotalParameterCount = cpu_to_le16(params);
3675 pSMB->ParameterCount = pSMB->TotalParameterCount;
3676 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003677 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3678 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003679 pSMB->DataCount = 0;
3680 pSMB->DataOffset = 0;
3681 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3682 pSMB->Reserved3 = 0;
3683 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3684 pSMB->SearchAttributes =
3685 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3686 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003687 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3688 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003689 CIFS_SEARCH_RETURN_RESUME);
3690 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3691
3692 /* BB what should we set StorageType to? Does it matter? BB */
3693 pSMB->SearchStorageType = 0;
3694 pSMB->hdr.smb_buf_length += byte_count;
3695 pSMB->ByteCount = cpu_to_le16(byte_count);
3696
3697 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3698 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003699 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003700
Steve French88274812006-03-09 22:21:45 +00003701 if (rc) {/* BB add logic to retry regular search if Unix search
3702 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003703 /* BB Add code to handle unsupported level rc */
Joe Perchesb6b38f72010-04-21 03:50:45 +00003704 cFYI(1, "Error in FindFirst = %d", rc);
Steve French1982c342005-08-17 12:38:22 -07003705
Steve French88274812006-03-09 22:21:45 +00003706 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003707
3708 /* BB eventually could optimize out free and realloc of buf */
3709 /* for this case */
3710 if (rc == -EAGAIN)
3711 goto findFirstRetry;
3712 } else { /* decode response */
3713 /* BB remember to free buffer if error BB */
3714 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003715 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003716 unsigned int lnoff;
3717
Linus Torvalds1da177e2005-04-16 15:20:36 -07003718 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003719 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003720 else
Steve French4b18f2a2008-04-29 00:06:05 +00003721 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003722
3723 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003724 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003725 psrch_inf->srch_entries_start =
3726 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003727 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003728 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3729 le16_to_cpu(pSMBr->t2.ParameterOffset));
3730
Steve French790fe572007-07-07 19:25:05 +00003731 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003732 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733 else
Steve French4b18f2a2008-04-29 00:06:05 +00003734 psrch_inf->endOfSearch = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003735
Steve French50c2f752007-07-13 00:33:32 +00003736 psrch_inf->entries_in_buffer =
3737 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003738 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003739 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003740 lnoff = le16_to_cpu(parms->LastNameOffset);
3741 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3742 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003743 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003744 psrch_inf->last_entry = NULL;
3745 return rc;
3746 }
3747
Steve French0752f152008-10-07 20:03:33 +00003748 psrch_inf->last_entry = psrch_inf->srch_entries_start +
Steve Frenchb77d7532008-10-08 19:13:46 +00003749 lnoff;
3750
Linus Torvalds1da177e2005-04-16 15:20:36 -07003751 *pnetfid = parms->SearchHandle;
3752 } else {
3753 cifs_buf_release(pSMB);
3754 }
3755 }
3756
3757 return rc;
3758}
3759
3760int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003761 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003762{
3763 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3764 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003765 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003766 char *response_data;
3767 int rc = 0;
3768 int bytes_returned, name_len;
3769 __u16 params, byte_count;
3770
Joe Perchesb6b38f72010-04-21 03:50:45 +00003771 cFYI(1, "In FindNext");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003772
Steve French4b18f2a2008-04-29 00:06:05 +00003773 if (psrch_inf->endOfSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003774 return -ENOENT;
3775
3776 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3777 (void **) &pSMBr);
3778 if (rc)
3779 return rc;
3780
Steve French50c2f752007-07-13 00:33:32 +00003781 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003782 byte_count = 0;
3783 pSMB->TotalDataCount = 0; /* no EAs */
3784 pSMB->MaxParameterCount = cpu_to_le16(8);
3785 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003786 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3787 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003788 pSMB->MaxSetupCount = 0;
3789 pSMB->Reserved = 0;
3790 pSMB->Flags = 0;
3791 pSMB->Timeout = 0;
3792 pSMB->Reserved2 = 0;
3793 pSMB->ParameterOffset = cpu_to_le16(
3794 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3795 pSMB->DataCount = 0;
3796 pSMB->DataOffset = 0;
3797 pSMB->SetupCount = 1;
3798 pSMB->Reserved3 = 0;
3799 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3800 pSMB->SearchHandle = searchHandle; /* always kept as le */
3801 pSMB->SearchCount =
Steve French630f3f0c2007-10-25 21:17:17 +00003802 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003803 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3804 pSMB->ResumeKey = psrch_inf->resume_key;
3805 pSMB->SearchFlags =
3806 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3807
3808 name_len = psrch_inf->resume_name_len;
3809 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003810 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003811 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3812 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003813 /* 14 byte parm len above enough for 2 byte null terminator */
3814 pSMB->ResumeFileName[name_len] = 0;
3815 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003816 } else {
3817 rc = -EINVAL;
3818 goto FNext2_err_exit;
3819 }
3820 byte_count = params + 1 /* pad */ ;
3821 pSMB->TotalParameterCount = cpu_to_le16(params);
3822 pSMB->ParameterCount = pSMB->TotalParameterCount;
3823 pSMB->hdr.smb_buf_length += byte_count;
3824 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003825
Linus Torvalds1da177e2005-04-16 15:20:36 -07003826 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3827 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003828 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003829 if (rc) {
3830 if (rc == -EBADF) {
Steve French4b18f2a2008-04-29 00:06:05 +00003831 psrch_inf->endOfSearch = true;
Jeff Layton63534502008-05-12 19:56:05 -07003832 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00003833 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003834 } else
Joe Perchesb6b38f72010-04-21 03:50:45 +00003835 cFYI(1, "FindNext returned = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003836 } else { /* decode response */
3837 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003838
Steve French790fe572007-07-07 19:25:05 +00003839 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003840 unsigned int lnoff;
3841
Linus Torvalds1da177e2005-04-16 15:20:36 -07003842 /* BB fixme add lock for file (srch_info) struct here */
3843 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003844 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003845 else
Steve French4b18f2a2008-04-29 00:06:05 +00003846 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003847 response_data = (char *) &pSMBr->hdr.Protocol +
3848 le16_to_cpu(pSMBr->t2.ParameterOffset);
3849 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3850 response_data = (char *)&pSMBr->hdr.Protocol +
3851 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003852 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003853 cifs_small_buf_release(
3854 psrch_inf->ntwrk_buf_start);
3855 else
3856 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003857 psrch_inf->srch_entries_start = response_data;
3858 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003859 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003860 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003861 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003862 else
Steve French4b18f2a2008-04-29 00:06:05 +00003863 psrch_inf->endOfSearch = false;
Steve French50c2f752007-07-13 00:33:32 +00003864 psrch_inf->entries_in_buffer =
3865 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003866 psrch_inf->index_of_last_entry +=
3867 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003868 lnoff = le16_to_cpu(parms->LastNameOffset);
3869 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3870 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003871 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003872 psrch_inf->last_entry = NULL;
3873 return rc;
3874 } else
3875 psrch_inf->last_entry =
3876 psrch_inf->srch_entries_start + lnoff;
3877
Joe Perchesb6b38f72010-04-21 03:50:45 +00003878/* cFYI(1, "fnxt2 entries in buf %d index_of_last %d",
3879 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003880
3881 /* BB fixme add unlock here */
3882 }
3883
3884 }
3885
3886 /* BB On error, should we leave previous search buf (and count and
3887 last entry fields) intact or free the previous one? */
3888
3889 /* Note: On -EAGAIN error only caller can retry on handle based calls
3890 since file handle passed in no longer valid */
3891FNext2_err_exit:
3892 if (rc != 0)
3893 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003894 return rc;
3895}
3896
3897int
Steve French50c2f752007-07-13 00:33:32 +00003898CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3899 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003900{
3901 int rc = 0;
3902 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003903
Joe Perchesb6b38f72010-04-21 03:50:45 +00003904 cFYI(1, "In CIFSSMBFindClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003905 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3906
3907 /* no sense returning error if session restarted
3908 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003909 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003910 return 0;
3911 if (rc)
3912 return rc;
3913
Linus Torvalds1da177e2005-04-16 15:20:36 -07003914 pSMB->FileID = searchHandle;
3915 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003916 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003917 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003918 cERROR(1, "Send error in FindClose = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003919
Steve Frencha4544342005-08-24 13:59:35 -07003920 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003921
3922 /* Since session is dead, search handle closed on server already */
3923 if (rc == -EAGAIN)
3924 rc = 0;
3925
3926 return rc;
3927}
3928
Linus Torvalds1da177e2005-04-16 15:20:36 -07003929int
3930CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003931 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003932 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003933 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003934{
3935 int rc = 0;
3936 TRANSACTION2_QPI_REQ *pSMB = NULL;
3937 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3938 int name_len, bytes_returned;
3939 __u16 params, byte_count;
3940
Joe Perchesb6b38f72010-04-21 03:50:45 +00003941 cFYI(1, "In GetSrvInodeNum for %s", searchName);
Steve French790fe572007-07-07 19:25:05 +00003942 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003943 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003944
3945GetInodeNumberRetry:
3946 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003947 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003948 if (rc)
3949 return rc;
3950
Linus Torvalds1da177e2005-04-16 15:20:36 -07003951 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3952 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003953 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003954 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003955 name_len++; /* trailing null */
3956 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003957 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003958 name_len = strnlen(searchName, PATH_MAX);
3959 name_len++; /* trailing null */
3960 strncpy(pSMB->FileName, searchName, name_len);
3961 }
3962
3963 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
3964 pSMB->TotalDataCount = 0;
3965 pSMB->MaxParameterCount = cpu_to_le16(2);
3966 /* BB find exact max data count below from sess structure BB */
3967 pSMB->MaxDataCount = cpu_to_le16(4000);
3968 pSMB->MaxSetupCount = 0;
3969 pSMB->Reserved = 0;
3970 pSMB->Flags = 0;
3971 pSMB->Timeout = 0;
3972 pSMB->Reserved2 = 0;
3973 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003974 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003975 pSMB->DataCount = 0;
3976 pSMB->DataOffset = 0;
3977 pSMB->SetupCount = 1;
3978 pSMB->Reserved3 = 0;
3979 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3980 byte_count = params + 1 /* pad */ ;
3981 pSMB->TotalParameterCount = cpu_to_le16(params);
3982 pSMB->ParameterCount = pSMB->TotalParameterCount;
3983 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
3984 pSMB->Reserved4 = 0;
3985 pSMB->hdr.smb_buf_length += byte_count;
3986 pSMB->ByteCount = cpu_to_le16(byte_count);
3987
3988 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3989 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3990 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003991 cFYI(1, "error %d in QueryInternalInfo", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003992 } else {
3993 /* decode response */
3994 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3995 if (rc || (pSMBr->ByteCount < 2))
3996 /* BB also check enough total bytes returned */
3997 /* If rc should we check for EOPNOSUPP and
3998 disable the srvino flag? or in caller? */
3999 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00004000 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004001 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4002 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00004003 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004004 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00004005 if (count < 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004006 cFYI(1, "Illegal size ret in QryIntrnlInf");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004007 rc = -EIO;
4008 goto GetInodeNumOut;
4009 }
4010 pfinfo = (struct file_internal_info *)
4011 (data_offset + (char *) &pSMBr->hdr.Protocol);
Steve French85a6dac2009-04-01 05:22:00 +00004012 *inode_number = le64_to_cpu(pfinfo->UniqueId);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004013 }
4014 }
4015GetInodeNumOut:
4016 cifs_buf_release(pSMB);
4017 if (rc == -EAGAIN)
4018 goto GetInodeNumberRetry;
4019 return rc;
4020}
Linus Torvalds1da177e2005-04-16 15:20:36 -07004021
Igor Mammedovfec45852008-05-16 13:06:30 +04004022/* parses DFS refferal V3 structure
4023 * caller is responsible for freeing target_nodes
4024 * returns:
4025 * on success - 0
4026 * on failure - errno
4027 */
4028static int
Steve Frencha1fe78f2008-05-16 18:48:38 +00004029parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
Igor Mammedovfec45852008-05-16 13:06:30 +04004030 unsigned int *num_of_nodes,
4031 struct dfs_info3_param **target_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004032 const struct nls_table *nls_codepage, int remap,
4033 const char *searchName)
Igor Mammedovfec45852008-05-16 13:06:30 +04004034{
4035 int i, rc = 0;
4036 char *data_end;
4037 bool is_unicode;
4038 struct dfs_referral_level_3 *ref;
4039
Harvey Harrison5ca33c62008-07-23 17:45:58 -07004040 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
4041 is_unicode = true;
4042 else
4043 is_unicode = false;
Igor Mammedovfec45852008-05-16 13:06:30 +04004044 *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
4045
4046 if (*num_of_nodes < 1) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004047 cERROR(1, "num_referrals: must be at least > 0,"
4048 "but we get num_referrals = %d\n", *num_of_nodes);
Igor Mammedovfec45852008-05-16 13:06:30 +04004049 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004050 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004051 }
4052
4053 ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
Al Viro1d92cfd2008-06-02 10:59:02 +01004054 if (ref->VersionNumber != cpu_to_le16(3)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004055 cERROR(1, "Referrals of V%d version are not supported,"
4056 "should be V3", le16_to_cpu(ref->VersionNumber));
Igor Mammedovfec45852008-05-16 13:06:30 +04004057 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004058 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004059 }
4060
4061 /* get the upper boundary of the resp buffer */
4062 data_end = (char *)(&(pSMBr->PathConsumed)) +
4063 le16_to_cpu(pSMBr->t2.DataCount);
4064
Steve Frenchf19159d2010-04-21 04:12:10 +00004065 cFYI(1, "num_referrals: %d dfs flags: 0x%x ...\n",
Igor Mammedovfec45852008-05-16 13:06:30 +04004066 *num_of_nodes,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004067 le32_to_cpu(pSMBr->DFSFlags));
Igor Mammedovfec45852008-05-16 13:06:30 +04004068
4069 *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
4070 *num_of_nodes, GFP_KERNEL);
4071 if (*target_nodes == NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004072 cERROR(1, "Failed to allocate buffer for target_nodes\n");
Igor Mammedovfec45852008-05-16 13:06:30 +04004073 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004074 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004075 }
4076
Daniel Mack3ad2f3f2010-02-03 08:01:28 +08004077 /* collect necessary data from referrals */
Igor Mammedovfec45852008-05-16 13:06:30 +04004078 for (i = 0; i < *num_of_nodes; i++) {
4079 char *temp;
4080 int max_len;
4081 struct dfs_info3_param *node = (*target_nodes)+i;
4082
Steve French0e0d2cf2009-05-01 05:27:32 +00004083 node->flags = le32_to_cpu(pSMBr->DFSFlags);
Igor Mammedov2c556082008-10-23 13:58:42 +04004084 if (is_unicode) {
Jeff Layton331c3132008-12-17 06:31:53 -05004085 __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
4086 GFP_KERNEL);
Steve French2920ee22009-08-31 15:27:26 +00004087 if (tmp == NULL) {
4088 rc = -ENOMEM;
4089 goto parse_DFS_referrals_exit;
4090 }
Igor Mammedov2c556082008-10-23 13:58:42 +04004091 cifsConvertToUCS((__le16 *) tmp, searchName,
4092 PATH_MAX, nls_codepage, remap);
Jeff Layton69f801f2009-04-30 06:46:32 -04004093 node->path_consumed = cifs_ucs2_bytes(tmp,
4094 le16_to_cpu(pSMBr->PathConsumed),
Igor Mammedov2c556082008-10-23 13:58:42 +04004095 nls_codepage);
4096 kfree(tmp);
4097 } else
4098 node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
4099
Igor Mammedovfec45852008-05-16 13:06:30 +04004100 node->server_type = le16_to_cpu(ref->ServerType);
4101 node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
4102
4103 /* copy DfsPath */
4104 temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
4105 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004106 node->path_name = cifs_strndup_from_ucs(temp, max_len,
4107 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004108 if (!node->path_name) {
4109 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004110 goto parse_DFS_referrals_exit;
Jeff Layton066ce682009-04-30 07:16:14 -04004111 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004112
4113 /* copy link target UNC */
4114 temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
4115 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004116 node->node_name = cifs_strndup_from_ucs(temp, max_len,
4117 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004118 if (!node->node_name)
4119 rc = -ENOMEM;
Igor Mammedovfec45852008-05-16 13:06:30 +04004120 }
4121
Steve Frencha1fe78f2008-05-16 18:48:38 +00004122parse_DFS_referrals_exit:
Igor Mammedovfec45852008-05-16 13:06:30 +04004123 if (rc) {
4124 free_dfs_info_array(*target_nodes, *num_of_nodes);
4125 *target_nodes = NULL;
4126 *num_of_nodes = 0;
4127 }
4128 return rc;
4129}
4130
Linus Torvalds1da177e2005-04-16 15:20:36 -07004131int
4132CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
4133 const unsigned char *searchName,
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004134 struct dfs_info3_param **target_nodes,
4135 unsigned int *num_of_nodes,
Steve French737b7582005-04-28 22:41:06 -07004136 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004137{
4138/* TRANS2_GET_DFS_REFERRAL */
4139 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
4140 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004141 int rc = 0;
4142 int bytes_returned;
4143 int name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004144 __u16 params, byte_count;
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004145 *num_of_nodes = 0;
4146 *target_nodes = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004147
Joe Perchesb6b38f72010-04-21 03:50:45 +00004148 cFYI(1, "In GetDFSRefer the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004149 if (ses == NULL)
4150 return -ENODEV;
4151getDFSRetry:
4152 rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
4153 (void **) &pSMBr);
4154 if (rc)
4155 return rc;
Steve French50c2f752007-07-13 00:33:32 +00004156
4157 /* server pointer checked in called function,
Steve French1982c342005-08-17 12:38:22 -07004158 but should never be null here anyway */
4159 pSMB->hdr.Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004160 pSMB->hdr.Tid = ses->ipc_tid;
4161 pSMB->hdr.Uid = ses->Suid;
Steve French26f57362007-08-30 22:09:15 +00004162 if (ses->capabilities & CAP_STATUS32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004163 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
Steve French26f57362007-08-30 22:09:15 +00004164 if (ses->capabilities & CAP_DFS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004165 pSMB->hdr.Flags2 |= SMBFLG2_DFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004166
4167 if (ses->capabilities & CAP_UNICODE) {
4168 pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
4169 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004170 cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
Steve French737b7582005-04-28 22:41:06 -07004171 searchName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004172 name_len++; /* trailing null */
4173 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004174 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004175 name_len = strnlen(searchName, PATH_MAX);
4176 name_len++; /* trailing null */
4177 strncpy(pSMB->RequestFileName, searchName, name_len);
4178 }
4179
Steve French790fe572007-07-07 19:25:05 +00004180 if (ses->server) {
4181 if (ses->server->secMode &
Steve French1a4e15a2006-10-12 21:33:51 +00004182 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
4183 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
4184 }
4185
Steve French50c2f752007-07-13 00:33:32 +00004186 pSMB->hdr.Uid = ses->Suid;
Steve French1a4e15a2006-10-12 21:33:51 +00004187
Linus Torvalds1da177e2005-04-16 15:20:36 -07004188 params = 2 /* level */ + name_len /*includes null */ ;
4189 pSMB->TotalDataCount = 0;
4190 pSMB->DataCount = 0;
4191 pSMB->DataOffset = 0;
4192 pSMB->MaxParameterCount = 0;
Steve French582d21e2008-05-13 04:54:12 +00004193 /* BB find exact max SMB PDU from sess structure BB */
4194 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004195 pSMB->MaxSetupCount = 0;
4196 pSMB->Reserved = 0;
4197 pSMB->Flags = 0;
4198 pSMB->Timeout = 0;
4199 pSMB->Reserved2 = 0;
4200 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004201 struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004202 pSMB->SetupCount = 1;
4203 pSMB->Reserved3 = 0;
4204 pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
4205 byte_count = params + 3 /* pad */ ;
4206 pSMB->ParameterCount = cpu_to_le16(params);
4207 pSMB->TotalParameterCount = pSMB->ParameterCount;
4208 pSMB->MaxReferralLevel = cpu_to_le16(3);
4209 pSMB->hdr.smb_buf_length += byte_count;
4210 pSMB->ByteCount = cpu_to_le16(byte_count);
4211
4212 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
4213 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4214 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004215 cFYI(1, "Send error in GetDFSRefer = %d", rc);
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004216 goto GetDFSRefExit;
4217 }
4218 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004219
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004220 /* BB Also check if enough total bytes returned? */
Igor Mammedovfec45852008-05-16 13:06:30 +04004221 if (rc || (pSMBr->ByteCount < 17)) {
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004222 rc = -EIO; /* bad smb */
Igor Mammedovfec45852008-05-16 13:06:30 +04004223 goto GetDFSRefExit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004224 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004225
Joe Perchesb6b38f72010-04-21 03:50:45 +00004226 cFYI(1, "Decoding GetDFSRefer response BCC: %d Offset %d",
Igor Mammedovfec45852008-05-16 13:06:30 +04004227 pSMBr->ByteCount,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004228 le16_to_cpu(pSMBr->t2.DataOffset));
Igor Mammedovfec45852008-05-16 13:06:30 +04004229
4230 /* parse returned result into more usable form */
Steve Frencha1fe78f2008-05-16 18:48:38 +00004231 rc = parse_DFS_referrals(pSMBr, num_of_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004232 target_nodes, nls_codepage, remap,
4233 searchName);
Igor Mammedovfec45852008-05-16 13:06:30 +04004234
Linus Torvalds1da177e2005-04-16 15:20:36 -07004235GetDFSRefExit:
Steve French0d817bc2008-05-22 02:02:03 +00004236 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004237
4238 if (rc == -EAGAIN)
4239 goto getDFSRetry;
4240
4241 return rc;
4242}
4243
Steve French20962432005-09-21 22:05:57 -07004244/* Query File System Info such as free space to old servers such as Win 9x */
4245int
4246SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4247{
4248/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4249 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4250 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4251 FILE_SYSTEM_ALLOC_INFO *response_data;
4252 int rc = 0;
4253 int bytes_returned = 0;
4254 __u16 params, byte_count;
4255
Joe Perchesb6b38f72010-04-21 03:50:45 +00004256 cFYI(1, "OldQFSInfo");
Steve French20962432005-09-21 22:05:57 -07004257oldQFSInfoRetry:
4258 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4259 (void **) &pSMBr);
4260 if (rc)
4261 return rc;
Steve French20962432005-09-21 22:05:57 -07004262
4263 params = 2; /* level */
4264 pSMB->TotalDataCount = 0;
4265 pSMB->MaxParameterCount = cpu_to_le16(2);
4266 pSMB->MaxDataCount = cpu_to_le16(1000);
4267 pSMB->MaxSetupCount = 0;
4268 pSMB->Reserved = 0;
4269 pSMB->Flags = 0;
4270 pSMB->Timeout = 0;
4271 pSMB->Reserved2 = 0;
4272 byte_count = params + 1 /* pad */ ;
4273 pSMB->TotalParameterCount = cpu_to_le16(params);
4274 pSMB->ParameterCount = pSMB->TotalParameterCount;
4275 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4276 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4277 pSMB->DataCount = 0;
4278 pSMB->DataOffset = 0;
4279 pSMB->SetupCount = 1;
4280 pSMB->Reserved3 = 0;
4281 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4282 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4283 pSMB->hdr.smb_buf_length += byte_count;
4284 pSMB->ByteCount = cpu_to_le16(byte_count);
4285
4286 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4287 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4288 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004289 cFYI(1, "Send error in QFSInfo = %d", rc);
Steve French20962432005-09-21 22:05:57 -07004290 } else { /* decode response */
4291 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4292
4293 if (rc || (pSMBr->ByteCount < 18))
4294 rc = -EIO; /* bad smb */
4295 else {
4296 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004297 cFYI(1, "qfsinf resp BCC: %d Offset %d",
4298 pSMBr->ByteCount, data_offset);
Steve French20962432005-09-21 22:05:57 -07004299
Steve French50c2f752007-07-13 00:33:32 +00004300 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004301 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4302 FSData->f_bsize =
4303 le16_to_cpu(response_data->BytesPerSector) *
4304 le32_to_cpu(response_data->
4305 SectorsPerAllocationUnit);
4306 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004307 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004308 FSData->f_bfree = FSData->f_bavail =
4309 le32_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004310 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4311 (unsigned long long)FSData->f_blocks,
4312 (unsigned long long)FSData->f_bfree,
4313 FSData->f_bsize);
Steve French20962432005-09-21 22:05:57 -07004314 }
4315 }
4316 cifs_buf_release(pSMB);
4317
4318 if (rc == -EAGAIN)
4319 goto oldQFSInfoRetry;
4320
4321 return rc;
4322}
4323
Linus Torvalds1da177e2005-04-16 15:20:36 -07004324int
Steve French737b7582005-04-28 22:41:06 -07004325CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004326{
4327/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4328 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4329 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4330 FILE_SYSTEM_INFO *response_data;
4331 int rc = 0;
4332 int bytes_returned = 0;
4333 __u16 params, byte_count;
4334
Joe Perchesb6b38f72010-04-21 03:50:45 +00004335 cFYI(1, "In QFSInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004336QFSInfoRetry:
4337 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4338 (void **) &pSMBr);
4339 if (rc)
4340 return rc;
4341
4342 params = 2; /* level */
4343 pSMB->TotalDataCount = 0;
4344 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004345 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004346 pSMB->MaxSetupCount = 0;
4347 pSMB->Reserved = 0;
4348 pSMB->Flags = 0;
4349 pSMB->Timeout = 0;
4350 pSMB->Reserved2 = 0;
4351 byte_count = params + 1 /* pad */ ;
4352 pSMB->TotalParameterCount = cpu_to_le16(params);
4353 pSMB->ParameterCount = pSMB->TotalParameterCount;
4354 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004355 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004356 pSMB->DataCount = 0;
4357 pSMB->DataOffset = 0;
4358 pSMB->SetupCount = 1;
4359 pSMB->Reserved3 = 0;
4360 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4361 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4362 pSMB->hdr.smb_buf_length += byte_count;
4363 pSMB->ByteCount = cpu_to_le16(byte_count);
4364
4365 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4366 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4367 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004368 cFYI(1, "Send error in QFSInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004369 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004370 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004371
Steve French20962432005-09-21 22:05:57 -07004372 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004373 rc = -EIO; /* bad smb */
4374 else {
4375 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004376
4377 response_data =
4378 (FILE_SYSTEM_INFO
4379 *) (((char *) &pSMBr->hdr.Protocol) +
4380 data_offset);
4381 FSData->f_bsize =
4382 le32_to_cpu(response_data->BytesPerSector) *
4383 le32_to_cpu(response_data->
4384 SectorsPerAllocationUnit);
4385 FSData->f_blocks =
4386 le64_to_cpu(response_data->TotalAllocationUnits);
4387 FSData->f_bfree = FSData->f_bavail =
4388 le64_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004389 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4390 (unsigned long long)FSData->f_blocks,
4391 (unsigned long long)FSData->f_bfree,
4392 FSData->f_bsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004393 }
4394 }
4395 cifs_buf_release(pSMB);
4396
4397 if (rc == -EAGAIN)
4398 goto QFSInfoRetry;
4399
4400 return rc;
4401}
4402
4403int
Steve French737b7582005-04-28 22:41:06 -07004404CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004405{
4406/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4407 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4408 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4409 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4410 int rc = 0;
4411 int bytes_returned = 0;
4412 __u16 params, byte_count;
4413
Joe Perchesb6b38f72010-04-21 03:50:45 +00004414 cFYI(1, "In QFSAttributeInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004415QFSAttributeRetry:
4416 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4417 (void **) &pSMBr);
4418 if (rc)
4419 return rc;
4420
4421 params = 2; /* level */
4422 pSMB->TotalDataCount = 0;
4423 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004424 /* BB find exact max SMB PDU from sess structure BB */
4425 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004426 pSMB->MaxSetupCount = 0;
4427 pSMB->Reserved = 0;
4428 pSMB->Flags = 0;
4429 pSMB->Timeout = 0;
4430 pSMB->Reserved2 = 0;
4431 byte_count = params + 1 /* pad */ ;
4432 pSMB->TotalParameterCount = cpu_to_le16(params);
4433 pSMB->ParameterCount = pSMB->TotalParameterCount;
4434 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004435 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004436 pSMB->DataCount = 0;
4437 pSMB->DataOffset = 0;
4438 pSMB->SetupCount = 1;
4439 pSMB->Reserved3 = 0;
4440 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4441 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4442 pSMB->hdr.smb_buf_length += byte_count;
4443 pSMB->ByteCount = cpu_to_le16(byte_count);
4444
4445 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4446 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4447 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004448 cERROR(1, "Send error in QFSAttributeInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004449 } else { /* decode response */
4450 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4451
Steve French50c2f752007-07-13 00:33:32 +00004452 if (rc || (pSMBr->ByteCount < 13)) {
4453 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004454 rc = -EIO; /* bad smb */
4455 } else {
4456 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4457 response_data =
4458 (FILE_SYSTEM_ATTRIBUTE_INFO
4459 *) (((char *) &pSMBr->hdr.Protocol) +
4460 data_offset);
4461 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004462 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004463 }
4464 }
4465 cifs_buf_release(pSMB);
4466
4467 if (rc == -EAGAIN)
4468 goto QFSAttributeRetry;
4469
4470 return rc;
4471}
4472
4473int
Steve French737b7582005-04-28 22:41:06 -07004474CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004475{
4476/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4477 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4478 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4479 FILE_SYSTEM_DEVICE_INFO *response_data;
4480 int rc = 0;
4481 int bytes_returned = 0;
4482 __u16 params, byte_count;
4483
Joe Perchesb6b38f72010-04-21 03:50:45 +00004484 cFYI(1, "In QFSDeviceInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004485QFSDeviceRetry:
4486 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4487 (void **) &pSMBr);
4488 if (rc)
4489 return rc;
4490
4491 params = 2; /* level */
4492 pSMB->TotalDataCount = 0;
4493 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004494 /* BB find exact max SMB PDU from sess structure BB */
4495 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004496 pSMB->MaxSetupCount = 0;
4497 pSMB->Reserved = 0;
4498 pSMB->Flags = 0;
4499 pSMB->Timeout = 0;
4500 pSMB->Reserved2 = 0;
4501 byte_count = params + 1 /* pad */ ;
4502 pSMB->TotalParameterCount = cpu_to_le16(params);
4503 pSMB->ParameterCount = pSMB->TotalParameterCount;
4504 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004505 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004506
4507 pSMB->DataCount = 0;
4508 pSMB->DataOffset = 0;
4509 pSMB->SetupCount = 1;
4510 pSMB->Reserved3 = 0;
4511 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4512 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4513 pSMB->hdr.smb_buf_length += byte_count;
4514 pSMB->ByteCount = cpu_to_le16(byte_count);
4515
4516 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4517 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4518 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004519 cFYI(1, "Send error in QFSDeviceInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004520 } else { /* decode response */
4521 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4522
Steve French630f3f0c2007-10-25 21:17:17 +00004523 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004524 rc = -EIO; /* bad smb */
4525 else {
4526 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4527 response_data =
Steve French737b7582005-04-28 22:41:06 -07004528 (FILE_SYSTEM_DEVICE_INFO *)
4529 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004530 data_offset);
4531 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004532 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004533 }
4534 }
4535 cifs_buf_release(pSMB);
4536
4537 if (rc == -EAGAIN)
4538 goto QFSDeviceRetry;
4539
4540 return rc;
4541}
4542
4543int
Steve French737b7582005-04-28 22:41:06 -07004544CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004545{
4546/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4547 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4548 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4549 FILE_SYSTEM_UNIX_INFO *response_data;
4550 int rc = 0;
4551 int bytes_returned = 0;
4552 __u16 params, byte_count;
4553
Joe Perchesb6b38f72010-04-21 03:50:45 +00004554 cFYI(1, "In QFSUnixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004555QFSUnixRetry:
Jeff Laytonf5695992010-09-29 15:27:08 -04004556 rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
4557 (void **) &pSMB, (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004558 if (rc)
4559 return rc;
4560
4561 params = 2; /* level */
4562 pSMB->TotalDataCount = 0;
4563 pSMB->DataCount = 0;
4564 pSMB->DataOffset = 0;
4565 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004566 /* BB find exact max SMB PDU from sess structure BB */
4567 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004568 pSMB->MaxSetupCount = 0;
4569 pSMB->Reserved = 0;
4570 pSMB->Flags = 0;
4571 pSMB->Timeout = 0;
4572 pSMB->Reserved2 = 0;
4573 byte_count = params + 1 /* pad */ ;
4574 pSMB->ParameterCount = cpu_to_le16(params);
4575 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004576 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4577 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004578 pSMB->SetupCount = 1;
4579 pSMB->Reserved3 = 0;
4580 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4581 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4582 pSMB->hdr.smb_buf_length += byte_count;
4583 pSMB->ByteCount = cpu_to_le16(byte_count);
4584
4585 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4586 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4587 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004588 cERROR(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004589 } else { /* decode response */
4590 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4591
4592 if (rc || (pSMBr->ByteCount < 13)) {
4593 rc = -EIO; /* bad smb */
4594 } else {
4595 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4596 response_data =
4597 (FILE_SYSTEM_UNIX_INFO
4598 *) (((char *) &pSMBr->hdr.Protocol) +
4599 data_offset);
4600 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004601 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004602 }
4603 }
4604 cifs_buf_release(pSMB);
4605
4606 if (rc == -EAGAIN)
4607 goto QFSUnixRetry;
4608
4609
4610 return rc;
4611}
4612
Jeremy Allisonac670552005-06-22 17:26:35 -07004613int
Steve French45abc6e2005-06-23 13:42:03 -05004614CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004615{
4616/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4617 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4618 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4619 int rc = 0;
4620 int bytes_returned = 0;
4621 __u16 params, param_offset, offset, byte_count;
4622
Joe Perchesb6b38f72010-04-21 03:50:45 +00004623 cFYI(1, "In SETFSUnixInfo");
Jeremy Allisonac670552005-06-22 17:26:35 -07004624SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004625 /* BB switch to small buf init to save memory */
Jeff Laytonf5695992010-09-29 15:27:08 -04004626 rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
4627 (void **) &pSMB, (void **) &pSMBr);
Jeremy Allisonac670552005-06-22 17:26:35 -07004628 if (rc)
4629 return rc;
4630
4631 params = 4; /* 2 bytes zero followed by info level. */
4632 pSMB->MaxSetupCount = 0;
4633 pSMB->Reserved = 0;
4634 pSMB->Flags = 0;
4635 pSMB->Timeout = 0;
4636 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004637 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4638 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004639 offset = param_offset + params;
4640
4641 pSMB->MaxParameterCount = cpu_to_le16(4);
Steve French582d21e2008-05-13 04:54:12 +00004642 /* BB find exact max SMB PDU from sess structure BB */
4643 pSMB->MaxDataCount = cpu_to_le16(100);
Jeremy Allisonac670552005-06-22 17:26:35 -07004644 pSMB->SetupCount = 1;
4645 pSMB->Reserved3 = 0;
4646 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4647 byte_count = 1 /* pad */ + params + 12;
4648
4649 pSMB->DataCount = cpu_to_le16(12);
4650 pSMB->ParameterCount = cpu_to_le16(params);
4651 pSMB->TotalDataCount = pSMB->DataCount;
4652 pSMB->TotalParameterCount = pSMB->ParameterCount;
4653 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4654 pSMB->DataOffset = cpu_to_le16(offset);
4655
4656 /* Params. */
4657 pSMB->FileNum = 0;
4658 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4659
4660 /* Data. */
4661 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4662 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4663 pSMB->ClientUnixCap = cpu_to_le64(cap);
4664
4665 pSMB->hdr.smb_buf_length += byte_count;
4666 pSMB->ByteCount = cpu_to_le16(byte_count);
4667
4668 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4669 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4670 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004671 cERROR(1, "Send error in SETFSUnixInfo = %d", rc);
Jeremy Allisonac670552005-06-22 17:26:35 -07004672 } else { /* decode response */
4673 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004674 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004675 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004676 }
4677 cifs_buf_release(pSMB);
4678
4679 if (rc == -EAGAIN)
4680 goto SETFSUnixRetry;
4681
4682 return rc;
4683}
4684
4685
Linus Torvalds1da177e2005-04-16 15:20:36 -07004686
4687int
4688CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004689 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004690{
4691/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4692 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4693 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4694 FILE_SYSTEM_POSIX_INFO *response_data;
4695 int rc = 0;
4696 int bytes_returned = 0;
4697 __u16 params, byte_count;
4698
Joe Perchesb6b38f72010-04-21 03:50:45 +00004699 cFYI(1, "In QFSPosixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004700QFSPosixRetry:
4701 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4702 (void **) &pSMBr);
4703 if (rc)
4704 return rc;
4705
4706 params = 2; /* level */
4707 pSMB->TotalDataCount = 0;
4708 pSMB->DataCount = 0;
4709 pSMB->DataOffset = 0;
4710 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004711 /* BB find exact max SMB PDU from sess structure BB */
4712 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004713 pSMB->MaxSetupCount = 0;
4714 pSMB->Reserved = 0;
4715 pSMB->Flags = 0;
4716 pSMB->Timeout = 0;
4717 pSMB->Reserved2 = 0;
4718 byte_count = params + 1 /* pad */ ;
4719 pSMB->ParameterCount = cpu_to_le16(params);
4720 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004721 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4722 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004723 pSMB->SetupCount = 1;
4724 pSMB->Reserved3 = 0;
4725 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4726 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4727 pSMB->hdr.smb_buf_length += byte_count;
4728 pSMB->ByteCount = cpu_to_le16(byte_count);
4729
4730 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4731 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4732 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004733 cFYI(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004734 } else { /* decode response */
4735 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4736
4737 if (rc || (pSMBr->ByteCount < 13)) {
4738 rc = -EIO; /* bad smb */
4739 } else {
4740 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4741 response_data =
4742 (FILE_SYSTEM_POSIX_INFO
4743 *) (((char *) &pSMBr->hdr.Protocol) +
4744 data_offset);
4745 FSData->f_bsize =
4746 le32_to_cpu(response_data->BlockSize);
4747 FSData->f_blocks =
4748 le64_to_cpu(response_data->TotalBlocks);
4749 FSData->f_bfree =
4750 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004751 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004752 FSData->f_bavail = FSData->f_bfree;
4753 } else {
4754 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004755 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004756 }
Steve French790fe572007-07-07 19:25:05 +00004757 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004758 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004759 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004760 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004761 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004762 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004763 }
4764 }
4765 cifs_buf_release(pSMB);
4766
4767 if (rc == -EAGAIN)
4768 goto QFSPosixRetry;
4769
4770 return rc;
4771}
4772
4773
Steve French50c2f752007-07-13 00:33:32 +00004774/* We can not use write of zero bytes trick to
4775 set file size due to need for large file support. Also note that
4776 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004777 routine which is only needed to work around a sharing violation bug
4778 in Samba which this routine can run into */
4779
4780int
4781CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French4b18f2a2008-04-29 00:06:05 +00004782 __u64 size, bool SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004783 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004784{
4785 struct smb_com_transaction2_spi_req *pSMB = NULL;
4786 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4787 struct file_end_of_file_info *parm_data;
4788 int name_len;
4789 int rc = 0;
4790 int bytes_returned = 0;
4791 __u16 params, byte_count, data_count, param_offset, offset;
4792
Joe Perchesb6b38f72010-04-21 03:50:45 +00004793 cFYI(1, "In SetEOF");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004794SetEOFRetry:
4795 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4796 (void **) &pSMBr);
4797 if (rc)
4798 return rc;
4799
4800 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4801 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004802 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004803 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004804 name_len++; /* trailing null */
4805 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004806 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004807 name_len = strnlen(fileName, PATH_MAX);
4808 name_len++; /* trailing null */
4809 strncpy(pSMB->FileName, fileName, name_len);
4810 }
4811 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004812 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004813 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004814 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004815 pSMB->MaxSetupCount = 0;
4816 pSMB->Reserved = 0;
4817 pSMB->Flags = 0;
4818 pSMB->Timeout = 0;
4819 pSMB->Reserved2 = 0;
4820 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004821 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004822 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004823 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004824 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4825 pSMB->InformationLevel =
4826 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4827 else
4828 pSMB->InformationLevel =
4829 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4830 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004831 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4832 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004833 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004834 else
4835 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004836 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004837 }
4838
4839 parm_data =
4840 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4841 offset);
4842 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4843 pSMB->DataOffset = cpu_to_le16(offset);
4844 pSMB->SetupCount = 1;
4845 pSMB->Reserved3 = 0;
4846 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4847 byte_count = 3 /* pad */ + params + data_count;
4848 pSMB->DataCount = cpu_to_le16(data_count);
4849 pSMB->TotalDataCount = pSMB->DataCount;
4850 pSMB->ParameterCount = cpu_to_le16(params);
4851 pSMB->TotalParameterCount = pSMB->ParameterCount;
4852 pSMB->Reserved4 = 0;
4853 pSMB->hdr.smb_buf_length += byte_count;
4854 parm_data->FileSize = cpu_to_le64(size);
4855 pSMB->ByteCount = cpu_to_le16(byte_count);
4856 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4857 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004858 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00004859 cFYI(1, "SetPathInfo (file size) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004860
4861 cifs_buf_release(pSMB);
4862
4863 if (rc == -EAGAIN)
4864 goto SetEOFRetry;
4865
4866 return rc;
4867}
4868
4869int
Steve French50c2f752007-07-13 00:33:32 +00004870CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
Steve French4b18f2a2008-04-29 00:06:05 +00004871 __u16 fid, __u32 pid_of_opener, bool SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004872{
4873 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004874 char *data_offset;
4875 struct file_end_of_file_info *parm_data;
4876 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004877 __u16 params, param_offset, offset, byte_count, count;
4878
Joe Perchesb6b38f72010-04-21 03:50:45 +00004879 cFYI(1, "SetFileSize (via SetFileInfo) %lld",
4880 (long long)size);
Steve Frenchcd634992005-04-28 22:41:10 -07004881 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4882
Linus Torvalds1da177e2005-04-16 15:20:36 -07004883 if (rc)
4884 return rc;
4885
4886 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4887 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004888
Linus Torvalds1da177e2005-04-16 15:20:36 -07004889 params = 6;
4890 pSMB->MaxSetupCount = 0;
4891 pSMB->Reserved = 0;
4892 pSMB->Flags = 0;
4893 pSMB->Timeout = 0;
4894 pSMB->Reserved2 = 0;
4895 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4896 offset = param_offset + params;
4897
Steve French50c2f752007-07-13 00:33:32 +00004898 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004899
4900 count = sizeof(struct file_end_of_file_info);
4901 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004902 /* BB find exact max SMB PDU from sess structure BB */
4903 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004904 pSMB->SetupCount = 1;
4905 pSMB->Reserved3 = 0;
4906 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4907 byte_count = 3 /* pad */ + params + count;
4908 pSMB->DataCount = cpu_to_le16(count);
4909 pSMB->ParameterCount = cpu_to_le16(params);
4910 pSMB->TotalDataCount = pSMB->DataCount;
4911 pSMB->TotalParameterCount = pSMB->ParameterCount;
4912 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4913 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004914 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4915 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004916 pSMB->DataOffset = cpu_to_le16(offset);
4917 parm_data->FileSize = cpu_to_le64(size);
4918 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004919 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004920 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4921 pSMB->InformationLevel =
4922 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4923 else
4924 pSMB->InformationLevel =
4925 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004926 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004927 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4928 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004929 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004930 else
4931 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004932 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004933 }
4934 pSMB->Reserved4 = 0;
4935 pSMB->hdr.smb_buf_length += byte_count;
4936 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004937 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004938 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004939 cFYI(1, "Send error in SetFileInfo (SetFileSize) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004940 }
4941
Steve French50c2f752007-07-13 00:33:32 +00004942 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004943 since file handle passed in no longer valid */
4944
4945 return rc;
4946}
4947
Steve French50c2f752007-07-13 00:33:32 +00004948/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004949 an open handle, rather than by pathname - this is awkward due to
4950 potential access conflicts on the open, but it is unavoidable for these
4951 old servers since the only other choice is to go from 100 nanosecond DCE
4952 time and resort to the original setpathinfo level which takes the ancient
4953 DOS time format with 2 second granularity */
4954int
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004955CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
4956 const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004957{
4958 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004959 char *data_offset;
4960 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004961 __u16 params, param_offset, offset, byte_count, count;
4962
Joe Perchesb6b38f72010-04-21 03:50:45 +00004963 cFYI(1, "Set Times (via SetFileInfo)");
Steve Frenchcd634992005-04-28 22:41:10 -07004964 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4965
Linus Torvalds1da177e2005-04-16 15:20:36 -07004966 if (rc)
4967 return rc;
4968
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004969 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4970 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004971
Linus Torvalds1da177e2005-04-16 15:20:36 -07004972 params = 6;
4973 pSMB->MaxSetupCount = 0;
4974 pSMB->Reserved = 0;
4975 pSMB->Flags = 0;
4976 pSMB->Timeout = 0;
4977 pSMB->Reserved2 = 0;
4978 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4979 offset = param_offset + params;
4980
Steve French50c2f752007-07-13 00:33:32 +00004981 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004982
Steve French26f57362007-08-30 22:09:15 +00004983 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004984 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004985 /* BB find max SMB PDU from sess */
4986 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004987 pSMB->SetupCount = 1;
4988 pSMB->Reserved3 = 0;
4989 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4990 byte_count = 3 /* pad */ + params + count;
4991 pSMB->DataCount = cpu_to_le16(count);
4992 pSMB->ParameterCount = cpu_to_le16(params);
4993 pSMB->TotalDataCount = pSMB->DataCount;
4994 pSMB->TotalParameterCount = pSMB->ParameterCount;
4995 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4996 pSMB->DataOffset = cpu_to_le16(offset);
4997 pSMB->Fid = fid;
4998 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4999 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5000 else
5001 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5002 pSMB->Reserved4 = 0;
5003 pSMB->hdr.smb_buf_length += byte_count;
5004 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00005005 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00005006 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005007 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005008 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005009
Steve French50c2f752007-07-13 00:33:32 +00005010 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07005011 since file handle passed in no longer valid */
5012
5013 return rc;
5014}
5015
Jeff Layton6d22f092008-09-23 11:48:35 -04005016int
5017CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
5018 bool delete_file, __u16 fid, __u32 pid_of_opener)
5019{
5020 struct smb_com_transaction2_sfi_req *pSMB = NULL;
5021 char *data_offset;
5022 int rc = 0;
5023 __u16 params, param_offset, offset, byte_count, count;
5024
Joe Perchesb6b38f72010-04-21 03:50:45 +00005025 cFYI(1, "Set File Disposition (via SetFileInfo)");
Jeff Layton6d22f092008-09-23 11:48:35 -04005026 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5027
5028 if (rc)
5029 return rc;
5030
5031 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5032 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5033
5034 params = 6;
5035 pSMB->MaxSetupCount = 0;
5036 pSMB->Reserved = 0;
5037 pSMB->Flags = 0;
5038 pSMB->Timeout = 0;
5039 pSMB->Reserved2 = 0;
5040 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5041 offset = param_offset + params;
5042
5043 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5044
5045 count = 1;
5046 pSMB->MaxParameterCount = cpu_to_le16(2);
5047 /* BB find max SMB PDU from sess */
5048 pSMB->MaxDataCount = cpu_to_le16(1000);
5049 pSMB->SetupCount = 1;
5050 pSMB->Reserved3 = 0;
5051 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5052 byte_count = 3 /* pad */ + params + count;
5053 pSMB->DataCount = cpu_to_le16(count);
5054 pSMB->ParameterCount = cpu_to_le16(params);
5055 pSMB->TotalDataCount = pSMB->DataCount;
5056 pSMB->TotalParameterCount = pSMB->ParameterCount;
5057 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5058 pSMB->DataOffset = cpu_to_le16(offset);
5059 pSMB->Fid = fid;
5060 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
5061 pSMB->Reserved4 = 0;
5062 pSMB->hdr.smb_buf_length += byte_count;
5063 pSMB->ByteCount = cpu_to_le16(byte_count);
5064 *data_offset = delete_file ? 1 : 0;
5065 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5066 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005067 cFYI(1, "Send error in SetFileDisposition = %d", rc);
Jeff Layton6d22f092008-09-23 11:48:35 -04005068
5069 return rc;
5070}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005071
5072int
Jeff Layton6fc000e2008-08-02 07:26:12 -04005073CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
5074 const char *fileName, const FILE_BASIC_INFO *data,
5075 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005076{
5077 TRANSACTION2_SPI_REQ *pSMB = NULL;
5078 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5079 int name_len;
5080 int rc = 0;
5081 int bytes_returned = 0;
5082 char *data_offset;
5083 __u16 params, param_offset, offset, byte_count, count;
5084
Joe Perchesb6b38f72010-04-21 03:50:45 +00005085 cFYI(1, "In SetTimes");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005086
5087SetTimesRetry:
5088 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5089 (void **) &pSMBr);
5090 if (rc)
5091 return rc;
5092
5093 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5094 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05005095 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005096 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005097 name_len++; /* trailing null */
5098 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005099 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005100 name_len = strnlen(fileName, PATH_MAX);
5101 name_len++; /* trailing null */
5102 strncpy(pSMB->FileName, fileName, name_len);
5103 }
5104
5105 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005106 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005107 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005108 /* BB find max SMB PDU from sess structure BB */
5109 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005110 pSMB->MaxSetupCount = 0;
5111 pSMB->Reserved = 0;
5112 pSMB->Flags = 0;
5113 pSMB->Timeout = 0;
5114 pSMB->Reserved2 = 0;
5115 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005116 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005117 offset = param_offset + params;
5118 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5119 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5120 pSMB->DataOffset = cpu_to_le16(offset);
5121 pSMB->SetupCount = 1;
5122 pSMB->Reserved3 = 0;
5123 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5124 byte_count = 3 /* pad */ + params + count;
5125
5126 pSMB->DataCount = cpu_to_le16(count);
5127 pSMB->ParameterCount = cpu_to_le16(params);
5128 pSMB->TotalDataCount = pSMB->DataCount;
5129 pSMB->TotalParameterCount = pSMB->ParameterCount;
5130 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5131 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5132 else
5133 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5134 pSMB->Reserved4 = 0;
5135 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00005136 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005137 pSMB->ByteCount = cpu_to_le16(byte_count);
5138 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5139 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005140 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005141 cFYI(1, "SetPathInfo (times) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005142
5143 cifs_buf_release(pSMB);
5144
5145 if (rc == -EAGAIN)
5146 goto SetTimesRetry;
5147
5148 return rc;
5149}
5150
5151/* Can not be used to set time stamps yet (due to old DOS time format) */
5152/* Can be used to set attributes */
5153#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
5154 handling it anyway and NT4 was what we thought it would be needed for
5155 Do not delete it until we prove whether needed for Win9x though */
5156int
5157CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
5158 __u16 dos_attrs, const struct nls_table *nls_codepage)
5159{
5160 SETATTR_REQ *pSMB = NULL;
5161 SETATTR_RSP *pSMBr = NULL;
5162 int rc = 0;
5163 int bytes_returned;
5164 int name_len;
5165
Joe Perchesb6b38f72010-04-21 03:50:45 +00005166 cFYI(1, "In SetAttrLegacy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005167
5168SetAttrLgcyRetry:
5169 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
5170 (void **) &pSMBr);
5171 if (rc)
5172 return rc;
5173
5174 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5175 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005176 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005177 PATH_MAX, nls_codepage);
5178 name_len++; /* trailing null */
5179 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005180 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005181 name_len = strnlen(fileName, PATH_MAX);
5182 name_len++; /* trailing null */
5183 strncpy(pSMB->fileName, fileName, name_len);
5184 }
5185 pSMB->attr = cpu_to_le16(dos_attrs);
5186 pSMB->BufferFormat = 0x04;
5187 pSMB->hdr.smb_buf_length += name_len + 1;
5188 pSMB->ByteCount = cpu_to_le16(name_len + 1);
5189 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5190 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005191 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005192 cFYI(1, "Error in LegacySetAttr = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005193
5194 cifs_buf_release(pSMB);
5195
5196 if (rc == -EAGAIN)
5197 goto SetAttrLgcyRetry;
5198
5199 return rc;
5200}
5201#endif /* temporarily unneeded SetAttr legacy function */
5202
Jeff Layton654cf142009-07-09 20:02:49 -04005203static void
5204cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,
5205 const struct cifs_unix_set_info_args *args)
5206{
5207 u64 mode = args->mode;
5208
5209 /*
5210 * Samba server ignores set of file size to zero due to bugs in some
5211 * older clients, but we should be precise - we use SetFileSize to
5212 * set file size and do not want to truncate file size to zero
5213 * accidently as happened on one Samba server beta by putting
5214 * zero instead of -1 here
5215 */
5216 data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
5217 data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
5218 data_offset->LastStatusChange = cpu_to_le64(args->ctime);
5219 data_offset->LastAccessTime = cpu_to_le64(args->atime);
5220 data_offset->LastModificationTime = cpu_to_le64(args->mtime);
5221 data_offset->Uid = cpu_to_le64(args->uid);
5222 data_offset->Gid = cpu_to_le64(args->gid);
5223 /* better to leave device as zero when it is */
5224 data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
5225 data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
5226 data_offset->Permissions = cpu_to_le64(mode);
5227
5228 if (S_ISREG(mode))
5229 data_offset->Type = cpu_to_le32(UNIX_FILE);
5230 else if (S_ISDIR(mode))
5231 data_offset->Type = cpu_to_le32(UNIX_DIR);
5232 else if (S_ISLNK(mode))
5233 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
5234 else if (S_ISCHR(mode))
5235 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
5236 else if (S_ISBLK(mode))
5237 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
5238 else if (S_ISFIFO(mode))
5239 data_offset->Type = cpu_to_le32(UNIX_FIFO);
5240 else if (S_ISSOCK(mode))
5241 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5242}
5243
Linus Torvalds1da177e2005-04-16 15:20:36 -07005244int
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005245CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon,
5246 const struct cifs_unix_set_info_args *args,
5247 u16 fid, u32 pid_of_opener)
5248{
5249 struct smb_com_transaction2_sfi_req *pSMB = NULL;
5250 FILE_UNIX_BASIC_INFO *data_offset;
5251 int rc = 0;
5252 u16 params, param_offset, offset, byte_count, count;
5253
Joe Perchesb6b38f72010-04-21 03:50:45 +00005254 cFYI(1, "Set Unix Info (via SetFileInfo)");
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005255 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5256
5257 if (rc)
5258 return rc;
5259
5260 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5261 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5262
5263 params = 6;
5264 pSMB->MaxSetupCount = 0;
5265 pSMB->Reserved = 0;
5266 pSMB->Flags = 0;
5267 pSMB->Timeout = 0;
5268 pSMB->Reserved2 = 0;
5269 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5270 offset = param_offset + params;
5271
5272 data_offset = (FILE_UNIX_BASIC_INFO *)
5273 ((char *)(&pSMB->hdr.Protocol) + offset);
5274 count = sizeof(FILE_UNIX_BASIC_INFO);
5275
5276 pSMB->MaxParameterCount = cpu_to_le16(2);
5277 /* BB find max SMB PDU from sess */
5278 pSMB->MaxDataCount = cpu_to_le16(1000);
5279 pSMB->SetupCount = 1;
5280 pSMB->Reserved3 = 0;
5281 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5282 byte_count = 3 /* pad */ + params + count;
5283 pSMB->DataCount = cpu_to_le16(count);
5284 pSMB->ParameterCount = cpu_to_le16(params);
5285 pSMB->TotalDataCount = pSMB->DataCount;
5286 pSMB->TotalParameterCount = pSMB->ParameterCount;
5287 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5288 pSMB->DataOffset = cpu_to_le16(offset);
5289 pSMB->Fid = fid;
5290 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5291 pSMB->Reserved4 = 0;
5292 pSMB->hdr.smb_buf_length += byte_count;
5293 pSMB->ByteCount = cpu_to_le16(byte_count);
5294
5295 cifs_fill_unix_set_info(data_offset, args);
5296
5297 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5298 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005299 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005300
5301 /* Note: On -EAGAIN error only caller can retry on handle based calls
5302 since file handle passed in no longer valid */
5303
5304 return rc;
5305}
5306
5307int
Jeff Layton01ea95e2009-07-09 20:02:49 -04005308CIFSSMBUnixSetPathInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,
5309 const struct cifs_unix_set_info_args *args,
5310 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005311{
5312 TRANSACTION2_SPI_REQ *pSMB = NULL;
5313 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5314 int name_len;
5315 int rc = 0;
5316 int bytes_returned = 0;
5317 FILE_UNIX_BASIC_INFO *data_offset;
5318 __u16 params, param_offset, offset, count, byte_count;
5319
Joe Perchesb6b38f72010-04-21 03:50:45 +00005320 cFYI(1, "In SetUID/GID/Mode");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005321setPermsRetry:
5322 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5323 (void **) &pSMBr);
5324 if (rc)
5325 return rc;
5326
5327 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5328 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005329 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005330 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005331 name_len++; /* trailing null */
5332 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07005333 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005334 name_len = strnlen(fileName, PATH_MAX);
5335 name_len++; /* trailing null */
5336 strncpy(pSMB->FileName, fileName, name_len);
5337 }
5338
5339 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005340 count = sizeof(FILE_UNIX_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005341 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005342 /* BB find max SMB PDU from sess structure BB */
5343 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005344 pSMB->MaxSetupCount = 0;
5345 pSMB->Reserved = 0;
5346 pSMB->Flags = 0;
5347 pSMB->Timeout = 0;
5348 pSMB->Reserved2 = 0;
5349 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005350 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005351 offset = param_offset + params;
5352 data_offset =
5353 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5354 offset);
5355 memset(data_offset, 0, count);
5356 pSMB->DataOffset = cpu_to_le16(offset);
5357 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5358 pSMB->SetupCount = 1;
5359 pSMB->Reserved3 = 0;
5360 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5361 byte_count = 3 /* pad */ + params + count;
5362 pSMB->ParameterCount = cpu_to_le16(params);
5363 pSMB->DataCount = cpu_to_le16(count);
5364 pSMB->TotalParameterCount = pSMB->ParameterCount;
5365 pSMB->TotalDataCount = pSMB->DataCount;
5366 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5367 pSMB->Reserved4 = 0;
5368 pSMB->hdr.smb_buf_length += byte_count;
Steve French50c2f752007-07-13 00:33:32 +00005369
Jeff Layton654cf142009-07-09 20:02:49 -04005370 cifs_fill_unix_set_info(data_offset, args);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005371
5372 pSMB->ByteCount = cpu_to_le16(byte_count);
5373 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5374 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005375 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005376 cFYI(1, "SetPathInfo (perms) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005377
Steve French0d817bc2008-05-22 02:02:03 +00005378 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005379 if (rc == -EAGAIN)
5380 goto setPermsRetry;
5381 return rc;
5382}
5383
Steve French50c2f752007-07-13 00:33:32 +00005384int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005385 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005386 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005387 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005388{
5389 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005390 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5391 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005392 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005393 int bytes_returned;
5394
Joe Perchesb6b38f72010-04-21 03:50:45 +00005395 cFYI(1, "In CIFSSMBNotify for file handle %d", (int)netfid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005396 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005397 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005398 if (rc)
5399 return rc;
5400
5401 pSMB->TotalParameterCount = 0 ;
5402 pSMB->TotalDataCount = 0;
5403 pSMB->MaxParameterCount = cpu_to_le32(2);
5404 /* BB find exact data count max from sess structure BB */
5405 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005406/* BB VERIFY verify which is correct for above BB */
5407 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5408 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5409
Linus Torvalds1da177e2005-04-16 15:20:36 -07005410 pSMB->MaxSetupCount = 4;
5411 pSMB->Reserved = 0;
5412 pSMB->ParameterOffset = 0;
5413 pSMB->DataCount = 0;
5414 pSMB->DataOffset = 0;
5415 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5416 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5417 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005418 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005419 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5420 pSMB->Reserved2 = 0;
5421 pSMB->CompletionFilter = cpu_to_le32(filter);
5422 pSMB->Fid = netfid; /* file handle always le */
5423 pSMB->ByteCount = 0;
5424
5425 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005426 (struct smb_hdr *)pSMBr, &bytes_returned,
5427 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005428 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005429 cFYI(1, "Error in Notify = %d", rc);
Steve Frenchff5dbd92005-08-24 17:10:36 -07005430 } else {
5431 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005432 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005433 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005434 sizeof(struct dir_notify_req),
5435 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005436 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005437 dnotify_req->Pid = pSMB->hdr.Pid;
5438 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5439 dnotify_req->Mid = pSMB->hdr.Mid;
5440 dnotify_req->Tid = pSMB->hdr.Tid;
5441 dnotify_req->Uid = pSMB->hdr.Uid;
5442 dnotify_req->netfid = netfid;
5443 dnotify_req->pfile = pfile;
5444 dnotify_req->filter = filter;
5445 dnotify_req->multishot = multishot;
5446 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005447 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005448 &GlobalDnotifyReqList);
5449 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005450 } else
Steve French47c786e2005-10-11 20:03:18 -07005451 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005452 }
5453 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005454 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005455}
Jeff Layton31c05192010-02-10 16:18:26 -05005456
Linus Torvalds1da177e2005-04-16 15:20:36 -07005457#ifdef CONFIG_CIFS_XATTR
Jeff Layton31c05192010-02-10 16:18:26 -05005458/*
5459 * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common
5460 * function used by listxattr and getxattr type calls. When ea_name is set,
5461 * it looks for that attribute name and stuffs that value into the EAData
5462 * buffer. When ea_name is NULL, it stuffs a list of attribute names into the
5463 * buffer. In both cases, the return value is either the length of the
5464 * resulting data or a negative error code. If EAData is a NULL pointer then
5465 * the data isn't copied to it, but the length is returned.
5466 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005467ssize_t
5468CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
Jeff Layton31c05192010-02-10 16:18:26 -05005469 const unsigned char *searchName, const unsigned char *ea_name,
5470 char *EAData, size_t buf_size,
5471 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005472{
5473 /* BB assumes one setup word */
5474 TRANSACTION2_QPI_REQ *pSMB = NULL;
5475 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5476 int rc = 0;
5477 int bytes_returned;
Jeff Layton6e462b92010-02-10 16:18:26 -05005478 int list_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005479 struct fealist *ea_response_data;
Steve French50c2f752007-07-13 00:33:32 +00005480 struct fea *temp_fea;
5481 char *temp_ptr;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005482 char *end_of_smb;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005483 __u16 params, byte_count, data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005484
Joe Perchesb6b38f72010-04-21 03:50:45 +00005485 cFYI(1, "In Query All EAs path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005486QAllEAsRetry:
5487 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5488 (void **) &pSMBr);
5489 if (rc)
5490 return rc;
5491
5492 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Jeff Layton6e462b92010-02-10 16:18:26 -05005493 list_len =
Steve French50c2f752007-07-13 00:33:32 +00005494 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005495 PATH_MAX, nls_codepage, remap);
Jeff Layton6e462b92010-02-10 16:18:26 -05005496 list_len++; /* trailing null */
5497 list_len *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005498 } else { /* BB improve the check for buffer overruns BB */
Jeff Layton6e462b92010-02-10 16:18:26 -05005499 list_len = strnlen(searchName, PATH_MAX);
5500 list_len++; /* trailing null */
5501 strncpy(pSMB->FileName, searchName, list_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005502 }
5503
Jeff Layton6e462b92010-02-10 16:18:26 -05005504 params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005505 pSMB->TotalDataCount = 0;
5506 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005507 /* BB find exact max SMB PDU from sess structure BB */
Jeff Laytone5296142010-02-10 16:18:26 -05005508 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005509 pSMB->MaxSetupCount = 0;
5510 pSMB->Reserved = 0;
5511 pSMB->Flags = 0;
5512 pSMB->Timeout = 0;
5513 pSMB->Reserved2 = 0;
5514 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005515 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005516 pSMB->DataCount = 0;
5517 pSMB->DataOffset = 0;
5518 pSMB->SetupCount = 1;
5519 pSMB->Reserved3 = 0;
5520 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5521 byte_count = params + 1 /* pad */ ;
5522 pSMB->TotalParameterCount = cpu_to_le16(params);
5523 pSMB->ParameterCount = pSMB->TotalParameterCount;
5524 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5525 pSMB->Reserved4 = 0;
5526 pSMB->hdr.smb_buf_length += byte_count;
5527 pSMB->ByteCount = cpu_to_le16(byte_count);
5528
5529 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5530 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5531 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005532 cFYI(1, "Send error in QueryAllEAs = %d", rc);
Jeff Laytonf0d38682010-02-10 16:18:26 -05005533 goto QAllEAsOut;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005534 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005535
5536
5537 /* BB also check enough total bytes returned */
5538 /* BB we need to improve the validity checking
5539 of these trans2 responses */
5540
5541 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5542 if (rc || (pSMBr->ByteCount < 4)) {
5543 rc = -EIO; /* bad smb */
5544 goto QAllEAsOut;
5545 }
5546
5547 /* check that length of list is not more than bcc */
5548 /* check that each entry does not go beyond length
5549 of list */
5550 /* check that each element of each entry does not
5551 go beyond end of list */
5552 /* validate_trans2_offsets() */
5553 /* BB check if start of smb + data_offset > &bcc+ bcc */
5554
5555 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5556 ea_response_data = (struct fealist *)
5557 (((char *) &pSMBr->hdr.Protocol) + data_offset);
5558
Jeff Layton6e462b92010-02-10 16:18:26 -05005559 list_len = le32_to_cpu(ea_response_data->list_len);
Joe Perchesb6b38f72010-04-21 03:50:45 +00005560 cFYI(1, "ea length %d", list_len);
Jeff Layton6e462b92010-02-10 16:18:26 -05005561 if (list_len <= 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005562 cFYI(1, "empty EA list returned from server");
Jeff Laytonf0d38682010-02-10 16:18:26 -05005563 goto QAllEAsOut;
5564 }
5565
Jeff Layton0cd126b2010-02-10 16:18:26 -05005566 /* make sure list_len doesn't go past end of SMB */
5567 end_of_smb = (char *)pByteArea(&pSMBr->hdr) + BCC(&pSMBr->hdr);
5568 if ((char *)ea_response_data + list_len > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005569 cFYI(1, "EA list appears to go beyond SMB");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005570 rc = -EIO;
5571 goto QAllEAsOut;
5572 }
5573
Jeff Laytonf0d38682010-02-10 16:18:26 -05005574 /* account for ea list len */
Jeff Layton6e462b92010-02-10 16:18:26 -05005575 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005576 temp_fea = ea_response_data->list;
5577 temp_ptr = (char *)temp_fea;
Jeff Layton6e462b92010-02-10 16:18:26 -05005578 while (list_len > 0) {
Steve French122ca002010-02-24 21:56:48 +00005579 unsigned int name_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005580 __u16 value_len;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005581
Jeff Layton6e462b92010-02-10 16:18:26 -05005582 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005583 temp_ptr += 4;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005584 /* make sure we can read name_len and value_len */
5585 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005586 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005587 rc = -EIO;
5588 goto QAllEAsOut;
5589 }
5590
5591 name_len = temp_fea->name_len;
5592 value_len = le16_to_cpu(temp_fea->value_len);
5593 list_len -= name_len + 1 + value_len;
5594 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005595 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005596 rc = -EIO;
5597 goto QAllEAsOut;
5598 }
5599
Jeff Layton31c05192010-02-10 16:18:26 -05005600 if (ea_name) {
5601 if (strncmp(ea_name, temp_ptr, name_len) == 0) {
5602 temp_ptr += name_len + 1;
5603 rc = value_len;
5604 if (buf_size == 0)
5605 goto QAllEAsOut;
5606 if ((size_t)value_len > buf_size) {
5607 rc = -ERANGE;
5608 goto QAllEAsOut;
5609 }
5610 memcpy(EAData, temp_ptr, value_len);
5611 goto QAllEAsOut;
5612 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005613 } else {
Jeff Layton31c05192010-02-10 16:18:26 -05005614 /* account for prefix user. and trailing null */
5615 rc += (5 + 1 + name_len);
5616 if (rc < (int) buf_size) {
5617 memcpy(EAData, "user.", 5);
5618 EAData += 5;
5619 memcpy(EAData, temp_ptr, name_len);
5620 EAData += name_len;
5621 /* null terminate name */
5622 *EAData = 0;
5623 ++EAData;
5624 } else if (buf_size == 0) {
5625 /* skip copy - calc size only */
5626 } else {
5627 /* stop before overrun buffer */
5628 rc = -ERANGE;
5629 break;
5630 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005631 }
Jeff Layton0cd126b2010-02-10 16:18:26 -05005632 temp_ptr += name_len + 1 + value_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005633 temp_fea = (struct fea *)temp_ptr;
5634 }
5635
Jeff Layton31c05192010-02-10 16:18:26 -05005636 /* didn't find the named attribute */
5637 if (ea_name)
5638 rc = -ENODATA;
5639
Jeff Laytonf0d38682010-02-10 16:18:26 -05005640QAllEAsOut:
Steve French0d817bc2008-05-22 02:02:03 +00005641 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005642 if (rc == -EAGAIN)
5643 goto QAllEAsRetry;
5644
5645 return (ssize_t)rc;
5646}
5647
Linus Torvalds1da177e2005-04-16 15:20:36 -07005648int
5649CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005650 const char *ea_name, const void *ea_value,
5651 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5652 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005653{
5654 struct smb_com_transaction2_spi_req *pSMB = NULL;
5655 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5656 struct fealist *parm_data;
5657 int name_len;
5658 int rc = 0;
5659 int bytes_returned = 0;
5660 __u16 params, param_offset, byte_count, offset, count;
5661
Joe Perchesb6b38f72010-04-21 03:50:45 +00005662 cFYI(1, "In SetEA");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005663SetEARetry:
5664 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5665 (void **) &pSMBr);
5666 if (rc)
5667 return rc;
5668
5669 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5670 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005671 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005672 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005673 name_len++; /* trailing null */
5674 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005675 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005676 name_len = strnlen(fileName, PATH_MAX);
5677 name_len++; /* trailing null */
5678 strncpy(pSMB->FileName, fileName, name_len);
5679 }
5680
5681 params = 6 + name_len;
5682
5683 /* done calculating parms using name_len of file name,
5684 now use name_len to calculate length of ea name
5685 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005686 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005687 name_len = 0;
5688 else
Steve French50c2f752007-07-13 00:33:32 +00005689 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005690
Steve Frenchdae5dbd2007-12-30 23:49:57 +00005691 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005692 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005693 /* BB find max SMB PDU from sess */
5694 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005695 pSMB->MaxSetupCount = 0;
5696 pSMB->Reserved = 0;
5697 pSMB->Flags = 0;
5698 pSMB->Timeout = 0;
5699 pSMB->Reserved2 = 0;
5700 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005701 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005702 offset = param_offset + params;
5703 pSMB->InformationLevel =
5704 cpu_to_le16(SMB_SET_FILE_EA);
5705
5706 parm_data =
5707 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5708 offset);
5709 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5710 pSMB->DataOffset = cpu_to_le16(offset);
5711 pSMB->SetupCount = 1;
5712 pSMB->Reserved3 = 0;
5713 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5714 byte_count = 3 /* pad */ + params + count;
5715 pSMB->DataCount = cpu_to_le16(count);
5716 parm_data->list_len = cpu_to_le32(count);
5717 parm_data->list[0].EA_flags = 0;
5718 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005719 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005720 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005721 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005722 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005723 parm_data->list[0].name[name_len] = 0;
5724 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5725 /* caller ensures that ea_value_len is less than 64K but
5726 we need to ensure that it fits within the smb */
5727
Steve French50c2f752007-07-13 00:33:32 +00005728 /*BB add length check to see if it would fit in
5729 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005730 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5731 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005732 memcpy(parm_data->list[0].name+name_len+1,
5733 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005734
5735 pSMB->TotalDataCount = pSMB->DataCount;
5736 pSMB->ParameterCount = cpu_to_le16(params);
5737 pSMB->TotalParameterCount = pSMB->ParameterCount;
5738 pSMB->Reserved4 = 0;
5739 pSMB->hdr.smb_buf_length += byte_count;
5740 pSMB->ByteCount = cpu_to_le16(byte_count);
5741 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5742 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005743 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005744 cFYI(1, "SetPathInfo (EA) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005745
5746 cifs_buf_release(pSMB);
5747
5748 if (rc == -EAGAIN)
5749 goto SetEARetry;
5750
5751 return rc;
5752}
5753
5754#endif