blob: 3919ec8f956e3096b9b4eea0420cef1b434888a9 [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
Jeff Layton9162ab22009-09-03 12:07:17 -0400139 /*
140 * Give demultiplex thread up to 10 seconds to reconnect, should be
141 * greater than cifs socket timeout which is 7 seconds
142 */
143 while (server->tcpStatus == CifsNeedReconnect) {
144 wait_event_interruptible_timeout(server->response_q,
145 (server->tcpStatus == CifsGood), 10 * HZ);
146
147 /* is TCP session is reestablished now ?*/
148 if (server->tcpStatus != CifsNeedReconnect)
149 break;
150
151 /*
152 * on "soft" mounts we wait once. Hard mounts keep
153 * retrying until process is killed or server comes
154 * back on-line
155 */
Jeff Laytond4025392011-02-07 08:54:35 -0500156 if (!tcon->retry) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000157 cFYI(1, "gave up waiting on reconnect in smb_init");
Jeff Layton9162ab22009-09-03 12:07:17 -0400158 return -EHOSTDOWN;
159 }
160 }
161
162 if (!ses->need_reconnect && !tcon->need_reconnect)
163 return 0;
164
165 nls_codepage = load_nls_default();
166
167 /*
168 * need to prevent multiple threads trying to simultaneously
169 * reconnect the same SMB session
170 */
Steve Frenchd7b619c2010-02-25 05:36:46 +0000171 mutex_lock(&ses->session_mutex);
Jeff Layton198b5682010-04-24 07:57:48 -0400172 rc = cifs_negotiate_protocol(0, ses);
173 if (rc == 0 && ses->need_reconnect)
Jeff Layton9162ab22009-09-03 12:07:17 -0400174 rc = cifs_setup_session(0, ses, nls_codepage);
175
176 /* do we need to reconnect tcon? */
177 if (rc || !tcon->need_reconnect) {
Steve Frenchd7b619c2010-02-25 05:36:46 +0000178 mutex_unlock(&ses->session_mutex);
Jeff Layton9162ab22009-09-03 12:07:17 -0400179 goto out;
180 }
181
182 mark_open_files_invalid(tcon);
183 rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage);
Steve Frenchd7b619c2010-02-25 05:36:46 +0000184 mutex_unlock(&ses->session_mutex);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000185 cFYI(1, "reconnect tcon rc = %d", rc);
Jeff Layton9162ab22009-09-03 12:07:17 -0400186
187 if (rc)
188 goto out;
189
190 /*
191 * FIXME: check if wsize needs updated due to negotiated smb buffer
192 * size shrinking
193 */
194 atomic_inc(&tconInfoReconnectCount);
195
196 /* tell server Unix caps we support */
197 if (ses->capabilities & CAP_UNIX)
198 reset_cifs_unix_caps(0, tcon, NULL, NULL);
199
200 /*
201 * Removed call to reopen open files here. It is safer (and faster) to
202 * reopen files one at a time as needed in read and write.
203 *
204 * FIXME: what about file locks? don't we need to reclaim them ASAP?
205 */
206
207out:
208 /*
209 * Check if handle based operation so we know whether we can continue
210 * or not without returning to caller to reset file handle
211 */
212 switch (smb_command) {
213 case SMB_COM_READ_ANDX:
214 case SMB_COM_WRITE_ANDX:
215 case SMB_COM_CLOSE:
216 case SMB_COM_FIND_CLOSE2:
217 case SMB_COM_LOCKING_ANDX:
218 rc = -EAGAIN;
219 }
220
221 unload_nls(nls_codepage);
222 return rc;
223}
224
Steve Frenchad7a2922008-02-07 23:25:02 +0000225/* Allocate and return pointer to an SMB request buffer, and set basic
226 SMB information in the SMB header. If the return code is zero, this
227 function must have filled in request_buf pointer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228static int
229small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000230 void **request_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231{
Jeff Laytonf5695992010-09-29 15:27:08 -0400232 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
Jeff Layton9162ab22009-09-03 12:07:17 -0400234 rc = cifs_reconnect_tcon(tcon, smb_command);
Steve French790fe572007-07-07 19:25:05 +0000235 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 return rc;
237
238 *request_buf = cifs_small_buf_get();
239 if (*request_buf == NULL) {
240 /* BB should we add a retry in here if not a writepage? */
241 return -ENOMEM;
242 }
243
Steve French63135e02007-07-17 17:34:02 +0000244 header_assemble((struct smb_hdr *) *request_buf, smb_command,
Steve Frenchc18c8422007-07-18 23:21:09 +0000245 tcon, wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
Steve French790fe572007-07-07 19:25:05 +0000247 if (tcon != NULL)
248 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700249
Jeff Laytonf5695992010-09-29 15:27:08 -0400250 return 0;
Steve French5815449d2006-02-14 01:36:20 +0000251}
252
Steve French12b3b8f2006-02-09 21:12:47 +0000253int
Steve French50c2f752007-07-13 00:33:32 +0000254small_smb_init_no_tc(const int smb_command, const int wct,
Steve French5815449d2006-02-14 01:36:20 +0000255 struct cifsSesInfo *ses, void **request_buf)
Steve French12b3b8f2006-02-09 21:12:47 +0000256{
257 int rc;
Steve French50c2f752007-07-13 00:33:32 +0000258 struct smb_hdr *buffer;
Steve French12b3b8f2006-02-09 21:12:47 +0000259
Steve French5815449d2006-02-14 01:36:20 +0000260 rc = small_smb_init(smb_command, wct, NULL, request_buf);
Steve French790fe572007-07-07 19:25:05 +0000261 if (rc)
Steve French12b3b8f2006-02-09 21:12:47 +0000262 return rc;
263
Steve French04fdabe2006-02-10 05:52:50 +0000264 buffer = (struct smb_hdr *)*request_buf;
Steve French12b3b8f2006-02-09 21:12:47 +0000265 buffer->Mid = GetNextMid(ses->server);
266 if (ses->capabilities & CAP_UNICODE)
267 buffer->Flags2 |= SMBFLG2_UNICODE;
Steve French04fdabe2006-02-10 05:52:50 +0000268 if (ses->capabilities & CAP_STATUS32)
Steve French12b3b8f2006-02-09 21:12:47 +0000269 buffer->Flags2 |= SMBFLG2_ERR_STATUS;
270
271 /* uid, tid can stay at zero as set in header assemble */
272
Steve French50c2f752007-07-13 00:33:32 +0000273 /* BB add support for turning on the signing when
Steve French12b3b8f2006-02-09 21:12:47 +0000274 this function is used after 1st of session setup requests */
275
276 return rc;
277}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278
279/* If the return code is zero, this function must fill in request_buf pointer */
280static int
Jeff Laytonf5695992010-09-29 15:27:08 -0400281__smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
282 void **request_buf, void **response_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 *request_buf = cifs_buf_get();
285 if (*request_buf == NULL) {
286 /* BB should we add a retry in here if not a writepage? */
287 return -ENOMEM;
288 }
289 /* Although the original thought was we needed the response buf for */
290 /* potential retries of smb operations it turns out we can determine */
291 /* from the mid flags when the request buffer can be resent without */
292 /* having to use a second distinct buffer for the response */
Steve French790fe572007-07-07 19:25:05 +0000293 if (response_buf)
Steve French50c2f752007-07-13 00:33:32 +0000294 *response_buf = *request_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296 header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000297 wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
Steve French790fe572007-07-07 19:25:05 +0000299 if (tcon != NULL)
300 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700301
Jeff Laytonf5695992010-09-29 15:27:08 -0400302 return 0;
303}
304
305/* If the return code is zero, this function must fill in request_buf pointer */
306static int
307smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
308 void **request_buf, void **response_buf)
309{
310 int rc;
311
312 rc = cifs_reconnect_tcon(tcon, smb_command);
313 if (rc)
314 return rc;
315
316 return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
317}
318
319static int
320smb_init_no_reconnect(int smb_command, int wct, struct cifsTconInfo *tcon,
321 void **request_buf, void **response_buf)
322{
323 if (tcon->ses->need_reconnect || tcon->need_reconnect)
324 return -EHOSTDOWN;
325
326 return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327}
328
Steve French50c2f752007-07-13 00:33:32 +0000329static int validate_t2(struct smb_t2_rsp *pSMB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330{
Jeff Layton12df83c2011-01-20 13:36:51 -0500331 unsigned int total_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
Jeff Layton12df83c2011-01-20 13:36:51 -0500333 /* check for plausible wct */
334 if (pSMB->hdr.WordCount < 10)
335 goto vt2_err;
336
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 /* check for parm and data offset going beyond end of smb */
Jeff Layton12df83c2011-01-20 13:36:51 -0500338 if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 ||
339 get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024)
340 goto vt2_err;
341
342 /* check that bcc is at least as big as parms + data */
343 /* check that bcc is less than negotiated smb buffer */
344 total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount);
345 if (total_size >= 512)
346 goto vt2_err;
347
348 total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount);
349 if (total_size > get_bcc(&pSMB->hdr) ||
350 total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE)
351 goto vt2_err;
352
353 return 0;
354vt2_err:
Steve French50c2f752007-07-13 00:33:32 +0000355 cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 sizeof(struct smb_t2_rsp) + 16);
Jeff Layton12df83c2011-01-20 13:36:51 -0500357 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358}
Jeff Layton690c5222011-01-20 13:36:51 -0500359
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360int
361CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
362{
363 NEGOTIATE_REQ *pSMB;
364 NEGOTIATE_RSP *pSMBr;
365 int rc = 0;
366 int bytes_returned;
Steve French39798772006-05-31 22:40:51 +0000367 int i;
Steve French50c2f752007-07-13 00:33:32 +0000368 struct TCP_Server_Info *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 u16 count;
Steve French750d1152006-06-27 06:28:30 +0000370 unsigned int secFlags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
Steve French790fe572007-07-07 19:25:05 +0000372 if (ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 server = ses->server;
374 else {
375 rc = -EIO;
376 return rc;
377 }
378 rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
379 (void **) &pSMB, (void **) &pSMBr);
380 if (rc)
381 return rc;
Steve French750d1152006-06-27 06:28:30 +0000382
383 /* if any of auth flags (ie not sign or seal) are overriden use them */
Steve French790fe572007-07-07 19:25:05 +0000384 if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
Steve French762e5ab2007-06-28 18:41:42 +0000385 secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */
Steve French750d1152006-06-27 06:28:30 +0000386 else /* if override flags set only sign/seal OR them with global auth */
Jeff Layton04912d62010-04-24 07:57:45 -0400387 secFlags = global_secflags | ses->overrideSecFlg;
Steve French750d1152006-06-27 06:28:30 +0000388
Joe Perchesb6b38f72010-04-21 03:50:45 +0000389 cFYI(1, "secFlags 0x%x", secFlags);
Steve Frenchf40c5622006-06-28 00:13:38 +0000390
Steve French1982c342005-08-17 12:38:22 -0700391 pSMB->hdr.Mid = GetNextMid(server);
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000392 pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
Steve Frencha0136892007-10-04 20:05:09 +0000393
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000394 if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
Steve French254e55e2006-06-04 05:53:15 +0000395 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Steve Frencha0136892007-10-04 20:05:09 +0000396 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000397 cFYI(1, "Kerberos only mechanism, enable extended security");
Steve Frencha0136892007-10-04 20:05:09 +0000398 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Jeff Laytonb4d6fcf2011-01-07 11:30:28 -0500399 } else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
Steve Frenchac683922009-05-06 04:16:04 +0000400 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
401 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000402 cFYI(1, "NTLMSSP only mechanism, enable extended security");
Steve Frenchac683922009-05-06 04:16:04 +0000403 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
404 }
Steve French50c2f752007-07-13 00:33:32 +0000405
Steve French39798772006-05-31 22:40:51 +0000406 count = 0;
Steve French50c2f752007-07-13 00:33:32 +0000407 for (i = 0; i < CIFS_NUM_PROT; i++) {
Steve French39798772006-05-31 22:40:51 +0000408 strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
409 count += strlen(protocols[i].name) + 1;
410 /* null at end of source and target buffers anyway */
411 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 pSMB->hdr.smb_buf_length += count;
413 pSMB->ByteCount = cpu_to_le16(count);
414
415 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
416 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French50c2f752007-07-13 00:33:32 +0000417 if (rc != 0)
Steve French254e55e2006-06-04 05:53:15 +0000418 goto neg_err_exit;
419
Jeff Layton9bf67e52010-04-24 07:57:46 -0400420 server->dialect = le16_to_cpu(pSMBr->DialectIndex);
421 cFYI(1, "Dialect: %d", server->dialect);
Steve French254e55e2006-06-04 05:53:15 +0000422 /* Check wct = 1 error case */
Jeff Layton9bf67e52010-04-24 07:57:46 -0400423 if ((pSMBr->hdr.WordCount < 13) || (server->dialect == BAD_PROT)) {
Steve French254e55e2006-06-04 05:53:15 +0000424 /* core returns wct = 1, but we do not ask for core - otherwise
Steve French50c2f752007-07-13 00:33:32 +0000425 small wct just comes when dialect index is -1 indicating we
Steve French254e55e2006-06-04 05:53:15 +0000426 could not negotiate a common dialect */
427 rc = -EOPNOTSUPP;
428 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000429#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French790fe572007-07-07 19:25:05 +0000430 } else if ((pSMBr->hdr.WordCount == 13)
Jeff Layton9bf67e52010-04-24 07:57:46 -0400431 && ((server->dialect == LANMAN_PROT)
432 || (server->dialect == LANMAN2_PROT))) {
Steve Frenchb815f1e2006-10-02 05:53:29 +0000433 __s16 tmp;
Steve French50c2f752007-07-13 00:33:32 +0000434 struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
Steve French254e55e2006-06-04 05:53:15 +0000435
Steve French790fe572007-07-07 19:25:05 +0000436 if ((secFlags & CIFSSEC_MAY_LANMAN) ||
Steve French750d1152006-06-27 06:28:30 +0000437 (secFlags & CIFSSEC_MAY_PLNTXT))
Steve French254e55e2006-06-04 05:53:15 +0000438 server->secType = LANMAN;
439 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000440 cERROR(1, "mount failed weak security disabled"
441 " in /proc/fs/cifs/SecurityFlags");
Steve French39798772006-05-31 22:40:51 +0000442 rc = -EOPNOTSUPP;
443 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000444 }
Steve French254e55e2006-06-04 05:53:15 +0000445 server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
446 server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
447 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
Steve French39798772006-05-31 22:40:51 +0000448 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000449 server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
Steve French254e55e2006-06-04 05:53:15 +0000450 /* even though we do not use raw we might as well set this
451 accurately, in case we ever find a need for it */
Steve French790fe572007-07-07 19:25:05 +0000452 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
Steve Frencheca6acf2009-02-20 05:43:09 +0000453 server->max_rw = 0xFF00;
Steve French254e55e2006-06-04 05:53:15 +0000454 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
455 } else {
Steve Frencheca6acf2009-02-20 05:43:09 +0000456 server->max_rw = 0;/* do not need to use raw anyway */
Steve French254e55e2006-06-04 05:53:15 +0000457 server->capabilities = CAP_MPX_MODE;
458 }
Steve Frenchb815f1e2006-10-02 05:53:29 +0000459 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
Steve French1a70d652006-10-02 05:59:18 +0000460 if (tmp == -1) {
Steve French25ee4a92006-09-30 00:54:23 +0000461 /* OS/2 often does not set timezone therefore
462 * we must use server time to calc time zone.
Steve Frenchb815f1e2006-10-02 05:53:29 +0000463 * Could deviate slightly from the right zone.
464 * Smallest defined timezone difference is 15 minutes
465 * (i.e. Nepal). Rounding up/down is done to match
466 * this requirement.
Steve French25ee4a92006-09-30 00:54:23 +0000467 */
Steve Frenchb815f1e2006-10-02 05:53:29 +0000468 int val, seconds, remain, result;
Steve French25ee4a92006-09-30 00:54:23 +0000469 struct timespec ts, utc;
470 utc = CURRENT_TIME;
Jeff Laytonc4a2c082009-05-27 09:37:33 -0400471 ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
472 rsp->SrvTime.Time, 0);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000473 cFYI(1, "SrvTime %d sec since 1970 (utc: %d) diff: %d",
Steve French50c2f752007-07-13 00:33:32 +0000474 (int)ts.tv_sec, (int)utc.tv_sec,
Joe Perchesb6b38f72010-04-21 03:50:45 +0000475 (int)(utc.tv_sec - ts.tv_sec));
Steve Frenchb815f1e2006-10-02 05:53:29 +0000476 val = (int)(utc.tv_sec - ts.tv_sec);
Andre Haupt8594c152007-08-30 20:18:41 +0000477 seconds = abs(val);
Steve French947a5062006-10-02 05:55:25 +0000478 result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
Steve Frenchb815f1e2006-10-02 05:53:29 +0000479 remain = seconds % MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000480 if (remain >= (MIN_TZ_ADJ / 2))
Steve Frenchb815f1e2006-10-02 05:53:29 +0000481 result += MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000482 if (val < 0)
Steve Frenchad7a2922008-02-07 23:25:02 +0000483 result = -result;
Steve Frenchb815f1e2006-10-02 05:53:29 +0000484 server->timeAdj = result;
Steve French25ee4a92006-09-30 00:54:23 +0000485 } else {
Steve Frenchb815f1e2006-10-02 05:53:29 +0000486 server->timeAdj = (int)tmp;
487 server->timeAdj *= 60; /* also in seconds */
Steve French25ee4a92006-09-30 00:54:23 +0000488 }
Joe Perchesb6b38f72010-04-21 03:50:45 +0000489 cFYI(1, "server->timeAdj: %d seconds", server->timeAdj);
Steve French25ee4a92006-09-30 00:54:23 +0000490
Steve French39798772006-05-31 22:40:51 +0000491
Steve French254e55e2006-06-04 05:53:15 +0000492 /* BB get server time for time conversions and add
Steve French50c2f752007-07-13 00:33:32 +0000493 code to use it and timezone since this is not UTC */
Steve French39798772006-05-31 22:40:51 +0000494
Steve French50c2f752007-07-13 00:33:32 +0000495 if (rsp->EncryptionKeyLength ==
Steve French25ee4a92006-09-30 00:54:23 +0000496 cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
Shirish Pargaonkard3ba50b2010-10-27 15:20:36 -0500497 memcpy(ses->server->cryptkey, rsp->EncryptionKey,
Steve French254e55e2006-06-04 05:53:15 +0000498 CIFS_CRYPTO_KEY_SIZE);
499 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
500 rc = -EIO; /* need cryptkey unless plain text */
501 goto neg_err_exit;
502 }
Steve French39798772006-05-31 22:40:51 +0000503
Steve Frenchf19159d2010-04-21 04:12:10 +0000504 cFYI(1, "LANMAN negotiated");
Steve French254e55e2006-06-04 05:53:15 +0000505 /* we will not end up setting signing flags - as no signing
506 was in LANMAN and server did not return the flags on */
507 goto signing_check;
Steve French7c7b25b2006-06-01 19:20:10 +0000508#else /* weak security disabled */
Steve French790fe572007-07-07 19:25:05 +0000509 } else if (pSMBr->hdr.WordCount == 13) {
Steve Frenchf19159d2010-04-21 04:12:10 +0000510 cERROR(1, "mount failed, cifs module not built "
511 "with CIFS_WEAK_PW_HASH support");
Dan Carpenter8212cf72010-03-15 11:22:26 +0300512 rc = -EOPNOTSUPP;
Steve French7c7b25b2006-06-01 19:20:10 +0000513#endif /* WEAK_PW_HASH */
Steve French254e55e2006-06-04 05:53:15 +0000514 goto neg_err_exit;
Steve French790fe572007-07-07 19:25:05 +0000515 } else if (pSMBr->hdr.WordCount != 17) {
Steve French254e55e2006-06-04 05:53:15 +0000516 /* unknown wct */
517 rc = -EOPNOTSUPP;
518 goto neg_err_exit;
519 }
520 /* else wct == 17 NTLM */
521 server->secMode = pSMBr->SecurityMode;
Steve French790fe572007-07-07 19:25:05 +0000522 if ((server->secMode & SECMODE_USER) == 0)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000523 cFYI(1, "share mode security");
Steve French39798772006-05-31 22:40:51 +0000524
Steve French790fe572007-07-07 19:25:05 +0000525 if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000526#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French750d1152006-06-27 06:28:30 +0000527 if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000528#endif /* CIFS_WEAK_PW_HASH */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000529 cERROR(1, "Server requests plain text password"
530 " but client support disabled");
Steve French9312f672006-06-04 22:21:07 +0000531
Steve French790fe572007-07-07 19:25:05 +0000532 if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
Steve French254e55e2006-06-04 05:53:15 +0000533 server->secType = NTLMv2;
Steve French790fe572007-07-07 19:25:05 +0000534 else if (secFlags & CIFSSEC_MAY_NTLM)
Steve French254e55e2006-06-04 05:53:15 +0000535 server->secType = NTLM;
Steve French790fe572007-07-07 19:25:05 +0000536 else if (secFlags & CIFSSEC_MAY_NTLMV2)
Steve Frenchf40c5622006-06-28 00:13:38 +0000537 server->secType = NTLMv2;
Steve Frencha0136892007-10-04 20:05:09 +0000538 else if (secFlags & CIFSSEC_MAY_KRB5)
539 server->secType = Kerberos;
Steve Frenchac683922009-05-06 04:16:04 +0000540 else if (secFlags & CIFSSEC_MAY_NTLMSSP)
Steve Frenchf46c7232009-06-25 03:04:20 +0000541 server->secType = RawNTLMSSP;
Steve Frencha0136892007-10-04 20:05:09 +0000542 else if (secFlags & CIFSSEC_MAY_LANMAN)
543 server->secType = LANMAN;
544/* #ifdef CONFIG_CIFS_EXPERIMENTAL
545 else if (secFlags & CIFSSEC_MAY_PLNTXT)
546 server->secType = ??
547#endif */
548 else {
549 rc = -EOPNOTSUPP;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000550 cERROR(1, "Invalid security type");
Steve Frencha0136892007-10-04 20:05:09 +0000551 goto neg_err_exit;
552 }
553 /* else ... any others ...? */
Steve French7c7b25b2006-06-01 19:20:10 +0000554
Steve French254e55e2006-06-04 05:53:15 +0000555 /* one byte, so no need to convert this or EncryptionKeyLen from
556 little endian */
557 server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
558 /* probably no need to store and check maxvcs */
559 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000561 server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000562 cFYI(DBG2, "Max buf = %d", ses->server->maxBuf);
Steve French254e55e2006-06-04 05:53:15 +0000563 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
Steve Frenchb815f1e2006-10-02 05:53:29 +0000564 server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
565 server->timeAdj *= 60;
Steve French254e55e2006-06-04 05:53:15 +0000566 if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
Shirish Pargaonkard3ba50b2010-10-27 15:20:36 -0500567 memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey,
Steve French254e55e2006-06-04 05:53:15 +0000568 CIFS_CRYPTO_KEY_SIZE);
569 } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
570 && (pSMBr->EncryptionKeyLength == 0)) {
571 /* decode security blob */
572 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
573 rc = -EIO; /* no crypt key only if plain text pwd */
574 goto neg_err_exit;
575 }
576
577 /* BB might be helpful to save off the domain of server here */
578
Steve French50c2f752007-07-13 00:33:32 +0000579 if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
Steve French254e55e2006-06-04 05:53:15 +0000580 (server->capabilities & CAP_EXTENDED_SECURITY)) {
581 count = pSMBr->ByteCount;
Jeff Laytone187e442007-10-16 17:10:44 +0000582 if (count < 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 rc = -EIO;
Jeff Laytone187e442007-10-16 17:10:44 +0000584 goto neg_err_exit;
585 }
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530586 spin_lock(&cifs_tcp_ses_lock);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500587 if (server->srv_count > 1) {
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530588 spin_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000589 if (memcmp(server->server_GUID,
590 pSMBr->u.extended_response.
591 GUID, 16) != 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000592 cFYI(1, "server UID changed");
Steve French254e55e2006-06-04 05:53:15 +0000593 memcpy(server->server_GUID,
Jeff Laytone187e442007-10-16 17:10:44 +0000594 pSMBr->u.extended_response.GUID,
595 16);
596 }
Jeff Laytone7ddee92008-11-14 13:44:38 -0500597 } else {
Suresh Jayaraman3f9bcca2010-10-18 23:29:37 +0530598 spin_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000599 memcpy(server->server_GUID,
600 pSMBr->u.extended_response.GUID, 16);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500601 }
Jeff Laytone187e442007-10-16 17:10:44 +0000602
603 if (count == 16) {
604 server->secType = RawNTLMSSP;
Steve French254e55e2006-06-04 05:53:15 +0000605 } else {
606 rc = decode_negTokenInit(pSMBr->u.extended_response.
Jeff Layton26efa0b2010-04-24 07:57:49 -0400607 SecurityBlob, count - 16,
608 server);
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000609 if (rc == 1)
Jeff Laytone5459372007-11-03 05:11:06 +0000610 rc = 0;
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000611 else
Steve French254e55e2006-06-04 05:53:15 +0000612 rc = -EINVAL;
Shirish Pargaonkar2b149f12010-09-18 22:02:18 -0500613 if (server->secType == Kerberos) {
614 if (!server->sec_kerberos &&
615 !server->sec_mskerberos)
616 rc = -EOPNOTSUPP;
617 } else if (server->secType == RawNTLMSSP) {
618 if (!server->sec_ntlmssp)
619 rc = -EOPNOTSUPP;
620 } else
621 rc = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 }
Steve French254e55e2006-06-04 05:53:15 +0000623 } else
624 server->capabilities &= ~CAP_EXTENDED_SECURITY;
625
Steve French6344a422006-06-12 04:18:35 +0000626#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French254e55e2006-06-04 05:53:15 +0000627signing_check:
Steve French6344a422006-06-12 04:18:35 +0000628#endif
Steve French762e5ab2007-06-28 18:41:42 +0000629 if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
630 /* MUST_SIGN already includes the MAY_SIGN FLAG
631 so if this is zero it means that signing is disabled */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000632 cFYI(1, "Signing disabled");
Steve Frenchabb63d62007-10-18 02:58:40 +0000633 if (server->secMode & SECMODE_SIGN_REQUIRED) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000634 cERROR(1, "Server requires "
Jeff Layton7111d212007-10-16 16:50:25 +0000635 "packet signing to be enabled in "
Joe Perchesb6b38f72010-04-21 03:50:45 +0000636 "/proc/fs/cifs/SecurityFlags.");
Steve Frenchabb63d62007-10-18 02:58:40 +0000637 rc = -EOPNOTSUPP;
638 }
Steve French50c2f752007-07-13 00:33:32 +0000639 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000640 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Steve French762e5ab2007-06-28 18:41:42 +0000641 } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
642 /* signing required */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000643 cFYI(1, "Must sign - secFlags 0x%x", secFlags);
Steve French762e5ab2007-06-28 18:41:42 +0000644 if ((server->secMode &
645 (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000646 cERROR(1, "signing required but server lacks support");
Jeff38c10a12007-07-06 21:10:07 +0000647 rc = -EOPNOTSUPP;
Steve French762e5ab2007-06-28 18:41:42 +0000648 } else
649 server->secMode |= SECMODE_SIGN_REQUIRED;
650 } else {
651 /* signing optional ie CIFSSEC_MAY_SIGN */
Steve French790fe572007-07-07 19:25:05 +0000652 if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
Steve French50c2f752007-07-13 00:33:32 +0000653 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000654 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 }
Steve French50c2f752007-07-13 00:33:32 +0000656
657neg_err_exit:
Steve French4a6d87f2005-08-13 08:15:54 -0700658 cifs_buf_release(pSMB);
Steve French254e55e2006-06-04 05:53:15 +0000659
Joe Perchesb6b38f72010-04-21 03:50:45 +0000660 cFYI(1, "negprot rc %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 return rc;
662}
663
664int
665CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
666{
667 struct smb_hdr *smb_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
Joe Perchesb6b38f72010-04-21 03:50:45 +0000670 cFYI(1, "In tree disconnect");
Jeff Laytonf1987b42008-11-15 11:12:47 -0500671
672 /* BB: do we need to check this? These should never be NULL. */
673 if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
674 return -EIO;
675
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 /*
Jeff Laytonf1987b42008-11-15 11:12:47 -0500677 * No need to return error on this operation if tid invalidated and
678 * closed on server already e.g. due to tcp session crashing. Also,
679 * the tcon is no longer on the list, so no need to take lock before
680 * checking this.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 */
Steve French268875b2009-06-25 00:29:21 +0000682 if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
Steve French50c2f752007-07-13 00:33:32 +0000683 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
Steve French50c2f752007-07-13 00:33:32 +0000685 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
Steve French09d1db52005-04-28 22:41:08 -0700686 (void **)&smb_buffer);
Jeff Laytonf1987b42008-11-15 11:12:47 -0500687 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 return rc;
Steve French133672e2007-11-13 22:41:37 +0000689
690 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000692 cFYI(1, "Tree disconnect failed %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Steve French50c2f752007-07-13 00:33:32 +0000694 /* No need to return error on this operation if tid invalidated and
Jeff Laytonf1987b42008-11-15 11:12:47 -0500695 closed on server already e.g. due to tcp session crashing */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 if (rc == -EAGAIN)
697 rc = 0;
698
699 return rc;
700}
701
Jeff Layton766fdbb2011-01-11 07:24:21 -0500702/*
703 * This is a no-op for now. We're not really interested in the reply, but
704 * rather in the fact that the server sent one and that server->lstrp
705 * gets updated.
706 *
707 * FIXME: maybe we should consider checking that the reply matches request?
708 */
709static void
710cifs_echo_callback(struct mid_q_entry *mid)
711{
712 struct TCP_Server_Info *server = mid->callback_data;
713
714 DeleteMidQEntry(mid);
715 atomic_dec(&server->inFlight);
716 wake_up(&server->request_q);
717}
718
719int
720CIFSSMBEcho(struct TCP_Server_Info *server)
721{
722 ECHO_REQ *smb;
723 int rc = 0;
724
725 cFYI(1, "In echo request");
726
727 rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb);
728 if (rc)
729 return rc;
730
731 /* set up echo request */
Steve French5443d132011-03-13 05:08:25 +0000732 smb->hdr.Tid = 0xffff;
Jeff Layton99d86c82011-01-20 21:19:25 -0500733 smb->hdr.WordCount = 1;
734 put_unaligned_le16(1, &smb->EchoCount);
735 put_bcc_le(1, &smb->hdr);
Jeff Layton766fdbb2011-01-11 07:24:21 -0500736 smb->Data[0] = 'a';
737 smb->hdr.smb_buf_length += 3;
738
739 rc = cifs_call_async(server, (struct smb_hdr *)smb,
740 cifs_echo_callback, server);
741 if (rc)
742 cFYI(1, "Echo request failed: %d", rc);
743
744 cifs_small_buf_release(smb);
745
746 return rc;
747}
748
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749int
750CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
751{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 LOGOFF_ANDX_REQ *pSMB;
753 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Joe Perchesb6b38f72010-04-21 03:50:45 +0000755 cFYI(1, "In SMBLogoff for session disconnect");
Jeff Layton14fbf502008-11-14 13:53:46 -0500756
757 /*
758 * BB: do we need to check validity of ses and server? They should
759 * always be valid since we have an active reference. If not, that
760 * should probably be a BUG()
761 */
762 if (!ses || !ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 return -EIO;
764
Steve Frenchd7b619c2010-02-25 05:36:46 +0000765 mutex_lock(&ses->session_mutex);
Steve French3b795212008-11-13 19:45:32 +0000766 if (ses->need_reconnect)
767 goto session_already_dead; /* no need to send SMBlogoff if uid
768 already closed due to reconnect */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
770 if (rc) {
Steve Frenchd7b619c2010-02-25 05:36:46 +0000771 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 return rc;
773 }
774
Steve French3b795212008-11-13 19:45:32 +0000775 pSMB->hdr.Mid = GetNextMid(ses->server);
Steve French1982c342005-08-17 12:38:22 -0700776
Steve French3b795212008-11-13 19:45:32 +0000777 if (ses->server->secMode &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
779 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
781 pSMB->hdr.Uid = ses->Suid;
782
783 pSMB->AndXCommand = 0xFF;
Steve French133672e2007-11-13 22:41:37 +0000784 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
Steve French3b795212008-11-13 19:45:32 +0000785session_already_dead:
Steve Frenchd7b619c2010-02-25 05:36:46 +0000786 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
788 /* if session dead then we do not need to do ulogoff,
Steve French50c2f752007-07-13 00:33:32 +0000789 since server closed smb session, no sense reporting
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 error */
791 if (rc == -EAGAIN)
792 rc = 0;
793 return rc;
794}
795
796int
Steve French2d785a52007-07-15 01:48:57 +0000797CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
798 __u16 type, const struct nls_table *nls_codepage, int remap)
799{
800 TRANSACTION2_SPI_REQ *pSMB = NULL;
801 TRANSACTION2_SPI_RSP *pSMBr = NULL;
802 struct unlink_psx_rq *pRqD;
803 int name_len;
804 int rc = 0;
805 int bytes_returned = 0;
806 __u16 params, param_offset, offset, byte_count;
807
Joe Perchesb6b38f72010-04-21 03:50:45 +0000808 cFYI(1, "In POSIX delete");
Steve French2d785a52007-07-15 01:48:57 +0000809PsxDelete:
810 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
811 (void **) &pSMBr);
812 if (rc)
813 return rc;
814
815 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
816 name_len =
817 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
818 PATH_MAX, nls_codepage, remap);
819 name_len++; /* trailing null */
820 name_len *= 2;
821 } else { /* BB add path length overrun check */
822 name_len = strnlen(fileName, PATH_MAX);
823 name_len++; /* trailing null */
824 strncpy(pSMB->FileName, fileName, name_len);
825 }
826
827 params = 6 + name_len;
828 pSMB->MaxParameterCount = cpu_to_le16(2);
829 pSMB->MaxDataCount = 0; /* BB double check this with jra */
830 pSMB->MaxSetupCount = 0;
831 pSMB->Reserved = 0;
832 pSMB->Flags = 0;
833 pSMB->Timeout = 0;
834 pSMB->Reserved2 = 0;
835 param_offset = offsetof(struct smb_com_transaction2_spi_req,
836 InformationLevel) - 4;
837 offset = param_offset + params;
838
839 /* Setup pointer to Request Data (inode type) */
840 pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
841 pRqD->type = cpu_to_le16(type);
842 pSMB->ParameterOffset = cpu_to_le16(param_offset);
843 pSMB->DataOffset = cpu_to_le16(offset);
844 pSMB->SetupCount = 1;
845 pSMB->Reserved3 = 0;
846 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
847 byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
848
849 pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
850 pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
851 pSMB->ParameterCount = cpu_to_le16(params);
852 pSMB->TotalParameterCount = pSMB->ParameterCount;
853 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
854 pSMB->Reserved4 = 0;
855 pSMB->hdr.smb_buf_length += byte_count;
856 pSMB->ByteCount = cpu_to_le16(byte_count);
857 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
858 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +0000859 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000860 cFYI(1, "Posix delete returned %d", rc);
Steve French2d785a52007-07-15 01:48:57 +0000861 cifs_buf_release(pSMB);
862
863 cifs_stats_inc(&tcon->num_deletes);
864
865 if (rc == -EAGAIN)
866 goto PsxDelete;
867
868 return rc;
869}
870
871int
Steve French737b7582005-04-28 22:41:06 -0700872CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
873 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874{
875 DELETE_FILE_REQ *pSMB = NULL;
876 DELETE_FILE_RSP *pSMBr = NULL;
877 int rc = 0;
878 int bytes_returned;
879 int name_len;
880
881DelFileRetry:
882 rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
883 (void **) &pSMBr);
884 if (rc)
885 return rc;
886
887 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
888 name_len =
Steve French50c2f752007-07-13 00:33:32 +0000889 cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
Steve French737b7582005-04-28 22:41:06 -0700890 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 name_len++; /* trailing null */
892 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700893 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 name_len = strnlen(fileName, PATH_MAX);
895 name_len++; /* trailing null */
896 strncpy(pSMB->fileName, fileName, name_len);
897 }
898 pSMB->SearchAttributes =
899 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
900 pSMB->BufferFormat = 0x04;
901 pSMB->hdr.smb_buf_length += name_len + 1;
902 pSMB->ByteCount = cpu_to_le16(name_len + 1);
903 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
904 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700905 cifs_stats_inc(&tcon->num_deletes);
Steve Frenchad7a2922008-02-07 23:25:02 +0000906 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000907 cFYI(1, "Error in RMFile = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
909 cifs_buf_release(pSMB);
910 if (rc == -EAGAIN)
911 goto DelFileRetry;
912
913 return rc;
914}
915
916int
Steve French50c2f752007-07-13 00:33:32 +0000917CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
Steve French737b7582005-04-28 22:41:06 -0700918 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919{
920 DELETE_DIRECTORY_REQ *pSMB = NULL;
921 DELETE_DIRECTORY_RSP *pSMBr = NULL;
922 int rc = 0;
923 int bytes_returned;
924 int name_len;
925
Joe Perchesb6b38f72010-04-21 03:50:45 +0000926 cFYI(1, "In CIFSSMBRmDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927RmDirRetry:
928 rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
929 (void **) &pSMBr);
930 if (rc)
931 return rc;
932
933 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French737b7582005-04-28 22:41:06 -0700934 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
935 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 name_len++; /* trailing null */
937 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700938 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 name_len = strnlen(dirName, PATH_MAX);
940 name_len++; /* trailing null */
941 strncpy(pSMB->DirName, dirName, name_len);
942 }
943
944 pSMB->BufferFormat = 0x04;
945 pSMB->hdr.smb_buf_length += name_len + 1;
946 pSMB->ByteCount = cpu_to_le16(name_len + 1);
947 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
948 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700949 cifs_stats_inc(&tcon->num_rmdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000950 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000951 cFYI(1, "Error in RMDir = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952
953 cifs_buf_release(pSMB);
954 if (rc == -EAGAIN)
955 goto RmDirRetry;
956 return rc;
957}
958
959int
960CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -0700961 const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962{
963 int rc = 0;
964 CREATE_DIRECTORY_REQ *pSMB = NULL;
965 CREATE_DIRECTORY_RSP *pSMBr = NULL;
966 int bytes_returned;
967 int name_len;
968
Joe Perchesb6b38f72010-04-21 03:50:45 +0000969 cFYI(1, "In CIFSSMBMkDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970MkDirRetry:
971 rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
972 (void **) &pSMBr);
973 if (rc)
974 return rc;
975
976 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +0000977 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
Steve French737b7582005-04-28 22:41:06 -0700978 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 name_len++; /* trailing null */
980 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700981 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 name_len = strnlen(name, PATH_MAX);
983 name_len++; /* trailing null */
984 strncpy(pSMB->DirName, name, name_len);
985 }
986
987 pSMB->BufferFormat = 0x04;
988 pSMB->hdr.smb_buf_length += name_len + 1;
989 pSMB->ByteCount = cpu_to_le16(name_len + 1);
990 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
991 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700992 cifs_stats_inc(&tcon->num_mkdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000993 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000994 cFYI(1, "Error in Mkdir = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -0700995
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 cifs_buf_release(pSMB);
997 if (rc == -EAGAIN)
998 goto MkDirRetry;
999 return rc;
1000}
1001
Steve French2dd29d32007-04-23 22:07:35 +00001002int
1003CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
Steve Frenchad7a2922008-02-07 23:25:02 +00001004 __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
Steve French50c2f752007-07-13 00:33:32 +00001005 __u32 *pOplock, const char *name,
Steve French2dd29d32007-04-23 22:07:35 +00001006 const struct nls_table *nls_codepage, int remap)
1007{
1008 TRANSACTION2_SPI_REQ *pSMB = NULL;
1009 TRANSACTION2_SPI_RSP *pSMBr = NULL;
1010 int name_len;
1011 int rc = 0;
1012 int bytes_returned = 0;
Steve French2dd29d32007-04-23 22:07:35 +00001013 __u16 params, param_offset, offset, byte_count, count;
Steve Frenchad7a2922008-02-07 23:25:02 +00001014 OPEN_PSX_REQ *pdata;
1015 OPEN_PSX_RSP *psx_rsp;
Steve French2dd29d32007-04-23 22:07:35 +00001016
Joe Perchesb6b38f72010-04-21 03:50:45 +00001017 cFYI(1, "In POSIX Create");
Steve French2dd29d32007-04-23 22:07:35 +00001018PsxCreat:
1019 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
1020 (void **) &pSMBr);
1021 if (rc)
1022 return rc;
1023
1024 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1025 name_len =
1026 cifsConvertToUCS((__le16 *) pSMB->FileName, name,
1027 PATH_MAX, nls_codepage, remap);
1028 name_len++; /* trailing null */
1029 name_len *= 2;
1030 } else { /* BB improve the check for buffer overruns BB */
1031 name_len = strnlen(name, PATH_MAX);
1032 name_len++; /* trailing null */
1033 strncpy(pSMB->FileName, name, name_len);
1034 }
1035
1036 params = 6 + name_len;
1037 count = sizeof(OPEN_PSX_REQ);
1038 pSMB->MaxParameterCount = cpu_to_le16(2);
1039 pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
1040 pSMB->MaxSetupCount = 0;
1041 pSMB->Reserved = 0;
1042 pSMB->Flags = 0;
1043 pSMB->Timeout = 0;
1044 pSMB->Reserved2 = 0;
1045 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00001046 InformationLevel) - 4;
Steve French2dd29d32007-04-23 22:07:35 +00001047 offset = param_offset + params;
Steve French2dd29d32007-04-23 22:07:35 +00001048 pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001049 pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
Steve French2dd29d32007-04-23 22:07:35 +00001050 pdata->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00001051 pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
Steve French2dd29d32007-04-23 22:07:35 +00001052 pdata->OpenFlags = cpu_to_le32(*pOplock);
1053 pSMB->ParameterOffset = cpu_to_le16(param_offset);
1054 pSMB->DataOffset = cpu_to_le16(offset);
1055 pSMB->SetupCount = 1;
1056 pSMB->Reserved3 = 0;
1057 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
1058 byte_count = 3 /* pad */ + params + count;
1059
1060 pSMB->DataCount = cpu_to_le16(count);
1061 pSMB->ParameterCount = cpu_to_le16(params);
1062 pSMB->TotalDataCount = pSMB->DataCount;
1063 pSMB->TotalParameterCount = pSMB->ParameterCount;
1064 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1065 pSMB->Reserved4 = 0;
Steve French50c2f752007-07-13 00:33:32 +00001066 pSMB->hdr.smb_buf_length += byte_count;
Steve French2dd29d32007-04-23 22:07:35 +00001067 pSMB->ByteCount = cpu_to_le16(byte_count);
1068 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1069 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1070 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001071 cFYI(1, "Posix create returned %d", rc);
Steve French2dd29d32007-04-23 22:07:35 +00001072 goto psx_create_err;
1073 }
1074
Joe Perchesb6b38f72010-04-21 03:50:45 +00001075 cFYI(1, "copying inode info");
Steve French2dd29d32007-04-23 22:07:35 +00001076 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1077
1078 if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
1079 rc = -EIO; /* bad smb */
1080 goto psx_create_err;
1081 }
1082
1083 /* copy return information to pRetData */
Steve French50c2f752007-07-13 00:33:32 +00001084 psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
Steve French2dd29d32007-04-23 22:07:35 +00001085 + le16_to_cpu(pSMBr->t2.DataOffset));
Steve French50c2f752007-07-13 00:33:32 +00001086
Steve French2dd29d32007-04-23 22:07:35 +00001087 *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
Steve French790fe572007-07-07 19:25:05 +00001088 if (netfid)
Steve French2dd29d32007-04-23 22:07:35 +00001089 *netfid = psx_rsp->Fid; /* cifs fid stays in le */
1090 /* Let caller know file was created so we can set the mode. */
1091 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001092 if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
Steve French2dd29d32007-04-23 22:07:35 +00001093 *pOplock |= CIFS_CREATE_ACTION;
1094 /* check to make sure response data is there */
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001095 if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1096 pRetData->Type = cpu_to_le32(-1); /* unknown */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001097 cFYI(DBG2, "unknown type");
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001098 } else {
Steve French790fe572007-07-07 19:25:05 +00001099 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001100 + sizeof(FILE_UNIX_BASIC_INFO)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001101 cERROR(1, "Open response data too small");
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001102 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001103 goto psx_create_err;
1104 }
Steve French50c2f752007-07-13 00:33:32 +00001105 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001106 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001107 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001108 }
Steve French2dd29d32007-04-23 22:07:35 +00001109
1110psx_create_err:
1111 cifs_buf_release(pSMB);
1112
Steve French65bc98b2009-07-10 15:27:25 +00001113 if (posix_flags & SMB_O_DIRECTORY)
1114 cifs_stats_inc(&tcon->num_posixmkdirs);
1115 else
1116 cifs_stats_inc(&tcon->num_posixopens);
Steve French2dd29d32007-04-23 22:07:35 +00001117
1118 if (rc == -EAGAIN)
1119 goto PsxCreat;
1120
Steve French50c2f752007-07-13 00:33:32 +00001121 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001122}
1123
Steve Frencha9d02ad2005-08-24 23:06:05 -07001124static __u16 convert_disposition(int disposition)
1125{
1126 __u16 ofun = 0;
1127
1128 switch (disposition) {
1129 case FILE_SUPERSEDE:
1130 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1131 break;
1132 case FILE_OPEN:
1133 ofun = SMBOPEN_OAPPEND;
1134 break;
1135 case FILE_CREATE:
1136 ofun = SMBOPEN_OCREATE;
1137 break;
1138 case FILE_OPEN_IF:
1139 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1140 break;
1141 case FILE_OVERWRITE:
1142 ofun = SMBOPEN_OTRUNC;
1143 break;
1144 case FILE_OVERWRITE_IF:
1145 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1146 break;
1147 default:
Joe Perchesb6b38f72010-04-21 03:50:45 +00001148 cFYI(1, "unknown disposition %d", disposition);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001149 ofun = SMBOPEN_OAPPEND; /* regular open */
1150 }
1151 return ofun;
1152}
1153
Jeff Layton35fc37d2008-05-14 10:22:03 -07001154static int
1155access_flags_to_smbopen_mode(const int access_flags)
1156{
1157 int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
1158
1159 if (masked_flags == GENERIC_READ)
1160 return SMBOPEN_READ;
1161 else if (masked_flags == GENERIC_WRITE)
1162 return SMBOPEN_WRITE;
1163
1164 /* just go for read/write */
1165 return SMBOPEN_READWRITE;
1166}
1167
Steve Frencha9d02ad2005-08-24 23:06:05 -07001168int
1169SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1170 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001171 const int access_flags, const int create_options, __u16 *netfid,
1172 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001173 const struct nls_table *nls_codepage, int remap)
1174{
1175 int rc = -EACCES;
1176 OPENX_REQ *pSMB = NULL;
1177 OPENX_RSP *pSMBr = NULL;
1178 int bytes_returned;
1179 int name_len;
1180 __u16 count;
1181
1182OldOpenRetry:
1183 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1184 (void **) &pSMBr);
1185 if (rc)
1186 return rc;
1187
1188 pSMB->AndXCommand = 0xFF; /* none */
1189
1190 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1191 count = 1; /* account for one byte pad to word boundary */
1192 name_len =
1193 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1194 fileName, PATH_MAX, nls_codepage, remap);
1195 name_len++; /* trailing null */
1196 name_len *= 2;
1197 } else { /* BB improve check for buffer overruns BB */
1198 count = 0; /* no pad */
1199 name_len = strnlen(fileName, PATH_MAX);
1200 name_len++; /* trailing null */
1201 strncpy(pSMB->fileName, fileName, name_len);
1202 }
1203 if (*pOplock & REQ_OPLOCK)
1204 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001205 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001206 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001207
Steve Frencha9d02ad2005-08-24 23:06:05 -07001208 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
Jeff Layton35fc37d2008-05-14 10:22:03 -07001209 pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001210 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1211 /* set file as system file if special file such
1212 as fifo and server expecting SFU style and
1213 no Unix extensions */
1214
Steve French790fe572007-07-07 19:25:05 +00001215 if (create_options & CREATE_OPTION_SPECIAL)
1216 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001217 else /* BB FIXME BB */
1218 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001219
Jeff Layton67750fb2008-05-09 22:28:02 +00001220 if (create_options & CREATE_OPTION_READONLY)
1221 pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001222
1223 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001224/* pSMB->CreateOptions = cpu_to_le32(create_options &
1225 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001226 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001227
1228 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001229 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001230 count += name_len;
1231 pSMB->hdr.smb_buf_length += count;
1232
1233 pSMB->ByteCount = cpu_to_le16(count);
1234 /* long_op set to 1 to allow for oplock break timeouts */
1235 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Jeff Layton77499812011-01-11 07:24:23 -05001236 (struct smb_hdr *)pSMBr, &bytes_returned, 0);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001237 cifs_stats_inc(&tcon->num_opens);
1238 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001239 cFYI(1, "Error in Open = %d", rc);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001240 } else {
1241 /* BB verify if wct == 15 */
1242
Steve French582d21e2008-05-13 04:54:12 +00001243/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
Steve Frencha9d02ad2005-08-24 23:06:05 -07001244
1245 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1246 /* Let caller know file was created so we can set the mode. */
1247 /* Do we care about the CreateAction in any other cases? */
1248 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001249/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001250 *pOplock |= CIFS_CREATE_ACTION; */
1251 /* BB FIXME END */
1252
Steve French790fe572007-07-07 19:25:05 +00001253 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001254 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1255 pfile_info->LastAccessTime = 0; /* BB fixme */
1256 pfile_info->LastWriteTime = 0; /* BB fixme */
1257 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001258 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001259 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001260 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001261 pfile_info->AllocationSize =
1262 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1263 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001264 pfile_info->NumberOfLinks = cpu_to_le32(1);
Jeff Layton9a8165f2008-10-17 21:03:20 -04001265 pfile_info->DeletePending = 0;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001266 }
1267 }
1268
1269 cifs_buf_release(pSMB);
1270 if (rc == -EAGAIN)
1271 goto OldOpenRetry;
1272 return rc;
1273}
1274
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275int
1276CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1277 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001278 const int access_flags, const int create_options, __u16 *netfid,
1279 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001280 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281{
1282 int rc = -EACCES;
1283 OPEN_REQ *pSMB = NULL;
1284 OPEN_RSP *pSMBr = NULL;
1285 int bytes_returned;
1286 int name_len;
1287 __u16 count;
1288
1289openRetry:
1290 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1291 (void **) &pSMBr);
1292 if (rc)
1293 return rc;
1294
1295 pSMB->AndXCommand = 0xFF; /* none */
1296
1297 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1298 count = 1; /* account for one byte pad to word boundary */
1299 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001300 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001301 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 name_len++; /* trailing null */
1303 name_len *= 2;
1304 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001305 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 count = 0; /* no pad */
1307 name_len = strnlen(fileName, PATH_MAX);
1308 name_len++; /* trailing null */
1309 pSMB->NameLength = cpu_to_le16(name_len);
1310 strncpy(pSMB->fileName, fileName, name_len);
1311 }
1312 if (*pOplock & REQ_OPLOCK)
1313 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001314 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1317 pSMB->AllocationSize = 0;
Steve Frencheda3c022005-07-21 15:20:28 -07001318 /* set file as system file if special file such
1319 as fifo and server expecting SFU style and
1320 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001321 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c022005-07-21 15:20:28 -07001322 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1323 else
1324 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Jeff Layton67750fb2008-05-09 22:28:02 +00001325
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 /* XP does not handle ATTR_POSIX_SEMANTICS */
1327 /* but it helps speed up case sensitive checks for other
1328 servers such as Samba */
1329 if (tcon->ses->capabilities & CAP_UNIX)
1330 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1331
Jeff Layton67750fb2008-05-09 22:28:02 +00001332 if (create_options & CREATE_OPTION_READONLY)
1333 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);
1334
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1336 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c022005-07-21 15:20:28 -07001337 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001338 /* BB Expirement with various impersonation levels and verify */
1339 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 pSMB->SecurityFlags =
1341 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1342
1343 count += name_len;
1344 pSMB->hdr.smb_buf_length += count;
1345
1346 pSMB->ByteCount = cpu_to_le16(count);
1347 /* long_op set to 1 to allow for oplock break timeouts */
1348 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Jeff Layton77499812011-01-11 07:24:23 -05001349 (struct smb_hdr *)pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001350 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001352 cFYI(1, "Error in Open = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 } else {
Steve French09d1db52005-04-28 22:41:08 -07001354 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1356 /* Let caller know file was created so we can set the mode. */
1357 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001358 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001359 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001360 if (pfile_info) {
Steve French61e74802008-12-03 00:57:54 +00001361 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
1362 36 /* CreationTime to Attributes */);
1363 /* the file_info buf is endian converted by caller */
1364 pfile_info->AllocationSize = pSMBr->AllocationSize;
1365 pfile_info->EndOfFile = pSMBr->EndOfFile;
1366 pfile_info->NumberOfLinks = cpu_to_le32(1);
1367 pfile_info->DeletePending = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001370
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 cifs_buf_release(pSMB);
1372 if (rc == -EAGAIN)
1373 goto openRetry;
1374 return rc;
1375}
1376
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377int
Steve French50c2f752007-07-13 00:33:32 +00001378CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1379 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1380 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381{
1382 int rc = -EACCES;
1383 READ_REQ *pSMB = NULL;
1384 READ_RSP *pSMBr = NULL;
1385 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001386 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001387 int resp_buf_type = 0;
1388 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
Joe Perchesb6b38f72010-04-21 03:50:45 +00001390 cFYI(1, "Reading %d bytes on fid %d", count, netfid);
Steve French790fe572007-07-07 19:25:05 +00001391 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001392 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001393 else {
Steve Frenchbfa0d752005-08-31 21:50:37 -07001394 wct = 10; /* old style read */
Steve French4c3130e2008-12-09 00:28:16 +00001395 if ((lseek >> 32) > 0) {
1396 /* can not handle this big offset for old */
1397 return -EIO;
1398 }
1399 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400
1401 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001402 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 if (rc)
1404 return rc;
1405
1406 /* tcon and ses pointer are checked in smb_init */
1407 if (tcon->ses->server == NULL)
1408 return -ECONNABORTED;
1409
Steve Frenchec637e32005-12-12 20:53:18 -08001410 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 pSMB->Fid = netfid;
1412 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001413 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001414 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve Frenchbfa0d752005-08-31 21:50:37 -07001415
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 pSMB->Remaining = 0;
1417 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1418 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001419 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001420 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1421 else {
1422 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001423 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001424 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001425 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001426 }
Steve Frenchec637e32005-12-12 20:53:18 -08001427
1428 iov[0].iov_base = (char *)pSMB;
1429 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001430 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Jeff Layton77499812011-01-11 07:24:23 -05001431 &resp_buf_type, CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001432 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001433 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001435 cERROR(1, "Send error in read = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 } else {
1437 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1438 data_length = data_length << 16;
1439 data_length += le16_to_cpu(pSMBr->DataLength);
1440 *nbytes = data_length;
1441
1442 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001443 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 || (data_length > count)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001445 cFYI(1, "bad length %d for count %d",
1446 data_length, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 rc = -EIO;
1448 *nbytes = 0;
1449 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001450 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001451 le16_to_cpu(pSMBr->DataOffset);
1452/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001453 cERROR(1, "Faulting on read rc = %d",rc);
Steve French50c2f752007-07-13 00:33:32 +00001454 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001455 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001456 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001457 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 }
1459 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460
Steve French4b8f9302006-02-26 16:41:18 +00001461/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001462 if (*buf) {
1463 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001464 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001465 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001466 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001467 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001468 /* return buffer to caller to free */
1469 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001470 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001471 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001472 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001473 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001474 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001475
1476 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477 since file handle passed in no longer valid */
1478 return rc;
1479}
1480
Steve Frenchec637e32005-12-12 20:53:18 -08001481
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482int
1483CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1484 const int netfid, const unsigned int count,
1485 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001486 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487{
1488 int rc = -EACCES;
1489 WRITE_REQ *pSMB = NULL;
1490 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001491 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 __u32 bytes_sent;
1493 __u16 byte_count;
1494
Steve Frencha24e2d72010-04-03 17:20:21 +00001495 *nbytes = 0;
1496
Joe Perchesb6b38f72010-04-21 03:50:45 +00001497 /* cFYI(1, "write at %lld %d bytes", offset, count);*/
Steve French790fe572007-07-07 19:25:05 +00001498 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001499 return -ECONNABORTED;
1500
Steve French790fe572007-07-07 19:25:05 +00001501 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001502 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001503 else {
Steve French1c955182005-08-30 20:58:07 -07001504 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001505 if ((offset >> 32) > 0) {
1506 /* can not handle big offset for old srv */
1507 return -EIO;
1508 }
1509 }
Steve French1c955182005-08-30 20:58:07 -07001510
1511 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 (void **) &pSMBr);
1513 if (rc)
1514 return rc;
1515 /* tcon and ses pointer are checked in smb_init */
1516 if (tcon->ses->server == NULL)
1517 return -ECONNABORTED;
1518
1519 pSMB->AndXCommand = 0xFF; /* none */
1520 pSMB->Fid = netfid;
1521 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001522 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001523 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French50c2f752007-07-13 00:33:32 +00001524
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 pSMB->Reserved = 0xFFFFFFFF;
1526 pSMB->WriteMode = 0;
1527 pSMB->Remaining = 0;
1528
Steve French50c2f752007-07-13 00:33:32 +00001529 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 can send more if LARGE_WRITE_X capability returned by the server and if
1531 our buffer is big enough or if we convert to iovecs on socket writes
1532 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001533 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1535 } else {
1536 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1537 & ~0xFF;
1538 }
1539
1540 if (bytes_sent > count)
1541 bytes_sent = count;
1542 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001543 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001544 if (buf)
Steve French61e74802008-12-03 00:57:54 +00001545 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001546 else if (ubuf) {
1547 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 cifs_buf_release(pSMB);
1549 return -EFAULT;
1550 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001551 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 /* No buffer */
1553 cifs_buf_release(pSMB);
1554 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001555 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001556 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001557 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001558 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001559 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001560
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1562 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001563 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001564
Steve French790fe572007-07-07 19:25:05 +00001565 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001566 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001567 else { /* old style write has byte count 4 bytes earlier
1568 so 4 bytes pad */
1569 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001570 (struct smb_com_writex_req *)pSMB;
1571 pSMBW->ByteCount = cpu_to_le16(byte_count);
1572 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573
1574 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1575 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001576 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00001578 cFYI(1, "Send error in write = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 } else {
1580 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1581 *nbytes = (*nbytes) << 16;
1582 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301583
1584 /*
1585 * Mask off high 16 bits when bytes written as returned by the
1586 * server is greater than bytes requested by the client. Some
1587 * OS/2 servers are known to set incorrect CountHigh values.
1588 */
1589 if (*nbytes > count)
1590 *nbytes &= 0xFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 }
1592
1593 cifs_buf_release(pSMB);
1594
Steve French50c2f752007-07-13 00:33:32 +00001595 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596 since file handle passed in no longer valid */
1597
1598 return rc;
1599}
1600
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001601int
1602CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001604 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1605 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606{
1607 int rc = -EACCES;
1608 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001609 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001610 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001611 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612
Jeff Laytonfbec9ab2009-04-03 13:44:00 -04001613 *nbytes = 0;
1614
Joe Perchesb6b38f72010-04-21 03:50:45 +00001615 cFYI(1, "write2 at %lld %d bytes", (long long)offset, count);
Steve Frenchff7feac2005-11-15 16:45:16 -08001616
Steve French4c3130e2008-12-09 00:28:16 +00001617 if (tcon->ses->capabilities & CAP_LARGE_FILES) {
Steve French8cc64c62005-10-03 13:49:43 -07001618 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001619 } else {
Steve French8cc64c62005-10-03 13:49:43 -07001620 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001621 if ((offset >> 32) > 0) {
1622 /* can not handle big offset for old srv */
1623 return -EIO;
1624 }
1625 }
Steve French8cc64c62005-10-03 13:49:43 -07001626 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 if (rc)
1628 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 /* tcon and ses pointer are checked in smb_init */
1630 if (tcon->ses->server == NULL)
1631 return -ECONNABORTED;
1632
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001633 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 pSMB->Fid = netfid;
1635 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001636 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001637 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638 pSMB->Reserved = 0xFFFFFFFF;
1639 pSMB->WriteMode = 0;
1640 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001641
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001643 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644
Steve French3e844692005-10-03 13:37:24 -07001645 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1646 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001647 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001648 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001649 pSMB->hdr.smb_buf_length += count+1;
1650 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001651 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1652 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001653 pSMB->ByteCount = cpu_to_le16(count + 1);
1654 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001655 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001656 (struct smb_com_writex_req *)pSMB;
1657 pSMBW->ByteCount = cpu_to_le16(count + 5);
1658 }
Steve French3e844692005-10-03 13:37:24 -07001659 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001660 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001661 iov[0].iov_len = smb_hdr_len + 4;
1662 else /* wct == 12 pad bigger by four bytes */
1663 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001664
Steve French3e844692005-10-03 13:37:24 -07001665
Steve Frenchec637e32005-12-12 20:53:18 -08001666 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001667 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001668 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001670 cFYI(1, "Send error Write2 = %d", rc);
Steve French790fe572007-07-07 19:25:05 +00001671 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001672 /* presumably this can not happen, but best to be safe */
1673 rc = -EIO;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001674 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001675 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001676 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1677 *nbytes = (*nbytes) << 16;
1678 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301679
1680 /*
1681 * Mask off high 16 bits when bytes written as returned by the
1682 * server is greater than bytes requested by the client. OS/2
1683 * servers are known to set incorrect CountHigh values.
1684 */
1685 if (*nbytes > count)
1686 *nbytes &= 0xFFFF;
Steve French50c2f752007-07-13 00:33:32 +00001687 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688
Steve French4b8f9302006-02-26 16:41:18 +00001689/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001690 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001691 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001692 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001693 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694
Steve French50c2f752007-07-13 00:33:32 +00001695 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 since file handle passed in no longer valid */
1697
1698 return rc;
1699}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001700
1701
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702int
1703CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1704 const __u16 smb_file_id, const __u64 len,
1705 const __u64 offset, const __u32 numUnlock,
Pavel Shilovsky12fed002011-01-17 20:15:44 +03001706 const __u32 numLock, const __u8 lockType,
1707 const bool waitFlag, const __u8 oplock_level)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708{
1709 int rc = 0;
1710 LOCK_REQ *pSMB = NULL;
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001711/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712 int bytes_returned;
1713 int timeout = 0;
1714 __u16 count;
1715
Joe Perchesb6b38f72010-04-21 03:50:45 +00001716 cFYI(1, "CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock);
Steve French46810cb2005-04-28 22:41:09 -07001717 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1718
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719 if (rc)
1720 return rc;
1721
Steve French790fe572007-07-07 19:25:05 +00001722 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001723 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724 pSMB->Timeout = 0;
Steve French4b18f2a2008-04-29 00:06:05 +00001725 } else if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001726 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1728 } else {
1729 pSMB->Timeout = 0;
1730 }
1731
1732 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1733 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1734 pSMB->LockType = lockType;
Pavel Shilovsky12fed002011-01-17 20:15:44 +03001735 pSMB->OplockLevel = oplock_level;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 pSMB->AndXCommand = 0xFF; /* none */
1737 pSMB->Fid = smb_file_id; /* netfid stays le */
1738
Steve French790fe572007-07-07 19:25:05 +00001739 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1741 /* BB where to store pid high? */
1742 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1743 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1744 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1745 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1746 count = sizeof(LOCKING_ANDX_RANGE);
1747 } else {
1748 /* oplock break */
1749 count = 0;
1750 }
1751 pSMB->hdr.smb_buf_length += count;
1752 pSMB->ByteCount = cpu_to_le16(count);
1753
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001754 if (waitFlag) {
1755 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001756 (struct smb_hdr *) pSMB, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001757 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001758 } else {
Steve French133672e2007-11-13 22:41:37 +00001759 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1760 timeout);
1761 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001762 }
Steve Frencha4544342005-08-24 13:59:35 -07001763 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001764 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001765 cFYI(1, "Send error in Lock = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766
Steve French50c2f752007-07-13 00:33:32 +00001767 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768 since file handle passed in no longer valid */
1769 return rc;
1770}
1771
1772int
Steve French08547b02006-02-28 22:39:25 +00001773CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1774 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001775 struct file_lock *pLockData, const __u16 lock_type,
Steve French4b18f2a2008-04-29 00:06:05 +00001776 const bool waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001777{
1778 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1779 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001780 struct cifs_posix_lock *parm_data;
1781 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001782 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001783 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001784 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001785 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001786 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001787
Joe Perchesb6b38f72010-04-21 03:50:45 +00001788 cFYI(1, "Posix Lock");
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001789
Steve French790fe572007-07-07 19:25:05 +00001790 if (pLockData == NULL)
Marcin Slusarzed5f0372008-05-13 04:01:01 +00001791 return -EINVAL;
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001792
Steve French08547b02006-02-28 22:39:25 +00001793 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1794
1795 if (rc)
1796 return rc;
1797
1798 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1799
Steve French50c2f752007-07-13 00:33:32 +00001800 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001801 pSMB->MaxSetupCount = 0;
1802 pSMB->Reserved = 0;
1803 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001804 pSMB->Reserved2 = 0;
1805 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1806 offset = param_offset + params;
1807
Steve French08547b02006-02-28 22:39:25 +00001808 count = sizeof(struct cifs_posix_lock);
1809 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001810 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001811 pSMB->SetupCount = 1;
1812 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001813 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001814 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1815 else
1816 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1817 byte_count = 3 /* pad */ + params + count;
1818 pSMB->DataCount = cpu_to_le16(count);
1819 pSMB->ParameterCount = cpu_to_le16(params);
1820 pSMB->TotalDataCount = pSMB->DataCount;
1821 pSMB->TotalParameterCount = pSMB->ParameterCount;
1822 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001823 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001824 (((char *) &pSMB->hdr.Protocol) + offset);
1825
1826 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001827 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001828 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001829 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001830 pSMB->Timeout = cpu_to_le32(-1);
1831 } else
1832 pSMB->Timeout = 0;
1833
Steve French08547b02006-02-28 22:39:25 +00001834 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001835 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001836 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001837
1838 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001839 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001840 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1841 pSMB->Reserved4 = 0;
1842 pSMB->hdr.smb_buf_length += byte_count;
1843 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001844 if (waitFlag) {
1845 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1846 (struct smb_hdr *) pSMBr, &bytes_returned);
1847 } else {
Steve French133672e2007-11-13 22:41:37 +00001848 iov[0].iov_base = (char *)pSMB;
1849 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1850 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1851 &resp_buf_type, timeout);
1852 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1853 not try to free it twice below on exit */
1854 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001855 }
1856
Steve French08547b02006-02-28 22:39:25 +00001857 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001858 cFYI(1, "Send error in Posix Lock = %d", rc);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001859 } else if (get_flag) {
1860 /* lock structure can be returned on get */
1861 __u16 data_offset;
1862 __u16 data_count;
1863 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001864
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001865 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1866 rc = -EIO; /* bad smb */
1867 goto plk_err_exit;
1868 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001869 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1870 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001871 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001872 rc = -EIO;
1873 goto plk_err_exit;
1874 }
1875 parm_data = (struct cifs_posix_lock *)
1876 ((char *)&pSMBr->hdr.Protocol + data_offset);
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001877 if (parm_data->lock_type == __constant_cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001878 pLockData->fl_type = F_UNLCK;
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001879 else {
1880 if (parm_data->lock_type ==
1881 __constant_cpu_to_le16(CIFS_RDLCK))
1882 pLockData->fl_type = F_RDLCK;
1883 else if (parm_data->lock_type ==
1884 __constant_cpu_to_le16(CIFS_WRLCK))
1885 pLockData->fl_type = F_WRLCK;
1886
Steve French5443d132011-03-13 05:08:25 +00001887 pLockData->fl_start = le64_to_cpu(parm_data->start);
1888 pLockData->fl_end = pLockData->fl_start +
1889 le64_to_cpu(parm_data->length) - 1;
1890 pLockData->fl_pid = le32_to_cpu(parm_data->pid);
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001891 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001892 }
Steve French50c2f752007-07-13 00:33:32 +00001893
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001894plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001895 if (pSMB)
1896 cifs_small_buf_release(pSMB);
1897
Steve French133672e2007-11-13 22:41:37 +00001898 if (resp_buf_type == CIFS_SMALL_BUFFER)
1899 cifs_small_buf_release(iov[0].iov_base);
1900 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1901 cifs_buf_release(iov[0].iov_base);
1902
Steve French08547b02006-02-28 22:39:25 +00001903 /* Note: On -EAGAIN error only caller can retry on handle based calls
1904 since file handle passed in no longer valid */
1905
1906 return rc;
1907}
1908
1909
1910int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1912{
1913 int rc = 0;
1914 CLOSE_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001915 cFYI(1, "In CIFSSMBClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916
1917/* do not retry on dead session on close */
1918 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001919 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920 return 0;
1921 if (rc)
1922 return rc;
1923
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e2006-10-02 05:53:29 +00001925 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001927 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001928 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001930 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 /* EINTR is expected when user ctl-c to kill app */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001932 cERROR(1, "Send error in Close = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 }
1934 }
1935
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001937 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938 rc = 0;
1939
1940 return rc;
1941}
1942
1943int
Steve Frenchb298f222009-02-21 21:17:43 +00001944CIFSSMBFlush(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1945{
1946 int rc = 0;
1947 FLUSH_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001948 cFYI(1, "In CIFSSMBFlush");
Steve Frenchb298f222009-02-21 21:17:43 +00001949
1950 rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB);
1951 if (rc)
1952 return rc;
1953
1954 pSMB->FileID = (__u16) smb_file_id;
1955 pSMB->ByteCount = 0;
1956 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
1957 cifs_stats_inc(&tcon->num_flushes);
1958 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001959 cERROR(1, "Send error in Flush = %d", rc);
Steve Frenchb298f222009-02-21 21:17:43 +00001960
1961 return rc;
1962}
1963
1964int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1966 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001967 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968{
1969 int rc = 0;
1970 RENAME_REQ *pSMB = NULL;
1971 RENAME_RSP *pSMBr = NULL;
1972 int bytes_returned;
1973 int name_len, name_len2;
1974 __u16 count;
1975
Joe Perchesb6b38f72010-04-21 03:50:45 +00001976 cFYI(1, "In CIFSSMBRename");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977renameRetry:
1978 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1979 (void **) &pSMBr);
1980 if (rc)
1981 return rc;
1982
1983 pSMB->BufferFormat = 0x04;
1984 pSMB->SearchAttributes =
1985 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1986 ATTR_DIRECTORY);
1987
1988 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1989 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001990 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001991 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 name_len++; /* trailing null */
1993 name_len *= 2;
1994 pSMB->OldFileName[name_len] = 0x04; /* pad */
1995 /* protocol requires ASCII signature byte on Unicode string */
1996 pSMB->OldFileName[name_len + 1] = 0x00;
1997 name_len2 =
Steve French582d21e2008-05-13 04:54:12 +00001998 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07001999 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2001 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002002 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 name_len = strnlen(fromName, PATH_MAX);
2004 name_len++; /* trailing null */
2005 strncpy(pSMB->OldFileName, fromName, name_len);
2006 name_len2 = strnlen(toName, PATH_MAX);
2007 name_len2++; /* trailing null */
2008 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2009 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2010 name_len2++; /* trailing null */
2011 name_len2++; /* signature byte */
2012 }
2013
2014 count = 1 /* 1st signature byte */ + name_len + name_len2;
2015 pSMB->hdr.smb_buf_length += count;
2016 pSMB->ByteCount = cpu_to_le16(count);
2017
2018 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2019 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002020 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002021 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002022 cFYI(1, "Send error in rename = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 cifs_buf_release(pSMB);
2025
2026 if (rc == -EAGAIN)
2027 goto renameRetry;
2028
2029 return rc;
2030}
2031
Steve French50c2f752007-07-13 00:33:32 +00002032int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
Jeff Layton391e5752008-09-24 11:32:59 -04002033 int netfid, const char *target_name,
Steve French50c2f752007-07-13 00:33:32 +00002034 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035{
2036 struct smb_com_transaction2_sfi_req *pSMB = NULL;
2037 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00002038 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 char *data_offset;
2040 char dummy_string[30];
2041 int rc = 0;
2042 int bytes_returned = 0;
2043 int len_of_str;
2044 __u16 params, param_offset, offset, count, byte_count;
2045
Joe Perchesb6b38f72010-04-21 03:50:45 +00002046 cFYI(1, "Rename to File by handle");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
2048 (void **) &pSMBr);
2049 if (rc)
2050 return rc;
2051
2052 params = 6;
2053 pSMB->MaxSetupCount = 0;
2054 pSMB->Reserved = 0;
2055 pSMB->Flags = 0;
2056 pSMB->Timeout = 0;
2057 pSMB->Reserved2 = 0;
2058 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2059 offset = param_offset + params;
2060
2061 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2062 rename_info = (struct set_file_rename *) data_offset;
2063 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002064 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 pSMB->SetupCount = 1;
2066 pSMB->Reserved3 = 0;
2067 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2068 byte_count = 3 /* pad */ + params;
2069 pSMB->ParameterCount = cpu_to_le16(params);
2070 pSMB->TotalParameterCount = pSMB->ParameterCount;
2071 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2072 pSMB->DataOffset = cpu_to_le16(offset);
2073 /* construct random name ".cifs_tmp<inodenum><mid>" */
2074 rename_info->overwrite = cpu_to_le32(1);
2075 rename_info->root_fid = 0;
2076 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002077 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002078 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2079 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002080 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002082 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002083 target_name, PATH_MAX, nls_codepage,
2084 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 }
2086 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
Jeff Layton391e5752008-09-24 11:32:59 -04002087 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 byte_count += count;
2089 pSMB->DataCount = cpu_to_le16(count);
2090 pSMB->TotalDataCount = pSMB->DataCount;
2091 pSMB->Fid = netfid;
2092 pSMB->InformationLevel =
2093 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2094 pSMB->Reserved4 = 0;
2095 pSMB->hdr.smb_buf_length += byte_count;
2096 pSMB->ByteCount = cpu_to_le16(byte_count);
2097 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002098 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002099 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002100 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002101 cFYI(1, "Send error in Rename (by file handle) = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -07002102
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 cifs_buf_release(pSMB);
2104
2105 /* Note: On -EAGAIN error only caller can retry on handle based calls
2106 since file handle passed in no longer valid */
2107
2108 return rc;
2109}
2110
2111int
Steve French50c2f752007-07-13 00:33:32 +00002112CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2113 const __u16 target_tid, const char *toName, const int flags,
2114 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115{
2116 int rc = 0;
2117 COPY_REQ *pSMB = NULL;
2118 COPY_RSP *pSMBr = NULL;
2119 int bytes_returned;
2120 int name_len, name_len2;
2121 __u16 count;
2122
Joe Perchesb6b38f72010-04-21 03:50:45 +00002123 cFYI(1, "In CIFSSMBCopy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124copyRetry:
2125 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2126 (void **) &pSMBr);
2127 if (rc)
2128 return rc;
2129
2130 pSMB->BufferFormat = 0x04;
2131 pSMB->Tid2 = target_tid;
2132
2133 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2134
2135 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002136 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002137 fromName, PATH_MAX, nls_codepage,
2138 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139 name_len++; /* trailing null */
2140 name_len *= 2;
2141 pSMB->OldFileName[name_len] = 0x04; /* pad */
2142 /* protocol requires ASCII signature byte on Unicode string */
2143 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002144 name_len2 =
2145 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002146 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2148 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002149 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150 name_len = strnlen(fromName, PATH_MAX);
2151 name_len++; /* trailing null */
2152 strncpy(pSMB->OldFileName, fromName, name_len);
2153 name_len2 = strnlen(toName, PATH_MAX);
2154 name_len2++; /* trailing null */
2155 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2156 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2157 name_len2++; /* trailing null */
2158 name_len2++; /* signature byte */
2159 }
2160
2161 count = 1 /* 1st signature byte */ + name_len + name_len2;
2162 pSMB->hdr.smb_buf_length += count;
2163 pSMB->ByteCount = cpu_to_le16(count);
2164
2165 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2166 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2167 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002168 cFYI(1, "Send error in copy = %d with %d files copied",
2169 rc, le16_to_cpu(pSMBr->CopyCount));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 }
Steve French0d817bc2008-05-22 02:02:03 +00002171 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172
2173 if (rc == -EAGAIN)
2174 goto copyRetry;
2175
2176 return rc;
2177}
2178
2179int
2180CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2181 const char *fromName, const char *toName,
2182 const struct nls_table *nls_codepage)
2183{
2184 TRANSACTION2_SPI_REQ *pSMB = NULL;
2185 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2186 char *data_offset;
2187 int name_len;
2188 int name_len_target;
2189 int rc = 0;
2190 int bytes_returned = 0;
2191 __u16 params, param_offset, offset, byte_count;
2192
Joe Perchesb6b38f72010-04-21 03:50:45 +00002193 cFYI(1, "In Symlink Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194createSymLinkRetry:
2195 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2196 (void **) &pSMBr);
2197 if (rc)
2198 return rc;
2199
2200 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2201 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002202 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203 /* find define for this maxpathcomponent */
2204 , nls_codepage);
2205 name_len++; /* trailing null */
2206 name_len *= 2;
2207
Steve French50c2f752007-07-13 00:33:32 +00002208 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209 name_len = strnlen(fromName, PATH_MAX);
2210 name_len++; /* trailing null */
2211 strncpy(pSMB->FileName, fromName, name_len);
2212 }
2213 params = 6 + name_len;
2214 pSMB->MaxSetupCount = 0;
2215 pSMB->Reserved = 0;
2216 pSMB->Flags = 0;
2217 pSMB->Timeout = 0;
2218 pSMB->Reserved2 = 0;
2219 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002220 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 offset = param_offset + params;
2222
2223 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2224 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2225 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002226 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227 /* find define for this maxpathcomponent */
2228 , nls_codepage);
2229 name_len_target++; /* trailing null */
2230 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002231 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232 name_len_target = strnlen(toName, PATH_MAX);
2233 name_len_target++; /* trailing null */
2234 strncpy(data_offset, toName, name_len_target);
2235 }
2236
2237 pSMB->MaxParameterCount = cpu_to_le16(2);
2238 /* BB find exact max on data count below from sess */
2239 pSMB->MaxDataCount = cpu_to_le16(1000);
2240 pSMB->SetupCount = 1;
2241 pSMB->Reserved3 = 0;
2242 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2243 byte_count = 3 /* pad */ + params + name_len_target;
2244 pSMB->DataCount = cpu_to_le16(name_len_target);
2245 pSMB->ParameterCount = cpu_to_le16(params);
2246 pSMB->TotalDataCount = pSMB->DataCount;
2247 pSMB->TotalParameterCount = pSMB->ParameterCount;
2248 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2249 pSMB->DataOffset = cpu_to_le16(offset);
2250 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2251 pSMB->Reserved4 = 0;
2252 pSMB->hdr.smb_buf_length += byte_count;
2253 pSMB->ByteCount = cpu_to_le16(byte_count);
2254 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2255 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002256 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002257 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002258 cFYI(1, "Send error in SetPathInfo create symlink = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259
Steve French0d817bc2008-05-22 02:02:03 +00002260 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261
2262 if (rc == -EAGAIN)
2263 goto createSymLinkRetry;
2264
2265 return rc;
2266}
2267
2268int
2269CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2270 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002271 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272{
2273 TRANSACTION2_SPI_REQ *pSMB = NULL;
2274 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2275 char *data_offset;
2276 int name_len;
2277 int name_len_target;
2278 int rc = 0;
2279 int bytes_returned = 0;
2280 __u16 params, param_offset, offset, byte_count;
2281
Joe Perchesb6b38f72010-04-21 03:50:45 +00002282 cFYI(1, "In Create Hard link Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283createHardLinkRetry:
2284 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2285 (void **) &pSMBr);
2286 if (rc)
2287 return rc;
2288
2289 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002290 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002291 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292 name_len++; /* trailing null */
2293 name_len *= 2;
2294
Steve French50c2f752007-07-13 00:33:32 +00002295 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 name_len = strnlen(toName, PATH_MAX);
2297 name_len++; /* trailing null */
2298 strncpy(pSMB->FileName, toName, name_len);
2299 }
2300 params = 6 + name_len;
2301 pSMB->MaxSetupCount = 0;
2302 pSMB->Reserved = 0;
2303 pSMB->Flags = 0;
2304 pSMB->Timeout = 0;
2305 pSMB->Reserved2 = 0;
2306 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002307 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308 offset = param_offset + params;
2309
2310 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2311 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2312 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002313 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002314 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315 name_len_target++; /* trailing null */
2316 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002317 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318 name_len_target = strnlen(fromName, PATH_MAX);
2319 name_len_target++; /* trailing null */
2320 strncpy(data_offset, fromName, name_len_target);
2321 }
2322
2323 pSMB->MaxParameterCount = cpu_to_le16(2);
2324 /* BB find exact max on data count below from sess*/
2325 pSMB->MaxDataCount = cpu_to_le16(1000);
2326 pSMB->SetupCount = 1;
2327 pSMB->Reserved3 = 0;
2328 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2329 byte_count = 3 /* pad */ + params + name_len_target;
2330 pSMB->ParameterCount = cpu_to_le16(params);
2331 pSMB->TotalParameterCount = pSMB->ParameterCount;
2332 pSMB->DataCount = cpu_to_le16(name_len_target);
2333 pSMB->TotalDataCount = pSMB->DataCount;
2334 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2335 pSMB->DataOffset = cpu_to_le16(offset);
2336 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2337 pSMB->Reserved4 = 0;
2338 pSMB->hdr.smb_buf_length += byte_count;
2339 pSMB->ByteCount = cpu_to_le16(byte_count);
2340 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2341 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002342 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002343 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002344 cFYI(1, "Send error in SetPathInfo (hard link) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345
2346 cifs_buf_release(pSMB);
2347 if (rc == -EAGAIN)
2348 goto createHardLinkRetry;
2349
2350 return rc;
2351}
2352
2353int
2354CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2355 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002356 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357{
2358 int rc = 0;
2359 NT_RENAME_REQ *pSMB = NULL;
2360 RENAME_RSP *pSMBr = NULL;
2361 int bytes_returned;
2362 int name_len, name_len2;
2363 __u16 count;
2364
Joe Perchesb6b38f72010-04-21 03:50:45 +00002365 cFYI(1, "In CIFSCreateHardLink");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366winCreateHardLinkRetry:
2367
2368 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2369 (void **) &pSMBr);
2370 if (rc)
2371 return rc;
2372
2373 pSMB->SearchAttributes =
2374 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2375 ATTR_DIRECTORY);
2376 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2377 pSMB->ClusterCount = 0;
2378
2379 pSMB->BufferFormat = 0x04;
2380
2381 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2382 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002383 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002384 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385 name_len++; /* trailing null */
2386 name_len *= 2;
Jeff Laytonfcc7c092009-02-28 12:59:03 -05002387
2388 /* protocol specifies ASCII buffer format (0x04) for unicode */
2389 pSMB->OldFileName[name_len] = 0x04;
2390 pSMB->OldFileName[name_len + 1] = 0x00; /* pad */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002391 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002392 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002393 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2395 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002396 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 name_len = strnlen(fromName, PATH_MAX);
2398 name_len++; /* trailing null */
2399 strncpy(pSMB->OldFileName, fromName, name_len);
2400 name_len2 = strnlen(toName, PATH_MAX);
2401 name_len2++; /* trailing null */
2402 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2403 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2404 name_len2++; /* trailing null */
2405 name_len2++; /* signature byte */
2406 }
2407
2408 count = 1 /* string type byte */ + name_len + name_len2;
2409 pSMB->hdr.smb_buf_length += count;
2410 pSMB->ByteCount = cpu_to_le16(count);
2411
2412 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2413 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002414 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002415 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002416 cFYI(1, "Send error in hard link (NT rename) = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00002417
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418 cifs_buf_release(pSMB);
2419 if (rc == -EAGAIN)
2420 goto winCreateHardLinkRetry;
2421
2422 return rc;
2423}
2424
2425int
2426CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
Jeff Layton460b9692009-04-30 07:17:56 -04002427 const unsigned char *searchName, char **symlinkinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 const struct nls_table *nls_codepage)
2429{
2430/* SMB_QUERY_FILE_UNIX_LINK */
2431 TRANSACTION2_QPI_REQ *pSMB = NULL;
2432 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2433 int rc = 0;
2434 int bytes_returned;
2435 int name_len;
2436 __u16 params, byte_count;
Jeff Layton460b9692009-04-30 07:17:56 -04002437 char *data_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438
Joe Perchesb6b38f72010-04-21 03:50:45 +00002439 cFYI(1, "In QPathSymLinkInfo (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440
2441querySymLinkRetry:
2442 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2443 (void **) &pSMBr);
2444 if (rc)
2445 return rc;
2446
2447 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2448 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002449 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2450 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451 name_len++; /* trailing null */
2452 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002453 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454 name_len = strnlen(searchName, PATH_MAX);
2455 name_len++; /* trailing null */
2456 strncpy(pSMB->FileName, searchName, name_len);
2457 }
2458
2459 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2460 pSMB->TotalDataCount = 0;
2461 pSMB->MaxParameterCount = cpu_to_le16(2);
Jeff Layton46a75742009-05-24 18:45:17 -04002462 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463 pSMB->MaxSetupCount = 0;
2464 pSMB->Reserved = 0;
2465 pSMB->Flags = 0;
2466 pSMB->Timeout = 0;
2467 pSMB->Reserved2 = 0;
2468 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002469 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470 pSMB->DataCount = 0;
2471 pSMB->DataOffset = 0;
2472 pSMB->SetupCount = 1;
2473 pSMB->Reserved3 = 0;
2474 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2475 byte_count = params + 1 /* pad */ ;
2476 pSMB->TotalParameterCount = cpu_to_le16(params);
2477 pSMB->ParameterCount = pSMB->TotalParameterCount;
2478 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2479 pSMB->Reserved4 = 0;
2480 pSMB->hdr.smb_buf_length += byte_count;
2481 pSMB->ByteCount = cpu_to_le16(byte_count);
2482
2483 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2484 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2485 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002486 cFYI(1, "Send error in QuerySymLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487 } else {
2488 /* decode response */
2489
2490 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491 /* BB also check enough total bytes returned */
Jeff Layton460b9692009-04-30 07:17:56 -04002492 if (rc || (pSMBr->ByteCount < 2))
2493 rc = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494 else {
Steve French0e0d2cf2009-05-01 05:27:32 +00002495 bool is_unicode;
Jeff Layton460b9692009-04-30 07:17:56 -04002496 u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497
Jeff Layton460b9692009-04-30 07:17:56 -04002498 data_start = ((char *) &pSMBr->hdr.Protocol) +
2499 le16_to_cpu(pSMBr->t2.DataOffset);
2500
Steve French0e0d2cf2009-05-01 05:27:32 +00002501 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
2502 is_unicode = true;
2503 else
2504 is_unicode = false;
2505
Steve French737b7582005-04-28 22:41:06 -07002506 /* BB FIXME investigate remapping reserved chars here */
Steve Frenchd185cda2009-04-30 17:45:10 +00002507 *symlinkinfo = cifs_strndup_from_ucs(data_start, count,
Steve French0e0d2cf2009-05-01 05:27:32 +00002508 is_unicode, nls_codepage);
Jeff Layton8b6427a2009-05-19 09:57:03 -04002509 if (!*symlinkinfo)
Jeff Layton460b9692009-04-30 07:17:56 -04002510 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511 }
2512 }
2513 cifs_buf_release(pSMB);
2514 if (rc == -EAGAIN)
2515 goto querySymLinkRetry;
2516 return rc;
2517}
2518
Parag Warudkarc9489772007-10-23 18:09:48 +00002519#ifdef CONFIG_CIFS_EXPERIMENTAL
Linus Torvalds1da177e2005-04-16 15:20:36 -07002520int
2521CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2522 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002523 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524 const struct nls_table *nls_codepage)
2525{
2526 int rc = 0;
2527 int bytes_returned;
Steve French50c2f752007-07-13 00:33:32 +00002528 struct smb_com_transaction_ioctl_req *pSMB;
2529 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530
Joe Perchesb6b38f72010-04-21 03:50:45 +00002531 cFYI(1, "In Windows reparse style QueryLink for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2533 (void **) &pSMBr);
2534 if (rc)
2535 return rc;
2536
2537 pSMB->TotalParameterCount = 0 ;
2538 pSMB->TotalDataCount = 0;
2539 pSMB->MaxParameterCount = cpu_to_le32(2);
2540 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002541 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2542 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543 pSMB->MaxSetupCount = 4;
2544 pSMB->Reserved = 0;
2545 pSMB->ParameterOffset = 0;
2546 pSMB->DataCount = 0;
2547 pSMB->DataOffset = 0;
2548 pSMB->SetupCount = 4;
2549 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2550 pSMB->ParameterCount = pSMB->TotalParameterCount;
2551 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2552 pSMB->IsFsctl = 1; /* FSCTL */
2553 pSMB->IsRootFlag = 0;
2554 pSMB->Fid = fid; /* file handle always le */
2555 pSMB->ByteCount = 0;
2556
2557 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2558 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2559 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002560 cFYI(1, "Send error in QueryReparseLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561 } else { /* decode response */
2562 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2563 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
Steve Frenchafe48c32009-05-02 05:25:46 +00002564 if ((pSMBr->ByteCount < 2) || (data_offset > 512)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 /* BB also check enough total bytes returned */
2566 rc = -EIO; /* bad smb */
Steve Frenchafe48c32009-05-02 05:25:46 +00002567 goto qreparse_out;
2568 }
2569 if (data_count && (data_count < 2048)) {
2570 char *end_of_smb = 2 /* sizeof byte count */ +
2571 pSMBr->ByteCount + (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572
Steve Frenchafe48c32009-05-02 05:25:46 +00002573 struct reparse_data *reparse_buf =
Steve French50c2f752007-07-13 00:33:32 +00002574 (struct reparse_data *)
2575 ((char *)&pSMBr->hdr.Protocol
2576 + data_offset);
Steve Frenchafe48c32009-05-02 05:25:46 +00002577 if ((char *)reparse_buf >= end_of_smb) {
2578 rc = -EIO;
2579 goto qreparse_out;
2580 }
2581 if ((reparse_buf->LinkNamesBuf +
2582 reparse_buf->TargetNameOffset +
2583 reparse_buf->TargetNameLen) > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002584 cFYI(1, "reparse buf beyond SMB");
Steve Frenchafe48c32009-05-02 05:25:46 +00002585 rc = -EIO;
2586 goto qreparse_out;
2587 }
Steve French50c2f752007-07-13 00:33:32 +00002588
Steve Frenchafe48c32009-05-02 05:25:46 +00002589 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2590 cifs_from_ucs2(symlinkinfo, (__le16 *)
Steve French50c2f752007-07-13 00:33:32 +00002591 (reparse_buf->LinkNamesBuf +
2592 reparse_buf->TargetNameOffset),
Steve Frenchafe48c32009-05-02 05:25:46 +00002593 buflen,
2594 reparse_buf->TargetNameLen,
2595 nls_codepage, 0);
2596 } else { /* ASCII names */
2597 strncpy(symlinkinfo,
2598 reparse_buf->LinkNamesBuf +
2599 reparse_buf->TargetNameOffset,
2600 min_t(const int, buflen,
2601 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002603 } else {
2604 rc = -EIO;
Joe Perchesb6b38f72010-04-21 03:50:45 +00002605 cFYI(1, "Invalid return data count on "
2606 "get reparse info ioctl");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002608 symlinkinfo[buflen] = 0; /* just in case so the caller
2609 does not go off the end of the buffer */
Joe Perchesb6b38f72010-04-21 03:50:45 +00002610 cFYI(1, "readlink result - %s", symlinkinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611 }
Steve French989c7e52009-05-02 05:32:20 +00002612
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002614 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615
2616 /* Note: On -EAGAIN error only caller can retry on handle based calls
2617 since file handle passed in no longer valid */
2618
2619 return rc;
2620}
Steve Frenchafe48c32009-05-02 05:25:46 +00002621#endif /* CIFS_EXPERIMENTAL */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622
2623#ifdef CONFIG_CIFS_POSIX
2624
2625/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002626static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2627 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628{
2629 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002630 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2631 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2632 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002633 /* cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634
2635 return;
2636}
2637
2638/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002639static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2640 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002641{
2642 int size = 0;
2643 int i;
2644 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002645 struct cifs_posix_ace *pACE;
2646 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2647 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648
2649 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2650 return -EOPNOTSUPP;
2651
Steve French790fe572007-07-07 19:25:05 +00002652 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653 count = le16_to_cpu(cifs_acl->access_entry_count);
2654 pACE = &cifs_acl->ace_array[0];
2655 size = sizeof(struct cifs_posix_acl);
2656 size += sizeof(struct cifs_posix_ace) * count;
2657 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002658 if (size_of_data_area < size) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002659 cFYI(1, "bad CIFS POSIX ACL size %d vs. %d",
2660 size_of_data_area, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661 return -EINVAL;
2662 }
Steve French790fe572007-07-07 19:25:05 +00002663 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664 count = le16_to_cpu(cifs_acl->access_entry_count);
2665 size = sizeof(struct cifs_posix_acl);
2666 size += sizeof(struct cifs_posix_ace) * count;
2667/* skip past access ACEs to get to default ACEs */
2668 pACE = &cifs_acl->ace_array[count];
2669 count = le16_to_cpu(cifs_acl->default_entry_count);
2670 size += sizeof(struct cifs_posix_ace) * count;
2671 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002672 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673 return -EINVAL;
2674 } else {
2675 /* illegal type */
2676 return -EINVAL;
2677 }
2678
2679 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002680 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002681 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002682 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683 return -ERANGE;
2684 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002685 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002686 for (i = 0; i < count ; i++) {
2687 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2688 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 }
2690 }
2691 return size;
2692}
2693
Steve French50c2f752007-07-13 00:33:32 +00002694static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2695 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696{
2697 __u16 rc = 0; /* 0 = ACL converted ok */
2698
Steve Frenchff7feac2005-11-15 16:45:16 -08002699 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2700 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002702 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 /* Probably no need to le convert -1 on any arch but can not hurt */
2704 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002705 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002706 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002707 /*cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id);*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708 return rc;
2709}
2710
2711/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002712static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2713 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714{
2715 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002716 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2717 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718 int count;
2719 int i;
2720
Steve French790fe572007-07-07 19:25:05 +00002721 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002722 return 0;
2723
2724 count = posix_acl_xattr_count((size_t)buflen);
Joe Perchesb6b38f72010-04-21 03:50:45 +00002725 cFYI(1, "setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002726 "version of %d",
Joe Perchesb6b38f72010-04-21 03:50:45 +00002727 count, buflen, le32_to_cpu(local_acl->a_version));
Steve French790fe572007-07-07 19:25:05 +00002728 if (le32_to_cpu(local_acl->a_version) != 2) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002729 cFYI(1, "unknown POSIX ACL version %d",
2730 le32_to_cpu(local_acl->a_version));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731 return 0;
2732 }
2733 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002734 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002735 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002736 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002737 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002739 cFYI(1, "unknown ACL type %d", acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740 return 0;
2741 }
Steve French50c2f752007-07-13 00:33:32 +00002742 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2744 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002745 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746 /* ACE not converted */
2747 break;
2748 }
2749 }
Steve French790fe572007-07-07 19:25:05 +00002750 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2752 rc += sizeof(struct cifs_posix_acl);
2753 /* BB add check to make sure ACL does not overflow SMB */
2754 }
2755 return rc;
2756}
2757
2758int
2759CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002760 const unsigned char *searchName,
2761 char *acl_inf, const int buflen, const int acl_type,
2762 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763{
2764/* SMB_QUERY_POSIX_ACL */
2765 TRANSACTION2_QPI_REQ *pSMB = NULL;
2766 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2767 int rc = 0;
2768 int bytes_returned;
2769 int name_len;
2770 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002771
Joe Perchesb6b38f72010-04-21 03:50:45 +00002772 cFYI(1, "In GetPosixACL (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773
2774queryAclRetry:
2775 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2776 (void **) &pSMBr);
2777 if (rc)
2778 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002779
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2781 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002782 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002783 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 name_len++; /* trailing null */
2785 name_len *= 2;
2786 pSMB->FileName[name_len] = 0;
2787 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002788 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 name_len = strnlen(searchName, PATH_MAX);
2790 name_len++; /* trailing null */
2791 strncpy(pSMB->FileName, searchName, name_len);
2792 }
2793
2794 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2795 pSMB->TotalDataCount = 0;
2796 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002797 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 pSMB->MaxDataCount = cpu_to_le16(4000);
2799 pSMB->MaxSetupCount = 0;
2800 pSMB->Reserved = 0;
2801 pSMB->Flags = 0;
2802 pSMB->Timeout = 0;
2803 pSMB->Reserved2 = 0;
2804 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002805 offsetof(struct smb_com_transaction2_qpi_req,
2806 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 pSMB->DataCount = 0;
2808 pSMB->DataOffset = 0;
2809 pSMB->SetupCount = 1;
2810 pSMB->Reserved3 = 0;
2811 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2812 byte_count = params + 1 /* pad */ ;
2813 pSMB->TotalParameterCount = cpu_to_le16(params);
2814 pSMB->ParameterCount = pSMB->TotalParameterCount;
2815 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2816 pSMB->Reserved4 = 0;
2817 pSMB->hdr.smb_buf_length += byte_count;
2818 pSMB->ByteCount = cpu_to_le16(byte_count);
2819
2820 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2821 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002822 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002824 cFYI(1, "Send error in Query POSIX ACL = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 } else {
2826 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002827
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2829 if (rc || (pSMBr->ByteCount < 2))
2830 /* BB also check enough total bytes returned */
2831 rc = -EIO; /* bad smb */
2832 else {
2833 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2834 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2835 rc = cifs_copy_posix_acl(acl_inf,
2836 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002837 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838 }
2839 }
2840 cifs_buf_release(pSMB);
2841 if (rc == -EAGAIN)
2842 goto queryAclRetry;
2843 return rc;
2844}
2845
2846int
2847CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002848 const unsigned char *fileName,
2849 const char *local_acl, const int buflen,
2850 const int acl_type,
2851 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852{
2853 struct smb_com_transaction2_spi_req *pSMB = NULL;
2854 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2855 char *parm_data;
2856 int name_len;
2857 int rc = 0;
2858 int bytes_returned = 0;
2859 __u16 params, byte_count, data_count, param_offset, offset;
2860
Joe Perchesb6b38f72010-04-21 03:50:45 +00002861 cFYI(1, "In SetPosixACL (Unix) for path %s", fileName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862setAclRetry:
2863 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002864 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865 if (rc)
2866 return rc;
2867 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2868 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002869 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002870 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871 name_len++; /* trailing null */
2872 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002873 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874 name_len = strnlen(fileName, PATH_MAX);
2875 name_len++; /* trailing null */
2876 strncpy(pSMB->FileName, fileName, name_len);
2877 }
2878 params = 6 + name_len;
2879 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00002880 /* BB find max SMB size from sess */
2881 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882 pSMB->MaxSetupCount = 0;
2883 pSMB->Reserved = 0;
2884 pSMB->Flags = 0;
2885 pSMB->Timeout = 0;
2886 pSMB->Reserved2 = 0;
2887 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002888 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889 offset = param_offset + params;
2890 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2891 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2892
2893 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002894 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895
Steve French790fe572007-07-07 19:25:05 +00002896 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897 rc = -EOPNOTSUPP;
2898 goto setACLerrorExit;
2899 }
2900 pSMB->DataOffset = cpu_to_le16(offset);
2901 pSMB->SetupCount = 1;
2902 pSMB->Reserved3 = 0;
2903 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2904 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2905 byte_count = 3 /* pad */ + params + data_count;
2906 pSMB->DataCount = cpu_to_le16(data_count);
2907 pSMB->TotalDataCount = pSMB->DataCount;
2908 pSMB->ParameterCount = cpu_to_le16(params);
2909 pSMB->TotalParameterCount = pSMB->ParameterCount;
2910 pSMB->Reserved4 = 0;
2911 pSMB->hdr.smb_buf_length += byte_count;
2912 pSMB->ByteCount = cpu_to_le16(byte_count);
2913 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002914 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00002915 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002916 cFYI(1, "Set POSIX ACL returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917
2918setACLerrorExit:
2919 cifs_buf_release(pSMB);
2920 if (rc == -EAGAIN)
2921 goto setAclRetry;
2922 return rc;
2923}
2924
Steve Frenchf654bac2005-04-28 22:41:04 -07002925/* BB fix tabs in this function FIXME BB */
2926int
2927CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00002928 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07002929{
Steve French50c2f752007-07-13 00:33:32 +00002930 int rc = 0;
2931 struct smb_t2_qfi_req *pSMB = NULL;
2932 struct smb_t2_qfi_rsp *pSMBr = NULL;
2933 int bytes_returned;
2934 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07002935
Joe Perchesb6b38f72010-04-21 03:50:45 +00002936 cFYI(1, "In GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00002937 if (tcon == NULL)
2938 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07002939
2940GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00002941 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2942 (void **) &pSMBr);
2943 if (rc)
2944 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07002945
Steve Frenchad7a2922008-02-07 23:25:02 +00002946 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00002947 pSMB->t2.TotalDataCount = 0;
2948 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
2949 /* BB find exact max data count below from sess structure BB */
2950 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
2951 pSMB->t2.MaxSetupCount = 0;
2952 pSMB->t2.Reserved = 0;
2953 pSMB->t2.Flags = 0;
2954 pSMB->t2.Timeout = 0;
2955 pSMB->t2.Reserved2 = 0;
2956 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
2957 Fid) - 4);
2958 pSMB->t2.DataCount = 0;
2959 pSMB->t2.DataOffset = 0;
2960 pSMB->t2.SetupCount = 1;
2961 pSMB->t2.Reserved3 = 0;
2962 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
2963 byte_count = params + 1 /* pad */ ;
2964 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
2965 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
2966 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
2967 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07002968 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00002969 pSMB->hdr.smb_buf_length += byte_count;
2970 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07002971
Steve French790fe572007-07-07 19:25:05 +00002972 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2973 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2974 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002975 cFYI(1, "error %d in GetExtAttr", rc);
Steve French790fe572007-07-07 19:25:05 +00002976 } else {
2977 /* decode response */
2978 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2979 if (rc || (pSMBr->ByteCount < 2))
2980 /* BB also check enough total bytes returned */
2981 /* If rc should we check for EOPNOSUPP and
2982 disable the srvino flag? or in caller? */
2983 rc = -EIO; /* bad smb */
2984 else {
2985 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2986 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2987 struct file_chattr_info *pfinfo;
2988 /* BB Do we need a cast or hash here ? */
2989 if (count != 16) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002990 cFYI(1, "Illegal size ret in GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00002991 rc = -EIO;
2992 goto GetExtAttrOut;
2993 }
2994 pfinfo = (struct file_chattr_info *)
2995 (data_offset + (char *) &pSMBr->hdr.Protocol);
2996 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07002997 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00002998 }
2999 }
Steve Frenchf654bac2005-04-28 22:41:04 -07003000GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00003001 cifs_buf_release(pSMB);
3002 if (rc == -EAGAIN)
3003 goto GetExtAttrRetry;
3004 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003005}
3006
Steve Frenchf654bac2005-04-28 22:41:04 -07003007#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003008
Jeff Layton79df1ba2010-12-06 12:52:08 -05003009#ifdef CONFIG_CIFS_ACL
3010/*
3011 * Initialize NT TRANSACT SMB into small smb request buffer. This assumes that
3012 * all NT TRANSACTS that we init here have total parm and data under about 400
3013 * bytes (to fit in small cifs buffer size), which is the case so far, it
3014 * easily fits. NB: Setup words themselves and ByteCount MaxSetupCount (size of
3015 * returned setup area) and MaxParameterCount (returned parms size) must be set
3016 * by caller
3017 */
3018static int
3019smb_init_nttransact(const __u16 sub_command, const int setup_count,
3020 const int parm_len, struct cifsTconInfo *tcon,
3021 void **ret_buf)
3022{
3023 int rc;
3024 __u32 temp_offset;
3025 struct smb_com_ntransact_req *pSMB;
3026
3027 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
3028 (void **)&pSMB);
3029 if (rc)
3030 return rc;
3031 *ret_buf = (void *)pSMB;
3032 pSMB->Reserved = 0;
3033 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
3034 pSMB->TotalDataCount = 0;
3035 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
3036 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3037 pSMB->ParameterCount = pSMB->TotalParameterCount;
3038 pSMB->DataCount = pSMB->TotalDataCount;
3039 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
3040 (setup_count * 2) - 4 /* for rfc1001 length itself */;
3041 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
3042 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
3043 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
3044 pSMB->SubCommand = cpu_to_le16(sub_command);
3045 return 0;
3046}
3047
3048static int
3049validate_ntransact(char *buf, char **ppparm, char **ppdata,
3050 __u32 *pparmlen, __u32 *pdatalen)
3051{
3052 char *end_of_smb;
3053 __u32 data_count, data_offset, parm_count, parm_offset;
3054 struct smb_com_ntransact_rsp *pSMBr;
3055
3056 *pdatalen = 0;
3057 *pparmlen = 0;
3058
3059 if (buf == NULL)
3060 return -EINVAL;
3061
3062 pSMBr = (struct smb_com_ntransact_rsp *)buf;
3063
3064 /* ByteCount was converted from little endian in SendReceive */
3065 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
3066 (char *)&pSMBr->ByteCount;
3067
3068 data_offset = le32_to_cpu(pSMBr->DataOffset);
3069 data_count = le32_to_cpu(pSMBr->DataCount);
3070 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
3071 parm_count = le32_to_cpu(pSMBr->ParameterCount);
3072
3073 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
3074 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
3075
3076 /* should we also check that parm and data areas do not overlap? */
3077 if (*ppparm > end_of_smb) {
3078 cFYI(1, "parms start after end of smb");
3079 return -EINVAL;
3080 } else if (parm_count + *ppparm > end_of_smb) {
3081 cFYI(1, "parm end after end of smb");
3082 return -EINVAL;
3083 } else if (*ppdata > end_of_smb) {
3084 cFYI(1, "data starts after end of smb");
3085 return -EINVAL;
3086 } else if (data_count + *ppdata > end_of_smb) {
3087 cFYI(1, "data %p + count %d (%p) past smb end %p start %p",
3088 *ppdata, data_count, (data_count + *ppdata),
3089 end_of_smb, pSMBr);
3090 return -EINVAL;
3091 } else if (parm_count + data_count > pSMBr->ByteCount) {
3092 cFYI(1, "parm count and data count larger than SMB");
3093 return -EINVAL;
3094 }
3095 *pdatalen = data_count;
3096 *pparmlen = parm_count;
3097 return 0;
3098}
3099
Steve French0a4b92c2006-01-12 15:44:21 -08003100/* Get Security Descriptor (by handle) from remote server for a file or dir */
3101int
3102CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f02007-10-25 21:17:17 +00003103 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003104{
3105 int rc = 0;
3106 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003107 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003108 struct kvec iov[1];
3109
Joe Perchesb6b38f72010-04-21 03:50:45 +00003110 cFYI(1, "GetCifsACL");
Steve French0a4b92c2006-01-12 15:44:21 -08003111
Steve French630f3f02007-10-25 21:17:17 +00003112 *pbuflen = 0;
3113 *acl_inf = NULL;
3114
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003115 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003116 8 /* parm len */, tcon, (void **) &pSMB);
3117 if (rc)
3118 return rc;
3119
3120 pSMB->MaxParameterCount = cpu_to_le32(4);
3121 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3122 pSMB->MaxSetupCount = 0;
3123 pSMB->Fid = fid; /* file handle always le */
3124 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3125 CIFS_ACL_DACL);
3126 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3127 pSMB->hdr.smb_buf_length += 11;
3128 iov[0].iov_base = (char *)pSMB;
3129 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3130
Steve Frencha761ac52007-10-18 21:45:27 +00003131 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Jeff Layton77499812011-01-11 07:24:23 -05003132 0);
Steve French0a4b92c2006-01-12 15:44:21 -08003133 cifs_stats_inc(&tcon->num_acl_get);
3134 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003135 cFYI(1, "Send error in QuerySecDesc = %d", rc);
Steve French0a4b92c2006-01-12 15:44:21 -08003136 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003137 __le32 *parm;
Steve French630f3f02007-10-25 21:17:17 +00003138 __u32 parm_len;
3139 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003140 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f02007-10-25 21:17:17 +00003141 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003142
3143/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003144 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f02007-10-25 21:17:17 +00003145 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003146 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003147 goto qsec_out;
3148 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3149
Joe Perchesb6b38f72010-04-21 03:50:45 +00003150 cFYI(1, "smb %p parm %p data %p", pSMBr, parm, *acl_inf);
Steve French0a4b92c2006-01-12 15:44:21 -08003151
3152 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3153 rc = -EIO; /* bad smb */
Steve French630f3f02007-10-25 21:17:17 +00003154 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003155 goto qsec_out;
3156 }
3157
3158/* BB check that data area is minimum length and as big as acl_len */
3159
Steve Frenchaf6f4612007-10-16 18:40:37 +00003160 acl_len = le32_to_cpu(*parm);
Steve French630f3f02007-10-25 21:17:17 +00003161 if (acl_len != *pbuflen) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003162 cERROR(1, "acl length %d does not match %d",
3163 acl_len, *pbuflen);
Steve French630f3f02007-10-25 21:17:17 +00003164 if (*pbuflen > acl_len)
3165 *pbuflen = acl_len;
3166 }
Steve French0a4b92c2006-01-12 15:44:21 -08003167
Steve French630f3f02007-10-25 21:17:17 +00003168 /* check if buffer is big enough for the acl
3169 header followed by the smallest SID */
3170 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3171 (*pbuflen >= 64 * 1024)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003172 cERROR(1, "bad acl length %d", *pbuflen);
Steve French630f3f02007-10-25 21:17:17 +00003173 rc = -EINVAL;
3174 *pbuflen = 0;
3175 } else {
3176 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3177 if (*acl_inf == NULL) {
3178 *pbuflen = 0;
3179 rc = -ENOMEM;
3180 }
3181 memcpy(*acl_inf, pdata, *pbuflen);
3182 }
Steve French0a4b92c2006-01-12 15:44:21 -08003183 }
3184qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003185 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003186 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003187 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003188 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003189/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003190 return rc;
3191}
Steve French97837582007-12-31 07:47:21 +00003192
3193int
3194CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3195 struct cifs_ntsd *pntsd, __u32 acllen)
3196{
3197 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3198 int rc = 0;
3199 int bytes_returned = 0;
3200 SET_SEC_DESC_REQ *pSMB = NULL;
3201 NTRANSACT_RSP *pSMBr = NULL;
3202
3203setCifsAclRetry:
3204 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3205 (void **) &pSMBr);
3206 if (rc)
3207 return (rc);
3208
3209 pSMB->MaxSetupCount = 0;
3210 pSMB->Reserved = 0;
3211
3212 param_count = 8;
3213 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3214 data_count = acllen;
3215 data_offset = param_offset + param_count;
3216 byte_count = 3 /* pad */ + param_count;
3217
3218 pSMB->DataCount = cpu_to_le32(data_count);
3219 pSMB->TotalDataCount = pSMB->DataCount;
3220 pSMB->MaxParameterCount = cpu_to_le32(4);
3221 pSMB->MaxDataCount = cpu_to_le32(16384);
3222 pSMB->ParameterCount = cpu_to_le32(param_count);
3223 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3224 pSMB->TotalParameterCount = pSMB->ParameterCount;
3225 pSMB->DataOffset = cpu_to_le32(data_offset);
3226 pSMB->SetupCount = 0;
3227 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3228 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3229
3230 pSMB->Fid = fid; /* file handle always le */
3231 pSMB->Reserved2 = 0;
3232 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3233
3234 if (pntsd && acllen) {
3235 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3236 (char *) pntsd,
3237 acllen);
3238 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3239
3240 } else
3241 pSMB->hdr.smb_buf_length += byte_count;
3242
3243 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3244 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3245
Joe Perchesb6b38f72010-04-21 03:50:45 +00003246 cFYI(1, "SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc);
Steve French97837582007-12-31 07:47:21 +00003247 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003248 cFYI(1, "Set CIFS ACL returned %d", rc);
Steve French97837582007-12-31 07:47:21 +00003249 cifs_buf_release(pSMB);
3250
3251 if (rc == -EAGAIN)
3252 goto setCifsAclRetry;
3253
3254 return (rc);
3255}
3256
Jeff Layton79df1ba2010-12-06 12:52:08 -05003257#endif /* CONFIG_CIFS_ACL */
Steve French0a4b92c2006-01-12 15:44:21 -08003258
Steve French6b8edfe2005-08-23 20:26:03 -07003259/* Legacy Query Path Information call for lookup to old servers such
3260 as Win9x/WinME */
3261int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003262 const unsigned char *searchName,
3263 FILE_ALL_INFO *pFinfo,
3264 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003265{
Steve Frenchad7a2922008-02-07 23:25:02 +00003266 QUERY_INFORMATION_REQ *pSMB;
3267 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003268 int rc = 0;
3269 int bytes_returned;
3270 int name_len;
3271
Joe Perchesb6b38f72010-04-21 03:50:45 +00003272 cFYI(1, "In SMBQPath path %s", searchName);
Steve French6b8edfe2005-08-23 20:26:03 -07003273QInfRetry:
3274 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003275 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003276 if (rc)
3277 return rc;
3278
3279 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3280 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003281 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3282 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003283 name_len++; /* trailing null */
3284 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003285 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003286 name_len = strnlen(searchName, PATH_MAX);
3287 name_len++; /* trailing null */
3288 strncpy(pSMB->FileName, searchName, name_len);
3289 }
3290 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003291 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003292 pSMB->hdr.smb_buf_length += (__u16) name_len;
3293 pSMB->ByteCount = cpu_to_le16(name_len);
3294
3295 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003296 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003297 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003298 cFYI(1, "Send error in QueryInfo = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003299 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003300 struct timespec ts;
3301 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003302
3303 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003304 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003305 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003306 ts.tv_nsec = 0;
3307 ts.tv_sec = time;
3308 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003309 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003310 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3311 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003312 pFinfo->AllocationSize =
3313 cpu_to_le64(le32_to_cpu(pSMBr->size));
3314 pFinfo->EndOfFile = pFinfo->AllocationSize;
3315 pFinfo->Attributes =
3316 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003317 } else
3318 rc = -EIO; /* bad buffer passed in */
3319
3320 cifs_buf_release(pSMB);
3321
3322 if (rc == -EAGAIN)
3323 goto QInfRetry;
3324
3325 return rc;
3326}
3327
Jeff Laytonbcd53572010-02-12 07:44:16 -05003328int
3329CIFSSMBQFileInfo(const int xid, struct cifsTconInfo *tcon,
3330 u16 netfid, FILE_ALL_INFO *pFindData)
3331{
3332 struct smb_t2_qfi_req *pSMB = NULL;
3333 struct smb_t2_qfi_rsp *pSMBr = NULL;
3334 int rc = 0;
3335 int bytes_returned;
3336 __u16 params, byte_count;
Steve French6b8edfe2005-08-23 20:26:03 -07003337
Jeff Laytonbcd53572010-02-12 07:44:16 -05003338QFileInfoRetry:
3339 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3340 (void **) &pSMBr);
3341 if (rc)
3342 return rc;
Steve French6b8edfe2005-08-23 20:26:03 -07003343
Jeff Laytonbcd53572010-02-12 07:44:16 -05003344 params = 2 /* level */ + 2 /* fid */;
3345 pSMB->t2.TotalDataCount = 0;
3346 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3347 /* BB find exact max data count below from sess structure BB */
3348 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3349 pSMB->t2.MaxSetupCount = 0;
3350 pSMB->t2.Reserved = 0;
3351 pSMB->t2.Flags = 0;
3352 pSMB->t2.Timeout = 0;
3353 pSMB->t2.Reserved2 = 0;
3354 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3355 Fid) - 4);
3356 pSMB->t2.DataCount = 0;
3357 pSMB->t2.DataOffset = 0;
3358 pSMB->t2.SetupCount = 1;
3359 pSMB->t2.Reserved3 = 0;
3360 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3361 byte_count = params + 1 /* pad */ ;
3362 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3363 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3364 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
3365 pSMB->Pad = 0;
3366 pSMB->Fid = netfid;
3367 pSMB->hdr.smb_buf_length += byte_count;
3368
3369 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3370 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3371 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003372 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonbcd53572010-02-12 07:44:16 -05003373 } else { /* decode response */
3374 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3375
3376 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3377 rc = -EIO;
3378 else if (pSMBr->ByteCount < 40)
3379 rc = -EIO; /* bad smb */
3380 else if (pFindData) {
3381 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3382 memcpy((char *) pFindData,
3383 (char *) &pSMBr->hdr.Protocol +
3384 data_offset, sizeof(FILE_ALL_INFO));
3385 } else
3386 rc = -ENOMEM;
3387 }
3388 cifs_buf_release(pSMB);
3389 if (rc == -EAGAIN)
3390 goto QFileInfoRetry;
3391
3392 return rc;
3393}
Steve French6b8edfe2005-08-23 20:26:03 -07003394
Linus Torvalds1da177e2005-04-16 15:20:36 -07003395int
3396CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3397 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003398 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003399 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003400 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003401{
3402/* level 263 SMB_QUERY_FILE_ALL_INFO */
3403 TRANSACTION2_QPI_REQ *pSMB = NULL;
3404 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3405 int rc = 0;
3406 int bytes_returned;
3407 int name_len;
3408 __u16 params, byte_count;
3409
Joe Perchesb6b38f72010-04-21 03:50:45 +00003410/* cFYI(1, "In QPathInfo path %s", searchName); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003411QPathInfoRetry:
3412 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3413 (void **) &pSMBr);
3414 if (rc)
3415 return rc;
3416
3417 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3418 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003419 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003420 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003421 name_len++; /* trailing null */
3422 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003423 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003424 name_len = strnlen(searchName, PATH_MAX);
3425 name_len++; /* trailing null */
3426 strncpy(pSMB->FileName, searchName, name_len);
3427 }
3428
Steve French50c2f752007-07-13 00:33:32 +00003429 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003430 pSMB->TotalDataCount = 0;
3431 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00003432 /* BB find exact max SMB PDU from sess structure BB */
3433 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003434 pSMB->MaxSetupCount = 0;
3435 pSMB->Reserved = 0;
3436 pSMB->Flags = 0;
3437 pSMB->Timeout = 0;
3438 pSMB->Reserved2 = 0;
3439 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003440 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003441 pSMB->DataCount = 0;
3442 pSMB->DataOffset = 0;
3443 pSMB->SetupCount = 1;
3444 pSMB->Reserved3 = 0;
3445 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3446 byte_count = params + 1 /* pad */ ;
3447 pSMB->TotalParameterCount = cpu_to_le16(params);
3448 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003449 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003450 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3451 else
3452 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003453 pSMB->Reserved4 = 0;
3454 pSMB->hdr.smb_buf_length += byte_count;
3455 pSMB->ByteCount = cpu_to_le16(byte_count);
3456
3457 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3458 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3459 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003460 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003461 } else { /* decode response */
3462 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3463
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003464 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3465 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003466 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003467 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003468 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003469 rc = -EIO; /* 24 or 26 expected but we do not read
3470 last field */
3471 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003472 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003473 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003474
3475 /* On legacy responses we do not read the last field,
3476 EAsize, fortunately since it varies by subdialect and
3477 also note it differs on Set vs. Get, ie two bytes or 4
3478 bytes depending but we don't care here */
3479 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003480 size = sizeof(FILE_INFO_STANDARD);
3481 else
3482 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003483 memcpy((char *) pFindData,
3484 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003485 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003486 } else
3487 rc = -ENOMEM;
3488 }
3489 cifs_buf_release(pSMB);
3490 if (rc == -EAGAIN)
3491 goto QPathInfoRetry;
3492
3493 return rc;
3494}
3495
3496int
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003497CIFSSMBUnixQFileInfo(const int xid, struct cifsTconInfo *tcon,
3498 u16 netfid, FILE_UNIX_BASIC_INFO *pFindData)
3499{
3500 struct smb_t2_qfi_req *pSMB = NULL;
3501 struct smb_t2_qfi_rsp *pSMBr = NULL;
3502 int rc = 0;
3503 int bytes_returned;
3504 __u16 params, byte_count;
3505
3506UnixQFileInfoRetry:
3507 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3508 (void **) &pSMBr);
3509 if (rc)
3510 return rc;
3511
3512 params = 2 /* level */ + 2 /* fid */;
3513 pSMB->t2.TotalDataCount = 0;
3514 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3515 /* BB find exact max data count below from sess structure BB */
3516 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3517 pSMB->t2.MaxSetupCount = 0;
3518 pSMB->t2.Reserved = 0;
3519 pSMB->t2.Flags = 0;
3520 pSMB->t2.Timeout = 0;
3521 pSMB->t2.Reserved2 = 0;
3522 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3523 Fid) - 4);
3524 pSMB->t2.DataCount = 0;
3525 pSMB->t2.DataOffset = 0;
3526 pSMB->t2.SetupCount = 1;
3527 pSMB->t2.Reserved3 = 0;
3528 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3529 byte_count = params + 1 /* pad */ ;
3530 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3531 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3532 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3533 pSMB->Pad = 0;
3534 pSMB->Fid = netfid;
3535 pSMB->hdr.smb_buf_length += byte_count;
3536
3537 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3538 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3539 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003540 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003541 } else { /* decode response */
3542 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3543
3544 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003545 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003546 "Unix Extensions can be disabled on mount "
Steve Frenchf19159d2010-04-21 04:12:10 +00003547 "by specifying the nosfu mount option.");
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003548 rc = -EIO; /* bad smb */
3549 } else {
3550 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3551 memcpy((char *) pFindData,
3552 (char *) &pSMBr->hdr.Protocol +
3553 data_offset,
3554 sizeof(FILE_UNIX_BASIC_INFO));
3555 }
3556 }
3557
3558 cifs_buf_release(pSMB);
3559 if (rc == -EAGAIN)
3560 goto UnixQFileInfoRetry;
3561
3562 return rc;
3563}
3564
3565int
Linus Torvalds1da177e2005-04-16 15:20:36 -07003566CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3567 const unsigned char *searchName,
Steve French582d21e2008-05-13 04:54:12 +00003568 FILE_UNIX_BASIC_INFO *pFindData,
Steve French737b7582005-04-28 22:41:06 -07003569 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003570{
3571/* SMB_QUERY_FILE_UNIX_BASIC */
3572 TRANSACTION2_QPI_REQ *pSMB = NULL;
3573 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3574 int rc = 0;
3575 int bytes_returned = 0;
3576 int name_len;
3577 __u16 params, byte_count;
3578
Joe Perchesb6b38f72010-04-21 03:50:45 +00003579 cFYI(1, "In QPathInfo (Unix) the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003580UnixQPathInfoRetry:
3581 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3582 (void **) &pSMBr);
3583 if (rc)
3584 return rc;
3585
3586 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3587 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003588 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003589 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003590 name_len++; /* trailing null */
3591 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003592 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003593 name_len = strnlen(searchName, PATH_MAX);
3594 name_len++; /* trailing null */
3595 strncpy(pSMB->FileName, searchName, name_len);
3596 }
3597
Steve French50c2f752007-07-13 00:33:32 +00003598 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003599 pSMB->TotalDataCount = 0;
3600 pSMB->MaxParameterCount = cpu_to_le16(2);
3601 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003602 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003603 pSMB->MaxSetupCount = 0;
3604 pSMB->Reserved = 0;
3605 pSMB->Flags = 0;
3606 pSMB->Timeout = 0;
3607 pSMB->Reserved2 = 0;
3608 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003609 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003610 pSMB->DataCount = 0;
3611 pSMB->DataOffset = 0;
3612 pSMB->SetupCount = 1;
3613 pSMB->Reserved3 = 0;
3614 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3615 byte_count = params + 1 /* pad */ ;
3616 pSMB->TotalParameterCount = cpu_to_le16(params);
3617 pSMB->ParameterCount = pSMB->TotalParameterCount;
3618 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3619 pSMB->Reserved4 = 0;
3620 pSMB->hdr.smb_buf_length += byte_count;
3621 pSMB->ByteCount = cpu_to_le16(byte_count);
3622
3623 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3624 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3625 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003626 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003627 } else { /* decode response */
3628 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3629
3630 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003631 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Steve French1e71f252007-09-20 15:30:07 +00003632 "Unix Extensions can be disabled on mount "
Joe Perchesb6b38f72010-04-21 03:50:45 +00003633 "by specifying the nosfu mount option.");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003634 rc = -EIO; /* bad smb */
3635 } else {
3636 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3637 memcpy((char *) pFindData,
3638 (char *) &pSMBr->hdr.Protocol +
3639 data_offset,
Steve French630f3f02007-10-25 21:17:17 +00003640 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003641 }
3642 }
3643 cifs_buf_release(pSMB);
3644 if (rc == -EAGAIN)
3645 goto UnixQPathInfoRetry;
3646
3647 return rc;
3648}
3649
Linus Torvalds1da177e2005-04-16 15:20:36 -07003650/* xid, tcon, searchName and codepage are input parms, rest are returned */
3651int
3652CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003653 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003654 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003655 __u16 *pnetfid,
3656 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003657{
3658/* level 257 SMB_ */
3659 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3660 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003661 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003662 int rc = 0;
3663 int bytes_returned = 0;
3664 int name_len;
3665 __u16 params, byte_count;
3666
Joe Perchesb6b38f72010-04-21 03:50:45 +00003667 cFYI(1, "In FindFirst for %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003668
3669findFirstRetry:
3670 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3671 (void **) &pSMBr);
3672 if (rc)
3673 return rc;
3674
3675 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3676 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003677 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003678 PATH_MAX, nls_codepage, remap);
3679 /* We can not add the asterik earlier in case
3680 it got remapped to 0xF03A as if it were part of the
3681 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003682 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003683 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003684 pSMB->FileName[name_len+1] = 0;
3685 pSMB->FileName[name_len+2] = '*';
3686 pSMB->FileName[name_len+3] = 0;
3687 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003688 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3689 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003690 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003691 } else { /* BB add check for overrun of SMB buf BB */
3692 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003693/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003694 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003695 free buffer exit; BB */
3696 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003697 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003698 pSMB->FileName[name_len+1] = '*';
3699 pSMB->FileName[name_len+2] = 0;
3700 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003701 }
3702
3703 params = 12 + name_len /* includes null */ ;
3704 pSMB->TotalDataCount = 0; /* no EAs */
3705 pSMB->MaxParameterCount = cpu_to_le16(10);
3706 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3707 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3708 pSMB->MaxSetupCount = 0;
3709 pSMB->Reserved = 0;
3710 pSMB->Flags = 0;
3711 pSMB->Timeout = 0;
3712 pSMB->Reserved2 = 0;
3713 byte_count = params + 1 /* pad */ ;
3714 pSMB->TotalParameterCount = cpu_to_le16(params);
3715 pSMB->ParameterCount = pSMB->TotalParameterCount;
3716 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003717 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3718 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719 pSMB->DataCount = 0;
3720 pSMB->DataOffset = 0;
3721 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3722 pSMB->Reserved3 = 0;
3723 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3724 pSMB->SearchAttributes =
3725 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3726 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003727 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3728 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003729 CIFS_SEARCH_RETURN_RESUME);
3730 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3731
3732 /* BB what should we set StorageType to? Does it matter? BB */
3733 pSMB->SearchStorageType = 0;
3734 pSMB->hdr.smb_buf_length += byte_count;
3735 pSMB->ByteCount = cpu_to_le16(byte_count);
3736
3737 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3738 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003739 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003740
Steve French88274812006-03-09 22:21:45 +00003741 if (rc) {/* BB add logic to retry regular search if Unix search
3742 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003743 /* BB Add code to handle unsupported level rc */
Joe Perchesb6b38f72010-04-21 03:50:45 +00003744 cFYI(1, "Error in FindFirst = %d", rc);
Steve French1982c342005-08-17 12:38:22 -07003745
Steve French88274812006-03-09 22:21:45 +00003746 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003747
3748 /* BB eventually could optimize out free and realloc of buf */
3749 /* for this case */
3750 if (rc == -EAGAIN)
3751 goto findFirstRetry;
3752 } else { /* decode response */
3753 /* BB remember to free buffer if error BB */
3754 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003755 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003756 unsigned int lnoff;
3757
Linus Torvalds1da177e2005-04-16 15:20:36 -07003758 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003759 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003760 else
Steve French4b18f2a2008-04-29 00:06:05 +00003761 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003762
3763 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003764 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003765 psrch_inf->srch_entries_start =
3766 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003767 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003768 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3769 le16_to_cpu(pSMBr->t2.ParameterOffset));
3770
Steve French790fe572007-07-07 19:25:05 +00003771 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003772 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003773 else
Steve French4b18f2a2008-04-29 00:06:05 +00003774 psrch_inf->endOfSearch = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003775
Steve French50c2f752007-07-13 00:33:32 +00003776 psrch_inf->entries_in_buffer =
3777 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003778 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003779 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003780 lnoff = le16_to_cpu(parms->LastNameOffset);
3781 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3782 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003783 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003784 psrch_inf->last_entry = NULL;
3785 return rc;
3786 }
3787
Steve French0752f152008-10-07 20:03:33 +00003788 psrch_inf->last_entry = psrch_inf->srch_entries_start +
Steve Frenchb77d7532008-10-08 19:13:46 +00003789 lnoff;
3790
Linus Torvalds1da177e2005-04-16 15:20:36 -07003791 *pnetfid = parms->SearchHandle;
3792 } else {
3793 cifs_buf_release(pSMB);
3794 }
3795 }
3796
3797 return rc;
3798}
3799
3800int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003801 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003802{
3803 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3804 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003805 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003806 char *response_data;
3807 int rc = 0;
3808 int bytes_returned, name_len;
3809 __u16 params, byte_count;
3810
Joe Perchesb6b38f72010-04-21 03:50:45 +00003811 cFYI(1, "In FindNext");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003812
Steve French4b18f2a2008-04-29 00:06:05 +00003813 if (psrch_inf->endOfSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003814 return -ENOENT;
3815
3816 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3817 (void **) &pSMBr);
3818 if (rc)
3819 return rc;
3820
Steve French50c2f752007-07-13 00:33:32 +00003821 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003822 byte_count = 0;
3823 pSMB->TotalDataCount = 0; /* no EAs */
3824 pSMB->MaxParameterCount = cpu_to_le16(8);
3825 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003826 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3827 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003828 pSMB->MaxSetupCount = 0;
3829 pSMB->Reserved = 0;
3830 pSMB->Flags = 0;
3831 pSMB->Timeout = 0;
3832 pSMB->Reserved2 = 0;
3833 pSMB->ParameterOffset = cpu_to_le16(
3834 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3835 pSMB->DataCount = 0;
3836 pSMB->DataOffset = 0;
3837 pSMB->SetupCount = 1;
3838 pSMB->Reserved3 = 0;
3839 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3840 pSMB->SearchHandle = searchHandle; /* always kept as le */
3841 pSMB->SearchCount =
Steve French630f3f02007-10-25 21:17:17 +00003842 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003843 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3844 pSMB->ResumeKey = psrch_inf->resume_key;
3845 pSMB->SearchFlags =
3846 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3847
3848 name_len = psrch_inf->resume_name_len;
3849 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003850 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003851 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3852 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003853 /* 14 byte parm len above enough for 2 byte null terminator */
3854 pSMB->ResumeFileName[name_len] = 0;
3855 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003856 } else {
3857 rc = -EINVAL;
3858 goto FNext2_err_exit;
3859 }
3860 byte_count = params + 1 /* pad */ ;
3861 pSMB->TotalParameterCount = cpu_to_le16(params);
3862 pSMB->ParameterCount = pSMB->TotalParameterCount;
3863 pSMB->hdr.smb_buf_length += byte_count;
3864 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003865
Linus Torvalds1da177e2005-04-16 15:20:36 -07003866 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3867 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003868 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003869 if (rc) {
3870 if (rc == -EBADF) {
Steve French4b18f2a2008-04-29 00:06:05 +00003871 psrch_inf->endOfSearch = true;
Jeff Layton63534502008-05-12 19:56:05 -07003872 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00003873 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003874 } else
Joe Perchesb6b38f72010-04-21 03:50:45 +00003875 cFYI(1, "FindNext returned = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003876 } else { /* decode response */
3877 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003878
Steve French790fe572007-07-07 19:25:05 +00003879 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003880 unsigned int lnoff;
3881
Linus Torvalds1da177e2005-04-16 15:20:36 -07003882 /* BB fixme add lock for file (srch_info) struct here */
3883 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003884 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003885 else
Steve French4b18f2a2008-04-29 00:06:05 +00003886 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003887 response_data = (char *) &pSMBr->hdr.Protocol +
3888 le16_to_cpu(pSMBr->t2.ParameterOffset);
3889 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3890 response_data = (char *)&pSMBr->hdr.Protocol +
3891 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003892 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003893 cifs_small_buf_release(
3894 psrch_inf->ntwrk_buf_start);
3895 else
3896 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003897 psrch_inf->srch_entries_start = response_data;
3898 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003899 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003900 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003901 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003902 else
Steve French4b18f2a2008-04-29 00:06:05 +00003903 psrch_inf->endOfSearch = false;
Steve French50c2f752007-07-13 00:33:32 +00003904 psrch_inf->entries_in_buffer =
3905 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003906 psrch_inf->index_of_last_entry +=
3907 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003908 lnoff = le16_to_cpu(parms->LastNameOffset);
3909 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3910 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003911 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003912 psrch_inf->last_entry = NULL;
3913 return rc;
3914 } else
3915 psrch_inf->last_entry =
3916 psrch_inf->srch_entries_start + lnoff;
3917
Joe Perchesb6b38f72010-04-21 03:50:45 +00003918/* cFYI(1, "fnxt2 entries in buf %d index_of_last %d",
3919 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003920
3921 /* BB fixme add unlock here */
3922 }
3923
3924 }
3925
3926 /* BB On error, should we leave previous search buf (and count and
3927 last entry fields) intact or free the previous one? */
3928
3929 /* Note: On -EAGAIN error only caller can retry on handle based calls
3930 since file handle passed in no longer valid */
3931FNext2_err_exit:
3932 if (rc != 0)
3933 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003934 return rc;
3935}
3936
3937int
Steve French50c2f752007-07-13 00:33:32 +00003938CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3939 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003940{
3941 int rc = 0;
3942 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003943
Joe Perchesb6b38f72010-04-21 03:50:45 +00003944 cFYI(1, "In CIFSSMBFindClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003945 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3946
3947 /* no sense returning error if session restarted
3948 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003949 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003950 return 0;
3951 if (rc)
3952 return rc;
3953
Linus Torvalds1da177e2005-04-16 15:20:36 -07003954 pSMB->FileID = searchHandle;
3955 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003956 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003957 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003958 cERROR(1, "Send error in FindClose = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003959
Steve Frencha4544342005-08-24 13:59:35 -07003960 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003961
3962 /* Since session is dead, search handle closed on server already */
3963 if (rc == -EAGAIN)
3964 rc = 0;
3965
3966 return rc;
3967}
3968
Linus Torvalds1da177e2005-04-16 15:20:36 -07003969int
3970CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003971 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003972 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003973 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003974{
3975 int rc = 0;
3976 TRANSACTION2_QPI_REQ *pSMB = NULL;
3977 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3978 int name_len, bytes_returned;
3979 __u16 params, byte_count;
3980
Joe Perchesb6b38f72010-04-21 03:50:45 +00003981 cFYI(1, "In GetSrvInodeNum for %s", searchName);
Steve French790fe572007-07-07 19:25:05 +00003982 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003983 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003984
3985GetInodeNumberRetry:
3986 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003987 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003988 if (rc)
3989 return rc;
3990
Linus Torvalds1da177e2005-04-16 15:20:36 -07003991 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3992 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003993 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003994 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003995 name_len++; /* trailing null */
3996 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003997 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003998 name_len = strnlen(searchName, PATH_MAX);
3999 name_len++; /* trailing null */
4000 strncpy(pSMB->FileName, searchName, name_len);
4001 }
4002
4003 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
4004 pSMB->TotalDataCount = 0;
4005 pSMB->MaxParameterCount = cpu_to_le16(2);
4006 /* BB find exact max data count below from sess structure BB */
4007 pSMB->MaxDataCount = cpu_to_le16(4000);
4008 pSMB->MaxSetupCount = 0;
4009 pSMB->Reserved = 0;
4010 pSMB->Flags = 0;
4011 pSMB->Timeout = 0;
4012 pSMB->Reserved2 = 0;
4013 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004014 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004015 pSMB->DataCount = 0;
4016 pSMB->DataOffset = 0;
4017 pSMB->SetupCount = 1;
4018 pSMB->Reserved3 = 0;
4019 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
4020 byte_count = params + 1 /* pad */ ;
4021 pSMB->TotalParameterCount = cpu_to_le16(params);
4022 pSMB->ParameterCount = pSMB->TotalParameterCount;
4023 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
4024 pSMB->Reserved4 = 0;
4025 pSMB->hdr.smb_buf_length += byte_count;
4026 pSMB->ByteCount = cpu_to_le16(byte_count);
4027
4028 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4029 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4030 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004031 cFYI(1, "error %d in QueryInternalInfo", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004032 } else {
4033 /* decode response */
4034 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4035 if (rc || (pSMBr->ByteCount < 2))
4036 /* BB also check enough total bytes returned */
4037 /* If rc should we check for EOPNOSUPP and
4038 disable the srvino flag? or in caller? */
4039 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00004040 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004041 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4042 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00004043 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004044 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00004045 if (count < 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004046 cFYI(1, "Illegal size ret in QryIntrnlInf");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004047 rc = -EIO;
4048 goto GetInodeNumOut;
4049 }
4050 pfinfo = (struct file_internal_info *)
4051 (data_offset + (char *) &pSMBr->hdr.Protocol);
Steve French85a6dac2009-04-01 05:22:00 +00004052 *inode_number = le64_to_cpu(pfinfo->UniqueId);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004053 }
4054 }
4055GetInodeNumOut:
4056 cifs_buf_release(pSMB);
4057 if (rc == -EAGAIN)
4058 goto GetInodeNumberRetry;
4059 return rc;
4060}
Linus Torvalds1da177e2005-04-16 15:20:36 -07004061
Igor Mammedovfec45852008-05-16 13:06:30 +04004062/* parses DFS refferal V3 structure
4063 * caller is responsible for freeing target_nodes
4064 * returns:
4065 * on success - 0
4066 * on failure - errno
4067 */
4068static int
Steve Frencha1fe78f2008-05-16 18:48:38 +00004069parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
Igor Mammedovfec45852008-05-16 13:06:30 +04004070 unsigned int *num_of_nodes,
4071 struct dfs_info3_param **target_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004072 const struct nls_table *nls_codepage, int remap,
4073 const char *searchName)
Igor Mammedovfec45852008-05-16 13:06:30 +04004074{
4075 int i, rc = 0;
4076 char *data_end;
4077 bool is_unicode;
4078 struct dfs_referral_level_3 *ref;
4079
Harvey Harrison5ca33c62008-07-23 17:45:58 -07004080 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
4081 is_unicode = true;
4082 else
4083 is_unicode = false;
Igor Mammedovfec45852008-05-16 13:06:30 +04004084 *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
4085
4086 if (*num_of_nodes < 1) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004087 cERROR(1, "num_referrals: must be at least > 0,"
4088 "but we get num_referrals = %d\n", *num_of_nodes);
Igor Mammedovfec45852008-05-16 13:06:30 +04004089 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004090 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004091 }
4092
4093 ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
Al Viro1d92cfd2008-06-02 10:59:02 +01004094 if (ref->VersionNumber != cpu_to_le16(3)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004095 cERROR(1, "Referrals of V%d version are not supported,"
4096 "should be V3", le16_to_cpu(ref->VersionNumber));
Igor Mammedovfec45852008-05-16 13:06:30 +04004097 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004098 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004099 }
4100
4101 /* get the upper boundary of the resp buffer */
4102 data_end = (char *)(&(pSMBr->PathConsumed)) +
4103 le16_to_cpu(pSMBr->t2.DataCount);
4104
Steve Frenchf19159d2010-04-21 04:12:10 +00004105 cFYI(1, "num_referrals: %d dfs flags: 0x%x ...\n",
Igor Mammedovfec45852008-05-16 13:06:30 +04004106 *num_of_nodes,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004107 le32_to_cpu(pSMBr->DFSFlags));
Igor Mammedovfec45852008-05-16 13:06:30 +04004108
4109 *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
4110 *num_of_nodes, GFP_KERNEL);
4111 if (*target_nodes == NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004112 cERROR(1, "Failed to allocate buffer for target_nodes\n");
Igor Mammedovfec45852008-05-16 13:06:30 +04004113 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004114 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004115 }
4116
Daniel Mack3ad2f3f2010-02-03 08:01:28 +08004117 /* collect necessary data from referrals */
Igor Mammedovfec45852008-05-16 13:06:30 +04004118 for (i = 0; i < *num_of_nodes; i++) {
4119 char *temp;
4120 int max_len;
4121 struct dfs_info3_param *node = (*target_nodes)+i;
4122
Steve French0e0d2cf2009-05-01 05:27:32 +00004123 node->flags = le32_to_cpu(pSMBr->DFSFlags);
Igor Mammedov2c556082008-10-23 13:58:42 +04004124 if (is_unicode) {
Jeff Layton331c3132008-12-17 06:31:53 -05004125 __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
4126 GFP_KERNEL);
Steve French2920ee22009-08-31 15:27:26 +00004127 if (tmp == NULL) {
4128 rc = -ENOMEM;
4129 goto parse_DFS_referrals_exit;
4130 }
Igor Mammedov2c556082008-10-23 13:58:42 +04004131 cifsConvertToUCS((__le16 *) tmp, searchName,
4132 PATH_MAX, nls_codepage, remap);
Jeff Layton69f801f2009-04-30 06:46:32 -04004133 node->path_consumed = cifs_ucs2_bytes(tmp,
4134 le16_to_cpu(pSMBr->PathConsumed),
Igor Mammedov2c556082008-10-23 13:58:42 +04004135 nls_codepage);
4136 kfree(tmp);
4137 } else
4138 node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
4139
Igor Mammedovfec45852008-05-16 13:06:30 +04004140 node->server_type = le16_to_cpu(ref->ServerType);
4141 node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
4142
4143 /* copy DfsPath */
4144 temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
4145 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004146 node->path_name = cifs_strndup_from_ucs(temp, max_len,
4147 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004148 if (!node->path_name) {
4149 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004150 goto parse_DFS_referrals_exit;
Jeff Layton066ce682009-04-30 07:16:14 -04004151 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004152
4153 /* copy link target UNC */
4154 temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
4155 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004156 node->node_name = cifs_strndup_from_ucs(temp, max_len,
4157 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004158 if (!node->node_name)
4159 rc = -ENOMEM;
Igor Mammedovfec45852008-05-16 13:06:30 +04004160 }
4161
Steve Frencha1fe78f2008-05-16 18:48:38 +00004162parse_DFS_referrals_exit:
Igor Mammedovfec45852008-05-16 13:06:30 +04004163 if (rc) {
4164 free_dfs_info_array(*target_nodes, *num_of_nodes);
4165 *target_nodes = NULL;
4166 *num_of_nodes = 0;
4167 }
4168 return rc;
4169}
4170
Linus Torvalds1da177e2005-04-16 15:20:36 -07004171int
4172CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
4173 const unsigned char *searchName,
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004174 struct dfs_info3_param **target_nodes,
4175 unsigned int *num_of_nodes,
Steve French737b7582005-04-28 22:41:06 -07004176 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004177{
4178/* TRANS2_GET_DFS_REFERRAL */
4179 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
4180 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004181 int rc = 0;
4182 int bytes_returned;
4183 int name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004184 __u16 params, byte_count;
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004185 *num_of_nodes = 0;
4186 *target_nodes = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004187
Joe Perchesb6b38f72010-04-21 03:50:45 +00004188 cFYI(1, "In GetDFSRefer the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004189 if (ses == NULL)
4190 return -ENODEV;
4191getDFSRetry:
4192 rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
4193 (void **) &pSMBr);
4194 if (rc)
4195 return rc;
Steve French50c2f752007-07-13 00:33:32 +00004196
4197 /* server pointer checked in called function,
Steve French1982c342005-08-17 12:38:22 -07004198 but should never be null here anyway */
4199 pSMB->hdr.Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004200 pSMB->hdr.Tid = ses->ipc_tid;
4201 pSMB->hdr.Uid = ses->Suid;
Steve French26f57362007-08-30 22:09:15 +00004202 if (ses->capabilities & CAP_STATUS32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004203 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
Steve French26f57362007-08-30 22:09:15 +00004204 if (ses->capabilities & CAP_DFS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004205 pSMB->hdr.Flags2 |= SMBFLG2_DFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004206
4207 if (ses->capabilities & CAP_UNICODE) {
4208 pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
4209 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004210 cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
Steve French737b7582005-04-28 22:41:06 -07004211 searchName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004212 name_len++; /* trailing null */
4213 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004214 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004215 name_len = strnlen(searchName, PATH_MAX);
4216 name_len++; /* trailing null */
4217 strncpy(pSMB->RequestFileName, searchName, name_len);
4218 }
4219
Steve French790fe572007-07-07 19:25:05 +00004220 if (ses->server) {
4221 if (ses->server->secMode &
Steve French1a4e15a2006-10-12 21:33:51 +00004222 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
4223 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
4224 }
4225
Steve French50c2f752007-07-13 00:33:32 +00004226 pSMB->hdr.Uid = ses->Suid;
Steve French1a4e15a2006-10-12 21:33:51 +00004227
Linus Torvalds1da177e2005-04-16 15:20:36 -07004228 params = 2 /* level */ + name_len /*includes null */ ;
4229 pSMB->TotalDataCount = 0;
4230 pSMB->DataCount = 0;
4231 pSMB->DataOffset = 0;
4232 pSMB->MaxParameterCount = 0;
Steve French582d21e2008-05-13 04:54:12 +00004233 /* BB find exact max SMB PDU from sess structure BB */
4234 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004235 pSMB->MaxSetupCount = 0;
4236 pSMB->Reserved = 0;
4237 pSMB->Flags = 0;
4238 pSMB->Timeout = 0;
4239 pSMB->Reserved2 = 0;
4240 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004241 struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004242 pSMB->SetupCount = 1;
4243 pSMB->Reserved3 = 0;
4244 pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
4245 byte_count = params + 3 /* pad */ ;
4246 pSMB->ParameterCount = cpu_to_le16(params);
4247 pSMB->TotalParameterCount = pSMB->ParameterCount;
4248 pSMB->MaxReferralLevel = cpu_to_le16(3);
4249 pSMB->hdr.smb_buf_length += byte_count;
4250 pSMB->ByteCount = cpu_to_le16(byte_count);
4251
4252 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
4253 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4254 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004255 cFYI(1, "Send error in GetDFSRefer = %d", rc);
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004256 goto GetDFSRefExit;
4257 }
4258 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004259
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004260 /* BB Also check if enough total bytes returned? */
Igor Mammedovfec45852008-05-16 13:06:30 +04004261 if (rc || (pSMBr->ByteCount < 17)) {
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004262 rc = -EIO; /* bad smb */
Igor Mammedovfec45852008-05-16 13:06:30 +04004263 goto GetDFSRefExit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004264 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004265
Joe Perchesb6b38f72010-04-21 03:50:45 +00004266 cFYI(1, "Decoding GetDFSRefer response BCC: %d Offset %d",
Igor Mammedovfec45852008-05-16 13:06:30 +04004267 pSMBr->ByteCount,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004268 le16_to_cpu(pSMBr->t2.DataOffset));
Igor Mammedovfec45852008-05-16 13:06:30 +04004269
4270 /* parse returned result into more usable form */
Steve Frencha1fe78f2008-05-16 18:48:38 +00004271 rc = parse_DFS_referrals(pSMBr, num_of_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004272 target_nodes, nls_codepage, remap,
4273 searchName);
Igor Mammedovfec45852008-05-16 13:06:30 +04004274
Linus Torvalds1da177e2005-04-16 15:20:36 -07004275GetDFSRefExit:
Steve French0d817bc2008-05-22 02:02:03 +00004276 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004277
4278 if (rc == -EAGAIN)
4279 goto getDFSRetry;
4280
4281 return rc;
4282}
4283
Steve French20962432005-09-21 22:05:57 -07004284/* Query File System Info such as free space to old servers such as Win 9x */
4285int
4286SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4287{
4288/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4289 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4290 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4291 FILE_SYSTEM_ALLOC_INFO *response_data;
4292 int rc = 0;
4293 int bytes_returned = 0;
4294 __u16 params, byte_count;
4295
Joe Perchesb6b38f72010-04-21 03:50:45 +00004296 cFYI(1, "OldQFSInfo");
Steve French20962432005-09-21 22:05:57 -07004297oldQFSInfoRetry:
4298 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4299 (void **) &pSMBr);
4300 if (rc)
4301 return rc;
Steve French20962432005-09-21 22:05:57 -07004302
4303 params = 2; /* level */
4304 pSMB->TotalDataCount = 0;
4305 pSMB->MaxParameterCount = cpu_to_le16(2);
4306 pSMB->MaxDataCount = cpu_to_le16(1000);
4307 pSMB->MaxSetupCount = 0;
4308 pSMB->Reserved = 0;
4309 pSMB->Flags = 0;
4310 pSMB->Timeout = 0;
4311 pSMB->Reserved2 = 0;
4312 byte_count = params + 1 /* pad */ ;
4313 pSMB->TotalParameterCount = cpu_to_le16(params);
4314 pSMB->ParameterCount = pSMB->TotalParameterCount;
4315 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4316 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4317 pSMB->DataCount = 0;
4318 pSMB->DataOffset = 0;
4319 pSMB->SetupCount = 1;
4320 pSMB->Reserved3 = 0;
4321 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4322 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4323 pSMB->hdr.smb_buf_length += byte_count;
4324 pSMB->ByteCount = cpu_to_le16(byte_count);
4325
4326 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4327 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4328 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004329 cFYI(1, "Send error in QFSInfo = %d", rc);
Steve French20962432005-09-21 22:05:57 -07004330 } else { /* decode response */
4331 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4332
4333 if (rc || (pSMBr->ByteCount < 18))
4334 rc = -EIO; /* bad smb */
4335 else {
4336 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004337 cFYI(1, "qfsinf resp BCC: %d Offset %d",
4338 pSMBr->ByteCount, data_offset);
Steve French20962432005-09-21 22:05:57 -07004339
Steve French50c2f752007-07-13 00:33:32 +00004340 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004341 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4342 FSData->f_bsize =
4343 le16_to_cpu(response_data->BytesPerSector) *
4344 le32_to_cpu(response_data->
4345 SectorsPerAllocationUnit);
4346 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004347 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004348 FSData->f_bfree = FSData->f_bavail =
4349 le32_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004350 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4351 (unsigned long long)FSData->f_blocks,
4352 (unsigned long long)FSData->f_bfree,
4353 FSData->f_bsize);
Steve French20962432005-09-21 22:05:57 -07004354 }
4355 }
4356 cifs_buf_release(pSMB);
4357
4358 if (rc == -EAGAIN)
4359 goto oldQFSInfoRetry;
4360
4361 return rc;
4362}
4363
Linus Torvalds1da177e2005-04-16 15:20:36 -07004364int
Steve French737b7582005-04-28 22:41:06 -07004365CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004366{
4367/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4368 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4369 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4370 FILE_SYSTEM_INFO *response_data;
4371 int rc = 0;
4372 int bytes_returned = 0;
4373 __u16 params, byte_count;
4374
Joe Perchesb6b38f72010-04-21 03:50:45 +00004375 cFYI(1, "In QFSInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004376QFSInfoRetry:
4377 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4378 (void **) &pSMBr);
4379 if (rc)
4380 return rc;
4381
4382 params = 2; /* level */
4383 pSMB->TotalDataCount = 0;
4384 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004385 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004386 pSMB->MaxSetupCount = 0;
4387 pSMB->Reserved = 0;
4388 pSMB->Flags = 0;
4389 pSMB->Timeout = 0;
4390 pSMB->Reserved2 = 0;
4391 byte_count = params + 1 /* pad */ ;
4392 pSMB->TotalParameterCount = cpu_to_le16(params);
4393 pSMB->ParameterCount = pSMB->TotalParameterCount;
4394 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004395 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004396 pSMB->DataCount = 0;
4397 pSMB->DataOffset = 0;
4398 pSMB->SetupCount = 1;
4399 pSMB->Reserved3 = 0;
4400 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4401 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4402 pSMB->hdr.smb_buf_length += byte_count;
4403 pSMB->ByteCount = cpu_to_le16(byte_count);
4404
4405 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4406 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4407 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004408 cFYI(1, "Send error in QFSInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004409 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004410 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004411
Steve French20962432005-09-21 22:05:57 -07004412 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004413 rc = -EIO; /* bad smb */
4414 else {
4415 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004416
4417 response_data =
4418 (FILE_SYSTEM_INFO
4419 *) (((char *) &pSMBr->hdr.Protocol) +
4420 data_offset);
4421 FSData->f_bsize =
4422 le32_to_cpu(response_data->BytesPerSector) *
4423 le32_to_cpu(response_data->
4424 SectorsPerAllocationUnit);
4425 FSData->f_blocks =
4426 le64_to_cpu(response_data->TotalAllocationUnits);
4427 FSData->f_bfree = FSData->f_bavail =
4428 le64_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004429 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4430 (unsigned long long)FSData->f_blocks,
4431 (unsigned long long)FSData->f_bfree,
4432 FSData->f_bsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004433 }
4434 }
4435 cifs_buf_release(pSMB);
4436
4437 if (rc == -EAGAIN)
4438 goto QFSInfoRetry;
4439
4440 return rc;
4441}
4442
4443int
Steve French737b7582005-04-28 22:41:06 -07004444CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004445{
4446/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4447 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4448 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4449 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4450 int rc = 0;
4451 int bytes_returned = 0;
4452 __u16 params, byte_count;
4453
Joe Perchesb6b38f72010-04-21 03:50:45 +00004454 cFYI(1, "In QFSAttributeInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004455QFSAttributeRetry:
4456 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4457 (void **) &pSMBr);
4458 if (rc)
4459 return rc;
4460
4461 params = 2; /* level */
4462 pSMB->TotalDataCount = 0;
4463 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004464 /* BB find exact max SMB PDU from sess structure BB */
4465 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004466 pSMB->MaxSetupCount = 0;
4467 pSMB->Reserved = 0;
4468 pSMB->Flags = 0;
4469 pSMB->Timeout = 0;
4470 pSMB->Reserved2 = 0;
4471 byte_count = params + 1 /* pad */ ;
4472 pSMB->TotalParameterCount = cpu_to_le16(params);
4473 pSMB->ParameterCount = pSMB->TotalParameterCount;
4474 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004475 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004476 pSMB->DataCount = 0;
4477 pSMB->DataOffset = 0;
4478 pSMB->SetupCount = 1;
4479 pSMB->Reserved3 = 0;
4480 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4481 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4482 pSMB->hdr.smb_buf_length += byte_count;
4483 pSMB->ByteCount = cpu_to_le16(byte_count);
4484
4485 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4486 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4487 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004488 cERROR(1, "Send error in QFSAttributeInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004489 } else { /* decode response */
4490 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4491
Steve French50c2f752007-07-13 00:33:32 +00004492 if (rc || (pSMBr->ByteCount < 13)) {
4493 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004494 rc = -EIO; /* bad smb */
4495 } else {
4496 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4497 response_data =
4498 (FILE_SYSTEM_ATTRIBUTE_INFO
4499 *) (((char *) &pSMBr->hdr.Protocol) +
4500 data_offset);
4501 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004502 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004503 }
4504 }
4505 cifs_buf_release(pSMB);
4506
4507 if (rc == -EAGAIN)
4508 goto QFSAttributeRetry;
4509
4510 return rc;
4511}
4512
4513int
Steve French737b7582005-04-28 22:41:06 -07004514CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004515{
4516/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4517 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4518 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4519 FILE_SYSTEM_DEVICE_INFO *response_data;
4520 int rc = 0;
4521 int bytes_returned = 0;
4522 __u16 params, byte_count;
4523
Joe Perchesb6b38f72010-04-21 03:50:45 +00004524 cFYI(1, "In QFSDeviceInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004525QFSDeviceRetry:
4526 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4527 (void **) &pSMBr);
4528 if (rc)
4529 return rc;
4530
4531 params = 2; /* level */
4532 pSMB->TotalDataCount = 0;
4533 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004534 /* BB find exact max SMB PDU from sess structure BB */
4535 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004536 pSMB->MaxSetupCount = 0;
4537 pSMB->Reserved = 0;
4538 pSMB->Flags = 0;
4539 pSMB->Timeout = 0;
4540 pSMB->Reserved2 = 0;
4541 byte_count = params + 1 /* pad */ ;
4542 pSMB->TotalParameterCount = cpu_to_le16(params);
4543 pSMB->ParameterCount = pSMB->TotalParameterCount;
4544 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004545 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004546
4547 pSMB->DataCount = 0;
4548 pSMB->DataOffset = 0;
4549 pSMB->SetupCount = 1;
4550 pSMB->Reserved3 = 0;
4551 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4552 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4553 pSMB->hdr.smb_buf_length += byte_count;
4554 pSMB->ByteCount = cpu_to_le16(byte_count);
4555
4556 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4557 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4558 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004559 cFYI(1, "Send error in QFSDeviceInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004560 } else { /* decode response */
4561 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4562
Steve French630f3f02007-10-25 21:17:17 +00004563 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004564 rc = -EIO; /* bad smb */
4565 else {
4566 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4567 response_data =
Steve French737b7582005-04-28 22:41:06 -07004568 (FILE_SYSTEM_DEVICE_INFO *)
4569 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004570 data_offset);
4571 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004572 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004573 }
4574 }
4575 cifs_buf_release(pSMB);
4576
4577 if (rc == -EAGAIN)
4578 goto QFSDeviceRetry;
4579
4580 return rc;
4581}
4582
4583int
Steve French737b7582005-04-28 22:41:06 -07004584CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004585{
4586/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4587 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4588 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4589 FILE_SYSTEM_UNIX_INFO *response_data;
4590 int rc = 0;
4591 int bytes_returned = 0;
4592 __u16 params, byte_count;
4593
Joe Perchesb6b38f72010-04-21 03:50:45 +00004594 cFYI(1, "In QFSUnixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004595QFSUnixRetry:
Jeff Laytonf5695992010-09-29 15:27:08 -04004596 rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
4597 (void **) &pSMB, (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004598 if (rc)
4599 return rc;
4600
4601 params = 2; /* level */
4602 pSMB->TotalDataCount = 0;
4603 pSMB->DataCount = 0;
4604 pSMB->DataOffset = 0;
4605 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004606 /* BB find exact max SMB PDU from sess structure BB */
4607 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004608 pSMB->MaxSetupCount = 0;
4609 pSMB->Reserved = 0;
4610 pSMB->Flags = 0;
4611 pSMB->Timeout = 0;
4612 pSMB->Reserved2 = 0;
4613 byte_count = params + 1 /* pad */ ;
4614 pSMB->ParameterCount = cpu_to_le16(params);
4615 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004616 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4617 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004618 pSMB->SetupCount = 1;
4619 pSMB->Reserved3 = 0;
4620 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4621 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4622 pSMB->hdr.smb_buf_length += byte_count;
4623 pSMB->ByteCount = cpu_to_le16(byte_count);
4624
4625 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4626 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4627 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004628 cERROR(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004629 } else { /* decode response */
4630 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4631
4632 if (rc || (pSMBr->ByteCount < 13)) {
4633 rc = -EIO; /* bad smb */
4634 } else {
4635 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4636 response_data =
4637 (FILE_SYSTEM_UNIX_INFO
4638 *) (((char *) &pSMBr->hdr.Protocol) +
4639 data_offset);
4640 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004641 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004642 }
4643 }
4644 cifs_buf_release(pSMB);
4645
4646 if (rc == -EAGAIN)
4647 goto QFSUnixRetry;
4648
4649
4650 return rc;
4651}
4652
Jeremy Allisonac670552005-06-22 17:26:35 -07004653int
Steve French45abc6e2005-06-23 13:42:03 -05004654CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004655{
4656/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4657 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4658 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4659 int rc = 0;
4660 int bytes_returned = 0;
4661 __u16 params, param_offset, offset, byte_count;
4662
Joe Perchesb6b38f72010-04-21 03:50:45 +00004663 cFYI(1, "In SETFSUnixInfo");
Jeremy Allisonac670552005-06-22 17:26:35 -07004664SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004665 /* BB switch to small buf init to save memory */
Jeff Laytonf5695992010-09-29 15:27:08 -04004666 rc = smb_init_no_reconnect(SMB_COM_TRANSACTION2, 15, tcon,
4667 (void **) &pSMB, (void **) &pSMBr);
Jeremy Allisonac670552005-06-22 17:26:35 -07004668 if (rc)
4669 return rc;
4670
4671 params = 4; /* 2 bytes zero followed by info level. */
4672 pSMB->MaxSetupCount = 0;
4673 pSMB->Reserved = 0;
4674 pSMB->Flags = 0;
4675 pSMB->Timeout = 0;
4676 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004677 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4678 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004679 offset = param_offset + params;
4680
4681 pSMB->MaxParameterCount = cpu_to_le16(4);
Steve French582d21e2008-05-13 04:54:12 +00004682 /* BB find exact max SMB PDU from sess structure BB */
4683 pSMB->MaxDataCount = cpu_to_le16(100);
Jeremy Allisonac670552005-06-22 17:26:35 -07004684 pSMB->SetupCount = 1;
4685 pSMB->Reserved3 = 0;
4686 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4687 byte_count = 1 /* pad */ + params + 12;
4688
4689 pSMB->DataCount = cpu_to_le16(12);
4690 pSMB->ParameterCount = cpu_to_le16(params);
4691 pSMB->TotalDataCount = pSMB->DataCount;
4692 pSMB->TotalParameterCount = pSMB->ParameterCount;
4693 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4694 pSMB->DataOffset = cpu_to_le16(offset);
4695
4696 /* Params. */
4697 pSMB->FileNum = 0;
4698 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4699
4700 /* Data. */
4701 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4702 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4703 pSMB->ClientUnixCap = cpu_to_le64(cap);
4704
4705 pSMB->hdr.smb_buf_length += byte_count;
4706 pSMB->ByteCount = cpu_to_le16(byte_count);
4707
4708 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4709 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4710 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004711 cERROR(1, "Send error in SETFSUnixInfo = %d", rc);
Jeremy Allisonac670552005-06-22 17:26:35 -07004712 } else { /* decode response */
4713 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004714 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004715 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004716 }
4717 cifs_buf_release(pSMB);
4718
4719 if (rc == -EAGAIN)
4720 goto SETFSUnixRetry;
4721
4722 return rc;
4723}
4724
4725
Linus Torvalds1da177e2005-04-16 15:20:36 -07004726
4727int
4728CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004729 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004730{
4731/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4732 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4733 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4734 FILE_SYSTEM_POSIX_INFO *response_data;
4735 int rc = 0;
4736 int bytes_returned = 0;
4737 __u16 params, byte_count;
4738
Joe Perchesb6b38f72010-04-21 03:50:45 +00004739 cFYI(1, "In QFSPosixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004740QFSPosixRetry:
4741 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4742 (void **) &pSMBr);
4743 if (rc)
4744 return rc;
4745
4746 params = 2; /* level */
4747 pSMB->TotalDataCount = 0;
4748 pSMB->DataCount = 0;
4749 pSMB->DataOffset = 0;
4750 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004751 /* BB find exact max SMB PDU from sess structure BB */
4752 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004753 pSMB->MaxSetupCount = 0;
4754 pSMB->Reserved = 0;
4755 pSMB->Flags = 0;
4756 pSMB->Timeout = 0;
4757 pSMB->Reserved2 = 0;
4758 byte_count = params + 1 /* pad */ ;
4759 pSMB->ParameterCount = cpu_to_le16(params);
4760 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004761 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4762 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004763 pSMB->SetupCount = 1;
4764 pSMB->Reserved3 = 0;
4765 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4766 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4767 pSMB->hdr.smb_buf_length += byte_count;
4768 pSMB->ByteCount = cpu_to_le16(byte_count);
4769
4770 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4771 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4772 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004773 cFYI(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004774 } else { /* decode response */
4775 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4776
4777 if (rc || (pSMBr->ByteCount < 13)) {
4778 rc = -EIO; /* bad smb */
4779 } else {
4780 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4781 response_data =
4782 (FILE_SYSTEM_POSIX_INFO
4783 *) (((char *) &pSMBr->hdr.Protocol) +
4784 data_offset);
4785 FSData->f_bsize =
4786 le32_to_cpu(response_data->BlockSize);
4787 FSData->f_blocks =
4788 le64_to_cpu(response_data->TotalBlocks);
4789 FSData->f_bfree =
4790 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004791 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004792 FSData->f_bavail = FSData->f_bfree;
4793 } else {
4794 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004795 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004796 }
Steve French790fe572007-07-07 19:25:05 +00004797 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004798 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004799 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004800 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004801 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004802 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004803 }
4804 }
4805 cifs_buf_release(pSMB);
4806
4807 if (rc == -EAGAIN)
4808 goto QFSPosixRetry;
4809
4810 return rc;
4811}
4812
4813
Steve French50c2f752007-07-13 00:33:32 +00004814/* We can not use write of zero bytes trick to
4815 set file size due to need for large file support. Also note that
4816 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004817 routine which is only needed to work around a sharing violation bug
4818 in Samba which this routine can run into */
4819
4820int
4821CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French4b18f2a2008-04-29 00:06:05 +00004822 __u64 size, bool SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004823 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004824{
4825 struct smb_com_transaction2_spi_req *pSMB = NULL;
4826 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4827 struct file_end_of_file_info *parm_data;
4828 int name_len;
4829 int rc = 0;
4830 int bytes_returned = 0;
4831 __u16 params, byte_count, data_count, param_offset, offset;
4832
Joe Perchesb6b38f72010-04-21 03:50:45 +00004833 cFYI(1, "In SetEOF");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004834SetEOFRetry:
4835 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4836 (void **) &pSMBr);
4837 if (rc)
4838 return rc;
4839
4840 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4841 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004842 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004843 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004844 name_len++; /* trailing null */
4845 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004846 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004847 name_len = strnlen(fileName, PATH_MAX);
4848 name_len++; /* trailing null */
4849 strncpy(pSMB->FileName, fileName, name_len);
4850 }
4851 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004852 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004853 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004854 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004855 pSMB->MaxSetupCount = 0;
4856 pSMB->Reserved = 0;
4857 pSMB->Flags = 0;
4858 pSMB->Timeout = 0;
4859 pSMB->Reserved2 = 0;
4860 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004861 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004862 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004863 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004864 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4865 pSMB->InformationLevel =
4866 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4867 else
4868 pSMB->InformationLevel =
4869 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4870 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004871 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4872 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004873 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004874 else
4875 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004876 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004877 }
4878
4879 parm_data =
4880 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4881 offset);
4882 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4883 pSMB->DataOffset = cpu_to_le16(offset);
4884 pSMB->SetupCount = 1;
4885 pSMB->Reserved3 = 0;
4886 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4887 byte_count = 3 /* pad */ + params + data_count;
4888 pSMB->DataCount = cpu_to_le16(data_count);
4889 pSMB->TotalDataCount = pSMB->DataCount;
4890 pSMB->ParameterCount = cpu_to_le16(params);
4891 pSMB->TotalParameterCount = pSMB->ParameterCount;
4892 pSMB->Reserved4 = 0;
4893 pSMB->hdr.smb_buf_length += byte_count;
4894 parm_data->FileSize = cpu_to_le64(size);
4895 pSMB->ByteCount = cpu_to_le16(byte_count);
4896 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4897 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004898 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00004899 cFYI(1, "SetPathInfo (file size) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004900
4901 cifs_buf_release(pSMB);
4902
4903 if (rc == -EAGAIN)
4904 goto SetEOFRetry;
4905
4906 return rc;
4907}
4908
4909int
Steve French50c2f752007-07-13 00:33:32 +00004910CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
Steve French4b18f2a2008-04-29 00:06:05 +00004911 __u16 fid, __u32 pid_of_opener, bool SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004912{
4913 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004914 struct file_end_of_file_info *parm_data;
4915 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004916 __u16 params, param_offset, offset, byte_count, count;
4917
Joe Perchesb6b38f72010-04-21 03:50:45 +00004918 cFYI(1, "SetFileSize (via SetFileInfo) %lld",
4919 (long long)size);
Steve Frenchcd634992005-04-28 22:41:10 -07004920 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4921
Linus Torvalds1da177e2005-04-16 15:20:36 -07004922 if (rc)
4923 return rc;
4924
4925 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4926 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004927
Linus Torvalds1da177e2005-04-16 15:20:36 -07004928 params = 6;
4929 pSMB->MaxSetupCount = 0;
4930 pSMB->Reserved = 0;
4931 pSMB->Flags = 0;
4932 pSMB->Timeout = 0;
4933 pSMB->Reserved2 = 0;
4934 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4935 offset = param_offset + params;
4936
Linus Torvalds1da177e2005-04-16 15:20:36 -07004937 count = sizeof(struct file_end_of_file_info);
4938 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004939 /* BB find exact max SMB PDU from sess structure BB */
4940 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004941 pSMB->SetupCount = 1;
4942 pSMB->Reserved3 = 0;
4943 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4944 byte_count = 3 /* pad */ + params + count;
4945 pSMB->DataCount = cpu_to_le16(count);
4946 pSMB->ParameterCount = cpu_to_le16(params);
4947 pSMB->TotalDataCount = pSMB->DataCount;
4948 pSMB->TotalParameterCount = pSMB->ParameterCount;
4949 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4950 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004951 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4952 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004953 pSMB->DataOffset = cpu_to_le16(offset);
4954 parm_data->FileSize = cpu_to_le64(size);
4955 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004956 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004957 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4958 pSMB->InformationLevel =
4959 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4960 else
4961 pSMB->InformationLevel =
4962 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004963 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004964 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4965 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004966 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004967 else
4968 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004969 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004970 }
4971 pSMB->Reserved4 = 0;
4972 pSMB->hdr.smb_buf_length += byte_count;
4973 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004974 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004975 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004976 cFYI(1, "Send error in SetFileInfo (SetFileSize) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004977 }
4978
Steve French50c2f752007-07-13 00:33:32 +00004979 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004980 since file handle passed in no longer valid */
4981
4982 return rc;
4983}
4984
Steve French50c2f752007-07-13 00:33:32 +00004985/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004986 an open handle, rather than by pathname - this is awkward due to
4987 potential access conflicts on the open, but it is unavoidable for these
4988 old servers since the only other choice is to go from 100 nanosecond DCE
4989 time and resort to the original setpathinfo level which takes the ancient
4990 DOS time format with 2 second granularity */
4991int
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004992CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
4993 const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004994{
4995 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004996 char *data_offset;
4997 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004998 __u16 params, param_offset, offset, byte_count, count;
4999
Joe Perchesb6b38f72010-04-21 03:50:45 +00005000 cFYI(1, "Set Times (via SetFileInfo)");
Steve Frenchcd634992005-04-28 22:41:10 -07005001 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5002
Linus Torvalds1da177e2005-04-16 15:20:36 -07005003 if (rc)
5004 return rc;
5005
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04005006 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5007 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00005008
Linus Torvalds1da177e2005-04-16 15:20:36 -07005009 params = 6;
5010 pSMB->MaxSetupCount = 0;
5011 pSMB->Reserved = 0;
5012 pSMB->Flags = 0;
5013 pSMB->Timeout = 0;
5014 pSMB->Reserved2 = 0;
5015 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5016 offset = param_offset + params;
5017
Steve French50c2f752007-07-13 00:33:32 +00005018 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005019
Steve French26f57362007-08-30 22:09:15 +00005020 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005021 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005022 /* BB find max SMB PDU from sess */
5023 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005024 pSMB->SetupCount = 1;
5025 pSMB->Reserved3 = 0;
5026 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5027 byte_count = 3 /* pad */ + params + count;
5028 pSMB->DataCount = cpu_to_le16(count);
5029 pSMB->ParameterCount = cpu_to_le16(params);
5030 pSMB->TotalDataCount = pSMB->DataCount;
5031 pSMB->TotalParameterCount = pSMB->ParameterCount;
5032 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5033 pSMB->DataOffset = cpu_to_le16(offset);
5034 pSMB->Fid = fid;
5035 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5036 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5037 else
5038 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5039 pSMB->Reserved4 = 0;
5040 pSMB->hdr.smb_buf_length += byte_count;
5041 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00005042 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00005043 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005044 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005045 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005046
Steve French50c2f752007-07-13 00:33:32 +00005047 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07005048 since file handle passed in no longer valid */
5049
5050 return rc;
5051}
5052
Jeff Layton6d22f092008-09-23 11:48:35 -04005053int
5054CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
5055 bool delete_file, __u16 fid, __u32 pid_of_opener)
5056{
5057 struct smb_com_transaction2_sfi_req *pSMB = NULL;
5058 char *data_offset;
5059 int rc = 0;
5060 __u16 params, param_offset, offset, byte_count, count;
5061
Joe Perchesb6b38f72010-04-21 03:50:45 +00005062 cFYI(1, "Set File Disposition (via SetFileInfo)");
Jeff Layton6d22f092008-09-23 11:48:35 -04005063 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5064
5065 if (rc)
5066 return rc;
5067
5068 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5069 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5070
5071 params = 6;
5072 pSMB->MaxSetupCount = 0;
5073 pSMB->Reserved = 0;
5074 pSMB->Flags = 0;
5075 pSMB->Timeout = 0;
5076 pSMB->Reserved2 = 0;
5077 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5078 offset = param_offset + params;
5079
5080 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5081
5082 count = 1;
5083 pSMB->MaxParameterCount = cpu_to_le16(2);
5084 /* BB find max SMB PDU from sess */
5085 pSMB->MaxDataCount = cpu_to_le16(1000);
5086 pSMB->SetupCount = 1;
5087 pSMB->Reserved3 = 0;
5088 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5089 byte_count = 3 /* pad */ + params + count;
5090 pSMB->DataCount = cpu_to_le16(count);
5091 pSMB->ParameterCount = cpu_to_le16(params);
5092 pSMB->TotalDataCount = pSMB->DataCount;
5093 pSMB->TotalParameterCount = pSMB->ParameterCount;
5094 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5095 pSMB->DataOffset = cpu_to_le16(offset);
5096 pSMB->Fid = fid;
5097 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
5098 pSMB->Reserved4 = 0;
5099 pSMB->hdr.smb_buf_length += byte_count;
5100 pSMB->ByteCount = cpu_to_le16(byte_count);
5101 *data_offset = delete_file ? 1 : 0;
5102 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5103 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005104 cFYI(1, "Send error in SetFileDisposition = %d", rc);
Jeff Layton6d22f092008-09-23 11:48:35 -04005105
5106 return rc;
5107}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005108
5109int
Jeff Layton6fc000e2008-08-02 07:26:12 -04005110CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
5111 const char *fileName, const FILE_BASIC_INFO *data,
5112 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005113{
5114 TRANSACTION2_SPI_REQ *pSMB = NULL;
5115 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5116 int name_len;
5117 int rc = 0;
5118 int bytes_returned = 0;
5119 char *data_offset;
5120 __u16 params, param_offset, offset, byte_count, count;
5121
Joe Perchesb6b38f72010-04-21 03:50:45 +00005122 cFYI(1, "In SetTimes");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005123
5124SetTimesRetry:
5125 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5126 (void **) &pSMBr);
5127 if (rc)
5128 return rc;
5129
5130 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5131 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05005132 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005133 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005134 name_len++; /* trailing null */
5135 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005136 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005137 name_len = strnlen(fileName, PATH_MAX);
5138 name_len++; /* trailing null */
5139 strncpy(pSMB->FileName, fileName, name_len);
5140 }
5141
5142 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005143 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005144 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005145 /* BB find max SMB PDU from sess structure BB */
5146 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005147 pSMB->MaxSetupCount = 0;
5148 pSMB->Reserved = 0;
5149 pSMB->Flags = 0;
5150 pSMB->Timeout = 0;
5151 pSMB->Reserved2 = 0;
5152 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005153 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005154 offset = param_offset + params;
5155 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5156 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5157 pSMB->DataOffset = cpu_to_le16(offset);
5158 pSMB->SetupCount = 1;
5159 pSMB->Reserved3 = 0;
5160 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5161 byte_count = 3 /* pad */ + params + count;
5162
5163 pSMB->DataCount = cpu_to_le16(count);
5164 pSMB->ParameterCount = cpu_to_le16(params);
5165 pSMB->TotalDataCount = pSMB->DataCount;
5166 pSMB->TotalParameterCount = pSMB->ParameterCount;
5167 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5168 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5169 else
5170 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5171 pSMB->Reserved4 = 0;
5172 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00005173 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005174 pSMB->ByteCount = cpu_to_le16(byte_count);
5175 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5176 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005177 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005178 cFYI(1, "SetPathInfo (times) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005179
5180 cifs_buf_release(pSMB);
5181
5182 if (rc == -EAGAIN)
5183 goto SetTimesRetry;
5184
5185 return rc;
5186}
5187
5188/* Can not be used to set time stamps yet (due to old DOS time format) */
5189/* Can be used to set attributes */
5190#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
5191 handling it anyway and NT4 was what we thought it would be needed for
5192 Do not delete it until we prove whether needed for Win9x though */
5193int
5194CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
5195 __u16 dos_attrs, const struct nls_table *nls_codepage)
5196{
5197 SETATTR_REQ *pSMB = NULL;
5198 SETATTR_RSP *pSMBr = NULL;
5199 int rc = 0;
5200 int bytes_returned;
5201 int name_len;
5202
Joe Perchesb6b38f72010-04-21 03:50:45 +00005203 cFYI(1, "In SetAttrLegacy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005204
5205SetAttrLgcyRetry:
5206 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
5207 (void **) &pSMBr);
5208 if (rc)
5209 return rc;
5210
5211 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5212 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005213 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005214 PATH_MAX, nls_codepage);
5215 name_len++; /* trailing null */
5216 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005217 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005218 name_len = strnlen(fileName, PATH_MAX);
5219 name_len++; /* trailing null */
5220 strncpy(pSMB->fileName, fileName, name_len);
5221 }
5222 pSMB->attr = cpu_to_le16(dos_attrs);
5223 pSMB->BufferFormat = 0x04;
5224 pSMB->hdr.smb_buf_length += name_len + 1;
5225 pSMB->ByteCount = cpu_to_le16(name_len + 1);
5226 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5227 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005228 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005229 cFYI(1, "Error in LegacySetAttr = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005230
5231 cifs_buf_release(pSMB);
5232
5233 if (rc == -EAGAIN)
5234 goto SetAttrLgcyRetry;
5235
5236 return rc;
5237}
5238#endif /* temporarily unneeded SetAttr legacy function */
5239
Jeff Layton654cf142009-07-09 20:02:49 -04005240static void
5241cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,
5242 const struct cifs_unix_set_info_args *args)
5243{
5244 u64 mode = args->mode;
5245
5246 /*
5247 * Samba server ignores set of file size to zero due to bugs in some
5248 * older clients, but we should be precise - we use SetFileSize to
5249 * set file size and do not want to truncate file size to zero
Lucas De Marchi25985ed2011-03-30 22:57:33 -03005250 * accidentally as happened on one Samba server beta by putting
Jeff Layton654cf142009-07-09 20:02:49 -04005251 * zero instead of -1 here
5252 */
5253 data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
5254 data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
5255 data_offset->LastStatusChange = cpu_to_le64(args->ctime);
5256 data_offset->LastAccessTime = cpu_to_le64(args->atime);
5257 data_offset->LastModificationTime = cpu_to_le64(args->mtime);
5258 data_offset->Uid = cpu_to_le64(args->uid);
5259 data_offset->Gid = cpu_to_le64(args->gid);
5260 /* better to leave device as zero when it is */
5261 data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
5262 data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
5263 data_offset->Permissions = cpu_to_le64(mode);
5264
5265 if (S_ISREG(mode))
5266 data_offset->Type = cpu_to_le32(UNIX_FILE);
5267 else if (S_ISDIR(mode))
5268 data_offset->Type = cpu_to_le32(UNIX_DIR);
5269 else if (S_ISLNK(mode))
5270 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
5271 else if (S_ISCHR(mode))
5272 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
5273 else if (S_ISBLK(mode))
5274 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
5275 else if (S_ISFIFO(mode))
5276 data_offset->Type = cpu_to_le32(UNIX_FIFO);
5277 else if (S_ISSOCK(mode))
5278 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5279}
5280
Linus Torvalds1da177e2005-04-16 15:20:36 -07005281int
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005282CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon,
5283 const struct cifs_unix_set_info_args *args,
5284 u16 fid, u32 pid_of_opener)
5285{
5286 struct smb_com_transaction2_sfi_req *pSMB = NULL;
5287 FILE_UNIX_BASIC_INFO *data_offset;
5288 int rc = 0;
5289 u16 params, param_offset, offset, byte_count, count;
5290
Joe Perchesb6b38f72010-04-21 03:50:45 +00005291 cFYI(1, "Set Unix Info (via SetFileInfo)");
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005292 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5293
5294 if (rc)
5295 return rc;
5296
5297 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5298 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5299
5300 params = 6;
5301 pSMB->MaxSetupCount = 0;
5302 pSMB->Reserved = 0;
5303 pSMB->Flags = 0;
5304 pSMB->Timeout = 0;
5305 pSMB->Reserved2 = 0;
5306 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5307 offset = param_offset + params;
5308
5309 data_offset = (FILE_UNIX_BASIC_INFO *)
5310 ((char *)(&pSMB->hdr.Protocol) + offset);
5311 count = sizeof(FILE_UNIX_BASIC_INFO);
5312
5313 pSMB->MaxParameterCount = cpu_to_le16(2);
5314 /* BB find max SMB PDU from sess */
5315 pSMB->MaxDataCount = cpu_to_le16(1000);
5316 pSMB->SetupCount = 1;
5317 pSMB->Reserved3 = 0;
5318 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5319 byte_count = 3 /* pad */ + params + count;
5320 pSMB->DataCount = cpu_to_le16(count);
5321 pSMB->ParameterCount = cpu_to_le16(params);
5322 pSMB->TotalDataCount = pSMB->DataCount;
5323 pSMB->TotalParameterCount = pSMB->ParameterCount;
5324 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5325 pSMB->DataOffset = cpu_to_le16(offset);
5326 pSMB->Fid = fid;
5327 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5328 pSMB->Reserved4 = 0;
5329 pSMB->hdr.smb_buf_length += byte_count;
5330 pSMB->ByteCount = cpu_to_le16(byte_count);
5331
5332 cifs_fill_unix_set_info(data_offset, args);
5333
5334 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5335 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005336 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005337
5338 /* Note: On -EAGAIN error only caller can retry on handle based calls
5339 since file handle passed in no longer valid */
5340
5341 return rc;
5342}
5343
5344int
Jeff Layton01ea95e2009-07-09 20:02:49 -04005345CIFSSMBUnixSetPathInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,
5346 const struct cifs_unix_set_info_args *args,
5347 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005348{
5349 TRANSACTION2_SPI_REQ *pSMB = NULL;
5350 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5351 int name_len;
5352 int rc = 0;
5353 int bytes_returned = 0;
5354 FILE_UNIX_BASIC_INFO *data_offset;
5355 __u16 params, param_offset, offset, count, byte_count;
5356
Joe Perchesb6b38f72010-04-21 03:50:45 +00005357 cFYI(1, "In SetUID/GID/Mode");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005358setPermsRetry:
5359 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5360 (void **) &pSMBr);
5361 if (rc)
5362 return rc;
5363
5364 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5365 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005366 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005367 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005368 name_len++; /* trailing null */
5369 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07005370 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005371 name_len = strnlen(fileName, PATH_MAX);
5372 name_len++; /* trailing null */
5373 strncpy(pSMB->FileName, fileName, name_len);
5374 }
5375
5376 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005377 count = sizeof(FILE_UNIX_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005378 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005379 /* BB find max SMB PDU from sess structure BB */
5380 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005381 pSMB->MaxSetupCount = 0;
5382 pSMB->Reserved = 0;
5383 pSMB->Flags = 0;
5384 pSMB->Timeout = 0;
5385 pSMB->Reserved2 = 0;
5386 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005387 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005388 offset = param_offset + params;
5389 data_offset =
5390 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5391 offset);
5392 memset(data_offset, 0, count);
5393 pSMB->DataOffset = cpu_to_le16(offset);
5394 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5395 pSMB->SetupCount = 1;
5396 pSMB->Reserved3 = 0;
5397 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5398 byte_count = 3 /* pad */ + params + count;
5399 pSMB->ParameterCount = cpu_to_le16(params);
5400 pSMB->DataCount = cpu_to_le16(count);
5401 pSMB->TotalParameterCount = pSMB->ParameterCount;
5402 pSMB->TotalDataCount = pSMB->DataCount;
5403 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5404 pSMB->Reserved4 = 0;
5405 pSMB->hdr.smb_buf_length += byte_count;
Steve French50c2f752007-07-13 00:33:32 +00005406
Jeff Layton654cf142009-07-09 20:02:49 -04005407 cifs_fill_unix_set_info(data_offset, args);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005408
5409 pSMB->ByteCount = cpu_to_le16(byte_count);
5410 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5411 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005412 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005413 cFYI(1, "SetPathInfo (perms) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005414
Steve French0d817bc2008-05-22 02:02:03 +00005415 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005416 if (rc == -EAGAIN)
5417 goto setPermsRetry;
5418 return rc;
5419}
5420
Steve French50c2f752007-07-13 00:33:32 +00005421int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005422 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005423 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005424 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005425{
5426 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005427 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5428 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005429 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005430 int bytes_returned;
5431
Joe Perchesb6b38f72010-04-21 03:50:45 +00005432 cFYI(1, "In CIFSSMBNotify for file handle %d", (int)netfid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005433 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005434 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005435 if (rc)
5436 return rc;
5437
5438 pSMB->TotalParameterCount = 0 ;
5439 pSMB->TotalDataCount = 0;
5440 pSMB->MaxParameterCount = cpu_to_le32(2);
5441 /* BB find exact data count max from sess structure BB */
5442 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005443/* BB VERIFY verify which is correct for above BB */
5444 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5445 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5446
Linus Torvalds1da177e2005-04-16 15:20:36 -07005447 pSMB->MaxSetupCount = 4;
5448 pSMB->Reserved = 0;
5449 pSMB->ParameterOffset = 0;
5450 pSMB->DataCount = 0;
5451 pSMB->DataOffset = 0;
5452 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5453 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5454 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005455 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005456 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5457 pSMB->Reserved2 = 0;
5458 pSMB->CompletionFilter = cpu_to_le32(filter);
5459 pSMB->Fid = netfid; /* file handle always le */
5460 pSMB->ByteCount = 0;
5461
5462 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005463 (struct smb_hdr *)pSMBr, &bytes_returned,
5464 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005465 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005466 cFYI(1, "Error in Notify = %d", rc);
Steve Frenchff5dbd92005-08-24 17:10:36 -07005467 } else {
5468 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005469 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005470 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005471 sizeof(struct dir_notify_req),
5472 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005473 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005474 dnotify_req->Pid = pSMB->hdr.Pid;
5475 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5476 dnotify_req->Mid = pSMB->hdr.Mid;
5477 dnotify_req->Tid = pSMB->hdr.Tid;
5478 dnotify_req->Uid = pSMB->hdr.Uid;
5479 dnotify_req->netfid = netfid;
5480 dnotify_req->pfile = pfile;
5481 dnotify_req->filter = filter;
5482 dnotify_req->multishot = multishot;
5483 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005484 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005485 &GlobalDnotifyReqList);
5486 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005487 } else
Steve French47c786e2005-10-11 20:03:18 -07005488 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005489 }
5490 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005491 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005492}
Jeff Layton31c05192010-02-10 16:18:26 -05005493
Linus Torvalds1da177e2005-04-16 15:20:36 -07005494#ifdef CONFIG_CIFS_XATTR
Jeff Layton31c05192010-02-10 16:18:26 -05005495/*
5496 * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common
5497 * function used by listxattr and getxattr type calls. When ea_name is set,
5498 * it looks for that attribute name and stuffs that value into the EAData
5499 * buffer. When ea_name is NULL, it stuffs a list of attribute names into the
5500 * buffer. In both cases, the return value is either the length of the
5501 * resulting data or a negative error code. If EAData is a NULL pointer then
5502 * the data isn't copied to it, but the length is returned.
5503 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005504ssize_t
5505CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
Jeff Layton31c05192010-02-10 16:18:26 -05005506 const unsigned char *searchName, const unsigned char *ea_name,
5507 char *EAData, size_t buf_size,
5508 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005509{
5510 /* BB assumes one setup word */
5511 TRANSACTION2_QPI_REQ *pSMB = NULL;
5512 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5513 int rc = 0;
5514 int bytes_returned;
Jeff Layton6e462b92010-02-10 16:18:26 -05005515 int list_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005516 struct fealist *ea_response_data;
Steve French50c2f752007-07-13 00:33:32 +00005517 struct fea *temp_fea;
5518 char *temp_ptr;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005519 char *end_of_smb;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005520 __u16 params, byte_count, data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005521
Joe Perchesb6b38f72010-04-21 03:50:45 +00005522 cFYI(1, "In Query All EAs path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005523QAllEAsRetry:
5524 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5525 (void **) &pSMBr);
5526 if (rc)
5527 return rc;
5528
5529 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Jeff Layton6e462b92010-02-10 16:18:26 -05005530 list_len =
Steve French50c2f752007-07-13 00:33:32 +00005531 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005532 PATH_MAX, nls_codepage, remap);
Jeff Layton6e462b92010-02-10 16:18:26 -05005533 list_len++; /* trailing null */
5534 list_len *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005535 } else { /* BB improve the check for buffer overruns BB */
Jeff Layton6e462b92010-02-10 16:18:26 -05005536 list_len = strnlen(searchName, PATH_MAX);
5537 list_len++; /* trailing null */
5538 strncpy(pSMB->FileName, searchName, list_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005539 }
5540
Jeff Layton6e462b92010-02-10 16:18:26 -05005541 params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005542 pSMB->TotalDataCount = 0;
5543 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005544 /* BB find exact max SMB PDU from sess structure BB */
Jeff Laytone5296142010-02-10 16:18:26 -05005545 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005546 pSMB->MaxSetupCount = 0;
5547 pSMB->Reserved = 0;
5548 pSMB->Flags = 0;
5549 pSMB->Timeout = 0;
5550 pSMB->Reserved2 = 0;
5551 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005552 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005553 pSMB->DataCount = 0;
5554 pSMB->DataOffset = 0;
5555 pSMB->SetupCount = 1;
5556 pSMB->Reserved3 = 0;
5557 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5558 byte_count = params + 1 /* pad */ ;
5559 pSMB->TotalParameterCount = cpu_to_le16(params);
5560 pSMB->ParameterCount = pSMB->TotalParameterCount;
5561 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5562 pSMB->Reserved4 = 0;
5563 pSMB->hdr.smb_buf_length += byte_count;
5564 pSMB->ByteCount = cpu_to_le16(byte_count);
5565
5566 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5567 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5568 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005569 cFYI(1, "Send error in QueryAllEAs = %d", rc);
Jeff Laytonf0d38682010-02-10 16:18:26 -05005570 goto QAllEAsOut;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005571 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005572
5573
5574 /* BB also check enough total bytes returned */
5575 /* BB we need to improve the validity checking
5576 of these trans2 responses */
5577
5578 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5579 if (rc || (pSMBr->ByteCount < 4)) {
5580 rc = -EIO; /* bad smb */
5581 goto QAllEAsOut;
5582 }
5583
5584 /* check that length of list is not more than bcc */
5585 /* check that each entry does not go beyond length
5586 of list */
5587 /* check that each element of each entry does not
5588 go beyond end of list */
5589 /* validate_trans2_offsets() */
5590 /* BB check if start of smb + data_offset > &bcc+ bcc */
5591
5592 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5593 ea_response_data = (struct fealist *)
5594 (((char *) &pSMBr->hdr.Protocol) + data_offset);
5595
Jeff Layton6e462b92010-02-10 16:18:26 -05005596 list_len = le32_to_cpu(ea_response_data->list_len);
Joe Perchesb6b38f72010-04-21 03:50:45 +00005597 cFYI(1, "ea length %d", list_len);
Jeff Layton6e462b92010-02-10 16:18:26 -05005598 if (list_len <= 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005599 cFYI(1, "empty EA list returned from server");
Jeff Laytonf0d38682010-02-10 16:18:26 -05005600 goto QAllEAsOut;
5601 }
5602
Jeff Layton0cd126b2010-02-10 16:18:26 -05005603 /* make sure list_len doesn't go past end of SMB */
Jeff Layton690c5222011-01-20 13:36:51 -05005604 end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr);
Jeff Layton0cd126b2010-02-10 16:18:26 -05005605 if ((char *)ea_response_data + list_len > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005606 cFYI(1, "EA list appears to go beyond SMB");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005607 rc = -EIO;
5608 goto QAllEAsOut;
5609 }
5610
Jeff Laytonf0d38682010-02-10 16:18:26 -05005611 /* account for ea list len */
Jeff Layton6e462b92010-02-10 16:18:26 -05005612 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005613 temp_fea = ea_response_data->list;
5614 temp_ptr = (char *)temp_fea;
Jeff Layton6e462b92010-02-10 16:18:26 -05005615 while (list_len > 0) {
Steve French122ca002010-02-24 21:56:48 +00005616 unsigned int name_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005617 __u16 value_len;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005618
Jeff Layton6e462b92010-02-10 16:18:26 -05005619 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005620 temp_ptr += 4;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005621 /* make sure we can read name_len and value_len */
5622 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005623 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005624 rc = -EIO;
5625 goto QAllEAsOut;
5626 }
5627
5628 name_len = temp_fea->name_len;
5629 value_len = le16_to_cpu(temp_fea->value_len);
5630 list_len -= name_len + 1 + value_len;
5631 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005632 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005633 rc = -EIO;
5634 goto QAllEAsOut;
5635 }
5636
Jeff Layton31c05192010-02-10 16:18:26 -05005637 if (ea_name) {
5638 if (strncmp(ea_name, temp_ptr, name_len) == 0) {
5639 temp_ptr += name_len + 1;
5640 rc = value_len;
5641 if (buf_size == 0)
5642 goto QAllEAsOut;
5643 if ((size_t)value_len > buf_size) {
5644 rc = -ERANGE;
5645 goto QAllEAsOut;
5646 }
5647 memcpy(EAData, temp_ptr, value_len);
5648 goto QAllEAsOut;
5649 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005650 } else {
Jeff Layton31c05192010-02-10 16:18:26 -05005651 /* account for prefix user. and trailing null */
5652 rc += (5 + 1 + name_len);
5653 if (rc < (int) buf_size) {
5654 memcpy(EAData, "user.", 5);
5655 EAData += 5;
5656 memcpy(EAData, temp_ptr, name_len);
5657 EAData += name_len;
5658 /* null terminate name */
5659 *EAData = 0;
5660 ++EAData;
5661 } else if (buf_size == 0) {
5662 /* skip copy - calc size only */
5663 } else {
5664 /* stop before overrun buffer */
5665 rc = -ERANGE;
5666 break;
5667 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005668 }
Jeff Layton0cd126b2010-02-10 16:18:26 -05005669 temp_ptr += name_len + 1 + value_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005670 temp_fea = (struct fea *)temp_ptr;
5671 }
5672
Jeff Layton31c05192010-02-10 16:18:26 -05005673 /* didn't find the named attribute */
5674 if (ea_name)
5675 rc = -ENODATA;
5676
Jeff Laytonf0d38682010-02-10 16:18:26 -05005677QAllEAsOut:
Steve French0d817bc2008-05-22 02:02:03 +00005678 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005679 if (rc == -EAGAIN)
5680 goto QAllEAsRetry;
5681
5682 return (ssize_t)rc;
5683}
5684
Linus Torvalds1da177e2005-04-16 15:20:36 -07005685int
5686CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005687 const char *ea_name, const void *ea_value,
5688 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5689 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005690{
5691 struct smb_com_transaction2_spi_req *pSMB = NULL;
5692 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5693 struct fealist *parm_data;
5694 int name_len;
5695 int rc = 0;
5696 int bytes_returned = 0;
5697 __u16 params, param_offset, byte_count, offset, count;
5698
Joe Perchesb6b38f72010-04-21 03:50:45 +00005699 cFYI(1, "In SetEA");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005700SetEARetry:
5701 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5702 (void **) &pSMBr);
5703 if (rc)
5704 return rc;
5705
5706 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5707 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005708 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005709 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005710 name_len++; /* trailing null */
5711 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005712 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005713 name_len = strnlen(fileName, PATH_MAX);
5714 name_len++; /* trailing null */
5715 strncpy(pSMB->FileName, fileName, name_len);
5716 }
5717
5718 params = 6 + name_len;
5719
5720 /* done calculating parms using name_len of file name,
5721 now use name_len to calculate length of ea name
5722 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005723 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005724 name_len = 0;
5725 else
Steve French50c2f752007-07-13 00:33:32 +00005726 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005727
Steve Frenchdae5dbd2007-12-30 23:49:57 +00005728 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005729 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005730 /* BB find max SMB PDU from sess */
5731 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005732 pSMB->MaxSetupCount = 0;
5733 pSMB->Reserved = 0;
5734 pSMB->Flags = 0;
5735 pSMB->Timeout = 0;
5736 pSMB->Reserved2 = 0;
5737 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005738 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005739 offset = param_offset + params;
5740 pSMB->InformationLevel =
5741 cpu_to_le16(SMB_SET_FILE_EA);
5742
5743 parm_data =
5744 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5745 offset);
5746 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5747 pSMB->DataOffset = cpu_to_le16(offset);
5748 pSMB->SetupCount = 1;
5749 pSMB->Reserved3 = 0;
5750 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5751 byte_count = 3 /* pad */ + params + count;
5752 pSMB->DataCount = cpu_to_le16(count);
5753 parm_data->list_len = cpu_to_le32(count);
5754 parm_data->list[0].EA_flags = 0;
5755 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005756 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005757 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005758 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005759 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005760 parm_data->list[0].name[name_len] = 0;
5761 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5762 /* caller ensures that ea_value_len is less than 64K but
5763 we need to ensure that it fits within the smb */
5764
Steve French50c2f752007-07-13 00:33:32 +00005765 /*BB add length check to see if it would fit in
5766 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005767 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5768 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005769 memcpy(parm_data->list[0].name+name_len+1,
5770 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005771
5772 pSMB->TotalDataCount = pSMB->DataCount;
5773 pSMB->ParameterCount = cpu_to_le16(params);
5774 pSMB->TotalParameterCount = pSMB->ParameterCount;
5775 pSMB->Reserved4 = 0;
5776 pSMB->hdr.smb_buf_length += byte_count;
5777 pSMB->ByteCount = cpu_to_le16(byte_count);
5778 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5779 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005780 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005781 cFYI(1, "SetPathInfo (EA) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005782
5783 cifs_buf_release(pSMB);
5784
5785 if (rc == -EAGAIN)
5786 goto SetEARetry;
5787
5788 return rc;
5789}
5790
5791#endif