blob: 67acfb3acad271a51be1a1446d4f9828119e6b39 [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;
404 }
Steve Frenchac683922009-05-06 04:16:04 +0000405#ifdef CONFIG_CIFS_EXPERIMENTAL
406 else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
407 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
408 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000409 cFYI(1, "NTLMSSP only mechanism, enable extended security");
Steve Frenchac683922009-05-06 04:16:04 +0000410 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
411 }
412#endif
Steve French50c2f752007-07-13 00:33:32 +0000413
Steve French39798772006-05-31 22:40:51 +0000414 count = 0;
Steve French50c2f752007-07-13 00:33:32 +0000415 for (i = 0; i < CIFS_NUM_PROT; i++) {
Steve French39798772006-05-31 22:40:51 +0000416 strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
417 count += strlen(protocols[i].name) + 1;
418 /* null at end of source and target buffers anyway */
419 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 pSMB->hdr.smb_buf_length += count;
421 pSMB->ByteCount = cpu_to_le16(count);
422
423 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
424 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French50c2f752007-07-13 00:33:32 +0000425 if (rc != 0)
Steve French254e55e2006-06-04 05:53:15 +0000426 goto neg_err_exit;
427
Jeff Layton9bf67e52010-04-24 07:57:46 -0400428 server->dialect = le16_to_cpu(pSMBr->DialectIndex);
429 cFYI(1, "Dialect: %d", server->dialect);
Steve French254e55e2006-06-04 05:53:15 +0000430 /* Check wct = 1 error case */
Jeff Layton9bf67e52010-04-24 07:57:46 -0400431 if ((pSMBr->hdr.WordCount < 13) || (server->dialect == BAD_PROT)) {
Steve French254e55e2006-06-04 05:53:15 +0000432 /* core returns wct = 1, but we do not ask for core - otherwise
Steve French50c2f752007-07-13 00:33:32 +0000433 small wct just comes when dialect index is -1 indicating we
Steve French254e55e2006-06-04 05:53:15 +0000434 could not negotiate a common dialect */
435 rc = -EOPNOTSUPP;
436 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000437#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French790fe572007-07-07 19:25:05 +0000438 } else if ((pSMBr->hdr.WordCount == 13)
Jeff Layton9bf67e52010-04-24 07:57:46 -0400439 && ((server->dialect == LANMAN_PROT)
440 || (server->dialect == LANMAN2_PROT))) {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000441 __s16 tmp;
Steve French50c2f752007-07-13 00:33:32 +0000442 struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
Steve French254e55e2006-06-04 05:53:15 +0000443
Steve French790fe572007-07-07 19:25:05 +0000444 if ((secFlags & CIFSSEC_MAY_LANMAN) ||
Steve French750d1152006-06-27 06:28:30 +0000445 (secFlags & CIFSSEC_MAY_PLNTXT))
Steve French254e55e2006-06-04 05:53:15 +0000446 server->secType = LANMAN;
447 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000448 cERROR(1, "mount failed weak security disabled"
449 " in /proc/fs/cifs/SecurityFlags");
Steve French39798772006-05-31 22:40:51 +0000450 rc = -EOPNOTSUPP;
451 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000452 }
Steve French254e55e2006-06-04 05:53:15 +0000453 server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
454 server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
455 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
Steve French39798772006-05-31 22:40:51 +0000456 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000457 server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
Steve French254e55e2006-06-04 05:53:15 +0000458 GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
459 /* even though we do not use raw we might as well set this
460 accurately, in case we ever find a need for it */
Steve French790fe572007-07-07 19:25:05 +0000461 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
Steve Frencheca6acf2009-02-20 05:43:09 +0000462 server->max_rw = 0xFF00;
Steve French254e55e2006-06-04 05:53:15 +0000463 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
464 } else {
Steve Frencheca6acf2009-02-20 05:43:09 +0000465 server->max_rw = 0;/* do not need to use raw anyway */
Steve French254e55e2006-06-04 05:53:15 +0000466 server->capabilities = CAP_MPX_MODE;
467 }
Steve Frenchb815f1e52006-10-02 05:53:29 +0000468 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
Steve French1a70d652006-10-02 05:59:18 +0000469 if (tmp == -1) {
Steve French25ee4a92006-09-30 00:54:23 +0000470 /* OS/2 often does not set timezone therefore
471 * we must use server time to calc time zone.
Steve Frenchb815f1e52006-10-02 05:53:29 +0000472 * Could deviate slightly from the right zone.
473 * Smallest defined timezone difference is 15 minutes
474 * (i.e. Nepal). Rounding up/down is done to match
475 * this requirement.
Steve French25ee4a92006-09-30 00:54:23 +0000476 */
Steve Frenchb815f1e52006-10-02 05:53:29 +0000477 int val, seconds, remain, result;
Steve French25ee4a92006-09-30 00:54:23 +0000478 struct timespec ts, utc;
479 utc = CURRENT_TIME;
Jeff Laytonc4a2c082009-05-27 09:37:33 -0400480 ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
481 rsp->SrvTime.Time, 0);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000482 cFYI(1, "SrvTime %d sec since 1970 (utc: %d) diff: %d",
Steve French50c2f752007-07-13 00:33:32 +0000483 (int)ts.tv_sec, (int)utc.tv_sec,
Joe Perchesb6b38f72010-04-21 03:50:45 +0000484 (int)(utc.tv_sec - ts.tv_sec));
Steve Frenchb815f1e52006-10-02 05:53:29 +0000485 val = (int)(utc.tv_sec - ts.tv_sec);
Andre Haupt8594c152007-08-30 20:18:41 +0000486 seconds = abs(val);
Steve French947a5062006-10-02 05:55:25 +0000487 result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000488 remain = seconds % MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000489 if (remain >= (MIN_TZ_ADJ / 2))
Steve Frenchb815f1e52006-10-02 05:53:29 +0000490 result += MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000491 if (val < 0)
Steve Frenchad7a2922008-02-07 23:25:02 +0000492 result = -result;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000493 server->timeAdj = result;
Steve French25ee4a92006-09-30 00:54:23 +0000494 } else {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000495 server->timeAdj = (int)tmp;
496 server->timeAdj *= 60; /* also in seconds */
Steve French25ee4a92006-09-30 00:54:23 +0000497 }
Joe Perchesb6b38f72010-04-21 03:50:45 +0000498 cFYI(1, "server->timeAdj: %d seconds", server->timeAdj);
Steve French25ee4a92006-09-30 00:54:23 +0000499
Steve French39798772006-05-31 22:40:51 +0000500
Steve French254e55e2006-06-04 05:53:15 +0000501 /* BB get server time for time conversions and add
Steve French50c2f752007-07-13 00:33:32 +0000502 code to use it and timezone since this is not UTC */
Steve French39798772006-05-31 22:40:51 +0000503
Steve French50c2f752007-07-13 00:33:32 +0000504 if (rsp->EncryptionKeyLength ==
Steve French25ee4a92006-09-30 00:54:23 +0000505 cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
Shirish Pargaonkard3ba50b2010-10-27 15:20:36 -0500506 memcpy(ses->server->cryptkey, rsp->EncryptionKey,
Steve French254e55e2006-06-04 05:53:15 +0000507 CIFS_CRYPTO_KEY_SIZE);
508 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
509 rc = -EIO; /* need cryptkey unless plain text */
510 goto neg_err_exit;
511 }
Steve French39798772006-05-31 22:40:51 +0000512
Steve Frenchf19159d2010-04-21 04:12:10 +0000513 cFYI(1, "LANMAN negotiated");
Steve French254e55e2006-06-04 05:53:15 +0000514 /* we will not end up setting signing flags - as no signing
515 was in LANMAN and server did not return the flags on */
516 goto signing_check;
Steve French7c7b25b2006-06-01 19:20:10 +0000517#else /* weak security disabled */
Steve French790fe572007-07-07 19:25:05 +0000518 } else if (pSMBr->hdr.WordCount == 13) {
Steve Frenchf19159d2010-04-21 04:12:10 +0000519 cERROR(1, "mount failed, cifs module not built "
520 "with CIFS_WEAK_PW_HASH support");
Dan Carpenter8212cf72010-03-15 11:22:26 +0300521 rc = -EOPNOTSUPP;
Steve French7c7b25b2006-06-01 19:20:10 +0000522#endif /* WEAK_PW_HASH */
Steve French254e55e2006-06-04 05:53:15 +0000523 goto neg_err_exit;
Steve French790fe572007-07-07 19:25:05 +0000524 } else if (pSMBr->hdr.WordCount != 17) {
Steve French254e55e2006-06-04 05:53:15 +0000525 /* unknown wct */
526 rc = -EOPNOTSUPP;
527 goto neg_err_exit;
528 }
529 /* else wct == 17 NTLM */
530 server->secMode = pSMBr->SecurityMode;
Steve French790fe572007-07-07 19:25:05 +0000531 if ((server->secMode & SECMODE_USER) == 0)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000532 cFYI(1, "share mode security");
Steve French39798772006-05-31 22:40:51 +0000533
Steve French790fe572007-07-07 19:25:05 +0000534 if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000535#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French750d1152006-06-27 06:28:30 +0000536 if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000537#endif /* CIFS_WEAK_PW_HASH */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000538 cERROR(1, "Server requests plain text password"
539 " but client support disabled");
Steve French9312f672006-06-04 22:21:07 +0000540
Steve French790fe572007-07-07 19:25:05 +0000541 if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
Steve French254e55e2006-06-04 05:53:15 +0000542 server->secType = NTLMv2;
Steve French790fe572007-07-07 19:25:05 +0000543 else if (secFlags & CIFSSEC_MAY_NTLM)
Steve French254e55e2006-06-04 05:53:15 +0000544 server->secType = NTLM;
Steve French790fe572007-07-07 19:25:05 +0000545 else if (secFlags & CIFSSEC_MAY_NTLMV2)
Steve Frenchf40c5622006-06-28 00:13:38 +0000546 server->secType = NTLMv2;
Steve Frencha0136892007-10-04 20:05:09 +0000547 else if (secFlags & CIFSSEC_MAY_KRB5)
548 server->secType = Kerberos;
Steve Frenchac683922009-05-06 04:16:04 +0000549 else if (secFlags & CIFSSEC_MAY_NTLMSSP)
Steve Frenchf46c7232009-06-25 03:04:20 +0000550 server->secType = RawNTLMSSP;
Steve Frencha0136892007-10-04 20:05:09 +0000551 else if (secFlags & CIFSSEC_MAY_LANMAN)
552 server->secType = LANMAN;
553/* #ifdef CONFIG_CIFS_EXPERIMENTAL
554 else if (secFlags & CIFSSEC_MAY_PLNTXT)
555 server->secType = ??
556#endif */
557 else {
558 rc = -EOPNOTSUPP;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000559 cERROR(1, "Invalid security type");
Steve Frencha0136892007-10-04 20:05:09 +0000560 goto neg_err_exit;
561 }
562 /* else ... any others ...? */
Steve French7c7b25b2006-06-01 19:20:10 +0000563
Steve French254e55e2006-06-04 05:53:15 +0000564 /* one byte, so no need to convert this or EncryptionKeyLen from
565 little endian */
566 server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
567 /* probably no need to store and check maxvcs */
568 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000570 server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000571 cFYI(DBG2, "Max buf = %d", ses->server->maxBuf);
Steve French254e55e2006-06-04 05:53:15 +0000572 GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
573 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
Steve Frenchb815f1e52006-10-02 05:53:29 +0000574 server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
575 server->timeAdj *= 60;
Steve French254e55e2006-06-04 05:53:15 +0000576 if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
Shirish Pargaonkard3ba50b2010-10-27 15:20:36 -0500577 memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey,
Steve French254e55e2006-06-04 05:53:15 +0000578 CIFS_CRYPTO_KEY_SIZE);
579 } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
580 && (pSMBr->EncryptionKeyLength == 0)) {
581 /* decode security blob */
582 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
583 rc = -EIO; /* no crypt key only if plain text pwd */
584 goto neg_err_exit;
585 }
586
587 /* BB might be helpful to save off the domain of server here */
588
Steve French50c2f752007-07-13 00:33:32 +0000589 if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
Steve French254e55e2006-06-04 05:53:15 +0000590 (server->capabilities & CAP_EXTENDED_SECURITY)) {
591 count = pSMBr->ByteCount;
Jeff Laytone187e442007-10-16 17:10:44 +0000592 if (count < 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 rc = -EIO;
Jeff Laytone187e442007-10-16 17:10:44 +0000594 goto neg_err_exit;
595 }
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530596 spin_lock(&cifs_tcp_ses_lock);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500597 if (server->srv_count > 1) {
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530598 spin_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000599 if (memcmp(server->server_GUID,
600 pSMBr->u.extended_response.
601 GUID, 16) != 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000602 cFYI(1, "server UID changed");
Steve French254e55e2006-06-04 05:53:15 +0000603 memcpy(server->server_GUID,
Jeff Laytone187e442007-10-16 17:10:44 +0000604 pSMBr->u.extended_response.GUID,
605 16);
606 }
Jeff Laytone7ddee92008-11-14 13:44:38 -0500607 } else {
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530608 spin_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000609 memcpy(server->server_GUID,
610 pSMBr->u.extended_response.GUID, 16);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500611 }
Jeff Laytone187e442007-10-16 17:10:44 +0000612
613 if (count == 16) {
614 server->secType = RawNTLMSSP;
Steve French254e55e2006-06-04 05:53:15 +0000615 } else {
616 rc = decode_negTokenInit(pSMBr->u.extended_response.
Jeff Layton26efa0b2010-04-24 07:57:49 -0400617 SecurityBlob, count - 16,
618 server);
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000619 if (rc == 1)
Jeff Laytone5459372007-11-03 05:11:06 +0000620 rc = 0;
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000621 else
Steve French254e55e2006-06-04 05:53:15 +0000622 rc = -EINVAL;
Shirish Pargaonkar2b149f12010-09-18 22:02:18 -0500623 if (server->secType == Kerberos) {
624 if (!server->sec_kerberos &&
625 !server->sec_mskerberos)
626 rc = -EOPNOTSUPP;
627 } else if (server->secType == RawNTLMSSP) {
628 if (!server->sec_ntlmssp)
629 rc = -EOPNOTSUPP;
630 } else
631 rc = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 }
Steve French254e55e2006-06-04 05:53:15 +0000633 } else
634 server->capabilities &= ~CAP_EXTENDED_SECURITY;
635
Steve French6344a422006-06-12 04:18:35 +0000636#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French254e55e2006-06-04 05:53:15 +0000637signing_check:
Steve French6344a422006-06-12 04:18:35 +0000638#endif
Steve French762e5ab2007-06-28 18:41:42 +0000639 if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
640 /* MUST_SIGN already includes the MAY_SIGN FLAG
641 so if this is zero it means that signing is disabled */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000642 cFYI(1, "Signing disabled");
Steve Frenchabb63d62007-10-18 02:58:40 +0000643 if (server->secMode & SECMODE_SIGN_REQUIRED) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000644 cERROR(1, "Server requires "
Jeff Layton7111d212007-10-16 16:50:25 +0000645 "packet signing to be enabled in "
Joe Perchesb6b38f72010-04-21 03:50:45 +0000646 "/proc/fs/cifs/SecurityFlags.");
Steve Frenchabb63d62007-10-18 02:58:40 +0000647 rc = -EOPNOTSUPP;
648 }
Steve French50c2f752007-07-13 00:33:32 +0000649 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000650 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Steve French762e5ab2007-06-28 18:41:42 +0000651 } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
652 /* signing required */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000653 cFYI(1, "Must sign - secFlags 0x%x", secFlags);
Steve French762e5ab2007-06-28 18:41:42 +0000654 if ((server->secMode &
655 (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000656 cERROR(1, "signing required but server lacks support");
Jeff38c10a12007-07-06 21:10:07 +0000657 rc = -EOPNOTSUPP;
Steve French762e5ab2007-06-28 18:41:42 +0000658 } else
659 server->secMode |= SECMODE_SIGN_REQUIRED;
660 } else {
661 /* signing optional ie CIFSSEC_MAY_SIGN */
Steve French790fe572007-07-07 19:25:05 +0000662 if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
Steve French50c2f752007-07-13 00:33:32 +0000663 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000664 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 }
Steve French50c2f752007-07-13 00:33:32 +0000666
667neg_err_exit:
Steve French4a6d87f2005-08-13 08:15:54 -0700668 cifs_buf_release(pSMB);
Steve French254e55e2006-06-04 05:53:15 +0000669
Joe Perchesb6b38f72010-04-21 03:50:45 +0000670 cFYI(1, "negprot rc %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 return rc;
672}
673
674int
675CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
676{
677 struct smb_hdr *smb_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679
Joe Perchesb6b38f72010-04-21 03:50:45 +0000680 cFYI(1, "In tree disconnect");
Jeff Laytonf1987b42008-11-15 11:12:47 -0500681
682 /* BB: do we need to check this? These should never be NULL. */
683 if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
684 return -EIO;
685
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 /*
Jeff Laytonf1987b42008-11-15 11:12:47 -0500687 * No need to return error on this operation if tid invalidated and
688 * closed on server already e.g. due to tcp session crashing. Also,
689 * the tcon is no longer on the list, so no need to take lock before
690 * checking this.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 */
Steve French268875b2009-06-25 00:29:21 +0000692 if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
Steve French50c2f752007-07-13 00:33:32 +0000693 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Steve French50c2f752007-07-13 00:33:32 +0000695 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
Steve French09d1db52005-04-28 22:41:08 -0700696 (void **)&smb_buffer);
Jeff Laytonf1987b42008-11-15 11:12:47 -0500697 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 return rc;
Steve French133672e2007-11-13 22:41:37 +0000699
700 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000702 cFYI(1, "Tree disconnect failed %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703
Steve French50c2f752007-07-13 00:33:32 +0000704 /* No need to return error on this operation if tid invalidated and
Jeff Laytonf1987b42008-11-15 11:12:47 -0500705 closed on server already e.g. due to tcp session crashing */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 if (rc == -EAGAIN)
707 rc = 0;
708
709 return rc;
710}
711
712int
713CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
714{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 LOGOFF_ANDX_REQ *pSMB;
716 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717
Joe Perchesb6b38f72010-04-21 03:50:45 +0000718 cFYI(1, "In SMBLogoff for session disconnect");
Jeff Layton14fbf502008-11-14 13:53:46 -0500719
720 /*
721 * BB: do we need to check validity of ses and server? They should
722 * always be valid since we have an active reference. If not, that
723 * should probably be a BUG()
724 */
725 if (!ses || !ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 return -EIO;
727
Steve Frenchd7b619c2010-02-25 05:36:46 +0000728 mutex_lock(&ses->session_mutex);
Steve French3b795212008-11-13 19:45:32 +0000729 if (ses->need_reconnect)
730 goto session_already_dead; /* no need to send SMBlogoff if uid
731 already closed due to reconnect */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
733 if (rc) {
Steve Frenchd7b619c2010-02-25 05:36:46 +0000734 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 return rc;
736 }
737
Steve French3b795212008-11-13 19:45:32 +0000738 pSMB->hdr.Mid = GetNextMid(ses->server);
Steve French1982c342005-08-17 12:38:22 -0700739
Steve French3b795212008-11-13 19:45:32 +0000740 if (ses->server->secMode &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
742 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
744 pSMB->hdr.Uid = ses->Suid;
745
746 pSMB->AndXCommand = 0xFF;
Steve French133672e2007-11-13 22:41:37 +0000747 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
Steve French3b795212008-11-13 19:45:32 +0000748session_already_dead:
Steve Frenchd7b619c2010-02-25 05:36:46 +0000749 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
751 /* if session dead then we do not need to do ulogoff,
Steve French50c2f752007-07-13 00:33:32 +0000752 since server closed smb session, no sense reporting
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 error */
754 if (rc == -EAGAIN)
755 rc = 0;
756 return rc;
757}
758
759int
Steve French2d785a52007-07-15 01:48:57 +0000760CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
761 __u16 type, const struct nls_table *nls_codepage, int remap)
762{
763 TRANSACTION2_SPI_REQ *pSMB = NULL;
764 TRANSACTION2_SPI_RSP *pSMBr = NULL;
765 struct unlink_psx_rq *pRqD;
766 int name_len;
767 int rc = 0;
768 int bytes_returned = 0;
769 __u16 params, param_offset, offset, byte_count;
770
Joe Perchesb6b38f72010-04-21 03:50:45 +0000771 cFYI(1, "In POSIX delete");
Steve French2d785a52007-07-15 01:48:57 +0000772PsxDelete:
773 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
774 (void **) &pSMBr);
775 if (rc)
776 return rc;
777
778 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
779 name_len =
780 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
781 PATH_MAX, nls_codepage, remap);
782 name_len++; /* trailing null */
783 name_len *= 2;
784 } else { /* BB add path length overrun check */
785 name_len = strnlen(fileName, PATH_MAX);
786 name_len++; /* trailing null */
787 strncpy(pSMB->FileName, fileName, name_len);
788 }
789
790 params = 6 + name_len;
791 pSMB->MaxParameterCount = cpu_to_le16(2);
792 pSMB->MaxDataCount = 0; /* BB double check this with jra */
793 pSMB->MaxSetupCount = 0;
794 pSMB->Reserved = 0;
795 pSMB->Flags = 0;
796 pSMB->Timeout = 0;
797 pSMB->Reserved2 = 0;
798 param_offset = offsetof(struct smb_com_transaction2_spi_req,
799 InformationLevel) - 4;
800 offset = param_offset + params;
801
802 /* Setup pointer to Request Data (inode type) */
803 pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
804 pRqD->type = cpu_to_le16(type);
805 pSMB->ParameterOffset = cpu_to_le16(param_offset);
806 pSMB->DataOffset = cpu_to_le16(offset);
807 pSMB->SetupCount = 1;
808 pSMB->Reserved3 = 0;
809 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
810 byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
811
812 pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
813 pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
814 pSMB->ParameterCount = cpu_to_le16(params);
815 pSMB->TotalParameterCount = pSMB->ParameterCount;
816 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
817 pSMB->Reserved4 = 0;
818 pSMB->hdr.smb_buf_length += byte_count;
819 pSMB->ByteCount = cpu_to_le16(byte_count);
820 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
821 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +0000822 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000823 cFYI(1, "Posix delete returned %d", rc);
Steve French2d785a52007-07-15 01:48:57 +0000824 cifs_buf_release(pSMB);
825
826 cifs_stats_inc(&tcon->num_deletes);
827
828 if (rc == -EAGAIN)
829 goto PsxDelete;
830
831 return rc;
832}
833
834int
Steve French737b7582005-04-28 22:41:06 -0700835CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
836 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837{
838 DELETE_FILE_REQ *pSMB = NULL;
839 DELETE_FILE_RSP *pSMBr = NULL;
840 int rc = 0;
841 int bytes_returned;
842 int name_len;
843
844DelFileRetry:
845 rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
846 (void **) &pSMBr);
847 if (rc)
848 return rc;
849
850 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
851 name_len =
Steve French50c2f752007-07-13 00:33:32 +0000852 cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
Steve French737b7582005-04-28 22:41:06 -0700853 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 name_len++; /* trailing null */
855 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700856 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 name_len = strnlen(fileName, PATH_MAX);
858 name_len++; /* trailing null */
859 strncpy(pSMB->fileName, fileName, name_len);
860 }
861 pSMB->SearchAttributes =
862 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
863 pSMB->BufferFormat = 0x04;
864 pSMB->hdr.smb_buf_length += name_len + 1;
865 pSMB->ByteCount = cpu_to_le16(name_len + 1);
866 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
867 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700868 cifs_stats_inc(&tcon->num_deletes);
Steve Frenchad7a2922008-02-07 23:25:02 +0000869 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000870 cFYI(1, "Error in RMFile = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871
872 cifs_buf_release(pSMB);
873 if (rc == -EAGAIN)
874 goto DelFileRetry;
875
876 return rc;
877}
878
879int
Steve French50c2f752007-07-13 00:33:32 +0000880CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
Steve French737b7582005-04-28 22:41:06 -0700881 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882{
883 DELETE_DIRECTORY_REQ *pSMB = NULL;
884 DELETE_DIRECTORY_RSP *pSMBr = NULL;
885 int rc = 0;
886 int bytes_returned;
887 int name_len;
888
Joe Perchesb6b38f72010-04-21 03:50:45 +0000889 cFYI(1, "In CIFSSMBRmDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890RmDirRetry:
891 rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
892 (void **) &pSMBr);
893 if (rc)
894 return rc;
895
896 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French737b7582005-04-28 22:41:06 -0700897 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
898 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 name_len++; /* trailing null */
900 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700901 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 name_len = strnlen(dirName, PATH_MAX);
903 name_len++; /* trailing null */
904 strncpy(pSMB->DirName, dirName, name_len);
905 }
906
907 pSMB->BufferFormat = 0x04;
908 pSMB->hdr.smb_buf_length += name_len + 1;
909 pSMB->ByteCount = cpu_to_le16(name_len + 1);
910 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
911 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700912 cifs_stats_inc(&tcon->num_rmdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000913 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000914 cFYI(1, "Error in RMDir = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
916 cifs_buf_release(pSMB);
917 if (rc == -EAGAIN)
918 goto RmDirRetry;
919 return rc;
920}
921
922int
923CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -0700924 const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925{
926 int rc = 0;
927 CREATE_DIRECTORY_REQ *pSMB = NULL;
928 CREATE_DIRECTORY_RSP *pSMBr = NULL;
929 int bytes_returned;
930 int name_len;
931
Joe Perchesb6b38f72010-04-21 03:50:45 +0000932 cFYI(1, "In CIFSSMBMkDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933MkDirRetry:
934 rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
935 (void **) &pSMBr);
936 if (rc)
937 return rc;
938
939 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +0000940 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
Steve French737b7582005-04-28 22:41:06 -0700941 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 name_len++; /* trailing null */
943 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700944 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 name_len = strnlen(name, PATH_MAX);
946 name_len++; /* trailing null */
947 strncpy(pSMB->DirName, name, name_len);
948 }
949
950 pSMB->BufferFormat = 0x04;
951 pSMB->hdr.smb_buf_length += name_len + 1;
952 pSMB->ByteCount = cpu_to_le16(name_len + 1);
953 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
954 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700955 cifs_stats_inc(&tcon->num_mkdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000956 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000957 cFYI(1, "Error in Mkdir = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -0700958
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 cifs_buf_release(pSMB);
960 if (rc == -EAGAIN)
961 goto MkDirRetry;
962 return rc;
963}
964
Steve French2dd29d32007-04-23 22:07:35 +0000965int
966CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
Steve Frenchad7a2922008-02-07 23:25:02 +0000967 __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
Steve French50c2f752007-07-13 00:33:32 +0000968 __u32 *pOplock, const char *name,
Steve French2dd29d32007-04-23 22:07:35 +0000969 const struct nls_table *nls_codepage, int remap)
970{
971 TRANSACTION2_SPI_REQ *pSMB = NULL;
972 TRANSACTION2_SPI_RSP *pSMBr = NULL;
973 int name_len;
974 int rc = 0;
975 int bytes_returned = 0;
Steve French2dd29d32007-04-23 22:07:35 +0000976 __u16 params, param_offset, offset, byte_count, count;
Steve Frenchad7a2922008-02-07 23:25:02 +0000977 OPEN_PSX_REQ *pdata;
978 OPEN_PSX_RSP *psx_rsp;
Steve French2dd29d32007-04-23 22:07:35 +0000979
Joe Perchesb6b38f72010-04-21 03:50:45 +0000980 cFYI(1, "In POSIX Create");
Steve French2dd29d32007-04-23 22:07:35 +0000981PsxCreat:
982 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
983 (void **) &pSMBr);
984 if (rc)
985 return rc;
986
987 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
988 name_len =
989 cifsConvertToUCS((__le16 *) pSMB->FileName, name,
990 PATH_MAX, nls_codepage, remap);
991 name_len++; /* trailing null */
992 name_len *= 2;
993 } else { /* BB improve the check for buffer overruns BB */
994 name_len = strnlen(name, PATH_MAX);
995 name_len++; /* trailing null */
996 strncpy(pSMB->FileName, name, name_len);
997 }
998
999 params = 6 + name_len;
1000 count = sizeof(OPEN_PSX_REQ);
1001 pSMB->MaxParameterCount = cpu_to_le16(2);
1002 pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
1003 pSMB->MaxSetupCount = 0;
1004 pSMB->Reserved = 0;
1005 pSMB->Flags = 0;
1006 pSMB->Timeout = 0;
1007 pSMB->Reserved2 = 0;
1008 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00001009 InformationLevel) - 4;
Steve French2dd29d32007-04-23 22:07:35 +00001010 offset = param_offset + params;
Steve French2dd29d32007-04-23 22:07:35 +00001011 pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001012 pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
Steve French2dd29d32007-04-23 22:07:35 +00001013 pdata->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00001014 pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
Steve French2dd29d32007-04-23 22:07:35 +00001015 pdata->OpenFlags = cpu_to_le32(*pOplock);
1016 pSMB->ParameterOffset = cpu_to_le16(param_offset);
1017 pSMB->DataOffset = cpu_to_le16(offset);
1018 pSMB->SetupCount = 1;
1019 pSMB->Reserved3 = 0;
1020 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
1021 byte_count = 3 /* pad */ + params + count;
1022
1023 pSMB->DataCount = cpu_to_le16(count);
1024 pSMB->ParameterCount = cpu_to_le16(params);
1025 pSMB->TotalDataCount = pSMB->DataCount;
1026 pSMB->TotalParameterCount = pSMB->ParameterCount;
1027 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1028 pSMB->Reserved4 = 0;
Steve French50c2f752007-07-13 00:33:32 +00001029 pSMB->hdr.smb_buf_length += byte_count;
Steve French2dd29d32007-04-23 22:07:35 +00001030 pSMB->ByteCount = cpu_to_le16(byte_count);
1031 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1032 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1033 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001034 cFYI(1, "Posix create returned %d", rc);
Steve French2dd29d32007-04-23 22:07:35 +00001035 goto psx_create_err;
1036 }
1037
Joe Perchesb6b38f72010-04-21 03:50:45 +00001038 cFYI(1, "copying inode info");
Steve French2dd29d32007-04-23 22:07:35 +00001039 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1040
1041 if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
1042 rc = -EIO; /* bad smb */
1043 goto psx_create_err;
1044 }
1045
1046 /* copy return information to pRetData */
Steve French50c2f752007-07-13 00:33:32 +00001047 psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
Steve French2dd29d32007-04-23 22:07:35 +00001048 + le16_to_cpu(pSMBr->t2.DataOffset));
Steve French50c2f752007-07-13 00:33:32 +00001049
Steve French2dd29d32007-04-23 22:07:35 +00001050 *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
Steve French790fe572007-07-07 19:25:05 +00001051 if (netfid)
Steve French2dd29d32007-04-23 22:07:35 +00001052 *netfid = psx_rsp->Fid; /* cifs fid stays in le */
1053 /* Let caller know file was created so we can set the mode. */
1054 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001055 if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
Steve French2dd29d32007-04-23 22:07:35 +00001056 *pOplock |= CIFS_CREATE_ACTION;
1057 /* check to make sure response data is there */
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001058 if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1059 pRetData->Type = cpu_to_le32(-1); /* unknown */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001060 cFYI(DBG2, "unknown type");
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001061 } else {
Steve French790fe572007-07-07 19:25:05 +00001062 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001063 + sizeof(FILE_UNIX_BASIC_INFO)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001064 cERROR(1, "Open response data too small");
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001065 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001066 goto psx_create_err;
1067 }
Steve French50c2f752007-07-13 00:33:32 +00001068 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001069 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001070 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001071 }
Steve French2dd29d32007-04-23 22:07:35 +00001072
1073psx_create_err:
1074 cifs_buf_release(pSMB);
1075
Steve French65bc98b2009-07-10 15:27:25 +00001076 if (posix_flags & SMB_O_DIRECTORY)
1077 cifs_stats_inc(&tcon->num_posixmkdirs);
1078 else
1079 cifs_stats_inc(&tcon->num_posixopens);
Steve French2dd29d32007-04-23 22:07:35 +00001080
1081 if (rc == -EAGAIN)
1082 goto PsxCreat;
1083
Steve French50c2f752007-07-13 00:33:32 +00001084 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001085}
1086
Steve Frencha9d02ad2005-08-24 23:06:05 -07001087static __u16 convert_disposition(int disposition)
1088{
1089 __u16 ofun = 0;
1090
1091 switch (disposition) {
1092 case FILE_SUPERSEDE:
1093 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1094 break;
1095 case FILE_OPEN:
1096 ofun = SMBOPEN_OAPPEND;
1097 break;
1098 case FILE_CREATE:
1099 ofun = SMBOPEN_OCREATE;
1100 break;
1101 case FILE_OPEN_IF:
1102 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1103 break;
1104 case FILE_OVERWRITE:
1105 ofun = SMBOPEN_OTRUNC;
1106 break;
1107 case FILE_OVERWRITE_IF:
1108 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1109 break;
1110 default:
Joe Perchesb6b38f72010-04-21 03:50:45 +00001111 cFYI(1, "unknown disposition %d", disposition);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001112 ofun = SMBOPEN_OAPPEND; /* regular open */
1113 }
1114 return ofun;
1115}
1116
Jeff Layton35fc37d2008-05-14 10:22:03 -07001117static int
1118access_flags_to_smbopen_mode(const int access_flags)
1119{
1120 int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
1121
1122 if (masked_flags == GENERIC_READ)
1123 return SMBOPEN_READ;
1124 else if (masked_flags == GENERIC_WRITE)
1125 return SMBOPEN_WRITE;
1126
1127 /* just go for read/write */
1128 return SMBOPEN_READWRITE;
1129}
1130
Steve Frencha9d02ad2005-08-24 23:06:05 -07001131int
1132SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1133 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001134 const int access_flags, const int create_options, __u16 *netfid,
1135 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001136 const struct nls_table *nls_codepage, int remap)
1137{
1138 int rc = -EACCES;
1139 OPENX_REQ *pSMB = NULL;
1140 OPENX_RSP *pSMBr = NULL;
1141 int bytes_returned;
1142 int name_len;
1143 __u16 count;
1144
1145OldOpenRetry:
1146 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1147 (void **) &pSMBr);
1148 if (rc)
1149 return rc;
1150
1151 pSMB->AndXCommand = 0xFF; /* none */
1152
1153 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1154 count = 1; /* account for one byte pad to word boundary */
1155 name_len =
1156 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1157 fileName, PATH_MAX, nls_codepage, remap);
1158 name_len++; /* trailing null */
1159 name_len *= 2;
1160 } else { /* BB improve check for buffer overruns BB */
1161 count = 0; /* no pad */
1162 name_len = strnlen(fileName, PATH_MAX);
1163 name_len++; /* trailing null */
1164 strncpy(pSMB->fileName, fileName, name_len);
1165 }
1166 if (*pOplock & REQ_OPLOCK)
1167 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001168 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001169 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001170
Steve Frencha9d02ad2005-08-24 23:06:05 -07001171 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
Jeff Layton35fc37d2008-05-14 10:22:03 -07001172 pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001173 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1174 /* set file as system file if special file such
1175 as fifo and server expecting SFU style and
1176 no Unix extensions */
1177
Steve French790fe572007-07-07 19:25:05 +00001178 if (create_options & CREATE_OPTION_SPECIAL)
1179 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001180 else /* BB FIXME BB */
1181 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001182
Jeff Layton67750fb2008-05-09 22:28:02 +00001183 if (create_options & CREATE_OPTION_READONLY)
1184 pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001185
1186 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001187/* pSMB->CreateOptions = cpu_to_le32(create_options &
1188 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001189 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001190
1191 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001192 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001193 count += name_len;
1194 pSMB->hdr.smb_buf_length += count;
1195
1196 pSMB->ByteCount = cpu_to_le16(count);
1197 /* long_op set to 1 to allow for oplock break timeouts */
1198 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001199 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001200 cifs_stats_inc(&tcon->num_opens);
1201 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001202 cFYI(1, "Error in Open = %d", rc);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001203 } else {
1204 /* BB verify if wct == 15 */
1205
Steve French582d21e2008-05-13 04:54:12 +00001206/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
Steve Frencha9d02ad2005-08-24 23:06:05 -07001207
1208 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1209 /* Let caller know file was created so we can set the mode. */
1210 /* Do we care about the CreateAction in any other cases? */
1211 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001212/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001213 *pOplock |= CIFS_CREATE_ACTION; */
1214 /* BB FIXME END */
1215
Steve French790fe572007-07-07 19:25:05 +00001216 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001217 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1218 pfile_info->LastAccessTime = 0; /* BB fixme */
1219 pfile_info->LastWriteTime = 0; /* BB fixme */
1220 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001221 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001222 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001223 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001224 pfile_info->AllocationSize =
1225 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1226 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001227 pfile_info->NumberOfLinks = cpu_to_le32(1);
Jeff Layton9a8165f2008-10-17 21:03:20 -04001228 pfile_info->DeletePending = 0;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001229 }
1230 }
1231
1232 cifs_buf_release(pSMB);
1233 if (rc == -EAGAIN)
1234 goto OldOpenRetry;
1235 return rc;
1236}
1237
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238int
1239CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1240 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001241 const int access_flags, const int create_options, __u16 *netfid,
1242 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001243 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244{
1245 int rc = -EACCES;
1246 OPEN_REQ *pSMB = NULL;
1247 OPEN_RSP *pSMBr = NULL;
1248 int bytes_returned;
1249 int name_len;
1250 __u16 count;
1251
1252openRetry:
1253 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1254 (void **) &pSMBr);
1255 if (rc)
1256 return rc;
1257
1258 pSMB->AndXCommand = 0xFF; /* none */
1259
1260 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1261 count = 1; /* account for one byte pad to word boundary */
1262 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001263 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001264 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 name_len++; /* trailing null */
1266 name_len *= 2;
1267 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001268 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 count = 0; /* no pad */
1270 name_len = strnlen(fileName, PATH_MAX);
1271 name_len++; /* trailing null */
1272 pSMB->NameLength = cpu_to_le16(name_len);
1273 strncpy(pSMB->fileName, fileName, name_len);
1274 }
1275 if (*pOplock & REQ_OPLOCK)
1276 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001277 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1280 pSMB->AllocationSize = 0;
Steve Frencheda3c0292005-07-21 15:20:28 -07001281 /* set file as system file if special file such
1282 as fifo and server expecting SFU style and
1283 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001284 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c0292005-07-21 15:20:28 -07001285 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1286 else
1287 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Jeff Layton67750fb2008-05-09 22:28:02 +00001288
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 /* XP does not handle ATTR_POSIX_SEMANTICS */
1290 /* but it helps speed up case sensitive checks for other
1291 servers such as Samba */
1292 if (tcon->ses->capabilities & CAP_UNIX)
1293 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1294
Jeff Layton67750fb2008-05-09 22:28:02 +00001295 if (create_options & CREATE_OPTION_READONLY)
1296 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);
1297
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1299 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c0292005-07-21 15:20:28 -07001300 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001301 /* BB Expirement with various impersonation levels and verify */
1302 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 pSMB->SecurityFlags =
1304 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1305
1306 count += name_len;
1307 pSMB->hdr.smb_buf_length += count;
1308
1309 pSMB->ByteCount = cpu_to_le16(count);
1310 /* long_op set to 1 to allow for oplock break timeouts */
1311 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001312 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha4544342005-08-24 13:59:35 -07001313 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001315 cFYI(1, "Error in Open = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 } else {
Steve French09d1db52005-04-28 22:41:08 -07001317 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1319 /* Let caller know file was created so we can set the mode. */
1320 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001321 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001322 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001323 if (pfile_info) {
Steve French61e74802008-12-03 00:57:54 +00001324 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
1325 36 /* CreationTime to Attributes */);
1326 /* the file_info buf is endian converted by caller */
1327 pfile_info->AllocationSize = pSMBr->AllocationSize;
1328 pfile_info->EndOfFile = pSMBr->EndOfFile;
1329 pfile_info->NumberOfLinks = cpu_to_le32(1);
1330 pfile_info->DeletePending = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001333
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334 cifs_buf_release(pSMB);
1335 if (rc == -EAGAIN)
1336 goto openRetry;
1337 return rc;
1338}
1339
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340int
Steve French50c2f752007-07-13 00:33:32 +00001341CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1342 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1343 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344{
1345 int rc = -EACCES;
1346 READ_REQ *pSMB = NULL;
1347 READ_RSP *pSMBr = NULL;
1348 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001349 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001350 int resp_buf_type = 0;
1351 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
Joe Perchesb6b38f72010-04-21 03:50:45 +00001353 cFYI(1, "Reading %d bytes on fid %d", count, netfid);
Steve French790fe572007-07-07 19:25:05 +00001354 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001355 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001356 else {
Steve Frenchbfa0d752005-08-31 21:50:37 -07001357 wct = 10; /* old style read */
Steve French4c3130e2008-12-09 00:28:16 +00001358 if ((lseek >> 32) > 0) {
1359 /* can not handle this big offset for old */
1360 return -EIO;
1361 }
1362 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
1364 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001365 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 if (rc)
1367 return rc;
1368
1369 /* tcon and ses pointer are checked in smb_init */
1370 if (tcon->ses->server == NULL)
1371 return -ECONNABORTED;
1372
Steve Frenchec637e32005-12-12 20:53:18 -08001373 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 pSMB->Fid = netfid;
1375 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001376 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001377 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve Frenchbfa0d752005-08-31 21:50:37 -07001378
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 pSMB->Remaining = 0;
1380 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1381 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001382 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001383 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1384 else {
1385 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001386 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001387 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001388 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001389 }
Steve Frenchec637e32005-12-12 20:53:18 -08001390
1391 iov[0].iov_base = (char *)pSMB;
1392 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001393 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Steve French133672e2007-11-13 22:41:37 +00001394 &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001395 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001396 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001398 cERROR(1, "Send error in read = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 } else {
1400 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1401 data_length = data_length << 16;
1402 data_length += le16_to_cpu(pSMBr->DataLength);
1403 *nbytes = data_length;
1404
1405 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001406 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 || (data_length > count)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001408 cFYI(1, "bad length %d for count %d",
1409 data_length, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 rc = -EIO;
1411 *nbytes = 0;
1412 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001413 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001414 le16_to_cpu(pSMBr->DataOffset);
1415/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001416 cERROR(1, "Faulting on read rc = %d",rc);
Steve French50c2f752007-07-13 00:33:32 +00001417 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001418 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001419 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001420 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 }
1422 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423
Steve French4b8f9302006-02-26 16:41:18 +00001424/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001425 if (*buf) {
1426 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001427 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001428 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001429 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001430 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001431 /* return buffer to caller to free */
1432 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001433 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001434 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001435 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001436 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001437 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001438
1439 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 since file handle passed in no longer valid */
1441 return rc;
1442}
1443
Steve Frenchec637e32005-12-12 20:53:18 -08001444
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445int
1446CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1447 const int netfid, const unsigned int count,
1448 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001449 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450{
1451 int rc = -EACCES;
1452 WRITE_REQ *pSMB = NULL;
1453 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001454 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 __u32 bytes_sent;
1456 __u16 byte_count;
1457
Steve Frencha24e2d72010-04-03 17:20:21 +00001458 *nbytes = 0;
1459
Joe Perchesb6b38f72010-04-21 03:50:45 +00001460 /* cFYI(1, "write at %lld %d bytes", offset, count);*/
Steve French790fe572007-07-07 19:25:05 +00001461 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001462 return -ECONNABORTED;
1463
Steve French790fe572007-07-07 19:25:05 +00001464 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001465 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001466 else {
Steve French1c955182005-08-30 20:58:07 -07001467 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001468 if ((offset >> 32) > 0) {
1469 /* can not handle big offset for old srv */
1470 return -EIO;
1471 }
1472 }
Steve French1c955182005-08-30 20:58:07 -07001473
1474 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475 (void **) &pSMBr);
1476 if (rc)
1477 return rc;
1478 /* tcon and ses pointer are checked in smb_init */
1479 if (tcon->ses->server == NULL)
1480 return -ECONNABORTED;
1481
1482 pSMB->AndXCommand = 0xFF; /* none */
1483 pSMB->Fid = netfid;
1484 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001485 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001486 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French50c2f752007-07-13 00:33:32 +00001487
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 pSMB->Reserved = 0xFFFFFFFF;
1489 pSMB->WriteMode = 0;
1490 pSMB->Remaining = 0;
1491
Steve French50c2f752007-07-13 00:33:32 +00001492 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 can send more if LARGE_WRITE_X capability returned by the server and if
1494 our buffer is big enough or if we convert to iovecs on socket writes
1495 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001496 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1498 } else {
1499 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1500 & ~0xFF;
1501 }
1502
1503 if (bytes_sent > count)
1504 bytes_sent = count;
1505 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001506 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001507 if (buf)
Steve French61e74802008-12-03 00:57:54 +00001508 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001509 else if (ubuf) {
1510 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 cifs_buf_release(pSMB);
1512 return -EFAULT;
1513 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001514 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 /* No buffer */
1516 cifs_buf_release(pSMB);
1517 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001518 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001519 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001520 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001521 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001522 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001523
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1525 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001526 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001527
Steve French790fe572007-07-07 19:25:05 +00001528 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001529 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001530 else { /* old style write has byte count 4 bytes earlier
1531 so 4 bytes pad */
1532 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001533 (struct smb_com_writex_req *)pSMB;
1534 pSMBW->ByteCount = cpu_to_le16(byte_count);
1535 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
1537 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1538 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001539 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00001541 cFYI(1, "Send error in write = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 } else {
1543 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1544 *nbytes = (*nbytes) << 16;
1545 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301546
1547 /*
1548 * Mask off high 16 bits when bytes written as returned by the
1549 * server is greater than bytes requested by the client. Some
1550 * OS/2 servers are known to set incorrect CountHigh values.
1551 */
1552 if (*nbytes > count)
1553 *nbytes &= 0xFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 }
1555
1556 cifs_buf_release(pSMB);
1557
Steve French50c2f752007-07-13 00:33:32 +00001558 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 since file handle passed in no longer valid */
1560
1561 return rc;
1562}
1563
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001564int
1565CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001567 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1568 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569{
1570 int rc = -EACCES;
1571 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001572 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001573 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001574 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575
Jeff Laytonfbec9ab2009-04-03 13:44:00 -04001576 *nbytes = 0;
1577
Joe Perchesb6b38f72010-04-21 03:50:45 +00001578 cFYI(1, "write2 at %lld %d bytes", (long long)offset, count);
Steve Frenchff7feac2005-11-15 16:45:16 -08001579
Steve French4c3130e2008-12-09 00:28:16 +00001580 if (tcon->ses->capabilities & CAP_LARGE_FILES) {
Steve French8cc64c62005-10-03 13:49:43 -07001581 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001582 } else {
Steve French8cc64c62005-10-03 13:49:43 -07001583 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001584 if ((offset >> 32) > 0) {
1585 /* can not handle big offset for old srv */
1586 return -EIO;
1587 }
1588 }
Steve French8cc64c62005-10-03 13:49:43 -07001589 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 if (rc)
1591 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592 /* tcon and ses pointer are checked in smb_init */
1593 if (tcon->ses->server == NULL)
1594 return -ECONNABORTED;
1595
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001596 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597 pSMB->Fid = netfid;
1598 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001599 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001600 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601 pSMB->Reserved = 0xFFFFFFFF;
1602 pSMB->WriteMode = 0;
1603 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001604
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001606 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607
Steve French3e844692005-10-03 13:37:24 -07001608 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1609 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001610 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001611 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001612 pSMB->hdr.smb_buf_length += count+1;
1613 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001614 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1615 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001616 pSMB->ByteCount = cpu_to_le16(count + 1);
1617 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001618 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001619 (struct smb_com_writex_req *)pSMB;
1620 pSMBW->ByteCount = cpu_to_le16(count + 5);
1621 }
Steve French3e844692005-10-03 13:37:24 -07001622 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001623 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001624 iov[0].iov_len = smb_hdr_len + 4;
1625 else /* wct == 12 pad bigger by four bytes */
1626 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001627
Steve French3e844692005-10-03 13:37:24 -07001628
Steve Frenchec637e32005-12-12 20:53:18 -08001629 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001630 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001631 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001633 cFYI(1, "Send error Write2 = %d", rc);
Steve French790fe572007-07-07 19:25:05 +00001634 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001635 /* presumably this can not happen, but best to be safe */
1636 rc = -EIO;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001637 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001638 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001639 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1640 *nbytes = (*nbytes) << 16;
1641 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301642
1643 /*
1644 * Mask off high 16 bits when bytes written as returned by the
1645 * server is greater than bytes requested by the client. OS/2
1646 * servers are known to set incorrect CountHigh values.
1647 */
1648 if (*nbytes > count)
1649 *nbytes &= 0xFFFF;
Steve French50c2f752007-07-13 00:33:32 +00001650 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
Steve French4b8f9302006-02-26 16:41:18 +00001652/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001653 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001654 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001655 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001656 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657
Steve French50c2f752007-07-13 00:33:32 +00001658 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 since file handle passed in no longer valid */
1660
1661 return rc;
1662}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001663
1664
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665int
1666CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1667 const __u16 smb_file_id, const __u64 len,
1668 const __u64 offset, const __u32 numUnlock,
Steve French4b18f2a2008-04-29 00:06:05 +00001669 const __u32 numLock, const __u8 lockType, const bool waitFlag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670{
1671 int rc = 0;
1672 LOCK_REQ *pSMB = NULL;
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001673/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 int bytes_returned;
1675 int timeout = 0;
1676 __u16 count;
1677
Joe Perchesb6b38f72010-04-21 03:50:45 +00001678 cFYI(1, "CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock);
Steve French46810cb2005-04-28 22:41:09 -07001679 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1680
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 if (rc)
1682 return rc;
1683
Steve French790fe572007-07-07 19:25:05 +00001684 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001685 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 pSMB->Timeout = 0;
Steve French4b18f2a2008-04-29 00:06:05 +00001687 } else if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001688 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1690 } else {
1691 pSMB->Timeout = 0;
1692 }
1693
1694 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1695 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1696 pSMB->LockType = lockType;
1697 pSMB->AndXCommand = 0xFF; /* none */
1698 pSMB->Fid = smb_file_id; /* netfid stays le */
1699
Steve French790fe572007-07-07 19:25:05 +00001700 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1702 /* BB where to store pid high? */
1703 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1704 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1705 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1706 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1707 count = sizeof(LOCKING_ANDX_RANGE);
1708 } else {
1709 /* oplock break */
1710 count = 0;
1711 }
1712 pSMB->hdr.smb_buf_length += count;
1713 pSMB->ByteCount = cpu_to_le16(count);
1714
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001715 if (waitFlag) {
1716 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001717 (struct smb_hdr *) pSMB, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001718 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001719 } else {
Steve French133672e2007-11-13 22:41:37 +00001720 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1721 timeout);
1722 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001723 }
Steve Frencha4544342005-08-24 13:59:35 -07001724 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001725 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001726 cFYI(1, "Send error in Lock = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727
Steve French50c2f752007-07-13 00:33:32 +00001728 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729 since file handle passed in no longer valid */
1730 return rc;
1731}
1732
1733int
Steve French08547b02006-02-28 22:39:25 +00001734CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1735 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001736 struct file_lock *pLockData, const __u16 lock_type,
Steve French4b18f2a2008-04-29 00:06:05 +00001737 const bool waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001738{
1739 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1740 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001741 struct cifs_posix_lock *parm_data;
1742 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001743 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001744 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001745 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001746 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001747 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001748
Joe Perchesb6b38f72010-04-21 03:50:45 +00001749 cFYI(1, "Posix Lock");
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001750
Steve French790fe572007-07-07 19:25:05 +00001751 if (pLockData == NULL)
Marcin Slusarzed5f0372008-05-13 04:01:01 +00001752 return -EINVAL;
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001753
Steve French08547b02006-02-28 22:39:25 +00001754 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1755
1756 if (rc)
1757 return rc;
1758
1759 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1760
Steve French50c2f752007-07-13 00:33:32 +00001761 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001762 pSMB->MaxSetupCount = 0;
1763 pSMB->Reserved = 0;
1764 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001765 pSMB->Reserved2 = 0;
1766 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1767 offset = param_offset + params;
1768
Steve French08547b02006-02-28 22:39:25 +00001769 count = sizeof(struct cifs_posix_lock);
1770 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001771 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001772 pSMB->SetupCount = 1;
1773 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001774 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001775 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1776 else
1777 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1778 byte_count = 3 /* pad */ + params + count;
1779 pSMB->DataCount = cpu_to_le16(count);
1780 pSMB->ParameterCount = cpu_to_le16(params);
1781 pSMB->TotalDataCount = pSMB->DataCount;
1782 pSMB->TotalParameterCount = pSMB->ParameterCount;
1783 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001784 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001785 (((char *) &pSMB->hdr.Protocol) + offset);
1786
1787 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001788 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001789 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001790 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001791 pSMB->Timeout = cpu_to_le32(-1);
1792 } else
1793 pSMB->Timeout = 0;
1794
Steve French08547b02006-02-28 22:39:25 +00001795 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001796 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001797 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001798
1799 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001800 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001801 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1802 pSMB->Reserved4 = 0;
1803 pSMB->hdr.smb_buf_length += byte_count;
1804 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001805 if (waitFlag) {
1806 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1807 (struct smb_hdr *) pSMBr, &bytes_returned);
1808 } else {
Steve French133672e2007-11-13 22:41:37 +00001809 iov[0].iov_base = (char *)pSMB;
1810 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1811 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1812 &resp_buf_type, timeout);
1813 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1814 not try to free it twice below on exit */
1815 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001816 }
1817
Steve French08547b02006-02-28 22:39:25 +00001818 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001819 cFYI(1, "Send error in Posix Lock = %d", rc);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001820 } else if (get_flag) {
1821 /* lock structure can be returned on get */
1822 __u16 data_offset;
1823 __u16 data_count;
1824 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001825
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001826 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1827 rc = -EIO; /* bad smb */
1828 goto plk_err_exit;
1829 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001830 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1831 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001832 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001833 rc = -EIO;
1834 goto plk_err_exit;
1835 }
1836 parm_data = (struct cifs_posix_lock *)
1837 ((char *)&pSMBr->hdr.Protocol + data_offset);
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001838 if (parm_data->lock_type == __constant_cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001839 pLockData->fl_type = F_UNLCK;
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001840 else {
1841 if (parm_data->lock_type ==
1842 __constant_cpu_to_le16(CIFS_RDLCK))
1843 pLockData->fl_type = F_RDLCK;
1844 else if (parm_data->lock_type ==
1845 __constant_cpu_to_le16(CIFS_WRLCK))
1846 pLockData->fl_type = F_WRLCK;
1847
1848 pLockData->fl_start = parm_data->start;
1849 pLockData->fl_end = parm_data->start +
1850 parm_data->length - 1;
1851 pLockData->fl_pid = parm_data->pid;
1852 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001853 }
Steve French50c2f752007-07-13 00:33:32 +00001854
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001855plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001856 if (pSMB)
1857 cifs_small_buf_release(pSMB);
1858
Steve French133672e2007-11-13 22:41:37 +00001859 if (resp_buf_type == CIFS_SMALL_BUFFER)
1860 cifs_small_buf_release(iov[0].iov_base);
1861 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1862 cifs_buf_release(iov[0].iov_base);
1863
Steve French08547b02006-02-28 22:39:25 +00001864 /* Note: On -EAGAIN error only caller can retry on handle based calls
1865 since file handle passed in no longer valid */
1866
1867 return rc;
1868}
1869
1870
1871int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1873{
1874 int rc = 0;
1875 CLOSE_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001876 cFYI(1, "In CIFSSMBClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877
1878/* do not retry on dead session on close */
1879 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001880 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 return 0;
1882 if (rc)
1883 return rc;
1884
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e52006-10-02 05:53:29 +00001886 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001888 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001889 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001891 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 /* EINTR is expected when user ctl-c to kill app */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001893 cERROR(1, "Send error in Close = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894 }
1895 }
1896
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001898 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 rc = 0;
1900
1901 return rc;
1902}
1903
1904int
Steve Frenchb298f222009-02-21 21:17:43 +00001905CIFSSMBFlush(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1906{
1907 int rc = 0;
1908 FLUSH_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001909 cFYI(1, "In CIFSSMBFlush");
Steve Frenchb298f222009-02-21 21:17:43 +00001910
1911 rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB);
1912 if (rc)
1913 return rc;
1914
1915 pSMB->FileID = (__u16) smb_file_id;
1916 pSMB->ByteCount = 0;
1917 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
1918 cifs_stats_inc(&tcon->num_flushes);
1919 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001920 cERROR(1, "Send error in Flush = %d", rc);
Steve Frenchb298f222009-02-21 21:17:43 +00001921
1922 return rc;
1923}
1924
1925int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1927 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001928 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929{
1930 int rc = 0;
1931 RENAME_REQ *pSMB = NULL;
1932 RENAME_RSP *pSMBr = NULL;
1933 int bytes_returned;
1934 int name_len, name_len2;
1935 __u16 count;
1936
Joe Perchesb6b38f72010-04-21 03:50:45 +00001937 cFYI(1, "In CIFSSMBRename");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938renameRetry:
1939 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1940 (void **) &pSMBr);
1941 if (rc)
1942 return rc;
1943
1944 pSMB->BufferFormat = 0x04;
1945 pSMB->SearchAttributes =
1946 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1947 ATTR_DIRECTORY);
1948
1949 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1950 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001951 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001952 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953 name_len++; /* trailing null */
1954 name_len *= 2;
1955 pSMB->OldFileName[name_len] = 0x04; /* pad */
1956 /* protocol requires ASCII signature byte on Unicode string */
1957 pSMB->OldFileName[name_len + 1] = 0x00;
1958 name_len2 =
Steve French582d21e2008-05-13 04:54:12 +00001959 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07001960 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
1962 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00001963 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964 name_len = strnlen(fromName, PATH_MAX);
1965 name_len++; /* trailing null */
1966 strncpy(pSMB->OldFileName, fromName, name_len);
1967 name_len2 = strnlen(toName, PATH_MAX);
1968 name_len2++; /* trailing null */
1969 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
1970 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
1971 name_len2++; /* trailing null */
1972 name_len2++; /* signature byte */
1973 }
1974
1975 count = 1 /* 1st signature byte */ + name_len + name_len2;
1976 pSMB->hdr.smb_buf_length += count;
1977 pSMB->ByteCount = cpu_to_le16(count);
1978
1979 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1980 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001981 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00001982 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001983 cFYI(1, "Send error in rename = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 cifs_buf_release(pSMB);
1986
1987 if (rc == -EAGAIN)
1988 goto renameRetry;
1989
1990 return rc;
1991}
1992
Steve French50c2f752007-07-13 00:33:32 +00001993int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
Jeff Layton391e5752008-09-24 11:32:59 -04001994 int netfid, const char *target_name,
Steve French50c2f752007-07-13 00:33:32 +00001995 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996{
1997 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1998 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00001999 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 char *data_offset;
2001 char dummy_string[30];
2002 int rc = 0;
2003 int bytes_returned = 0;
2004 int len_of_str;
2005 __u16 params, param_offset, offset, count, byte_count;
2006
Joe Perchesb6b38f72010-04-21 03:50:45 +00002007 cFYI(1, "Rename to File by handle");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
2009 (void **) &pSMBr);
2010 if (rc)
2011 return rc;
2012
2013 params = 6;
2014 pSMB->MaxSetupCount = 0;
2015 pSMB->Reserved = 0;
2016 pSMB->Flags = 0;
2017 pSMB->Timeout = 0;
2018 pSMB->Reserved2 = 0;
2019 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2020 offset = param_offset + params;
2021
2022 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2023 rename_info = (struct set_file_rename *) data_offset;
2024 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002025 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026 pSMB->SetupCount = 1;
2027 pSMB->Reserved3 = 0;
2028 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2029 byte_count = 3 /* pad */ + params;
2030 pSMB->ParameterCount = cpu_to_le16(params);
2031 pSMB->TotalParameterCount = pSMB->ParameterCount;
2032 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2033 pSMB->DataOffset = cpu_to_le16(offset);
2034 /* construct random name ".cifs_tmp<inodenum><mid>" */
2035 rename_info->overwrite = cpu_to_le32(1);
2036 rename_info->root_fid = 0;
2037 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002038 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002039 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2040 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002041 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002043 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002044 target_name, PATH_MAX, nls_codepage,
2045 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 }
2047 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
Jeff Layton391e5752008-09-24 11:32:59 -04002048 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049 byte_count += count;
2050 pSMB->DataCount = cpu_to_le16(count);
2051 pSMB->TotalDataCount = pSMB->DataCount;
2052 pSMB->Fid = netfid;
2053 pSMB->InformationLevel =
2054 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2055 pSMB->Reserved4 = 0;
2056 pSMB->hdr.smb_buf_length += byte_count;
2057 pSMB->ByteCount = cpu_to_le16(byte_count);
2058 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002059 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002060 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002061 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002062 cFYI(1, "Send error in Rename (by file handle) = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -07002063
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 cifs_buf_release(pSMB);
2065
2066 /* Note: On -EAGAIN error only caller can retry on handle based calls
2067 since file handle passed in no longer valid */
2068
2069 return rc;
2070}
2071
2072int
Steve French50c2f752007-07-13 00:33:32 +00002073CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2074 const __u16 target_tid, const char *toName, const int flags,
2075 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076{
2077 int rc = 0;
2078 COPY_REQ *pSMB = NULL;
2079 COPY_RSP *pSMBr = NULL;
2080 int bytes_returned;
2081 int name_len, name_len2;
2082 __u16 count;
2083
Joe Perchesb6b38f72010-04-21 03:50:45 +00002084 cFYI(1, "In CIFSSMBCopy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085copyRetry:
2086 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2087 (void **) &pSMBr);
2088 if (rc)
2089 return rc;
2090
2091 pSMB->BufferFormat = 0x04;
2092 pSMB->Tid2 = target_tid;
2093
2094 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2095
2096 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002097 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002098 fromName, PATH_MAX, nls_codepage,
2099 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100 name_len++; /* trailing null */
2101 name_len *= 2;
2102 pSMB->OldFileName[name_len] = 0x04; /* pad */
2103 /* protocol requires ASCII signature byte on Unicode string */
2104 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002105 name_len2 =
2106 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002107 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2109 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002110 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 name_len = strnlen(fromName, PATH_MAX);
2112 name_len++; /* trailing null */
2113 strncpy(pSMB->OldFileName, fromName, name_len);
2114 name_len2 = strnlen(toName, PATH_MAX);
2115 name_len2++; /* trailing null */
2116 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2117 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2118 name_len2++; /* trailing null */
2119 name_len2++; /* signature byte */
2120 }
2121
2122 count = 1 /* 1st signature byte */ + name_len + name_len2;
2123 pSMB->hdr.smb_buf_length += count;
2124 pSMB->ByteCount = cpu_to_le16(count);
2125
2126 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2127 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2128 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002129 cFYI(1, "Send error in copy = %d with %d files copied",
2130 rc, le16_to_cpu(pSMBr->CopyCount));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 }
Steve French0d817bc2008-05-22 02:02:03 +00002132 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133
2134 if (rc == -EAGAIN)
2135 goto copyRetry;
2136
2137 return rc;
2138}
2139
2140int
2141CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2142 const char *fromName, const char *toName,
2143 const struct nls_table *nls_codepage)
2144{
2145 TRANSACTION2_SPI_REQ *pSMB = NULL;
2146 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2147 char *data_offset;
2148 int name_len;
2149 int name_len_target;
2150 int rc = 0;
2151 int bytes_returned = 0;
2152 __u16 params, param_offset, offset, byte_count;
2153
Joe Perchesb6b38f72010-04-21 03:50:45 +00002154 cFYI(1, "In Symlink Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155createSymLinkRetry:
2156 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2157 (void **) &pSMBr);
2158 if (rc)
2159 return rc;
2160
2161 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2162 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002163 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164 /* find define for this maxpathcomponent */
2165 , nls_codepage);
2166 name_len++; /* trailing null */
2167 name_len *= 2;
2168
Steve French50c2f752007-07-13 00:33:32 +00002169 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 name_len = strnlen(fromName, PATH_MAX);
2171 name_len++; /* trailing null */
2172 strncpy(pSMB->FileName, fromName, name_len);
2173 }
2174 params = 6 + name_len;
2175 pSMB->MaxSetupCount = 0;
2176 pSMB->Reserved = 0;
2177 pSMB->Flags = 0;
2178 pSMB->Timeout = 0;
2179 pSMB->Reserved2 = 0;
2180 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002181 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182 offset = param_offset + params;
2183
2184 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2185 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2186 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002187 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 /* find define for this maxpathcomponent */
2189 , nls_codepage);
2190 name_len_target++; /* trailing null */
2191 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002192 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193 name_len_target = strnlen(toName, PATH_MAX);
2194 name_len_target++; /* trailing null */
2195 strncpy(data_offset, toName, name_len_target);
2196 }
2197
2198 pSMB->MaxParameterCount = cpu_to_le16(2);
2199 /* BB find exact max on data count below from sess */
2200 pSMB->MaxDataCount = cpu_to_le16(1000);
2201 pSMB->SetupCount = 1;
2202 pSMB->Reserved3 = 0;
2203 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2204 byte_count = 3 /* pad */ + params + name_len_target;
2205 pSMB->DataCount = cpu_to_le16(name_len_target);
2206 pSMB->ParameterCount = cpu_to_le16(params);
2207 pSMB->TotalDataCount = pSMB->DataCount;
2208 pSMB->TotalParameterCount = pSMB->ParameterCount;
2209 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2210 pSMB->DataOffset = cpu_to_le16(offset);
2211 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2212 pSMB->Reserved4 = 0;
2213 pSMB->hdr.smb_buf_length += byte_count;
2214 pSMB->ByteCount = cpu_to_le16(byte_count);
2215 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2216 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002217 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002218 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002219 cFYI(1, "Send error in SetPathInfo create symlink = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220
Steve French0d817bc2008-05-22 02:02:03 +00002221 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222
2223 if (rc == -EAGAIN)
2224 goto createSymLinkRetry;
2225
2226 return rc;
2227}
2228
2229int
2230CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2231 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002232 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233{
2234 TRANSACTION2_SPI_REQ *pSMB = NULL;
2235 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2236 char *data_offset;
2237 int name_len;
2238 int name_len_target;
2239 int rc = 0;
2240 int bytes_returned = 0;
2241 __u16 params, param_offset, offset, byte_count;
2242
Joe Perchesb6b38f72010-04-21 03:50:45 +00002243 cFYI(1, "In Create Hard link Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244createHardLinkRetry:
2245 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2246 (void **) &pSMBr);
2247 if (rc)
2248 return rc;
2249
2250 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002251 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002252 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 name_len++; /* trailing null */
2254 name_len *= 2;
2255
Steve French50c2f752007-07-13 00:33:32 +00002256 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 name_len = strnlen(toName, PATH_MAX);
2258 name_len++; /* trailing null */
2259 strncpy(pSMB->FileName, toName, name_len);
2260 }
2261 params = 6 + name_len;
2262 pSMB->MaxSetupCount = 0;
2263 pSMB->Reserved = 0;
2264 pSMB->Flags = 0;
2265 pSMB->Timeout = 0;
2266 pSMB->Reserved2 = 0;
2267 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002268 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 offset = param_offset + params;
2270
2271 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2272 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2273 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002274 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002275 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 name_len_target++; /* trailing null */
2277 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002278 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 name_len_target = strnlen(fromName, PATH_MAX);
2280 name_len_target++; /* trailing null */
2281 strncpy(data_offset, fromName, name_len_target);
2282 }
2283
2284 pSMB->MaxParameterCount = cpu_to_le16(2);
2285 /* BB find exact max on data count below from sess*/
2286 pSMB->MaxDataCount = cpu_to_le16(1000);
2287 pSMB->SetupCount = 1;
2288 pSMB->Reserved3 = 0;
2289 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2290 byte_count = 3 /* pad */ + params + name_len_target;
2291 pSMB->ParameterCount = cpu_to_le16(params);
2292 pSMB->TotalParameterCount = pSMB->ParameterCount;
2293 pSMB->DataCount = cpu_to_le16(name_len_target);
2294 pSMB->TotalDataCount = pSMB->DataCount;
2295 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2296 pSMB->DataOffset = cpu_to_le16(offset);
2297 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2298 pSMB->Reserved4 = 0;
2299 pSMB->hdr.smb_buf_length += byte_count;
2300 pSMB->ByteCount = cpu_to_le16(byte_count);
2301 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2302 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002303 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002304 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002305 cFYI(1, "Send error in SetPathInfo (hard link) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306
2307 cifs_buf_release(pSMB);
2308 if (rc == -EAGAIN)
2309 goto createHardLinkRetry;
2310
2311 return rc;
2312}
2313
2314int
2315CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2316 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002317 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318{
2319 int rc = 0;
2320 NT_RENAME_REQ *pSMB = NULL;
2321 RENAME_RSP *pSMBr = NULL;
2322 int bytes_returned;
2323 int name_len, name_len2;
2324 __u16 count;
2325
Joe Perchesb6b38f72010-04-21 03:50:45 +00002326 cFYI(1, "In CIFSCreateHardLink");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327winCreateHardLinkRetry:
2328
2329 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2330 (void **) &pSMBr);
2331 if (rc)
2332 return rc;
2333
2334 pSMB->SearchAttributes =
2335 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2336 ATTR_DIRECTORY);
2337 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2338 pSMB->ClusterCount = 0;
2339
2340 pSMB->BufferFormat = 0x04;
2341
2342 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2343 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002344 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002345 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346 name_len++; /* trailing null */
2347 name_len *= 2;
Jeff Laytonfcc7c092009-02-28 12:59:03 -05002348
2349 /* protocol specifies ASCII buffer format (0x04) for unicode */
2350 pSMB->OldFileName[name_len] = 0x04;
2351 pSMB->OldFileName[name_len + 1] = 0x00; /* pad */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002352 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002353 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002354 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2356 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002357 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358 name_len = strnlen(fromName, PATH_MAX);
2359 name_len++; /* trailing null */
2360 strncpy(pSMB->OldFileName, fromName, name_len);
2361 name_len2 = strnlen(toName, PATH_MAX);
2362 name_len2++; /* trailing null */
2363 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2364 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2365 name_len2++; /* trailing null */
2366 name_len2++; /* signature byte */
2367 }
2368
2369 count = 1 /* string type byte */ + name_len + name_len2;
2370 pSMB->hdr.smb_buf_length += count;
2371 pSMB->ByteCount = cpu_to_le16(count);
2372
2373 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2374 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002375 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002376 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002377 cFYI(1, "Send error in hard link (NT rename) = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00002378
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379 cifs_buf_release(pSMB);
2380 if (rc == -EAGAIN)
2381 goto winCreateHardLinkRetry;
2382
2383 return rc;
2384}
2385
2386int
2387CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
Jeff Layton460b9692009-04-30 07:17:56 -04002388 const unsigned char *searchName, char **symlinkinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389 const struct nls_table *nls_codepage)
2390{
2391/* SMB_QUERY_FILE_UNIX_LINK */
2392 TRANSACTION2_QPI_REQ *pSMB = NULL;
2393 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2394 int rc = 0;
2395 int bytes_returned;
2396 int name_len;
2397 __u16 params, byte_count;
Jeff Layton460b9692009-04-30 07:17:56 -04002398 char *data_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399
Joe Perchesb6b38f72010-04-21 03:50:45 +00002400 cFYI(1, "In QPathSymLinkInfo (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401
2402querySymLinkRetry:
2403 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2404 (void **) &pSMBr);
2405 if (rc)
2406 return rc;
2407
2408 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2409 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002410 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2411 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412 name_len++; /* trailing null */
2413 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002414 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415 name_len = strnlen(searchName, PATH_MAX);
2416 name_len++; /* trailing null */
2417 strncpy(pSMB->FileName, searchName, name_len);
2418 }
2419
2420 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2421 pSMB->TotalDataCount = 0;
2422 pSMB->MaxParameterCount = cpu_to_le16(2);
Jeff Layton46a75742009-05-24 18:45:17 -04002423 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424 pSMB->MaxSetupCount = 0;
2425 pSMB->Reserved = 0;
2426 pSMB->Flags = 0;
2427 pSMB->Timeout = 0;
2428 pSMB->Reserved2 = 0;
2429 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002430 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431 pSMB->DataCount = 0;
2432 pSMB->DataOffset = 0;
2433 pSMB->SetupCount = 1;
2434 pSMB->Reserved3 = 0;
2435 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2436 byte_count = params + 1 /* pad */ ;
2437 pSMB->TotalParameterCount = cpu_to_le16(params);
2438 pSMB->ParameterCount = pSMB->TotalParameterCount;
2439 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2440 pSMB->Reserved4 = 0;
2441 pSMB->hdr.smb_buf_length += byte_count;
2442 pSMB->ByteCount = cpu_to_le16(byte_count);
2443
2444 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2445 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2446 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002447 cFYI(1, "Send error in QuerySymLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448 } else {
2449 /* decode response */
2450
2451 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 /* BB also check enough total bytes returned */
Jeff Layton460b9692009-04-30 07:17:56 -04002453 if (rc || (pSMBr->ByteCount < 2))
2454 rc = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455 else {
Steve French0e0d2cf2009-05-01 05:27:32 +00002456 bool is_unicode;
Jeff Layton460b9692009-04-30 07:17:56 -04002457 u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458
Jeff Layton460b9692009-04-30 07:17:56 -04002459 data_start = ((char *) &pSMBr->hdr.Protocol) +
2460 le16_to_cpu(pSMBr->t2.DataOffset);
2461
Steve French0e0d2cf2009-05-01 05:27:32 +00002462 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
2463 is_unicode = true;
2464 else
2465 is_unicode = false;
2466
Steve French737b7582005-04-28 22:41:06 -07002467 /* BB FIXME investigate remapping reserved chars here */
Steve Frenchd185cda2009-04-30 17:45:10 +00002468 *symlinkinfo = cifs_strndup_from_ucs(data_start, count,
Steve French0e0d2cf2009-05-01 05:27:32 +00002469 is_unicode, nls_codepage);
Jeff Layton8b6427a2009-05-19 09:57:03 -04002470 if (!*symlinkinfo)
Jeff Layton460b9692009-04-30 07:17:56 -04002471 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472 }
2473 }
2474 cifs_buf_release(pSMB);
2475 if (rc == -EAGAIN)
2476 goto querySymLinkRetry;
2477 return rc;
2478}
2479
Parag Warudkarc9489772007-10-23 18:09:48 +00002480#ifdef CONFIG_CIFS_EXPERIMENTAL
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481int
2482CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2483 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002484 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485 const struct nls_table *nls_codepage)
2486{
2487 int rc = 0;
2488 int bytes_returned;
Steve French50c2f752007-07-13 00:33:32 +00002489 struct smb_com_transaction_ioctl_req *pSMB;
2490 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491
Joe Perchesb6b38f72010-04-21 03:50:45 +00002492 cFYI(1, "In Windows reparse style QueryLink for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2494 (void **) &pSMBr);
2495 if (rc)
2496 return rc;
2497
2498 pSMB->TotalParameterCount = 0 ;
2499 pSMB->TotalDataCount = 0;
2500 pSMB->MaxParameterCount = cpu_to_le32(2);
2501 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002502 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2503 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504 pSMB->MaxSetupCount = 4;
2505 pSMB->Reserved = 0;
2506 pSMB->ParameterOffset = 0;
2507 pSMB->DataCount = 0;
2508 pSMB->DataOffset = 0;
2509 pSMB->SetupCount = 4;
2510 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2511 pSMB->ParameterCount = pSMB->TotalParameterCount;
2512 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2513 pSMB->IsFsctl = 1; /* FSCTL */
2514 pSMB->IsRootFlag = 0;
2515 pSMB->Fid = fid; /* file handle always le */
2516 pSMB->ByteCount = 0;
2517
2518 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2519 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2520 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002521 cFYI(1, "Send error in QueryReparseLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522 } else { /* decode response */
2523 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2524 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
Steve Frenchafe48c32009-05-02 05:25:46 +00002525 if ((pSMBr->ByteCount < 2) || (data_offset > 512)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526 /* BB also check enough total bytes returned */
2527 rc = -EIO; /* bad smb */
Steve Frenchafe48c32009-05-02 05:25:46 +00002528 goto qreparse_out;
2529 }
2530 if (data_count && (data_count < 2048)) {
2531 char *end_of_smb = 2 /* sizeof byte count */ +
2532 pSMBr->ByteCount + (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533
Steve Frenchafe48c32009-05-02 05:25:46 +00002534 struct reparse_data *reparse_buf =
Steve French50c2f752007-07-13 00:33:32 +00002535 (struct reparse_data *)
2536 ((char *)&pSMBr->hdr.Protocol
2537 + data_offset);
Steve Frenchafe48c32009-05-02 05:25:46 +00002538 if ((char *)reparse_buf >= end_of_smb) {
2539 rc = -EIO;
2540 goto qreparse_out;
2541 }
2542 if ((reparse_buf->LinkNamesBuf +
2543 reparse_buf->TargetNameOffset +
2544 reparse_buf->TargetNameLen) > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002545 cFYI(1, "reparse buf beyond SMB");
Steve Frenchafe48c32009-05-02 05:25:46 +00002546 rc = -EIO;
2547 goto qreparse_out;
2548 }
Steve French50c2f752007-07-13 00:33:32 +00002549
Steve Frenchafe48c32009-05-02 05:25:46 +00002550 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2551 cifs_from_ucs2(symlinkinfo, (__le16 *)
Steve French50c2f752007-07-13 00:33:32 +00002552 (reparse_buf->LinkNamesBuf +
2553 reparse_buf->TargetNameOffset),
Steve Frenchafe48c32009-05-02 05:25:46 +00002554 buflen,
2555 reparse_buf->TargetNameLen,
2556 nls_codepage, 0);
2557 } else { /* ASCII names */
2558 strncpy(symlinkinfo,
2559 reparse_buf->LinkNamesBuf +
2560 reparse_buf->TargetNameOffset,
2561 min_t(const int, buflen,
2562 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002564 } else {
2565 rc = -EIO;
Joe Perchesb6b38f72010-04-21 03:50:45 +00002566 cFYI(1, "Invalid return data count on "
2567 "get reparse info ioctl");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002569 symlinkinfo[buflen] = 0; /* just in case so the caller
2570 does not go off the end of the buffer */
Joe Perchesb6b38f72010-04-21 03:50:45 +00002571 cFYI(1, "readlink result - %s", symlinkinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 }
Steve French989c7e52009-05-02 05:32:20 +00002573
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002575 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576
2577 /* Note: On -EAGAIN error only caller can retry on handle based calls
2578 since file handle passed in no longer valid */
2579
2580 return rc;
2581}
Steve Frenchafe48c32009-05-02 05:25:46 +00002582#endif /* CIFS_EXPERIMENTAL */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583
2584#ifdef CONFIG_CIFS_POSIX
2585
2586/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002587static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2588 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589{
2590 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002591 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2592 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2593 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002594 /* cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002595
2596 return;
2597}
2598
2599/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002600static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2601 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602{
2603 int size = 0;
2604 int i;
2605 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002606 struct cifs_posix_ace *pACE;
2607 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2608 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609
2610 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2611 return -EOPNOTSUPP;
2612
Steve French790fe572007-07-07 19:25:05 +00002613 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614 count = le16_to_cpu(cifs_acl->access_entry_count);
2615 pACE = &cifs_acl->ace_array[0];
2616 size = sizeof(struct cifs_posix_acl);
2617 size += sizeof(struct cifs_posix_ace) * count;
2618 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002619 if (size_of_data_area < size) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002620 cFYI(1, "bad CIFS POSIX ACL size %d vs. %d",
2621 size_of_data_area, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 return -EINVAL;
2623 }
Steve French790fe572007-07-07 19:25:05 +00002624 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625 count = le16_to_cpu(cifs_acl->access_entry_count);
2626 size = sizeof(struct cifs_posix_acl);
2627 size += sizeof(struct cifs_posix_ace) * count;
2628/* skip past access ACEs to get to default ACEs */
2629 pACE = &cifs_acl->ace_array[count];
2630 count = le16_to_cpu(cifs_acl->default_entry_count);
2631 size += sizeof(struct cifs_posix_ace) * count;
2632 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002633 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634 return -EINVAL;
2635 } else {
2636 /* illegal type */
2637 return -EINVAL;
2638 }
2639
2640 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002641 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002642 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002643 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644 return -ERANGE;
2645 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002646 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002647 for (i = 0; i < count ; i++) {
2648 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2649 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650 }
2651 }
2652 return size;
2653}
2654
Steve French50c2f752007-07-13 00:33:32 +00002655static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2656 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657{
2658 __u16 rc = 0; /* 0 = ACL converted ok */
2659
Steve Frenchff7feac2005-11-15 16:45:16 -08002660 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2661 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002663 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664 /* Probably no need to le convert -1 on any arch but can not hurt */
2665 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002666 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002667 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002668 /*cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id);*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 return rc;
2670}
2671
2672/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002673static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2674 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675{
2676 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002677 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2678 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679 int count;
2680 int i;
2681
Steve French790fe572007-07-07 19:25:05 +00002682 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683 return 0;
2684
2685 count = posix_acl_xattr_count((size_t)buflen);
Joe Perchesb6b38f72010-04-21 03:50:45 +00002686 cFYI(1, "setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002687 "version of %d",
Joe Perchesb6b38f72010-04-21 03:50:45 +00002688 count, buflen, le32_to_cpu(local_acl->a_version));
Steve French790fe572007-07-07 19:25:05 +00002689 if (le32_to_cpu(local_acl->a_version) != 2) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002690 cFYI(1, "unknown POSIX ACL version %d",
2691 le32_to_cpu(local_acl->a_version));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692 return 0;
2693 }
2694 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002695 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002696 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002697 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002698 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002700 cFYI(1, "unknown ACL type %d", acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701 return 0;
2702 }
Steve French50c2f752007-07-13 00:33:32 +00002703 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2705 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002706 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 /* ACE not converted */
2708 break;
2709 }
2710 }
Steve French790fe572007-07-07 19:25:05 +00002711 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2713 rc += sizeof(struct cifs_posix_acl);
2714 /* BB add check to make sure ACL does not overflow SMB */
2715 }
2716 return rc;
2717}
2718
2719int
2720CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002721 const unsigned char *searchName,
2722 char *acl_inf, const int buflen, const int acl_type,
2723 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724{
2725/* SMB_QUERY_POSIX_ACL */
2726 TRANSACTION2_QPI_REQ *pSMB = NULL;
2727 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2728 int rc = 0;
2729 int bytes_returned;
2730 int name_len;
2731 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002732
Joe Perchesb6b38f72010-04-21 03:50:45 +00002733 cFYI(1, "In GetPosixACL (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734
2735queryAclRetry:
2736 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2737 (void **) &pSMBr);
2738 if (rc)
2739 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002740
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2742 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002743 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002744 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745 name_len++; /* trailing null */
2746 name_len *= 2;
2747 pSMB->FileName[name_len] = 0;
2748 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002749 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 name_len = strnlen(searchName, PATH_MAX);
2751 name_len++; /* trailing null */
2752 strncpy(pSMB->FileName, searchName, name_len);
2753 }
2754
2755 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2756 pSMB->TotalDataCount = 0;
2757 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002758 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759 pSMB->MaxDataCount = cpu_to_le16(4000);
2760 pSMB->MaxSetupCount = 0;
2761 pSMB->Reserved = 0;
2762 pSMB->Flags = 0;
2763 pSMB->Timeout = 0;
2764 pSMB->Reserved2 = 0;
2765 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002766 offsetof(struct smb_com_transaction2_qpi_req,
2767 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768 pSMB->DataCount = 0;
2769 pSMB->DataOffset = 0;
2770 pSMB->SetupCount = 1;
2771 pSMB->Reserved3 = 0;
2772 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2773 byte_count = params + 1 /* pad */ ;
2774 pSMB->TotalParameterCount = cpu_to_le16(params);
2775 pSMB->ParameterCount = pSMB->TotalParameterCount;
2776 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2777 pSMB->Reserved4 = 0;
2778 pSMB->hdr.smb_buf_length += byte_count;
2779 pSMB->ByteCount = cpu_to_le16(byte_count);
2780
2781 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2782 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002783 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002785 cFYI(1, "Send error in Query POSIX ACL = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 } else {
2787 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002788
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2790 if (rc || (pSMBr->ByteCount < 2))
2791 /* BB also check enough total bytes returned */
2792 rc = -EIO; /* bad smb */
2793 else {
2794 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2795 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2796 rc = cifs_copy_posix_acl(acl_inf,
2797 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002798 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 }
2800 }
2801 cifs_buf_release(pSMB);
2802 if (rc == -EAGAIN)
2803 goto queryAclRetry;
2804 return rc;
2805}
2806
2807int
2808CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002809 const unsigned char *fileName,
2810 const char *local_acl, const int buflen,
2811 const int acl_type,
2812 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813{
2814 struct smb_com_transaction2_spi_req *pSMB = NULL;
2815 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2816 char *parm_data;
2817 int name_len;
2818 int rc = 0;
2819 int bytes_returned = 0;
2820 __u16 params, byte_count, data_count, param_offset, offset;
2821
Joe Perchesb6b38f72010-04-21 03:50:45 +00002822 cFYI(1, "In SetPosixACL (Unix) for path %s", fileName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823setAclRetry:
2824 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002825 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826 if (rc)
2827 return rc;
2828 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2829 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002830 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002831 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 name_len++; /* trailing null */
2833 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002834 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835 name_len = strnlen(fileName, PATH_MAX);
2836 name_len++; /* trailing null */
2837 strncpy(pSMB->FileName, fileName, name_len);
2838 }
2839 params = 6 + name_len;
2840 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00002841 /* BB find max SMB size from sess */
2842 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843 pSMB->MaxSetupCount = 0;
2844 pSMB->Reserved = 0;
2845 pSMB->Flags = 0;
2846 pSMB->Timeout = 0;
2847 pSMB->Reserved2 = 0;
2848 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002849 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850 offset = param_offset + params;
2851 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2852 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2853
2854 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002855 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856
Steve French790fe572007-07-07 19:25:05 +00002857 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858 rc = -EOPNOTSUPP;
2859 goto setACLerrorExit;
2860 }
2861 pSMB->DataOffset = cpu_to_le16(offset);
2862 pSMB->SetupCount = 1;
2863 pSMB->Reserved3 = 0;
2864 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2865 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2866 byte_count = 3 /* pad */ + params + data_count;
2867 pSMB->DataCount = cpu_to_le16(data_count);
2868 pSMB->TotalDataCount = pSMB->DataCount;
2869 pSMB->ParameterCount = cpu_to_le16(params);
2870 pSMB->TotalParameterCount = pSMB->ParameterCount;
2871 pSMB->Reserved4 = 0;
2872 pSMB->hdr.smb_buf_length += byte_count;
2873 pSMB->ByteCount = cpu_to_le16(byte_count);
2874 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002875 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00002876 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002877 cFYI(1, "Set POSIX ACL returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878
2879setACLerrorExit:
2880 cifs_buf_release(pSMB);
2881 if (rc == -EAGAIN)
2882 goto setAclRetry;
2883 return rc;
2884}
2885
Steve Frenchf654bac2005-04-28 22:41:04 -07002886/* BB fix tabs in this function FIXME BB */
2887int
2888CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00002889 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07002890{
Steve French50c2f752007-07-13 00:33:32 +00002891 int rc = 0;
2892 struct smb_t2_qfi_req *pSMB = NULL;
2893 struct smb_t2_qfi_rsp *pSMBr = NULL;
2894 int bytes_returned;
2895 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07002896
Joe Perchesb6b38f72010-04-21 03:50:45 +00002897 cFYI(1, "In GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00002898 if (tcon == NULL)
2899 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07002900
2901GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00002902 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2903 (void **) &pSMBr);
2904 if (rc)
2905 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07002906
Steve Frenchad7a2922008-02-07 23:25:02 +00002907 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00002908 pSMB->t2.TotalDataCount = 0;
2909 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
2910 /* BB find exact max data count below from sess structure BB */
2911 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
2912 pSMB->t2.MaxSetupCount = 0;
2913 pSMB->t2.Reserved = 0;
2914 pSMB->t2.Flags = 0;
2915 pSMB->t2.Timeout = 0;
2916 pSMB->t2.Reserved2 = 0;
2917 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
2918 Fid) - 4);
2919 pSMB->t2.DataCount = 0;
2920 pSMB->t2.DataOffset = 0;
2921 pSMB->t2.SetupCount = 1;
2922 pSMB->t2.Reserved3 = 0;
2923 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
2924 byte_count = params + 1 /* pad */ ;
2925 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
2926 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
2927 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
2928 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07002929 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00002930 pSMB->hdr.smb_buf_length += byte_count;
2931 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07002932
Steve French790fe572007-07-07 19:25:05 +00002933 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2934 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2935 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002936 cFYI(1, "error %d in GetExtAttr", rc);
Steve French790fe572007-07-07 19:25:05 +00002937 } else {
2938 /* decode response */
2939 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2940 if (rc || (pSMBr->ByteCount < 2))
2941 /* BB also check enough total bytes returned */
2942 /* If rc should we check for EOPNOSUPP and
2943 disable the srvino flag? or in caller? */
2944 rc = -EIO; /* bad smb */
2945 else {
2946 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2947 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2948 struct file_chattr_info *pfinfo;
2949 /* BB Do we need a cast or hash here ? */
2950 if (count != 16) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002951 cFYI(1, "Illegal size ret in GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00002952 rc = -EIO;
2953 goto GetExtAttrOut;
2954 }
2955 pfinfo = (struct file_chattr_info *)
2956 (data_offset + (char *) &pSMBr->hdr.Protocol);
2957 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07002958 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00002959 }
2960 }
Steve Frenchf654bac2005-04-28 22:41:04 -07002961GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00002962 cifs_buf_release(pSMB);
2963 if (rc == -EAGAIN)
2964 goto GetExtAttrRetry;
2965 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07002966}
2967
Steve Frenchf654bac2005-04-28 22:41:04 -07002968#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002969
Jeff Layton79df1ba2010-12-06 12:52:08 -05002970#ifdef CONFIG_CIFS_ACL
2971/*
2972 * Initialize NT TRANSACT SMB into small smb request buffer. This assumes that
2973 * all NT TRANSACTS that we init here have total parm and data under about 400
2974 * bytes (to fit in small cifs buffer size), which is the case so far, it
2975 * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of
2976 * returned setup area) and MaxParameterCount (returned parms size) must be set
2977 * by caller
2978 */
2979static int
2980smb_init_nttransact(const __u16 sub_command, const int setup_count,
2981 const int parm_len, struct cifsTconInfo *tcon,
2982 void **ret_buf)
2983{
2984 int rc;
2985 __u32 temp_offset;
2986 struct smb_com_ntransact_req *pSMB;
2987
2988 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
2989 (void **)&pSMB);
2990 if (rc)
2991 return rc;
2992 *ret_buf = (void *)pSMB;
2993 pSMB->Reserved = 0;
2994 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
2995 pSMB->TotalDataCount = 0;
2996 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2997 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
2998 pSMB->ParameterCount = pSMB->TotalParameterCount;
2999 pSMB->DataCount = pSMB->TotalDataCount;
3000 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
3001 (setup_count * 2) - 4 /* for rfc1001 length itself */;
3002 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
3003 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
3004 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
3005 pSMB->SubCommand = cpu_to_le16(sub_command);
3006 return 0;
3007}
3008
3009static int
3010validate_ntransact(char *buf, char **ppparm, char **ppdata,
3011 __u32 *pparmlen, __u32 *pdatalen)
3012{
3013 char *end_of_smb;
3014 __u32 data_count, data_offset, parm_count, parm_offset;
3015 struct smb_com_ntransact_rsp *pSMBr;
3016
3017 *pdatalen = 0;
3018 *pparmlen = 0;
3019
3020 if (buf == NULL)
3021 return -EINVAL;
3022
3023 pSMBr = (struct smb_com_ntransact_rsp *)buf;
3024
3025 /* ByteCount was converted from little endian in SendReceive */
3026 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
3027 (char *)&pSMBr->ByteCount;
3028
3029 data_offset = le32_to_cpu(pSMBr->DataOffset);
3030 data_count = le32_to_cpu(pSMBr->DataCount);
3031 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
3032 parm_count = le32_to_cpu(pSMBr->ParameterCount);
3033
3034 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
3035 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
3036
3037 /* should we also check that parm and data areas do not overlap? */
3038 if (*ppparm > end_of_smb) {
3039 cFYI(1, "parms start after end of smb");
3040 return -EINVAL;
3041 } else if (parm_count + *ppparm > end_of_smb) {
3042 cFYI(1, "parm end after end of smb");
3043 return -EINVAL;
3044 } else if (*ppdata > end_of_smb) {
3045 cFYI(1, "data starts after end of smb");
3046 return -EINVAL;
3047 } else if (data_count + *ppdata > end_of_smb) {
3048 cFYI(1, "data %p + count %d (%p) past smb end %p start %p",
3049 *ppdata, data_count, (data_count + *ppdata),
3050 end_of_smb, pSMBr);
3051 return -EINVAL;
3052 } else if (parm_count + data_count > pSMBr->ByteCount) {
3053 cFYI(1, "parm count and data count larger than SMB");
3054 return -EINVAL;
3055 }
3056 *pdatalen = data_count;
3057 *pparmlen = parm_count;
3058 return 0;
3059}
3060
Steve French0a4b92c2006-01-12 15:44:21 -08003061/* Get Security Descriptor (by handle) from remote server for a file or dir */
3062int
3063CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f0c2007-10-25 21:17:17 +00003064 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003065{
3066 int rc = 0;
3067 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003068 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003069 struct kvec iov[1];
3070
Joe Perchesb6b38f72010-04-21 03:50:45 +00003071 cFYI(1, "GetCifsACL");
Steve French0a4b92c2006-01-12 15:44:21 -08003072
Steve French630f3f0c2007-10-25 21:17:17 +00003073 *pbuflen = 0;
3074 *acl_inf = NULL;
3075
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003076 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003077 8 /* parm len */, tcon, (void **) &pSMB);
3078 if (rc)
3079 return rc;
3080
3081 pSMB->MaxParameterCount = cpu_to_le32(4);
3082 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3083 pSMB->MaxSetupCount = 0;
3084 pSMB->Fid = fid; /* file handle always le */
3085 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3086 CIFS_ACL_DACL);
3087 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3088 pSMB->hdr.smb_buf_length += 11;
3089 iov[0].iov_base = (char *)pSMB;
3090 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3091
Steve Frencha761ac52007-10-18 21:45:27 +00003092 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Steve French133672e2007-11-13 22:41:37 +00003093 CIFS_STD_OP);
Steve French0a4b92c2006-01-12 15:44:21 -08003094 cifs_stats_inc(&tcon->num_acl_get);
3095 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003096 cFYI(1, "Send error in QuerySecDesc = %d", rc);
Steve French0a4b92c2006-01-12 15:44:21 -08003097 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003098 __le32 *parm;
Steve French630f3f0c2007-10-25 21:17:17 +00003099 __u32 parm_len;
3100 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003101 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f0c2007-10-25 21:17:17 +00003102 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003103
3104/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003105 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f0c2007-10-25 21:17:17 +00003106 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003107 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003108 goto qsec_out;
3109 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3110
Joe Perchesb6b38f72010-04-21 03:50:45 +00003111 cFYI(1, "smb %p parm %p data %p", pSMBr, parm, *acl_inf);
Steve French0a4b92c2006-01-12 15:44:21 -08003112
3113 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3114 rc = -EIO; /* bad smb */
Steve French630f3f0c2007-10-25 21:17:17 +00003115 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003116 goto qsec_out;
3117 }
3118
3119/* BB check that data area is minimum length and as big as acl_len */
3120
Steve Frenchaf6f4612007-10-16 18:40:37 +00003121 acl_len = le32_to_cpu(*parm);
Steve French630f3f0c2007-10-25 21:17:17 +00003122 if (acl_len != *pbuflen) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003123 cERROR(1, "acl length %d does not match %d",
3124 acl_len, *pbuflen);
Steve French630f3f0c2007-10-25 21:17:17 +00003125 if (*pbuflen > acl_len)
3126 *pbuflen = acl_len;
3127 }
Steve French0a4b92c2006-01-12 15:44:21 -08003128
Steve French630f3f0c2007-10-25 21:17:17 +00003129 /* check if buffer is big enough for the acl
3130 header followed by the smallest SID */
3131 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3132 (*pbuflen >= 64 * 1024)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003133 cERROR(1, "bad acl length %d", *pbuflen);
Steve French630f3f0c2007-10-25 21:17:17 +00003134 rc = -EINVAL;
3135 *pbuflen = 0;
3136 } else {
3137 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3138 if (*acl_inf == NULL) {
3139 *pbuflen = 0;
3140 rc = -ENOMEM;
3141 }
3142 memcpy(*acl_inf, pdata, *pbuflen);
3143 }
Steve French0a4b92c2006-01-12 15:44:21 -08003144 }
3145qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003146 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003147 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003148 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003149 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003150/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003151 return rc;
3152}
Steve French97837582007-12-31 07:47:21 +00003153
3154int
3155CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3156 struct cifs_ntsd *pntsd, __u32 acllen)
3157{
3158 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3159 int rc = 0;
3160 int bytes_returned = 0;
3161 SET_SEC_DESC_REQ *pSMB = NULL;
3162 NTRANSACT_RSP *pSMBr = NULL;
3163
3164setCifsAclRetry:
3165 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3166 (void **) &pSMBr);
3167 if (rc)
3168 return (rc);
3169
3170 pSMB->MaxSetupCount = 0;
3171 pSMB->Reserved = 0;
3172
3173 param_count = 8;
3174 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3175 data_count = acllen;
3176 data_offset = param_offset + param_count;
3177 byte_count = 3 /* pad */ + param_count;
3178
3179 pSMB->DataCount = cpu_to_le32(data_count);
3180 pSMB->TotalDataCount = pSMB->DataCount;
3181 pSMB->MaxParameterCount = cpu_to_le32(4);
3182 pSMB->MaxDataCount = cpu_to_le32(16384);
3183 pSMB->ParameterCount = cpu_to_le32(param_count);
3184 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3185 pSMB->TotalParameterCount = pSMB->ParameterCount;
3186 pSMB->DataOffset = cpu_to_le32(data_offset);
3187 pSMB->SetupCount = 0;
3188 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3189 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3190
3191 pSMB->Fid = fid; /* file handle always le */
3192 pSMB->Reserved2 = 0;
3193 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3194
3195 if (pntsd && acllen) {
3196 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3197 (char *) pntsd,
3198 acllen);
3199 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3200
3201 } else
3202 pSMB->hdr.smb_buf_length += byte_count;
3203
3204 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3205 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3206
Joe Perchesb6b38f72010-04-21 03:50:45 +00003207 cFYI(1, "SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc);
Steve French97837582007-12-31 07:47:21 +00003208 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003209 cFYI(1, "Set CIFS ACL returned %d", rc);
Steve French97837582007-12-31 07:47:21 +00003210 cifs_buf_release(pSMB);
3211
3212 if (rc == -EAGAIN)
3213 goto setCifsAclRetry;
3214
3215 return (rc);
3216}
3217
Jeff Layton79df1ba2010-12-06 12:52:08 -05003218#endif /* CONFIG_CIFS_ACL */
Steve French0a4b92c2006-01-12 15:44:21 -08003219
Steve French6b8edfe2005-08-23 20:26:03 -07003220/* Legacy Query Path Information call for lookup to old servers such
3221 as Win9x/WinME */
3222int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003223 const unsigned char *searchName,
3224 FILE_ALL_INFO *pFinfo,
3225 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003226{
Steve Frenchad7a2922008-02-07 23:25:02 +00003227 QUERY_INFORMATION_REQ *pSMB;
3228 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003229 int rc = 0;
3230 int bytes_returned;
3231 int name_len;
3232
Joe Perchesb6b38f72010-04-21 03:50:45 +00003233 cFYI(1, "In SMBQPath path %s", searchName);
Steve French6b8edfe2005-08-23 20:26:03 -07003234QInfRetry:
3235 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003236 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003237 if (rc)
3238 return rc;
3239
3240 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3241 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003242 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3243 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003244 name_len++; /* trailing null */
3245 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003246 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003247 name_len = strnlen(searchName, PATH_MAX);
3248 name_len++; /* trailing null */
3249 strncpy(pSMB->FileName, searchName, name_len);
3250 }
3251 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003252 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003253 pSMB->hdr.smb_buf_length += (__u16) name_len;
3254 pSMB->ByteCount = cpu_to_le16(name_len);
3255
3256 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003257 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003258 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003259 cFYI(1, "Send error in QueryInfo = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003260 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003261 struct timespec ts;
3262 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003263
3264 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003265 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003266 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003267 ts.tv_nsec = 0;
3268 ts.tv_sec = time;
3269 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003270 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003271 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3272 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003273 pFinfo->AllocationSize =
3274 cpu_to_le64(le32_to_cpu(pSMBr->size));
3275 pFinfo->EndOfFile = pFinfo->AllocationSize;
3276 pFinfo->Attributes =
3277 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003278 } else
3279 rc = -EIO; /* bad buffer passed in */
3280
3281 cifs_buf_release(pSMB);
3282
3283 if (rc == -EAGAIN)
3284 goto QInfRetry;
3285
3286 return rc;
3287}
3288
Jeff Laytonbcd53572010-02-12 07:44:16 -05003289int
3290CIFSSMBQFileInfo(const int xid, struct cifsTconInfo *tcon,
3291 u16 netfid, FILE_ALL_INFO *pFindData)
3292{
3293 struct smb_t2_qfi_req *pSMB = NULL;
3294 struct smb_t2_qfi_rsp *pSMBr = NULL;
3295 int rc = 0;
3296 int bytes_returned;
3297 __u16 params, byte_count;
Steve French6b8edfe2005-08-23 20:26:03 -07003298
Jeff Laytonbcd53572010-02-12 07:44:16 -05003299QFileInfoRetry:
3300 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3301 (void **) &pSMBr);
3302 if (rc)
3303 return rc;
Steve French6b8edfe2005-08-23 20:26:03 -07003304
Jeff Laytonbcd53572010-02-12 07:44:16 -05003305 params = 2 /* level */ + 2 /* fid */;
3306 pSMB->t2.TotalDataCount = 0;
3307 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3308 /* BB find exact max data count below from sess structure BB */
3309 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3310 pSMB->t2.MaxSetupCount = 0;
3311 pSMB->t2.Reserved = 0;
3312 pSMB->t2.Flags = 0;
3313 pSMB->t2.Timeout = 0;
3314 pSMB->t2.Reserved2 = 0;
3315 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3316 Fid) - 4);
3317 pSMB->t2.DataCount = 0;
3318 pSMB->t2.DataOffset = 0;
3319 pSMB->t2.SetupCount = 1;
3320 pSMB->t2.Reserved3 = 0;
3321 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3322 byte_count = params + 1 /* pad */ ;
3323 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3324 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3325 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
3326 pSMB->Pad = 0;
3327 pSMB->Fid = netfid;
3328 pSMB->hdr.smb_buf_length += byte_count;
3329
3330 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3331 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3332 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003333 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonbcd53572010-02-12 07:44:16 -05003334 } else { /* decode response */
3335 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3336
3337 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3338 rc = -EIO;
3339 else if (pSMBr->ByteCount < 40)
3340 rc = -EIO; /* bad smb */
3341 else if (pFindData) {
3342 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3343 memcpy((char *) pFindData,
3344 (char *) &pSMBr->hdr.Protocol +
3345 data_offset, sizeof(FILE_ALL_INFO));
3346 } else
3347 rc = -ENOMEM;
3348 }
3349 cifs_buf_release(pSMB);
3350 if (rc == -EAGAIN)
3351 goto QFileInfoRetry;
3352
3353 return rc;
3354}
Steve French6b8edfe2005-08-23 20:26:03 -07003355
Linus Torvalds1da177e2005-04-16 15:20:36 -07003356int
3357CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3358 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003359 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003360 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003361 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003362{
3363/* level 263 SMB_QUERY_FILE_ALL_INFO */
3364 TRANSACTION2_QPI_REQ *pSMB = NULL;
3365 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3366 int rc = 0;
3367 int bytes_returned;
3368 int name_len;
3369 __u16 params, byte_count;
3370
Joe Perchesb6b38f72010-04-21 03:50:45 +00003371/* cFYI(1, "In QPathInfo path %s", searchName); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003372QPathInfoRetry:
3373 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3374 (void **) &pSMBr);
3375 if (rc)
3376 return rc;
3377
3378 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3379 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003380 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003381 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003382 name_len++; /* trailing null */
3383 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003384 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003385 name_len = strnlen(searchName, PATH_MAX);
3386 name_len++; /* trailing null */
3387 strncpy(pSMB->FileName, searchName, name_len);
3388 }
3389
Steve French50c2f752007-07-13 00:33:32 +00003390 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003391 pSMB->TotalDataCount = 0;
3392 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00003393 /* BB find exact max SMB PDU from sess structure BB */
3394 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003395 pSMB->MaxSetupCount = 0;
3396 pSMB->Reserved = 0;
3397 pSMB->Flags = 0;
3398 pSMB->Timeout = 0;
3399 pSMB->Reserved2 = 0;
3400 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003401 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003402 pSMB->DataCount = 0;
3403 pSMB->DataOffset = 0;
3404 pSMB->SetupCount = 1;
3405 pSMB->Reserved3 = 0;
3406 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3407 byte_count = params + 1 /* pad */ ;
3408 pSMB->TotalParameterCount = cpu_to_le16(params);
3409 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003410 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003411 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3412 else
3413 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003414 pSMB->Reserved4 = 0;
3415 pSMB->hdr.smb_buf_length += byte_count;
3416 pSMB->ByteCount = cpu_to_le16(byte_count);
3417
3418 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3419 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3420 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003421 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003422 } else { /* decode response */
3423 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3424
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003425 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3426 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003427 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003428 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003429 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003430 rc = -EIO; /* 24 or 26 expected but we do not read
3431 last field */
3432 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003433 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003434 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003435
3436 /* On legacy responses we do not read the last field,
3437 EAsize, fortunately since it varies by subdialect and
3438 also note it differs on Set vs. Get, ie two bytes or 4
3439 bytes depending but we don't care here */
3440 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003441 size = sizeof(FILE_INFO_STANDARD);
3442 else
3443 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003444 memcpy((char *) pFindData,
3445 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003446 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003447 } else
3448 rc = -ENOMEM;
3449 }
3450 cifs_buf_release(pSMB);
3451 if (rc == -EAGAIN)
3452 goto QPathInfoRetry;
3453
3454 return rc;
3455}
3456
3457int
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003458CIFSSMBUnixQFileInfo(const int xid, struct cifsTconInfo *tcon,
3459 u16 netfid, FILE_UNIX_BASIC_INFO *pFindData)
3460{
3461 struct smb_t2_qfi_req *pSMB = NULL;
3462 struct smb_t2_qfi_rsp *pSMBr = NULL;
3463 int rc = 0;
3464 int bytes_returned;
3465 __u16 params, byte_count;
3466
3467UnixQFileInfoRetry:
3468 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3469 (void **) &pSMBr);
3470 if (rc)
3471 return rc;
3472
3473 params = 2 /* level */ + 2 /* fid */;
3474 pSMB->t2.TotalDataCount = 0;
3475 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3476 /* BB find exact max data count below from sess structure BB */
3477 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3478 pSMB->t2.MaxSetupCount = 0;
3479 pSMB->t2.Reserved = 0;
3480 pSMB->t2.Flags = 0;
3481 pSMB->t2.Timeout = 0;
3482 pSMB->t2.Reserved2 = 0;
3483 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3484 Fid) - 4);
3485 pSMB->t2.DataCount = 0;
3486 pSMB->t2.DataOffset = 0;
3487 pSMB->t2.SetupCount = 1;
3488 pSMB->t2.Reserved3 = 0;
3489 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3490 byte_count = params + 1 /* pad */ ;
3491 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3492 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3493 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3494 pSMB->Pad = 0;
3495 pSMB->Fid = netfid;
3496 pSMB->hdr.smb_buf_length += byte_count;
3497
3498 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3499 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3500 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003501 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003502 } else { /* decode response */
3503 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3504
3505 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003506 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003507 "Unix Extensions can be disabled on mount "
Steve Frenchf19159d2010-04-21 04:12:10 +00003508 "by specifying the nosfu mount option.");
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003509 rc = -EIO; /* bad smb */
3510 } else {
3511 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3512 memcpy((char *) pFindData,
3513 (char *) &pSMBr->hdr.Protocol +
3514 data_offset,
3515 sizeof(FILE_UNIX_BASIC_INFO));
3516 }
3517 }
3518
3519 cifs_buf_release(pSMB);
3520 if (rc == -EAGAIN)
3521 goto UnixQFileInfoRetry;
3522
3523 return rc;
3524}
3525
3526int
Linus Torvalds1da177e2005-04-16 15:20:36 -07003527CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3528 const unsigned char *searchName,
Steve French582d21e2008-05-13 04:54:12 +00003529 FILE_UNIX_BASIC_INFO *pFindData,
Steve French737b7582005-04-28 22:41:06 -07003530 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003531{
3532/* SMB_QUERY_FILE_UNIX_BASIC */
3533 TRANSACTION2_QPI_REQ *pSMB = NULL;
3534 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3535 int rc = 0;
3536 int bytes_returned = 0;
3537 int name_len;
3538 __u16 params, byte_count;
3539
Joe Perchesb6b38f72010-04-21 03:50:45 +00003540 cFYI(1, "In QPathInfo (Unix) the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003541UnixQPathInfoRetry:
3542 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3543 (void **) &pSMBr);
3544 if (rc)
3545 return rc;
3546
3547 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3548 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003549 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003550 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003551 name_len++; /* trailing null */
3552 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003553 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003554 name_len = strnlen(searchName, PATH_MAX);
3555 name_len++; /* trailing null */
3556 strncpy(pSMB->FileName, searchName, name_len);
3557 }
3558
Steve French50c2f752007-07-13 00:33:32 +00003559 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003560 pSMB->TotalDataCount = 0;
3561 pSMB->MaxParameterCount = cpu_to_le16(2);
3562 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003563 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003564 pSMB->MaxSetupCount = 0;
3565 pSMB->Reserved = 0;
3566 pSMB->Flags = 0;
3567 pSMB->Timeout = 0;
3568 pSMB->Reserved2 = 0;
3569 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003570 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003571 pSMB->DataCount = 0;
3572 pSMB->DataOffset = 0;
3573 pSMB->SetupCount = 1;
3574 pSMB->Reserved3 = 0;
3575 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3576 byte_count = params + 1 /* pad */ ;
3577 pSMB->TotalParameterCount = cpu_to_le16(params);
3578 pSMB->ParameterCount = pSMB->TotalParameterCount;
3579 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3580 pSMB->Reserved4 = 0;
3581 pSMB->hdr.smb_buf_length += byte_count;
3582 pSMB->ByteCount = cpu_to_le16(byte_count);
3583
3584 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3585 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3586 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003587 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003588 } else { /* decode response */
3589 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3590
3591 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003592 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Steve French1e71f252007-09-20 15:30:07 +00003593 "Unix Extensions can be disabled on mount "
Joe Perchesb6b38f72010-04-21 03:50:45 +00003594 "by specifying the nosfu mount option.");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003595 rc = -EIO; /* bad smb */
3596 } else {
3597 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3598 memcpy((char *) pFindData,
3599 (char *) &pSMBr->hdr.Protocol +
3600 data_offset,
Steve French630f3f0c2007-10-25 21:17:17 +00003601 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003602 }
3603 }
3604 cifs_buf_release(pSMB);
3605 if (rc == -EAGAIN)
3606 goto UnixQPathInfoRetry;
3607
3608 return rc;
3609}
3610
Linus Torvalds1da177e2005-04-16 15:20:36 -07003611/* xid, tcon, searchName and codepage are input parms, rest are returned */
3612int
3613CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003614 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003615 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003616 __u16 *pnetfid,
3617 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003618{
3619/* level 257 SMB_ */
3620 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3621 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003622 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003623 int rc = 0;
3624 int bytes_returned = 0;
3625 int name_len;
3626 __u16 params, byte_count;
3627
Joe Perchesb6b38f72010-04-21 03:50:45 +00003628 cFYI(1, "In FindFirst for %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003629
3630findFirstRetry:
3631 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3632 (void **) &pSMBr);
3633 if (rc)
3634 return rc;
3635
3636 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3637 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003638 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003639 PATH_MAX, nls_codepage, remap);
3640 /* We can not add the asterik earlier in case
3641 it got remapped to 0xF03A as if it were part of the
3642 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003643 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003644 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003645 pSMB->FileName[name_len+1] = 0;
3646 pSMB->FileName[name_len+2] = '*';
3647 pSMB->FileName[name_len+3] = 0;
3648 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003649 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3650 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003651 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003652 } else { /* BB add check for overrun of SMB buf BB */
3653 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003654/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003655 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003656 free buffer exit; BB */
3657 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003658 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003659 pSMB->FileName[name_len+1] = '*';
3660 pSMB->FileName[name_len+2] = 0;
3661 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003662 }
3663
3664 params = 12 + name_len /* includes null */ ;
3665 pSMB->TotalDataCount = 0; /* no EAs */
3666 pSMB->MaxParameterCount = cpu_to_le16(10);
3667 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3668 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3669 pSMB->MaxSetupCount = 0;
3670 pSMB->Reserved = 0;
3671 pSMB->Flags = 0;
3672 pSMB->Timeout = 0;
3673 pSMB->Reserved2 = 0;
3674 byte_count = params + 1 /* pad */ ;
3675 pSMB->TotalParameterCount = cpu_to_le16(params);
3676 pSMB->ParameterCount = pSMB->TotalParameterCount;
3677 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003678 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3679 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003680 pSMB->DataCount = 0;
3681 pSMB->DataOffset = 0;
3682 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3683 pSMB->Reserved3 = 0;
3684 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3685 pSMB->SearchAttributes =
3686 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3687 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003688 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3689 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003690 CIFS_SEARCH_RETURN_RESUME);
3691 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3692
3693 /* BB what should we set StorageType to? Does it matter? BB */
3694 pSMB->SearchStorageType = 0;
3695 pSMB->hdr.smb_buf_length += byte_count;
3696 pSMB->ByteCount = cpu_to_le16(byte_count);
3697
3698 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3699 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003700 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003701
Steve French88274812006-03-09 22:21:45 +00003702 if (rc) {/* BB add logic to retry regular search if Unix search
3703 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003704 /* BB Add code to handle unsupported level rc */
Joe Perchesb6b38f72010-04-21 03:50:45 +00003705 cFYI(1, "Error in FindFirst = %d", rc);
Steve French1982c342005-08-17 12:38:22 -07003706
Steve French88274812006-03-09 22:21:45 +00003707 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003708
3709 /* BB eventually could optimize out free and realloc of buf */
3710 /* for this case */
3711 if (rc == -EAGAIN)
3712 goto findFirstRetry;
3713 } else { /* decode response */
3714 /* BB remember to free buffer if error BB */
3715 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003716 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003717 unsigned int lnoff;
3718
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003720 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003721 else
Steve French4b18f2a2008-04-29 00:06:05 +00003722 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003723
3724 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003725 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003726 psrch_inf->srch_entries_start =
3727 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003728 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003729 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3730 le16_to_cpu(pSMBr->t2.ParameterOffset));
3731
Steve French790fe572007-07-07 19:25:05 +00003732 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003733 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003734 else
Steve French4b18f2a2008-04-29 00:06:05 +00003735 psrch_inf->endOfSearch = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003736
Steve French50c2f752007-07-13 00:33:32 +00003737 psrch_inf->entries_in_buffer =
3738 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003739 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003740 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003741 lnoff = le16_to_cpu(parms->LastNameOffset);
3742 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3743 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003744 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003745 psrch_inf->last_entry = NULL;
3746 return rc;
3747 }
3748
Steve French0752f152008-10-07 20:03:33 +00003749 psrch_inf->last_entry = psrch_inf->srch_entries_start +
Steve Frenchb77d7532008-10-08 19:13:46 +00003750 lnoff;
3751
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752 *pnetfid = parms->SearchHandle;
3753 } else {
3754 cifs_buf_release(pSMB);
3755 }
3756 }
3757
3758 return rc;
3759}
3760
3761int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003762 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003763{
3764 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3765 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003766 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003767 char *response_data;
3768 int rc = 0;
3769 int bytes_returned, name_len;
3770 __u16 params, byte_count;
3771
Joe Perchesb6b38f72010-04-21 03:50:45 +00003772 cFYI(1, "In FindNext");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003773
Steve French4b18f2a2008-04-29 00:06:05 +00003774 if (psrch_inf->endOfSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003775 return -ENOENT;
3776
3777 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3778 (void **) &pSMBr);
3779 if (rc)
3780 return rc;
3781
Steve French50c2f752007-07-13 00:33:32 +00003782 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003783 byte_count = 0;
3784 pSMB->TotalDataCount = 0; /* no EAs */
3785 pSMB->MaxParameterCount = cpu_to_le16(8);
3786 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003787 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3788 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003789 pSMB->MaxSetupCount = 0;
3790 pSMB->Reserved = 0;
3791 pSMB->Flags = 0;
3792 pSMB->Timeout = 0;
3793 pSMB->Reserved2 = 0;
3794 pSMB->ParameterOffset = cpu_to_le16(
3795 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3796 pSMB->DataCount = 0;
3797 pSMB->DataOffset = 0;
3798 pSMB->SetupCount = 1;
3799 pSMB->Reserved3 = 0;
3800 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3801 pSMB->SearchHandle = searchHandle; /* always kept as le */
3802 pSMB->SearchCount =
Steve French630f3f0c2007-10-25 21:17:17 +00003803 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003804 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3805 pSMB->ResumeKey = psrch_inf->resume_key;
3806 pSMB->SearchFlags =
3807 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3808
3809 name_len = psrch_inf->resume_name_len;
3810 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003811 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003812 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3813 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003814 /* 14 byte parm len above enough for 2 byte null terminator */
3815 pSMB->ResumeFileName[name_len] = 0;
3816 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003817 } else {
3818 rc = -EINVAL;
3819 goto FNext2_err_exit;
3820 }
3821 byte_count = params + 1 /* pad */ ;
3822 pSMB->TotalParameterCount = cpu_to_le16(params);
3823 pSMB->ParameterCount = pSMB->TotalParameterCount;
3824 pSMB->hdr.smb_buf_length += byte_count;
3825 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003826
Linus Torvalds1da177e2005-04-16 15:20:36 -07003827 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3828 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003829 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003830 if (rc) {
3831 if (rc == -EBADF) {
Steve French4b18f2a2008-04-29 00:06:05 +00003832 psrch_inf->endOfSearch = true;
Jeff Layton63534502008-05-12 19:56:05 -07003833 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00003834 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003835 } else
Joe Perchesb6b38f72010-04-21 03:50:45 +00003836 cFYI(1, "FindNext returned = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003837 } else { /* decode response */
3838 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003839
Steve French790fe572007-07-07 19:25:05 +00003840 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003841 unsigned int lnoff;
3842
Linus Torvalds1da177e2005-04-16 15:20:36 -07003843 /* BB fixme add lock for file (srch_info) struct here */
3844 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003845 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003846 else
Steve French4b18f2a2008-04-29 00:06:05 +00003847 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003848 response_data = (char *) &pSMBr->hdr.Protocol +
3849 le16_to_cpu(pSMBr->t2.ParameterOffset);
3850 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3851 response_data = (char *)&pSMBr->hdr.Protocol +
3852 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003853 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003854 cifs_small_buf_release(
3855 psrch_inf->ntwrk_buf_start);
3856 else
3857 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003858 psrch_inf->srch_entries_start = response_data;
3859 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003860 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003861 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003862 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003863 else
Steve French4b18f2a2008-04-29 00:06:05 +00003864 psrch_inf->endOfSearch = false;
Steve French50c2f752007-07-13 00:33:32 +00003865 psrch_inf->entries_in_buffer =
3866 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003867 psrch_inf->index_of_last_entry +=
3868 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003869 lnoff = le16_to_cpu(parms->LastNameOffset);
3870 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3871 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003872 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003873 psrch_inf->last_entry = NULL;
3874 return rc;
3875 } else
3876 psrch_inf->last_entry =
3877 psrch_inf->srch_entries_start + lnoff;
3878
Joe Perchesb6b38f72010-04-21 03:50:45 +00003879/* cFYI(1, "fnxt2 entries in buf %d index_of_last %d",
3880 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003881
3882 /* BB fixme add unlock here */
3883 }
3884
3885 }
3886
3887 /* BB On error, should we leave previous search buf (and count and
3888 last entry fields) intact or free the previous one? */
3889
3890 /* Note: On -EAGAIN error only caller can retry on handle based calls
3891 since file handle passed in no longer valid */
3892FNext2_err_exit:
3893 if (rc != 0)
3894 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003895 return rc;
3896}
3897
3898int
Steve French50c2f752007-07-13 00:33:32 +00003899CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3900 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003901{
3902 int rc = 0;
3903 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003904
Joe Perchesb6b38f72010-04-21 03:50:45 +00003905 cFYI(1, "In CIFSSMBFindClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003906 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3907
3908 /* no sense returning error if session restarted
3909 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003910 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003911 return 0;
3912 if (rc)
3913 return rc;
3914
Linus Torvalds1da177e2005-04-16 15:20:36 -07003915 pSMB->FileID = searchHandle;
3916 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003917 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003918 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003919 cERROR(1, "Send error in FindClose = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003920
Steve Frencha4544342005-08-24 13:59:35 -07003921 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003922
3923 /* Since session is dead, search handle closed on server already */
3924 if (rc == -EAGAIN)
3925 rc = 0;
3926
3927 return rc;
3928}
3929
Linus Torvalds1da177e2005-04-16 15:20:36 -07003930int
3931CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003932 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003933 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003934 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003935{
3936 int rc = 0;
3937 TRANSACTION2_QPI_REQ *pSMB = NULL;
3938 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3939 int name_len, bytes_returned;
3940 __u16 params, byte_count;
3941
Joe Perchesb6b38f72010-04-21 03:50:45 +00003942 cFYI(1, "In GetSrvInodeNum for %s", searchName);
Steve French790fe572007-07-07 19:25:05 +00003943 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003944 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003945
3946GetInodeNumberRetry:
3947 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003948 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003949 if (rc)
3950 return rc;
3951
Linus Torvalds1da177e2005-04-16 15:20:36 -07003952 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3953 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003954 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003955 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003956 name_len++; /* trailing null */
3957 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003958 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003959 name_len = strnlen(searchName, PATH_MAX);
3960 name_len++; /* trailing null */
3961 strncpy(pSMB->FileName, searchName, name_len);
3962 }
3963
3964 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
3965 pSMB->TotalDataCount = 0;
3966 pSMB->MaxParameterCount = cpu_to_le16(2);
3967 /* BB find exact max data count below from sess structure BB */
3968 pSMB->MaxDataCount = cpu_to_le16(4000);
3969 pSMB->MaxSetupCount = 0;
3970 pSMB->Reserved = 0;
3971 pSMB->Flags = 0;
3972 pSMB->Timeout = 0;
3973 pSMB->Reserved2 = 0;
3974 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003975 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003976 pSMB->DataCount = 0;
3977 pSMB->DataOffset = 0;
3978 pSMB->SetupCount = 1;
3979 pSMB->Reserved3 = 0;
3980 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3981 byte_count = params + 1 /* pad */ ;
3982 pSMB->TotalParameterCount = cpu_to_le16(params);
3983 pSMB->ParameterCount = pSMB->TotalParameterCount;
3984 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
3985 pSMB->Reserved4 = 0;
3986 pSMB->hdr.smb_buf_length += byte_count;
3987 pSMB->ByteCount = cpu_to_le16(byte_count);
3988
3989 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3990 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3991 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003992 cFYI(1, "error %d in QueryInternalInfo", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003993 } else {
3994 /* decode response */
3995 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3996 if (rc || (pSMBr->ByteCount < 2))
3997 /* BB also check enough total bytes returned */
3998 /* If rc should we check for EOPNOSUPP and
3999 disable the srvino flag? or in caller? */
4000 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00004001 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004002 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4003 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00004004 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004005 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00004006 if (count < 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004007 cFYI(1, "Illegal size ret in QryIntrnlInf");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004008 rc = -EIO;
4009 goto GetInodeNumOut;
4010 }
4011 pfinfo = (struct file_internal_info *)
4012 (data_offset + (char *) &pSMBr->hdr.Protocol);
Steve French85a6dac2009-04-01 05:22:00 +00004013 *inode_number = le64_to_cpu(pfinfo->UniqueId);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004014 }
4015 }
4016GetInodeNumOut:
4017 cifs_buf_release(pSMB);
4018 if (rc == -EAGAIN)
4019 goto GetInodeNumberRetry;
4020 return rc;
4021}
Linus Torvalds1da177e2005-04-16 15:20:36 -07004022
Igor Mammedovfec45852008-05-16 13:06:30 +04004023/* parses DFS refferal V3 structure
4024 * caller is responsible for freeing target_nodes
4025 * returns:
4026 * on success - 0
4027 * on failure - errno
4028 */
4029static int
Steve Frencha1fe78f2008-05-16 18:48:38 +00004030parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
Igor Mammedovfec45852008-05-16 13:06:30 +04004031 unsigned int *num_of_nodes,
4032 struct dfs_info3_param **target_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004033 const struct nls_table *nls_codepage, int remap,
4034 const char *searchName)
Igor Mammedovfec45852008-05-16 13:06:30 +04004035{
4036 int i, rc = 0;
4037 char *data_end;
4038 bool is_unicode;
4039 struct dfs_referral_level_3 *ref;
4040
Harvey Harrison5ca33c62008-07-23 17:45:58 -07004041 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
4042 is_unicode = true;
4043 else
4044 is_unicode = false;
Igor Mammedovfec45852008-05-16 13:06:30 +04004045 *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
4046
4047 if (*num_of_nodes < 1) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004048 cERROR(1, "num_referrals: must be at least > 0,"
4049 "but we get num_referrals = %d\n", *num_of_nodes);
Igor Mammedovfec45852008-05-16 13:06:30 +04004050 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004051 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004052 }
4053
4054 ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
Al Viro1d92cfd2008-06-02 10:59:02 +01004055 if (ref->VersionNumber != cpu_to_le16(3)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004056 cERROR(1, "Referrals of V%d version are not supported,"
4057 "should be V3", le16_to_cpu(ref->VersionNumber));
Igor Mammedovfec45852008-05-16 13:06:30 +04004058 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004059 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004060 }
4061
4062 /* get the upper boundary of the resp buffer */
4063 data_end = (char *)(&(pSMBr->PathConsumed)) +
4064 le16_to_cpu(pSMBr->t2.DataCount);
4065
Steve Frenchf19159d2010-04-21 04:12:10 +00004066 cFYI(1, "num_referrals: %d dfs flags: 0x%x ...\n",
Igor Mammedovfec45852008-05-16 13:06:30 +04004067 *num_of_nodes,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004068 le32_to_cpu(pSMBr->DFSFlags));
Igor Mammedovfec45852008-05-16 13:06:30 +04004069
4070 *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
4071 *num_of_nodes, GFP_KERNEL);
4072 if (*target_nodes == NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004073 cERROR(1, "Failed to allocate buffer for target_nodes\n");
Igor Mammedovfec45852008-05-16 13:06:30 +04004074 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004075 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004076 }
4077
Daniel Mack3ad2f3f2010-02-03 08:01:28 +08004078 /* collect necessary data from referrals */
Igor Mammedovfec45852008-05-16 13:06:30 +04004079 for (i = 0; i < *num_of_nodes; i++) {
4080 char *temp;
4081 int max_len;
4082 struct dfs_info3_param *node = (*target_nodes)+i;
4083
Steve French0e0d2cf2009-05-01 05:27:32 +00004084 node->flags = le32_to_cpu(pSMBr->DFSFlags);
Igor Mammedov2c556082008-10-23 13:58:42 +04004085 if (is_unicode) {
Jeff Layton331c3132008-12-17 06:31:53 -05004086 __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
4087 GFP_KERNEL);
Steve French2920ee22009-08-31 15:27:26 +00004088 if (tmp == NULL) {
4089 rc = -ENOMEM;
4090 goto parse_DFS_referrals_exit;
4091 }
Igor Mammedov2c556082008-10-23 13:58:42 +04004092 cifsConvertToUCS((__le16 *) tmp, searchName,
4093 PATH_MAX, nls_codepage, remap);
Jeff Layton69f801f2009-04-30 06:46:32 -04004094 node->path_consumed = cifs_ucs2_bytes(tmp,
4095 le16_to_cpu(pSMBr->PathConsumed),
Igor Mammedov2c556082008-10-23 13:58:42 +04004096 nls_codepage);
4097 kfree(tmp);
4098 } else
4099 node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
4100
Igor Mammedovfec45852008-05-16 13:06:30 +04004101 node->server_type = le16_to_cpu(ref->ServerType);
4102 node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
4103
4104 /* copy DfsPath */
4105 temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
4106 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004107 node->path_name = cifs_strndup_from_ucs(temp, max_len,
4108 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004109 if (!node->path_name) {
4110 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004111 goto parse_DFS_referrals_exit;
Jeff Layton066ce682009-04-30 07:16:14 -04004112 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004113
4114 /* copy link target UNC */
4115 temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
4116 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004117 node->node_name = cifs_strndup_from_ucs(temp, max_len,
4118 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004119 if (!node->node_name)
4120 rc = -ENOMEM;
Igor Mammedovfec45852008-05-16 13:06:30 +04004121 }
4122
Steve Frencha1fe78f2008-05-16 18:48:38 +00004123parse_DFS_referrals_exit:
Igor Mammedovfec45852008-05-16 13:06:30 +04004124 if (rc) {
4125 free_dfs_info_array(*target_nodes, *num_of_nodes);
4126 *target_nodes = NULL;
4127 *num_of_nodes = 0;
4128 }
4129 return rc;
4130}
4131
Linus Torvalds1da177e2005-04-16 15:20:36 -07004132int
4133CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
4134 const unsigned char *searchName,
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004135 struct dfs_info3_param **target_nodes,
4136 unsigned int *num_of_nodes,
Steve French737b7582005-04-28 22:41:06 -07004137 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004138{
4139/* TRANS2_GET_DFS_REFERRAL */
4140 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
4141 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004142 int rc = 0;
4143 int bytes_returned;
4144 int name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004145 __u16 params, byte_count;
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004146 *num_of_nodes = 0;
4147 *target_nodes = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004148
Joe Perchesb6b38f72010-04-21 03:50:45 +00004149 cFYI(1, "In GetDFSRefer the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004150 if (ses == NULL)
4151 return -ENODEV;
4152getDFSRetry:
4153 rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
4154 (void **) &pSMBr);
4155 if (rc)
4156 return rc;
Steve French50c2f752007-07-13 00:33:32 +00004157
4158 /* server pointer checked in called function,
Steve French1982c342005-08-17 12:38:22 -07004159 but should never be null here anyway */
4160 pSMB->hdr.Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004161 pSMB->hdr.Tid = ses->ipc_tid;
4162 pSMB->hdr.Uid = ses->Suid;
Steve French26f57362007-08-30 22:09:15 +00004163 if (ses->capabilities & CAP_STATUS32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004164 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
Steve French26f57362007-08-30 22:09:15 +00004165 if (ses->capabilities & CAP_DFS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004166 pSMB->hdr.Flags2 |= SMBFLG2_DFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004167
4168 if (ses->capabilities & CAP_UNICODE) {
4169 pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
4170 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004171 cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
Steve French737b7582005-04-28 22:41:06 -07004172 searchName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004173 name_len++; /* trailing null */
4174 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004175 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004176 name_len = strnlen(searchName, PATH_MAX);
4177 name_len++; /* trailing null */
4178 strncpy(pSMB->RequestFileName, searchName, name_len);
4179 }
4180
Steve French790fe572007-07-07 19:25:05 +00004181 if (ses->server) {
4182 if (ses->server->secMode &
Steve French1a4e15a2006-10-12 21:33:51 +00004183 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
4184 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
4185 }
4186
Steve French50c2f752007-07-13 00:33:32 +00004187 pSMB->hdr.Uid = ses->Suid;
Steve French1a4e15a2006-10-12 21:33:51 +00004188
Linus Torvalds1da177e2005-04-16 15:20:36 -07004189 params = 2 /* level */ + name_len /*includes null */ ;
4190 pSMB->TotalDataCount = 0;
4191 pSMB->DataCount = 0;
4192 pSMB->DataOffset = 0;
4193 pSMB->MaxParameterCount = 0;
Steve French582d21e2008-05-13 04:54:12 +00004194 /* BB find exact max SMB PDU from sess structure BB */
4195 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004196 pSMB->MaxSetupCount = 0;
4197 pSMB->Reserved = 0;
4198 pSMB->Flags = 0;
4199 pSMB->Timeout = 0;
4200 pSMB->Reserved2 = 0;
4201 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004202 struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004203 pSMB->SetupCount = 1;
4204 pSMB->Reserved3 = 0;
4205 pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
4206 byte_count = params + 3 /* pad */ ;
4207 pSMB->ParameterCount = cpu_to_le16(params);
4208 pSMB->TotalParameterCount = pSMB->ParameterCount;
4209 pSMB->MaxReferralLevel = cpu_to_le16(3);
4210 pSMB->hdr.smb_buf_length += byte_count;
4211 pSMB->ByteCount = cpu_to_le16(byte_count);
4212
4213 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
4214 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4215 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004216 cFYI(1, "Send error in GetDFSRefer = %d", rc);
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004217 goto GetDFSRefExit;
4218 }
4219 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004220
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004221 /* BB Also check if enough total bytes returned? */
Igor Mammedovfec45852008-05-16 13:06:30 +04004222 if (rc || (pSMBr->ByteCount < 17)) {
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004223 rc = -EIO; /* bad smb */
Igor Mammedovfec45852008-05-16 13:06:30 +04004224 goto GetDFSRefExit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004225 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004226
Joe Perchesb6b38f72010-04-21 03:50:45 +00004227 cFYI(1, "Decoding GetDFSRefer response BCC: %d Offset %d",
Igor Mammedovfec45852008-05-16 13:06:30 +04004228 pSMBr->ByteCount,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004229 le16_to_cpu(pSMBr->t2.DataOffset));
Igor Mammedovfec45852008-05-16 13:06:30 +04004230
4231 /* parse returned result into more usable form */
Steve Frencha1fe78f2008-05-16 18:48:38 +00004232 rc = parse_DFS_referrals(pSMBr, num_of_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004233 target_nodes, nls_codepage, remap,
4234 searchName);
Igor Mammedovfec45852008-05-16 13:06:30 +04004235
Linus Torvalds1da177e2005-04-16 15:20:36 -07004236GetDFSRefExit:
Steve French0d817bc2008-05-22 02:02:03 +00004237 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004238
4239 if (rc == -EAGAIN)
4240 goto getDFSRetry;
4241
4242 return rc;
4243}
4244
Steve French20962432005-09-21 22:05:57 -07004245/* Query File System Info such as free space to old servers such as Win 9x */
4246int
4247SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4248{
4249/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4250 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4251 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4252 FILE_SYSTEM_ALLOC_INFO *response_data;
4253 int rc = 0;
4254 int bytes_returned = 0;
4255 __u16 params, byte_count;
4256
Joe Perchesb6b38f72010-04-21 03:50:45 +00004257 cFYI(1, "OldQFSInfo");
Steve French20962432005-09-21 22:05:57 -07004258oldQFSInfoRetry:
4259 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4260 (void **) &pSMBr);
4261 if (rc)
4262 return rc;
Steve French20962432005-09-21 22:05:57 -07004263
4264 params = 2; /* level */
4265 pSMB->TotalDataCount = 0;
4266 pSMB->MaxParameterCount = cpu_to_le16(2);
4267 pSMB->MaxDataCount = cpu_to_le16(1000);
4268 pSMB->MaxSetupCount = 0;
4269 pSMB->Reserved = 0;
4270 pSMB->Flags = 0;
4271 pSMB->Timeout = 0;
4272 pSMB->Reserved2 = 0;
4273 byte_count = params + 1 /* pad */ ;
4274 pSMB->TotalParameterCount = cpu_to_le16(params);
4275 pSMB->ParameterCount = pSMB->TotalParameterCount;
4276 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4277 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4278 pSMB->DataCount = 0;
4279 pSMB->DataOffset = 0;
4280 pSMB->SetupCount = 1;
4281 pSMB->Reserved3 = 0;
4282 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4283 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4284 pSMB->hdr.smb_buf_length += byte_count;
4285 pSMB->ByteCount = cpu_to_le16(byte_count);
4286
4287 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4288 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4289 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004290 cFYI(1, "Send error in QFSInfo = %d", rc);
Steve French20962432005-09-21 22:05:57 -07004291 } else { /* decode response */
4292 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4293
4294 if (rc || (pSMBr->ByteCount < 18))
4295 rc = -EIO; /* bad smb */
4296 else {
4297 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004298 cFYI(1, "qfsinf resp BCC: %d Offset %d",
4299 pSMBr->ByteCount, data_offset);
Steve French20962432005-09-21 22:05:57 -07004300
Steve French50c2f752007-07-13 00:33:32 +00004301 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004302 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4303 FSData->f_bsize =
4304 le16_to_cpu(response_data->BytesPerSector) *
4305 le32_to_cpu(response_data->
4306 SectorsPerAllocationUnit);
4307 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004308 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004309 FSData->f_bfree = FSData->f_bavail =
4310 le32_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004311 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4312 (unsigned long long)FSData->f_blocks,
4313 (unsigned long long)FSData->f_bfree,
4314 FSData->f_bsize);
Steve French20962432005-09-21 22:05:57 -07004315 }
4316 }
4317 cifs_buf_release(pSMB);
4318
4319 if (rc == -EAGAIN)
4320 goto oldQFSInfoRetry;
4321
4322 return rc;
4323}
4324
Linus Torvalds1da177e2005-04-16 15:20:36 -07004325int
Steve French737b7582005-04-28 22:41:06 -07004326CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004327{
4328/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4329 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4330 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4331 FILE_SYSTEM_INFO *response_data;
4332 int rc = 0;
4333 int bytes_returned = 0;
4334 __u16 params, byte_count;
4335
Joe Perchesb6b38f72010-04-21 03:50:45 +00004336 cFYI(1, "In QFSInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004337QFSInfoRetry:
4338 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4339 (void **) &pSMBr);
4340 if (rc)
4341 return rc;
4342
4343 params = 2; /* level */
4344 pSMB->TotalDataCount = 0;
4345 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004346 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004347 pSMB->MaxSetupCount = 0;
4348 pSMB->Reserved = 0;
4349 pSMB->Flags = 0;
4350 pSMB->Timeout = 0;
4351 pSMB->Reserved2 = 0;
4352 byte_count = params + 1 /* pad */ ;
4353 pSMB->TotalParameterCount = cpu_to_le16(params);
4354 pSMB->ParameterCount = pSMB->TotalParameterCount;
4355 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004356 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004357 pSMB->DataCount = 0;
4358 pSMB->DataOffset = 0;
4359 pSMB->SetupCount = 1;
4360 pSMB->Reserved3 = 0;
4361 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4362 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4363 pSMB->hdr.smb_buf_length += byte_count;
4364 pSMB->ByteCount = cpu_to_le16(byte_count);
4365
4366 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4367 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4368 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004369 cFYI(1, "Send error in QFSInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004370 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004371 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004372
Steve French20962432005-09-21 22:05:57 -07004373 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004374 rc = -EIO; /* bad smb */
4375 else {
4376 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004377
4378 response_data =
4379 (FILE_SYSTEM_INFO
4380 *) (((char *) &pSMBr->hdr.Protocol) +
4381 data_offset);
4382 FSData->f_bsize =
4383 le32_to_cpu(response_data->BytesPerSector) *
4384 le32_to_cpu(response_data->
4385 SectorsPerAllocationUnit);
4386 FSData->f_blocks =
4387 le64_to_cpu(response_data->TotalAllocationUnits);
4388 FSData->f_bfree = FSData->f_bavail =
4389 le64_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004390 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4391 (unsigned long long)FSData->f_blocks,
4392 (unsigned long long)FSData->f_bfree,
4393 FSData->f_bsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004394 }
4395 }
4396 cifs_buf_release(pSMB);
4397
4398 if (rc == -EAGAIN)
4399 goto QFSInfoRetry;
4400
4401 return rc;
4402}
4403
4404int
Steve French737b7582005-04-28 22:41:06 -07004405CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004406{
4407/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4408 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4409 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4410 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4411 int rc = 0;
4412 int bytes_returned = 0;
4413 __u16 params, byte_count;
4414
Joe Perchesb6b38f72010-04-21 03:50:45 +00004415 cFYI(1, "In QFSAttributeInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004416QFSAttributeRetry:
4417 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4418 (void **) &pSMBr);
4419 if (rc)
4420 return rc;
4421
4422 params = 2; /* level */
4423 pSMB->TotalDataCount = 0;
4424 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004425 /* BB find exact max SMB PDU from sess structure BB */
4426 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004427 pSMB->MaxSetupCount = 0;
4428 pSMB->Reserved = 0;
4429 pSMB->Flags = 0;
4430 pSMB->Timeout = 0;
4431 pSMB->Reserved2 = 0;
4432 byte_count = params + 1 /* pad */ ;
4433 pSMB->TotalParameterCount = cpu_to_le16(params);
4434 pSMB->ParameterCount = pSMB->TotalParameterCount;
4435 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004436 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004437 pSMB->DataCount = 0;
4438 pSMB->DataOffset = 0;
4439 pSMB->SetupCount = 1;
4440 pSMB->Reserved3 = 0;
4441 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4442 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4443 pSMB->hdr.smb_buf_length += byte_count;
4444 pSMB->ByteCount = cpu_to_le16(byte_count);
4445
4446 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4447 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4448 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004449 cERROR(1, "Send error in QFSAttributeInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004450 } else { /* decode response */
4451 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4452
Steve French50c2f752007-07-13 00:33:32 +00004453 if (rc || (pSMBr->ByteCount < 13)) {
4454 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004455 rc = -EIO; /* bad smb */
4456 } else {
4457 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4458 response_data =
4459 (FILE_SYSTEM_ATTRIBUTE_INFO
4460 *) (((char *) &pSMBr->hdr.Protocol) +
4461 data_offset);
4462 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004463 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004464 }
4465 }
4466 cifs_buf_release(pSMB);
4467
4468 if (rc == -EAGAIN)
4469 goto QFSAttributeRetry;
4470
4471 return rc;
4472}
4473
4474int
Steve French737b7582005-04-28 22:41:06 -07004475CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004476{
4477/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4478 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4479 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4480 FILE_SYSTEM_DEVICE_INFO *response_data;
4481 int rc = 0;
4482 int bytes_returned = 0;
4483 __u16 params, byte_count;
4484
Joe Perchesb6b38f72010-04-21 03:50:45 +00004485 cFYI(1, "In QFSDeviceInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004486QFSDeviceRetry:
4487 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4488 (void **) &pSMBr);
4489 if (rc)
4490 return rc;
4491
4492 params = 2; /* level */
4493 pSMB->TotalDataCount = 0;
4494 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004495 /* BB find exact max SMB PDU from sess structure BB */
4496 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004497 pSMB->MaxSetupCount = 0;
4498 pSMB->Reserved = 0;
4499 pSMB->Flags = 0;
4500 pSMB->Timeout = 0;
4501 pSMB->Reserved2 = 0;
4502 byte_count = params + 1 /* pad */ ;
4503 pSMB->TotalParameterCount = cpu_to_le16(params);
4504 pSMB->ParameterCount = pSMB->TotalParameterCount;
4505 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004506 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004507
4508 pSMB->DataCount = 0;
4509 pSMB->DataOffset = 0;
4510 pSMB->SetupCount = 1;
4511 pSMB->Reserved3 = 0;
4512 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4513 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4514 pSMB->hdr.smb_buf_length += byte_count;
4515 pSMB->ByteCount = cpu_to_le16(byte_count);
4516
4517 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4518 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4519 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004520 cFYI(1, "Send error in QFSDeviceInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004521 } else { /* decode response */
4522 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4523
Steve French630f3f0c2007-10-25 21:17:17 +00004524 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004525 rc = -EIO; /* bad smb */
4526 else {
4527 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4528 response_data =
Steve French737b7582005-04-28 22:41:06 -07004529 (FILE_SYSTEM_DEVICE_INFO *)
4530 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004531 data_offset);
4532 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004533 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004534 }
4535 }
4536 cifs_buf_release(pSMB);
4537
4538 if (rc == -EAGAIN)
4539 goto QFSDeviceRetry;
4540
4541 return rc;
4542}
4543
4544int
Steve French737b7582005-04-28 22:41:06 -07004545CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004546{
4547/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4548 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4549 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4550 FILE_SYSTEM_UNIX_INFO *response_data;
4551 int rc = 0;
4552 int bytes_returned = 0;
4553 __u16 params, byte_count;
4554
Joe Perchesb6b38f72010-04-21 03:50:45 +00004555 cFYI(1, "In QFSUnixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004556QFSUnixRetry:
Jeff Laytonf5695992010-09-29 15:27:08 -04004557 rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
4558 (void **) &pSMB, (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004559 if (rc)
4560 return rc;
4561
4562 params = 2; /* level */
4563 pSMB->TotalDataCount = 0;
4564 pSMB->DataCount = 0;
4565 pSMB->DataOffset = 0;
4566 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004567 /* BB find exact max SMB PDU from sess structure BB */
4568 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004569 pSMB->MaxSetupCount = 0;
4570 pSMB->Reserved = 0;
4571 pSMB->Flags = 0;
4572 pSMB->Timeout = 0;
4573 pSMB->Reserved2 = 0;
4574 byte_count = params + 1 /* pad */ ;
4575 pSMB->ParameterCount = cpu_to_le16(params);
4576 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004577 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4578 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004579 pSMB->SetupCount = 1;
4580 pSMB->Reserved3 = 0;
4581 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4582 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4583 pSMB->hdr.smb_buf_length += byte_count;
4584 pSMB->ByteCount = cpu_to_le16(byte_count);
4585
4586 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4587 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4588 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004589 cERROR(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004590 } else { /* decode response */
4591 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4592
4593 if (rc || (pSMBr->ByteCount < 13)) {
4594 rc = -EIO; /* bad smb */
4595 } else {
4596 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4597 response_data =
4598 (FILE_SYSTEM_UNIX_INFO
4599 *) (((char *) &pSMBr->hdr.Protocol) +
4600 data_offset);
4601 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004602 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004603 }
4604 }
4605 cifs_buf_release(pSMB);
4606
4607 if (rc == -EAGAIN)
4608 goto QFSUnixRetry;
4609
4610
4611 return rc;
4612}
4613
Jeremy Allisonac670552005-06-22 17:26:35 -07004614int
Steve French45abc6e2005-06-23 13:42:03 -05004615CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004616{
4617/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4618 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4619 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4620 int rc = 0;
4621 int bytes_returned = 0;
4622 __u16 params, param_offset, offset, byte_count;
4623
Joe Perchesb6b38f72010-04-21 03:50:45 +00004624 cFYI(1, "In SETFSUnixInfo");
Jeremy Allisonac670552005-06-22 17:26:35 -07004625SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004626 /* BB switch to small buf init to save memory */
Jeff Laytonf5695992010-09-29 15:27:08 -04004627 rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
4628 (void **) &pSMB, (void **) &pSMBr);
Jeremy Allisonac670552005-06-22 17:26:35 -07004629 if (rc)
4630 return rc;
4631
4632 params = 4; /* 2 bytes zero followed by info level. */
4633 pSMB->MaxSetupCount = 0;
4634 pSMB->Reserved = 0;
4635 pSMB->Flags = 0;
4636 pSMB->Timeout = 0;
4637 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004638 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4639 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004640 offset = param_offset + params;
4641
4642 pSMB->MaxParameterCount = cpu_to_le16(4);
Steve French582d21e2008-05-13 04:54:12 +00004643 /* BB find exact max SMB PDU from sess structure BB */
4644 pSMB->MaxDataCount = cpu_to_le16(100);
Jeremy Allisonac670552005-06-22 17:26:35 -07004645 pSMB->SetupCount = 1;
4646 pSMB->Reserved3 = 0;
4647 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4648 byte_count = 1 /* pad */ + params + 12;
4649
4650 pSMB->DataCount = cpu_to_le16(12);
4651 pSMB->ParameterCount = cpu_to_le16(params);
4652 pSMB->TotalDataCount = pSMB->DataCount;
4653 pSMB->TotalParameterCount = pSMB->ParameterCount;
4654 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4655 pSMB->DataOffset = cpu_to_le16(offset);
4656
4657 /* Params. */
4658 pSMB->FileNum = 0;
4659 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4660
4661 /* Data. */
4662 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4663 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4664 pSMB->ClientUnixCap = cpu_to_le64(cap);
4665
4666 pSMB->hdr.smb_buf_length += byte_count;
4667 pSMB->ByteCount = cpu_to_le16(byte_count);
4668
4669 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4670 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4671 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004672 cERROR(1, "Send error in SETFSUnixInfo = %d", rc);
Jeremy Allisonac670552005-06-22 17:26:35 -07004673 } else { /* decode response */
4674 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004675 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004676 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004677 }
4678 cifs_buf_release(pSMB);
4679
4680 if (rc == -EAGAIN)
4681 goto SETFSUnixRetry;
4682
4683 return rc;
4684}
4685
4686
Linus Torvalds1da177e2005-04-16 15:20:36 -07004687
4688int
4689CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004690 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004691{
4692/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4693 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4694 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4695 FILE_SYSTEM_POSIX_INFO *response_data;
4696 int rc = 0;
4697 int bytes_returned = 0;
4698 __u16 params, byte_count;
4699
Joe Perchesb6b38f72010-04-21 03:50:45 +00004700 cFYI(1, "In QFSPosixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004701QFSPosixRetry:
4702 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4703 (void **) &pSMBr);
4704 if (rc)
4705 return rc;
4706
4707 params = 2; /* level */
4708 pSMB->TotalDataCount = 0;
4709 pSMB->DataCount = 0;
4710 pSMB->DataOffset = 0;
4711 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004712 /* BB find exact max SMB PDU from sess structure BB */
4713 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004714 pSMB->MaxSetupCount = 0;
4715 pSMB->Reserved = 0;
4716 pSMB->Flags = 0;
4717 pSMB->Timeout = 0;
4718 pSMB->Reserved2 = 0;
4719 byte_count = params + 1 /* pad */ ;
4720 pSMB->ParameterCount = cpu_to_le16(params);
4721 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004722 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4723 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004724 pSMB->SetupCount = 1;
4725 pSMB->Reserved3 = 0;
4726 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4727 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4728 pSMB->hdr.smb_buf_length += byte_count;
4729 pSMB->ByteCount = cpu_to_le16(byte_count);
4730
4731 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4732 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4733 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004734 cFYI(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004735 } else { /* decode response */
4736 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4737
4738 if (rc || (pSMBr->ByteCount < 13)) {
4739 rc = -EIO; /* bad smb */
4740 } else {
4741 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4742 response_data =
4743 (FILE_SYSTEM_POSIX_INFO
4744 *) (((char *) &pSMBr->hdr.Protocol) +
4745 data_offset);
4746 FSData->f_bsize =
4747 le32_to_cpu(response_data->BlockSize);
4748 FSData->f_blocks =
4749 le64_to_cpu(response_data->TotalBlocks);
4750 FSData->f_bfree =
4751 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004752 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004753 FSData->f_bavail = FSData->f_bfree;
4754 } else {
4755 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004756 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004757 }
Steve French790fe572007-07-07 19:25:05 +00004758 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004759 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004760 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004761 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004762 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004763 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004764 }
4765 }
4766 cifs_buf_release(pSMB);
4767
4768 if (rc == -EAGAIN)
4769 goto QFSPosixRetry;
4770
4771 return rc;
4772}
4773
4774
Steve French50c2f752007-07-13 00:33:32 +00004775/* We can not use write of zero bytes trick to
4776 set file size due to need for large file support. Also note that
4777 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004778 routine which is only needed to work around a sharing violation bug
4779 in Samba which this routine can run into */
4780
4781int
4782CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French4b18f2a2008-04-29 00:06:05 +00004783 __u64 size, bool SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004784 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004785{
4786 struct smb_com_transaction2_spi_req *pSMB = NULL;
4787 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4788 struct file_end_of_file_info *parm_data;
4789 int name_len;
4790 int rc = 0;
4791 int bytes_returned = 0;
4792 __u16 params, byte_count, data_count, param_offset, offset;
4793
Joe Perchesb6b38f72010-04-21 03:50:45 +00004794 cFYI(1, "In SetEOF");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004795SetEOFRetry:
4796 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4797 (void **) &pSMBr);
4798 if (rc)
4799 return rc;
4800
4801 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4802 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004803 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004804 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004805 name_len++; /* trailing null */
4806 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004807 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004808 name_len = strnlen(fileName, PATH_MAX);
4809 name_len++; /* trailing null */
4810 strncpy(pSMB->FileName, fileName, name_len);
4811 }
4812 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004813 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004814 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004815 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004816 pSMB->MaxSetupCount = 0;
4817 pSMB->Reserved = 0;
4818 pSMB->Flags = 0;
4819 pSMB->Timeout = 0;
4820 pSMB->Reserved2 = 0;
4821 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004822 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004823 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004824 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004825 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4826 pSMB->InformationLevel =
4827 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4828 else
4829 pSMB->InformationLevel =
4830 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4831 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004832 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4833 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004834 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004835 else
4836 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004837 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004838 }
4839
4840 parm_data =
4841 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4842 offset);
4843 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4844 pSMB->DataOffset = cpu_to_le16(offset);
4845 pSMB->SetupCount = 1;
4846 pSMB->Reserved3 = 0;
4847 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4848 byte_count = 3 /* pad */ + params + data_count;
4849 pSMB->DataCount = cpu_to_le16(data_count);
4850 pSMB->TotalDataCount = pSMB->DataCount;
4851 pSMB->ParameterCount = cpu_to_le16(params);
4852 pSMB->TotalParameterCount = pSMB->ParameterCount;
4853 pSMB->Reserved4 = 0;
4854 pSMB->hdr.smb_buf_length += byte_count;
4855 parm_data->FileSize = cpu_to_le64(size);
4856 pSMB->ByteCount = cpu_to_le16(byte_count);
4857 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4858 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004859 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00004860 cFYI(1, "SetPathInfo (file size) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004861
4862 cifs_buf_release(pSMB);
4863
4864 if (rc == -EAGAIN)
4865 goto SetEOFRetry;
4866
4867 return rc;
4868}
4869
4870int
Steve French50c2f752007-07-13 00:33:32 +00004871CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
Steve French4b18f2a2008-04-29 00:06:05 +00004872 __u16 fid, __u32 pid_of_opener, bool SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004873{
4874 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004875 char *data_offset;
4876 struct file_end_of_file_info *parm_data;
4877 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004878 __u16 params, param_offset, offset, byte_count, count;
4879
Joe Perchesb6b38f72010-04-21 03:50:45 +00004880 cFYI(1, "SetFileSize (via SetFileInfo) %lld",
4881 (long long)size);
Steve Frenchcd634992005-04-28 22:41:10 -07004882 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4883
Linus Torvalds1da177e2005-04-16 15:20:36 -07004884 if (rc)
4885 return rc;
4886
4887 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4888 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004889
Linus Torvalds1da177e2005-04-16 15:20:36 -07004890 params = 6;
4891 pSMB->MaxSetupCount = 0;
4892 pSMB->Reserved = 0;
4893 pSMB->Flags = 0;
4894 pSMB->Timeout = 0;
4895 pSMB->Reserved2 = 0;
4896 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4897 offset = param_offset + params;
4898
Steve French50c2f752007-07-13 00:33:32 +00004899 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004900
4901 count = sizeof(struct file_end_of_file_info);
4902 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004903 /* BB find exact max SMB PDU from sess structure BB */
4904 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004905 pSMB->SetupCount = 1;
4906 pSMB->Reserved3 = 0;
4907 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4908 byte_count = 3 /* pad */ + params + count;
4909 pSMB->DataCount = cpu_to_le16(count);
4910 pSMB->ParameterCount = cpu_to_le16(params);
4911 pSMB->TotalDataCount = pSMB->DataCount;
4912 pSMB->TotalParameterCount = pSMB->ParameterCount;
4913 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4914 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004915 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4916 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004917 pSMB->DataOffset = cpu_to_le16(offset);
4918 parm_data->FileSize = cpu_to_le64(size);
4919 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004920 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004921 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4922 pSMB->InformationLevel =
4923 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4924 else
4925 pSMB->InformationLevel =
4926 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004927 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004928 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4929 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004930 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004931 else
4932 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004933 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004934 }
4935 pSMB->Reserved4 = 0;
4936 pSMB->hdr.smb_buf_length += byte_count;
4937 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004938 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004939 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004940 cFYI(1, "Send error in SetFileInfo (SetFileSize) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004941 }
4942
Steve French50c2f752007-07-13 00:33:32 +00004943 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004944 since file handle passed in no longer valid */
4945
4946 return rc;
4947}
4948
Steve French50c2f752007-07-13 00:33:32 +00004949/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004950 an open handle, rather than by pathname - this is awkward due to
4951 potential access conflicts on the open, but it is unavoidable for these
4952 old servers since the only other choice is to go from 100 nanosecond DCE
4953 time and resort to the original setpathinfo level which takes the ancient
4954 DOS time format with 2 second granularity */
4955int
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004956CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
4957 const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004958{
4959 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004960 char *data_offset;
4961 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004962 __u16 params, param_offset, offset, byte_count, count;
4963
Joe Perchesb6b38f72010-04-21 03:50:45 +00004964 cFYI(1, "Set Times (via SetFileInfo)");
Steve Frenchcd634992005-04-28 22:41:10 -07004965 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4966
Linus Torvalds1da177e2005-04-16 15:20:36 -07004967 if (rc)
4968 return rc;
4969
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004970 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4971 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004972
Linus Torvalds1da177e2005-04-16 15:20:36 -07004973 params = 6;
4974 pSMB->MaxSetupCount = 0;
4975 pSMB->Reserved = 0;
4976 pSMB->Flags = 0;
4977 pSMB->Timeout = 0;
4978 pSMB->Reserved2 = 0;
4979 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4980 offset = param_offset + params;
4981
Steve French50c2f752007-07-13 00:33:32 +00004982 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004983
Steve French26f57362007-08-30 22:09:15 +00004984 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004985 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004986 /* BB find max SMB PDU from sess */
4987 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004988 pSMB->SetupCount = 1;
4989 pSMB->Reserved3 = 0;
4990 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4991 byte_count = 3 /* pad */ + params + count;
4992 pSMB->DataCount = cpu_to_le16(count);
4993 pSMB->ParameterCount = cpu_to_le16(params);
4994 pSMB->TotalDataCount = pSMB->DataCount;
4995 pSMB->TotalParameterCount = pSMB->ParameterCount;
4996 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4997 pSMB->DataOffset = cpu_to_le16(offset);
4998 pSMB->Fid = fid;
4999 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5000 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5001 else
5002 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5003 pSMB->Reserved4 = 0;
5004 pSMB->hdr.smb_buf_length += byte_count;
5005 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00005006 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00005007 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005008 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005009 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005010
Steve French50c2f752007-07-13 00:33:32 +00005011 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07005012 since file handle passed in no longer valid */
5013
5014 return rc;
5015}
5016
Jeff Layton6d22f092008-09-23 11:48:35 -04005017int
5018CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
5019 bool delete_file, __u16 fid, __u32 pid_of_opener)
5020{
5021 struct smb_com_transaction2_sfi_req *pSMB = NULL;
5022 char *data_offset;
5023 int rc = 0;
5024 __u16 params, param_offset, offset, byte_count, count;
5025
Joe Perchesb6b38f72010-04-21 03:50:45 +00005026 cFYI(1, "Set File Disposition (via SetFileInfo)");
Jeff Layton6d22f092008-09-23 11:48:35 -04005027 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5028
5029 if (rc)
5030 return rc;
5031
5032 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5033 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5034
5035 params = 6;
5036 pSMB->MaxSetupCount = 0;
5037 pSMB->Reserved = 0;
5038 pSMB->Flags = 0;
5039 pSMB->Timeout = 0;
5040 pSMB->Reserved2 = 0;
5041 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5042 offset = param_offset + params;
5043
5044 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5045
5046 count = 1;
5047 pSMB->MaxParameterCount = cpu_to_le16(2);
5048 /* BB find max SMB PDU from sess */
5049 pSMB->MaxDataCount = cpu_to_le16(1000);
5050 pSMB->SetupCount = 1;
5051 pSMB->Reserved3 = 0;
5052 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5053 byte_count = 3 /* pad */ + params + count;
5054 pSMB->DataCount = cpu_to_le16(count);
5055 pSMB->ParameterCount = cpu_to_le16(params);
5056 pSMB->TotalDataCount = pSMB->DataCount;
5057 pSMB->TotalParameterCount = pSMB->ParameterCount;
5058 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5059 pSMB->DataOffset = cpu_to_le16(offset);
5060 pSMB->Fid = fid;
5061 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
5062 pSMB->Reserved4 = 0;
5063 pSMB->hdr.smb_buf_length += byte_count;
5064 pSMB->ByteCount = cpu_to_le16(byte_count);
5065 *data_offset = delete_file ? 1 : 0;
5066 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5067 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005068 cFYI(1, "Send error in SetFileDisposition = %d", rc);
Jeff Layton6d22f092008-09-23 11:48:35 -04005069
5070 return rc;
5071}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005072
5073int
Jeff Layton6fc000e2008-08-02 07:26:12 -04005074CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
5075 const char *fileName, const FILE_BASIC_INFO *data,
5076 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005077{
5078 TRANSACTION2_SPI_REQ *pSMB = NULL;
5079 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5080 int name_len;
5081 int rc = 0;
5082 int bytes_returned = 0;
5083 char *data_offset;
5084 __u16 params, param_offset, offset, byte_count, count;
5085
Joe Perchesb6b38f72010-04-21 03:50:45 +00005086 cFYI(1, "In SetTimes");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005087
5088SetTimesRetry:
5089 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5090 (void **) &pSMBr);
5091 if (rc)
5092 return rc;
5093
5094 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5095 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05005096 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005097 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005098 name_len++; /* trailing null */
5099 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005100 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005101 name_len = strnlen(fileName, PATH_MAX);
5102 name_len++; /* trailing null */
5103 strncpy(pSMB->FileName, fileName, name_len);
5104 }
5105
5106 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005107 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005108 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005109 /* BB find max SMB PDU from sess structure BB */
5110 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005111 pSMB->MaxSetupCount = 0;
5112 pSMB->Reserved = 0;
5113 pSMB->Flags = 0;
5114 pSMB->Timeout = 0;
5115 pSMB->Reserved2 = 0;
5116 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005117 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005118 offset = param_offset + params;
5119 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5120 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5121 pSMB->DataOffset = cpu_to_le16(offset);
5122 pSMB->SetupCount = 1;
5123 pSMB->Reserved3 = 0;
5124 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5125 byte_count = 3 /* pad */ + params + count;
5126
5127 pSMB->DataCount = cpu_to_le16(count);
5128 pSMB->ParameterCount = cpu_to_le16(params);
5129 pSMB->TotalDataCount = pSMB->DataCount;
5130 pSMB->TotalParameterCount = pSMB->ParameterCount;
5131 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5132 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5133 else
5134 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5135 pSMB->Reserved4 = 0;
5136 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00005137 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005138 pSMB->ByteCount = cpu_to_le16(byte_count);
5139 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5140 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005141 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005142 cFYI(1, "SetPathInfo (times) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005143
5144 cifs_buf_release(pSMB);
5145
5146 if (rc == -EAGAIN)
5147 goto SetTimesRetry;
5148
5149 return rc;
5150}
5151
5152/* Can not be used to set time stamps yet (due to old DOS time format) */
5153/* Can be used to set attributes */
5154#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
5155 handling it anyway and NT4 was what we thought it would be needed for
5156 Do not delete it until we prove whether needed for Win9x though */
5157int
5158CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
5159 __u16 dos_attrs, const struct nls_table *nls_codepage)
5160{
5161 SETATTR_REQ *pSMB = NULL;
5162 SETATTR_RSP *pSMBr = NULL;
5163 int rc = 0;
5164 int bytes_returned;
5165 int name_len;
5166
Joe Perchesb6b38f72010-04-21 03:50:45 +00005167 cFYI(1, "In SetAttrLegacy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005168
5169SetAttrLgcyRetry:
5170 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
5171 (void **) &pSMBr);
5172 if (rc)
5173 return rc;
5174
5175 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5176 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005177 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005178 PATH_MAX, nls_codepage);
5179 name_len++; /* trailing null */
5180 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005181 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005182 name_len = strnlen(fileName, PATH_MAX);
5183 name_len++; /* trailing null */
5184 strncpy(pSMB->fileName, fileName, name_len);
5185 }
5186 pSMB->attr = cpu_to_le16(dos_attrs);
5187 pSMB->BufferFormat = 0x04;
5188 pSMB->hdr.smb_buf_length += name_len + 1;
5189 pSMB->ByteCount = cpu_to_le16(name_len + 1);
5190 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5191 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005192 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005193 cFYI(1, "Error in LegacySetAttr = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005194
5195 cifs_buf_release(pSMB);
5196
5197 if (rc == -EAGAIN)
5198 goto SetAttrLgcyRetry;
5199
5200 return rc;
5201}
5202#endif /* temporarily unneeded SetAttr legacy function */
5203
Jeff Layton654cf142009-07-09 20:02:49 -04005204static void
5205cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,
5206 const struct cifs_unix_set_info_args *args)
5207{
5208 u64 mode = args->mode;
5209
5210 /*
5211 * Samba server ignores set of file size to zero due to bugs in some
5212 * older clients, but we should be precise - we use SetFileSize to
5213 * set file size and do not want to truncate file size to zero
5214 * accidently as happened on one Samba server beta by putting
5215 * zero instead of -1 here
5216 */
5217 data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
5218 data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
5219 data_offset->LastStatusChange = cpu_to_le64(args->ctime);
5220 data_offset->LastAccessTime = cpu_to_le64(args->atime);
5221 data_offset->LastModificationTime = cpu_to_le64(args->mtime);
5222 data_offset->Uid = cpu_to_le64(args->uid);
5223 data_offset->Gid = cpu_to_le64(args->gid);
5224 /* better to leave device as zero when it is */
5225 data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
5226 data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
5227 data_offset->Permissions = cpu_to_le64(mode);
5228
5229 if (S_ISREG(mode))
5230 data_offset->Type = cpu_to_le32(UNIX_FILE);
5231 else if (S_ISDIR(mode))
5232 data_offset->Type = cpu_to_le32(UNIX_DIR);
5233 else if (S_ISLNK(mode))
5234 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
5235 else if (S_ISCHR(mode))
5236 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
5237 else if (S_ISBLK(mode))
5238 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
5239 else if (S_ISFIFO(mode))
5240 data_offset->Type = cpu_to_le32(UNIX_FIFO);
5241 else if (S_ISSOCK(mode))
5242 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5243}
5244
Linus Torvalds1da177e2005-04-16 15:20:36 -07005245int
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005246CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon,
5247 const struct cifs_unix_set_info_args *args,
5248 u16 fid, u32 pid_of_opener)
5249{
5250 struct smb_com_transaction2_sfi_req *pSMB = NULL;
5251 FILE_UNIX_BASIC_INFO *data_offset;
5252 int rc = 0;
5253 u16 params, param_offset, offset, byte_count, count;
5254
Joe Perchesb6b38f72010-04-21 03:50:45 +00005255 cFYI(1, "Set Unix Info (via SetFileInfo)");
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005256 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5257
5258 if (rc)
5259 return rc;
5260
5261 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5262 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5263
5264 params = 6;
5265 pSMB->MaxSetupCount = 0;
5266 pSMB->Reserved = 0;
5267 pSMB->Flags = 0;
5268 pSMB->Timeout = 0;
5269 pSMB->Reserved2 = 0;
5270 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5271 offset = param_offset + params;
5272
5273 data_offset = (FILE_UNIX_BASIC_INFO *)
5274 ((char *)(&pSMB->hdr.Protocol) + offset);
5275 count = sizeof(FILE_UNIX_BASIC_INFO);
5276
5277 pSMB->MaxParameterCount = cpu_to_le16(2);
5278 /* BB find max SMB PDU from sess */
5279 pSMB->MaxDataCount = cpu_to_le16(1000);
5280 pSMB->SetupCount = 1;
5281 pSMB->Reserved3 = 0;
5282 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5283 byte_count = 3 /* pad */ + params + count;
5284 pSMB->DataCount = cpu_to_le16(count);
5285 pSMB->ParameterCount = cpu_to_le16(params);
5286 pSMB->TotalDataCount = pSMB->DataCount;
5287 pSMB->TotalParameterCount = pSMB->ParameterCount;
5288 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5289 pSMB->DataOffset = cpu_to_le16(offset);
5290 pSMB->Fid = fid;
5291 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5292 pSMB->Reserved4 = 0;
5293 pSMB->hdr.smb_buf_length += byte_count;
5294 pSMB->ByteCount = cpu_to_le16(byte_count);
5295
5296 cifs_fill_unix_set_info(data_offset, args);
5297
5298 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5299 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005300 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005301
5302 /* Note: On -EAGAIN error only caller can retry on handle based calls
5303 since file handle passed in no longer valid */
5304
5305 return rc;
5306}
5307
5308int
Jeff Layton01ea95e2009-07-09 20:02:49 -04005309CIFSSMBUnixSetPathInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,
5310 const struct cifs_unix_set_info_args *args,
5311 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005312{
5313 TRANSACTION2_SPI_REQ *pSMB = NULL;
5314 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5315 int name_len;
5316 int rc = 0;
5317 int bytes_returned = 0;
5318 FILE_UNIX_BASIC_INFO *data_offset;
5319 __u16 params, param_offset, offset, count, byte_count;
5320
Joe Perchesb6b38f72010-04-21 03:50:45 +00005321 cFYI(1, "In SetUID/GID/Mode");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005322setPermsRetry:
5323 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5324 (void **) &pSMBr);
5325 if (rc)
5326 return rc;
5327
5328 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5329 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005330 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005331 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005332 name_len++; /* trailing null */
5333 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07005334 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005335 name_len = strnlen(fileName, PATH_MAX);
5336 name_len++; /* trailing null */
5337 strncpy(pSMB->FileName, fileName, name_len);
5338 }
5339
5340 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005341 count = sizeof(FILE_UNIX_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005342 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005343 /* BB find max SMB PDU from sess structure BB */
5344 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005345 pSMB->MaxSetupCount = 0;
5346 pSMB->Reserved = 0;
5347 pSMB->Flags = 0;
5348 pSMB->Timeout = 0;
5349 pSMB->Reserved2 = 0;
5350 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005351 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005352 offset = param_offset + params;
5353 data_offset =
5354 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5355 offset);
5356 memset(data_offset, 0, count);
5357 pSMB->DataOffset = cpu_to_le16(offset);
5358 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5359 pSMB->SetupCount = 1;
5360 pSMB->Reserved3 = 0;
5361 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5362 byte_count = 3 /* pad */ + params + count;
5363 pSMB->ParameterCount = cpu_to_le16(params);
5364 pSMB->DataCount = cpu_to_le16(count);
5365 pSMB->TotalParameterCount = pSMB->ParameterCount;
5366 pSMB->TotalDataCount = pSMB->DataCount;
5367 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5368 pSMB->Reserved4 = 0;
5369 pSMB->hdr.smb_buf_length += byte_count;
Steve French50c2f752007-07-13 00:33:32 +00005370
Jeff Layton654cf142009-07-09 20:02:49 -04005371 cifs_fill_unix_set_info(data_offset, args);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005372
5373 pSMB->ByteCount = cpu_to_le16(byte_count);
5374 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5375 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005376 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005377 cFYI(1, "SetPathInfo (perms) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005378
Steve French0d817bc2008-05-22 02:02:03 +00005379 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005380 if (rc == -EAGAIN)
5381 goto setPermsRetry;
5382 return rc;
5383}
5384
Steve French50c2f752007-07-13 00:33:32 +00005385int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005386 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005387 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005388 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005389{
5390 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005391 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5392 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005393 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005394 int bytes_returned;
5395
Joe Perchesb6b38f72010-04-21 03:50:45 +00005396 cFYI(1, "In CIFSSMBNotify for file handle %d", (int)netfid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005397 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005398 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005399 if (rc)
5400 return rc;
5401
5402 pSMB->TotalParameterCount = 0 ;
5403 pSMB->TotalDataCount = 0;
5404 pSMB->MaxParameterCount = cpu_to_le32(2);
5405 /* BB find exact data count max from sess structure BB */
5406 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005407/* BB VERIFY verify which is correct for above BB */
5408 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5409 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5410
Linus Torvalds1da177e2005-04-16 15:20:36 -07005411 pSMB->MaxSetupCount = 4;
5412 pSMB->Reserved = 0;
5413 pSMB->ParameterOffset = 0;
5414 pSMB->DataCount = 0;
5415 pSMB->DataOffset = 0;
5416 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5417 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5418 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005419 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005420 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5421 pSMB->Reserved2 = 0;
5422 pSMB->CompletionFilter = cpu_to_le32(filter);
5423 pSMB->Fid = netfid; /* file handle always le */
5424 pSMB->ByteCount = 0;
5425
5426 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005427 (struct smb_hdr *)pSMBr, &bytes_returned,
5428 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005429 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005430 cFYI(1, "Error in Notify = %d", rc);
Steve Frenchff5dbd92005-08-24 17:10:36 -07005431 } else {
5432 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005433 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005434 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005435 sizeof(struct dir_notify_req),
5436 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005437 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005438 dnotify_req->Pid = pSMB->hdr.Pid;
5439 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5440 dnotify_req->Mid = pSMB->hdr.Mid;
5441 dnotify_req->Tid = pSMB->hdr.Tid;
5442 dnotify_req->Uid = pSMB->hdr.Uid;
5443 dnotify_req->netfid = netfid;
5444 dnotify_req->pfile = pfile;
5445 dnotify_req->filter = filter;
5446 dnotify_req->multishot = multishot;
5447 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005448 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005449 &GlobalDnotifyReqList);
5450 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005451 } else
Steve French47c786e2005-10-11 20:03:18 -07005452 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005453 }
5454 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005455 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005456}
Jeff Layton31c05192010-02-10 16:18:26 -05005457
Linus Torvalds1da177e2005-04-16 15:20:36 -07005458#ifdef CONFIG_CIFS_XATTR
Jeff Layton31c05192010-02-10 16:18:26 -05005459/*
5460 * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common
5461 * function used by listxattr and getxattr type calls. When ea_name is set,
5462 * it looks for that attribute name and stuffs that value into the EAData
5463 * buffer. When ea_name is NULL, it stuffs a list of attribute names into the
5464 * buffer. In both cases, the return value is either the length of the
5465 * resulting data or a negative error code. If EAData is a NULL pointer then
5466 * the data isn't copied to it, but the length is returned.
5467 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005468ssize_t
5469CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
Jeff Layton31c05192010-02-10 16:18:26 -05005470 const unsigned char *searchName, const unsigned char *ea_name,
5471 char *EAData, size_t buf_size,
5472 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005473{
5474 /* BB assumes one setup word */
5475 TRANSACTION2_QPI_REQ *pSMB = NULL;
5476 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5477 int rc = 0;
5478 int bytes_returned;
Jeff Layton6e462b92010-02-10 16:18:26 -05005479 int list_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005480 struct fealist *ea_response_data;
Steve French50c2f752007-07-13 00:33:32 +00005481 struct fea *temp_fea;
5482 char *temp_ptr;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005483 char *end_of_smb;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005484 __u16 params, byte_count, data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005485
Joe Perchesb6b38f72010-04-21 03:50:45 +00005486 cFYI(1, "In Query All EAs path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005487QAllEAsRetry:
5488 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5489 (void **) &pSMBr);
5490 if (rc)
5491 return rc;
5492
5493 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Jeff Layton6e462b92010-02-10 16:18:26 -05005494 list_len =
Steve French50c2f752007-07-13 00:33:32 +00005495 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005496 PATH_MAX, nls_codepage, remap);
Jeff Layton6e462b92010-02-10 16:18:26 -05005497 list_len++; /* trailing null */
5498 list_len *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005499 } else { /* BB improve the check for buffer overruns BB */
Jeff Layton6e462b92010-02-10 16:18:26 -05005500 list_len = strnlen(searchName, PATH_MAX);
5501 list_len++; /* trailing null */
5502 strncpy(pSMB->FileName, searchName, list_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005503 }
5504
Jeff Layton6e462b92010-02-10 16:18:26 -05005505 params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005506 pSMB->TotalDataCount = 0;
5507 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005508 /* BB find exact max SMB PDU from sess structure BB */
Jeff Laytone5296142010-02-10 16:18:26 -05005509 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005510 pSMB->MaxSetupCount = 0;
5511 pSMB->Reserved = 0;
5512 pSMB->Flags = 0;
5513 pSMB->Timeout = 0;
5514 pSMB->Reserved2 = 0;
5515 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005516 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005517 pSMB->DataCount = 0;
5518 pSMB->DataOffset = 0;
5519 pSMB->SetupCount = 1;
5520 pSMB->Reserved3 = 0;
5521 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5522 byte_count = params + 1 /* pad */ ;
5523 pSMB->TotalParameterCount = cpu_to_le16(params);
5524 pSMB->ParameterCount = pSMB->TotalParameterCount;
5525 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5526 pSMB->Reserved4 = 0;
5527 pSMB->hdr.smb_buf_length += byte_count;
5528 pSMB->ByteCount = cpu_to_le16(byte_count);
5529
5530 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5531 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5532 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005533 cFYI(1, "Send error in QueryAllEAs = %d", rc);
Jeff Laytonf0d38682010-02-10 16:18:26 -05005534 goto QAllEAsOut;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005535 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005536
5537
5538 /* BB also check enough total bytes returned */
5539 /* BB we need to improve the validity checking
5540 of these trans2 responses */
5541
5542 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5543 if (rc || (pSMBr->ByteCount < 4)) {
5544 rc = -EIO; /* bad smb */
5545 goto QAllEAsOut;
5546 }
5547
5548 /* check that length of list is not more than bcc */
5549 /* check that each entry does not go beyond length
5550 of list */
5551 /* check that each element of each entry does not
5552 go beyond end of list */
5553 /* validate_trans2_offsets() */
5554 /* BB check if start of smb + data_offset > &bcc+ bcc */
5555
5556 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5557 ea_response_data = (struct fealist *)
5558 (((char *) &pSMBr->hdr.Protocol) + data_offset);
5559
Jeff Layton6e462b92010-02-10 16:18:26 -05005560 list_len = le32_to_cpu(ea_response_data->list_len);
Joe Perchesb6b38f72010-04-21 03:50:45 +00005561 cFYI(1, "ea length %d", list_len);
Jeff Layton6e462b92010-02-10 16:18:26 -05005562 if (list_len <= 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005563 cFYI(1, "empty EA list returned from server");
Jeff Laytonf0d38682010-02-10 16:18:26 -05005564 goto QAllEAsOut;
5565 }
5566
Jeff Layton0cd126b2010-02-10 16:18:26 -05005567 /* make sure list_len doesn't go past end of SMB */
5568 end_of_smb = (char *)pByteArea(&pSMBr->hdr) + BCC(&pSMBr->hdr);
5569 if ((char *)ea_response_data + list_len > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005570 cFYI(1, "EA list appears to go beyond SMB");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005571 rc = -EIO;
5572 goto QAllEAsOut;
5573 }
5574
Jeff Laytonf0d38682010-02-10 16:18:26 -05005575 /* account for ea list len */
Jeff Layton6e462b92010-02-10 16:18:26 -05005576 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005577 temp_fea = ea_response_data->list;
5578 temp_ptr = (char *)temp_fea;
Jeff Layton6e462b92010-02-10 16:18:26 -05005579 while (list_len > 0) {
Steve French122ca002010-02-24 21:56:48 +00005580 unsigned int name_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005581 __u16 value_len;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005582
Jeff Layton6e462b92010-02-10 16:18:26 -05005583 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005584 temp_ptr += 4;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005585 /* make sure we can read name_len and value_len */
5586 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005587 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005588 rc = -EIO;
5589 goto QAllEAsOut;
5590 }
5591
5592 name_len = temp_fea->name_len;
5593 value_len = le16_to_cpu(temp_fea->value_len);
5594 list_len -= name_len + 1 + value_len;
5595 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005596 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005597 rc = -EIO;
5598 goto QAllEAsOut;
5599 }
5600
Jeff Layton31c05192010-02-10 16:18:26 -05005601 if (ea_name) {
5602 if (strncmp(ea_name, temp_ptr, name_len) == 0) {
5603 temp_ptr += name_len + 1;
5604 rc = value_len;
5605 if (buf_size == 0)
5606 goto QAllEAsOut;
5607 if ((size_t)value_len > buf_size) {
5608 rc = -ERANGE;
5609 goto QAllEAsOut;
5610 }
5611 memcpy(EAData, temp_ptr, value_len);
5612 goto QAllEAsOut;
5613 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005614 } else {
Jeff Layton31c05192010-02-10 16:18:26 -05005615 /* account for prefix user. and trailing null */
5616 rc += (5 + 1 + name_len);
5617 if (rc < (int) buf_size) {
5618 memcpy(EAData, "user.", 5);
5619 EAData += 5;
5620 memcpy(EAData, temp_ptr, name_len);
5621 EAData += name_len;
5622 /* null terminate name */
5623 *EAData = 0;
5624 ++EAData;
5625 } else if (buf_size == 0) {
5626 /* skip copy - calc size only */
5627 } else {
5628 /* stop before overrun buffer */
5629 rc = -ERANGE;
5630 break;
5631 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005632 }
Jeff Layton0cd126b2010-02-10 16:18:26 -05005633 temp_ptr += name_len + 1 + value_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005634 temp_fea = (struct fea *)temp_ptr;
5635 }
5636
Jeff Layton31c05192010-02-10 16:18:26 -05005637 /* didn't find the named attribute */
5638 if (ea_name)
5639 rc = -ENODATA;
5640
Jeff Laytonf0d38682010-02-10 16:18:26 -05005641QAllEAsOut:
Steve French0d817bc2008-05-22 02:02:03 +00005642 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005643 if (rc == -EAGAIN)
5644 goto QAllEAsRetry;
5645
5646 return (ssize_t)rc;
5647}
5648
Linus Torvalds1da177e2005-04-16 15:20:36 -07005649int
5650CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005651 const char *ea_name, const void *ea_value,
5652 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5653 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005654{
5655 struct smb_com_transaction2_spi_req *pSMB = NULL;
5656 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5657 struct fealist *parm_data;
5658 int name_len;
5659 int rc = 0;
5660 int bytes_returned = 0;
5661 __u16 params, param_offset, byte_count, offset, count;
5662
Joe Perchesb6b38f72010-04-21 03:50:45 +00005663 cFYI(1, "In SetEA");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005664SetEARetry:
5665 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5666 (void **) &pSMBr);
5667 if (rc)
5668 return rc;
5669
5670 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5671 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005672 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005673 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005674 name_len++; /* trailing null */
5675 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005676 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005677 name_len = strnlen(fileName, PATH_MAX);
5678 name_len++; /* trailing null */
5679 strncpy(pSMB->FileName, fileName, name_len);
5680 }
5681
5682 params = 6 + name_len;
5683
5684 /* done calculating parms using name_len of file name,
5685 now use name_len to calculate length of ea name
5686 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005687 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005688 name_len = 0;
5689 else
Steve French50c2f752007-07-13 00:33:32 +00005690 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005691
Steve Frenchdae5dbdb2007-12-30 23:49:57 +00005692 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005693 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005694 /* BB find max SMB PDU from sess */
5695 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005696 pSMB->MaxSetupCount = 0;
5697 pSMB->Reserved = 0;
5698 pSMB->Flags = 0;
5699 pSMB->Timeout = 0;
5700 pSMB->Reserved2 = 0;
5701 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005702 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005703 offset = param_offset + params;
5704 pSMB->InformationLevel =
5705 cpu_to_le16(SMB_SET_FILE_EA);
5706
5707 parm_data =
5708 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5709 offset);
5710 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5711 pSMB->DataOffset = cpu_to_le16(offset);
5712 pSMB->SetupCount = 1;
5713 pSMB->Reserved3 = 0;
5714 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5715 byte_count = 3 /* pad */ + params + count;
5716 pSMB->DataCount = cpu_to_le16(count);
5717 parm_data->list_len = cpu_to_le32(count);
5718 parm_data->list[0].EA_flags = 0;
5719 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005720 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005721 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005722 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005723 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005724 parm_data->list[0].name[name_len] = 0;
5725 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5726 /* caller ensures that ea_value_len is less than 64K but
5727 we need to ensure that it fits within the smb */
5728
Steve French50c2f752007-07-13 00:33:32 +00005729 /*BB add length check to see if it would fit in
5730 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005731 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5732 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005733 memcpy(parm_data->list[0].name+name_len+1,
5734 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005735
5736 pSMB->TotalDataCount = pSMB->DataCount;
5737 pSMB->ParameterCount = cpu_to_le16(params);
5738 pSMB->TotalParameterCount = pSMB->ParameterCount;
5739 pSMB->Reserved4 = 0;
5740 pSMB->hdr.smb_buf_length += byte_count;
5741 pSMB->ByteCount = cpu_to_le16(byte_count);
5742 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5743 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005744 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005745 cFYI(1, "SetPathInfo (EA) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005746
5747 cifs_buf_release(pSMB);
5748
5749 if (rc == -EAGAIN)
5750 goto SetEARetry;
5751
5752 return rc;
5753}
5754
5755#endif