blob: 2f6795e524d3f39c34c2a1dbed77e11149f9330c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/cifssmb.c
3 *
Steve Frenchf19159d2010-04-21 04:12:10 +00004 * Copyright (C) International Business Machines Corp., 2002,2010
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Author(s): Steve French (sfrench@us.ibm.com)
6 *
7 * Contains the routines for constructing the SMB PDUs themselves
8 *
9 * This library is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published
11 * by the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */
25 /* These are mostly routines that operate on a pathname, or on a tree id */
26 /* (mounted volume), but there are eight handle based routines which must be */
Steve French2dd29d32007-04-23 22:07:35 +000027 /* treated slightly differently for reconnection purposes since we never */
28 /* want to reuse a stale file handle and only the caller knows the file info */
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#include <linux/fs.h>
31#include <linux/kernel.h>
32#include <linux/vfs.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090033#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <linux/posix_acl_xattr.h>
35#include <asm/uaccess.h>
36#include "cifspdu.h"
37#include "cifsglob.h"
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +000038#include "cifsacl.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include "cifsproto.h"
40#include "cifs_unicode.h"
41#include "cifs_debug.h"
42
43#ifdef CONFIG_CIFS_POSIX
44static struct {
45 int index;
46 char *name;
47} protocols[] = {
Steve French39798772006-05-31 22:40:51 +000048#ifdef CONFIG_CIFS_WEAK_PW_HASH
49 {LANMAN_PROT, "\2LM1.2X002"},
Steve French9ac00b72006-09-30 04:13:17 +000050 {LANMAN2_PROT, "\2LANMAN2.1"},
Steve French39798772006-05-31 22:40:51 +000051#endif /* weak password hashing for legacy clients */
Steve French50c2f752007-07-13 00:33:32 +000052 {CIFS_PROT, "\2NT LM 0.12"},
Steve French39798772006-05-31 22:40:51 +000053 {POSIX_PROT, "\2POSIX 2"},
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 {BAD_PROT, "\2"}
55};
56#else
57static struct {
58 int index;
59 char *name;
60} protocols[] = {
Steve French39798772006-05-31 22:40:51 +000061#ifdef CONFIG_CIFS_WEAK_PW_HASH
62 {LANMAN_PROT, "\2LM1.2X002"},
Steve French18f75ca2006-10-01 03:13:01 +000063 {LANMAN2_PROT, "\2LANMAN2.1"},
Steve French39798772006-05-31 22:40:51 +000064#endif /* weak password hashing for legacy clients */
Steve French790fe572007-07-07 19:25:05 +000065 {CIFS_PROT, "\2NT LM 0.12"},
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 {BAD_PROT, "\2"}
67};
68#endif
69
Steve French39798772006-05-31 22:40:51 +000070/* define the number of elements in the cifs dialect array */
71#ifdef CONFIG_CIFS_POSIX
72#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French9ac00b72006-09-30 04:13:17 +000073#define CIFS_NUM_PROT 4
Steve French39798772006-05-31 22:40:51 +000074#else
75#define CIFS_NUM_PROT 2
76#endif /* CIFS_WEAK_PW_HASH */
77#else /* not posix */
78#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French9ac00b72006-09-30 04:13:17 +000079#define CIFS_NUM_PROT 3
Steve French39798772006-05-31 22:40:51 +000080#else
81#define CIFS_NUM_PROT 1
82#endif /* CONFIG_CIFS_WEAK_PW_HASH */
83#endif /* CIFS_POSIX */
84
Linus Torvalds1da177e2005-04-16 15:20:36 -070085/* Mark as invalid, all open files on tree connections since they
86 were closed when session to server was lost */
Steve French790fe572007-07-07 19:25:05 +000087static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088{
89 struct cifsFileInfo *open_file = NULL;
Steve French790fe572007-07-07 19:25:05 +000090 struct list_head *tmp;
91 struct list_head *tmp1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
93/* list all files open on tree connection and mark them invalid */
Jeff Layton44772882010-10-15 15:34:03 -040094 spin_lock(&cifs_file_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
Steve French790fe572007-07-07 19:25:05 +000096 open_file = list_entry(tmp, struct cifsFileInfo, tlist);
Steve Frenchad8b15f2008-08-08 21:10:16 +000097 open_file->invalidHandle = true;
Jeff Layton3bc303c2009-09-21 06:47:50 -040098 open_file->oplock_break_cancelled = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 }
Jeff Layton44772882010-10-15 15:34:03 -0400100 spin_unlock(&cifs_file_list_lock);
Steve French09d1db52005-04-28 22:41:08 -0700101 /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
102 to this tcon */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103}
104
Jeff Layton9162ab22009-09-03 12:07:17 -0400105/* reconnect the socket, tcon, and smb session if needed */
106static int
107cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)
108{
109 int rc = 0;
110 struct cifsSesInfo *ses;
111 struct TCP_Server_Info *server;
112 struct nls_table *nls_codepage;
113
114 /*
115 * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
116 * tcp and smb session status done differently for those three - in the
117 * calling routine
118 */
119 if (!tcon)
120 return 0;
121
122 ses = tcon->ses;
123 server = ses->server;
124
125 /*
126 * only tree disconnect, open, and write, (and ulogoff which does not
127 * have tcon) are allowed as we start force umount
128 */
129 if (tcon->tidStatus == CifsExiting) {
130 if (smb_command != SMB_COM_WRITE_ANDX &&
131 smb_command != SMB_COM_OPEN_ANDX &&
132 smb_command != SMB_COM_TREE_DISCONNECT) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000133 cFYI(1, "can not send cmd %d while umounting",
134 smb_command);
Jeff Layton9162ab22009-09-03 12:07:17 -0400135 return -ENODEV;
136 }
137 }
138
139 if (ses->status == CifsExiting)
140 return -EIO;
141
142 /*
143 * Give demultiplex thread up to 10 seconds to reconnect, should be
144 * greater than cifs socket timeout which is 7 seconds
145 */
146 while (server->tcpStatus == CifsNeedReconnect) {
147 wait_event_interruptible_timeout(server->response_q,
148 (server->tcpStatus == CifsGood), 10 * HZ);
149
150 /* is TCP session is reestablished now ?*/
151 if (server->tcpStatus != CifsNeedReconnect)
152 break;
153
154 /*
155 * on "soft" mounts we wait once. Hard mounts keep
156 * retrying until process is killed or server comes
157 * back on-line
158 */
159 if (!tcon->retry || ses->status == CifsExiting) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000160 cFYI(1, "gave up waiting on reconnect in smb_init");
Jeff Layton9162ab22009-09-03 12:07:17 -0400161 return -EHOSTDOWN;
162 }
163 }
164
165 if (!ses->need_reconnect && !tcon->need_reconnect)
166 return 0;
167
168 nls_codepage = load_nls_default();
169
170 /*
171 * need to prevent multiple threads trying to simultaneously
172 * reconnect the same SMB session
173 */
Steve Frenchd7b619c2010-02-25 05:36:46 +0000174 mutex_lock(&ses->session_mutex);
Jeff Layton198b5682010-04-24 07:57:48 -0400175 rc = cifs_negotiate_protocol(0, ses);
176 if (rc == 0 && ses->need_reconnect)
Jeff Layton9162ab22009-09-03 12:07:17 -0400177 rc = cifs_setup_session(0, ses, nls_codepage);
178
179 /* do we need to reconnect tcon? */
180 if (rc || !tcon->need_reconnect) {
Steve Frenchd7b619c2010-02-25 05:36:46 +0000181 mutex_unlock(&ses->session_mutex);
Jeff Layton9162ab22009-09-03 12:07:17 -0400182 goto out;
183 }
184
185 mark_open_files_invalid(tcon);
186 rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage);
Steve Frenchd7b619c2010-02-25 05:36:46 +0000187 mutex_unlock(&ses->session_mutex);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000188 cFYI(1, "reconnect tcon rc = %d", rc);
Jeff Layton9162ab22009-09-03 12:07:17 -0400189
190 if (rc)
191 goto out;
192
193 /*
194 * FIXME: check if wsize needs updated due to negotiated smb buffer
195 * size shrinking
196 */
197 atomic_inc(&tconInfoReconnectCount);
198
199 /* tell server Unix caps we support */
200 if (ses->capabilities & CAP_UNIX)
201 reset_cifs_unix_caps(0, tcon, NULL, NULL);
202
203 /*
204 * Removed call to reopen open files here. It is safer (and faster) to
205 * reopen files one at a time as needed in read and write.
206 *
207 * FIXME: what about file locks? don't we need to reclaim them ASAP?
208 */
209
210out:
211 /*
212 * Check if handle based operation so we know whether we can continue
213 * or not without returning to caller to reset file handle
214 */
215 switch (smb_command) {
216 case SMB_COM_READ_ANDX:
217 case SMB_COM_WRITE_ANDX:
218 case SMB_COM_CLOSE:
219 case SMB_COM_FIND_CLOSE2:
220 case SMB_COM_LOCKING_ANDX:
221 rc = -EAGAIN;
222 }
223
224 unload_nls(nls_codepage);
225 return rc;
226}
227
Steve Frenchad7a2922008-02-07 23:25:02 +0000228/* Allocate and return pointer to an SMB request buffer, and set basic
229 SMB information in the SMB header. If the return code is zero, this
230 function must have filled in request_buf pointer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231static int
232small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000233 void **request_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234{
Jeff Laytonf5695992010-09-29 15:27:08 -0400235 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
Jeff Layton9162ab22009-09-03 12:07:17 -0400237 rc = cifs_reconnect_tcon(tcon, smb_command);
Steve French790fe572007-07-07 19:25:05 +0000238 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 return rc;
240
241 *request_buf = cifs_small_buf_get();
242 if (*request_buf == NULL) {
243 /* BB should we add a retry in here if not a writepage? */
244 return -ENOMEM;
245 }
246
Steve French63135e02007-07-17 17:34:02 +0000247 header_assemble((struct smb_hdr *) *request_buf, smb_command,
Steve Frenchc18c8422007-07-18 23:21:09 +0000248 tcon, wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Steve French790fe572007-07-07 19:25:05 +0000250 if (tcon != NULL)
251 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700252
Jeff Laytonf5695992010-09-29 15:27:08 -0400253 return 0;
Steve French5815449d2006-02-14 01:36:20 +0000254}
255
Steve French12b3b8f2006-02-09 21:12:47 +0000256int
Steve French50c2f752007-07-13 00:33:32 +0000257small_smb_init_no_tc(const int smb_command, const int wct,
Steve French5815449d2006-02-14 01:36:20 +0000258 struct cifsSesInfo *ses, void **request_buf)
Steve French12b3b8f2006-02-09 21:12:47 +0000259{
260 int rc;
Steve French50c2f752007-07-13 00:33:32 +0000261 struct smb_hdr *buffer;
Steve French12b3b8f2006-02-09 21:12:47 +0000262
Steve French5815449d2006-02-14 01:36:20 +0000263 rc = small_smb_init(smb_command, wct, NULL, request_buf);
Steve French790fe572007-07-07 19:25:05 +0000264 if (rc)
Steve French12b3b8f2006-02-09 21:12:47 +0000265 return rc;
266
Steve French04fdabe2006-02-10 05:52:50 +0000267 buffer = (struct smb_hdr *)*request_buf;
Steve French12b3b8f2006-02-09 21:12:47 +0000268 buffer->Mid = GetNextMid(ses->server);
269 if (ses->capabilities & CAP_UNICODE)
270 buffer->Flags2 |= SMBFLG2_UNICODE;
Steve French04fdabe2006-02-10 05:52:50 +0000271 if (ses->capabilities & CAP_STATUS32)
Steve French12b3b8f2006-02-09 21:12:47 +0000272 buffer->Flags2 |= SMBFLG2_ERR_STATUS;
273
274 /* uid, tid can stay at zero as set in header assemble */
275
Steve French50c2f752007-07-13 00:33:32 +0000276 /* BB add support for turning on the signing when
Steve French12b3b8f2006-02-09 21:12:47 +0000277 this function is used after 1st of session setup requests */
278
279 return rc;
280}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
282/* If the return code is zero, this function must fill in request_buf pointer */
283static int
Jeff Laytonf5695992010-09-29 15:27:08 -0400284__smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
285 void **request_buf, void **response_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 *request_buf = cifs_buf_get();
288 if (*request_buf == NULL) {
289 /* BB should we add a retry in here if not a writepage? */
290 return -ENOMEM;
291 }
292 /* Although the original thought was we needed the response buf for */
293 /* potential retries of smb operations it turns out we can determine */
294 /* from the mid flags when the request buffer can be resent without */
295 /* having to use a second distinct buffer for the response */
Steve French790fe572007-07-07 19:25:05 +0000296 if (response_buf)
Steve French50c2f752007-07-13 00:33:32 +0000297 *response_buf = *request_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299 header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000300 wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
Steve French790fe572007-07-07 19:25:05 +0000302 if (tcon != NULL)
303 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700304
Jeff Laytonf5695992010-09-29 15:27:08 -0400305 return 0;
306}
307
308/* If the return code is zero, this function must fill in request_buf pointer */
309static int
310smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
311 void **request_buf, void **response_buf)
312{
313 int rc;
314
315 rc = cifs_reconnect_tcon(tcon, smb_command);
316 if (rc)
317 return rc;
318
319 return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
320}
321
322static int
323smb_init_no_reconnect(int smb_command, int wct, struct cifsTconInfo *tcon,
324 void **request_buf, void **response_buf)
325{
326 if (tcon->ses->need_reconnect || tcon->need_reconnect)
327 return -EHOSTDOWN;
328
329 return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330}
331
Steve French50c2f752007-07-13 00:33:32 +0000332static int validate_t2(struct smb_t2_rsp *pSMB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
334 int rc = -EINVAL;
335 int total_size;
Steve French50c2f752007-07-13 00:33:32 +0000336 char *pBCC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338 /* check for plausible wct, bcc and t2 data and parm sizes */
339 /* check for parm and data offset going beyond end of smb */
Steve French790fe572007-07-07 19:25:05 +0000340 if (pSMB->hdr.WordCount >= 10) {
341 if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
343 /* check that bcc is at least as big as parms + data */
344 /* check that bcc is less than negotiated smb buffer */
345 total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
Steve French790fe572007-07-07 19:25:05 +0000346 if (total_size < 512) {
Steve Frenchc18c8422007-07-18 23:21:09 +0000347 total_size +=
Steve French63135e02007-07-17 17:34:02 +0000348 le16_to_cpu(pSMB->t2_rsp.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 /* BCC le converted in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +0000350 pBCC = (pSMB->hdr.WordCount * 2) +
Steve French09d1db52005-04-28 22:41:08 -0700351 sizeof(struct smb_hdr) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 (char *)pSMB;
Steve French790fe572007-07-07 19:25:05 +0000353 if ((total_size <= (*(u16 *)pBCC)) &&
Steve French50c2f752007-07-13 00:33:32 +0000354 (total_size <
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
356 return 0;
357 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 }
359 }
360 }
Steve French50c2f752007-07-13 00:33:32 +0000361 cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 sizeof(struct smb_t2_rsp) + 16);
363 return rc;
364}
365int
366CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
367{
368 NEGOTIATE_REQ *pSMB;
369 NEGOTIATE_RSP *pSMBr;
370 int rc = 0;
371 int bytes_returned;
Steve French39798772006-05-31 22:40:51 +0000372 int i;
Steve French50c2f752007-07-13 00:33:32 +0000373 struct TCP_Server_Info *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 u16 count;
Steve French750d1152006-06-27 06:28:30 +0000375 unsigned int secFlags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
Steve French790fe572007-07-07 19:25:05 +0000377 if (ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 server = ses->server;
379 else {
380 rc = -EIO;
381 return rc;
382 }
383 rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
384 (void **) &pSMB, (void **) &pSMBr);
385 if (rc)
386 return rc;
Steve French750d1152006-06-27 06:28:30 +0000387
388 /* if any of auth flags (ie not sign or seal) are overriden use them */
Steve French790fe572007-07-07 19:25:05 +0000389 if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
Steve French762e5ab2007-06-28 18:41:42 +0000390 secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */
Steve French750d1152006-06-27 06:28:30 +0000391 else /* if override flags set only sign/seal OR them with global auth */
Jeff Layton04912d62010-04-24 07:57:45 -0400392 secFlags = global_secflags | ses->overrideSecFlg;
Steve French750d1152006-06-27 06:28:30 +0000393
Joe Perchesb6b38f72010-04-21 03:50:45 +0000394 cFYI(1, "secFlags 0x%x", secFlags);
Steve Frenchf40c5622006-06-28 00:13:38 +0000395
Steve French1982c342005-08-17 12:38:22 -0700396 pSMB->hdr.Mid = GetNextMid(server);
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000397 pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
Steve Frencha0136892007-10-04 20:05:09 +0000398
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000399 if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
Steve French254e55e2006-06-04 05:53:15 +0000400 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Steve Frencha0136892007-10-04 20:05:09 +0000401 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000402 cFYI(1, "Kerberos only mechanism, enable extended security");
Steve Frencha0136892007-10-04 20:05:09 +0000403 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Jeff Laytonb4d6fcf2011-01-07 11:30:28 -0500404 } else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
Steve Frenchac683922009-05-06 04:16:04 +0000405 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
406 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000407 cFYI(1, "NTLMSSP only mechanism, enable extended security");
Steve Frenchac683922009-05-06 04:16:04 +0000408 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
409 }
Steve French50c2f752007-07-13 00:33:32 +0000410
Steve French39798772006-05-31 22:40:51 +0000411 count = 0;
Steve French50c2f752007-07-13 00:33:32 +0000412 for (i = 0; i < CIFS_NUM_PROT; i++) {
Steve French39798772006-05-31 22:40:51 +0000413 strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
414 count += strlen(protocols[i].name) + 1;
415 /* null at end of source and target buffers anyway */
416 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 pSMB->hdr.smb_buf_length += count;
418 pSMB->ByteCount = cpu_to_le16(count);
419
420 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
421 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French50c2f752007-07-13 00:33:32 +0000422 if (rc != 0)
Steve French254e55e2006-06-04 05:53:15 +0000423 goto neg_err_exit;
424
Jeff Layton9bf67e52010-04-24 07:57:46 -0400425 server->dialect = le16_to_cpu(pSMBr->DialectIndex);
426 cFYI(1, "Dialect: %d", server->dialect);
Steve French254e55e2006-06-04 05:53:15 +0000427 /* Check wct = 1 error case */
Jeff Layton9bf67e52010-04-24 07:57:46 -0400428 if ((pSMBr->hdr.WordCount < 13) || (server->dialect == BAD_PROT)) {
Steve French254e55e2006-06-04 05:53:15 +0000429 /* core returns wct = 1, but we do not ask for core - otherwise
Steve French50c2f752007-07-13 00:33:32 +0000430 small wct just comes when dialect index is -1 indicating we
Steve French254e55e2006-06-04 05:53:15 +0000431 could not negotiate a common dialect */
432 rc = -EOPNOTSUPP;
433 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000434#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French790fe572007-07-07 19:25:05 +0000435 } else if ((pSMBr->hdr.WordCount == 13)
Jeff Layton9bf67e52010-04-24 07:57:46 -0400436 && ((server->dialect == LANMAN_PROT)
437 || (server->dialect == LANMAN2_PROT))) {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000438 __s16 tmp;
Steve French50c2f752007-07-13 00:33:32 +0000439 struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
Steve French254e55e2006-06-04 05:53:15 +0000440
Steve French790fe572007-07-07 19:25:05 +0000441 if ((secFlags & CIFSSEC_MAY_LANMAN) ||
Steve French750d1152006-06-27 06:28:30 +0000442 (secFlags & CIFSSEC_MAY_PLNTXT))
Steve French254e55e2006-06-04 05:53:15 +0000443 server->secType = LANMAN;
444 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000445 cERROR(1, "mount failed weak security disabled"
446 " in /proc/fs/cifs/SecurityFlags");
Steve French39798772006-05-31 22:40:51 +0000447 rc = -EOPNOTSUPP;
448 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000449 }
Steve French254e55e2006-06-04 05:53:15 +0000450 server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
451 server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
452 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
Steve French39798772006-05-31 22:40:51 +0000453 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000454 server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
Steve French254e55e2006-06-04 05:53:15 +0000455 GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
456 /* even though we do not use raw we might as well set this
457 accurately, in case we ever find a need for it */
Steve French790fe572007-07-07 19:25:05 +0000458 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
Steve Frencheca6acf2009-02-20 05:43:09 +0000459 server->max_rw = 0xFF00;
Steve French254e55e2006-06-04 05:53:15 +0000460 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
461 } else {
Steve Frencheca6acf2009-02-20 05:43:09 +0000462 server->max_rw = 0;/* do not need to use raw anyway */
Steve French254e55e2006-06-04 05:53:15 +0000463 server->capabilities = CAP_MPX_MODE;
464 }
Steve Frenchb815f1e52006-10-02 05:53:29 +0000465 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
Steve French1a70d652006-10-02 05:59:18 +0000466 if (tmp == -1) {
Steve French25ee4a92006-09-30 00:54:23 +0000467 /* OS/2 often does not set timezone therefore
468 * we must use server time to calc time zone.
Steve Frenchb815f1e52006-10-02 05:53:29 +0000469 * Could deviate slightly from the right zone.
470 * Smallest defined timezone difference is 15 minutes
471 * (i.e. Nepal). Rounding up/down is done to match
472 * this requirement.
Steve French25ee4a92006-09-30 00:54:23 +0000473 */
Steve Frenchb815f1e52006-10-02 05:53:29 +0000474 int val, seconds, remain, result;
Steve French25ee4a92006-09-30 00:54:23 +0000475 struct timespec ts, utc;
476 utc = CURRENT_TIME;
Jeff Laytonc4a2c082009-05-27 09:37:33 -0400477 ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
478 rsp->SrvTime.Time, 0);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000479 cFYI(1, "SrvTime %d sec since 1970 (utc: %d) diff: %d",
Steve French50c2f752007-07-13 00:33:32 +0000480 (int)ts.tv_sec, (int)utc.tv_sec,
Joe Perchesb6b38f72010-04-21 03:50:45 +0000481 (int)(utc.tv_sec - ts.tv_sec));
Steve Frenchb815f1e52006-10-02 05:53:29 +0000482 val = (int)(utc.tv_sec - ts.tv_sec);
Andre Haupt8594c152007-08-30 20:18:41 +0000483 seconds = abs(val);
Steve French947a5062006-10-02 05:55:25 +0000484 result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000485 remain = seconds % MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000486 if (remain >= (MIN_TZ_ADJ / 2))
Steve Frenchb815f1e52006-10-02 05:53:29 +0000487 result += MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000488 if (val < 0)
Steve Frenchad7a2922008-02-07 23:25:02 +0000489 result = -result;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000490 server->timeAdj = result;
Steve French25ee4a92006-09-30 00:54:23 +0000491 } else {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000492 server->timeAdj = (int)tmp;
493 server->timeAdj *= 60; /* also in seconds */
Steve French25ee4a92006-09-30 00:54:23 +0000494 }
Joe Perchesb6b38f72010-04-21 03:50:45 +0000495 cFYI(1, "server->timeAdj: %d seconds", server->timeAdj);
Steve French25ee4a92006-09-30 00:54:23 +0000496
Steve French39798772006-05-31 22:40:51 +0000497
Steve French254e55e2006-06-04 05:53:15 +0000498 /* BB get server time for time conversions and add
Steve French50c2f752007-07-13 00:33:32 +0000499 code to use it and timezone since this is not UTC */
Steve French39798772006-05-31 22:40:51 +0000500
Steve French50c2f752007-07-13 00:33:32 +0000501 if (rsp->EncryptionKeyLength ==
Steve French25ee4a92006-09-30 00:54:23 +0000502 cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
Shirish Pargaonkard3ba50b2010-10-27 15:20:36 -0500503 memcpy(ses->server->cryptkey, rsp->EncryptionKey,
Steve French254e55e2006-06-04 05:53:15 +0000504 CIFS_CRYPTO_KEY_SIZE);
505 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
506 rc = -EIO; /* need cryptkey unless plain text */
507 goto neg_err_exit;
508 }
Steve French39798772006-05-31 22:40:51 +0000509
Steve Frenchf19159d2010-04-21 04:12:10 +0000510 cFYI(1, "LANMAN negotiated");
Steve French254e55e2006-06-04 05:53:15 +0000511 /* we will not end up setting signing flags - as no signing
512 was in LANMAN and server did not return the flags on */
513 goto signing_check;
Steve French7c7b25b2006-06-01 19:20:10 +0000514#else /* weak security disabled */
Steve French790fe572007-07-07 19:25:05 +0000515 } else if (pSMBr->hdr.WordCount == 13) {
Steve Frenchf19159d2010-04-21 04:12:10 +0000516 cERROR(1, "mount failed, cifs module not built "
517 "with CIFS_WEAK_PW_HASH support");
Dan Carpenter8212cf72010-03-15 11:22:26 +0300518 rc = -EOPNOTSUPP;
Steve French7c7b25b2006-06-01 19:20:10 +0000519#endif /* WEAK_PW_HASH */
Steve French254e55e2006-06-04 05:53:15 +0000520 goto neg_err_exit;
Steve French790fe572007-07-07 19:25:05 +0000521 } else if (pSMBr->hdr.WordCount != 17) {
Steve French254e55e2006-06-04 05:53:15 +0000522 /* unknown wct */
523 rc = -EOPNOTSUPP;
524 goto neg_err_exit;
525 }
526 /* else wct == 17 NTLM */
527 server->secMode = pSMBr->SecurityMode;
Steve French790fe572007-07-07 19:25:05 +0000528 if ((server->secMode & SECMODE_USER) == 0)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000529 cFYI(1, "share mode security");
Steve French39798772006-05-31 22:40:51 +0000530
Steve French790fe572007-07-07 19:25:05 +0000531 if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000532#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French750d1152006-06-27 06:28:30 +0000533 if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000534#endif /* CIFS_WEAK_PW_HASH */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000535 cERROR(1, "Server requests plain text password"
536 " but client support disabled");
Steve French9312f672006-06-04 22:21:07 +0000537
Steve French790fe572007-07-07 19:25:05 +0000538 if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
Steve French254e55e2006-06-04 05:53:15 +0000539 server->secType = NTLMv2;
Steve French790fe572007-07-07 19:25:05 +0000540 else if (secFlags & CIFSSEC_MAY_NTLM)
Steve French254e55e2006-06-04 05:53:15 +0000541 server->secType = NTLM;
Steve French790fe572007-07-07 19:25:05 +0000542 else if (secFlags & CIFSSEC_MAY_NTLMV2)
Steve Frenchf40c5622006-06-28 00:13:38 +0000543 server->secType = NTLMv2;
Steve Frencha0136892007-10-04 20:05:09 +0000544 else if (secFlags & CIFSSEC_MAY_KRB5)
545 server->secType = Kerberos;
Steve Frenchac683922009-05-06 04:16:04 +0000546 else if (secFlags & CIFSSEC_MAY_NTLMSSP)
Steve Frenchf46c7232009-06-25 03:04:20 +0000547 server->secType = RawNTLMSSP;
Steve Frencha0136892007-10-04 20:05:09 +0000548 else if (secFlags & CIFSSEC_MAY_LANMAN)
549 server->secType = LANMAN;
550/* #ifdef CONFIG_CIFS_EXPERIMENTAL
551 else if (secFlags & CIFSSEC_MAY_PLNTXT)
552 server->secType = ??
553#endif */
554 else {
555 rc = -EOPNOTSUPP;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000556 cERROR(1, "Invalid security type");
Steve Frencha0136892007-10-04 20:05:09 +0000557 goto neg_err_exit;
558 }
559 /* else ... any others ...? */
Steve French7c7b25b2006-06-01 19:20:10 +0000560
Steve French254e55e2006-06-04 05:53:15 +0000561 /* one byte, so no need to convert this or EncryptionKeyLen from
562 little endian */
563 server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
564 /* probably no need to store and check maxvcs */
565 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000567 server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000568 cFYI(DBG2, "Max buf = %d", ses->server->maxBuf);
Steve French254e55e2006-06-04 05:53:15 +0000569 GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
570 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
Steve Frenchb815f1e52006-10-02 05:53:29 +0000571 server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
572 server->timeAdj *= 60;
Steve French254e55e2006-06-04 05:53:15 +0000573 if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
Shirish Pargaonkard3ba50b2010-10-27 15:20:36 -0500574 memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey,
Steve French254e55e2006-06-04 05:53:15 +0000575 CIFS_CRYPTO_KEY_SIZE);
576 } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
577 && (pSMBr->EncryptionKeyLength == 0)) {
578 /* decode security blob */
579 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
580 rc = -EIO; /* no crypt key only if plain text pwd */
581 goto neg_err_exit;
582 }
583
584 /* BB might be helpful to save off the domain of server here */
585
Steve French50c2f752007-07-13 00:33:32 +0000586 if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
Steve French254e55e2006-06-04 05:53:15 +0000587 (server->capabilities & CAP_EXTENDED_SECURITY)) {
588 count = pSMBr->ByteCount;
Jeff Laytone187e442007-10-16 17:10:44 +0000589 if (count < 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 rc = -EIO;
Jeff Laytone187e442007-10-16 17:10:44 +0000591 goto neg_err_exit;
592 }
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530593 spin_lock(&cifs_tcp_ses_lock);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500594 if (server->srv_count > 1) {
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530595 spin_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000596 if (memcmp(server->server_GUID,
597 pSMBr->u.extended_response.
598 GUID, 16) != 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000599 cFYI(1, "server UID changed");
Steve French254e55e2006-06-04 05:53:15 +0000600 memcpy(server->server_GUID,
Jeff Laytone187e442007-10-16 17:10:44 +0000601 pSMBr->u.extended_response.GUID,
602 16);
603 }
Jeff Laytone7ddee92008-11-14 13:44:38 -0500604 } else {
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530605 spin_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000606 memcpy(server->server_GUID,
607 pSMBr->u.extended_response.GUID, 16);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500608 }
Jeff Laytone187e442007-10-16 17:10:44 +0000609
610 if (count == 16) {
611 server->secType = RawNTLMSSP;
Steve French254e55e2006-06-04 05:53:15 +0000612 } else {
613 rc = decode_negTokenInit(pSMBr->u.extended_response.
Jeff Layton26efa0b2010-04-24 07:57:49 -0400614 SecurityBlob, count - 16,
615 server);
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000616 if (rc == 1)
Jeff Laytone5459372007-11-03 05:11:06 +0000617 rc = 0;
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000618 else
Steve French254e55e2006-06-04 05:53:15 +0000619 rc = -EINVAL;
Shirish Pargaonkar2b149f12010-09-18 22:02:18 -0500620 if (server->secType == Kerberos) {
621 if (!server->sec_kerberos &&
622 !server->sec_mskerberos)
623 rc = -EOPNOTSUPP;
624 } else if (server->secType == RawNTLMSSP) {
625 if (!server->sec_ntlmssp)
626 rc = -EOPNOTSUPP;
627 } else
628 rc = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 }
Steve French254e55e2006-06-04 05:53:15 +0000630 } else
631 server->capabilities &= ~CAP_EXTENDED_SECURITY;
632
Steve French6344a422006-06-12 04:18:35 +0000633#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French254e55e2006-06-04 05:53:15 +0000634signing_check:
Steve French6344a422006-06-12 04:18:35 +0000635#endif
Steve French762e5ab2007-06-28 18:41:42 +0000636 if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
637 /* MUST_SIGN already includes the MAY_SIGN FLAG
638 so if this is zero it means that signing is disabled */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000639 cFYI(1, "Signing disabled");
Steve Frenchabb63d62007-10-18 02:58:40 +0000640 if (server->secMode & SECMODE_SIGN_REQUIRED) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000641 cERROR(1, "Server requires "
Jeff Layton7111d212007-10-16 16:50:25 +0000642 "packet signing to be enabled in "
Joe Perchesb6b38f72010-04-21 03:50:45 +0000643 "/proc/fs/cifs/SecurityFlags.");
Steve Frenchabb63d62007-10-18 02:58:40 +0000644 rc = -EOPNOTSUPP;
645 }
Steve French50c2f752007-07-13 00:33:32 +0000646 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000647 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Steve French762e5ab2007-06-28 18:41:42 +0000648 } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
649 /* signing required */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000650 cFYI(1, "Must sign - secFlags 0x%x", secFlags);
Steve French762e5ab2007-06-28 18:41:42 +0000651 if ((server->secMode &
652 (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000653 cERROR(1, "signing required but server lacks support");
Jeff38c10a12007-07-06 21:10:07 +0000654 rc = -EOPNOTSUPP;
Steve French762e5ab2007-06-28 18:41:42 +0000655 } else
656 server->secMode |= SECMODE_SIGN_REQUIRED;
657 } else {
658 /* signing optional ie CIFSSEC_MAY_SIGN */
Steve French790fe572007-07-07 19:25:05 +0000659 if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
Steve French50c2f752007-07-13 00:33:32 +0000660 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000661 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 }
Steve French50c2f752007-07-13 00:33:32 +0000663
664neg_err_exit:
Steve French4a6d87f2005-08-13 08:15:54 -0700665 cifs_buf_release(pSMB);
Steve French254e55e2006-06-04 05:53:15 +0000666
Joe Perchesb6b38f72010-04-21 03:50:45 +0000667 cFYI(1, "negprot rc %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 return rc;
669}
670
671int
672CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
673{
674 struct smb_hdr *smb_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
Joe Perchesb6b38f72010-04-21 03:50:45 +0000677 cFYI(1, "In tree disconnect");
Jeff Laytonf1987b42008-11-15 11:12:47 -0500678
679 /* BB: do we need to check this? These should never be NULL. */
680 if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
681 return -EIO;
682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 /*
Jeff Laytonf1987b42008-11-15 11:12:47 -0500684 * No need to return error on this operation if tid invalidated and
685 * closed on server already e.g. due to tcp session crashing. Also,
686 * the tcon is no longer on the list, so no need to take lock before
687 * checking this.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 */
Steve French268875b2009-06-25 00:29:21 +0000689 if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
Steve French50c2f752007-07-13 00:33:32 +0000690 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
Steve French50c2f752007-07-13 00:33:32 +0000692 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
Steve French09d1db52005-04-28 22:41:08 -0700693 (void **)&smb_buffer);
Jeff Laytonf1987b42008-11-15 11:12:47 -0500694 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 return rc;
Steve French133672e2007-11-13 22:41:37 +0000696
697 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000699 cFYI(1, "Tree disconnect failed %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700
Steve French50c2f752007-07-13 00:33:32 +0000701 /* No need to return error on this operation if tid invalidated and
Jeff Laytonf1987b42008-11-15 11:12:47 -0500702 closed on server already e.g. due to tcp session crashing */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 if (rc == -EAGAIN)
704 rc = 0;
705
706 return rc;
707}
708
709int
710CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
711{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 LOGOFF_ANDX_REQ *pSMB;
713 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
Joe Perchesb6b38f72010-04-21 03:50:45 +0000715 cFYI(1, "In SMBLogoff for session disconnect");
Jeff Layton14fbf502008-11-14 13:53:46 -0500716
717 /*
718 * BB: do we need to check validity of ses and server? They should
719 * always be valid since we have an active reference. If not, that
720 * should probably be a BUG()
721 */
722 if (!ses || !ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 return -EIO;
724
Steve Frenchd7b619c2010-02-25 05:36:46 +0000725 mutex_lock(&ses->session_mutex);
Steve French3b795212008-11-13 19:45:32 +0000726 if (ses->need_reconnect)
727 goto session_already_dead; /* no need to send SMBlogoff if uid
728 already closed due to reconnect */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
730 if (rc) {
Steve Frenchd7b619c2010-02-25 05:36:46 +0000731 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 return rc;
733 }
734
Steve French3b795212008-11-13 19:45:32 +0000735 pSMB->hdr.Mid = GetNextMid(ses->server);
Steve French1982c342005-08-17 12:38:22 -0700736
Steve French3b795212008-11-13 19:45:32 +0000737 if (ses->server->secMode &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
739 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
741 pSMB->hdr.Uid = ses->Suid;
742
743 pSMB->AndXCommand = 0xFF;
Steve French133672e2007-11-13 22:41:37 +0000744 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
Steve French3b795212008-11-13 19:45:32 +0000745session_already_dead:
Steve Frenchd7b619c2010-02-25 05:36:46 +0000746 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
748 /* if session dead then we do not need to do ulogoff,
Steve French50c2f752007-07-13 00:33:32 +0000749 since server closed smb session, no sense reporting
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 error */
751 if (rc == -EAGAIN)
752 rc = 0;
753 return rc;
754}
755
756int
Steve French2d785a52007-07-15 01:48:57 +0000757CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
758 __u16 type, const struct nls_table *nls_codepage, int remap)
759{
760 TRANSACTION2_SPI_REQ *pSMB = NULL;
761 TRANSACTION2_SPI_RSP *pSMBr = NULL;
762 struct unlink_psx_rq *pRqD;
763 int name_len;
764 int rc = 0;
765 int bytes_returned = 0;
766 __u16 params, param_offset, offset, byte_count;
767
Joe Perchesb6b38f72010-04-21 03:50:45 +0000768 cFYI(1, "In POSIX delete");
Steve French2d785a52007-07-15 01:48:57 +0000769PsxDelete:
770 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
771 (void **) &pSMBr);
772 if (rc)
773 return rc;
774
775 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
776 name_len =
777 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
778 PATH_MAX, nls_codepage, remap);
779 name_len++; /* trailing null */
780 name_len *= 2;
781 } else { /* BB add path length overrun check */
782 name_len = strnlen(fileName, PATH_MAX);
783 name_len++; /* trailing null */
784 strncpy(pSMB->FileName, fileName, name_len);
785 }
786
787 params = 6 + name_len;
788 pSMB->MaxParameterCount = cpu_to_le16(2);
789 pSMB->MaxDataCount = 0; /* BB double check this with jra */
790 pSMB->MaxSetupCount = 0;
791 pSMB->Reserved = 0;
792 pSMB->Flags = 0;
793 pSMB->Timeout = 0;
794 pSMB->Reserved2 = 0;
795 param_offset = offsetof(struct smb_com_transaction2_spi_req,
796 InformationLevel) - 4;
797 offset = param_offset + params;
798
799 /* Setup pointer to Request Data (inode type) */
800 pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
801 pRqD->type = cpu_to_le16(type);
802 pSMB->ParameterOffset = cpu_to_le16(param_offset);
803 pSMB->DataOffset = cpu_to_le16(offset);
804 pSMB->SetupCount = 1;
805 pSMB->Reserved3 = 0;
806 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
807 byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
808
809 pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
810 pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
811 pSMB->ParameterCount = cpu_to_le16(params);
812 pSMB->TotalParameterCount = pSMB->ParameterCount;
813 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
814 pSMB->Reserved4 = 0;
815 pSMB->hdr.smb_buf_length += byte_count;
816 pSMB->ByteCount = cpu_to_le16(byte_count);
817 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
818 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +0000819 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000820 cFYI(1, "Posix delete returned %d", rc);
Steve French2d785a52007-07-15 01:48:57 +0000821 cifs_buf_release(pSMB);
822
823 cifs_stats_inc(&tcon->num_deletes);
824
825 if (rc == -EAGAIN)
826 goto PsxDelete;
827
828 return rc;
829}
830
831int
Steve French737b7582005-04-28 22:41:06 -0700832CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
833 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834{
835 DELETE_FILE_REQ *pSMB = NULL;
836 DELETE_FILE_RSP *pSMBr = NULL;
837 int rc = 0;
838 int bytes_returned;
839 int name_len;
840
841DelFileRetry:
842 rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
843 (void **) &pSMBr);
844 if (rc)
845 return rc;
846
847 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
848 name_len =
Steve French50c2f752007-07-13 00:33:32 +0000849 cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
Steve French737b7582005-04-28 22:41:06 -0700850 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 name_len++; /* trailing null */
852 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700853 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 name_len = strnlen(fileName, PATH_MAX);
855 name_len++; /* trailing null */
856 strncpy(pSMB->fileName, fileName, name_len);
857 }
858 pSMB->SearchAttributes =
859 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
860 pSMB->BufferFormat = 0x04;
861 pSMB->hdr.smb_buf_length += name_len + 1;
862 pSMB->ByteCount = cpu_to_le16(name_len + 1);
863 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
864 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700865 cifs_stats_inc(&tcon->num_deletes);
Steve Frenchad7a2922008-02-07 23:25:02 +0000866 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000867 cFYI(1, "Error in RMFile = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
869 cifs_buf_release(pSMB);
870 if (rc == -EAGAIN)
871 goto DelFileRetry;
872
873 return rc;
874}
875
876int
Steve French50c2f752007-07-13 00:33:32 +0000877CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
Steve French737b7582005-04-28 22:41:06 -0700878 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879{
880 DELETE_DIRECTORY_REQ *pSMB = NULL;
881 DELETE_DIRECTORY_RSP *pSMBr = NULL;
882 int rc = 0;
883 int bytes_returned;
884 int name_len;
885
Joe Perchesb6b38f72010-04-21 03:50:45 +0000886 cFYI(1, "In CIFSSMBRmDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887RmDirRetry:
888 rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
889 (void **) &pSMBr);
890 if (rc)
891 return rc;
892
893 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French737b7582005-04-28 22:41:06 -0700894 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
895 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 name_len++; /* trailing null */
897 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700898 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 name_len = strnlen(dirName, PATH_MAX);
900 name_len++; /* trailing null */
901 strncpy(pSMB->DirName, dirName, name_len);
902 }
903
904 pSMB->BufferFormat = 0x04;
905 pSMB->hdr.smb_buf_length += name_len + 1;
906 pSMB->ByteCount = cpu_to_le16(name_len + 1);
907 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
908 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700909 cifs_stats_inc(&tcon->num_rmdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000910 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000911 cFYI(1, "Error in RMDir = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912
913 cifs_buf_release(pSMB);
914 if (rc == -EAGAIN)
915 goto RmDirRetry;
916 return rc;
917}
918
919int
920CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -0700921 const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922{
923 int rc = 0;
924 CREATE_DIRECTORY_REQ *pSMB = NULL;
925 CREATE_DIRECTORY_RSP *pSMBr = NULL;
926 int bytes_returned;
927 int name_len;
928
Joe Perchesb6b38f72010-04-21 03:50:45 +0000929 cFYI(1, "In CIFSSMBMkDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930MkDirRetry:
931 rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
932 (void **) &pSMBr);
933 if (rc)
934 return rc;
935
936 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +0000937 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
Steve French737b7582005-04-28 22:41:06 -0700938 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 name_len++; /* trailing null */
940 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700941 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 name_len = strnlen(name, PATH_MAX);
943 name_len++; /* trailing null */
944 strncpy(pSMB->DirName, name, name_len);
945 }
946
947 pSMB->BufferFormat = 0x04;
948 pSMB->hdr.smb_buf_length += name_len + 1;
949 pSMB->ByteCount = cpu_to_le16(name_len + 1);
950 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
951 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700952 cifs_stats_inc(&tcon->num_mkdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000953 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000954 cFYI(1, "Error in Mkdir = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -0700955
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 cifs_buf_release(pSMB);
957 if (rc == -EAGAIN)
958 goto MkDirRetry;
959 return rc;
960}
961
Steve French2dd29d32007-04-23 22:07:35 +0000962int
963CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
Steve Frenchad7a2922008-02-07 23:25:02 +0000964 __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
Steve French50c2f752007-07-13 00:33:32 +0000965 __u32 *pOplock, const char *name,
Steve French2dd29d32007-04-23 22:07:35 +0000966 const struct nls_table *nls_codepage, int remap)
967{
968 TRANSACTION2_SPI_REQ *pSMB = NULL;
969 TRANSACTION2_SPI_RSP *pSMBr = NULL;
970 int name_len;
971 int rc = 0;
972 int bytes_returned = 0;
Steve French2dd29d32007-04-23 22:07:35 +0000973 __u16 params, param_offset, offset, byte_count, count;
Steve Frenchad7a2922008-02-07 23:25:02 +0000974 OPEN_PSX_REQ *pdata;
975 OPEN_PSX_RSP *psx_rsp;
Steve French2dd29d32007-04-23 22:07:35 +0000976
Joe Perchesb6b38f72010-04-21 03:50:45 +0000977 cFYI(1, "In POSIX Create");
Steve French2dd29d32007-04-23 22:07:35 +0000978PsxCreat:
979 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
980 (void **) &pSMBr);
981 if (rc)
982 return rc;
983
984 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
985 name_len =
986 cifsConvertToUCS((__le16 *) pSMB->FileName, name,
987 PATH_MAX, nls_codepage, remap);
988 name_len++; /* trailing null */
989 name_len *= 2;
990 } else { /* BB improve the check for buffer overruns BB */
991 name_len = strnlen(name, PATH_MAX);
992 name_len++; /* trailing null */
993 strncpy(pSMB->FileName, name, name_len);
994 }
995
996 params = 6 + name_len;
997 count = sizeof(OPEN_PSX_REQ);
998 pSMB->MaxParameterCount = cpu_to_le16(2);
999 pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
1000 pSMB->MaxSetupCount = 0;
1001 pSMB->Reserved = 0;
1002 pSMB->Flags = 0;
1003 pSMB->Timeout = 0;
1004 pSMB->Reserved2 = 0;
1005 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00001006 InformationLevel) - 4;
Steve French2dd29d32007-04-23 22:07:35 +00001007 offset = param_offset + params;
Steve French2dd29d32007-04-23 22:07:35 +00001008 pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001009 pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
Steve French2dd29d32007-04-23 22:07:35 +00001010 pdata->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00001011 pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
Steve French2dd29d32007-04-23 22:07:35 +00001012 pdata->OpenFlags = cpu_to_le32(*pOplock);
1013 pSMB->ParameterOffset = cpu_to_le16(param_offset);
1014 pSMB->DataOffset = cpu_to_le16(offset);
1015 pSMB->SetupCount = 1;
1016 pSMB->Reserved3 = 0;
1017 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
1018 byte_count = 3 /* pad */ + params + count;
1019
1020 pSMB->DataCount = cpu_to_le16(count);
1021 pSMB->ParameterCount = cpu_to_le16(params);
1022 pSMB->TotalDataCount = pSMB->DataCount;
1023 pSMB->TotalParameterCount = pSMB->ParameterCount;
1024 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1025 pSMB->Reserved4 = 0;
Steve French50c2f752007-07-13 00:33:32 +00001026 pSMB->hdr.smb_buf_length += byte_count;
Steve French2dd29d32007-04-23 22:07:35 +00001027 pSMB->ByteCount = cpu_to_le16(byte_count);
1028 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1029 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1030 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001031 cFYI(1, "Posix create returned %d", rc);
Steve French2dd29d32007-04-23 22:07:35 +00001032 goto psx_create_err;
1033 }
1034
Joe Perchesb6b38f72010-04-21 03:50:45 +00001035 cFYI(1, "copying inode info");
Steve French2dd29d32007-04-23 22:07:35 +00001036 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1037
1038 if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
1039 rc = -EIO; /* bad smb */
1040 goto psx_create_err;
1041 }
1042
1043 /* copy return information to pRetData */
Steve French50c2f752007-07-13 00:33:32 +00001044 psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
Steve French2dd29d32007-04-23 22:07:35 +00001045 + le16_to_cpu(pSMBr->t2.DataOffset));
Steve French50c2f752007-07-13 00:33:32 +00001046
Steve French2dd29d32007-04-23 22:07:35 +00001047 *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
Steve French790fe572007-07-07 19:25:05 +00001048 if (netfid)
Steve French2dd29d32007-04-23 22:07:35 +00001049 *netfid = psx_rsp->Fid; /* cifs fid stays in le */
1050 /* Let caller know file was created so we can set the mode. */
1051 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001052 if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
Steve French2dd29d32007-04-23 22:07:35 +00001053 *pOplock |= CIFS_CREATE_ACTION;
1054 /* check to make sure response data is there */
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001055 if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1056 pRetData->Type = cpu_to_le32(-1); /* unknown */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001057 cFYI(DBG2, "unknown type");
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001058 } else {
Steve French790fe572007-07-07 19:25:05 +00001059 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001060 + sizeof(FILE_UNIX_BASIC_INFO)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001061 cERROR(1, "Open response data too small");
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001062 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001063 goto psx_create_err;
1064 }
Steve French50c2f752007-07-13 00:33:32 +00001065 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001066 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001067 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001068 }
Steve French2dd29d32007-04-23 22:07:35 +00001069
1070psx_create_err:
1071 cifs_buf_release(pSMB);
1072
Steve French65bc98b2009-07-10 15:27:25 +00001073 if (posix_flags & SMB_O_DIRECTORY)
1074 cifs_stats_inc(&tcon->num_posixmkdirs);
1075 else
1076 cifs_stats_inc(&tcon->num_posixopens);
Steve French2dd29d32007-04-23 22:07:35 +00001077
1078 if (rc == -EAGAIN)
1079 goto PsxCreat;
1080
Steve French50c2f752007-07-13 00:33:32 +00001081 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001082}
1083
Steve Frencha9d02ad2005-08-24 23:06:05 -07001084static __u16 convert_disposition(int disposition)
1085{
1086 __u16 ofun = 0;
1087
1088 switch (disposition) {
1089 case FILE_SUPERSEDE:
1090 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1091 break;
1092 case FILE_OPEN:
1093 ofun = SMBOPEN_OAPPEND;
1094 break;
1095 case FILE_CREATE:
1096 ofun = SMBOPEN_OCREATE;
1097 break;
1098 case FILE_OPEN_IF:
1099 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1100 break;
1101 case FILE_OVERWRITE:
1102 ofun = SMBOPEN_OTRUNC;
1103 break;
1104 case FILE_OVERWRITE_IF:
1105 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1106 break;
1107 default:
Joe Perchesb6b38f72010-04-21 03:50:45 +00001108 cFYI(1, "unknown disposition %d", disposition);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001109 ofun = SMBOPEN_OAPPEND; /* regular open */
1110 }
1111 return ofun;
1112}
1113
Jeff Layton35fc37d2008-05-14 10:22:03 -07001114static int
1115access_flags_to_smbopen_mode(const int access_flags)
1116{
1117 int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
1118
1119 if (masked_flags == GENERIC_READ)
1120 return SMBOPEN_READ;
1121 else if (masked_flags == GENERIC_WRITE)
1122 return SMBOPEN_WRITE;
1123
1124 /* just go for read/write */
1125 return SMBOPEN_READWRITE;
1126}
1127
Steve Frencha9d02ad2005-08-24 23:06:05 -07001128int
1129SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1130 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001131 const int access_flags, const int create_options, __u16 *netfid,
1132 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001133 const struct nls_table *nls_codepage, int remap)
1134{
1135 int rc = -EACCES;
1136 OPENX_REQ *pSMB = NULL;
1137 OPENX_RSP *pSMBr = NULL;
1138 int bytes_returned;
1139 int name_len;
1140 __u16 count;
1141
1142OldOpenRetry:
1143 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1144 (void **) &pSMBr);
1145 if (rc)
1146 return rc;
1147
1148 pSMB->AndXCommand = 0xFF; /* none */
1149
1150 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1151 count = 1; /* account for one byte pad to word boundary */
1152 name_len =
1153 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1154 fileName, PATH_MAX, nls_codepage, remap);
1155 name_len++; /* trailing null */
1156 name_len *= 2;
1157 } else { /* BB improve check for buffer overruns BB */
1158 count = 0; /* no pad */
1159 name_len = strnlen(fileName, PATH_MAX);
1160 name_len++; /* trailing null */
1161 strncpy(pSMB->fileName, fileName, name_len);
1162 }
1163 if (*pOplock & REQ_OPLOCK)
1164 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001165 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001166 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001167
Steve Frencha9d02ad2005-08-24 23:06:05 -07001168 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
Jeff Layton35fc37d2008-05-14 10:22:03 -07001169 pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001170 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1171 /* set file as system file if special file such
1172 as fifo and server expecting SFU style and
1173 no Unix extensions */
1174
Steve French790fe572007-07-07 19:25:05 +00001175 if (create_options & CREATE_OPTION_SPECIAL)
1176 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001177 else /* BB FIXME BB */
1178 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001179
Jeff Layton67750fb2008-05-09 22:28:02 +00001180 if (create_options & CREATE_OPTION_READONLY)
1181 pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001182
1183 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001184/* pSMB->CreateOptions = cpu_to_le32(create_options &
1185 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001186 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001187
1188 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001189 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001190 count += name_len;
1191 pSMB->hdr.smb_buf_length += count;
1192
1193 pSMB->ByteCount = cpu_to_le16(count);
1194 /* long_op set to 1 to allow for oplock break timeouts */
1195 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001196 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001197 cifs_stats_inc(&tcon->num_opens);
1198 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001199 cFYI(1, "Error in Open = %d", rc);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001200 } else {
1201 /* BB verify if wct == 15 */
1202
Steve French582d21e2008-05-13 04:54:12 +00001203/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
Steve Frencha9d02ad2005-08-24 23:06:05 -07001204
1205 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1206 /* Let caller know file was created so we can set the mode. */
1207 /* Do we care about the CreateAction in any other cases? */
1208 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001209/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001210 *pOplock |= CIFS_CREATE_ACTION; */
1211 /* BB FIXME END */
1212
Steve French790fe572007-07-07 19:25:05 +00001213 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001214 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1215 pfile_info->LastAccessTime = 0; /* BB fixme */
1216 pfile_info->LastWriteTime = 0; /* BB fixme */
1217 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001218 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001219 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001220 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001221 pfile_info->AllocationSize =
1222 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1223 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001224 pfile_info->NumberOfLinks = cpu_to_le32(1);
Jeff Layton9a8165f2008-10-17 21:03:20 -04001225 pfile_info->DeletePending = 0;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001226 }
1227 }
1228
1229 cifs_buf_release(pSMB);
1230 if (rc == -EAGAIN)
1231 goto OldOpenRetry;
1232 return rc;
1233}
1234
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235int
1236CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1237 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001238 const int access_flags, const int create_options, __u16 *netfid,
1239 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001240 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241{
1242 int rc = -EACCES;
1243 OPEN_REQ *pSMB = NULL;
1244 OPEN_RSP *pSMBr = NULL;
1245 int bytes_returned;
1246 int name_len;
1247 __u16 count;
1248
1249openRetry:
1250 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1251 (void **) &pSMBr);
1252 if (rc)
1253 return rc;
1254
1255 pSMB->AndXCommand = 0xFF; /* none */
1256
1257 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1258 count = 1; /* account for one byte pad to word boundary */
1259 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001260 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001261 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 name_len++; /* trailing null */
1263 name_len *= 2;
1264 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001265 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 count = 0; /* no pad */
1267 name_len = strnlen(fileName, PATH_MAX);
1268 name_len++; /* trailing null */
1269 pSMB->NameLength = cpu_to_le16(name_len);
1270 strncpy(pSMB->fileName, fileName, name_len);
1271 }
1272 if (*pOplock & REQ_OPLOCK)
1273 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001274 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1277 pSMB->AllocationSize = 0;
Steve Frencheda3c0292005-07-21 15:20:28 -07001278 /* set file as system file if special file such
1279 as fifo and server expecting SFU style and
1280 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001281 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c0292005-07-21 15:20:28 -07001282 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1283 else
1284 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Jeff Layton67750fb2008-05-09 22:28:02 +00001285
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 /* XP does not handle ATTR_POSIX_SEMANTICS */
1287 /* but it helps speed up case sensitive checks for other
1288 servers such as Samba */
1289 if (tcon->ses->capabilities & CAP_UNIX)
1290 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1291
Jeff Layton67750fb2008-05-09 22:28:02 +00001292 if (create_options & CREATE_OPTION_READONLY)
1293 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);
1294
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1296 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c0292005-07-21 15:20:28 -07001297 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001298 /* BB Expirement with various impersonation levels and verify */
1299 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 pSMB->SecurityFlags =
1301 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1302
1303 count += name_len;
1304 pSMB->hdr.smb_buf_length += count;
1305
1306 pSMB->ByteCount = cpu_to_le16(count);
1307 /* long_op set to 1 to allow for oplock break timeouts */
1308 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001309 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha4544342005-08-24 13:59:35 -07001310 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001312 cFYI(1, "Error in Open = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 } else {
Steve French09d1db52005-04-28 22:41:08 -07001314 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1316 /* Let caller know file was created so we can set the mode. */
1317 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001318 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001319 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001320 if (pfile_info) {
Steve French61e74802008-12-03 00:57:54 +00001321 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
1322 36 /* CreationTime to Attributes */);
1323 /* the file_info buf is endian converted by caller */
1324 pfile_info->AllocationSize = pSMBr->AllocationSize;
1325 pfile_info->EndOfFile = pSMBr->EndOfFile;
1326 pfile_info->NumberOfLinks = cpu_to_le32(1);
1327 pfile_info->DeletePending = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001330
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 cifs_buf_release(pSMB);
1332 if (rc == -EAGAIN)
1333 goto openRetry;
1334 return rc;
1335}
1336
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337int
Steve French50c2f752007-07-13 00:33:32 +00001338CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1339 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1340 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341{
1342 int rc = -EACCES;
1343 READ_REQ *pSMB = NULL;
1344 READ_RSP *pSMBr = NULL;
1345 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001346 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001347 int resp_buf_type = 0;
1348 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
Joe Perchesb6b38f72010-04-21 03:50:45 +00001350 cFYI(1, "Reading %d bytes on fid %d", count, netfid);
Steve French790fe572007-07-07 19:25:05 +00001351 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001352 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001353 else {
Steve Frenchbfa0d752005-08-31 21:50:37 -07001354 wct = 10; /* old style read */
Steve French4c3130e2008-12-09 00:28:16 +00001355 if ((lseek >> 32) > 0) {
1356 /* can not handle this big offset for old */
1357 return -EIO;
1358 }
1359 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
1361 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001362 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 if (rc)
1364 return rc;
1365
1366 /* tcon and ses pointer are checked in smb_init */
1367 if (tcon->ses->server == NULL)
1368 return -ECONNABORTED;
1369
Steve Frenchec637e32005-12-12 20:53:18 -08001370 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 pSMB->Fid = netfid;
1372 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001373 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001374 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve Frenchbfa0d752005-08-31 21:50:37 -07001375
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 pSMB->Remaining = 0;
1377 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1378 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001379 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001380 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1381 else {
1382 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001383 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001384 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001385 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001386 }
Steve Frenchec637e32005-12-12 20:53:18 -08001387
1388 iov[0].iov_base = (char *)pSMB;
1389 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001390 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Steve French133672e2007-11-13 22:41:37 +00001391 &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001392 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001393 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001395 cERROR(1, "Send error in read = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 } else {
1397 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1398 data_length = data_length << 16;
1399 data_length += le16_to_cpu(pSMBr->DataLength);
1400 *nbytes = data_length;
1401
1402 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001403 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 || (data_length > count)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001405 cFYI(1, "bad length %d for count %d",
1406 data_length, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 rc = -EIO;
1408 *nbytes = 0;
1409 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001410 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001411 le16_to_cpu(pSMBr->DataOffset);
1412/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001413 cERROR(1, "Faulting on read rc = %d",rc);
Steve French50c2f752007-07-13 00:33:32 +00001414 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001415 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001416 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001417 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 }
1419 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420
Steve French4b8f9302006-02-26 16:41:18 +00001421/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001422 if (*buf) {
1423 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001424 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001425 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001426 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001427 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001428 /* return buffer to caller to free */
1429 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001430 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001431 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001432 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001433 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001434 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001435
1436 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 since file handle passed in no longer valid */
1438 return rc;
1439}
1440
Steve Frenchec637e32005-12-12 20:53:18 -08001441
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442int
1443CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1444 const int netfid, const unsigned int count,
1445 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001446 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447{
1448 int rc = -EACCES;
1449 WRITE_REQ *pSMB = NULL;
1450 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001451 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 __u32 bytes_sent;
1453 __u16 byte_count;
1454
Steve Frencha24e2d72010-04-03 17:20:21 +00001455 *nbytes = 0;
1456
Joe Perchesb6b38f72010-04-21 03:50:45 +00001457 /* cFYI(1, "write at %lld %d bytes", offset, count);*/
Steve French790fe572007-07-07 19:25:05 +00001458 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001459 return -ECONNABORTED;
1460
Steve French790fe572007-07-07 19:25:05 +00001461 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001462 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001463 else {
Steve French1c955182005-08-30 20:58:07 -07001464 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001465 if ((offset >> 32) > 0) {
1466 /* can not handle big offset for old srv */
1467 return -EIO;
1468 }
1469 }
Steve French1c955182005-08-30 20:58:07 -07001470
1471 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 (void **) &pSMBr);
1473 if (rc)
1474 return rc;
1475 /* tcon and ses pointer are checked in smb_init */
1476 if (tcon->ses->server == NULL)
1477 return -ECONNABORTED;
1478
1479 pSMB->AndXCommand = 0xFF; /* none */
1480 pSMB->Fid = netfid;
1481 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001482 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001483 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French50c2f752007-07-13 00:33:32 +00001484
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485 pSMB->Reserved = 0xFFFFFFFF;
1486 pSMB->WriteMode = 0;
1487 pSMB->Remaining = 0;
1488
Steve French50c2f752007-07-13 00:33:32 +00001489 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 can send more if LARGE_WRITE_X capability returned by the server and if
1491 our buffer is big enough or if we convert to iovecs on socket writes
1492 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001493 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1495 } else {
1496 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1497 & ~0xFF;
1498 }
1499
1500 if (bytes_sent > count)
1501 bytes_sent = count;
1502 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001503 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001504 if (buf)
Steve French61e74802008-12-03 00:57:54 +00001505 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001506 else if (ubuf) {
1507 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 cifs_buf_release(pSMB);
1509 return -EFAULT;
1510 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001511 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 /* No buffer */
1513 cifs_buf_release(pSMB);
1514 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001515 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001516 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001517 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001518 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001519 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001520
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1522 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001523 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001524
Steve French790fe572007-07-07 19:25:05 +00001525 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001526 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001527 else { /* old style write has byte count 4 bytes earlier
1528 so 4 bytes pad */
1529 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001530 (struct smb_com_writex_req *)pSMB;
1531 pSMBW->ByteCount = cpu_to_le16(byte_count);
1532 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
1534 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1535 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001536 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00001538 cFYI(1, "Send error in write = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 } else {
1540 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1541 *nbytes = (*nbytes) << 16;
1542 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301543
1544 /*
1545 * Mask off high 16 bits when bytes written as returned by the
1546 * server is greater than bytes requested by the client. Some
1547 * OS/2 servers are known to set incorrect CountHigh values.
1548 */
1549 if (*nbytes > count)
1550 *nbytes &= 0xFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 }
1552
1553 cifs_buf_release(pSMB);
1554
Steve French50c2f752007-07-13 00:33:32 +00001555 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 since file handle passed in no longer valid */
1557
1558 return rc;
1559}
1560
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001561int
1562CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001564 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1565 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566{
1567 int rc = -EACCES;
1568 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001569 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001570 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001571 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572
Jeff Laytonfbec9ab2009-04-03 13:44:00 -04001573 *nbytes = 0;
1574
Joe Perchesb6b38f72010-04-21 03:50:45 +00001575 cFYI(1, "write2 at %lld %d bytes", (long long)offset, count);
Steve Frenchff7feac2005-11-15 16:45:16 -08001576
Steve French4c3130e2008-12-09 00:28:16 +00001577 if (tcon->ses->capabilities & CAP_LARGE_FILES) {
Steve French8cc64c62005-10-03 13:49:43 -07001578 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001579 } else {
Steve French8cc64c62005-10-03 13:49:43 -07001580 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001581 if ((offset >> 32) > 0) {
1582 /* can not handle big offset for old srv */
1583 return -EIO;
1584 }
1585 }
Steve French8cc64c62005-10-03 13:49:43 -07001586 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 if (rc)
1588 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 /* tcon and ses pointer are checked in smb_init */
1590 if (tcon->ses->server == NULL)
1591 return -ECONNABORTED;
1592
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001593 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594 pSMB->Fid = netfid;
1595 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001596 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001597 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 pSMB->Reserved = 0xFFFFFFFF;
1599 pSMB->WriteMode = 0;
1600 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001601
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001603 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
Steve French3e844692005-10-03 13:37:24 -07001605 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1606 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001607 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001608 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001609 pSMB->hdr.smb_buf_length += count+1;
1610 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001611 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1612 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001613 pSMB->ByteCount = cpu_to_le16(count + 1);
1614 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001615 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001616 (struct smb_com_writex_req *)pSMB;
1617 pSMBW->ByteCount = cpu_to_le16(count + 5);
1618 }
Steve French3e844692005-10-03 13:37:24 -07001619 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001620 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001621 iov[0].iov_len = smb_hdr_len + 4;
1622 else /* wct == 12 pad bigger by four bytes */
1623 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001624
Steve French3e844692005-10-03 13:37:24 -07001625
Steve Frenchec637e32005-12-12 20:53:18 -08001626 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001627 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001628 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001630 cFYI(1, "Send error Write2 = %d", rc);
Steve French790fe572007-07-07 19:25:05 +00001631 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001632 /* presumably this can not happen, but best to be safe */
1633 rc = -EIO;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001634 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001635 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001636 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1637 *nbytes = (*nbytes) << 16;
1638 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301639
1640 /*
1641 * Mask off high 16 bits when bytes written as returned by the
1642 * server is greater than bytes requested by the client. OS/2
1643 * servers are known to set incorrect CountHigh values.
1644 */
1645 if (*nbytes > count)
1646 *nbytes &= 0xFFFF;
Steve French50c2f752007-07-13 00:33:32 +00001647 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
Steve French4b8f9302006-02-26 16:41:18 +00001649/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001650 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001651 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001652 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001653 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654
Steve French50c2f752007-07-13 00:33:32 +00001655 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656 since file handle passed in no longer valid */
1657
1658 return rc;
1659}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001660
1661
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662int
1663CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1664 const __u16 smb_file_id, const __u64 len,
1665 const __u64 offset, const __u32 numUnlock,
Steve French4b18f2a2008-04-29 00:06:05 +00001666 const __u32 numLock, const __u8 lockType, const bool waitFlag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667{
1668 int rc = 0;
1669 LOCK_REQ *pSMB = NULL;
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001670/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 int bytes_returned;
1672 int timeout = 0;
1673 __u16 count;
1674
Joe Perchesb6b38f72010-04-21 03:50:45 +00001675 cFYI(1, "CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock);
Steve French46810cb2005-04-28 22:41:09 -07001676 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1677
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678 if (rc)
1679 return rc;
1680
Steve French790fe572007-07-07 19:25:05 +00001681 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001682 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 pSMB->Timeout = 0;
Steve French4b18f2a2008-04-29 00:06:05 +00001684 } else if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001685 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1687 } else {
1688 pSMB->Timeout = 0;
1689 }
1690
1691 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1692 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1693 pSMB->LockType = lockType;
1694 pSMB->AndXCommand = 0xFF; /* none */
1695 pSMB->Fid = smb_file_id; /* netfid stays le */
1696
Steve French790fe572007-07-07 19:25:05 +00001697 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1699 /* BB where to store pid high? */
1700 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1701 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1702 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1703 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1704 count = sizeof(LOCKING_ANDX_RANGE);
1705 } else {
1706 /* oplock break */
1707 count = 0;
1708 }
1709 pSMB->hdr.smb_buf_length += count;
1710 pSMB->ByteCount = cpu_to_le16(count);
1711
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001712 if (waitFlag) {
1713 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001714 (struct smb_hdr *) pSMB, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001715 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001716 } else {
Steve French133672e2007-11-13 22:41:37 +00001717 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1718 timeout);
1719 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001720 }
Steve Frencha4544342005-08-24 13:59:35 -07001721 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001722 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001723 cFYI(1, "Send error in Lock = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724
Steve French50c2f752007-07-13 00:33:32 +00001725 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726 since file handle passed in no longer valid */
1727 return rc;
1728}
1729
1730int
Steve French08547b02006-02-28 22:39:25 +00001731CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1732 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001733 struct file_lock *pLockData, const __u16 lock_type,
Steve French4b18f2a2008-04-29 00:06:05 +00001734 const bool waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001735{
1736 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1737 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001738 struct cifs_posix_lock *parm_data;
1739 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001740 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001741 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001742 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001743 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001744 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001745
Joe Perchesb6b38f72010-04-21 03:50:45 +00001746 cFYI(1, "Posix Lock");
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001747
Steve French790fe572007-07-07 19:25:05 +00001748 if (pLockData == NULL)
Marcin Slusarzed5f0372008-05-13 04:01:01 +00001749 return -EINVAL;
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001750
Steve French08547b02006-02-28 22:39:25 +00001751 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1752
1753 if (rc)
1754 return rc;
1755
1756 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1757
Steve French50c2f752007-07-13 00:33:32 +00001758 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001759 pSMB->MaxSetupCount = 0;
1760 pSMB->Reserved = 0;
1761 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001762 pSMB->Reserved2 = 0;
1763 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1764 offset = param_offset + params;
1765
Steve French08547b02006-02-28 22:39:25 +00001766 count = sizeof(struct cifs_posix_lock);
1767 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001768 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001769 pSMB->SetupCount = 1;
1770 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001771 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001772 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1773 else
1774 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1775 byte_count = 3 /* pad */ + params + count;
1776 pSMB->DataCount = cpu_to_le16(count);
1777 pSMB->ParameterCount = cpu_to_le16(params);
1778 pSMB->TotalDataCount = pSMB->DataCount;
1779 pSMB->TotalParameterCount = pSMB->ParameterCount;
1780 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001781 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001782 (((char *) &pSMB->hdr.Protocol) + offset);
1783
1784 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001785 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001786 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001787 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001788 pSMB->Timeout = cpu_to_le32(-1);
1789 } else
1790 pSMB->Timeout = 0;
1791
Steve French08547b02006-02-28 22:39:25 +00001792 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001793 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001794 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001795
1796 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001797 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001798 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1799 pSMB->Reserved4 = 0;
1800 pSMB->hdr.smb_buf_length += byte_count;
1801 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001802 if (waitFlag) {
1803 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1804 (struct smb_hdr *) pSMBr, &bytes_returned);
1805 } else {
Steve French133672e2007-11-13 22:41:37 +00001806 iov[0].iov_base = (char *)pSMB;
1807 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1808 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1809 &resp_buf_type, timeout);
1810 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1811 not try to free it twice below on exit */
1812 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001813 }
1814
Steve French08547b02006-02-28 22:39:25 +00001815 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001816 cFYI(1, "Send error in Posix Lock = %d", rc);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001817 } else if (get_flag) {
1818 /* lock structure can be returned on get */
1819 __u16 data_offset;
1820 __u16 data_count;
1821 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001822
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001823 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1824 rc = -EIO; /* bad smb */
1825 goto plk_err_exit;
1826 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001827 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1828 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001829 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001830 rc = -EIO;
1831 goto plk_err_exit;
1832 }
1833 parm_data = (struct cifs_posix_lock *)
1834 ((char *)&pSMBr->hdr.Protocol + data_offset);
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001835 if (parm_data->lock_type == __constant_cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001836 pLockData->fl_type = F_UNLCK;
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001837 else {
1838 if (parm_data->lock_type ==
1839 __constant_cpu_to_le16(CIFS_RDLCK))
1840 pLockData->fl_type = F_RDLCK;
1841 else if (parm_data->lock_type ==
1842 __constant_cpu_to_le16(CIFS_WRLCK))
1843 pLockData->fl_type = F_WRLCK;
1844
1845 pLockData->fl_start = parm_data->start;
1846 pLockData->fl_end = parm_data->start +
1847 parm_data->length - 1;
1848 pLockData->fl_pid = parm_data->pid;
1849 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001850 }
Steve French50c2f752007-07-13 00:33:32 +00001851
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001852plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001853 if (pSMB)
1854 cifs_small_buf_release(pSMB);
1855
Steve French133672e2007-11-13 22:41:37 +00001856 if (resp_buf_type == CIFS_SMALL_BUFFER)
1857 cifs_small_buf_release(iov[0].iov_base);
1858 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1859 cifs_buf_release(iov[0].iov_base);
1860
Steve French08547b02006-02-28 22:39:25 +00001861 /* Note: On -EAGAIN error only caller can retry on handle based calls
1862 since file handle passed in no longer valid */
1863
1864 return rc;
1865}
1866
1867
1868int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1870{
1871 int rc = 0;
1872 CLOSE_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001873 cFYI(1, "In CIFSSMBClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874
1875/* do not retry on dead session on close */
1876 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001877 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878 return 0;
1879 if (rc)
1880 return rc;
1881
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e52006-10-02 05:53:29 +00001883 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001885 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001886 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001888 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001889 /* EINTR is expected when user ctl-c to kill app */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001890 cERROR(1, "Send error in Close = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001891 }
1892 }
1893
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001895 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 rc = 0;
1897
1898 return rc;
1899}
1900
1901int
Steve Frenchb298f222009-02-21 21:17:43 +00001902CIFSSMBFlush(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1903{
1904 int rc = 0;
1905 FLUSH_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001906 cFYI(1, "In CIFSSMBFlush");
Steve Frenchb298f222009-02-21 21:17:43 +00001907
1908 rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB);
1909 if (rc)
1910 return rc;
1911
1912 pSMB->FileID = (__u16) smb_file_id;
1913 pSMB->ByteCount = 0;
1914 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
1915 cifs_stats_inc(&tcon->num_flushes);
1916 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001917 cERROR(1, "Send error in Flush = %d", rc);
Steve Frenchb298f222009-02-21 21:17:43 +00001918
1919 return rc;
1920}
1921
1922int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1924 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001925 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926{
1927 int rc = 0;
1928 RENAME_REQ *pSMB = NULL;
1929 RENAME_RSP *pSMBr = NULL;
1930 int bytes_returned;
1931 int name_len, name_len2;
1932 __u16 count;
1933
Joe Perchesb6b38f72010-04-21 03:50:45 +00001934 cFYI(1, "In CIFSSMBRename");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935renameRetry:
1936 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1937 (void **) &pSMBr);
1938 if (rc)
1939 return rc;
1940
1941 pSMB->BufferFormat = 0x04;
1942 pSMB->SearchAttributes =
1943 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1944 ATTR_DIRECTORY);
1945
1946 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1947 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001948 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001949 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950 name_len++; /* trailing null */
1951 name_len *= 2;
1952 pSMB->OldFileName[name_len] = 0x04; /* pad */
1953 /* protocol requires ASCII signature byte on Unicode string */
1954 pSMB->OldFileName[name_len + 1] = 0x00;
1955 name_len2 =
Steve French582d21e2008-05-13 04:54:12 +00001956 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07001957 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
1959 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00001960 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961 name_len = strnlen(fromName, PATH_MAX);
1962 name_len++; /* trailing null */
1963 strncpy(pSMB->OldFileName, fromName, name_len);
1964 name_len2 = strnlen(toName, PATH_MAX);
1965 name_len2++; /* trailing null */
1966 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
1967 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
1968 name_len2++; /* trailing null */
1969 name_len2++; /* signature byte */
1970 }
1971
1972 count = 1 /* 1st signature byte */ + name_len + name_len2;
1973 pSMB->hdr.smb_buf_length += count;
1974 pSMB->ByteCount = cpu_to_le16(count);
1975
1976 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1977 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001978 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00001979 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001980 cFYI(1, "Send error in rename = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 cifs_buf_release(pSMB);
1983
1984 if (rc == -EAGAIN)
1985 goto renameRetry;
1986
1987 return rc;
1988}
1989
Steve French50c2f752007-07-13 00:33:32 +00001990int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
Jeff Layton391e5752008-09-24 11:32:59 -04001991 int netfid, const char *target_name,
Steve French50c2f752007-07-13 00:33:32 +00001992 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993{
1994 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1995 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00001996 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 char *data_offset;
1998 char dummy_string[30];
1999 int rc = 0;
2000 int bytes_returned = 0;
2001 int len_of_str;
2002 __u16 params, param_offset, offset, count, byte_count;
2003
Joe Perchesb6b38f72010-04-21 03:50:45 +00002004 cFYI(1, "Rename to File by handle");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
2006 (void **) &pSMBr);
2007 if (rc)
2008 return rc;
2009
2010 params = 6;
2011 pSMB->MaxSetupCount = 0;
2012 pSMB->Reserved = 0;
2013 pSMB->Flags = 0;
2014 pSMB->Timeout = 0;
2015 pSMB->Reserved2 = 0;
2016 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2017 offset = param_offset + params;
2018
2019 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2020 rename_info = (struct set_file_rename *) data_offset;
2021 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002022 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 pSMB->SetupCount = 1;
2024 pSMB->Reserved3 = 0;
2025 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2026 byte_count = 3 /* pad */ + params;
2027 pSMB->ParameterCount = cpu_to_le16(params);
2028 pSMB->TotalParameterCount = pSMB->ParameterCount;
2029 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2030 pSMB->DataOffset = cpu_to_le16(offset);
2031 /* construct random name ".cifs_tmp<inodenum><mid>" */
2032 rename_info->overwrite = cpu_to_le32(1);
2033 rename_info->root_fid = 0;
2034 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002035 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002036 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2037 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002038 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002040 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002041 target_name, PATH_MAX, nls_codepage,
2042 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043 }
2044 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
Jeff Layton391e5752008-09-24 11:32:59 -04002045 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 byte_count += count;
2047 pSMB->DataCount = cpu_to_le16(count);
2048 pSMB->TotalDataCount = pSMB->DataCount;
2049 pSMB->Fid = netfid;
2050 pSMB->InformationLevel =
2051 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2052 pSMB->Reserved4 = 0;
2053 pSMB->hdr.smb_buf_length += byte_count;
2054 pSMB->ByteCount = cpu_to_le16(byte_count);
2055 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002056 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002057 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002058 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002059 cFYI(1, "Send error in Rename (by file handle) = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -07002060
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 cifs_buf_release(pSMB);
2062
2063 /* Note: On -EAGAIN error only caller can retry on handle based calls
2064 since file handle passed in no longer valid */
2065
2066 return rc;
2067}
2068
2069int
Steve French50c2f752007-07-13 00:33:32 +00002070CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2071 const __u16 target_tid, const char *toName, const int flags,
2072 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073{
2074 int rc = 0;
2075 COPY_REQ *pSMB = NULL;
2076 COPY_RSP *pSMBr = NULL;
2077 int bytes_returned;
2078 int name_len, name_len2;
2079 __u16 count;
2080
Joe Perchesb6b38f72010-04-21 03:50:45 +00002081 cFYI(1, "In CIFSSMBCopy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082copyRetry:
2083 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2084 (void **) &pSMBr);
2085 if (rc)
2086 return rc;
2087
2088 pSMB->BufferFormat = 0x04;
2089 pSMB->Tid2 = target_tid;
2090
2091 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2092
2093 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002094 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002095 fromName, PATH_MAX, nls_codepage,
2096 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 name_len++; /* trailing null */
2098 name_len *= 2;
2099 pSMB->OldFileName[name_len] = 0x04; /* pad */
2100 /* protocol requires ASCII signature byte on Unicode string */
2101 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002102 name_len2 =
2103 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002104 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2106 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002107 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108 name_len = strnlen(fromName, PATH_MAX);
2109 name_len++; /* trailing null */
2110 strncpy(pSMB->OldFileName, fromName, name_len);
2111 name_len2 = strnlen(toName, PATH_MAX);
2112 name_len2++; /* trailing null */
2113 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2114 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2115 name_len2++; /* trailing null */
2116 name_len2++; /* signature byte */
2117 }
2118
2119 count = 1 /* 1st signature byte */ + name_len + name_len2;
2120 pSMB->hdr.smb_buf_length += count;
2121 pSMB->ByteCount = cpu_to_le16(count);
2122
2123 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2124 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2125 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002126 cFYI(1, "Send error in copy = %d with %d files copied",
2127 rc, le16_to_cpu(pSMBr->CopyCount));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 }
Steve French0d817bc2008-05-22 02:02:03 +00002129 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130
2131 if (rc == -EAGAIN)
2132 goto copyRetry;
2133
2134 return rc;
2135}
2136
2137int
2138CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2139 const char *fromName, const char *toName,
2140 const struct nls_table *nls_codepage)
2141{
2142 TRANSACTION2_SPI_REQ *pSMB = NULL;
2143 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2144 char *data_offset;
2145 int name_len;
2146 int name_len_target;
2147 int rc = 0;
2148 int bytes_returned = 0;
2149 __u16 params, param_offset, offset, byte_count;
2150
Joe Perchesb6b38f72010-04-21 03:50:45 +00002151 cFYI(1, "In Symlink Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152createSymLinkRetry:
2153 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2154 (void **) &pSMBr);
2155 if (rc)
2156 return rc;
2157
2158 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2159 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002160 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 /* find define for this maxpathcomponent */
2162 , nls_codepage);
2163 name_len++; /* trailing null */
2164 name_len *= 2;
2165
Steve French50c2f752007-07-13 00:33:32 +00002166 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 name_len = strnlen(fromName, PATH_MAX);
2168 name_len++; /* trailing null */
2169 strncpy(pSMB->FileName, fromName, name_len);
2170 }
2171 params = 6 + name_len;
2172 pSMB->MaxSetupCount = 0;
2173 pSMB->Reserved = 0;
2174 pSMB->Flags = 0;
2175 pSMB->Timeout = 0;
2176 pSMB->Reserved2 = 0;
2177 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002178 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 offset = param_offset + params;
2180
2181 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2182 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2183 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002184 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 /* find define for this maxpathcomponent */
2186 , nls_codepage);
2187 name_len_target++; /* trailing null */
2188 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002189 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190 name_len_target = strnlen(toName, PATH_MAX);
2191 name_len_target++; /* trailing null */
2192 strncpy(data_offset, toName, name_len_target);
2193 }
2194
2195 pSMB->MaxParameterCount = cpu_to_le16(2);
2196 /* BB find exact max on data count below from sess */
2197 pSMB->MaxDataCount = cpu_to_le16(1000);
2198 pSMB->SetupCount = 1;
2199 pSMB->Reserved3 = 0;
2200 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2201 byte_count = 3 /* pad */ + params + name_len_target;
2202 pSMB->DataCount = cpu_to_le16(name_len_target);
2203 pSMB->ParameterCount = cpu_to_le16(params);
2204 pSMB->TotalDataCount = pSMB->DataCount;
2205 pSMB->TotalParameterCount = pSMB->ParameterCount;
2206 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2207 pSMB->DataOffset = cpu_to_le16(offset);
2208 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2209 pSMB->Reserved4 = 0;
2210 pSMB->hdr.smb_buf_length += byte_count;
2211 pSMB->ByteCount = cpu_to_le16(byte_count);
2212 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2213 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002214 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002215 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002216 cFYI(1, "Send error in SetPathInfo create symlink = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217
Steve French0d817bc2008-05-22 02:02:03 +00002218 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219
2220 if (rc == -EAGAIN)
2221 goto createSymLinkRetry;
2222
2223 return rc;
2224}
2225
2226int
2227CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2228 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002229 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230{
2231 TRANSACTION2_SPI_REQ *pSMB = NULL;
2232 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2233 char *data_offset;
2234 int name_len;
2235 int name_len_target;
2236 int rc = 0;
2237 int bytes_returned = 0;
2238 __u16 params, param_offset, offset, byte_count;
2239
Joe Perchesb6b38f72010-04-21 03:50:45 +00002240 cFYI(1, "In Create Hard link Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241createHardLinkRetry:
2242 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2243 (void **) &pSMBr);
2244 if (rc)
2245 return rc;
2246
2247 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002248 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002249 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250 name_len++; /* trailing null */
2251 name_len *= 2;
2252
Steve French50c2f752007-07-13 00:33:32 +00002253 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 name_len = strnlen(toName, PATH_MAX);
2255 name_len++; /* trailing null */
2256 strncpy(pSMB->FileName, toName, name_len);
2257 }
2258 params = 6 + name_len;
2259 pSMB->MaxSetupCount = 0;
2260 pSMB->Reserved = 0;
2261 pSMB->Flags = 0;
2262 pSMB->Timeout = 0;
2263 pSMB->Reserved2 = 0;
2264 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002265 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 offset = param_offset + params;
2267
2268 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2269 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2270 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002271 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002272 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273 name_len_target++; /* trailing null */
2274 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002275 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 name_len_target = strnlen(fromName, PATH_MAX);
2277 name_len_target++; /* trailing null */
2278 strncpy(data_offset, fromName, name_len_target);
2279 }
2280
2281 pSMB->MaxParameterCount = cpu_to_le16(2);
2282 /* BB find exact max on data count below from sess*/
2283 pSMB->MaxDataCount = cpu_to_le16(1000);
2284 pSMB->SetupCount = 1;
2285 pSMB->Reserved3 = 0;
2286 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2287 byte_count = 3 /* pad */ + params + name_len_target;
2288 pSMB->ParameterCount = cpu_to_le16(params);
2289 pSMB->TotalParameterCount = pSMB->ParameterCount;
2290 pSMB->DataCount = cpu_to_le16(name_len_target);
2291 pSMB->TotalDataCount = pSMB->DataCount;
2292 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2293 pSMB->DataOffset = cpu_to_le16(offset);
2294 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2295 pSMB->Reserved4 = 0;
2296 pSMB->hdr.smb_buf_length += byte_count;
2297 pSMB->ByteCount = cpu_to_le16(byte_count);
2298 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2299 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002300 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002301 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002302 cFYI(1, "Send error in SetPathInfo (hard link) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303
2304 cifs_buf_release(pSMB);
2305 if (rc == -EAGAIN)
2306 goto createHardLinkRetry;
2307
2308 return rc;
2309}
2310
2311int
2312CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2313 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002314 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315{
2316 int rc = 0;
2317 NT_RENAME_REQ *pSMB = NULL;
2318 RENAME_RSP *pSMBr = NULL;
2319 int bytes_returned;
2320 int name_len, name_len2;
2321 __u16 count;
2322
Joe Perchesb6b38f72010-04-21 03:50:45 +00002323 cFYI(1, "In CIFSCreateHardLink");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324winCreateHardLinkRetry:
2325
2326 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2327 (void **) &pSMBr);
2328 if (rc)
2329 return rc;
2330
2331 pSMB->SearchAttributes =
2332 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2333 ATTR_DIRECTORY);
2334 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2335 pSMB->ClusterCount = 0;
2336
2337 pSMB->BufferFormat = 0x04;
2338
2339 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2340 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002341 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002342 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343 name_len++; /* trailing null */
2344 name_len *= 2;
Jeff Laytonfcc7c092009-02-28 12:59:03 -05002345
2346 /* protocol specifies ASCII buffer format (0x04) for unicode */
2347 pSMB->OldFileName[name_len] = 0x04;
2348 pSMB->OldFileName[name_len + 1] = 0x00; /* pad */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002350 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002351 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002352 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2353 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002354 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355 name_len = strnlen(fromName, PATH_MAX);
2356 name_len++; /* trailing null */
2357 strncpy(pSMB->OldFileName, fromName, name_len);
2358 name_len2 = strnlen(toName, PATH_MAX);
2359 name_len2++; /* trailing null */
2360 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2361 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2362 name_len2++; /* trailing null */
2363 name_len2++; /* signature byte */
2364 }
2365
2366 count = 1 /* string type byte */ + name_len + name_len2;
2367 pSMB->hdr.smb_buf_length += count;
2368 pSMB->ByteCount = cpu_to_le16(count);
2369
2370 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2371 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002372 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002373 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002374 cFYI(1, "Send error in hard link (NT rename) = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00002375
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376 cifs_buf_release(pSMB);
2377 if (rc == -EAGAIN)
2378 goto winCreateHardLinkRetry;
2379
2380 return rc;
2381}
2382
2383int
2384CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
Jeff Layton460b9692009-04-30 07:17:56 -04002385 const unsigned char *searchName, char **symlinkinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002386 const struct nls_table *nls_codepage)
2387{
2388/* SMB_QUERY_FILE_UNIX_LINK */
2389 TRANSACTION2_QPI_REQ *pSMB = NULL;
2390 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2391 int rc = 0;
2392 int bytes_returned;
2393 int name_len;
2394 __u16 params, byte_count;
Jeff Layton460b9692009-04-30 07:17:56 -04002395 char *data_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396
Joe Perchesb6b38f72010-04-21 03:50:45 +00002397 cFYI(1, "In QPathSymLinkInfo (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398
2399querySymLinkRetry:
2400 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2401 (void **) &pSMBr);
2402 if (rc)
2403 return rc;
2404
2405 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2406 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002407 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2408 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409 name_len++; /* trailing null */
2410 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002411 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412 name_len = strnlen(searchName, PATH_MAX);
2413 name_len++; /* trailing null */
2414 strncpy(pSMB->FileName, searchName, name_len);
2415 }
2416
2417 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2418 pSMB->TotalDataCount = 0;
2419 pSMB->MaxParameterCount = cpu_to_le16(2);
Jeff Layton46a75742009-05-24 18:45:17 -04002420 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421 pSMB->MaxSetupCount = 0;
2422 pSMB->Reserved = 0;
2423 pSMB->Flags = 0;
2424 pSMB->Timeout = 0;
2425 pSMB->Reserved2 = 0;
2426 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002427 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 pSMB->DataCount = 0;
2429 pSMB->DataOffset = 0;
2430 pSMB->SetupCount = 1;
2431 pSMB->Reserved3 = 0;
2432 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2433 byte_count = params + 1 /* pad */ ;
2434 pSMB->TotalParameterCount = cpu_to_le16(params);
2435 pSMB->ParameterCount = pSMB->TotalParameterCount;
2436 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2437 pSMB->Reserved4 = 0;
2438 pSMB->hdr.smb_buf_length += byte_count;
2439 pSMB->ByteCount = cpu_to_le16(byte_count);
2440
2441 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2442 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2443 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002444 cFYI(1, "Send error in QuerySymLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445 } else {
2446 /* decode response */
2447
2448 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449 /* BB also check enough total bytes returned */
Jeff Layton460b9692009-04-30 07:17:56 -04002450 if (rc || (pSMBr->ByteCount < 2))
2451 rc = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 else {
Steve French0e0d2cf2009-05-01 05:27:32 +00002453 bool is_unicode;
Jeff Layton460b9692009-04-30 07:17:56 -04002454 u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455
Jeff Layton460b9692009-04-30 07:17:56 -04002456 data_start = ((char *) &pSMBr->hdr.Protocol) +
2457 le16_to_cpu(pSMBr->t2.DataOffset);
2458
Steve French0e0d2cf2009-05-01 05:27:32 +00002459 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
2460 is_unicode = true;
2461 else
2462 is_unicode = false;
2463
Steve French737b7582005-04-28 22:41:06 -07002464 /* BB FIXME investigate remapping reserved chars here */
Steve Frenchd185cda2009-04-30 17:45:10 +00002465 *symlinkinfo = cifs_strndup_from_ucs(data_start, count,
Steve French0e0d2cf2009-05-01 05:27:32 +00002466 is_unicode, nls_codepage);
Jeff Layton8b6427a2009-05-19 09:57:03 -04002467 if (!*symlinkinfo)
Jeff Layton460b9692009-04-30 07:17:56 -04002468 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469 }
2470 }
2471 cifs_buf_release(pSMB);
2472 if (rc == -EAGAIN)
2473 goto querySymLinkRetry;
2474 return rc;
2475}
2476
Parag Warudkarc9489772007-10-23 18:09:48 +00002477#ifdef CONFIG_CIFS_EXPERIMENTAL
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478int
2479CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2480 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002481 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482 const struct nls_table *nls_codepage)
2483{
2484 int rc = 0;
2485 int bytes_returned;
Steve French50c2f752007-07-13 00:33:32 +00002486 struct smb_com_transaction_ioctl_req *pSMB;
2487 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488
Joe Perchesb6b38f72010-04-21 03:50:45 +00002489 cFYI(1, "In Windows reparse style QueryLink for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2491 (void **) &pSMBr);
2492 if (rc)
2493 return rc;
2494
2495 pSMB->TotalParameterCount = 0 ;
2496 pSMB->TotalDataCount = 0;
2497 pSMB->MaxParameterCount = cpu_to_le32(2);
2498 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002499 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2500 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501 pSMB->MaxSetupCount = 4;
2502 pSMB->Reserved = 0;
2503 pSMB->ParameterOffset = 0;
2504 pSMB->DataCount = 0;
2505 pSMB->DataOffset = 0;
2506 pSMB->SetupCount = 4;
2507 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2508 pSMB->ParameterCount = pSMB->TotalParameterCount;
2509 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2510 pSMB->IsFsctl = 1; /* FSCTL */
2511 pSMB->IsRootFlag = 0;
2512 pSMB->Fid = fid; /* file handle always le */
2513 pSMB->ByteCount = 0;
2514
2515 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2516 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2517 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002518 cFYI(1, "Send error in QueryReparseLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519 } else { /* decode response */
2520 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2521 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
Steve Frenchafe48c32009-05-02 05:25:46 +00002522 if ((pSMBr->ByteCount < 2) || (data_offset > 512)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 /* BB also check enough total bytes returned */
2524 rc = -EIO; /* bad smb */
Steve Frenchafe48c32009-05-02 05:25:46 +00002525 goto qreparse_out;
2526 }
2527 if (data_count && (data_count < 2048)) {
2528 char *end_of_smb = 2 /* sizeof byte count */ +
2529 pSMBr->ByteCount + (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530
Steve Frenchafe48c32009-05-02 05:25:46 +00002531 struct reparse_data *reparse_buf =
Steve French50c2f752007-07-13 00:33:32 +00002532 (struct reparse_data *)
2533 ((char *)&pSMBr->hdr.Protocol
2534 + data_offset);
Steve Frenchafe48c32009-05-02 05:25:46 +00002535 if ((char *)reparse_buf >= end_of_smb) {
2536 rc = -EIO;
2537 goto qreparse_out;
2538 }
2539 if ((reparse_buf->LinkNamesBuf +
2540 reparse_buf->TargetNameOffset +
2541 reparse_buf->TargetNameLen) > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002542 cFYI(1, "reparse buf beyond SMB");
Steve Frenchafe48c32009-05-02 05:25:46 +00002543 rc = -EIO;
2544 goto qreparse_out;
2545 }
Steve French50c2f752007-07-13 00:33:32 +00002546
Steve Frenchafe48c32009-05-02 05:25:46 +00002547 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2548 cifs_from_ucs2(symlinkinfo, (__le16 *)
Steve French50c2f752007-07-13 00:33:32 +00002549 (reparse_buf->LinkNamesBuf +
2550 reparse_buf->TargetNameOffset),
Steve Frenchafe48c32009-05-02 05:25:46 +00002551 buflen,
2552 reparse_buf->TargetNameLen,
2553 nls_codepage, 0);
2554 } else { /* ASCII names */
2555 strncpy(symlinkinfo,
2556 reparse_buf->LinkNamesBuf +
2557 reparse_buf->TargetNameOffset,
2558 min_t(const int, buflen,
2559 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002561 } else {
2562 rc = -EIO;
Joe Perchesb6b38f72010-04-21 03:50:45 +00002563 cFYI(1, "Invalid return data count on "
2564 "get reparse info ioctl");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002566 symlinkinfo[buflen] = 0; /* just in case so the caller
2567 does not go off the end of the buffer */
Joe Perchesb6b38f72010-04-21 03:50:45 +00002568 cFYI(1, "readlink result - %s", symlinkinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569 }
Steve French989c7e52009-05-02 05:32:20 +00002570
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002572 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573
2574 /* Note: On -EAGAIN error only caller can retry on handle based calls
2575 since file handle passed in no longer valid */
2576
2577 return rc;
2578}
Steve Frenchafe48c32009-05-02 05:25:46 +00002579#endif /* CIFS_EXPERIMENTAL */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580
2581#ifdef CONFIG_CIFS_POSIX
2582
2583/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002584static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2585 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586{
2587 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002588 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2589 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2590 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002591 /* cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592
2593 return;
2594}
2595
2596/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002597static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2598 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599{
2600 int size = 0;
2601 int i;
2602 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002603 struct cifs_posix_ace *pACE;
2604 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2605 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606
2607 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2608 return -EOPNOTSUPP;
2609
Steve French790fe572007-07-07 19:25:05 +00002610 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611 count = le16_to_cpu(cifs_acl->access_entry_count);
2612 pACE = &cifs_acl->ace_array[0];
2613 size = sizeof(struct cifs_posix_acl);
2614 size += sizeof(struct cifs_posix_ace) * count;
2615 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002616 if (size_of_data_area < size) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002617 cFYI(1, "bad CIFS POSIX ACL size %d vs. %d",
2618 size_of_data_area, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619 return -EINVAL;
2620 }
Steve French790fe572007-07-07 19:25:05 +00002621 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 count = le16_to_cpu(cifs_acl->access_entry_count);
2623 size = sizeof(struct cifs_posix_acl);
2624 size += sizeof(struct cifs_posix_ace) * count;
2625/* skip past access ACEs to get to default ACEs */
2626 pACE = &cifs_acl->ace_array[count];
2627 count = le16_to_cpu(cifs_acl->default_entry_count);
2628 size += sizeof(struct cifs_posix_ace) * count;
2629 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002630 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631 return -EINVAL;
2632 } else {
2633 /* illegal type */
2634 return -EINVAL;
2635 }
2636
2637 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002638 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002639 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002640 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002641 return -ERANGE;
2642 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002643 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002644 for (i = 0; i < count ; i++) {
2645 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2646 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647 }
2648 }
2649 return size;
2650}
2651
Steve French50c2f752007-07-13 00:33:32 +00002652static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2653 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654{
2655 __u16 rc = 0; /* 0 = ACL converted ok */
2656
Steve Frenchff7feac2005-11-15 16:45:16 -08002657 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2658 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002660 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661 /* Probably no need to le convert -1 on any arch but can not hurt */
2662 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002663 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002664 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002665 /*cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id);*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666 return rc;
2667}
2668
2669/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002670static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2671 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672{
2673 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002674 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2675 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676 int count;
2677 int i;
2678
Steve French790fe572007-07-07 19:25:05 +00002679 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680 return 0;
2681
2682 count = posix_acl_xattr_count((size_t)buflen);
Joe Perchesb6b38f72010-04-21 03:50:45 +00002683 cFYI(1, "setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002684 "version of %d",
Joe Perchesb6b38f72010-04-21 03:50:45 +00002685 count, buflen, le32_to_cpu(local_acl->a_version));
Steve French790fe572007-07-07 19:25:05 +00002686 if (le32_to_cpu(local_acl->a_version) != 2) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002687 cFYI(1, "unknown POSIX ACL version %d",
2688 le32_to_cpu(local_acl->a_version));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 return 0;
2690 }
2691 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002692 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002693 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002694 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002695 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002697 cFYI(1, "unknown ACL type %d", acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698 return 0;
2699 }
Steve French50c2f752007-07-13 00:33:32 +00002700 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2702 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002703 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704 /* ACE not converted */
2705 break;
2706 }
2707 }
Steve French790fe572007-07-07 19:25:05 +00002708 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2710 rc += sizeof(struct cifs_posix_acl);
2711 /* BB add check to make sure ACL does not overflow SMB */
2712 }
2713 return rc;
2714}
2715
2716int
2717CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002718 const unsigned char *searchName,
2719 char *acl_inf, const int buflen, const int acl_type,
2720 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721{
2722/* SMB_QUERY_POSIX_ACL */
2723 TRANSACTION2_QPI_REQ *pSMB = NULL;
2724 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2725 int rc = 0;
2726 int bytes_returned;
2727 int name_len;
2728 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002729
Joe Perchesb6b38f72010-04-21 03:50:45 +00002730 cFYI(1, "In GetPosixACL (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731
2732queryAclRetry:
2733 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2734 (void **) &pSMBr);
2735 if (rc)
2736 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002737
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2739 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002740 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002741 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742 name_len++; /* trailing null */
2743 name_len *= 2;
2744 pSMB->FileName[name_len] = 0;
2745 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002746 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747 name_len = strnlen(searchName, PATH_MAX);
2748 name_len++; /* trailing null */
2749 strncpy(pSMB->FileName, searchName, name_len);
2750 }
2751
2752 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2753 pSMB->TotalDataCount = 0;
2754 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002755 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 pSMB->MaxDataCount = cpu_to_le16(4000);
2757 pSMB->MaxSetupCount = 0;
2758 pSMB->Reserved = 0;
2759 pSMB->Flags = 0;
2760 pSMB->Timeout = 0;
2761 pSMB->Reserved2 = 0;
2762 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002763 offsetof(struct smb_com_transaction2_qpi_req,
2764 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 pSMB->DataCount = 0;
2766 pSMB->DataOffset = 0;
2767 pSMB->SetupCount = 1;
2768 pSMB->Reserved3 = 0;
2769 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2770 byte_count = params + 1 /* pad */ ;
2771 pSMB->TotalParameterCount = cpu_to_le16(params);
2772 pSMB->ParameterCount = pSMB->TotalParameterCount;
2773 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2774 pSMB->Reserved4 = 0;
2775 pSMB->hdr.smb_buf_length += byte_count;
2776 pSMB->ByteCount = cpu_to_le16(byte_count);
2777
2778 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2779 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002780 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002782 cFYI(1, "Send error in Query POSIX ACL = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 } else {
2784 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002785
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2787 if (rc || (pSMBr->ByteCount < 2))
2788 /* BB also check enough total bytes returned */
2789 rc = -EIO; /* bad smb */
2790 else {
2791 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2792 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2793 rc = cifs_copy_posix_acl(acl_inf,
2794 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002795 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 }
2797 }
2798 cifs_buf_release(pSMB);
2799 if (rc == -EAGAIN)
2800 goto queryAclRetry;
2801 return rc;
2802}
2803
2804int
2805CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002806 const unsigned char *fileName,
2807 const char *local_acl, const int buflen,
2808 const int acl_type,
2809 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810{
2811 struct smb_com_transaction2_spi_req *pSMB = NULL;
2812 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2813 char *parm_data;
2814 int name_len;
2815 int rc = 0;
2816 int bytes_returned = 0;
2817 __u16 params, byte_count, data_count, param_offset, offset;
2818
Joe Perchesb6b38f72010-04-21 03:50:45 +00002819 cFYI(1, "In SetPosixACL (Unix) for path %s", fileName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820setAclRetry:
2821 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002822 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823 if (rc)
2824 return rc;
2825 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2826 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002827 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002828 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829 name_len++; /* trailing null */
2830 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002831 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 name_len = strnlen(fileName, PATH_MAX);
2833 name_len++; /* trailing null */
2834 strncpy(pSMB->FileName, fileName, name_len);
2835 }
2836 params = 6 + name_len;
2837 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00002838 /* BB find max SMB size from sess */
2839 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840 pSMB->MaxSetupCount = 0;
2841 pSMB->Reserved = 0;
2842 pSMB->Flags = 0;
2843 pSMB->Timeout = 0;
2844 pSMB->Reserved2 = 0;
2845 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002846 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847 offset = param_offset + params;
2848 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2849 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2850
2851 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002852 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002853
Steve French790fe572007-07-07 19:25:05 +00002854 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855 rc = -EOPNOTSUPP;
2856 goto setACLerrorExit;
2857 }
2858 pSMB->DataOffset = cpu_to_le16(offset);
2859 pSMB->SetupCount = 1;
2860 pSMB->Reserved3 = 0;
2861 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2862 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2863 byte_count = 3 /* pad */ + params + data_count;
2864 pSMB->DataCount = cpu_to_le16(data_count);
2865 pSMB->TotalDataCount = pSMB->DataCount;
2866 pSMB->ParameterCount = cpu_to_le16(params);
2867 pSMB->TotalParameterCount = pSMB->ParameterCount;
2868 pSMB->Reserved4 = 0;
2869 pSMB->hdr.smb_buf_length += byte_count;
2870 pSMB->ByteCount = cpu_to_le16(byte_count);
2871 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002872 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00002873 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002874 cFYI(1, "Set POSIX ACL returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875
2876setACLerrorExit:
2877 cifs_buf_release(pSMB);
2878 if (rc == -EAGAIN)
2879 goto setAclRetry;
2880 return rc;
2881}
2882
Steve Frenchf654bac2005-04-28 22:41:04 -07002883/* BB fix tabs in this function FIXME BB */
2884int
2885CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00002886 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07002887{
Steve French50c2f752007-07-13 00:33:32 +00002888 int rc = 0;
2889 struct smb_t2_qfi_req *pSMB = NULL;
2890 struct smb_t2_qfi_rsp *pSMBr = NULL;
2891 int bytes_returned;
2892 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07002893
Joe Perchesb6b38f72010-04-21 03:50:45 +00002894 cFYI(1, "In GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00002895 if (tcon == NULL)
2896 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07002897
2898GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00002899 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2900 (void **) &pSMBr);
2901 if (rc)
2902 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07002903
Steve Frenchad7a2922008-02-07 23:25:02 +00002904 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00002905 pSMB->t2.TotalDataCount = 0;
2906 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
2907 /* BB find exact max data count below from sess structure BB */
2908 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
2909 pSMB->t2.MaxSetupCount = 0;
2910 pSMB->t2.Reserved = 0;
2911 pSMB->t2.Flags = 0;
2912 pSMB->t2.Timeout = 0;
2913 pSMB->t2.Reserved2 = 0;
2914 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
2915 Fid) - 4);
2916 pSMB->t2.DataCount = 0;
2917 pSMB->t2.DataOffset = 0;
2918 pSMB->t2.SetupCount = 1;
2919 pSMB->t2.Reserved3 = 0;
2920 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
2921 byte_count = params + 1 /* pad */ ;
2922 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
2923 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
2924 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
2925 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07002926 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00002927 pSMB->hdr.smb_buf_length += byte_count;
2928 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07002929
Steve French790fe572007-07-07 19:25:05 +00002930 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2931 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2932 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002933 cFYI(1, "error %d in GetExtAttr", rc);
Steve French790fe572007-07-07 19:25:05 +00002934 } else {
2935 /* decode response */
2936 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2937 if (rc || (pSMBr->ByteCount < 2))
2938 /* BB also check enough total bytes returned */
2939 /* If rc should we check for EOPNOSUPP and
2940 disable the srvino flag? or in caller? */
2941 rc = -EIO; /* bad smb */
2942 else {
2943 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2944 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2945 struct file_chattr_info *pfinfo;
2946 /* BB Do we need a cast or hash here ? */
2947 if (count != 16) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002948 cFYI(1, "Illegal size ret in GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00002949 rc = -EIO;
2950 goto GetExtAttrOut;
2951 }
2952 pfinfo = (struct file_chattr_info *)
2953 (data_offset + (char *) &pSMBr->hdr.Protocol);
2954 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07002955 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00002956 }
2957 }
Steve Frenchf654bac2005-04-28 22:41:04 -07002958GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00002959 cifs_buf_release(pSMB);
2960 if (rc == -EAGAIN)
2961 goto GetExtAttrRetry;
2962 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07002963}
2964
Steve Frenchf654bac2005-04-28 22:41:04 -07002965#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966
Jeff Layton79df1ba2010-12-06 12:52:08 -05002967#ifdef CONFIG_CIFS_ACL
2968/*
2969 * Initialize NT TRANSACT SMB into small smb request buffer. This assumes that
2970 * all NT TRANSACTS that we init here have total parm and data under about 400
2971 * bytes (to fit in small cifs buffer size), which is the case so far, it
2972 * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of
2973 * returned setup area) and MaxParameterCount (returned parms size) must be set
2974 * by caller
2975 */
2976static int
2977smb_init_nttransact(const __u16 sub_command, const int setup_count,
2978 const int parm_len, struct cifsTconInfo *tcon,
2979 void **ret_buf)
2980{
2981 int rc;
2982 __u32 temp_offset;
2983 struct smb_com_ntransact_req *pSMB;
2984
2985 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
2986 (void **)&pSMB);
2987 if (rc)
2988 return rc;
2989 *ret_buf = (void *)pSMB;
2990 pSMB->Reserved = 0;
2991 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
2992 pSMB->TotalDataCount = 0;
2993 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2994 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
2995 pSMB->ParameterCount = pSMB->TotalParameterCount;
2996 pSMB->DataCount = pSMB->TotalDataCount;
2997 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
2998 (setup_count * 2) - 4 /* for rfc1001 length itself */;
2999 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
3000 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
3001 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
3002 pSMB->SubCommand = cpu_to_le16(sub_command);
3003 return 0;
3004}
3005
3006static int
3007validate_ntransact(char *buf, char **ppparm, char **ppdata,
3008 __u32 *pparmlen, __u32 *pdatalen)
3009{
3010 char *end_of_smb;
3011 __u32 data_count, data_offset, parm_count, parm_offset;
3012 struct smb_com_ntransact_rsp *pSMBr;
3013
3014 *pdatalen = 0;
3015 *pparmlen = 0;
3016
3017 if (buf == NULL)
3018 return -EINVAL;
3019
3020 pSMBr = (struct smb_com_ntransact_rsp *)buf;
3021
3022 /* ByteCount was converted from little endian in SendReceive */
3023 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
3024 (char *)&pSMBr->ByteCount;
3025
3026 data_offset = le32_to_cpu(pSMBr->DataOffset);
3027 data_count = le32_to_cpu(pSMBr->DataCount);
3028 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
3029 parm_count = le32_to_cpu(pSMBr->ParameterCount);
3030
3031 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
3032 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
3033
3034 /* should we also check that parm and data areas do not overlap? */
3035 if (*ppparm > end_of_smb) {
3036 cFYI(1, "parms start after end of smb");
3037 return -EINVAL;
3038 } else if (parm_count + *ppparm > end_of_smb) {
3039 cFYI(1, "parm end after end of smb");
3040 return -EINVAL;
3041 } else if (*ppdata > end_of_smb) {
3042 cFYI(1, "data starts after end of smb");
3043 return -EINVAL;
3044 } else if (data_count + *ppdata > end_of_smb) {
3045 cFYI(1, "data %p + count %d (%p) past smb end %p start %p",
3046 *ppdata, data_count, (data_count + *ppdata),
3047 end_of_smb, pSMBr);
3048 return -EINVAL;
3049 } else if (parm_count + data_count > pSMBr->ByteCount) {
3050 cFYI(1, "parm count and data count larger than SMB");
3051 return -EINVAL;
3052 }
3053 *pdatalen = data_count;
3054 *pparmlen = parm_count;
3055 return 0;
3056}
3057
Steve French0a4b92c2006-01-12 15:44:21 -08003058/* Get Security Descriptor (by handle) from remote server for a file or dir */
3059int
3060CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f0c2007-10-25 21:17:17 +00003061 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003062{
3063 int rc = 0;
3064 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003065 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003066 struct kvec iov[1];
3067
Joe Perchesb6b38f72010-04-21 03:50:45 +00003068 cFYI(1, "GetCifsACL");
Steve French0a4b92c2006-01-12 15:44:21 -08003069
Steve French630f3f0c2007-10-25 21:17:17 +00003070 *pbuflen = 0;
3071 *acl_inf = NULL;
3072
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003073 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003074 8 /* parm len */, tcon, (void **) &pSMB);
3075 if (rc)
3076 return rc;
3077
3078 pSMB->MaxParameterCount = cpu_to_le32(4);
3079 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3080 pSMB->MaxSetupCount = 0;
3081 pSMB->Fid = fid; /* file handle always le */
3082 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3083 CIFS_ACL_DACL);
3084 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3085 pSMB->hdr.smb_buf_length += 11;
3086 iov[0].iov_base = (char *)pSMB;
3087 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3088
Steve Frencha761ac52007-10-18 21:45:27 +00003089 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Steve French133672e2007-11-13 22:41:37 +00003090 CIFS_STD_OP);
Steve French0a4b92c2006-01-12 15:44:21 -08003091 cifs_stats_inc(&tcon->num_acl_get);
3092 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003093 cFYI(1, "Send error in QuerySecDesc = %d", rc);
Steve French0a4b92c2006-01-12 15:44:21 -08003094 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003095 __le32 *parm;
Steve French630f3f0c2007-10-25 21:17:17 +00003096 __u32 parm_len;
3097 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003098 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f0c2007-10-25 21:17:17 +00003099 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003100
3101/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003102 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f0c2007-10-25 21:17:17 +00003103 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003104 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003105 goto qsec_out;
3106 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3107
Joe Perchesb6b38f72010-04-21 03:50:45 +00003108 cFYI(1, "smb %p parm %p data %p", pSMBr, parm, *acl_inf);
Steve French0a4b92c2006-01-12 15:44:21 -08003109
3110 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3111 rc = -EIO; /* bad smb */
Steve French630f3f0c2007-10-25 21:17:17 +00003112 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003113 goto qsec_out;
3114 }
3115
3116/* BB check that data area is minimum length and as big as acl_len */
3117
Steve Frenchaf6f4612007-10-16 18:40:37 +00003118 acl_len = le32_to_cpu(*parm);
Steve French630f3f0c2007-10-25 21:17:17 +00003119 if (acl_len != *pbuflen) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003120 cERROR(1, "acl length %d does not match %d",
3121 acl_len, *pbuflen);
Steve French630f3f0c2007-10-25 21:17:17 +00003122 if (*pbuflen > acl_len)
3123 *pbuflen = acl_len;
3124 }
Steve French0a4b92c2006-01-12 15:44:21 -08003125
Steve French630f3f0c2007-10-25 21:17:17 +00003126 /* check if buffer is big enough for the acl
3127 header followed by the smallest SID */
3128 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3129 (*pbuflen >= 64 * 1024)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003130 cERROR(1, "bad acl length %d", *pbuflen);
Steve French630f3f0c2007-10-25 21:17:17 +00003131 rc = -EINVAL;
3132 *pbuflen = 0;
3133 } else {
3134 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3135 if (*acl_inf == NULL) {
3136 *pbuflen = 0;
3137 rc = -ENOMEM;
3138 }
3139 memcpy(*acl_inf, pdata, *pbuflen);
3140 }
Steve French0a4b92c2006-01-12 15:44:21 -08003141 }
3142qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003143 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003144 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003145 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003146 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003147/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003148 return rc;
3149}
Steve French97837582007-12-31 07:47:21 +00003150
3151int
3152CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3153 struct cifs_ntsd *pntsd, __u32 acllen)
3154{
3155 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3156 int rc = 0;
3157 int bytes_returned = 0;
3158 SET_SEC_DESC_REQ *pSMB = NULL;
3159 NTRANSACT_RSP *pSMBr = NULL;
3160
3161setCifsAclRetry:
3162 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3163 (void **) &pSMBr);
3164 if (rc)
3165 return (rc);
3166
3167 pSMB->MaxSetupCount = 0;
3168 pSMB->Reserved = 0;
3169
3170 param_count = 8;
3171 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3172 data_count = acllen;
3173 data_offset = param_offset + param_count;
3174 byte_count = 3 /* pad */ + param_count;
3175
3176 pSMB->DataCount = cpu_to_le32(data_count);
3177 pSMB->TotalDataCount = pSMB->DataCount;
3178 pSMB->MaxParameterCount = cpu_to_le32(4);
3179 pSMB->MaxDataCount = cpu_to_le32(16384);
3180 pSMB->ParameterCount = cpu_to_le32(param_count);
3181 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3182 pSMB->TotalParameterCount = pSMB->ParameterCount;
3183 pSMB->DataOffset = cpu_to_le32(data_offset);
3184 pSMB->SetupCount = 0;
3185 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3186 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3187
3188 pSMB->Fid = fid; /* file handle always le */
3189 pSMB->Reserved2 = 0;
3190 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3191
3192 if (pntsd && acllen) {
3193 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3194 (char *) pntsd,
3195 acllen);
3196 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3197
3198 } else
3199 pSMB->hdr.smb_buf_length += byte_count;
3200
3201 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3202 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3203
Joe Perchesb6b38f72010-04-21 03:50:45 +00003204 cFYI(1, "SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc);
Steve French97837582007-12-31 07:47:21 +00003205 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003206 cFYI(1, "Set CIFS ACL returned %d", rc);
Steve French97837582007-12-31 07:47:21 +00003207 cifs_buf_release(pSMB);
3208
3209 if (rc == -EAGAIN)
3210 goto setCifsAclRetry;
3211
3212 return (rc);
3213}
3214
Jeff Layton79df1ba2010-12-06 12:52:08 -05003215#endif /* CONFIG_CIFS_ACL */
Steve French0a4b92c2006-01-12 15:44:21 -08003216
Steve French6b8edfe2005-08-23 20:26:03 -07003217/* Legacy Query Path Information call for lookup to old servers such
3218 as Win9x/WinME */
3219int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003220 const unsigned char *searchName,
3221 FILE_ALL_INFO *pFinfo,
3222 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003223{
Steve Frenchad7a2922008-02-07 23:25:02 +00003224 QUERY_INFORMATION_REQ *pSMB;
3225 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003226 int rc = 0;
3227 int bytes_returned;
3228 int name_len;
3229
Joe Perchesb6b38f72010-04-21 03:50:45 +00003230 cFYI(1, "In SMBQPath path %s", searchName);
Steve French6b8edfe2005-08-23 20:26:03 -07003231QInfRetry:
3232 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003233 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003234 if (rc)
3235 return rc;
3236
3237 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3238 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003239 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3240 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003241 name_len++; /* trailing null */
3242 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003243 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003244 name_len = strnlen(searchName, PATH_MAX);
3245 name_len++; /* trailing null */
3246 strncpy(pSMB->FileName, searchName, name_len);
3247 }
3248 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003249 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003250 pSMB->hdr.smb_buf_length += (__u16) name_len;
3251 pSMB->ByteCount = cpu_to_le16(name_len);
3252
3253 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003254 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003255 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003256 cFYI(1, "Send error in QueryInfo = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003257 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003258 struct timespec ts;
3259 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003260
3261 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003262 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003263 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003264 ts.tv_nsec = 0;
3265 ts.tv_sec = time;
3266 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003267 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003268 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3269 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003270 pFinfo->AllocationSize =
3271 cpu_to_le64(le32_to_cpu(pSMBr->size));
3272 pFinfo->EndOfFile = pFinfo->AllocationSize;
3273 pFinfo->Attributes =
3274 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003275 } else
3276 rc = -EIO; /* bad buffer passed in */
3277
3278 cifs_buf_release(pSMB);
3279
3280 if (rc == -EAGAIN)
3281 goto QInfRetry;
3282
3283 return rc;
3284}
3285
Jeff Laytonbcd53572010-02-12 07:44:16 -05003286int
3287CIFSSMBQFileInfo(const int xid, struct cifsTconInfo *tcon,
3288 u16 netfid, FILE_ALL_INFO *pFindData)
3289{
3290 struct smb_t2_qfi_req *pSMB = NULL;
3291 struct smb_t2_qfi_rsp *pSMBr = NULL;
3292 int rc = 0;
3293 int bytes_returned;
3294 __u16 params, byte_count;
Steve French6b8edfe2005-08-23 20:26:03 -07003295
Jeff Laytonbcd53572010-02-12 07:44:16 -05003296QFileInfoRetry:
3297 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3298 (void **) &pSMBr);
3299 if (rc)
3300 return rc;
Steve French6b8edfe2005-08-23 20:26:03 -07003301
Jeff Laytonbcd53572010-02-12 07:44:16 -05003302 params = 2 /* level */ + 2 /* fid */;
3303 pSMB->t2.TotalDataCount = 0;
3304 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3305 /* BB find exact max data count below from sess structure BB */
3306 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3307 pSMB->t2.MaxSetupCount = 0;
3308 pSMB->t2.Reserved = 0;
3309 pSMB->t2.Flags = 0;
3310 pSMB->t2.Timeout = 0;
3311 pSMB->t2.Reserved2 = 0;
3312 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3313 Fid) - 4);
3314 pSMB->t2.DataCount = 0;
3315 pSMB->t2.DataOffset = 0;
3316 pSMB->t2.SetupCount = 1;
3317 pSMB->t2.Reserved3 = 0;
3318 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3319 byte_count = params + 1 /* pad */ ;
3320 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3321 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3322 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
3323 pSMB->Pad = 0;
3324 pSMB->Fid = netfid;
3325 pSMB->hdr.smb_buf_length += byte_count;
3326
3327 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3328 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3329 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003330 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonbcd53572010-02-12 07:44:16 -05003331 } else { /* decode response */
3332 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3333
3334 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3335 rc = -EIO;
3336 else if (pSMBr->ByteCount < 40)
3337 rc = -EIO; /* bad smb */
3338 else if (pFindData) {
3339 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3340 memcpy((char *) pFindData,
3341 (char *) &pSMBr->hdr.Protocol +
3342 data_offset, sizeof(FILE_ALL_INFO));
3343 } else
3344 rc = -ENOMEM;
3345 }
3346 cifs_buf_release(pSMB);
3347 if (rc == -EAGAIN)
3348 goto QFileInfoRetry;
3349
3350 return rc;
3351}
Steve French6b8edfe2005-08-23 20:26:03 -07003352
Linus Torvalds1da177e2005-04-16 15:20:36 -07003353int
3354CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3355 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003356 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003357 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003358 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003359{
3360/* level 263 SMB_QUERY_FILE_ALL_INFO */
3361 TRANSACTION2_QPI_REQ *pSMB = NULL;
3362 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3363 int rc = 0;
3364 int bytes_returned;
3365 int name_len;
3366 __u16 params, byte_count;
3367
Joe Perchesb6b38f72010-04-21 03:50:45 +00003368/* cFYI(1, "In QPathInfo path %s", searchName); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003369QPathInfoRetry:
3370 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3371 (void **) &pSMBr);
3372 if (rc)
3373 return rc;
3374
3375 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3376 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003377 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003378 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003379 name_len++; /* trailing null */
3380 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003381 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003382 name_len = strnlen(searchName, PATH_MAX);
3383 name_len++; /* trailing null */
3384 strncpy(pSMB->FileName, searchName, name_len);
3385 }
3386
Steve French50c2f752007-07-13 00:33:32 +00003387 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388 pSMB->TotalDataCount = 0;
3389 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00003390 /* BB find exact max SMB PDU from sess structure BB */
3391 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003392 pSMB->MaxSetupCount = 0;
3393 pSMB->Reserved = 0;
3394 pSMB->Flags = 0;
3395 pSMB->Timeout = 0;
3396 pSMB->Reserved2 = 0;
3397 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003398 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003399 pSMB->DataCount = 0;
3400 pSMB->DataOffset = 0;
3401 pSMB->SetupCount = 1;
3402 pSMB->Reserved3 = 0;
3403 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3404 byte_count = params + 1 /* pad */ ;
3405 pSMB->TotalParameterCount = cpu_to_le16(params);
3406 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003407 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003408 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3409 else
3410 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003411 pSMB->Reserved4 = 0;
3412 pSMB->hdr.smb_buf_length += byte_count;
3413 pSMB->ByteCount = cpu_to_le16(byte_count);
3414
3415 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3416 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3417 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003418 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003419 } else { /* decode response */
3420 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3421
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003422 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3423 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003424 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003425 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003426 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003427 rc = -EIO; /* 24 or 26 expected but we do not read
3428 last field */
3429 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003430 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003431 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003432
3433 /* On legacy responses we do not read the last field,
3434 EAsize, fortunately since it varies by subdialect and
3435 also note it differs on Set vs. Get, ie two bytes or 4
3436 bytes depending but we don't care here */
3437 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003438 size = sizeof(FILE_INFO_STANDARD);
3439 else
3440 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003441 memcpy((char *) pFindData,
3442 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003443 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003444 } else
3445 rc = -ENOMEM;
3446 }
3447 cifs_buf_release(pSMB);
3448 if (rc == -EAGAIN)
3449 goto QPathInfoRetry;
3450
3451 return rc;
3452}
3453
3454int
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003455CIFSSMBUnixQFileInfo(const int xid, struct cifsTconInfo *tcon,
3456 u16 netfid, FILE_UNIX_BASIC_INFO *pFindData)
3457{
3458 struct smb_t2_qfi_req *pSMB = NULL;
3459 struct smb_t2_qfi_rsp *pSMBr = NULL;
3460 int rc = 0;
3461 int bytes_returned;
3462 __u16 params, byte_count;
3463
3464UnixQFileInfoRetry:
3465 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3466 (void **) &pSMBr);
3467 if (rc)
3468 return rc;
3469
3470 params = 2 /* level */ + 2 /* fid */;
3471 pSMB->t2.TotalDataCount = 0;
3472 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3473 /* BB find exact max data count below from sess structure BB */
3474 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3475 pSMB->t2.MaxSetupCount = 0;
3476 pSMB->t2.Reserved = 0;
3477 pSMB->t2.Flags = 0;
3478 pSMB->t2.Timeout = 0;
3479 pSMB->t2.Reserved2 = 0;
3480 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3481 Fid) - 4);
3482 pSMB->t2.DataCount = 0;
3483 pSMB->t2.DataOffset = 0;
3484 pSMB->t2.SetupCount = 1;
3485 pSMB->t2.Reserved3 = 0;
3486 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3487 byte_count = params + 1 /* pad */ ;
3488 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3489 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3490 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3491 pSMB->Pad = 0;
3492 pSMB->Fid = netfid;
3493 pSMB->hdr.smb_buf_length += byte_count;
3494
3495 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3496 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3497 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003498 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003499 } else { /* decode response */
3500 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3501
3502 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003503 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003504 "Unix Extensions can be disabled on mount "
Steve Frenchf19159d2010-04-21 04:12:10 +00003505 "by specifying the nosfu mount option.");
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003506 rc = -EIO; /* bad smb */
3507 } else {
3508 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3509 memcpy((char *) pFindData,
3510 (char *) &pSMBr->hdr.Protocol +
3511 data_offset,
3512 sizeof(FILE_UNIX_BASIC_INFO));
3513 }
3514 }
3515
3516 cifs_buf_release(pSMB);
3517 if (rc == -EAGAIN)
3518 goto UnixQFileInfoRetry;
3519
3520 return rc;
3521}
3522
3523int
Linus Torvalds1da177e2005-04-16 15:20:36 -07003524CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3525 const unsigned char *searchName,
Steve French582d21e2008-05-13 04:54:12 +00003526 FILE_UNIX_BASIC_INFO *pFindData,
Steve French737b7582005-04-28 22:41:06 -07003527 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003528{
3529/* SMB_QUERY_FILE_UNIX_BASIC */
3530 TRANSACTION2_QPI_REQ *pSMB = NULL;
3531 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3532 int rc = 0;
3533 int bytes_returned = 0;
3534 int name_len;
3535 __u16 params, byte_count;
3536
Joe Perchesb6b38f72010-04-21 03:50:45 +00003537 cFYI(1, "In QPathInfo (Unix) the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003538UnixQPathInfoRetry:
3539 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3540 (void **) &pSMBr);
3541 if (rc)
3542 return rc;
3543
3544 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3545 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003546 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003547 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003548 name_len++; /* trailing null */
3549 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003550 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003551 name_len = strnlen(searchName, PATH_MAX);
3552 name_len++; /* trailing null */
3553 strncpy(pSMB->FileName, searchName, name_len);
3554 }
3555
Steve French50c2f752007-07-13 00:33:32 +00003556 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003557 pSMB->TotalDataCount = 0;
3558 pSMB->MaxParameterCount = cpu_to_le16(2);
3559 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003560 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003561 pSMB->MaxSetupCount = 0;
3562 pSMB->Reserved = 0;
3563 pSMB->Flags = 0;
3564 pSMB->Timeout = 0;
3565 pSMB->Reserved2 = 0;
3566 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003567 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003568 pSMB->DataCount = 0;
3569 pSMB->DataOffset = 0;
3570 pSMB->SetupCount = 1;
3571 pSMB->Reserved3 = 0;
3572 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3573 byte_count = params + 1 /* pad */ ;
3574 pSMB->TotalParameterCount = cpu_to_le16(params);
3575 pSMB->ParameterCount = pSMB->TotalParameterCount;
3576 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3577 pSMB->Reserved4 = 0;
3578 pSMB->hdr.smb_buf_length += byte_count;
3579 pSMB->ByteCount = cpu_to_le16(byte_count);
3580
3581 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3582 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3583 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003584 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003585 } else { /* decode response */
3586 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3587
3588 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003589 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Steve French1e71f252007-09-20 15:30:07 +00003590 "Unix Extensions can be disabled on mount "
Joe Perchesb6b38f72010-04-21 03:50:45 +00003591 "by specifying the nosfu mount option.");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003592 rc = -EIO; /* bad smb */
3593 } else {
3594 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3595 memcpy((char *) pFindData,
3596 (char *) &pSMBr->hdr.Protocol +
3597 data_offset,
Steve French630f3f0c2007-10-25 21:17:17 +00003598 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003599 }
3600 }
3601 cifs_buf_release(pSMB);
3602 if (rc == -EAGAIN)
3603 goto UnixQPathInfoRetry;
3604
3605 return rc;
3606}
3607
Linus Torvalds1da177e2005-04-16 15:20:36 -07003608/* xid, tcon, searchName and codepage are input parms, rest are returned */
3609int
3610CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003611 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003612 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003613 __u16 *pnetfid,
3614 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003615{
3616/* level 257 SMB_ */
3617 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3618 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003619 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003620 int rc = 0;
3621 int bytes_returned = 0;
3622 int name_len;
3623 __u16 params, byte_count;
3624
Joe Perchesb6b38f72010-04-21 03:50:45 +00003625 cFYI(1, "In FindFirst for %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003626
3627findFirstRetry:
3628 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3629 (void **) &pSMBr);
3630 if (rc)
3631 return rc;
3632
3633 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3634 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003635 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003636 PATH_MAX, nls_codepage, remap);
3637 /* We can not add the asterik earlier in case
3638 it got remapped to 0xF03A as if it were part of the
3639 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003640 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003641 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003642 pSMB->FileName[name_len+1] = 0;
3643 pSMB->FileName[name_len+2] = '*';
3644 pSMB->FileName[name_len+3] = 0;
3645 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003646 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3647 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003648 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003649 } else { /* BB add check for overrun of SMB buf BB */
3650 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003651/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003652 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003653 free buffer exit; BB */
3654 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003655 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003656 pSMB->FileName[name_len+1] = '*';
3657 pSMB->FileName[name_len+2] = 0;
3658 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003659 }
3660
3661 params = 12 + name_len /* includes null */ ;
3662 pSMB->TotalDataCount = 0; /* no EAs */
3663 pSMB->MaxParameterCount = cpu_to_le16(10);
3664 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3665 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3666 pSMB->MaxSetupCount = 0;
3667 pSMB->Reserved = 0;
3668 pSMB->Flags = 0;
3669 pSMB->Timeout = 0;
3670 pSMB->Reserved2 = 0;
3671 byte_count = params + 1 /* pad */ ;
3672 pSMB->TotalParameterCount = cpu_to_le16(params);
3673 pSMB->ParameterCount = pSMB->TotalParameterCount;
3674 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003675 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3676 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003677 pSMB->DataCount = 0;
3678 pSMB->DataOffset = 0;
3679 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3680 pSMB->Reserved3 = 0;
3681 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3682 pSMB->SearchAttributes =
3683 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3684 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003685 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3686 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003687 CIFS_SEARCH_RETURN_RESUME);
3688 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3689
3690 /* BB what should we set StorageType to? Does it matter? BB */
3691 pSMB->SearchStorageType = 0;
3692 pSMB->hdr.smb_buf_length += byte_count;
3693 pSMB->ByteCount = cpu_to_le16(byte_count);
3694
3695 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3696 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003697 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003698
Steve French88274812006-03-09 22:21:45 +00003699 if (rc) {/* BB add logic to retry regular search if Unix search
3700 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003701 /* BB Add code to handle unsupported level rc */
Joe Perchesb6b38f72010-04-21 03:50:45 +00003702 cFYI(1, "Error in FindFirst = %d", rc);
Steve French1982c342005-08-17 12:38:22 -07003703
Steve French88274812006-03-09 22:21:45 +00003704 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003705
3706 /* BB eventually could optimize out free and realloc of buf */
3707 /* for this case */
3708 if (rc == -EAGAIN)
3709 goto findFirstRetry;
3710 } else { /* decode response */
3711 /* BB remember to free buffer if error BB */
3712 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003713 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003714 unsigned int lnoff;
3715
Linus Torvalds1da177e2005-04-16 15:20:36 -07003716 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003717 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003718 else
Steve French4b18f2a2008-04-29 00:06:05 +00003719 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003720
3721 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003722 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003723 psrch_inf->srch_entries_start =
3724 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003725 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003726 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3727 le16_to_cpu(pSMBr->t2.ParameterOffset));
3728
Steve French790fe572007-07-07 19:25:05 +00003729 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003730 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003731 else
Steve French4b18f2a2008-04-29 00:06:05 +00003732 psrch_inf->endOfSearch = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733
Steve French50c2f752007-07-13 00:33:32 +00003734 psrch_inf->entries_in_buffer =
3735 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003736 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003737 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003738 lnoff = le16_to_cpu(parms->LastNameOffset);
3739 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3740 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003741 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003742 psrch_inf->last_entry = NULL;
3743 return rc;
3744 }
3745
Steve French0752f152008-10-07 20:03:33 +00003746 psrch_inf->last_entry = psrch_inf->srch_entries_start +
Steve Frenchb77d7532008-10-08 19:13:46 +00003747 lnoff;
3748
Linus Torvalds1da177e2005-04-16 15:20:36 -07003749 *pnetfid = parms->SearchHandle;
3750 } else {
3751 cifs_buf_release(pSMB);
3752 }
3753 }
3754
3755 return rc;
3756}
3757
3758int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003759 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003760{
3761 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3762 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003763 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003764 char *response_data;
3765 int rc = 0;
3766 int bytes_returned, name_len;
3767 __u16 params, byte_count;
3768
Joe Perchesb6b38f72010-04-21 03:50:45 +00003769 cFYI(1, "In FindNext");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003770
Steve French4b18f2a2008-04-29 00:06:05 +00003771 if (psrch_inf->endOfSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003772 return -ENOENT;
3773
3774 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3775 (void **) &pSMBr);
3776 if (rc)
3777 return rc;
3778
Steve French50c2f752007-07-13 00:33:32 +00003779 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003780 byte_count = 0;
3781 pSMB->TotalDataCount = 0; /* no EAs */
3782 pSMB->MaxParameterCount = cpu_to_le16(8);
3783 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003784 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3785 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003786 pSMB->MaxSetupCount = 0;
3787 pSMB->Reserved = 0;
3788 pSMB->Flags = 0;
3789 pSMB->Timeout = 0;
3790 pSMB->Reserved2 = 0;
3791 pSMB->ParameterOffset = cpu_to_le16(
3792 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3793 pSMB->DataCount = 0;
3794 pSMB->DataOffset = 0;
3795 pSMB->SetupCount = 1;
3796 pSMB->Reserved3 = 0;
3797 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3798 pSMB->SearchHandle = searchHandle; /* always kept as le */
3799 pSMB->SearchCount =
Steve French630f3f0c2007-10-25 21:17:17 +00003800 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003801 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3802 pSMB->ResumeKey = psrch_inf->resume_key;
3803 pSMB->SearchFlags =
3804 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3805
3806 name_len = psrch_inf->resume_name_len;
3807 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003808 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003809 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3810 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003811 /* 14 byte parm len above enough for 2 byte null terminator */
3812 pSMB->ResumeFileName[name_len] = 0;
3813 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003814 } else {
3815 rc = -EINVAL;
3816 goto FNext2_err_exit;
3817 }
3818 byte_count = params + 1 /* pad */ ;
3819 pSMB->TotalParameterCount = cpu_to_le16(params);
3820 pSMB->ParameterCount = pSMB->TotalParameterCount;
3821 pSMB->hdr.smb_buf_length += byte_count;
3822 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003823
Linus Torvalds1da177e2005-04-16 15:20:36 -07003824 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3825 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003826 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003827 if (rc) {
3828 if (rc == -EBADF) {
Steve French4b18f2a2008-04-29 00:06:05 +00003829 psrch_inf->endOfSearch = true;
Jeff Layton63534502008-05-12 19:56:05 -07003830 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00003831 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003832 } else
Joe Perchesb6b38f72010-04-21 03:50:45 +00003833 cFYI(1, "FindNext returned = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003834 } else { /* decode response */
3835 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003836
Steve French790fe572007-07-07 19:25:05 +00003837 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003838 unsigned int lnoff;
3839
Linus Torvalds1da177e2005-04-16 15:20:36 -07003840 /* BB fixme add lock for file (srch_info) struct here */
3841 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003842 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003843 else
Steve French4b18f2a2008-04-29 00:06:05 +00003844 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003845 response_data = (char *) &pSMBr->hdr.Protocol +
3846 le16_to_cpu(pSMBr->t2.ParameterOffset);
3847 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3848 response_data = (char *)&pSMBr->hdr.Protocol +
3849 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003850 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003851 cifs_small_buf_release(
3852 psrch_inf->ntwrk_buf_start);
3853 else
3854 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003855 psrch_inf->srch_entries_start = response_data;
3856 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003857 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003858 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003859 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003860 else
Steve French4b18f2a2008-04-29 00:06:05 +00003861 psrch_inf->endOfSearch = false;
Steve French50c2f752007-07-13 00:33:32 +00003862 psrch_inf->entries_in_buffer =
3863 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003864 psrch_inf->index_of_last_entry +=
3865 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003866 lnoff = le16_to_cpu(parms->LastNameOffset);
3867 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3868 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003869 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003870 psrch_inf->last_entry = NULL;
3871 return rc;
3872 } else
3873 psrch_inf->last_entry =
3874 psrch_inf->srch_entries_start + lnoff;
3875
Joe Perchesb6b38f72010-04-21 03:50:45 +00003876/* cFYI(1, "fnxt2 entries in buf %d index_of_last %d",
3877 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003878
3879 /* BB fixme add unlock here */
3880 }
3881
3882 }
3883
3884 /* BB On error, should we leave previous search buf (and count and
3885 last entry fields) intact or free the previous one? */
3886
3887 /* Note: On -EAGAIN error only caller can retry on handle based calls
3888 since file handle passed in no longer valid */
3889FNext2_err_exit:
3890 if (rc != 0)
3891 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003892 return rc;
3893}
3894
3895int
Steve French50c2f752007-07-13 00:33:32 +00003896CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3897 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003898{
3899 int rc = 0;
3900 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003901
Joe Perchesb6b38f72010-04-21 03:50:45 +00003902 cFYI(1, "In CIFSSMBFindClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003903 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3904
3905 /* no sense returning error if session restarted
3906 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003907 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003908 return 0;
3909 if (rc)
3910 return rc;
3911
Linus Torvalds1da177e2005-04-16 15:20:36 -07003912 pSMB->FileID = searchHandle;
3913 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003914 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003915 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003916 cERROR(1, "Send error in FindClose = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003917
Steve Frencha4544342005-08-24 13:59:35 -07003918 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003919
3920 /* Since session is dead, search handle closed on server already */
3921 if (rc == -EAGAIN)
3922 rc = 0;
3923
3924 return rc;
3925}
3926
Linus Torvalds1da177e2005-04-16 15:20:36 -07003927int
3928CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003929 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003930 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003931 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003932{
3933 int rc = 0;
3934 TRANSACTION2_QPI_REQ *pSMB = NULL;
3935 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3936 int name_len, bytes_returned;
3937 __u16 params, byte_count;
3938
Joe Perchesb6b38f72010-04-21 03:50:45 +00003939 cFYI(1, "In GetSrvInodeNum for %s", searchName);
Steve French790fe572007-07-07 19:25:05 +00003940 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003941 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003942
3943GetInodeNumberRetry:
3944 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003945 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003946 if (rc)
3947 return rc;
3948
Linus Torvalds1da177e2005-04-16 15:20:36 -07003949 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3950 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003951 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003952 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003953 name_len++; /* trailing null */
3954 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003955 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003956 name_len = strnlen(searchName, PATH_MAX);
3957 name_len++; /* trailing null */
3958 strncpy(pSMB->FileName, searchName, name_len);
3959 }
3960
3961 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
3962 pSMB->TotalDataCount = 0;
3963 pSMB->MaxParameterCount = cpu_to_le16(2);
3964 /* BB find exact max data count below from sess structure BB */
3965 pSMB->MaxDataCount = cpu_to_le16(4000);
3966 pSMB->MaxSetupCount = 0;
3967 pSMB->Reserved = 0;
3968 pSMB->Flags = 0;
3969 pSMB->Timeout = 0;
3970 pSMB->Reserved2 = 0;
3971 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003972 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003973 pSMB->DataCount = 0;
3974 pSMB->DataOffset = 0;
3975 pSMB->SetupCount = 1;
3976 pSMB->Reserved3 = 0;
3977 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3978 byte_count = params + 1 /* pad */ ;
3979 pSMB->TotalParameterCount = cpu_to_le16(params);
3980 pSMB->ParameterCount = pSMB->TotalParameterCount;
3981 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
3982 pSMB->Reserved4 = 0;
3983 pSMB->hdr.smb_buf_length += byte_count;
3984 pSMB->ByteCount = cpu_to_le16(byte_count);
3985
3986 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3987 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3988 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003989 cFYI(1, "error %d in QueryInternalInfo", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003990 } else {
3991 /* decode response */
3992 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3993 if (rc || (pSMBr->ByteCount < 2))
3994 /* BB also check enough total bytes returned */
3995 /* If rc should we check for EOPNOSUPP and
3996 disable the srvino flag? or in caller? */
3997 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00003998 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003999 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4000 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00004001 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004002 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00004003 if (count < 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004004 cFYI(1, "Illegal size ret in QryIntrnlInf");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004005 rc = -EIO;
4006 goto GetInodeNumOut;
4007 }
4008 pfinfo = (struct file_internal_info *)
4009 (data_offset + (char *) &pSMBr->hdr.Protocol);
Steve French85a6dac2009-04-01 05:22:00 +00004010 *inode_number = le64_to_cpu(pfinfo->UniqueId);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004011 }
4012 }
4013GetInodeNumOut:
4014 cifs_buf_release(pSMB);
4015 if (rc == -EAGAIN)
4016 goto GetInodeNumberRetry;
4017 return rc;
4018}
Linus Torvalds1da177e2005-04-16 15:20:36 -07004019
Igor Mammedovfec45852008-05-16 13:06:30 +04004020/* parses DFS refferal V3 structure
4021 * caller is responsible for freeing target_nodes
4022 * returns:
4023 * on success - 0
4024 * on failure - errno
4025 */
4026static int
Steve Frencha1fe78f2008-05-16 18:48:38 +00004027parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
Igor Mammedovfec45852008-05-16 13:06:30 +04004028 unsigned int *num_of_nodes,
4029 struct dfs_info3_param **target_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004030 const struct nls_table *nls_codepage, int remap,
4031 const char *searchName)
Igor Mammedovfec45852008-05-16 13:06:30 +04004032{
4033 int i, rc = 0;
4034 char *data_end;
4035 bool is_unicode;
4036 struct dfs_referral_level_3 *ref;
4037
Harvey Harrison5ca33c62008-07-23 17:45:58 -07004038 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
4039 is_unicode = true;
4040 else
4041 is_unicode = false;
Igor Mammedovfec45852008-05-16 13:06:30 +04004042 *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
4043
4044 if (*num_of_nodes < 1) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004045 cERROR(1, "num_referrals: must be at least > 0,"
4046 "but we get num_referrals = %d\n", *num_of_nodes);
Igor Mammedovfec45852008-05-16 13:06:30 +04004047 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004048 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004049 }
4050
4051 ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
Al Viro1d92cfd2008-06-02 10:59:02 +01004052 if (ref->VersionNumber != cpu_to_le16(3)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004053 cERROR(1, "Referrals of V%d version are not supported,"
4054 "should be V3", le16_to_cpu(ref->VersionNumber));
Igor Mammedovfec45852008-05-16 13:06:30 +04004055 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004056 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004057 }
4058
4059 /* get the upper boundary of the resp buffer */
4060 data_end = (char *)(&(pSMBr->PathConsumed)) +
4061 le16_to_cpu(pSMBr->t2.DataCount);
4062
Steve Frenchf19159d2010-04-21 04:12:10 +00004063 cFYI(1, "num_referrals: %d dfs flags: 0x%x ...\n",
Igor Mammedovfec45852008-05-16 13:06:30 +04004064 *num_of_nodes,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004065 le32_to_cpu(pSMBr->DFSFlags));
Igor Mammedovfec45852008-05-16 13:06:30 +04004066
4067 *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
4068 *num_of_nodes, GFP_KERNEL);
4069 if (*target_nodes == NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004070 cERROR(1, "Failed to allocate buffer for target_nodes\n");
Igor Mammedovfec45852008-05-16 13:06:30 +04004071 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004072 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004073 }
4074
Daniel Mack3ad2f3f2010-02-03 08:01:28 +08004075 /* collect necessary data from referrals */
Igor Mammedovfec45852008-05-16 13:06:30 +04004076 for (i = 0; i < *num_of_nodes; i++) {
4077 char *temp;
4078 int max_len;
4079 struct dfs_info3_param *node = (*target_nodes)+i;
4080
Steve French0e0d2cf2009-05-01 05:27:32 +00004081 node->flags = le32_to_cpu(pSMBr->DFSFlags);
Igor Mammedov2c556082008-10-23 13:58:42 +04004082 if (is_unicode) {
Jeff Layton331c3132008-12-17 06:31:53 -05004083 __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
4084 GFP_KERNEL);
Steve French2920ee22009-08-31 15:27:26 +00004085 if (tmp == NULL) {
4086 rc = -ENOMEM;
4087 goto parse_DFS_referrals_exit;
4088 }
Igor Mammedov2c556082008-10-23 13:58:42 +04004089 cifsConvertToUCS((__le16 *) tmp, searchName,
4090 PATH_MAX, nls_codepage, remap);
Jeff Layton69f801f2009-04-30 06:46:32 -04004091 node->path_consumed = cifs_ucs2_bytes(tmp,
4092 le16_to_cpu(pSMBr->PathConsumed),
Igor Mammedov2c556082008-10-23 13:58:42 +04004093 nls_codepage);
4094 kfree(tmp);
4095 } else
4096 node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
4097
Igor Mammedovfec45852008-05-16 13:06:30 +04004098 node->server_type = le16_to_cpu(ref->ServerType);
4099 node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
4100
4101 /* copy DfsPath */
4102 temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
4103 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004104 node->path_name = cifs_strndup_from_ucs(temp, max_len,
4105 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004106 if (!node->path_name) {
4107 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004108 goto parse_DFS_referrals_exit;
Jeff Layton066ce682009-04-30 07:16:14 -04004109 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004110
4111 /* copy link target UNC */
4112 temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
4113 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004114 node->node_name = cifs_strndup_from_ucs(temp, max_len,
4115 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004116 if (!node->node_name)
4117 rc = -ENOMEM;
Igor Mammedovfec45852008-05-16 13:06:30 +04004118 }
4119
Steve Frencha1fe78f2008-05-16 18:48:38 +00004120parse_DFS_referrals_exit:
Igor Mammedovfec45852008-05-16 13:06:30 +04004121 if (rc) {
4122 free_dfs_info_array(*target_nodes, *num_of_nodes);
4123 *target_nodes = NULL;
4124 *num_of_nodes = 0;
4125 }
4126 return rc;
4127}
4128
Linus Torvalds1da177e2005-04-16 15:20:36 -07004129int
4130CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
4131 const unsigned char *searchName,
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004132 struct dfs_info3_param **target_nodes,
4133 unsigned int *num_of_nodes,
Steve French737b7582005-04-28 22:41:06 -07004134 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004135{
4136/* TRANS2_GET_DFS_REFERRAL */
4137 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
4138 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004139 int rc = 0;
4140 int bytes_returned;
4141 int name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004142 __u16 params, byte_count;
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004143 *num_of_nodes = 0;
4144 *target_nodes = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004145
Joe Perchesb6b38f72010-04-21 03:50:45 +00004146 cFYI(1, "In GetDFSRefer the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004147 if (ses == NULL)
4148 return -ENODEV;
4149getDFSRetry:
4150 rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
4151 (void **) &pSMBr);
4152 if (rc)
4153 return rc;
Steve French50c2f752007-07-13 00:33:32 +00004154
4155 /* server pointer checked in called function,
Steve French1982c342005-08-17 12:38:22 -07004156 but should never be null here anyway */
4157 pSMB->hdr.Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004158 pSMB->hdr.Tid = ses->ipc_tid;
4159 pSMB->hdr.Uid = ses->Suid;
Steve French26f57362007-08-30 22:09:15 +00004160 if (ses->capabilities & CAP_STATUS32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004161 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
Steve French26f57362007-08-30 22:09:15 +00004162 if (ses->capabilities & CAP_DFS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004163 pSMB->hdr.Flags2 |= SMBFLG2_DFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004164
4165 if (ses->capabilities & CAP_UNICODE) {
4166 pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
4167 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004168 cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
Steve French737b7582005-04-28 22:41:06 -07004169 searchName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004170 name_len++; /* trailing null */
4171 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004172 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004173 name_len = strnlen(searchName, PATH_MAX);
4174 name_len++; /* trailing null */
4175 strncpy(pSMB->RequestFileName, searchName, name_len);
4176 }
4177
Steve French790fe572007-07-07 19:25:05 +00004178 if (ses->server) {
4179 if (ses->server->secMode &
Steve French1a4e15a2006-10-12 21:33:51 +00004180 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
4181 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
4182 }
4183
Steve French50c2f752007-07-13 00:33:32 +00004184 pSMB->hdr.Uid = ses->Suid;
Steve French1a4e15a2006-10-12 21:33:51 +00004185
Linus Torvalds1da177e2005-04-16 15:20:36 -07004186 params = 2 /* level */ + name_len /*includes null */ ;
4187 pSMB->TotalDataCount = 0;
4188 pSMB->DataCount = 0;
4189 pSMB->DataOffset = 0;
4190 pSMB->MaxParameterCount = 0;
Steve French582d21e2008-05-13 04:54:12 +00004191 /* BB find exact max SMB PDU from sess structure BB */
4192 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004193 pSMB->MaxSetupCount = 0;
4194 pSMB->Reserved = 0;
4195 pSMB->Flags = 0;
4196 pSMB->Timeout = 0;
4197 pSMB->Reserved2 = 0;
4198 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004199 struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004200 pSMB->SetupCount = 1;
4201 pSMB->Reserved3 = 0;
4202 pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
4203 byte_count = params + 3 /* pad */ ;
4204 pSMB->ParameterCount = cpu_to_le16(params);
4205 pSMB->TotalParameterCount = pSMB->ParameterCount;
4206 pSMB->MaxReferralLevel = cpu_to_le16(3);
4207 pSMB->hdr.smb_buf_length += byte_count;
4208 pSMB->ByteCount = cpu_to_le16(byte_count);
4209
4210 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
4211 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4212 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004213 cFYI(1, "Send error in GetDFSRefer = %d", rc);
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004214 goto GetDFSRefExit;
4215 }
4216 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004217
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004218 /* BB Also check if enough total bytes returned? */
Igor Mammedovfec45852008-05-16 13:06:30 +04004219 if (rc || (pSMBr->ByteCount < 17)) {
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004220 rc = -EIO; /* bad smb */
Igor Mammedovfec45852008-05-16 13:06:30 +04004221 goto GetDFSRefExit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004222 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004223
Joe Perchesb6b38f72010-04-21 03:50:45 +00004224 cFYI(1, "Decoding GetDFSRefer response BCC: %d Offset %d",
Igor Mammedovfec45852008-05-16 13:06:30 +04004225 pSMBr->ByteCount,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004226 le16_to_cpu(pSMBr->t2.DataOffset));
Igor Mammedovfec45852008-05-16 13:06:30 +04004227
4228 /* parse returned result into more usable form */
Steve Frencha1fe78f2008-05-16 18:48:38 +00004229 rc = parse_DFS_referrals(pSMBr, num_of_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004230 target_nodes, nls_codepage, remap,
4231 searchName);
Igor Mammedovfec45852008-05-16 13:06:30 +04004232
Linus Torvalds1da177e2005-04-16 15:20:36 -07004233GetDFSRefExit:
Steve French0d817bc2008-05-22 02:02:03 +00004234 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004235
4236 if (rc == -EAGAIN)
4237 goto getDFSRetry;
4238
4239 return rc;
4240}
4241
Steve French20962432005-09-21 22:05:57 -07004242/* Query File System Info such as free space to old servers such as Win 9x */
4243int
4244SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4245{
4246/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4247 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4248 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4249 FILE_SYSTEM_ALLOC_INFO *response_data;
4250 int rc = 0;
4251 int bytes_returned = 0;
4252 __u16 params, byte_count;
4253
Joe Perchesb6b38f72010-04-21 03:50:45 +00004254 cFYI(1, "OldQFSInfo");
Steve French20962432005-09-21 22:05:57 -07004255oldQFSInfoRetry:
4256 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4257 (void **) &pSMBr);
4258 if (rc)
4259 return rc;
Steve French20962432005-09-21 22:05:57 -07004260
4261 params = 2; /* level */
4262 pSMB->TotalDataCount = 0;
4263 pSMB->MaxParameterCount = cpu_to_le16(2);
4264 pSMB->MaxDataCount = cpu_to_le16(1000);
4265 pSMB->MaxSetupCount = 0;
4266 pSMB->Reserved = 0;
4267 pSMB->Flags = 0;
4268 pSMB->Timeout = 0;
4269 pSMB->Reserved2 = 0;
4270 byte_count = params + 1 /* pad */ ;
4271 pSMB->TotalParameterCount = cpu_to_le16(params);
4272 pSMB->ParameterCount = pSMB->TotalParameterCount;
4273 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4274 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4275 pSMB->DataCount = 0;
4276 pSMB->DataOffset = 0;
4277 pSMB->SetupCount = 1;
4278 pSMB->Reserved3 = 0;
4279 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4280 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4281 pSMB->hdr.smb_buf_length += byte_count;
4282 pSMB->ByteCount = cpu_to_le16(byte_count);
4283
4284 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4285 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4286 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004287 cFYI(1, "Send error in QFSInfo = %d", rc);
Steve French20962432005-09-21 22:05:57 -07004288 } else { /* decode response */
4289 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4290
4291 if (rc || (pSMBr->ByteCount < 18))
4292 rc = -EIO; /* bad smb */
4293 else {
4294 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004295 cFYI(1, "qfsinf resp BCC: %d Offset %d",
4296 pSMBr->ByteCount, data_offset);
Steve French20962432005-09-21 22:05:57 -07004297
Steve French50c2f752007-07-13 00:33:32 +00004298 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004299 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4300 FSData->f_bsize =
4301 le16_to_cpu(response_data->BytesPerSector) *
4302 le32_to_cpu(response_data->
4303 SectorsPerAllocationUnit);
4304 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004305 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004306 FSData->f_bfree = FSData->f_bavail =
4307 le32_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004308 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4309 (unsigned long long)FSData->f_blocks,
4310 (unsigned long long)FSData->f_bfree,
4311 FSData->f_bsize);
Steve French20962432005-09-21 22:05:57 -07004312 }
4313 }
4314 cifs_buf_release(pSMB);
4315
4316 if (rc == -EAGAIN)
4317 goto oldQFSInfoRetry;
4318
4319 return rc;
4320}
4321
Linus Torvalds1da177e2005-04-16 15:20:36 -07004322int
Steve French737b7582005-04-28 22:41:06 -07004323CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004324{
4325/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4326 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4327 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4328 FILE_SYSTEM_INFO *response_data;
4329 int rc = 0;
4330 int bytes_returned = 0;
4331 __u16 params, byte_count;
4332
Joe Perchesb6b38f72010-04-21 03:50:45 +00004333 cFYI(1, "In QFSInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004334QFSInfoRetry:
4335 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4336 (void **) &pSMBr);
4337 if (rc)
4338 return rc;
4339
4340 params = 2; /* level */
4341 pSMB->TotalDataCount = 0;
4342 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004343 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004344 pSMB->MaxSetupCount = 0;
4345 pSMB->Reserved = 0;
4346 pSMB->Flags = 0;
4347 pSMB->Timeout = 0;
4348 pSMB->Reserved2 = 0;
4349 byte_count = params + 1 /* pad */ ;
4350 pSMB->TotalParameterCount = cpu_to_le16(params);
4351 pSMB->ParameterCount = pSMB->TotalParameterCount;
4352 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004353 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004354 pSMB->DataCount = 0;
4355 pSMB->DataOffset = 0;
4356 pSMB->SetupCount = 1;
4357 pSMB->Reserved3 = 0;
4358 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4359 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4360 pSMB->hdr.smb_buf_length += byte_count;
4361 pSMB->ByteCount = cpu_to_le16(byte_count);
4362
4363 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4364 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4365 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004366 cFYI(1, "Send error in QFSInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004367 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004368 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004369
Steve French20962432005-09-21 22:05:57 -07004370 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004371 rc = -EIO; /* bad smb */
4372 else {
4373 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004374
4375 response_data =
4376 (FILE_SYSTEM_INFO
4377 *) (((char *) &pSMBr->hdr.Protocol) +
4378 data_offset);
4379 FSData->f_bsize =
4380 le32_to_cpu(response_data->BytesPerSector) *
4381 le32_to_cpu(response_data->
4382 SectorsPerAllocationUnit);
4383 FSData->f_blocks =
4384 le64_to_cpu(response_data->TotalAllocationUnits);
4385 FSData->f_bfree = FSData->f_bavail =
4386 le64_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004387 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4388 (unsigned long long)FSData->f_blocks,
4389 (unsigned long long)FSData->f_bfree,
4390 FSData->f_bsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004391 }
4392 }
4393 cifs_buf_release(pSMB);
4394
4395 if (rc == -EAGAIN)
4396 goto QFSInfoRetry;
4397
4398 return rc;
4399}
4400
4401int
Steve French737b7582005-04-28 22:41:06 -07004402CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004403{
4404/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4405 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4406 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4407 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4408 int rc = 0;
4409 int bytes_returned = 0;
4410 __u16 params, byte_count;
4411
Joe Perchesb6b38f72010-04-21 03:50:45 +00004412 cFYI(1, "In QFSAttributeInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004413QFSAttributeRetry:
4414 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4415 (void **) &pSMBr);
4416 if (rc)
4417 return rc;
4418
4419 params = 2; /* level */
4420 pSMB->TotalDataCount = 0;
4421 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004422 /* BB find exact max SMB PDU from sess structure BB */
4423 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004424 pSMB->MaxSetupCount = 0;
4425 pSMB->Reserved = 0;
4426 pSMB->Flags = 0;
4427 pSMB->Timeout = 0;
4428 pSMB->Reserved2 = 0;
4429 byte_count = params + 1 /* pad */ ;
4430 pSMB->TotalParameterCount = cpu_to_le16(params);
4431 pSMB->ParameterCount = pSMB->TotalParameterCount;
4432 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004433 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004434 pSMB->DataCount = 0;
4435 pSMB->DataOffset = 0;
4436 pSMB->SetupCount = 1;
4437 pSMB->Reserved3 = 0;
4438 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4439 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4440 pSMB->hdr.smb_buf_length += byte_count;
4441 pSMB->ByteCount = cpu_to_le16(byte_count);
4442
4443 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4444 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4445 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004446 cERROR(1, "Send error in QFSAttributeInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004447 } else { /* decode response */
4448 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4449
Steve French50c2f752007-07-13 00:33:32 +00004450 if (rc || (pSMBr->ByteCount < 13)) {
4451 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004452 rc = -EIO; /* bad smb */
4453 } else {
4454 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4455 response_data =
4456 (FILE_SYSTEM_ATTRIBUTE_INFO
4457 *) (((char *) &pSMBr->hdr.Protocol) +
4458 data_offset);
4459 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004460 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004461 }
4462 }
4463 cifs_buf_release(pSMB);
4464
4465 if (rc == -EAGAIN)
4466 goto QFSAttributeRetry;
4467
4468 return rc;
4469}
4470
4471int
Steve French737b7582005-04-28 22:41:06 -07004472CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004473{
4474/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4475 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4476 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4477 FILE_SYSTEM_DEVICE_INFO *response_data;
4478 int rc = 0;
4479 int bytes_returned = 0;
4480 __u16 params, byte_count;
4481
Joe Perchesb6b38f72010-04-21 03:50:45 +00004482 cFYI(1, "In QFSDeviceInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004483QFSDeviceRetry:
4484 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4485 (void **) &pSMBr);
4486 if (rc)
4487 return rc;
4488
4489 params = 2; /* level */
4490 pSMB->TotalDataCount = 0;
4491 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004492 /* BB find exact max SMB PDU from sess structure BB */
4493 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004494 pSMB->MaxSetupCount = 0;
4495 pSMB->Reserved = 0;
4496 pSMB->Flags = 0;
4497 pSMB->Timeout = 0;
4498 pSMB->Reserved2 = 0;
4499 byte_count = params + 1 /* pad */ ;
4500 pSMB->TotalParameterCount = cpu_to_le16(params);
4501 pSMB->ParameterCount = pSMB->TotalParameterCount;
4502 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004503 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004504
4505 pSMB->DataCount = 0;
4506 pSMB->DataOffset = 0;
4507 pSMB->SetupCount = 1;
4508 pSMB->Reserved3 = 0;
4509 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4510 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4511 pSMB->hdr.smb_buf_length += byte_count;
4512 pSMB->ByteCount = cpu_to_le16(byte_count);
4513
4514 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4515 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4516 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004517 cFYI(1, "Send error in QFSDeviceInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004518 } else { /* decode response */
4519 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4520
Steve French630f3f0c2007-10-25 21:17:17 +00004521 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004522 rc = -EIO; /* bad smb */
4523 else {
4524 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4525 response_data =
Steve French737b7582005-04-28 22:41:06 -07004526 (FILE_SYSTEM_DEVICE_INFO *)
4527 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004528 data_offset);
4529 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004530 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004531 }
4532 }
4533 cifs_buf_release(pSMB);
4534
4535 if (rc == -EAGAIN)
4536 goto QFSDeviceRetry;
4537
4538 return rc;
4539}
4540
4541int
Steve French737b7582005-04-28 22:41:06 -07004542CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004543{
4544/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4545 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4546 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4547 FILE_SYSTEM_UNIX_INFO *response_data;
4548 int rc = 0;
4549 int bytes_returned = 0;
4550 __u16 params, byte_count;
4551
Joe Perchesb6b38f72010-04-21 03:50:45 +00004552 cFYI(1, "In QFSUnixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004553QFSUnixRetry:
Jeff Laytonf5695992010-09-29 15:27:08 -04004554 rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
4555 (void **) &pSMB, (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004556 if (rc)
4557 return rc;
4558
4559 params = 2; /* level */
4560 pSMB->TotalDataCount = 0;
4561 pSMB->DataCount = 0;
4562 pSMB->DataOffset = 0;
4563 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004564 /* BB find exact max SMB PDU from sess structure BB */
4565 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004566 pSMB->MaxSetupCount = 0;
4567 pSMB->Reserved = 0;
4568 pSMB->Flags = 0;
4569 pSMB->Timeout = 0;
4570 pSMB->Reserved2 = 0;
4571 byte_count = params + 1 /* pad */ ;
4572 pSMB->ParameterCount = cpu_to_le16(params);
4573 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004574 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4575 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004576 pSMB->SetupCount = 1;
4577 pSMB->Reserved3 = 0;
4578 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4579 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4580 pSMB->hdr.smb_buf_length += byte_count;
4581 pSMB->ByteCount = cpu_to_le16(byte_count);
4582
4583 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4584 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4585 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004586 cERROR(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004587 } else { /* decode response */
4588 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4589
4590 if (rc || (pSMBr->ByteCount < 13)) {
4591 rc = -EIO; /* bad smb */
4592 } else {
4593 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4594 response_data =
4595 (FILE_SYSTEM_UNIX_INFO
4596 *) (((char *) &pSMBr->hdr.Protocol) +
4597 data_offset);
4598 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004599 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004600 }
4601 }
4602 cifs_buf_release(pSMB);
4603
4604 if (rc == -EAGAIN)
4605 goto QFSUnixRetry;
4606
4607
4608 return rc;
4609}
4610
Jeremy Allisonac670552005-06-22 17:26:35 -07004611int
Steve French45abc6e2005-06-23 13:42:03 -05004612CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004613{
4614/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4615 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4616 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4617 int rc = 0;
4618 int bytes_returned = 0;
4619 __u16 params, param_offset, offset, byte_count;
4620
Joe Perchesb6b38f72010-04-21 03:50:45 +00004621 cFYI(1, "In SETFSUnixInfo");
Jeremy Allisonac670552005-06-22 17:26:35 -07004622SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004623 /* BB switch to small buf init to save memory */
Jeff Laytonf5695992010-09-29 15:27:08 -04004624 rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
4625 (void **) &pSMB, (void **) &pSMBr);
Jeremy Allisonac670552005-06-22 17:26:35 -07004626 if (rc)
4627 return rc;
4628
4629 params = 4; /* 2 bytes zero followed by info level. */
4630 pSMB->MaxSetupCount = 0;
4631 pSMB->Reserved = 0;
4632 pSMB->Flags = 0;
4633 pSMB->Timeout = 0;
4634 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004635 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4636 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004637 offset = param_offset + params;
4638
4639 pSMB->MaxParameterCount = cpu_to_le16(4);
Steve French582d21e2008-05-13 04:54:12 +00004640 /* BB find exact max SMB PDU from sess structure BB */
4641 pSMB->MaxDataCount = cpu_to_le16(100);
Jeremy Allisonac670552005-06-22 17:26:35 -07004642 pSMB->SetupCount = 1;
4643 pSMB->Reserved3 = 0;
4644 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4645 byte_count = 1 /* pad */ + params + 12;
4646
4647 pSMB->DataCount = cpu_to_le16(12);
4648 pSMB->ParameterCount = cpu_to_le16(params);
4649 pSMB->TotalDataCount = pSMB->DataCount;
4650 pSMB->TotalParameterCount = pSMB->ParameterCount;
4651 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4652 pSMB->DataOffset = cpu_to_le16(offset);
4653
4654 /* Params. */
4655 pSMB->FileNum = 0;
4656 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4657
4658 /* Data. */
4659 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4660 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4661 pSMB->ClientUnixCap = cpu_to_le64(cap);
4662
4663 pSMB->hdr.smb_buf_length += byte_count;
4664 pSMB->ByteCount = cpu_to_le16(byte_count);
4665
4666 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4667 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4668 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004669 cERROR(1, "Send error in SETFSUnixInfo = %d", rc);
Jeremy Allisonac670552005-06-22 17:26:35 -07004670 } else { /* decode response */
4671 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004672 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004673 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004674 }
4675 cifs_buf_release(pSMB);
4676
4677 if (rc == -EAGAIN)
4678 goto SETFSUnixRetry;
4679
4680 return rc;
4681}
4682
4683
Linus Torvalds1da177e2005-04-16 15:20:36 -07004684
4685int
4686CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004687 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004688{
4689/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4690 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4691 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4692 FILE_SYSTEM_POSIX_INFO *response_data;
4693 int rc = 0;
4694 int bytes_returned = 0;
4695 __u16 params, byte_count;
4696
Joe Perchesb6b38f72010-04-21 03:50:45 +00004697 cFYI(1, "In QFSPosixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004698QFSPosixRetry:
4699 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4700 (void **) &pSMBr);
4701 if (rc)
4702 return rc;
4703
4704 params = 2; /* level */
4705 pSMB->TotalDataCount = 0;
4706 pSMB->DataCount = 0;
4707 pSMB->DataOffset = 0;
4708 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004709 /* BB find exact max SMB PDU from sess structure BB */
4710 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004711 pSMB->MaxSetupCount = 0;
4712 pSMB->Reserved = 0;
4713 pSMB->Flags = 0;
4714 pSMB->Timeout = 0;
4715 pSMB->Reserved2 = 0;
4716 byte_count = params + 1 /* pad */ ;
4717 pSMB->ParameterCount = cpu_to_le16(params);
4718 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004719 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4720 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004721 pSMB->SetupCount = 1;
4722 pSMB->Reserved3 = 0;
4723 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4724 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4725 pSMB->hdr.smb_buf_length += byte_count;
4726 pSMB->ByteCount = cpu_to_le16(byte_count);
4727
4728 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4729 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4730 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004731 cFYI(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004732 } else { /* decode response */
4733 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4734
4735 if (rc || (pSMBr->ByteCount < 13)) {
4736 rc = -EIO; /* bad smb */
4737 } else {
4738 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4739 response_data =
4740 (FILE_SYSTEM_POSIX_INFO
4741 *) (((char *) &pSMBr->hdr.Protocol) +
4742 data_offset);
4743 FSData->f_bsize =
4744 le32_to_cpu(response_data->BlockSize);
4745 FSData->f_blocks =
4746 le64_to_cpu(response_data->TotalBlocks);
4747 FSData->f_bfree =
4748 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004749 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004750 FSData->f_bavail = FSData->f_bfree;
4751 } else {
4752 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004753 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004754 }
Steve French790fe572007-07-07 19:25:05 +00004755 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004756 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004757 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004758 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004759 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004760 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004761 }
4762 }
4763 cifs_buf_release(pSMB);
4764
4765 if (rc == -EAGAIN)
4766 goto QFSPosixRetry;
4767
4768 return rc;
4769}
4770
4771
Steve French50c2f752007-07-13 00:33:32 +00004772/* We can not use write of zero bytes trick to
4773 set file size due to need for large file support. Also note that
4774 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004775 routine which is only needed to work around a sharing violation bug
4776 in Samba which this routine can run into */
4777
4778int
4779CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French4b18f2a2008-04-29 00:06:05 +00004780 __u64 size, bool SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004781 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004782{
4783 struct smb_com_transaction2_spi_req *pSMB = NULL;
4784 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4785 struct file_end_of_file_info *parm_data;
4786 int name_len;
4787 int rc = 0;
4788 int bytes_returned = 0;
4789 __u16 params, byte_count, data_count, param_offset, offset;
4790
Joe Perchesb6b38f72010-04-21 03:50:45 +00004791 cFYI(1, "In SetEOF");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004792SetEOFRetry:
4793 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4794 (void **) &pSMBr);
4795 if (rc)
4796 return rc;
4797
4798 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4799 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004800 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004801 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004802 name_len++; /* trailing null */
4803 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004804 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004805 name_len = strnlen(fileName, PATH_MAX);
4806 name_len++; /* trailing null */
4807 strncpy(pSMB->FileName, fileName, name_len);
4808 }
4809 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004810 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004811 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004812 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004813 pSMB->MaxSetupCount = 0;
4814 pSMB->Reserved = 0;
4815 pSMB->Flags = 0;
4816 pSMB->Timeout = 0;
4817 pSMB->Reserved2 = 0;
4818 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004819 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004820 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004821 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004822 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4823 pSMB->InformationLevel =
4824 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4825 else
4826 pSMB->InformationLevel =
4827 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4828 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004829 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4830 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004831 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004832 else
4833 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004834 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004835 }
4836
4837 parm_data =
4838 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4839 offset);
4840 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4841 pSMB->DataOffset = cpu_to_le16(offset);
4842 pSMB->SetupCount = 1;
4843 pSMB->Reserved3 = 0;
4844 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4845 byte_count = 3 /* pad */ + params + data_count;
4846 pSMB->DataCount = cpu_to_le16(data_count);
4847 pSMB->TotalDataCount = pSMB->DataCount;
4848 pSMB->ParameterCount = cpu_to_le16(params);
4849 pSMB->TotalParameterCount = pSMB->ParameterCount;
4850 pSMB->Reserved4 = 0;
4851 pSMB->hdr.smb_buf_length += byte_count;
4852 parm_data->FileSize = cpu_to_le64(size);
4853 pSMB->ByteCount = cpu_to_le16(byte_count);
4854 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4855 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004856 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00004857 cFYI(1, "SetPathInfo (file size) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004858
4859 cifs_buf_release(pSMB);
4860
4861 if (rc == -EAGAIN)
4862 goto SetEOFRetry;
4863
4864 return rc;
4865}
4866
4867int
Steve French50c2f752007-07-13 00:33:32 +00004868CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
Steve French4b18f2a2008-04-29 00:06:05 +00004869 __u16 fid, __u32 pid_of_opener, bool SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004870{
4871 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004872 char *data_offset;
4873 struct file_end_of_file_info *parm_data;
4874 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004875 __u16 params, param_offset, offset, byte_count, count;
4876
Joe Perchesb6b38f72010-04-21 03:50:45 +00004877 cFYI(1, "SetFileSize (via SetFileInfo) %lld",
4878 (long long)size);
Steve Frenchcd634992005-04-28 22:41:10 -07004879 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4880
Linus Torvalds1da177e2005-04-16 15:20:36 -07004881 if (rc)
4882 return rc;
4883
4884 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4885 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004886
Linus Torvalds1da177e2005-04-16 15:20:36 -07004887 params = 6;
4888 pSMB->MaxSetupCount = 0;
4889 pSMB->Reserved = 0;
4890 pSMB->Flags = 0;
4891 pSMB->Timeout = 0;
4892 pSMB->Reserved2 = 0;
4893 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4894 offset = param_offset + params;
4895
Steve French50c2f752007-07-13 00:33:32 +00004896 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004897
4898 count = sizeof(struct file_end_of_file_info);
4899 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004900 /* BB find exact max SMB PDU from sess structure BB */
4901 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004902 pSMB->SetupCount = 1;
4903 pSMB->Reserved3 = 0;
4904 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4905 byte_count = 3 /* pad */ + params + count;
4906 pSMB->DataCount = cpu_to_le16(count);
4907 pSMB->ParameterCount = cpu_to_le16(params);
4908 pSMB->TotalDataCount = pSMB->DataCount;
4909 pSMB->TotalParameterCount = pSMB->ParameterCount;
4910 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4911 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004912 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4913 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004914 pSMB->DataOffset = cpu_to_le16(offset);
4915 parm_data->FileSize = cpu_to_le64(size);
4916 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004917 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004918 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4919 pSMB->InformationLevel =
4920 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4921 else
4922 pSMB->InformationLevel =
4923 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004924 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004925 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4926 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004927 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004928 else
4929 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004930 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004931 }
4932 pSMB->Reserved4 = 0;
4933 pSMB->hdr.smb_buf_length += byte_count;
4934 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004935 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004936 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004937 cFYI(1, "Send error in SetFileInfo (SetFileSize) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004938 }
4939
Steve French50c2f752007-07-13 00:33:32 +00004940 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004941 since file handle passed in no longer valid */
4942
4943 return rc;
4944}
4945
Steve French50c2f752007-07-13 00:33:32 +00004946/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004947 an open handle, rather than by pathname - this is awkward due to
4948 potential access conflicts on the open, but it is unavoidable for these
4949 old servers since the only other choice is to go from 100 nanosecond DCE
4950 time and resort to the original setpathinfo level which takes the ancient
4951 DOS time format with 2 second granularity */
4952int
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004953CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
4954 const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004955{
4956 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004957 char *data_offset;
4958 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004959 __u16 params, param_offset, offset, byte_count, count;
4960
Joe Perchesb6b38f72010-04-21 03:50:45 +00004961 cFYI(1, "Set Times (via SetFileInfo)");
Steve Frenchcd634992005-04-28 22:41:10 -07004962 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4963
Linus Torvalds1da177e2005-04-16 15:20:36 -07004964 if (rc)
4965 return rc;
4966
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004967 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4968 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004969
Linus Torvalds1da177e2005-04-16 15:20:36 -07004970 params = 6;
4971 pSMB->MaxSetupCount = 0;
4972 pSMB->Reserved = 0;
4973 pSMB->Flags = 0;
4974 pSMB->Timeout = 0;
4975 pSMB->Reserved2 = 0;
4976 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4977 offset = param_offset + params;
4978
Steve French50c2f752007-07-13 00:33:32 +00004979 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004980
Steve French26f57362007-08-30 22:09:15 +00004981 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004982 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004983 /* BB find max SMB PDU from sess */
4984 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004985 pSMB->SetupCount = 1;
4986 pSMB->Reserved3 = 0;
4987 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4988 byte_count = 3 /* pad */ + params + count;
4989 pSMB->DataCount = cpu_to_le16(count);
4990 pSMB->ParameterCount = cpu_to_le16(params);
4991 pSMB->TotalDataCount = pSMB->DataCount;
4992 pSMB->TotalParameterCount = pSMB->ParameterCount;
4993 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4994 pSMB->DataOffset = cpu_to_le16(offset);
4995 pSMB->Fid = fid;
4996 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4997 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4998 else
4999 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5000 pSMB->Reserved4 = 0;
5001 pSMB->hdr.smb_buf_length += byte_count;
5002 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00005003 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00005004 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005005 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005006 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005007
Steve French50c2f752007-07-13 00:33:32 +00005008 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07005009 since file handle passed in no longer valid */
5010
5011 return rc;
5012}
5013
Jeff Layton6d22f092008-09-23 11:48:35 -04005014int
5015CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
5016 bool delete_file, __u16 fid, __u32 pid_of_opener)
5017{
5018 struct smb_com_transaction2_sfi_req *pSMB = NULL;
5019 char *data_offset;
5020 int rc = 0;
5021 __u16 params, param_offset, offset, byte_count, count;
5022
Joe Perchesb6b38f72010-04-21 03:50:45 +00005023 cFYI(1, "Set File Disposition (via SetFileInfo)");
Jeff Layton6d22f092008-09-23 11:48:35 -04005024 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5025
5026 if (rc)
5027 return rc;
5028
5029 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5030 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5031
5032 params = 6;
5033 pSMB->MaxSetupCount = 0;
5034 pSMB->Reserved = 0;
5035 pSMB->Flags = 0;
5036 pSMB->Timeout = 0;
5037 pSMB->Reserved2 = 0;
5038 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5039 offset = param_offset + params;
5040
5041 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5042
5043 count = 1;
5044 pSMB->MaxParameterCount = cpu_to_le16(2);
5045 /* BB find max SMB PDU from sess */
5046 pSMB->MaxDataCount = cpu_to_le16(1000);
5047 pSMB->SetupCount = 1;
5048 pSMB->Reserved3 = 0;
5049 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5050 byte_count = 3 /* pad */ + params + count;
5051 pSMB->DataCount = cpu_to_le16(count);
5052 pSMB->ParameterCount = cpu_to_le16(params);
5053 pSMB->TotalDataCount = pSMB->DataCount;
5054 pSMB->TotalParameterCount = pSMB->ParameterCount;
5055 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5056 pSMB->DataOffset = cpu_to_le16(offset);
5057 pSMB->Fid = fid;
5058 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
5059 pSMB->Reserved4 = 0;
5060 pSMB->hdr.smb_buf_length += byte_count;
5061 pSMB->ByteCount = cpu_to_le16(byte_count);
5062 *data_offset = delete_file ? 1 : 0;
5063 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5064 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005065 cFYI(1, "Send error in SetFileDisposition = %d", rc);
Jeff Layton6d22f092008-09-23 11:48:35 -04005066
5067 return rc;
5068}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005069
5070int
Jeff Layton6fc000e2008-08-02 07:26:12 -04005071CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
5072 const char *fileName, const FILE_BASIC_INFO *data,
5073 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005074{
5075 TRANSACTION2_SPI_REQ *pSMB = NULL;
5076 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5077 int name_len;
5078 int rc = 0;
5079 int bytes_returned = 0;
5080 char *data_offset;
5081 __u16 params, param_offset, offset, byte_count, count;
5082
Joe Perchesb6b38f72010-04-21 03:50:45 +00005083 cFYI(1, "In SetTimes");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005084
5085SetTimesRetry:
5086 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5087 (void **) &pSMBr);
5088 if (rc)
5089 return rc;
5090
5091 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5092 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05005093 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005094 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005095 name_len++; /* trailing null */
5096 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005097 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005098 name_len = strnlen(fileName, PATH_MAX);
5099 name_len++; /* trailing null */
5100 strncpy(pSMB->FileName, fileName, name_len);
5101 }
5102
5103 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005104 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005105 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005106 /* BB find max SMB PDU from sess structure BB */
5107 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005108 pSMB->MaxSetupCount = 0;
5109 pSMB->Reserved = 0;
5110 pSMB->Flags = 0;
5111 pSMB->Timeout = 0;
5112 pSMB->Reserved2 = 0;
5113 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005114 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005115 offset = param_offset + params;
5116 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5117 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5118 pSMB->DataOffset = cpu_to_le16(offset);
5119 pSMB->SetupCount = 1;
5120 pSMB->Reserved3 = 0;
5121 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5122 byte_count = 3 /* pad */ + params + count;
5123
5124 pSMB->DataCount = cpu_to_le16(count);
5125 pSMB->ParameterCount = cpu_to_le16(params);
5126 pSMB->TotalDataCount = pSMB->DataCount;
5127 pSMB->TotalParameterCount = pSMB->ParameterCount;
5128 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5129 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5130 else
5131 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5132 pSMB->Reserved4 = 0;
5133 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00005134 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005135 pSMB->ByteCount = cpu_to_le16(byte_count);
5136 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5137 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005138 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005139 cFYI(1, "SetPathInfo (times) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005140
5141 cifs_buf_release(pSMB);
5142
5143 if (rc == -EAGAIN)
5144 goto SetTimesRetry;
5145
5146 return rc;
5147}
5148
5149/* Can not be used to set time stamps yet (due to old DOS time format) */
5150/* Can be used to set attributes */
5151#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
5152 handling it anyway and NT4 was what we thought it would be needed for
5153 Do not delete it until we prove whether needed for Win9x though */
5154int
5155CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
5156 __u16 dos_attrs, const struct nls_table *nls_codepage)
5157{
5158 SETATTR_REQ *pSMB = NULL;
5159 SETATTR_RSP *pSMBr = NULL;
5160 int rc = 0;
5161 int bytes_returned;
5162 int name_len;
5163
Joe Perchesb6b38f72010-04-21 03:50:45 +00005164 cFYI(1, "In SetAttrLegacy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005165
5166SetAttrLgcyRetry:
5167 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
5168 (void **) &pSMBr);
5169 if (rc)
5170 return rc;
5171
5172 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5173 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005174 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005175 PATH_MAX, nls_codepage);
5176 name_len++; /* trailing null */
5177 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005178 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005179 name_len = strnlen(fileName, PATH_MAX);
5180 name_len++; /* trailing null */
5181 strncpy(pSMB->fileName, fileName, name_len);
5182 }
5183 pSMB->attr = cpu_to_le16(dos_attrs);
5184 pSMB->BufferFormat = 0x04;
5185 pSMB->hdr.smb_buf_length += name_len + 1;
5186 pSMB->ByteCount = cpu_to_le16(name_len + 1);
5187 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5188 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005189 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005190 cFYI(1, "Error in LegacySetAttr = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005191
5192 cifs_buf_release(pSMB);
5193
5194 if (rc == -EAGAIN)
5195 goto SetAttrLgcyRetry;
5196
5197 return rc;
5198}
5199#endif /* temporarily unneeded SetAttr legacy function */
5200
Jeff Layton654cf142009-07-09 20:02:49 -04005201static void
5202cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,
5203 const struct cifs_unix_set_info_args *args)
5204{
5205 u64 mode = args->mode;
5206
5207 /*
5208 * Samba server ignores set of file size to zero due to bugs in some
5209 * older clients, but we should be precise - we use SetFileSize to
5210 * set file size and do not want to truncate file size to zero
5211 * accidently as happened on one Samba server beta by putting
5212 * zero instead of -1 here
5213 */
5214 data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
5215 data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
5216 data_offset->LastStatusChange = cpu_to_le64(args->ctime);
5217 data_offset->LastAccessTime = cpu_to_le64(args->atime);
5218 data_offset->LastModificationTime = cpu_to_le64(args->mtime);
5219 data_offset->Uid = cpu_to_le64(args->uid);
5220 data_offset->Gid = cpu_to_le64(args->gid);
5221 /* better to leave device as zero when it is */
5222 data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
5223 data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
5224 data_offset->Permissions = cpu_to_le64(mode);
5225
5226 if (S_ISREG(mode))
5227 data_offset->Type = cpu_to_le32(UNIX_FILE);
5228 else if (S_ISDIR(mode))
5229 data_offset->Type = cpu_to_le32(UNIX_DIR);
5230 else if (S_ISLNK(mode))
5231 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
5232 else if (S_ISCHR(mode))
5233 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
5234 else if (S_ISBLK(mode))
5235 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
5236 else if (S_ISFIFO(mode))
5237 data_offset->Type = cpu_to_le32(UNIX_FIFO);
5238 else if (S_ISSOCK(mode))
5239 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5240}
5241
Linus Torvalds1da177e2005-04-16 15:20:36 -07005242int
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005243CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon,
5244 const struct cifs_unix_set_info_args *args,
5245 u16 fid, u32 pid_of_opener)
5246{
5247 struct smb_com_transaction2_sfi_req *pSMB = NULL;
5248 FILE_UNIX_BASIC_INFO *data_offset;
5249 int rc = 0;
5250 u16 params, param_offset, offset, byte_count, count;
5251
Joe Perchesb6b38f72010-04-21 03:50:45 +00005252 cFYI(1, "Set Unix Info (via SetFileInfo)");
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005253 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5254
5255 if (rc)
5256 return rc;
5257
5258 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5259 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5260
5261 params = 6;
5262 pSMB->MaxSetupCount = 0;
5263 pSMB->Reserved = 0;
5264 pSMB->Flags = 0;
5265 pSMB->Timeout = 0;
5266 pSMB->Reserved2 = 0;
5267 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5268 offset = param_offset + params;
5269
5270 data_offset = (FILE_UNIX_BASIC_INFO *)
5271 ((char *)(&pSMB->hdr.Protocol) + offset);
5272 count = sizeof(FILE_UNIX_BASIC_INFO);
5273
5274 pSMB->MaxParameterCount = cpu_to_le16(2);
5275 /* BB find max SMB PDU from sess */
5276 pSMB->MaxDataCount = cpu_to_le16(1000);
5277 pSMB->SetupCount = 1;
5278 pSMB->Reserved3 = 0;
5279 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5280 byte_count = 3 /* pad */ + params + count;
5281 pSMB->DataCount = cpu_to_le16(count);
5282 pSMB->ParameterCount = cpu_to_le16(params);
5283 pSMB->TotalDataCount = pSMB->DataCount;
5284 pSMB->TotalParameterCount = pSMB->ParameterCount;
5285 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5286 pSMB->DataOffset = cpu_to_le16(offset);
5287 pSMB->Fid = fid;
5288 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5289 pSMB->Reserved4 = 0;
5290 pSMB->hdr.smb_buf_length += byte_count;
5291 pSMB->ByteCount = cpu_to_le16(byte_count);
5292
5293 cifs_fill_unix_set_info(data_offset, args);
5294
5295 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5296 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005297 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005298
5299 /* Note: On -EAGAIN error only caller can retry on handle based calls
5300 since file handle passed in no longer valid */
5301
5302 return rc;
5303}
5304
5305int
Jeff Layton01ea95e2009-07-09 20:02:49 -04005306CIFSSMBUnixSetPathInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,
5307 const struct cifs_unix_set_info_args *args,
5308 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005309{
5310 TRANSACTION2_SPI_REQ *pSMB = NULL;
5311 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5312 int name_len;
5313 int rc = 0;
5314 int bytes_returned = 0;
5315 FILE_UNIX_BASIC_INFO *data_offset;
5316 __u16 params, param_offset, offset, count, byte_count;
5317
Joe Perchesb6b38f72010-04-21 03:50:45 +00005318 cFYI(1, "In SetUID/GID/Mode");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005319setPermsRetry:
5320 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5321 (void **) &pSMBr);
5322 if (rc)
5323 return rc;
5324
5325 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5326 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005327 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005328 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005329 name_len++; /* trailing null */
5330 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07005331 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005332 name_len = strnlen(fileName, PATH_MAX);
5333 name_len++; /* trailing null */
5334 strncpy(pSMB->FileName, fileName, name_len);
5335 }
5336
5337 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005338 count = sizeof(FILE_UNIX_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005339 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005340 /* BB find max SMB PDU from sess structure BB */
5341 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005342 pSMB->MaxSetupCount = 0;
5343 pSMB->Reserved = 0;
5344 pSMB->Flags = 0;
5345 pSMB->Timeout = 0;
5346 pSMB->Reserved2 = 0;
5347 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005348 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005349 offset = param_offset + params;
5350 data_offset =
5351 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5352 offset);
5353 memset(data_offset, 0, count);
5354 pSMB->DataOffset = cpu_to_le16(offset);
5355 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5356 pSMB->SetupCount = 1;
5357 pSMB->Reserved3 = 0;
5358 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5359 byte_count = 3 /* pad */ + params + count;
5360 pSMB->ParameterCount = cpu_to_le16(params);
5361 pSMB->DataCount = cpu_to_le16(count);
5362 pSMB->TotalParameterCount = pSMB->ParameterCount;
5363 pSMB->TotalDataCount = pSMB->DataCount;
5364 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5365 pSMB->Reserved4 = 0;
5366 pSMB->hdr.smb_buf_length += byte_count;
Steve French50c2f752007-07-13 00:33:32 +00005367
Jeff Layton654cf142009-07-09 20:02:49 -04005368 cifs_fill_unix_set_info(data_offset, args);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005369
5370 pSMB->ByteCount = cpu_to_le16(byte_count);
5371 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5372 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005373 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005374 cFYI(1, "SetPathInfo (perms) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005375
Steve French0d817bc2008-05-22 02:02:03 +00005376 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005377 if (rc == -EAGAIN)
5378 goto setPermsRetry;
5379 return rc;
5380}
5381
Steve French50c2f752007-07-13 00:33:32 +00005382int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005383 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005384 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005385 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005386{
5387 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005388 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5389 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005390 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005391 int bytes_returned;
5392
Joe Perchesb6b38f72010-04-21 03:50:45 +00005393 cFYI(1, "In CIFSSMBNotify for file handle %d", (int)netfid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005394 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005395 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005396 if (rc)
5397 return rc;
5398
5399 pSMB->TotalParameterCount = 0 ;
5400 pSMB->TotalDataCount = 0;
5401 pSMB->MaxParameterCount = cpu_to_le32(2);
5402 /* BB find exact data count max from sess structure BB */
5403 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005404/* BB VERIFY verify which is correct for above BB */
5405 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5406 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5407
Linus Torvalds1da177e2005-04-16 15:20:36 -07005408 pSMB->MaxSetupCount = 4;
5409 pSMB->Reserved = 0;
5410 pSMB->ParameterOffset = 0;
5411 pSMB->DataCount = 0;
5412 pSMB->DataOffset = 0;
5413 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5414 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5415 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005416 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005417 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5418 pSMB->Reserved2 = 0;
5419 pSMB->CompletionFilter = cpu_to_le32(filter);
5420 pSMB->Fid = netfid; /* file handle always le */
5421 pSMB->ByteCount = 0;
5422
5423 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005424 (struct smb_hdr *)pSMBr, &bytes_returned,
5425 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005426 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005427 cFYI(1, "Error in Notify = %d", rc);
Steve Frenchff5dbd92005-08-24 17:10:36 -07005428 } else {
5429 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005430 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005431 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005432 sizeof(struct dir_notify_req),
5433 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005434 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005435 dnotify_req->Pid = pSMB->hdr.Pid;
5436 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5437 dnotify_req->Mid = pSMB->hdr.Mid;
5438 dnotify_req->Tid = pSMB->hdr.Tid;
5439 dnotify_req->Uid = pSMB->hdr.Uid;
5440 dnotify_req->netfid = netfid;
5441 dnotify_req->pfile = pfile;
5442 dnotify_req->filter = filter;
5443 dnotify_req->multishot = multishot;
5444 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005445 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005446 &GlobalDnotifyReqList);
5447 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005448 } else
Steve French47c786e2005-10-11 20:03:18 -07005449 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005450 }
5451 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005452 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005453}
Jeff Layton31c05192010-02-10 16:18:26 -05005454
Linus Torvalds1da177e2005-04-16 15:20:36 -07005455#ifdef CONFIG_CIFS_XATTR
Jeff Layton31c05192010-02-10 16:18:26 -05005456/*
5457 * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common
5458 * function used by listxattr and getxattr type calls. When ea_name is set,
5459 * it looks for that attribute name and stuffs that value into the EAData
5460 * buffer. When ea_name is NULL, it stuffs a list of attribute names into the
5461 * buffer. In both cases, the return value is either the length of the
5462 * resulting data or a negative error code. If EAData is a NULL pointer then
5463 * the data isn't copied to it, but the length is returned.
5464 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005465ssize_t
5466CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
Jeff Layton31c05192010-02-10 16:18:26 -05005467 const unsigned char *searchName, const unsigned char *ea_name,
5468 char *EAData, size_t buf_size,
5469 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005470{
5471 /* BB assumes one setup word */
5472 TRANSACTION2_QPI_REQ *pSMB = NULL;
5473 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5474 int rc = 0;
5475 int bytes_returned;
Jeff Layton6e462b92010-02-10 16:18:26 -05005476 int list_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005477 struct fealist *ea_response_data;
Steve French50c2f752007-07-13 00:33:32 +00005478 struct fea *temp_fea;
5479 char *temp_ptr;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005480 char *end_of_smb;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005481 __u16 params, byte_count, data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005482
Joe Perchesb6b38f72010-04-21 03:50:45 +00005483 cFYI(1, "In Query All EAs path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005484QAllEAsRetry:
5485 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5486 (void **) &pSMBr);
5487 if (rc)
5488 return rc;
5489
5490 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Jeff Layton6e462b92010-02-10 16:18:26 -05005491 list_len =
Steve French50c2f752007-07-13 00:33:32 +00005492 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005493 PATH_MAX, nls_codepage, remap);
Jeff Layton6e462b92010-02-10 16:18:26 -05005494 list_len++; /* trailing null */
5495 list_len *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005496 } else { /* BB improve the check for buffer overruns BB */
Jeff Layton6e462b92010-02-10 16:18:26 -05005497 list_len = strnlen(searchName, PATH_MAX);
5498 list_len++; /* trailing null */
5499 strncpy(pSMB->FileName, searchName, list_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005500 }
5501
Jeff Layton6e462b92010-02-10 16:18:26 -05005502 params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005503 pSMB->TotalDataCount = 0;
5504 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005505 /* BB find exact max SMB PDU from sess structure BB */
Jeff Laytone5296142010-02-10 16:18:26 -05005506 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005507 pSMB->MaxSetupCount = 0;
5508 pSMB->Reserved = 0;
5509 pSMB->Flags = 0;
5510 pSMB->Timeout = 0;
5511 pSMB->Reserved2 = 0;
5512 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005513 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005514 pSMB->DataCount = 0;
5515 pSMB->DataOffset = 0;
5516 pSMB->SetupCount = 1;
5517 pSMB->Reserved3 = 0;
5518 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5519 byte_count = params + 1 /* pad */ ;
5520 pSMB->TotalParameterCount = cpu_to_le16(params);
5521 pSMB->ParameterCount = pSMB->TotalParameterCount;
5522 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5523 pSMB->Reserved4 = 0;
5524 pSMB->hdr.smb_buf_length += byte_count;
5525 pSMB->ByteCount = cpu_to_le16(byte_count);
5526
5527 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5528 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5529 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005530 cFYI(1, "Send error in QueryAllEAs = %d", rc);
Jeff Laytonf0d38682010-02-10 16:18:26 -05005531 goto QAllEAsOut;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005532 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005533
5534
5535 /* BB also check enough total bytes returned */
5536 /* BB we need to improve the validity checking
5537 of these trans2 responses */
5538
5539 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5540 if (rc || (pSMBr->ByteCount < 4)) {
5541 rc = -EIO; /* bad smb */
5542 goto QAllEAsOut;
5543 }
5544
5545 /* check that length of list is not more than bcc */
5546 /* check that each entry does not go beyond length
5547 of list */
5548 /* check that each element of each entry does not
5549 go beyond end of list */
5550 /* validate_trans2_offsets() */
5551 /* BB check if start of smb + data_offset > &bcc+ bcc */
5552
5553 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5554 ea_response_data = (struct fealist *)
5555 (((char *) &pSMBr->hdr.Protocol) + data_offset);
5556
Jeff Layton6e462b92010-02-10 16:18:26 -05005557 list_len = le32_to_cpu(ea_response_data->list_len);
Joe Perchesb6b38f72010-04-21 03:50:45 +00005558 cFYI(1, "ea length %d", list_len);
Jeff Layton6e462b92010-02-10 16:18:26 -05005559 if (list_len <= 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005560 cFYI(1, "empty EA list returned from server");
Jeff Laytonf0d38682010-02-10 16:18:26 -05005561 goto QAllEAsOut;
5562 }
5563
Jeff Layton0cd126b2010-02-10 16:18:26 -05005564 /* make sure list_len doesn't go past end of SMB */
5565 end_of_smb = (char *)pByteArea(&pSMBr->hdr) + BCC(&pSMBr->hdr);
5566 if ((char *)ea_response_data + list_len > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005567 cFYI(1, "EA list appears to go beyond SMB");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005568 rc = -EIO;
5569 goto QAllEAsOut;
5570 }
5571
Jeff Laytonf0d38682010-02-10 16:18:26 -05005572 /* account for ea list len */
Jeff Layton6e462b92010-02-10 16:18:26 -05005573 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005574 temp_fea = ea_response_data->list;
5575 temp_ptr = (char *)temp_fea;
Jeff Layton6e462b92010-02-10 16:18:26 -05005576 while (list_len > 0) {
Steve French122ca002010-02-24 21:56:48 +00005577 unsigned int name_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005578 __u16 value_len;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005579
Jeff Layton6e462b92010-02-10 16:18:26 -05005580 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005581 temp_ptr += 4;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005582 /* make sure we can read name_len and value_len */
5583 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005584 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005585 rc = -EIO;
5586 goto QAllEAsOut;
5587 }
5588
5589 name_len = temp_fea->name_len;
5590 value_len = le16_to_cpu(temp_fea->value_len);
5591 list_len -= name_len + 1 + value_len;
5592 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005593 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005594 rc = -EIO;
5595 goto QAllEAsOut;
5596 }
5597
Jeff Layton31c05192010-02-10 16:18:26 -05005598 if (ea_name) {
5599 if (strncmp(ea_name, temp_ptr, name_len) == 0) {
5600 temp_ptr += name_len + 1;
5601 rc = value_len;
5602 if (buf_size == 0)
5603 goto QAllEAsOut;
5604 if ((size_t)value_len > buf_size) {
5605 rc = -ERANGE;
5606 goto QAllEAsOut;
5607 }
5608 memcpy(EAData, temp_ptr, value_len);
5609 goto QAllEAsOut;
5610 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005611 } else {
Jeff Layton31c05192010-02-10 16:18:26 -05005612 /* account for prefix user. and trailing null */
5613 rc += (5 + 1 + name_len);
5614 if (rc < (int) buf_size) {
5615 memcpy(EAData, "user.", 5);
5616 EAData += 5;
5617 memcpy(EAData, temp_ptr, name_len);
5618 EAData += name_len;
5619 /* null terminate name */
5620 *EAData = 0;
5621 ++EAData;
5622 } else if (buf_size == 0) {
5623 /* skip copy - calc size only */
5624 } else {
5625 /* stop before overrun buffer */
5626 rc = -ERANGE;
5627 break;
5628 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005629 }
Jeff Layton0cd126b2010-02-10 16:18:26 -05005630 temp_ptr += name_len + 1 + value_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005631 temp_fea = (struct fea *)temp_ptr;
5632 }
5633
Jeff Layton31c05192010-02-10 16:18:26 -05005634 /* didn't find the named attribute */
5635 if (ea_name)
5636 rc = -ENODATA;
5637
Jeff Laytonf0d38682010-02-10 16:18:26 -05005638QAllEAsOut:
Steve French0d817bc2008-05-22 02:02:03 +00005639 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005640 if (rc == -EAGAIN)
5641 goto QAllEAsRetry;
5642
5643 return (ssize_t)rc;
5644}
5645
Linus Torvalds1da177e2005-04-16 15:20:36 -07005646int
5647CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005648 const char *ea_name, const void *ea_value,
5649 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5650 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005651{
5652 struct smb_com_transaction2_spi_req *pSMB = NULL;
5653 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5654 struct fealist *parm_data;
5655 int name_len;
5656 int rc = 0;
5657 int bytes_returned = 0;
5658 __u16 params, param_offset, byte_count, offset, count;
5659
Joe Perchesb6b38f72010-04-21 03:50:45 +00005660 cFYI(1, "In SetEA");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005661SetEARetry:
5662 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5663 (void **) &pSMBr);
5664 if (rc)
5665 return rc;
5666
5667 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5668 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005669 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005670 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005671 name_len++; /* trailing null */
5672 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005673 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005674 name_len = strnlen(fileName, PATH_MAX);
5675 name_len++; /* trailing null */
5676 strncpy(pSMB->FileName, fileName, name_len);
5677 }
5678
5679 params = 6 + name_len;
5680
5681 /* done calculating parms using name_len of file name,
5682 now use name_len to calculate length of ea name
5683 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005684 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005685 name_len = 0;
5686 else
Steve French50c2f752007-07-13 00:33:32 +00005687 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005688
Steve Frenchdae5dbdb2007-12-30 23:49:57 +00005689 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005690 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005691 /* BB find max SMB PDU from sess */
5692 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005693 pSMB->MaxSetupCount = 0;
5694 pSMB->Reserved = 0;
5695 pSMB->Flags = 0;
5696 pSMB->Timeout = 0;
5697 pSMB->Reserved2 = 0;
5698 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005699 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005700 offset = param_offset + params;
5701 pSMB->InformationLevel =
5702 cpu_to_le16(SMB_SET_FILE_EA);
5703
5704 parm_data =
5705 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5706 offset);
5707 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5708 pSMB->DataOffset = cpu_to_le16(offset);
5709 pSMB->SetupCount = 1;
5710 pSMB->Reserved3 = 0;
5711 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5712 byte_count = 3 /* pad */ + params + count;
5713 pSMB->DataCount = cpu_to_le16(count);
5714 parm_data->list_len = cpu_to_le32(count);
5715 parm_data->list[0].EA_flags = 0;
5716 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005717 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005718 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005719 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005720 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005721 parm_data->list[0].name[name_len] = 0;
5722 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5723 /* caller ensures that ea_value_len is less than 64K but
5724 we need to ensure that it fits within the smb */
5725
Steve French50c2f752007-07-13 00:33:32 +00005726 /*BB add length check to see if it would fit in
5727 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005728 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5729 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005730 memcpy(parm_data->list[0].name+name_len+1,
5731 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005732
5733 pSMB->TotalDataCount = pSMB->DataCount;
5734 pSMB->ParameterCount = cpu_to_le16(params);
5735 pSMB->TotalParameterCount = pSMB->ParameterCount;
5736 pSMB->Reserved4 = 0;
5737 pSMB->hdr.smb_buf_length += byte_count;
5738 pSMB->ByteCount = cpu_to_le16(byte_count);
5739 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5740 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005741 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005742 cFYI(1, "SetPathInfo (EA) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005743
5744 cifs_buf_release(pSMB);
5745
5746 if (rc == -EAGAIN)
5747 goto SetEARetry;
5748
5749 return rc;
5750}
5751
5752#endif