blob: 7e175e1399d1660aff73fcc03481c67f751bd0f0 [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 French26f57362007-08-30 22:09:15 +0000131 if (open_file)
Steve French4b18f2a2008-04-29 00:06:05 +0000132 open_file->invalidHandle = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 }
134 write_unlock(&GlobalSMBSeslock);
Steve French09d1db52005-04-28 22:41:08 -0700135 /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
136 to this tcon */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137}
138
Steve Frenchad7a2922008-02-07 23:25:02 +0000139/* Allocate and return pointer to an SMB request buffer, and set basic
140 SMB information in the SMB header. If the return code is zero, this
141 function must have filled in request_buf pointer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142static int
143small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000144 void **request_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145{
146 int rc = 0;
147
148 /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
149 check for tcp and smb session status done differently
150 for those three - in the calling routine */
Steve French790fe572007-07-07 19:25:05 +0000151 if (tcon) {
152 if (tcon->tidStatus == CifsExiting) {
Steve French6ab16d22005-11-29 20:55:11 -0800153 /* only tree disconnect, open, and write,
154 (and ulogoff which does not have tcon)
155 are allowed as we start force umount */
Steve French790fe572007-07-07 19:25:05 +0000156 if ((smb_command != SMB_COM_WRITE_ANDX) &&
Steve French50c2f752007-07-13 00:33:32 +0000157 (smb_command != SMB_COM_OPEN_ANDX) &&
Steve French6ab16d22005-11-29 20:55:11 -0800158 (smb_command != SMB_COM_TREE_DISCONNECT)) {
Steve French790fe572007-07-07 19:25:05 +0000159 cFYI(1, ("can not send cmd %d while umounting",
Steve French6ab16d22005-11-29 20:55:11 -0800160 smb_command));
161 return -ENODEV;
162 }
163 }
Steve French790fe572007-07-07 19:25:05 +0000164 if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
Steve French50c2f752007-07-13 00:33:32 +0000165 (tcon->ses->server)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 struct nls_table *nls_codepage;
Steve French50c2f752007-07-13 00:33:32 +0000167 /* Give Demultiplex thread up to 10 seconds to
Steve French09d1db52005-04-28 22:41:08 -0700168 reconnect, should be greater than cifs socket
169 timeout which is 7 seconds */
Steve Frenchc18c8422007-07-18 23:21:09 +0000170 while (tcon->ses->server->tcpStatus ==
Steve French63135e02007-07-17 17:34:02 +0000171 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 wait_event_interruptible_timeout(tcon->ses->server->response_q,
Steve Frenchc18c8422007-07-18 23:21:09 +0000173 (tcon->ses->server->tcpStatus ==
Steve French63135e02007-07-17 17:34:02 +0000174 CifsGood), 10 * HZ);
175 if (tcon->ses->server->tcpStatus ==
176 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 /* on "soft" mounts we wait once */
Steve French4b18f2a2008-04-29 00:06:05 +0000178 if (!tcon->retry ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 (tcon->ses->status == CifsExiting)) {
Steve French50c2f752007-07-13 00:33:32 +0000180 cFYI(1, ("gave up waiting on "
181 "reconnect in smb_init"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 return -EHOSTDOWN;
Steve French09d1db52005-04-28 22:41:08 -0700183 } /* else "hard" mount - keep retrying
184 until process is killed or server
185 comes back on-line */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 } else /* TCP session is reestablished now */
187 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 }
Steve French50c2f752007-07-13 00:33:32 +0000189
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 nls_codepage = load_nls_default();
191 /* need to prevent multiple threads trying to
192 simultaneously reconnect the same SMB session */
193 down(&tcon->ses->sesSem);
Steve French790fe572007-07-07 19:25:05 +0000194 if (tcon->ses->status == CifsNeedReconnect)
Steve French50c2f752007-07-13 00:33:32 +0000195 rc = cifs_setup_session(0, tcon->ses,
Steve French09d1db52005-04-28 22:41:08 -0700196 nls_codepage);
Steve French790fe572007-07-07 19:25:05 +0000197 if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 mark_open_files_invalid(tcon);
Steve French50c2f752007-07-13 00:33:32 +0000199 rc = CIFSTCon(0, tcon->ses, tcon->treeName,
Steve French8af18972007-02-14 04:42:51 +0000200 tcon, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 up(&tcon->ses->sesSem);
Steve French3e844692005-10-03 13:37:24 -0700202 /* BB FIXME add code to check if wsize needs
203 update due to negotiated smb buffer size
204 shrinking */
Steve French35028d72008-04-09 20:32:42 +0000205 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 atomic_inc(&tconInfoReconnectCount);
Steve French35028d72008-04-09 20:32:42 +0000207 /* tell server Unix caps we support */
208 if (tcon->ses->capabilities & CAP_UNIX)
209 reset_cifs_unix_caps(
210 0 /* no xid */,
211 tcon,
212 NULL /* we do not know sb */,
213 NULL /* no vol info */);
214 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215
216 cFYI(1, ("reconnect tcon rc = %d", rc));
Steve French50c2f752007-07-13 00:33:32 +0000217 /* Removed call to reopen open files here.
218 It is safer (and faster) to reopen files
Steve French09d1db52005-04-28 22:41:08 -0700219 one at a time as needed in read and write */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
Steve French50c2f752007-07-13 00:33:32 +0000221 /* Check if handle based operation so we
Steve French09d1db52005-04-28 22:41:08 -0700222 know whether we can continue or not without
223 returning to caller to reset file handle */
Steve French50c2f752007-07-13 00:33:32 +0000224 switch (smb_command) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 case SMB_COM_READ_ANDX:
226 case SMB_COM_WRITE_ANDX:
227 case SMB_COM_CLOSE:
228 case SMB_COM_FIND_CLOSE2:
229 case SMB_COM_LOCKING_ANDX: {
230 unload_nls(nls_codepage);
231 return -EAGAIN;
232 }
233 }
234 } else {
235 up(&tcon->ses->sesSem);
236 }
237 unload_nls(nls_codepage);
238
239 } else {
240 return -EIO;
241 }
242 }
Steve French790fe572007-07-07 19:25:05 +0000243 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 return rc;
245
246 *request_buf = cifs_small_buf_get();
247 if (*request_buf == NULL) {
248 /* BB should we add a retry in here if not a writepage? */
249 return -ENOMEM;
250 }
251
Steve French63135e02007-07-17 17:34:02 +0000252 header_assemble((struct smb_hdr *) *request_buf, smb_command,
Steve Frenchc18c8422007-07-18 23:21:09 +0000253 tcon, wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
Steve French790fe572007-07-07 19:25:05 +0000255 if (tcon != NULL)
256 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700257
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 return rc;
Steve French5815449d2006-02-14 01:36:20 +0000259}
260
Steve French12b3b8f2006-02-09 21:12:47 +0000261int
Steve French50c2f752007-07-13 00:33:32 +0000262small_smb_init_no_tc(const int smb_command, const int wct,
Steve French5815449d2006-02-14 01:36:20 +0000263 struct cifsSesInfo *ses, void **request_buf)
Steve French12b3b8f2006-02-09 21:12:47 +0000264{
265 int rc;
Steve French50c2f752007-07-13 00:33:32 +0000266 struct smb_hdr *buffer;
Steve French12b3b8f2006-02-09 21:12:47 +0000267
Steve French5815449d2006-02-14 01:36:20 +0000268 rc = small_smb_init(smb_command, wct, NULL, request_buf);
Steve French790fe572007-07-07 19:25:05 +0000269 if (rc)
Steve French12b3b8f2006-02-09 21:12:47 +0000270 return rc;
271
Steve French04fdabe2006-02-10 05:52:50 +0000272 buffer = (struct smb_hdr *)*request_buf;
Steve French12b3b8f2006-02-09 21:12:47 +0000273 buffer->Mid = GetNextMid(ses->server);
274 if (ses->capabilities & CAP_UNICODE)
275 buffer->Flags2 |= SMBFLG2_UNICODE;
Steve French04fdabe2006-02-10 05:52:50 +0000276 if (ses->capabilities & CAP_STATUS32)
Steve French12b3b8f2006-02-09 21:12:47 +0000277 buffer->Flags2 |= SMBFLG2_ERR_STATUS;
278
279 /* uid, tid can stay at zero as set in header assemble */
280
Steve French50c2f752007-07-13 00:33:32 +0000281 /* BB add support for turning on the signing when
Steve French12b3b8f2006-02-09 21:12:47 +0000282 this function is used after 1st of session setup requests */
283
284 return rc;
285}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
287/* If the return code is zero, this function must fill in request_buf pointer */
288static int
289smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
290 void **request_buf /* returned */ ,
291 void **response_buf /* returned */ )
292{
293 int rc = 0;
294
295 /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
296 check for tcp and smb session status done differently
297 for those three - in the calling routine */
Steve French790fe572007-07-07 19:25:05 +0000298 if (tcon) {
299 if (tcon->tidStatus == CifsExiting) {
Steve French6ab16d22005-11-29 20:55:11 -0800300 /* only tree disconnect, open, and write,
301 (and ulogoff which does not have tcon)
302 are allowed as we start force umount */
Steve French790fe572007-07-07 19:25:05 +0000303 if ((smb_command != SMB_COM_WRITE_ANDX) &&
Steve French6ab16d22005-11-29 20:55:11 -0800304 (smb_command != SMB_COM_OPEN_ANDX) &&
305 (smb_command != SMB_COM_TREE_DISCONNECT)) {
Steve French790fe572007-07-07 19:25:05 +0000306 cFYI(1, ("can not send cmd %d while umounting",
Steve French6ab16d22005-11-29 20:55:11 -0800307 smb_command));
308 return -ENODEV;
309 }
310 }
311
Steve French790fe572007-07-07 19:25:05 +0000312 if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
Steve French50c2f752007-07-13 00:33:32 +0000313 (tcon->ses->server)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 struct nls_table *nls_codepage;
Steve French09d1db52005-04-28 22:41:08 -0700315 /* Give Demultiplex thread up to 10 seconds to
316 reconnect, should be greater than cifs socket
317 timeout which is 7 seconds */
Steve French63135e02007-07-17 17:34:02 +0000318 while (tcon->ses->server->tcpStatus ==
319 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 wait_event_interruptible_timeout(tcon->ses->server->response_q,
Steve French63135e02007-07-17 17:34:02 +0000321 (tcon->ses->server->tcpStatus ==
322 CifsGood), 10 * HZ);
Steve French790fe572007-07-07 19:25:05 +0000323 if (tcon->ses->server->tcpStatus ==
Steve French09d1db52005-04-28 22:41:08 -0700324 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 /* on "soft" mounts we wait once */
Steve French4b18f2a2008-04-29 00:06:05 +0000326 if (!tcon->retry ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 (tcon->ses->status == CifsExiting)) {
Steve French50c2f752007-07-13 00:33:32 +0000328 cFYI(1, ("gave up waiting on "
329 "reconnect in smb_init"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 return -EHOSTDOWN;
Steve French09d1db52005-04-28 22:41:08 -0700331 } /* else "hard" mount - keep retrying
332 until process is killed or server
333 comes on-line */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 } else /* TCP session is reestablished now */
335 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 nls_codepage = load_nls_default();
338 /* need to prevent multiple threads trying to
339 simultaneously reconnect the same SMB session */
340 down(&tcon->ses->sesSem);
Steve French790fe572007-07-07 19:25:05 +0000341 if (tcon->ses->status == CifsNeedReconnect)
Steve French50c2f752007-07-13 00:33:32 +0000342 rc = cifs_setup_session(0, tcon->ses,
Steve French09d1db52005-04-28 22:41:08 -0700343 nls_codepage);
Steve French790fe572007-07-07 19:25:05 +0000344 if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 mark_open_files_invalid(tcon);
Steve French09d1db52005-04-28 22:41:08 -0700346 rc = CIFSTCon(0, tcon->ses, tcon->treeName,
347 tcon, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 up(&tcon->ses->sesSem);
Steve French3e844692005-10-03 13:37:24 -0700349 /* BB FIXME add code to check if wsize needs
350 update due to negotiated smb buffer size
351 shrinking */
Steve French35028d72008-04-09 20:32:42 +0000352 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 atomic_inc(&tconInfoReconnectCount);
Steve French35028d72008-04-09 20:32:42 +0000354 /* tell server Unix caps we support */
355 if (tcon->ses->capabilities & CAP_UNIX)
356 reset_cifs_unix_caps(
357 0 /* no xid */,
358 tcon,
359 NULL /* do not know sb */,
360 NULL /* no vol info */);
361 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
363 cFYI(1, ("reconnect tcon rc = %d", rc));
Steve French50c2f752007-07-13 00:33:32 +0000364 /* Removed call to reopen open files here.
365 It is safer (and faster) to reopen files
Steve French09d1db52005-04-28 22:41:08 -0700366 one at a time as needed in read and write */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367
Steve French50c2f752007-07-13 00:33:32 +0000368 /* Check if handle based operation so we
Steve French09d1db52005-04-28 22:41:08 -0700369 know whether we can continue or not without
370 returning to caller to reset file handle */
Steve French50c2f752007-07-13 00:33:32 +0000371 switch (smb_command) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 case SMB_COM_READ_ANDX:
373 case SMB_COM_WRITE_ANDX:
374 case SMB_COM_CLOSE:
375 case SMB_COM_FIND_CLOSE2:
376 case SMB_COM_LOCKING_ANDX: {
377 unload_nls(nls_codepage);
378 return -EAGAIN;
379 }
380 }
381 } else {
382 up(&tcon->ses->sesSem);
383 }
384 unload_nls(nls_codepage);
385
386 } else {
387 return -EIO;
388 }
389 }
Steve French790fe572007-07-07 19:25:05 +0000390 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 return rc;
392
393 *request_buf = cifs_buf_get();
394 if (*request_buf == NULL) {
395 /* BB should we add a retry in here if not a writepage? */
396 return -ENOMEM;
397 }
398 /* Although the original thought was we needed the response buf for */
399 /* potential retries of smb operations it turns out we can determine */
400 /* from the mid flags when the request buffer can be resent without */
401 /* having to use a second distinct buffer for the response */
Steve French790fe572007-07-07 19:25:05 +0000402 if (response_buf)
Steve French50c2f752007-07-13 00:33:32 +0000403 *response_buf = *request_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
405 header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000406 wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
Steve French790fe572007-07-07 19:25:05 +0000408 if (tcon != NULL)
409 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700410
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 return rc;
412}
413
Steve French50c2f752007-07-13 00:33:32 +0000414static int validate_t2(struct smb_t2_rsp *pSMB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415{
416 int rc = -EINVAL;
417 int total_size;
Steve French50c2f752007-07-13 00:33:32 +0000418 char *pBCC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
420 /* check for plausible wct, bcc and t2 data and parm sizes */
421 /* check for parm and data offset going beyond end of smb */
Steve French790fe572007-07-07 19:25:05 +0000422 if (pSMB->hdr.WordCount >= 10) {
423 if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
425 /* check that bcc is at least as big as parms + data */
426 /* check that bcc is less than negotiated smb buffer */
427 total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
Steve French790fe572007-07-07 19:25:05 +0000428 if (total_size < 512) {
Steve Frenchc18c8422007-07-18 23:21:09 +0000429 total_size +=
Steve French63135e02007-07-17 17:34:02 +0000430 le16_to_cpu(pSMB->t2_rsp.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 /* BCC le converted in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +0000432 pBCC = (pSMB->hdr.WordCount * 2) +
Steve French09d1db52005-04-28 22:41:08 -0700433 sizeof(struct smb_hdr) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 (char *)pSMB;
Steve French790fe572007-07-07 19:25:05 +0000435 if ((total_size <= (*(u16 *)pBCC)) &&
Steve French50c2f752007-07-13 00:33:32 +0000436 (total_size <
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
438 return 0;
439 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 }
441 }
442 }
Steve French50c2f752007-07-13 00:33:32 +0000443 cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 sizeof(struct smb_t2_rsp) + 16);
445 return rc;
446}
447int
448CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
449{
450 NEGOTIATE_REQ *pSMB;
451 NEGOTIATE_RSP *pSMBr;
452 int rc = 0;
453 int bytes_returned;
Steve French39798772006-05-31 22:40:51 +0000454 int i;
Steve French50c2f752007-07-13 00:33:32 +0000455 struct TCP_Server_Info *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 u16 count;
Steve French750d1152006-06-27 06:28:30 +0000457 unsigned int secFlags;
Al Viro733f99a2006-10-14 16:48:26 +0100458 u16 dialect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
Steve French790fe572007-07-07 19:25:05 +0000460 if (ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 server = ses->server;
462 else {
463 rc = -EIO;
464 return rc;
465 }
466 rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
467 (void **) &pSMB, (void **) &pSMBr);
468 if (rc)
469 return rc;
Steve French750d1152006-06-27 06:28:30 +0000470
471 /* if any of auth flags (ie not sign or seal) are overriden use them */
Steve French790fe572007-07-07 19:25:05 +0000472 if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
Steve French762e5ab2007-06-28 18:41:42 +0000473 secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */
Steve French750d1152006-06-27 06:28:30 +0000474 else /* if override flags set only sign/seal OR them with global auth */
475 secFlags = extended_security | ses->overrideSecFlg;
476
Steve French762e5ab2007-06-28 18:41:42 +0000477 cFYI(1, ("secFlags 0x%x", secFlags));
Steve Frenchf40c5622006-06-28 00:13:38 +0000478
Steve French1982c342005-08-17 12:38:22 -0700479 pSMB->hdr.Mid = GetNextMid(server);
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000480 pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
Steve Frencha0136892007-10-04 20:05:09 +0000481
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000482 if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
Steve French254e55e2006-06-04 05:53:15 +0000483 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Steve Frencha0136892007-10-04 20:05:09 +0000484 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
485 cFYI(1, ("Kerberos only mechanism, enable extended security"));
486 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
487 }
Steve French50c2f752007-07-13 00:33:32 +0000488
Steve French39798772006-05-31 22:40:51 +0000489 count = 0;
Steve French50c2f752007-07-13 00:33:32 +0000490 for (i = 0; i < CIFS_NUM_PROT; i++) {
Steve French39798772006-05-31 22:40:51 +0000491 strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
492 count += strlen(protocols[i].name) + 1;
493 /* null at end of source and target buffers anyway */
494 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 pSMB->hdr.smb_buf_length += count;
496 pSMB->ByteCount = cpu_to_le16(count);
497
498 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
499 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French50c2f752007-07-13 00:33:32 +0000500 if (rc != 0)
Steve French254e55e2006-06-04 05:53:15 +0000501 goto neg_err_exit;
502
Al Viro733f99a2006-10-14 16:48:26 +0100503 dialect = le16_to_cpu(pSMBr->DialectIndex);
Steve French790fe572007-07-07 19:25:05 +0000504 cFYI(1, ("Dialect: %d", dialect));
Steve French254e55e2006-06-04 05:53:15 +0000505 /* Check wct = 1 error case */
Steve French790fe572007-07-07 19:25:05 +0000506 if ((pSMBr->hdr.WordCount < 13) || (dialect == BAD_PROT)) {
Steve French254e55e2006-06-04 05:53:15 +0000507 /* core returns wct = 1, but we do not ask for core - otherwise
Steve French50c2f752007-07-13 00:33:32 +0000508 small wct just comes when dialect index is -1 indicating we
Steve French254e55e2006-06-04 05:53:15 +0000509 could not negotiate a common dialect */
510 rc = -EOPNOTSUPP;
511 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000512#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French790fe572007-07-07 19:25:05 +0000513 } else if ((pSMBr->hdr.WordCount == 13)
Al Viro733f99a2006-10-14 16:48:26 +0100514 && ((dialect == LANMAN_PROT)
515 || (dialect == LANMAN2_PROT))) {
Steve Frenchb815f1e2006-10-02 05:53:29 +0000516 __s16 tmp;
Steve French50c2f752007-07-13 00:33:32 +0000517 struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
Steve French254e55e2006-06-04 05:53:15 +0000518
Steve French790fe572007-07-07 19:25:05 +0000519 if ((secFlags & CIFSSEC_MAY_LANMAN) ||
Steve French750d1152006-06-27 06:28:30 +0000520 (secFlags & CIFSSEC_MAY_PLNTXT))
Steve French254e55e2006-06-04 05:53:15 +0000521 server->secType = LANMAN;
522 else {
523 cERROR(1, ("mount failed weak security disabled"
524 " in /proc/fs/cifs/SecurityFlags"));
Steve French39798772006-05-31 22:40:51 +0000525 rc = -EOPNOTSUPP;
526 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000527 }
Steve French254e55e2006-06-04 05:53:15 +0000528 server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
529 server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
530 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
Steve French39798772006-05-31 22:40:51 +0000531 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve French254e55e2006-06-04 05:53:15 +0000532 GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
533 /* even though we do not use raw we might as well set this
534 accurately, in case we ever find a need for it */
Steve French790fe572007-07-07 19:25:05 +0000535 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
Steve French254e55e2006-06-04 05:53:15 +0000536 server->maxRw = 0xFF00;
537 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
538 } else {
539 server->maxRw = 0;/* we do not need to use raw anyway */
540 server->capabilities = CAP_MPX_MODE;
541 }
Steve Frenchb815f1e2006-10-02 05:53:29 +0000542 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
Steve French1a70d652006-10-02 05:59:18 +0000543 if (tmp == -1) {
Steve French25ee4a92006-09-30 00:54:23 +0000544 /* OS/2 often does not set timezone therefore
545 * we must use server time to calc time zone.
Steve Frenchb815f1e2006-10-02 05:53:29 +0000546 * Could deviate slightly from the right zone.
547 * Smallest defined timezone difference is 15 minutes
548 * (i.e. Nepal). Rounding up/down is done to match
549 * this requirement.
Steve French25ee4a92006-09-30 00:54:23 +0000550 */
Steve Frenchb815f1e2006-10-02 05:53:29 +0000551 int val, seconds, remain, result;
Steve French25ee4a92006-09-30 00:54:23 +0000552 struct timespec ts, utc;
553 utc = CURRENT_TIME;
554 ts = cnvrtDosUnixTm(le16_to_cpu(rsp->SrvTime.Date),
555 le16_to_cpu(rsp->SrvTime.Time));
Steve French50c2f752007-07-13 00:33:32 +0000556 cFYI(1, ("SrvTime %d sec since 1970 (utc: %d) diff: %d",
557 (int)ts.tv_sec, (int)utc.tv_sec,
Steve French25ee4a92006-09-30 00:54:23 +0000558 (int)(utc.tv_sec - ts.tv_sec)));
Steve Frenchb815f1e2006-10-02 05:53:29 +0000559 val = (int)(utc.tv_sec - ts.tv_sec);
Andre Haupt8594c152007-08-30 20:18:41 +0000560 seconds = abs(val);
Steve French947a5062006-10-02 05:55:25 +0000561 result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
Steve Frenchb815f1e2006-10-02 05:53:29 +0000562 remain = seconds % MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000563 if (remain >= (MIN_TZ_ADJ / 2))
Steve Frenchb815f1e2006-10-02 05:53:29 +0000564 result += MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000565 if (val < 0)
Steve Frenchad7a2922008-02-07 23:25:02 +0000566 result = -result;
Steve Frenchb815f1e2006-10-02 05:53:29 +0000567 server->timeAdj = result;
Steve French25ee4a92006-09-30 00:54:23 +0000568 } else {
Steve Frenchb815f1e2006-10-02 05:53:29 +0000569 server->timeAdj = (int)tmp;
570 server->timeAdj *= 60; /* also in seconds */
Steve French25ee4a92006-09-30 00:54:23 +0000571 }
Steve French790fe572007-07-07 19:25:05 +0000572 cFYI(1, ("server->timeAdj: %d seconds", server->timeAdj));
Steve French25ee4a92006-09-30 00:54:23 +0000573
Steve French39798772006-05-31 22:40:51 +0000574
Steve French254e55e2006-06-04 05:53:15 +0000575 /* BB get server time for time conversions and add
Steve French50c2f752007-07-13 00:33:32 +0000576 code to use it and timezone since this is not UTC */
Steve French39798772006-05-31 22:40:51 +0000577
Steve French50c2f752007-07-13 00:33:32 +0000578 if (rsp->EncryptionKeyLength ==
Steve French25ee4a92006-09-30 00:54:23 +0000579 cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
Steve French254e55e2006-06-04 05:53:15 +0000580 memcpy(server->cryptKey, rsp->EncryptionKey,
581 CIFS_CRYPTO_KEY_SIZE);
582 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
583 rc = -EIO; /* need cryptkey unless plain text */
584 goto neg_err_exit;
585 }
Steve French39798772006-05-31 22:40:51 +0000586
Steve French790fe572007-07-07 19:25:05 +0000587 cFYI(1, ("LANMAN negotiated"));
Steve French254e55e2006-06-04 05:53:15 +0000588 /* we will not end up setting signing flags - as no signing
589 was in LANMAN and server did not return the flags on */
590 goto signing_check;
Steve French7c7b25b2006-06-01 19:20:10 +0000591#else /* weak security disabled */
Steve French790fe572007-07-07 19:25:05 +0000592 } else if (pSMBr->hdr.WordCount == 13) {
Steve French50c2f752007-07-13 00:33:32 +0000593 cERROR(1, ("mount failed, cifs module not built "
Steve French254e55e2006-06-04 05:53:15 +0000594 "with CIFS_WEAK_PW_HASH support"));
Steve French7c7b25b2006-06-01 19:20:10 +0000595 rc = -EOPNOTSUPP;
596#endif /* WEAK_PW_HASH */
Steve French254e55e2006-06-04 05:53:15 +0000597 goto neg_err_exit;
Steve French790fe572007-07-07 19:25:05 +0000598 } else if (pSMBr->hdr.WordCount != 17) {
Steve French254e55e2006-06-04 05:53:15 +0000599 /* unknown wct */
600 rc = -EOPNOTSUPP;
601 goto neg_err_exit;
602 }
603 /* else wct == 17 NTLM */
604 server->secMode = pSMBr->SecurityMode;
Steve French790fe572007-07-07 19:25:05 +0000605 if ((server->secMode & SECMODE_USER) == 0)
606 cFYI(1, ("share mode security"));
Steve French39798772006-05-31 22:40:51 +0000607
Steve French790fe572007-07-07 19:25:05 +0000608 if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000609#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French750d1152006-06-27 06:28:30 +0000610 if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000611#endif /* CIFS_WEAK_PW_HASH */
Steve French50c2f752007-07-13 00:33:32 +0000612 cERROR(1, ("Server requests plain text password"
Steve French254e55e2006-06-04 05:53:15 +0000613 " but client support disabled"));
Steve French9312f672006-06-04 22:21:07 +0000614
Steve French790fe572007-07-07 19:25:05 +0000615 if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
Steve French254e55e2006-06-04 05:53:15 +0000616 server->secType = NTLMv2;
Steve French790fe572007-07-07 19:25:05 +0000617 else if (secFlags & CIFSSEC_MAY_NTLM)
Steve French254e55e2006-06-04 05:53:15 +0000618 server->secType = NTLM;
Steve French790fe572007-07-07 19:25:05 +0000619 else if (secFlags & CIFSSEC_MAY_NTLMV2)
Steve Frenchf40c5622006-06-28 00:13:38 +0000620 server->secType = NTLMv2;
Steve Frencha0136892007-10-04 20:05:09 +0000621 else if (secFlags & CIFSSEC_MAY_KRB5)
622 server->secType = Kerberos;
623 else if (secFlags & CIFSSEC_MAY_LANMAN)
624 server->secType = LANMAN;
625/* #ifdef CONFIG_CIFS_EXPERIMENTAL
626 else if (secFlags & CIFSSEC_MAY_PLNTXT)
627 server->secType = ??
628#endif */
629 else {
630 rc = -EOPNOTSUPP;
631 cERROR(1, ("Invalid security type"));
632 goto neg_err_exit;
633 }
634 /* else ... any others ...? */
Steve French7c7b25b2006-06-01 19:20:10 +0000635
Steve French254e55e2006-06-04 05:53:15 +0000636 /* one byte, so no need to convert this or EncryptionKeyLen from
637 little endian */
638 server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
639 /* probably no need to store and check maxvcs */
640 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve French254e55e2006-06-04 05:53:15 +0000642 server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
Steve French90c81e02008-02-12 20:32:36 +0000643 cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf));
Steve French254e55e2006-06-04 05:53:15 +0000644 GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
645 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
Steve Frenchb815f1e2006-10-02 05:53:29 +0000646 server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
647 server->timeAdj *= 60;
Steve French254e55e2006-06-04 05:53:15 +0000648 if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
649 memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
650 CIFS_CRYPTO_KEY_SIZE);
651 } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
652 && (pSMBr->EncryptionKeyLength == 0)) {
653 /* decode security blob */
654 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
655 rc = -EIO; /* no crypt key only if plain text pwd */
656 goto neg_err_exit;
657 }
658
659 /* BB might be helpful to save off the domain of server here */
660
Steve French50c2f752007-07-13 00:33:32 +0000661 if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
Steve French254e55e2006-06-04 05:53:15 +0000662 (server->capabilities & CAP_EXTENDED_SECURITY)) {
663 count = pSMBr->ByteCount;
Jeff Laytone187e442007-10-16 17:10:44 +0000664 if (count < 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 rc = -EIO;
Jeff Laytone187e442007-10-16 17:10:44 +0000666 goto neg_err_exit;
667 }
668
669 if (server->socketUseCount.counter > 1) {
670 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 }
678 } else
679 memcpy(server->server_GUID,
680 pSMBr->u.extended_response.GUID, 16);
681
682 if (count == 16) {
683 server->secType = RawNTLMSSP;
Steve French254e55e2006-06-04 05:53:15 +0000684 } else {
685 rc = decode_negTokenInit(pSMBr->u.extended_response.
686 SecurityBlob,
687 count - 16,
688 &server->secType);
Steve French790fe572007-07-07 19:25:05 +0000689 if (rc == 1) {
Jeff Laytone5459372007-11-03 05:11:06 +0000690 rc = 0;
Steve French254e55e2006-06-04 05:53:15 +0000691 } else {
692 rc = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 }
Steve French254e55e2006-06-04 05:53:15 +0000695 } else
696 server->capabilities &= ~CAP_EXTENDED_SECURITY;
697
Steve French6344a422006-06-12 04:18:35 +0000698#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French254e55e2006-06-04 05:53:15 +0000699signing_check:
Steve French6344a422006-06-12 04:18:35 +0000700#endif
Steve French762e5ab2007-06-28 18:41:42 +0000701 if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
702 /* MUST_SIGN already includes the MAY_SIGN FLAG
703 so if this is zero it means that signing is disabled */
704 cFYI(1, ("Signing disabled"));
Steve Frenchabb63d62007-10-18 02:58:40 +0000705 if (server->secMode & SECMODE_SIGN_REQUIRED) {
Steve French762e5ab2007-06-28 18:41:42 +0000706 cERROR(1, ("Server requires "
Jeff Layton7111d212007-10-16 16:50:25 +0000707 "packet signing to be enabled in "
708 "/proc/fs/cifs/SecurityFlags."));
Steve Frenchabb63d62007-10-18 02:58:40 +0000709 rc = -EOPNOTSUPP;
710 }
Steve French50c2f752007-07-13 00:33:32 +0000711 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000712 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Steve French762e5ab2007-06-28 18:41:42 +0000713 } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
714 /* signing required */
Jeff38c10a12007-07-06 21:10:07 +0000715 cFYI(1, ("Must sign - secFlags 0x%x", secFlags));
Steve French762e5ab2007-06-28 18:41:42 +0000716 if ((server->secMode &
717 (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
718 cERROR(1,
719 ("signing required but server lacks support"));
Jeff38c10a12007-07-06 21:10:07 +0000720 rc = -EOPNOTSUPP;
Steve French762e5ab2007-06-28 18:41:42 +0000721 } else
722 server->secMode |= SECMODE_SIGN_REQUIRED;
723 } else {
724 /* signing optional ie CIFSSEC_MAY_SIGN */
Steve French790fe572007-07-07 19:25:05 +0000725 if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
Steve French50c2f752007-07-13 00:33:32 +0000726 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000727 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 }
Steve French50c2f752007-07-13 00:33:32 +0000729
730neg_err_exit:
Steve French4a6d87f2005-08-13 08:15:54 -0700731 cifs_buf_release(pSMB);
Steve French254e55e2006-06-04 05:53:15 +0000732
Steve French790fe572007-07-07 19:25:05 +0000733 cFYI(1, ("negprot rc %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 return rc;
735}
736
737int
738CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
739{
740 struct smb_hdr *smb_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
743 cFYI(1, ("In tree disconnect"));
744 /*
745 * If last user of the connection and
746 * connection alive - disconnect it
747 * If this is the last connection on the server session disconnect it
Steve French50c2f752007-07-13 00:33:32 +0000748 * (and inside session disconnect we should check if tcp socket needs
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 * to be freed and kernel thread woken up).
750 */
751 if (tcon)
752 down(&tcon->tconSem);
753 else
754 return -EIO;
755
756 atomic_dec(&tcon->useCount);
757 if (atomic_read(&tcon->useCount) > 0) {
758 up(&tcon->tconSem);
759 return -EBUSY;
760 }
761
Steve French50c2f752007-07-13 00:33:32 +0000762 /* No need to return error on this operation if tid invalidated and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 closed on server already e.g. due to tcp session crashing */
Steve French790fe572007-07-07 19:25:05 +0000764 if (tcon->tidStatus == CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 up(&tcon->tconSem);
Steve French50c2f752007-07-13 00:33:32 +0000766 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 }
768
Steve French790fe572007-07-07 19:25:05 +0000769 if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 up(&tcon->tconSem);
771 return -EIO;
772 }
Steve French50c2f752007-07-13 00:33:32 +0000773 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
Steve French09d1db52005-04-28 22:41:08 -0700774 (void **)&smb_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 if (rc) {
776 up(&tcon->tconSem);
777 return rc;
Steve Frenchcd634992005-04-28 22:41:10 -0700778 }
Steve French133672e2007-11-13 22:41:37 +0000779
780 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 if (rc)
Steve French966ca922005-04-28 22:41:08 -0700782 cFYI(1, ("Tree disconnect failed %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 up(&tcon->tconSem);
785
Steve French50c2f752007-07-13 00:33:32 +0000786 /* No need to return error on this operation if tid invalidated and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 closed on server already e.g. due to tcp session crashing */
788 if (rc == -EAGAIN)
789 rc = 0;
790
791 return rc;
792}
793
794int
795CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
796{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 LOGOFF_ANDX_REQ *pSMB;
798 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800 cFYI(1, ("In SMBLogoff for session disconnect"));
801 if (ses)
802 down(&ses->sesSem);
803 else
804 return -EIO;
805
806 atomic_dec(&ses->inUse);
807 if (atomic_read(&ses->inUse) > 0) {
808 up(&ses->sesSem);
809 return -EBUSY;
810 }
811 rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
812 if (rc) {
813 up(&ses->sesSem);
814 return rc;
815 }
816
Steve French790fe572007-07-07 19:25:05 +0000817 if (ses->server) {
Steve French1982c342005-08-17 12:38:22 -0700818 pSMB->hdr.Mid = GetNextMid(ses->server);
819
Steve French790fe572007-07-07 19:25:05 +0000820 if (ses->server->secMode &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
822 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
823 }
824
825 pSMB->hdr.Uid = ses->Suid;
826
827 pSMB->AndXCommand = 0xFF;
Steve French133672e2007-11-13 22:41:37 +0000828 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 if (ses->server) {
830 atomic_dec(&ses->server->socketUseCount);
831 if (atomic_read(&ses->server->socketUseCount) == 0) {
832 spin_lock(&GlobalMid_Lock);
833 ses->server->tcpStatus = CifsExiting;
834 spin_unlock(&GlobalMid_Lock);
835 rc = -ESHUTDOWN;
836 }
837 }
Steve Frencha59c6582005-08-17 12:12:19 -0700838 up(&ses->sesSem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
840 /* if session dead then we do not need to do ulogoff,
Steve French50c2f752007-07-13 00:33:32 +0000841 since server closed smb session, no sense reporting
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 error */
843 if (rc == -EAGAIN)
844 rc = 0;
845 return rc;
846}
847
848int
Steve French2d785a52007-07-15 01:48:57 +0000849CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
850 __u16 type, const struct nls_table *nls_codepage, int remap)
851{
852 TRANSACTION2_SPI_REQ *pSMB = NULL;
853 TRANSACTION2_SPI_RSP *pSMBr = NULL;
854 struct unlink_psx_rq *pRqD;
855 int name_len;
856 int rc = 0;
857 int bytes_returned = 0;
858 __u16 params, param_offset, offset, byte_count;
859
860 cFYI(1, ("In POSIX delete"));
861PsxDelete:
862 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
863 (void **) &pSMBr);
864 if (rc)
865 return rc;
866
867 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
868 name_len =
869 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
870 PATH_MAX, nls_codepage, remap);
871 name_len++; /* trailing null */
872 name_len *= 2;
873 } else { /* BB add path length overrun check */
874 name_len = strnlen(fileName, PATH_MAX);
875 name_len++; /* trailing null */
876 strncpy(pSMB->FileName, fileName, name_len);
877 }
878
879 params = 6 + name_len;
880 pSMB->MaxParameterCount = cpu_to_le16(2);
881 pSMB->MaxDataCount = 0; /* BB double check this with jra */
882 pSMB->MaxSetupCount = 0;
883 pSMB->Reserved = 0;
884 pSMB->Flags = 0;
885 pSMB->Timeout = 0;
886 pSMB->Reserved2 = 0;
887 param_offset = offsetof(struct smb_com_transaction2_spi_req,
888 InformationLevel) - 4;
889 offset = param_offset + params;
890
891 /* Setup pointer to Request Data (inode type) */
892 pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
893 pRqD->type = cpu_to_le16(type);
894 pSMB->ParameterOffset = cpu_to_le16(param_offset);
895 pSMB->DataOffset = cpu_to_le16(offset);
896 pSMB->SetupCount = 1;
897 pSMB->Reserved3 = 0;
898 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
899 byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
900
901 pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
902 pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
903 pSMB->ParameterCount = cpu_to_le16(params);
904 pSMB->TotalParameterCount = pSMB->ParameterCount;
905 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
906 pSMB->Reserved4 = 0;
907 pSMB->hdr.smb_buf_length += byte_count;
908 pSMB->ByteCount = cpu_to_le16(byte_count);
909 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
910 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +0000911 if (rc)
Steve French2d785a52007-07-15 01:48:57 +0000912 cFYI(1, ("Posix delete returned %d", rc));
Steve French2d785a52007-07-15 01:48:57 +0000913 cifs_buf_release(pSMB);
914
915 cifs_stats_inc(&tcon->num_deletes);
916
917 if (rc == -EAGAIN)
918 goto PsxDelete;
919
920 return rc;
921}
922
923int
Steve French737b7582005-04-28 22:41:06 -0700924CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
925 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926{
927 DELETE_FILE_REQ *pSMB = NULL;
928 DELETE_FILE_RSP *pSMBr = NULL;
929 int rc = 0;
930 int bytes_returned;
931 int name_len;
932
933DelFileRetry:
934 rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
935 (void **) &pSMBr);
936 if (rc)
937 return rc;
938
939 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
940 name_len =
Steve French50c2f752007-07-13 00:33:32 +0000941 cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
Steve French737b7582005-04-28 22:41:06 -0700942 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 name_len++; /* trailing null */
944 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700945 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 name_len = strnlen(fileName, PATH_MAX);
947 name_len++; /* trailing null */
948 strncpy(pSMB->fileName, fileName, name_len);
949 }
950 pSMB->SearchAttributes =
951 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
952 pSMB->BufferFormat = 0x04;
953 pSMB->hdr.smb_buf_length += name_len + 1;
954 pSMB->ByteCount = cpu_to_le16(name_len + 1);
955 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
956 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700957 cifs_stats_inc(&tcon->num_deletes);
Steve Frenchad7a2922008-02-07 23:25:02 +0000958 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 cFYI(1, ("Error in RMFile = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960
961 cifs_buf_release(pSMB);
962 if (rc == -EAGAIN)
963 goto DelFileRetry;
964
965 return rc;
966}
967
968int
Steve French50c2f752007-07-13 00:33:32 +0000969CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
Steve French737b7582005-04-28 22:41:06 -0700970 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971{
972 DELETE_DIRECTORY_REQ *pSMB = NULL;
973 DELETE_DIRECTORY_RSP *pSMBr = NULL;
974 int rc = 0;
975 int bytes_returned;
976 int name_len;
977
978 cFYI(1, ("In CIFSSMBRmDir"));
979RmDirRetry:
980 rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
981 (void **) &pSMBr);
982 if (rc)
983 return rc;
984
985 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French737b7582005-04-28 22:41:06 -0700986 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
987 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 name_len++; /* trailing null */
989 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700990 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 name_len = strnlen(dirName, PATH_MAX);
992 name_len++; /* trailing null */
993 strncpy(pSMB->DirName, dirName, name_len);
994 }
995
996 pSMB->BufferFormat = 0x04;
997 pSMB->hdr.smb_buf_length += name_len + 1;
998 pSMB->ByteCount = cpu_to_le16(name_len + 1);
999 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1000 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001001 cifs_stats_inc(&tcon->num_rmdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +00001002 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 cFYI(1, ("Error in RMDir = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004
1005 cifs_buf_release(pSMB);
1006 if (rc == -EAGAIN)
1007 goto RmDirRetry;
1008 return rc;
1009}
1010
1011int
1012CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07001013 const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014{
1015 int rc = 0;
1016 CREATE_DIRECTORY_REQ *pSMB = NULL;
1017 CREATE_DIRECTORY_RSP *pSMBr = NULL;
1018 int bytes_returned;
1019 int name_len;
1020
1021 cFYI(1, ("In CIFSSMBMkDir"));
1022MkDirRetry:
1023 rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
1024 (void **) &pSMBr);
1025 if (rc)
1026 return rc;
1027
1028 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00001029 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
Steve French737b7582005-04-28 22:41:06 -07001030 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 name_len++; /* trailing null */
1032 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -07001033 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 name_len = strnlen(name, PATH_MAX);
1035 name_len++; /* trailing null */
1036 strncpy(pSMB->DirName, name, name_len);
1037 }
1038
1039 pSMB->BufferFormat = 0x04;
1040 pSMB->hdr.smb_buf_length += name_len + 1;
1041 pSMB->ByteCount = cpu_to_le16(name_len + 1);
1042 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1043 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001044 cifs_stats_inc(&tcon->num_mkdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +00001045 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 cFYI(1, ("Error in Mkdir = %d", rc));
Steve Frencha5a2b482005-08-20 21:42:53 -07001047
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 cifs_buf_release(pSMB);
1049 if (rc == -EAGAIN)
1050 goto MkDirRetry;
1051 return rc;
1052}
1053
Steve French2dd29d32007-04-23 22:07:35 +00001054int
1055CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
Steve Frenchad7a2922008-02-07 23:25:02 +00001056 __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
Steve French50c2f752007-07-13 00:33:32 +00001057 __u32 *pOplock, const char *name,
Steve French2dd29d32007-04-23 22:07:35 +00001058 const struct nls_table *nls_codepage, int remap)
1059{
1060 TRANSACTION2_SPI_REQ *pSMB = NULL;
1061 TRANSACTION2_SPI_RSP *pSMBr = NULL;
1062 int name_len;
1063 int rc = 0;
1064 int bytes_returned = 0;
Steve French2dd29d32007-04-23 22:07:35 +00001065 __u16 params, param_offset, offset, byte_count, count;
Steve Frenchad7a2922008-02-07 23:25:02 +00001066 OPEN_PSX_REQ *pdata;
1067 OPEN_PSX_RSP *psx_rsp;
Steve French2dd29d32007-04-23 22:07:35 +00001068
1069 cFYI(1, ("In POSIX Create"));
1070PsxCreat:
1071 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
1072 (void **) &pSMBr);
1073 if (rc)
1074 return rc;
1075
1076 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1077 name_len =
1078 cifsConvertToUCS((__le16 *) pSMB->FileName, name,
1079 PATH_MAX, nls_codepage, remap);
1080 name_len++; /* trailing null */
1081 name_len *= 2;
1082 } else { /* BB improve the check for buffer overruns BB */
1083 name_len = strnlen(name, PATH_MAX);
1084 name_len++; /* trailing null */
1085 strncpy(pSMB->FileName, name, name_len);
1086 }
1087
1088 params = 6 + name_len;
1089 count = sizeof(OPEN_PSX_REQ);
1090 pSMB->MaxParameterCount = cpu_to_le16(2);
1091 pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
1092 pSMB->MaxSetupCount = 0;
1093 pSMB->Reserved = 0;
1094 pSMB->Flags = 0;
1095 pSMB->Timeout = 0;
1096 pSMB->Reserved2 = 0;
1097 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00001098 InformationLevel) - 4;
Steve French2dd29d32007-04-23 22:07:35 +00001099 offset = param_offset + params;
Steve French2dd29d32007-04-23 22:07:35 +00001100 pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001101 pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
Steve French2dd29d32007-04-23 22:07:35 +00001102 pdata->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00001103 pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
Steve French2dd29d32007-04-23 22:07:35 +00001104 pdata->OpenFlags = cpu_to_le32(*pOplock);
1105 pSMB->ParameterOffset = cpu_to_le16(param_offset);
1106 pSMB->DataOffset = cpu_to_le16(offset);
1107 pSMB->SetupCount = 1;
1108 pSMB->Reserved3 = 0;
1109 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
1110 byte_count = 3 /* pad */ + params + count;
1111
1112 pSMB->DataCount = cpu_to_le16(count);
1113 pSMB->ParameterCount = cpu_to_le16(params);
1114 pSMB->TotalDataCount = pSMB->DataCount;
1115 pSMB->TotalParameterCount = pSMB->ParameterCount;
1116 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1117 pSMB->Reserved4 = 0;
Steve French50c2f752007-07-13 00:33:32 +00001118 pSMB->hdr.smb_buf_length += byte_count;
Steve French2dd29d32007-04-23 22:07:35 +00001119 pSMB->ByteCount = cpu_to_le16(byte_count);
1120 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1121 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1122 if (rc) {
1123 cFYI(1, ("Posix create returned %d", rc));
1124 goto psx_create_err;
1125 }
1126
Steve French790fe572007-07-07 19:25:05 +00001127 cFYI(1, ("copying inode info"));
Steve French2dd29d32007-04-23 22:07:35 +00001128 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1129
1130 if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
1131 rc = -EIO; /* bad smb */
1132 goto psx_create_err;
1133 }
1134
1135 /* copy return information to pRetData */
Steve French50c2f752007-07-13 00:33:32 +00001136 psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
Steve French2dd29d32007-04-23 22:07:35 +00001137 + le16_to_cpu(pSMBr->t2.DataOffset));
Steve French50c2f752007-07-13 00:33:32 +00001138
Steve French2dd29d32007-04-23 22:07:35 +00001139 *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
Steve French790fe572007-07-07 19:25:05 +00001140 if (netfid)
Steve French2dd29d32007-04-23 22:07:35 +00001141 *netfid = psx_rsp->Fid; /* cifs fid stays in le */
1142 /* Let caller know file was created so we can set the mode. */
1143 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001144 if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
Steve French2dd29d32007-04-23 22:07:35 +00001145 *pOplock |= CIFS_CREATE_ACTION;
1146 /* check to make sure response data is there */
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001147 if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1148 pRetData->Type = cpu_to_le32(-1); /* unknown */
Steve French90c81e02008-02-12 20:32:36 +00001149 cFYI(DBG2, ("unknown type"));
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001150 } else {
Steve French790fe572007-07-07 19:25:05 +00001151 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001152 + sizeof(FILE_UNIX_BASIC_INFO)) {
Steve French50c2f752007-07-13 00:33:32 +00001153 cERROR(1, ("Open response data too small"));
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001154 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001155 goto psx_create_err;
1156 }
Steve French50c2f752007-07-13 00:33:32 +00001157 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001158 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001159 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001160 }
Steve French2dd29d32007-04-23 22:07:35 +00001161
1162psx_create_err:
1163 cifs_buf_release(pSMB);
1164
1165 cifs_stats_inc(&tcon->num_mkdirs);
1166
1167 if (rc == -EAGAIN)
1168 goto PsxCreat;
1169
Steve French50c2f752007-07-13 00:33:32 +00001170 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001171}
1172
Steve Frencha9d02ad2005-08-24 23:06:05 -07001173static __u16 convert_disposition(int disposition)
1174{
1175 __u16 ofun = 0;
1176
1177 switch (disposition) {
1178 case FILE_SUPERSEDE:
1179 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1180 break;
1181 case FILE_OPEN:
1182 ofun = SMBOPEN_OAPPEND;
1183 break;
1184 case FILE_CREATE:
1185 ofun = SMBOPEN_OCREATE;
1186 break;
1187 case FILE_OPEN_IF:
1188 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1189 break;
1190 case FILE_OVERWRITE:
1191 ofun = SMBOPEN_OTRUNC;
1192 break;
1193 case FILE_OVERWRITE_IF:
1194 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1195 break;
1196 default:
Steve French790fe572007-07-07 19:25:05 +00001197 cFYI(1, ("unknown disposition %d", disposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001198 ofun = SMBOPEN_OAPPEND; /* regular open */
1199 }
1200 return ofun;
1201}
1202
Jeff Layton35fc37d2008-05-14 10:22:03 -07001203static int
1204access_flags_to_smbopen_mode(const int access_flags)
1205{
1206 int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
1207
1208 if (masked_flags == GENERIC_READ)
1209 return SMBOPEN_READ;
1210 else if (masked_flags == GENERIC_WRITE)
1211 return SMBOPEN_WRITE;
1212
1213 /* just go for read/write */
1214 return SMBOPEN_READWRITE;
1215}
1216
Steve Frencha9d02ad2005-08-24 23:06:05 -07001217int
1218SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1219 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001220 const int access_flags, const int create_options, __u16 *netfid,
1221 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001222 const struct nls_table *nls_codepage, int remap)
1223{
1224 int rc = -EACCES;
1225 OPENX_REQ *pSMB = NULL;
1226 OPENX_RSP *pSMBr = NULL;
1227 int bytes_returned;
1228 int name_len;
1229 __u16 count;
1230
1231OldOpenRetry:
1232 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1233 (void **) &pSMBr);
1234 if (rc)
1235 return rc;
1236
1237 pSMB->AndXCommand = 0xFF; /* none */
1238
1239 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1240 count = 1; /* account for one byte pad to word boundary */
1241 name_len =
1242 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1243 fileName, PATH_MAX, nls_codepage, remap);
1244 name_len++; /* trailing null */
1245 name_len *= 2;
1246 } else { /* BB improve check for buffer overruns BB */
1247 count = 0; /* no pad */
1248 name_len = strnlen(fileName, PATH_MAX);
1249 name_len++; /* trailing null */
1250 strncpy(pSMB->fileName, fileName, name_len);
1251 }
1252 if (*pOplock & REQ_OPLOCK)
1253 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001254 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001255 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001256
Steve Frencha9d02ad2005-08-24 23:06:05 -07001257 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
Jeff Layton35fc37d2008-05-14 10:22:03 -07001258 pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001259 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1260 /* set file as system file if special file such
1261 as fifo and server expecting SFU style and
1262 no Unix extensions */
1263
Steve French790fe572007-07-07 19:25:05 +00001264 if (create_options & CREATE_OPTION_SPECIAL)
1265 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001266 else /* BB FIXME BB */
1267 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001268
Jeff Layton67750fb2008-05-09 22:28:02 +00001269 if (create_options & CREATE_OPTION_READONLY)
1270 pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001271
1272 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001273/* pSMB->CreateOptions = cpu_to_le32(create_options &
1274 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001275 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001276
1277 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001278 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001279 count += name_len;
1280 pSMB->hdr.smb_buf_length += count;
1281
1282 pSMB->ByteCount = cpu_to_le16(count);
1283 /* long_op set to 1 to allow for oplock break timeouts */
1284 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001285 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001286 cifs_stats_inc(&tcon->num_opens);
1287 if (rc) {
1288 cFYI(1, ("Error in Open = %d", rc));
1289 } else {
1290 /* BB verify if wct == 15 */
1291
Steve French582d21e2008-05-13 04:54:12 +00001292/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
Steve Frencha9d02ad2005-08-24 23:06:05 -07001293
1294 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1295 /* Let caller know file was created so we can set the mode. */
1296 /* Do we care about the CreateAction in any other cases? */
1297 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001298/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001299 *pOplock |= CIFS_CREATE_ACTION; */
1300 /* BB FIXME END */
1301
Steve French790fe572007-07-07 19:25:05 +00001302 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001303 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1304 pfile_info->LastAccessTime = 0; /* BB fixme */
1305 pfile_info->LastWriteTime = 0; /* BB fixme */
1306 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001307 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001308 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001309 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001310 pfile_info->AllocationSize =
1311 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1312 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001313 pfile_info->NumberOfLinks = cpu_to_le32(1);
1314 }
1315 }
1316
1317 cifs_buf_release(pSMB);
1318 if (rc == -EAGAIN)
1319 goto OldOpenRetry;
1320 return rc;
1321}
1322
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323int
1324CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1325 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001326 const int access_flags, const int create_options, __u16 *netfid,
1327 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001328 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329{
1330 int rc = -EACCES;
1331 OPEN_REQ *pSMB = NULL;
1332 OPEN_RSP *pSMBr = NULL;
1333 int bytes_returned;
1334 int name_len;
1335 __u16 count;
1336
1337openRetry:
1338 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1339 (void **) &pSMBr);
1340 if (rc)
1341 return rc;
1342
1343 pSMB->AndXCommand = 0xFF; /* none */
1344
1345 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1346 count = 1; /* account for one byte pad to word boundary */
1347 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001348 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001349 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 name_len++; /* trailing null */
1351 name_len *= 2;
1352 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001353 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 count = 0; /* no pad */
1355 name_len = strnlen(fileName, PATH_MAX);
1356 name_len++; /* trailing null */
1357 pSMB->NameLength = cpu_to_le16(name_len);
1358 strncpy(pSMB->fileName, fileName, name_len);
1359 }
1360 if (*pOplock & REQ_OPLOCK)
1361 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001362 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1365 pSMB->AllocationSize = 0;
Steve Frencheda3c022005-07-21 15:20:28 -07001366 /* set file as system file if special file such
1367 as fifo and server expecting SFU style and
1368 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001369 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c022005-07-21 15:20:28 -07001370 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1371 else
1372 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Jeff Layton67750fb2008-05-09 22:28:02 +00001373
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 /* XP does not handle ATTR_POSIX_SEMANTICS */
1375 /* but it helps speed up case sensitive checks for other
1376 servers such as Samba */
1377 if (tcon->ses->capabilities & CAP_UNIX)
1378 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1379
Jeff Layton67750fb2008-05-09 22:28:02 +00001380 if (create_options & CREATE_OPTION_READONLY)
1381 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);
1382
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1384 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c022005-07-21 15:20:28 -07001385 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001386 /* BB Expirement with various impersonation levels and verify */
1387 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 pSMB->SecurityFlags =
1389 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1390
1391 count += name_len;
1392 pSMB->hdr.smb_buf_length += count;
1393
1394 pSMB->ByteCount = cpu_to_le16(count);
1395 /* long_op set to 1 to allow for oplock break timeouts */
1396 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001397 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha4544342005-08-24 13:59:35 -07001398 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 if (rc) {
1400 cFYI(1, ("Error in Open = %d", rc));
1401 } else {
Steve French09d1db52005-04-28 22:41:08 -07001402 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1404 /* Let caller know file was created so we can set the mode. */
1405 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001406 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001407 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001408 if (pfile_info) {
Steve French50c2f752007-07-13 00:33:32 +00001409 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 36 /* CreationTime to Attributes */);
1411 /* the file_info buf is endian converted by caller */
1412 pfile_info->AllocationSize = pSMBr->AllocationSize;
1413 pfile_info->EndOfFile = pSMBr->EndOfFile;
1414 pfile_info->NumberOfLinks = cpu_to_le32(1);
1415 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001417
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 cifs_buf_release(pSMB);
1419 if (rc == -EAGAIN)
1420 goto openRetry;
1421 return rc;
1422}
1423
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424int
Steve French50c2f752007-07-13 00:33:32 +00001425CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1426 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1427 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428{
1429 int rc = -EACCES;
1430 READ_REQ *pSMB = NULL;
1431 READ_RSP *pSMBr = NULL;
1432 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001433 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001434 int resp_buf_type = 0;
1435 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436
Steve French790fe572007-07-07 19:25:05 +00001437 cFYI(1, ("Reading %d bytes on fid %d", count, netfid));
1438 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001439 wct = 12;
1440 else
1441 wct = 10; /* old style read */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442
1443 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001444 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 if (rc)
1446 return rc;
1447
1448 /* tcon and ses pointer are checked in smb_init */
1449 if (tcon->ses->server == NULL)
1450 return -ECONNABORTED;
1451
Steve Frenchec637e32005-12-12 20:53:18 -08001452 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 pSMB->Fid = netfid;
1454 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001455 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001456 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve French790fe572007-07-07 19:25:05 +00001457 else if ((lseek >> 32) > 0) /* can not handle this big offset for old */
Steve Frenchec637e32005-12-12 20:53:18 -08001458 return -EIO;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001459
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 pSMB->Remaining = 0;
1461 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1462 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001463 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001464 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1465 else {
1466 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001467 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001468 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001469 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001470 }
Steve Frenchec637e32005-12-12 20:53:18 -08001471
1472 iov[0].iov_base = (char *)pSMB;
1473 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001474 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Steve French133672e2007-11-13 22:41:37 +00001475 &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001476 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001477 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 if (rc) {
1479 cERROR(1, ("Send error in read = %d", rc));
1480 } else {
1481 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1482 data_length = data_length << 16;
1483 data_length += le16_to_cpu(pSMBr->DataLength);
1484 *nbytes = data_length;
1485
1486 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001487 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 || (data_length > count)) {
Steve French50c2f752007-07-13 00:33:32 +00001489 cFYI(1, ("bad length %d for count %d",
1490 data_length, count));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 rc = -EIO;
1492 *nbytes = 0;
1493 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001494 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001495 le16_to_cpu(pSMBr->DataOffset);
1496/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Steve French50c2f752007-07-13 00:33:32 +00001497 cERROR(1,("Faulting on read rc = %d",rc));
1498 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001499 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001500 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001501 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 }
1503 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
Steve French4b8f9302006-02-26 16:41:18 +00001505/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001506 if (*buf) {
1507 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001508 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001509 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001510 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001511 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001512 /* return buffer to caller to free */
1513 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001514 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001515 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001516 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001517 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001518 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001519
1520 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521 since file handle passed in no longer valid */
1522 return rc;
1523}
1524
Steve Frenchec637e32005-12-12 20:53:18 -08001525
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526int
1527CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1528 const int netfid, const unsigned int count,
1529 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001530 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531{
1532 int rc = -EACCES;
1533 WRITE_REQ *pSMB = NULL;
1534 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001535 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 __u32 bytes_sent;
1537 __u16 byte_count;
1538
1539 /* cFYI(1,("write at %lld %d bytes",offset,count));*/
Steve French790fe572007-07-07 19:25:05 +00001540 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001541 return -ECONNABORTED;
1542
Steve French790fe572007-07-07 19:25:05 +00001543 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001544 wct = 14;
1545 else
1546 wct = 12;
1547
1548 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 (void **) &pSMBr);
1550 if (rc)
1551 return rc;
1552 /* tcon and ses pointer are checked in smb_init */
1553 if (tcon->ses->server == NULL)
1554 return -ECONNABORTED;
1555
1556 pSMB->AndXCommand = 0xFF; /* none */
1557 pSMB->Fid = netfid;
1558 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001559 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001560 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French790fe572007-07-07 19:25:05 +00001561 else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
Steve French1c955182005-08-30 20:58:07 -07001562 return -EIO;
Steve French50c2f752007-07-13 00:33:32 +00001563
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 pSMB->Reserved = 0xFFFFFFFF;
1565 pSMB->WriteMode = 0;
1566 pSMB->Remaining = 0;
1567
Steve French50c2f752007-07-13 00:33:32 +00001568 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 can send more if LARGE_WRITE_X capability returned by the server and if
1570 our buffer is big enough or if we convert to iovecs on socket writes
1571 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001572 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1574 } else {
1575 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1576 & ~0xFF;
1577 }
1578
1579 if (bytes_sent > count)
1580 bytes_sent = count;
1581 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001582 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001583 if (buf)
Steve French50c2f752007-07-13 00:33:32 +00001584 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001585 else if (ubuf) {
1586 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 cifs_buf_release(pSMB);
1588 return -EFAULT;
1589 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001590 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 /* No buffer */
1592 cifs_buf_release(pSMB);
1593 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001594 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001595 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001596 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001597 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001598 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001599
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1601 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001602 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001603
Steve French790fe572007-07-07 19:25:05 +00001604 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001605 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001606 else { /* old style write has byte count 4 bytes earlier
1607 so 4 bytes pad */
1608 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001609 (struct smb_com_writex_req *)pSMB;
1610 pSMBW->ByteCount = cpu_to_le16(byte_count);
1611 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612
1613 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1614 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001615 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 if (rc) {
1617 cFYI(1, ("Send error in write = %d", rc));
1618 *nbytes = 0;
1619 } else {
1620 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1621 *nbytes = (*nbytes) << 16;
1622 *nbytes += le16_to_cpu(pSMBr->Count);
1623 }
1624
1625 cifs_buf_release(pSMB);
1626
Steve French50c2f752007-07-13 00:33:32 +00001627 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 since file handle passed in no longer valid */
1629
1630 return rc;
1631}
1632
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001633int
1634CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001636 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1637 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638{
1639 int rc = -EACCES;
1640 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001641 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001642 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001643 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644
Steve French790fe572007-07-07 19:25:05 +00001645 cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count));
Steve Frenchff7feac2005-11-15 16:45:16 -08001646
Steve French790fe572007-07-07 19:25:05 +00001647 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French8cc64c62005-10-03 13:49:43 -07001648 wct = 14;
1649 else
1650 wct = 12;
1651 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 if (rc)
1653 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 /* tcon and ses pointer are checked in smb_init */
1655 if (tcon->ses->server == NULL)
1656 return -ECONNABORTED;
1657
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001658 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 pSMB->Fid = netfid;
1660 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001661 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001662 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French790fe572007-07-07 19:25:05 +00001663 else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
Steve French8cc64c62005-10-03 13:49:43 -07001664 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 pSMB->Reserved = 0xFFFFFFFF;
1666 pSMB->WriteMode = 0;
1667 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001668
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001670 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671
Steve French3e844692005-10-03 13:37:24 -07001672 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1673 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001674 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001675 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001676 pSMB->hdr.smb_buf_length += count+1;
1677 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001678 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1679 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001680 pSMB->ByteCount = cpu_to_le16(count + 1);
1681 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001682 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001683 (struct smb_com_writex_req *)pSMB;
1684 pSMBW->ByteCount = cpu_to_le16(count + 5);
1685 }
Steve French3e844692005-10-03 13:37:24 -07001686 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001687 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001688 iov[0].iov_len = smb_hdr_len + 4;
1689 else /* wct == 12 pad bigger by four bytes */
1690 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001691
Steve French3e844692005-10-03 13:37:24 -07001692
Steve Frenchec637e32005-12-12 20:53:18 -08001693 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001694 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001695 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 if (rc) {
Steve French8cc64c62005-10-03 13:49:43 -07001697 cFYI(1, ("Send error Write2 = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 *nbytes = 0;
Steve French790fe572007-07-07 19:25:05 +00001699 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001700 /* presumably this can not happen, but best to be safe */
1701 rc = -EIO;
1702 *nbytes = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001703 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001704 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001705 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1706 *nbytes = (*nbytes) << 16;
1707 *nbytes += le16_to_cpu(pSMBr->Count);
Steve French50c2f752007-07-13 00:33:32 +00001708 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709
Steve French4b8f9302006-02-26 16:41:18 +00001710/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001711 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001712 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001713 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001714 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715
Steve French50c2f752007-07-13 00:33:32 +00001716 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 since file handle passed in no longer valid */
1718
1719 return rc;
1720}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001721
1722
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723int
1724CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1725 const __u16 smb_file_id, const __u64 len,
1726 const __u64 offset, const __u32 numUnlock,
Steve French4b18f2a2008-04-29 00:06:05 +00001727 const __u32 numLock, const __u8 lockType, const bool waitFlag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728{
1729 int rc = 0;
1730 LOCK_REQ *pSMB = NULL;
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001731/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 int bytes_returned;
1733 int timeout = 0;
1734 __u16 count;
1735
Steve French4b18f2a2008-04-29 00:06:05 +00001736 cFYI(1, ("CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock));
Steve French46810cb2005-04-28 22:41:09 -07001737 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1738
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 if (rc)
1740 return rc;
1741
Steve French790fe572007-07-07 19:25:05 +00001742 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001743 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 pSMB->Timeout = 0;
Steve French4b18f2a2008-04-29 00:06:05 +00001745 } else if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001746 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1748 } else {
1749 pSMB->Timeout = 0;
1750 }
1751
1752 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1753 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1754 pSMB->LockType = lockType;
1755 pSMB->AndXCommand = 0xFF; /* none */
1756 pSMB->Fid = smb_file_id; /* netfid stays le */
1757
Steve French790fe572007-07-07 19:25:05 +00001758 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1760 /* BB where to store pid high? */
1761 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1762 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1763 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1764 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1765 count = sizeof(LOCKING_ANDX_RANGE);
1766 } else {
1767 /* oplock break */
1768 count = 0;
1769 }
1770 pSMB->hdr.smb_buf_length += count;
1771 pSMB->ByteCount = cpu_to_le16(count);
1772
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001773 if (waitFlag) {
1774 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001775 (struct smb_hdr *) pSMB, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001776 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001777 } else {
Steve French133672e2007-11-13 22:41:37 +00001778 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1779 timeout);
1780 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001781 }
Steve Frencha4544342005-08-24 13:59:35 -07001782 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001783 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 cFYI(1, ("Send error in Lock = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785
Steve French50c2f752007-07-13 00:33:32 +00001786 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787 since file handle passed in no longer valid */
1788 return rc;
1789}
1790
1791int
Steve French08547b02006-02-28 22:39:25 +00001792CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1793 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001794 struct file_lock *pLockData, const __u16 lock_type,
Steve French4b18f2a2008-04-29 00:06:05 +00001795 const bool waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001796{
1797 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1798 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001799 struct cifs_posix_lock *parm_data;
1800 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001801 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001802 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001803 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001804 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001805 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001806
1807 cFYI(1, ("Posix Lock"));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001808
Steve French790fe572007-07-07 19:25:05 +00001809 if (pLockData == NULL)
Marcin Slusarzed5f0372008-05-13 04:01:01 +00001810 return -EINVAL;
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001811
Steve French08547b02006-02-28 22:39:25 +00001812 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1813
1814 if (rc)
1815 return rc;
1816
1817 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1818
Steve French50c2f752007-07-13 00:33:32 +00001819 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001820 pSMB->MaxSetupCount = 0;
1821 pSMB->Reserved = 0;
1822 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001823 pSMB->Reserved2 = 0;
1824 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1825 offset = param_offset + params;
1826
Steve French08547b02006-02-28 22:39:25 +00001827 count = sizeof(struct cifs_posix_lock);
1828 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001829 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001830 pSMB->SetupCount = 1;
1831 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001832 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001833 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1834 else
1835 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1836 byte_count = 3 /* pad */ + params + count;
1837 pSMB->DataCount = cpu_to_le16(count);
1838 pSMB->ParameterCount = cpu_to_le16(params);
1839 pSMB->TotalDataCount = pSMB->DataCount;
1840 pSMB->TotalParameterCount = pSMB->ParameterCount;
1841 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001842 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001843 (((char *) &pSMB->hdr.Protocol) + offset);
1844
1845 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001846 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001847 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001848 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001849 pSMB->Timeout = cpu_to_le32(-1);
1850 } else
1851 pSMB->Timeout = 0;
1852
Steve French08547b02006-02-28 22:39:25 +00001853 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001854 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001855 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001856
1857 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001858 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001859 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1860 pSMB->Reserved4 = 0;
1861 pSMB->hdr.smb_buf_length += byte_count;
1862 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001863 if (waitFlag) {
1864 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1865 (struct smb_hdr *) pSMBr, &bytes_returned);
1866 } else {
Steve French133672e2007-11-13 22:41:37 +00001867 iov[0].iov_base = (char *)pSMB;
1868 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1869 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1870 &resp_buf_type, timeout);
1871 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1872 not try to free it twice below on exit */
1873 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001874 }
1875
Steve French08547b02006-02-28 22:39:25 +00001876 if (rc) {
1877 cFYI(1, ("Send error in Posix Lock = %d", rc));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001878 } else if (get_flag) {
1879 /* lock structure can be returned on get */
1880 __u16 data_offset;
1881 __u16 data_count;
1882 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001883
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001884 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1885 rc = -EIO; /* bad smb */
1886 goto plk_err_exit;
1887 }
Steve French790fe572007-07-07 19:25:05 +00001888 if (pLockData == NULL) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001889 rc = -EINVAL;
1890 goto plk_err_exit;
1891 }
1892 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1893 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001894 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001895 rc = -EIO;
1896 goto plk_err_exit;
1897 }
1898 parm_data = (struct cifs_posix_lock *)
1899 ((char *)&pSMBr->hdr.Protocol + data_offset);
Steve French790fe572007-07-07 19:25:05 +00001900 if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001901 pLockData->fl_type = F_UNLCK;
1902 }
Steve French50c2f752007-07-13 00:33:32 +00001903
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001904plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001905 if (pSMB)
1906 cifs_small_buf_release(pSMB);
1907
Steve French133672e2007-11-13 22:41:37 +00001908 if (resp_buf_type == CIFS_SMALL_BUFFER)
1909 cifs_small_buf_release(iov[0].iov_base);
1910 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1911 cifs_buf_release(iov[0].iov_base);
1912
Steve French08547b02006-02-28 22:39:25 +00001913 /* Note: On -EAGAIN error only caller can retry on handle based calls
1914 since file handle passed in no longer valid */
1915
1916 return rc;
1917}
1918
1919
1920int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1922{
1923 int rc = 0;
1924 CLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925 cFYI(1, ("In CIFSSMBClose"));
1926
1927/* do not retry on dead session on close */
1928 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001929 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930 return 0;
1931 if (rc)
1932 return rc;
1933
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e2006-10-02 05:53:29 +00001935 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001937 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001938 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001940 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 /* EINTR is expected when user ctl-c to kill app */
1942 cERROR(1, ("Send error in Close = %d", rc));
1943 }
1944 }
1945
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001947 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948 rc = 0;
1949
1950 return rc;
1951}
1952
1953int
1954CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1955 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001956 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957{
1958 int rc = 0;
1959 RENAME_REQ *pSMB = NULL;
1960 RENAME_RSP *pSMBr = NULL;
1961 int bytes_returned;
1962 int name_len, name_len2;
1963 __u16 count;
1964
1965 cFYI(1, ("In CIFSSMBRename"));
1966renameRetry:
1967 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1968 (void **) &pSMBr);
1969 if (rc)
1970 return rc;
1971
1972 pSMB->BufferFormat = 0x04;
1973 pSMB->SearchAttributes =
1974 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1975 ATTR_DIRECTORY);
1976
1977 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1978 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001979 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001980 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981 name_len++; /* trailing null */
1982 name_len *= 2;
1983 pSMB->OldFileName[name_len] = 0x04; /* pad */
1984 /* protocol requires ASCII signature byte on Unicode string */
1985 pSMB->OldFileName[name_len + 1] = 0x00;
1986 name_len2 =
Steve French582d21e2008-05-13 04:54:12 +00001987 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07001988 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
1990 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00001991 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 name_len = strnlen(fromName, PATH_MAX);
1993 name_len++; /* trailing null */
1994 strncpy(pSMB->OldFileName, fromName, name_len);
1995 name_len2 = strnlen(toName, PATH_MAX);
1996 name_len2++; /* trailing null */
1997 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
1998 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
1999 name_len2++; /* trailing null */
2000 name_len2++; /* signature byte */
2001 }
2002
2003 count = 1 /* 1st signature byte */ + name_len + name_len2;
2004 pSMB->hdr.smb_buf_length += count;
2005 pSMB->ByteCount = cpu_to_le16(count);
2006
2007 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2008 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002009 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002010 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 cFYI(1, ("Send error in rename = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 cifs_buf_release(pSMB);
2014
2015 if (rc == -EAGAIN)
2016 goto renameRetry;
2017
2018 return rc;
2019}
2020
Steve French50c2f752007-07-13 00:33:32 +00002021int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
2022 int netfid, char *target_name,
2023 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024{
2025 struct smb_com_transaction2_sfi_req *pSMB = NULL;
2026 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00002027 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028 char *data_offset;
2029 char dummy_string[30];
2030 int rc = 0;
2031 int bytes_returned = 0;
2032 int len_of_str;
2033 __u16 params, param_offset, offset, count, byte_count;
2034
2035 cFYI(1, ("Rename to File by handle"));
2036 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
2037 (void **) &pSMBr);
2038 if (rc)
2039 return rc;
2040
2041 params = 6;
2042 pSMB->MaxSetupCount = 0;
2043 pSMB->Reserved = 0;
2044 pSMB->Flags = 0;
2045 pSMB->Timeout = 0;
2046 pSMB->Reserved2 = 0;
2047 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2048 offset = param_offset + params;
2049
2050 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2051 rename_info = (struct set_file_rename *) data_offset;
2052 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002053 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 pSMB->SetupCount = 1;
2055 pSMB->Reserved3 = 0;
2056 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2057 byte_count = 3 /* pad */ + params;
2058 pSMB->ParameterCount = cpu_to_le16(params);
2059 pSMB->TotalParameterCount = pSMB->ParameterCount;
2060 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2061 pSMB->DataOffset = cpu_to_le16(offset);
2062 /* construct random name ".cifs_tmp<inodenum><mid>" */
2063 rename_info->overwrite = cpu_to_le32(1);
2064 rename_info->root_fid = 0;
2065 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002066 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002067 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2068 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002069 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002071 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002072 target_name, PATH_MAX, nls_codepage,
2073 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 }
2075 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
2076 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str) + 2;
2077 byte_count += count;
2078 pSMB->DataCount = cpu_to_le16(count);
2079 pSMB->TotalDataCount = pSMB->DataCount;
2080 pSMB->Fid = netfid;
2081 pSMB->InformationLevel =
2082 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2083 pSMB->Reserved4 = 0;
2084 pSMB->hdr.smb_buf_length += byte_count;
2085 pSMB->ByteCount = cpu_to_le16(byte_count);
2086 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002087 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002088 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002089 if (rc)
Steve French790fe572007-07-07 19:25:05 +00002090 cFYI(1, ("Send error in Rename (by file handle) = %d", rc));
Steve Frencha5a2b482005-08-20 21:42:53 -07002091
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 cifs_buf_release(pSMB);
2093
2094 /* Note: On -EAGAIN error only caller can retry on handle based calls
2095 since file handle passed in no longer valid */
2096
2097 return rc;
2098}
2099
2100int
Steve French50c2f752007-07-13 00:33:32 +00002101CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2102 const __u16 target_tid, const char *toName, const int flags,
2103 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104{
2105 int rc = 0;
2106 COPY_REQ *pSMB = NULL;
2107 COPY_RSP *pSMBr = NULL;
2108 int bytes_returned;
2109 int name_len, name_len2;
2110 __u16 count;
2111
2112 cFYI(1, ("In CIFSSMBCopy"));
2113copyRetry:
2114 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2115 (void **) &pSMBr);
2116 if (rc)
2117 return rc;
2118
2119 pSMB->BufferFormat = 0x04;
2120 pSMB->Tid2 = target_tid;
2121
2122 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2123
2124 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002125 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002126 fromName, PATH_MAX, nls_codepage,
2127 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 name_len++; /* trailing null */
2129 name_len *= 2;
2130 pSMB->OldFileName[name_len] = 0x04; /* pad */
2131 /* protocol requires ASCII signature byte on Unicode string */
2132 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002133 name_len2 =
2134 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002135 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2137 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002138 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139 name_len = strnlen(fromName, PATH_MAX);
2140 name_len++; /* trailing null */
2141 strncpy(pSMB->OldFileName, fromName, name_len);
2142 name_len2 = strnlen(toName, PATH_MAX);
2143 name_len2++; /* trailing null */
2144 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2145 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2146 name_len2++; /* trailing null */
2147 name_len2++; /* signature byte */
2148 }
2149
2150 count = 1 /* 1st signature byte */ + name_len + name_len2;
2151 pSMB->hdr.smb_buf_length += count;
2152 pSMB->ByteCount = cpu_to_le16(count);
2153
2154 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2155 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2156 if (rc) {
2157 cFYI(1, ("Send error in copy = %d with %d files copied",
2158 rc, le16_to_cpu(pSMBr->CopyCount)));
2159 }
Steve French0d817bc2008-05-22 02:02:03 +00002160 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161
2162 if (rc == -EAGAIN)
2163 goto copyRetry;
2164
2165 return rc;
2166}
2167
2168int
2169CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2170 const char *fromName, const char *toName,
2171 const struct nls_table *nls_codepage)
2172{
2173 TRANSACTION2_SPI_REQ *pSMB = NULL;
2174 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2175 char *data_offset;
2176 int name_len;
2177 int name_len_target;
2178 int rc = 0;
2179 int bytes_returned = 0;
2180 __u16 params, param_offset, offset, byte_count;
2181
2182 cFYI(1, ("In Symlink Unix style"));
2183createSymLinkRetry:
2184 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2185 (void **) &pSMBr);
2186 if (rc)
2187 return rc;
2188
2189 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2190 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002191 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 /* find define for this maxpathcomponent */
2193 , nls_codepage);
2194 name_len++; /* trailing null */
2195 name_len *= 2;
2196
Steve French50c2f752007-07-13 00:33:32 +00002197 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198 name_len = strnlen(fromName, PATH_MAX);
2199 name_len++; /* trailing null */
2200 strncpy(pSMB->FileName, fromName, name_len);
2201 }
2202 params = 6 + name_len;
2203 pSMB->MaxSetupCount = 0;
2204 pSMB->Reserved = 0;
2205 pSMB->Flags = 0;
2206 pSMB->Timeout = 0;
2207 pSMB->Reserved2 = 0;
2208 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002209 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210 offset = param_offset + params;
2211
2212 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2213 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2214 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002215 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002216 /* find define for this maxpathcomponent */
2217 , nls_codepage);
2218 name_len_target++; /* trailing null */
2219 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002220 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 name_len_target = strnlen(toName, PATH_MAX);
2222 name_len_target++; /* trailing null */
2223 strncpy(data_offset, toName, name_len_target);
2224 }
2225
2226 pSMB->MaxParameterCount = cpu_to_le16(2);
2227 /* BB find exact max on data count below from sess */
2228 pSMB->MaxDataCount = cpu_to_le16(1000);
2229 pSMB->SetupCount = 1;
2230 pSMB->Reserved3 = 0;
2231 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2232 byte_count = 3 /* pad */ + params + name_len_target;
2233 pSMB->DataCount = cpu_to_le16(name_len_target);
2234 pSMB->ParameterCount = cpu_to_le16(params);
2235 pSMB->TotalDataCount = pSMB->DataCount;
2236 pSMB->TotalParameterCount = pSMB->ParameterCount;
2237 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2238 pSMB->DataOffset = cpu_to_le16(offset);
2239 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2240 pSMB->Reserved4 = 0;
2241 pSMB->hdr.smb_buf_length += byte_count;
2242 pSMB->ByteCount = cpu_to_le16(byte_count);
2243 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2244 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002245 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002246 if (rc)
Steve French2d785a52007-07-15 01:48:57 +00002247 cFYI(1, ("Send error in SetPathInfo create symlink = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248
Steve French0d817bc2008-05-22 02:02:03 +00002249 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250
2251 if (rc == -EAGAIN)
2252 goto createSymLinkRetry;
2253
2254 return rc;
2255}
2256
2257int
2258CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2259 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002260 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261{
2262 TRANSACTION2_SPI_REQ *pSMB = NULL;
2263 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2264 char *data_offset;
2265 int name_len;
2266 int name_len_target;
2267 int rc = 0;
2268 int bytes_returned = 0;
2269 __u16 params, param_offset, offset, byte_count;
2270
2271 cFYI(1, ("In Create Hard link Unix style"));
2272createHardLinkRetry:
2273 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2274 (void **) &pSMBr);
2275 if (rc)
2276 return rc;
2277
2278 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002279 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002280 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 name_len++; /* trailing null */
2282 name_len *= 2;
2283
Steve French50c2f752007-07-13 00:33:32 +00002284 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285 name_len = strnlen(toName, PATH_MAX);
2286 name_len++; /* trailing null */
2287 strncpy(pSMB->FileName, toName, name_len);
2288 }
2289 params = 6 + name_len;
2290 pSMB->MaxSetupCount = 0;
2291 pSMB->Reserved = 0;
2292 pSMB->Flags = 0;
2293 pSMB->Timeout = 0;
2294 pSMB->Reserved2 = 0;
2295 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002296 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297 offset = param_offset + params;
2298
2299 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2300 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2301 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002302 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002303 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 name_len_target++; /* trailing null */
2305 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002306 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 name_len_target = strnlen(fromName, PATH_MAX);
2308 name_len_target++; /* trailing null */
2309 strncpy(data_offset, fromName, name_len_target);
2310 }
2311
2312 pSMB->MaxParameterCount = cpu_to_le16(2);
2313 /* BB find exact max on data count below from sess*/
2314 pSMB->MaxDataCount = cpu_to_le16(1000);
2315 pSMB->SetupCount = 1;
2316 pSMB->Reserved3 = 0;
2317 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2318 byte_count = 3 /* pad */ + params + name_len_target;
2319 pSMB->ParameterCount = cpu_to_le16(params);
2320 pSMB->TotalParameterCount = pSMB->ParameterCount;
2321 pSMB->DataCount = cpu_to_le16(name_len_target);
2322 pSMB->TotalDataCount = pSMB->DataCount;
2323 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2324 pSMB->DataOffset = cpu_to_le16(offset);
2325 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2326 pSMB->Reserved4 = 0;
2327 pSMB->hdr.smb_buf_length += byte_count;
2328 pSMB->ByteCount = cpu_to_le16(byte_count);
2329 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2330 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002331 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002332 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333 cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334
2335 cifs_buf_release(pSMB);
2336 if (rc == -EAGAIN)
2337 goto createHardLinkRetry;
2338
2339 return rc;
2340}
2341
2342int
2343CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2344 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002345 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346{
2347 int rc = 0;
2348 NT_RENAME_REQ *pSMB = NULL;
2349 RENAME_RSP *pSMBr = NULL;
2350 int bytes_returned;
2351 int name_len, name_len2;
2352 __u16 count;
2353
2354 cFYI(1, ("In CIFSCreateHardLink"));
2355winCreateHardLinkRetry:
2356
2357 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2358 (void **) &pSMBr);
2359 if (rc)
2360 return rc;
2361
2362 pSMB->SearchAttributes =
2363 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2364 ATTR_DIRECTORY);
2365 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2366 pSMB->ClusterCount = 0;
2367
2368 pSMB->BufferFormat = 0x04;
2369
2370 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2371 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002372 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002373 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374 name_len++; /* trailing null */
2375 name_len *= 2;
2376 pSMB->OldFileName[name_len] = 0; /* pad */
Steve French50c2f752007-07-13 00:33:32 +00002377 pSMB->OldFileName[name_len + 1] = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002379 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002380 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2382 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002383 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384 name_len = strnlen(fromName, PATH_MAX);
2385 name_len++; /* trailing null */
2386 strncpy(pSMB->OldFileName, fromName, name_len);
2387 name_len2 = strnlen(toName, PATH_MAX);
2388 name_len2++; /* trailing null */
2389 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2390 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2391 name_len2++; /* trailing null */
2392 name_len2++; /* signature byte */
2393 }
2394
2395 count = 1 /* string type byte */ + name_len + name_len2;
2396 pSMB->hdr.smb_buf_length += count;
2397 pSMB->ByteCount = cpu_to_le16(count);
2398
2399 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2400 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002401 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002402 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403 cFYI(1, ("Send error in hard link (NT rename) = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00002404
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 cifs_buf_release(pSMB);
2406 if (rc == -EAGAIN)
2407 goto winCreateHardLinkRetry;
2408
2409 return rc;
2410}
2411
2412int
2413CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
2414 const unsigned char *searchName,
2415 char *symlinkinfo, const int buflen,
2416 const struct nls_table *nls_codepage)
2417{
2418/* SMB_QUERY_FILE_UNIX_LINK */
2419 TRANSACTION2_QPI_REQ *pSMB = NULL;
2420 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2421 int rc = 0;
2422 int bytes_returned;
2423 int name_len;
2424 __u16 params, byte_count;
2425
2426 cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName));
2427
2428querySymLinkRetry:
2429 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2430 (void **) &pSMBr);
2431 if (rc)
2432 return rc;
2433
2434 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2435 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002436 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2437 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438 name_len++; /* trailing null */
2439 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002440 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441 name_len = strnlen(searchName, PATH_MAX);
2442 name_len++; /* trailing null */
2443 strncpy(pSMB->FileName, searchName, name_len);
2444 }
2445
2446 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2447 pSMB->TotalDataCount = 0;
2448 pSMB->MaxParameterCount = cpu_to_le16(2);
2449 /* BB find exact max data count below from sess structure BB */
2450 pSMB->MaxDataCount = cpu_to_le16(4000);
2451 pSMB->MaxSetupCount = 0;
2452 pSMB->Reserved = 0;
2453 pSMB->Flags = 0;
2454 pSMB->Timeout = 0;
2455 pSMB->Reserved2 = 0;
2456 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002457 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458 pSMB->DataCount = 0;
2459 pSMB->DataOffset = 0;
2460 pSMB->SetupCount = 1;
2461 pSMB->Reserved3 = 0;
2462 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2463 byte_count = params + 1 /* pad */ ;
2464 pSMB->TotalParameterCount = cpu_to_le16(params);
2465 pSMB->ParameterCount = pSMB->TotalParameterCount;
2466 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2467 pSMB->Reserved4 = 0;
2468 pSMB->hdr.smb_buf_length += byte_count;
2469 pSMB->ByteCount = cpu_to_le16(byte_count);
2470
2471 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2472 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2473 if (rc) {
2474 cFYI(1, ("Send error in QuerySymLinkInfo = %d", rc));
2475 } else {
2476 /* decode response */
2477
2478 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2479 if (rc || (pSMBr->ByteCount < 2))
2480 /* BB also check enough total bytes returned */
2481 rc = -EIO; /* bad smb */
2482 else {
2483 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2484 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2485
2486 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2487 name_len = UniStrnlen((wchar_t *) ((char *)
Steve French50c2f752007-07-13 00:33:32 +00002488 &pSMBr->hdr.Protocol + data_offset),
2489 min_t(const int, buflen, count) / 2);
Steve French737b7582005-04-28 22:41:06 -07002490 /* BB FIXME investigate remapping reserved chars here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002492 (__le16 *) ((char *)&pSMBr->hdr.Protocol
2493 + data_offset),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494 name_len, nls_codepage);
2495 } else {
2496 strncpy(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002497 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498 data_offset,
2499 min_t(const int, buflen, count));
2500 }
2501 symlinkinfo[buflen] = 0;
2502 /* just in case so calling code does not go off the end of buffer */
2503 }
2504 }
2505 cifs_buf_release(pSMB);
2506 if (rc == -EAGAIN)
2507 goto querySymLinkRetry;
2508 return rc;
2509}
2510
Parag Warudkarc9489772007-10-23 18:09:48 +00002511#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08002512/* Initialize NT TRANSACT SMB into small smb request buffer.
2513 This assumes that all NT TRANSACTS that we init here have
2514 total parm and data under about 400 bytes (to fit in small cifs
2515 buffer size), which is the case so far, it easily fits. NB:
2516 Setup words themselves and ByteCount
2517 MaxSetupCount (size of returned setup area) and
2518 MaxParameterCount (returned parms size) must be set by caller */
Steve French50c2f752007-07-13 00:33:32 +00002519static int
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002520smb_init_nttransact(const __u16 sub_command, const int setup_count,
Steve French0a4b92c2006-01-12 15:44:21 -08002521 const int parm_len, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002522 void **ret_buf)
Steve French0a4b92c2006-01-12 15:44:21 -08002523{
2524 int rc;
2525 __u32 temp_offset;
Steve French50c2f752007-07-13 00:33:32 +00002526 struct smb_com_ntransact_req *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08002527
2528 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
2529 (void **)&pSMB);
2530 if (rc)
2531 return rc;
2532 *ret_buf = (void *)pSMB;
2533 pSMB->Reserved = 0;
2534 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
2535 pSMB->TotalDataCount = 0;
2536 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2537 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
2538 pSMB->ParameterCount = pSMB->TotalParameterCount;
2539 pSMB->DataCount = pSMB->TotalDataCount;
2540 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
2541 (setup_count * 2) - 4 /* for rfc1001 length itself */;
2542 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
2543 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
2544 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
2545 pSMB->SubCommand = cpu_to_le16(sub_command);
2546 return 0;
2547}
2548
2549static int
Steve French50c2f752007-07-13 00:33:32 +00002550validate_ntransact(char *buf, char **ppparm, char **ppdata,
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002551 __u32 *pparmlen, __u32 *pdatalen)
Steve French0a4b92c2006-01-12 15:44:21 -08002552{
Steve French50c2f752007-07-13 00:33:32 +00002553 char *end_of_smb;
Steve French0a4b92c2006-01-12 15:44:21 -08002554 __u32 data_count, data_offset, parm_count, parm_offset;
Steve French50c2f752007-07-13 00:33:32 +00002555 struct smb_com_ntransact_rsp *pSMBr;
Steve French0a4b92c2006-01-12 15:44:21 -08002556
Steve French630f3f02007-10-25 21:17:17 +00002557 *pdatalen = 0;
2558 *pparmlen = 0;
2559
Steve French790fe572007-07-07 19:25:05 +00002560 if (buf == NULL)
Steve French0a4b92c2006-01-12 15:44:21 -08002561 return -EINVAL;
2562
2563 pSMBr = (struct smb_com_ntransact_rsp *)buf;
2564
2565 /* ByteCount was converted from little endian in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +00002566 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
Steve French0a4b92c2006-01-12 15:44:21 -08002567 (char *)&pSMBr->ByteCount;
2568
Steve French0a4b92c2006-01-12 15:44:21 -08002569 data_offset = le32_to_cpu(pSMBr->DataOffset);
2570 data_count = le32_to_cpu(pSMBr->DataCount);
Steve French50c2f752007-07-13 00:33:32 +00002571 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
Steve French0a4b92c2006-01-12 15:44:21 -08002572 parm_count = le32_to_cpu(pSMBr->ParameterCount);
2573
2574 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
2575 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
2576
2577 /* should we also check that parm and data areas do not overlap? */
Steve French790fe572007-07-07 19:25:05 +00002578 if (*ppparm > end_of_smb) {
2579 cFYI(1, ("parms start after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002580 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002581 } else if (parm_count + *ppparm > end_of_smb) {
2582 cFYI(1, ("parm end after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002583 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002584 } else if (*ppdata > end_of_smb) {
2585 cFYI(1, ("data starts after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002586 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002587 } else if (data_count + *ppdata > end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002588 cFYI(1, ("data %p + count %d (%p) ends after end of smb %p start %p",
Steve French50c2f752007-07-13 00:33:32 +00002589 *ppdata, data_count, (data_count + *ppdata),
2590 end_of_smb, pSMBr));
Steve French0a4b92c2006-01-12 15:44:21 -08002591 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002592 } else if (parm_count + data_count > pSMBr->ByteCount) {
2593 cFYI(1, ("parm count and data count larger than SMB"));
Steve French0a4b92c2006-01-12 15:44:21 -08002594 return -EINVAL;
2595 }
Steve French630f3f02007-10-25 21:17:17 +00002596 *pdatalen = data_count;
2597 *pparmlen = parm_count;
Steve French0a4b92c2006-01-12 15:44:21 -08002598 return 0;
2599}
Parag Warudkarc9489772007-10-23 18:09:48 +00002600#endif /* CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08002601
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602int
2603CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2604 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002605 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606 const struct nls_table *nls_codepage)
2607{
2608 int rc = 0;
2609 int bytes_returned;
2610 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00002611 struct smb_com_transaction_ioctl_req *pSMB;
2612 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613
2614 cFYI(1, ("In Windows reparse style QueryLink for path %s", searchName));
2615 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2616 (void **) &pSMBr);
2617 if (rc)
2618 return rc;
2619
2620 pSMB->TotalParameterCount = 0 ;
2621 pSMB->TotalDataCount = 0;
2622 pSMB->MaxParameterCount = cpu_to_le32(2);
2623 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002624 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2625 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626 pSMB->MaxSetupCount = 4;
2627 pSMB->Reserved = 0;
2628 pSMB->ParameterOffset = 0;
2629 pSMB->DataCount = 0;
2630 pSMB->DataOffset = 0;
2631 pSMB->SetupCount = 4;
2632 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2633 pSMB->ParameterCount = pSMB->TotalParameterCount;
2634 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2635 pSMB->IsFsctl = 1; /* FSCTL */
2636 pSMB->IsRootFlag = 0;
2637 pSMB->Fid = fid; /* file handle always le */
2638 pSMB->ByteCount = 0;
2639
2640 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2641 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2642 if (rc) {
2643 cFYI(1, ("Send error in QueryReparseLinkInfo = %d", rc));
2644 } else { /* decode response */
2645 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2646 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
2647 if ((pSMBr->ByteCount < 2) || (data_offset > 512))
2648 /* BB also check enough total bytes returned */
2649 rc = -EIO; /* bad smb */
2650 else {
Steve French790fe572007-07-07 19:25:05 +00002651 if (data_count && (data_count < 2048)) {
Steve French50c2f752007-07-13 00:33:32 +00002652 char *end_of_smb = 2 /* sizeof byte count */ +
Steve French0a4b92c2006-01-12 15:44:21 -08002653 pSMBr->ByteCount +
2654 (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655
Steve French50c2f752007-07-13 00:33:32 +00002656 struct reparse_data *reparse_buf =
2657 (struct reparse_data *)
2658 ((char *)&pSMBr->hdr.Protocol
2659 + data_offset);
Steve French790fe572007-07-07 19:25:05 +00002660 if ((char *)reparse_buf >= end_of_smb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661 rc = -EIO;
2662 goto qreparse_out;
2663 }
Steve French790fe572007-07-07 19:25:05 +00002664 if ((reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665 reparse_buf->TargetNameOffset +
2666 reparse_buf->TargetNameLen) >
2667 end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002668 cFYI(1, ("reparse buf beyond SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 rc = -EIO;
2670 goto qreparse_out;
2671 }
Steve French50c2f752007-07-13 00:33:32 +00002672
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2674 name_len = UniStrnlen((wchar_t *)
Steve French50c2f752007-07-13 00:33:32 +00002675 (reparse_buf->LinkNamesBuf +
2676 reparse_buf->TargetNameOffset),
2677 min(buflen/2,
2678 reparse_buf->TargetNameLen / 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002680 (__le16 *) (reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681 reparse_buf->TargetNameOffset),
2682 name_len, nls_codepage);
2683 } else { /* ASCII names */
Steve French50c2f752007-07-13 00:33:32 +00002684 strncpy(symlinkinfo,
2685 reparse_buf->LinkNamesBuf +
2686 reparse_buf->TargetNameOffset,
2687 min_t(const int, buflen,
2688 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 }
2690 } else {
2691 rc = -EIO;
Steve French63135e02007-07-17 17:34:02 +00002692 cFYI(1, ("Invalid return data count on "
2693 "get reparse info ioctl"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694 }
2695 symlinkinfo[buflen] = 0; /* just in case so the caller
2696 does not go off the end of the buffer */
Steve French50c2f752007-07-13 00:33:32 +00002697 cFYI(1, ("readlink result - %s", symlinkinfo));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698 }
2699 }
2700qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002701 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702
2703 /* Note: On -EAGAIN error only caller can retry on handle based calls
2704 since file handle passed in no longer valid */
2705
2706 return rc;
2707}
2708
2709#ifdef CONFIG_CIFS_POSIX
2710
2711/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002712static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2713 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714{
2715 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002716 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2717 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2718 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002719 /* cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id)); */
2720
2721 return;
2722}
2723
2724/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002725static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2726 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727{
2728 int size = 0;
2729 int i;
2730 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002731 struct cifs_posix_ace *pACE;
2732 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2733 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734
2735 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2736 return -EOPNOTSUPP;
2737
Steve French790fe572007-07-07 19:25:05 +00002738 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739 count = le16_to_cpu(cifs_acl->access_entry_count);
2740 pACE = &cifs_acl->ace_array[0];
2741 size = sizeof(struct cifs_posix_acl);
2742 size += sizeof(struct cifs_posix_ace) * count;
2743 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002744 if (size_of_data_area < size) {
Steve French50c2f752007-07-13 00:33:32 +00002745 cFYI(1, ("bad CIFS POSIX ACL size %d vs. %d",
2746 size_of_data_area, size));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747 return -EINVAL;
2748 }
Steve French790fe572007-07-07 19:25:05 +00002749 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 count = le16_to_cpu(cifs_acl->access_entry_count);
2751 size = sizeof(struct cifs_posix_acl);
2752 size += sizeof(struct cifs_posix_ace) * count;
2753/* skip past access ACEs to get to default ACEs */
2754 pACE = &cifs_acl->ace_array[count];
2755 count = le16_to_cpu(cifs_acl->default_entry_count);
2756 size += sizeof(struct cifs_posix_ace) * count;
2757 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002758 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759 return -EINVAL;
2760 } else {
2761 /* illegal type */
2762 return -EINVAL;
2763 }
2764
2765 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002766 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002767 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002768 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 return -ERANGE;
2770 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002771 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002772 for (i = 0; i < count ; i++) {
2773 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2774 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 }
2776 }
2777 return size;
2778}
2779
Steve French50c2f752007-07-13 00:33:32 +00002780static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2781 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782{
2783 __u16 rc = 0; /* 0 = ACL converted ok */
2784
Steve Frenchff7feac2005-11-15 16:45:16 -08002785 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2786 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002788 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 /* Probably no need to le convert -1 on any arch but can not hurt */
2790 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002791 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002792 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Steve French50c2f752007-07-13 00:33:32 +00002793 /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 return rc;
2795}
2796
2797/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002798static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2799 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800{
2801 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002802 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2803 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 int count;
2805 int i;
2806
Steve French790fe572007-07-07 19:25:05 +00002807 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808 return 0;
2809
2810 count = posix_acl_xattr_count((size_t)buflen);
Steve Frenchc18c8422007-07-18 23:21:09 +00002811 cFYI(1, ("setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002812 "version of %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002813 count, buflen, le32_to_cpu(local_acl->a_version)));
Steve French790fe572007-07-07 19:25:05 +00002814 if (le32_to_cpu(local_acl->a_version) != 2) {
Steve French50c2f752007-07-13 00:33:32 +00002815 cFYI(1, ("unknown POSIX ACL version %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002816 le32_to_cpu(local_acl->a_version)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817 return 0;
2818 }
2819 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002820 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002821 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002822 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002823 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824 else {
Steve French50c2f752007-07-13 00:33:32 +00002825 cFYI(1, ("unknown ACL type %d", acl_type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826 return 0;
2827 }
Steve French50c2f752007-07-13 00:33:32 +00002828 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2830 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002831 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 /* ACE not converted */
2833 break;
2834 }
2835 }
Steve French790fe572007-07-07 19:25:05 +00002836 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2838 rc += sizeof(struct cifs_posix_acl);
2839 /* BB add check to make sure ACL does not overflow SMB */
2840 }
2841 return rc;
2842}
2843
2844int
2845CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002846 const unsigned char *searchName,
2847 char *acl_inf, const int buflen, const int acl_type,
2848 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849{
2850/* SMB_QUERY_POSIX_ACL */
2851 TRANSACTION2_QPI_REQ *pSMB = NULL;
2852 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2853 int rc = 0;
2854 int bytes_returned;
2855 int name_len;
2856 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002857
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858 cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName));
2859
2860queryAclRetry:
2861 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2862 (void **) &pSMBr);
2863 if (rc)
2864 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002865
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2867 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002868 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002869 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870 name_len++; /* trailing null */
2871 name_len *= 2;
2872 pSMB->FileName[name_len] = 0;
2873 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002874 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875 name_len = strnlen(searchName, PATH_MAX);
2876 name_len++; /* trailing null */
2877 strncpy(pSMB->FileName, searchName, name_len);
2878 }
2879
2880 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2881 pSMB->TotalDataCount = 0;
2882 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002883 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884 pSMB->MaxDataCount = cpu_to_le16(4000);
2885 pSMB->MaxSetupCount = 0;
2886 pSMB->Reserved = 0;
2887 pSMB->Flags = 0;
2888 pSMB->Timeout = 0;
2889 pSMB->Reserved2 = 0;
2890 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002891 offsetof(struct smb_com_transaction2_qpi_req,
2892 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893 pSMB->DataCount = 0;
2894 pSMB->DataOffset = 0;
2895 pSMB->SetupCount = 1;
2896 pSMB->Reserved3 = 0;
2897 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2898 byte_count = params + 1 /* pad */ ;
2899 pSMB->TotalParameterCount = cpu_to_le16(params);
2900 pSMB->ParameterCount = pSMB->TotalParameterCount;
2901 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2902 pSMB->Reserved4 = 0;
2903 pSMB->hdr.smb_buf_length += byte_count;
2904 pSMB->ByteCount = cpu_to_le16(byte_count);
2905
2906 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2907 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002908 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002909 if (rc) {
2910 cFYI(1, ("Send error in Query POSIX ACL = %d", rc));
2911 } else {
2912 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002913
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2915 if (rc || (pSMBr->ByteCount < 2))
2916 /* BB also check enough total bytes returned */
2917 rc = -EIO; /* bad smb */
2918 else {
2919 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2920 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2921 rc = cifs_copy_posix_acl(acl_inf,
2922 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002923 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002924 }
2925 }
2926 cifs_buf_release(pSMB);
2927 if (rc == -EAGAIN)
2928 goto queryAclRetry;
2929 return rc;
2930}
2931
2932int
2933CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002934 const unsigned char *fileName,
2935 const char *local_acl, const int buflen,
2936 const int acl_type,
2937 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002938{
2939 struct smb_com_transaction2_spi_req *pSMB = NULL;
2940 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2941 char *parm_data;
2942 int name_len;
2943 int rc = 0;
2944 int bytes_returned = 0;
2945 __u16 params, byte_count, data_count, param_offset, offset;
2946
2947 cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName));
2948setAclRetry:
2949 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002950 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002951 if (rc)
2952 return rc;
2953 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2954 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002955 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002956 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957 name_len++; /* trailing null */
2958 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002959 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002960 name_len = strnlen(fileName, PATH_MAX);
2961 name_len++; /* trailing null */
2962 strncpy(pSMB->FileName, fileName, name_len);
2963 }
2964 params = 6 + name_len;
2965 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00002966 /* BB find max SMB size from sess */
2967 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968 pSMB->MaxSetupCount = 0;
2969 pSMB->Reserved = 0;
2970 pSMB->Flags = 0;
2971 pSMB->Timeout = 0;
2972 pSMB->Reserved2 = 0;
2973 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002974 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975 offset = param_offset + params;
2976 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2977 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2978
2979 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002980 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002981
Steve French790fe572007-07-07 19:25:05 +00002982 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002983 rc = -EOPNOTSUPP;
2984 goto setACLerrorExit;
2985 }
2986 pSMB->DataOffset = cpu_to_le16(offset);
2987 pSMB->SetupCount = 1;
2988 pSMB->Reserved3 = 0;
2989 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2990 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2991 byte_count = 3 /* pad */ + params + data_count;
2992 pSMB->DataCount = cpu_to_le16(data_count);
2993 pSMB->TotalDataCount = pSMB->DataCount;
2994 pSMB->ParameterCount = cpu_to_le16(params);
2995 pSMB->TotalParameterCount = pSMB->ParameterCount;
2996 pSMB->Reserved4 = 0;
2997 pSMB->hdr.smb_buf_length += byte_count;
2998 pSMB->ByteCount = cpu_to_le16(byte_count);
2999 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003000 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003001 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003002 cFYI(1, ("Set POSIX ACL returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003003
3004setACLerrorExit:
3005 cifs_buf_release(pSMB);
3006 if (rc == -EAGAIN)
3007 goto setAclRetry;
3008 return rc;
3009}
3010
Steve Frenchf654bac2005-04-28 22:41:04 -07003011/* BB fix tabs in this function FIXME BB */
3012int
3013CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00003014 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07003015{
Steve French50c2f752007-07-13 00:33:32 +00003016 int rc = 0;
3017 struct smb_t2_qfi_req *pSMB = NULL;
3018 struct smb_t2_qfi_rsp *pSMBr = NULL;
3019 int bytes_returned;
3020 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07003021
Steve French790fe572007-07-07 19:25:05 +00003022 cFYI(1, ("In GetExtAttr"));
3023 if (tcon == NULL)
3024 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07003025
3026GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00003027 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3028 (void **) &pSMBr);
3029 if (rc)
3030 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003031
Steve Frenchad7a2922008-02-07 23:25:02 +00003032 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00003033 pSMB->t2.TotalDataCount = 0;
3034 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3035 /* BB find exact max data count below from sess structure BB */
3036 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
3037 pSMB->t2.MaxSetupCount = 0;
3038 pSMB->t2.Reserved = 0;
3039 pSMB->t2.Flags = 0;
3040 pSMB->t2.Timeout = 0;
3041 pSMB->t2.Reserved2 = 0;
3042 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3043 Fid) - 4);
3044 pSMB->t2.DataCount = 0;
3045 pSMB->t2.DataOffset = 0;
3046 pSMB->t2.SetupCount = 1;
3047 pSMB->t2.Reserved3 = 0;
3048 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3049 byte_count = params + 1 /* pad */ ;
3050 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3051 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3052 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
3053 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07003054 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00003055 pSMB->hdr.smb_buf_length += byte_count;
3056 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07003057
Steve French790fe572007-07-07 19:25:05 +00003058 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3059 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3060 if (rc) {
3061 cFYI(1, ("error %d in GetExtAttr", rc));
3062 } else {
3063 /* decode response */
3064 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3065 if (rc || (pSMBr->ByteCount < 2))
3066 /* BB also check enough total bytes returned */
3067 /* If rc should we check for EOPNOSUPP and
3068 disable the srvino flag? or in caller? */
3069 rc = -EIO; /* bad smb */
3070 else {
3071 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3072 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
3073 struct file_chattr_info *pfinfo;
3074 /* BB Do we need a cast or hash here ? */
3075 if (count != 16) {
3076 cFYI(1, ("Illegal size ret in GetExtAttr"));
3077 rc = -EIO;
3078 goto GetExtAttrOut;
3079 }
3080 pfinfo = (struct file_chattr_info *)
3081 (data_offset + (char *) &pSMBr->hdr.Protocol);
3082 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07003083 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00003084 }
3085 }
Steve Frenchf654bac2005-04-28 22:41:04 -07003086GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00003087 cifs_buf_release(pSMB);
3088 if (rc == -EAGAIN)
3089 goto GetExtAttrRetry;
3090 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003091}
3092
Steve Frenchf654bac2005-04-28 22:41:04 -07003093#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003094
Steve French297647c2007-10-12 04:11:59 +00003095#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08003096/* Get Security Descriptor (by handle) from remote server for a file or dir */
3097int
3098CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f02007-10-25 21:17:17 +00003099 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003100{
3101 int rc = 0;
3102 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003103 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003104 struct kvec iov[1];
3105
3106 cFYI(1, ("GetCifsACL"));
3107
Steve French630f3f02007-10-25 21:17:17 +00003108 *pbuflen = 0;
3109 *acl_inf = NULL;
3110
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003111 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003112 8 /* parm len */, tcon, (void **) &pSMB);
3113 if (rc)
3114 return rc;
3115
3116 pSMB->MaxParameterCount = cpu_to_le32(4);
3117 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3118 pSMB->MaxSetupCount = 0;
3119 pSMB->Fid = fid; /* file handle always le */
3120 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3121 CIFS_ACL_DACL);
3122 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3123 pSMB->hdr.smb_buf_length += 11;
3124 iov[0].iov_base = (char *)pSMB;
3125 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3126
Steve Frencha761ac52007-10-18 21:45:27 +00003127 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Steve French133672e2007-11-13 22:41:37 +00003128 CIFS_STD_OP);
Steve French0a4b92c2006-01-12 15:44:21 -08003129 cifs_stats_inc(&tcon->num_acl_get);
3130 if (rc) {
3131 cFYI(1, ("Send error in QuerySecDesc = %d", rc));
3132 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003133 __le32 *parm;
Steve French630f3f02007-10-25 21:17:17 +00003134 __u32 parm_len;
3135 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003136 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f02007-10-25 21:17:17 +00003137 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003138
3139/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003140 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f02007-10-25 21:17:17 +00003141 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003142 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003143 goto qsec_out;
3144 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3145
Steve French630f3f02007-10-25 21:17:17 +00003146 cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, *acl_inf));
Steve French0a4b92c2006-01-12 15:44:21 -08003147
3148 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3149 rc = -EIO; /* bad smb */
Steve French630f3f02007-10-25 21:17:17 +00003150 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003151 goto qsec_out;
3152 }
3153
3154/* BB check that data area is minimum length and as big as acl_len */
3155
Steve Frenchaf6f4612007-10-16 18:40:37 +00003156 acl_len = le32_to_cpu(*parm);
Steve French630f3f02007-10-25 21:17:17 +00003157 if (acl_len != *pbuflen) {
3158 cERROR(1, ("acl length %d does not match %d",
3159 acl_len, *pbuflen));
3160 if (*pbuflen > acl_len)
3161 *pbuflen = acl_len;
3162 }
Steve French0a4b92c2006-01-12 15:44:21 -08003163
Steve French630f3f02007-10-25 21:17:17 +00003164 /* check if buffer is big enough for the acl
3165 header followed by the smallest SID */
3166 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3167 (*pbuflen >= 64 * 1024)) {
3168 cERROR(1, ("bad acl length %d", *pbuflen));
3169 rc = -EINVAL;
3170 *pbuflen = 0;
3171 } else {
3172 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3173 if (*acl_inf == NULL) {
3174 *pbuflen = 0;
3175 rc = -ENOMEM;
3176 }
3177 memcpy(*acl_inf, pdata, *pbuflen);
3178 }
Steve French0a4b92c2006-01-12 15:44:21 -08003179 }
3180qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003181 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003182 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003183 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003184 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003185/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003186 return rc;
3187}
Steve French97837582007-12-31 07:47:21 +00003188
3189int
3190CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3191 struct cifs_ntsd *pntsd, __u32 acllen)
3192{
3193 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3194 int rc = 0;
3195 int bytes_returned = 0;
3196 SET_SEC_DESC_REQ *pSMB = NULL;
3197 NTRANSACT_RSP *pSMBr = NULL;
3198
3199setCifsAclRetry:
3200 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3201 (void **) &pSMBr);
3202 if (rc)
3203 return (rc);
3204
3205 pSMB->MaxSetupCount = 0;
3206 pSMB->Reserved = 0;
3207
3208 param_count = 8;
3209 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3210 data_count = acllen;
3211 data_offset = param_offset + param_count;
3212 byte_count = 3 /* pad */ + param_count;
3213
3214 pSMB->DataCount = cpu_to_le32(data_count);
3215 pSMB->TotalDataCount = pSMB->DataCount;
3216 pSMB->MaxParameterCount = cpu_to_le32(4);
3217 pSMB->MaxDataCount = cpu_to_le32(16384);
3218 pSMB->ParameterCount = cpu_to_le32(param_count);
3219 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3220 pSMB->TotalParameterCount = pSMB->ParameterCount;
3221 pSMB->DataOffset = cpu_to_le32(data_offset);
3222 pSMB->SetupCount = 0;
3223 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3224 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3225
3226 pSMB->Fid = fid; /* file handle always le */
3227 pSMB->Reserved2 = 0;
3228 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3229
3230 if (pntsd && acllen) {
3231 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3232 (char *) pntsd,
3233 acllen);
3234 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3235
3236 } else
3237 pSMB->hdr.smb_buf_length += byte_count;
3238
3239 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3240 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3241
3242 cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc));
3243 if (rc)
3244 cFYI(1, ("Set CIFS ACL returned %d", rc));
3245 cifs_buf_release(pSMB);
3246
3247 if (rc == -EAGAIN)
3248 goto setCifsAclRetry;
3249
3250 return (rc);
3251}
3252
Steve French297647c2007-10-12 04:11:59 +00003253#endif /* CONFIG_CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08003254
Steve French6b8edfe2005-08-23 20:26:03 -07003255/* Legacy Query Path Information call for lookup to old servers such
3256 as Win9x/WinME */
3257int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003258 const unsigned char *searchName,
3259 FILE_ALL_INFO *pFinfo,
3260 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003261{
Steve Frenchad7a2922008-02-07 23:25:02 +00003262 QUERY_INFORMATION_REQ *pSMB;
3263 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003264 int rc = 0;
3265 int bytes_returned;
3266 int name_len;
3267
Steve French50c2f752007-07-13 00:33:32 +00003268 cFYI(1, ("In SMBQPath path %s", searchName));
Steve French6b8edfe2005-08-23 20:26:03 -07003269QInfRetry:
3270 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003271 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003272 if (rc)
3273 return rc;
3274
3275 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3276 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003277 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3278 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003279 name_len++; /* trailing null */
3280 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003281 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003282 name_len = strnlen(searchName, PATH_MAX);
3283 name_len++; /* trailing null */
3284 strncpy(pSMB->FileName, searchName, name_len);
3285 }
3286 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003287 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003288 pSMB->hdr.smb_buf_length += (__u16) name_len;
3289 pSMB->ByteCount = cpu_to_le16(name_len);
3290
3291 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003292 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003293 if (rc) {
3294 cFYI(1, ("Send error in QueryInfo = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003295 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003296 struct timespec ts;
3297 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003298
3299 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003300 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003301 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003302 ts.tv_nsec = 0;
3303 ts.tv_sec = time;
3304 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003305 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003306 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3307 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003308 pFinfo->AllocationSize =
3309 cpu_to_le64(le32_to_cpu(pSMBr->size));
3310 pFinfo->EndOfFile = pFinfo->AllocationSize;
3311 pFinfo->Attributes =
3312 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003313 } else
3314 rc = -EIO; /* bad buffer passed in */
3315
3316 cifs_buf_release(pSMB);
3317
3318 if (rc == -EAGAIN)
3319 goto QInfRetry;
3320
3321 return rc;
3322}
3323
3324
3325
3326
Linus Torvalds1da177e2005-04-16 15:20:36 -07003327int
3328CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3329 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003330 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003331 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003332 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003333{
3334/* level 263 SMB_QUERY_FILE_ALL_INFO */
3335 TRANSACTION2_QPI_REQ *pSMB = NULL;
3336 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3337 int rc = 0;
3338 int bytes_returned;
3339 int name_len;
3340 __u16 params, byte_count;
3341
3342/* cFYI(1, ("In QPathInfo path %s", searchName)); */
3343QPathInfoRetry:
3344 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3345 (void **) &pSMBr);
3346 if (rc)
3347 return rc;
3348
3349 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3350 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003351 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003352 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003353 name_len++; /* trailing null */
3354 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003355 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003356 name_len = strnlen(searchName, PATH_MAX);
3357 name_len++; /* trailing null */
3358 strncpy(pSMB->FileName, searchName, name_len);
3359 }
3360
Steve French50c2f752007-07-13 00:33:32 +00003361 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003362 pSMB->TotalDataCount = 0;
3363 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00003364 /* BB find exact max SMB PDU from sess structure BB */
3365 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003366 pSMB->MaxSetupCount = 0;
3367 pSMB->Reserved = 0;
3368 pSMB->Flags = 0;
3369 pSMB->Timeout = 0;
3370 pSMB->Reserved2 = 0;
3371 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003372 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003373 pSMB->DataCount = 0;
3374 pSMB->DataOffset = 0;
3375 pSMB->SetupCount = 1;
3376 pSMB->Reserved3 = 0;
3377 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3378 byte_count = params + 1 /* pad */ ;
3379 pSMB->TotalParameterCount = cpu_to_le16(params);
3380 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003381 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003382 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3383 else
3384 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003385 pSMB->Reserved4 = 0;
3386 pSMB->hdr.smb_buf_length += byte_count;
3387 pSMB->ByteCount = cpu_to_le16(byte_count);
3388
3389 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3390 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3391 if (rc) {
3392 cFYI(1, ("Send error in QPathInfo = %d", rc));
3393 } else { /* decode response */
3394 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3395
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003396 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3397 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003398 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003399 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003400 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003401 rc = -EIO; /* 24 or 26 expected but we do not read
3402 last field */
3403 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003404 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003405 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003406
3407 /* On legacy responses we do not read the last field,
3408 EAsize, fortunately since it varies by subdialect and
3409 also note it differs on Set vs. Get, ie two bytes or 4
3410 bytes depending but we don't care here */
3411 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003412 size = sizeof(FILE_INFO_STANDARD);
3413 else
3414 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003415 memcpy((char *) pFindData,
3416 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003417 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003418 } else
3419 rc = -ENOMEM;
3420 }
3421 cifs_buf_release(pSMB);
3422 if (rc == -EAGAIN)
3423 goto QPathInfoRetry;
3424
3425 return rc;
3426}
3427
3428int
3429CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3430 const unsigned char *searchName,
Steve French582d21e2008-05-13 04:54:12 +00003431 FILE_UNIX_BASIC_INFO *pFindData,
Steve French737b7582005-04-28 22:41:06 -07003432 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003433{
3434/* SMB_QUERY_FILE_UNIX_BASIC */
3435 TRANSACTION2_QPI_REQ *pSMB = NULL;
3436 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3437 int rc = 0;
3438 int bytes_returned = 0;
3439 int name_len;
3440 __u16 params, byte_count;
3441
3442 cFYI(1, ("In QPathInfo (Unix) the path %s", searchName));
3443UnixQPathInfoRetry:
3444 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3445 (void **) &pSMBr);
3446 if (rc)
3447 return rc;
3448
3449 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3450 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003451 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003452 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003453 name_len++; /* trailing null */
3454 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003455 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003456 name_len = strnlen(searchName, PATH_MAX);
3457 name_len++; /* trailing null */
3458 strncpy(pSMB->FileName, searchName, name_len);
3459 }
3460
Steve French50c2f752007-07-13 00:33:32 +00003461 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003462 pSMB->TotalDataCount = 0;
3463 pSMB->MaxParameterCount = cpu_to_le16(2);
3464 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003465 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003466 pSMB->MaxSetupCount = 0;
3467 pSMB->Reserved = 0;
3468 pSMB->Flags = 0;
3469 pSMB->Timeout = 0;
3470 pSMB->Reserved2 = 0;
3471 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003472 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003473 pSMB->DataCount = 0;
3474 pSMB->DataOffset = 0;
3475 pSMB->SetupCount = 1;
3476 pSMB->Reserved3 = 0;
3477 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3478 byte_count = params + 1 /* pad */ ;
3479 pSMB->TotalParameterCount = cpu_to_le16(params);
3480 pSMB->ParameterCount = pSMB->TotalParameterCount;
3481 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3482 pSMB->Reserved4 = 0;
3483 pSMB->hdr.smb_buf_length += byte_count;
3484 pSMB->ByteCount = cpu_to_le16(byte_count);
3485
3486 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3487 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3488 if (rc) {
3489 cFYI(1, ("Send error in QPathInfo = %d", rc));
3490 } else { /* decode response */
3491 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3492
3493 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve French1e71f252007-09-20 15:30:07 +00003494 cERROR(1, ("Malformed FILE_UNIX_BASIC_INFO response.\n"
3495 "Unix Extensions can be disabled on mount "
3496 "by specifying the nosfu mount option."));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003497 rc = -EIO; /* bad smb */
3498 } else {
3499 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3500 memcpy((char *) pFindData,
3501 (char *) &pSMBr->hdr.Protocol +
3502 data_offset,
Steve French630f3f02007-10-25 21:17:17 +00003503 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003504 }
3505 }
3506 cifs_buf_release(pSMB);
3507 if (rc == -EAGAIN)
3508 goto UnixQPathInfoRetry;
3509
3510 return rc;
3511}
3512
Linus Torvalds1da177e2005-04-16 15:20:36 -07003513/* xid, tcon, searchName and codepage are input parms, rest are returned */
3514int
3515CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003516 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003517 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003518 __u16 *pnetfid,
3519 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003520{
3521/* level 257 SMB_ */
3522 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3523 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003524 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003525 int rc = 0;
3526 int bytes_returned = 0;
3527 int name_len;
3528 __u16 params, byte_count;
3529
Steve French50c2f752007-07-13 00:33:32 +00003530 cFYI(1, ("In FindFirst for %s", searchName));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003531
3532findFirstRetry:
3533 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3534 (void **) &pSMBr);
3535 if (rc)
3536 return rc;
3537
3538 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3539 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003540 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003541 PATH_MAX, nls_codepage, remap);
3542 /* We can not add the asterik earlier in case
3543 it got remapped to 0xF03A as if it were part of the
3544 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003545 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003546 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003547 pSMB->FileName[name_len+1] = 0;
3548 pSMB->FileName[name_len+2] = '*';
3549 pSMB->FileName[name_len+3] = 0;
3550 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003551 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3552 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003553 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003554 } else { /* BB add check for overrun of SMB buf BB */
3555 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003556/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003557 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003558 free buffer exit; BB */
3559 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003560 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003561 pSMB->FileName[name_len+1] = '*';
3562 pSMB->FileName[name_len+2] = 0;
3563 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003564 }
3565
3566 params = 12 + name_len /* includes null */ ;
3567 pSMB->TotalDataCount = 0; /* no EAs */
3568 pSMB->MaxParameterCount = cpu_to_le16(10);
3569 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3570 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3571 pSMB->MaxSetupCount = 0;
3572 pSMB->Reserved = 0;
3573 pSMB->Flags = 0;
3574 pSMB->Timeout = 0;
3575 pSMB->Reserved2 = 0;
3576 byte_count = params + 1 /* pad */ ;
3577 pSMB->TotalParameterCount = cpu_to_le16(params);
3578 pSMB->ParameterCount = pSMB->TotalParameterCount;
3579 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003580 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3581 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003582 pSMB->DataCount = 0;
3583 pSMB->DataOffset = 0;
3584 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3585 pSMB->Reserved3 = 0;
3586 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3587 pSMB->SearchAttributes =
3588 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3589 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003590 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3591 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003592 CIFS_SEARCH_RETURN_RESUME);
3593 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3594
3595 /* BB what should we set StorageType to? Does it matter? BB */
3596 pSMB->SearchStorageType = 0;
3597 pSMB->hdr.smb_buf_length += byte_count;
3598 pSMB->ByteCount = cpu_to_le16(byte_count);
3599
3600 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3601 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003602 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003603
Steve French88274812006-03-09 22:21:45 +00003604 if (rc) {/* BB add logic to retry regular search if Unix search
3605 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003606 /* BB Add code to handle unsupported level rc */
3607 cFYI(1, ("Error in FindFirst = %d", rc));
Steve French1982c342005-08-17 12:38:22 -07003608
Steve French88274812006-03-09 22:21:45 +00003609 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003610
3611 /* BB eventually could optimize out free and realloc of buf */
3612 /* for this case */
3613 if (rc == -EAGAIN)
3614 goto findFirstRetry;
3615 } else { /* decode response */
3616 /* BB remember to free buffer if error BB */
3617 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003618 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003619 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003620 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003621 else
Steve French4b18f2a2008-04-29 00:06:05 +00003622 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003623
3624 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003625 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003626 psrch_inf->srch_entries_start =
3627 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003628 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003629 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3630 le16_to_cpu(pSMBr->t2.ParameterOffset));
3631
Steve French790fe572007-07-07 19:25:05 +00003632 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003633 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003634 else
Steve French4b18f2a2008-04-29 00:06:05 +00003635 psrch_inf->endOfSearch = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003636
Steve French50c2f752007-07-13 00:33:32 +00003637 psrch_inf->entries_in_buffer =
3638 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003639 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003640 psrch_inf->entries_in_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003641 *pnetfid = parms->SearchHandle;
3642 } else {
3643 cifs_buf_release(pSMB);
3644 }
3645 }
3646
3647 return rc;
3648}
3649
3650int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003651 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003652{
3653 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3654 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003655 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003656 char *response_data;
3657 int rc = 0;
3658 int bytes_returned, name_len;
3659 __u16 params, byte_count;
3660
3661 cFYI(1, ("In FindNext"));
3662
Steve French4b18f2a2008-04-29 00:06:05 +00003663 if (psrch_inf->endOfSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003664 return -ENOENT;
3665
3666 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3667 (void **) &pSMBr);
3668 if (rc)
3669 return rc;
3670
Steve French50c2f752007-07-13 00:33:32 +00003671 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003672 byte_count = 0;
3673 pSMB->TotalDataCount = 0; /* no EAs */
3674 pSMB->MaxParameterCount = cpu_to_le16(8);
3675 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003676 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3677 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003678 pSMB->MaxSetupCount = 0;
3679 pSMB->Reserved = 0;
3680 pSMB->Flags = 0;
3681 pSMB->Timeout = 0;
3682 pSMB->Reserved2 = 0;
3683 pSMB->ParameterOffset = cpu_to_le16(
3684 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3685 pSMB->DataCount = 0;
3686 pSMB->DataOffset = 0;
3687 pSMB->SetupCount = 1;
3688 pSMB->Reserved3 = 0;
3689 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3690 pSMB->SearchHandle = searchHandle; /* always kept as le */
3691 pSMB->SearchCount =
Steve French630f3f02007-10-25 21:17:17 +00003692 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003693 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3694 pSMB->ResumeKey = psrch_inf->resume_key;
3695 pSMB->SearchFlags =
3696 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3697
3698 name_len = psrch_inf->resume_name_len;
3699 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003700 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003701 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3702 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003703 /* 14 byte parm len above enough for 2 byte null terminator */
3704 pSMB->ResumeFileName[name_len] = 0;
3705 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003706 } else {
3707 rc = -EINVAL;
3708 goto FNext2_err_exit;
3709 }
3710 byte_count = params + 1 /* pad */ ;
3711 pSMB->TotalParameterCount = cpu_to_le16(params);
3712 pSMB->ParameterCount = pSMB->TotalParameterCount;
3713 pSMB->hdr.smb_buf_length += byte_count;
3714 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003715
Linus Torvalds1da177e2005-04-16 15:20:36 -07003716 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3717 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003718 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719 if (rc) {
3720 if (rc == -EBADF) {
Steve French4b18f2a2008-04-29 00:06:05 +00003721 psrch_inf->endOfSearch = true;
Jeff Layton63534502008-05-12 19:56:05 -07003722 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00003723 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003724 } else
3725 cFYI(1, ("FindNext returned = %d", rc));
3726 } else { /* decode response */
3727 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003728
Steve French790fe572007-07-07 19:25:05 +00003729 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003730 /* BB fixme add lock for file (srch_info) struct here */
3731 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003732 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733 else
Steve French4b18f2a2008-04-29 00:06:05 +00003734 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003735 response_data = (char *) &pSMBr->hdr.Protocol +
3736 le16_to_cpu(pSMBr->t2.ParameterOffset);
3737 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3738 response_data = (char *)&pSMBr->hdr.Protocol +
3739 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003740 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003741 cifs_small_buf_release(
3742 psrch_inf->ntwrk_buf_start);
3743 else
3744 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003745 psrch_inf->srch_entries_start = response_data;
3746 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003747 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003748 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003749 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003750 else
Steve French4b18f2a2008-04-29 00:06:05 +00003751 psrch_inf->endOfSearch = false;
Steve French50c2f752007-07-13 00:33:32 +00003752 psrch_inf->entries_in_buffer =
3753 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003754 psrch_inf->index_of_last_entry +=
3755 psrch_inf->entries_in_buffer;
Steve French50c2f752007-07-13 00:33:32 +00003756/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",
3757 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003758
3759 /* BB fixme add unlock here */
3760 }
3761
3762 }
3763
3764 /* BB On error, should we leave previous search buf (and count and
3765 last entry fields) intact or free the previous one? */
3766
3767 /* Note: On -EAGAIN error only caller can retry on handle based calls
3768 since file handle passed in no longer valid */
3769FNext2_err_exit:
3770 if (rc != 0)
3771 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003772 return rc;
3773}
3774
3775int
Steve French50c2f752007-07-13 00:33:32 +00003776CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3777 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003778{
3779 int rc = 0;
3780 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003781
3782 cFYI(1, ("In CIFSSMBFindClose"));
3783 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3784
3785 /* no sense returning error if session restarted
3786 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003787 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003788 return 0;
3789 if (rc)
3790 return rc;
3791
Linus Torvalds1da177e2005-04-16 15:20:36 -07003792 pSMB->FileID = searchHandle;
3793 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003794 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003795 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003796 cERROR(1, ("Send error in FindClose = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003797
Steve Frencha4544342005-08-24 13:59:35 -07003798 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003799
3800 /* Since session is dead, search handle closed on server already */
3801 if (rc == -EAGAIN)
3802 rc = 0;
3803
3804 return rc;
3805}
3806
Linus Torvalds1da177e2005-04-16 15:20:36 -07003807int
3808CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003809 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003810 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003811 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003812{
3813 int rc = 0;
3814 TRANSACTION2_QPI_REQ *pSMB = NULL;
3815 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3816 int name_len, bytes_returned;
3817 __u16 params, byte_count;
3818
Steve French50c2f752007-07-13 00:33:32 +00003819 cFYI(1, ("In GetSrvInodeNum for %s", searchName));
Steve French790fe572007-07-07 19:25:05 +00003820 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003821 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003822
3823GetInodeNumberRetry:
3824 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003825 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003826 if (rc)
3827 return rc;
3828
Linus Torvalds1da177e2005-04-16 15:20:36 -07003829 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3830 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003831 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003832 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003833 name_len++; /* trailing null */
3834 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003835 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003836 name_len = strnlen(searchName, PATH_MAX);
3837 name_len++; /* trailing null */
3838 strncpy(pSMB->FileName, searchName, name_len);
3839 }
3840
3841 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
3842 pSMB->TotalDataCount = 0;
3843 pSMB->MaxParameterCount = cpu_to_le16(2);
3844 /* BB find exact max data count below from sess structure BB */
3845 pSMB->MaxDataCount = cpu_to_le16(4000);
3846 pSMB->MaxSetupCount = 0;
3847 pSMB->Reserved = 0;
3848 pSMB->Flags = 0;
3849 pSMB->Timeout = 0;
3850 pSMB->Reserved2 = 0;
3851 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003852 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003853 pSMB->DataCount = 0;
3854 pSMB->DataOffset = 0;
3855 pSMB->SetupCount = 1;
3856 pSMB->Reserved3 = 0;
3857 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3858 byte_count = params + 1 /* pad */ ;
3859 pSMB->TotalParameterCount = cpu_to_le16(params);
3860 pSMB->ParameterCount = pSMB->TotalParameterCount;
3861 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
3862 pSMB->Reserved4 = 0;
3863 pSMB->hdr.smb_buf_length += byte_count;
3864 pSMB->ByteCount = cpu_to_le16(byte_count);
3865
3866 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3867 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3868 if (rc) {
3869 cFYI(1, ("error %d in QueryInternalInfo", rc));
3870 } else {
3871 /* decode response */
3872 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3873 if (rc || (pSMBr->ByteCount < 2))
3874 /* BB also check enough total bytes returned */
3875 /* If rc should we check for EOPNOSUPP and
3876 disable the srvino flag? or in caller? */
3877 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00003878 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003879 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3880 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00003881 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003882 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00003883 if (count < 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003884 cFYI(1, ("Illegal size ret in QryIntrnlInf"));
3885 rc = -EIO;
3886 goto GetInodeNumOut;
3887 }
3888 pfinfo = (struct file_internal_info *)
3889 (data_offset + (char *) &pSMBr->hdr.Protocol);
3890 *inode_number = pfinfo->UniqueId;
3891 }
3892 }
3893GetInodeNumOut:
3894 cifs_buf_release(pSMB);
3895 if (rc == -EAGAIN)
3896 goto GetInodeNumberRetry;
3897 return rc;
3898}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003899
Igor Mammedovfec45852008-05-16 13:06:30 +04003900/* parses DFS refferal V3 structure
3901 * caller is responsible for freeing target_nodes
3902 * returns:
3903 * on success - 0
3904 * on failure - errno
3905 */
3906static int
Steve Frencha1fe78f2008-05-16 18:48:38 +00003907parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
Igor Mammedovfec45852008-05-16 13:06:30 +04003908 unsigned int *num_of_nodes,
3909 struct dfs_info3_param **target_nodes,
3910 const struct nls_table *nls_codepage)
3911{
3912 int i, rc = 0;
3913 char *data_end;
3914 bool is_unicode;
3915 struct dfs_referral_level_3 *ref;
3916
Harvey Harrison5ca33c62008-07-23 17:45:58 -07003917 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
3918 is_unicode = true;
3919 else
3920 is_unicode = false;
Igor Mammedovfec45852008-05-16 13:06:30 +04003921 *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
3922
3923 if (*num_of_nodes < 1) {
3924 cERROR(1, ("num_referrals: must be at least > 0,"
3925 "but we get num_referrals = %d\n", *num_of_nodes));
3926 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00003927 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003928 }
3929
3930 ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
Al Viro1d92cfd2008-06-02 10:59:02 +01003931 if (ref->VersionNumber != cpu_to_le16(3)) {
Igor Mammedovfec45852008-05-16 13:06:30 +04003932 cERROR(1, ("Referrals of V%d version are not supported,"
Al Viro1d92cfd2008-06-02 10:59:02 +01003933 "should be V3", le16_to_cpu(ref->VersionNumber)));
Igor Mammedovfec45852008-05-16 13:06:30 +04003934 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00003935 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003936 }
3937
3938 /* get the upper boundary of the resp buffer */
3939 data_end = (char *)(&(pSMBr->PathConsumed)) +
3940 le16_to_cpu(pSMBr->t2.DataCount);
3941
3942 cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n",
3943 *num_of_nodes,
3944 le16_to_cpu(pSMBr->DFSFlags)));
3945
3946 *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
3947 *num_of_nodes, GFP_KERNEL);
3948 if (*target_nodes == NULL) {
3949 cERROR(1, ("Failed to allocate buffer for target_nodes\n"));
3950 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00003951 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003952 }
3953
3954 /* collect neccessary data from referrals */
3955 for (i = 0; i < *num_of_nodes; i++) {
3956 char *temp;
3957 int max_len;
3958 struct dfs_info3_param *node = (*target_nodes)+i;
3959
3960 node->flags = le16_to_cpu(pSMBr->DFSFlags);
3961 node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
3962 node->server_type = le16_to_cpu(ref->ServerType);
3963 node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
3964
3965 /* copy DfsPath */
3966 temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
3967 max_len = data_end - temp;
3968 rc = cifs_strncpy_to_host(&(node->path_name), temp,
3969 max_len, is_unicode, nls_codepage);
3970 if (rc)
Steve Frencha1fe78f2008-05-16 18:48:38 +00003971 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003972
3973 /* copy link target UNC */
3974 temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
3975 max_len = data_end - temp;
3976 rc = cifs_strncpy_to_host(&(node->node_name), temp,
3977 max_len, is_unicode, nls_codepage);
3978 if (rc)
Steve Frencha1fe78f2008-05-16 18:48:38 +00003979 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003980
Al Viro1d92cfd2008-06-02 10:59:02 +01003981 ref += le16_to_cpu(ref->Size);
Igor Mammedovfec45852008-05-16 13:06:30 +04003982 }
3983
Steve Frencha1fe78f2008-05-16 18:48:38 +00003984parse_DFS_referrals_exit:
Igor Mammedovfec45852008-05-16 13:06:30 +04003985 if (rc) {
3986 free_dfs_info_array(*target_nodes, *num_of_nodes);
3987 *target_nodes = NULL;
3988 *num_of_nodes = 0;
3989 }
3990 return rc;
3991}
3992
Linus Torvalds1da177e2005-04-16 15:20:36 -07003993int
3994CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
3995 const unsigned char *searchName,
Steve Frenchc2cf07d2008-05-15 06:20:02 +00003996 struct dfs_info3_param **target_nodes,
3997 unsigned int *num_of_nodes,
Steve French737b7582005-04-28 22:41:06 -07003998 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003999{
4000/* TRANS2_GET_DFS_REFERRAL */
4001 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
4002 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004003 int rc = 0;
4004 int bytes_returned;
4005 int name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004006 __u16 params, byte_count;
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004007 *num_of_nodes = 0;
4008 *target_nodes = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004009
4010 cFYI(1, ("In GetDFSRefer the path %s", searchName));
4011 if (ses == NULL)
4012 return -ENODEV;
4013getDFSRetry:
4014 rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
4015 (void **) &pSMBr);
4016 if (rc)
4017 return rc;
Steve French50c2f752007-07-13 00:33:32 +00004018
4019 /* server pointer checked in called function,
Steve French1982c342005-08-17 12:38:22 -07004020 but should never be null here anyway */
4021 pSMB->hdr.Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004022 pSMB->hdr.Tid = ses->ipc_tid;
4023 pSMB->hdr.Uid = ses->Suid;
Steve French26f57362007-08-30 22:09:15 +00004024 if (ses->capabilities & CAP_STATUS32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004025 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
Steve French26f57362007-08-30 22:09:15 +00004026 if (ses->capabilities & CAP_DFS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004027 pSMB->hdr.Flags2 |= SMBFLG2_DFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004028
4029 if (ses->capabilities & CAP_UNICODE) {
4030 pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
4031 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004032 cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
Steve French737b7582005-04-28 22:41:06 -07004033 searchName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004034 name_len++; /* trailing null */
4035 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004036 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004037 name_len = strnlen(searchName, PATH_MAX);
4038 name_len++; /* trailing null */
4039 strncpy(pSMB->RequestFileName, searchName, name_len);
4040 }
4041
Steve French790fe572007-07-07 19:25:05 +00004042 if (ses->server) {
4043 if (ses->server->secMode &
Steve French1a4e15a2006-10-12 21:33:51 +00004044 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
4045 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
4046 }
4047
Steve French50c2f752007-07-13 00:33:32 +00004048 pSMB->hdr.Uid = ses->Suid;
Steve French1a4e15a2006-10-12 21:33:51 +00004049
Linus Torvalds1da177e2005-04-16 15:20:36 -07004050 params = 2 /* level */ + name_len /*includes null */ ;
4051 pSMB->TotalDataCount = 0;
4052 pSMB->DataCount = 0;
4053 pSMB->DataOffset = 0;
4054 pSMB->MaxParameterCount = 0;
Steve French582d21e2008-05-13 04:54:12 +00004055 /* BB find exact max SMB PDU from sess structure BB */
4056 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004057 pSMB->MaxSetupCount = 0;
4058 pSMB->Reserved = 0;
4059 pSMB->Flags = 0;
4060 pSMB->Timeout = 0;
4061 pSMB->Reserved2 = 0;
4062 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004063 struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004064 pSMB->SetupCount = 1;
4065 pSMB->Reserved3 = 0;
4066 pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
4067 byte_count = params + 3 /* pad */ ;
4068 pSMB->ParameterCount = cpu_to_le16(params);
4069 pSMB->TotalParameterCount = pSMB->ParameterCount;
4070 pSMB->MaxReferralLevel = cpu_to_le16(3);
4071 pSMB->hdr.smb_buf_length += byte_count;
4072 pSMB->ByteCount = cpu_to_le16(byte_count);
4073
4074 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
4075 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4076 if (rc) {
4077 cFYI(1, ("Send error in GetDFSRefer = %d", rc));
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004078 goto GetDFSRefExit;
4079 }
4080 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004081
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004082 /* BB Also check if enough total bytes returned? */
Igor Mammedovfec45852008-05-16 13:06:30 +04004083 if (rc || (pSMBr->ByteCount < 17)) {
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004084 rc = -EIO; /* bad smb */
Igor Mammedovfec45852008-05-16 13:06:30 +04004085 goto GetDFSRefExit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004086 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004087
4088 cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d",
4089 pSMBr->ByteCount,
4090 le16_to_cpu(pSMBr->t2.DataOffset)));
4091
4092 /* parse returned result into more usable form */
Steve Frencha1fe78f2008-05-16 18:48:38 +00004093 rc = parse_DFS_referrals(pSMBr, num_of_nodes,
Igor Mammedovfec45852008-05-16 13:06:30 +04004094 target_nodes, nls_codepage);
4095
Linus Torvalds1da177e2005-04-16 15:20:36 -07004096GetDFSRefExit:
Steve French0d817bc2008-05-22 02:02:03 +00004097 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004098
4099 if (rc == -EAGAIN)
4100 goto getDFSRetry;
4101
4102 return rc;
4103}
4104
Steve French20962432005-09-21 22:05:57 -07004105/* Query File System Info such as free space to old servers such as Win 9x */
4106int
4107SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4108{
4109/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4110 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4111 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4112 FILE_SYSTEM_ALLOC_INFO *response_data;
4113 int rc = 0;
4114 int bytes_returned = 0;
4115 __u16 params, byte_count;
4116
4117 cFYI(1, ("OldQFSInfo"));
4118oldQFSInfoRetry:
4119 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4120 (void **) &pSMBr);
4121 if (rc)
4122 return rc;
Steve French20962432005-09-21 22:05:57 -07004123
4124 params = 2; /* level */
4125 pSMB->TotalDataCount = 0;
4126 pSMB->MaxParameterCount = cpu_to_le16(2);
4127 pSMB->MaxDataCount = cpu_to_le16(1000);
4128 pSMB->MaxSetupCount = 0;
4129 pSMB->Reserved = 0;
4130 pSMB->Flags = 0;
4131 pSMB->Timeout = 0;
4132 pSMB->Reserved2 = 0;
4133 byte_count = params + 1 /* pad */ ;
4134 pSMB->TotalParameterCount = cpu_to_le16(params);
4135 pSMB->ParameterCount = pSMB->TotalParameterCount;
4136 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4137 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4138 pSMB->DataCount = 0;
4139 pSMB->DataOffset = 0;
4140 pSMB->SetupCount = 1;
4141 pSMB->Reserved3 = 0;
4142 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4143 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4144 pSMB->hdr.smb_buf_length += byte_count;
4145 pSMB->ByteCount = cpu_to_le16(byte_count);
4146
4147 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4148 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4149 if (rc) {
4150 cFYI(1, ("Send error in QFSInfo = %d", rc));
4151 } else { /* decode response */
4152 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4153
4154 if (rc || (pSMBr->ByteCount < 18))
4155 rc = -EIO; /* bad smb */
4156 else {
4157 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00004158 cFYI(1, ("qfsinf resp BCC: %d Offset %d",
Steve French20962432005-09-21 22:05:57 -07004159 pSMBr->ByteCount, data_offset));
4160
Steve French50c2f752007-07-13 00:33:32 +00004161 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004162 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4163 FSData->f_bsize =
4164 le16_to_cpu(response_data->BytesPerSector) *
4165 le32_to_cpu(response_data->
4166 SectorsPerAllocationUnit);
4167 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004168 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004169 FSData->f_bfree = FSData->f_bavail =
4170 le32_to_cpu(response_data->FreeAllocationUnits);
4171 cFYI(1,
4172 ("Blocks: %lld Free: %lld Block size %ld",
4173 (unsigned long long)FSData->f_blocks,
4174 (unsigned long long)FSData->f_bfree,
4175 FSData->f_bsize));
4176 }
4177 }
4178 cifs_buf_release(pSMB);
4179
4180 if (rc == -EAGAIN)
4181 goto oldQFSInfoRetry;
4182
4183 return rc;
4184}
4185
Linus Torvalds1da177e2005-04-16 15:20:36 -07004186int
Steve French737b7582005-04-28 22:41:06 -07004187CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004188{
4189/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4190 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4191 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4192 FILE_SYSTEM_INFO *response_data;
4193 int rc = 0;
4194 int bytes_returned = 0;
4195 __u16 params, byte_count;
4196
4197 cFYI(1, ("In QFSInfo"));
4198QFSInfoRetry:
4199 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4200 (void **) &pSMBr);
4201 if (rc)
4202 return rc;
4203
4204 params = 2; /* level */
4205 pSMB->TotalDataCount = 0;
4206 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004207 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004208 pSMB->MaxSetupCount = 0;
4209 pSMB->Reserved = 0;
4210 pSMB->Flags = 0;
4211 pSMB->Timeout = 0;
4212 pSMB->Reserved2 = 0;
4213 byte_count = params + 1 /* pad */ ;
4214 pSMB->TotalParameterCount = cpu_to_le16(params);
4215 pSMB->ParameterCount = pSMB->TotalParameterCount;
4216 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004217 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004218 pSMB->DataCount = 0;
4219 pSMB->DataOffset = 0;
4220 pSMB->SetupCount = 1;
4221 pSMB->Reserved3 = 0;
4222 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4223 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4224 pSMB->hdr.smb_buf_length += byte_count;
4225 pSMB->ByteCount = cpu_to_le16(byte_count);
4226
4227 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4228 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4229 if (rc) {
Steve French20962432005-09-21 22:05:57 -07004230 cFYI(1, ("Send error in QFSInfo = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004231 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004232 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004233
Steve French20962432005-09-21 22:05:57 -07004234 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004235 rc = -EIO; /* bad smb */
4236 else {
4237 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004238
4239 response_data =
4240 (FILE_SYSTEM_INFO
4241 *) (((char *) &pSMBr->hdr.Protocol) +
4242 data_offset);
4243 FSData->f_bsize =
4244 le32_to_cpu(response_data->BytesPerSector) *
4245 le32_to_cpu(response_data->
4246 SectorsPerAllocationUnit);
4247 FSData->f_blocks =
4248 le64_to_cpu(response_data->TotalAllocationUnits);
4249 FSData->f_bfree = FSData->f_bavail =
4250 le64_to_cpu(response_data->FreeAllocationUnits);
4251 cFYI(1,
4252 ("Blocks: %lld Free: %lld Block size %ld",
4253 (unsigned long long)FSData->f_blocks,
4254 (unsigned long long)FSData->f_bfree,
4255 FSData->f_bsize));
4256 }
4257 }
4258 cifs_buf_release(pSMB);
4259
4260 if (rc == -EAGAIN)
4261 goto QFSInfoRetry;
4262
4263 return rc;
4264}
4265
4266int
Steve French737b7582005-04-28 22:41:06 -07004267CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004268{
4269/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4270 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4271 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4272 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4273 int rc = 0;
4274 int bytes_returned = 0;
4275 __u16 params, byte_count;
4276
4277 cFYI(1, ("In QFSAttributeInfo"));
4278QFSAttributeRetry:
4279 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4280 (void **) &pSMBr);
4281 if (rc)
4282 return rc;
4283
4284 params = 2; /* level */
4285 pSMB->TotalDataCount = 0;
4286 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004287 /* BB find exact max SMB PDU from sess structure BB */
4288 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004289 pSMB->MaxSetupCount = 0;
4290 pSMB->Reserved = 0;
4291 pSMB->Flags = 0;
4292 pSMB->Timeout = 0;
4293 pSMB->Reserved2 = 0;
4294 byte_count = params + 1 /* pad */ ;
4295 pSMB->TotalParameterCount = cpu_to_le16(params);
4296 pSMB->ParameterCount = pSMB->TotalParameterCount;
4297 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004298 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004299 pSMB->DataCount = 0;
4300 pSMB->DataOffset = 0;
4301 pSMB->SetupCount = 1;
4302 pSMB->Reserved3 = 0;
4303 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4304 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4305 pSMB->hdr.smb_buf_length += byte_count;
4306 pSMB->ByteCount = cpu_to_le16(byte_count);
4307
4308 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4309 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4310 if (rc) {
4311 cERROR(1, ("Send error in QFSAttributeInfo = %d", rc));
4312 } else { /* decode response */
4313 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4314
Steve French50c2f752007-07-13 00:33:32 +00004315 if (rc || (pSMBr->ByteCount < 13)) {
4316 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004317 rc = -EIO; /* bad smb */
4318 } else {
4319 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4320 response_data =
4321 (FILE_SYSTEM_ATTRIBUTE_INFO
4322 *) (((char *) &pSMBr->hdr.Protocol) +
4323 data_offset);
4324 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004325 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004326 }
4327 }
4328 cifs_buf_release(pSMB);
4329
4330 if (rc == -EAGAIN)
4331 goto QFSAttributeRetry;
4332
4333 return rc;
4334}
4335
4336int
Steve French737b7582005-04-28 22:41:06 -07004337CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004338{
4339/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4340 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4341 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4342 FILE_SYSTEM_DEVICE_INFO *response_data;
4343 int rc = 0;
4344 int bytes_returned = 0;
4345 __u16 params, byte_count;
4346
4347 cFYI(1, ("In QFSDeviceInfo"));
4348QFSDeviceRetry:
4349 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4350 (void **) &pSMBr);
4351 if (rc)
4352 return rc;
4353
4354 params = 2; /* level */
4355 pSMB->TotalDataCount = 0;
4356 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004357 /* BB find exact max SMB PDU from sess structure BB */
4358 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004359 pSMB->MaxSetupCount = 0;
4360 pSMB->Reserved = 0;
4361 pSMB->Flags = 0;
4362 pSMB->Timeout = 0;
4363 pSMB->Reserved2 = 0;
4364 byte_count = params + 1 /* pad */ ;
4365 pSMB->TotalParameterCount = cpu_to_le16(params);
4366 pSMB->ParameterCount = pSMB->TotalParameterCount;
4367 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004368 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004369
4370 pSMB->DataCount = 0;
4371 pSMB->DataOffset = 0;
4372 pSMB->SetupCount = 1;
4373 pSMB->Reserved3 = 0;
4374 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4375 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4376 pSMB->hdr.smb_buf_length += byte_count;
4377 pSMB->ByteCount = cpu_to_le16(byte_count);
4378
4379 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4380 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4381 if (rc) {
4382 cFYI(1, ("Send error in QFSDeviceInfo = %d", rc));
4383 } else { /* decode response */
4384 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4385
Steve French630f3f02007-10-25 21:17:17 +00004386 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004387 rc = -EIO; /* bad smb */
4388 else {
4389 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4390 response_data =
Steve French737b7582005-04-28 22:41:06 -07004391 (FILE_SYSTEM_DEVICE_INFO *)
4392 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004393 data_offset);
4394 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004395 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004396 }
4397 }
4398 cifs_buf_release(pSMB);
4399
4400 if (rc == -EAGAIN)
4401 goto QFSDeviceRetry;
4402
4403 return rc;
4404}
4405
4406int
Steve French737b7582005-04-28 22:41:06 -07004407CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004408{
4409/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4410 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4411 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4412 FILE_SYSTEM_UNIX_INFO *response_data;
4413 int rc = 0;
4414 int bytes_returned = 0;
4415 __u16 params, byte_count;
4416
4417 cFYI(1, ("In QFSUnixInfo"));
4418QFSUnixRetry:
4419 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4420 (void **) &pSMBr);
4421 if (rc)
4422 return rc;
4423
4424 params = 2; /* level */
4425 pSMB->TotalDataCount = 0;
4426 pSMB->DataCount = 0;
4427 pSMB->DataOffset = 0;
4428 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004429 /* BB find exact max SMB PDU from sess structure BB */
4430 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004431 pSMB->MaxSetupCount = 0;
4432 pSMB->Reserved = 0;
4433 pSMB->Flags = 0;
4434 pSMB->Timeout = 0;
4435 pSMB->Reserved2 = 0;
4436 byte_count = params + 1 /* pad */ ;
4437 pSMB->ParameterCount = cpu_to_le16(params);
4438 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004439 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4440 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004441 pSMB->SetupCount = 1;
4442 pSMB->Reserved3 = 0;
4443 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4444 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4445 pSMB->hdr.smb_buf_length += byte_count;
4446 pSMB->ByteCount = cpu_to_le16(byte_count);
4447
4448 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4449 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4450 if (rc) {
4451 cERROR(1, ("Send error in QFSUnixInfo = %d", rc));
4452 } else { /* decode response */
4453 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4454
4455 if (rc || (pSMBr->ByteCount < 13)) {
4456 rc = -EIO; /* bad smb */
4457 } else {
4458 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4459 response_data =
4460 (FILE_SYSTEM_UNIX_INFO
4461 *) (((char *) &pSMBr->hdr.Protocol) +
4462 data_offset);
4463 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004464 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004465 }
4466 }
4467 cifs_buf_release(pSMB);
4468
4469 if (rc == -EAGAIN)
4470 goto QFSUnixRetry;
4471
4472
4473 return rc;
4474}
4475
Jeremy Allisonac670552005-06-22 17:26:35 -07004476int
Steve French45abc6e2005-06-23 13:42:03 -05004477CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004478{
4479/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4480 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4481 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4482 int rc = 0;
4483 int bytes_returned = 0;
4484 __u16 params, param_offset, offset, byte_count;
4485
4486 cFYI(1, ("In SETFSUnixInfo"));
4487SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004488 /* BB switch to small buf init to save memory */
Jeremy Allisonac670552005-06-22 17:26:35 -07004489 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4490 (void **) &pSMBr);
4491 if (rc)
4492 return rc;
4493
4494 params = 4; /* 2 bytes zero followed by info level. */
4495 pSMB->MaxSetupCount = 0;
4496 pSMB->Reserved = 0;
4497 pSMB->Flags = 0;
4498 pSMB->Timeout = 0;
4499 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004500 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4501 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004502 offset = param_offset + params;
4503
4504 pSMB->MaxParameterCount = cpu_to_le16(4);
Steve French582d21e2008-05-13 04:54:12 +00004505 /* BB find exact max SMB PDU from sess structure BB */
4506 pSMB->MaxDataCount = cpu_to_le16(100);
Jeremy Allisonac670552005-06-22 17:26:35 -07004507 pSMB->SetupCount = 1;
4508 pSMB->Reserved3 = 0;
4509 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4510 byte_count = 1 /* pad */ + params + 12;
4511
4512 pSMB->DataCount = cpu_to_le16(12);
4513 pSMB->ParameterCount = cpu_to_le16(params);
4514 pSMB->TotalDataCount = pSMB->DataCount;
4515 pSMB->TotalParameterCount = pSMB->ParameterCount;
4516 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4517 pSMB->DataOffset = cpu_to_le16(offset);
4518
4519 /* Params. */
4520 pSMB->FileNum = 0;
4521 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4522
4523 /* Data. */
4524 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4525 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4526 pSMB->ClientUnixCap = cpu_to_le64(cap);
4527
4528 pSMB->hdr.smb_buf_length += byte_count;
4529 pSMB->ByteCount = cpu_to_le16(byte_count);
4530
4531 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4532 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4533 if (rc) {
4534 cERROR(1, ("Send error in SETFSUnixInfo = %d", rc));
4535 } else { /* decode response */
4536 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004537 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004538 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004539 }
4540 cifs_buf_release(pSMB);
4541
4542 if (rc == -EAGAIN)
4543 goto SETFSUnixRetry;
4544
4545 return rc;
4546}
4547
4548
Linus Torvalds1da177e2005-04-16 15:20:36 -07004549
4550int
4551CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004552 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004553{
4554/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4555 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4556 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4557 FILE_SYSTEM_POSIX_INFO *response_data;
4558 int rc = 0;
4559 int bytes_returned = 0;
4560 __u16 params, byte_count;
4561
4562 cFYI(1, ("In QFSPosixInfo"));
4563QFSPosixRetry:
4564 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4565 (void **) &pSMBr);
4566 if (rc)
4567 return rc;
4568
4569 params = 2; /* level */
4570 pSMB->TotalDataCount = 0;
4571 pSMB->DataCount = 0;
4572 pSMB->DataOffset = 0;
4573 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004574 /* BB find exact max SMB PDU from sess structure BB */
4575 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004576 pSMB->MaxSetupCount = 0;
4577 pSMB->Reserved = 0;
4578 pSMB->Flags = 0;
4579 pSMB->Timeout = 0;
4580 pSMB->Reserved2 = 0;
4581 byte_count = params + 1 /* pad */ ;
4582 pSMB->ParameterCount = cpu_to_le16(params);
4583 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004584 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4585 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004586 pSMB->SetupCount = 1;
4587 pSMB->Reserved3 = 0;
4588 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4589 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4590 pSMB->hdr.smb_buf_length += byte_count;
4591 pSMB->ByteCount = cpu_to_le16(byte_count);
4592
4593 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4594 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4595 if (rc) {
4596 cFYI(1, ("Send error in QFSUnixInfo = %d", rc));
4597 } else { /* decode response */
4598 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4599
4600 if (rc || (pSMBr->ByteCount < 13)) {
4601 rc = -EIO; /* bad smb */
4602 } else {
4603 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4604 response_data =
4605 (FILE_SYSTEM_POSIX_INFO
4606 *) (((char *) &pSMBr->hdr.Protocol) +
4607 data_offset);
4608 FSData->f_bsize =
4609 le32_to_cpu(response_data->BlockSize);
4610 FSData->f_blocks =
4611 le64_to_cpu(response_data->TotalBlocks);
4612 FSData->f_bfree =
4613 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004614 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004615 FSData->f_bavail = FSData->f_bfree;
4616 } else {
4617 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004618 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004619 }
Steve French790fe572007-07-07 19:25:05 +00004620 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004621 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004622 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004623 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004624 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004625 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004626 }
4627 }
4628 cifs_buf_release(pSMB);
4629
4630 if (rc == -EAGAIN)
4631 goto QFSPosixRetry;
4632
4633 return rc;
4634}
4635
4636
Steve French50c2f752007-07-13 00:33:32 +00004637/* We can not use write of zero bytes trick to
4638 set file size due to need for large file support. Also note that
4639 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004640 routine which is only needed to work around a sharing violation bug
4641 in Samba which this routine can run into */
4642
4643int
4644CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French4b18f2a2008-04-29 00:06:05 +00004645 __u64 size, bool SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004646 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004647{
4648 struct smb_com_transaction2_spi_req *pSMB = NULL;
4649 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4650 struct file_end_of_file_info *parm_data;
4651 int name_len;
4652 int rc = 0;
4653 int bytes_returned = 0;
4654 __u16 params, byte_count, data_count, param_offset, offset;
4655
4656 cFYI(1, ("In SetEOF"));
4657SetEOFRetry:
4658 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4659 (void **) &pSMBr);
4660 if (rc)
4661 return rc;
4662
4663 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4664 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004665 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004666 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004667 name_len++; /* trailing null */
4668 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004669 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004670 name_len = strnlen(fileName, PATH_MAX);
4671 name_len++; /* trailing null */
4672 strncpy(pSMB->FileName, fileName, name_len);
4673 }
4674 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004675 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004676 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004677 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004678 pSMB->MaxSetupCount = 0;
4679 pSMB->Reserved = 0;
4680 pSMB->Flags = 0;
4681 pSMB->Timeout = 0;
4682 pSMB->Reserved2 = 0;
4683 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004684 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004685 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004686 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004687 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4688 pSMB->InformationLevel =
4689 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4690 else
4691 pSMB->InformationLevel =
4692 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4693 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004694 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4695 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004696 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004697 else
4698 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004699 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004700 }
4701
4702 parm_data =
4703 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4704 offset);
4705 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4706 pSMB->DataOffset = cpu_to_le16(offset);
4707 pSMB->SetupCount = 1;
4708 pSMB->Reserved3 = 0;
4709 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4710 byte_count = 3 /* pad */ + params + data_count;
4711 pSMB->DataCount = cpu_to_le16(data_count);
4712 pSMB->TotalDataCount = pSMB->DataCount;
4713 pSMB->ParameterCount = cpu_to_le16(params);
4714 pSMB->TotalParameterCount = pSMB->ParameterCount;
4715 pSMB->Reserved4 = 0;
4716 pSMB->hdr.smb_buf_length += byte_count;
4717 parm_data->FileSize = cpu_to_le64(size);
4718 pSMB->ByteCount = cpu_to_le16(byte_count);
4719 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4720 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004721 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004722 cFYI(1, ("SetPathInfo (file size) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004723
4724 cifs_buf_release(pSMB);
4725
4726 if (rc == -EAGAIN)
4727 goto SetEOFRetry;
4728
4729 return rc;
4730}
4731
4732int
Steve French50c2f752007-07-13 00:33:32 +00004733CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
Steve French4b18f2a2008-04-29 00:06:05 +00004734 __u16 fid, __u32 pid_of_opener, bool SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004735{
4736 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004737 char *data_offset;
4738 struct file_end_of_file_info *parm_data;
4739 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004740 __u16 params, param_offset, offset, byte_count, count;
4741
4742 cFYI(1, ("SetFileSize (via SetFileInfo) %lld",
4743 (long long)size));
Steve Frenchcd634992005-04-28 22:41:10 -07004744 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4745
Linus Torvalds1da177e2005-04-16 15:20:36 -07004746 if (rc)
4747 return rc;
4748
4749 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4750 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004751
Linus Torvalds1da177e2005-04-16 15:20:36 -07004752 params = 6;
4753 pSMB->MaxSetupCount = 0;
4754 pSMB->Reserved = 0;
4755 pSMB->Flags = 0;
4756 pSMB->Timeout = 0;
4757 pSMB->Reserved2 = 0;
4758 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4759 offset = param_offset + params;
4760
Steve French50c2f752007-07-13 00:33:32 +00004761 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004762
4763 count = sizeof(struct file_end_of_file_info);
4764 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004765 /* BB find exact max SMB PDU from sess structure BB */
4766 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004767 pSMB->SetupCount = 1;
4768 pSMB->Reserved3 = 0;
4769 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4770 byte_count = 3 /* pad */ + params + count;
4771 pSMB->DataCount = cpu_to_le16(count);
4772 pSMB->ParameterCount = cpu_to_le16(params);
4773 pSMB->TotalDataCount = pSMB->DataCount;
4774 pSMB->TotalParameterCount = pSMB->ParameterCount;
4775 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4776 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004777 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4778 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004779 pSMB->DataOffset = cpu_to_le16(offset);
4780 parm_data->FileSize = cpu_to_le64(size);
4781 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004782 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004783 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4784 pSMB->InformationLevel =
4785 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4786 else
4787 pSMB->InformationLevel =
4788 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004789 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004790 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4791 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004792 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004793 else
4794 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004795 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004796 }
4797 pSMB->Reserved4 = 0;
4798 pSMB->hdr.smb_buf_length += byte_count;
4799 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004800 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004801 if (rc) {
4802 cFYI(1,
4803 ("Send error in SetFileInfo (SetFileSize) = %d",
4804 rc));
4805 }
4806
Steve French50c2f752007-07-13 00:33:32 +00004807 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004808 since file handle passed in no longer valid */
4809
4810 return rc;
4811}
4812
Steve French50c2f752007-07-13 00:33:32 +00004813/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004814 an open handle, rather than by pathname - this is awkward due to
4815 potential access conflicts on the open, but it is unavoidable for these
4816 old servers since the only other choice is to go from 100 nanosecond DCE
4817 time and resort to the original setpathinfo level which takes the ancient
4818 DOS time format with 2 second granularity */
4819int
Steve French50c2f752007-07-13 00:33:32 +00004820CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon,
4821 const FILE_BASIC_INFO *data, __u16 fid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004822{
4823 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004824 char *data_offset;
4825 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004826 __u16 params, param_offset, offset, byte_count, count;
4827
4828 cFYI(1, ("Set Times (via SetFileInfo)"));
Steve Frenchcd634992005-04-28 22:41:10 -07004829 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4830
Linus Torvalds1da177e2005-04-16 15:20:36 -07004831 if (rc)
4832 return rc;
4833
4834 /* At this point there is no need to override the current pid
4835 with the pid of the opener, but that could change if we someday
4836 use an existing handle (rather than opening one on the fly) */
4837 /* pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4838 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));*/
Steve French50c2f752007-07-13 00:33:32 +00004839
Linus Torvalds1da177e2005-04-16 15:20:36 -07004840 params = 6;
4841 pSMB->MaxSetupCount = 0;
4842 pSMB->Reserved = 0;
4843 pSMB->Flags = 0;
4844 pSMB->Timeout = 0;
4845 pSMB->Reserved2 = 0;
4846 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4847 offset = param_offset + params;
4848
Steve French50c2f752007-07-13 00:33:32 +00004849 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004850
Steve French26f57362007-08-30 22:09:15 +00004851 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004852 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004853 /* BB find max SMB PDU from sess */
4854 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004855 pSMB->SetupCount = 1;
4856 pSMB->Reserved3 = 0;
4857 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4858 byte_count = 3 /* pad */ + params + count;
4859 pSMB->DataCount = cpu_to_le16(count);
4860 pSMB->ParameterCount = cpu_to_le16(params);
4861 pSMB->TotalDataCount = pSMB->DataCount;
4862 pSMB->TotalParameterCount = pSMB->ParameterCount;
4863 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4864 pSMB->DataOffset = cpu_to_le16(offset);
4865 pSMB->Fid = fid;
4866 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4867 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4868 else
4869 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
4870 pSMB->Reserved4 = 0;
4871 pSMB->hdr.smb_buf_length += byte_count;
4872 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00004873 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00004874 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004875 if (rc)
Steve French50c2f752007-07-13 00:33:32 +00004876 cFYI(1, ("Send error in Set Time (SetFileInfo) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004877
Steve French50c2f752007-07-13 00:33:32 +00004878 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004879 since file handle passed in no longer valid */
4880
4881 return rc;
4882}
4883
4884
4885int
4886CIFSSMBSetTimes(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00004887 const FILE_BASIC_INFO *data,
Steve French737b7582005-04-28 22:41:06 -07004888 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004889{
4890 TRANSACTION2_SPI_REQ *pSMB = NULL;
4891 TRANSACTION2_SPI_RSP *pSMBr = NULL;
4892 int name_len;
4893 int rc = 0;
4894 int bytes_returned = 0;
4895 char *data_offset;
4896 __u16 params, param_offset, offset, byte_count, count;
4897
4898 cFYI(1, ("In SetTimes"));
4899
4900SetTimesRetry:
4901 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4902 (void **) &pSMBr);
4903 if (rc)
4904 return rc;
4905
4906 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4907 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004908 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004909 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004910 name_len++; /* trailing null */
4911 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004912 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004913 name_len = strnlen(fileName, PATH_MAX);
4914 name_len++; /* trailing null */
4915 strncpy(pSMB->FileName, fileName, name_len);
4916 }
4917
4918 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004919 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004920 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004921 /* BB find max SMB PDU from sess structure BB */
4922 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004923 pSMB->MaxSetupCount = 0;
4924 pSMB->Reserved = 0;
4925 pSMB->Flags = 0;
4926 pSMB->Timeout = 0;
4927 pSMB->Reserved2 = 0;
4928 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004929 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004930 offset = param_offset + params;
4931 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
4932 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4933 pSMB->DataOffset = cpu_to_le16(offset);
4934 pSMB->SetupCount = 1;
4935 pSMB->Reserved3 = 0;
4936 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4937 byte_count = 3 /* pad */ + params + count;
4938
4939 pSMB->DataCount = cpu_to_le16(count);
4940 pSMB->ParameterCount = cpu_to_le16(params);
4941 pSMB->TotalDataCount = pSMB->DataCount;
4942 pSMB->TotalParameterCount = pSMB->ParameterCount;
4943 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4944 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4945 else
4946 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
4947 pSMB->Reserved4 = 0;
4948 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00004949 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004950 pSMB->ByteCount = cpu_to_le16(byte_count);
4951 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4952 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004953 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004954 cFYI(1, ("SetPathInfo (times) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004955
4956 cifs_buf_release(pSMB);
4957
4958 if (rc == -EAGAIN)
4959 goto SetTimesRetry;
4960
4961 return rc;
4962}
4963
4964/* Can not be used to set time stamps yet (due to old DOS time format) */
4965/* Can be used to set attributes */
4966#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
4967 handling it anyway and NT4 was what we thought it would be needed for
4968 Do not delete it until we prove whether needed for Win9x though */
4969int
4970CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
4971 __u16 dos_attrs, const struct nls_table *nls_codepage)
4972{
4973 SETATTR_REQ *pSMB = NULL;
4974 SETATTR_RSP *pSMBr = NULL;
4975 int rc = 0;
4976 int bytes_returned;
4977 int name_len;
4978
4979 cFYI(1, ("In SetAttrLegacy"));
4980
4981SetAttrLgcyRetry:
4982 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
4983 (void **) &pSMBr);
4984 if (rc)
4985 return rc;
4986
4987 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4988 name_len =
Steve French50c2f752007-07-13 00:33:32 +00004989 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004990 PATH_MAX, nls_codepage);
4991 name_len++; /* trailing null */
4992 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004993 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004994 name_len = strnlen(fileName, PATH_MAX);
4995 name_len++; /* trailing null */
4996 strncpy(pSMB->fileName, fileName, name_len);
4997 }
4998 pSMB->attr = cpu_to_le16(dos_attrs);
4999 pSMB->BufferFormat = 0x04;
5000 pSMB->hdr.smb_buf_length += name_len + 1;
5001 pSMB->ByteCount = cpu_to_le16(name_len + 1);
5002 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5003 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005004 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005005 cFYI(1, ("Error in LegacySetAttr = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005006
5007 cifs_buf_release(pSMB);
5008
5009 if (rc == -EAGAIN)
5010 goto SetAttrLgcyRetry;
5011
5012 return rc;
5013}
5014#endif /* temporarily unneeded SetAttr legacy function */
5015
5016int
5017CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00005018 char *fileName, __u64 mode, __u64 uid, __u64 gid,
5019 dev_t device, const struct nls_table *nls_codepage,
Steve French737b7582005-04-28 22:41:06 -07005020 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005021{
5022 TRANSACTION2_SPI_REQ *pSMB = NULL;
5023 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5024 int name_len;
5025 int rc = 0;
5026 int bytes_returned = 0;
5027 FILE_UNIX_BASIC_INFO *data_offset;
5028 __u16 params, param_offset, offset, count, byte_count;
5029
5030 cFYI(1, ("In SetUID/GID/Mode"));
5031setPermsRetry:
5032 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5033 (void **) &pSMBr);
5034 if (rc)
5035 return rc;
5036
5037 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5038 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005039 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005040 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005041 name_len++; /* trailing null */
5042 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07005043 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005044 name_len = strnlen(fileName, PATH_MAX);
5045 name_len++; /* trailing null */
5046 strncpy(pSMB->FileName, fileName, name_len);
5047 }
5048
5049 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005050 count = sizeof(FILE_UNIX_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005051 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005052 /* BB find max SMB PDU from sess structure BB */
5053 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005054 pSMB->MaxSetupCount = 0;
5055 pSMB->Reserved = 0;
5056 pSMB->Flags = 0;
5057 pSMB->Timeout = 0;
5058 pSMB->Reserved2 = 0;
5059 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005060 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005061 offset = param_offset + params;
5062 data_offset =
5063 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5064 offset);
5065 memset(data_offset, 0, count);
5066 pSMB->DataOffset = cpu_to_le16(offset);
5067 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5068 pSMB->SetupCount = 1;
5069 pSMB->Reserved3 = 0;
5070 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5071 byte_count = 3 /* pad */ + params + count;
5072 pSMB->ParameterCount = cpu_to_le16(params);
5073 pSMB->DataCount = cpu_to_le16(count);
5074 pSMB->TotalParameterCount = pSMB->ParameterCount;
5075 pSMB->TotalDataCount = pSMB->DataCount;
5076 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5077 pSMB->Reserved4 = 0;
5078 pSMB->hdr.smb_buf_length += byte_count;
Steve Frenchc7af1852007-03-01 04:11:22 +00005079 /* Samba server ignores set of file size to zero due to bugs in some
5080 older clients, but we should be precise - we use SetFileSize to
5081 set file size and do not want to truncate file size to zero
5082 accidently as happened on one Samba server beta by putting
Steve French50c2f752007-07-13 00:33:32 +00005083 zero instead of -1 here */
Steve Frenchc7af1852007-03-01 04:11:22 +00005084 data_offset->EndOfFile = NO_CHANGE_64;
5085 data_offset->NumOfBytes = NO_CHANGE_64;
5086 data_offset->LastStatusChange = NO_CHANGE_64;
5087 data_offset->LastAccessTime = NO_CHANGE_64;
5088 data_offset->LastModificationTime = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005089 data_offset->Uid = cpu_to_le64(uid);
5090 data_offset->Gid = cpu_to_le64(gid);
5091 /* better to leave device as zero when it is */
5092 data_offset->DevMajor = cpu_to_le64(MAJOR(device));
5093 data_offset->DevMinor = cpu_to_le64(MINOR(device));
5094 data_offset->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00005095
Steve French790fe572007-07-07 19:25:05 +00005096 if (S_ISREG(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005097 data_offset->Type = cpu_to_le32(UNIX_FILE);
Steve French790fe572007-07-07 19:25:05 +00005098 else if (S_ISDIR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005099 data_offset->Type = cpu_to_le32(UNIX_DIR);
Steve French790fe572007-07-07 19:25:05 +00005100 else if (S_ISLNK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005101 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
Steve French790fe572007-07-07 19:25:05 +00005102 else if (S_ISCHR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005103 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
Steve French790fe572007-07-07 19:25:05 +00005104 else if (S_ISBLK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005105 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
Steve French790fe572007-07-07 19:25:05 +00005106 else if (S_ISFIFO(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005107 data_offset->Type = cpu_to_le32(UNIX_FIFO);
Steve French790fe572007-07-07 19:25:05 +00005108 else if (S_ISSOCK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005109 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5110
5111
5112 pSMB->ByteCount = cpu_to_le16(byte_count);
5113 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5114 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005115 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005116 cFYI(1, ("SetPathInfo (perms) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005117
Steve French0d817bc2008-05-22 02:02:03 +00005118 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005119 if (rc == -EAGAIN)
5120 goto setPermsRetry;
5121 return rc;
5122}
5123
Steve French50c2f752007-07-13 00:33:32 +00005124int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005125 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005126 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005127 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005128{
5129 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005130 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5131 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005132 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005133 int bytes_returned;
5134
Steve French50c2f752007-07-13 00:33:32 +00005135 cFYI(1, ("In CIFSSMBNotify for file handle %d", (int)netfid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005136 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005137 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005138 if (rc)
5139 return rc;
5140
5141 pSMB->TotalParameterCount = 0 ;
5142 pSMB->TotalDataCount = 0;
5143 pSMB->MaxParameterCount = cpu_to_le32(2);
5144 /* BB find exact data count max from sess structure BB */
5145 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005146/* BB VERIFY verify which is correct for above BB */
5147 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5148 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5149
Linus Torvalds1da177e2005-04-16 15:20:36 -07005150 pSMB->MaxSetupCount = 4;
5151 pSMB->Reserved = 0;
5152 pSMB->ParameterOffset = 0;
5153 pSMB->DataCount = 0;
5154 pSMB->DataOffset = 0;
5155 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5156 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5157 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005158 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005159 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5160 pSMB->Reserved2 = 0;
5161 pSMB->CompletionFilter = cpu_to_le32(filter);
5162 pSMB->Fid = netfid; /* file handle always le */
5163 pSMB->ByteCount = 0;
5164
5165 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005166 (struct smb_hdr *)pSMBr, &bytes_returned,
5167 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005168 if (rc) {
5169 cFYI(1, ("Error in Notify = %d", rc));
Steve Frenchff5dbd92005-08-24 17:10:36 -07005170 } else {
5171 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005172 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005173 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005174 sizeof(struct dir_notify_req),
5175 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005176 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005177 dnotify_req->Pid = pSMB->hdr.Pid;
5178 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5179 dnotify_req->Mid = pSMB->hdr.Mid;
5180 dnotify_req->Tid = pSMB->hdr.Tid;
5181 dnotify_req->Uid = pSMB->hdr.Uid;
5182 dnotify_req->netfid = netfid;
5183 dnotify_req->pfile = pfile;
5184 dnotify_req->filter = filter;
5185 dnotify_req->multishot = multishot;
5186 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005187 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005188 &GlobalDnotifyReqList);
5189 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005190 } else
Steve French47c786e2005-10-11 20:03:18 -07005191 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005192 }
5193 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005194 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005195}
5196#ifdef CONFIG_CIFS_XATTR
5197ssize_t
5198CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
5199 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00005200 char *EAData, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005201 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005202{
5203 /* BB assumes one setup word */
5204 TRANSACTION2_QPI_REQ *pSMB = NULL;
5205 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5206 int rc = 0;
5207 int bytes_returned;
5208 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005209 struct fea *temp_fea;
5210 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005211 __u16 params, byte_count;
5212
5213 cFYI(1, ("In Query All EAs path %s", searchName));
5214QAllEAsRetry:
5215 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5216 (void **) &pSMBr);
5217 if (rc)
5218 return rc;
5219
5220 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5221 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005222 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005223 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005224 name_len++; /* trailing null */
5225 name_len *= 2;
5226 } else { /* BB improve the check for buffer overruns BB */
5227 name_len = strnlen(searchName, PATH_MAX);
5228 name_len++; /* trailing null */
5229 strncpy(pSMB->FileName, searchName, name_len);
5230 }
5231
Steve French50c2f752007-07-13 00:33:32 +00005232 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005233 pSMB->TotalDataCount = 0;
5234 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005235 /* BB find exact max SMB PDU from sess structure BB */
5236 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005237 pSMB->MaxSetupCount = 0;
5238 pSMB->Reserved = 0;
5239 pSMB->Flags = 0;
5240 pSMB->Timeout = 0;
5241 pSMB->Reserved2 = 0;
5242 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005243 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005244 pSMB->DataCount = 0;
5245 pSMB->DataOffset = 0;
5246 pSMB->SetupCount = 1;
5247 pSMB->Reserved3 = 0;
5248 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5249 byte_count = params + 1 /* pad */ ;
5250 pSMB->TotalParameterCount = cpu_to_le16(params);
5251 pSMB->ParameterCount = pSMB->TotalParameterCount;
5252 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5253 pSMB->Reserved4 = 0;
5254 pSMB->hdr.smb_buf_length += byte_count;
5255 pSMB->ByteCount = cpu_to_le16(byte_count);
5256
5257 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5258 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5259 if (rc) {
5260 cFYI(1, ("Send error in QueryAllEAs = %d", rc));
5261 } else { /* decode response */
5262 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5263
5264 /* BB also check enough total bytes returned */
5265 /* BB we need to improve the validity checking
5266 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005267 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005268 rc = -EIO; /* bad smb */
5269 /* else if (pFindData){
5270 memcpy((char *) pFindData,
5271 (char *) &pSMBr->hdr.Protocol +
5272 data_offset, kl);
5273 }*/ else {
5274 /* check that length of list is not more than bcc */
5275 /* check that each entry does not go beyond length
5276 of list */
5277 /* check that each element of each entry does not
5278 go beyond end of list */
5279 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005280 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005281 rc = 0;
5282 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005283 /* BB check if start of smb + data_offset > &bcc+ bcc */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005284 ea_response_data = (struct fealist *)
5285 (((char *) &pSMBr->hdr.Protocol) +
5286 data_offset);
5287 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005288 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005289 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005290 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005291 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005292 } else {
5293 /* account for ea list len */
5294 name_len -= 4;
5295 temp_fea = ea_response_data->list;
5296 temp_ptr = (char *)temp_fea;
Steve French50c2f752007-07-13 00:33:32 +00005297 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005298 __u16 value_len;
5299 name_len -= 4;
5300 temp_ptr += 4;
5301 rc += temp_fea->name_len;
5302 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005303 rc = rc + 5 + 1;
5304 if (rc < (int)buf_size) {
Steve French50c2f752007-07-13 00:33:32 +00005305 memcpy(EAData, "user.", 5);
5306 EAData += 5;
5307 memcpy(EAData, temp_ptr,
5308 temp_fea->name_len);
5309 EAData += temp_fea->name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005310 /* null terminate name */
5311 *EAData = 0;
5312 EAData = EAData + 1;
Steve French790fe572007-07-07 19:25:05 +00005313 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005314 /* skip copy - calc size only */
5315 } else {
5316 /* stop before overrun buffer */
5317 rc = -ERANGE;
5318 break;
5319 }
5320 name_len -= temp_fea->name_len;
5321 temp_ptr += temp_fea->name_len;
5322 /* account for trailing null */
5323 name_len--;
5324 temp_ptr++;
Steve French50c2f752007-07-13 00:33:32 +00005325 value_len =
5326 le16_to_cpu(temp_fea->value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005327 name_len -= value_len;
5328 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005329 /* BB check that temp_ptr is still
5330 within the SMB BB*/
5331
5332 /* no trailing null to account for
5333 in value len */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005334 /* go on to next EA */
5335 temp_fea = (struct fea *)temp_ptr;
5336 }
5337 }
5338 }
5339 }
Steve French0d817bc2008-05-22 02:02:03 +00005340 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005341 if (rc == -EAGAIN)
5342 goto QAllEAsRetry;
5343
5344 return (ssize_t)rc;
5345}
5346
Steve French50c2f752007-07-13 00:33:32 +00005347ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon,
5348 const unsigned char *searchName, const unsigned char *ea_name,
5349 unsigned char *ea_value, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005350 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005351{
5352 TRANSACTION2_QPI_REQ *pSMB = NULL;
5353 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5354 int rc = 0;
5355 int bytes_returned;
5356 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005357 struct fea *temp_fea;
5358 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005359 __u16 params, byte_count;
5360
5361 cFYI(1, ("In Query EA path %s", searchName));
5362QEARetry:
5363 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5364 (void **) &pSMBr);
5365 if (rc)
5366 return rc;
5367
5368 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5369 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005370 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005371 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005372 name_len++; /* trailing null */
5373 name_len *= 2;
5374 } else { /* BB improve the check for buffer overruns BB */
5375 name_len = strnlen(searchName, PATH_MAX);
5376 name_len++; /* trailing null */
5377 strncpy(pSMB->FileName, searchName, name_len);
5378 }
5379
Steve French50c2f752007-07-13 00:33:32 +00005380 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005381 pSMB->TotalDataCount = 0;
5382 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005383 /* BB find exact max SMB PDU from sess structure BB */
5384 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005385 pSMB->MaxSetupCount = 0;
5386 pSMB->Reserved = 0;
5387 pSMB->Flags = 0;
5388 pSMB->Timeout = 0;
5389 pSMB->Reserved2 = 0;
5390 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005391 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005392 pSMB->DataCount = 0;
5393 pSMB->DataOffset = 0;
5394 pSMB->SetupCount = 1;
5395 pSMB->Reserved3 = 0;
5396 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5397 byte_count = params + 1 /* pad */ ;
5398 pSMB->TotalParameterCount = cpu_to_le16(params);
5399 pSMB->ParameterCount = pSMB->TotalParameterCount;
5400 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5401 pSMB->Reserved4 = 0;
5402 pSMB->hdr.smb_buf_length += byte_count;
5403 pSMB->ByteCount = cpu_to_le16(byte_count);
5404
5405 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5406 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5407 if (rc) {
5408 cFYI(1, ("Send error in Query EA = %d", rc));
5409 } else { /* decode response */
5410 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5411
5412 /* BB also check enough total bytes returned */
5413 /* BB we need to improve the validity checking
5414 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005415 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005416 rc = -EIO; /* bad smb */
5417 /* else if (pFindData){
5418 memcpy((char *) pFindData,
5419 (char *) &pSMBr->hdr.Protocol +
5420 data_offset, kl);
5421 }*/ else {
5422 /* check that length of list is not more than bcc */
5423 /* check that each entry does not go beyond length
5424 of list */
5425 /* check that each element of each entry does not
5426 go beyond end of list */
5427 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005428 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005429 rc = -ENODATA;
5430 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005431 /* BB check if start of smb + data_offset > &bcc+ bcc*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07005432 ea_response_data = (struct fealist *)
5433 (((char *) &pSMBr->hdr.Protocol) +
5434 data_offset);
5435 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005436 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005437 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005438 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005439 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005440 } else {
5441 /* account for ea list len */
5442 name_len -= 4;
5443 temp_fea = ea_response_data->list;
5444 temp_ptr = (char *)temp_fea;
5445 /* loop through checking if we have a matching
5446 name and then return the associated value */
Steve French50c2f752007-07-13 00:33:32 +00005447 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005448 __u16 value_len;
5449 name_len -= 4;
5450 temp_ptr += 4;
Steve French50c2f752007-07-13 00:33:32 +00005451 value_len =
5452 le16_to_cpu(temp_fea->value_len);
5453 /* BB validate that value_len falls within SMB,
5454 even though maximum for name_len is 255 */
Steve French790fe572007-07-07 19:25:05 +00005455 if (memcmp(temp_fea->name, ea_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005456 temp_fea->name_len) == 0) {
5457 /* found a match */
5458 rc = value_len;
5459 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005460 if (rc <= (int)buf_size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005461 memcpy(ea_value,
5462 temp_fea->name+temp_fea->name_len+1,
5463 rc);
Steve French50c2f752007-07-13 00:33:32 +00005464 /* ea values, unlike ea
5465 names, are not null
5466 terminated */
Steve French790fe572007-07-07 19:25:05 +00005467 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005468 /* skip copy - calc size only */
5469 } else {
Steve French50c2f752007-07-13 00:33:32 +00005470 /* stop before overrun buffer */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005471 rc = -ERANGE;
5472 }
5473 break;
5474 }
5475 name_len -= temp_fea->name_len;
5476 temp_ptr += temp_fea->name_len;
5477 /* account for trailing null */
5478 name_len--;
5479 temp_ptr++;
5480 name_len -= value_len;
5481 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005482 /* No trailing null to account for in
5483 value_len. Go on to next EA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005484 temp_fea = (struct fea *)temp_ptr;
5485 }
Steve French50c2f752007-07-13 00:33:32 +00005486 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005487 }
5488 }
Steve French0d817bc2008-05-22 02:02:03 +00005489 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005490 if (rc == -EAGAIN)
5491 goto QEARetry;
5492
5493 return (ssize_t)rc;
5494}
5495
5496int
5497CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005498 const char *ea_name, const void *ea_value,
5499 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5500 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005501{
5502 struct smb_com_transaction2_spi_req *pSMB = NULL;
5503 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5504 struct fealist *parm_data;
5505 int name_len;
5506 int rc = 0;
5507 int bytes_returned = 0;
5508 __u16 params, param_offset, byte_count, offset, count;
5509
5510 cFYI(1, ("In SetEA"));
5511SetEARetry:
5512 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5513 (void **) &pSMBr);
5514 if (rc)
5515 return rc;
5516
5517 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5518 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005519 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005520 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005521 name_len++; /* trailing null */
5522 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005523 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005524 name_len = strnlen(fileName, PATH_MAX);
5525 name_len++; /* trailing null */
5526 strncpy(pSMB->FileName, fileName, name_len);
5527 }
5528
5529 params = 6 + name_len;
5530
5531 /* done calculating parms using name_len of file name,
5532 now use name_len to calculate length of ea name
5533 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005534 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005535 name_len = 0;
5536 else
Steve French50c2f752007-07-13 00:33:32 +00005537 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005538
Steve Frenchdae5dbd2007-12-30 23:49:57 +00005539 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005540 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005541 /* BB find max SMB PDU from sess */
5542 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005543 pSMB->MaxSetupCount = 0;
5544 pSMB->Reserved = 0;
5545 pSMB->Flags = 0;
5546 pSMB->Timeout = 0;
5547 pSMB->Reserved2 = 0;
5548 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005549 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005550 offset = param_offset + params;
5551 pSMB->InformationLevel =
5552 cpu_to_le16(SMB_SET_FILE_EA);
5553
5554 parm_data =
5555 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5556 offset);
5557 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5558 pSMB->DataOffset = cpu_to_le16(offset);
5559 pSMB->SetupCount = 1;
5560 pSMB->Reserved3 = 0;
5561 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5562 byte_count = 3 /* pad */ + params + count;
5563 pSMB->DataCount = cpu_to_le16(count);
5564 parm_data->list_len = cpu_to_le32(count);
5565 parm_data->list[0].EA_flags = 0;
5566 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005567 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005568 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005569 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005570 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005571 parm_data->list[0].name[name_len] = 0;
5572 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5573 /* caller ensures that ea_value_len is less than 64K but
5574 we need to ensure that it fits within the smb */
5575
Steve French50c2f752007-07-13 00:33:32 +00005576 /*BB add length check to see if it would fit in
5577 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005578 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5579 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005580 memcpy(parm_data->list[0].name+name_len+1,
5581 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005582
5583 pSMB->TotalDataCount = pSMB->DataCount;
5584 pSMB->ParameterCount = cpu_to_le16(params);
5585 pSMB->TotalParameterCount = pSMB->ParameterCount;
5586 pSMB->Reserved4 = 0;
5587 pSMB->hdr.smb_buf_length += byte_count;
5588 pSMB->ByteCount = cpu_to_le16(byte_count);
5589 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5590 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005591 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005592 cFYI(1, ("SetPathInfo (EA) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005593
5594 cifs_buf_release(pSMB);
5595
5596 if (rc == -EAGAIN)
5597 goto SetEARetry;
5598
5599 return rc;
5600}
5601
5602#endif