blob: 552642a507c4be6744e32a0738c07d27bd89a635 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/cifssmb.c
3 *
Steve Frenchad7a2922008-02-07 23:25:02 +00004 * Copyright (C) International Business Machines Corp., 2002,2008
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>
33#include <linux/posix_acl_xattr.h>
34#include <asm/uaccess.h>
35#include "cifspdu.h"
36#include "cifsglob.h"
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +000037#include "cifsacl.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include "cifsproto.h"
39#include "cifs_unicode.h"
40#include "cifs_debug.h"
41
42#ifdef CONFIG_CIFS_POSIX
43static struct {
44 int index;
45 char *name;
46} protocols[] = {
Steve French39798772006-05-31 22:40:51 +000047#ifdef CONFIG_CIFS_WEAK_PW_HASH
48 {LANMAN_PROT, "\2LM1.2X002"},
Steve French9ac00b72006-09-30 04:13:17 +000049 {LANMAN2_PROT, "\2LANMAN2.1"},
Steve French39798772006-05-31 22:40:51 +000050#endif /* weak password hashing for legacy clients */
Steve French50c2f752007-07-13 00:33:32 +000051 {CIFS_PROT, "\2NT LM 0.12"},
Steve French39798772006-05-31 22:40:51 +000052 {POSIX_PROT, "\2POSIX 2"},
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 {BAD_PROT, "\2"}
54};
55#else
56static struct {
57 int index;
58 char *name;
59} protocols[] = {
Steve French39798772006-05-31 22:40:51 +000060#ifdef CONFIG_CIFS_WEAK_PW_HASH
61 {LANMAN_PROT, "\2LM1.2X002"},
Steve French18f75ca2006-10-01 03:13:01 +000062 {LANMAN2_PROT, "\2LANMAN2.1"},
Steve French39798772006-05-31 22:40:51 +000063#endif /* weak password hashing for legacy clients */
Steve French790fe572007-07-07 19:25:05 +000064 {CIFS_PROT, "\2NT LM 0.12"},
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 {BAD_PROT, "\2"}
66};
67#endif
68
Steve French39798772006-05-31 22:40:51 +000069/* define the number of elements in the cifs dialect array */
70#ifdef CONFIG_CIFS_POSIX
71#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French9ac00b72006-09-30 04:13:17 +000072#define CIFS_NUM_PROT 4
Steve French39798772006-05-31 22:40:51 +000073#else
74#define CIFS_NUM_PROT 2
75#endif /* CIFS_WEAK_PW_HASH */
76#else /* not posix */
77#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French9ac00b72006-09-30 04:13:17 +000078#define CIFS_NUM_PROT 3
Steve French39798772006-05-31 22:40:51 +000079#else
80#define CIFS_NUM_PROT 1
81#endif /* CONFIG_CIFS_WEAK_PW_HASH */
82#endif /* CIFS_POSIX */
83
Igor Mammedovfec45852008-05-16 13:06:30 +040084/* Allocates buffer into dst and copies smb string from src to it.
85 * caller is responsible for freeing dst if function returned 0.
86 * returns:
87 * on success - 0
88 * on failure - errno
89 */
90static int
91cifs_strncpy_to_host(char **dst, const char *src, const int maxlen,
92 const bool is_unicode, const struct nls_table *nls_codepage)
93{
94 int plen;
95
96 if (is_unicode) {
97 plen = UniStrnlen((wchar_t *)src, maxlen);
98 *dst = kmalloc(plen + 2, GFP_KERNEL);
99 if (!*dst)
100 goto cifs_strncpy_to_host_ErrExit;
101 cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage);
102 } else {
103 plen = strnlen(src, maxlen);
104 *dst = kmalloc(plen + 2, GFP_KERNEL);
105 if (!*dst)
106 goto cifs_strncpy_to_host_ErrExit;
107 strncpy(*dst, src, plen);
108 }
109 (*dst)[plen] = 0;
Steve Frencha1fe78f2008-05-16 18:48:38 +0000110 (*dst)[plen+1] = 0; /* harmless for ASCII case, needed for Unicode */
Igor Mammedovfec45852008-05-16 13:06:30 +0400111 return 0;
112
113cifs_strncpy_to_host_ErrExit:
114 cERROR(1, ("Failed to allocate buffer for string\n"));
115 return -ENOMEM;
116}
117
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
119/* Mark as invalid, all open files on tree connections since they
120 were closed when session to server was lost */
Steve French790fe572007-07-07 19:25:05 +0000121static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122{
123 struct cifsFileInfo *open_file = NULL;
Steve French790fe572007-07-07 19:25:05 +0000124 struct list_head *tmp;
125 struct list_head *tmp1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127/* list all files open on tree connection and mark them invalid */
128 write_lock(&GlobalSMBSeslock);
129 list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
Steve French790fe572007-07-07 19:25:05 +0000130 open_file = list_entry(tmp, struct cifsFileInfo, tlist);
Steve Frenchad8b15f2008-08-08 21:10:16 +0000131 open_file->invalidHandle = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 }
133 write_unlock(&GlobalSMBSeslock);
Steve French09d1db52005-04-28 22:41:08 -0700134 /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
135 to this tcon */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136}
137
Steve Frenchad7a2922008-02-07 23:25:02 +0000138/* Allocate and return pointer to an SMB request buffer, and set basic
139 SMB information in the SMB header. If the return code is zero, this
140 function must have filled in request_buf pointer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141static int
142small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000143 void **request_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144{
145 int rc = 0;
146
147 /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
148 check for tcp and smb session status done differently
149 for those three - in the calling routine */
Steve French790fe572007-07-07 19:25:05 +0000150 if (tcon) {
151 if (tcon->tidStatus == CifsExiting) {
Steve French6ab16d22005-11-29 20:55:11 -0800152 /* only tree disconnect, open, and write,
153 (and ulogoff which does not have tcon)
154 are allowed as we start force umount */
Steve French790fe572007-07-07 19:25:05 +0000155 if ((smb_command != SMB_COM_WRITE_ANDX) &&
Steve French50c2f752007-07-13 00:33:32 +0000156 (smb_command != SMB_COM_OPEN_ANDX) &&
Steve French6ab16d22005-11-29 20:55:11 -0800157 (smb_command != SMB_COM_TREE_DISCONNECT)) {
Steve French790fe572007-07-07 19:25:05 +0000158 cFYI(1, ("can not send cmd %d while umounting",
Steve French6ab16d22005-11-29 20:55:11 -0800159 smb_command));
160 return -ENODEV;
161 }
162 }
Steve French790fe572007-07-07 19:25:05 +0000163 if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
Steve French50c2f752007-07-13 00:33:32 +0000164 (tcon->ses->server)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 struct nls_table *nls_codepage;
Steve French50c2f752007-07-13 00:33:32 +0000166 /* Give Demultiplex thread up to 10 seconds to
Steve French09d1db52005-04-28 22:41:08 -0700167 reconnect, should be greater than cifs socket
168 timeout which is 7 seconds */
Steve Frenchc18c8422007-07-18 23:21:09 +0000169 while (tcon->ses->server->tcpStatus ==
Steve French63135e02007-07-17 17:34:02 +0000170 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 wait_event_interruptible_timeout(tcon->ses->server->response_q,
Steve Frenchc18c8422007-07-18 23:21:09 +0000172 (tcon->ses->server->tcpStatus ==
Steve French63135e02007-07-17 17:34:02 +0000173 CifsGood), 10 * HZ);
174 if (tcon->ses->server->tcpStatus ==
175 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 /* on "soft" mounts we wait once */
Steve French4b18f2a2008-04-29 00:06:05 +0000177 if (!tcon->retry ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 (tcon->ses->status == CifsExiting)) {
Steve French50c2f752007-07-13 00:33:32 +0000179 cFYI(1, ("gave up waiting on "
180 "reconnect in smb_init"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 return -EHOSTDOWN;
Steve French09d1db52005-04-28 22:41:08 -0700182 } /* else "hard" mount - keep retrying
183 until process is killed or server
184 comes back on-line */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 } else /* TCP session is reestablished now */
186 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 }
Steve French50c2f752007-07-13 00:33:32 +0000188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 nls_codepage = load_nls_default();
190 /* need to prevent multiple threads trying to
191 simultaneously reconnect the same SMB session */
192 down(&tcon->ses->sesSem);
Steve French3b795212008-11-13 19:45:32 +0000193 if (tcon->ses->need_reconnect)
Steve French50c2f752007-07-13 00:33:32 +0000194 rc = cifs_setup_session(0, tcon->ses,
Steve French09d1db52005-04-28 22:41:08 -0700195 nls_codepage);
Steve French3b795212008-11-13 19:45:32 +0000196 if (!rc && (tcon->need_reconnect)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 mark_open_files_invalid(tcon);
Steve French50c2f752007-07-13 00:33:32 +0000198 rc = CIFSTCon(0, tcon->ses, tcon->treeName,
Steve French8af18972007-02-14 04:42:51 +0000199 tcon, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 up(&tcon->ses->sesSem);
Steve French3e844692005-10-03 13:37:24 -0700201 /* BB FIXME add code to check if wsize needs
202 update due to negotiated smb buffer size
203 shrinking */
Steve French35028d72008-04-09 20:32:42 +0000204 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 atomic_inc(&tconInfoReconnectCount);
Steve French35028d72008-04-09 20:32:42 +0000206 /* tell server Unix caps we support */
207 if (tcon->ses->capabilities & CAP_UNIX)
208 reset_cifs_unix_caps(
209 0 /* no xid */,
210 tcon,
211 NULL /* we do not know sb */,
212 NULL /* no vol info */);
213 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
215 cFYI(1, ("reconnect tcon rc = %d", rc));
Steve French50c2f752007-07-13 00:33:32 +0000216 /* Removed call to reopen open files here.
217 It is safer (and faster) to reopen files
Steve French09d1db52005-04-28 22:41:08 -0700218 one at a time as needed in read and write */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
Steve French50c2f752007-07-13 00:33:32 +0000220 /* Check if handle based operation so we
Steve French09d1db52005-04-28 22:41:08 -0700221 know whether we can continue or not without
222 returning to caller to reset file handle */
Steve French50c2f752007-07-13 00:33:32 +0000223 switch (smb_command) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 case SMB_COM_READ_ANDX:
225 case SMB_COM_WRITE_ANDX:
226 case SMB_COM_CLOSE:
227 case SMB_COM_FIND_CLOSE2:
228 case SMB_COM_LOCKING_ANDX: {
229 unload_nls(nls_codepage);
230 return -EAGAIN;
231 }
232 }
233 } else {
234 up(&tcon->ses->sesSem);
235 }
236 unload_nls(nls_codepage);
237
238 } else {
239 return -EIO;
240 }
241 }
Steve French790fe572007-07-07 19:25:05 +0000242 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 return rc;
244
245 *request_buf = cifs_small_buf_get();
246 if (*request_buf == NULL) {
247 /* BB should we add a retry in here if not a writepage? */
248 return -ENOMEM;
249 }
250
Steve French63135e02007-07-17 17:34:02 +0000251 header_assemble((struct smb_hdr *) *request_buf, smb_command,
Steve Frenchc18c8422007-07-18 23:21:09 +0000252 tcon, wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Steve French790fe572007-07-07 19:25:05 +0000254 if (tcon != NULL)
255 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700256
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 return rc;
Steve French5815449d2006-02-14 01:36:20 +0000258}
259
Steve French12b3b8f2006-02-09 21:12:47 +0000260int
Steve French50c2f752007-07-13 00:33:32 +0000261small_smb_init_no_tc(const int smb_command, const int wct,
Steve French5815449d2006-02-14 01:36:20 +0000262 struct cifsSesInfo *ses, void **request_buf)
Steve French12b3b8f2006-02-09 21:12:47 +0000263{
264 int rc;
Steve French50c2f752007-07-13 00:33:32 +0000265 struct smb_hdr *buffer;
Steve French12b3b8f2006-02-09 21:12:47 +0000266
Steve French5815449d2006-02-14 01:36:20 +0000267 rc = small_smb_init(smb_command, wct, NULL, request_buf);
Steve French790fe572007-07-07 19:25:05 +0000268 if (rc)
Steve French12b3b8f2006-02-09 21:12:47 +0000269 return rc;
270
Steve French04fdabe2006-02-10 05:52:50 +0000271 buffer = (struct smb_hdr *)*request_buf;
Steve French12b3b8f2006-02-09 21:12:47 +0000272 buffer->Mid = GetNextMid(ses->server);
273 if (ses->capabilities & CAP_UNICODE)
274 buffer->Flags2 |= SMBFLG2_UNICODE;
Steve French04fdabe2006-02-10 05:52:50 +0000275 if (ses->capabilities & CAP_STATUS32)
Steve French12b3b8f2006-02-09 21:12:47 +0000276 buffer->Flags2 |= SMBFLG2_ERR_STATUS;
277
278 /* uid, tid can stay at zero as set in header assemble */
279
Steve French50c2f752007-07-13 00:33:32 +0000280 /* BB add support for turning on the signing when
Steve French12b3b8f2006-02-09 21:12:47 +0000281 this function is used after 1st of session setup requests */
282
283 return rc;
284}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
286/* If the return code is zero, this function must fill in request_buf pointer */
287static int
288smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
289 void **request_buf /* returned */ ,
290 void **response_buf /* returned */ )
291{
292 int rc = 0;
293
294 /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
295 check for tcp and smb session status done differently
296 for those three - in the calling routine */
Steve French790fe572007-07-07 19:25:05 +0000297 if (tcon) {
Steve Frenchbfb598202008-11-18 16:33:48 +0000298 if (tcon->tidStatus == CifsExiting) {
Steve French6ab16d22005-11-29 20:55:11 -0800299 /* only tree disconnect, open, and write,
300 (and ulogoff which does not have tcon)
301 are allowed as we start force umount */
Steve French790fe572007-07-07 19:25:05 +0000302 if ((smb_command != SMB_COM_WRITE_ANDX) &&
Steve French6ab16d22005-11-29 20:55:11 -0800303 (smb_command != SMB_COM_OPEN_ANDX) &&
304 (smb_command != SMB_COM_TREE_DISCONNECT)) {
Steve French790fe572007-07-07 19:25:05 +0000305 cFYI(1, ("can not send cmd %d while umounting",
Steve French6ab16d22005-11-29 20:55:11 -0800306 smb_command));
307 return -ENODEV;
308 }
309 }
310
Steve French790fe572007-07-07 19:25:05 +0000311 if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
Steve French50c2f752007-07-13 00:33:32 +0000312 (tcon->ses->server)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 struct nls_table *nls_codepage;
Steve French09d1db52005-04-28 22:41:08 -0700314 /* Give Demultiplex thread up to 10 seconds to
315 reconnect, should be greater than cifs socket
316 timeout which is 7 seconds */
Steve French63135e02007-07-17 17:34:02 +0000317 while (tcon->ses->server->tcpStatus ==
318 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 wait_event_interruptible_timeout(tcon->ses->server->response_q,
Steve French63135e02007-07-17 17:34:02 +0000320 (tcon->ses->server->tcpStatus ==
321 CifsGood), 10 * HZ);
Steve French790fe572007-07-07 19:25:05 +0000322 if (tcon->ses->server->tcpStatus ==
Steve French09d1db52005-04-28 22:41:08 -0700323 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 /* on "soft" mounts we wait once */
Steve French4b18f2a2008-04-29 00:06:05 +0000325 if (!tcon->retry ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 (tcon->ses->status == CifsExiting)) {
Steve French50c2f752007-07-13 00:33:32 +0000327 cFYI(1, ("gave up waiting on "
328 "reconnect in smb_init"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 return -EHOSTDOWN;
Steve French09d1db52005-04-28 22:41:08 -0700330 } /* else "hard" mount - keep retrying
331 until process is killed or server
332 comes on-line */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 } else /* TCP session is reestablished now */
334 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 nls_codepage = load_nls_default();
337 /* need to prevent multiple threads trying to
338 simultaneously reconnect the same SMB session */
339 down(&tcon->ses->sesSem);
Steve French3b795212008-11-13 19:45:32 +0000340 if (tcon->ses->need_reconnect)
Steve French50c2f752007-07-13 00:33:32 +0000341 rc = cifs_setup_session(0, tcon->ses,
Steve French09d1db52005-04-28 22:41:08 -0700342 nls_codepage);
Steve French3b795212008-11-13 19:45:32 +0000343 if (!rc && (tcon->need_reconnect)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 mark_open_files_invalid(tcon);
Steve French09d1db52005-04-28 22:41:08 -0700345 rc = CIFSTCon(0, tcon->ses, tcon->treeName,
346 tcon, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 up(&tcon->ses->sesSem);
Steve French3e844692005-10-03 13:37:24 -0700348 /* BB FIXME add code to check if wsize needs
349 update due to negotiated smb buffer size
350 shrinking */
Steve French35028d72008-04-09 20:32:42 +0000351 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 atomic_inc(&tconInfoReconnectCount);
Steve French35028d72008-04-09 20:32:42 +0000353 /* tell server Unix caps we support */
354 if (tcon->ses->capabilities & CAP_UNIX)
355 reset_cifs_unix_caps(
356 0 /* no xid */,
357 tcon,
358 NULL /* do not know sb */,
359 NULL /* no vol info */);
360 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
362 cFYI(1, ("reconnect tcon rc = %d", rc));
Steve French50c2f752007-07-13 00:33:32 +0000363 /* Removed call to reopen open files here.
364 It is safer (and faster) to reopen files
Steve French09d1db52005-04-28 22:41:08 -0700365 one at a time as needed in read and write */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
Steve French50c2f752007-07-13 00:33:32 +0000367 /* Check if handle based operation so we
Steve French09d1db52005-04-28 22:41:08 -0700368 know whether we can continue or not without
369 returning to caller to reset file handle */
Steve French50c2f752007-07-13 00:33:32 +0000370 switch (smb_command) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 case SMB_COM_READ_ANDX:
372 case SMB_COM_WRITE_ANDX:
373 case SMB_COM_CLOSE:
374 case SMB_COM_FIND_CLOSE2:
375 case SMB_COM_LOCKING_ANDX: {
376 unload_nls(nls_codepage);
377 return -EAGAIN;
378 }
379 }
380 } else {
381 up(&tcon->ses->sesSem);
382 }
383 unload_nls(nls_codepage);
384
385 } else {
386 return -EIO;
387 }
388 }
Steve French790fe572007-07-07 19:25:05 +0000389 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 return rc;
391
392 *request_buf = cifs_buf_get();
393 if (*request_buf == NULL) {
394 /* BB should we add a retry in here if not a writepage? */
395 return -ENOMEM;
396 }
397 /* Although the original thought was we needed the response buf for */
398 /* potential retries of smb operations it turns out we can determine */
399 /* from the mid flags when the request buffer can be resent without */
400 /* having to use a second distinct buffer for the response */
Steve French790fe572007-07-07 19:25:05 +0000401 if (response_buf)
Steve French50c2f752007-07-13 00:33:32 +0000402 *response_buf = *request_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
404 header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000405 wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
Steve French790fe572007-07-07 19:25:05 +0000407 if (tcon != NULL)
408 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700409
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 return rc;
411}
412
Steve French50c2f752007-07-13 00:33:32 +0000413static int validate_t2(struct smb_t2_rsp *pSMB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414{
415 int rc = -EINVAL;
416 int total_size;
Steve French50c2f752007-07-13 00:33:32 +0000417 char *pBCC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
419 /* check for plausible wct, bcc and t2 data and parm sizes */
420 /* check for parm and data offset going beyond end of smb */
Steve French790fe572007-07-07 19:25:05 +0000421 if (pSMB->hdr.WordCount >= 10) {
422 if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
424 /* check that bcc is at least as big as parms + data */
425 /* check that bcc is less than negotiated smb buffer */
426 total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
Steve French790fe572007-07-07 19:25:05 +0000427 if (total_size < 512) {
Steve Frenchc18c8422007-07-18 23:21:09 +0000428 total_size +=
Steve French63135e02007-07-17 17:34:02 +0000429 le16_to_cpu(pSMB->t2_rsp.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 /* BCC le converted in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +0000431 pBCC = (pSMB->hdr.WordCount * 2) +
Steve French09d1db52005-04-28 22:41:08 -0700432 sizeof(struct smb_hdr) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 (char *)pSMB;
Steve French790fe572007-07-07 19:25:05 +0000434 if ((total_size <= (*(u16 *)pBCC)) &&
Steve French50c2f752007-07-13 00:33:32 +0000435 (total_size <
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
437 return 0;
438 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 }
440 }
441 }
Steve French50c2f752007-07-13 00:33:32 +0000442 cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 sizeof(struct smb_t2_rsp) + 16);
444 return rc;
445}
446int
447CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
448{
449 NEGOTIATE_REQ *pSMB;
450 NEGOTIATE_RSP *pSMBr;
451 int rc = 0;
452 int bytes_returned;
Steve French39798772006-05-31 22:40:51 +0000453 int i;
Steve French50c2f752007-07-13 00:33:32 +0000454 struct TCP_Server_Info *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 u16 count;
Steve French750d1152006-06-27 06:28:30 +0000456 unsigned int secFlags;
Al Viro733f99a2006-10-14 16:48:26 +0100457 u16 dialect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
Steve French790fe572007-07-07 19:25:05 +0000459 if (ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 server = ses->server;
461 else {
462 rc = -EIO;
463 return rc;
464 }
465 rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
466 (void **) &pSMB, (void **) &pSMBr);
467 if (rc)
468 return rc;
Steve French750d1152006-06-27 06:28:30 +0000469
470 /* if any of auth flags (ie not sign or seal) are overriden use them */
Steve French790fe572007-07-07 19:25:05 +0000471 if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
Steve French762e5ab2007-06-28 18:41:42 +0000472 secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */
Steve French750d1152006-06-27 06:28:30 +0000473 else /* if override flags set only sign/seal OR them with global auth */
474 secFlags = extended_security | ses->overrideSecFlg;
475
Steve French762e5ab2007-06-28 18:41:42 +0000476 cFYI(1, ("secFlags 0x%x", secFlags));
Steve Frenchf40c5622006-06-28 00:13:38 +0000477
Steve French1982c342005-08-17 12:38:22 -0700478 pSMB->hdr.Mid = GetNextMid(server);
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000479 pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
Steve Frencha0136892007-10-04 20:05:09 +0000480
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000481 if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
Steve French254e55e2006-06-04 05:53:15 +0000482 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Steve Frencha0136892007-10-04 20:05:09 +0000483 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
484 cFYI(1, ("Kerberos only mechanism, enable extended security"));
485 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
486 }
Steve French50c2f752007-07-13 00:33:32 +0000487
Steve French39798772006-05-31 22:40:51 +0000488 count = 0;
Steve French50c2f752007-07-13 00:33:32 +0000489 for (i = 0; i < CIFS_NUM_PROT; i++) {
Steve French39798772006-05-31 22:40:51 +0000490 strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
491 count += strlen(protocols[i].name) + 1;
492 /* null at end of source and target buffers anyway */
493 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 pSMB->hdr.smb_buf_length += count;
495 pSMB->ByteCount = cpu_to_le16(count);
496
497 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
498 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French50c2f752007-07-13 00:33:32 +0000499 if (rc != 0)
Steve French254e55e2006-06-04 05:53:15 +0000500 goto neg_err_exit;
501
Al Viro733f99a2006-10-14 16:48:26 +0100502 dialect = le16_to_cpu(pSMBr->DialectIndex);
Steve French790fe572007-07-07 19:25:05 +0000503 cFYI(1, ("Dialect: %d", dialect));
Steve French254e55e2006-06-04 05:53:15 +0000504 /* Check wct = 1 error case */
Steve French790fe572007-07-07 19:25:05 +0000505 if ((pSMBr->hdr.WordCount < 13) || (dialect == BAD_PROT)) {
Steve French254e55e2006-06-04 05:53:15 +0000506 /* core returns wct = 1, but we do not ask for core - otherwise
Steve French50c2f752007-07-13 00:33:32 +0000507 small wct just comes when dialect index is -1 indicating we
Steve French254e55e2006-06-04 05:53:15 +0000508 could not negotiate a common dialect */
509 rc = -EOPNOTSUPP;
510 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000511#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French790fe572007-07-07 19:25:05 +0000512 } else if ((pSMBr->hdr.WordCount == 13)
Al Viro733f99a2006-10-14 16:48:26 +0100513 && ((dialect == LANMAN_PROT)
514 || (dialect == LANMAN2_PROT))) {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000515 __s16 tmp;
Steve French50c2f752007-07-13 00:33:32 +0000516 struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
Steve French254e55e2006-06-04 05:53:15 +0000517
Steve French790fe572007-07-07 19:25:05 +0000518 if ((secFlags & CIFSSEC_MAY_LANMAN) ||
Steve French750d1152006-06-27 06:28:30 +0000519 (secFlags & CIFSSEC_MAY_PLNTXT))
Steve French254e55e2006-06-04 05:53:15 +0000520 server->secType = LANMAN;
521 else {
522 cERROR(1, ("mount failed weak security disabled"
523 " in /proc/fs/cifs/SecurityFlags"));
Steve French39798772006-05-31 22:40:51 +0000524 rc = -EOPNOTSUPP;
525 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000526 }
Steve French254e55e2006-06-04 05:53:15 +0000527 server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
528 server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
529 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
Steve French39798772006-05-31 22:40:51 +0000530 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve French254e55e2006-06-04 05:53:15 +0000531 GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
532 /* even though we do not use raw we might as well set this
533 accurately, in case we ever find a need for it */
Steve French790fe572007-07-07 19:25:05 +0000534 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
Steve French254e55e2006-06-04 05:53:15 +0000535 server->maxRw = 0xFF00;
536 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
537 } else {
538 server->maxRw = 0;/* we do not need to use raw anyway */
539 server->capabilities = CAP_MPX_MODE;
540 }
Steve Frenchb815f1e52006-10-02 05:53:29 +0000541 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
Steve French1a70d652006-10-02 05:59:18 +0000542 if (tmp == -1) {
Steve French25ee4a92006-09-30 00:54:23 +0000543 /* OS/2 often does not set timezone therefore
544 * we must use server time to calc time zone.
Steve Frenchb815f1e52006-10-02 05:53:29 +0000545 * Could deviate slightly from the right zone.
546 * Smallest defined timezone difference is 15 minutes
547 * (i.e. Nepal). Rounding up/down is done to match
548 * this requirement.
Steve French25ee4a92006-09-30 00:54:23 +0000549 */
Steve Frenchb815f1e52006-10-02 05:53:29 +0000550 int val, seconds, remain, result;
Steve French25ee4a92006-09-30 00:54:23 +0000551 struct timespec ts, utc;
552 utc = CURRENT_TIME;
553 ts = cnvrtDosUnixTm(le16_to_cpu(rsp->SrvTime.Date),
554 le16_to_cpu(rsp->SrvTime.Time));
Steve French50c2f752007-07-13 00:33:32 +0000555 cFYI(1, ("SrvTime %d sec since 1970 (utc: %d) diff: %d",
556 (int)ts.tv_sec, (int)utc.tv_sec,
Steve French25ee4a92006-09-30 00:54:23 +0000557 (int)(utc.tv_sec - ts.tv_sec)));
Steve Frenchb815f1e52006-10-02 05:53:29 +0000558 val = (int)(utc.tv_sec - ts.tv_sec);
Andre Haupt8594c152007-08-30 20:18:41 +0000559 seconds = abs(val);
Steve French947a5062006-10-02 05:55:25 +0000560 result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000561 remain = seconds % MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000562 if (remain >= (MIN_TZ_ADJ / 2))
Steve Frenchb815f1e52006-10-02 05:53:29 +0000563 result += MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000564 if (val < 0)
Steve Frenchad7a2922008-02-07 23:25:02 +0000565 result = -result;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000566 server->timeAdj = result;
Steve French25ee4a92006-09-30 00:54:23 +0000567 } else {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000568 server->timeAdj = (int)tmp;
569 server->timeAdj *= 60; /* also in seconds */
Steve French25ee4a92006-09-30 00:54:23 +0000570 }
Steve French790fe572007-07-07 19:25:05 +0000571 cFYI(1, ("server->timeAdj: %d seconds", server->timeAdj));
Steve French25ee4a92006-09-30 00:54:23 +0000572
Steve French39798772006-05-31 22:40:51 +0000573
Steve French254e55e2006-06-04 05:53:15 +0000574 /* BB get server time for time conversions and add
Steve French50c2f752007-07-13 00:33:32 +0000575 code to use it and timezone since this is not UTC */
Steve French39798772006-05-31 22:40:51 +0000576
Steve French50c2f752007-07-13 00:33:32 +0000577 if (rsp->EncryptionKeyLength ==
Steve French25ee4a92006-09-30 00:54:23 +0000578 cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
Steve French254e55e2006-06-04 05:53:15 +0000579 memcpy(server->cryptKey, rsp->EncryptionKey,
580 CIFS_CRYPTO_KEY_SIZE);
581 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
582 rc = -EIO; /* need cryptkey unless plain text */
583 goto neg_err_exit;
584 }
Steve French39798772006-05-31 22:40:51 +0000585
Steve French790fe572007-07-07 19:25:05 +0000586 cFYI(1, ("LANMAN negotiated"));
Steve French254e55e2006-06-04 05:53:15 +0000587 /* we will not end up setting signing flags - as no signing
588 was in LANMAN and server did not return the flags on */
589 goto signing_check;
Steve French7c7b25b2006-06-01 19:20:10 +0000590#else /* weak security disabled */
Steve French790fe572007-07-07 19:25:05 +0000591 } else if (pSMBr->hdr.WordCount == 13) {
Steve French50c2f752007-07-13 00:33:32 +0000592 cERROR(1, ("mount failed, cifs module not built "
Steve French254e55e2006-06-04 05:53:15 +0000593 "with CIFS_WEAK_PW_HASH support"));
Steve French7c7b25b2006-06-01 19:20:10 +0000594 rc = -EOPNOTSUPP;
595#endif /* WEAK_PW_HASH */
Steve French254e55e2006-06-04 05:53:15 +0000596 goto neg_err_exit;
Steve French790fe572007-07-07 19:25:05 +0000597 } else if (pSMBr->hdr.WordCount != 17) {
Steve French254e55e2006-06-04 05:53:15 +0000598 /* unknown wct */
599 rc = -EOPNOTSUPP;
600 goto neg_err_exit;
601 }
602 /* else wct == 17 NTLM */
603 server->secMode = pSMBr->SecurityMode;
Steve French790fe572007-07-07 19:25:05 +0000604 if ((server->secMode & SECMODE_USER) == 0)
605 cFYI(1, ("share mode security"));
Steve French39798772006-05-31 22:40:51 +0000606
Steve French790fe572007-07-07 19:25:05 +0000607 if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000608#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French750d1152006-06-27 06:28:30 +0000609 if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000610#endif /* CIFS_WEAK_PW_HASH */
Steve French50c2f752007-07-13 00:33:32 +0000611 cERROR(1, ("Server requests plain text password"
Steve French254e55e2006-06-04 05:53:15 +0000612 " but client support disabled"));
Steve French9312f672006-06-04 22:21:07 +0000613
Steve French790fe572007-07-07 19:25:05 +0000614 if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
Steve French254e55e2006-06-04 05:53:15 +0000615 server->secType = NTLMv2;
Steve French790fe572007-07-07 19:25:05 +0000616 else if (secFlags & CIFSSEC_MAY_NTLM)
Steve French254e55e2006-06-04 05:53:15 +0000617 server->secType = NTLM;
Steve French790fe572007-07-07 19:25:05 +0000618 else if (secFlags & CIFSSEC_MAY_NTLMV2)
Steve Frenchf40c5622006-06-28 00:13:38 +0000619 server->secType = NTLMv2;
Steve Frencha0136892007-10-04 20:05:09 +0000620 else if (secFlags & CIFSSEC_MAY_KRB5)
621 server->secType = Kerberos;
622 else if (secFlags & CIFSSEC_MAY_LANMAN)
623 server->secType = LANMAN;
624/* #ifdef CONFIG_CIFS_EXPERIMENTAL
625 else if (secFlags & CIFSSEC_MAY_PLNTXT)
626 server->secType = ??
627#endif */
628 else {
629 rc = -EOPNOTSUPP;
630 cERROR(1, ("Invalid security type"));
631 goto neg_err_exit;
632 }
633 /* else ... any others ...? */
Steve French7c7b25b2006-06-01 19:20:10 +0000634
Steve French254e55e2006-06-04 05:53:15 +0000635 /* one byte, so no need to convert this or EncryptionKeyLen from
636 little endian */
637 server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
638 /* probably no need to store and check maxvcs */
639 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve French254e55e2006-06-04 05:53:15 +0000641 server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
Steve French90c81e02008-02-12 20:32:36 +0000642 cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf));
Steve French254e55e2006-06-04 05:53:15 +0000643 GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
644 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
Steve Frenchb815f1e52006-10-02 05:53:29 +0000645 server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
646 server->timeAdj *= 60;
Steve French254e55e2006-06-04 05:53:15 +0000647 if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
648 memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
649 CIFS_CRYPTO_KEY_SIZE);
650 } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
651 && (pSMBr->EncryptionKeyLength == 0)) {
652 /* decode security blob */
653 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
654 rc = -EIO; /* no crypt key only if plain text pwd */
655 goto neg_err_exit;
656 }
657
658 /* BB might be helpful to save off the domain of server here */
659
Steve French50c2f752007-07-13 00:33:32 +0000660 if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
Steve French254e55e2006-06-04 05:53:15 +0000661 (server->capabilities & CAP_EXTENDED_SECURITY)) {
662 count = pSMBr->ByteCount;
Jeff Laytone187e442007-10-16 17:10:44 +0000663 if (count < 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 rc = -EIO;
Jeff Laytone187e442007-10-16 17:10:44 +0000665 goto neg_err_exit;
666 }
Jeff Laytone7ddee92008-11-14 13:44:38 -0500667 read_lock(&cifs_tcp_ses_lock);
668 if (server->srv_count > 1) {
669 read_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000670 if (memcmp(server->server_GUID,
671 pSMBr->u.extended_response.
672 GUID, 16) != 0) {
673 cFYI(1, ("server UID changed"));
Steve French254e55e2006-06-04 05:53:15 +0000674 memcpy(server->server_GUID,
Jeff Laytone187e442007-10-16 17:10:44 +0000675 pSMBr->u.extended_response.GUID,
676 16);
677 }
Jeff Laytone7ddee92008-11-14 13:44:38 -0500678 } else {
679 read_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000680 memcpy(server->server_GUID,
681 pSMBr->u.extended_response.GUID, 16);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500682 }
Jeff Laytone187e442007-10-16 17:10:44 +0000683
684 if (count == 16) {
685 server->secType = RawNTLMSSP;
Steve French254e55e2006-06-04 05:53:15 +0000686 } else {
687 rc = decode_negTokenInit(pSMBr->u.extended_response.
688 SecurityBlob,
689 count - 16,
690 &server->secType);
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000691 if (rc == 1)
Jeff Laytone5459372007-11-03 05:11:06 +0000692 rc = 0;
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000693 else
Steve French254e55e2006-06-04 05:53:15 +0000694 rc = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 }
Steve French254e55e2006-06-04 05:53:15 +0000696 } else
697 server->capabilities &= ~CAP_EXTENDED_SECURITY;
698
Steve French6344a422006-06-12 04:18:35 +0000699#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French254e55e2006-06-04 05:53:15 +0000700signing_check:
Steve French6344a422006-06-12 04:18:35 +0000701#endif
Steve French762e5ab2007-06-28 18:41:42 +0000702 if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
703 /* MUST_SIGN already includes the MAY_SIGN FLAG
704 so if this is zero it means that signing is disabled */
705 cFYI(1, ("Signing disabled"));
Steve Frenchabb63d62007-10-18 02:58:40 +0000706 if (server->secMode & SECMODE_SIGN_REQUIRED) {
Steve French762e5ab2007-06-28 18:41:42 +0000707 cERROR(1, ("Server requires "
Jeff Layton7111d212007-10-16 16:50:25 +0000708 "packet signing to be enabled in "
709 "/proc/fs/cifs/SecurityFlags."));
Steve Frenchabb63d62007-10-18 02:58:40 +0000710 rc = -EOPNOTSUPP;
711 }
Steve French50c2f752007-07-13 00:33:32 +0000712 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000713 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Steve French762e5ab2007-06-28 18:41:42 +0000714 } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
715 /* signing required */
Jeff38c10a12007-07-06 21:10:07 +0000716 cFYI(1, ("Must sign - secFlags 0x%x", secFlags));
Steve French762e5ab2007-06-28 18:41:42 +0000717 if ((server->secMode &
718 (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
719 cERROR(1,
720 ("signing required but server lacks support"));
Jeff38c10a12007-07-06 21:10:07 +0000721 rc = -EOPNOTSUPP;
Steve French762e5ab2007-06-28 18:41:42 +0000722 } else
723 server->secMode |= SECMODE_SIGN_REQUIRED;
724 } else {
725 /* signing optional ie CIFSSEC_MAY_SIGN */
Steve French790fe572007-07-07 19:25:05 +0000726 if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
Steve French50c2f752007-07-13 00:33:32 +0000727 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000728 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 }
Steve French50c2f752007-07-13 00:33:32 +0000730
731neg_err_exit:
Steve French4a6d87f2005-08-13 08:15:54 -0700732 cifs_buf_release(pSMB);
Steve French254e55e2006-06-04 05:53:15 +0000733
Steve French790fe572007-07-07 19:25:05 +0000734 cFYI(1, ("negprot rc %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 return rc;
736}
737
738int
739CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
740{
741 struct smb_hdr *smb_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
744 cFYI(1, ("In tree disconnect"));
Jeff Laytonf1987b42008-11-15 11:12:47 -0500745
746 /* BB: do we need to check this? These should never be NULL. */
747 if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
748 return -EIO;
749
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 /*
Jeff Laytonf1987b42008-11-15 11:12:47 -0500751 * No need to return error on this operation if tid invalidated and
752 * closed on server already e.g. due to tcp session crashing. Also,
753 * the tcon is no longer on the list, so no need to take lock before
754 * checking this.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 */
Jeff Laytonf1987b42008-11-15 11:12:47 -0500756 if (tcon->need_reconnect)
Steve French50c2f752007-07-13 00:33:32 +0000757 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758
Steve French50c2f752007-07-13 00:33:32 +0000759 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
Steve French09d1db52005-04-28 22:41:08 -0700760 (void **)&smb_buffer);
Jeff Laytonf1987b42008-11-15 11:12:47 -0500761 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 return rc;
Steve French133672e2007-11-13 22:41:37 +0000763
764 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 if (rc)
Steve French966ca922005-04-28 22:41:08 -0700766 cFYI(1, ("Tree disconnect failed %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Steve French50c2f752007-07-13 00:33:32 +0000768 /* No need to return error on this operation if tid invalidated and
Jeff Laytonf1987b42008-11-15 11:12:47 -0500769 closed on server already e.g. due to tcp session crashing */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 if (rc == -EAGAIN)
771 rc = 0;
772
773 return rc;
774}
775
776int
777CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
778{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 LOGOFF_ANDX_REQ *pSMB;
780 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781
782 cFYI(1, ("In SMBLogoff for session disconnect"));
Jeff Layton14fbf502008-11-14 13:53:46 -0500783
784 /*
785 * BB: do we need to check validity of ses and server? They should
786 * always be valid since we have an active reference. If not, that
787 * should probably be a BUG()
788 */
789 if (!ses || !ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 return -EIO;
791
Jeff Layton14fbf502008-11-14 13:53:46 -0500792 down(&ses->sesSem);
Steve French3b795212008-11-13 19:45:32 +0000793 if (ses->need_reconnect)
794 goto session_already_dead; /* no need to send SMBlogoff if uid
795 already closed due to reconnect */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
797 if (rc) {
798 up(&ses->sesSem);
799 return rc;
800 }
801
Steve French3b795212008-11-13 19:45:32 +0000802 pSMB->hdr.Mid = GetNextMid(ses->server);
Steve French1982c342005-08-17 12:38:22 -0700803
Steve French3b795212008-11-13 19:45:32 +0000804 if (ses->server->secMode &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
806 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807
808 pSMB->hdr.Uid = ses->Suid;
809
810 pSMB->AndXCommand = 0xFF;
Steve French133672e2007-11-13 22:41:37 +0000811 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
Steve French3b795212008-11-13 19:45:32 +0000812session_already_dead:
Steve Frencha59c6582005-08-17 12:12:19 -0700813 up(&ses->sesSem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
815 /* if session dead then we do not need to do ulogoff,
Steve French50c2f752007-07-13 00:33:32 +0000816 since server closed smb session, no sense reporting
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 error */
818 if (rc == -EAGAIN)
819 rc = 0;
820 return rc;
821}
822
823int
Steve French2d785a52007-07-15 01:48:57 +0000824CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
825 __u16 type, const struct nls_table *nls_codepage, int remap)
826{
827 TRANSACTION2_SPI_REQ *pSMB = NULL;
828 TRANSACTION2_SPI_RSP *pSMBr = NULL;
829 struct unlink_psx_rq *pRqD;
830 int name_len;
831 int rc = 0;
832 int bytes_returned = 0;
833 __u16 params, param_offset, offset, byte_count;
834
835 cFYI(1, ("In POSIX delete"));
836PsxDelete:
837 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
838 (void **) &pSMBr);
839 if (rc)
840 return rc;
841
842 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
843 name_len =
844 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
845 PATH_MAX, nls_codepage, remap);
846 name_len++; /* trailing null */
847 name_len *= 2;
848 } else { /* BB add path length overrun check */
849 name_len = strnlen(fileName, PATH_MAX);
850 name_len++; /* trailing null */
851 strncpy(pSMB->FileName, fileName, name_len);
852 }
853
854 params = 6 + name_len;
855 pSMB->MaxParameterCount = cpu_to_le16(2);
856 pSMB->MaxDataCount = 0; /* BB double check this with jra */
857 pSMB->MaxSetupCount = 0;
858 pSMB->Reserved = 0;
859 pSMB->Flags = 0;
860 pSMB->Timeout = 0;
861 pSMB->Reserved2 = 0;
862 param_offset = offsetof(struct smb_com_transaction2_spi_req,
863 InformationLevel) - 4;
864 offset = param_offset + params;
865
866 /* Setup pointer to Request Data (inode type) */
867 pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
868 pRqD->type = cpu_to_le16(type);
869 pSMB->ParameterOffset = cpu_to_le16(param_offset);
870 pSMB->DataOffset = cpu_to_le16(offset);
871 pSMB->SetupCount = 1;
872 pSMB->Reserved3 = 0;
873 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
874 byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
875
876 pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
877 pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
878 pSMB->ParameterCount = cpu_to_le16(params);
879 pSMB->TotalParameterCount = pSMB->ParameterCount;
880 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
881 pSMB->Reserved4 = 0;
882 pSMB->hdr.smb_buf_length += byte_count;
883 pSMB->ByteCount = cpu_to_le16(byte_count);
884 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
885 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +0000886 if (rc)
Steve French2d785a52007-07-15 01:48:57 +0000887 cFYI(1, ("Posix delete returned %d", rc));
Steve French2d785a52007-07-15 01:48:57 +0000888 cifs_buf_release(pSMB);
889
890 cifs_stats_inc(&tcon->num_deletes);
891
892 if (rc == -EAGAIN)
893 goto PsxDelete;
894
895 return rc;
896}
897
898int
Steve French737b7582005-04-28 22:41:06 -0700899CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
900 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901{
902 DELETE_FILE_REQ *pSMB = NULL;
903 DELETE_FILE_RSP *pSMBr = NULL;
904 int rc = 0;
905 int bytes_returned;
906 int name_len;
907
908DelFileRetry:
909 rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
910 (void **) &pSMBr);
911 if (rc)
912 return rc;
913
914 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
915 name_len =
Steve French50c2f752007-07-13 00:33:32 +0000916 cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
Steve French737b7582005-04-28 22:41:06 -0700917 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 name_len++; /* trailing null */
919 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700920 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 name_len = strnlen(fileName, PATH_MAX);
922 name_len++; /* trailing null */
923 strncpy(pSMB->fileName, fileName, name_len);
924 }
925 pSMB->SearchAttributes =
926 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
927 pSMB->BufferFormat = 0x04;
928 pSMB->hdr.smb_buf_length += name_len + 1;
929 pSMB->ByteCount = cpu_to_le16(name_len + 1);
930 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
931 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700932 cifs_stats_inc(&tcon->num_deletes);
Steve Frenchad7a2922008-02-07 23:25:02 +0000933 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 cFYI(1, ("Error in RMFile = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
936 cifs_buf_release(pSMB);
937 if (rc == -EAGAIN)
938 goto DelFileRetry;
939
940 return rc;
941}
942
943int
Steve French50c2f752007-07-13 00:33:32 +0000944CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
Steve French737b7582005-04-28 22:41:06 -0700945 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946{
947 DELETE_DIRECTORY_REQ *pSMB = NULL;
948 DELETE_DIRECTORY_RSP *pSMBr = NULL;
949 int rc = 0;
950 int bytes_returned;
951 int name_len;
952
953 cFYI(1, ("In CIFSSMBRmDir"));
954RmDirRetry:
955 rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
956 (void **) &pSMBr);
957 if (rc)
958 return rc;
959
960 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French737b7582005-04-28 22:41:06 -0700961 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
962 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 name_len++; /* trailing null */
964 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700965 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 name_len = strnlen(dirName, PATH_MAX);
967 name_len++; /* trailing null */
968 strncpy(pSMB->DirName, dirName, name_len);
969 }
970
971 pSMB->BufferFormat = 0x04;
972 pSMB->hdr.smb_buf_length += name_len + 1;
973 pSMB->ByteCount = cpu_to_le16(name_len + 1);
974 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
975 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700976 cifs_stats_inc(&tcon->num_rmdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000977 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 cFYI(1, ("Error in RMDir = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979
980 cifs_buf_release(pSMB);
981 if (rc == -EAGAIN)
982 goto RmDirRetry;
983 return rc;
984}
985
986int
987CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -0700988 const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989{
990 int rc = 0;
991 CREATE_DIRECTORY_REQ *pSMB = NULL;
992 CREATE_DIRECTORY_RSP *pSMBr = NULL;
993 int bytes_returned;
994 int name_len;
995
996 cFYI(1, ("In CIFSSMBMkDir"));
997MkDirRetry:
998 rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
999 (void **) &pSMBr);
1000 if (rc)
1001 return rc;
1002
1003 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00001004 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
Steve French737b7582005-04-28 22:41:06 -07001005 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 name_len++; /* trailing null */
1007 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -07001008 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 name_len = strnlen(name, PATH_MAX);
1010 name_len++; /* trailing null */
1011 strncpy(pSMB->DirName, name, name_len);
1012 }
1013
1014 pSMB->BufferFormat = 0x04;
1015 pSMB->hdr.smb_buf_length += name_len + 1;
1016 pSMB->ByteCount = cpu_to_le16(name_len + 1);
1017 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1018 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001019 cifs_stats_inc(&tcon->num_mkdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +00001020 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 cFYI(1, ("Error in Mkdir = %d", rc));
Steve Frencha5a2b482005-08-20 21:42:53 -07001022
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 cifs_buf_release(pSMB);
1024 if (rc == -EAGAIN)
1025 goto MkDirRetry;
1026 return rc;
1027}
1028
Steve French2dd29d32007-04-23 22:07:35 +00001029int
1030CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
Steve Frenchad7a2922008-02-07 23:25:02 +00001031 __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
Steve French50c2f752007-07-13 00:33:32 +00001032 __u32 *pOplock, const char *name,
Steve French2dd29d32007-04-23 22:07:35 +00001033 const struct nls_table *nls_codepage, int remap)
1034{
1035 TRANSACTION2_SPI_REQ *pSMB = NULL;
1036 TRANSACTION2_SPI_RSP *pSMBr = NULL;
1037 int name_len;
1038 int rc = 0;
1039 int bytes_returned = 0;
Steve French2dd29d32007-04-23 22:07:35 +00001040 __u16 params, param_offset, offset, byte_count, count;
Steve Frenchad7a2922008-02-07 23:25:02 +00001041 OPEN_PSX_REQ *pdata;
1042 OPEN_PSX_RSP *psx_rsp;
Steve French2dd29d32007-04-23 22:07:35 +00001043
1044 cFYI(1, ("In POSIX Create"));
1045PsxCreat:
1046 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
1047 (void **) &pSMBr);
1048 if (rc)
1049 return rc;
1050
1051 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1052 name_len =
1053 cifsConvertToUCS((__le16 *) pSMB->FileName, name,
1054 PATH_MAX, nls_codepage, remap);
1055 name_len++; /* trailing null */
1056 name_len *= 2;
1057 } else { /* BB improve the check for buffer overruns BB */
1058 name_len = strnlen(name, PATH_MAX);
1059 name_len++; /* trailing null */
1060 strncpy(pSMB->FileName, name, name_len);
1061 }
1062
1063 params = 6 + name_len;
1064 count = sizeof(OPEN_PSX_REQ);
1065 pSMB->MaxParameterCount = cpu_to_le16(2);
1066 pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
1067 pSMB->MaxSetupCount = 0;
1068 pSMB->Reserved = 0;
1069 pSMB->Flags = 0;
1070 pSMB->Timeout = 0;
1071 pSMB->Reserved2 = 0;
1072 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00001073 InformationLevel) - 4;
Steve French2dd29d32007-04-23 22:07:35 +00001074 offset = param_offset + params;
Steve French2dd29d32007-04-23 22:07:35 +00001075 pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001076 pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
Steve French2dd29d32007-04-23 22:07:35 +00001077 pdata->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00001078 pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
Steve French2dd29d32007-04-23 22:07:35 +00001079 pdata->OpenFlags = cpu_to_le32(*pOplock);
1080 pSMB->ParameterOffset = cpu_to_le16(param_offset);
1081 pSMB->DataOffset = cpu_to_le16(offset);
1082 pSMB->SetupCount = 1;
1083 pSMB->Reserved3 = 0;
1084 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
1085 byte_count = 3 /* pad */ + params + count;
1086
1087 pSMB->DataCount = cpu_to_le16(count);
1088 pSMB->ParameterCount = cpu_to_le16(params);
1089 pSMB->TotalDataCount = pSMB->DataCount;
1090 pSMB->TotalParameterCount = pSMB->ParameterCount;
1091 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1092 pSMB->Reserved4 = 0;
Steve French50c2f752007-07-13 00:33:32 +00001093 pSMB->hdr.smb_buf_length += byte_count;
Steve French2dd29d32007-04-23 22:07:35 +00001094 pSMB->ByteCount = cpu_to_le16(byte_count);
1095 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1096 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1097 if (rc) {
1098 cFYI(1, ("Posix create returned %d", rc));
1099 goto psx_create_err;
1100 }
1101
Steve French790fe572007-07-07 19:25:05 +00001102 cFYI(1, ("copying inode info"));
Steve French2dd29d32007-04-23 22:07:35 +00001103 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1104
1105 if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
1106 rc = -EIO; /* bad smb */
1107 goto psx_create_err;
1108 }
1109
1110 /* copy return information to pRetData */
Steve French50c2f752007-07-13 00:33:32 +00001111 psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
Steve French2dd29d32007-04-23 22:07:35 +00001112 + le16_to_cpu(pSMBr->t2.DataOffset));
Steve French50c2f752007-07-13 00:33:32 +00001113
Steve French2dd29d32007-04-23 22:07:35 +00001114 *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
Steve French790fe572007-07-07 19:25:05 +00001115 if (netfid)
Steve French2dd29d32007-04-23 22:07:35 +00001116 *netfid = psx_rsp->Fid; /* cifs fid stays in le */
1117 /* Let caller know file was created so we can set the mode. */
1118 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001119 if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
Steve French2dd29d32007-04-23 22:07:35 +00001120 *pOplock |= CIFS_CREATE_ACTION;
1121 /* check to make sure response data is there */
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001122 if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1123 pRetData->Type = cpu_to_le32(-1); /* unknown */
Steve French90c81e02008-02-12 20:32:36 +00001124 cFYI(DBG2, ("unknown type"));
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001125 } else {
Steve French790fe572007-07-07 19:25:05 +00001126 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001127 + sizeof(FILE_UNIX_BASIC_INFO)) {
Steve French50c2f752007-07-13 00:33:32 +00001128 cERROR(1, ("Open response data too small"));
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001129 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001130 goto psx_create_err;
1131 }
Steve French50c2f752007-07-13 00:33:32 +00001132 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001133 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001134 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001135 }
Steve French2dd29d32007-04-23 22:07:35 +00001136
1137psx_create_err:
1138 cifs_buf_release(pSMB);
1139
1140 cifs_stats_inc(&tcon->num_mkdirs);
1141
1142 if (rc == -EAGAIN)
1143 goto PsxCreat;
1144
Steve French50c2f752007-07-13 00:33:32 +00001145 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001146}
1147
Steve Frencha9d02ad2005-08-24 23:06:05 -07001148static __u16 convert_disposition(int disposition)
1149{
1150 __u16 ofun = 0;
1151
1152 switch (disposition) {
1153 case FILE_SUPERSEDE:
1154 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1155 break;
1156 case FILE_OPEN:
1157 ofun = SMBOPEN_OAPPEND;
1158 break;
1159 case FILE_CREATE:
1160 ofun = SMBOPEN_OCREATE;
1161 break;
1162 case FILE_OPEN_IF:
1163 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1164 break;
1165 case FILE_OVERWRITE:
1166 ofun = SMBOPEN_OTRUNC;
1167 break;
1168 case FILE_OVERWRITE_IF:
1169 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1170 break;
1171 default:
Steve French790fe572007-07-07 19:25:05 +00001172 cFYI(1, ("unknown disposition %d", disposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001173 ofun = SMBOPEN_OAPPEND; /* regular open */
1174 }
1175 return ofun;
1176}
1177
Jeff Layton35fc37d2008-05-14 10:22:03 -07001178static int
1179access_flags_to_smbopen_mode(const int access_flags)
1180{
1181 int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
1182
1183 if (masked_flags == GENERIC_READ)
1184 return SMBOPEN_READ;
1185 else if (masked_flags == GENERIC_WRITE)
1186 return SMBOPEN_WRITE;
1187
1188 /* just go for read/write */
1189 return SMBOPEN_READWRITE;
1190}
1191
Steve Frencha9d02ad2005-08-24 23:06:05 -07001192int
1193SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1194 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001195 const int access_flags, const int create_options, __u16 *netfid,
1196 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001197 const struct nls_table *nls_codepage, int remap)
1198{
1199 int rc = -EACCES;
1200 OPENX_REQ *pSMB = NULL;
1201 OPENX_RSP *pSMBr = NULL;
1202 int bytes_returned;
1203 int name_len;
1204 __u16 count;
1205
1206OldOpenRetry:
1207 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1208 (void **) &pSMBr);
1209 if (rc)
1210 return rc;
1211
1212 pSMB->AndXCommand = 0xFF; /* none */
1213
1214 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1215 count = 1; /* account for one byte pad to word boundary */
1216 name_len =
1217 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1218 fileName, PATH_MAX, nls_codepage, remap);
1219 name_len++; /* trailing null */
1220 name_len *= 2;
1221 } else { /* BB improve check for buffer overruns BB */
1222 count = 0; /* no pad */
1223 name_len = strnlen(fileName, PATH_MAX);
1224 name_len++; /* trailing null */
1225 strncpy(pSMB->fileName, fileName, name_len);
1226 }
1227 if (*pOplock & REQ_OPLOCK)
1228 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001229 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001230 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001231
Steve Frencha9d02ad2005-08-24 23:06:05 -07001232 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
Jeff Layton35fc37d2008-05-14 10:22:03 -07001233 pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001234 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1235 /* set file as system file if special file such
1236 as fifo and server expecting SFU style and
1237 no Unix extensions */
1238
Steve French790fe572007-07-07 19:25:05 +00001239 if (create_options & CREATE_OPTION_SPECIAL)
1240 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001241 else /* BB FIXME BB */
1242 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001243
Jeff Layton67750fb2008-05-09 22:28:02 +00001244 if (create_options & CREATE_OPTION_READONLY)
1245 pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001246
1247 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001248/* pSMB->CreateOptions = cpu_to_le32(create_options &
1249 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001250 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001251
1252 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001253 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001254 count += name_len;
1255 pSMB->hdr.smb_buf_length += count;
1256
1257 pSMB->ByteCount = cpu_to_le16(count);
1258 /* long_op set to 1 to allow for oplock break timeouts */
1259 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001260 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001261 cifs_stats_inc(&tcon->num_opens);
1262 if (rc) {
1263 cFYI(1, ("Error in Open = %d", rc));
1264 } else {
1265 /* BB verify if wct == 15 */
1266
Steve French582d21e2008-05-13 04:54:12 +00001267/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
Steve Frencha9d02ad2005-08-24 23:06:05 -07001268
1269 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1270 /* Let caller know file was created so we can set the mode. */
1271 /* Do we care about the CreateAction in any other cases? */
1272 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001273/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001274 *pOplock |= CIFS_CREATE_ACTION; */
1275 /* BB FIXME END */
1276
Steve French790fe572007-07-07 19:25:05 +00001277 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001278 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1279 pfile_info->LastAccessTime = 0; /* BB fixme */
1280 pfile_info->LastWriteTime = 0; /* BB fixme */
1281 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001282 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001283 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001284 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001285 pfile_info->AllocationSize =
1286 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1287 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001288 pfile_info->NumberOfLinks = cpu_to_le32(1);
Jeff Layton9a8165f2008-10-17 21:03:20 -04001289 pfile_info->DeletePending = 0;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001290 }
1291 }
1292
1293 cifs_buf_release(pSMB);
1294 if (rc == -EAGAIN)
1295 goto OldOpenRetry;
1296 return rc;
1297}
1298
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299int
1300CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1301 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001302 const int access_flags, const int create_options, __u16 *netfid,
1303 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001304 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305{
1306 int rc = -EACCES;
1307 OPEN_REQ *pSMB = NULL;
1308 OPEN_RSP *pSMBr = NULL;
1309 int bytes_returned;
1310 int name_len;
1311 __u16 count;
1312
1313openRetry:
1314 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1315 (void **) &pSMBr);
1316 if (rc)
1317 return rc;
1318
1319 pSMB->AndXCommand = 0xFF; /* none */
1320
1321 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1322 count = 1; /* account for one byte pad to word boundary */
1323 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001324 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001325 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 name_len++; /* trailing null */
1327 name_len *= 2;
1328 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001329 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 count = 0; /* no pad */
1331 name_len = strnlen(fileName, PATH_MAX);
1332 name_len++; /* trailing null */
1333 pSMB->NameLength = cpu_to_le16(name_len);
1334 strncpy(pSMB->fileName, fileName, name_len);
1335 }
1336 if (*pOplock & REQ_OPLOCK)
1337 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001338 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1341 pSMB->AllocationSize = 0;
Steve Frencheda3c0292005-07-21 15:20:28 -07001342 /* set file as system file if special file such
1343 as fifo and server expecting SFU style and
1344 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001345 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c0292005-07-21 15:20:28 -07001346 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1347 else
1348 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Jeff Layton67750fb2008-05-09 22:28:02 +00001349
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 /* XP does not handle ATTR_POSIX_SEMANTICS */
1351 /* but it helps speed up case sensitive checks for other
1352 servers such as Samba */
1353 if (tcon->ses->capabilities & CAP_UNIX)
1354 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1355
Jeff Layton67750fb2008-05-09 22:28:02 +00001356 if (create_options & CREATE_OPTION_READONLY)
1357 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);
1358
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1360 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c0292005-07-21 15:20:28 -07001361 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001362 /* BB Expirement with various impersonation levels and verify */
1363 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 pSMB->SecurityFlags =
1365 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1366
1367 count += name_len;
1368 pSMB->hdr.smb_buf_length += count;
1369
1370 pSMB->ByteCount = cpu_to_le16(count);
1371 /* long_op set to 1 to allow for oplock break timeouts */
1372 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001373 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha4544342005-08-24 13:59:35 -07001374 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 if (rc) {
1376 cFYI(1, ("Error in Open = %d", rc));
1377 } else {
Steve French09d1db52005-04-28 22:41:08 -07001378 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1380 /* Let caller know file was created so we can set the mode. */
1381 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001382 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001383 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001384 if (pfile_info) {
Steve French61e74802008-12-03 00:57:54 +00001385 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
1386 36 /* CreationTime to Attributes */);
1387 /* the file_info buf is endian converted by caller */
1388 pfile_info->AllocationSize = pSMBr->AllocationSize;
1389 pfile_info->EndOfFile = pSMBr->EndOfFile;
1390 pfile_info->NumberOfLinks = cpu_to_le32(1);
1391 pfile_info->DeletePending = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001394
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 cifs_buf_release(pSMB);
1396 if (rc == -EAGAIN)
1397 goto openRetry;
1398 return rc;
1399}
1400
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401int
Steve French50c2f752007-07-13 00:33:32 +00001402CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1403 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1404 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405{
1406 int rc = -EACCES;
1407 READ_REQ *pSMB = NULL;
1408 READ_RSP *pSMBr = NULL;
1409 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001410 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001411 int resp_buf_type = 0;
1412 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413
Steve French790fe572007-07-07 19:25:05 +00001414 cFYI(1, ("Reading %d bytes on fid %d", count, netfid));
1415 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001416 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001417 else {
Steve Frenchbfa0d752005-08-31 21:50:37 -07001418 wct = 10; /* old style read */
Steve French4c3130e2008-12-09 00:28:16 +00001419 if ((lseek >> 32) > 0) {
1420 /* can not handle this big offset for old */
1421 return -EIO;
1422 }
1423 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424
1425 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001426 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 if (rc)
1428 return rc;
1429
1430 /* tcon and ses pointer are checked in smb_init */
1431 if (tcon->ses->server == NULL)
1432 return -ECONNABORTED;
1433
Steve Frenchec637e32005-12-12 20:53:18 -08001434 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 pSMB->Fid = netfid;
1436 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001437 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001438 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve Frenchbfa0d752005-08-31 21:50:37 -07001439
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 pSMB->Remaining = 0;
1441 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1442 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001443 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001444 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1445 else {
1446 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001447 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001448 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001449 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001450 }
Steve Frenchec637e32005-12-12 20:53:18 -08001451
1452 iov[0].iov_base = (char *)pSMB;
1453 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001454 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Steve French133672e2007-11-13 22:41:37 +00001455 &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001456 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001457 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 if (rc) {
1459 cERROR(1, ("Send error in read = %d", rc));
1460 } else {
1461 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1462 data_length = data_length << 16;
1463 data_length += le16_to_cpu(pSMBr->DataLength);
1464 *nbytes = data_length;
1465
1466 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001467 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 || (data_length > count)) {
Steve French50c2f752007-07-13 00:33:32 +00001469 cFYI(1, ("bad length %d for count %d",
1470 data_length, count));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 rc = -EIO;
1472 *nbytes = 0;
1473 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001474 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001475 le16_to_cpu(pSMBr->DataOffset);
1476/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Steve French50c2f752007-07-13 00:33:32 +00001477 cERROR(1,("Faulting on read rc = %d",rc));
1478 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001479 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001480 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001481 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 }
1483 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484
Steve French4b8f9302006-02-26 16:41:18 +00001485/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001486 if (*buf) {
1487 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001488 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001489 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001490 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001491 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001492 /* return buffer to caller to free */
1493 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001494 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001495 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001496 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001497 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001498 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001499
1500 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 since file handle passed in no longer valid */
1502 return rc;
1503}
1504
Steve Frenchec637e32005-12-12 20:53:18 -08001505
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506int
1507CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1508 const int netfid, const unsigned int count,
1509 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001510 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511{
1512 int rc = -EACCES;
1513 WRITE_REQ *pSMB = NULL;
1514 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001515 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 __u32 bytes_sent;
1517 __u16 byte_count;
1518
Steve French61de8002008-10-30 20:15:22 +00001519 /* cFYI(1, ("write at %lld %d bytes", offset, count));*/
Steve French790fe572007-07-07 19:25:05 +00001520 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001521 return -ECONNABORTED;
1522
Steve French790fe572007-07-07 19:25:05 +00001523 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001524 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001525 else {
Steve French1c955182005-08-30 20:58:07 -07001526 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001527 if ((offset >> 32) > 0) {
1528 /* can not handle big offset for old srv */
1529 return -EIO;
1530 }
1531 }
Steve French1c955182005-08-30 20:58:07 -07001532
1533 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 (void **) &pSMBr);
1535 if (rc)
1536 return rc;
1537 /* tcon and ses pointer are checked in smb_init */
1538 if (tcon->ses->server == NULL)
1539 return -ECONNABORTED;
1540
1541 pSMB->AndXCommand = 0xFF; /* none */
1542 pSMB->Fid = netfid;
1543 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001544 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001545 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French50c2f752007-07-13 00:33:32 +00001546
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 pSMB->Reserved = 0xFFFFFFFF;
1548 pSMB->WriteMode = 0;
1549 pSMB->Remaining = 0;
1550
Steve French50c2f752007-07-13 00:33:32 +00001551 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 can send more if LARGE_WRITE_X capability returned by the server and if
1553 our buffer is big enough or if we convert to iovecs on socket writes
1554 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001555 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1557 } else {
1558 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1559 & ~0xFF;
1560 }
1561
1562 if (bytes_sent > count)
1563 bytes_sent = count;
1564 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001565 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001566 if (buf)
Steve French61e74802008-12-03 00:57:54 +00001567 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001568 else if (ubuf) {
1569 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 cifs_buf_release(pSMB);
1571 return -EFAULT;
1572 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001573 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 /* No buffer */
1575 cifs_buf_release(pSMB);
1576 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001577 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001578 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001579 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001580 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001581 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001582
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1584 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001585 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001586
Steve French790fe572007-07-07 19:25:05 +00001587 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001588 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001589 else { /* old style write has byte count 4 bytes earlier
1590 so 4 bytes pad */
1591 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001592 (struct smb_com_writex_req *)pSMB;
1593 pSMBW->ByteCount = cpu_to_le16(byte_count);
1594 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595
1596 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1597 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001598 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 if (rc) {
1600 cFYI(1, ("Send error in write = %d", rc));
1601 *nbytes = 0;
1602 } else {
1603 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1604 *nbytes = (*nbytes) << 16;
1605 *nbytes += le16_to_cpu(pSMBr->Count);
1606 }
1607
1608 cifs_buf_release(pSMB);
1609
Steve French50c2f752007-07-13 00:33:32 +00001610 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 since file handle passed in no longer valid */
1612
1613 return rc;
1614}
1615
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001616int
1617CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001619 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1620 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621{
1622 int rc = -EACCES;
1623 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001624 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001625 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001626 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627
Steve French790fe572007-07-07 19:25:05 +00001628 cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count));
Steve Frenchff7feac2005-11-15 16:45:16 -08001629
Steve French4c3130e2008-12-09 00:28:16 +00001630 if (tcon->ses->capabilities & CAP_LARGE_FILES) {
Steve French8cc64c62005-10-03 13:49:43 -07001631 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001632 } else {
Steve French8cc64c62005-10-03 13:49:43 -07001633 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001634 if ((offset >> 32) > 0) {
1635 /* can not handle big offset for old srv */
1636 return -EIO;
1637 }
1638 }
Steve French8cc64c62005-10-03 13:49:43 -07001639 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 if (rc)
1641 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 /* tcon and ses pointer are checked in smb_init */
1643 if (tcon->ses->server == NULL)
1644 return -ECONNABORTED;
1645
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001646 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647 pSMB->Fid = netfid;
1648 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001649 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001650 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 pSMB->Reserved = 0xFFFFFFFF;
1652 pSMB->WriteMode = 0;
1653 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001654
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001656 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657
Steve French3e844692005-10-03 13:37:24 -07001658 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1659 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001660 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001661 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001662 pSMB->hdr.smb_buf_length += count+1;
1663 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001664 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1665 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001666 pSMB->ByteCount = cpu_to_le16(count + 1);
1667 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001668 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001669 (struct smb_com_writex_req *)pSMB;
1670 pSMBW->ByteCount = cpu_to_le16(count + 5);
1671 }
Steve French3e844692005-10-03 13:37:24 -07001672 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001673 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001674 iov[0].iov_len = smb_hdr_len + 4;
1675 else /* wct == 12 pad bigger by four bytes */
1676 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001677
Steve French3e844692005-10-03 13:37:24 -07001678
Steve Frenchec637e32005-12-12 20:53:18 -08001679 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001680 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001681 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 if (rc) {
Steve French8cc64c62005-10-03 13:49:43 -07001683 cFYI(1, ("Send error Write2 = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684 *nbytes = 0;
Steve French790fe572007-07-07 19:25:05 +00001685 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001686 /* presumably this can not happen, but best to be safe */
1687 rc = -EIO;
1688 *nbytes = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001689 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001690 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001691 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1692 *nbytes = (*nbytes) << 16;
1693 *nbytes += le16_to_cpu(pSMBr->Count);
Steve French50c2f752007-07-13 00:33:32 +00001694 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695
Steve French4b8f9302006-02-26 16:41:18 +00001696/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001697 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001698 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001699 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001700 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701
Steve French50c2f752007-07-13 00:33:32 +00001702 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703 since file handle passed in no longer valid */
1704
1705 return rc;
1706}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001707
1708
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709int
1710CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1711 const __u16 smb_file_id, const __u64 len,
1712 const __u64 offset, const __u32 numUnlock,
Steve French4b18f2a2008-04-29 00:06:05 +00001713 const __u32 numLock, const __u8 lockType, const bool waitFlag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714{
1715 int rc = 0;
1716 LOCK_REQ *pSMB = NULL;
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001717/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718 int bytes_returned;
1719 int timeout = 0;
1720 __u16 count;
1721
Steve French4b18f2a2008-04-29 00:06:05 +00001722 cFYI(1, ("CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock));
Steve French46810cb2005-04-28 22:41:09 -07001723 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1724
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 if (rc)
1726 return rc;
1727
Steve French790fe572007-07-07 19:25:05 +00001728 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001729 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 pSMB->Timeout = 0;
Steve French4b18f2a2008-04-29 00:06:05 +00001731 } else if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001732 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1734 } else {
1735 pSMB->Timeout = 0;
1736 }
1737
1738 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1739 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1740 pSMB->LockType = lockType;
1741 pSMB->AndXCommand = 0xFF; /* none */
1742 pSMB->Fid = smb_file_id; /* netfid stays le */
1743
Steve French790fe572007-07-07 19:25:05 +00001744 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1746 /* BB where to store pid high? */
1747 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1748 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1749 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1750 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1751 count = sizeof(LOCKING_ANDX_RANGE);
1752 } else {
1753 /* oplock break */
1754 count = 0;
1755 }
1756 pSMB->hdr.smb_buf_length += count;
1757 pSMB->ByteCount = cpu_to_le16(count);
1758
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001759 if (waitFlag) {
1760 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001761 (struct smb_hdr *) pSMB, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001762 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001763 } else {
Steve French133672e2007-11-13 22:41:37 +00001764 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1765 timeout);
1766 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001767 }
Steve Frencha4544342005-08-24 13:59:35 -07001768 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001769 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770 cFYI(1, ("Send error in Lock = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771
Steve French50c2f752007-07-13 00:33:32 +00001772 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 since file handle passed in no longer valid */
1774 return rc;
1775}
1776
1777int
Steve French08547b02006-02-28 22:39:25 +00001778CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1779 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001780 struct file_lock *pLockData, const __u16 lock_type,
Steve French4b18f2a2008-04-29 00:06:05 +00001781 const bool waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001782{
1783 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1784 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001785 struct cifs_posix_lock *parm_data;
1786 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001787 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001788 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001789 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001790 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001791 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001792
1793 cFYI(1, ("Posix Lock"));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001794
Steve French790fe572007-07-07 19:25:05 +00001795 if (pLockData == NULL)
Marcin Slusarzed5f0372008-05-13 04:01:01 +00001796 return -EINVAL;
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001797
Steve French08547b02006-02-28 22:39:25 +00001798 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1799
1800 if (rc)
1801 return rc;
1802
1803 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1804
Steve French50c2f752007-07-13 00:33:32 +00001805 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001806 pSMB->MaxSetupCount = 0;
1807 pSMB->Reserved = 0;
1808 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001809 pSMB->Reserved2 = 0;
1810 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1811 offset = param_offset + params;
1812
Steve French08547b02006-02-28 22:39:25 +00001813 count = sizeof(struct cifs_posix_lock);
1814 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001815 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001816 pSMB->SetupCount = 1;
1817 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001818 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001819 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1820 else
1821 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1822 byte_count = 3 /* pad */ + params + count;
1823 pSMB->DataCount = cpu_to_le16(count);
1824 pSMB->ParameterCount = cpu_to_le16(params);
1825 pSMB->TotalDataCount = pSMB->DataCount;
1826 pSMB->TotalParameterCount = pSMB->ParameterCount;
1827 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001828 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001829 (((char *) &pSMB->hdr.Protocol) + offset);
1830
1831 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001832 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001833 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001834 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001835 pSMB->Timeout = cpu_to_le32(-1);
1836 } else
1837 pSMB->Timeout = 0;
1838
Steve French08547b02006-02-28 22:39:25 +00001839 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001840 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001841 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001842
1843 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001844 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001845 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1846 pSMB->Reserved4 = 0;
1847 pSMB->hdr.smb_buf_length += byte_count;
1848 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001849 if (waitFlag) {
1850 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1851 (struct smb_hdr *) pSMBr, &bytes_returned);
1852 } else {
Steve French133672e2007-11-13 22:41:37 +00001853 iov[0].iov_base = (char *)pSMB;
1854 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1855 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1856 &resp_buf_type, timeout);
1857 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1858 not try to free it twice below on exit */
1859 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001860 }
1861
Steve French08547b02006-02-28 22:39:25 +00001862 if (rc) {
1863 cFYI(1, ("Send error in Posix Lock = %d", rc));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001864 } else if (get_flag) {
1865 /* lock structure can be returned on get */
1866 __u16 data_offset;
1867 __u16 data_count;
1868 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001869
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001870 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1871 rc = -EIO; /* bad smb */
1872 goto plk_err_exit;
1873 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001874 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1875 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001876 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001877 rc = -EIO;
1878 goto plk_err_exit;
1879 }
1880 parm_data = (struct cifs_posix_lock *)
1881 ((char *)&pSMBr->hdr.Protocol + data_offset);
Steve French790fe572007-07-07 19:25:05 +00001882 if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001883 pLockData->fl_type = F_UNLCK;
1884 }
Steve French50c2f752007-07-13 00:33:32 +00001885
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001886plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001887 if (pSMB)
1888 cifs_small_buf_release(pSMB);
1889
Steve French133672e2007-11-13 22:41:37 +00001890 if (resp_buf_type == CIFS_SMALL_BUFFER)
1891 cifs_small_buf_release(iov[0].iov_base);
1892 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1893 cifs_buf_release(iov[0].iov_base);
1894
Steve French08547b02006-02-28 22:39:25 +00001895 /* Note: On -EAGAIN error only caller can retry on handle based calls
1896 since file handle passed in no longer valid */
1897
1898 return rc;
1899}
1900
1901
1902int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1904{
1905 int rc = 0;
1906 CLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907 cFYI(1, ("In CIFSSMBClose"));
1908
1909/* do not retry on dead session on close */
1910 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001911 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 return 0;
1913 if (rc)
1914 return rc;
1915
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e52006-10-02 05:53:29 +00001917 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001919 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001920 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001922 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 /* EINTR is expected when user ctl-c to kill app */
1924 cERROR(1, ("Send error in Close = %d", rc));
1925 }
1926 }
1927
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001929 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930 rc = 0;
1931
1932 return rc;
1933}
1934
1935int
1936CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1937 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001938 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939{
1940 int rc = 0;
1941 RENAME_REQ *pSMB = NULL;
1942 RENAME_RSP *pSMBr = NULL;
1943 int bytes_returned;
1944 int name_len, name_len2;
1945 __u16 count;
1946
1947 cFYI(1, ("In CIFSSMBRename"));
1948renameRetry:
1949 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1950 (void **) &pSMBr);
1951 if (rc)
1952 return rc;
1953
1954 pSMB->BufferFormat = 0x04;
1955 pSMB->SearchAttributes =
1956 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1957 ATTR_DIRECTORY);
1958
1959 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1960 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001961 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001962 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963 name_len++; /* trailing null */
1964 name_len *= 2;
1965 pSMB->OldFileName[name_len] = 0x04; /* pad */
1966 /* protocol requires ASCII signature byte on Unicode string */
1967 pSMB->OldFileName[name_len + 1] = 0x00;
1968 name_len2 =
Steve French582d21e2008-05-13 04:54:12 +00001969 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07001970 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
1972 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00001973 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974 name_len = strnlen(fromName, PATH_MAX);
1975 name_len++; /* trailing null */
1976 strncpy(pSMB->OldFileName, fromName, name_len);
1977 name_len2 = strnlen(toName, PATH_MAX);
1978 name_len2++; /* trailing null */
1979 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
1980 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
1981 name_len2++; /* trailing null */
1982 name_len2++; /* signature byte */
1983 }
1984
1985 count = 1 /* 1st signature byte */ + name_len + name_len2;
1986 pSMB->hdr.smb_buf_length += count;
1987 pSMB->ByteCount = cpu_to_le16(count);
1988
1989 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1990 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001991 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00001992 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 cFYI(1, ("Send error in rename = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 cifs_buf_release(pSMB);
1996
1997 if (rc == -EAGAIN)
1998 goto renameRetry;
1999
2000 return rc;
2001}
2002
Steve French50c2f752007-07-13 00:33:32 +00002003int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
Jeff Layton391e5752008-09-24 11:32:59 -04002004 int netfid, const char *target_name,
Steve French50c2f752007-07-13 00:33:32 +00002005 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006{
2007 struct smb_com_transaction2_sfi_req *pSMB = NULL;
2008 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00002009 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 char *data_offset;
2011 char dummy_string[30];
2012 int rc = 0;
2013 int bytes_returned = 0;
2014 int len_of_str;
2015 __u16 params, param_offset, offset, count, byte_count;
2016
2017 cFYI(1, ("Rename to File by handle"));
2018 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
2019 (void **) &pSMBr);
2020 if (rc)
2021 return rc;
2022
2023 params = 6;
2024 pSMB->MaxSetupCount = 0;
2025 pSMB->Reserved = 0;
2026 pSMB->Flags = 0;
2027 pSMB->Timeout = 0;
2028 pSMB->Reserved2 = 0;
2029 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2030 offset = param_offset + params;
2031
2032 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2033 rename_info = (struct set_file_rename *) data_offset;
2034 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002035 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 pSMB->SetupCount = 1;
2037 pSMB->Reserved3 = 0;
2038 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2039 byte_count = 3 /* pad */ + params;
2040 pSMB->ParameterCount = cpu_to_le16(params);
2041 pSMB->TotalParameterCount = pSMB->ParameterCount;
2042 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2043 pSMB->DataOffset = cpu_to_le16(offset);
2044 /* construct random name ".cifs_tmp<inodenum><mid>" */
2045 rename_info->overwrite = cpu_to_le32(1);
2046 rename_info->root_fid = 0;
2047 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002048 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002049 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2050 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002051 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002053 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002054 target_name, PATH_MAX, nls_codepage,
2055 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 }
2057 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
Jeff Layton391e5752008-09-24 11:32:59 -04002058 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 byte_count += count;
2060 pSMB->DataCount = cpu_to_le16(count);
2061 pSMB->TotalDataCount = pSMB->DataCount;
2062 pSMB->Fid = netfid;
2063 pSMB->InformationLevel =
2064 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2065 pSMB->Reserved4 = 0;
2066 pSMB->hdr.smb_buf_length += byte_count;
2067 pSMB->ByteCount = cpu_to_le16(byte_count);
2068 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002069 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002070 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002071 if (rc)
Steve French790fe572007-07-07 19:25:05 +00002072 cFYI(1, ("Send error in Rename (by file handle) = %d", rc));
Steve Frencha5a2b482005-08-20 21:42:53 -07002073
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 cifs_buf_release(pSMB);
2075
2076 /* Note: On -EAGAIN error only caller can retry on handle based calls
2077 since file handle passed in no longer valid */
2078
2079 return rc;
2080}
2081
2082int
Steve French50c2f752007-07-13 00:33:32 +00002083CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2084 const __u16 target_tid, const char *toName, const int flags,
2085 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086{
2087 int rc = 0;
2088 COPY_REQ *pSMB = NULL;
2089 COPY_RSP *pSMBr = NULL;
2090 int bytes_returned;
2091 int name_len, name_len2;
2092 __u16 count;
2093
2094 cFYI(1, ("In CIFSSMBCopy"));
2095copyRetry:
2096 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2097 (void **) &pSMBr);
2098 if (rc)
2099 return rc;
2100
2101 pSMB->BufferFormat = 0x04;
2102 pSMB->Tid2 = target_tid;
2103
2104 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2105
2106 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002107 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002108 fromName, PATH_MAX, nls_codepage,
2109 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110 name_len++; /* trailing null */
2111 name_len *= 2;
2112 pSMB->OldFileName[name_len] = 0x04; /* pad */
2113 /* protocol requires ASCII signature byte on Unicode string */
2114 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002115 name_len2 =
2116 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002117 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2119 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002120 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 name_len = strnlen(fromName, PATH_MAX);
2122 name_len++; /* trailing null */
2123 strncpy(pSMB->OldFileName, fromName, name_len);
2124 name_len2 = strnlen(toName, PATH_MAX);
2125 name_len2++; /* trailing null */
2126 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2127 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2128 name_len2++; /* trailing null */
2129 name_len2++; /* signature byte */
2130 }
2131
2132 count = 1 /* 1st signature byte */ + name_len + name_len2;
2133 pSMB->hdr.smb_buf_length += count;
2134 pSMB->ByteCount = cpu_to_le16(count);
2135
2136 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2137 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2138 if (rc) {
2139 cFYI(1, ("Send error in copy = %d with %d files copied",
2140 rc, le16_to_cpu(pSMBr->CopyCount)));
2141 }
Steve French0d817bc2008-05-22 02:02:03 +00002142 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143
2144 if (rc == -EAGAIN)
2145 goto copyRetry;
2146
2147 return rc;
2148}
2149
2150int
2151CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2152 const char *fromName, const char *toName,
2153 const struct nls_table *nls_codepage)
2154{
2155 TRANSACTION2_SPI_REQ *pSMB = NULL;
2156 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2157 char *data_offset;
2158 int name_len;
2159 int name_len_target;
2160 int rc = 0;
2161 int bytes_returned = 0;
2162 __u16 params, param_offset, offset, byte_count;
2163
2164 cFYI(1, ("In Symlink Unix style"));
2165createSymLinkRetry:
2166 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2167 (void **) &pSMBr);
2168 if (rc)
2169 return rc;
2170
2171 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2172 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002173 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174 /* find define for this maxpathcomponent */
2175 , nls_codepage);
2176 name_len++; /* trailing null */
2177 name_len *= 2;
2178
Steve French50c2f752007-07-13 00:33:32 +00002179 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180 name_len = strnlen(fromName, PATH_MAX);
2181 name_len++; /* trailing null */
2182 strncpy(pSMB->FileName, fromName, name_len);
2183 }
2184 params = 6 + name_len;
2185 pSMB->MaxSetupCount = 0;
2186 pSMB->Reserved = 0;
2187 pSMB->Flags = 0;
2188 pSMB->Timeout = 0;
2189 pSMB->Reserved2 = 0;
2190 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002191 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 offset = param_offset + params;
2193
2194 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2195 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2196 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002197 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198 /* find define for this maxpathcomponent */
2199 , nls_codepage);
2200 name_len_target++; /* trailing null */
2201 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002202 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203 name_len_target = strnlen(toName, PATH_MAX);
2204 name_len_target++; /* trailing null */
2205 strncpy(data_offset, toName, name_len_target);
2206 }
2207
2208 pSMB->MaxParameterCount = cpu_to_le16(2);
2209 /* BB find exact max on data count below from sess */
2210 pSMB->MaxDataCount = cpu_to_le16(1000);
2211 pSMB->SetupCount = 1;
2212 pSMB->Reserved3 = 0;
2213 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2214 byte_count = 3 /* pad */ + params + name_len_target;
2215 pSMB->DataCount = cpu_to_le16(name_len_target);
2216 pSMB->ParameterCount = cpu_to_le16(params);
2217 pSMB->TotalDataCount = pSMB->DataCount;
2218 pSMB->TotalParameterCount = pSMB->ParameterCount;
2219 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2220 pSMB->DataOffset = cpu_to_le16(offset);
2221 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2222 pSMB->Reserved4 = 0;
2223 pSMB->hdr.smb_buf_length += byte_count;
2224 pSMB->ByteCount = cpu_to_le16(byte_count);
2225 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2226 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002227 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002228 if (rc)
Steve French2d785a52007-07-15 01:48:57 +00002229 cFYI(1, ("Send error in SetPathInfo create symlink = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230
Steve French0d817bc2008-05-22 02:02:03 +00002231 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232
2233 if (rc == -EAGAIN)
2234 goto createSymLinkRetry;
2235
2236 return rc;
2237}
2238
2239int
2240CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2241 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002242 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243{
2244 TRANSACTION2_SPI_REQ *pSMB = NULL;
2245 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2246 char *data_offset;
2247 int name_len;
2248 int name_len_target;
2249 int rc = 0;
2250 int bytes_returned = 0;
2251 __u16 params, param_offset, offset, byte_count;
2252
2253 cFYI(1, ("In Create Hard link Unix style"));
2254createHardLinkRetry:
2255 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2256 (void **) &pSMBr);
2257 if (rc)
2258 return rc;
2259
2260 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002261 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002262 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 name_len++; /* trailing null */
2264 name_len *= 2;
2265
Steve French50c2f752007-07-13 00:33:32 +00002266 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 name_len = strnlen(toName, PATH_MAX);
2268 name_len++; /* trailing null */
2269 strncpy(pSMB->FileName, toName, name_len);
2270 }
2271 params = 6 + name_len;
2272 pSMB->MaxSetupCount = 0;
2273 pSMB->Reserved = 0;
2274 pSMB->Flags = 0;
2275 pSMB->Timeout = 0;
2276 pSMB->Reserved2 = 0;
2277 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002278 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 offset = param_offset + params;
2280
2281 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2282 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2283 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002284 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002285 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 name_len_target++; /* trailing null */
2287 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002288 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289 name_len_target = strnlen(fromName, PATH_MAX);
2290 name_len_target++; /* trailing null */
2291 strncpy(data_offset, fromName, name_len_target);
2292 }
2293
2294 pSMB->MaxParameterCount = cpu_to_le16(2);
2295 /* BB find exact max on data count below from sess*/
2296 pSMB->MaxDataCount = cpu_to_le16(1000);
2297 pSMB->SetupCount = 1;
2298 pSMB->Reserved3 = 0;
2299 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2300 byte_count = 3 /* pad */ + params + name_len_target;
2301 pSMB->ParameterCount = cpu_to_le16(params);
2302 pSMB->TotalParameterCount = pSMB->ParameterCount;
2303 pSMB->DataCount = cpu_to_le16(name_len_target);
2304 pSMB->TotalDataCount = pSMB->DataCount;
2305 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2306 pSMB->DataOffset = cpu_to_le16(offset);
2307 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2308 pSMB->Reserved4 = 0;
2309 pSMB->hdr.smb_buf_length += byte_count;
2310 pSMB->ByteCount = cpu_to_le16(byte_count);
2311 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2312 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002313 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002314 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315 cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316
2317 cifs_buf_release(pSMB);
2318 if (rc == -EAGAIN)
2319 goto createHardLinkRetry;
2320
2321 return rc;
2322}
2323
2324int
2325CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2326 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002327 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328{
2329 int rc = 0;
2330 NT_RENAME_REQ *pSMB = NULL;
2331 RENAME_RSP *pSMBr = NULL;
2332 int bytes_returned;
2333 int name_len, name_len2;
2334 __u16 count;
2335
2336 cFYI(1, ("In CIFSCreateHardLink"));
2337winCreateHardLinkRetry:
2338
2339 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2340 (void **) &pSMBr);
2341 if (rc)
2342 return rc;
2343
2344 pSMB->SearchAttributes =
2345 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2346 ATTR_DIRECTORY);
2347 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2348 pSMB->ClusterCount = 0;
2349
2350 pSMB->BufferFormat = 0x04;
2351
2352 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2353 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002354 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002355 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356 name_len++; /* trailing null */
2357 name_len *= 2;
2358 pSMB->OldFileName[name_len] = 0; /* pad */
Steve French50c2f752007-07-13 00:33:32 +00002359 pSMB->OldFileName[name_len + 1] = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002361 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002362 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2364 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002365 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366 name_len = strnlen(fromName, PATH_MAX);
2367 name_len++; /* trailing null */
2368 strncpy(pSMB->OldFileName, fromName, name_len);
2369 name_len2 = strnlen(toName, PATH_MAX);
2370 name_len2++; /* trailing null */
2371 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2372 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2373 name_len2++; /* trailing null */
2374 name_len2++; /* signature byte */
2375 }
2376
2377 count = 1 /* string type byte */ + name_len + name_len2;
2378 pSMB->hdr.smb_buf_length += count;
2379 pSMB->ByteCount = cpu_to_le16(count);
2380
2381 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2382 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002383 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002384 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385 cFYI(1, ("Send error in hard link (NT rename) = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00002386
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387 cifs_buf_release(pSMB);
2388 if (rc == -EAGAIN)
2389 goto winCreateHardLinkRetry;
2390
2391 return rc;
2392}
2393
2394int
2395CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
2396 const unsigned char *searchName,
2397 char *symlinkinfo, const int buflen,
2398 const struct nls_table *nls_codepage)
2399{
2400/* SMB_QUERY_FILE_UNIX_LINK */
2401 TRANSACTION2_QPI_REQ *pSMB = NULL;
2402 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2403 int rc = 0;
2404 int bytes_returned;
2405 int name_len;
2406 __u16 params, byte_count;
2407
2408 cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName));
2409
2410querySymLinkRetry:
2411 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2412 (void **) &pSMBr);
2413 if (rc)
2414 return rc;
2415
2416 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2417 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002418 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2419 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420 name_len++; /* trailing null */
2421 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002422 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 name_len = strnlen(searchName, PATH_MAX);
2424 name_len++; /* trailing null */
2425 strncpy(pSMB->FileName, searchName, name_len);
2426 }
2427
2428 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2429 pSMB->TotalDataCount = 0;
2430 pSMB->MaxParameterCount = cpu_to_le16(2);
2431 /* BB find exact max data count below from sess structure BB */
2432 pSMB->MaxDataCount = cpu_to_le16(4000);
2433 pSMB->MaxSetupCount = 0;
2434 pSMB->Reserved = 0;
2435 pSMB->Flags = 0;
2436 pSMB->Timeout = 0;
2437 pSMB->Reserved2 = 0;
2438 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002439 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440 pSMB->DataCount = 0;
2441 pSMB->DataOffset = 0;
2442 pSMB->SetupCount = 1;
2443 pSMB->Reserved3 = 0;
2444 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2445 byte_count = params + 1 /* pad */ ;
2446 pSMB->TotalParameterCount = cpu_to_le16(params);
2447 pSMB->ParameterCount = pSMB->TotalParameterCount;
2448 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2449 pSMB->Reserved4 = 0;
2450 pSMB->hdr.smb_buf_length += byte_count;
2451 pSMB->ByteCount = cpu_to_le16(byte_count);
2452
2453 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2454 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2455 if (rc) {
2456 cFYI(1, ("Send error in QuerySymLinkInfo = %d", rc));
2457 } else {
2458 /* decode response */
2459
2460 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2461 if (rc || (pSMBr->ByteCount < 2))
2462 /* BB also check enough total bytes returned */
2463 rc = -EIO; /* bad smb */
2464 else {
2465 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2466 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2467
2468 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2469 name_len = UniStrnlen((wchar_t *) ((char *)
Steve French50c2f752007-07-13 00:33:32 +00002470 &pSMBr->hdr.Protocol + data_offset),
2471 min_t(const int, buflen, count) / 2);
Steve French737b7582005-04-28 22:41:06 -07002472 /* BB FIXME investigate remapping reserved chars here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002474 (__le16 *) ((char *)&pSMBr->hdr.Protocol
2475 + data_offset),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476 name_len, nls_codepage);
2477 } else {
2478 strncpy(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002479 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480 data_offset,
2481 min_t(const int, buflen, count));
2482 }
2483 symlinkinfo[buflen] = 0;
2484 /* just in case so calling code does not go off the end of buffer */
2485 }
2486 }
2487 cifs_buf_release(pSMB);
2488 if (rc == -EAGAIN)
2489 goto querySymLinkRetry;
2490 return rc;
2491}
2492
Parag Warudkarc9489772007-10-23 18:09:48 +00002493#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08002494/* Initialize NT TRANSACT SMB into small smb request buffer.
2495 This assumes that all NT TRANSACTS that we init here have
2496 total parm and data under about 400 bytes (to fit in small cifs
2497 buffer size), which is the case so far, it easily fits. NB:
2498 Setup words themselves and ByteCount
2499 MaxSetupCount (size of returned setup area) and
2500 MaxParameterCount (returned parms size) must be set by caller */
Steve French50c2f752007-07-13 00:33:32 +00002501static int
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002502smb_init_nttransact(const __u16 sub_command, const int setup_count,
Steve French0a4b92c2006-01-12 15:44:21 -08002503 const int parm_len, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002504 void **ret_buf)
Steve French0a4b92c2006-01-12 15:44:21 -08002505{
2506 int rc;
2507 __u32 temp_offset;
Steve French50c2f752007-07-13 00:33:32 +00002508 struct smb_com_ntransact_req *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08002509
2510 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
2511 (void **)&pSMB);
2512 if (rc)
2513 return rc;
2514 *ret_buf = (void *)pSMB;
2515 pSMB->Reserved = 0;
2516 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
2517 pSMB->TotalDataCount = 0;
2518 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2519 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
2520 pSMB->ParameterCount = pSMB->TotalParameterCount;
2521 pSMB->DataCount = pSMB->TotalDataCount;
2522 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
2523 (setup_count * 2) - 4 /* for rfc1001 length itself */;
2524 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
2525 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
2526 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
2527 pSMB->SubCommand = cpu_to_le16(sub_command);
2528 return 0;
2529}
2530
2531static int
Steve French50c2f752007-07-13 00:33:32 +00002532validate_ntransact(char *buf, char **ppparm, char **ppdata,
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002533 __u32 *pparmlen, __u32 *pdatalen)
Steve French0a4b92c2006-01-12 15:44:21 -08002534{
Steve French50c2f752007-07-13 00:33:32 +00002535 char *end_of_smb;
Steve French0a4b92c2006-01-12 15:44:21 -08002536 __u32 data_count, data_offset, parm_count, parm_offset;
Steve French50c2f752007-07-13 00:33:32 +00002537 struct smb_com_ntransact_rsp *pSMBr;
Steve French0a4b92c2006-01-12 15:44:21 -08002538
Steve French630f3f0c2007-10-25 21:17:17 +00002539 *pdatalen = 0;
2540 *pparmlen = 0;
2541
Steve French790fe572007-07-07 19:25:05 +00002542 if (buf == NULL)
Steve French0a4b92c2006-01-12 15:44:21 -08002543 return -EINVAL;
2544
2545 pSMBr = (struct smb_com_ntransact_rsp *)buf;
2546
2547 /* ByteCount was converted from little endian in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +00002548 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
Steve French0a4b92c2006-01-12 15:44:21 -08002549 (char *)&pSMBr->ByteCount;
2550
Steve French0a4b92c2006-01-12 15:44:21 -08002551 data_offset = le32_to_cpu(pSMBr->DataOffset);
2552 data_count = le32_to_cpu(pSMBr->DataCount);
Steve French50c2f752007-07-13 00:33:32 +00002553 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
Steve French0a4b92c2006-01-12 15:44:21 -08002554 parm_count = le32_to_cpu(pSMBr->ParameterCount);
2555
2556 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
2557 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
2558
2559 /* should we also check that parm and data areas do not overlap? */
Steve French790fe572007-07-07 19:25:05 +00002560 if (*ppparm > end_of_smb) {
2561 cFYI(1, ("parms start after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002562 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002563 } else if (parm_count + *ppparm > end_of_smb) {
2564 cFYI(1, ("parm end after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002565 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002566 } else if (*ppdata > end_of_smb) {
2567 cFYI(1, ("data starts after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002568 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002569 } else if (data_count + *ppdata > end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002570 cFYI(1, ("data %p + count %d (%p) ends after end of smb %p start %p",
Steve French50c2f752007-07-13 00:33:32 +00002571 *ppdata, data_count, (data_count + *ppdata),
2572 end_of_smb, pSMBr));
Steve French0a4b92c2006-01-12 15:44:21 -08002573 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002574 } else if (parm_count + data_count > pSMBr->ByteCount) {
2575 cFYI(1, ("parm count and data count larger than SMB"));
Steve French0a4b92c2006-01-12 15:44:21 -08002576 return -EINVAL;
2577 }
Steve French630f3f0c2007-10-25 21:17:17 +00002578 *pdatalen = data_count;
2579 *pparmlen = parm_count;
Steve French0a4b92c2006-01-12 15:44:21 -08002580 return 0;
2581}
Parag Warudkarc9489772007-10-23 18:09:48 +00002582#endif /* CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08002583
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584int
2585CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2586 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002587 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 const struct nls_table *nls_codepage)
2589{
2590 int rc = 0;
2591 int bytes_returned;
2592 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00002593 struct smb_com_transaction_ioctl_req *pSMB;
2594 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002595
2596 cFYI(1, ("In Windows reparse style QueryLink for path %s", searchName));
2597 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2598 (void **) &pSMBr);
2599 if (rc)
2600 return rc;
2601
2602 pSMB->TotalParameterCount = 0 ;
2603 pSMB->TotalDataCount = 0;
2604 pSMB->MaxParameterCount = cpu_to_le32(2);
2605 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002606 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2607 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608 pSMB->MaxSetupCount = 4;
2609 pSMB->Reserved = 0;
2610 pSMB->ParameterOffset = 0;
2611 pSMB->DataCount = 0;
2612 pSMB->DataOffset = 0;
2613 pSMB->SetupCount = 4;
2614 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2615 pSMB->ParameterCount = pSMB->TotalParameterCount;
2616 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2617 pSMB->IsFsctl = 1; /* FSCTL */
2618 pSMB->IsRootFlag = 0;
2619 pSMB->Fid = fid; /* file handle always le */
2620 pSMB->ByteCount = 0;
2621
2622 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2623 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2624 if (rc) {
2625 cFYI(1, ("Send error in QueryReparseLinkInfo = %d", rc));
2626 } else { /* decode response */
2627 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2628 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
2629 if ((pSMBr->ByteCount < 2) || (data_offset > 512))
2630 /* BB also check enough total bytes returned */
2631 rc = -EIO; /* bad smb */
2632 else {
Steve French790fe572007-07-07 19:25:05 +00002633 if (data_count && (data_count < 2048)) {
Steve French50c2f752007-07-13 00:33:32 +00002634 char *end_of_smb = 2 /* sizeof byte count */ +
Steve French0a4b92c2006-01-12 15:44:21 -08002635 pSMBr->ByteCount +
2636 (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637
Steve French50c2f752007-07-13 00:33:32 +00002638 struct reparse_data *reparse_buf =
2639 (struct reparse_data *)
2640 ((char *)&pSMBr->hdr.Protocol
2641 + data_offset);
Steve French790fe572007-07-07 19:25:05 +00002642 if ((char *)reparse_buf >= end_of_smb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643 rc = -EIO;
2644 goto qreparse_out;
2645 }
Steve French790fe572007-07-07 19:25:05 +00002646 if ((reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647 reparse_buf->TargetNameOffset +
2648 reparse_buf->TargetNameLen) >
2649 end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002650 cFYI(1, ("reparse buf beyond SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 rc = -EIO;
2652 goto qreparse_out;
2653 }
Steve French50c2f752007-07-13 00:33:32 +00002654
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2656 name_len = UniStrnlen((wchar_t *)
Steve French50c2f752007-07-13 00:33:32 +00002657 (reparse_buf->LinkNamesBuf +
2658 reparse_buf->TargetNameOffset),
2659 min(buflen/2,
2660 reparse_buf->TargetNameLen / 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002662 (__le16 *) (reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663 reparse_buf->TargetNameOffset),
2664 name_len, nls_codepage);
2665 } else { /* ASCII names */
Steve French50c2f752007-07-13 00:33:32 +00002666 strncpy(symlinkinfo,
2667 reparse_buf->LinkNamesBuf +
2668 reparse_buf->TargetNameOffset,
2669 min_t(const int, buflen,
2670 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671 }
2672 } else {
2673 rc = -EIO;
Steve French63135e02007-07-17 17:34:02 +00002674 cFYI(1, ("Invalid return data count on "
2675 "get reparse info ioctl"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676 }
2677 symlinkinfo[buflen] = 0; /* just in case so the caller
2678 does not go off the end of the buffer */
Steve French50c2f752007-07-13 00:33:32 +00002679 cFYI(1, ("readlink result - %s", symlinkinfo));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680 }
2681 }
2682qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002683 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684
2685 /* Note: On -EAGAIN error only caller can retry on handle based calls
2686 since file handle passed in no longer valid */
2687
2688 return rc;
2689}
2690
2691#ifdef CONFIG_CIFS_POSIX
2692
2693/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002694static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2695 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696{
2697 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002698 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2699 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2700 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701 /* cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id)); */
2702
2703 return;
2704}
2705
2706/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002707static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2708 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709{
2710 int size = 0;
2711 int i;
2712 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002713 struct cifs_posix_ace *pACE;
2714 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2715 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716
2717 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2718 return -EOPNOTSUPP;
2719
Steve French790fe572007-07-07 19:25:05 +00002720 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721 count = le16_to_cpu(cifs_acl->access_entry_count);
2722 pACE = &cifs_acl->ace_array[0];
2723 size = sizeof(struct cifs_posix_acl);
2724 size += sizeof(struct cifs_posix_ace) * count;
2725 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002726 if (size_of_data_area < size) {
Steve French50c2f752007-07-13 00:33:32 +00002727 cFYI(1, ("bad CIFS POSIX ACL size %d vs. %d",
2728 size_of_data_area, size));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729 return -EINVAL;
2730 }
Steve French790fe572007-07-07 19:25:05 +00002731 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732 count = le16_to_cpu(cifs_acl->access_entry_count);
2733 size = sizeof(struct cifs_posix_acl);
2734 size += sizeof(struct cifs_posix_ace) * count;
2735/* skip past access ACEs to get to default ACEs */
2736 pACE = &cifs_acl->ace_array[count];
2737 count = le16_to_cpu(cifs_acl->default_entry_count);
2738 size += sizeof(struct cifs_posix_ace) * count;
2739 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002740 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741 return -EINVAL;
2742 } else {
2743 /* illegal type */
2744 return -EINVAL;
2745 }
2746
2747 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002748 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002749 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002750 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751 return -ERANGE;
2752 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002753 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002754 for (i = 0; i < count ; i++) {
2755 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2756 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757 }
2758 }
2759 return size;
2760}
2761
Steve French50c2f752007-07-13 00:33:32 +00002762static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2763 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764{
2765 __u16 rc = 0; /* 0 = ACL converted ok */
2766
Steve Frenchff7feac2005-11-15 16:45:16 -08002767 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2768 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002770 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 /* Probably no need to le convert -1 on any arch but can not hurt */
2772 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002773 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002774 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Steve French50c2f752007-07-13 00:33:32 +00002775 /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776 return rc;
2777}
2778
2779/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002780static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2781 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782{
2783 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002784 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2785 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 int count;
2787 int i;
2788
Steve French790fe572007-07-07 19:25:05 +00002789 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 return 0;
2791
2792 count = posix_acl_xattr_count((size_t)buflen);
Steve Frenchc18c8422007-07-18 23:21:09 +00002793 cFYI(1, ("setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002794 "version of %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002795 count, buflen, le32_to_cpu(local_acl->a_version)));
Steve French790fe572007-07-07 19:25:05 +00002796 if (le32_to_cpu(local_acl->a_version) != 2) {
Steve French50c2f752007-07-13 00:33:32 +00002797 cFYI(1, ("unknown POSIX ACL version %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002798 le32_to_cpu(local_acl->a_version)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 return 0;
2800 }
2801 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002802 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002803 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002804 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002805 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806 else {
Steve French50c2f752007-07-13 00:33:32 +00002807 cFYI(1, ("unknown ACL type %d", acl_type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808 return 0;
2809 }
Steve French50c2f752007-07-13 00:33:32 +00002810 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2812 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002813 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 /* ACE not converted */
2815 break;
2816 }
2817 }
Steve French790fe572007-07-07 19:25:05 +00002818 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2820 rc += sizeof(struct cifs_posix_acl);
2821 /* BB add check to make sure ACL does not overflow SMB */
2822 }
2823 return rc;
2824}
2825
2826int
2827CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002828 const unsigned char *searchName,
2829 char *acl_inf, const int buflen, const int acl_type,
2830 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831{
2832/* SMB_QUERY_POSIX_ACL */
2833 TRANSACTION2_QPI_REQ *pSMB = NULL;
2834 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2835 int rc = 0;
2836 int bytes_returned;
2837 int name_len;
2838 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002839
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840 cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName));
2841
2842queryAclRetry:
2843 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2844 (void **) &pSMBr);
2845 if (rc)
2846 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002847
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2849 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002850 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002851 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852 name_len++; /* trailing null */
2853 name_len *= 2;
2854 pSMB->FileName[name_len] = 0;
2855 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002856 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857 name_len = strnlen(searchName, PATH_MAX);
2858 name_len++; /* trailing null */
2859 strncpy(pSMB->FileName, searchName, name_len);
2860 }
2861
2862 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2863 pSMB->TotalDataCount = 0;
2864 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002865 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 pSMB->MaxDataCount = cpu_to_le16(4000);
2867 pSMB->MaxSetupCount = 0;
2868 pSMB->Reserved = 0;
2869 pSMB->Flags = 0;
2870 pSMB->Timeout = 0;
2871 pSMB->Reserved2 = 0;
2872 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002873 offsetof(struct smb_com_transaction2_qpi_req,
2874 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875 pSMB->DataCount = 0;
2876 pSMB->DataOffset = 0;
2877 pSMB->SetupCount = 1;
2878 pSMB->Reserved3 = 0;
2879 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2880 byte_count = params + 1 /* pad */ ;
2881 pSMB->TotalParameterCount = cpu_to_le16(params);
2882 pSMB->ParameterCount = pSMB->TotalParameterCount;
2883 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2884 pSMB->Reserved4 = 0;
2885 pSMB->hdr.smb_buf_length += byte_count;
2886 pSMB->ByteCount = cpu_to_le16(byte_count);
2887
2888 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2889 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002890 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002891 if (rc) {
2892 cFYI(1, ("Send error in Query POSIX ACL = %d", rc));
2893 } else {
2894 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002895
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2897 if (rc || (pSMBr->ByteCount < 2))
2898 /* BB also check enough total bytes returned */
2899 rc = -EIO; /* bad smb */
2900 else {
2901 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2902 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2903 rc = cifs_copy_posix_acl(acl_inf,
2904 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002905 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002906 }
2907 }
2908 cifs_buf_release(pSMB);
2909 if (rc == -EAGAIN)
2910 goto queryAclRetry;
2911 return rc;
2912}
2913
2914int
2915CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002916 const unsigned char *fileName,
2917 const char *local_acl, const int buflen,
2918 const int acl_type,
2919 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002920{
2921 struct smb_com_transaction2_spi_req *pSMB = NULL;
2922 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2923 char *parm_data;
2924 int name_len;
2925 int rc = 0;
2926 int bytes_returned = 0;
2927 __u16 params, byte_count, data_count, param_offset, offset;
2928
2929 cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName));
2930setAclRetry:
2931 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002932 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002933 if (rc)
2934 return rc;
2935 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2936 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002937 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002938 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002939 name_len++; /* trailing null */
2940 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002941 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942 name_len = strnlen(fileName, PATH_MAX);
2943 name_len++; /* trailing null */
2944 strncpy(pSMB->FileName, fileName, name_len);
2945 }
2946 params = 6 + name_len;
2947 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00002948 /* BB find max SMB size from sess */
2949 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002950 pSMB->MaxSetupCount = 0;
2951 pSMB->Reserved = 0;
2952 pSMB->Flags = 0;
2953 pSMB->Timeout = 0;
2954 pSMB->Reserved2 = 0;
2955 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002956 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957 offset = param_offset + params;
2958 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2959 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2960
2961 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002962 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002963
Steve French790fe572007-07-07 19:25:05 +00002964 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002965 rc = -EOPNOTSUPP;
2966 goto setACLerrorExit;
2967 }
2968 pSMB->DataOffset = cpu_to_le16(offset);
2969 pSMB->SetupCount = 1;
2970 pSMB->Reserved3 = 0;
2971 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2972 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2973 byte_count = 3 /* pad */ + params + data_count;
2974 pSMB->DataCount = cpu_to_le16(data_count);
2975 pSMB->TotalDataCount = pSMB->DataCount;
2976 pSMB->ParameterCount = cpu_to_le16(params);
2977 pSMB->TotalParameterCount = pSMB->ParameterCount;
2978 pSMB->Reserved4 = 0;
2979 pSMB->hdr.smb_buf_length += byte_count;
2980 pSMB->ByteCount = cpu_to_le16(byte_count);
2981 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002982 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00002983 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002984 cFYI(1, ("Set POSIX ACL returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985
2986setACLerrorExit:
2987 cifs_buf_release(pSMB);
2988 if (rc == -EAGAIN)
2989 goto setAclRetry;
2990 return rc;
2991}
2992
Steve Frenchf654bac2005-04-28 22:41:04 -07002993/* BB fix tabs in this function FIXME BB */
2994int
2995CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00002996 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07002997{
Steve French50c2f752007-07-13 00:33:32 +00002998 int rc = 0;
2999 struct smb_t2_qfi_req *pSMB = NULL;
3000 struct smb_t2_qfi_rsp *pSMBr = NULL;
3001 int bytes_returned;
3002 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07003003
Steve French790fe572007-07-07 19:25:05 +00003004 cFYI(1, ("In GetExtAttr"));
3005 if (tcon == NULL)
3006 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07003007
3008GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00003009 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3010 (void **) &pSMBr);
3011 if (rc)
3012 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003013
Steve Frenchad7a2922008-02-07 23:25:02 +00003014 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00003015 pSMB->t2.TotalDataCount = 0;
3016 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3017 /* BB find exact max data count below from sess structure BB */
3018 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
3019 pSMB->t2.MaxSetupCount = 0;
3020 pSMB->t2.Reserved = 0;
3021 pSMB->t2.Flags = 0;
3022 pSMB->t2.Timeout = 0;
3023 pSMB->t2.Reserved2 = 0;
3024 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3025 Fid) - 4);
3026 pSMB->t2.DataCount = 0;
3027 pSMB->t2.DataOffset = 0;
3028 pSMB->t2.SetupCount = 1;
3029 pSMB->t2.Reserved3 = 0;
3030 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3031 byte_count = params + 1 /* pad */ ;
3032 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3033 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3034 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
3035 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07003036 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00003037 pSMB->hdr.smb_buf_length += byte_count;
3038 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07003039
Steve French790fe572007-07-07 19:25:05 +00003040 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3041 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3042 if (rc) {
3043 cFYI(1, ("error %d in GetExtAttr", rc));
3044 } else {
3045 /* decode response */
3046 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3047 if (rc || (pSMBr->ByteCount < 2))
3048 /* BB also check enough total bytes returned */
3049 /* If rc should we check for EOPNOSUPP and
3050 disable the srvino flag? or in caller? */
3051 rc = -EIO; /* bad smb */
3052 else {
3053 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3054 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
3055 struct file_chattr_info *pfinfo;
3056 /* BB Do we need a cast or hash here ? */
3057 if (count != 16) {
3058 cFYI(1, ("Illegal size ret in GetExtAttr"));
3059 rc = -EIO;
3060 goto GetExtAttrOut;
3061 }
3062 pfinfo = (struct file_chattr_info *)
3063 (data_offset + (char *) &pSMBr->hdr.Protocol);
3064 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07003065 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00003066 }
3067 }
Steve Frenchf654bac2005-04-28 22:41:04 -07003068GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00003069 cifs_buf_release(pSMB);
3070 if (rc == -EAGAIN)
3071 goto GetExtAttrRetry;
3072 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003073}
3074
Steve Frenchf654bac2005-04-28 22:41:04 -07003075#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003076
Steve French297647c2007-10-12 04:11:59 +00003077#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08003078/* Get Security Descriptor (by handle) from remote server for a file or dir */
3079int
3080CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f0c2007-10-25 21:17:17 +00003081 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003082{
3083 int rc = 0;
3084 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003085 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003086 struct kvec iov[1];
3087
3088 cFYI(1, ("GetCifsACL"));
3089
Steve French630f3f0c2007-10-25 21:17:17 +00003090 *pbuflen = 0;
3091 *acl_inf = NULL;
3092
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003093 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003094 8 /* parm len */, tcon, (void **) &pSMB);
3095 if (rc)
3096 return rc;
3097
3098 pSMB->MaxParameterCount = cpu_to_le32(4);
3099 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3100 pSMB->MaxSetupCount = 0;
3101 pSMB->Fid = fid; /* file handle always le */
3102 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3103 CIFS_ACL_DACL);
3104 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3105 pSMB->hdr.smb_buf_length += 11;
3106 iov[0].iov_base = (char *)pSMB;
3107 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3108
Steve Frencha761ac52007-10-18 21:45:27 +00003109 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Steve French133672e2007-11-13 22:41:37 +00003110 CIFS_STD_OP);
Steve French0a4b92c2006-01-12 15:44:21 -08003111 cifs_stats_inc(&tcon->num_acl_get);
3112 if (rc) {
3113 cFYI(1, ("Send error in QuerySecDesc = %d", rc));
3114 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003115 __le32 *parm;
Steve French630f3f0c2007-10-25 21:17:17 +00003116 __u32 parm_len;
3117 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003118 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f0c2007-10-25 21:17:17 +00003119 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003120
3121/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003122 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f0c2007-10-25 21:17:17 +00003123 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003124 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003125 goto qsec_out;
3126 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3127
Steve French630f3f0c2007-10-25 21:17:17 +00003128 cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, *acl_inf));
Steve French0a4b92c2006-01-12 15:44:21 -08003129
3130 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3131 rc = -EIO; /* bad smb */
Steve French630f3f0c2007-10-25 21:17:17 +00003132 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003133 goto qsec_out;
3134 }
3135
3136/* BB check that data area is minimum length and as big as acl_len */
3137
Steve Frenchaf6f4612007-10-16 18:40:37 +00003138 acl_len = le32_to_cpu(*parm);
Steve French630f3f0c2007-10-25 21:17:17 +00003139 if (acl_len != *pbuflen) {
3140 cERROR(1, ("acl length %d does not match %d",
3141 acl_len, *pbuflen));
3142 if (*pbuflen > acl_len)
3143 *pbuflen = acl_len;
3144 }
Steve French0a4b92c2006-01-12 15:44:21 -08003145
Steve French630f3f0c2007-10-25 21:17:17 +00003146 /* check if buffer is big enough for the acl
3147 header followed by the smallest SID */
3148 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3149 (*pbuflen >= 64 * 1024)) {
3150 cERROR(1, ("bad acl length %d", *pbuflen));
3151 rc = -EINVAL;
3152 *pbuflen = 0;
3153 } else {
3154 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3155 if (*acl_inf == NULL) {
3156 *pbuflen = 0;
3157 rc = -ENOMEM;
3158 }
3159 memcpy(*acl_inf, pdata, *pbuflen);
3160 }
Steve French0a4b92c2006-01-12 15:44:21 -08003161 }
3162qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003163 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003164 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003165 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003166 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003167/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003168 return rc;
3169}
Steve French97837582007-12-31 07:47:21 +00003170
3171int
3172CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3173 struct cifs_ntsd *pntsd, __u32 acllen)
3174{
3175 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3176 int rc = 0;
3177 int bytes_returned = 0;
3178 SET_SEC_DESC_REQ *pSMB = NULL;
3179 NTRANSACT_RSP *pSMBr = NULL;
3180
3181setCifsAclRetry:
3182 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3183 (void **) &pSMBr);
3184 if (rc)
3185 return (rc);
3186
3187 pSMB->MaxSetupCount = 0;
3188 pSMB->Reserved = 0;
3189
3190 param_count = 8;
3191 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3192 data_count = acllen;
3193 data_offset = param_offset + param_count;
3194 byte_count = 3 /* pad */ + param_count;
3195
3196 pSMB->DataCount = cpu_to_le32(data_count);
3197 pSMB->TotalDataCount = pSMB->DataCount;
3198 pSMB->MaxParameterCount = cpu_to_le32(4);
3199 pSMB->MaxDataCount = cpu_to_le32(16384);
3200 pSMB->ParameterCount = cpu_to_le32(param_count);
3201 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3202 pSMB->TotalParameterCount = pSMB->ParameterCount;
3203 pSMB->DataOffset = cpu_to_le32(data_offset);
3204 pSMB->SetupCount = 0;
3205 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3206 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3207
3208 pSMB->Fid = fid; /* file handle always le */
3209 pSMB->Reserved2 = 0;
3210 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3211
3212 if (pntsd && acllen) {
3213 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3214 (char *) pntsd,
3215 acllen);
3216 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3217
3218 } else
3219 pSMB->hdr.smb_buf_length += byte_count;
3220
3221 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3222 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3223
3224 cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc));
3225 if (rc)
3226 cFYI(1, ("Set CIFS ACL returned %d", rc));
3227 cifs_buf_release(pSMB);
3228
3229 if (rc == -EAGAIN)
3230 goto setCifsAclRetry;
3231
3232 return (rc);
3233}
3234
Steve French297647c2007-10-12 04:11:59 +00003235#endif /* CONFIG_CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08003236
Steve French6b8edfe2005-08-23 20:26:03 -07003237/* Legacy Query Path Information call for lookup to old servers such
3238 as Win9x/WinME */
3239int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003240 const unsigned char *searchName,
3241 FILE_ALL_INFO *pFinfo,
3242 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003243{
Steve Frenchad7a2922008-02-07 23:25:02 +00003244 QUERY_INFORMATION_REQ *pSMB;
3245 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003246 int rc = 0;
3247 int bytes_returned;
3248 int name_len;
3249
Steve French50c2f752007-07-13 00:33:32 +00003250 cFYI(1, ("In SMBQPath path %s", searchName));
Steve French6b8edfe2005-08-23 20:26:03 -07003251QInfRetry:
3252 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003253 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003254 if (rc)
3255 return rc;
3256
3257 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3258 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003259 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3260 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003261 name_len++; /* trailing null */
3262 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003263 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003264 name_len = strnlen(searchName, PATH_MAX);
3265 name_len++; /* trailing null */
3266 strncpy(pSMB->FileName, searchName, name_len);
3267 }
3268 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003269 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003270 pSMB->hdr.smb_buf_length += (__u16) name_len;
3271 pSMB->ByteCount = cpu_to_le16(name_len);
3272
3273 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003274 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003275 if (rc) {
3276 cFYI(1, ("Send error in QueryInfo = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003277 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003278 struct timespec ts;
3279 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003280
3281 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003282 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003283 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003284 ts.tv_nsec = 0;
3285 ts.tv_sec = time;
3286 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003287 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003288 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3289 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003290 pFinfo->AllocationSize =
3291 cpu_to_le64(le32_to_cpu(pSMBr->size));
3292 pFinfo->EndOfFile = pFinfo->AllocationSize;
3293 pFinfo->Attributes =
3294 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003295 } else
3296 rc = -EIO; /* bad buffer passed in */
3297
3298 cifs_buf_release(pSMB);
3299
3300 if (rc == -EAGAIN)
3301 goto QInfRetry;
3302
3303 return rc;
3304}
3305
3306
3307
3308
Linus Torvalds1da177e2005-04-16 15:20:36 -07003309int
3310CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3311 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003312 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003313 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003314 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003315{
3316/* level 263 SMB_QUERY_FILE_ALL_INFO */
3317 TRANSACTION2_QPI_REQ *pSMB = NULL;
3318 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3319 int rc = 0;
3320 int bytes_returned;
3321 int name_len;
3322 __u16 params, byte_count;
3323
3324/* cFYI(1, ("In QPathInfo path %s", searchName)); */
3325QPathInfoRetry:
3326 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3327 (void **) &pSMBr);
3328 if (rc)
3329 return rc;
3330
3331 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3332 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003333 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003334 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003335 name_len++; /* trailing null */
3336 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003337 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003338 name_len = strnlen(searchName, PATH_MAX);
3339 name_len++; /* trailing null */
3340 strncpy(pSMB->FileName, searchName, name_len);
3341 }
3342
Steve French50c2f752007-07-13 00:33:32 +00003343 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003344 pSMB->TotalDataCount = 0;
3345 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00003346 /* BB find exact max SMB PDU from sess structure BB */
3347 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003348 pSMB->MaxSetupCount = 0;
3349 pSMB->Reserved = 0;
3350 pSMB->Flags = 0;
3351 pSMB->Timeout = 0;
3352 pSMB->Reserved2 = 0;
3353 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003354 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003355 pSMB->DataCount = 0;
3356 pSMB->DataOffset = 0;
3357 pSMB->SetupCount = 1;
3358 pSMB->Reserved3 = 0;
3359 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3360 byte_count = params + 1 /* pad */ ;
3361 pSMB->TotalParameterCount = cpu_to_le16(params);
3362 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003363 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003364 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3365 else
3366 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003367 pSMB->Reserved4 = 0;
3368 pSMB->hdr.smb_buf_length += byte_count;
3369 pSMB->ByteCount = cpu_to_le16(byte_count);
3370
3371 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3372 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3373 if (rc) {
3374 cFYI(1, ("Send error in QPathInfo = %d", rc));
3375 } else { /* decode response */
3376 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3377
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003378 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3379 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003380 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003381 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003382 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003383 rc = -EIO; /* 24 or 26 expected but we do not read
3384 last field */
3385 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003386 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003387 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003388
3389 /* On legacy responses we do not read the last field,
3390 EAsize, fortunately since it varies by subdialect and
3391 also note it differs on Set vs. Get, ie two bytes or 4
3392 bytes depending but we don't care here */
3393 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003394 size = sizeof(FILE_INFO_STANDARD);
3395 else
3396 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003397 memcpy((char *) pFindData,
3398 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003399 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 } else
3401 rc = -ENOMEM;
3402 }
3403 cifs_buf_release(pSMB);
3404 if (rc == -EAGAIN)
3405 goto QPathInfoRetry;
3406
3407 return rc;
3408}
3409
3410int
3411CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3412 const unsigned char *searchName,
Steve French582d21e2008-05-13 04:54:12 +00003413 FILE_UNIX_BASIC_INFO *pFindData,
Steve French737b7582005-04-28 22:41:06 -07003414 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003415{
3416/* SMB_QUERY_FILE_UNIX_BASIC */
3417 TRANSACTION2_QPI_REQ *pSMB = NULL;
3418 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3419 int rc = 0;
3420 int bytes_returned = 0;
3421 int name_len;
3422 __u16 params, byte_count;
3423
3424 cFYI(1, ("In QPathInfo (Unix) the path %s", searchName));
3425UnixQPathInfoRetry:
3426 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3427 (void **) &pSMBr);
3428 if (rc)
3429 return rc;
3430
3431 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3432 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003433 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003434 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003435 name_len++; /* trailing null */
3436 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003437 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003438 name_len = strnlen(searchName, PATH_MAX);
3439 name_len++; /* trailing null */
3440 strncpy(pSMB->FileName, searchName, name_len);
3441 }
3442
Steve French50c2f752007-07-13 00:33:32 +00003443 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003444 pSMB->TotalDataCount = 0;
3445 pSMB->MaxParameterCount = cpu_to_le16(2);
3446 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003447 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003448 pSMB->MaxSetupCount = 0;
3449 pSMB->Reserved = 0;
3450 pSMB->Flags = 0;
3451 pSMB->Timeout = 0;
3452 pSMB->Reserved2 = 0;
3453 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003454 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003455 pSMB->DataCount = 0;
3456 pSMB->DataOffset = 0;
3457 pSMB->SetupCount = 1;
3458 pSMB->Reserved3 = 0;
3459 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3460 byte_count = params + 1 /* pad */ ;
3461 pSMB->TotalParameterCount = cpu_to_le16(params);
3462 pSMB->ParameterCount = pSMB->TotalParameterCount;
3463 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3464 pSMB->Reserved4 = 0;
3465 pSMB->hdr.smb_buf_length += byte_count;
3466 pSMB->ByteCount = cpu_to_le16(byte_count);
3467
3468 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3469 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3470 if (rc) {
3471 cFYI(1, ("Send error in QPathInfo = %d", rc));
3472 } else { /* decode response */
3473 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3474
3475 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve French1e71f252007-09-20 15:30:07 +00003476 cERROR(1, ("Malformed FILE_UNIX_BASIC_INFO response.\n"
3477 "Unix Extensions can be disabled on mount "
3478 "by specifying the nosfu mount option."));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003479 rc = -EIO; /* bad smb */
3480 } else {
3481 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3482 memcpy((char *) pFindData,
3483 (char *) &pSMBr->hdr.Protocol +
3484 data_offset,
Steve French630f3f0c2007-10-25 21:17:17 +00003485 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003486 }
3487 }
3488 cifs_buf_release(pSMB);
3489 if (rc == -EAGAIN)
3490 goto UnixQPathInfoRetry;
3491
3492 return rc;
3493}
3494
Linus Torvalds1da177e2005-04-16 15:20:36 -07003495/* xid, tcon, searchName and codepage are input parms, rest are returned */
3496int
3497CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003498 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003499 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003500 __u16 *pnetfid,
3501 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003502{
3503/* level 257 SMB_ */
3504 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3505 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003506 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003507 int rc = 0;
3508 int bytes_returned = 0;
3509 int name_len;
3510 __u16 params, byte_count;
3511
Steve French50c2f752007-07-13 00:33:32 +00003512 cFYI(1, ("In FindFirst for %s", searchName));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003513
3514findFirstRetry:
3515 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3516 (void **) &pSMBr);
3517 if (rc)
3518 return rc;
3519
3520 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3521 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003522 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003523 PATH_MAX, nls_codepage, remap);
3524 /* We can not add the asterik earlier in case
3525 it got remapped to 0xF03A as if it were part of the
3526 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003527 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003528 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003529 pSMB->FileName[name_len+1] = 0;
3530 pSMB->FileName[name_len+2] = '*';
3531 pSMB->FileName[name_len+3] = 0;
3532 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003533 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3534 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003535 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003536 } else { /* BB add check for overrun of SMB buf BB */
3537 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003538/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003539 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003540 free buffer exit; BB */
3541 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003542 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003543 pSMB->FileName[name_len+1] = '*';
3544 pSMB->FileName[name_len+2] = 0;
3545 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003546 }
3547
3548 params = 12 + name_len /* includes null */ ;
3549 pSMB->TotalDataCount = 0; /* no EAs */
3550 pSMB->MaxParameterCount = cpu_to_le16(10);
3551 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3552 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3553 pSMB->MaxSetupCount = 0;
3554 pSMB->Reserved = 0;
3555 pSMB->Flags = 0;
3556 pSMB->Timeout = 0;
3557 pSMB->Reserved2 = 0;
3558 byte_count = params + 1 /* pad */ ;
3559 pSMB->TotalParameterCount = cpu_to_le16(params);
3560 pSMB->ParameterCount = pSMB->TotalParameterCount;
3561 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003562 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3563 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003564 pSMB->DataCount = 0;
3565 pSMB->DataOffset = 0;
3566 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3567 pSMB->Reserved3 = 0;
3568 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3569 pSMB->SearchAttributes =
3570 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3571 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003572 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3573 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003574 CIFS_SEARCH_RETURN_RESUME);
3575 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3576
3577 /* BB what should we set StorageType to? Does it matter? BB */
3578 pSMB->SearchStorageType = 0;
3579 pSMB->hdr.smb_buf_length += byte_count;
3580 pSMB->ByteCount = cpu_to_le16(byte_count);
3581
3582 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3583 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003584 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003585
Steve French88274812006-03-09 22:21:45 +00003586 if (rc) {/* BB add logic to retry regular search if Unix search
3587 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003588 /* BB Add code to handle unsupported level rc */
3589 cFYI(1, ("Error in FindFirst = %d", rc));
Steve French1982c342005-08-17 12:38:22 -07003590
Steve French88274812006-03-09 22:21:45 +00003591 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003592
3593 /* BB eventually could optimize out free and realloc of buf */
3594 /* for this case */
3595 if (rc == -EAGAIN)
3596 goto findFirstRetry;
3597 } else { /* decode response */
3598 /* BB remember to free buffer if error BB */
3599 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003600 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003601 unsigned int lnoff;
3602
Linus Torvalds1da177e2005-04-16 15:20:36 -07003603 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003604 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003605 else
Steve French4b18f2a2008-04-29 00:06:05 +00003606 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003607
3608 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003609 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003610 psrch_inf->srch_entries_start =
3611 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003612 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003613 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3614 le16_to_cpu(pSMBr->t2.ParameterOffset));
3615
Steve French790fe572007-07-07 19:25:05 +00003616 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003617 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003618 else
Steve French4b18f2a2008-04-29 00:06:05 +00003619 psrch_inf->endOfSearch = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003620
Steve French50c2f752007-07-13 00:33:32 +00003621 psrch_inf->entries_in_buffer =
3622 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003623 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003624 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003625 lnoff = le16_to_cpu(parms->LastNameOffset);
3626 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3627 lnoff) {
3628 cERROR(1, ("ignoring corrupt resume name"));
3629 psrch_inf->last_entry = NULL;
3630 return rc;
3631 }
3632
Steve French0752f152008-10-07 20:03:33 +00003633 psrch_inf->last_entry = psrch_inf->srch_entries_start +
Steve Frenchb77d7532008-10-08 19:13:46 +00003634 lnoff;
3635
Linus Torvalds1da177e2005-04-16 15:20:36 -07003636 *pnetfid = parms->SearchHandle;
3637 } else {
3638 cifs_buf_release(pSMB);
3639 }
3640 }
3641
3642 return rc;
3643}
3644
3645int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003646 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003647{
3648 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3649 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003650 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003651 char *response_data;
3652 int rc = 0;
3653 int bytes_returned, name_len;
3654 __u16 params, byte_count;
3655
3656 cFYI(1, ("In FindNext"));
3657
Steve French4b18f2a2008-04-29 00:06:05 +00003658 if (psrch_inf->endOfSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003659 return -ENOENT;
3660
3661 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3662 (void **) &pSMBr);
3663 if (rc)
3664 return rc;
3665
Steve French50c2f752007-07-13 00:33:32 +00003666 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003667 byte_count = 0;
3668 pSMB->TotalDataCount = 0; /* no EAs */
3669 pSMB->MaxParameterCount = cpu_to_le16(8);
3670 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003671 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3672 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003673 pSMB->MaxSetupCount = 0;
3674 pSMB->Reserved = 0;
3675 pSMB->Flags = 0;
3676 pSMB->Timeout = 0;
3677 pSMB->Reserved2 = 0;
3678 pSMB->ParameterOffset = cpu_to_le16(
3679 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3680 pSMB->DataCount = 0;
3681 pSMB->DataOffset = 0;
3682 pSMB->SetupCount = 1;
3683 pSMB->Reserved3 = 0;
3684 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3685 pSMB->SearchHandle = searchHandle; /* always kept as le */
3686 pSMB->SearchCount =
Steve French630f3f0c2007-10-25 21:17:17 +00003687 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003688 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3689 pSMB->ResumeKey = psrch_inf->resume_key;
3690 pSMB->SearchFlags =
3691 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3692
3693 name_len = psrch_inf->resume_name_len;
3694 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003695 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003696 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3697 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003698 /* 14 byte parm len above enough for 2 byte null terminator */
3699 pSMB->ResumeFileName[name_len] = 0;
3700 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003701 } else {
3702 rc = -EINVAL;
3703 goto FNext2_err_exit;
3704 }
3705 byte_count = params + 1 /* pad */ ;
3706 pSMB->TotalParameterCount = cpu_to_le16(params);
3707 pSMB->ParameterCount = pSMB->TotalParameterCount;
3708 pSMB->hdr.smb_buf_length += byte_count;
3709 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003710
Linus Torvalds1da177e2005-04-16 15:20:36 -07003711 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3712 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003713 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003714 if (rc) {
3715 if (rc == -EBADF) {
Steve French4b18f2a2008-04-29 00:06:05 +00003716 psrch_inf->endOfSearch = true;
Jeff Layton63534502008-05-12 19:56:05 -07003717 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00003718 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719 } else
3720 cFYI(1, ("FindNext returned = %d", rc));
3721 } else { /* decode response */
3722 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003723
Steve French790fe572007-07-07 19:25:05 +00003724 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003725 unsigned int lnoff;
3726
Linus Torvalds1da177e2005-04-16 15:20:36 -07003727 /* BB fixme add lock for file (srch_info) struct here */
3728 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003729 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003730 else
Steve French4b18f2a2008-04-29 00:06:05 +00003731 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003732 response_data = (char *) &pSMBr->hdr.Protocol +
3733 le16_to_cpu(pSMBr->t2.ParameterOffset);
3734 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3735 response_data = (char *)&pSMBr->hdr.Protocol +
3736 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003737 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003738 cifs_small_buf_release(
3739 psrch_inf->ntwrk_buf_start);
3740 else
3741 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003742 psrch_inf->srch_entries_start = response_data;
3743 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003744 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003745 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003746 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003747 else
Steve French4b18f2a2008-04-29 00:06:05 +00003748 psrch_inf->endOfSearch = false;
Steve French50c2f752007-07-13 00:33:32 +00003749 psrch_inf->entries_in_buffer =
3750 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003751 psrch_inf->index_of_last_entry +=
3752 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003753 lnoff = le16_to_cpu(parms->LastNameOffset);
3754 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3755 lnoff) {
3756 cERROR(1, ("ignoring corrupt resume name"));
3757 psrch_inf->last_entry = NULL;
3758 return rc;
3759 } else
3760 psrch_inf->last_entry =
3761 psrch_inf->srch_entries_start + lnoff;
3762
Steve French50c2f752007-07-13 00:33:32 +00003763/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",
3764 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003765
3766 /* BB fixme add unlock here */
3767 }
3768
3769 }
3770
3771 /* BB On error, should we leave previous search buf (and count and
3772 last entry fields) intact or free the previous one? */
3773
3774 /* Note: On -EAGAIN error only caller can retry on handle based calls
3775 since file handle passed in no longer valid */
3776FNext2_err_exit:
3777 if (rc != 0)
3778 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003779 return rc;
3780}
3781
3782int
Steve French50c2f752007-07-13 00:33:32 +00003783CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3784 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003785{
3786 int rc = 0;
3787 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003788
3789 cFYI(1, ("In CIFSSMBFindClose"));
3790 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3791
3792 /* no sense returning error if session restarted
3793 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003794 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003795 return 0;
3796 if (rc)
3797 return rc;
3798
Linus Torvalds1da177e2005-04-16 15:20:36 -07003799 pSMB->FileID = searchHandle;
3800 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003801 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003802 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003803 cERROR(1, ("Send error in FindClose = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003804
Steve Frencha4544342005-08-24 13:59:35 -07003805 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003806
3807 /* Since session is dead, search handle closed on server already */
3808 if (rc == -EAGAIN)
3809 rc = 0;
3810
3811 return rc;
3812}
3813
Linus Torvalds1da177e2005-04-16 15:20:36 -07003814int
3815CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003816 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003817 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003818 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003819{
3820 int rc = 0;
3821 TRANSACTION2_QPI_REQ *pSMB = NULL;
3822 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3823 int name_len, bytes_returned;
3824 __u16 params, byte_count;
3825
Steve French50c2f752007-07-13 00:33:32 +00003826 cFYI(1, ("In GetSrvInodeNum for %s", searchName));
Steve French790fe572007-07-07 19:25:05 +00003827 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003828 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003829
3830GetInodeNumberRetry:
3831 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003832 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003833 if (rc)
3834 return rc;
3835
Linus Torvalds1da177e2005-04-16 15:20:36 -07003836 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3837 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003838 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003839 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003840 name_len++; /* trailing null */
3841 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003842 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003843 name_len = strnlen(searchName, PATH_MAX);
3844 name_len++; /* trailing null */
3845 strncpy(pSMB->FileName, searchName, name_len);
3846 }
3847
3848 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
3849 pSMB->TotalDataCount = 0;
3850 pSMB->MaxParameterCount = cpu_to_le16(2);
3851 /* BB find exact max data count below from sess structure BB */
3852 pSMB->MaxDataCount = cpu_to_le16(4000);
3853 pSMB->MaxSetupCount = 0;
3854 pSMB->Reserved = 0;
3855 pSMB->Flags = 0;
3856 pSMB->Timeout = 0;
3857 pSMB->Reserved2 = 0;
3858 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003859 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003860 pSMB->DataCount = 0;
3861 pSMB->DataOffset = 0;
3862 pSMB->SetupCount = 1;
3863 pSMB->Reserved3 = 0;
3864 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3865 byte_count = params + 1 /* pad */ ;
3866 pSMB->TotalParameterCount = cpu_to_le16(params);
3867 pSMB->ParameterCount = pSMB->TotalParameterCount;
3868 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
3869 pSMB->Reserved4 = 0;
3870 pSMB->hdr.smb_buf_length += byte_count;
3871 pSMB->ByteCount = cpu_to_le16(byte_count);
3872
3873 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3874 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3875 if (rc) {
3876 cFYI(1, ("error %d in QueryInternalInfo", rc));
3877 } else {
3878 /* decode response */
3879 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3880 if (rc || (pSMBr->ByteCount < 2))
3881 /* BB also check enough total bytes returned */
3882 /* If rc should we check for EOPNOSUPP and
3883 disable the srvino flag? or in caller? */
3884 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00003885 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003886 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3887 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00003888 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003889 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00003890 if (count < 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003891 cFYI(1, ("Illegal size ret in QryIntrnlInf"));
3892 rc = -EIO;
3893 goto GetInodeNumOut;
3894 }
3895 pfinfo = (struct file_internal_info *)
3896 (data_offset + (char *) &pSMBr->hdr.Protocol);
3897 *inode_number = pfinfo->UniqueId;
3898 }
3899 }
3900GetInodeNumOut:
3901 cifs_buf_release(pSMB);
3902 if (rc == -EAGAIN)
3903 goto GetInodeNumberRetry;
3904 return rc;
3905}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003906
Igor Mammedov2c556082008-10-23 13:58:42 +04003907/* computes length of UCS string converted to host codepage
3908 * @src: UCS string
3909 * @maxlen: length of the input string in UCS characters
3910 * (not in bytes)
3911 *
3912 * return: size of input string in host codepage
3913 */
3914static int hostlen_fromUCS(const __le16 *src, const int maxlen,
3915 const struct nls_table *nls_codepage) {
3916 int i;
3917 int hostlen = 0;
3918 char to[4];
3919 int charlen;
3920 for (i = 0; (i < maxlen) && src[i]; ++i) {
3921 charlen = nls_codepage->uni2char(le16_to_cpu(src[i]),
3922 to, NLS_MAX_CHARSET_SIZE);
3923 hostlen += charlen > 0 ? charlen : 1;
3924 }
3925 return hostlen;
3926}
3927
Igor Mammedovfec45852008-05-16 13:06:30 +04003928/* parses DFS refferal V3 structure
3929 * caller is responsible for freeing target_nodes
3930 * returns:
3931 * on success - 0
3932 * on failure - errno
3933 */
3934static int
Steve Frencha1fe78f2008-05-16 18:48:38 +00003935parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
Igor Mammedovfec45852008-05-16 13:06:30 +04003936 unsigned int *num_of_nodes,
3937 struct dfs_info3_param **target_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04003938 const struct nls_table *nls_codepage, int remap,
3939 const char *searchName)
Igor Mammedovfec45852008-05-16 13:06:30 +04003940{
3941 int i, rc = 0;
3942 char *data_end;
3943 bool is_unicode;
3944 struct dfs_referral_level_3 *ref;
3945
Harvey Harrison5ca33c62008-07-23 17:45:58 -07003946 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
3947 is_unicode = true;
3948 else
3949 is_unicode = false;
Igor Mammedovfec45852008-05-16 13:06:30 +04003950 *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
3951
3952 if (*num_of_nodes < 1) {
3953 cERROR(1, ("num_referrals: must be at least > 0,"
3954 "but we get num_referrals = %d\n", *num_of_nodes));
3955 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00003956 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003957 }
3958
3959 ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
Al Viro1d92cfd2008-06-02 10:59:02 +01003960 if (ref->VersionNumber != cpu_to_le16(3)) {
Igor Mammedovfec45852008-05-16 13:06:30 +04003961 cERROR(1, ("Referrals of V%d version are not supported,"
Al Viro1d92cfd2008-06-02 10:59:02 +01003962 "should be V3", le16_to_cpu(ref->VersionNumber)));
Igor Mammedovfec45852008-05-16 13:06:30 +04003963 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00003964 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003965 }
3966
3967 /* get the upper boundary of the resp buffer */
3968 data_end = (char *)(&(pSMBr->PathConsumed)) +
3969 le16_to_cpu(pSMBr->t2.DataCount);
3970
3971 cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n",
3972 *num_of_nodes,
3973 le16_to_cpu(pSMBr->DFSFlags)));
3974
3975 *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
3976 *num_of_nodes, GFP_KERNEL);
3977 if (*target_nodes == NULL) {
3978 cERROR(1, ("Failed to allocate buffer for target_nodes\n"));
3979 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00003980 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003981 }
3982
3983 /* collect neccessary data from referrals */
3984 for (i = 0; i < *num_of_nodes; i++) {
3985 char *temp;
3986 int max_len;
3987 struct dfs_info3_param *node = (*target_nodes)+i;
3988
3989 node->flags = le16_to_cpu(pSMBr->DFSFlags);
Igor Mammedov2c556082008-10-23 13:58:42 +04003990 if (is_unicode) {
Jeff Layton331c3132008-12-17 06:31:53 -05003991 __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
3992 GFP_KERNEL);
Igor Mammedov2c556082008-10-23 13:58:42 +04003993 cifsConvertToUCS((__le16 *) tmp, searchName,
3994 PATH_MAX, nls_codepage, remap);
3995 node->path_consumed = hostlen_fromUCS(tmp,
3996 le16_to_cpu(pSMBr->PathConsumed)/2,
3997 nls_codepage);
3998 kfree(tmp);
3999 } else
4000 node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
4001
Igor Mammedovfec45852008-05-16 13:06:30 +04004002 node->server_type = le16_to_cpu(ref->ServerType);
4003 node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
4004
4005 /* copy DfsPath */
4006 temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
4007 max_len = data_end - temp;
4008 rc = cifs_strncpy_to_host(&(node->path_name), temp,
4009 max_len, is_unicode, nls_codepage);
4010 if (rc)
Steve Frencha1fe78f2008-05-16 18:48:38 +00004011 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004012
4013 /* copy link target UNC */
4014 temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
4015 max_len = data_end - temp;
4016 rc = cifs_strncpy_to_host(&(node->node_name), temp,
4017 max_len, is_unicode, nls_codepage);
4018 if (rc)
Steve Frencha1fe78f2008-05-16 18:48:38 +00004019 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004020
Al Viro1d92cfd2008-06-02 10:59:02 +01004021 ref += le16_to_cpu(ref->Size);
Igor Mammedovfec45852008-05-16 13:06:30 +04004022 }
4023
Steve Frencha1fe78f2008-05-16 18:48:38 +00004024parse_DFS_referrals_exit:
Igor Mammedovfec45852008-05-16 13:06:30 +04004025 if (rc) {
4026 free_dfs_info_array(*target_nodes, *num_of_nodes);
4027 *target_nodes = NULL;
4028 *num_of_nodes = 0;
4029 }
4030 return rc;
4031}
4032
Linus Torvalds1da177e2005-04-16 15:20:36 -07004033int
4034CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
4035 const unsigned char *searchName,
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004036 struct dfs_info3_param **target_nodes,
4037 unsigned int *num_of_nodes,
Steve French737b7582005-04-28 22:41:06 -07004038 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004039{
4040/* TRANS2_GET_DFS_REFERRAL */
4041 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
4042 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004043 int rc = 0;
4044 int bytes_returned;
4045 int name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004046 __u16 params, byte_count;
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004047 *num_of_nodes = 0;
4048 *target_nodes = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004049
4050 cFYI(1, ("In GetDFSRefer the path %s", searchName));
4051 if (ses == NULL)
4052 return -ENODEV;
4053getDFSRetry:
4054 rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
4055 (void **) &pSMBr);
4056 if (rc)
4057 return rc;
Steve French50c2f752007-07-13 00:33:32 +00004058
4059 /* server pointer checked in called function,
Steve French1982c342005-08-17 12:38:22 -07004060 but should never be null here anyway */
4061 pSMB->hdr.Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004062 pSMB->hdr.Tid = ses->ipc_tid;
4063 pSMB->hdr.Uid = ses->Suid;
Steve French26f57362007-08-30 22:09:15 +00004064 if (ses->capabilities & CAP_STATUS32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004065 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
Steve French26f57362007-08-30 22:09:15 +00004066 if (ses->capabilities & CAP_DFS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004067 pSMB->hdr.Flags2 |= SMBFLG2_DFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004068
4069 if (ses->capabilities & CAP_UNICODE) {
4070 pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
4071 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004072 cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
Steve French737b7582005-04-28 22:41:06 -07004073 searchName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004074 name_len++; /* trailing null */
4075 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004076 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004077 name_len = strnlen(searchName, PATH_MAX);
4078 name_len++; /* trailing null */
4079 strncpy(pSMB->RequestFileName, searchName, name_len);
4080 }
4081
Steve French790fe572007-07-07 19:25:05 +00004082 if (ses->server) {
4083 if (ses->server->secMode &
Steve French1a4e15a2006-10-12 21:33:51 +00004084 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
4085 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
4086 }
4087
Steve French50c2f752007-07-13 00:33:32 +00004088 pSMB->hdr.Uid = ses->Suid;
Steve French1a4e15a2006-10-12 21:33:51 +00004089
Linus Torvalds1da177e2005-04-16 15:20:36 -07004090 params = 2 /* level */ + name_len /*includes null */ ;
4091 pSMB->TotalDataCount = 0;
4092 pSMB->DataCount = 0;
4093 pSMB->DataOffset = 0;
4094 pSMB->MaxParameterCount = 0;
Steve French582d21e2008-05-13 04:54:12 +00004095 /* BB find exact max SMB PDU from sess structure BB */
4096 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004097 pSMB->MaxSetupCount = 0;
4098 pSMB->Reserved = 0;
4099 pSMB->Flags = 0;
4100 pSMB->Timeout = 0;
4101 pSMB->Reserved2 = 0;
4102 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004103 struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004104 pSMB->SetupCount = 1;
4105 pSMB->Reserved3 = 0;
4106 pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
4107 byte_count = params + 3 /* pad */ ;
4108 pSMB->ParameterCount = cpu_to_le16(params);
4109 pSMB->TotalParameterCount = pSMB->ParameterCount;
4110 pSMB->MaxReferralLevel = cpu_to_le16(3);
4111 pSMB->hdr.smb_buf_length += byte_count;
4112 pSMB->ByteCount = cpu_to_le16(byte_count);
4113
4114 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
4115 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4116 if (rc) {
4117 cFYI(1, ("Send error in GetDFSRefer = %d", rc));
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004118 goto GetDFSRefExit;
4119 }
4120 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004121
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004122 /* BB Also check if enough total bytes returned? */
Igor Mammedovfec45852008-05-16 13:06:30 +04004123 if (rc || (pSMBr->ByteCount < 17)) {
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004124 rc = -EIO; /* bad smb */
Igor Mammedovfec45852008-05-16 13:06:30 +04004125 goto GetDFSRefExit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004126 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004127
4128 cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d",
4129 pSMBr->ByteCount,
4130 le16_to_cpu(pSMBr->t2.DataOffset)));
4131
4132 /* parse returned result into more usable form */
Steve Frencha1fe78f2008-05-16 18:48:38 +00004133 rc = parse_DFS_referrals(pSMBr, num_of_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004134 target_nodes, nls_codepage, remap,
4135 searchName);
Igor Mammedovfec45852008-05-16 13:06:30 +04004136
Linus Torvalds1da177e2005-04-16 15:20:36 -07004137GetDFSRefExit:
Steve French0d817bc2008-05-22 02:02:03 +00004138 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004139
4140 if (rc == -EAGAIN)
4141 goto getDFSRetry;
4142
4143 return rc;
4144}
4145
Steve French20962432005-09-21 22:05:57 -07004146/* Query File System Info such as free space to old servers such as Win 9x */
4147int
4148SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4149{
4150/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4151 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4152 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4153 FILE_SYSTEM_ALLOC_INFO *response_data;
4154 int rc = 0;
4155 int bytes_returned = 0;
4156 __u16 params, byte_count;
4157
4158 cFYI(1, ("OldQFSInfo"));
4159oldQFSInfoRetry:
4160 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4161 (void **) &pSMBr);
4162 if (rc)
4163 return rc;
Steve French20962432005-09-21 22:05:57 -07004164
4165 params = 2; /* level */
4166 pSMB->TotalDataCount = 0;
4167 pSMB->MaxParameterCount = cpu_to_le16(2);
4168 pSMB->MaxDataCount = cpu_to_le16(1000);
4169 pSMB->MaxSetupCount = 0;
4170 pSMB->Reserved = 0;
4171 pSMB->Flags = 0;
4172 pSMB->Timeout = 0;
4173 pSMB->Reserved2 = 0;
4174 byte_count = params + 1 /* pad */ ;
4175 pSMB->TotalParameterCount = cpu_to_le16(params);
4176 pSMB->ParameterCount = pSMB->TotalParameterCount;
4177 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4178 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4179 pSMB->DataCount = 0;
4180 pSMB->DataOffset = 0;
4181 pSMB->SetupCount = 1;
4182 pSMB->Reserved3 = 0;
4183 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4184 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4185 pSMB->hdr.smb_buf_length += byte_count;
4186 pSMB->ByteCount = cpu_to_le16(byte_count);
4187
4188 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4189 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4190 if (rc) {
4191 cFYI(1, ("Send error in QFSInfo = %d", rc));
4192 } else { /* decode response */
4193 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4194
4195 if (rc || (pSMBr->ByteCount < 18))
4196 rc = -EIO; /* bad smb */
4197 else {
4198 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00004199 cFYI(1, ("qfsinf resp BCC: %d Offset %d",
Steve French20962432005-09-21 22:05:57 -07004200 pSMBr->ByteCount, data_offset));
4201
Steve French50c2f752007-07-13 00:33:32 +00004202 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004203 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4204 FSData->f_bsize =
4205 le16_to_cpu(response_data->BytesPerSector) *
4206 le32_to_cpu(response_data->
4207 SectorsPerAllocationUnit);
4208 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004209 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004210 FSData->f_bfree = FSData->f_bavail =
4211 le32_to_cpu(response_data->FreeAllocationUnits);
4212 cFYI(1,
4213 ("Blocks: %lld Free: %lld Block size %ld",
4214 (unsigned long long)FSData->f_blocks,
4215 (unsigned long long)FSData->f_bfree,
4216 FSData->f_bsize));
4217 }
4218 }
4219 cifs_buf_release(pSMB);
4220
4221 if (rc == -EAGAIN)
4222 goto oldQFSInfoRetry;
4223
4224 return rc;
4225}
4226
Linus Torvalds1da177e2005-04-16 15:20:36 -07004227int
Steve French737b7582005-04-28 22:41:06 -07004228CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004229{
4230/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4231 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4232 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4233 FILE_SYSTEM_INFO *response_data;
4234 int rc = 0;
4235 int bytes_returned = 0;
4236 __u16 params, byte_count;
4237
4238 cFYI(1, ("In QFSInfo"));
4239QFSInfoRetry:
4240 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4241 (void **) &pSMBr);
4242 if (rc)
4243 return rc;
4244
4245 params = 2; /* level */
4246 pSMB->TotalDataCount = 0;
4247 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004248 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004249 pSMB->MaxSetupCount = 0;
4250 pSMB->Reserved = 0;
4251 pSMB->Flags = 0;
4252 pSMB->Timeout = 0;
4253 pSMB->Reserved2 = 0;
4254 byte_count = params + 1 /* pad */ ;
4255 pSMB->TotalParameterCount = cpu_to_le16(params);
4256 pSMB->ParameterCount = pSMB->TotalParameterCount;
4257 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004258 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004259 pSMB->DataCount = 0;
4260 pSMB->DataOffset = 0;
4261 pSMB->SetupCount = 1;
4262 pSMB->Reserved3 = 0;
4263 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4264 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4265 pSMB->hdr.smb_buf_length += byte_count;
4266 pSMB->ByteCount = cpu_to_le16(byte_count);
4267
4268 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4269 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4270 if (rc) {
Steve French20962432005-09-21 22:05:57 -07004271 cFYI(1, ("Send error in QFSInfo = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004272 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004273 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004274
Steve French20962432005-09-21 22:05:57 -07004275 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004276 rc = -EIO; /* bad smb */
4277 else {
4278 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004279
4280 response_data =
4281 (FILE_SYSTEM_INFO
4282 *) (((char *) &pSMBr->hdr.Protocol) +
4283 data_offset);
4284 FSData->f_bsize =
4285 le32_to_cpu(response_data->BytesPerSector) *
4286 le32_to_cpu(response_data->
4287 SectorsPerAllocationUnit);
4288 FSData->f_blocks =
4289 le64_to_cpu(response_data->TotalAllocationUnits);
4290 FSData->f_bfree = FSData->f_bavail =
4291 le64_to_cpu(response_data->FreeAllocationUnits);
4292 cFYI(1,
4293 ("Blocks: %lld Free: %lld Block size %ld",
4294 (unsigned long long)FSData->f_blocks,
4295 (unsigned long long)FSData->f_bfree,
4296 FSData->f_bsize));
4297 }
4298 }
4299 cifs_buf_release(pSMB);
4300
4301 if (rc == -EAGAIN)
4302 goto QFSInfoRetry;
4303
4304 return rc;
4305}
4306
4307int
Steve French737b7582005-04-28 22:41:06 -07004308CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004309{
4310/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4311 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4312 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4313 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4314 int rc = 0;
4315 int bytes_returned = 0;
4316 __u16 params, byte_count;
4317
4318 cFYI(1, ("In QFSAttributeInfo"));
4319QFSAttributeRetry:
4320 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4321 (void **) &pSMBr);
4322 if (rc)
4323 return rc;
4324
4325 params = 2; /* level */
4326 pSMB->TotalDataCount = 0;
4327 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004328 /* BB find exact max SMB PDU from sess structure BB */
4329 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004330 pSMB->MaxSetupCount = 0;
4331 pSMB->Reserved = 0;
4332 pSMB->Flags = 0;
4333 pSMB->Timeout = 0;
4334 pSMB->Reserved2 = 0;
4335 byte_count = params + 1 /* pad */ ;
4336 pSMB->TotalParameterCount = cpu_to_le16(params);
4337 pSMB->ParameterCount = pSMB->TotalParameterCount;
4338 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004339 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004340 pSMB->DataCount = 0;
4341 pSMB->DataOffset = 0;
4342 pSMB->SetupCount = 1;
4343 pSMB->Reserved3 = 0;
4344 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4345 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4346 pSMB->hdr.smb_buf_length += byte_count;
4347 pSMB->ByteCount = cpu_to_le16(byte_count);
4348
4349 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4350 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4351 if (rc) {
4352 cERROR(1, ("Send error in QFSAttributeInfo = %d", rc));
4353 } else { /* decode response */
4354 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4355
Steve French50c2f752007-07-13 00:33:32 +00004356 if (rc || (pSMBr->ByteCount < 13)) {
4357 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004358 rc = -EIO; /* bad smb */
4359 } else {
4360 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4361 response_data =
4362 (FILE_SYSTEM_ATTRIBUTE_INFO
4363 *) (((char *) &pSMBr->hdr.Protocol) +
4364 data_offset);
4365 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004366 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004367 }
4368 }
4369 cifs_buf_release(pSMB);
4370
4371 if (rc == -EAGAIN)
4372 goto QFSAttributeRetry;
4373
4374 return rc;
4375}
4376
4377int
Steve French737b7582005-04-28 22:41:06 -07004378CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004379{
4380/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4381 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4382 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4383 FILE_SYSTEM_DEVICE_INFO *response_data;
4384 int rc = 0;
4385 int bytes_returned = 0;
4386 __u16 params, byte_count;
4387
4388 cFYI(1, ("In QFSDeviceInfo"));
4389QFSDeviceRetry:
4390 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4391 (void **) &pSMBr);
4392 if (rc)
4393 return rc;
4394
4395 params = 2; /* level */
4396 pSMB->TotalDataCount = 0;
4397 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004398 /* BB find exact max SMB PDU from sess structure BB */
4399 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004400 pSMB->MaxSetupCount = 0;
4401 pSMB->Reserved = 0;
4402 pSMB->Flags = 0;
4403 pSMB->Timeout = 0;
4404 pSMB->Reserved2 = 0;
4405 byte_count = params + 1 /* pad */ ;
4406 pSMB->TotalParameterCount = cpu_to_le16(params);
4407 pSMB->ParameterCount = pSMB->TotalParameterCount;
4408 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004409 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004410
4411 pSMB->DataCount = 0;
4412 pSMB->DataOffset = 0;
4413 pSMB->SetupCount = 1;
4414 pSMB->Reserved3 = 0;
4415 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4416 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4417 pSMB->hdr.smb_buf_length += byte_count;
4418 pSMB->ByteCount = cpu_to_le16(byte_count);
4419
4420 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4421 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4422 if (rc) {
4423 cFYI(1, ("Send error in QFSDeviceInfo = %d", rc));
4424 } else { /* decode response */
4425 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4426
Steve French630f3f0c2007-10-25 21:17:17 +00004427 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004428 rc = -EIO; /* bad smb */
4429 else {
4430 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4431 response_data =
Steve French737b7582005-04-28 22:41:06 -07004432 (FILE_SYSTEM_DEVICE_INFO *)
4433 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004434 data_offset);
4435 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004436 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004437 }
4438 }
4439 cifs_buf_release(pSMB);
4440
4441 if (rc == -EAGAIN)
4442 goto QFSDeviceRetry;
4443
4444 return rc;
4445}
4446
4447int
Steve French737b7582005-04-28 22:41:06 -07004448CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004449{
4450/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4451 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4452 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4453 FILE_SYSTEM_UNIX_INFO *response_data;
4454 int rc = 0;
4455 int bytes_returned = 0;
4456 __u16 params, byte_count;
4457
4458 cFYI(1, ("In QFSUnixInfo"));
4459QFSUnixRetry:
4460 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4461 (void **) &pSMBr);
4462 if (rc)
4463 return rc;
4464
4465 params = 2; /* level */
4466 pSMB->TotalDataCount = 0;
4467 pSMB->DataCount = 0;
4468 pSMB->DataOffset = 0;
4469 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004470 /* BB find exact max SMB PDU from sess structure BB */
4471 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004472 pSMB->MaxSetupCount = 0;
4473 pSMB->Reserved = 0;
4474 pSMB->Flags = 0;
4475 pSMB->Timeout = 0;
4476 pSMB->Reserved2 = 0;
4477 byte_count = params + 1 /* pad */ ;
4478 pSMB->ParameterCount = cpu_to_le16(params);
4479 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004480 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4481 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004482 pSMB->SetupCount = 1;
4483 pSMB->Reserved3 = 0;
4484 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4485 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4486 pSMB->hdr.smb_buf_length += byte_count;
4487 pSMB->ByteCount = cpu_to_le16(byte_count);
4488
4489 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4490 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4491 if (rc) {
4492 cERROR(1, ("Send error in QFSUnixInfo = %d", rc));
4493 } else { /* decode response */
4494 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4495
4496 if (rc || (pSMBr->ByteCount < 13)) {
4497 rc = -EIO; /* bad smb */
4498 } else {
4499 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4500 response_data =
4501 (FILE_SYSTEM_UNIX_INFO
4502 *) (((char *) &pSMBr->hdr.Protocol) +
4503 data_offset);
4504 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004505 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004506 }
4507 }
4508 cifs_buf_release(pSMB);
4509
4510 if (rc == -EAGAIN)
4511 goto QFSUnixRetry;
4512
4513
4514 return rc;
4515}
4516
Jeremy Allisonac670552005-06-22 17:26:35 -07004517int
Steve French45abc6e2005-06-23 13:42:03 -05004518CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004519{
4520/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4521 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4522 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4523 int rc = 0;
4524 int bytes_returned = 0;
4525 __u16 params, param_offset, offset, byte_count;
4526
4527 cFYI(1, ("In SETFSUnixInfo"));
4528SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004529 /* BB switch to small buf init to save memory */
Jeremy Allisonac670552005-06-22 17:26:35 -07004530 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4531 (void **) &pSMBr);
4532 if (rc)
4533 return rc;
4534
4535 params = 4; /* 2 bytes zero followed by info level. */
4536 pSMB->MaxSetupCount = 0;
4537 pSMB->Reserved = 0;
4538 pSMB->Flags = 0;
4539 pSMB->Timeout = 0;
4540 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004541 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4542 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004543 offset = param_offset + params;
4544
4545 pSMB->MaxParameterCount = cpu_to_le16(4);
Steve French582d21e2008-05-13 04:54:12 +00004546 /* BB find exact max SMB PDU from sess structure BB */
4547 pSMB->MaxDataCount = cpu_to_le16(100);
Jeremy Allisonac670552005-06-22 17:26:35 -07004548 pSMB->SetupCount = 1;
4549 pSMB->Reserved3 = 0;
4550 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4551 byte_count = 1 /* pad */ + params + 12;
4552
4553 pSMB->DataCount = cpu_to_le16(12);
4554 pSMB->ParameterCount = cpu_to_le16(params);
4555 pSMB->TotalDataCount = pSMB->DataCount;
4556 pSMB->TotalParameterCount = pSMB->ParameterCount;
4557 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4558 pSMB->DataOffset = cpu_to_le16(offset);
4559
4560 /* Params. */
4561 pSMB->FileNum = 0;
4562 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4563
4564 /* Data. */
4565 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4566 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4567 pSMB->ClientUnixCap = cpu_to_le64(cap);
4568
4569 pSMB->hdr.smb_buf_length += byte_count;
4570 pSMB->ByteCount = cpu_to_le16(byte_count);
4571
4572 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4573 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4574 if (rc) {
4575 cERROR(1, ("Send error in SETFSUnixInfo = %d", rc));
4576 } else { /* decode response */
4577 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004578 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004579 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004580 }
4581 cifs_buf_release(pSMB);
4582
4583 if (rc == -EAGAIN)
4584 goto SETFSUnixRetry;
4585
4586 return rc;
4587}
4588
4589
Linus Torvalds1da177e2005-04-16 15:20:36 -07004590
4591int
4592CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004593 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004594{
4595/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4596 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4597 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4598 FILE_SYSTEM_POSIX_INFO *response_data;
4599 int rc = 0;
4600 int bytes_returned = 0;
4601 __u16 params, byte_count;
4602
4603 cFYI(1, ("In QFSPosixInfo"));
4604QFSPosixRetry:
4605 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4606 (void **) &pSMBr);
4607 if (rc)
4608 return rc;
4609
4610 params = 2; /* level */
4611 pSMB->TotalDataCount = 0;
4612 pSMB->DataCount = 0;
4613 pSMB->DataOffset = 0;
4614 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004615 /* BB find exact max SMB PDU from sess structure BB */
4616 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004617 pSMB->MaxSetupCount = 0;
4618 pSMB->Reserved = 0;
4619 pSMB->Flags = 0;
4620 pSMB->Timeout = 0;
4621 pSMB->Reserved2 = 0;
4622 byte_count = params + 1 /* pad */ ;
4623 pSMB->ParameterCount = cpu_to_le16(params);
4624 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004625 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4626 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004627 pSMB->SetupCount = 1;
4628 pSMB->Reserved3 = 0;
4629 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4630 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4631 pSMB->hdr.smb_buf_length += byte_count;
4632 pSMB->ByteCount = cpu_to_le16(byte_count);
4633
4634 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4635 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4636 if (rc) {
4637 cFYI(1, ("Send error in QFSUnixInfo = %d", rc));
4638 } else { /* decode response */
4639 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4640
4641 if (rc || (pSMBr->ByteCount < 13)) {
4642 rc = -EIO; /* bad smb */
4643 } else {
4644 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4645 response_data =
4646 (FILE_SYSTEM_POSIX_INFO
4647 *) (((char *) &pSMBr->hdr.Protocol) +
4648 data_offset);
4649 FSData->f_bsize =
4650 le32_to_cpu(response_data->BlockSize);
4651 FSData->f_blocks =
4652 le64_to_cpu(response_data->TotalBlocks);
4653 FSData->f_bfree =
4654 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004655 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004656 FSData->f_bavail = FSData->f_bfree;
4657 } else {
4658 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004659 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004660 }
Steve French790fe572007-07-07 19:25:05 +00004661 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004662 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004663 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004664 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004665 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004666 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004667 }
4668 }
4669 cifs_buf_release(pSMB);
4670
4671 if (rc == -EAGAIN)
4672 goto QFSPosixRetry;
4673
4674 return rc;
4675}
4676
4677
Steve French50c2f752007-07-13 00:33:32 +00004678/* We can not use write of zero bytes trick to
4679 set file size due to need for large file support. Also note that
4680 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004681 routine which is only needed to work around a sharing violation bug
4682 in Samba which this routine can run into */
4683
4684int
4685CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French4b18f2a2008-04-29 00:06:05 +00004686 __u64 size, bool SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004687 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004688{
4689 struct smb_com_transaction2_spi_req *pSMB = NULL;
4690 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4691 struct file_end_of_file_info *parm_data;
4692 int name_len;
4693 int rc = 0;
4694 int bytes_returned = 0;
4695 __u16 params, byte_count, data_count, param_offset, offset;
4696
4697 cFYI(1, ("In SetEOF"));
4698SetEOFRetry:
4699 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4700 (void **) &pSMBr);
4701 if (rc)
4702 return rc;
4703
4704 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4705 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004706 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004707 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004708 name_len++; /* trailing null */
4709 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004710 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004711 name_len = strnlen(fileName, PATH_MAX);
4712 name_len++; /* trailing null */
4713 strncpy(pSMB->FileName, fileName, name_len);
4714 }
4715 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004716 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004717 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004718 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004719 pSMB->MaxSetupCount = 0;
4720 pSMB->Reserved = 0;
4721 pSMB->Flags = 0;
4722 pSMB->Timeout = 0;
4723 pSMB->Reserved2 = 0;
4724 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004725 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004726 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004727 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004728 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4729 pSMB->InformationLevel =
4730 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4731 else
4732 pSMB->InformationLevel =
4733 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4734 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004735 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4736 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004737 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004738 else
4739 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004740 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004741 }
4742
4743 parm_data =
4744 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4745 offset);
4746 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4747 pSMB->DataOffset = cpu_to_le16(offset);
4748 pSMB->SetupCount = 1;
4749 pSMB->Reserved3 = 0;
4750 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4751 byte_count = 3 /* pad */ + params + data_count;
4752 pSMB->DataCount = cpu_to_le16(data_count);
4753 pSMB->TotalDataCount = pSMB->DataCount;
4754 pSMB->ParameterCount = cpu_to_le16(params);
4755 pSMB->TotalParameterCount = pSMB->ParameterCount;
4756 pSMB->Reserved4 = 0;
4757 pSMB->hdr.smb_buf_length += byte_count;
4758 parm_data->FileSize = cpu_to_le64(size);
4759 pSMB->ByteCount = cpu_to_le16(byte_count);
4760 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4761 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004762 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004763 cFYI(1, ("SetPathInfo (file size) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004764
4765 cifs_buf_release(pSMB);
4766
4767 if (rc == -EAGAIN)
4768 goto SetEOFRetry;
4769
4770 return rc;
4771}
4772
4773int
Steve French50c2f752007-07-13 00:33:32 +00004774CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
Steve French4b18f2a2008-04-29 00:06:05 +00004775 __u16 fid, __u32 pid_of_opener, bool SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004776{
4777 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004778 char *data_offset;
4779 struct file_end_of_file_info *parm_data;
4780 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004781 __u16 params, param_offset, offset, byte_count, count;
4782
4783 cFYI(1, ("SetFileSize (via SetFileInfo) %lld",
4784 (long long)size));
Steve Frenchcd634992005-04-28 22:41:10 -07004785 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4786
Linus Torvalds1da177e2005-04-16 15:20:36 -07004787 if (rc)
4788 return rc;
4789
4790 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4791 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004792
Linus Torvalds1da177e2005-04-16 15:20:36 -07004793 params = 6;
4794 pSMB->MaxSetupCount = 0;
4795 pSMB->Reserved = 0;
4796 pSMB->Flags = 0;
4797 pSMB->Timeout = 0;
4798 pSMB->Reserved2 = 0;
4799 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4800 offset = param_offset + params;
4801
Steve French50c2f752007-07-13 00:33:32 +00004802 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004803
4804 count = sizeof(struct file_end_of_file_info);
4805 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004806 /* BB find exact max SMB PDU from sess structure BB */
4807 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004808 pSMB->SetupCount = 1;
4809 pSMB->Reserved3 = 0;
4810 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4811 byte_count = 3 /* pad */ + params + count;
4812 pSMB->DataCount = cpu_to_le16(count);
4813 pSMB->ParameterCount = cpu_to_le16(params);
4814 pSMB->TotalDataCount = pSMB->DataCount;
4815 pSMB->TotalParameterCount = pSMB->ParameterCount;
4816 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4817 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004818 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4819 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004820 pSMB->DataOffset = cpu_to_le16(offset);
4821 parm_data->FileSize = cpu_to_le64(size);
4822 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004823 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004824 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4825 pSMB->InformationLevel =
4826 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4827 else
4828 pSMB->InformationLevel =
4829 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004830 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004831 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4832 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004833 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004834 else
4835 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004836 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004837 }
4838 pSMB->Reserved4 = 0;
4839 pSMB->hdr.smb_buf_length += byte_count;
4840 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004841 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004842 if (rc) {
4843 cFYI(1,
4844 ("Send error in SetFileInfo (SetFileSize) = %d",
4845 rc));
4846 }
4847
Steve French50c2f752007-07-13 00:33:32 +00004848 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004849 since file handle passed in no longer valid */
4850
4851 return rc;
4852}
4853
Steve French50c2f752007-07-13 00:33:32 +00004854/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004855 an open handle, rather than by pathname - this is awkward due to
4856 potential access conflicts on the open, but it is unavoidable for these
4857 old servers since the only other choice is to go from 100 nanosecond DCE
4858 time and resort to the original setpathinfo level which takes the ancient
4859 DOS time format with 2 second granularity */
4860int
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004861CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
4862 const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004863{
4864 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004865 char *data_offset;
4866 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004867 __u16 params, param_offset, offset, byte_count, count;
4868
4869 cFYI(1, ("Set Times (via SetFileInfo)"));
Steve Frenchcd634992005-04-28 22:41:10 -07004870 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4871
Linus Torvalds1da177e2005-04-16 15:20:36 -07004872 if (rc)
4873 return rc;
4874
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004875 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4876 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004877
Linus Torvalds1da177e2005-04-16 15:20:36 -07004878 params = 6;
4879 pSMB->MaxSetupCount = 0;
4880 pSMB->Reserved = 0;
4881 pSMB->Flags = 0;
4882 pSMB->Timeout = 0;
4883 pSMB->Reserved2 = 0;
4884 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4885 offset = param_offset + params;
4886
Steve French50c2f752007-07-13 00:33:32 +00004887 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004888
Steve French26f57362007-08-30 22:09:15 +00004889 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004890 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004891 /* BB find max SMB PDU from sess */
4892 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004893 pSMB->SetupCount = 1;
4894 pSMB->Reserved3 = 0;
4895 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4896 byte_count = 3 /* pad */ + params + count;
4897 pSMB->DataCount = cpu_to_le16(count);
4898 pSMB->ParameterCount = cpu_to_le16(params);
4899 pSMB->TotalDataCount = pSMB->DataCount;
4900 pSMB->TotalParameterCount = pSMB->ParameterCount;
4901 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4902 pSMB->DataOffset = cpu_to_le16(offset);
4903 pSMB->Fid = fid;
4904 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4905 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4906 else
4907 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
4908 pSMB->Reserved4 = 0;
4909 pSMB->hdr.smb_buf_length += byte_count;
4910 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00004911 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00004912 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004913 if (rc)
Steve French50c2f752007-07-13 00:33:32 +00004914 cFYI(1, ("Send error in Set Time (SetFileInfo) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004915
Steve French50c2f752007-07-13 00:33:32 +00004916 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004917 since file handle passed in no longer valid */
4918
4919 return rc;
4920}
4921
Jeff Layton6d22f092008-09-23 11:48:35 -04004922int
4923CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
4924 bool delete_file, __u16 fid, __u32 pid_of_opener)
4925{
4926 struct smb_com_transaction2_sfi_req *pSMB = NULL;
4927 char *data_offset;
4928 int rc = 0;
4929 __u16 params, param_offset, offset, byte_count, count;
4930
4931 cFYI(1, ("Set File Disposition (via SetFileInfo)"));
4932 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4933
4934 if (rc)
4935 return rc;
4936
4937 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4938 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
4939
4940 params = 6;
4941 pSMB->MaxSetupCount = 0;
4942 pSMB->Reserved = 0;
4943 pSMB->Flags = 0;
4944 pSMB->Timeout = 0;
4945 pSMB->Reserved2 = 0;
4946 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4947 offset = param_offset + params;
4948
4949 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
4950
4951 count = 1;
4952 pSMB->MaxParameterCount = cpu_to_le16(2);
4953 /* BB find max SMB PDU from sess */
4954 pSMB->MaxDataCount = cpu_to_le16(1000);
4955 pSMB->SetupCount = 1;
4956 pSMB->Reserved3 = 0;
4957 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4958 byte_count = 3 /* pad */ + params + count;
4959 pSMB->DataCount = cpu_to_le16(count);
4960 pSMB->ParameterCount = cpu_to_le16(params);
4961 pSMB->TotalDataCount = pSMB->DataCount;
4962 pSMB->TotalParameterCount = pSMB->ParameterCount;
4963 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4964 pSMB->DataOffset = cpu_to_le16(offset);
4965 pSMB->Fid = fid;
4966 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
4967 pSMB->Reserved4 = 0;
4968 pSMB->hdr.smb_buf_length += byte_count;
4969 pSMB->ByteCount = cpu_to_le16(byte_count);
4970 *data_offset = delete_file ? 1 : 0;
4971 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
4972 if (rc)
4973 cFYI(1, ("Send error in SetFileDisposition = %d", rc));
4974
4975 return rc;
4976}
Linus Torvalds1da177e2005-04-16 15:20:36 -07004977
4978int
Jeff Layton6fc000e2008-08-02 07:26:12 -04004979CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
4980 const char *fileName, const FILE_BASIC_INFO *data,
4981 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004982{
4983 TRANSACTION2_SPI_REQ *pSMB = NULL;
4984 TRANSACTION2_SPI_RSP *pSMBr = NULL;
4985 int name_len;
4986 int rc = 0;
4987 int bytes_returned = 0;
4988 char *data_offset;
4989 __u16 params, param_offset, offset, byte_count, count;
4990
4991 cFYI(1, ("In SetTimes"));
4992
4993SetTimesRetry:
4994 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4995 (void **) &pSMBr);
4996 if (rc)
4997 return rc;
4998
4999 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5000 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05005001 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005002 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005003 name_len++; /* trailing null */
5004 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005005 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005006 name_len = strnlen(fileName, PATH_MAX);
5007 name_len++; /* trailing null */
5008 strncpy(pSMB->FileName, fileName, name_len);
5009 }
5010
5011 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005012 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005013 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005014 /* BB find max SMB PDU from sess structure BB */
5015 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005016 pSMB->MaxSetupCount = 0;
5017 pSMB->Reserved = 0;
5018 pSMB->Flags = 0;
5019 pSMB->Timeout = 0;
5020 pSMB->Reserved2 = 0;
5021 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005022 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005023 offset = param_offset + params;
5024 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5025 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5026 pSMB->DataOffset = cpu_to_le16(offset);
5027 pSMB->SetupCount = 1;
5028 pSMB->Reserved3 = 0;
5029 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5030 byte_count = 3 /* pad */ + params + count;
5031
5032 pSMB->DataCount = cpu_to_le16(count);
5033 pSMB->ParameterCount = cpu_to_le16(params);
5034 pSMB->TotalDataCount = pSMB->DataCount;
5035 pSMB->TotalParameterCount = pSMB->ParameterCount;
5036 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5037 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5038 else
5039 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5040 pSMB->Reserved4 = 0;
5041 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00005042 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005043 pSMB->ByteCount = cpu_to_le16(byte_count);
5044 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5045 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005046 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005047 cFYI(1, ("SetPathInfo (times) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005048
5049 cifs_buf_release(pSMB);
5050
5051 if (rc == -EAGAIN)
5052 goto SetTimesRetry;
5053
5054 return rc;
5055}
5056
5057/* Can not be used to set time stamps yet (due to old DOS time format) */
5058/* Can be used to set attributes */
5059#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
5060 handling it anyway and NT4 was what we thought it would be needed for
5061 Do not delete it until we prove whether needed for Win9x though */
5062int
5063CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
5064 __u16 dos_attrs, const struct nls_table *nls_codepage)
5065{
5066 SETATTR_REQ *pSMB = NULL;
5067 SETATTR_RSP *pSMBr = NULL;
5068 int rc = 0;
5069 int bytes_returned;
5070 int name_len;
5071
5072 cFYI(1, ("In SetAttrLegacy"));
5073
5074SetAttrLgcyRetry:
5075 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
5076 (void **) &pSMBr);
5077 if (rc)
5078 return rc;
5079
5080 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5081 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005082 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005083 PATH_MAX, nls_codepage);
5084 name_len++; /* trailing null */
5085 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005086 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005087 name_len = strnlen(fileName, PATH_MAX);
5088 name_len++; /* trailing null */
5089 strncpy(pSMB->fileName, fileName, name_len);
5090 }
5091 pSMB->attr = cpu_to_le16(dos_attrs);
5092 pSMB->BufferFormat = 0x04;
5093 pSMB->hdr.smb_buf_length += name_len + 1;
5094 pSMB->ByteCount = cpu_to_le16(name_len + 1);
5095 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5096 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005097 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005098 cFYI(1, ("Error in LegacySetAttr = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005099
5100 cifs_buf_release(pSMB);
5101
5102 if (rc == -EAGAIN)
5103 goto SetAttrLgcyRetry;
5104
5105 return rc;
5106}
5107#endif /* temporarily unneeded SetAttr legacy function */
5108
5109int
Jeff Layton4e1e7fb2008-08-02 07:26:12 -04005110CIFSSMBUnixSetInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,
Steve French063ea272008-08-06 04:23:13 +00005111 const struct cifs_unix_set_info_args *args,
Jeff Layton4e1e7fb2008-08-02 07:26:12 -04005112 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 FILE_UNIX_BASIC_INFO *data_offset;
5120 __u16 params, param_offset, offset, count, byte_count;
Jeff Layton4e1e7fb2008-08-02 07:26:12 -04005121 __u64 mode = args->mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005122
5123 cFYI(1, ("In SetUID/GID/Mode"));
5124setPermsRetry:
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 French50c2f752007-07-13 00:33:32 +00005132 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 French3e87d802005-09-18 20:49:21 -07005136 } 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_UNIX_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 =
5156 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5157 offset);
5158 memset(data_offset, 0, count);
5159 pSMB->DataOffset = cpu_to_le16(offset);
5160 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5161 pSMB->SetupCount = 1;
5162 pSMB->Reserved3 = 0;
5163 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5164 byte_count = 3 /* pad */ + params + count;
5165 pSMB->ParameterCount = cpu_to_le16(params);
5166 pSMB->DataCount = cpu_to_le16(count);
5167 pSMB->TotalParameterCount = pSMB->ParameterCount;
5168 pSMB->TotalDataCount = pSMB->DataCount;
5169 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5170 pSMB->Reserved4 = 0;
5171 pSMB->hdr.smb_buf_length += byte_count;
Steve Frenchc7af1852007-03-01 04:11:22 +00005172 /* Samba server ignores set of file size to zero due to bugs in some
5173 older clients, but we should be precise - we use SetFileSize to
5174 set file size and do not want to truncate file size to zero
5175 accidently as happened on one Samba server beta by putting
Steve French50c2f752007-07-13 00:33:32 +00005176 zero instead of -1 here */
Jeff Layton4e1e7fb2008-08-02 07:26:12 -04005177 data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
5178 data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
5179 data_offset->LastStatusChange = cpu_to_le64(args->ctime);
5180 data_offset->LastAccessTime = cpu_to_le64(args->atime);
5181 data_offset->LastModificationTime = cpu_to_le64(args->mtime);
5182 data_offset->Uid = cpu_to_le64(args->uid);
5183 data_offset->Gid = cpu_to_le64(args->gid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005184 /* better to leave device as zero when it is */
Jeff Layton4e1e7fb2008-08-02 07:26:12 -04005185 data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
5186 data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005187 data_offset->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00005188
Steve French790fe572007-07-07 19:25:05 +00005189 if (S_ISREG(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005190 data_offset->Type = cpu_to_le32(UNIX_FILE);
Steve French790fe572007-07-07 19:25:05 +00005191 else if (S_ISDIR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005192 data_offset->Type = cpu_to_le32(UNIX_DIR);
Steve French790fe572007-07-07 19:25:05 +00005193 else if (S_ISLNK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005194 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
Steve French790fe572007-07-07 19:25:05 +00005195 else if (S_ISCHR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005196 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
Steve French790fe572007-07-07 19:25:05 +00005197 else if (S_ISBLK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005198 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
Steve French790fe572007-07-07 19:25:05 +00005199 else if (S_ISFIFO(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005200 data_offset->Type = cpu_to_le32(UNIX_FIFO);
Steve French790fe572007-07-07 19:25:05 +00005201 else if (S_ISSOCK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005202 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5203
5204
5205 pSMB->ByteCount = cpu_to_le16(byte_count);
5206 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5207 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005208 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005209 cFYI(1, ("SetPathInfo (perms) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005210
Steve French0d817bc2008-05-22 02:02:03 +00005211 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005212 if (rc == -EAGAIN)
5213 goto setPermsRetry;
5214 return rc;
5215}
5216
Steve French50c2f752007-07-13 00:33:32 +00005217int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005218 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005219 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005220 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005221{
5222 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005223 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5224 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005225 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005226 int bytes_returned;
5227
Steve French50c2f752007-07-13 00:33:32 +00005228 cFYI(1, ("In CIFSSMBNotify for file handle %d", (int)netfid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005229 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005230 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005231 if (rc)
5232 return rc;
5233
5234 pSMB->TotalParameterCount = 0 ;
5235 pSMB->TotalDataCount = 0;
5236 pSMB->MaxParameterCount = cpu_to_le32(2);
5237 /* BB find exact data count max from sess structure BB */
5238 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005239/* BB VERIFY verify which is correct for above BB */
5240 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5241 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5242
Linus Torvalds1da177e2005-04-16 15:20:36 -07005243 pSMB->MaxSetupCount = 4;
5244 pSMB->Reserved = 0;
5245 pSMB->ParameterOffset = 0;
5246 pSMB->DataCount = 0;
5247 pSMB->DataOffset = 0;
5248 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5249 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5250 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005251 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005252 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5253 pSMB->Reserved2 = 0;
5254 pSMB->CompletionFilter = cpu_to_le32(filter);
5255 pSMB->Fid = netfid; /* file handle always le */
5256 pSMB->ByteCount = 0;
5257
5258 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005259 (struct smb_hdr *)pSMBr, &bytes_returned,
5260 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005261 if (rc) {
5262 cFYI(1, ("Error in Notify = %d", rc));
Steve Frenchff5dbd92005-08-24 17:10:36 -07005263 } else {
5264 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005265 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005266 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005267 sizeof(struct dir_notify_req),
5268 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005269 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005270 dnotify_req->Pid = pSMB->hdr.Pid;
5271 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5272 dnotify_req->Mid = pSMB->hdr.Mid;
5273 dnotify_req->Tid = pSMB->hdr.Tid;
5274 dnotify_req->Uid = pSMB->hdr.Uid;
5275 dnotify_req->netfid = netfid;
5276 dnotify_req->pfile = pfile;
5277 dnotify_req->filter = filter;
5278 dnotify_req->multishot = multishot;
5279 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005280 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005281 &GlobalDnotifyReqList);
5282 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005283 } else
Steve French47c786e2005-10-11 20:03:18 -07005284 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005285 }
5286 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005287 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005288}
5289#ifdef CONFIG_CIFS_XATTR
5290ssize_t
5291CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
5292 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00005293 char *EAData, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005294 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005295{
5296 /* BB assumes one setup word */
5297 TRANSACTION2_QPI_REQ *pSMB = NULL;
5298 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5299 int rc = 0;
5300 int bytes_returned;
5301 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005302 struct fea *temp_fea;
5303 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005304 __u16 params, byte_count;
5305
5306 cFYI(1, ("In Query All EAs path %s", searchName));
5307QAllEAsRetry:
5308 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5309 (void **) &pSMBr);
5310 if (rc)
5311 return rc;
5312
5313 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5314 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005315 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005316 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005317 name_len++; /* trailing null */
5318 name_len *= 2;
5319 } else { /* BB improve the check for buffer overruns BB */
5320 name_len = strnlen(searchName, PATH_MAX);
5321 name_len++; /* trailing null */
5322 strncpy(pSMB->FileName, searchName, name_len);
5323 }
5324
Steve French50c2f752007-07-13 00:33:32 +00005325 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005326 pSMB->TotalDataCount = 0;
5327 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005328 /* BB find exact max SMB PDU from sess structure BB */
5329 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005330 pSMB->MaxSetupCount = 0;
5331 pSMB->Reserved = 0;
5332 pSMB->Flags = 0;
5333 pSMB->Timeout = 0;
5334 pSMB->Reserved2 = 0;
5335 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005336 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005337 pSMB->DataCount = 0;
5338 pSMB->DataOffset = 0;
5339 pSMB->SetupCount = 1;
5340 pSMB->Reserved3 = 0;
5341 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5342 byte_count = params + 1 /* pad */ ;
5343 pSMB->TotalParameterCount = cpu_to_le16(params);
5344 pSMB->ParameterCount = pSMB->TotalParameterCount;
5345 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5346 pSMB->Reserved4 = 0;
5347 pSMB->hdr.smb_buf_length += byte_count;
5348 pSMB->ByteCount = cpu_to_le16(byte_count);
5349
5350 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5351 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5352 if (rc) {
5353 cFYI(1, ("Send error in QueryAllEAs = %d", rc));
5354 } else { /* decode response */
5355 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5356
5357 /* BB also check enough total bytes returned */
5358 /* BB we need to improve the validity checking
5359 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005360 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005361 rc = -EIO; /* bad smb */
5362 /* else if (pFindData){
5363 memcpy((char *) pFindData,
5364 (char *) &pSMBr->hdr.Protocol +
5365 data_offset, kl);
5366 }*/ else {
5367 /* check that length of list is not more than bcc */
5368 /* check that each entry does not go beyond length
5369 of list */
5370 /* check that each element of each entry does not
5371 go beyond end of list */
5372 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005373 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005374 rc = 0;
5375 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005376 /* BB check if start of smb + data_offset > &bcc+ bcc */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005377 ea_response_data = (struct fealist *)
5378 (((char *) &pSMBr->hdr.Protocol) +
5379 data_offset);
5380 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005381 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005382 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005383 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005384 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005385 } else {
5386 /* account for ea list len */
5387 name_len -= 4;
5388 temp_fea = ea_response_data->list;
5389 temp_ptr = (char *)temp_fea;
Steve French50c2f752007-07-13 00:33:32 +00005390 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005391 __u16 value_len;
5392 name_len -= 4;
5393 temp_ptr += 4;
5394 rc += temp_fea->name_len;
5395 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005396 rc = rc + 5 + 1;
5397 if (rc < (int)buf_size) {
Steve French50c2f752007-07-13 00:33:32 +00005398 memcpy(EAData, "user.", 5);
5399 EAData += 5;
5400 memcpy(EAData, temp_ptr,
5401 temp_fea->name_len);
5402 EAData += temp_fea->name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005403 /* null terminate name */
5404 *EAData = 0;
5405 EAData = EAData + 1;
Steve French790fe572007-07-07 19:25:05 +00005406 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005407 /* skip copy - calc size only */
5408 } else {
5409 /* stop before overrun buffer */
5410 rc = -ERANGE;
5411 break;
5412 }
5413 name_len -= temp_fea->name_len;
5414 temp_ptr += temp_fea->name_len;
5415 /* account for trailing null */
5416 name_len--;
5417 temp_ptr++;
Steve French50c2f752007-07-13 00:33:32 +00005418 value_len =
5419 le16_to_cpu(temp_fea->value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005420 name_len -= value_len;
5421 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005422 /* BB check that temp_ptr is still
5423 within the SMB BB*/
5424
5425 /* no trailing null to account for
5426 in value len */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005427 /* go on to next EA */
5428 temp_fea = (struct fea *)temp_ptr;
5429 }
5430 }
5431 }
5432 }
Steve French0d817bc2008-05-22 02:02:03 +00005433 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005434 if (rc == -EAGAIN)
5435 goto QAllEAsRetry;
5436
5437 return (ssize_t)rc;
5438}
5439
Steve French50c2f752007-07-13 00:33:32 +00005440ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon,
5441 const unsigned char *searchName, const unsigned char *ea_name,
5442 unsigned char *ea_value, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005443 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005444{
5445 TRANSACTION2_QPI_REQ *pSMB = NULL;
5446 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5447 int rc = 0;
5448 int bytes_returned;
5449 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005450 struct fea *temp_fea;
5451 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005452 __u16 params, byte_count;
5453
5454 cFYI(1, ("In Query EA path %s", searchName));
5455QEARetry:
5456 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5457 (void **) &pSMBr);
5458 if (rc)
5459 return rc;
5460
5461 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5462 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005463 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005464 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005465 name_len++; /* trailing null */
5466 name_len *= 2;
5467 } else { /* BB improve the check for buffer overruns BB */
5468 name_len = strnlen(searchName, PATH_MAX);
5469 name_len++; /* trailing null */
5470 strncpy(pSMB->FileName, searchName, name_len);
5471 }
5472
Steve French50c2f752007-07-13 00:33:32 +00005473 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005474 pSMB->TotalDataCount = 0;
5475 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005476 /* BB find exact max SMB PDU from sess structure BB */
5477 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005478 pSMB->MaxSetupCount = 0;
5479 pSMB->Reserved = 0;
5480 pSMB->Flags = 0;
5481 pSMB->Timeout = 0;
5482 pSMB->Reserved2 = 0;
5483 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005484 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005485 pSMB->DataCount = 0;
5486 pSMB->DataOffset = 0;
5487 pSMB->SetupCount = 1;
5488 pSMB->Reserved3 = 0;
5489 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5490 byte_count = params + 1 /* pad */ ;
5491 pSMB->TotalParameterCount = cpu_to_le16(params);
5492 pSMB->ParameterCount = pSMB->TotalParameterCount;
5493 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5494 pSMB->Reserved4 = 0;
5495 pSMB->hdr.smb_buf_length += byte_count;
5496 pSMB->ByteCount = cpu_to_le16(byte_count);
5497
5498 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5499 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5500 if (rc) {
5501 cFYI(1, ("Send error in Query EA = %d", rc));
5502 } else { /* decode response */
5503 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5504
5505 /* BB also check enough total bytes returned */
5506 /* BB we need to improve the validity checking
5507 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005508 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005509 rc = -EIO; /* bad smb */
5510 /* else if (pFindData){
5511 memcpy((char *) pFindData,
5512 (char *) &pSMBr->hdr.Protocol +
5513 data_offset, kl);
5514 }*/ else {
5515 /* check that length of list is not more than bcc */
5516 /* check that each entry does not go beyond length
5517 of list */
5518 /* check that each element of each entry does not
5519 go beyond end of list */
5520 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005521 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005522 rc = -ENODATA;
5523 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005524 /* BB check if start of smb + data_offset > &bcc+ bcc*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07005525 ea_response_data = (struct fealist *)
5526 (((char *) &pSMBr->hdr.Protocol) +
5527 data_offset);
5528 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005529 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005530 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005531 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005532 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005533 } else {
5534 /* account for ea list len */
5535 name_len -= 4;
5536 temp_fea = ea_response_data->list;
5537 temp_ptr = (char *)temp_fea;
5538 /* loop through checking if we have a matching
5539 name and then return the associated value */
Steve French50c2f752007-07-13 00:33:32 +00005540 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005541 __u16 value_len;
5542 name_len -= 4;
5543 temp_ptr += 4;
Steve French50c2f752007-07-13 00:33:32 +00005544 value_len =
5545 le16_to_cpu(temp_fea->value_len);
5546 /* BB validate that value_len falls within SMB,
5547 even though maximum for name_len is 255 */
Steve French790fe572007-07-07 19:25:05 +00005548 if (memcmp(temp_fea->name, ea_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005549 temp_fea->name_len) == 0) {
5550 /* found a match */
5551 rc = value_len;
5552 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005553 if (rc <= (int)buf_size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005554 memcpy(ea_value,
5555 temp_fea->name+temp_fea->name_len+1,
5556 rc);
Steve French50c2f752007-07-13 00:33:32 +00005557 /* ea values, unlike ea
5558 names, are not null
5559 terminated */
Steve French790fe572007-07-07 19:25:05 +00005560 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005561 /* skip copy - calc size only */
5562 } else {
Steve French50c2f752007-07-13 00:33:32 +00005563 /* stop before overrun buffer */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005564 rc = -ERANGE;
5565 }
5566 break;
5567 }
5568 name_len -= temp_fea->name_len;
5569 temp_ptr += temp_fea->name_len;
5570 /* account for trailing null */
5571 name_len--;
5572 temp_ptr++;
5573 name_len -= value_len;
5574 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005575 /* No trailing null to account for in
5576 value_len. Go on to next EA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005577 temp_fea = (struct fea *)temp_ptr;
5578 }
Steve French50c2f752007-07-13 00:33:32 +00005579 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005580 }
5581 }
Steve French0d817bc2008-05-22 02:02:03 +00005582 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005583 if (rc == -EAGAIN)
5584 goto QEARetry;
5585
5586 return (ssize_t)rc;
5587}
5588
5589int
5590CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005591 const char *ea_name, const void *ea_value,
5592 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5593 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005594{
5595 struct smb_com_transaction2_spi_req *pSMB = NULL;
5596 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5597 struct fealist *parm_data;
5598 int name_len;
5599 int rc = 0;
5600 int bytes_returned = 0;
5601 __u16 params, param_offset, byte_count, offset, count;
5602
5603 cFYI(1, ("In SetEA"));
5604SetEARetry:
5605 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5606 (void **) &pSMBr);
5607 if (rc)
5608 return rc;
5609
5610 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5611 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005612 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005613 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005614 name_len++; /* trailing null */
5615 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005616 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005617 name_len = strnlen(fileName, PATH_MAX);
5618 name_len++; /* trailing null */
5619 strncpy(pSMB->FileName, fileName, name_len);
5620 }
5621
5622 params = 6 + name_len;
5623
5624 /* done calculating parms using name_len of file name,
5625 now use name_len to calculate length of ea name
5626 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005627 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005628 name_len = 0;
5629 else
Steve French50c2f752007-07-13 00:33:32 +00005630 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005631
Steve Frenchdae5dbdb2007-12-30 23:49:57 +00005632 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005633 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005634 /* BB find max SMB PDU from sess */
5635 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005636 pSMB->MaxSetupCount = 0;
5637 pSMB->Reserved = 0;
5638 pSMB->Flags = 0;
5639 pSMB->Timeout = 0;
5640 pSMB->Reserved2 = 0;
5641 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005642 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005643 offset = param_offset + params;
5644 pSMB->InformationLevel =
5645 cpu_to_le16(SMB_SET_FILE_EA);
5646
5647 parm_data =
5648 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5649 offset);
5650 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5651 pSMB->DataOffset = cpu_to_le16(offset);
5652 pSMB->SetupCount = 1;
5653 pSMB->Reserved3 = 0;
5654 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5655 byte_count = 3 /* pad */ + params + count;
5656 pSMB->DataCount = cpu_to_le16(count);
5657 parm_data->list_len = cpu_to_le32(count);
5658 parm_data->list[0].EA_flags = 0;
5659 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005660 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005661 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005662 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005663 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005664 parm_data->list[0].name[name_len] = 0;
5665 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5666 /* caller ensures that ea_value_len is less than 64K but
5667 we need to ensure that it fits within the smb */
5668
Steve French50c2f752007-07-13 00:33:32 +00005669 /*BB add length check to see if it would fit in
5670 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005671 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5672 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005673 memcpy(parm_data->list[0].name+name_len+1,
5674 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005675
5676 pSMB->TotalDataCount = pSMB->DataCount;
5677 pSMB->ParameterCount = cpu_to_le16(params);
5678 pSMB->TotalParameterCount = pSMB->ParameterCount;
5679 pSMB->Reserved4 = 0;
5680 pSMB->hdr.smb_buf_length += byte_count;
5681 pSMB->ByteCount = cpu_to_le16(byte_count);
5682 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5683 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005684 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005685 cFYI(1, ("SetPathInfo (EA) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005686
5687 cifs_buf_release(pSMB);
5688
5689 if (rc == -EAGAIN)
5690 goto SetEARetry;
5691
5692 return rc;
5693}
5694
5695#endif