blob: 6f8ed93a4ae8671fbbffbb7d761e5742623cc06a [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;
110 return 0;
111
112cifs_strncpy_to_host_ErrExit:
113 cERROR(1, ("Failed to allocate buffer for string\n"));
114 return -ENOMEM;
115}
116
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
118/* Mark as invalid, all open files on tree connections since they
119 were closed when session to server was lost */
Steve French790fe572007-07-07 19:25:05 +0000120static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121{
122 struct cifsFileInfo *open_file = NULL;
Steve French790fe572007-07-07 19:25:05 +0000123 struct list_head *tmp;
124 struct list_head *tmp1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
126/* list all files open on tree connection and mark them invalid */
127 write_lock(&GlobalSMBSeslock);
128 list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
Steve French790fe572007-07-07 19:25:05 +0000129 open_file = list_entry(tmp, struct cifsFileInfo, tlist);
Steve French26f57362007-08-30 22:09:15 +0000130 if (open_file)
Steve French4b18f2a2008-04-29 00:06:05 +0000131 open_file->invalidHandle = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 }
133 write_unlock(&GlobalSMBSeslock);
Steve French09d1db52005-04-28 22:41:08 -0700134 /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
135 to this tcon */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136}
137
Steve Frenchad7a2922008-02-07 23:25:02 +0000138/* Allocate and return pointer to an SMB request buffer, and set basic
139 SMB information in the SMB header. If the return code is zero, this
140 function must have filled in request_buf pointer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141static int
142small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000143 void **request_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144{
145 int rc = 0;
146
147 /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
148 check for tcp and smb session status done differently
149 for those three - in the calling routine */
Steve French790fe572007-07-07 19:25:05 +0000150 if (tcon) {
151 if (tcon->tidStatus == CifsExiting) {
Steve French6ab16d22005-11-29 20:55:11 -0800152 /* only tree disconnect, open, and write,
153 (and ulogoff which does not have tcon)
154 are allowed as we start force umount */
Steve French790fe572007-07-07 19:25:05 +0000155 if ((smb_command != SMB_COM_WRITE_ANDX) &&
Steve French50c2f752007-07-13 00:33:32 +0000156 (smb_command != SMB_COM_OPEN_ANDX) &&
Steve French6ab16d22005-11-29 20:55:11 -0800157 (smb_command != SMB_COM_TREE_DISCONNECT)) {
Steve French790fe572007-07-07 19:25:05 +0000158 cFYI(1, ("can not send cmd %d while umounting",
Steve French6ab16d22005-11-29 20:55:11 -0800159 smb_command));
160 return -ENODEV;
161 }
162 }
Steve French790fe572007-07-07 19:25:05 +0000163 if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
Steve French50c2f752007-07-13 00:33:32 +0000164 (tcon->ses->server)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 struct nls_table *nls_codepage;
Steve French50c2f752007-07-13 00:33:32 +0000166 /* Give Demultiplex thread up to 10 seconds to
Steve French09d1db52005-04-28 22:41:08 -0700167 reconnect, should be greater than cifs socket
168 timeout which is 7 seconds */
Steve Frenchc18c8422007-07-18 23:21:09 +0000169 while (tcon->ses->server->tcpStatus ==
Steve French63135e02007-07-17 17:34:02 +0000170 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 wait_event_interruptible_timeout(tcon->ses->server->response_q,
Steve Frenchc18c8422007-07-18 23:21:09 +0000172 (tcon->ses->server->tcpStatus ==
Steve French63135e02007-07-17 17:34:02 +0000173 CifsGood), 10 * HZ);
174 if (tcon->ses->server->tcpStatus ==
175 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 /* on "soft" mounts we wait once */
Steve French4b18f2a2008-04-29 00:06:05 +0000177 if (!tcon->retry ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 (tcon->ses->status == CifsExiting)) {
Steve French50c2f752007-07-13 00:33:32 +0000179 cFYI(1, ("gave up waiting on "
180 "reconnect in smb_init"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 return -EHOSTDOWN;
Steve French09d1db52005-04-28 22:41:08 -0700182 } /* else "hard" mount - keep retrying
183 until process is killed or server
184 comes back on-line */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 } else /* TCP session is reestablished now */
186 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 }
Steve French50c2f752007-07-13 00:33:32 +0000188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 nls_codepage = load_nls_default();
190 /* need to prevent multiple threads trying to
191 simultaneously reconnect the same SMB session */
192 down(&tcon->ses->sesSem);
Steve French790fe572007-07-07 19:25:05 +0000193 if (tcon->ses->status == CifsNeedReconnect)
Steve French50c2f752007-07-13 00:33:32 +0000194 rc = cifs_setup_session(0, tcon->ses,
Steve French09d1db52005-04-28 22:41:08 -0700195 nls_codepage);
Steve French790fe572007-07-07 19:25:05 +0000196 if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 mark_open_files_invalid(tcon);
Steve French50c2f752007-07-13 00:33:32 +0000198 rc = CIFSTCon(0, tcon->ses, tcon->treeName,
Steve French8af18972007-02-14 04:42:51 +0000199 tcon, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 up(&tcon->ses->sesSem);
Steve French3e844692005-10-03 13:37:24 -0700201 /* BB FIXME add code to check if wsize needs
202 update due to negotiated smb buffer size
203 shrinking */
Steve French35028d72008-04-09 20:32:42 +0000204 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 atomic_inc(&tconInfoReconnectCount);
Steve French35028d72008-04-09 20:32:42 +0000206 /* tell server Unix caps we support */
207 if (tcon->ses->capabilities & CAP_UNIX)
208 reset_cifs_unix_caps(
209 0 /* no xid */,
210 tcon,
211 NULL /* we do not know sb */,
212 NULL /* no vol info */);
213 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
215 cFYI(1, ("reconnect tcon rc = %d", rc));
Steve French50c2f752007-07-13 00:33:32 +0000216 /* Removed call to reopen open files here.
217 It is safer (and faster) to reopen files
Steve French09d1db52005-04-28 22:41:08 -0700218 one at a time as needed in read and write */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
Steve French50c2f752007-07-13 00:33:32 +0000220 /* Check if handle based operation so we
Steve French09d1db52005-04-28 22:41:08 -0700221 know whether we can continue or not without
222 returning to caller to reset file handle */
Steve French50c2f752007-07-13 00:33:32 +0000223 switch (smb_command) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 case SMB_COM_READ_ANDX:
225 case SMB_COM_WRITE_ANDX:
226 case SMB_COM_CLOSE:
227 case SMB_COM_FIND_CLOSE2:
228 case SMB_COM_LOCKING_ANDX: {
229 unload_nls(nls_codepage);
230 return -EAGAIN;
231 }
232 }
233 } else {
234 up(&tcon->ses->sesSem);
235 }
236 unload_nls(nls_codepage);
237
238 } else {
239 return -EIO;
240 }
241 }
Steve French790fe572007-07-07 19:25:05 +0000242 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 return rc;
244
245 *request_buf = cifs_small_buf_get();
246 if (*request_buf == NULL) {
247 /* BB should we add a retry in here if not a writepage? */
248 return -ENOMEM;
249 }
250
Steve French63135e02007-07-17 17:34:02 +0000251 header_assemble((struct smb_hdr *) *request_buf, smb_command,
Steve Frenchc18c8422007-07-18 23:21:09 +0000252 tcon, wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Steve French790fe572007-07-07 19:25:05 +0000254 if (tcon != NULL)
255 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700256
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 return rc;
Steve French5815449d2006-02-14 01:36:20 +0000258}
259
Steve French12b3b8f2006-02-09 21:12:47 +0000260int
Steve French50c2f752007-07-13 00:33:32 +0000261small_smb_init_no_tc(const int smb_command, const int wct,
Steve French5815449d2006-02-14 01:36:20 +0000262 struct cifsSesInfo *ses, void **request_buf)
Steve French12b3b8f2006-02-09 21:12:47 +0000263{
264 int rc;
Steve French50c2f752007-07-13 00:33:32 +0000265 struct smb_hdr *buffer;
Steve French12b3b8f2006-02-09 21:12:47 +0000266
Steve French5815449d2006-02-14 01:36:20 +0000267 rc = small_smb_init(smb_command, wct, NULL, request_buf);
Steve French790fe572007-07-07 19:25:05 +0000268 if (rc)
Steve French12b3b8f2006-02-09 21:12:47 +0000269 return rc;
270
Steve French04fdabe2006-02-10 05:52:50 +0000271 buffer = (struct smb_hdr *)*request_buf;
Steve French12b3b8f2006-02-09 21:12:47 +0000272 buffer->Mid = GetNextMid(ses->server);
273 if (ses->capabilities & CAP_UNICODE)
274 buffer->Flags2 |= SMBFLG2_UNICODE;
Steve French04fdabe2006-02-10 05:52:50 +0000275 if (ses->capabilities & CAP_STATUS32)
Steve French12b3b8f2006-02-09 21:12:47 +0000276 buffer->Flags2 |= SMBFLG2_ERR_STATUS;
277
278 /* uid, tid can stay at zero as set in header assemble */
279
Steve French50c2f752007-07-13 00:33:32 +0000280 /* BB add support for turning on the signing when
Steve French12b3b8f2006-02-09 21:12:47 +0000281 this function is used after 1st of session setup requests */
282
283 return rc;
284}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
286/* If the return code is zero, this function must fill in request_buf pointer */
287static int
288smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
289 void **request_buf /* returned */ ,
290 void **response_buf /* returned */ )
291{
292 int rc = 0;
293
294 /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
295 check for tcp and smb session status done differently
296 for those three - in the calling routine */
Steve French790fe572007-07-07 19:25:05 +0000297 if (tcon) {
298 if (tcon->tidStatus == CifsExiting) {
Steve French6ab16d22005-11-29 20:55:11 -0800299 /* only tree disconnect, open, and write,
300 (and ulogoff which does not have tcon)
301 are allowed as we start force umount */
Steve French790fe572007-07-07 19:25:05 +0000302 if ((smb_command != SMB_COM_WRITE_ANDX) &&
Steve French6ab16d22005-11-29 20:55:11 -0800303 (smb_command != SMB_COM_OPEN_ANDX) &&
304 (smb_command != SMB_COM_TREE_DISCONNECT)) {
Steve French790fe572007-07-07 19:25:05 +0000305 cFYI(1, ("can not send cmd %d while umounting",
Steve French6ab16d22005-11-29 20:55:11 -0800306 smb_command));
307 return -ENODEV;
308 }
309 }
310
Steve French790fe572007-07-07 19:25:05 +0000311 if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
Steve French50c2f752007-07-13 00:33:32 +0000312 (tcon->ses->server)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 struct nls_table *nls_codepage;
Steve French09d1db52005-04-28 22:41:08 -0700314 /* Give Demultiplex thread up to 10 seconds to
315 reconnect, should be greater than cifs socket
316 timeout which is 7 seconds */
Steve French63135e02007-07-17 17:34:02 +0000317 while (tcon->ses->server->tcpStatus ==
318 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 wait_event_interruptible_timeout(tcon->ses->server->response_q,
Steve French63135e02007-07-17 17:34:02 +0000320 (tcon->ses->server->tcpStatus ==
321 CifsGood), 10 * HZ);
Steve French790fe572007-07-07 19:25:05 +0000322 if (tcon->ses->server->tcpStatus ==
Steve French09d1db52005-04-28 22:41:08 -0700323 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 /* on "soft" mounts we wait once */
Steve French4b18f2a2008-04-29 00:06:05 +0000325 if (!tcon->retry ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 (tcon->ses->status == CifsExiting)) {
Steve French50c2f752007-07-13 00:33:32 +0000327 cFYI(1, ("gave up waiting on "
328 "reconnect in smb_init"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 return -EHOSTDOWN;
Steve French09d1db52005-04-28 22:41:08 -0700330 } /* else "hard" mount - keep retrying
331 until process is killed or server
332 comes on-line */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 } else /* TCP session is reestablished now */
334 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 nls_codepage = load_nls_default();
337 /* need to prevent multiple threads trying to
338 simultaneously reconnect the same SMB session */
339 down(&tcon->ses->sesSem);
Steve French790fe572007-07-07 19:25:05 +0000340 if (tcon->ses->status == CifsNeedReconnect)
Steve French50c2f752007-07-13 00:33:32 +0000341 rc = cifs_setup_session(0, tcon->ses,
Steve French09d1db52005-04-28 22:41:08 -0700342 nls_codepage);
Steve French790fe572007-07-07 19:25:05 +0000343 if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 mark_open_files_invalid(tcon);
Steve French09d1db52005-04-28 22:41:08 -0700345 rc = CIFSTCon(0, tcon->ses, tcon->treeName,
346 tcon, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 up(&tcon->ses->sesSem);
Steve French3e844692005-10-03 13:37:24 -0700348 /* BB FIXME add code to check if wsize needs
349 update due to negotiated smb buffer size
350 shrinking */
Steve French35028d72008-04-09 20:32:42 +0000351 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 atomic_inc(&tconInfoReconnectCount);
Steve French35028d72008-04-09 20:32:42 +0000353 /* tell server Unix caps we support */
354 if (tcon->ses->capabilities & CAP_UNIX)
355 reset_cifs_unix_caps(
356 0 /* no xid */,
357 tcon,
358 NULL /* do not know sb */,
359 NULL /* no vol info */);
360 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
362 cFYI(1, ("reconnect tcon rc = %d", rc));
Steve French50c2f752007-07-13 00:33:32 +0000363 /* Removed call to reopen open files here.
364 It is safer (and faster) to reopen files
Steve French09d1db52005-04-28 22:41:08 -0700365 one at a time as needed in read and write */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
Steve French50c2f752007-07-13 00:33:32 +0000367 /* Check if handle based operation so we
Steve French09d1db52005-04-28 22:41:08 -0700368 know whether we can continue or not without
369 returning to caller to reset file handle */
Steve French50c2f752007-07-13 00:33:32 +0000370 switch (smb_command) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 case SMB_COM_READ_ANDX:
372 case SMB_COM_WRITE_ANDX:
373 case SMB_COM_CLOSE:
374 case SMB_COM_FIND_CLOSE2:
375 case SMB_COM_LOCKING_ANDX: {
376 unload_nls(nls_codepage);
377 return -EAGAIN;
378 }
379 }
380 } else {
381 up(&tcon->ses->sesSem);
382 }
383 unload_nls(nls_codepage);
384
385 } else {
386 return -EIO;
387 }
388 }
Steve French790fe572007-07-07 19:25:05 +0000389 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 return rc;
391
392 *request_buf = cifs_buf_get();
393 if (*request_buf == NULL) {
394 /* BB should we add a retry in here if not a writepage? */
395 return -ENOMEM;
396 }
397 /* Although the original thought was we needed the response buf for */
398 /* potential retries of smb operations it turns out we can determine */
399 /* from the mid flags when the request buffer can be resent without */
400 /* having to use a second distinct buffer for the response */
Steve French790fe572007-07-07 19:25:05 +0000401 if (response_buf)
Steve French50c2f752007-07-13 00:33:32 +0000402 *response_buf = *request_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
404 header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000405 wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
Steve French790fe572007-07-07 19:25:05 +0000407 if (tcon != NULL)
408 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700409
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 return rc;
411}
412
Steve French50c2f752007-07-13 00:33:32 +0000413static int validate_t2(struct smb_t2_rsp *pSMB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414{
415 int rc = -EINVAL;
416 int total_size;
Steve French50c2f752007-07-13 00:33:32 +0000417 char *pBCC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
419 /* check for plausible wct, bcc and t2 data and parm sizes */
420 /* check for parm and data offset going beyond end of smb */
Steve French790fe572007-07-07 19:25:05 +0000421 if (pSMB->hdr.WordCount >= 10) {
422 if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
424 /* check that bcc is at least as big as parms + data */
425 /* check that bcc is less than negotiated smb buffer */
426 total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
Steve French790fe572007-07-07 19:25:05 +0000427 if (total_size < 512) {
Steve Frenchc18c8422007-07-18 23:21:09 +0000428 total_size +=
Steve French63135e02007-07-17 17:34:02 +0000429 le16_to_cpu(pSMB->t2_rsp.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 /* BCC le converted in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +0000431 pBCC = (pSMB->hdr.WordCount * 2) +
Steve French09d1db52005-04-28 22:41:08 -0700432 sizeof(struct smb_hdr) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 (char *)pSMB;
Steve French790fe572007-07-07 19:25:05 +0000434 if ((total_size <= (*(u16 *)pBCC)) &&
Steve French50c2f752007-07-13 00:33:32 +0000435 (total_size <
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
437 return 0;
438 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 }
440 }
441 }
Steve French50c2f752007-07-13 00:33:32 +0000442 cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 sizeof(struct smb_t2_rsp) + 16);
444 return rc;
445}
446int
447CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
448{
449 NEGOTIATE_REQ *pSMB;
450 NEGOTIATE_RSP *pSMBr;
451 int rc = 0;
452 int bytes_returned;
Steve French39798772006-05-31 22:40:51 +0000453 int i;
Steve French50c2f752007-07-13 00:33:32 +0000454 struct TCP_Server_Info *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 u16 count;
Steve French750d1152006-06-27 06:28:30 +0000456 unsigned int secFlags;
Al Viro733f99a2006-10-14 16:48:26 +0100457 u16 dialect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
Steve French790fe572007-07-07 19:25:05 +0000459 if (ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 server = ses->server;
461 else {
462 rc = -EIO;
463 return rc;
464 }
465 rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
466 (void **) &pSMB, (void **) &pSMBr);
467 if (rc)
468 return rc;
Steve French750d1152006-06-27 06:28:30 +0000469
470 /* if any of auth flags (ie not sign or seal) are overriden use them */
Steve French790fe572007-07-07 19:25:05 +0000471 if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
Steve French762e5ab2007-06-28 18:41:42 +0000472 secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */
Steve French750d1152006-06-27 06:28:30 +0000473 else /* if override flags set only sign/seal OR them with global auth */
474 secFlags = extended_security | ses->overrideSecFlg;
475
Steve French762e5ab2007-06-28 18:41:42 +0000476 cFYI(1, ("secFlags 0x%x", secFlags));
Steve Frenchf40c5622006-06-28 00:13:38 +0000477
Steve French1982c342005-08-17 12:38:22 -0700478 pSMB->hdr.Mid = GetNextMid(server);
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000479 pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
Steve Frencha0136892007-10-04 20:05:09 +0000480
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000481 if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
Steve French254e55e2006-06-04 05:53:15 +0000482 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Steve Frencha0136892007-10-04 20:05:09 +0000483 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
484 cFYI(1, ("Kerberos only mechanism, enable extended security"));
485 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
486 }
Steve French50c2f752007-07-13 00:33:32 +0000487
Steve French39798772006-05-31 22:40:51 +0000488 count = 0;
Steve French50c2f752007-07-13 00:33:32 +0000489 for (i = 0; i < CIFS_NUM_PROT; i++) {
Steve French39798772006-05-31 22:40:51 +0000490 strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
491 count += strlen(protocols[i].name) + 1;
492 /* null at end of source and target buffers anyway */
493 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 pSMB->hdr.smb_buf_length += count;
495 pSMB->ByteCount = cpu_to_le16(count);
496
497 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
498 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French50c2f752007-07-13 00:33:32 +0000499 if (rc != 0)
Steve French254e55e2006-06-04 05:53:15 +0000500 goto neg_err_exit;
501
Al Viro733f99a2006-10-14 16:48:26 +0100502 dialect = le16_to_cpu(pSMBr->DialectIndex);
Steve French790fe572007-07-07 19:25:05 +0000503 cFYI(1, ("Dialect: %d", dialect));
Steve French254e55e2006-06-04 05:53:15 +0000504 /* Check wct = 1 error case */
Steve French790fe572007-07-07 19:25:05 +0000505 if ((pSMBr->hdr.WordCount < 13) || (dialect == BAD_PROT)) {
Steve French254e55e2006-06-04 05:53:15 +0000506 /* core returns wct = 1, but we do not ask for core - otherwise
Steve French50c2f752007-07-13 00:33:32 +0000507 small wct just comes when dialect index is -1 indicating we
Steve French254e55e2006-06-04 05:53:15 +0000508 could not negotiate a common dialect */
509 rc = -EOPNOTSUPP;
510 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000511#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French790fe572007-07-07 19:25:05 +0000512 } else if ((pSMBr->hdr.WordCount == 13)
Al Viro733f99a2006-10-14 16:48:26 +0100513 && ((dialect == LANMAN_PROT)
514 || (dialect == LANMAN2_PROT))) {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000515 __s16 tmp;
Steve French50c2f752007-07-13 00:33:32 +0000516 struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
Steve French254e55e2006-06-04 05:53:15 +0000517
Steve French790fe572007-07-07 19:25:05 +0000518 if ((secFlags & CIFSSEC_MAY_LANMAN) ||
Steve French750d1152006-06-27 06:28:30 +0000519 (secFlags & CIFSSEC_MAY_PLNTXT))
Steve French254e55e2006-06-04 05:53:15 +0000520 server->secType = LANMAN;
521 else {
522 cERROR(1, ("mount failed weak security disabled"
523 " in /proc/fs/cifs/SecurityFlags"));
Steve French39798772006-05-31 22:40:51 +0000524 rc = -EOPNOTSUPP;
525 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000526 }
Steve French254e55e2006-06-04 05:53:15 +0000527 server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
528 server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
529 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
Steve French39798772006-05-31 22:40:51 +0000530 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve French254e55e2006-06-04 05:53:15 +0000531 GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
532 /* even though we do not use raw we might as well set this
533 accurately, in case we ever find a need for it */
Steve French790fe572007-07-07 19:25:05 +0000534 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
Steve French254e55e2006-06-04 05:53:15 +0000535 server->maxRw = 0xFF00;
536 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
537 } else {
538 server->maxRw = 0;/* we do not need to use raw anyway */
539 server->capabilities = CAP_MPX_MODE;
540 }
Steve Frenchb815f1e52006-10-02 05:53:29 +0000541 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
Steve French1a70d652006-10-02 05:59:18 +0000542 if (tmp == -1) {
Steve French25ee4a92006-09-30 00:54:23 +0000543 /* OS/2 often does not set timezone therefore
544 * we must use server time to calc time zone.
Steve Frenchb815f1e52006-10-02 05:53:29 +0000545 * Could deviate slightly from the right zone.
546 * Smallest defined timezone difference is 15 minutes
547 * (i.e. Nepal). Rounding up/down is done to match
548 * this requirement.
Steve French25ee4a92006-09-30 00:54:23 +0000549 */
Steve Frenchb815f1e52006-10-02 05:53:29 +0000550 int val, seconds, remain, result;
Steve French25ee4a92006-09-30 00:54:23 +0000551 struct timespec ts, utc;
552 utc = CURRENT_TIME;
553 ts = cnvrtDosUnixTm(le16_to_cpu(rsp->SrvTime.Date),
554 le16_to_cpu(rsp->SrvTime.Time));
Steve French50c2f752007-07-13 00:33:32 +0000555 cFYI(1, ("SrvTime %d sec since 1970 (utc: %d) diff: %d",
556 (int)ts.tv_sec, (int)utc.tv_sec,
Steve French25ee4a92006-09-30 00:54:23 +0000557 (int)(utc.tv_sec - ts.tv_sec)));
Steve Frenchb815f1e52006-10-02 05:53:29 +0000558 val = (int)(utc.tv_sec - ts.tv_sec);
Andre Haupt8594c152007-08-30 20:18:41 +0000559 seconds = abs(val);
Steve French947a5062006-10-02 05:55:25 +0000560 result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000561 remain = seconds % MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000562 if (remain >= (MIN_TZ_ADJ / 2))
Steve Frenchb815f1e52006-10-02 05:53:29 +0000563 result += MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000564 if (val < 0)
Steve Frenchad7a2922008-02-07 23:25:02 +0000565 result = -result;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000566 server->timeAdj = result;
Steve French25ee4a92006-09-30 00:54:23 +0000567 } else {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000568 server->timeAdj = (int)tmp;
569 server->timeAdj *= 60; /* also in seconds */
Steve French25ee4a92006-09-30 00:54:23 +0000570 }
Steve French790fe572007-07-07 19:25:05 +0000571 cFYI(1, ("server->timeAdj: %d seconds", server->timeAdj));
Steve French25ee4a92006-09-30 00:54:23 +0000572
Steve French39798772006-05-31 22:40:51 +0000573
Steve French254e55e2006-06-04 05:53:15 +0000574 /* BB get server time for time conversions and add
Steve French50c2f752007-07-13 00:33:32 +0000575 code to use it and timezone since this is not UTC */
Steve French39798772006-05-31 22:40:51 +0000576
Steve French50c2f752007-07-13 00:33:32 +0000577 if (rsp->EncryptionKeyLength ==
Steve French25ee4a92006-09-30 00:54:23 +0000578 cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
Steve French254e55e2006-06-04 05:53:15 +0000579 memcpy(server->cryptKey, rsp->EncryptionKey,
580 CIFS_CRYPTO_KEY_SIZE);
581 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
582 rc = -EIO; /* need cryptkey unless plain text */
583 goto neg_err_exit;
584 }
Steve French39798772006-05-31 22:40:51 +0000585
Steve French790fe572007-07-07 19:25:05 +0000586 cFYI(1, ("LANMAN negotiated"));
Steve French254e55e2006-06-04 05:53:15 +0000587 /* we will not end up setting signing flags - as no signing
588 was in LANMAN and server did not return the flags on */
589 goto signing_check;
Steve French7c7b25b2006-06-01 19:20:10 +0000590#else /* weak security disabled */
Steve French790fe572007-07-07 19:25:05 +0000591 } else if (pSMBr->hdr.WordCount == 13) {
Steve French50c2f752007-07-13 00:33:32 +0000592 cERROR(1, ("mount failed, cifs module not built "
Steve French254e55e2006-06-04 05:53:15 +0000593 "with CIFS_WEAK_PW_HASH support"));
Steve French7c7b25b2006-06-01 19:20:10 +0000594 rc = -EOPNOTSUPP;
595#endif /* WEAK_PW_HASH */
Steve French254e55e2006-06-04 05:53:15 +0000596 goto neg_err_exit;
Steve French790fe572007-07-07 19:25:05 +0000597 } else if (pSMBr->hdr.WordCount != 17) {
Steve French254e55e2006-06-04 05:53:15 +0000598 /* unknown wct */
599 rc = -EOPNOTSUPP;
600 goto neg_err_exit;
601 }
602 /* else wct == 17 NTLM */
603 server->secMode = pSMBr->SecurityMode;
Steve French790fe572007-07-07 19:25:05 +0000604 if ((server->secMode & SECMODE_USER) == 0)
605 cFYI(1, ("share mode security"));
Steve French39798772006-05-31 22:40:51 +0000606
Steve French790fe572007-07-07 19:25:05 +0000607 if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000608#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French750d1152006-06-27 06:28:30 +0000609 if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000610#endif /* CIFS_WEAK_PW_HASH */
Steve French50c2f752007-07-13 00:33:32 +0000611 cERROR(1, ("Server requests plain text password"
Steve French254e55e2006-06-04 05:53:15 +0000612 " but client support disabled"));
Steve French9312f672006-06-04 22:21:07 +0000613
Steve French790fe572007-07-07 19:25:05 +0000614 if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
Steve French254e55e2006-06-04 05:53:15 +0000615 server->secType = NTLMv2;
Steve French790fe572007-07-07 19:25:05 +0000616 else if (secFlags & CIFSSEC_MAY_NTLM)
Steve French254e55e2006-06-04 05:53:15 +0000617 server->secType = NTLM;
Steve French790fe572007-07-07 19:25:05 +0000618 else if (secFlags & CIFSSEC_MAY_NTLMV2)
Steve Frenchf40c5622006-06-28 00:13:38 +0000619 server->secType = NTLMv2;
Steve Frencha0136892007-10-04 20:05:09 +0000620 else if (secFlags & CIFSSEC_MAY_KRB5)
621 server->secType = Kerberos;
622 else if (secFlags & CIFSSEC_MAY_LANMAN)
623 server->secType = LANMAN;
624/* #ifdef CONFIG_CIFS_EXPERIMENTAL
625 else if (secFlags & CIFSSEC_MAY_PLNTXT)
626 server->secType = ??
627#endif */
628 else {
629 rc = -EOPNOTSUPP;
630 cERROR(1, ("Invalid security type"));
631 goto neg_err_exit;
632 }
633 /* else ... any others ...? */
Steve French7c7b25b2006-06-01 19:20:10 +0000634
Steve French254e55e2006-06-04 05:53:15 +0000635 /* one byte, so no need to convert this or EncryptionKeyLen from
636 little endian */
637 server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
638 /* probably no need to store and check maxvcs */
639 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve French254e55e2006-06-04 05:53:15 +0000641 server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
Steve French90c81e02008-02-12 20:32:36 +0000642 cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf));
Steve French254e55e2006-06-04 05:53:15 +0000643 GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
644 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
Steve Frenchb815f1e52006-10-02 05:53:29 +0000645 server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
646 server->timeAdj *= 60;
Steve French254e55e2006-06-04 05:53:15 +0000647 if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
648 memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
649 CIFS_CRYPTO_KEY_SIZE);
650 } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
651 && (pSMBr->EncryptionKeyLength == 0)) {
652 /* decode security blob */
653 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
654 rc = -EIO; /* no crypt key only if plain text pwd */
655 goto neg_err_exit;
656 }
657
658 /* BB might be helpful to save off the domain of server here */
659
Steve French50c2f752007-07-13 00:33:32 +0000660 if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
Steve French254e55e2006-06-04 05:53:15 +0000661 (server->capabilities & CAP_EXTENDED_SECURITY)) {
662 count = pSMBr->ByteCount;
Jeff Laytone187e442007-10-16 17:10:44 +0000663 if (count < 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 rc = -EIO;
Jeff Laytone187e442007-10-16 17:10:44 +0000665 goto neg_err_exit;
666 }
667
668 if (server->socketUseCount.counter > 1) {
669 if (memcmp(server->server_GUID,
670 pSMBr->u.extended_response.
671 GUID, 16) != 0) {
672 cFYI(1, ("server UID changed"));
Steve French254e55e2006-06-04 05:53:15 +0000673 memcpy(server->server_GUID,
Jeff Laytone187e442007-10-16 17:10:44 +0000674 pSMBr->u.extended_response.GUID,
675 16);
676 }
677 } else
678 memcpy(server->server_GUID,
679 pSMBr->u.extended_response.GUID, 16);
680
681 if (count == 16) {
682 server->secType = RawNTLMSSP;
Steve French254e55e2006-06-04 05:53:15 +0000683 } else {
684 rc = decode_negTokenInit(pSMBr->u.extended_response.
685 SecurityBlob,
686 count - 16,
687 &server->secType);
Steve French790fe572007-07-07 19:25:05 +0000688 if (rc == 1) {
Jeff Laytone5459372007-11-03 05:11:06 +0000689 rc = 0;
Steve French254e55e2006-06-04 05:53:15 +0000690 } else {
691 rc = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 }
Steve French254e55e2006-06-04 05:53:15 +0000694 } else
695 server->capabilities &= ~CAP_EXTENDED_SECURITY;
696
Steve French6344a422006-06-12 04:18:35 +0000697#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French254e55e2006-06-04 05:53:15 +0000698signing_check:
Steve French6344a422006-06-12 04:18:35 +0000699#endif
Steve French762e5ab2007-06-28 18:41:42 +0000700 if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
701 /* MUST_SIGN already includes the MAY_SIGN FLAG
702 so if this is zero it means that signing is disabled */
703 cFYI(1, ("Signing disabled"));
Steve Frenchabb63d62007-10-18 02:58:40 +0000704 if (server->secMode & SECMODE_SIGN_REQUIRED) {
Steve French762e5ab2007-06-28 18:41:42 +0000705 cERROR(1, ("Server requires "
Jeff Layton7111d212007-10-16 16:50:25 +0000706 "packet signing to be enabled in "
707 "/proc/fs/cifs/SecurityFlags."));
Steve Frenchabb63d62007-10-18 02:58:40 +0000708 rc = -EOPNOTSUPP;
709 }
Steve French50c2f752007-07-13 00:33:32 +0000710 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000711 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Steve French762e5ab2007-06-28 18:41:42 +0000712 } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
713 /* signing required */
Jeff38c10a12007-07-06 21:10:07 +0000714 cFYI(1, ("Must sign - secFlags 0x%x", secFlags));
Steve French762e5ab2007-06-28 18:41:42 +0000715 if ((server->secMode &
716 (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
717 cERROR(1,
718 ("signing required but server lacks support"));
Jeff38c10a12007-07-06 21:10:07 +0000719 rc = -EOPNOTSUPP;
Steve French762e5ab2007-06-28 18:41:42 +0000720 } else
721 server->secMode |= SECMODE_SIGN_REQUIRED;
722 } else {
723 /* signing optional ie CIFSSEC_MAY_SIGN */
Steve French790fe572007-07-07 19:25:05 +0000724 if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
Steve French50c2f752007-07-13 00:33:32 +0000725 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000726 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 }
Steve French50c2f752007-07-13 00:33:32 +0000728
729neg_err_exit:
Steve French4a6d87f2005-08-13 08:15:54 -0700730 cifs_buf_release(pSMB);
Steve French254e55e2006-06-04 05:53:15 +0000731
Steve French790fe572007-07-07 19:25:05 +0000732 cFYI(1, ("negprot rc %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 return rc;
734}
735
736int
737CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
738{
739 struct smb_hdr *smb_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
742 cFYI(1, ("In tree disconnect"));
743 /*
744 * If last user of the connection and
745 * connection alive - disconnect it
746 * If this is the last connection on the server session disconnect it
Steve French50c2f752007-07-13 00:33:32 +0000747 * (and inside session disconnect we should check if tcp socket needs
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 * to be freed and kernel thread woken up).
749 */
750 if (tcon)
751 down(&tcon->tconSem);
752 else
753 return -EIO;
754
755 atomic_dec(&tcon->useCount);
756 if (atomic_read(&tcon->useCount) > 0) {
757 up(&tcon->tconSem);
758 return -EBUSY;
759 }
760
Steve French50c2f752007-07-13 00:33:32 +0000761 /* No need to return error on this operation if tid invalidated and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 closed on server already e.g. due to tcp session crashing */
Steve French790fe572007-07-07 19:25:05 +0000763 if (tcon->tidStatus == CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 up(&tcon->tconSem);
Steve French50c2f752007-07-13 00:33:32 +0000765 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 }
767
Steve French790fe572007-07-07 19:25:05 +0000768 if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 up(&tcon->tconSem);
770 return -EIO;
771 }
Steve French50c2f752007-07-13 00:33:32 +0000772 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
Steve French09d1db52005-04-28 22:41:08 -0700773 (void **)&smb_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 if (rc) {
775 up(&tcon->tconSem);
776 return rc;
Steve Frenchcd634992005-04-28 22:41:10 -0700777 }
Steve French133672e2007-11-13 22:41:37 +0000778
779 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 if (rc)
Steve French966ca922005-04-28 22:41:08 -0700781 cFYI(1, ("Tree disconnect failed %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 up(&tcon->tconSem);
784
Steve French50c2f752007-07-13 00:33:32 +0000785 /* No need to return error on this operation if tid invalidated and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 closed on server already e.g. due to tcp session crashing */
787 if (rc == -EAGAIN)
788 rc = 0;
789
790 return rc;
791}
792
793int
794CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
795{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 LOGOFF_ANDX_REQ *pSMB;
797 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
799 cFYI(1, ("In SMBLogoff for session disconnect"));
800 if (ses)
801 down(&ses->sesSem);
802 else
803 return -EIO;
804
805 atomic_dec(&ses->inUse);
806 if (atomic_read(&ses->inUse) > 0) {
807 up(&ses->sesSem);
808 return -EBUSY;
809 }
810 rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
811 if (rc) {
812 up(&ses->sesSem);
813 return rc;
814 }
815
Steve French790fe572007-07-07 19:25:05 +0000816 if (ses->server) {
Steve French1982c342005-08-17 12:38:22 -0700817 pSMB->hdr.Mid = GetNextMid(ses->server);
818
Steve French790fe572007-07-07 19:25:05 +0000819 if (ses->server->secMode &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
821 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
822 }
823
824 pSMB->hdr.Uid = ses->Suid;
825
826 pSMB->AndXCommand = 0xFF;
Steve French133672e2007-11-13 22:41:37 +0000827 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 if (ses->server) {
829 atomic_dec(&ses->server->socketUseCount);
830 if (atomic_read(&ses->server->socketUseCount) == 0) {
831 spin_lock(&GlobalMid_Lock);
832 ses->server->tcpStatus = CifsExiting;
833 spin_unlock(&GlobalMid_Lock);
834 rc = -ESHUTDOWN;
835 }
836 }
Steve Frencha59c6582005-08-17 12:12:19 -0700837 up(&ses->sesSem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
839 /* if session dead then we do not need to do ulogoff,
Steve French50c2f752007-07-13 00:33:32 +0000840 since server closed smb session, no sense reporting
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 error */
842 if (rc == -EAGAIN)
843 rc = 0;
844 return rc;
845}
846
847int
Steve French2d785a52007-07-15 01:48:57 +0000848CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
849 __u16 type, const struct nls_table *nls_codepage, int remap)
850{
851 TRANSACTION2_SPI_REQ *pSMB = NULL;
852 TRANSACTION2_SPI_RSP *pSMBr = NULL;
853 struct unlink_psx_rq *pRqD;
854 int name_len;
855 int rc = 0;
856 int bytes_returned = 0;
857 __u16 params, param_offset, offset, byte_count;
858
859 cFYI(1, ("In POSIX delete"));
860PsxDelete:
861 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
862 (void **) &pSMBr);
863 if (rc)
864 return rc;
865
866 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
867 name_len =
868 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
869 PATH_MAX, nls_codepage, remap);
870 name_len++; /* trailing null */
871 name_len *= 2;
872 } else { /* BB add path length overrun check */
873 name_len = strnlen(fileName, PATH_MAX);
874 name_len++; /* trailing null */
875 strncpy(pSMB->FileName, fileName, name_len);
876 }
877
878 params = 6 + name_len;
879 pSMB->MaxParameterCount = cpu_to_le16(2);
880 pSMB->MaxDataCount = 0; /* BB double check this with jra */
881 pSMB->MaxSetupCount = 0;
882 pSMB->Reserved = 0;
883 pSMB->Flags = 0;
884 pSMB->Timeout = 0;
885 pSMB->Reserved2 = 0;
886 param_offset = offsetof(struct smb_com_transaction2_spi_req,
887 InformationLevel) - 4;
888 offset = param_offset + params;
889
890 /* Setup pointer to Request Data (inode type) */
891 pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
892 pRqD->type = cpu_to_le16(type);
893 pSMB->ParameterOffset = cpu_to_le16(param_offset);
894 pSMB->DataOffset = cpu_to_le16(offset);
895 pSMB->SetupCount = 1;
896 pSMB->Reserved3 = 0;
897 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
898 byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
899
900 pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
901 pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
902 pSMB->ParameterCount = cpu_to_le16(params);
903 pSMB->TotalParameterCount = pSMB->ParameterCount;
904 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
905 pSMB->Reserved4 = 0;
906 pSMB->hdr.smb_buf_length += byte_count;
907 pSMB->ByteCount = cpu_to_le16(byte_count);
908 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
909 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +0000910 if (rc)
Steve French2d785a52007-07-15 01:48:57 +0000911 cFYI(1, ("Posix delete returned %d", rc));
Steve French2d785a52007-07-15 01:48:57 +0000912 cifs_buf_release(pSMB);
913
914 cifs_stats_inc(&tcon->num_deletes);
915
916 if (rc == -EAGAIN)
917 goto PsxDelete;
918
919 return rc;
920}
921
922int
Steve French737b7582005-04-28 22:41:06 -0700923CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
924 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925{
926 DELETE_FILE_REQ *pSMB = NULL;
927 DELETE_FILE_RSP *pSMBr = NULL;
928 int rc = 0;
929 int bytes_returned;
930 int name_len;
931
932DelFileRetry:
933 rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
934 (void **) &pSMBr);
935 if (rc)
936 return rc;
937
938 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
939 name_len =
Steve French50c2f752007-07-13 00:33:32 +0000940 cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
Steve French737b7582005-04-28 22:41:06 -0700941 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 name_len++; /* trailing null */
943 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700944 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 name_len = strnlen(fileName, PATH_MAX);
946 name_len++; /* trailing null */
947 strncpy(pSMB->fileName, fileName, name_len);
948 }
949 pSMB->SearchAttributes =
950 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
951 pSMB->BufferFormat = 0x04;
952 pSMB->hdr.smb_buf_length += name_len + 1;
953 pSMB->ByteCount = cpu_to_le16(name_len + 1);
954 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
955 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700956 cifs_stats_inc(&tcon->num_deletes);
Steve Frenchad7a2922008-02-07 23:25:02 +0000957 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 cFYI(1, ("Error in RMFile = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959
960 cifs_buf_release(pSMB);
961 if (rc == -EAGAIN)
962 goto DelFileRetry;
963
964 return rc;
965}
966
967int
Steve French50c2f752007-07-13 00:33:32 +0000968CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
Steve French737b7582005-04-28 22:41:06 -0700969 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970{
971 DELETE_DIRECTORY_REQ *pSMB = NULL;
972 DELETE_DIRECTORY_RSP *pSMBr = NULL;
973 int rc = 0;
974 int bytes_returned;
975 int name_len;
976
977 cFYI(1, ("In CIFSSMBRmDir"));
978RmDirRetry:
979 rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
980 (void **) &pSMBr);
981 if (rc)
982 return rc;
983
984 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French737b7582005-04-28 22:41:06 -0700985 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
986 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 name_len++; /* trailing null */
988 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700989 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 name_len = strnlen(dirName, PATH_MAX);
991 name_len++; /* trailing null */
992 strncpy(pSMB->DirName, dirName, name_len);
993 }
994
995 pSMB->BufferFormat = 0x04;
996 pSMB->hdr.smb_buf_length += name_len + 1;
997 pSMB->ByteCount = cpu_to_le16(name_len + 1);
998 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
999 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001000 cifs_stats_inc(&tcon->num_rmdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +00001001 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 cFYI(1, ("Error in RMDir = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
1004 cifs_buf_release(pSMB);
1005 if (rc == -EAGAIN)
1006 goto RmDirRetry;
1007 return rc;
1008}
1009
1010int
1011CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07001012 const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013{
1014 int rc = 0;
1015 CREATE_DIRECTORY_REQ *pSMB = NULL;
1016 CREATE_DIRECTORY_RSP *pSMBr = NULL;
1017 int bytes_returned;
1018 int name_len;
1019
1020 cFYI(1, ("In CIFSSMBMkDir"));
1021MkDirRetry:
1022 rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
1023 (void **) &pSMBr);
1024 if (rc)
1025 return rc;
1026
1027 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00001028 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
Steve French737b7582005-04-28 22:41:06 -07001029 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 name_len++; /* trailing null */
1031 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -07001032 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 name_len = strnlen(name, PATH_MAX);
1034 name_len++; /* trailing null */
1035 strncpy(pSMB->DirName, name, name_len);
1036 }
1037
1038 pSMB->BufferFormat = 0x04;
1039 pSMB->hdr.smb_buf_length += name_len + 1;
1040 pSMB->ByteCount = cpu_to_le16(name_len + 1);
1041 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1042 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001043 cifs_stats_inc(&tcon->num_mkdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +00001044 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 cFYI(1, ("Error in Mkdir = %d", rc));
Steve Frencha5a2b482005-08-20 21:42:53 -07001046
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 cifs_buf_release(pSMB);
1048 if (rc == -EAGAIN)
1049 goto MkDirRetry;
1050 return rc;
1051}
1052
Steve French2dd29d32007-04-23 22:07:35 +00001053int
1054CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
Steve Frenchad7a2922008-02-07 23:25:02 +00001055 __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
Steve French50c2f752007-07-13 00:33:32 +00001056 __u32 *pOplock, const char *name,
Steve French2dd29d32007-04-23 22:07:35 +00001057 const struct nls_table *nls_codepage, int remap)
1058{
1059 TRANSACTION2_SPI_REQ *pSMB = NULL;
1060 TRANSACTION2_SPI_RSP *pSMBr = NULL;
1061 int name_len;
1062 int rc = 0;
1063 int bytes_returned = 0;
Steve French2dd29d32007-04-23 22:07:35 +00001064 __u16 params, param_offset, offset, byte_count, count;
Steve Frenchad7a2922008-02-07 23:25:02 +00001065 OPEN_PSX_REQ *pdata;
1066 OPEN_PSX_RSP *psx_rsp;
Steve French2dd29d32007-04-23 22:07:35 +00001067
1068 cFYI(1, ("In POSIX Create"));
1069PsxCreat:
1070 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
1071 (void **) &pSMBr);
1072 if (rc)
1073 return rc;
1074
1075 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1076 name_len =
1077 cifsConvertToUCS((__le16 *) pSMB->FileName, name,
1078 PATH_MAX, nls_codepage, remap);
1079 name_len++; /* trailing null */
1080 name_len *= 2;
1081 } else { /* BB improve the check for buffer overruns BB */
1082 name_len = strnlen(name, PATH_MAX);
1083 name_len++; /* trailing null */
1084 strncpy(pSMB->FileName, name, name_len);
1085 }
1086
1087 params = 6 + name_len;
1088 count = sizeof(OPEN_PSX_REQ);
1089 pSMB->MaxParameterCount = cpu_to_le16(2);
1090 pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
1091 pSMB->MaxSetupCount = 0;
1092 pSMB->Reserved = 0;
1093 pSMB->Flags = 0;
1094 pSMB->Timeout = 0;
1095 pSMB->Reserved2 = 0;
1096 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00001097 InformationLevel) - 4;
Steve French2dd29d32007-04-23 22:07:35 +00001098 offset = param_offset + params;
Steve French2dd29d32007-04-23 22:07:35 +00001099 pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001100 pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
Steve French2dd29d32007-04-23 22:07:35 +00001101 pdata->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00001102 pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
Steve French2dd29d32007-04-23 22:07:35 +00001103 pdata->OpenFlags = cpu_to_le32(*pOplock);
1104 pSMB->ParameterOffset = cpu_to_le16(param_offset);
1105 pSMB->DataOffset = cpu_to_le16(offset);
1106 pSMB->SetupCount = 1;
1107 pSMB->Reserved3 = 0;
1108 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
1109 byte_count = 3 /* pad */ + params + count;
1110
1111 pSMB->DataCount = cpu_to_le16(count);
1112 pSMB->ParameterCount = cpu_to_le16(params);
1113 pSMB->TotalDataCount = pSMB->DataCount;
1114 pSMB->TotalParameterCount = pSMB->ParameterCount;
1115 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1116 pSMB->Reserved4 = 0;
Steve French50c2f752007-07-13 00:33:32 +00001117 pSMB->hdr.smb_buf_length += byte_count;
Steve French2dd29d32007-04-23 22:07:35 +00001118 pSMB->ByteCount = cpu_to_le16(byte_count);
1119 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1120 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1121 if (rc) {
1122 cFYI(1, ("Posix create returned %d", rc));
1123 goto psx_create_err;
1124 }
1125
Steve French790fe572007-07-07 19:25:05 +00001126 cFYI(1, ("copying inode info"));
Steve French2dd29d32007-04-23 22:07:35 +00001127 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1128
1129 if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
1130 rc = -EIO; /* bad smb */
1131 goto psx_create_err;
1132 }
1133
1134 /* copy return information to pRetData */
Steve French50c2f752007-07-13 00:33:32 +00001135 psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
Steve French2dd29d32007-04-23 22:07:35 +00001136 + le16_to_cpu(pSMBr->t2.DataOffset));
Steve French50c2f752007-07-13 00:33:32 +00001137
Steve French2dd29d32007-04-23 22:07:35 +00001138 *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
Steve French790fe572007-07-07 19:25:05 +00001139 if (netfid)
Steve French2dd29d32007-04-23 22:07:35 +00001140 *netfid = psx_rsp->Fid; /* cifs fid stays in le */
1141 /* Let caller know file was created so we can set the mode. */
1142 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001143 if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
Steve French2dd29d32007-04-23 22:07:35 +00001144 *pOplock |= CIFS_CREATE_ACTION;
1145 /* check to make sure response data is there */
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001146 if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1147 pRetData->Type = cpu_to_le32(-1); /* unknown */
Steve French90c81e02008-02-12 20:32:36 +00001148 cFYI(DBG2, ("unknown type"));
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001149 } else {
Steve French790fe572007-07-07 19:25:05 +00001150 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001151 + sizeof(FILE_UNIX_BASIC_INFO)) {
Steve French50c2f752007-07-13 00:33:32 +00001152 cERROR(1, ("Open response data too small"));
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001153 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001154 goto psx_create_err;
1155 }
Steve French50c2f752007-07-13 00:33:32 +00001156 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001157 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001158 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001159 }
Steve French2dd29d32007-04-23 22:07:35 +00001160
1161psx_create_err:
1162 cifs_buf_release(pSMB);
1163
1164 cifs_stats_inc(&tcon->num_mkdirs);
1165
1166 if (rc == -EAGAIN)
1167 goto PsxCreat;
1168
Steve French50c2f752007-07-13 00:33:32 +00001169 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001170}
1171
Steve Frencha9d02ad2005-08-24 23:06:05 -07001172static __u16 convert_disposition(int disposition)
1173{
1174 __u16 ofun = 0;
1175
1176 switch (disposition) {
1177 case FILE_SUPERSEDE:
1178 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1179 break;
1180 case FILE_OPEN:
1181 ofun = SMBOPEN_OAPPEND;
1182 break;
1183 case FILE_CREATE:
1184 ofun = SMBOPEN_OCREATE;
1185 break;
1186 case FILE_OPEN_IF:
1187 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1188 break;
1189 case FILE_OVERWRITE:
1190 ofun = SMBOPEN_OTRUNC;
1191 break;
1192 case FILE_OVERWRITE_IF:
1193 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1194 break;
1195 default:
Steve French790fe572007-07-07 19:25:05 +00001196 cFYI(1, ("unknown disposition %d", disposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001197 ofun = SMBOPEN_OAPPEND; /* regular open */
1198 }
1199 return ofun;
1200}
1201
Jeff Layton35fc37d2008-05-14 10:22:03 -07001202static int
1203access_flags_to_smbopen_mode(const int access_flags)
1204{
1205 int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
1206
1207 if (masked_flags == GENERIC_READ)
1208 return SMBOPEN_READ;
1209 else if (masked_flags == GENERIC_WRITE)
1210 return SMBOPEN_WRITE;
1211
1212 /* just go for read/write */
1213 return SMBOPEN_READWRITE;
1214}
1215
Steve Frencha9d02ad2005-08-24 23:06:05 -07001216int
1217SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1218 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001219 const int access_flags, const int create_options, __u16 *netfid,
1220 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001221 const struct nls_table *nls_codepage, int remap)
1222{
1223 int rc = -EACCES;
1224 OPENX_REQ *pSMB = NULL;
1225 OPENX_RSP *pSMBr = NULL;
1226 int bytes_returned;
1227 int name_len;
1228 __u16 count;
1229
1230OldOpenRetry:
1231 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1232 (void **) &pSMBr);
1233 if (rc)
1234 return rc;
1235
1236 pSMB->AndXCommand = 0xFF; /* none */
1237
1238 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1239 count = 1; /* account for one byte pad to word boundary */
1240 name_len =
1241 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1242 fileName, PATH_MAX, nls_codepage, remap);
1243 name_len++; /* trailing null */
1244 name_len *= 2;
1245 } else { /* BB improve check for buffer overruns BB */
1246 count = 0; /* no pad */
1247 name_len = strnlen(fileName, PATH_MAX);
1248 name_len++; /* trailing null */
1249 strncpy(pSMB->fileName, fileName, name_len);
1250 }
1251 if (*pOplock & REQ_OPLOCK)
1252 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001253 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001254 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001255
Steve Frencha9d02ad2005-08-24 23:06:05 -07001256 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
Jeff Layton35fc37d2008-05-14 10:22:03 -07001257 pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001258 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1259 /* set file as system file if special file such
1260 as fifo and server expecting SFU style and
1261 no Unix extensions */
1262
Steve French790fe572007-07-07 19:25:05 +00001263 if (create_options & CREATE_OPTION_SPECIAL)
1264 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001265 else /* BB FIXME BB */
1266 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001267
Jeff Layton67750fb2008-05-09 22:28:02 +00001268 if (create_options & CREATE_OPTION_READONLY)
1269 pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001270
1271 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001272/* pSMB->CreateOptions = cpu_to_le32(create_options &
1273 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001274 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001275
1276 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001277 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001278 count += name_len;
1279 pSMB->hdr.smb_buf_length += count;
1280
1281 pSMB->ByteCount = cpu_to_le16(count);
1282 /* long_op set to 1 to allow for oplock break timeouts */
1283 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001284 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001285 cifs_stats_inc(&tcon->num_opens);
1286 if (rc) {
1287 cFYI(1, ("Error in Open = %d", rc));
1288 } else {
1289 /* BB verify if wct == 15 */
1290
Steve French582d21e2008-05-13 04:54:12 +00001291/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
Steve Frencha9d02ad2005-08-24 23:06:05 -07001292
1293 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1294 /* Let caller know file was created so we can set the mode. */
1295 /* Do we care about the CreateAction in any other cases? */
1296 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001297/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001298 *pOplock |= CIFS_CREATE_ACTION; */
1299 /* BB FIXME END */
1300
Steve French790fe572007-07-07 19:25:05 +00001301 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001302 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1303 pfile_info->LastAccessTime = 0; /* BB fixme */
1304 pfile_info->LastWriteTime = 0; /* BB fixme */
1305 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001306 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001307 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001308 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001309 pfile_info->AllocationSize =
1310 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1311 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001312 pfile_info->NumberOfLinks = cpu_to_le32(1);
1313 }
1314 }
1315
1316 cifs_buf_release(pSMB);
1317 if (rc == -EAGAIN)
1318 goto OldOpenRetry;
1319 return rc;
1320}
1321
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322int
1323CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1324 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001325 const int access_flags, const int create_options, __u16 *netfid,
1326 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001327 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328{
1329 int rc = -EACCES;
1330 OPEN_REQ *pSMB = NULL;
1331 OPEN_RSP *pSMBr = NULL;
1332 int bytes_returned;
1333 int name_len;
1334 __u16 count;
1335
1336openRetry:
1337 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1338 (void **) &pSMBr);
1339 if (rc)
1340 return rc;
1341
1342 pSMB->AndXCommand = 0xFF; /* none */
1343
1344 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1345 count = 1; /* account for one byte pad to word boundary */
1346 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001347 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001348 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 name_len++; /* trailing null */
1350 name_len *= 2;
1351 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001352 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 count = 0; /* no pad */
1354 name_len = strnlen(fileName, PATH_MAX);
1355 name_len++; /* trailing null */
1356 pSMB->NameLength = cpu_to_le16(name_len);
1357 strncpy(pSMB->fileName, fileName, name_len);
1358 }
1359 if (*pOplock & REQ_OPLOCK)
1360 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001361 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1364 pSMB->AllocationSize = 0;
Steve Frencheda3c0292005-07-21 15:20:28 -07001365 /* set file as system file if special file such
1366 as fifo and server expecting SFU style and
1367 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001368 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c0292005-07-21 15:20:28 -07001369 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1370 else
1371 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Jeff Layton67750fb2008-05-09 22:28:02 +00001372
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 /* XP does not handle ATTR_POSIX_SEMANTICS */
1374 /* but it helps speed up case sensitive checks for other
1375 servers such as Samba */
1376 if (tcon->ses->capabilities & CAP_UNIX)
1377 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1378
Jeff Layton67750fb2008-05-09 22:28:02 +00001379 if (create_options & CREATE_OPTION_READONLY)
1380 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);
1381
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1383 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c0292005-07-21 15:20:28 -07001384 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001385 /* BB Expirement with various impersonation levels and verify */
1386 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 pSMB->SecurityFlags =
1388 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1389
1390 count += name_len;
1391 pSMB->hdr.smb_buf_length += count;
1392
1393 pSMB->ByteCount = cpu_to_le16(count);
1394 /* long_op set to 1 to allow for oplock break timeouts */
1395 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001396 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha4544342005-08-24 13:59:35 -07001397 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 if (rc) {
1399 cFYI(1, ("Error in Open = %d", rc));
1400 } else {
Steve French09d1db52005-04-28 22:41:08 -07001401 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1403 /* Let caller know file was created so we can set the mode. */
1404 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001405 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001406 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001407 if (pfile_info) {
Steve French50c2f752007-07-13 00:33:32 +00001408 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 36 /* CreationTime to Attributes */);
1410 /* the file_info buf is endian converted by caller */
1411 pfile_info->AllocationSize = pSMBr->AllocationSize;
1412 pfile_info->EndOfFile = pSMBr->EndOfFile;
1413 pfile_info->NumberOfLinks = cpu_to_le32(1);
1414 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001416
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 cifs_buf_release(pSMB);
1418 if (rc == -EAGAIN)
1419 goto openRetry;
1420 return rc;
1421}
1422
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423int
Steve French50c2f752007-07-13 00:33:32 +00001424CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1425 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1426 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427{
1428 int rc = -EACCES;
1429 READ_REQ *pSMB = NULL;
1430 READ_RSP *pSMBr = NULL;
1431 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001432 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001433 int resp_buf_type = 0;
1434 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435
Steve French790fe572007-07-07 19:25:05 +00001436 cFYI(1, ("Reading %d bytes on fid %d", count, netfid));
1437 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001438 wct = 12;
1439 else
1440 wct = 10; /* old style read */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441
1442 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001443 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 if (rc)
1445 return rc;
1446
1447 /* tcon and ses pointer are checked in smb_init */
1448 if (tcon->ses->server == NULL)
1449 return -ECONNABORTED;
1450
Steve Frenchec637e32005-12-12 20:53:18 -08001451 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 pSMB->Fid = netfid;
1453 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001454 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001455 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve French790fe572007-07-07 19:25:05 +00001456 else if ((lseek >> 32) > 0) /* can not handle this big offset for old */
Steve Frenchec637e32005-12-12 20:53:18 -08001457 return -EIO;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001458
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 pSMB->Remaining = 0;
1460 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1461 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001462 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001463 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1464 else {
1465 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001466 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001467 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001468 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001469 }
Steve Frenchec637e32005-12-12 20:53:18 -08001470
1471 iov[0].iov_base = (char *)pSMB;
1472 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001473 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Steve French133672e2007-11-13 22:41:37 +00001474 &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001475 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001476 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477 if (rc) {
1478 cERROR(1, ("Send error in read = %d", rc));
1479 } else {
1480 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1481 data_length = data_length << 16;
1482 data_length += le16_to_cpu(pSMBr->DataLength);
1483 *nbytes = data_length;
1484
1485 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001486 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 || (data_length > count)) {
Steve French50c2f752007-07-13 00:33:32 +00001488 cFYI(1, ("bad length %d for count %d",
1489 data_length, count));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 rc = -EIO;
1491 *nbytes = 0;
1492 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001493 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001494 le16_to_cpu(pSMBr->DataOffset);
1495/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Steve French50c2f752007-07-13 00:33:32 +00001496 cERROR(1,("Faulting on read rc = %d",rc));
1497 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001498 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001499 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001500 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501 }
1502 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503
Steve French4b8f9302006-02-26 16:41:18 +00001504/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001505 if (*buf) {
1506 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001507 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001508 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001509 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001510 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001511 /* return buffer to caller to free */
1512 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001513 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001514 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001515 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001516 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001517 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001518
1519 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 since file handle passed in no longer valid */
1521 return rc;
1522}
1523
Steve Frenchec637e32005-12-12 20:53:18 -08001524
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525int
1526CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1527 const int netfid, const unsigned int count,
1528 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001529 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530{
1531 int rc = -EACCES;
1532 WRITE_REQ *pSMB = NULL;
1533 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001534 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535 __u32 bytes_sent;
1536 __u16 byte_count;
1537
1538 /* cFYI(1,("write at %lld %d bytes",offset,count));*/
Steve French790fe572007-07-07 19:25:05 +00001539 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001540 return -ECONNABORTED;
1541
Steve French790fe572007-07-07 19:25:05 +00001542 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001543 wct = 14;
1544 else
1545 wct = 12;
1546
1547 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 (void **) &pSMBr);
1549 if (rc)
1550 return rc;
1551 /* tcon and ses pointer are checked in smb_init */
1552 if (tcon->ses->server == NULL)
1553 return -ECONNABORTED;
1554
1555 pSMB->AndXCommand = 0xFF; /* none */
1556 pSMB->Fid = netfid;
1557 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001558 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001559 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French790fe572007-07-07 19:25:05 +00001560 else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
Steve French1c955182005-08-30 20:58:07 -07001561 return -EIO;
Steve French50c2f752007-07-13 00:33:32 +00001562
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 pSMB->Reserved = 0xFFFFFFFF;
1564 pSMB->WriteMode = 0;
1565 pSMB->Remaining = 0;
1566
Steve French50c2f752007-07-13 00:33:32 +00001567 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 can send more if LARGE_WRITE_X capability returned by the server and if
1569 our buffer is big enough or if we convert to iovecs on socket writes
1570 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001571 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1573 } else {
1574 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1575 & ~0xFF;
1576 }
1577
1578 if (bytes_sent > count)
1579 bytes_sent = count;
1580 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001581 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001582 if (buf)
Steve French50c2f752007-07-13 00:33:32 +00001583 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001584 else if (ubuf) {
1585 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586 cifs_buf_release(pSMB);
1587 return -EFAULT;
1588 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001589 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 /* No buffer */
1591 cifs_buf_release(pSMB);
1592 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001593 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001594 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001595 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001596 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001597 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001598
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1600 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001601 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001602
Steve French790fe572007-07-07 19:25:05 +00001603 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001604 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001605 else { /* old style write has byte count 4 bytes earlier
1606 so 4 bytes pad */
1607 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001608 (struct smb_com_writex_req *)pSMB;
1609 pSMBW->ByteCount = cpu_to_le16(byte_count);
1610 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611
1612 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1613 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001614 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 if (rc) {
1616 cFYI(1, ("Send error in write = %d", rc));
1617 *nbytes = 0;
1618 } else {
1619 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1620 *nbytes = (*nbytes) << 16;
1621 *nbytes += le16_to_cpu(pSMBr->Count);
1622 }
1623
1624 cifs_buf_release(pSMB);
1625
Steve French50c2f752007-07-13 00:33:32 +00001626 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 since file handle passed in no longer valid */
1628
1629 return rc;
1630}
1631
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001632int
1633CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001635 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1636 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637{
1638 int rc = -EACCES;
1639 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001640 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001641 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001642 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643
Steve French790fe572007-07-07 19:25:05 +00001644 cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count));
Steve Frenchff7feac2005-11-15 16:45:16 -08001645
Steve French790fe572007-07-07 19:25:05 +00001646 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French8cc64c62005-10-03 13:49:43 -07001647 wct = 14;
1648 else
1649 wct = 12;
1650 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 if (rc)
1652 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 /* tcon and ses pointer are checked in smb_init */
1654 if (tcon->ses->server == NULL)
1655 return -ECONNABORTED;
1656
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001657 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658 pSMB->Fid = netfid;
1659 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001660 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001661 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French790fe572007-07-07 19:25:05 +00001662 else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
Steve French8cc64c62005-10-03 13:49:43 -07001663 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 pSMB->Reserved = 0xFFFFFFFF;
1665 pSMB->WriteMode = 0;
1666 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001667
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001669 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670
Steve French3e844692005-10-03 13:37:24 -07001671 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1672 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001673 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001674 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001675 pSMB->hdr.smb_buf_length += count+1;
1676 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001677 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1678 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001679 pSMB->ByteCount = cpu_to_le16(count + 1);
1680 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001681 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001682 (struct smb_com_writex_req *)pSMB;
1683 pSMBW->ByteCount = cpu_to_le16(count + 5);
1684 }
Steve French3e844692005-10-03 13:37:24 -07001685 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001686 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001687 iov[0].iov_len = smb_hdr_len + 4;
1688 else /* wct == 12 pad bigger by four bytes */
1689 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001690
Steve French3e844692005-10-03 13:37:24 -07001691
Steve Frenchec637e32005-12-12 20:53:18 -08001692 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001693 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001694 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 if (rc) {
Steve French8cc64c62005-10-03 13:49:43 -07001696 cFYI(1, ("Send error Write2 = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 *nbytes = 0;
Steve French790fe572007-07-07 19:25:05 +00001698 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001699 /* presumably this can not happen, but best to be safe */
1700 rc = -EIO;
1701 *nbytes = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001702 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001703 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001704 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1705 *nbytes = (*nbytes) << 16;
1706 *nbytes += le16_to_cpu(pSMBr->Count);
Steve French50c2f752007-07-13 00:33:32 +00001707 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708
Steve French4b8f9302006-02-26 16:41:18 +00001709/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001710 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001711 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001712 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001713 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714
Steve French50c2f752007-07-13 00:33:32 +00001715 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 since file handle passed in no longer valid */
1717
1718 return rc;
1719}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001720
1721
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722int
1723CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1724 const __u16 smb_file_id, const __u64 len,
1725 const __u64 offset, const __u32 numUnlock,
Steve French4b18f2a2008-04-29 00:06:05 +00001726 const __u32 numLock, const __u8 lockType, const bool waitFlag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727{
1728 int rc = 0;
1729 LOCK_REQ *pSMB = NULL;
1730 LOCK_RSP *pSMBr = NULL;
1731 int bytes_returned;
1732 int timeout = 0;
1733 __u16 count;
1734
Steve French4b18f2a2008-04-29 00:06:05 +00001735 cFYI(1, ("CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock));
Steve French46810cb2005-04-28 22:41:09 -07001736 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1737
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738 if (rc)
1739 return rc;
1740
Steve French46810cb2005-04-28 22:41:09 -07001741 pSMBr = (LOCK_RSP *)pSMB; /* BB removeme BB */
1742
Steve French790fe572007-07-07 19:25:05 +00001743 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001744 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745 pSMB->Timeout = 0;
Steve French4b18f2a2008-04-29 00:06:05 +00001746 } else if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001747 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1749 } else {
1750 pSMB->Timeout = 0;
1751 }
1752
1753 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1754 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1755 pSMB->LockType = lockType;
1756 pSMB->AndXCommand = 0xFF; /* none */
1757 pSMB->Fid = smb_file_id; /* netfid stays le */
1758
Steve French790fe572007-07-07 19:25:05 +00001759 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1761 /* BB where to store pid high? */
1762 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1763 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1764 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1765 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1766 count = sizeof(LOCKING_ANDX_RANGE);
1767 } else {
1768 /* oplock break */
1769 count = 0;
1770 }
1771 pSMB->hdr.smb_buf_length += count;
1772 pSMB->ByteCount = cpu_to_le16(count);
1773
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001774 if (waitFlag) {
1775 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1776 (struct smb_hdr *) pSMBr, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001777 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001778 } else {
Steve French133672e2007-11-13 22:41:37 +00001779 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1780 timeout);
1781 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001782 }
Steve Frencha4544342005-08-24 13:59:35 -07001783 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001784 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785 cFYI(1, ("Send error in Lock = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786
Steve French50c2f752007-07-13 00:33:32 +00001787 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 since file handle passed in no longer valid */
1789 return rc;
1790}
1791
1792int
Steve French08547b02006-02-28 22:39:25 +00001793CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1794 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001795 struct file_lock *pLockData, const __u16 lock_type,
Steve French4b18f2a2008-04-29 00:06:05 +00001796 const bool waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001797{
1798 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1799 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001800 struct cifs_posix_lock *parm_data;
1801 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001802 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001803 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001804 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001805 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001806 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001807
1808 cFYI(1, ("Posix Lock"));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001809
Steve French790fe572007-07-07 19:25:05 +00001810 if (pLockData == NULL)
Marcin Slusarzed5f0372008-05-13 04:01:01 +00001811 return -EINVAL;
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001812
Steve French08547b02006-02-28 22:39:25 +00001813 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1814
1815 if (rc)
1816 return rc;
1817
1818 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1819
Steve French50c2f752007-07-13 00:33:32 +00001820 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001821 pSMB->MaxSetupCount = 0;
1822 pSMB->Reserved = 0;
1823 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001824 pSMB->Reserved2 = 0;
1825 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1826 offset = param_offset + params;
1827
Steve French08547b02006-02-28 22:39:25 +00001828 count = sizeof(struct cifs_posix_lock);
1829 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001830 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001831 pSMB->SetupCount = 1;
1832 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001833 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001834 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1835 else
1836 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1837 byte_count = 3 /* pad */ + params + count;
1838 pSMB->DataCount = cpu_to_le16(count);
1839 pSMB->ParameterCount = cpu_to_le16(params);
1840 pSMB->TotalDataCount = pSMB->DataCount;
1841 pSMB->TotalParameterCount = pSMB->ParameterCount;
1842 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001843 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001844 (((char *) &pSMB->hdr.Protocol) + offset);
1845
1846 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001847 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001848 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001849 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001850 pSMB->Timeout = cpu_to_le32(-1);
1851 } else
1852 pSMB->Timeout = 0;
1853
Steve French08547b02006-02-28 22:39:25 +00001854 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001855 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001856 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001857
1858 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001859 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001860 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1861 pSMB->Reserved4 = 0;
1862 pSMB->hdr.smb_buf_length += byte_count;
1863 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001864 if (waitFlag) {
1865 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1866 (struct smb_hdr *) pSMBr, &bytes_returned);
1867 } else {
Steve French133672e2007-11-13 22:41:37 +00001868 iov[0].iov_base = (char *)pSMB;
1869 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1870 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1871 &resp_buf_type, timeout);
1872 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1873 not try to free it twice below on exit */
1874 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001875 }
1876
Steve French08547b02006-02-28 22:39:25 +00001877 if (rc) {
1878 cFYI(1, ("Send error in Posix Lock = %d", rc));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001879 } else if (get_flag) {
1880 /* lock structure can be returned on get */
1881 __u16 data_offset;
1882 __u16 data_count;
1883 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001884
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001885 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1886 rc = -EIO; /* bad smb */
1887 goto plk_err_exit;
1888 }
Steve French790fe572007-07-07 19:25:05 +00001889 if (pLockData == NULL) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001890 rc = -EINVAL;
1891 goto plk_err_exit;
1892 }
1893 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1894 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001895 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001896 rc = -EIO;
1897 goto plk_err_exit;
1898 }
1899 parm_data = (struct cifs_posix_lock *)
1900 ((char *)&pSMBr->hdr.Protocol + data_offset);
Steve French790fe572007-07-07 19:25:05 +00001901 if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001902 pLockData->fl_type = F_UNLCK;
1903 }
Steve French50c2f752007-07-13 00:33:32 +00001904
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001905plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001906 if (pSMB)
1907 cifs_small_buf_release(pSMB);
1908
Steve French133672e2007-11-13 22:41:37 +00001909 if (resp_buf_type == CIFS_SMALL_BUFFER)
1910 cifs_small_buf_release(iov[0].iov_base);
1911 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1912 cifs_buf_release(iov[0].iov_base);
1913
Steve French08547b02006-02-28 22:39:25 +00001914 /* Note: On -EAGAIN error only caller can retry on handle based calls
1915 since file handle passed in no longer valid */
1916
1917 return rc;
1918}
1919
1920
1921int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1923{
1924 int rc = 0;
1925 CLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926 cFYI(1, ("In CIFSSMBClose"));
1927
1928/* do not retry on dead session on close */
1929 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001930 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931 return 0;
1932 if (rc)
1933 return rc;
1934
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e52006-10-02 05:53:29 +00001936 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001938 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001939 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001941 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 /* EINTR is expected when user ctl-c to kill app */
1943 cERROR(1, ("Send error in Close = %d", rc));
1944 }
1945 }
1946
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001948 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 rc = 0;
1950
1951 return rc;
1952}
1953
1954int
1955CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1956 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001957 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958{
1959 int rc = 0;
1960 RENAME_REQ *pSMB = NULL;
1961 RENAME_RSP *pSMBr = NULL;
1962 int bytes_returned;
1963 int name_len, name_len2;
1964 __u16 count;
1965
1966 cFYI(1, ("In CIFSSMBRename"));
1967renameRetry:
1968 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1969 (void **) &pSMBr);
1970 if (rc)
1971 return rc;
1972
1973 pSMB->BufferFormat = 0x04;
1974 pSMB->SearchAttributes =
1975 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1976 ATTR_DIRECTORY);
1977
1978 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1979 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001980 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001981 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 name_len++; /* trailing null */
1983 name_len *= 2;
1984 pSMB->OldFileName[name_len] = 0x04; /* pad */
1985 /* protocol requires ASCII signature byte on Unicode string */
1986 pSMB->OldFileName[name_len + 1] = 0x00;
1987 name_len2 =
Steve French582d21e2008-05-13 04:54:12 +00001988 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07001989 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
1991 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00001992 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 name_len = strnlen(fromName, PATH_MAX);
1994 name_len++; /* trailing null */
1995 strncpy(pSMB->OldFileName, fromName, name_len);
1996 name_len2 = strnlen(toName, PATH_MAX);
1997 name_len2++; /* trailing null */
1998 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
1999 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2000 name_len2++; /* trailing null */
2001 name_len2++; /* signature byte */
2002 }
2003
2004 count = 1 /* 1st signature byte */ + name_len + name_len2;
2005 pSMB->hdr.smb_buf_length += count;
2006 pSMB->ByteCount = cpu_to_le16(count);
2007
2008 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2009 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002010 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002011 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 cFYI(1, ("Send error in rename = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 cifs_buf_release(pSMB);
2015
2016 if (rc == -EAGAIN)
2017 goto renameRetry;
2018
2019 return rc;
2020}
2021
Steve French50c2f752007-07-13 00:33:32 +00002022int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
2023 int netfid, char *target_name,
2024 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025{
2026 struct smb_com_transaction2_sfi_req *pSMB = NULL;
2027 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00002028 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 char *data_offset;
2030 char dummy_string[30];
2031 int rc = 0;
2032 int bytes_returned = 0;
2033 int len_of_str;
2034 __u16 params, param_offset, offset, count, byte_count;
2035
2036 cFYI(1, ("Rename to File by handle"));
2037 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
2038 (void **) &pSMBr);
2039 if (rc)
2040 return rc;
2041
2042 params = 6;
2043 pSMB->MaxSetupCount = 0;
2044 pSMB->Reserved = 0;
2045 pSMB->Flags = 0;
2046 pSMB->Timeout = 0;
2047 pSMB->Reserved2 = 0;
2048 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2049 offset = param_offset + params;
2050
2051 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2052 rename_info = (struct set_file_rename *) data_offset;
2053 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002054 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 pSMB->SetupCount = 1;
2056 pSMB->Reserved3 = 0;
2057 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2058 byte_count = 3 /* pad */ + params;
2059 pSMB->ParameterCount = cpu_to_le16(params);
2060 pSMB->TotalParameterCount = pSMB->ParameterCount;
2061 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2062 pSMB->DataOffset = cpu_to_le16(offset);
2063 /* construct random name ".cifs_tmp<inodenum><mid>" */
2064 rename_info->overwrite = cpu_to_le32(1);
2065 rename_info->root_fid = 0;
2066 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002067 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002068 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2069 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002070 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002072 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002073 target_name, PATH_MAX, nls_codepage,
2074 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 }
2076 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
2077 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str) + 2;
2078 byte_count += count;
2079 pSMB->DataCount = cpu_to_le16(count);
2080 pSMB->TotalDataCount = pSMB->DataCount;
2081 pSMB->Fid = netfid;
2082 pSMB->InformationLevel =
2083 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2084 pSMB->Reserved4 = 0;
2085 pSMB->hdr.smb_buf_length += byte_count;
2086 pSMB->ByteCount = cpu_to_le16(byte_count);
2087 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002088 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002089 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002090 if (rc)
Steve French790fe572007-07-07 19:25:05 +00002091 cFYI(1, ("Send error in Rename (by file handle) = %d", rc));
Steve Frencha5a2b482005-08-20 21:42:53 -07002092
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093 cifs_buf_release(pSMB);
2094
2095 /* Note: On -EAGAIN error only caller can retry on handle based calls
2096 since file handle passed in no longer valid */
2097
2098 return rc;
2099}
2100
2101int
Steve French50c2f752007-07-13 00:33:32 +00002102CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2103 const __u16 target_tid, const char *toName, const int flags,
2104 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105{
2106 int rc = 0;
2107 COPY_REQ *pSMB = NULL;
2108 COPY_RSP *pSMBr = NULL;
2109 int bytes_returned;
2110 int name_len, name_len2;
2111 __u16 count;
2112
2113 cFYI(1, ("In CIFSSMBCopy"));
2114copyRetry:
2115 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2116 (void **) &pSMBr);
2117 if (rc)
2118 return rc;
2119
2120 pSMB->BufferFormat = 0x04;
2121 pSMB->Tid2 = target_tid;
2122
2123 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2124
2125 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002126 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002127 fromName, PATH_MAX, nls_codepage,
2128 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129 name_len++; /* trailing null */
2130 name_len *= 2;
2131 pSMB->OldFileName[name_len] = 0x04; /* pad */
2132 /* protocol requires ASCII signature byte on Unicode string */
2133 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002134 name_len2 =
2135 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002136 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2138 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002139 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 name_len = strnlen(fromName, PATH_MAX);
2141 name_len++; /* trailing null */
2142 strncpy(pSMB->OldFileName, fromName, name_len);
2143 name_len2 = strnlen(toName, PATH_MAX);
2144 name_len2++; /* trailing null */
2145 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2146 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2147 name_len2++; /* trailing null */
2148 name_len2++; /* signature byte */
2149 }
2150
2151 count = 1 /* 1st signature byte */ + name_len + name_len2;
2152 pSMB->hdr.smb_buf_length += count;
2153 pSMB->ByteCount = cpu_to_le16(count);
2154
2155 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2156 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2157 if (rc) {
2158 cFYI(1, ("Send error in copy = %d with %d files copied",
2159 rc, le16_to_cpu(pSMBr->CopyCount)));
2160 }
2161 if (pSMB)
2162 cifs_buf_release(pSMB);
2163
2164 if (rc == -EAGAIN)
2165 goto copyRetry;
2166
2167 return rc;
2168}
2169
2170int
2171CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2172 const char *fromName, const char *toName,
2173 const struct nls_table *nls_codepage)
2174{
2175 TRANSACTION2_SPI_REQ *pSMB = NULL;
2176 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2177 char *data_offset;
2178 int name_len;
2179 int name_len_target;
2180 int rc = 0;
2181 int bytes_returned = 0;
2182 __u16 params, param_offset, offset, byte_count;
2183
2184 cFYI(1, ("In Symlink Unix style"));
2185createSymLinkRetry:
2186 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2187 (void **) &pSMBr);
2188 if (rc)
2189 return rc;
2190
2191 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2192 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002193 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 /* find define for this maxpathcomponent */
2195 , nls_codepage);
2196 name_len++; /* trailing null */
2197 name_len *= 2;
2198
Steve French50c2f752007-07-13 00:33:32 +00002199 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 name_len = strnlen(fromName, PATH_MAX);
2201 name_len++; /* trailing null */
2202 strncpy(pSMB->FileName, fromName, name_len);
2203 }
2204 params = 6 + name_len;
2205 pSMB->MaxSetupCount = 0;
2206 pSMB->Reserved = 0;
2207 pSMB->Flags = 0;
2208 pSMB->Timeout = 0;
2209 pSMB->Reserved2 = 0;
2210 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002211 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 offset = param_offset + params;
2213
2214 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2215 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2216 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002217 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218 /* find define for this maxpathcomponent */
2219 , nls_codepage);
2220 name_len_target++; /* trailing null */
2221 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002222 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223 name_len_target = strnlen(toName, PATH_MAX);
2224 name_len_target++; /* trailing null */
2225 strncpy(data_offset, toName, name_len_target);
2226 }
2227
2228 pSMB->MaxParameterCount = cpu_to_le16(2);
2229 /* BB find exact max on data count below from sess */
2230 pSMB->MaxDataCount = cpu_to_le16(1000);
2231 pSMB->SetupCount = 1;
2232 pSMB->Reserved3 = 0;
2233 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2234 byte_count = 3 /* pad */ + params + name_len_target;
2235 pSMB->DataCount = cpu_to_le16(name_len_target);
2236 pSMB->ParameterCount = cpu_to_le16(params);
2237 pSMB->TotalDataCount = pSMB->DataCount;
2238 pSMB->TotalParameterCount = pSMB->ParameterCount;
2239 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2240 pSMB->DataOffset = cpu_to_le16(offset);
2241 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2242 pSMB->Reserved4 = 0;
2243 pSMB->hdr.smb_buf_length += byte_count;
2244 pSMB->ByteCount = cpu_to_le16(byte_count);
2245 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2246 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002247 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002248 if (rc)
Steve French2d785a52007-07-15 01:48:57 +00002249 cFYI(1, ("Send error in SetPathInfo create symlink = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250
2251 if (pSMB)
2252 cifs_buf_release(pSMB);
2253
2254 if (rc == -EAGAIN)
2255 goto createSymLinkRetry;
2256
2257 return rc;
2258}
2259
2260int
2261CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2262 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002263 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264{
2265 TRANSACTION2_SPI_REQ *pSMB = NULL;
2266 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2267 char *data_offset;
2268 int name_len;
2269 int name_len_target;
2270 int rc = 0;
2271 int bytes_returned = 0;
2272 __u16 params, param_offset, offset, byte_count;
2273
2274 cFYI(1, ("In Create Hard link Unix style"));
2275createHardLinkRetry:
2276 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2277 (void **) &pSMBr);
2278 if (rc)
2279 return rc;
2280
2281 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002282 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002283 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284 name_len++; /* trailing null */
2285 name_len *= 2;
2286
Steve French50c2f752007-07-13 00:33:32 +00002287 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288 name_len = strnlen(toName, PATH_MAX);
2289 name_len++; /* trailing null */
2290 strncpy(pSMB->FileName, toName, name_len);
2291 }
2292 params = 6 + name_len;
2293 pSMB->MaxSetupCount = 0;
2294 pSMB->Reserved = 0;
2295 pSMB->Flags = 0;
2296 pSMB->Timeout = 0;
2297 pSMB->Reserved2 = 0;
2298 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002299 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300 offset = param_offset + params;
2301
2302 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2303 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2304 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002305 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002306 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 name_len_target++; /* trailing null */
2308 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002309 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 name_len_target = strnlen(fromName, PATH_MAX);
2311 name_len_target++; /* trailing null */
2312 strncpy(data_offset, fromName, name_len_target);
2313 }
2314
2315 pSMB->MaxParameterCount = cpu_to_le16(2);
2316 /* BB find exact max on data count below from sess*/
2317 pSMB->MaxDataCount = cpu_to_le16(1000);
2318 pSMB->SetupCount = 1;
2319 pSMB->Reserved3 = 0;
2320 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2321 byte_count = 3 /* pad */ + params + name_len_target;
2322 pSMB->ParameterCount = cpu_to_le16(params);
2323 pSMB->TotalParameterCount = pSMB->ParameterCount;
2324 pSMB->DataCount = cpu_to_le16(name_len_target);
2325 pSMB->TotalDataCount = pSMB->DataCount;
2326 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2327 pSMB->DataOffset = cpu_to_le16(offset);
2328 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2329 pSMB->Reserved4 = 0;
2330 pSMB->hdr.smb_buf_length += byte_count;
2331 pSMB->ByteCount = cpu_to_le16(byte_count);
2332 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2333 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002334 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002335 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336 cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337
2338 cifs_buf_release(pSMB);
2339 if (rc == -EAGAIN)
2340 goto createHardLinkRetry;
2341
2342 return rc;
2343}
2344
2345int
2346CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2347 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002348 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349{
2350 int rc = 0;
2351 NT_RENAME_REQ *pSMB = NULL;
2352 RENAME_RSP *pSMBr = NULL;
2353 int bytes_returned;
2354 int name_len, name_len2;
2355 __u16 count;
2356
2357 cFYI(1, ("In CIFSCreateHardLink"));
2358winCreateHardLinkRetry:
2359
2360 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2361 (void **) &pSMBr);
2362 if (rc)
2363 return rc;
2364
2365 pSMB->SearchAttributes =
2366 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2367 ATTR_DIRECTORY);
2368 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2369 pSMB->ClusterCount = 0;
2370
2371 pSMB->BufferFormat = 0x04;
2372
2373 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2374 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002375 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002376 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377 name_len++; /* trailing null */
2378 name_len *= 2;
2379 pSMB->OldFileName[name_len] = 0; /* pad */
Steve French50c2f752007-07-13 00:33:32 +00002380 pSMB->OldFileName[name_len + 1] = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002382 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002383 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2385 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002386 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387 name_len = strnlen(fromName, PATH_MAX);
2388 name_len++; /* trailing null */
2389 strncpy(pSMB->OldFileName, fromName, name_len);
2390 name_len2 = strnlen(toName, PATH_MAX);
2391 name_len2++; /* trailing null */
2392 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2393 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2394 name_len2++; /* trailing null */
2395 name_len2++; /* signature byte */
2396 }
2397
2398 count = 1 /* string type byte */ + name_len + name_len2;
2399 pSMB->hdr.smb_buf_length += count;
2400 pSMB->ByteCount = cpu_to_le16(count);
2401
2402 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2403 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002404 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002405 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406 cFYI(1, ("Send error in hard link (NT rename) = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00002407
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408 cifs_buf_release(pSMB);
2409 if (rc == -EAGAIN)
2410 goto winCreateHardLinkRetry;
2411
2412 return rc;
2413}
2414
2415int
2416CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
2417 const unsigned char *searchName,
2418 char *symlinkinfo, const int buflen,
2419 const struct nls_table *nls_codepage)
2420{
2421/* SMB_QUERY_FILE_UNIX_LINK */
2422 TRANSACTION2_QPI_REQ *pSMB = NULL;
2423 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2424 int rc = 0;
2425 int bytes_returned;
2426 int name_len;
2427 __u16 params, byte_count;
2428
2429 cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName));
2430
2431querySymLinkRetry:
2432 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2433 (void **) &pSMBr);
2434 if (rc)
2435 return rc;
2436
2437 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2438 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002439 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2440 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441 name_len++; /* trailing null */
2442 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002443 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444 name_len = strnlen(searchName, PATH_MAX);
2445 name_len++; /* trailing null */
2446 strncpy(pSMB->FileName, searchName, name_len);
2447 }
2448
2449 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2450 pSMB->TotalDataCount = 0;
2451 pSMB->MaxParameterCount = cpu_to_le16(2);
2452 /* BB find exact max data count below from sess structure BB */
2453 pSMB->MaxDataCount = cpu_to_le16(4000);
2454 pSMB->MaxSetupCount = 0;
2455 pSMB->Reserved = 0;
2456 pSMB->Flags = 0;
2457 pSMB->Timeout = 0;
2458 pSMB->Reserved2 = 0;
2459 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002460 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 pSMB->DataCount = 0;
2462 pSMB->DataOffset = 0;
2463 pSMB->SetupCount = 1;
2464 pSMB->Reserved3 = 0;
2465 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2466 byte_count = params + 1 /* pad */ ;
2467 pSMB->TotalParameterCount = cpu_to_le16(params);
2468 pSMB->ParameterCount = pSMB->TotalParameterCount;
2469 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2470 pSMB->Reserved4 = 0;
2471 pSMB->hdr.smb_buf_length += byte_count;
2472 pSMB->ByteCount = cpu_to_le16(byte_count);
2473
2474 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2475 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2476 if (rc) {
2477 cFYI(1, ("Send error in QuerySymLinkInfo = %d", rc));
2478 } else {
2479 /* decode response */
2480
2481 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2482 if (rc || (pSMBr->ByteCount < 2))
2483 /* BB also check enough total bytes returned */
2484 rc = -EIO; /* bad smb */
2485 else {
2486 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2487 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2488
2489 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2490 name_len = UniStrnlen((wchar_t *) ((char *)
Steve French50c2f752007-07-13 00:33:32 +00002491 &pSMBr->hdr.Protocol + data_offset),
2492 min_t(const int, buflen, count) / 2);
Steve French737b7582005-04-28 22:41:06 -07002493 /* BB FIXME investigate remapping reserved chars here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002495 (__le16 *) ((char *)&pSMBr->hdr.Protocol
2496 + data_offset),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497 name_len, nls_codepage);
2498 } else {
2499 strncpy(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002500 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501 data_offset,
2502 min_t(const int, buflen, count));
2503 }
2504 symlinkinfo[buflen] = 0;
2505 /* just in case so calling code does not go off the end of buffer */
2506 }
2507 }
2508 cifs_buf_release(pSMB);
2509 if (rc == -EAGAIN)
2510 goto querySymLinkRetry;
2511 return rc;
2512}
2513
Parag Warudkarc9489772007-10-23 18:09:48 +00002514#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08002515/* Initialize NT TRANSACT SMB into small smb request buffer.
2516 This assumes that all NT TRANSACTS that we init here have
2517 total parm and data under about 400 bytes (to fit in small cifs
2518 buffer size), which is the case so far, it easily fits. NB:
2519 Setup words themselves and ByteCount
2520 MaxSetupCount (size of returned setup area) and
2521 MaxParameterCount (returned parms size) must be set by caller */
Steve French50c2f752007-07-13 00:33:32 +00002522static int
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002523smb_init_nttransact(const __u16 sub_command, const int setup_count,
Steve French0a4b92c2006-01-12 15:44:21 -08002524 const int parm_len, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002525 void **ret_buf)
Steve French0a4b92c2006-01-12 15:44:21 -08002526{
2527 int rc;
2528 __u32 temp_offset;
Steve French50c2f752007-07-13 00:33:32 +00002529 struct smb_com_ntransact_req *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08002530
2531 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
2532 (void **)&pSMB);
2533 if (rc)
2534 return rc;
2535 *ret_buf = (void *)pSMB;
2536 pSMB->Reserved = 0;
2537 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
2538 pSMB->TotalDataCount = 0;
2539 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2540 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
2541 pSMB->ParameterCount = pSMB->TotalParameterCount;
2542 pSMB->DataCount = pSMB->TotalDataCount;
2543 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
2544 (setup_count * 2) - 4 /* for rfc1001 length itself */;
2545 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
2546 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
2547 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
2548 pSMB->SubCommand = cpu_to_le16(sub_command);
2549 return 0;
2550}
2551
2552static int
Steve French50c2f752007-07-13 00:33:32 +00002553validate_ntransact(char *buf, char **ppparm, char **ppdata,
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002554 __u32 *pparmlen, __u32 *pdatalen)
Steve French0a4b92c2006-01-12 15:44:21 -08002555{
Steve French50c2f752007-07-13 00:33:32 +00002556 char *end_of_smb;
Steve French0a4b92c2006-01-12 15:44:21 -08002557 __u32 data_count, data_offset, parm_count, parm_offset;
Steve French50c2f752007-07-13 00:33:32 +00002558 struct smb_com_ntransact_rsp *pSMBr;
Steve French0a4b92c2006-01-12 15:44:21 -08002559
Steve French630f3f0c2007-10-25 21:17:17 +00002560 *pdatalen = 0;
2561 *pparmlen = 0;
2562
Steve French790fe572007-07-07 19:25:05 +00002563 if (buf == NULL)
Steve French0a4b92c2006-01-12 15:44:21 -08002564 return -EINVAL;
2565
2566 pSMBr = (struct smb_com_ntransact_rsp *)buf;
2567
2568 /* ByteCount was converted from little endian in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +00002569 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
Steve French0a4b92c2006-01-12 15:44:21 -08002570 (char *)&pSMBr->ByteCount;
2571
Steve French0a4b92c2006-01-12 15:44:21 -08002572 data_offset = le32_to_cpu(pSMBr->DataOffset);
2573 data_count = le32_to_cpu(pSMBr->DataCount);
Steve French50c2f752007-07-13 00:33:32 +00002574 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
Steve French0a4b92c2006-01-12 15:44:21 -08002575 parm_count = le32_to_cpu(pSMBr->ParameterCount);
2576
2577 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
2578 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
2579
2580 /* should we also check that parm and data areas do not overlap? */
Steve French790fe572007-07-07 19:25:05 +00002581 if (*ppparm > end_of_smb) {
2582 cFYI(1, ("parms start after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002583 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002584 } else if (parm_count + *ppparm > end_of_smb) {
2585 cFYI(1, ("parm end after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002586 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002587 } else if (*ppdata > end_of_smb) {
2588 cFYI(1, ("data starts after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002589 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002590 } else if (data_count + *ppdata > end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002591 cFYI(1, ("data %p + count %d (%p) ends after end of smb %p start %p",
Steve French50c2f752007-07-13 00:33:32 +00002592 *ppdata, data_count, (data_count + *ppdata),
2593 end_of_smb, pSMBr));
Steve French0a4b92c2006-01-12 15:44:21 -08002594 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002595 } else if (parm_count + data_count > pSMBr->ByteCount) {
2596 cFYI(1, ("parm count and data count larger than SMB"));
Steve French0a4b92c2006-01-12 15:44:21 -08002597 return -EINVAL;
2598 }
Steve French630f3f0c2007-10-25 21:17:17 +00002599 *pdatalen = data_count;
2600 *pparmlen = parm_count;
Steve French0a4b92c2006-01-12 15:44:21 -08002601 return 0;
2602}
Parag Warudkarc9489772007-10-23 18:09:48 +00002603#endif /* CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08002604
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605int
2606CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2607 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002608 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609 const struct nls_table *nls_codepage)
2610{
2611 int rc = 0;
2612 int bytes_returned;
2613 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00002614 struct smb_com_transaction_ioctl_req *pSMB;
2615 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616
2617 cFYI(1, ("In Windows reparse style QueryLink for path %s", searchName));
2618 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2619 (void **) &pSMBr);
2620 if (rc)
2621 return rc;
2622
2623 pSMB->TotalParameterCount = 0 ;
2624 pSMB->TotalDataCount = 0;
2625 pSMB->MaxParameterCount = cpu_to_le32(2);
2626 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002627 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2628 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 pSMB->MaxSetupCount = 4;
2630 pSMB->Reserved = 0;
2631 pSMB->ParameterOffset = 0;
2632 pSMB->DataCount = 0;
2633 pSMB->DataOffset = 0;
2634 pSMB->SetupCount = 4;
2635 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2636 pSMB->ParameterCount = pSMB->TotalParameterCount;
2637 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2638 pSMB->IsFsctl = 1; /* FSCTL */
2639 pSMB->IsRootFlag = 0;
2640 pSMB->Fid = fid; /* file handle always le */
2641 pSMB->ByteCount = 0;
2642
2643 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2644 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2645 if (rc) {
2646 cFYI(1, ("Send error in QueryReparseLinkInfo = %d", rc));
2647 } else { /* decode response */
2648 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2649 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
2650 if ((pSMBr->ByteCount < 2) || (data_offset > 512))
2651 /* BB also check enough total bytes returned */
2652 rc = -EIO; /* bad smb */
2653 else {
Steve French790fe572007-07-07 19:25:05 +00002654 if (data_count && (data_count < 2048)) {
Steve French50c2f752007-07-13 00:33:32 +00002655 char *end_of_smb = 2 /* sizeof byte count */ +
Steve French0a4b92c2006-01-12 15:44:21 -08002656 pSMBr->ByteCount +
2657 (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658
Steve French50c2f752007-07-13 00:33:32 +00002659 struct reparse_data *reparse_buf =
2660 (struct reparse_data *)
2661 ((char *)&pSMBr->hdr.Protocol
2662 + data_offset);
Steve French790fe572007-07-07 19:25:05 +00002663 if ((char *)reparse_buf >= end_of_smb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664 rc = -EIO;
2665 goto qreparse_out;
2666 }
Steve French790fe572007-07-07 19:25:05 +00002667 if ((reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668 reparse_buf->TargetNameOffset +
2669 reparse_buf->TargetNameLen) >
2670 end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002671 cFYI(1, ("reparse buf beyond SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002672 rc = -EIO;
2673 goto qreparse_out;
2674 }
Steve French50c2f752007-07-13 00:33:32 +00002675
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2677 name_len = UniStrnlen((wchar_t *)
Steve French50c2f752007-07-13 00:33:32 +00002678 (reparse_buf->LinkNamesBuf +
2679 reparse_buf->TargetNameOffset),
2680 min(buflen/2,
2681 reparse_buf->TargetNameLen / 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002683 (__le16 *) (reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684 reparse_buf->TargetNameOffset),
2685 name_len, nls_codepage);
2686 } else { /* ASCII names */
Steve French50c2f752007-07-13 00:33:32 +00002687 strncpy(symlinkinfo,
2688 reparse_buf->LinkNamesBuf +
2689 reparse_buf->TargetNameOffset,
2690 min_t(const int, buflen,
2691 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692 }
2693 } else {
2694 rc = -EIO;
Steve French63135e02007-07-17 17:34:02 +00002695 cFYI(1, ("Invalid return data count on "
2696 "get reparse info ioctl"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697 }
2698 symlinkinfo[buflen] = 0; /* just in case so the caller
2699 does not go off the end of the buffer */
Steve French50c2f752007-07-13 00:33:32 +00002700 cFYI(1, ("readlink result - %s", symlinkinfo));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701 }
2702 }
2703qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002704 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705
2706 /* Note: On -EAGAIN error only caller can retry on handle based calls
2707 since file handle passed in no longer valid */
2708
2709 return rc;
2710}
2711
2712#ifdef CONFIG_CIFS_POSIX
2713
2714/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002715static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2716 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717{
2718 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002719 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2720 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2721 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002722 /* cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id)); */
2723
2724 return;
2725}
2726
2727/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002728static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2729 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730{
2731 int size = 0;
2732 int i;
2733 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002734 struct cifs_posix_ace *pACE;
2735 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2736 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737
2738 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2739 return -EOPNOTSUPP;
2740
Steve French790fe572007-07-07 19:25:05 +00002741 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742 count = le16_to_cpu(cifs_acl->access_entry_count);
2743 pACE = &cifs_acl->ace_array[0];
2744 size = sizeof(struct cifs_posix_acl);
2745 size += sizeof(struct cifs_posix_ace) * count;
2746 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002747 if (size_of_data_area < size) {
Steve French50c2f752007-07-13 00:33:32 +00002748 cFYI(1, ("bad CIFS POSIX ACL size %d vs. %d",
2749 size_of_data_area, size));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 return -EINVAL;
2751 }
Steve French790fe572007-07-07 19:25:05 +00002752 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753 count = le16_to_cpu(cifs_acl->access_entry_count);
2754 size = sizeof(struct cifs_posix_acl);
2755 size += sizeof(struct cifs_posix_ace) * count;
2756/* skip past access ACEs to get to default ACEs */
2757 pACE = &cifs_acl->ace_array[count];
2758 count = le16_to_cpu(cifs_acl->default_entry_count);
2759 size += sizeof(struct cifs_posix_ace) * count;
2760 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002761 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762 return -EINVAL;
2763 } else {
2764 /* illegal type */
2765 return -EINVAL;
2766 }
2767
2768 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002769 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002770 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002771 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772 return -ERANGE;
2773 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002774 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002775 for (i = 0; i < count ; i++) {
2776 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2777 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 }
2779 }
2780 return size;
2781}
2782
Steve French50c2f752007-07-13 00:33:32 +00002783static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2784 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785{
2786 __u16 rc = 0; /* 0 = ACL converted ok */
2787
Steve Frenchff7feac2005-11-15 16:45:16 -08002788 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2789 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002791 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 /* Probably no need to le convert -1 on any arch but can not hurt */
2793 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002794 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002795 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Steve French50c2f752007-07-13 00:33:32 +00002796 /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 return rc;
2798}
2799
2800/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002801static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2802 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803{
2804 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002805 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2806 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002807 int count;
2808 int i;
2809
Steve French790fe572007-07-07 19:25:05 +00002810 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 return 0;
2812
2813 count = posix_acl_xattr_count((size_t)buflen);
Steve Frenchc18c8422007-07-18 23:21:09 +00002814 cFYI(1, ("setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002815 "version of %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002816 count, buflen, le32_to_cpu(local_acl->a_version)));
Steve French790fe572007-07-07 19:25:05 +00002817 if (le32_to_cpu(local_acl->a_version) != 2) {
Steve French50c2f752007-07-13 00:33:32 +00002818 cFYI(1, ("unknown POSIX ACL version %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002819 le32_to_cpu(local_acl->a_version)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820 return 0;
2821 }
2822 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002823 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002824 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002825 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002826 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827 else {
Steve French50c2f752007-07-13 00:33:32 +00002828 cFYI(1, ("unknown ACL type %d", acl_type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829 return 0;
2830 }
Steve French50c2f752007-07-13 00:33:32 +00002831 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2833 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002834 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835 /* ACE not converted */
2836 break;
2837 }
2838 }
Steve French790fe572007-07-07 19:25:05 +00002839 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2841 rc += sizeof(struct cifs_posix_acl);
2842 /* BB add check to make sure ACL does not overflow SMB */
2843 }
2844 return rc;
2845}
2846
2847int
2848CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002849 const unsigned char *searchName,
2850 char *acl_inf, const int buflen, const int acl_type,
2851 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852{
2853/* SMB_QUERY_POSIX_ACL */
2854 TRANSACTION2_QPI_REQ *pSMB = NULL;
2855 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2856 int rc = 0;
2857 int bytes_returned;
2858 int name_len;
2859 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002860
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861 cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName));
2862
2863queryAclRetry:
2864 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2865 (void **) &pSMBr);
2866 if (rc)
2867 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002868
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2870 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002871 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002872 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873 name_len++; /* trailing null */
2874 name_len *= 2;
2875 pSMB->FileName[name_len] = 0;
2876 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002877 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878 name_len = strnlen(searchName, PATH_MAX);
2879 name_len++; /* trailing null */
2880 strncpy(pSMB->FileName, searchName, name_len);
2881 }
2882
2883 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2884 pSMB->TotalDataCount = 0;
2885 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002886 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002887 pSMB->MaxDataCount = cpu_to_le16(4000);
2888 pSMB->MaxSetupCount = 0;
2889 pSMB->Reserved = 0;
2890 pSMB->Flags = 0;
2891 pSMB->Timeout = 0;
2892 pSMB->Reserved2 = 0;
2893 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002894 offsetof(struct smb_com_transaction2_qpi_req,
2895 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896 pSMB->DataCount = 0;
2897 pSMB->DataOffset = 0;
2898 pSMB->SetupCount = 1;
2899 pSMB->Reserved3 = 0;
2900 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2901 byte_count = params + 1 /* pad */ ;
2902 pSMB->TotalParameterCount = cpu_to_le16(params);
2903 pSMB->ParameterCount = pSMB->TotalParameterCount;
2904 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2905 pSMB->Reserved4 = 0;
2906 pSMB->hdr.smb_buf_length += byte_count;
2907 pSMB->ByteCount = cpu_to_le16(byte_count);
2908
2909 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2910 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002911 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912 if (rc) {
2913 cFYI(1, ("Send error in Query POSIX ACL = %d", rc));
2914 } else {
2915 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002916
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2918 if (rc || (pSMBr->ByteCount < 2))
2919 /* BB also check enough total bytes returned */
2920 rc = -EIO; /* bad smb */
2921 else {
2922 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2923 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2924 rc = cifs_copy_posix_acl(acl_inf,
2925 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002926 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002927 }
2928 }
2929 cifs_buf_release(pSMB);
2930 if (rc == -EAGAIN)
2931 goto queryAclRetry;
2932 return rc;
2933}
2934
2935int
2936CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002937 const unsigned char *fileName,
2938 const char *local_acl, const int buflen,
2939 const int acl_type,
2940 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002941{
2942 struct smb_com_transaction2_spi_req *pSMB = NULL;
2943 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2944 char *parm_data;
2945 int name_len;
2946 int rc = 0;
2947 int bytes_returned = 0;
2948 __u16 params, byte_count, data_count, param_offset, offset;
2949
2950 cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName));
2951setAclRetry:
2952 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002953 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002954 if (rc)
2955 return rc;
2956 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2957 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002958 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002959 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002960 name_len++; /* trailing null */
2961 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002962 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002963 name_len = strnlen(fileName, PATH_MAX);
2964 name_len++; /* trailing null */
2965 strncpy(pSMB->FileName, fileName, name_len);
2966 }
2967 params = 6 + name_len;
2968 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00002969 /* BB find max SMB size from sess */
2970 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002971 pSMB->MaxSetupCount = 0;
2972 pSMB->Reserved = 0;
2973 pSMB->Flags = 0;
2974 pSMB->Timeout = 0;
2975 pSMB->Reserved2 = 0;
2976 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002977 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002978 offset = param_offset + params;
2979 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2980 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2981
2982 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002983 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002984
Steve French790fe572007-07-07 19:25:05 +00002985 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002986 rc = -EOPNOTSUPP;
2987 goto setACLerrorExit;
2988 }
2989 pSMB->DataOffset = cpu_to_le16(offset);
2990 pSMB->SetupCount = 1;
2991 pSMB->Reserved3 = 0;
2992 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2993 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2994 byte_count = 3 /* pad */ + params + data_count;
2995 pSMB->DataCount = cpu_to_le16(data_count);
2996 pSMB->TotalDataCount = pSMB->DataCount;
2997 pSMB->ParameterCount = cpu_to_le16(params);
2998 pSMB->TotalParameterCount = pSMB->ParameterCount;
2999 pSMB->Reserved4 = 0;
3000 pSMB->hdr.smb_buf_length += byte_count;
3001 pSMB->ByteCount = cpu_to_le16(byte_count);
3002 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003003 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003004 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003005 cFYI(1, ("Set POSIX ACL returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003006
3007setACLerrorExit:
3008 cifs_buf_release(pSMB);
3009 if (rc == -EAGAIN)
3010 goto setAclRetry;
3011 return rc;
3012}
3013
Steve Frenchf654bac2005-04-28 22:41:04 -07003014/* BB fix tabs in this function FIXME BB */
3015int
3016CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00003017 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07003018{
Steve French50c2f752007-07-13 00:33:32 +00003019 int rc = 0;
3020 struct smb_t2_qfi_req *pSMB = NULL;
3021 struct smb_t2_qfi_rsp *pSMBr = NULL;
3022 int bytes_returned;
3023 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07003024
Steve French790fe572007-07-07 19:25:05 +00003025 cFYI(1, ("In GetExtAttr"));
3026 if (tcon == NULL)
3027 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07003028
3029GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00003030 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3031 (void **) &pSMBr);
3032 if (rc)
3033 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003034
Steve Frenchad7a2922008-02-07 23:25:02 +00003035 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00003036 pSMB->t2.TotalDataCount = 0;
3037 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3038 /* BB find exact max data count below from sess structure BB */
3039 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
3040 pSMB->t2.MaxSetupCount = 0;
3041 pSMB->t2.Reserved = 0;
3042 pSMB->t2.Flags = 0;
3043 pSMB->t2.Timeout = 0;
3044 pSMB->t2.Reserved2 = 0;
3045 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3046 Fid) - 4);
3047 pSMB->t2.DataCount = 0;
3048 pSMB->t2.DataOffset = 0;
3049 pSMB->t2.SetupCount = 1;
3050 pSMB->t2.Reserved3 = 0;
3051 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3052 byte_count = params + 1 /* pad */ ;
3053 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3054 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3055 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
3056 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07003057 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00003058 pSMB->hdr.smb_buf_length += byte_count;
3059 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07003060
Steve French790fe572007-07-07 19:25:05 +00003061 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3062 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3063 if (rc) {
3064 cFYI(1, ("error %d in GetExtAttr", rc));
3065 } else {
3066 /* decode response */
3067 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3068 if (rc || (pSMBr->ByteCount < 2))
3069 /* BB also check enough total bytes returned */
3070 /* If rc should we check for EOPNOSUPP and
3071 disable the srvino flag? or in caller? */
3072 rc = -EIO; /* bad smb */
3073 else {
3074 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3075 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
3076 struct file_chattr_info *pfinfo;
3077 /* BB Do we need a cast or hash here ? */
3078 if (count != 16) {
3079 cFYI(1, ("Illegal size ret in GetExtAttr"));
3080 rc = -EIO;
3081 goto GetExtAttrOut;
3082 }
3083 pfinfo = (struct file_chattr_info *)
3084 (data_offset + (char *) &pSMBr->hdr.Protocol);
3085 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07003086 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00003087 }
3088 }
Steve Frenchf654bac2005-04-28 22:41:04 -07003089GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00003090 cifs_buf_release(pSMB);
3091 if (rc == -EAGAIN)
3092 goto GetExtAttrRetry;
3093 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003094}
3095
Steve Frenchf654bac2005-04-28 22:41:04 -07003096#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003097
Steve French297647c2007-10-12 04:11:59 +00003098#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08003099/* Get Security Descriptor (by handle) from remote server for a file or dir */
3100int
3101CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f0c2007-10-25 21:17:17 +00003102 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003103{
3104 int rc = 0;
3105 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003106 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003107 struct kvec iov[1];
3108
3109 cFYI(1, ("GetCifsACL"));
3110
Steve French630f3f0c2007-10-25 21:17:17 +00003111 *pbuflen = 0;
3112 *acl_inf = NULL;
3113
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003114 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003115 8 /* parm len */, tcon, (void **) &pSMB);
3116 if (rc)
3117 return rc;
3118
3119 pSMB->MaxParameterCount = cpu_to_le32(4);
3120 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3121 pSMB->MaxSetupCount = 0;
3122 pSMB->Fid = fid; /* file handle always le */
3123 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3124 CIFS_ACL_DACL);
3125 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3126 pSMB->hdr.smb_buf_length += 11;
3127 iov[0].iov_base = (char *)pSMB;
3128 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3129
Steve Frencha761ac52007-10-18 21:45:27 +00003130 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Steve French133672e2007-11-13 22:41:37 +00003131 CIFS_STD_OP);
Steve French0a4b92c2006-01-12 15:44:21 -08003132 cifs_stats_inc(&tcon->num_acl_get);
3133 if (rc) {
3134 cFYI(1, ("Send error in QuerySecDesc = %d", rc));
3135 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003136 __le32 *parm;
Steve French630f3f0c2007-10-25 21:17:17 +00003137 __u32 parm_len;
3138 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003139 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f0c2007-10-25 21:17:17 +00003140 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003141
3142/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003143 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f0c2007-10-25 21:17:17 +00003144 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003145 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003146 goto qsec_out;
3147 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3148
Steve French630f3f0c2007-10-25 21:17:17 +00003149 cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, *acl_inf));
Steve French0a4b92c2006-01-12 15:44:21 -08003150
3151 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3152 rc = -EIO; /* bad smb */
Steve French630f3f0c2007-10-25 21:17:17 +00003153 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003154 goto qsec_out;
3155 }
3156
3157/* BB check that data area is minimum length and as big as acl_len */
3158
Steve Frenchaf6f4612007-10-16 18:40:37 +00003159 acl_len = le32_to_cpu(*parm);
Steve French630f3f0c2007-10-25 21:17:17 +00003160 if (acl_len != *pbuflen) {
3161 cERROR(1, ("acl length %d does not match %d",
3162 acl_len, *pbuflen));
3163 if (*pbuflen > acl_len)
3164 *pbuflen = acl_len;
3165 }
Steve French0a4b92c2006-01-12 15:44:21 -08003166
Steve French630f3f0c2007-10-25 21:17:17 +00003167 /* check if buffer is big enough for the acl
3168 header followed by the smallest SID */
3169 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3170 (*pbuflen >= 64 * 1024)) {
3171 cERROR(1, ("bad acl length %d", *pbuflen));
3172 rc = -EINVAL;
3173 *pbuflen = 0;
3174 } else {
3175 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3176 if (*acl_inf == NULL) {
3177 *pbuflen = 0;
3178 rc = -ENOMEM;
3179 }
3180 memcpy(*acl_inf, pdata, *pbuflen);
3181 }
Steve French0a4b92c2006-01-12 15:44:21 -08003182 }
3183qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003184 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003185 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003186 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003187 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003188/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003189 return rc;
3190}
Steve French97837582007-12-31 07:47:21 +00003191
3192int
3193CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3194 struct cifs_ntsd *pntsd, __u32 acllen)
3195{
3196 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3197 int rc = 0;
3198 int bytes_returned = 0;
3199 SET_SEC_DESC_REQ *pSMB = NULL;
3200 NTRANSACT_RSP *pSMBr = NULL;
3201
3202setCifsAclRetry:
3203 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3204 (void **) &pSMBr);
3205 if (rc)
3206 return (rc);
3207
3208 pSMB->MaxSetupCount = 0;
3209 pSMB->Reserved = 0;
3210
3211 param_count = 8;
3212 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3213 data_count = acllen;
3214 data_offset = param_offset + param_count;
3215 byte_count = 3 /* pad */ + param_count;
3216
3217 pSMB->DataCount = cpu_to_le32(data_count);
3218 pSMB->TotalDataCount = pSMB->DataCount;
3219 pSMB->MaxParameterCount = cpu_to_le32(4);
3220 pSMB->MaxDataCount = cpu_to_le32(16384);
3221 pSMB->ParameterCount = cpu_to_le32(param_count);
3222 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3223 pSMB->TotalParameterCount = pSMB->ParameterCount;
3224 pSMB->DataOffset = cpu_to_le32(data_offset);
3225 pSMB->SetupCount = 0;
3226 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3227 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3228
3229 pSMB->Fid = fid; /* file handle always le */
3230 pSMB->Reserved2 = 0;
3231 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3232
3233 if (pntsd && acllen) {
3234 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3235 (char *) pntsd,
3236 acllen);
3237 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3238
3239 } else
3240 pSMB->hdr.smb_buf_length += byte_count;
3241
3242 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3243 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3244
3245 cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc));
3246 if (rc)
3247 cFYI(1, ("Set CIFS ACL returned %d", rc));
3248 cifs_buf_release(pSMB);
3249
3250 if (rc == -EAGAIN)
3251 goto setCifsAclRetry;
3252
3253 return (rc);
3254}
3255
Steve French297647c2007-10-12 04:11:59 +00003256#endif /* CONFIG_CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08003257
Steve French6b8edfe2005-08-23 20:26:03 -07003258/* Legacy Query Path Information call for lookup to old servers such
3259 as Win9x/WinME */
3260int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003261 const unsigned char *searchName,
3262 FILE_ALL_INFO *pFinfo,
3263 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003264{
Steve Frenchad7a2922008-02-07 23:25:02 +00003265 QUERY_INFORMATION_REQ *pSMB;
3266 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003267 int rc = 0;
3268 int bytes_returned;
3269 int name_len;
3270
Steve French50c2f752007-07-13 00:33:32 +00003271 cFYI(1, ("In SMBQPath path %s", searchName));
Steve French6b8edfe2005-08-23 20:26:03 -07003272QInfRetry:
3273 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003274 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003275 if (rc)
3276 return rc;
3277
3278 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3279 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003280 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3281 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003282 name_len++; /* trailing null */
3283 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003284 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003285 name_len = strnlen(searchName, PATH_MAX);
3286 name_len++; /* trailing null */
3287 strncpy(pSMB->FileName, searchName, name_len);
3288 }
3289 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003290 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003291 pSMB->hdr.smb_buf_length += (__u16) name_len;
3292 pSMB->ByteCount = cpu_to_le16(name_len);
3293
3294 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003295 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003296 if (rc) {
3297 cFYI(1, ("Send error in QueryInfo = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003298 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003299 struct timespec ts;
3300 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003301
3302 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003303 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003304 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003305 ts.tv_nsec = 0;
3306 ts.tv_sec = time;
3307 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003308 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003309 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3310 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003311 pFinfo->AllocationSize =
3312 cpu_to_le64(le32_to_cpu(pSMBr->size));
3313 pFinfo->EndOfFile = pFinfo->AllocationSize;
3314 pFinfo->Attributes =
3315 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003316 } else
3317 rc = -EIO; /* bad buffer passed in */
3318
3319 cifs_buf_release(pSMB);
3320
3321 if (rc == -EAGAIN)
3322 goto QInfRetry;
3323
3324 return rc;
3325}
3326
3327
3328
3329
Linus Torvalds1da177e2005-04-16 15:20:36 -07003330int
3331CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3332 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003333 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003334 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003335 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003336{
3337/* level 263 SMB_QUERY_FILE_ALL_INFO */
3338 TRANSACTION2_QPI_REQ *pSMB = NULL;
3339 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3340 int rc = 0;
3341 int bytes_returned;
3342 int name_len;
3343 __u16 params, byte_count;
3344
3345/* cFYI(1, ("In QPathInfo path %s", searchName)); */
3346QPathInfoRetry:
3347 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3348 (void **) &pSMBr);
3349 if (rc)
3350 return rc;
3351
3352 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3353 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003354 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003355 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003356 name_len++; /* trailing null */
3357 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003358 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003359 name_len = strnlen(searchName, PATH_MAX);
3360 name_len++; /* trailing null */
3361 strncpy(pSMB->FileName, searchName, name_len);
3362 }
3363
Steve French50c2f752007-07-13 00:33:32 +00003364 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003365 pSMB->TotalDataCount = 0;
3366 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00003367 /* BB find exact max SMB PDU from sess structure BB */
3368 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003369 pSMB->MaxSetupCount = 0;
3370 pSMB->Reserved = 0;
3371 pSMB->Flags = 0;
3372 pSMB->Timeout = 0;
3373 pSMB->Reserved2 = 0;
3374 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003375 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003376 pSMB->DataCount = 0;
3377 pSMB->DataOffset = 0;
3378 pSMB->SetupCount = 1;
3379 pSMB->Reserved3 = 0;
3380 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3381 byte_count = params + 1 /* pad */ ;
3382 pSMB->TotalParameterCount = cpu_to_le16(params);
3383 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003384 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003385 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3386 else
3387 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388 pSMB->Reserved4 = 0;
3389 pSMB->hdr.smb_buf_length += byte_count;
3390 pSMB->ByteCount = cpu_to_le16(byte_count);
3391
3392 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3393 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3394 if (rc) {
3395 cFYI(1, ("Send error in QPathInfo = %d", rc));
3396 } else { /* decode response */
3397 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3398
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003399 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3400 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003401 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003402 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003403 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003404 rc = -EIO; /* 24 or 26 expected but we do not read
3405 last field */
3406 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003407 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003408 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003409
3410 /* On legacy responses we do not read the last field,
3411 EAsize, fortunately since it varies by subdialect and
3412 also note it differs on Set vs. Get, ie two bytes or 4
3413 bytes depending but we don't care here */
3414 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003415 size = sizeof(FILE_INFO_STANDARD);
3416 else
3417 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003418 memcpy((char *) pFindData,
3419 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003420 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003421 } else
3422 rc = -ENOMEM;
3423 }
3424 cifs_buf_release(pSMB);
3425 if (rc == -EAGAIN)
3426 goto QPathInfoRetry;
3427
3428 return rc;
3429}
3430
3431int
3432CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3433 const unsigned char *searchName,
Steve French582d21e2008-05-13 04:54:12 +00003434 FILE_UNIX_BASIC_INFO *pFindData,
Steve French737b7582005-04-28 22:41:06 -07003435 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003436{
3437/* SMB_QUERY_FILE_UNIX_BASIC */
3438 TRANSACTION2_QPI_REQ *pSMB = NULL;
3439 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3440 int rc = 0;
3441 int bytes_returned = 0;
3442 int name_len;
3443 __u16 params, byte_count;
3444
3445 cFYI(1, ("In QPathInfo (Unix) the path %s", searchName));
3446UnixQPathInfoRetry:
3447 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3448 (void **) &pSMBr);
3449 if (rc)
3450 return rc;
3451
3452 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3453 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003454 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003455 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003456 name_len++; /* trailing null */
3457 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003458 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003459 name_len = strnlen(searchName, PATH_MAX);
3460 name_len++; /* trailing null */
3461 strncpy(pSMB->FileName, searchName, name_len);
3462 }
3463
Steve French50c2f752007-07-13 00:33:32 +00003464 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003465 pSMB->TotalDataCount = 0;
3466 pSMB->MaxParameterCount = cpu_to_le16(2);
3467 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003468 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003469 pSMB->MaxSetupCount = 0;
3470 pSMB->Reserved = 0;
3471 pSMB->Flags = 0;
3472 pSMB->Timeout = 0;
3473 pSMB->Reserved2 = 0;
3474 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003475 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003476 pSMB->DataCount = 0;
3477 pSMB->DataOffset = 0;
3478 pSMB->SetupCount = 1;
3479 pSMB->Reserved3 = 0;
3480 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3481 byte_count = params + 1 /* pad */ ;
3482 pSMB->TotalParameterCount = cpu_to_le16(params);
3483 pSMB->ParameterCount = pSMB->TotalParameterCount;
3484 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3485 pSMB->Reserved4 = 0;
3486 pSMB->hdr.smb_buf_length += byte_count;
3487 pSMB->ByteCount = cpu_to_le16(byte_count);
3488
3489 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3490 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3491 if (rc) {
3492 cFYI(1, ("Send error in QPathInfo = %d", rc));
3493 } else { /* decode response */
3494 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3495
3496 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve French1e71f252007-09-20 15:30:07 +00003497 cERROR(1, ("Malformed FILE_UNIX_BASIC_INFO response.\n"
3498 "Unix Extensions can be disabled on mount "
3499 "by specifying the nosfu mount option."));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003500 rc = -EIO; /* bad smb */
3501 } else {
3502 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3503 memcpy((char *) pFindData,
3504 (char *) &pSMBr->hdr.Protocol +
3505 data_offset,
Steve French630f3f0c2007-10-25 21:17:17 +00003506 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003507 }
3508 }
3509 cifs_buf_release(pSMB);
3510 if (rc == -EAGAIN)
3511 goto UnixQPathInfoRetry;
3512
3513 return rc;
3514}
3515
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516/* xid, tcon, searchName and codepage are input parms, rest are returned */
3517int
3518CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003519 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003520 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003521 __u16 *pnetfid,
3522 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003523{
3524/* level 257 SMB_ */
3525 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3526 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003527 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003528 int rc = 0;
3529 int bytes_returned = 0;
3530 int name_len;
3531 __u16 params, byte_count;
3532
Steve French50c2f752007-07-13 00:33:32 +00003533 cFYI(1, ("In FindFirst for %s", searchName));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003534
3535findFirstRetry:
3536 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3537 (void **) &pSMBr);
3538 if (rc)
3539 return rc;
3540
3541 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3542 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003543 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003544 PATH_MAX, nls_codepage, remap);
3545 /* We can not add the asterik earlier in case
3546 it got remapped to 0xF03A as if it were part of the
3547 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003548 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003549 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003550 pSMB->FileName[name_len+1] = 0;
3551 pSMB->FileName[name_len+2] = '*';
3552 pSMB->FileName[name_len+3] = 0;
3553 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003554 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3555 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003556 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003557 } else { /* BB add check for overrun of SMB buf BB */
3558 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003559/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003560 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003561 free buffer exit; BB */
3562 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003563 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003564 pSMB->FileName[name_len+1] = '*';
3565 pSMB->FileName[name_len+2] = 0;
3566 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003567 }
3568
3569 params = 12 + name_len /* includes null */ ;
3570 pSMB->TotalDataCount = 0; /* no EAs */
3571 pSMB->MaxParameterCount = cpu_to_le16(10);
3572 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3573 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3574 pSMB->MaxSetupCount = 0;
3575 pSMB->Reserved = 0;
3576 pSMB->Flags = 0;
3577 pSMB->Timeout = 0;
3578 pSMB->Reserved2 = 0;
3579 byte_count = params + 1 /* pad */ ;
3580 pSMB->TotalParameterCount = cpu_to_le16(params);
3581 pSMB->ParameterCount = pSMB->TotalParameterCount;
3582 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003583 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3584 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003585 pSMB->DataCount = 0;
3586 pSMB->DataOffset = 0;
3587 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3588 pSMB->Reserved3 = 0;
3589 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3590 pSMB->SearchAttributes =
3591 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3592 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003593 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3594 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003595 CIFS_SEARCH_RETURN_RESUME);
3596 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3597
3598 /* BB what should we set StorageType to? Does it matter? BB */
3599 pSMB->SearchStorageType = 0;
3600 pSMB->hdr.smb_buf_length += byte_count;
3601 pSMB->ByteCount = cpu_to_le16(byte_count);
3602
3603 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3604 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003605 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003606
Steve French88274812006-03-09 22:21:45 +00003607 if (rc) {/* BB add logic to retry regular search if Unix search
3608 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003609 /* BB Add code to handle unsupported level rc */
3610 cFYI(1, ("Error in FindFirst = %d", rc));
Steve French1982c342005-08-17 12:38:22 -07003611
Steve French88274812006-03-09 22:21:45 +00003612 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003613
3614 /* BB eventually could optimize out free and realloc of buf */
3615 /* for this case */
3616 if (rc == -EAGAIN)
3617 goto findFirstRetry;
3618 } else { /* decode response */
3619 /* BB remember to free buffer if error BB */
3620 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003621 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003622 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003623 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003624 else
Steve French4b18f2a2008-04-29 00:06:05 +00003625 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003626
3627 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003628 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003629 psrch_inf->srch_entries_start =
3630 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003631 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003632 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3633 le16_to_cpu(pSMBr->t2.ParameterOffset));
3634
Steve French790fe572007-07-07 19:25:05 +00003635 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003636 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003637 else
Steve French4b18f2a2008-04-29 00:06:05 +00003638 psrch_inf->endOfSearch = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003639
Steve French50c2f752007-07-13 00:33:32 +00003640 psrch_inf->entries_in_buffer =
3641 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003642 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003643 psrch_inf->entries_in_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003644 *pnetfid = parms->SearchHandle;
3645 } else {
3646 cifs_buf_release(pSMB);
3647 }
3648 }
3649
3650 return rc;
3651}
3652
3653int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003654 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003655{
3656 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3657 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003658 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003659 char *response_data;
3660 int rc = 0;
3661 int bytes_returned, name_len;
3662 __u16 params, byte_count;
3663
3664 cFYI(1, ("In FindNext"));
3665
Steve French4b18f2a2008-04-29 00:06:05 +00003666 if (psrch_inf->endOfSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003667 return -ENOENT;
3668
3669 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3670 (void **) &pSMBr);
3671 if (rc)
3672 return rc;
3673
Steve French50c2f752007-07-13 00:33:32 +00003674 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003675 byte_count = 0;
3676 pSMB->TotalDataCount = 0; /* no EAs */
3677 pSMB->MaxParameterCount = cpu_to_le16(8);
3678 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003679 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3680 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003681 pSMB->MaxSetupCount = 0;
3682 pSMB->Reserved = 0;
3683 pSMB->Flags = 0;
3684 pSMB->Timeout = 0;
3685 pSMB->Reserved2 = 0;
3686 pSMB->ParameterOffset = cpu_to_le16(
3687 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3688 pSMB->DataCount = 0;
3689 pSMB->DataOffset = 0;
3690 pSMB->SetupCount = 1;
3691 pSMB->Reserved3 = 0;
3692 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3693 pSMB->SearchHandle = searchHandle; /* always kept as le */
3694 pSMB->SearchCount =
Steve French630f3f0c2007-10-25 21:17:17 +00003695 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003696 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3697 pSMB->ResumeKey = psrch_inf->resume_key;
3698 pSMB->SearchFlags =
3699 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3700
3701 name_len = psrch_inf->resume_name_len;
3702 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003703 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003704 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3705 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003706 /* 14 byte parm len above enough for 2 byte null terminator */
3707 pSMB->ResumeFileName[name_len] = 0;
3708 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003709 } else {
3710 rc = -EINVAL;
3711 goto FNext2_err_exit;
3712 }
3713 byte_count = params + 1 /* pad */ ;
3714 pSMB->TotalParameterCount = cpu_to_le16(params);
3715 pSMB->ParameterCount = pSMB->TotalParameterCount;
3716 pSMB->hdr.smb_buf_length += byte_count;
3717 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003718
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3720 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003721 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003722 if (rc) {
3723 if (rc == -EBADF) {
Steve French4b18f2a2008-04-29 00:06:05 +00003724 psrch_inf->endOfSearch = true;
Jeff Layton63534502008-05-12 19:56:05 -07003725 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00003726 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003727 } else
3728 cFYI(1, ("FindNext returned = %d", rc));
3729 } else { /* decode response */
3730 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003731
Steve French790fe572007-07-07 19:25:05 +00003732 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733 /* BB fixme add lock for file (srch_info) struct here */
3734 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003735 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003736 else
Steve French4b18f2a2008-04-29 00:06:05 +00003737 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003738 response_data = (char *) &pSMBr->hdr.Protocol +
3739 le16_to_cpu(pSMBr->t2.ParameterOffset);
3740 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3741 response_data = (char *)&pSMBr->hdr.Protocol +
3742 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003743 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003744 cifs_small_buf_release(
3745 psrch_inf->ntwrk_buf_start);
3746 else
3747 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003748 psrch_inf->srch_entries_start = response_data;
3749 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003750 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003751 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003752 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003753 else
Steve French4b18f2a2008-04-29 00:06:05 +00003754 psrch_inf->endOfSearch = false;
Steve French50c2f752007-07-13 00:33:32 +00003755 psrch_inf->entries_in_buffer =
3756 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003757 psrch_inf->index_of_last_entry +=
3758 psrch_inf->entries_in_buffer;
Steve French50c2f752007-07-13 00:33:32 +00003759/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",
3760 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003761
3762 /* BB fixme add unlock here */
3763 }
3764
3765 }
3766
3767 /* BB On error, should we leave previous search buf (and count and
3768 last entry fields) intact or free the previous one? */
3769
3770 /* Note: On -EAGAIN error only caller can retry on handle based calls
3771 since file handle passed in no longer valid */
3772FNext2_err_exit:
3773 if (rc != 0)
3774 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003775 return rc;
3776}
3777
3778int
Steve French50c2f752007-07-13 00:33:32 +00003779CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3780 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003781{
3782 int rc = 0;
3783 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003784
3785 cFYI(1, ("In CIFSSMBFindClose"));
3786 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3787
3788 /* no sense returning error if session restarted
3789 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003790 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003791 return 0;
3792 if (rc)
3793 return rc;
3794
Linus Torvalds1da177e2005-04-16 15:20:36 -07003795 pSMB->FileID = searchHandle;
3796 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003797 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003798 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003799 cERROR(1, ("Send error in FindClose = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003800
Steve Frencha4544342005-08-24 13:59:35 -07003801 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003802
3803 /* Since session is dead, search handle closed on server already */
3804 if (rc == -EAGAIN)
3805 rc = 0;
3806
3807 return rc;
3808}
3809
Linus Torvalds1da177e2005-04-16 15:20:36 -07003810int
3811CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003812 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003813 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003814 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003815{
3816 int rc = 0;
3817 TRANSACTION2_QPI_REQ *pSMB = NULL;
3818 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3819 int name_len, bytes_returned;
3820 __u16 params, byte_count;
3821
Steve French50c2f752007-07-13 00:33:32 +00003822 cFYI(1, ("In GetSrvInodeNum for %s", searchName));
Steve French790fe572007-07-07 19:25:05 +00003823 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003824 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003825
3826GetInodeNumberRetry:
3827 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003828 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003829 if (rc)
3830 return rc;
3831
Linus Torvalds1da177e2005-04-16 15:20:36 -07003832 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3833 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003834 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003835 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003836 name_len++; /* trailing null */
3837 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003838 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003839 name_len = strnlen(searchName, PATH_MAX);
3840 name_len++; /* trailing null */
3841 strncpy(pSMB->FileName, searchName, name_len);
3842 }
3843
3844 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
3845 pSMB->TotalDataCount = 0;
3846 pSMB->MaxParameterCount = cpu_to_le16(2);
3847 /* BB find exact max data count below from sess structure BB */
3848 pSMB->MaxDataCount = cpu_to_le16(4000);
3849 pSMB->MaxSetupCount = 0;
3850 pSMB->Reserved = 0;
3851 pSMB->Flags = 0;
3852 pSMB->Timeout = 0;
3853 pSMB->Reserved2 = 0;
3854 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003855 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003856 pSMB->DataCount = 0;
3857 pSMB->DataOffset = 0;
3858 pSMB->SetupCount = 1;
3859 pSMB->Reserved3 = 0;
3860 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3861 byte_count = params + 1 /* pad */ ;
3862 pSMB->TotalParameterCount = cpu_to_le16(params);
3863 pSMB->ParameterCount = pSMB->TotalParameterCount;
3864 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
3865 pSMB->Reserved4 = 0;
3866 pSMB->hdr.smb_buf_length += byte_count;
3867 pSMB->ByteCount = cpu_to_le16(byte_count);
3868
3869 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3870 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3871 if (rc) {
3872 cFYI(1, ("error %d in QueryInternalInfo", rc));
3873 } else {
3874 /* decode response */
3875 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3876 if (rc || (pSMBr->ByteCount < 2))
3877 /* BB also check enough total bytes returned */
3878 /* If rc should we check for EOPNOSUPP and
3879 disable the srvino flag? or in caller? */
3880 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00003881 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003882 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3883 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00003884 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003885 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00003886 if (count < 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003887 cFYI(1, ("Illegal size ret in QryIntrnlInf"));
3888 rc = -EIO;
3889 goto GetInodeNumOut;
3890 }
3891 pfinfo = (struct file_internal_info *)
3892 (data_offset + (char *) &pSMBr->hdr.Protocol);
3893 *inode_number = pfinfo->UniqueId;
3894 }
3895 }
3896GetInodeNumOut:
3897 cifs_buf_release(pSMB);
3898 if (rc == -EAGAIN)
3899 goto GetInodeNumberRetry;
3900 return rc;
3901}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003902
Igor Mammedovfec45852008-05-16 13:06:30 +04003903/* parses DFS refferal V3 structure
3904 * caller is responsible for freeing target_nodes
3905 * returns:
3906 * on success - 0
3907 * on failure - errno
3908 */
3909static int
3910parse_DFS_REFERRALS(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
3911 unsigned int *num_of_nodes,
3912 struct dfs_info3_param **target_nodes,
3913 const struct nls_table *nls_codepage)
3914{
3915 int i, rc = 0;
3916 char *data_end;
3917 bool is_unicode;
3918 struct dfs_referral_level_3 *ref;
3919
3920 is_unicode = pSMBr->hdr.Flags2 & SMBFLG2_UNICODE;
3921 *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;
3927 goto parse_DFS_REFERRALS_exit;
3928 }
3929
3930 ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
3931 if (ref->VersionNumber != 3) {
3932 cERROR(1, ("Referrals of V%d version are not supported,"
3933 "should be V3", ref->VersionNumber));
3934 rc = -EINVAL;
3935 goto parse_DFS_REFERRALS_exit;
3936 }
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;
3951 goto parse_DFS_REFERRALS_exit;
3952 }
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)
3971 goto parse_DFS_REFERRALS_exit;
3972
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)
3979 goto parse_DFS_REFERRALS_exit;
3980
3981 ref += ref->Size;
3982 }
3983
3984parse_DFS_REFERRALS_exit:
3985 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 */
4093 rc = parse_DFS_REFERRALS(pSMBr, num_of_nodes,
4094 target_nodes, nls_codepage);
4095
Linus Torvalds1da177e2005-04-16 15:20:36 -07004096GetDFSRefExit:
4097 if (pSMB)
4098 cifs_buf_release(pSMB);
4099
4100 if (rc == -EAGAIN)
4101 goto getDFSRetry;
4102
4103 return rc;
4104}
4105
Steve French20962432005-09-21 22:05:57 -07004106/* Query File System Info such as free space to old servers such as Win 9x */
4107int
4108SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4109{
4110/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4111 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4112 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4113 FILE_SYSTEM_ALLOC_INFO *response_data;
4114 int rc = 0;
4115 int bytes_returned = 0;
4116 __u16 params, byte_count;
4117
4118 cFYI(1, ("OldQFSInfo"));
4119oldQFSInfoRetry:
4120 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4121 (void **) &pSMBr);
4122 if (rc)
4123 return rc;
Steve French20962432005-09-21 22:05:57 -07004124
4125 params = 2; /* level */
4126 pSMB->TotalDataCount = 0;
4127 pSMB->MaxParameterCount = cpu_to_le16(2);
4128 pSMB->MaxDataCount = cpu_to_le16(1000);
4129 pSMB->MaxSetupCount = 0;
4130 pSMB->Reserved = 0;
4131 pSMB->Flags = 0;
4132 pSMB->Timeout = 0;
4133 pSMB->Reserved2 = 0;
4134 byte_count = params + 1 /* pad */ ;
4135 pSMB->TotalParameterCount = cpu_to_le16(params);
4136 pSMB->ParameterCount = pSMB->TotalParameterCount;
4137 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4138 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4139 pSMB->DataCount = 0;
4140 pSMB->DataOffset = 0;
4141 pSMB->SetupCount = 1;
4142 pSMB->Reserved3 = 0;
4143 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4144 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4145 pSMB->hdr.smb_buf_length += byte_count;
4146 pSMB->ByteCount = cpu_to_le16(byte_count);
4147
4148 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4149 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4150 if (rc) {
4151 cFYI(1, ("Send error in QFSInfo = %d", rc));
4152 } else { /* decode response */
4153 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4154
4155 if (rc || (pSMBr->ByteCount < 18))
4156 rc = -EIO; /* bad smb */
4157 else {
4158 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00004159 cFYI(1, ("qfsinf resp BCC: %d Offset %d",
Steve French20962432005-09-21 22:05:57 -07004160 pSMBr->ByteCount, data_offset));
4161
Steve French50c2f752007-07-13 00:33:32 +00004162 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004163 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4164 FSData->f_bsize =
4165 le16_to_cpu(response_data->BytesPerSector) *
4166 le32_to_cpu(response_data->
4167 SectorsPerAllocationUnit);
4168 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004169 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004170 FSData->f_bfree = FSData->f_bavail =
4171 le32_to_cpu(response_data->FreeAllocationUnits);
4172 cFYI(1,
4173 ("Blocks: %lld Free: %lld Block size %ld",
4174 (unsigned long long)FSData->f_blocks,
4175 (unsigned long long)FSData->f_bfree,
4176 FSData->f_bsize));
4177 }
4178 }
4179 cifs_buf_release(pSMB);
4180
4181 if (rc == -EAGAIN)
4182 goto oldQFSInfoRetry;
4183
4184 return rc;
4185}
4186
Linus Torvalds1da177e2005-04-16 15:20:36 -07004187int
Steve French737b7582005-04-28 22:41:06 -07004188CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004189{
4190/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4191 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4192 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4193 FILE_SYSTEM_INFO *response_data;
4194 int rc = 0;
4195 int bytes_returned = 0;
4196 __u16 params, byte_count;
4197
4198 cFYI(1, ("In QFSInfo"));
4199QFSInfoRetry:
4200 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4201 (void **) &pSMBr);
4202 if (rc)
4203 return rc;
4204
4205 params = 2; /* level */
4206 pSMB->TotalDataCount = 0;
4207 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004208 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004209 pSMB->MaxSetupCount = 0;
4210 pSMB->Reserved = 0;
4211 pSMB->Flags = 0;
4212 pSMB->Timeout = 0;
4213 pSMB->Reserved2 = 0;
4214 byte_count = params + 1 /* pad */ ;
4215 pSMB->TotalParameterCount = cpu_to_le16(params);
4216 pSMB->ParameterCount = pSMB->TotalParameterCount;
4217 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004218 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004219 pSMB->DataCount = 0;
4220 pSMB->DataOffset = 0;
4221 pSMB->SetupCount = 1;
4222 pSMB->Reserved3 = 0;
4223 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4224 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4225 pSMB->hdr.smb_buf_length += byte_count;
4226 pSMB->ByteCount = cpu_to_le16(byte_count);
4227
4228 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4229 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4230 if (rc) {
Steve French20962432005-09-21 22:05:57 -07004231 cFYI(1, ("Send error in QFSInfo = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004232 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004233 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004234
Steve French20962432005-09-21 22:05:57 -07004235 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004236 rc = -EIO; /* bad smb */
4237 else {
4238 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004239
4240 response_data =
4241 (FILE_SYSTEM_INFO
4242 *) (((char *) &pSMBr->hdr.Protocol) +
4243 data_offset);
4244 FSData->f_bsize =
4245 le32_to_cpu(response_data->BytesPerSector) *
4246 le32_to_cpu(response_data->
4247 SectorsPerAllocationUnit);
4248 FSData->f_blocks =
4249 le64_to_cpu(response_data->TotalAllocationUnits);
4250 FSData->f_bfree = FSData->f_bavail =
4251 le64_to_cpu(response_data->FreeAllocationUnits);
4252 cFYI(1,
4253 ("Blocks: %lld Free: %lld Block size %ld",
4254 (unsigned long long)FSData->f_blocks,
4255 (unsigned long long)FSData->f_bfree,
4256 FSData->f_bsize));
4257 }
4258 }
4259 cifs_buf_release(pSMB);
4260
4261 if (rc == -EAGAIN)
4262 goto QFSInfoRetry;
4263
4264 return rc;
4265}
4266
4267int
Steve French737b7582005-04-28 22:41:06 -07004268CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004269{
4270/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4271 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4272 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4273 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4274 int rc = 0;
4275 int bytes_returned = 0;
4276 __u16 params, byte_count;
4277
4278 cFYI(1, ("In QFSAttributeInfo"));
4279QFSAttributeRetry:
4280 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4281 (void **) &pSMBr);
4282 if (rc)
4283 return rc;
4284
4285 params = 2; /* level */
4286 pSMB->TotalDataCount = 0;
4287 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004288 /* BB find exact max SMB PDU from sess structure BB */
4289 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004290 pSMB->MaxSetupCount = 0;
4291 pSMB->Reserved = 0;
4292 pSMB->Flags = 0;
4293 pSMB->Timeout = 0;
4294 pSMB->Reserved2 = 0;
4295 byte_count = params + 1 /* pad */ ;
4296 pSMB->TotalParameterCount = cpu_to_le16(params);
4297 pSMB->ParameterCount = pSMB->TotalParameterCount;
4298 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004299 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004300 pSMB->DataCount = 0;
4301 pSMB->DataOffset = 0;
4302 pSMB->SetupCount = 1;
4303 pSMB->Reserved3 = 0;
4304 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4305 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4306 pSMB->hdr.smb_buf_length += byte_count;
4307 pSMB->ByteCount = cpu_to_le16(byte_count);
4308
4309 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4310 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4311 if (rc) {
4312 cERROR(1, ("Send error in QFSAttributeInfo = %d", rc));
4313 } else { /* decode response */
4314 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4315
Steve French50c2f752007-07-13 00:33:32 +00004316 if (rc || (pSMBr->ByteCount < 13)) {
4317 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004318 rc = -EIO; /* bad smb */
4319 } else {
4320 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4321 response_data =
4322 (FILE_SYSTEM_ATTRIBUTE_INFO
4323 *) (((char *) &pSMBr->hdr.Protocol) +
4324 data_offset);
4325 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004326 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004327 }
4328 }
4329 cifs_buf_release(pSMB);
4330
4331 if (rc == -EAGAIN)
4332 goto QFSAttributeRetry;
4333
4334 return rc;
4335}
4336
4337int
Steve French737b7582005-04-28 22:41:06 -07004338CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004339{
4340/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4341 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4342 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4343 FILE_SYSTEM_DEVICE_INFO *response_data;
4344 int rc = 0;
4345 int bytes_returned = 0;
4346 __u16 params, byte_count;
4347
4348 cFYI(1, ("In QFSDeviceInfo"));
4349QFSDeviceRetry:
4350 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4351 (void **) &pSMBr);
4352 if (rc)
4353 return rc;
4354
4355 params = 2; /* level */
4356 pSMB->TotalDataCount = 0;
4357 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004358 /* BB find exact max SMB PDU from sess structure BB */
4359 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004360 pSMB->MaxSetupCount = 0;
4361 pSMB->Reserved = 0;
4362 pSMB->Flags = 0;
4363 pSMB->Timeout = 0;
4364 pSMB->Reserved2 = 0;
4365 byte_count = params + 1 /* pad */ ;
4366 pSMB->TotalParameterCount = cpu_to_le16(params);
4367 pSMB->ParameterCount = pSMB->TotalParameterCount;
4368 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004369 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004370
4371 pSMB->DataCount = 0;
4372 pSMB->DataOffset = 0;
4373 pSMB->SetupCount = 1;
4374 pSMB->Reserved3 = 0;
4375 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4376 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4377 pSMB->hdr.smb_buf_length += byte_count;
4378 pSMB->ByteCount = cpu_to_le16(byte_count);
4379
4380 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4381 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4382 if (rc) {
4383 cFYI(1, ("Send error in QFSDeviceInfo = %d", rc));
4384 } else { /* decode response */
4385 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4386
Steve French630f3f0c2007-10-25 21:17:17 +00004387 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004388 rc = -EIO; /* bad smb */
4389 else {
4390 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4391 response_data =
Steve French737b7582005-04-28 22:41:06 -07004392 (FILE_SYSTEM_DEVICE_INFO *)
4393 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004394 data_offset);
4395 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004396 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004397 }
4398 }
4399 cifs_buf_release(pSMB);
4400
4401 if (rc == -EAGAIN)
4402 goto QFSDeviceRetry;
4403
4404 return rc;
4405}
4406
4407int
Steve French737b7582005-04-28 22:41:06 -07004408CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004409{
4410/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4411 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4412 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4413 FILE_SYSTEM_UNIX_INFO *response_data;
4414 int rc = 0;
4415 int bytes_returned = 0;
4416 __u16 params, byte_count;
4417
4418 cFYI(1, ("In QFSUnixInfo"));
4419QFSUnixRetry:
4420 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4421 (void **) &pSMBr);
4422 if (rc)
4423 return rc;
4424
4425 params = 2; /* level */
4426 pSMB->TotalDataCount = 0;
4427 pSMB->DataCount = 0;
4428 pSMB->DataOffset = 0;
4429 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004430 /* BB find exact max SMB PDU from sess structure BB */
4431 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004432 pSMB->MaxSetupCount = 0;
4433 pSMB->Reserved = 0;
4434 pSMB->Flags = 0;
4435 pSMB->Timeout = 0;
4436 pSMB->Reserved2 = 0;
4437 byte_count = params + 1 /* pad */ ;
4438 pSMB->ParameterCount = cpu_to_le16(params);
4439 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004440 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4441 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004442 pSMB->SetupCount = 1;
4443 pSMB->Reserved3 = 0;
4444 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4445 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4446 pSMB->hdr.smb_buf_length += byte_count;
4447 pSMB->ByteCount = cpu_to_le16(byte_count);
4448
4449 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4450 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4451 if (rc) {
4452 cERROR(1, ("Send error in QFSUnixInfo = %d", rc));
4453 } else { /* decode response */
4454 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4455
4456 if (rc || (pSMBr->ByteCount < 13)) {
4457 rc = -EIO; /* bad smb */
4458 } else {
4459 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4460 response_data =
4461 (FILE_SYSTEM_UNIX_INFO
4462 *) (((char *) &pSMBr->hdr.Protocol) +
4463 data_offset);
4464 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004465 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004466 }
4467 }
4468 cifs_buf_release(pSMB);
4469
4470 if (rc == -EAGAIN)
4471 goto QFSUnixRetry;
4472
4473
4474 return rc;
4475}
4476
Jeremy Allisonac670552005-06-22 17:26:35 -07004477int
Steve French45abc6e2005-06-23 13:42:03 -05004478CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004479{
4480/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4481 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4482 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4483 int rc = 0;
4484 int bytes_returned = 0;
4485 __u16 params, param_offset, offset, byte_count;
4486
4487 cFYI(1, ("In SETFSUnixInfo"));
4488SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004489 /* BB switch to small buf init to save memory */
Jeremy Allisonac670552005-06-22 17:26:35 -07004490 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4491 (void **) &pSMBr);
4492 if (rc)
4493 return rc;
4494
4495 params = 4; /* 2 bytes zero followed by info level. */
4496 pSMB->MaxSetupCount = 0;
4497 pSMB->Reserved = 0;
4498 pSMB->Flags = 0;
4499 pSMB->Timeout = 0;
4500 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004501 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4502 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004503 offset = param_offset + params;
4504
4505 pSMB->MaxParameterCount = cpu_to_le16(4);
Steve French582d21e2008-05-13 04:54:12 +00004506 /* BB find exact max SMB PDU from sess structure BB */
4507 pSMB->MaxDataCount = cpu_to_le16(100);
Jeremy Allisonac670552005-06-22 17:26:35 -07004508 pSMB->SetupCount = 1;
4509 pSMB->Reserved3 = 0;
4510 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4511 byte_count = 1 /* pad */ + params + 12;
4512
4513 pSMB->DataCount = cpu_to_le16(12);
4514 pSMB->ParameterCount = cpu_to_le16(params);
4515 pSMB->TotalDataCount = pSMB->DataCount;
4516 pSMB->TotalParameterCount = pSMB->ParameterCount;
4517 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4518 pSMB->DataOffset = cpu_to_le16(offset);
4519
4520 /* Params. */
4521 pSMB->FileNum = 0;
4522 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4523
4524 /* Data. */
4525 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4526 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4527 pSMB->ClientUnixCap = cpu_to_le64(cap);
4528
4529 pSMB->hdr.smb_buf_length += byte_count;
4530 pSMB->ByteCount = cpu_to_le16(byte_count);
4531
4532 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4533 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4534 if (rc) {
4535 cERROR(1, ("Send error in SETFSUnixInfo = %d", rc));
4536 } else { /* decode response */
4537 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004538 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004539 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004540 }
4541 cifs_buf_release(pSMB);
4542
4543 if (rc == -EAGAIN)
4544 goto SETFSUnixRetry;
4545
4546 return rc;
4547}
4548
4549
Linus Torvalds1da177e2005-04-16 15:20:36 -07004550
4551int
4552CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004553 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004554{
4555/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4556 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4557 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4558 FILE_SYSTEM_POSIX_INFO *response_data;
4559 int rc = 0;
4560 int bytes_returned = 0;
4561 __u16 params, byte_count;
4562
4563 cFYI(1, ("In QFSPosixInfo"));
4564QFSPosixRetry:
4565 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4566 (void **) &pSMBr);
4567 if (rc)
4568 return rc;
4569
4570 params = 2; /* level */
4571 pSMB->TotalDataCount = 0;
4572 pSMB->DataCount = 0;
4573 pSMB->DataOffset = 0;
4574 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004575 /* BB find exact max SMB PDU from sess structure BB */
4576 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004577 pSMB->MaxSetupCount = 0;
4578 pSMB->Reserved = 0;
4579 pSMB->Flags = 0;
4580 pSMB->Timeout = 0;
4581 pSMB->Reserved2 = 0;
4582 byte_count = params + 1 /* pad */ ;
4583 pSMB->ParameterCount = cpu_to_le16(params);
4584 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004585 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4586 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004587 pSMB->SetupCount = 1;
4588 pSMB->Reserved3 = 0;
4589 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4590 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4591 pSMB->hdr.smb_buf_length += byte_count;
4592 pSMB->ByteCount = cpu_to_le16(byte_count);
4593
4594 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4595 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4596 if (rc) {
4597 cFYI(1, ("Send error in QFSUnixInfo = %d", rc));
4598 } else { /* decode response */
4599 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4600
4601 if (rc || (pSMBr->ByteCount < 13)) {
4602 rc = -EIO; /* bad smb */
4603 } else {
4604 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4605 response_data =
4606 (FILE_SYSTEM_POSIX_INFO
4607 *) (((char *) &pSMBr->hdr.Protocol) +
4608 data_offset);
4609 FSData->f_bsize =
4610 le32_to_cpu(response_data->BlockSize);
4611 FSData->f_blocks =
4612 le64_to_cpu(response_data->TotalBlocks);
4613 FSData->f_bfree =
4614 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004615 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004616 FSData->f_bavail = FSData->f_bfree;
4617 } else {
4618 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004619 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004620 }
Steve French790fe572007-07-07 19:25:05 +00004621 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004622 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004623 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004624 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004625 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004626 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004627 }
4628 }
4629 cifs_buf_release(pSMB);
4630
4631 if (rc == -EAGAIN)
4632 goto QFSPosixRetry;
4633
4634 return rc;
4635}
4636
4637
Steve French50c2f752007-07-13 00:33:32 +00004638/* We can not use write of zero bytes trick to
4639 set file size due to need for large file support. Also note that
4640 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004641 routine which is only needed to work around a sharing violation bug
4642 in Samba which this routine can run into */
4643
4644int
4645CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French4b18f2a2008-04-29 00:06:05 +00004646 __u64 size, bool SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004647 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004648{
4649 struct smb_com_transaction2_spi_req *pSMB = NULL;
4650 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4651 struct file_end_of_file_info *parm_data;
4652 int name_len;
4653 int rc = 0;
4654 int bytes_returned = 0;
4655 __u16 params, byte_count, data_count, param_offset, offset;
4656
4657 cFYI(1, ("In SetEOF"));
4658SetEOFRetry:
4659 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4660 (void **) &pSMBr);
4661 if (rc)
4662 return rc;
4663
4664 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4665 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004666 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004667 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004668 name_len++; /* trailing null */
4669 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004670 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004671 name_len = strnlen(fileName, PATH_MAX);
4672 name_len++; /* trailing null */
4673 strncpy(pSMB->FileName, fileName, name_len);
4674 }
4675 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004676 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004677 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004678 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004679 pSMB->MaxSetupCount = 0;
4680 pSMB->Reserved = 0;
4681 pSMB->Flags = 0;
4682 pSMB->Timeout = 0;
4683 pSMB->Reserved2 = 0;
4684 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004685 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004686 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004687 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004688 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4689 pSMB->InformationLevel =
4690 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4691 else
4692 pSMB->InformationLevel =
4693 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4694 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004695 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4696 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004697 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004698 else
4699 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004700 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004701 }
4702
4703 parm_data =
4704 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4705 offset);
4706 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4707 pSMB->DataOffset = cpu_to_le16(offset);
4708 pSMB->SetupCount = 1;
4709 pSMB->Reserved3 = 0;
4710 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4711 byte_count = 3 /* pad */ + params + data_count;
4712 pSMB->DataCount = cpu_to_le16(data_count);
4713 pSMB->TotalDataCount = pSMB->DataCount;
4714 pSMB->ParameterCount = cpu_to_le16(params);
4715 pSMB->TotalParameterCount = pSMB->ParameterCount;
4716 pSMB->Reserved4 = 0;
4717 pSMB->hdr.smb_buf_length += byte_count;
4718 parm_data->FileSize = cpu_to_le64(size);
4719 pSMB->ByteCount = cpu_to_le16(byte_count);
4720 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4721 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004722 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004723 cFYI(1, ("SetPathInfo (file size) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004724
4725 cifs_buf_release(pSMB);
4726
4727 if (rc == -EAGAIN)
4728 goto SetEOFRetry;
4729
4730 return rc;
4731}
4732
4733int
Steve French50c2f752007-07-13 00:33:32 +00004734CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
Steve French4b18f2a2008-04-29 00:06:05 +00004735 __u16 fid, __u32 pid_of_opener, bool SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004736{
4737 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004738 char *data_offset;
4739 struct file_end_of_file_info *parm_data;
4740 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004741 __u16 params, param_offset, offset, byte_count, count;
4742
4743 cFYI(1, ("SetFileSize (via SetFileInfo) %lld",
4744 (long long)size));
Steve Frenchcd634992005-04-28 22:41:10 -07004745 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4746
Linus Torvalds1da177e2005-04-16 15:20:36 -07004747 if (rc)
4748 return rc;
4749
4750 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4751 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004752
Linus Torvalds1da177e2005-04-16 15:20:36 -07004753 params = 6;
4754 pSMB->MaxSetupCount = 0;
4755 pSMB->Reserved = 0;
4756 pSMB->Flags = 0;
4757 pSMB->Timeout = 0;
4758 pSMB->Reserved2 = 0;
4759 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4760 offset = param_offset + params;
4761
Steve French50c2f752007-07-13 00:33:32 +00004762 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004763
4764 count = sizeof(struct file_end_of_file_info);
4765 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004766 /* BB find exact max SMB PDU from sess structure BB */
4767 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004768 pSMB->SetupCount = 1;
4769 pSMB->Reserved3 = 0;
4770 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4771 byte_count = 3 /* pad */ + params + count;
4772 pSMB->DataCount = cpu_to_le16(count);
4773 pSMB->ParameterCount = cpu_to_le16(params);
4774 pSMB->TotalDataCount = pSMB->DataCount;
4775 pSMB->TotalParameterCount = pSMB->ParameterCount;
4776 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4777 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004778 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4779 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004780 pSMB->DataOffset = cpu_to_le16(offset);
4781 parm_data->FileSize = cpu_to_le64(size);
4782 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004783 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004784 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4785 pSMB->InformationLevel =
4786 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4787 else
4788 pSMB->InformationLevel =
4789 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004790 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004791 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4792 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004793 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004794 else
4795 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004796 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004797 }
4798 pSMB->Reserved4 = 0;
4799 pSMB->hdr.smb_buf_length += byte_count;
4800 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004801 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004802 if (rc) {
4803 cFYI(1,
4804 ("Send error in SetFileInfo (SetFileSize) = %d",
4805 rc));
4806 }
4807
Steve French50c2f752007-07-13 00:33:32 +00004808 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004809 since file handle passed in no longer valid */
4810
4811 return rc;
4812}
4813
Steve French50c2f752007-07-13 00:33:32 +00004814/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004815 an open handle, rather than by pathname - this is awkward due to
4816 potential access conflicts on the open, but it is unavoidable for these
4817 old servers since the only other choice is to go from 100 nanosecond DCE
4818 time and resort to the original setpathinfo level which takes the ancient
4819 DOS time format with 2 second granularity */
4820int
Steve French50c2f752007-07-13 00:33:32 +00004821CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon,
4822 const FILE_BASIC_INFO *data, __u16 fid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004823{
4824 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004825 char *data_offset;
4826 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004827 __u16 params, param_offset, offset, byte_count, count;
4828
4829 cFYI(1, ("Set Times (via SetFileInfo)"));
Steve Frenchcd634992005-04-28 22:41:10 -07004830 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4831
Linus Torvalds1da177e2005-04-16 15:20:36 -07004832 if (rc)
4833 return rc;
4834
4835 /* At this point there is no need to override the current pid
4836 with the pid of the opener, but that could change if we someday
4837 use an existing handle (rather than opening one on the fly) */
4838 /* pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4839 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));*/
Steve French50c2f752007-07-13 00:33:32 +00004840
Linus Torvalds1da177e2005-04-16 15:20:36 -07004841 params = 6;
4842 pSMB->MaxSetupCount = 0;
4843 pSMB->Reserved = 0;
4844 pSMB->Flags = 0;
4845 pSMB->Timeout = 0;
4846 pSMB->Reserved2 = 0;
4847 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4848 offset = param_offset + params;
4849
Steve French50c2f752007-07-13 00:33:32 +00004850 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004851
Steve French26f57362007-08-30 22:09:15 +00004852 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004853 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004854 /* BB find max SMB PDU from sess */
4855 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004856 pSMB->SetupCount = 1;
4857 pSMB->Reserved3 = 0;
4858 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4859 byte_count = 3 /* pad */ + params + count;
4860 pSMB->DataCount = cpu_to_le16(count);
4861 pSMB->ParameterCount = cpu_to_le16(params);
4862 pSMB->TotalDataCount = pSMB->DataCount;
4863 pSMB->TotalParameterCount = pSMB->ParameterCount;
4864 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4865 pSMB->DataOffset = cpu_to_le16(offset);
4866 pSMB->Fid = fid;
4867 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4868 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4869 else
4870 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
4871 pSMB->Reserved4 = 0;
4872 pSMB->hdr.smb_buf_length += byte_count;
4873 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00004874 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00004875 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004876 if (rc)
Steve French50c2f752007-07-13 00:33:32 +00004877 cFYI(1, ("Send error in Set Time (SetFileInfo) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004878
Steve French50c2f752007-07-13 00:33:32 +00004879 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004880 since file handle passed in no longer valid */
4881
4882 return rc;
4883}
4884
4885
4886int
4887CIFSSMBSetTimes(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00004888 const FILE_BASIC_INFO *data,
Steve French737b7582005-04-28 22:41:06 -07004889 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004890{
4891 TRANSACTION2_SPI_REQ *pSMB = NULL;
4892 TRANSACTION2_SPI_RSP *pSMBr = NULL;
4893 int name_len;
4894 int rc = 0;
4895 int bytes_returned = 0;
4896 char *data_offset;
4897 __u16 params, param_offset, offset, byte_count, count;
4898
4899 cFYI(1, ("In SetTimes"));
4900
4901SetTimesRetry:
4902 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4903 (void **) &pSMBr);
4904 if (rc)
4905 return rc;
4906
4907 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4908 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004909 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004910 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004911 name_len++; /* trailing null */
4912 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004913 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004914 name_len = strnlen(fileName, PATH_MAX);
4915 name_len++; /* trailing null */
4916 strncpy(pSMB->FileName, fileName, name_len);
4917 }
4918
4919 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004920 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004921 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004922 /* BB find max SMB PDU from sess structure BB */
4923 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004924 pSMB->MaxSetupCount = 0;
4925 pSMB->Reserved = 0;
4926 pSMB->Flags = 0;
4927 pSMB->Timeout = 0;
4928 pSMB->Reserved2 = 0;
4929 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004930 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004931 offset = param_offset + params;
4932 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
4933 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4934 pSMB->DataOffset = cpu_to_le16(offset);
4935 pSMB->SetupCount = 1;
4936 pSMB->Reserved3 = 0;
4937 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4938 byte_count = 3 /* pad */ + params + count;
4939
4940 pSMB->DataCount = cpu_to_le16(count);
4941 pSMB->ParameterCount = cpu_to_le16(params);
4942 pSMB->TotalDataCount = pSMB->DataCount;
4943 pSMB->TotalParameterCount = pSMB->ParameterCount;
4944 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4945 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4946 else
4947 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
4948 pSMB->Reserved4 = 0;
4949 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00004950 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004951 pSMB->ByteCount = cpu_to_le16(byte_count);
4952 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4953 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004954 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004955 cFYI(1, ("SetPathInfo (times) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004956
4957 cifs_buf_release(pSMB);
4958
4959 if (rc == -EAGAIN)
4960 goto SetTimesRetry;
4961
4962 return rc;
4963}
4964
4965/* Can not be used to set time stamps yet (due to old DOS time format) */
4966/* Can be used to set attributes */
4967#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
4968 handling it anyway and NT4 was what we thought it would be needed for
4969 Do not delete it until we prove whether needed for Win9x though */
4970int
4971CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
4972 __u16 dos_attrs, const struct nls_table *nls_codepage)
4973{
4974 SETATTR_REQ *pSMB = NULL;
4975 SETATTR_RSP *pSMBr = NULL;
4976 int rc = 0;
4977 int bytes_returned;
4978 int name_len;
4979
4980 cFYI(1, ("In SetAttrLegacy"));
4981
4982SetAttrLgcyRetry:
4983 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
4984 (void **) &pSMBr);
4985 if (rc)
4986 return rc;
4987
4988 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4989 name_len =
Steve French50c2f752007-07-13 00:33:32 +00004990 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004991 PATH_MAX, nls_codepage);
4992 name_len++; /* trailing null */
4993 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004994 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004995 name_len = strnlen(fileName, PATH_MAX);
4996 name_len++; /* trailing null */
4997 strncpy(pSMB->fileName, fileName, name_len);
4998 }
4999 pSMB->attr = cpu_to_le16(dos_attrs);
5000 pSMB->BufferFormat = 0x04;
5001 pSMB->hdr.smb_buf_length += name_len + 1;
5002 pSMB->ByteCount = cpu_to_le16(name_len + 1);
5003 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5004 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005005 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005006 cFYI(1, ("Error in LegacySetAttr = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005007
5008 cifs_buf_release(pSMB);
5009
5010 if (rc == -EAGAIN)
5011 goto SetAttrLgcyRetry;
5012
5013 return rc;
5014}
5015#endif /* temporarily unneeded SetAttr legacy function */
5016
5017int
5018CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00005019 char *fileName, __u64 mode, __u64 uid, __u64 gid,
5020 dev_t device, const struct nls_table *nls_codepage,
Steve French737b7582005-04-28 22:41:06 -07005021 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005022{
5023 TRANSACTION2_SPI_REQ *pSMB = NULL;
5024 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5025 int name_len;
5026 int rc = 0;
5027 int bytes_returned = 0;
5028 FILE_UNIX_BASIC_INFO *data_offset;
5029 __u16 params, param_offset, offset, count, byte_count;
5030
5031 cFYI(1, ("In SetUID/GID/Mode"));
5032setPermsRetry:
5033 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5034 (void **) &pSMBr);
5035 if (rc)
5036 return rc;
5037
5038 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5039 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005040 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005041 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005042 name_len++; /* trailing null */
5043 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07005044 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005045 name_len = strnlen(fileName, PATH_MAX);
5046 name_len++; /* trailing null */
5047 strncpy(pSMB->FileName, fileName, name_len);
5048 }
5049
5050 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005051 count = sizeof(FILE_UNIX_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005052 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005053 /* BB find max SMB PDU from sess structure BB */
5054 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005055 pSMB->MaxSetupCount = 0;
5056 pSMB->Reserved = 0;
5057 pSMB->Flags = 0;
5058 pSMB->Timeout = 0;
5059 pSMB->Reserved2 = 0;
5060 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005061 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005062 offset = param_offset + params;
5063 data_offset =
5064 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5065 offset);
5066 memset(data_offset, 0, count);
5067 pSMB->DataOffset = cpu_to_le16(offset);
5068 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5069 pSMB->SetupCount = 1;
5070 pSMB->Reserved3 = 0;
5071 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5072 byte_count = 3 /* pad */ + params + count;
5073 pSMB->ParameterCount = cpu_to_le16(params);
5074 pSMB->DataCount = cpu_to_le16(count);
5075 pSMB->TotalParameterCount = pSMB->ParameterCount;
5076 pSMB->TotalDataCount = pSMB->DataCount;
5077 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5078 pSMB->Reserved4 = 0;
5079 pSMB->hdr.smb_buf_length += byte_count;
Steve Frenchc7af1852007-03-01 04:11:22 +00005080 /* Samba server ignores set of file size to zero due to bugs in some
5081 older clients, but we should be precise - we use SetFileSize to
5082 set file size and do not want to truncate file size to zero
5083 accidently as happened on one Samba server beta by putting
Steve French50c2f752007-07-13 00:33:32 +00005084 zero instead of -1 here */
Steve Frenchc7af1852007-03-01 04:11:22 +00005085 data_offset->EndOfFile = NO_CHANGE_64;
5086 data_offset->NumOfBytes = NO_CHANGE_64;
5087 data_offset->LastStatusChange = NO_CHANGE_64;
5088 data_offset->LastAccessTime = NO_CHANGE_64;
5089 data_offset->LastModificationTime = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005090 data_offset->Uid = cpu_to_le64(uid);
5091 data_offset->Gid = cpu_to_le64(gid);
5092 /* better to leave device as zero when it is */
5093 data_offset->DevMajor = cpu_to_le64(MAJOR(device));
5094 data_offset->DevMinor = cpu_to_le64(MINOR(device));
5095 data_offset->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00005096
Steve French790fe572007-07-07 19:25:05 +00005097 if (S_ISREG(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005098 data_offset->Type = cpu_to_le32(UNIX_FILE);
Steve French790fe572007-07-07 19:25:05 +00005099 else if (S_ISDIR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005100 data_offset->Type = cpu_to_le32(UNIX_DIR);
Steve French790fe572007-07-07 19:25:05 +00005101 else if (S_ISLNK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005102 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
Steve French790fe572007-07-07 19:25:05 +00005103 else if (S_ISCHR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005104 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
Steve French790fe572007-07-07 19:25:05 +00005105 else if (S_ISBLK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005106 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
Steve French790fe572007-07-07 19:25:05 +00005107 else if (S_ISFIFO(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005108 data_offset->Type = cpu_to_le32(UNIX_FIFO);
Steve French790fe572007-07-07 19:25:05 +00005109 else if (S_ISSOCK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005110 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5111
5112
5113 pSMB->ByteCount = cpu_to_le16(byte_count);
5114 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5115 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005116 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005117 cFYI(1, ("SetPathInfo (perms) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005118
5119 if (pSMB)
5120 cifs_buf_release(pSMB);
5121 if (rc == -EAGAIN)
5122 goto setPermsRetry;
5123 return rc;
5124}
5125
Steve French50c2f752007-07-13 00:33:32 +00005126int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005127 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005128 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005129 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005130{
5131 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005132 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5133 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005134 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005135 int bytes_returned;
5136
Steve French50c2f752007-07-13 00:33:32 +00005137 cFYI(1, ("In CIFSSMBNotify for file handle %d", (int)netfid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005138 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005139 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005140 if (rc)
5141 return rc;
5142
5143 pSMB->TotalParameterCount = 0 ;
5144 pSMB->TotalDataCount = 0;
5145 pSMB->MaxParameterCount = cpu_to_le32(2);
5146 /* BB find exact data count max from sess structure BB */
5147 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005148/* BB VERIFY verify which is correct for above BB */
5149 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5150 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5151
Linus Torvalds1da177e2005-04-16 15:20:36 -07005152 pSMB->MaxSetupCount = 4;
5153 pSMB->Reserved = 0;
5154 pSMB->ParameterOffset = 0;
5155 pSMB->DataCount = 0;
5156 pSMB->DataOffset = 0;
5157 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5158 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5159 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005160 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005161 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5162 pSMB->Reserved2 = 0;
5163 pSMB->CompletionFilter = cpu_to_le32(filter);
5164 pSMB->Fid = netfid; /* file handle always le */
5165 pSMB->ByteCount = 0;
5166
5167 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005168 (struct smb_hdr *)pSMBr, &bytes_returned,
5169 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005170 if (rc) {
5171 cFYI(1, ("Error in Notify = %d", rc));
Steve Frenchff5dbd92005-08-24 17:10:36 -07005172 } else {
5173 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005174 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005175 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005176 sizeof(struct dir_notify_req),
5177 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005178 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005179 dnotify_req->Pid = pSMB->hdr.Pid;
5180 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5181 dnotify_req->Mid = pSMB->hdr.Mid;
5182 dnotify_req->Tid = pSMB->hdr.Tid;
5183 dnotify_req->Uid = pSMB->hdr.Uid;
5184 dnotify_req->netfid = netfid;
5185 dnotify_req->pfile = pfile;
5186 dnotify_req->filter = filter;
5187 dnotify_req->multishot = multishot;
5188 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005189 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005190 &GlobalDnotifyReqList);
5191 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005192 } else
Steve French47c786e2005-10-11 20:03:18 -07005193 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005194 }
5195 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005196 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005197}
5198#ifdef CONFIG_CIFS_XATTR
5199ssize_t
5200CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
5201 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00005202 char *EAData, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005203 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005204{
5205 /* BB assumes one setup word */
5206 TRANSACTION2_QPI_REQ *pSMB = NULL;
5207 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5208 int rc = 0;
5209 int bytes_returned;
5210 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005211 struct fea *temp_fea;
5212 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005213 __u16 params, byte_count;
5214
5215 cFYI(1, ("In Query All EAs path %s", searchName));
5216QAllEAsRetry:
5217 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5218 (void **) &pSMBr);
5219 if (rc)
5220 return rc;
5221
5222 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5223 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005224 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005225 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005226 name_len++; /* trailing null */
5227 name_len *= 2;
5228 } else { /* BB improve the check for buffer overruns BB */
5229 name_len = strnlen(searchName, PATH_MAX);
5230 name_len++; /* trailing null */
5231 strncpy(pSMB->FileName, searchName, name_len);
5232 }
5233
Steve French50c2f752007-07-13 00:33:32 +00005234 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005235 pSMB->TotalDataCount = 0;
5236 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005237 /* BB find exact max SMB PDU from sess structure BB */
5238 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005239 pSMB->MaxSetupCount = 0;
5240 pSMB->Reserved = 0;
5241 pSMB->Flags = 0;
5242 pSMB->Timeout = 0;
5243 pSMB->Reserved2 = 0;
5244 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005245 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005246 pSMB->DataCount = 0;
5247 pSMB->DataOffset = 0;
5248 pSMB->SetupCount = 1;
5249 pSMB->Reserved3 = 0;
5250 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5251 byte_count = params + 1 /* pad */ ;
5252 pSMB->TotalParameterCount = cpu_to_le16(params);
5253 pSMB->ParameterCount = pSMB->TotalParameterCount;
5254 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5255 pSMB->Reserved4 = 0;
5256 pSMB->hdr.smb_buf_length += byte_count;
5257 pSMB->ByteCount = cpu_to_le16(byte_count);
5258
5259 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5260 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5261 if (rc) {
5262 cFYI(1, ("Send error in QueryAllEAs = %d", rc));
5263 } else { /* decode response */
5264 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5265
5266 /* BB also check enough total bytes returned */
5267 /* BB we need to improve the validity checking
5268 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005269 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005270 rc = -EIO; /* bad smb */
5271 /* else if (pFindData){
5272 memcpy((char *) pFindData,
5273 (char *) &pSMBr->hdr.Protocol +
5274 data_offset, kl);
5275 }*/ else {
5276 /* check that length of list is not more than bcc */
5277 /* check that each entry does not go beyond length
5278 of list */
5279 /* check that each element of each entry does not
5280 go beyond end of list */
5281 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005282 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005283 rc = 0;
5284 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005285 /* BB check if start of smb + data_offset > &bcc+ bcc */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005286 ea_response_data = (struct fealist *)
5287 (((char *) &pSMBr->hdr.Protocol) +
5288 data_offset);
5289 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005290 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005291 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005292 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005293 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005294 } else {
5295 /* account for ea list len */
5296 name_len -= 4;
5297 temp_fea = ea_response_data->list;
5298 temp_ptr = (char *)temp_fea;
Steve French50c2f752007-07-13 00:33:32 +00005299 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005300 __u16 value_len;
5301 name_len -= 4;
5302 temp_ptr += 4;
5303 rc += temp_fea->name_len;
5304 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005305 rc = rc + 5 + 1;
5306 if (rc < (int)buf_size) {
Steve French50c2f752007-07-13 00:33:32 +00005307 memcpy(EAData, "user.", 5);
5308 EAData += 5;
5309 memcpy(EAData, temp_ptr,
5310 temp_fea->name_len);
5311 EAData += temp_fea->name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005312 /* null terminate name */
5313 *EAData = 0;
5314 EAData = EAData + 1;
Steve French790fe572007-07-07 19:25:05 +00005315 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005316 /* skip copy - calc size only */
5317 } else {
5318 /* stop before overrun buffer */
5319 rc = -ERANGE;
5320 break;
5321 }
5322 name_len -= temp_fea->name_len;
5323 temp_ptr += temp_fea->name_len;
5324 /* account for trailing null */
5325 name_len--;
5326 temp_ptr++;
Steve French50c2f752007-07-13 00:33:32 +00005327 value_len =
5328 le16_to_cpu(temp_fea->value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005329 name_len -= value_len;
5330 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005331 /* BB check that temp_ptr is still
5332 within the SMB BB*/
5333
5334 /* no trailing null to account for
5335 in value len */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005336 /* go on to next EA */
5337 temp_fea = (struct fea *)temp_ptr;
5338 }
5339 }
5340 }
5341 }
5342 if (pSMB)
5343 cifs_buf_release(pSMB);
5344 if (rc == -EAGAIN)
5345 goto QAllEAsRetry;
5346
5347 return (ssize_t)rc;
5348}
5349
Steve French50c2f752007-07-13 00:33:32 +00005350ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon,
5351 const unsigned char *searchName, const unsigned char *ea_name,
5352 unsigned char *ea_value, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005353 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005354{
5355 TRANSACTION2_QPI_REQ *pSMB = NULL;
5356 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5357 int rc = 0;
5358 int bytes_returned;
5359 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005360 struct fea *temp_fea;
5361 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005362 __u16 params, byte_count;
5363
5364 cFYI(1, ("In Query EA path %s", searchName));
5365QEARetry:
5366 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5367 (void **) &pSMBr);
5368 if (rc)
5369 return rc;
5370
5371 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5372 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005373 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005374 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005375 name_len++; /* trailing null */
5376 name_len *= 2;
5377 } else { /* BB improve the check for buffer overruns BB */
5378 name_len = strnlen(searchName, PATH_MAX);
5379 name_len++; /* trailing null */
5380 strncpy(pSMB->FileName, searchName, name_len);
5381 }
5382
Steve French50c2f752007-07-13 00:33:32 +00005383 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005384 pSMB->TotalDataCount = 0;
5385 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005386 /* BB find exact max SMB PDU from sess structure BB */
5387 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005388 pSMB->MaxSetupCount = 0;
5389 pSMB->Reserved = 0;
5390 pSMB->Flags = 0;
5391 pSMB->Timeout = 0;
5392 pSMB->Reserved2 = 0;
5393 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005394 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005395 pSMB->DataCount = 0;
5396 pSMB->DataOffset = 0;
5397 pSMB->SetupCount = 1;
5398 pSMB->Reserved3 = 0;
5399 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5400 byte_count = params + 1 /* pad */ ;
5401 pSMB->TotalParameterCount = cpu_to_le16(params);
5402 pSMB->ParameterCount = pSMB->TotalParameterCount;
5403 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5404 pSMB->Reserved4 = 0;
5405 pSMB->hdr.smb_buf_length += byte_count;
5406 pSMB->ByteCount = cpu_to_le16(byte_count);
5407
5408 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5409 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5410 if (rc) {
5411 cFYI(1, ("Send error in Query EA = %d", rc));
5412 } else { /* decode response */
5413 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5414
5415 /* BB also check enough total bytes returned */
5416 /* BB we need to improve the validity checking
5417 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005418 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005419 rc = -EIO; /* bad smb */
5420 /* else if (pFindData){
5421 memcpy((char *) pFindData,
5422 (char *) &pSMBr->hdr.Protocol +
5423 data_offset, kl);
5424 }*/ else {
5425 /* check that length of list is not more than bcc */
5426 /* check that each entry does not go beyond length
5427 of list */
5428 /* check that each element of each entry does not
5429 go beyond end of list */
5430 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005431 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005432 rc = -ENODATA;
5433 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005434 /* BB check if start of smb + data_offset > &bcc+ bcc*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07005435 ea_response_data = (struct fealist *)
5436 (((char *) &pSMBr->hdr.Protocol) +
5437 data_offset);
5438 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005439 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005440 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005441 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005442 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005443 } else {
5444 /* account for ea list len */
5445 name_len -= 4;
5446 temp_fea = ea_response_data->list;
5447 temp_ptr = (char *)temp_fea;
5448 /* loop through checking if we have a matching
5449 name and then return the associated value */
Steve French50c2f752007-07-13 00:33:32 +00005450 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005451 __u16 value_len;
5452 name_len -= 4;
5453 temp_ptr += 4;
Steve French50c2f752007-07-13 00:33:32 +00005454 value_len =
5455 le16_to_cpu(temp_fea->value_len);
5456 /* BB validate that value_len falls within SMB,
5457 even though maximum for name_len is 255 */
Steve French790fe572007-07-07 19:25:05 +00005458 if (memcmp(temp_fea->name, ea_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005459 temp_fea->name_len) == 0) {
5460 /* found a match */
5461 rc = value_len;
5462 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005463 if (rc <= (int)buf_size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005464 memcpy(ea_value,
5465 temp_fea->name+temp_fea->name_len+1,
5466 rc);
Steve French50c2f752007-07-13 00:33:32 +00005467 /* ea values, unlike ea
5468 names, are not null
5469 terminated */
Steve French790fe572007-07-07 19:25:05 +00005470 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005471 /* skip copy - calc size only */
5472 } else {
Steve French50c2f752007-07-13 00:33:32 +00005473 /* stop before overrun buffer */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005474 rc = -ERANGE;
5475 }
5476 break;
5477 }
5478 name_len -= temp_fea->name_len;
5479 temp_ptr += temp_fea->name_len;
5480 /* account for trailing null */
5481 name_len--;
5482 temp_ptr++;
5483 name_len -= value_len;
5484 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005485 /* No trailing null to account for in
5486 value_len. Go on to next EA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005487 temp_fea = (struct fea *)temp_ptr;
5488 }
Steve French50c2f752007-07-13 00:33:32 +00005489 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005490 }
5491 }
5492 if (pSMB)
5493 cifs_buf_release(pSMB);
5494 if (rc == -EAGAIN)
5495 goto QEARetry;
5496
5497 return (ssize_t)rc;
5498}
5499
5500int
5501CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005502 const char *ea_name, const void *ea_value,
5503 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5504 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005505{
5506 struct smb_com_transaction2_spi_req *pSMB = NULL;
5507 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5508 struct fealist *parm_data;
5509 int name_len;
5510 int rc = 0;
5511 int bytes_returned = 0;
5512 __u16 params, param_offset, byte_count, offset, count;
5513
5514 cFYI(1, ("In SetEA"));
5515SetEARetry:
5516 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5517 (void **) &pSMBr);
5518 if (rc)
5519 return rc;
5520
5521 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5522 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005523 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005524 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005525 name_len++; /* trailing null */
5526 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005527 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005528 name_len = strnlen(fileName, PATH_MAX);
5529 name_len++; /* trailing null */
5530 strncpy(pSMB->FileName, fileName, name_len);
5531 }
5532
5533 params = 6 + name_len;
5534
5535 /* done calculating parms using name_len of file name,
5536 now use name_len to calculate length of ea name
5537 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005538 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005539 name_len = 0;
5540 else
Steve French50c2f752007-07-13 00:33:32 +00005541 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005542
Steve Frenchdae5dbd2007-12-30 23:49:57 +00005543 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005544 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005545 /* BB find max SMB PDU from sess */
5546 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005547 pSMB->MaxSetupCount = 0;
5548 pSMB->Reserved = 0;
5549 pSMB->Flags = 0;
5550 pSMB->Timeout = 0;
5551 pSMB->Reserved2 = 0;
5552 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005553 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005554 offset = param_offset + params;
5555 pSMB->InformationLevel =
5556 cpu_to_le16(SMB_SET_FILE_EA);
5557
5558 parm_data =
5559 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5560 offset);
5561 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5562 pSMB->DataOffset = cpu_to_le16(offset);
5563 pSMB->SetupCount = 1;
5564 pSMB->Reserved3 = 0;
5565 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5566 byte_count = 3 /* pad */ + params + count;
5567 pSMB->DataCount = cpu_to_le16(count);
5568 parm_data->list_len = cpu_to_le32(count);
5569 parm_data->list[0].EA_flags = 0;
5570 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005571 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005572 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005573 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005574 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005575 parm_data->list[0].name[name_len] = 0;
5576 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5577 /* caller ensures that ea_value_len is less than 64K but
5578 we need to ensure that it fits within the smb */
5579
Steve French50c2f752007-07-13 00:33:32 +00005580 /*BB add length check to see if it would fit in
5581 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005582 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5583 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005584 memcpy(parm_data->list[0].name+name_len+1,
5585 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005586
5587 pSMB->TotalDataCount = pSMB->DataCount;
5588 pSMB->ParameterCount = cpu_to_le16(params);
5589 pSMB->TotalParameterCount = pSMB->ParameterCount;
5590 pSMB->Reserved4 = 0;
5591 pSMB->hdr.smb_buf_length += byte_count;
5592 pSMB->ByteCount = cpu_to_le16(byte_count);
5593 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5594 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005595 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005596 cFYI(1, ("SetPathInfo (EA) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005597
5598 cifs_buf_release(pSMB);
5599
5600 if (rc == -EAGAIN)
5601 goto SetEARetry;
5602
5603 return rc;
5604}
5605
5606#endif