blob: 9c95617baa4dbc13638ad98582163a35aacd993e [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/cifssmb.c
3 *
Steve Frenchad7a2922008-02-07 23:25:02 +00004 * Copyright (C) International Business Machines Corp., 2002,2008
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Author(s): Steve French (sfrench@us.ibm.com)
6 *
7 * Contains the routines for constructing the SMB PDUs themselves
8 *
9 * This library is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published
11 * by the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c */
25 /* These are mostly routines that operate on a pathname, or on a tree id */
26 /* (mounted volume), but there are eight handle based routines which must be */
Steve French2dd29d32007-04-23 22:07:35 +000027 /* treated slightly differently for reconnection purposes since we never */
28 /* want to reuse a stale file handle and only the caller knows the file info */
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#include <linux/fs.h>
31#include <linux/kernel.h>
32#include <linux/vfs.h>
33#include <linux/posix_acl_xattr.h>
34#include <asm/uaccess.h>
35#include "cifspdu.h"
36#include "cifsglob.h"
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +000037#include "cifsacl.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include "cifsproto.h"
39#include "cifs_unicode.h"
40#include "cifs_debug.h"
41
42#ifdef CONFIG_CIFS_POSIX
43static struct {
44 int index;
45 char *name;
46} protocols[] = {
Steve French39798772006-05-31 22:40:51 +000047#ifdef CONFIG_CIFS_WEAK_PW_HASH
48 {LANMAN_PROT, "\2LM1.2X002"},
Steve French9ac00b72006-09-30 04:13:17 +000049 {LANMAN2_PROT, "\2LANMAN2.1"},
Steve French39798772006-05-31 22:40:51 +000050#endif /* weak password hashing for legacy clients */
Steve French50c2f752007-07-13 00:33:32 +000051 {CIFS_PROT, "\2NT LM 0.12"},
Steve French39798772006-05-31 22:40:51 +000052 {POSIX_PROT, "\2POSIX 2"},
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 {BAD_PROT, "\2"}
54};
55#else
56static struct {
57 int index;
58 char *name;
59} protocols[] = {
Steve French39798772006-05-31 22:40:51 +000060#ifdef CONFIG_CIFS_WEAK_PW_HASH
61 {LANMAN_PROT, "\2LM1.2X002"},
Steve French18f75ca2006-10-01 03:13:01 +000062 {LANMAN2_PROT, "\2LANMAN2.1"},
Steve French39798772006-05-31 22:40:51 +000063#endif /* weak password hashing for legacy clients */
Steve French790fe572007-07-07 19:25:05 +000064 {CIFS_PROT, "\2NT LM 0.12"},
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 {BAD_PROT, "\2"}
66};
67#endif
68
Steve French39798772006-05-31 22:40:51 +000069/* define the number of elements in the cifs dialect array */
70#ifdef CONFIG_CIFS_POSIX
71#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French9ac00b72006-09-30 04:13:17 +000072#define CIFS_NUM_PROT 4
Steve French39798772006-05-31 22:40:51 +000073#else
74#define CIFS_NUM_PROT 2
75#endif /* CIFS_WEAK_PW_HASH */
76#else /* not posix */
77#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French9ac00b72006-09-30 04:13:17 +000078#define CIFS_NUM_PROT 3
Steve French39798772006-05-31 22:40:51 +000079#else
80#define CIFS_NUM_PROT 1
81#endif /* CONFIG_CIFS_WEAK_PW_HASH */
82#endif /* CIFS_POSIX */
83
Igor Mammedovfec45852008-05-16 13:06:30 +040084/* Allocates buffer into dst and copies smb string from src to it.
85 * caller is responsible for freeing dst if function returned 0.
86 * returns:
87 * on success - 0
88 * on failure - errno
89 */
90static int
91cifs_strncpy_to_host(char **dst, const char *src, const int maxlen,
92 const bool is_unicode, const struct nls_table *nls_codepage)
93{
94 int plen;
95
96 if (is_unicode) {
97 plen = UniStrnlen((wchar_t *)src, maxlen);
98 *dst = kmalloc(plen + 2, GFP_KERNEL);
99 if (!*dst)
100 goto cifs_strncpy_to_host_ErrExit;
101 cifs_strfromUCS_le(*dst, (__le16 *)src, plen, nls_codepage);
102 } else {
103 plen = strnlen(src, maxlen);
104 *dst = kmalloc(plen + 2, GFP_KERNEL);
105 if (!*dst)
106 goto cifs_strncpy_to_host_ErrExit;
107 strncpy(*dst, src, plen);
108 }
109 (*dst)[plen] = 0;
Steve Frencha1fe78f2008-05-16 18:48:38 +0000110 (*dst)[plen+1] = 0; /* harmless for ASCII case, needed for Unicode */
Igor Mammedovfec45852008-05-16 13:06:30 +0400111 return 0;
112
113cifs_strncpy_to_host_ErrExit:
114 cERROR(1, ("Failed to allocate buffer for string\n"));
115 return -ENOMEM;
116}
117
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
119/* Mark as invalid, all open files on tree connections since they
120 were closed when session to server was lost */
Steve French790fe572007-07-07 19:25:05 +0000121static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122{
123 struct cifsFileInfo *open_file = NULL;
Steve French790fe572007-07-07 19:25:05 +0000124 struct list_head *tmp;
125 struct list_head *tmp1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127/* list all files open on tree connection and mark them invalid */
128 write_lock(&GlobalSMBSeslock);
129 list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
Steve French790fe572007-07-07 19:25:05 +0000130 open_file = list_entry(tmp, struct cifsFileInfo, tlist);
Steve Frenchad8b15f2008-08-08 21:10:16 +0000131 open_file->invalidHandle = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 }
133 write_unlock(&GlobalSMBSeslock);
Steve French09d1db52005-04-28 22:41:08 -0700134 /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
135 to this tcon */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136}
137
Steve Frenchad7a2922008-02-07 23:25:02 +0000138/* Allocate and return pointer to an SMB request buffer, and set basic
139 SMB information in the SMB header. If the return code is zero, this
140 function must have filled in request_buf pointer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141static int
142small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000143 void **request_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144{
145 int rc = 0;
146
147 /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
148 check for tcp and smb session status done differently
149 for those three - in the calling routine */
Steve French790fe572007-07-07 19:25:05 +0000150 if (tcon) {
151 if (tcon->tidStatus == CifsExiting) {
Steve French6ab16d22005-11-29 20:55:11 -0800152 /* only tree disconnect, open, and write,
153 (and ulogoff which does not have tcon)
154 are allowed as we start force umount */
Steve French790fe572007-07-07 19:25:05 +0000155 if ((smb_command != SMB_COM_WRITE_ANDX) &&
Steve French50c2f752007-07-13 00:33:32 +0000156 (smb_command != SMB_COM_OPEN_ANDX) &&
Steve French6ab16d22005-11-29 20:55:11 -0800157 (smb_command != SMB_COM_TREE_DISCONNECT)) {
Steve French790fe572007-07-07 19:25:05 +0000158 cFYI(1, ("can not send cmd %d while umounting",
Steve French6ab16d22005-11-29 20:55:11 -0800159 smb_command));
160 return -ENODEV;
161 }
162 }
Steve French790fe572007-07-07 19:25:05 +0000163 if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
Steve French50c2f752007-07-13 00:33:32 +0000164 (tcon->ses->server)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 struct nls_table *nls_codepage;
Steve French50c2f752007-07-13 00:33:32 +0000166 /* Give Demultiplex thread up to 10 seconds to
Steve French09d1db52005-04-28 22:41:08 -0700167 reconnect, should be greater than cifs socket
168 timeout which is 7 seconds */
Steve Frenchc18c8422007-07-18 23:21:09 +0000169 while (tcon->ses->server->tcpStatus ==
Steve French63135e02007-07-17 17:34:02 +0000170 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 wait_event_interruptible_timeout(tcon->ses->server->response_q,
Steve Frenchc18c8422007-07-18 23:21:09 +0000172 (tcon->ses->server->tcpStatus ==
Steve French63135e02007-07-17 17:34:02 +0000173 CifsGood), 10 * HZ);
174 if (tcon->ses->server->tcpStatus ==
175 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 /* on "soft" mounts we wait once */
Steve French4b18f2a2008-04-29 00:06:05 +0000177 if (!tcon->retry ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 (tcon->ses->status == CifsExiting)) {
Steve French50c2f752007-07-13 00:33:32 +0000179 cFYI(1, ("gave up waiting on "
180 "reconnect in smb_init"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 return -EHOSTDOWN;
Steve French09d1db52005-04-28 22:41:08 -0700182 } /* else "hard" mount - keep retrying
183 until process is killed or server
184 comes back on-line */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 } else /* TCP session is reestablished now */
186 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 }
Steve French50c2f752007-07-13 00:33:32 +0000188
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 nls_codepage = load_nls_default();
190 /* need to prevent multiple threads trying to
191 simultaneously reconnect the same SMB session */
192 down(&tcon->ses->sesSem);
Steve French3b795212008-11-13 19:45:32 +0000193 if (tcon->ses->need_reconnect)
Steve French50c2f752007-07-13 00:33:32 +0000194 rc = cifs_setup_session(0, tcon->ses,
Steve French09d1db52005-04-28 22:41:08 -0700195 nls_codepage);
Steve French3b795212008-11-13 19:45:32 +0000196 if (!rc && (tcon->need_reconnect)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 mark_open_files_invalid(tcon);
Steve French50c2f752007-07-13 00:33:32 +0000198 rc = CIFSTCon(0, tcon->ses, tcon->treeName,
Steve French8af18972007-02-14 04:42:51 +0000199 tcon, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 up(&tcon->ses->sesSem);
Steve French3e844692005-10-03 13:37:24 -0700201 /* BB FIXME add code to check if wsize needs
202 update due to negotiated smb buffer size
203 shrinking */
Steve French35028d72008-04-09 20:32:42 +0000204 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 atomic_inc(&tconInfoReconnectCount);
Steve French35028d72008-04-09 20:32:42 +0000206 /* tell server Unix caps we support */
207 if (tcon->ses->capabilities & CAP_UNIX)
208 reset_cifs_unix_caps(
209 0 /* no xid */,
210 tcon,
211 NULL /* we do not know sb */,
212 NULL /* no vol info */);
213 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
215 cFYI(1, ("reconnect tcon rc = %d", rc));
Steve French50c2f752007-07-13 00:33:32 +0000216 /* Removed call to reopen open files here.
217 It is safer (and faster) to reopen files
Steve French09d1db52005-04-28 22:41:08 -0700218 one at a time as needed in read and write */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
Steve French50c2f752007-07-13 00:33:32 +0000220 /* Check if handle based operation so we
Steve French09d1db52005-04-28 22:41:08 -0700221 know whether we can continue or not without
222 returning to caller to reset file handle */
Steve French50c2f752007-07-13 00:33:32 +0000223 switch (smb_command) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 case SMB_COM_READ_ANDX:
225 case SMB_COM_WRITE_ANDX:
226 case SMB_COM_CLOSE:
227 case SMB_COM_FIND_CLOSE2:
228 case SMB_COM_LOCKING_ANDX: {
229 unload_nls(nls_codepage);
230 return -EAGAIN;
231 }
232 }
233 } else {
234 up(&tcon->ses->sesSem);
235 }
236 unload_nls(nls_codepage);
237
238 } else {
239 return -EIO;
240 }
241 }
Steve French790fe572007-07-07 19:25:05 +0000242 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 return rc;
244
245 *request_buf = cifs_small_buf_get();
246 if (*request_buf == NULL) {
247 /* BB should we add a retry in here if not a writepage? */
248 return -ENOMEM;
249 }
250
Steve French63135e02007-07-17 17:34:02 +0000251 header_assemble((struct smb_hdr *) *request_buf, smb_command,
Steve Frenchc18c8422007-07-18 23:21:09 +0000252 tcon, wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Steve French790fe572007-07-07 19:25:05 +0000254 if (tcon != NULL)
255 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700256
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 return rc;
Steve French5815449d2006-02-14 01:36:20 +0000258}
259
Steve French12b3b8f2006-02-09 21:12:47 +0000260int
Steve French50c2f752007-07-13 00:33:32 +0000261small_smb_init_no_tc(const int smb_command, const int wct,
Steve French5815449d2006-02-14 01:36:20 +0000262 struct cifsSesInfo *ses, void **request_buf)
Steve French12b3b8f2006-02-09 21:12:47 +0000263{
264 int rc;
Steve French50c2f752007-07-13 00:33:32 +0000265 struct smb_hdr *buffer;
Steve French12b3b8f2006-02-09 21:12:47 +0000266
Steve French5815449d2006-02-14 01:36:20 +0000267 rc = small_smb_init(smb_command, wct, NULL, request_buf);
Steve French790fe572007-07-07 19:25:05 +0000268 if (rc)
Steve French12b3b8f2006-02-09 21:12:47 +0000269 return rc;
270
Steve French04fdabe2006-02-10 05:52:50 +0000271 buffer = (struct smb_hdr *)*request_buf;
Steve French12b3b8f2006-02-09 21:12:47 +0000272 buffer->Mid = GetNextMid(ses->server);
273 if (ses->capabilities & CAP_UNICODE)
274 buffer->Flags2 |= SMBFLG2_UNICODE;
Steve French04fdabe2006-02-10 05:52:50 +0000275 if (ses->capabilities & CAP_STATUS32)
Steve French12b3b8f2006-02-09 21:12:47 +0000276 buffer->Flags2 |= SMBFLG2_ERR_STATUS;
277
278 /* uid, tid can stay at zero as set in header assemble */
279
Steve French50c2f752007-07-13 00:33:32 +0000280 /* BB add support for turning on the signing when
Steve French12b3b8f2006-02-09 21:12:47 +0000281 this function is used after 1st of session setup requests */
282
283 return rc;
284}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
286/* If the return code is zero, this function must fill in request_buf pointer */
287static int
288smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
289 void **request_buf /* returned */ ,
290 void **response_buf /* returned */ )
291{
292 int rc = 0;
293
294 /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
295 check for tcp and smb session status done differently
296 for those three - in the calling routine */
Steve French790fe572007-07-07 19:25:05 +0000297 if (tcon) {
Steve French3b795212008-11-13 19:45:32 +0000298 if (tcon->need_reconnect) {
Steve French6ab16d22005-11-29 20:55:11 -0800299 /* only tree disconnect, open, and write,
300 (and ulogoff which does not have tcon)
301 are allowed as we start force umount */
Steve French790fe572007-07-07 19:25:05 +0000302 if ((smb_command != SMB_COM_WRITE_ANDX) &&
Steve French6ab16d22005-11-29 20:55:11 -0800303 (smb_command != SMB_COM_OPEN_ANDX) &&
304 (smb_command != SMB_COM_TREE_DISCONNECT)) {
Steve French790fe572007-07-07 19:25:05 +0000305 cFYI(1, ("can not send cmd %d while umounting",
Steve French6ab16d22005-11-29 20:55:11 -0800306 smb_command));
307 return -ENODEV;
308 }
309 }
310
Steve French790fe572007-07-07 19:25:05 +0000311 if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
Steve French50c2f752007-07-13 00:33:32 +0000312 (tcon->ses->server)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 struct nls_table *nls_codepage;
Steve French09d1db52005-04-28 22:41:08 -0700314 /* Give Demultiplex thread up to 10 seconds to
315 reconnect, should be greater than cifs socket
316 timeout which is 7 seconds */
Steve French63135e02007-07-17 17:34:02 +0000317 while (tcon->ses->server->tcpStatus ==
318 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 wait_event_interruptible_timeout(tcon->ses->server->response_q,
Steve French63135e02007-07-17 17:34:02 +0000320 (tcon->ses->server->tcpStatus ==
321 CifsGood), 10 * HZ);
Steve French790fe572007-07-07 19:25:05 +0000322 if (tcon->ses->server->tcpStatus ==
Steve French09d1db52005-04-28 22:41:08 -0700323 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 /* on "soft" mounts we wait once */
Steve French4b18f2a2008-04-29 00:06:05 +0000325 if (!tcon->retry ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 (tcon->ses->status == CifsExiting)) {
Steve French50c2f752007-07-13 00:33:32 +0000327 cFYI(1, ("gave up waiting on "
328 "reconnect in smb_init"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 return -EHOSTDOWN;
Steve French09d1db52005-04-28 22:41:08 -0700330 } /* else "hard" mount - keep retrying
331 until process is killed or server
332 comes on-line */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 } else /* TCP session is reestablished now */
334 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 nls_codepage = load_nls_default();
337 /* need to prevent multiple threads trying to
338 simultaneously reconnect the same SMB session */
339 down(&tcon->ses->sesSem);
Steve French3b795212008-11-13 19:45:32 +0000340 if (tcon->ses->need_reconnect)
Steve French50c2f752007-07-13 00:33:32 +0000341 rc = cifs_setup_session(0, tcon->ses,
Steve French09d1db52005-04-28 22:41:08 -0700342 nls_codepage);
Steve French3b795212008-11-13 19:45:32 +0000343 if (!rc && (tcon->need_reconnect)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 mark_open_files_invalid(tcon);
Steve French09d1db52005-04-28 22:41:08 -0700345 rc = CIFSTCon(0, tcon->ses, tcon->treeName,
346 tcon, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 up(&tcon->ses->sesSem);
Steve French3e844692005-10-03 13:37:24 -0700348 /* BB FIXME add code to check if wsize needs
349 update due to negotiated smb buffer size
350 shrinking */
Steve French35028d72008-04-09 20:32:42 +0000351 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 atomic_inc(&tconInfoReconnectCount);
Steve French35028d72008-04-09 20:32:42 +0000353 /* tell server Unix caps we support */
354 if (tcon->ses->capabilities & CAP_UNIX)
355 reset_cifs_unix_caps(
356 0 /* no xid */,
357 tcon,
358 NULL /* do not know sb */,
359 NULL /* no vol info */);
360 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
362 cFYI(1, ("reconnect tcon rc = %d", rc));
Steve French50c2f752007-07-13 00:33:32 +0000363 /* Removed call to reopen open files here.
364 It is safer (and faster) to reopen files
Steve French09d1db52005-04-28 22:41:08 -0700365 one at a time as needed in read and write */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
Steve French50c2f752007-07-13 00:33:32 +0000367 /* Check if handle based operation so we
Steve French09d1db52005-04-28 22:41:08 -0700368 know whether we can continue or not without
369 returning to caller to reset file handle */
Steve French50c2f752007-07-13 00:33:32 +0000370 switch (smb_command) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 case SMB_COM_READ_ANDX:
372 case SMB_COM_WRITE_ANDX:
373 case SMB_COM_CLOSE:
374 case SMB_COM_FIND_CLOSE2:
375 case SMB_COM_LOCKING_ANDX: {
376 unload_nls(nls_codepage);
377 return -EAGAIN;
378 }
379 }
380 } else {
381 up(&tcon->ses->sesSem);
382 }
383 unload_nls(nls_codepage);
384
385 } else {
386 return -EIO;
387 }
388 }
Steve French790fe572007-07-07 19:25:05 +0000389 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 return rc;
391
392 *request_buf = cifs_buf_get();
393 if (*request_buf == NULL) {
394 /* BB should we add a retry in here if not a writepage? */
395 return -ENOMEM;
396 }
397 /* Although the original thought was we needed the response buf for */
398 /* potential retries of smb operations it turns out we can determine */
399 /* from the mid flags when the request buffer can be resent without */
400 /* having to use a second distinct buffer for the response */
Steve French790fe572007-07-07 19:25:05 +0000401 if (response_buf)
Steve French50c2f752007-07-13 00:33:32 +0000402 *response_buf = *request_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
404 header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000405 wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
Steve French790fe572007-07-07 19:25:05 +0000407 if (tcon != NULL)
408 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700409
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 return rc;
411}
412
Steve French50c2f752007-07-13 00:33:32 +0000413static int validate_t2(struct smb_t2_rsp *pSMB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414{
415 int rc = -EINVAL;
416 int total_size;
Steve French50c2f752007-07-13 00:33:32 +0000417 char *pBCC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
419 /* check for plausible wct, bcc and t2 data and parm sizes */
420 /* check for parm and data offset going beyond end of smb */
Steve French790fe572007-07-07 19:25:05 +0000421 if (pSMB->hdr.WordCount >= 10) {
422 if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
424 /* check that bcc is at least as big as parms + data */
425 /* check that bcc is less than negotiated smb buffer */
426 total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
Steve French790fe572007-07-07 19:25:05 +0000427 if (total_size < 512) {
Steve Frenchc18c8422007-07-18 23:21:09 +0000428 total_size +=
Steve French63135e02007-07-17 17:34:02 +0000429 le16_to_cpu(pSMB->t2_rsp.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 /* BCC le converted in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +0000431 pBCC = (pSMB->hdr.WordCount * 2) +
Steve French09d1db52005-04-28 22:41:08 -0700432 sizeof(struct smb_hdr) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 (char *)pSMB;
Steve French790fe572007-07-07 19:25:05 +0000434 if ((total_size <= (*(u16 *)pBCC)) &&
Steve French50c2f752007-07-13 00:33:32 +0000435 (total_size <
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
437 return 0;
438 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 }
440 }
441 }
Steve French50c2f752007-07-13 00:33:32 +0000442 cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 sizeof(struct smb_t2_rsp) + 16);
444 return rc;
445}
446int
447CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
448{
449 NEGOTIATE_REQ *pSMB;
450 NEGOTIATE_RSP *pSMBr;
451 int rc = 0;
452 int bytes_returned;
Steve French39798772006-05-31 22:40:51 +0000453 int i;
Steve French50c2f752007-07-13 00:33:32 +0000454 struct TCP_Server_Info *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 u16 count;
Steve French750d1152006-06-27 06:28:30 +0000456 unsigned int secFlags;
Al Viro733f99a2006-10-14 16:48:26 +0100457 u16 dialect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
Steve French790fe572007-07-07 19:25:05 +0000459 if (ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 server = ses->server;
461 else {
462 rc = -EIO;
463 return rc;
464 }
465 rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
466 (void **) &pSMB, (void **) &pSMBr);
467 if (rc)
468 return rc;
Steve French750d1152006-06-27 06:28:30 +0000469
470 /* if any of auth flags (ie not sign or seal) are overriden use them */
Steve French790fe572007-07-07 19:25:05 +0000471 if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
Steve French762e5ab2007-06-28 18:41:42 +0000472 secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */
Steve French750d1152006-06-27 06:28:30 +0000473 else /* if override flags set only sign/seal OR them with global auth */
474 secFlags = extended_security | ses->overrideSecFlg;
475
Steve French762e5ab2007-06-28 18:41:42 +0000476 cFYI(1, ("secFlags 0x%x", secFlags));
Steve Frenchf40c5622006-06-28 00:13:38 +0000477
Steve French1982c342005-08-17 12:38:22 -0700478 pSMB->hdr.Mid = GetNextMid(server);
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000479 pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
Steve Frencha0136892007-10-04 20:05:09 +0000480
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000481 if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
Steve French254e55e2006-06-04 05:53:15 +0000482 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Steve Frencha0136892007-10-04 20:05:09 +0000483 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
484 cFYI(1, ("Kerberos only mechanism, enable extended security"));
485 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
486 }
Steve French50c2f752007-07-13 00:33:32 +0000487
Steve French39798772006-05-31 22:40:51 +0000488 count = 0;
Steve French50c2f752007-07-13 00:33:32 +0000489 for (i = 0; i < CIFS_NUM_PROT; i++) {
Steve French39798772006-05-31 22:40:51 +0000490 strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
491 count += strlen(protocols[i].name) + 1;
492 /* null at end of source and target buffers anyway */
493 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 pSMB->hdr.smb_buf_length += count;
495 pSMB->ByteCount = cpu_to_le16(count);
496
497 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
498 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French50c2f752007-07-13 00:33:32 +0000499 if (rc != 0)
Steve French254e55e2006-06-04 05:53:15 +0000500 goto neg_err_exit;
501
Al Viro733f99a2006-10-14 16:48:26 +0100502 dialect = le16_to_cpu(pSMBr->DialectIndex);
Steve French790fe572007-07-07 19:25:05 +0000503 cFYI(1, ("Dialect: %d", dialect));
Steve French254e55e2006-06-04 05:53:15 +0000504 /* Check wct = 1 error case */
Steve French790fe572007-07-07 19:25:05 +0000505 if ((pSMBr->hdr.WordCount < 13) || (dialect == BAD_PROT)) {
Steve French254e55e2006-06-04 05:53:15 +0000506 /* core returns wct = 1, but we do not ask for core - otherwise
Steve French50c2f752007-07-13 00:33:32 +0000507 small wct just comes when dialect index is -1 indicating we
Steve French254e55e2006-06-04 05:53:15 +0000508 could not negotiate a common dialect */
509 rc = -EOPNOTSUPP;
510 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000511#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French790fe572007-07-07 19:25:05 +0000512 } else if ((pSMBr->hdr.WordCount == 13)
Al Viro733f99a2006-10-14 16:48:26 +0100513 && ((dialect == LANMAN_PROT)
514 || (dialect == LANMAN2_PROT))) {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000515 __s16 tmp;
Steve French50c2f752007-07-13 00:33:32 +0000516 struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
Steve French254e55e2006-06-04 05:53:15 +0000517
Steve French790fe572007-07-07 19:25:05 +0000518 if ((secFlags & CIFSSEC_MAY_LANMAN) ||
Steve French750d1152006-06-27 06:28:30 +0000519 (secFlags & CIFSSEC_MAY_PLNTXT))
Steve French254e55e2006-06-04 05:53:15 +0000520 server->secType = LANMAN;
521 else {
522 cERROR(1, ("mount failed weak security disabled"
523 " in /proc/fs/cifs/SecurityFlags"));
Steve French39798772006-05-31 22:40:51 +0000524 rc = -EOPNOTSUPP;
525 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000526 }
Steve French254e55e2006-06-04 05:53:15 +0000527 server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
528 server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
529 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
Steve French39798772006-05-31 22:40:51 +0000530 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve French254e55e2006-06-04 05:53:15 +0000531 GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
532 /* even though we do not use raw we might as well set this
533 accurately, in case we ever find a need for it */
Steve French790fe572007-07-07 19:25:05 +0000534 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
Steve French254e55e2006-06-04 05:53:15 +0000535 server->maxRw = 0xFF00;
536 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
537 } else {
538 server->maxRw = 0;/* we do not need to use raw anyway */
539 server->capabilities = CAP_MPX_MODE;
540 }
Steve Frenchb815f1e52006-10-02 05:53:29 +0000541 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
Steve French1a70d652006-10-02 05:59:18 +0000542 if (tmp == -1) {
Steve French25ee4a92006-09-30 00:54:23 +0000543 /* OS/2 often does not set timezone therefore
544 * we must use server time to calc time zone.
Steve Frenchb815f1e52006-10-02 05:53:29 +0000545 * Could deviate slightly from the right zone.
546 * Smallest defined timezone difference is 15 minutes
547 * (i.e. Nepal). Rounding up/down is done to match
548 * this requirement.
Steve French25ee4a92006-09-30 00:54:23 +0000549 */
Steve Frenchb815f1e52006-10-02 05:53:29 +0000550 int val, seconds, remain, result;
Steve French25ee4a92006-09-30 00:54:23 +0000551 struct timespec ts, utc;
552 utc = CURRENT_TIME;
553 ts = cnvrtDosUnixTm(le16_to_cpu(rsp->SrvTime.Date),
554 le16_to_cpu(rsp->SrvTime.Time));
Steve French50c2f752007-07-13 00:33:32 +0000555 cFYI(1, ("SrvTime %d sec since 1970 (utc: %d) diff: %d",
556 (int)ts.tv_sec, (int)utc.tv_sec,
Steve French25ee4a92006-09-30 00:54:23 +0000557 (int)(utc.tv_sec - ts.tv_sec)));
Steve Frenchb815f1e52006-10-02 05:53:29 +0000558 val = (int)(utc.tv_sec - ts.tv_sec);
Andre Haupt8594c152007-08-30 20:18:41 +0000559 seconds = abs(val);
Steve French947a5062006-10-02 05:55:25 +0000560 result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000561 remain = seconds % MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000562 if (remain >= (MIN_TZ_ADJ / 2))
Steve Frenchb815f1e52006-10-02 05:53:29 +0000563 result += MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000564 if (val < 0)
Steve Frenchad7a2922008-02-07 23:25:02 +0000565 result = -result;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000566 server->timeAdj = result;
Steve French25ee4a92006-09-30 00:54:23 +0000567 } else {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000568 server->timeAdj = (int)tmp;
569 server->timeAdj *= 60; /* also in seconds */
Steve French25ee4a92006-09-30 00:54:23 +0000570 }
Steve French790fe572007-07-07 19:25:05 +0000571 cFYI(1, ("server->timeAdj: %d seconds", server->timeAdj));
Steve French25ee4a92006-09-30 00:54:23 +0000572
Steve French39798772006-05-31 22:40:51 +0000573
Steve French254e55e2006-06-04 05:53:15 +0000574 /* BB get server time for time conversions and add
Steve French50c2f752007-07-13 00:33:32 +0000575 code to use it and timezone since this is not UTC */
Steve French39798772006-05-31 22:40:51 +0000576
Steve French50c2f752007-07-13 00:33:32 +0000577 if (rsp->EncryptionKeyLength ==
Steve French25ee4a92006-09-30 00:54:23 +0000578 cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
Steve French254e55e2006-06-04 05:53:15 +0000579 memcpy(server->cryptKey, rsp->EncryptionKey,
580 CIFS_CRYPTO_KEY_SIZE);
581 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
582 rc = -EIO; /* need cryptkey unless plain text */
583 goto neg_err_exit;
584 }
Steve French39798772006-05-31 22:40:51 +0000585
Steve French790fe572007-07-07 19:25:05 +0000586 cFYI(1, ("LANMAN negotiated"));
Steve French254e55e2006-06-04 05:53:15 +0000587 /* we will not end up setting signing flags - as no signing
588 was in LANMAN and server did not return the flags on */
589 goto signing_check;
Steve French7c7b25b2006-06-01 19:20:10 +0000590#else /* weak security disabled */
Steve French790fe572007-07-07 19:25:05 +0000591 } else if (pSMBr->hdr.WordCount == 13) {
Steve French50c2f752007-07-13 00:33:32 +0000592 cERROR(1, ("mount failed, cifs module not built "
Steve French254e55e2006-06-04 05:53:15 +0000593 "with CIFS_WEAK_PW_HASH support"));
Steve French7c7b25b2006-06-01 19:20:10 +0000594 rc = -EOPNOTSUPP;
595#endif /* WEAK_PW_HASH */
Steve French254e55e2006-06-04 05:53:15 +0000596 goto neg_err_exit;
Steve French790fe572007-07-07 19:25:05 +0000597 } else if (pSMBr->hdr.WordCount != 17) {
Steve French254e55e2006-06-04 05:53:15 +0000598 /* unknown wct */
599 rc = -EOPNOTSUPP;
600 goto neg_err_exit;
601 }
602 /* else wct == 17 NTLM */
603 server->secMode = pSMBr->SecurityMode;
Steve French790fe572007-07-07 19:25:05 +0000604 if ((server->secMode & SECMODE_USER) == 0)
605 cFYI(1, ("share mode security"));
Steve French39798772006-05-31 22:40:51 +0000606
Steve French790fe572007-07-07 19:25:05 +0000607 if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000608#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French750d1152006-06-27 06:28:30 +0000609 if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000610#endif /* CIFS_WEAK_PW_HASH */
Steve French50c2f752007-07-13 00:33:32 +0000611 cERROR(1, ("Server requests plain text password"
Steve French254e55e2006-06-04 05:53:15 +0000612 " but client support disabled"));
Steve French9312f672006-06-04 22:21:07 +0000613
Steve French790fe572007-07-07 19:25:05 +0000614 if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
Steve French254e55e2006-06-04 05:53:15 +0000615 server->secType = NTLMv2;
Steve French790fe572007-07-07 19:25:05 +0000616 else if (secFlags & CIFSSEC_MAY_NTLM)
Steve French254e55e2006-06-04 05:53:15 +0000617 server->secType = NTLM;
Steve French790fe572007-07-07 19:25:05 +0000618 else if (secFlags & CIFSSEC_MAY_NTLMV2)
Steve Frenchf40c5622006-06-28 00:13:38 +0000619 server->secType = NTLMv2;
Steve Frencha0136892007-10-04 20:05:09 +0000620 else if (secFlags & CIFSSEC_MAY_KRB5)
621 server->secType = Kerberos;
622 else if (secFlags & CIFSSEC_MAY_LANMAN)
623 server->secType = LANMAN;
624/* #ifdef CONFIG_CIFS_EXPERIMENTAL
625 else if (secFlags & CIFSSEC_MAY_PLNTXT)
626 server->secType = ??
627#endif */
628 else {
629 rc = -EOPNOTSUPP;
630 cERROR(1, ("Invalid security type"));
631 goto neg_err_exit;
632 }
633 /* else ... any others ...? */
Steve French7c7b25b2006-06-01 19:20:10 +0000634
Steve French254e55e2006-06-04 05:53:15 +0000635 /* one byte, so no need to convert this or EncryptionKeyLen from
636 little endian */
637 server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
638 /* probably no need to store and check maxvcs */
639 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve French254e55e2006-06-04 05:53:15 +0000641 server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
Steve French90c81e02008-02-12 20:32:36 +0000642 cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf));
Steve French254e55e2006-06-04 05:53:15 +0000643 GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
644 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
Steve Frenchb815f1e52006-10-02 05:53:29 +0000645 server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
646 server->timeAdj *= 60;
Steve French254e55e2006-06-04 05:53:15 +0000647 if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
648 memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
649 CIFS_CRYPTO_KEY_SIZE);
650 } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
651 && (pSMBr->EncryptionKeyLength == 0)) {
652 /* decode security blob */
653 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
654 rc = -EIO; /* no crypt key only if plain text pwd */
655 goto neg_err_exit;
656 }
657
658 /* BB might be helpful to save off the domain of server here */
659
Steve French50c2f752007-07-13 00:33:32 +0000660 if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
Steve French254e55e2006-06-04 05:53:15 +0000661 (server->capabilities & CAP_EXTENDED_SECURITY)) {
662 count = pSMBr->ByteCount;
Jeff Laytone187e442007-10-16 17:10:44 +0000663 if (count < 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 rc = -EIO;
Jeff Laytone187e442007-10-16 17:10:44 +0000665 goto neg_err_exit;
666 }
Jeff Laytone7ddee92008-11-14 13:44:38 -0500667 read_lock(&cifs_tcp_ses_lock);
668 if (server->srv_count > 1) {
669 read_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000670 if (memcmp(server->server_GUID,
671 pSMBr->u.extended_response.
672 GUID, 16) != 0) {
673 cFYI(1, ("server UID changed"));
Steve French254e55e2006-06-04 05:53:15 +0000674 memcpy(server->server_GUID,
Jeff Laytone187e442007-10-16 17:10:44 +0000675 pSMBr->u.extended_response.GUID,
676 16);
677 }
Jeff Laytone7ddee92008-11-14 13:44:38 -0500678 } else {
679 read_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000680 memcpy(server->server_GUID,
681 pSMBr->u.extended_response.GUID, 16);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500682 }
Jeff Laytone187e442007-10-16 17:10:44 +0000683
684 if (count == 16) {
685 server->secType = RawNTLMSSP;
Steve French254e55e2006-06-04 05:53:15 +0000686 } else {
687 rc = decode_negTokenInit(pSMBr->u.extended_response.
688 SecurityBlob,
689 count - 16,
690 &server->secType);
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000691 if (rc == 1)
Jeff Laytone5459372007-11-03 05:11:06 +0000692 rc = 0;
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000693 else
Steve French254e55e2006-06-04 05:53:15 +0000694 rc = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 }
Steve French254e55e2006-06-04 05:53:15 +0000696 } else
697 server->capabilities &= ~CAP_EXTENDED_SECURITY;
698
Steve French6344a422006-06-12 04:18:35 +0000699#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French254e55e2006-06-04 05:53:15 +0000700signing_check:
Steve French6344a422006-06-12 04:18:35 +0000701#endif
Steve French762e5ab2007-06-28 18:41:42 +0000702 if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
703 /* MUST_SIGN already includes the MAY_SIGN FLAG
704 so if this is zero it means that signing is disabled */
705 cFYI(1, ("Signing disabled"));
Steve Frenchabb63d62007-10-18 02:58:40 +0000706 if (server->secMode & SECMODE_SIGN_REQUIRED) {
Steve French762e5ab2007-06-28 18:41:42 +0000707 cERROR(1, ("Server requires "
Jeff Layton7111d212007-10-16 16:50:25 +0000708 "packet signing to be enabled in "
709 "/proc/fs/cifs/SecurityFlags."));
Steve Frenchabb63d62007-10-18 02:58:40 +0000710 rc = -EOPNOTSUPP;
711 }
Steve French50c2f752007-07-13 00:33:32 +0000712 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000713 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Steve French762e5ab2007-06-28 18:41:42 +0000714 } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
715 /* signing required */
Jeff38c10a12007-07-06 21:10:07 +0000716 cFYI(1, ("Must sign - secFlags 0x%x", secFlags));
Steve French762e5ab2007-06-28 18:41:42 +0000717 if ((server->secMode &
718 (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
719 cERROR(1,
720 ("signing required but server lacks support"));
Jeff38c10a12007-07-06 21:10:07 +0000721 rc = -EOPNOTSUPP;
Steve French762e5ab2007-06-28 18:41:42 +0000722 } else
723 server->secMode |= SECMODE_SIGN_REQUIRED;
724 } else {
725 /* signing optional ie CIFSSEC_MAY_SIGN */
Steve French790fe572007-07-07 19:25:05 +0000726 if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
Steve French50c2f752007-07-13 00:33:32 +0000727 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000728 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 }
Steve French50c2f752007-07-13 00:33:32 +0000730
731neg_err_exit:
Steve French4a6d87f2005-08-13 08:15:54 -0700732 cifs_buf_release(pSMB);
Steve French254e55e2006-06-04 05:53:15 +0000733
Steve French790fe572007-07-07 19:25:05 +0000734 cFYI(1, ("negprot rc %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 return rc;
736}
737
738int
739CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
740{
741 struct smb_hdr *smb_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
744 cFYI(1, ("In tree disconnect"));
745 /*
746 * If last user of the connection and
747 * connection alive - disconnect it
748 * If this is the last connection on the server session disconnect it
Steve French50c2f752007-07-13 00:33:32 +0000749 * (and inside session disconnect we should check if tcp socket needs
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 * to be freed and kernel thread woken up).
751 */
752 if (tcon)
753 down(&tcon->tconSem);
754 else
755 return -EIO;
756
757 atomic_dec(&tcon->useCount);
758 if (atomic_read(&tcon->useCount) > 0) {
759 up(&tcon->tconSem);
760 return -EBUSY;
761 }
762
Steve French50c2f752007-07-13 00:33:32 +0000763 /* No need to return error on this operation if tid invalidated and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 closed on server already e.g. due to tcp session crashing */
Steve French3b795212008-11-13 19:45:32 +0000765 if (tcon->need_reconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 up(&tcon->tconSem);
Steve French50c2f752007-07-13 00:33:32 +0000767 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 }
769
Steve French790fe572007-07-07 19:25:05 +0000770 if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 up(&tcon->tconSem);
772 return -EIO;
773 }
Steve French50c2f752007-07-13 00:33:32 +0000774 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
Steve French09d1db52005-04-28 22:41:08 -0700775 (void **)&smb_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 if (rc) {
777 up(&tcon->tconSem);
778 return rc;
Steve Frenchcd634992005-04-28 22:41:10 -0700779 }
Steve French133672e2007-11-13 22:41:37 +0000780
781 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 if (rc)
Steve French966ca922005-04-28 22:41:08 -0700783 cFYI(1, ("Tree disconnect failed %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 up(&tcon->tconSem);
786
Steve French50c2f752007-07-13 00:33:32 +0000787 /* No need to return error on this operation if tid invalidated and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 closed on server already e.g. due to tcp session crashing */
789 if (rc == -EAGAIN)
790 rc = 0;
791
792 return rc;
793}
794
795int
796CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
797{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 LOGOFF_ANDX_REQ *pSMB;
799 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
801 cFYI(1, ("In SMBLogoff for session disconnect"));
Jeff Layton14fbf502008-11-14 13:53:46 -0500802
803 /*
804 * BB: do we need to check validity of ses and server? They should
805 * always be valid since we have an active reference. If not, that
806 * should probably be a BUG()
807 */
808 if (!ses || !ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 return -EIO;
810
Jeff Layton14fbf502008-11-14 13:53:46 -0500811 down(&ses->sesSem);
Steve French3b795212008-11-13 19:45:32 +0000812 if (ses->need_reconnect)
813 goto session_already_dead; /* no need to send SMBlogoff if uid
814 already closed due to reconnect */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
816 if (rc) {
817 up(&ses->sesSem);
818 return rc;
819 }
820
Steve French3b795212008-11-13 19:45:32 +0000821 pSMB->hdr.Mid = GetNextMid(ses->server);
Steve French1982c342005-08-17 12:38:22 -0700822
Steve French3b795212008-11-13 19:45:32 +0000823 if (ses->server->secMode &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
825 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
827 pSMB->hdr.Uid = ses->Suid;
828
829 pSMB->AndXCommand = 0xFF;
Steve French133672e2007-11-13 22:41:37 +0000830 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
Steve French3b795212008-11-13 19:45:32 +0000831session_already_dead:
Steve Frencha59c6582005-08-17 12:12:19 -0700832 up(&ses->sesSem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
834 /* if session dead then we do not need to do ulogoff,
Steve French50c2f752007-07-13 00:33:32 +0000835 since server closed smb session, no sense reporting
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 error */
837 if (rc == -EAGAIN)
838 rc = 0;
839 return rc;
840}
841
842int
Steve French2d785a52007-07-15 01:48:57 +0000843CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
844 __u16 type, const struct nls_table *nls_codepage, int remap)
845{
846 TRANSACTION2_SPI_REQ *pSMB = NULL;
847 TRANSACTION2_SPI_RSP *pSMBr = NULL;
848 struct unlink_psx_rq *pRqD;
849 int name_len;
850 int rc = 0;
851 int bytes_returned = 0;
852 __u16 params, param_offset, offset, byte_count;
853
854 cFYI(1, ("In POSIX delete"));
855PsxDelete:
856 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
857 (void **) &pSMBr);
858 if (rc)
859 return rc;
860
861 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
862 name_len =
863 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
864 PATH_MAX, nls_codepage, remap);
865 name_len++; /* trailing null */
866 name_len *= 2;
867 } else { /* BB add path length overrun check */
868 name_len = strnlen(fileName, PATH_MAX);
869 name_len++; /* trailing null */
870 strncpy(pSMB->FileName, fileName, name_len);
871 }
872
873 params = 6 + name_len;
874 pSMB->MaxParameterCount = cpu_to_le16(2);
875 pSMB->MaxDataCount = 0; /* BB double check this with jra */
876 pSMB->MaxSetupCount = 0;
877 pSMB->Reserved = 0;
878 pSMB->Flags = 0;
879 pSMB->Timeout = 0;
880 pSMB->Reserved2 = 0;
881 param_offset = offsetof(struct smb_com_transaction2_spi_req,
882 InformationLevel) - 4;
883 offset = param_offset + params;
884
885 /* Setup pointer to Request Data (inode type) */
886 pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
887 pRqD->type = cpu_to_le16(type);
888 pSMB->ParameterOffset = cpu_to_le16(param_offset);
889 pSMB->DataOffset = cpu_to_le16(offset);
890 pSMB->SetupCount = 1;
891 pSMB->Reserved3 = 0;
892 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
893 byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
894
895 pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
896 pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
897 pSMB->ParameterCount = cpu_to_le16(params);
898 pSMB->TotalParameterCount = pSMB->ParameterCount;
899 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
900 pSMB->Reserved4 = 0;
901 pSMB->hdr.smb_buf_length += byte_count;
902 pSMB->ByteCount = cpu_to_le16(byte_count);
903 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
904 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +0000905 if (rc)
Steve French2d785a52007-07-15 01:48:57 +0000906 cFYI(1, ("Posix delete returned %d", rc));
Steve French2d785a52007-07-15 01:48:57 +0000907 cifs_buf_release(pSMB);
908
909 cifs_stats_inc(&tcon->num_deletes);
910
911 if (rc == -EAGAIN)
912 goto PsxDelete;
913
914 return rc;
915}
916
917int
Steve French737b7582005-04-28 22:41:06 -0700918CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
919 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920{
921 DELETE_FILE_REQ *pSMB = NULL;
922 DELETE_FILE_RSP *pSMBr = NULL;
923 int rc = 0;
924 int bytes_returned;
925 int name_len;
926
927DelFileRetry:
928 rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
929 (void **) &pSMBr);
930 if (rc)
931 return rc;
932
933 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
934 name_len =
Steve French50c2f752007-07-13 00:33:32 +0000935 cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
Steve French737b7582005-04-28 22:41:06 -0700936 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 name_len++; /* trailing null */
938 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700939 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 name_len = strnlen(fileName, PATH_MAX);
941 name_len++; /* trailing null */
942 strncpy(pSMB->fileName, fileName, name_len);
943 }
944 pSMB->SearchAttributes =
945 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
946 pSMB->BufferFormat = 0x04;
947 pSMB->hdr.smb_buf_length += name_len + 1;
948 pSMB->ByteCount = cpu_to_le16(name_len + 1);
949 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
950 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700951 cifs_stats_inc(&tcon->num_deletes);
Steve Frenchad7a2922008-02-07 23:25:02 +0000952 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 cFYI(1, ("Error in RMFile = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
955 cifs_buf_release(pSMB);
956 if (rc == -EAGAIN)
957 goto DelFileRetry;
958
959 return rc;
960}
961
962int
Steve French50c2f752007-07-13 00:33:32 +0000963CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
Steve French737b7582005-04-28 22:41:06 -0700964 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965{
966 DELETE_DIRECTORY_REQ *pSMB = NULL;
967 DELETE_DIRECTORY_RSP *pSMBr = NULL;
968 int rc = 0;
969 int bytes_returned;
970 int name_len;
971
972 cFYI(1, ("In CIFSSMBRmDir"));
973RmDirRetry:
974 rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
975 (void **) &pSMBr);
976 if (rc)
977 return rc;
978
979 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French737b7582005-04-28 22:41:06 -0700980 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
981 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 name_len++; /* trailing null */
983 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700984 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 name_len = strnlen(dirName, PATH_MAX);
986 name_len++; /* trailing null */
987 strncpy(pSMB->DirName, dirName, name_len);
988 }
989
990 pSMB->BufferFormat = 0x04;
991 pSMB->hdr.smb_buf_length += name_len + 1;
992 pSMB->ByteCount = cpu_to_le16(name_len + 1);
993 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
994 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700995 cifs_stats_inc(&tcon->num_rmdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000996 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 cFYI(1, ("Error in RMDir = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998
999 cifs_buf_release(pSMB);
1000 if (rc == -EAGAIN)
1001 goto RmDirRetry;
1002 return rc;
1003}
1004
1005int
1006CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07001007 const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008{
1009 int rc = 0;
1010 CREATE_DIRECTORY_REQ *pSMB = NULL;
1011 CREATE_DIRECTORY_RSP *pSMBr = NULL;
1012 int bytes_returned;
1013 int name_len;
1014
1015 cFYI(1, ("In CIFSSMBMkDir"));
1016MkDirRetry:
1017 rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
1018 (void **) &pSMBr);
1019 if (rc)
1020 return rc;
1021
1022 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00001023 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
Steve French737b7582005-04-28 22:41:06 -07001024 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 name_len++; /* trailing null */
1026 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -07001027 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 name_len = strnlen(name, PATH_MAX);
1029 name_len++; /* trailing null */
1030 strncpy(pSMB->DirName, name, name_len);
1031 }
1032
1033 pSMB->BufferFormat = 0x04;
1034 pSMB->hdr.smb_buf_length += name_len + 1;
1035 pSMB->ByteCount = cpu_to_le16(name_len + 1);
1036 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1037 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001038 cifs_stats_inc(&tcon->num_mkdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +00001039 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 cFYI(1, ("Error in Mkdir = %d", rc));
Steve Frencha5a2b482005-08-20 21:42:53 -07001041
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 cifs_buf_release(pSMB);
1043 if (rc == -EAGAIN)
1044 goto MkDirRetry;
1045 return rc;
1046}
1047
Steve French2dd29d32007-04-23 22:07:35 +00001048int
1049CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
Steve Frenchad7a2922008-02-07 23:25:02 +00001050 __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
Steve French50c2f752007-07-13 00:33:32 +00001051 __u32 *pOplock, const char *name,
Steve French2dd29d32007-04-23 22:07:35 +00001052 const struct nls_table *nls_codepage, int remap)
1053{
1054 TRANSACTION2_SPI_REQ *pSMB = NULL;
1055 TRANSACTION2_SPI_RSP *pSMBr = NULL;
1056 int name_len;
1057 int rc = 0;
1058 int bytes_returned = 0;
Steve French2dd29d32007-04-23 22:07:35 +00001059 __u16 params, param_offset, offset, byte_count, count;
Steve Frenchad7a2922008-02-07 23:25:02 +00001060 OPEN_PSX_REQ *pdata;
1061 OPEN_PSX_RSP *psx_rsp;
Steve French2dd29d32007-04-23 22:07:35 +00001062
1063 cFYI(1, ("In POSIX Create"));
1064PsxCreat:
1065 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
1066 (void **) &pSMBr);
1067 if (rc)
1068 return rc;
1069
1070 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1071 name_len =
1072 cifsConvertToUCS((__le16 *) pSMB->FileName, name,
1073 PATH_MAX, nls_codepage, remap);
1074 name_len++; /* trailing null */
1075 name_len *= 2;
1076 } else { /* BB improve the check for buffer overruns BB */
1077 name_len = strnlen(name, PATH_MAX);
1078 name_len++; /* trailing null */
1079 strncpy(pSMB->FileName, name, name_len);
1080 }
1081
1082 params = 6 + name_len;
1083 count = sizeof(OPEN_PSX_REQ);
1084 pSMB->MaxParameterCount = cpu_to_le16(2);
1085 pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
1086 pSMB->MaxSetupCount = 0;
1087 pSMB->Reserved = 0;
1088 pSMB->Flags = 0;
1089 pSMB->Timeout = 0;
1090 pSMB->Reserved2 = 0;
1091 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00001092 InformationLevel) - 4;
Steve French2dd29d32007-04-23 22:07:35 +00001093 offset = param_offset + params;
Steve French2dd29d32007-04-23 22:07:35 +00001094 pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001095 pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
Steve French2dd29d32007-04-23 22:07:35 +00001096 pdata->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00001097 pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
Steve French2dd29d32007-04-23 22:07:35 +00001098 pdata->OpenFlags = cpu_to_le32(*pOplock);
1099 pSMB->ParameterOffset = cpu_to_le16(param_offset);
1100 pSMB->DataOffset = cpu_to_le16(offset);
1101 pSMB->SetupCount = 1;
1102 pSMB->Reserved3 = 0;
1103 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
1104 byte_count = 3 /* pad */ + params + count;
1105
1106 pSMB->DataCount = cpu_to_le16(count);
1107 pSMB->ParameterCount = cpu_to_le16(params);
1108 pSMB->TotalDataCount = pSMB->DataCount;
1109 pSMB->TotalParameterCount = pSMB->ParameterCount;
1110 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1111 pSMB->Reserved4 = 0;
Steve French50c2f752007-07-13 00:33:32 +00001112 pSMB->hdr.smb_buf_length += byte_count;
Steve French2dd29d32007-04-23 22:07:35 +00001113 pSMB->ByteCount = cpu_to_le16(byte_count);
1114 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1115 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1116 if (rc) {
1117 cFYI(1, ("Posix create returned %d", rc));
1118 goto psx_create_err;
1119 }
1120
Steve French790fe572007-07-07 19:25:05 +00001121 cFYI(1, ("copying inode info"));
Steve French2dd29d32007-04-23 22:07:35 +00001122 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1123
1124 if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
1125 rc = -EIO; /* bad smb */
1126 goto psx_create_err;
1127 }
1128
1129 /* copy return information to pRetData */
Steve French50c2f752007-07-13 00:33:32 +00001130 psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
Steve French2dd29d32007-04-23 22:07:35 +00001131 + le16_to_cpu(pSMBr->t2.DataOffset));
Steve French50c2f752007-07-13 00:33:32 +00001132
Steve French2dd29d32007-04-23 22:07:35 +00001133 *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
Steve French790fe572007-07-07 19:25:05 +00001134 if (netfid)
Steve French2dd29d32007-04-23 22:07:35 +00001135 *netfid = psx_rsp->Fid; /* cifs fid stays in le */
1136 /* Let caller know file was created so we can set the mode. */
1137 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001138 if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
Steve French2dd29d32007-04-23 22:07:35 +00001139 *pOplock |= CIFS_CREATE_ACTION;
1140 /* check to make sure response data is there */
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001141 if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1142 pRetData->Type = cpu_to_le32(-1); /* unknown */
Steve French90c81e02008-02-12 20:32:36 +00001143 cFYI(DBG2, ("unknown type"));
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001144 } else {
Steve French790fe572007-07-07 19:25:05 +00001145 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001146 + sizeof(FILE_UNIX_BASIC_INFO)) {
Steve French50c2f752007-07-13 00:33:32 +00001147 cERROR(1, ("Open response data too small"));
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001148 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001149 goto psx_create_err;
1150 }
Steve French50c2f752007-07-13 00:33:32 +00001151 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001152 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001153 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001154 }
Steve French2dd29d32007-04-23 22:07:35 +00001155
1156psx_create_err:
1157 cifs_buf_release(pSMB);
1158
1159 cifs_stats_inc(&tcon->num_mkdirs);
1160
1161 if (rc == -EAGAIN)
1162 goto PsxCreat;
1163
Steve French50c2f752007-07-13 00:33:32 +00001164 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001165}
1166
Steve Frencha9d02ad2005-08-24 23:06:05 -07001167static __u16 convert_disposition(int disposition)
1168{
1169 __u16 ofun = 0;
1170
1171 switch (disposition) {
1172 case FILE_SUPERSEDE:
1173 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1174 break;
1175 case FILE_OPEN:
1176 ofun = SMBOPEN_OAPPEND;
1177 break;
1178 case FILE_CREATE:
1179 ofun = SMBOPEN_OCREATE;
1180 break;
1181 case FILE_OPEN_IF:
1182 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1183 break;
1184 case FILE_OVERWRITE:
1185 ofun = SMBOPEN_OTRUNC;
1186 break;
1187 case FILE_OVERWRITE_IF:
1188 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1189 break;
1190 default:
Steve French790fe572007-07-07 19:25:05 +00001191 cFYI(1, ("unknown disposition %d", disposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001192 ofun = SMBOPEN_OAPPEND; /* regular open */
1193 }
1194 return ofun;
1195}
1196
Jeff Layton35fc37d2008-05-14 10:22:03 -07001197static int
1198access_flags_to_smbopen_mode(const int access_flags)
1199{
1200 int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
1201
1202 if (masked_flags == GENERIC_READ)
1203 return SMBOPEN_READ;
1204 else if (masked_flags == GENERIC_WRITE)
1205 return SMBOPEN_WRITE;
1206
1207 /* just go for read/write */
1208 return SMBOPEN_READWRITE;
1209}
1210
Steve Frencha9d02ad2005-08-24 23:06:05 -07001211int
1212SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1213 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001214 const int access_flags, const int create_options, __u16 *netfid,
1215 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001216 const struct nls_table *nls_codepage, int remap)
1217{
1218 int rc = -EACCES;
1219 OPENX_REQ *pSMB = NULL;
1220 OPENX_RSP *pSMBr = NULL;
1221 int bytes_returned;
1222 int name_len;
1223 __u16 count;
1224
1225OldOpenRetry:
1226 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1227 (void **) &pSMBr);
1228 if (rc)
1229 return rc;
1230
1231 pSMB->AndXCommand = 0xFF; /* none */
1232
1233 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1234 count = 1; /* account for one byte pad to word boundary */
1235 name_len =
1236 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1237 fileName, PATH_MAX, nls_codepage, remap);
1238 name_len++; /* trailing null */
1239 name_len *= 2;
1240 } else { /* BB improve check for buffer overruns BB */
1241 count = 0; /* no pad */
1242 name_len = strnlen(fileName, PATH_MAX);
1243 name_len++; /* trailing null */
1244 strncpy(pSMB->fileName, fileName, name_len);
1245 }
1246 if (*pOplock & REQ_OPLOCK)
1247 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001248 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001249 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001250
Steve Frencha9d02ad2005-08-24 23:06:05 -07001251 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
Jeff Layton35fc37d2008-05-14 10:22:03 -07001252 pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001253 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1254 /* set file as system file if special file such
1255 as fifo and server expecting SFU style and
1256 no Unix extensions */
1257
Steve French790fe572007-07-07 19:25:05 +00001258 if (create_options & CREATE_OPTION_SPECIAL)
1259 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001260 else /* BB FIXME BB */
1261 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001262
Jeff Layton67750fb2008-05-09 22:28:02 +00001263 if (create_options & CREATE_OPTION_READONLY)
1264 pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001265
1266 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001267/* pSMB->CreateOptions = cpu_to_le32(create_options &
1268 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001269 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001270
1271 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001272 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001273 count += name_len;
1274 pSMB->hdr.smb_buf_length += count;
1275
1276 pSMB->ByteCount = cpu_to_le16(count);
1277 /* long_op set to 1 to allow for oplock break timeouts */
1278 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001279 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001280 cifs_stats_inc(&tcon->num_opens);
1281 if (rc) {
1282 cFYI(1, ("Error in Open = %d", rc));
1283 } else {
1284 /* BB verify if wct == 15 */
1285
Steve French582d21e2008-05-13 04:54:12 +00001286/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
Steve Frencha9d02ad2005-08-24 23:06:05 -07001287
1288 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1289 /* Let caller know file was created so we can set the mode. */
1290 /* Do we care about the CreateAction in any other cases? */
1291 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001292/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001293 *pOplock |= CIFS_CREATE_ACTION; */
1294 /* BB FIXME END */
1295
Steve French790fe572007-07-07 19:25:05 +00001296 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001297 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1298 pfile_info->LastAccessTime = 0; /* BB fixme */
1299 pfile_info->LastWriteTime = 0; /* BB fixme */
1300 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001301 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001302 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001303 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001304 pfile_info->AllocationSize =
1305 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1306 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001307 pfile_info->NumberOfLinks = cpu_to_le32(1);
Jeff Layton9a8165f2008-10-17 21:03:20 -04001308 pfile_info->DeletePending = 0;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001309 }
1310 }
1311
1312 cifs_buf_release(pSMB);
1313 if (rc == -EAGAIN)
1314 goto OldOpenRetry;
1315 return rc;
1316}
1317
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318int
1319CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1320 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001321 const int access_flags, const int create_options, __u16 *netfid,
1322 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001323 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324{
1325 int rc = -EACCES;
1326 OPEN_REQ *pSMB = NULL;
1327 OPEN_RSP *pSMBr = NULL;
1328 int bytes_returned;
1329 int name_len;
1330 __u16 count;
1331
1332openRetry:
1333 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1334 (void **) &pSMBr);
1335 if (rc)
1336 return rc;
1337
1338 pSMB->AndXCommand = 0xFF; /* none */
1339
1340 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1341 count = 1; /* account for one byte pad to word boundary */
1342 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001343 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001344 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 name_len++; /* trailing null */
1346 name_len *= 2;
1347 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001348 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 count = 0; /* no pad */
1350 name_len = strnlen(fileName, PATH_MAX);
1351 name_len++; /* trailing null */
1352 pSMB->NameLength = cpu_to_le16(name_len);
1353 strncpy(pSMB->fileName, fileName, name_len);
1354 }
1355 if (*pOplock & REQ_OPLOCK)
1356 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001357 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1360 pSMB->AllocationSize = 0;
Steve Frencheda3c0292005-07-21 15:20:28 -07001361 /* set file as system file if special file such
1362 as fifo and server expecting SFU style and
1363 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001364 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c0292005-07-21 15:20:28 -07001365 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1366 else
1367 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Jeff Layton67750fb2008-05-09 22:28:02 +00001368
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 /* XP does not handle ATTR_POSIX_SEMANTICS */
1370 /* but it helps speed up case sensitive checks for other
1371 servers such as Samba */
1372 if (tcon->ses->capabilities & CAP_UNIX)
1373 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1374
Jeff Layton67750fb2008-05-09 22:28:02 +00001375 if (create_options & CREATE_OPTION_READONLY)
1376 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);
1377
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1379 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c0292005-07-21 15:20:28 -07001380 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001381 /* BB Expirement with various impersonation levels and verify */
1382 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 pSMB->SecurityFlags =
1384 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1385
1386 count += name_len;
1387 pSMB->hdr.smb_buf_length += count;
1388
1389 pSMB->ByteCount = cpu_to_le16(count);
1390 /* long_op set to 1 to allow for oplock break timeouts */
1391 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001392 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha4544342005-08-24 13:59:35 -07001393 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 if (rc) {
1395 cFYI(1, ("Error in Open = %d", rc));
1396 } else {
Steve French09d1db52005-04-28 22:41:08 -07001397 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1399 /* Let caller know file was created so we can set the mode. */
1400 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001401 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001402 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001403 if (pfile_info) {
Steve French50c2f752007-07-13 00:33:32 +00001404 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 36 /* CreationTime to Attributes */);
1406 /* the file_info buf is endian converted by caller */
1407 pfile_info->AllocationSize = pSMBr->AllocationSize;
1408 pfile_info->EndOfFile = pSMBr->EndOfFile;
1409 pfile_info->NumberOfLinks = cpu_to_le32(1);
Jeff Layton9a8165f2008-10-17 21:03:20 -04001410 pfile_info->DeletePending = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001413
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 cifs_buf_release(pSMB);
1415 if (rc == -EAGAIN)
1416 goto openRetry;
1417 return rc;
1418}
1419
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420int
Steve French50c2f752007-07-13 00:33:32 +00001421CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1422 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1423 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424{
1425 int rc = -EACCES;
1426 READ_REQ *pSMB = NULL;
1427 READ_RSP *pSMBr = NULL;
1428 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001429 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001430 int resp_buf_type = 0;
1431 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432
Steve French790fe572007-07-07 19:25:05 +00001433 cFYI(1, ("Reading %d bytes on fid %d", count, netfid));
1434 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001435 wct = 12;
1436 else
1437 wct = 10; /* old style read */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438
1439 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001440 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 if (rc)
1442 return rc;
1443
1444 /* tcon and ses pointer are checked in smb_init */
1445 if (tcon->ses->server == NULL)
1446 return -ECONNABORTED;
1447
Steve Frenchec637e32005-12-12 20:53:18 -08001448 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 pSMB->Fid = netfid;
1450 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001451 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001452 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve French790fe572007-07-07 19:25:05 +00001453 else if ((lseek >> 32) > 0) /* can not handle this big offset for old */
Steve Frenchec637e32005-12-12 20:53:18 -08001454 return -EIO;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001455
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 pSMB->Remaining = 0;
1457 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1458 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001459 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001460 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1461 else {
1462 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001463 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001464 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001465 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001466 }
Steve Frenchec637e32005-12-12 20:53:18 -08001467
1468 iov[0].iov_base = (char *)pSMB;
1469 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001470 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Steve French133672e2007-11-13 22:41:37 +00001471 &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001472 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001473 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 if (rc) {
1475 cERROR(1, ("Send error in read = %d", rc));
1476 } else {
1477 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1478 data_length = data_length << 16;
1479 data_length += le16_to_cpu(pSMBr->DataLength);
1480 *nbytes = data_length;
1481
1482 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001483 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 || (data_length > count)) {
Steve French50c2f752007-07-13 00:33:32 +00001485 cFYI(1, ("bad length %d for count %d",
1486 data_length, count));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 rc = -EIO;
1488 *nbytes = 0;
1489 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001490 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001491 le16_to_cpu(pSMBr->DataOffset);
1492/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Steve French50c2f752007-07-13 00:33:32 +00001493 cERROR(1,("Faulting on read rc = %d",rc));
1494 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001495 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001496 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001497 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 }
1499 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
Steve French4b8f9302006-02-26 16:41:18 +00001501/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001502 if (*buf) {
1503 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001504 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001505 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001506 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001507 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001508 /* return buffer to caller to free */
1509 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001510 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001511 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001512 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001513 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001514 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001515
1516 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 since file handle passed in no longer valid */
1518 return rc;
1519}
1520
Steve Frenchec637e32005-12-12 20:53:18 -08001521
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522int
1523CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1524 const int netfid, const unsigned int count,
1525 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001526 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527{
1528 int rc = -EACCES;
1529 WRITE_REQ *pSMB = NULL;
1530 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001531 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 __u32 bytes_sent;
1533 __u16 byte_count;
1534
Steve French61de8002008-10-30 20:15:22 +00001535 /* cFYI(1, ("write at %lld %d bytes", offset, count));*/
Steve French790fe572007-07-07 19:25:05 +00001536 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001537 return -ECONNABORTED;
1538
Steve French790fe572007-07-07 19:25:05 +00001539 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001540 wct = 14;
1541 else
1542 wct = 12;
1543
1544 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 (void **) &pSMBr);
1546 if (rc)
1547 return rc;
1548 /* tcon and ses pointer are checked in smb_init */
1549 if (tcon->ses->server == NULL)
1550 return -ECONNABORTED;
1551
1552 pSMB->AndXCommand = 0xFF; /* none */
1553 pSMB->Fid = netfid;
1554 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001555 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001556 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French790fe572007-07-07 19:25:05 +00001557 else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
Steve French1c955182005-08-30 20:58:07 -07001558 return -EIO;
Steve French50c2f752007-07-13 00:33:32 +00001559
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 pSMB->Reserved = 0xFFFFFFFF;
1561 pSMB->WriteMode = 0;
1562 pSMB->Remaining = 0;
1563
Steve French50c2f752007-07-13 00:33:32 +00001564 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 can send more if LARGE_WRITE_X capability returned by the server and if
1566 our buffer is big enough or if we convert to iovecs on socket writes
1567 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001568 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1570 } else {
1571 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1572 & ~0xFF;
1573 }
1574
1575 if (bytes_sent > count)
1576 bytes_sent = count;
1577 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001578 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001579 if (buf)
Steve French50c2f752007-07-13 00:33:32 +00001580 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001581 else if (ubuf) {
1582 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 cifs_buf_release(pSMB);
1584 return -EFAULT;
1585 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001586 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 /* No buffer */
1588 cifs_buf_release(pSMB);
1589 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001590 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001591 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001592 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001593 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001594 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001595
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1597 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001598 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001599
Steve French790fe572007-07-07 19:25:05 +00001600 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001601 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001602 else { /* old style write has byte count 4 bytes earlier
1603 so 4 bytes pad */
1604 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001605 (struct smb_com_writex_req *)pSMB;
1606 pSMBW->ByteCount = cpu_to_le16(byte_count);
1607 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608
1609 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1610 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001611 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 if (rc) {
1613 cFYI(1, ("Send error in write = %d", rc));
1614 *nbytes = 0;
1615 } else {
1616 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1617 *nbytes = (*nbytes) << 16;
1618 *nbytes += le16_to_cpu(pSMBr->Count);
1619 }
1620
1621 cifs_buf_release(pSMB);
1622
Steve French50c2f752007-07-13 00:33:32 +00001623 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 since file handle passed in no longer valid */
1625
1626 return rc;
1627}
1628
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001629int
1630CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001632 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1633 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634{
1635 int rc = -EACCES;
1636 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001637 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001638 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001639 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
Steve French790fe572007-07-07 19:25:05 +00001641 cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count));
Steve Frenchff7feac2005-11-15 16:45:16 -08001642
Steve French790fe572007-07-07 19:25:05 +00001643 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French8cc64c62005-10-03 13:49:43 -07001644 wct = 14;
1645 else
1646 wct = 12;
1647 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 if (rc)
1649 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 /* tcon and ses pointer are checked in smb_init */
1651 if (tcon->ses->server == NULL)
1652 return -ECONNABORTED;
1653
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001654 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 pSMB->Fid = netfid;
1656 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001657 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001658 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French790fe572007-07-07 19:25:05 +00001659 else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
Steve French8cc64c62005-10-03 13:49:43 -07001660 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 pSMB->Reserved = 0xFFFFFFFF;
1662 pSMB->WriteMode = 0;
1663 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001664
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001666 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667
Steve French3e844692005-10-03 13:37:24 -07001668 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1669 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001670 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001671 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001672 pSMB->hdr.smb_buf_length += count+1;
1673 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001674 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1675 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001676 pSMB->ByteCount = cpu_to_le16(count + 1);
1677 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001678 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001679 (struct smb_com_writex_req *)pSMB;
1680 pSMBW->ByteCount = cpu_to_le16(count + 5);
1681 }
Steve French3e844692005-10-03 13:37:24 -07001682 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001683 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001684 iov[0].iov_len = smb_hdr_len + 4;
1685 else /* wct == 12 pad bigger by four bytes */
1686 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001687
Steve French3e844692005-10-03 13:37:24 -07001688
Steve Frenchec637e32005-12-12 20:53:18 -08001689 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001690 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001691 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 if (rc) {
Steve French8cc64c62005-10-03 13:49:43 -07001693 cFYI(1, ("Send error Write2 = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 *nbytes = 0;
Steve French790fe572007-07-07 19:25:05 +00001695 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001696 /* presumably this can not happen, but best to be safe */
1697 rc = -EIO;
1698 *nbytes = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001699 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001700 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001701 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1702 *nbytes = (*nbytes) << 16;
1703 *nbytes += le16_to_cpu(pSMBr->Count);
Steve French50c2f752007-07-13 00:33:32 +00001704 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705
Steve French4b8f9302006-02-26 16:41:18 +00001706/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001707 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001708 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001709 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001710 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711
Steve French50c2f752007-07-13 00:33:32 +00001712 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 since file handle passed in no longer valid */
1714
1715 return rc;
1716}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001717
1718
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719int
1720CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1721 const __u16 smb_file_id, const __u64 len,
1722 const __u64 offset, const __u32 numUnlock,
Steve French4b18f2a2008-04-29 00:06:05 +00001723 const __u32 numLock, const __u8 lockType, const bool waitFlag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724{
1725 int rc = 0;
1726 LOCK_REQ *pSMB = NULL;
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001727/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 int bytes_returned;
1729 int timeout = 0;
1730 __u16 count;
1731
Steve French4b18f2a2008-04-29 00:06:05 +00001732 cFYI(1, ("CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock));
Steve French46810cb2005-04-28 22:41:09 -07001733 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1734
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 if (rc)
1736 return rc;
1737
Steve French790fe572007-07-07 19:25:05 +00001738 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001739 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740 pSMB->Timeout = 0;
Steve French4b18f2a2008-04-29 00:06:05 +00001741 } else if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001742 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1744 } else {
1745 pSMB->Timeout = 0;
1746 }
1747
1748 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1749 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1750 pSMB->LockType = lockType;
1751 pSMB->AndXCommand = 0xFF; /* none */
1752 pSMB->Fid = smb_file_id; /* netfid stays le */
1753
Steve French790fe572007-07-07 19:25:05 +00001754 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1756 /* BB where to store pid high? */
1757 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1758 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1759 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1760 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1761 count = sizeof(LOCKING_ANDX_RANGE);
1762 } else {
1763 /* oplock break */
1764 count = 0;
1765 }
1766 pSMB->hdr.smb_buf_length += count;
1767 pSMB->ByteCount = cpu_to_le16(count);
1768
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001769 if (waitFlag) {
1770 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001771 (struct smb_hdr *) pSMB, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001772 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001773 } else {
Steve French133672e2007-11-13 22:41:37 +00001774 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1775 timeout);
1776 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001777 }
Steve Frencha4544342005-08-24 13:59:35 -07001778 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001779 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780 cFYI(1, ("Send error in Lock = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781
Steve French50c2f752007-07-13 00:33:32 +00001782 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 since file handle passed in no longer valid */
1784 return rc;
1785}
1786
1787int
Steve French08547b02006-02-28 22:39:25 +00001788CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1789 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001790 struct file_lock *pLockData, const __u16 lock_type,
Steve French4b18f2a2008-04-29 00:06:05 +00001791 const bool waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001792{
1793 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1794 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001795 struct cifs_posix_lock *parm_data;
1796 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001797 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001798 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001799 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001800 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001801 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001802
1803 cFYI(1, ("Posix Lock"));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001804
Steve French790fe572007-07-07 19:25:05 +00001805 if (pLockData == NULL)
Marcin Slusarzed5f0372008-05-13 04:01:01 +00001806 return -EINVAL;
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001807
Steve French08547b02006-02-28 22:39:25 +00001808 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1809
1810 if (rc)
1811 return rc;
1812
1813 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1814
Steve French50c2f752007-07-13 00:33:32 +00001815 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001816 pSMB->MaxSetupCount = 0;
1817 pSMB->Reserved = 0;
1818 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001819 pSMB->Reserved2 = 0;
1820 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1821 offset = param_offset + params;
1822
Steve French08547b02006-02-28 22:39:25 +00001823 count = sizeof(struct cifs_posix_lock);
1824 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001825 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001826 pSMB->SetupCount = 1;
1827 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001828 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001829 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1830 else
1831 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1832 byte_count = 3 /* pad */ + params + count;
1833 pSMB->DataCount = cpu_to_le16(count);
1834 pSMB->ParameterCount = cpu_to_le16(params);
1835 pSMB->TotalDataCount = pSMB->DataCount;
1836 pSMB->TotalParameterCount = pSMB->ParameterCount;
1837 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001838 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001839 (((char *) &pSMB->hdr.Protocol) + offset);
1840
1841 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001842 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001843 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001844 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001845 pSMB->Timeout = cpu_to_le32(-1);
1846 } else
1847 pSMB->Timeout = 0;
1848
Steve French08547b02006-02-28 22:39:25 +00001849 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001850 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001851 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001852
1853 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001854 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001855 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1856 pSMB->Reserved4 = 0;
1857 pSMB->hdr.smb_buf_length += byte_count;
1858 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001859 if (waitFlag) {
1860 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1861 (struct smb_hdr *) pSMBr, &bytes_returned);
1862 } else {
Steve French133672e2007-11-13 22:41:37 +00001863 iov[0].iov_base = (char *)pSMB;
1864 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1865 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1866 &resp_buf_type, timeout);
1867 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1868 not try to free it twice below on exit */
1869 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001870 }
1871
Steve French08547b02006-02-28 22:39:25 +00001872 if (rc) {
1873 cFYI(1, ("Send error in Posix Lock = %d", rc));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001874 } else if (get_flag) {
1875 /* lock structure can be returned on get */
1876 __u16 data_offset;
1877 __u16 data_count;
1878 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001879
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001880 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1881 rc = -EIO; /* bad smb */
1882 goto plk_err_exit;
1883 }
Steve French790fe572007-07-07 19:25:05 +00001884 if (pLockData == NULL) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001885 rc = -EINVAL;
1886 goto plk_err_exit;
1887 }
1888 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1889 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001890 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001891 rc = -EIO;
1892 goto plk_err_exit;
1893 }
1894 parm_data = (struct cifs_posix_lock *)
1895 ((char *)&pSMBr->hdr.Protocol + data_offset);
Steve French790fe572007-07-07 19:25:05 +00001896 if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001897 pLockData->fl_type = F_UNLCK;
1898 }
Steve French50c2f752007-07-13 00:33:32 +00001899
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001900plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001901 if (pSMB)
1902 cifs_small_buf_release(pSMB);
1903
Steve French133672e2007-11-13 22:41:37 +00001904 if (resp_buf_type == CIFS_SMALL_BUFFER)
1905 cifs_small_buf_release(iov[0].iov_base);
1906 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1907 cifs_buf_release(iov[0].iov_base);
1908
Steve French08547b02006-02-28 22:39:25 +00001909 /* Note: On -EAGAIN error only caller can retry on handle based calls
1910 since file handle passed in no longer valid */
1911
1912 return rc;
1913}
1914
1915
1916int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1918{
1919 int rc = 0;
1920 CLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921 cFYI(1, ("In CIFSSMBClose"));
1922
1923/* do not retry on dead session on close */
1924 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001925 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926 return 0;
1927 if (rc)
1928 return rc;
1929
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e52006-10-02 05:53:29 +00001931 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001933 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001934 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001936 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 /* EINTR is expected when user ctl-c to kill app */
1938 cERROR(1, ("Send error in Close = %d", rc));
1939 }
1940 }
1941
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001943 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 rc = 0;
1945
1946 return rc;
1947}
1948
1949int
1950CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1951 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001952 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953{
1954 int rc = 0;
1955 RENAME_REQ *pSMB = NULL;
1956 RENAME_RSP *pSMBr = NULL;
1957 int bytes_returned;
1958 int name_len, name_len2;
1959 __u16 count;
1960
1961 cFYI(1, ("In CIFSSMBRename"));
1962renameRetry:
1963 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1964 (void **) &pSMBr);
1965 if (rc)
1966 return rc;
1967
1968 pSMB->BufferFormat = 0x04;
1969 pSMB->SearchAttributes =
1970 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1971 ATTR_DIRECTORY);
1972
1973 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1974 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001975 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001976 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 name_len++; /* trailing null */
1978 name_len *= 2;
1979 pSMB->OldFileName[name_len] = 0x04; /* pad */
1980 /* protocol requires ASCII signature byte on Unicode string */
1981 pSMB->OldFileName[name_len + 1] = 0x00;
1982 name_len2 =
Steve French582d21e2008-05-13 04:54:12 +00001983 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07001984 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
1986 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00001987 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988 name_len = strnlen(fromName, PATH_MAX);
1989 name_len++; /* trailing null */
1990 strncpy(pSMB->OldFileName, fromName, name_len);
1991 name_len2 = strnlen(toName, PATH_MAX);
1992 name_len2++; /* trailing null */
1993 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
1994 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
1995 name_len2++; /* trailing null */
1996 name_len2++; /* signature byte */
1997 }
1998
1999 count = 1 /* 1st signature byte */ + name_len + name_len2;
2000 pSMB->hdr.smb_buf_length += count;
2001 pSMB->ByteCount = cpu_to_le16(count);
2002
2003 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2004 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002005 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002006 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 cFYI(1, ("Send error in rename = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 cifs_buf_release(pSMB);
2010
2011 if (rc == -EAGAIN)
2012 goto renameRetry;
2013
2014 return rc;
2015}
2016
Steve French50c2f752007-07-13 00:33:32 +00002017int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
Jeff Layton391e5752008-09-24 11:32:59 -04002018 int netfid, const char *target_name,
Steve French50c2f752007-07-13 00:33:32 +00002019 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020{
2021 struct smb_com_transaction2_sfi_req *pSMB = NULL;
2022 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00002023 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 char *data_offset;
2025 char dummy_string[30];
2026 int rc = 0;
2027 int bytes_returned = 0;
2028 int len_of_str;
2029 __u16 params, param_offset, offset, count, byte_count;
2030
2031 cFYI(1, ("Rename to File by handle"));
2032 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
2033 (void **) &pSMBr);
2034 if (rc)
2035 return rc;
2036
2037 params = 6;
2038 pSMB->MaxSetupCount = 0;
2039 pSMB->Reserved = 0;
2040 pSMB->Flags = 0;
2041 pSMB->Timeout = 0;
2042 pSMB->Reserved2 = 0;
2043 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2044 offset = param_offset + params;
2045
2046 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2047 rename_info = (struct set_file_rename *) data_offset;
2048 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002049 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 pSMB->SetupCount = 1;
2051 pSMB->Reserved3 = 0;
2052 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2053 byte_count = 3 /* pad */ + params;
2054 pSMB->ParameterCount = cpu_to_le16(params);
2055 pSMB->TotalParameterCount = pSMB->ParameterCount;
2056 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2057 pSMB->DataOffset = cpu_to_le16(offset);
2058 /* construct random name ".cifs_tmp<inodenum><mid>" */
2059 rename_info->overwrite = cpu_to_le32(1);
2060 rename_info->root_fid = 0;
2061 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002062 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002063 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2064 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002065 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002067 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002068 target_name, PATH_MAX, nls_codepage,
2069 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070 }
2071 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
Jeff Layton391e5752008-09-24 11:32:59 -04002072 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 byte_count += count;
2074 pSMB->DataCount = cpu_to_le16(count);
2075 pSMB->TotalDataCount = pSMB->DataCount;
2076 pSMB->Fid = netfid;
2077 pSMB->InformationLevel =
2078 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2079 pSMB->Reserved4 = 0;
2080 pSMB->hdr.smb_buf_length += byte_count;
2081 pSMB->ByteCount = cpu_to_le16(byte_count);
2082 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002083 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002084 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002085 if (rc)
Steve French790fe572007-07-07 19:25:05 +00002086 cFYI(1, ("Send error in Rename (by file handle) = %d", rc));
Steve Frencha5a2b482005-08-20 21:42:53 -07002087
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 cifs_buf_release(pSMB);
2089
2090 /* Note: On -EAGAIN error only caller can retry on handle based calls
2091 since file handle passed in no longer valid */
2092
2093 return rc;
2094}
2095
2096int
Steve French50c2f752007-07-13 00:33:32 +00002097CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2098 const __u16 target_tid, const char *toName, const int flags,
2099 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100{
2101 int rc = 0;
2102 COPY_REQ *pSMB = NULL;
2103 COPY_RSP *pSMBr = NULL;
2104 int bytes_returned;
2105 int name_len, name_len2;
2106 __u16 count;
2107
2108 cFYI(1, ("In CIFSSMBCopy"));
2109copyRetry:
2110 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2111 (void **) &pSMBr);
2112 if (rc)
2113 return rc;
2114
2115 pSMB->BufferFormat = 0x04;
2116 pSMB->Tid2 = target_tid;
2117
2118 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2119
2120 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002121 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002122 fromName, PATH_MAX, nls_codepage,
2123 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124 name_len++; /* trailing null */
2125 name_len *= 2;
2126 pSMB->OldFileName[name_len] = 0x04; /* pad */
2127 /* protocol requires ASCII signature byte on Unicode string */
2128 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002129 name_len2 =
2130 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002131 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2133 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002134 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 name_len = strnlen(fromName, PATH_MAX);
2136 name_len++; /* trailing null */
2137 strncpy(pSMB->OldFileName, fromName, name_len);
2138 name_len2 = strnlen(toName, PATH_MAX);
2139 name_len2++; /* trailing null */
2140 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2141 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2142 name_len2++; /* trailing null */
2143 name_len2++; /* signature byte */
2144 }
2145
2146 count = 1 /* 1st signature byte */ + name_len + name_len2;
2147 pSMB->hdr.smb_buf_length += count;
2148 pSMB->ByteCount = cpu_to_le16(count);
2149
2150 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2151 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2152 if (rc) {
2153 cFYI(1, ("Send error in copy = %d with %d files copied",
2154 rc, le16_to_cpu(pSMBr->CopyCount)));
2155 }
Steve French0d817bc2008-05-22 02:02:03 +00002156 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157
2158 if (rc == -EAGAIN)
2159 goto copyRetry;
2160
2161 return rc;
2162}
2163
2164int
2165CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2166 const char *fromName, const char *toName,
2167 const struct nls_table *nls_codepage)
2168{
2169 TRANSACTION2_SPI_REQ *pSMB = NULL;
2170 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2171 char *data_offset;
2172 int name_len;
2173 int name_len_target;
2174 int rc = 0;
2175 int bytes_returned = 0;
2176 __u16 params, param_offset, offset, byte_count;
2177
2178 cFYI(1, ("In Symlink Unix style"));
2179createSymLinkRetry:
2180 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2181 (void **) &pSMBr);
2182 if (rc)
2183 return rc;
2184
2185 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2186 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002187 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 /* find define for this maxpathcomponent */
2189 , nls_codepage);
2190 name_len++; /* trailing null */
2191 name_len *= 2;
2192
Steve French50c2f752007-07-13 00:33:32 +00002193 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 name_len = strnlen(fromName, PATH_MAX);
2195 name_len++; /* trailing null */
2196 strncpy(pSMB->FileName, fromName, name_len);
2197 }
2198 params = 6 + name_len;
2199 pSMB->MaxSetupCount = 0;
2200 pSMB->Reserved = 0;
2201 pSMB->Flags = 0;
2202 pSMB->Timeout = 0;
2203 pSMB->Reserved2 = 0;
2204 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002205 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206 offset = param_offset + params;
2207
2208 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2209 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2210 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002211 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 /* find define for this maxpathcomponent */
2213 , nls_codepage);
2214 name_len_target++; /* trailing null */
2215 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002216 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217 name_len_target = strnlen(toName, PATH_MAX);
2218 name_len_target++; /* trailing null */
2219 strncpy(data_offset, toName, name_len_target);
2220 }
2221
2222 pSMB->MaxParameterCount = cpu_to_le16(2);
2223 /* BB find exact max on data count below from sess */
2224 pSMB->MaxDataCount = cpu_to_le16(1000);
2225 pSMB->SetupCount = 1;
2226 pSMB->Reserved3 = 0;
2227 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2228 byte_count = 3 /* pad */ + params + name_len_target;
2229 pSMB->DataCount = cpu_to_le16(name_len_target);
2230 pSMB->ParameterCount = cpu_to_le16(params);
2231 pSMB->TotalDataCount = pSMB->DataCount;
2232 pSMB->TotalParameterCount = pSMB->ParameterCount;
2233 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2234 pSMB->DataOffset = cpu_to_le16(offset);
2235 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2236 pSMB->Reserved4 = 0;
2237 pSMB->hdr.smb_buf_length += byte_count;
2238 pSMB->ByteCount = cpu_to_le16(byte_count);
2239 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2240 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002241 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002242 if (rc)
Steve French2d785a52007-07-15 01:48:57 +00002243 cFYI(1, ("Send error in SetPathInfo create symlink = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244
Steve French0d817bc2008-05-22 02:02:03 +00002245 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246
2247 if (rc == -EAGAIN)
2248 goto createSymLinkRetry;
2249
2250 return rc;
2251}
2252
2253int
2254CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2255 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002256 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257{
2258 TRANSACTION2_SPI_REQ *pSMB = NULL;
2259 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2260 char *data_offset;
2261 int name_len;
2262 int name_len_target;
2263 int rc = 0;
2264 int bytes_returned = 0;
2265 __u16 params, param_offset, offset, byte_count;
2266
2267 cFYI(1, ("In Create Hard link Unix style"));
2268createHardLinkRetry:
2269 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2270 (void **) &pSMBr);
2271 if (rc)
2272 return rc;
2273
2274 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002275 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002276 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 name_len++; /* trailing null */
2278 name_len *= 2;
2279
Steve French50c2f752007-07-13 00:33:32 +00002280 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 name_len = strnlen(toName, PATH_MAX);
2282 name_len++; /* trailing null */
2283 strncpy(pSMB->FileName, toName, name_len);
2284 }
2285 params = 6 + name_len;
2286 pSMB->MaxSetupCount = 0;
2287 pSMB->Reserved = 0;
2288 pSMB->Flags = 0;
2289 pSMB->Timeout = 0;
2290 pSMB->Reserved2 = 0;
2291 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002292 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 offset = param_offset + params;
2294
2295 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2296 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2297 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002298 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002299 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300 name_len_target++; /* trailing null */
2301 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002302 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303 name_len_target = strnlen(fromName, PATH_MAX);
2304 name_len_target++; /* trailing null */
2305 strncpy(data_offset, fromName, name_len_target);
2306 }
2307
2308 pSMB->MaxParameterCount = cpu_to_le16(2);
2309 /* BB find exact max on data count below from sess*/
2310 pSMB->MaxDataCount = cpu_to_le16(1000);
2311 pSMB->SetupCount = 1;
2312 pSMB->Reserved3 = 0;
2313 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2314 byte_count = 3 /* pad */ + params + name_len_target;
2315 pSMB->ParameterCount = cpu_to_le16(params);
2316 pSMB->TotalParameterCount = pSMB->ParameterCount;
2317 pSMB->DataCount = cpu_to_le16(name_len_target);
2318 pSMB->TotalDataCount = pSMB->DataCount;
2319 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2320 pSMB->DataOffset = cpu_to_le16(offset);
2321 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2322 pSMB->Reserved4 = 0;
2323 pSMB->hdr.smb_buf_length += byte_count;
2324 pSMB->ByteCount = cpu_to_le16(byte_count);
2325 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2326 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002327 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002328 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329 cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330
2331 cifs_buf_release(pSMB);
2332 if (rc == -EAGAIN)
2333 goto createHardLinkRetry;
2334
2335 return rc;
2336}
2337
2338int
2339CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2340 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002341 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342{
2343 int rc = 0;
2344 NT_RENAME_REQ *pSMB = NULL;
2345 RENAME_RSP *pSMBr = NULL;
2346 int bytes_returned;
2347 int name_len, name_len2;
2348 __u16 count;
2349
2350 cFYI(1, ("In CIFSCreateHardLink"));
2351winCreateHardLinkRetry:
2352
2353 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2354 (void **) &pSMBr);
2355 if (rc)
2356 return rc;
2357
2358 pSMB->SearchAttributes =
2359 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2360 ATTR_DIRECTORY);
2361 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2362 pSMB->ClusterCount = 0;
2363
2364 pSMB->BufferFormat = 0x04;
2365
2366 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2367 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002368 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002369 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370 name_len++; /* trailing null */
2371 name_len *= 2;
2372 pSMB->OldFileName[name_len] = 0; /* pad */
Steve French50c2f752007-07-13 00:33:32 +00002373 pSMB->OldFileName[name_len + 1] = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002375 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002376 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2378 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002379 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002380 name_len = strnlen(fromName, PATH_MAX);
2381 name_len++; /* trailing null */
2382 strncpy(pSMB->OldFileName, fromName, name_len);
2383 name_len2 = strnlen(toName, PATH_MAX);
2384 name_len2++; /* trailing null */
2385 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2386 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2387 name_len2++; /* trailing null */
2388 name_len2++; /* signature byte */
2389 }
2390
2391 count = 1 /* string type byte */ + name_len + name_len2;
2392 pSMB->hdr.smb_buf_length += count;
2393 pSMB->ByteCount = cpu_to_le16(count);
2394
2395 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2396 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002397 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002398 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399 cFYI(1, ("Send error in hard link (NT rename) = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00002400
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401 cifs_buf_release(pSMB);
2402 if (rc == -EAGAIN)
2403 goto winCreateHardLinkRetry;
2404
2405 return rc;
2406}
2407
2408int
2409CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
2410 const unsigned char *searchName,
2411 char *symlinkinfo, const int buflen,
2412 const struct nls_table *nls_codepage)
2413{
2414/* SMB_QUERY_FILE_UNIX_LINK */
2415 TRANSACTION2_QPI_REQ *pSMB = NULL;
2416 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2417 int rc = 0;
2418 int bytes_returned;
2419 int name_len;
2420 __u16 params, byte_count;
2421
2422 cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName));
2423
2424querySymLinkRetry:
2425 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2426 (void **) &pSMBr);
2427 if (rc)
2428 return rc;
2429
2430 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2431 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002432 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2433 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434 name_len++; /* trailing null */
2435 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002436 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437 name_len = strnlen(searchName, PATH_MAX);
2438 name_len++; /* trailing null */
2439 strncpy(pSMB->FileName, searchName, name_len);
2440 }
2441
2442 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2443 pSMB->TotalDataCount = 0;
2444 pSMB->MaxParameterCount = cpu_to_le16(2);
2445 /* BB find exact max data count below from sess structure BB */
2446 pSMB->MaxDataCount = cpu_to_le16(4000);
2447 pSMB->MaxSetupCount = 0;
2448 pSMB->Reserved = 0;
2449 pSMB->Flags = 0;
2450 pSMB->Timeout = 0;
2451 pSMB->Reserved2 = 0;
2452 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002453 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454 pSMB->DataCount = 0;
2455 pSMB->DataOffset = 0;
2456 pSMB->SetupCount = 1;
2457 pSMB->Reserved3 = 0;
2458 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2459 byte_count = params + 1 /* pad */ ;
2460 pSMB->TotalParameterCount = cpu_to_le16(params);
2461 pSMB->ParameterCount = pSMB->TotalParameterCount;
2462 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2463 pSMB->Reserved4 = 0;
2464 pSMB->hdr.smb_buf_length += byte_count;
2465 pSMB->ByteCount = cpu_to_le16(byte_count);
2466
2467 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2468 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2469 if (rc) {
2470 cFYI(1, ("Send error in QuerySymLinkInfo = %d", rc));
2471 } else {
2472 /* decode response */
2473
2474 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2475 if (rc || (pSMBr->ByteCount < 2))
2476 /* BB also check enough total bytes returned */
2477 rc = -EIO; /* bad smb */
2478 else {
2479 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2480 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2481
2482 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2483 name_len = UniStrnlen((wchar_t *) ((char *)
Steve French50c2f752007-07-13 00:33:32 +00002484 &pSMBr->hdr.Protocol + data_offset),
2485 min_t(const int, buflen, count) / 2);
Steve French737b7582005-04-28 22:41:06 -07002486 /* BB FIXME investigate remapping reserved chars here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002487 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002488 (__le16 *) ((char *)&pSMBr->hdr.Protocol
2489 + data_offset),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490 name_len, nls_codepage);
2491 } else {
2492 strncpy(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002493 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002494 data_offset,
2495 min_t(const int, buflen, count));
2496 }
2497 symlinkinfo[buflen] = 0;
2498 /* just in case so calling code does not go off the end of buffer */
2499 }
2500 }
2501 cifs_buf_release(pSMB);
2502 if (rc == -EAGAIN)
2503 goto querySymLinkRetry;
2504 return rc;
2505}
2506
Parag Warudkarc9489772007-10-23 18:09:48 +00002507#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08002508/* Initialize NT TRANSACT SMB into small smb request buffer.
2509 This assumes that all NT TRANSACTS that we init here have
2510 total parm and data under about 400 bytes (to fit in small cifs
2511 buffer size), which is the case so far, it easily fits. NB:
2512 Setup words themselves and ByteCount
2513 MaxSetupCount (size of returned setup area) and
2514 MaxParameterCount (returned parms size) must be set by caller */
Steve French50c2f752007-07-13 00:33:32 +00002515static int
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002516smb_init_nttransact(const __u16 sub_command, const int setup_count,
Steve French0a4b92c2006-01-12 15:44:21 -08002517 const int parm_len, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002518 void **ret_buf)
Steve French0a4b92c2006-01-12 15:44:21 -08002519{
2520 int rc;
2521 __u32 temp_offset;
Steve French50c2f752007-07-13 00:33:32 +00002522 struct smb_com_ntransact_req *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08002523
2524 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
2525 (void **)&pSMB);
2526 if (rc)
2527 return rc;
2528 *ret_buf = (void *)pSMB;
2529 pSMB->Reserved = 0;
2530 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
2531 pSMB->TotalDataCount = 0;
2532 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2533 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
2534 pSMB->ParameterCount = pSMB->TotalParameterCount;
2535 pSMB->DataCount = pSMB->TotalDataCount;
2536 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
2537 (setup_count * 2) - 4 /* for rfc1001 length itself */;
2538 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
2539 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
2540 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
2541 pSMB->SubCommand = cpu_to_le16(sub_command);
2542 return 0;
2543}
2544
2545static int
Steve French50c2f752007-07-13 00:33:32 +00002546validate_ntransact(char *buf, char **ppparm, char **ppdata,
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002547 __u32 *pparmlen, __u32 *pdatalen)
Steve French0a4b92c2006-01-12 15:44:21 -08002548{
Steve French50c2f752007-07-13 00:33:32 +00002549 char *end_of_smb;
Steve French0a4b92c2006-01-12 15:44:21 -08002550 __u32 data_count, data_offset, parm_count, parm_offset;
Steve French50c2f752007-07-13 00:33:32 +00002551 struct smb_com_ntransact_rsp *pSMBr;
Steve French0a4b92c2006-01-12 15:44:21 -08002552
Steve French630f3f0c2007-10-25 21:17:17 +00002553 *pdatalen = 0;
2554 *pparmlen = 0;
2555
Steve French790fe572007-07-07 19:25:05 +00002556 if (buf == NULL)
Steve French0a4b92c2006-01-12 15:44:21 -08002557 return -EINVAL;
2558
2559 pSMBr = (struct smb_com_ntransact_rsp *)buf;
2560
2561 /* ByteCount was converted from little endian in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +00002562 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
Steve French0a4b92c2006-01-12 15:44:21 -08002563 (char *)&pSMBr->ByteCount;
2564
Steve French0a4b92c2006-01-12 15:44:21 -08002565 data_offset = le32_to_cpu(pSMBr->DataOffset);
2566 data_count = le32_to_cpu(pSMBr->DataCount);
Steve French50c2f752007-07-13 00:33:32 +00002567 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
Steve French0a4b92c2006-01-12 15:44:21 -08002568 parm_count = le32_to_cpu(pSMBr->ParameterCount);
2569
2570 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
2571 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
2572
2573 /* should we also check that parm and data areas do not overlap? */
Steve French790fe572007-07-07 19:25:05 +00002574 if (*ppparm > end_of_smb) {
2575 cFYI(1, ("parms start after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002576 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002577 } else if (parm_count + *ppparm > end_of_smb) {
2578 cFYI(1, ("parm end after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002579 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002580 } else if (*ppdata > end_of_smb) {
2581 cFYI(1, ("data starts after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002582 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002583 } else if (data_count + *ppdata > end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002584 cFYI(1, ("data %p + count %d (%p) ends after end of smb %p start %p",
Steve French50c2f752007-07-13 00:33:32 +00002585 *ppdata, data_count, (data_count + *ppdata),
2586 end_of_smb, pSMBr));
Steve French0a4b92c2006-01-12 15:44:21 -08002587 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002588 } else if (parm_count + data_count > pSMBr->ByteCount) {
2589 cFYI(1, ("parm count and data count larger than SMB"));
Steve French0a4b92c2006-01-12 15:44:21 -08002590 return -EINVAL;
2591 }
Steve French630f3f0c2007-10-25 21:17:17 +00002592 *pdatalen = data_count;
2593 *pparmlen = parm_count;
Steve French0a4b92c2006-01-12 15:44:21 -08002594 return 0;
2595}
Parag Warudkarc9489772007-10-23 18:09:48 +00002596#endif /* CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08002597
Linus Torvalds1da177e2005-04-16 15:20:36 -07002598int
2599CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2600 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002601 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602 const struct nls_table *nls_codepage)
2603{
2604 int rc = 0;
2605 int bytes_returned;
2606 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00002607 struct smb_com_transaction_ioctl_req *pSMB;
2608 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609
2610 cFYI(1, ("In Windows reparse style QueryLink for path %s", searchName));
2611 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2612 (void **) &pSMBr);
2613 if (rc)
2614 return rc;
2615
2616 pSMB->TotalParameterCount = 0 ;
2617 pSMB->TotalDataCount = 0;
2618 pSMB->MaxParameterCount = cpu_to_le32(2);
2619 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002620 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2621 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 pSMB->MaxSetupCount = 4;
2623 pSMB->Reserved = 0;
2624 pSMB->ParameterOffset = 0;
2625 pSMB->DataCount = 0;
2626 pSMB->DataOffset = 0;
2627 pSMB->SetupCount = 4;
2628 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2629 pSMB->ParameterCount = pSMB->TotalParameterCount;
2630 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2631 pSMB->IsFsctl = 1; /* FSCTL */
2632 pSMB->IsRootFlag = 0;
2633 pSMB->Fid = fid; /* file handle always le */
2634 pSMB->ByteCount = 0;
2635
2636 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2637 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2638 if (rc) {
2639 cFYI(1, ("Send error in QueryReparseLinkInfo = %d", rc));
2640 } else { /* decode response */
2641 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2642 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
2643 if ((pSMBr->ByteCount < 2) || (data_offset > 512))
2644 /* BB also check enough total bytes returned */
2645 rc = -EIO; /* bad smb */
2646 else {
Steve French790fe572007-07-07 19:25:05 +00002647 if (data_count && (data_count < 2048)) {
Steve French50c2f752007-07-13 00:33:32 +00002648 char *end_of_smb = 2 /* sizeof byte count */ +
Steve French0a4b92c2006-01-12 15:44:21 -08002649 pSMBr->ByteCount +
2650 (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651
Steve French50c2f752007-07-13 00:33:32 +00002652 struct reparse_data *reparse_buf =
2653 (struct reparse_data *)
2654 ((char *)&pSMBr->hdr.Protocol
2655 + data_offset);
Steve French790fe572007-07-07 19:25:05 +00002656 if ((char *)reparse_buf >= end_of_smb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657 rc = -EIO;
2658 goto qreparse_out;
2659 }
Steve French790fe572007-07-07 19:25:05 +00002660 if ((reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661 reparse_buf->TargetNameOffset +
2662 reparse_buf->TargetNameLen) >
2663 end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002664 cFYI(1, ("reparse buf beyond SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665 rc = -EIO;
2666 goto qreparse_out;
2667 }
Steve French50c2f752007-07-13 00:33:32 +00002668
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2670 name_len = UniStrnlen((wchar_t *)
Steve French50c2f752007-07-13 00:33:32 +00002671 (reparse_buf->LinkNamesBuf +
2672 reparse_buf->TargetNameOffset),
2673 min(buflen/2,
2674 reparse_buf->TargetNameLen / 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002676 (__le16 *) (reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677 reparse_buf->TargetNameOffset),
2678 name_len, nls_codepage);
2679 } else { /* ASCII names */
Steve French50c2f752007-07-13 00:33:32 +00002680 strncpy(symlinkinfo,
2681 reparse_buf->LinkNamesBuf +
2682 reparse_buf->TargetNameOffset,
2683 min_t(const int, buflen,
2684 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002685 }
2686 } else {
2687 rc = -EIO;
Steve French63135e02007-07-17 17:34:02 +00002688 cFYI(1, ("Invalid return data count on "
2689 "get reparse info ioctl"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690 }
2691 symlinkinfo[buflen] = 0; /* just in case so the caller
2692 does not go off the end of the buffer */
Steve French50c2f752007-07-13 00:33:32 +00002693 cFYI(1, ("readlink result - %s", symlinkinfo));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694 }
2695 }
2696qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002697 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698
2699 /* Note: On -EAGAIN error only caller can retry on handle based calls
2700 since file handle passed in no longer valid */
2701
2702 return rc;
2703}
2704
2705#ifdef CONFIG_CIFS_POSIX
2706
2707/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002708static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2709 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710{
2711 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002712 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2713 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2714 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715 /* cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id)); */
2716
2717 return;
2718}
2719
2720/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002721static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2722 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723{
2724 int size = 0;
2725 int i;
2726 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002727 struct cifs_posix_ace *pACE;
2728 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2729 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730
2731 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2732 return -EOPNOTSUPP;
2733
Steve French790fe572007-07-07 19:25:05 +00002734 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735 count = le16_to_cpu(cifs_acl->access_entry_count);
2736 pACE = &cifs_acl->ace_array[0];
2737 size = sizeof(struct cifs_posix_acl);
2738 size += sizeof(struct cifs_posix_ace) * count;
2739 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002740 if (size_of_data_area < size) {
Steve French50c2f752007-07-13 00:33:32 +00002741 cFYI(1, ("bad CIFS POSIX ACL size %d vs. %d",
2742 size_of_data_area, size));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743 return -EINVAL;
2744 }
Steve French790fe572007-07-07 19:25:05 +00002745 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746 count = le16_to_cpu(cifs_acl->access_entry_count);
2747 size = sizeof(struct cifs_posix_acl);
2748 size += sizeof(struct cifs_posix_ace) * count;
2749/* skip past access ACEs to get to default ACEs */
2750 pACE = &cifs_acl->ace_array[count];
2751 count = le16_to_cpu(cifs_acl->default_entry_count);
2752 size += sizeof(struct cifs_posix_ace) * count;
2753 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002754 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755 return -EINVAL;
2756 } else {
2757 /* illegal type */
2758 return -EINVAL;
2759 }
2760
2761 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002762 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002763 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002764 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 return -ERANGE;
2766 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002767 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002768 for (i = 0; i < count ; i++) {
2769 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2770 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 }
2772 }
2773 return size;
2774}
2775
Steve French50c2f752007-07-13 00:33:32 +00002776static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2777 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778{
2779 __u16 rc = 0; /* 0 = ACL converted ok */
2780
Steve Frenchff7feac2005-11-15 16:45:16 -08002781 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2782 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002784 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 /* Probably no need to le convert -1 on any arch but can not hurt */
2786 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002787 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002788 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Steve French50c2f752007-07-13 00:33:32 +00002789 /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 return rc;
2791}
2792
2793/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002794static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2795 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796{
2797 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002798 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2799 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 int count;
2801 int i;
2802
Steve French790fe572007-07-07 19:25:05 +00002803 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 return 0;
2805
2806 count = posix_acl_xattr_count((size_t)buflen);
Steve Frenchc18c8422007-07-18 23:21:09 +00002807 cFYI(1, ("setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002808 "version of %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002809 count, buflen, le32_to_cpu(local_acl->a_version)));
Steve French790fe572007-07-07 19:25:05 +00002810 if (le32_to_cpu(local_acl->a_version) != 2) {
Steve French50c2f752007-07-13 00:33:32 +00002811 cFYI(1, ("unknown POSIX ACL version %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002812 le32_to_cpu(local_acl->a_version)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813 return 0;
2814 }
2815 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002816 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002817 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002818 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002819 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820 else {
Steve French50c2f752007-07-13 00:33:32 +00002821 cFYI(1, ("unknown ACL type %d", acl_type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 return 0;
2823 }
Steve French50c2f752007-07-13 00:33:32 +00002824 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2826 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002827 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 /* ACE not converted */
2829 break;
2830 }
2831 }
Steve French790fe572007-07-07 19:25:05 +00002832 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2834 rc += sizeof(struct cifs_posix_acl);
2835 /* BB add check to make sure ACL does not overflow SMB */
2836 }
2837 return rc;
2838}
2839
2840int
2841CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002842 const unsigned char *searchName,
2843 char *acl_inf, const int buflen, const int acl_type,
2844 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002845{
2846/* SMB_QUERY_POSIX_ACL */
2847 TRANSACTION2_QPI_REQ *pSMB = NULL;
2848 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2849 int rc = 0;
2850 int bytes_returned;
2851 int name_len;
2852 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002853
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854 cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName));
2855
2856queryAclRetry:
2857 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2858 (void **) &pSMBr);
2859 if (rc)
2860 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002861
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2863 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002864 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002865 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 name_len++; /* trailing null */
2867 name_len *= 2;
2868 pSMB->FileName[name_len] = 0;
2869 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002870 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871 name_len = strnlen(searchName, PATH_MAX);
2872 name_len++; /* trailing null */
2873 strncpy(pSMB->FileName, searchName, name_len);
2874 }
2875
2876 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2877 pSMB->TotalDataCount = 0;
2878 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002879 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880 pSMB->MaxDataCount = cpu_to_le16(4000);
2881 pSMB->MaxSetupCount = 0;
2882 pSMB->Reserved = 0;
2883 pSMB->Flags = 0;
2884 pSMB->Timeout = 0;
2885 pSMB->Reserved2 = 0;
2886 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002887 offsetof(struct smb_com_transaction2_qpi_req,
2888 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889 pSMB->DataCount = 0;
2890 pSMB->DataOffset = 0;
2891 pSMB->SetupCount = 1;
2892 pSMB->Reserved3 = 0;
2893 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2894 byte_count = params + 1 /* pad */ ;
2895 pSMB->TotalParameterCount = cpu_to_le16(params);
2896 pSMB->ParameterCount = pSMB->TotalParameterCount;
2897 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2898 pSMB->Reserved4 = 0;
2899 pSMB->hdr.smb_buf_length += byte_count;
2900 pSMB->ByteCount = cpu_to_le16(byte_count);
2901
2902 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2903 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002904 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002905 if (rc) {
2906 cFYI(1, ("Send error in Query POSIX ACL = %d", rc));
2907 } else {
2908 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002909
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2911 if (rc || (pSMBr->ByteCount < 2))
2912 /* BB also check enough total bytes returned */
2913 rc = -EIO; /* bad smb */
2914 else {
2915 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2916 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2917 rc = cifs_copy_posix_acl(acl_inf,
2918 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002919 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002920 }
2921 }
2922 cifs_buf_release(pSMB);
2923 if (rc == -EAGAIN)
2924 goto queryAclRetry;
2925 return rc;
2926}
2927
2928int
2929CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002930 const unsigned char *fileName,
2931 const char *local_acl, const int buflen,
2932 const int acl_type,
2933 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002934{
2935 struct smb_com_transaction2_spi_req *pSMB = NULL;
2936 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2937 char *parm_data;
2938 int name_len;
2939 int rc = 0;
2940 int bytes_returned = 0;
2941 __u16 params, byte_count, data_count, param_offset, offset;
2942
2943 cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName));
2944setAclRetry:
2945 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002946 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002947 if (rc)
2948 return rc;
2949 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2950 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002951 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002952 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953 name_len++; /* trailing null */
2954 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002955 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002956 name_len = strnlen(fileName, PATH_MAX);
2957 name_len++; /* trailing null */
2958 strncpy(pSMB->FileName, fileName, name_len);
2959 }
2960 params = 6 + name_len;
2961 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00002962 /* BB find max SMB size from sess */
2963 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964 pSMB->MaxSetupCount = 0;
2965 pSMB->Reserved = 0;
2966 pSMB->Flags = 0;
2967 pSMB->Timeout = 0;
2968 pSMB->Reserved2 = 0;
2969 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002970 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002971 offset = param_offset + params;
2972 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2973 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2974
2975 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002976 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002977
Steve French790fe572007-07-07 19:25:05 +00002978 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979 rc = -EOPNOTSUPP;
2980 goto setACLerrorExit;
2981 }
2982 pSMB->DataOffset = cpu_to_le16(offset);
2983 pSMB->SetupCount = 1;
2984 pSMB->Reserved3 = 0;
2985 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2986 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2987 byte_count = 3 /* pad */ + params + data_count;
2988 pSMB->DataCount = cpu_to_le16(data_count);
2989 pSMB->TotalDataCount = pSMB->DataCount;
2990 pSMB->ParameterCount = cpu_to_le16(params);
2991 pSMB->TotalParameterCount = pSMB->ParameterCount;
2992 pSMB->Reserved4 = 0;
2993 pSMB->hdr.smb_buf_length += byte_count;
2994 pSMB->ByteCount = cpu_to_le16(byte_count);
2995 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002996 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00002997 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002998 cFYI(1, ("Set POSIX ACL returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002999
3000setACLerrorExit:
3001 cifs_buf_release(pSMB);
3002 if (rc == -EAGAIN)
3003 goto setAclRetry;
3004 return rc;
3005}
3006
Steve Frenchf654bac2005-04-28 22:41:04 -07003007/* BB fix tabs in this function FIXME BB */
3008int
3009CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00003010 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07003011{
Steve French50c2f752007-07-13 00:33:32 +00003012 int rc = 0;
3013 struct smb_t2_qfi_req *pSMB = NULL;
3014 struct smb_t2_qfi_rsp *pSMBr = NULL;
3015 int bytes_returned;
3016 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07003017
Steve French790fe572007-07-07 19:25:05 +00003018 cFYI(1, ("In GetExtAttr"));
3019 if (tcon == NULL)
3020 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07003021
3022GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00003023 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3024 (void **) &pSMBr);
3025 if (rc)
3026 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003027
Steve Frenchad7a2922008-02-07 23:25:02 +00003028 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00003029 pSMB->t2.TotalDataCount = 0;
3030 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3031 /* BB find exact max data count below from sess structure BB */
3032 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
3033 pSMB->t2.MaxSetupCount = 0;
3034 pSMB->t2.Reserved = 0;
3035 pSMB->t2.Flags = 0;
3036 pSMB->t2.Timeout = 0;
3037 pSMB->t2.Reserved2 = 0;
3038 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3039 Fid) - 4);
3040 pSMB->t2.DataCount = 0;
3041 pSMB->t2.DataOffset = 0;
3042 pSMB->t2.SetupCount = 1;
3043 pSMB->t2.Reserved3 = 0;
3044 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3045 byte_count = params + 1 /* pad */ ;
3046 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3047 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3048 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
3049 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07003050 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00003051 pSMB->hdr.smb_buf_length += byte_count;
3052 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07003053
Steve French790fe572007-07-07 19:25:05 +00003054 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3055 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3056 if (rc) {
3057 cFYI(1, ("error %d in GetExtAttr", rc));
3058 } else {
3059 /* decode response */
3060 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3061 if (rc || (pSMBr->ByteCount < 2))
3062 /* BB also check enough total bytes returned */
3063 /* If rc should we check for EOPNOSUPP and
3064 disable the srvino flag? or in caller? */
3065 rc = -EIO; /* bad smb */
3066 else {
3067 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3068 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
3069 struct file_chattr_info *pfinfo;
3070 /* BB Do we need a cast or hash here ? */
3071 if (count != 16) {
3072 cFYI(1, ("Illegal size ret in GetExtAttr"));
3073 rc = -EIO;
3074 goto GetExtAttrOut;
3075 }
3076 pfinfo = (struct file_chattr_info *)
3077 (data_offset + (char *) &pSMBr->hdr.Protocol);
3078 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07003079 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00003080 }
3081 }
Steve Frenchf654bac2005-04-28 22:41:04 -07003082GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00003083 cifs_buf_release(pSMB);
3084 if (rc == -EAGAIN)
3085 goto GetExtAttrRetry;
3086 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003087}
3088
Steve Frenchf654bac2005-04-28 22:41:04 -07003089#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003090
Steve French297647c2007-10-12 04:11:59 +00003091#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08003092/* Get Security Descriptor (by handle) from remote server for a file or dir */
3093int
3094CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f0c2007-10-25 21:17:17 +00003095 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003096{
3097 int rc = 0;
3098 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003099 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003100 struct kvec iov[1];
3101
3102 cFYI(1, ("GetCifsACL"));
3103
Steve French630f3f0c2007-10-25 21:17:17 +00003104 *pbuflen = 0;
3105 *acl_inf = NULL;
3106
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003107 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003108 8 /* parm len */, tcon, (void **) &pSMB);
3109 if (rc)
3110 return rc;
3111
3112 pSMB->MaxParameterCount = cpu_to_le32(4);
3113 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3114 pSMB->MaxSetupCount = 0;
3115 pSMB->Fid = fid; /* file handle always le */
3116 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3117 CIFS_ACL_DACL);
3118 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3119 pSMB->hdr.smb_buf_length += 11;
3120 iov[0].iov_base = (char *)pSMB;
3121 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3122
Steve Frencha761ac52007-10-18 21:45:27 +00003123 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Steve French133672e2007-11-13 22:41:37 +00003124 CIFS_STD_OP);
Steve French0a4b92c2006-01-12 15:44:21 -08003125 cifs_stats_inc(&tcon->num_acl_get);
3126 if (rc) {
3127 cFYI(1, ("Send error in QuerySecDesc = %d", rc));
3128 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003129 __le32 *parm;
Steve French630f3f0c2007-10-25 21:17:17 +00003130 __u32 parm_len;
3131 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003132 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f0c2007-10-25 21:17:17 +00003133 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003134
3135/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003136 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f0c2007-10-25 21:17:17 +00003137 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003138 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003139 goto qsec_out;
3140 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3141
Steve French630f3f0c2007-10-25 21:17:17 +00003142 cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, *acl_inf));
Steve French0a4b92c2006-01-12 15:44:21 -08003143
3144 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3145 rc = -EIO; /* bad smb */
Steve French630f3f0c2007-10-25 21:17:17 +00003146 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003147 goto qsec_out;
3148 }
3149
3150/* BB check that data area is minimum length and as big as acl_len */
3151
Steve Frenchaf6f4612007-10-16 18:40:37 +00003152 acl_len = le32_to_cpu(*parm);
Steve French630f3f0c2007-10-25 21:17:17 +00003153 if (acl_len != *pbuflen) {
3154 cERROR(1, ("acl length %d does not match %d",
3155 acl_len, *pbuflen));
3156 if (*pbuflen > acl_len)
3157 *pbuflen = acl_len;
3158 }
Steve French0a4b92c2006-01-12 15:44:21 -08003159
Steve French630f3f0c2007-10-25 21:17:17 +00003160 /* check if buffer is big enough for the acl
3161 header followed by the smallest SID */
3162 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3163 (*pbuflen >= 64 * 1024)) {
3164 cERROR(1, ("bad acl length %d", *pbuflen));
3165 rc = -EINVAL;
3166 *pbuflen = 0;
3167 } else {
3168 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3169 if (*acl_inf == NULL) {
3170 *pbuflen = 0;
3171 rc = -ENOMEM;
3172 }
3173 memcpy(*acl_inf, pdata, *pbuflen);
3174 }
Steve French0a4b92c2006-01-12 15:44:21 -08003175 }
3176qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003177 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003178 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003179 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003180 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003181/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003182 return rc;
3183}
Steve French97837582007-12-31 07:47:21 +00003184
3185int
3186CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3187 struct cifs_ntsd *pntsd, __u32 acllen)
3188{
3189 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3190 int rc = 0;
3191 int bytes_returned = 0;
3192 SET_SEC_DESC_REQ *pSMB = NULL;
3193 NTRANSACT_RSP *pSMBr = NULL;
3194
3195setCifsAclRetry:
3196 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3197 (void **) &pSMBr);
3198 if (rc)
3199 return (rc);
3200
3201 pSMB->MaxSetupCount = 0;
3202 pSMB->Reserved = 0;
3203
3204 param_count = 8;
3205 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3206 data_count = acllen;
3207 data_offset = param_offset + param_count;
3208 byte_count = 3 /* pad */ + param_count;
3209
3210 pSMB->DataCount = cpu_to_le32(data_count);
3211 pSMB->TotalDataCount = pSMB->DataCount;
3212 pSMB->MaxParameterCount = cpu_to_le32(4);
3213 pSMB->MaxDataCount = cpu_to_le32(16384);
3214 pSMB->ParameterCount = cpu_to_le32(param_count);
3215 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3216 pSMB->TotalParameterCount = pSMB->ParameterCount;
3217 pSMB->DataOffset = cpu_to_le32(data_offset);
3218 pSMB->SetupCount = 0;
3219 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3220 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3221
3222 pSMB->Fid = fid; /* file handle always le */
3223 pSMB->Reserved2 = 0;
3224 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3225
3226 if (pntsd && acllen) {
3227 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3228 (char *) pntsd,
3229 acllen);
3230 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3231
3232 } else
3233 pSMB->hdr.smb_buf_length += byte_count;
3234
3235 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3236 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3237
3238 cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc));
3239 if (rc)
3240 cFYI(1, ("Set CIFS ACL returned %d", rc));
3241 cifs_buf_release(pSMB);
3242
3243 if (rc == -EAGAIN)
3244 goto setCifsAclRetry;
3245
3246 return (rc);
3247}
3248
Steve French297647c2007-10-12 04:11:59 +00003249#endif /* CONFIG_CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08003250
Steve French6b8edfe2005-08-23 20:26:03 -07003251/* Legacy Query Path Information call for lookup to old servers such
3252 as Win9x/WinME */
3253int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003254 const unsigned char *searchName,
3255 FILE_ALL_INFO *pFinfo,
3256 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003257{
Steve Frenchad7a2922008-02-07 23:25:02 +00003258 QUERY_INFORMATION_REQ *pSMB;
3259 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003260 int rc = 0;
3261 int bytes_returned;
3262 int name_len;
3263
Steve French50c2f752007-07-13 00:33:32 +00003264 cFYI(1, ("In SMBQPath path %s", searchName));
Steve French6b8edfe2005-08-23 20:26:03 -07003265QInfRetry:
3266 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003267 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003268 if (rc)
3269 return rc;
3270
3271 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3272 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003273 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3274 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003275 name_len++; /* trailing null */
3276 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003277 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003278 name_len = strnlen(searchName, PATH_MAX);
3279 name_len++; /* trailing null */
3280 strncpy(pSMB->FileName, searchName, name_len);
3281 }
3282 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003283 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003284 pSMB->hdr.smb_buf_length += (__u16) name_len;
3285 pSMB->ByteCount = cpu_to_le16(name_len);
3286
3287 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003288 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003289 if (rc) {
3290 cFYI(1, ("Send error in QueryInfo = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003291 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003292 struct timespec ts;
3293 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003294
3295 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003296 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003297 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003298 ts.tv_nsec = 0;
3299 ts.tv_sec = time;
3300 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003301 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003302 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3303 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003304 pFinfo->AllocationSize =
3305 cpu_to_le64(le32_to_cpu(pSMBr->size));
3306 pFinfo->EndOfFile = pFinfo->AllocationSize;
3307 pFinfo->Attributes =
3308 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003309 } else
3310 rc = -EIO; /* bad buffer passed in */
3311
3312 cifs_buf_release(pSMB);
3313
3314 if (rc == -EAGAIN)
3315 goto QInfRetry;
3316
3317 return rc;
3318}
3319
3320
3321
3322
Linus Torvalds1da177e2005-04-16 15:20:36 -07003323int
3324CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3325 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003326 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003327 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003328 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003329{
3330/* level 263 SMB_QUERY_FILE_ALL_INFO */
3331 TRANSACTION2_QPI_REQ *pSMB = NULL;
3332 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3333 int rc = 0;
3334 int bytes_returned;
3335 int name_len;
3336 __u16 params, byte_count;
3337
3338/* cFYI(1, ("In QPathInfo path %s", searchName)); */
3339QPathInfoRetry:
3340 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3341 (void **) &pSMBr);
3342 if (rc)
3343 return rc;
3344
3345 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3346 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003347 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003348 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003349 name_len++; /* trailing null */
3350 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003351 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003352 name_len = strnlen(searchName, PATH_MAX);
3353 name_len++; /* trailing null */
3354 strncpy(pSMB->FileName, searchName, name_len);
3355 }
3356
Steve French50c2f752007-07-13 00:33:32 +00003357 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003358 pSMB->TotalDataCount = 0;
3359 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00003360 /* BB find exact max SMB PDU from sess structure BB */
3361 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003362 pSMB->MaxSetupCount = 0;
3363 pSMB->Reserved = 0;
3364 pSMB->Flags = 0;
3365 pSMB->Timeout = 0;
3366 pSMB->Reserved2 = 0;
3367 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003368 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003369 pSMB->DataCount = 0;
3370 pSMB->DataOffset = 0;
3371 pSMB->SetupCount = 1;
3372 pSMB->Reserved3 = 0;
3373 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3374 byte_count = params + 1 /* pad */ ;
3375 pSMB->TotalParameterCount = cpu_to_le16(params);
3376 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003377 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003378 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3379 else
3380 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003381 pSMB->Reserved4 = 0;
3382 pSMB->hdr.smb_buf_length += byte_count;
3383 pSMB->ByteCount = cpu_to_le16(byte_count);
3384
3385 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3386 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3387 if (rc) {
3388 cFYI(1, ("Send error in QPathInfo = %d", rc));
3389 } else { /* decode response */
3390 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3391
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003392 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3393 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003394 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003395 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003396 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003397 rc = -EIO; /* 24 or 26 expected but we do not read
3398 last field */
3399 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003400 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003401 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003402
3403 /* On legacy responses we do not read the last field,
3404 EAsize, fortunately since it varies by subdialect and
3405 also note it differs on Set vs. Get, ie two bytes or 4
3406 bytes depending but we don't care here */
3407 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003408 size = sizeof(FILE_INFO_STANDARD);
3409 else
3410 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003411 memcpy((char *) pFindData,
3412 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003413 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003414 } else
3415 rc = -ENOMEM;
3416 }
3417 cifs_buf_release(pSMB);
3418 if (rc == -EAGAIN)
3419 goto QPathInfoRetry;
3420
3421 return rc;
3422}
3423
3424int
3425CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3426 const unsigned char *searchName,
Steve French582d21e2008-05-13 04:54:12 +00003427 FILE_UNIX_BASIC_INFO *pFindData,
Steve French737b7582005-04-28 22:41:06 -07003428 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003429{
3430/* SMB_QUERY_FILE_UNIX_BASIC */
3431 TRANSACTION2_QPI_REQ *pSMB = NULL;
3432 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3433 int rc = 0;
3434 int bytes_returned = 0;
3435 int name_len;
3436 __u16 params, byte_count;
3437
3438 cFYI(1, ("In QPathInfo (Unix) the path %s", searchName));
3439UnixQPathInfoRetry:
3440 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3441 (void **) &pSMBr);
3442 if (rc)
3443 return rc;
3444
3445 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3446 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003447 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003448 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003449 name_len++; /* trailing null */
3450 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003451 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003452 name_len = strnlen(searchName, PATH_MAX);
3453 name_len++; /* trailing null */
3454 strncpy(pSMB->FileName, searchName, name_len);
3455 }
3456
Steve French50c2f752007-07-13 00:33:32 +00003457 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003458 pSMB->TotalDataCount = 0;
3459 pSMB->MaxParameterCount = cpu_to_le16(2);
3460 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003461 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003462 pSMB->MaxSetupCount = 0;
3463 pSMB->Reserved = 0;
3464 pSMB->Flags = 0;
3465 pSMB->Timeout = 0;
3466 pSMB->Reserved2 = 0;
3467 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003468 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003469 pSMB->DataCount = 0;
3470 pSMB->DataOffset = 0;
3471 pSMB->SetupCount = 1;
3472 pSMB->Reserved3 = 0;
3473 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3474 byte_count = params + 1 /* pad */ ;
3475 pSMB->TotalParameterCount = cpu_to_le16(params);
3476 pSMB->ParameterCount = pSMB->TotalParameterCount;
3477 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3478 pSMB->Reserved4 = 0;
3479 pSMB->hdr.smb_buf_length += byte_count;
3480 pSMB->ByteCount = cpu_to_le16(byte_count);
3481
3482 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3483 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3484 if (rc) {
3485 cFYI(1, ("Send error in QPathInfo = %d", rc));
3486 } else { /* decode response */
3487 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3488
3489 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve French1e71f252007-09-20 15:30:07 +00003490 cERROR(1, ("Malformed FILE_UNIX_BASIC_INFO response.\n"
3491 "Unix Extensions can be disabled on mount "
3492 "by specifying the nosfu mount option."));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003493 rc = -EIO; /* bad smb */
3494 } else {
3495 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3496 memcpy((char *) pFindData,
3497 (char *) &pSMBr->hdr.Protocol +
3498 data_offset,
Steve French630f3f0c2007-10-25 21:17:17 +00003499 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003500 }
3501 }
3502 cifs_buf_release(pSMB);
3503 if (rc == -EAGAIN)
3504 goto UnixQPathInfoRetry;
3505
3506 return rc;
3507}
3508
Linus Torvalds1da177e2005-04-16 15:20:36 -07003509/* xid, tcon, searchName and codepage are input parms, rest are returned */
3510int
3511CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003512 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003513 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003514 __u16 *pnetfid,
3515 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516{
3517/* level 257 SMB_ */
3518 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3519 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003520 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003521 int rc = 0;
3522 int bytes_returned = 0;
3523 int name_len;
3524 __u16 params, byte_count;
3525
Steve French50c2f752007-07-13 00:33:32 +00003526 cFYI(1, ("In FindFirst for %s", searchName));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003527
3528findFirstRetry:
3529 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3530 (void **) &pSMBr);
3531 if (rc)
3532 return rc;
3533
3534 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3535 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003536 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003537 PATH_MAX, nls_codepage, remap);
3538 /* We can not add the asterik earlier in case
3539 it got remapped to 0xF03A as if it were part of the
3540 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003541 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003542 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003543 pSMB->FileName[name_len+1] = 0;
3544 pSMB->FileName[name_len+2] = '*';
3545 pSMB->FileName[name_len+3] = 0;
3546 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003547 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3548 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003549 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003550 } else { /* BB add check for overrun of SMB buf BB */
3551 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003552/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003553 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003554 free buffer exit; BB */
3555 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003556 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003557 pSMB->FileName[name_len+1] = '*';
3558 pSMB->FileName[name_len+2] = 0;
3559 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003560 }
3561
3562 params = 12 + name_len /* includes null */ ;
3563 pSMB->TotalDataCount = 0; /* no EAs */
3564 pSMB->MaxParameterCount = cpu_to_le16(10);
3565 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3566 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3567 pSMB->MaxSetupCount = 0;
3568 pSMB->Reserved = 0;
3569 pSMB->Flags = 0;
3570 pSMB->Timeout = 0;
3571 pSMB->Reserved2 = 0;
3572 byte_count = params + 1 /* pad */ ;
3573 pSMB->TotalParameterCount = cpu_to_le16(params);
3574 pSMB->ParameterCount = pSMB->TotalParameterCount;
3575 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003576 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3577 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003578 pSMB->DataCount = 0;
3579 pSMB->DataOffset = 0;
3580 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3581 pSMB->Reserved3 = 0;
3582 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3583 pSMB->SearchAttributes =
3584 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3585 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003586 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3587 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003588 CIFS_SEARCH_RETURN_RESUME);
3589 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3590
3591 /* BB what should we set StorageType to? Does it matter? BB */
3592 pSMB->SearchStorageType = 0;
3593 pSMB->hdr.smb_buf_length += byte_count;
3594 pSMB->ByteCount = cpu_to_le16(byte_count);
3595
3596 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3597 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003598 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003599
Steve French88274812006-03-09 22:21:45 +00003600 if (rc) {/* BB add logic to retry regular search if Unix search
3601 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003602 /* BB Add code to handle unsupported level rc */
3603 cFYI(1, ("Error in FindFirst = %d", rc));
Steve French1982c342005-08-17 12:38:22 -07003604
Steve French88274812006-03-09 22:21:45 +00003605 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003606
3607 /* BB eventually could optimize out free and realloc of buf */
3608 /* for this case */
3609 if (rc == -EAGAIN)
3610 goto findFirstRetry;
3611 } else { /* decode response */
3612 /* BB remember to free buffer if error BB */
3613 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003614 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003615 unsigned int lnoff;
3616
Linus Torvalds1da177e2005-04-16 15:20:36 -07003617 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003618 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003619 else
Steve French4b18f2a2008-04-29 00:06:05 +00003620 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003621
3622 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003623 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003624 psrch_inf->srch_entries_start =
3625 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003626 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003627 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3628 le16_to_cpu(pSMBr->t2.ParameterOffset));
3629
Steve French790fe572007-07-07 19:25:05 +00003630 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003631 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003632 else
Steve French4b18f2a2008-04-29 00:06:05 +00003633 psrch_inf->endOfSearch = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003634
Steve French50c2f752007-07-13 00:33:32 +00003635 psrch_inf->entries_in_buffer =
3636 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003637 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003638 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003639 lnoff = le16_to_cpu(parms->LastNameOffset);
3640 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3641 lnoff) {
3642 cERROR(1, ("ignoring corrupt resume name"));
3643 psrch_inf->last_entry = NULL;
3644 return rc;
3645 }
3646
Steve French0752f152008-10-07 20:03:33 +00003647 psrch_inf->last_entry = psrch_inf->srch_entries_start +
Steve Frenchb77d7532008-10-08 19:13:46 +00003648 lnoff;
3649
Linus Torvalds1da177e2005-04-16 15:20:36 -07003650 *pnetfid = parms->SearchHandle;
3651 } else {
3652 cifs_buf_release(pSMB);
3653 }
3654 }
3655
3656 return rc;
3657}
3658
3659int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003660 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003661{
3662 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3663 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003664 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003665 char *response_data;
3666 int rc = 0;
3667 int bytes_returned, name_len;
3668 __u16 params, byte_count;
3669
3670 cFYI(1, ("In FindNext"));
3671
Steve French4b18f2a2008-04-29 00:06:05 +00003672 if (psrch_inf->endOfSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003673 return -ENOENT;
3674
3675 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3676 (void **) &pSMBr);
3677 if (rc)
3678 return rc;
3679
Steve French50c2f752007-07-13 00:33:32 +00003680 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003681 byte_count = 0;
3682 pSMB->TotalDataCount = 0; /* no EAs */
3683 pSMB->MaxParameterCount = cpu_to_le16(8);
3684 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003685 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3686 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003687 pSMB->MaxSetupCount = 0;
3688 pSMB->Reserved = 0;
3689 pSMB->Flags = 0;
3690 pSMB->Timeout = 0;
3691 pSMB->Reserved2 = 0;
3692 pSMB->ParameterOffset = cpu_to_le16(
3693 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3694 pSMB->DataCount = 0;
3695 pSMB->DataOffset = 0;
3696 pSMB->SetupCount = 1;
3697 pSMB->Reserved3 = 0;
3698 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3699 pSMB->SearchHandle = searchHandle; /* always kept as le */
3700 pSMB->SearchCount =
Steve French630f3f0c2007-10-25 21:17:17 +00003701 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003702 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3703 pSMB->ResumeKey = psrch_inf->resume_key;
3704 pSMB->SearchFlags =
3705 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3706
3707 name_len = psrch_inf->resume_name_len;
3708 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003709 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003710 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3711 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003712 /* 14 byte parm len above enough for 2 byte null terminator */
3713 pSMB->ResumeFileName[name_len] = 0;
3714 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003715 } else {
3716 rc = -EINVAL;
3717 goto FNext2_err_exit;
3718 }
3719 byte_count = params + 1 /* pad */ ;
3720 pSMB->TotalParameterCount = cpu_to_le16(params);
3721 pSMB->ParameterCount = pSMB->TotalParameterCount;
3722 pSMB->hdr.smb_buf_length += byte_count;
3723 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003724
Linus Torvalds1da177e2005-04-16 15:20:36 -07003725 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3726 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003727 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003728 if (rc) {
3729 if (rc == -EBADF) {
Steve French4b18f2a2008-04-29 00:06:05 +00003730 psrch_inf->endOfSearch = true;
Jeff Layton63534502008-05-12 19:56:05 -07003731 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00003732 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733 } else
3734 cFYI(1, ("FindNext returned = %d", rc));
3735 } else { /* decode response */
3736 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003737
Steve French790fe572007-07-07 19:25:05 +00003738 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003739 unsigned int lnoff;
3740
Linus Torvalds1da177e2005-04-16 15:20:36 -07003741 /* BB fixme add lock for file (srch_info) struct here */
3742 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003743 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003744 else
Steve French4b18f2a2008-04-29 00:06:05 +00003745 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003746 response_data = (char *) &pSMBr->hdr.Protocol +
3747 le16_to_cpu(pSMBr->t2.ParameterOffset);
3748 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3749 response_data = (char *)&pSMBr->hdr.Protocol +
3750 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003751 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003752 cifs_small_buf_release(
3753 psrch_inf->ntwrk_buf_start);
3754 else
3755 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003756 psrch_inf->srch_entries_start = response_data;
3757 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003758 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003759 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003760 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003761 else
Steve French4b18f2a2008-04-29 00:06:05 +00003762 psrch_inf->endOfSearch = false;
Steve French50c2f752007-07-13 00:33:32 +00003763 psrch_inf->entries_in_buffer =
3764 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003765 psrch_inf->index_of_last_entry +=
3766 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003767 lnoff = le16_to_cpu(parms->LastNameOffset);
3768 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3769 lnoff) {
3770 cERROR(1, ("ignoring corrupt resume name"));
3771 psrch_inf->last_entry = NULL;
3772 return rc;
3773 } else
3774 psrch_inf->last_entry =
3775 psrch_inf->srch_entries_start + lnoff;
3776
Steve French50c2f752007-07-13 00:33:32 +00003777/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",
3778 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003779
3780 /* BB fixme add unlock here */
3781 }
3782
3783 }
3784
3785 /* BB On error, should we leave previous search buf (and count and
3786 last entry fields) intact or free the previous one? */
3787
3788 /* Note: On -EAGAIN error only caller can retry on handle based calls
3789 since file handle passed in no longer valid */
3790FNext2_err_exit:
3791 if (rc != 0)
3792 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003793 return rc;
3794}
3795
3796int
Steve French50c2f752007-07-13 00:33:32 +00003797CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3798 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003799{
3800 int rc = 0;
3801 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003802
3803 cFYI(1, ("In CIFSSMBFindClose"));
3804 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3805
3806 /* no sense returning error if session restarted
3807 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003808 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003809 return 0;
3810 if (rc)
3811 return rc;
3812
Linus Torvalds1da177e2005-04-16 15:20:36 -07003813 pSMB->FileID = searchHandle;
3814 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003815 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003816 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003817 cERROR(1, ("Send error in FindClose = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003818
Steve Frencha4544342005-08-24 13:59:35 -07003819 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003820
3821 /* Since session is dead, search handle closed on server already */
3822 if (rc == -EAGAIN)
3823 rc = 0;
3824
3825 return rc;
3826}
3827
Linus Torvalds1da177e2005-04-16 15:20:36 -07003828int
3829CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003830 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003831 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003832 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003833{
3834 int rc = 0;
3835 TRANSACTION2_QPI_REQ *pSMB = NULL;
3836 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3837 int name_len, bytes_returned;
3838 __u16 params, byte_count;
3839
Steve French50c2f752007-07-13 00:33:32 +00003840 cFYI(1, ("In GetSrvInodeNum for %s", searchName));
Steve French790fe572007-07-07 19:25:05 +00003841 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003842 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003843
3844GetInodeNumberRetry:
3845 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003846 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003847 if (rc)
3848 return rc;
3849
Linus Torvalds1da177e2005-04-16 15:20:36 -07003850 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3851 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003852 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003853 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003854 name_len++; /* trailing null */
3855 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003856 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003857 name_len = strnlen(searchName, PATH_MAX);
3858 name_len++; /* trailing null */
3859 strncpy(pSMB->FileName, searchName, name_len);
3860 }
3861
3862 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
3863 pSMB->TotalDataCount = 0;
3864 pSMB->MaxParameterCount = cpu_to_le16(2);
3865 /* BB find exact max data count below from sess structure BB */
3866 pSMB->MaxDataCount = cpu_to_le16(4000);
3867 pSMB->MaxSetupCount = 0;
3868 pSMB->Reserved = 0;
3869 pSMB->Flags = 0;
3870 pSMB->Timeout = 0;
3871 pSMB->Reserved2 = 0;
3872 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003873 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003874 pSMB->DataCount = 0;
3875 pSMB->DataOffset = 0;
3876 pSMB->SetupCount = 1;
3877 pSMB->Reserved3 = 0;
3878 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3879 byte_count = params + 1 /* pad */ ;
3880 pSMB->TotalParameterCount = cpu_to_le16(params);
3881 pSMB->ParameterCount = pSMB->TotalParameterCount;
3882 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
3883 pSMB->Reserved4 = 0;
3884 pSMB->hdr.smb_buf_length += byte_count;
3885 pSMB->ByteCount = cpu_to_le16(byte_count);
3886
3887 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3888 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3889 if (rc) {
3890 cFYI(1, ("error %d in QueryInternalInfo", rc));
3891 } else {
3892 /* decode response */
3893 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3894 if (rc || (pSMBr->ByteCount < 2))
3895 /* BB also check enough total bytes returned */
3896 /* If rc should we check for EOPNOSUPP and
3897 disable the srvino flag? or in caller? */
3898 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00003899 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003900 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3901 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00003902 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003903 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00003904 if (count < 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003905 cFYI(1, ("Illegal size ret in QryIntrnlInf"));
3906 rc = -EIO;
3907 goto GetInodeNumOut;
3908 }
3909 pfinfo = (struct file_internal_info *)
3910 (data_offset + (char *) &pSMBr->hdr.Protocol);
3911 *inode_number = pfinfo->UniqueId;
3912 }
3913 }
3914GetInodeNumOut:
3915 cifs_buf_release(pSMB);
3916 if (rc == -EAGAIN)
3917 goto GetInodeNumberRetry;
3918 return rc;
3919}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003920
Igor Mammedovfec45852008-05-16 13:06:30 +04003921/* parses DFS refferal V3 structure
3922 * caller is responsible for freeing target_nodes
3923 * returns:
3924 * on success - 0
3925 * on failure - errno
3926 */
3927static int
Steve Frencha1fe78f2008-05-16 18:48:38 +00003928parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
Igor Mammedovfec45852008-05-16 13:06:30 +04003929 unsigned int *num_of_nodes,
3930 struct dfs_info3_param **target_nodes,
3931 const struct nls_table *nls_codepage)
3932{
3933 int i, rc = 0;
3934 char *data_end;
3935 bool is_unicode;
3936 struct dfs_referral_level_3 *ref;
3937
Harvey Harrison5ca33c62008-07-23 17:45:58 -07003938 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
3939 is_unicode = true;
3940 else
3941 is_unicode = false;
Igor Mammedovfec45852008-05-16 13:06:30 +04003942 *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
3943
3944 if (*num_of_nodes < 1) {
3945 cERROR(1, ("num_referrals: must be at least > 0,"
3946 "but we get num_referrals = %d\n", *num_of_nodes));
3947 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00003948 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003949 }
3950
3951 ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
Al Viro1d92cfd2008-06-02 10:59:02 +01003952 if (ref->VersionNumber != cpu_to_le16(3)) {
Igor Mammedovfec45852008-05-16 13:06:30 +04003953 cERROR(1, ("Referrals of V%d version are not supported,"
Al Viro1d92cfd2008-06-02 10:59:02 +01003954 "should be V3", le16_to_cpu(ref->VersionNumber)));
Igor Mammedovfec45852008-05-16 13:06:30 +04003955 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00003956 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003957 }
3958
3959 /* get the upper boundary of the resp buffer */
3960 data_end = (char *)(&(pSMBr->PathConsumed)) +
3961 le16_to_cpu(pSMBr->t2.DataCount);
3962
3963 cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n",
3964 *num_of_nodes,
3965 le16_to_cpu(pSMBr->DFSFlags)));
3966
3967 *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
3968 *num_of_nodes, GFP_KERNEL);
3969 if (*target_nodes == NULL) {
3970 cERROR(1, ("Failed to allocate buffer for target_nodes\n"));
3971 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00003972 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003973 }
3974
3975 /* collect neccessary data from referrals */
3976 for (i = 0; i < *num_of_nodes; i++) {
3977 char *temp;
3978 int max_len;
3979 struct dfs_info3_param *node = (*target_nodes)+i;
3980
3981 node->flags = le16_to_cpu(pSMBr->DFSFlags);
3982 node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
3983 node->server_type = le16_to_cpu(ref->ServerType);
3984 node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
3985
3986 /* copy DfsPath */
3987 temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
3988 max_len = data_end - temp;
3989 rc = cifs_strncpy_to_host(&(node->path_name), temp,
3990 max_len, is_unicode, nls_codepage);
3991 if (rc)
Steve Frencha1fe78f2008-05-16 18:48:38 +00003992 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04003993
3994 /* copy link target UNC */
3995 temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
3996 max_len = data_end - temp;
3997 rc = cifs_strncpy_to_host(&(node->node_name), temp,
3998 max_len, is_unicode, nls_codepage);
3999 if (rc)
Steve Frencha1fe78f2008-05-16 18:48:38 +00004000 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004001
Al Viro1d92cfd2008-06-02 10:59:02 +01004002 ref += le16_to_cpu(ref->Size);
Igor Mammedovfec45852008-05-16 13:06:30 +04004003 }
4004
Steve Frencha1fe78f2008-05-16 18:48:38 +00004005parse_DFS_referrals_exit:
Igor Mammedovfec45852008-05-16 13:06:30 +04004006 if (rc) {
4007 free_dfs_info_array(*target_nodes, *num_of_nodes);
4008 *target_nodes = NULL;
4009 *num_of_nodes = 0;
4010 }
4011 return rc;
4012}
4013
Linus Torvalds1da177e2005-04-16 15:20:36 -07004014int
4015CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
4016 const unsigned char *searchName,
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004017 struct dfs_info3_param **target_nodes,
4018 unsigned int *num_of_nodes,
Steve French737b7582005-04-28 22:41:06 -07004019 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004020{
4021/* TRANS2_GET_DFS_REFERRAL */
4022 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
4023 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004024 int rc = 0;
4025 int bytes_returned;
4026 int name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004027 __u16 params, byte_count;
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004028 *num_of_nodes = 0;
4029 *target_nodes = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004030
4031 cFYI(1, ("In GetDFSRefer the path %s", searchName));
4032 if (ses == NULL)
4033 return -ENODEV;
4034getDFSRetry:
4035 rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
4036 (void **) &pSMBr);
4037 if (rc)
4038 return rc;
Steve French50c2f752007-07-13 00:33:32 +00004039
4040 /* server pointer checked in called function,
Steve French1982c342005-08-17 12:38:22 -07004041 but should never be null here anyway */
4042 pSMB->hdr.Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004043 pSMB->hdr.Tid = ses->ipc_tid;
4044 pSMB->hdr.Uid = ses->Suid;
Steve French26f57362007-08-30 22:09:15 +00004045 if (ses->capabilities & CAP_STATUS32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004046 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
Steve French26f57362007-08-30 22:09:15 +00004047 if (ses->capabilities & CAP_DFS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004048 pSMB->hdr.Flags2 |= SMBFLG2_DFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004049
4050 if (ses->capabilities & CAP_UNICODE) {
4051 pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
4052 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004053 cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
Steve French737b7582005-04-28 22:41:06 -07004054 searchName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004055 name_len++; /* trailing null */
4056 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004057 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004058 name_len = strnlen(searchName, PATH_MAX);
4059 name_len++; /* trailing null */
4060 strncpy(pSMB->RequestFileName, searchName, name_len);
4061 }
4062
Steve French790fe572007-07-07 19:25:05 +00004063 if (ses->server) {
4064 if (ses->server->secMode &
Steve French1a4e15a2006-10-12 21:33:51 +00004065 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
4066 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
4067 }
4068
Steve French50c2f752007-07-13 00:33:32 +00004069 pSMB->hdr.Uid = ses->Suid;
Steve French1a4e15a2006-10-12 21:33:51 +00004070
Linus Torvalds1da177e2005-04-16 15:20:36 -07004071 params = 2 /* level */ + name_len /*includes null */ ;
4072 pSMB->TotalDataCount = 0;
4073 pSMB->DataCount = 0;
4074 pSMB->DataOffset = 0;
4075 pSMB->MaxParameterCount = 0;
Steve French582d21e2008-05-13 04:54:12 +00004076 /* BB find exact max SMB PDU from sess structure BB */
4077 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004078 pSMB->MaxSetupCount = 0;
4079 pSMB->Reserved = 0;
4080 pSMB->Flags = 0;
4081 pSMB->Timeout = 0;
4082 pSMB->Reserved2 = 0;
4083 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004084 struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004085 pSMB->SetupCount = 1;
4086 pSMB->Reserved3 = 0;
4087 pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
4088 byte_count = params + 3 /* pad */ ;
4089 pSMB->ParameterCount = cpu_to_le16(params);
4090 pSMB->TotalParameterCount = pSMB->ParameterCount;
4091 pSMB->MaxReferralLevel = cpu_to_le16(3);
4092 pSMB->hdr.smb_buf_length += byte_count;
4093 pSMB->ByteCount = cpu_to_le16(byte_count);
4094
4095 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
4096 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4097 if (rc) {
4098 cFYI(1, ("Send error in GetDFSRefer = %d", rc));
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004099 goto GetDFSRefExit;
4100 }
4101 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004102
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004103 /* BB Also check if enough total bytes returned? */
Igor Mammedovfec45852008-05-16 13:06:30 +04004104 if (rc || (pSMBr->ByteCount < 17)) {
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004105 rc = -EIO; /* bad smb */
Igor Mammedovfec45852008-05-16 13:06:30 +04004106 goto GetDFSRefExit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004107 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004108
4109 cFYI(1, ("Decoding GetDFSRefer response BCC: %d Offset %d",
4110 pSMBr->ByteCount,
4111 le16_to_cpu(pSMBr->t2.DataOffset)));
4112
4113 /* parse returned result into more usable form */
Steve Frencha1fe78f2008-05-16 18:48:38 +00004114 rc = parse_DFS_referrals(pSMBr, num_of_nodes,
Igor Mammedovfec45852008-05-16 13:06:30 +04004115 target_nodes, nls_codepage);
4116
Linus Torvalds1da177e2005-04-16 15:20:36 -07004117GetDFSRefExit:
Steve French0d817bc2008-05-22 02:02:03 +00004118 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004119
4120 if (rc == -EAGAIN)
4121 goto getDFSRetry;
4122
4123 return rc;
4124}
4125
Steve French20962432005-09-21 22:05:57 -07004126/* Query File System Info such as free space to old servers such as Win 9x */
4127int
4128SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4129{
4130/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4131 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4132 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4133 FILE_SYSTEM_ALLOC_INFO *response_data;
4134 int rc = 0;
4135 int bytes_returned = 0;
4136 __u16 params, byte_count;
4137
4138 cFYI(1, ("OldQFSInfo"));
4139oldQFSInfoRetry:
4140 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4141 (void **) &pSMBr);
4142 if (rc)
4143 return rc;
Steve French20962432005-09-21 22:05:57 -07004144
4145 params = 2; /* level */
4146 pSMB->TotalDataCount = 0;
4147 pSMB->MaxParameterCount = cpu_to_le16(2);
4148 pSMB->MaxDataCount = cpu_to_le16(1000);
4149 pSMB->MaxSetupCount = 0;
4150 pSMB->Reserved = 0;
4151 pSMB->Flags = 0;
4152 pSMB->Timeout = 0;
4153 pSMB->Reserved2 = 0;
4154 byte_count = params + 1 /* pad */ ;
4155 pSMB->TotalParameterCount = cpu_to_le16(params);
4156 pSMB->ParameterCount = pSMB->TotalParameterCount;
4157 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4158 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4159 pSMB->DataCount = 0;
4160 pSMB->DataOffset = 0;
4161 pSMB->SetupCount = 1;
4162 pSMB->Reserved3 = 0;
4163 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4164 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4165 pSMB->hdr.smb_buf_length += byte_count;
4166 pSMB->ByteCount = cpu_to_le16(byte_count);
4167
4168 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4169 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4170 if (rc) {
4171 cFYI(1, ("Send error in QFSInfo = %d", rc));
4172 } else { /* decode response */
4173 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4174
4175 if (rc || (pSMBr->ByteCount < 18))
4176 rc = -EIO; /* bad smb */
4177 else {
4178 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00004179 cFYI(1, ("qfsinf resp BCC: %d Offset %d",
Steve French20962432005-09-21 22:05:57 -07004180 pSMBr->ByteCount, data_offset));
4181
Steve French50c2f752007-07-13 00:33:32 +00004182 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004183 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4184 FSData->f_bsize =
4185 le16_to_cpu(response_data->BytesPerSector) *
4186 le32_to_cpu(response_data->
4187 SectorsPerAllocationUnit);
4188 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004189 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004190 FSData->f_bfree = FSData->f_bavail =
4191 le32_to_cpu(response_data->FreeAllocationUnits);
4192 cFYI(1,
4193 ("Blocks: %lld Free: %lld Block size %ld",
4194 (unsigned long long)FSData->f_blocks,
4195 (unsigned long long)FSData->f_bfree,
4196 FSData->f_bsize));
4197 }
4198 }
4199 cifs_buf_release(pSMB);
4200
4201 if (rc == -EAGAIN)
4202 goto oldQFSInfoRetry;
4203
4204 return rc;
4205}
4206
Linus Torvalds1da177e2005-04-16 15:20:36 -07004207int
Steve French737b7582005-04-28 22:41:06 -07004208CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004209{
4210/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4211 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4212 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4213 FILE_SYSTEM_INFO *response_data;
4214 int rc = 0;
4215 int bytes_returned = 0;
4216 __u16 params, byte_count;
4217
4218 cFYI(1, ("In QFSInfo"));
4219QFSInfoRetry:
4220 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4221 (void **) &pSMBr);
4222 if (rc)
4223 return rc;
4224
4225 params = 2; /* level */
4226 pSMB->TotalDataCount = 0;
4227 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004228 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004229 pSMB->MaxSetupCount = 0;
4230 pSMB->Reserved = 0;
4231 pSMB->Flags = 0;
4232 pSMB->Timeout = 0;
4233 pSMB->Reserved2 = 0;
4234 byte_count = params + 1 /* pad */ ;
4235 pSMB->TotalParameterCount = cpu_to_le16(params);
4236 pSMB->ParameterCount = pSMB->TotalParameterCount;
4237 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004238 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004239 pSMB->DataCount = 0;
4240 pSMB->DataOffset = 0;
4241 pSMB->SetupCount = 1;
4242 pSMB->Reserved3 = 0;
4243 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4244 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4245 pSMB->hdr.smb_buf_length += byte_count;
4246 pSMB->ByteCount = cpu_to_le16(byte_count);
4247
4248 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4249 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4250 if (rc) {
Steve French20962432005-09-21 22:05:57 -07004251 cFYI(1, ("Send error in QFSInfo = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004252 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004253 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004254
Steve French20962432005-09-21 22:05:57 -07004255 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004256 rc = -EIO; /* bad smb */
4257 else {
4258 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004259
4260 response_data =
4261 (FILE_SYSTEM_INFO
4262 *) (((char *) &pSMBr->hdr.Protocol) +
4263 data_offset);
4264 FSData->f_bsize =
4265 le32_to_cpu(response_data->BytesPerSector) *
4266 le32_to_cpu(response_data->
4267 SectorsPerAllocationUnit);
4268 FSData->f_blocks =
4269 le64_to_cpu(response_data->TotalAllocationUnits);
4270 FSData->f_bfree = FSData->f_bavail =
4271 le64_to_cpu(response_data->FreeAllocationUnits);
4272 cFYI(1,
4273 ("Blocks: %lld Free: %lld Block size %ld",
4274 (unsigned long long)FSData->f_blocks,
4275 (unsigned long long)FSData->f_bfree,
4276 FSData->f_bsize));
4277 }
4278 }
4279 cifs_buf_release(pSMB);
4280
4281 if (rc == -EAGAIN)
4282 goto QFSInfoRetry;
4283
4284 return rc;
4285}
4286
4287int
Steve French737b7582005-04-28 22:41:06 -07004288CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004289{
4290/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4291 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4292 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4293 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4294 int rc = 0;
4295 int bytes_returned = 0;
4296 __u16 params, byte_count;
4297
4298 cFYI(1, ("In QFSAttributeInfo"));
4299QFSAttributeRetry:
4300 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4301 (void **) &pSMBr);
4302 if (rc)
4303 return rc;
4304
4305 params = 2; /* level */
4306 pSMB->TotalDataCount = 0;
4307 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004308 /* BB find exact max SMB PDU from sess structure BB */
4309 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004310 pSMB->MaxSetupCount = 0;
4311 pSMB->Reserved = 0;
4312 pSMB->Flags = 0;
4313 pSMB->Timeout = 0;
4314 pSMB->Reserved2 = 0;
4315 byte_count = params + 1 /* pad */ ;
4316 pSMB->TotalParameterCount = cpu_to_le16(params);
4317 pSMB->ParameterCount = pSMB->TotalParameterCount;
4318 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004319 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004320 pSMB->DataCount = 0;
4321 pSMB->DataOffset = 0;
4322 pSMB->SetupCount = 1;
4323 pSMB->Reserved3 = 0;
4324 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4325 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4326 pSMB->hdr.smb_buf_length += byte_count;
4327 pSMB->ByteCount = cpu_to_le16(byte_count);
4328
4329 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4330 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4331 if (rc) {
4332 cERROR(1, ("Send error in QFSAttributeInfo = %d", rc));
4333 } else { /* decode response */
4334 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4335
Steve French50c2f752007-07-13 00:33:32 +00004336 if (rc || (pSMBr->ByteCount < 13)) {
4337 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004338 rc = -EIO; /* bad smb */
4339 } else {
4340 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4341 response_data =
4342 (FILE_SYSTEM_ATTRIBUTE_INFO
4343 *) (((char *) &pSMBr->hdr.Protocol) +
4344 data_offset);
4345 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004346 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004347 }
4348 }
4349 cifs_buf_release(pSMB);
4350
4351 if (rc == -EAGAIN)
4352 goto QFSAttributeRetry;
4353
4354 return rc;
4355}
4356
4357int
Steve French737b7582005-04-28 22:41:06 -07004358CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004359{
4360/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4361 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4362 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4363 FILE_SYSTEM_DEVICE_INFO *response_data;
4364 int rc = 0;
4365 int bytes_returned = 0;
4366 __u16 params, byte_count;
4367
4368 cFYI(1, ("In QFSDeviceInfo"));
4369QFSDeviceRetry:
4370 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4371 (void **) &pSMBr);
4372 if (rc)
4373 return rc;
4374
4375 params = 2; /* level */
4376 pSMB->TotalDataCount = 0;
4377 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004378 /* BB find exact max SMB PDU from sess structure BB */
4379 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004380 pSMB->MaxSetupCount = 0;
4381 pSMB->Reserved = 0;
4382 pSMB->Flags = 0;
4383 pSMB->Timeout = 0;
4384 pSMB->Reserved2 = 0;
4385 byte_count = params + 1 /* pad */ ;
4386 pSMB->TotalParameterCount = cpu_to_le16(params);
4387 pSMB->ParameterCount = pSMB->TotalParameterCount;
4388 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004389 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004390
4391 pSMB->DataCount = 0;
4392 pSMB->DataOffset = 0;
4393 pSMB->SetupCount = 1;
4394 pSMB->Reserved3 = 0;
4395 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4396 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4397 pSMB->hdr.smb_buf_length += byte_count;
4398 pSMB->ByteCount = cpu_to_le16(byte_count);
4399
4400 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4401 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4402 if (rc) {
4403 cFYI(1, ("Send error in QFSDeviceInfo = %d", rc));
4404 } else { /* decode response */
4405 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4406
Steve French630f3f0c2007-10-25 21:17:17 +00004407 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004408 rc = -EIO; /* bad smb */
4409 else {
4410 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4411 response_data =
Steve French737b7582005-04-28 22:41:06 -07004412 (FILE_SYSTEM_DEVICE_INFO *)
4413 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004414 data_offset);
4415 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004416 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004417 }
4418 }
4419 cifs_buf_release(pSMB);
4420
4421 if (rc == -EAGAIN)
4422 goto QFSDeviceRetry;
4423
4424 return rc;
4425}
4426
4427int
Steve French737b7582005-04-28 22:41:06 -07004428CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004429{
4430/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4431 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4432 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4433 FILE_SYSTEM_UNIX_INFO *response_data;
4434 int rc = 0;
4435 int bytes_returned = 0;
4436 __u16 params, byte_count;
4437
4438 cFYI(1, ("In QFSUnixInfo"));
4439QFSUnixRetry:
4440 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4441 (void **) &pSMBr);
4442 if (rc)
4443 return rc;
4444
4445 params = 2; /* level */
4446 pSMB->TotalDataCount = 0;
4447 pSMB->DataCount = 0;
4448 pSMB->DataOffset = 0;
4449 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004450 /* BB find exact max SMB PDU from sess structure BB */
4451 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004452 pSMB->MaxSetupCount = 0;
4453 pSMB->Reserved = 0;
4454 pSMB->Flags = 0;
4455 pSMB->Timeout = 0;
4456 pSMB->Reserved2 = 0;
4457 byte_count = params + 1 /* pad */ ;
4458 pSMB->ParameterCount = cpu_to_le16(params);
4459 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004460 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4461 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004462 pSMB->SetupCount = 1;
4463 pSMB->Reserved3 = 0;
4464 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4465 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4466 pSMB->hdr.smb_buf_length += byte_count;
4467 pSMB->ByteCount = cpu_to_le16(byte_count);
4468
4469 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4470 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4471 if (rc) {
4472 cERROR(1, ("Send error in QFSUnixInfo = %d", rc));
4473 } else { /* decode response */
4474 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4475
4476 if (rc || (pSMBr->ByteCount < 13)) {
4477 rc = -EIO; /* bad smb */
4478 } else {
4479 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4480 response_data =
4481 (FILE_SYSTEM_UNIX_INFO
4482 *) (((char *) &pSMBr->hdr.Protocol) +
4483 data_offset);
4484 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004485 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004486 }
4487 }
4488 cifs_buf_release(pSMB);
4489
4490 if (rc == -EAGAIN)
4491 goto QFSUnixRetry;
4492
4493
4494 return rc;
4495}
4496
Jeremy Allisonac670552005-06-22 17:26:35 -07004497int
Steve French45abc6e2005-06-23 13:42:03 -05004498CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004499{
4500/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4501 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4502 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4503 int rc = 0;
4504 int bytes_returned = 0;
4505 __u16 params, param_offset, offset, byte_count;
4506
4507 cFYI(1, ("In SETFSUnixInfo"));
4508SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004509 /* BB switch to small buf init to save memory */
Jeremy Allisonac670552005-06-22 17:26:35 -07004510 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4511 (void **) &pSMBr);
4512 if (rc)
4513 return rc;
4514
4515 params = 4; /* 2 bytes zero followed by info level. */
4516 pSMB->MaxSetupCount = 0;
4517 pSMB->Reserved = 0;
4518 pSMB->Flags = 0;
4519 pSMB->Timeout = 0;
4520 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004521 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4522 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004523 offset = param_offset + params;
4524
4525 pSMB->MaxParameterCount = cpu_to_le16(4);
Steve French582d21e2008-05-13 04:54:12 +00004526 /* BB find exact max SMB PDU from sess structure BB */
4527 pSMB->MaxDataCount = cpu_to_le16(100);
Jeremy Allisonac670552005-06-22 17:26:35 -07004528 pSMB->SetupCount = 1;
4529 pSMB->Reserved3 = 0;
4530 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4531 byte_count = 1 /* pad */ + params + 12;
4532
4533 pSMB->DataCount = cpu_to_le16(12);
4534 pSMB->ParameterCount = cpu_to_le16(params);
4535 pSMB->TotalDataCount = pSMB->DataCount;
4536 pSMB->TotalParameterCount = pSMB->ParameterCount;
4537 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4538 pSMB->DataOffset = cpu_to_le16(offset);
4539
4540 /* Params. */
4541 pSMB->FileNum = 0;
4542 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4543
4544 /* Data. */
4545 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4546 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4547 pSMB->ClientUnixCap = cpu_to_le64(cap);
4548
4549 pSMB->hdr.smb_buf_length += byte_count;
4550 pSMB->ByteCount = cpu_to_le16(byte_count);
4551
4552 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4553 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4554 if (rc) {
4555 cERROR(1, ("Send error in SETFSUnixInfo = %d", rc));
4556 } else { /* decode response */
4557 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004558 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004559 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004560 }
4561 cifs_buf_release(pSMB);
4562
4563 if (rc == -EAGAIN)
4564 goto SETFSUnixRetry;
4565
4566 return rc;
4567}
4568
4569
Linus Torvalds1da177e2005-04-16 15:20:36 -07004570
4571int
4572CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004573 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004574{
4575/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4576 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4577 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4578 FILE_SYSTEM_POSIX_INFO *response_data;
4579 int rc = 0;
4580 int bytes_returned = 0;
4581 __u16 params, byte_count;
4582
4583 cFYI(1, ("In QFSPosixInfo"));
4584QFSPosixRetry:
4585 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4586 (void **) &pSMBr);
4587 if (rc)
4588 return rc;
4589
4590 params = 2; /* level */
4591 pSMB->TotalDataCount = 0;
4592 pSMB->DataCount = 0;
4593 pSMB->DataOffset = 0;
4594 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004595 /* BB find exact max SMB PDU from sess structure BB */
4596 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004597 pSMB->MaxSetupCount = 0;
4598 pSMB->Reserved = 0;
4599 pSMB->Flags = 0;
4600 pSMB->Timeout = 0;
4601 pSMB->Reserved2 = 0;
4602 byte_count = params + 1 /* pad */ ;
4603 pSMB->ParameterCount = cpu_to_le16(params);
4604 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004605 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4606 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004607 pSMB->SetupCount = 1;
4608 pSMB->Reserved3 = 0;
4609 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4610 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4611 pSMB->hdr.smb_buf_length += byte_count;
4612 pSMB->ByteCount = cpu_to_le16(byte_count);
4613
4614 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4615 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4616 if (rc) {
4617 cFYI(1, ("Send error in QFSUnixInfo = %d", rc));
4618 } else { /* decode response */
4619 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4620
4621 if (rc || (pSMBr->ByteCount < 13)) {
4622 rc = -EIO; /* bad smb */
4623 } else {
4624 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4625 response_data =
4626 (FILE_SYSTEM_POSIX_INFO
4627 *) (((char *) &pSMBr->hdr.Protocol) +
4628 data_offset);
4629 FSData->f_bsize =
4630 le32_to_cpu(response_data->BlockSize);
4631 FSData->f_blocks =
4632 le64_to_cpu(response_data->TotalBlocks);
4633 FSData->f_bfree =
4634 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004635 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004636 FSData->f_bavail = FSData->f_bfree;
4637 } else {
4638 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004639 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004640 }
Steve French790fe572007-07-07 19:25:05 +00004641 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004642 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004643 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004644 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004645 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004646 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004647 }
4648 }
4649 cifs_buf_release(pSMB);
4650
4651 if (rc == -EAGAIN)
4652 goto QFSPosixRetry;
4653
4654 return rc;
4655}
4656
4657
Steve French50c2f752007-07-13 00:33:32 +00004658/* We can not use write of zero bytes trick to
4659 set file size due to need for large file support. Also note that
4660 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004661 routine which is only needed to work around a sharing violation bug
4662 in Samba which this routine can run into */
4663
4664int
4665CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French4b18f2a2008-04-29 00:06:05 +00004666 __u64 size, bool SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004667 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004668{
4669 struct smb_com_transaction2_spi_req *pSMB = NULL;
4670 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4671 struct file_end_of_file_info *parm_data;
4672 int name_len;
4673 int rc = 0;
4674 int bytes_returned = 0;
4675 __u16 params, byte_count, data_count, param_offset, offset;
4676
4677 cFYI(1, ("In SetEOF"));
4678SetEOFRetry:
4679 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4680 (void **) &pSMBr);
4681 if (rc)
4682 return rc;
4683
4684 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4685 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004686 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004687 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004688 name_len++; /* trailing null */
4689 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004690 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004691 name_len = strnlen(fileName, PATH_MAX);
4692 name_len++; /* trailing null */
4693 strncpy(pSMB->FileName, fileName, name_len);
4694 }
4695 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004696 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004697 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004698 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004699 pSMB->MaxSetupCount = 0;
4700 pSMB->Reserved = 0;
4701 pSMB->Flags = 0;
4702 pSMB->Timeout = 0;
4703 pSMB->Reserved2 = 0;
4704 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004705 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004706 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004707 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004708 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4709 pSMB->InformationLevel =
4710 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4711 else
4712 pSMB->InformationLevel =
4713 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4714 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004715 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4716 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004717 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004718 else
4719 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004720 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004721 }
4722
4723 parm_data =
4724 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4725 offset);
4726 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4727 pSMB->DataOffset = cpu_to_le16(offset);
4728 pSMB->SetupCount = 1;
4729 pSMB->Reserved3 = 0;
4730 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4731 byte_count = 3 /* pad */ + params + data_count;
4732 pSMB->DataCount = cpu_to_le16(data_count);
4733 pSMB->TotalDataCount = pSMB->DataCount;
4734 pSMB->ParameterCount = cpu_to_le16(params);
4735 pSMB->TotalParameterCount = pSMB->ParameterCount;
4736 pSMB->Reserved4 = 0;
4737 pSMB->hdr.smb_buf_length += byte_count;
4738 parm_data->FileSize = cpu_to_le64(size);
4739 pSMB->ByteCount = cpu_to_le16(byte_count);
4740 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4741 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004742 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004743 cFYI(1, ("SetPathInfo (file size) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004744
4745 cifs_buf_release(pSMB);
4746
4747 if (rc == -EAGAIN)
4748 goto SetEOFRetry;
4749
4750 return rc;
4751}
4752
4753int
Steve French50c2f752007-07-13 00:33:32 +00004754CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
Steve French4b18f2a2008-04-29 00:06:05 +00004755 __u16 fid, __u32 pid_of_opener, bool SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004756{
4757 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004758 char *data_offset;
4759 struct file_end_of_file_info *parm_data;
4760 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004761 __u16 params, param_offset, offset, byte_count, count;
4762
4763 cFYI(1, ("SetFileSize (via SetFileInfo) %lld",
4764 (long long)size));
Steve Frenchcd634992005-04-28 22:41:10 -07004765 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4766
Linus Torvalds1da177e2005-04-16 15:20:36 -07004767 if (rc)
4768 return rc;
4769
4770 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4771 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004772
Linus Torvalds1da177e2005-04-16 15:20:36 -07004773 params = 6;
4774 pSMB->MaxSetupCount = 0;
4775 pSMB->Reserved = 0;
4776 pSMB->Flags = 0;
4777 pSMB->Timeout = 0;
4778 pSMB->Reserved2 = 0;
4779 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4780 offset = param_offset + params;
4781
Steve French50c2f752007-07-13 00:33:32 +00004782 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004783
4784 count = sizeof(struct file_end_of_file_info);
4785 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004786 /* BB find exact max SMB PDU from sess structure BB */
4787 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004788 pSMB->SetupCount = 1;
4789 pSMB->Reserved3 = 0;
4790 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4791 byte_count = 3 /* pad */ + params + count;
4792 pSMB->DataCount = cpu_to_le16(count);
4793 pSMB->ParameterCount = cpu_to_le16(params);
4794 pSMB->TotalDataCount = pSMB->DataCount;
4795 pSMB->TotalParameterCount = pSMB->ParameterCount;
4796 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4797 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004798 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4799 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004800 pSMB->DataOffset = cpu_to_le16(offset);
4801 parm_data->FileSize = cpu_to_le64(size);
4802 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004803 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004804 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4805 pSMB->InformationLevel =
4806 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4807 else
4808 pSMB->InformationLevel =
4809 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004810 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004811 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4812 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004813 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004814 else
4815 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004816 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004817 }
4818 pSMB->Reserved4 = 0;
4819 pSMB->hdr.smb_buf_length += byte_count;
4820 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004821 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004822 if (rc) {
4823 cFYI(1,
4824 ("Send error in SetFileInfo (SetFileSize) = %d",
4825 rc));
4826 }
4827
Steve French50c2f752007-07-13 00:33:32 +00004828 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004829 since file handle passed in no longer valid */
4830
4831 return rc;
4832}
4833
Steve French50c2f752007-07-13 00:33:32 +00004834/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004835 an open handle, rather than by pathname - this is awkward due to
4836 potential access conflicts on the open, but it is unavoidable for these
4837 old servers since the only other choice is to go from 100 nanosecond DCE
4838 time and resort to the original setpathinfo level which takes the ancient
4839 DOS time format with 2 second granularity */
4840int
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004841CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
4842 const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004843{
4844 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004845 char *data_offset;
4846 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004847 __u16 params, param_offset, offset, byte_count, count;
4848
4849 cFYI(1, ("Set Times (via SetFileInfo)"));
Steve Frenchcd634992005-04-28 22:41:10 -07004850 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4851
Linus Torvalds1da177e2005-04-16 15:20:36 -07004852 if (rc)
4853 return rc;
4854
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004855 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4856 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004857
Linus Torvalds1da177e2005-04-16 15:20:36 -07004858 params = 6;
4859 pSMB->MaxSetupCount = 0;
4860 pSMB->Reserved = 0;
4861 pSMB->Flags = 0;
4862 pSMB->Timeout = 0;
4863 pSMB->Reserved2 = 0;
4864 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4865 offset = param_offset + params;
4866
Steve French50c2f752007-07-13 00:33:32 +00004867 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004868
Steve French26f57362007-08-30 22:09:15 +00004869 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004870 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004871 /* BB find max SMB PDU from sess */
4872 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004873 pSMB->SetupCount = 1;
4874 pSMB->Reserved3 = 0;
4875 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4876 byte_count = 3 /* pad */ + params + count;
4877 pSMB->DataCount = cpu_to_le16(count);
4878 pSMB->ParameterCount = cpu_to_le16(params);
4879 pSMB->TotalDataCount = pSMB->DataCount;
4880 pSMB->TotalParameterCount = pSMB->ParameterCount;
4881 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4882 pSMB->DataOffset = cpu_to_le16(offset);
4883 pSMB->Fid = fid;
4884 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4885 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4886 else
4887 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
4888 pSMB->Reserved4 = 0;
4889 pSMB->hdr.smb_buf_length += byte_count;
4890 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00004891 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00004892 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004893 if (rc)
Steve French50c2f752007-07-13 00:33:32 +00004894 cFYI(1, ("Send error in Set Time (SetFileInfo) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004895
Steve French50c2f752007-07-13 00:33:32 +00004896 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004897 since file handle passed in no longer valid */
4898
4899 return rc;
4900}
4901
Jeff Layton6d22f092008-09-23 11:48:35 -04004902int
4903CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
4904 bool delete_file, __u16 fid, __u32 pid_of_opener)
4905{
4906 struct smb_com_transaction2_sfi_req *pSMB = NULL;
4907 char *data_offset;
4908 int rc = 0;
4909 __u16 params, param_offset, offset, byte_count, count;
4910
4911 cFYI(1, ("Set File Disposition (via SetFileInfo)"));
4912 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4913
4914 if (rc)
4915 return rc;
4916
4917 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4918 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
4919
4920 params = 6;
4921 pSMB->MaxSetupCount = 0;
4922 pSMB->Reserved = 0;
4923 pSMB->Flags = 0;
4924 pSMB->Timeout = 0;
4925 pSMB->Reserved2 = 0;
4926 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4927 offset = param_offset + params;
4928
4929 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
4930
4931 count = 1;
4932 pSMB->MaxParameterCount = cpu_to_le16(2);
4933 /* BB find max SMB PDU from sess */
4934 pSMB->MaxDataCount = cpu_to_le16(1000);
4935 pSMB->SetupCount = 1;
4936 pSMB->Reserved3 = 0;
4937 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4938 byte_count = 3 /* pad */ + params + count;
4939 pSMB->DataCount = cpu_to_le16(count);
4940 pSMB->ParameterCount = cpu_to_le16(params);
4941 pSMB->TotalDataCount = pSMB->DataCount;
4942 pSMB->TotalParameterCount = pSMB->ParameterCount;
4943 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4944 pSMB->DataOffset = cpu_to_le16(offset);
4945 pSMB->Fid = fid;
4946 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
4947 pSMB->Reserved4 = 0;
4948 pSMB->hdr.smb_buf_length += byte_count;
4949 pSMB->ByteCount = cpu_to_le16(byte_count);
4950 *data_offset = delete_file ? 1 : 0;
4951 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
4952 if (rc)
4953 cFYI(1, ("Send error in SetFileDisposition = %d", rc));
4954
4955 return rc;
4956}
Linus Torvalds1da177e2005-04-16 15:20:36 -07004957
4958int
Jeff Layton6fc000e2008-08-02 07:26:12 -04004959CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
4960 const char *fileName, const FILE_BASIC_INFO *data,
4961 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004962{
4963 TRANSACTION2_SPI_REQ *pSMB = NULL;
4964 TRANSACTION2_SPI_RSP *pSMBr = NULL;
4965 int name_len;
4966 int rc = 0;
4967 int bytes_returned = 0;
4968 char *data_offset;
4969 __u16 params, param_offset, offset, byte_count, count;
4970
4971 cFYI(1, ("In SetTimes"));
4972
4973SetTimesRetry:
4974 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4975 (void **) &pSMBr);
4976 if (rc)
4977 return rc;
4978
4979 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4980 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004981 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004982 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004983 name_len++; /* trailing null */
4984 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004985 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004986 name_len = strnlen(fileName, PATH_MAX);
4987 name_len++; /* trailing null */
4988 strncpy(pSMB->FileName, fileName, name_len);
4989 }
4990
4991 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004992 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004993 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004994 /* BB find max SMB PDU from sess structure BB */
4995 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004996 pSMB->MaxSetupCount = 0;
4997 pSMB->Reserved = 0;
4998 pSMB->Flags = 0;
4999 pSMB->Timeout = 0;
5000 pSMB->Reserved2 = 0;
5001 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005002 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005003 offset = param_offset + params;
5004 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5005 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5006 pSMB->DataOffset = cpu_to_le16(offset);
5007 pSMB->SetupCount = 1;
5008 pSMB->Reserved3 = 0;
5009 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5010 byte_count = 3 /* pad */ + params + count;
5011
5012 pSMB->DataCount = cpu_to_le16(count);
5013 pSMB->ParameterCount = cpu_to_le16(params);
5014 pSMB->TotalDataCount = pSMB->DataCount;
5015 pSMB->TotalParameterCount = pSMB->ParameterCount;
5016 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5017 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5018 else
5019 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5020 pSMB->Reserved4 = 0;
5021 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00005022 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005023 pSMB->ByteCount = cpu_to_le16(byte_count);
5024 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5025 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005026 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005027 cFYI(1, ("SetPathInfo (times) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005028
5029 cifs_buf_release(pSMB);
5030
5031 if (rc == -EAGAIN)
5032 goto SetTimesRetry;
5033
5034 return rc;
5035}
5036
5037/* Can not be used to set time stamps yet (due to old DOS time format) */
5038/* Can be used to set attributes */
5039#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
5040 handling it anyway and NT4 was what we thought it would be needed for
5041 Do not delete it until we prove whether needed for Win9x though */
5042int
5043CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
5044 __u16 dos_attrs, const struct nls_table *nls_codepage)
5045{
5046 SETATTR_REQ *pSMB = NULL;
5047 SETATTR_RSP *pSMBr = NULL;
5048 int rc = 0;
5049 int bytes_returned;
5050 int name_len;
5051
5052 cFYI(1, ("In SetAttrLegacy"));
5053
5054SetAttrLgcyRetry:
5055 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
5056 (void **) &pSMBr);
5057 if (rc)
5058 return rc;
5059
5060 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5061 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005062 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005063 PATH_MAX, nls_codepage);
5064 name_len++; /* trailing null */
5065 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005066 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005067 name_len = strnlen(fileName, PATH_MAX);
5068 name_len++; /* trailing null */
5069 strncpy(pSMB->fileName, fileName, name_len);
5070 }
5071 pSMB->attr = cpu_to_le16(dos_attrs);
5072 pSMB->BufferFormat = 0x04;
5073 pSMB->hdr.smb_buf_length += name_len + 1;
5074 pSMB->ByteCount = cpu_to_le16(name_len + 1);
5075 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5076 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005077 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005078 cFYI(1, ("Error in LegacySetAttr = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005079
5080 cifs_buf_release(pSMB);
5081
5082 if (rc == -EAGAIN)
5083 goto SetAttrLgcyRetry;
5084
5085 return rc;
5086}
5087#endif /* temporarily unneeded SetAttr legacy function */
5088
5089int
Jeff Layton4e1e7fb2008-08-02 07:26:12 -04005090CIFSSMBUnixSetInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,
Steve French063ea272008-08-06 04:23:13 +00005091 const struct cifs_unix_set_info_args *args,
Jeff Layton4e1e7fb2008-08-02 07:26:12 -04005092 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005093{
5094 TRANSACTION2_SPI_REQ *pSMB = NULL;
5095 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5096 int name_len;
5097 int rc = 0;
5098 int bytes_returned = 0;
5099 FILE_UNIX_BASIC_INFO *data_offset;
5100 __u16 params, param_offset, offset, count, byte_count;
Jeff Layton4e1e7fb2008-08-02 07:26:12 -04005101 __u64 mode = args->mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005102
5103 cFYI(1, ("In SetUID/GID/Mode"));
5104setPermsRetry:
5105 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5106 (void **) &pSMBr);
5107 if (rc)
5108 return rc;
5109
5110 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5111 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005112 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005113 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005114 name_len++; /* trailing null */
5115 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07005116 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005117 name_len = strnlen(fileName, PATH_MAX);
5118 name_len++; /* trailing null */
5119 strncpy(pSMB->FileName, fileName, name_len);
5120 }
5121
5122 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005123 count = sizeof(FILE_UNIX_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005124 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005125 /* BB find max SMB PDU from sess structure BB */
5126 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005127 pSMB->MaxSetupCount = 0;
5128 pSMB->Reserved = 0;
5129 pSMB->Flags = 0;
5130 pSMB->Timeout = 0;
5131 pSMB->Reserved2 = 0;
5132 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005133 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005134 offset = param_offset + params;
5135 data_offset =
5136 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5137 offset);
5138 memset(data_offset, 0, count);
5139 pSMB->DataOffset = cpu_to_le16(offset);
5140 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5141 pSMB->SetupCount = 1;
5142 pSMB->Reserved3 = 0;
5143 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5144 byte_count = 3 /* pad */ + params + count;
5145 pSMB->ParameterCount = cpu_to_le16(params);
5146 pSMB->DataCount = cpu_to_le16(count);
5147 pSMB->TotalParameterCount = pSMB->ParameterCount;
5148 pSMB->TotalDataCount = pSMB->DataCount;
5149 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5150 pSMB->Reserved4 = 0;
5151 pSMB->hdr.smb_buf_length += byte_count;
Steve Frenchc7af1852007-03-01 04:11:22 +00005152 /* Samba server ignores set of file size to zero due to bugs in some
5153 older clients, but we should be precise - we use SetFileSize to
5154 set file size and do not want to truncate file size to zero
5155 accidently as happened on one Samba server beta by putting
Steve French50c2f752007-07-13 00:33:32 +00005156 zero instead of -1 here */
Jeff Layton4e1e7fb2008-08-02 07:26:12 -04005157 data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
5158 data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
5159 data_offset->LastStatusChange = cpu_to_le64(args->ctime);
5160 data_offset->LastAccessTime = cpu_to_le64(args->atime);
5161 data_offset->LastModificationTime = cpu_to_le64(args->mtime);
5162 data_offset->Uid = cpu_to_le64(args->uid);
5163 data_offset->Gid = cpu_to_le64(args->gid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005164 /* better to leave device as zero when it is */
Jeff Layton4e1e7fb2008-08-02 07:26:12 -04005165 data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
5166 data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005167 data_offset->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00005168
Steve French790fe572007-07-07 19:25:05 +00005169 if (S_ISREG(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005170 data_offset->Type = cpu_to_le32(UNIX_FILE);
Steve French790fe572007-07-07 19:25:05 +00005171 else if (S_ISDIR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005172 data_offset->Type = cpu_to_le32(UNIX_DIR);
Steve French790fe572007-07-07 19:25:05 +00005173 else if (S_ISLNK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005174 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
Steve French790fe572007-07-07 19:25:05 +00005175 else if (S_ISCHR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005176 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
Steve French790fe572007-07-07 19:25:05 +00005177 else if (S_ISBLK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005178 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
Steve French790fe572007-07-07 19:25:05 +00005179 else if (S_ISFIFO(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005180 data_offset->Type = cpu_to_le32(UNIX_FIFO);
Steve French790fe572007-07-07 19:25:05 +00005181 else if (S_ISSOCK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005182 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5183
5184
5185 pSMB->ByteCount = cpu_to_le16(byte_count);
5186 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5187 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005188 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005189 cFYI(1, ("SetPathInfo (perms) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005190
Steve French0d817bc2008-05-22 02:02:03 +00005191 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005192 if (rc == -EAGAIN)
5193 goto setPermsRetry;
5194 return rc;
5195}
5196
Steve French50c2f752007-07-13 00:33:32 +00005197int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005198 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005199 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005200 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005201{
5202 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005203 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5204 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005205 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005206 int bytes_returned;
5207
Steve French50c2f752007-07-13 00:33:32 +00005208 cFYI(1, ("In CIFSSMBNotify for file handle %d", (int)netfid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005209 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005210 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005211 if (rc)
5212 return rc;
5213
5214 pSMB->TotalParameterCount = 0 ;
5215 pSMB->TotalDataCount = 0;
5216 pSMB->MaxParameterCount = cpu_to_le32(2);
5217 /* BB find exact data count max from sess structure BB */
5218 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005219/* BB VERIFY verify which is correct for above BB */
5220 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5221 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5222
Linus Torvalds1da177e2005-04-16 15:20:36 -07005223 pSMB->MaxSetupCount = 4;
5224 pSMB->Reserved = 0;
5225 pSMB->ParameterOffset = 0;
5226 pSMB->DataCount = 0;
5227 pSMB->DataOffset = 0;
5228 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5229 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5230 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005231 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005232 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5233 pSMB->Reserved2 = 0;
5234 pSMB->CompletionFilter = cpu_to_le32(filter);
5235 pSMB->Fid = netfid; /* file handle always le */
5236 pSMB->ByteCount = 0;
5237
5238 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005239 (struct smb_hdr *)pSMBr, &bytes_returned,
5240 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005241 if (rc) {
5242 cFYI(1, ("Error in Notify = %d", rc));
Steve Frenchff5dbd92005-08-24 17:10:36 -07005243 } else {
5244 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005245 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005246 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005247 sizeof(struct dir_notify_req),
5248 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005249 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005250 dnotify_req->Pid = pSMB->hdr.Pid;
5251 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5252 dnotify_req->Mid = pSMB->hdr.Mid;
5253 dnotify_req->Tid = pSMB->hdr.Tid;
5254 dnotify_req->Uid = pSMB->hdr.Uid;
5255 dnotify_req->netfid = netfid;
5256 dnotify_req->pfile = pfile;
5257 dnotify_req->filter = filter;
5258 dnotify_req->multishot = multishot;
5259 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005260 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005261 &GlobalDnotifyReqList);
5262 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005263 } else
Steve French47c786e2005-10-11 20:03:18 -07005264 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005265 }
5266 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005267 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005268}
5269#ifdef CONFIG_CIFS_XATTR
5270ssize_t
5271CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
5272 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00005273 char *EAData, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005274 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005275{
5276 /* BB assumes one setup word */
5277 TRANSACTION2_QPI_REQ *pSMB = NULL;
5278 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5279 int rc = 0;
5280 int bytes_returned;
5281 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005282 struct fea *temp_fea;
5283 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005284 __u16 params, byte_count;
5285
5286 cFYI(1, ("In Query All EAs path %s", searchName));
5287QAllEAsRetry:
5288 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5289 (void **) &pSMBr);
5290 if (rc)
5291 return rc;
5292
5293 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5294 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005295 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005296 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005297 name_len++; /* trailing null */
5298 name_len *= 2;
5299 } else { /* BB improve the check for buffer overruns BB */
5300 name_len = strnlen(searchName, PATH_MAX);
5301 name_len++; /* trailing null */
5302 strncpy(pSMB->FileName, searchName, name_len);
5303 }
5304
Steve French50c2f752007-07-13 00:33:32 +00005305 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005306 pSMB->TotalDataCount = 0;
5307 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005308 /* BB find exact max SMB PDU from sess structure BB */
5309 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005310 pSMB->MaxSetupCount = 0;
5311 pSMB->Reserved = 0;
5312 pSMB->Flags = 0;
5313 pSMB->Timeout = 0;
5314 pSMB->Reserved2 = 0;
5315 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005316 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005317 pSMB->DataCount = 0;
5318 pSMB->DataOffset = 0;
5319 pSMB->SetupCount = 1;
5320 pSMB->Reserved3 = 0;
5321 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5322 byte_count = params + 1 /* pad */ ;
5323 pSMB->TotalParameterCount = cpu_to_le16(params);
5324 pSMB->ParameterCount = pSMB->TotalParameterCount;
5325 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5326 pSMB->Reserved4 = 0;
5327 pSMB->hdr.smb_buf_length += byte_count;
5328 pSMB->ByteCount = cpu_to_le16(byte_count);
5329
5330 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5331 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5332 if (rc) {
5333 cFYI(1, ("Send error in QueryAllEAs = %d", rc));
5334 } else { /* decode response */
5335 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5336
5337 /* BB also check enough total bytes returned */
5338 /* BB we need to improve the validity checking
5339 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005340 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005341 rc = -EIO; /* bad smb */
5342 /* else if (pFindData){
5343 memcpy((char *) pFindData,
5344 (char *) &pSMBr->hdr.Protocol +
5345 data_offset, kl);
5346 }*/ else {
5347 /* check that length of list is not more than bcc */
5348 /* check that each entry does not go beyond length
5349 of list */
5350 /* check that each element of each entry does not
5351 go beyond end of list */
5352 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005353 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005354 rc = 0;
5355 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005356 /* BB check if start of smb + data_offset > &bcc+ bcc */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005357 ea_response_data = (struct fealist *)
5358 (((char *) &pSMBr->hdr.Protocol) +
5359 data_offset);
5360 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005361 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005362 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005363 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005364 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005365 } else {
5366 /* account for ea list len */
5367 name_len -= 4;
5368 temp_fea = ea_response_data->list;
5369 temp_ptr = (char *)temp_fea;
Steve French50c2f752007-07-13 00:33:32 +00005370 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005371 __u16 value_len;
5372 name_len -= 4;
5373 temp_ptr += 4;
5374 rc += temp_fea->name_len;
5375 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005376 rc = rc + 5 + 1;
5377 if (rc < (int)buf_size) {
Steve French50c2f752007-07-13 00:33:32 +00005378 memcpy(EAData, "user.", 5);
5379 EAData += 5;
5380 memcpy(EAData, temp_ptr,
5381 temp_fea->name_len);
5382 EAData += temp_fea->name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005383 /* null terminate name */
5384 *EAData = 0;
5385 EAData = EAData + 1;
Steve French790fe572007-07-07 19:25:05 +00005386 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005387 /* skip copy - calc size only */
5388 } else {
5389 /* stop before overrun buffer */
5390 rc = -ERANGE;
5391 break;
5392 }
5393 name_len -= temp_fea->name_len;
5394 temp_ptr += temp_fea->name_len;
5395 /* account for trailing null */
5396 name_len--;
5397 temp_ptr++;
Steve French50c2f752007-07-13 00:33:32 +00005398 value_len =
5399 le16_to_cpu(temp_fea->value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005400 name_len -= value_len;
5401 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005402 /* BB check that temp_ptr is still
5403 within the SMB BB*/
5404
5405 /* no trailing null to account for
5406 in value len */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005407 /* go on to next EA */
5408 temp_fea = (struct fea *)temp_ptr;
5409 }
5410 }
5411 }
5412 }
Steve French0d817bc2008-05-22 02:02:03 +00005413 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005414 if (rc == -EAGAIN)
5415 goto QAllEAsRetry;
5416
5417 return (ssize_t)rc;
5418}
5419
Steve French50c2f752007-07-13 00:33:32 +00005420ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon,
5421 const unsigned char *searchName, const unsigned char *ea_name,
5422 unsigned char *ea_value, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005423 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005424{
5425 TRANSACTION2_QPI_REQ *pSMB = NULL;
5426 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5427 int rc = 0;
5428 int bytes_returned;
5429 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005430 struct fea *temp_fea;
5431 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005432 __u16 params, byte_count;
5433
5434 cFYI(1, ("In Query EA path %s", searchName));
5435QEARetry:
5436 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5437 (void **) &pSMBr);
5438 if (rc)
5439 return rc;
5440
5441 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5442 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005443 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005444 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005445 name_len++; /* trailing null */
5446 name_len *= 2;
5447 } else { /* BB improve the check for buffer overruns BB */
5448 name_len = strnlen(searchName, PATH_MAX);
5449 name_len++; /* trailing null */
5450 strncpy(pSMB->FileName, searchName, name_len);
5451 }
5452
Steve French50c2f752007-07-13 00:33:32 +00005453 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005454 pSMB->TotalDataCount = 0;
5455 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005456 /* BB find exact max SMB PDU from sess structure BB */
5457 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005458 pSMB->MaxSetupCount = 0;
5459 pSMB->Reserved = 0;
5460 pSMB->Flags = 0;
5461 pSMB->Timeout = 0;
5462 pSMB->Reserved2 = 0;
5463 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005464 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005465 pSMB->DataCount = 0;
5466 pSMB->DataOffset = 0;
5467 pSMB->SetupCount = 1;
5468 pSMB->Reserved3 = 0;
5469 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5470 byte_count = params + 1 /* pad */ ;
5471 pSMB->TotalParameterCount = cpu_to_le16(params);
5472 pSMB->ParameterCount = pSMB->TotalParameterCount;
5473 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5474 pSMB->Reserved4 = 0;
5475 pSMB->hdr.smb_buf_length += byte_count;
5476 pSMB->ByteCount = cpu_to_le16(byte_count);
5477
5478 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5479 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5480 if (rc) {
5481 cFYI(1, ("Send error in Query EA = %d", rc));
5482 } else { /* decode response */
5483 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5484
5485 /* BB also check enough total bytes returned */
5486 /* BB we need to improve the validity checking
5487 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005488 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005489 rc = -EIO; /* bad smb */
5490 /* else if (pFindData){
5491 memcpy((char *) pFindData,
5492 (char *) &pSMBr->hdr.Protocol +
5493 data_offset, kl);
5494 }*/ else {
5495 /* check that length of list is not more than bcc */
5496 /* check that each entry does not go beyond length
5497 of list */
5498 /* check that each element of each entry does not
5499 go beyond end of list */
5500 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005501 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005502 rc = -ENODATA;
5503 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005504 /* BB check if start of smb + data_offset > &bcc+ bcc*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07005505 ea_response_data = (struct fealist *)
5506 (((char *) &pSMBr->hdr.Protocol) +
5507 data_offset);
5508 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005509 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005510 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005511 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005512 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005513 } else {
5514 /* account for ea list len */
5515 name_len -= 4;
5516 temp_fea = ea_response_data->list;
5517 temp_ptr = (char *)temp_fea;
5518 /* loop through checking if we have a matching
5519 name and then return the associated value */
Steve French50c2f752007-07-13 00:33:32 +00005520 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005521 __u16 value_len;
5522 name_len -= 4;
5523 temp_ptr += 4;
Steve French50c2f752007-07-13 00:33:32 +00005524 value_len =
5525 le16_to_cpu(temp_fea->value_len);
5526 /* BB validate that value_len falls within SMB,
5527 even though maximum for name_len is 255 */
Steve French790fe572007-07-07 19:25:05 +00005528 if (memcmp(temp_fea->name, ea_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005529 temp_fea->name_len) == 0) {
5530 /* found a match */
5531 rc = value_len;
5532 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005533 if (rc <= (int)buf_size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005534 memcpy(ea_value,
5535 temp_fea->name+temp_fea->name_len+1,
5536 rc);
Steve French50c2f752007-07-13 00:33:32 +00005537 /* ea values, unlike ea
5538 names, are not null
5539 terminated */
Steve French790fe572007-07-07 19:25:05 +00005540 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005541 /* skip copy - calc size only */
5542 } else {
Steve French50c2f752007-07-13 00:33:32 +00005543 /* stop before overrun buffer */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005544 rc = -ERANGE;
5545 }
5546 break;
5547 }
5548 name_len -= temp_fea->name_len;
5549 temp_ptr += temp_fea->name_len;
5550 /* account for trailing null */
5551 name_len--;
5552 temp_ptr++;
5553 name_len -= value_len;
5554 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005555 /* No trailing null to account for in
5556 value_len. Go on to next EA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005557 temp_fea = (struct fea *)temp_ptr;
5558 }
Steve French50c2f752007-07-13 00:33:32 +00005559 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005560 }
5561 }
Steve French0d817bc2008-05-22 02:02:03 +00005562 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005563 if (rc == -EAGAIN)
5564 goto QEARetry;
5565
5566 return (ssize_t)rc;
5567}
5568
5569int
5570CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005571 const char *ea_name, const void *ea_value,
5572 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5573 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005574{
5575 struct smb_com_transaction2_spi_req *pSMB = NULL;
5576 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5577 struct fealist *parm_data;
5578 int name_len;
5579 int rc = 0;
5580 int bytes_returned = 0;
5581 __u16 params, param_offset, byte_count, offset, count;
5582
5583 cFYI(1, ("In SetEA"));
5584SetEARetry:
5585 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5586 (void **) &pSMBr);
5587 if (rc)
5588 return rc;
5589
5590 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5591 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005592 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005593 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005594 name_len++; /* trailing null */
5595 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005596 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005597 name_len = strnlen(fileName, PATH_MAX);
5598 name_len++; /* trailing null */
5599 strncpy(pSMB->FileName, fileName, name_len);
5600 }
5601
5602 params = 6 + name_len;
5603
5604 /* done calculating parms using name_len of file name,
5605 now use name_len to calculate length of ea name
5606 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005607 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005608 name_len = 0;
5609 else
Steve French50c2f752007-07-13 00:33:32 +00005610 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005611
Steve Frenchdae5dbd2007-12-30 23:49:57 +00005612 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005613 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005614 /* BB find max SMB PDU from sess */
5615 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005616 pSMB->MaxSetupCount = 0;
5617 pSMB->Reserved = 0;
5618 pSMB->Flags = 0;
5619 pSMB->Timeout = 0;
5620 pSMB->Reserved2 = 0;
5621 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005622 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005623 offset = param_offset + params;
5624 pSMB->InformationLevel =
5625 cpu_to_le16(SMB_SET_FILE_EA);
5626
5627 parm_data =
5628 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5629 offset);
5630 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5631 pSMB->DataOffset = cpu_to_le16(offset);
5632 pSMB->SetupCount = 1;
5633 pSMB->Reserved3 = 0;
5634 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5635 byte_count = 3 /* pad */ + params + count;
5636 pSMB->DataCount = cpu_to_le16(count);
5637 parm_data->list_len = cpu_to_le32(count);
5638 parm_data->list[0].EA_flags = 0;
5639 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005640 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005641 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005642 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005643 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005644 parm_data->list[0].name[name_len] = 0;
5645 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5646 /* caller ensures that ea_value_len is less than 64K but
5647 we need to ensure that it fits within the smb */
5648
Steve French50c2f752007-07-13 00:33:32 +00005649 /*BB add length check to see if it would fit in
5650 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005651 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5652 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005653 memcpy(parm_data->list[0].name+name_len+1,
5654 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005655
5656 pSMB->TotalDataCount = pSMB->DataCount;
5657 pSMB->ParameterCount = cpu_to_le16(params);
5658 pSMB->TotalParameterCount = pSMB->ParameterCount;
5659 pSMB->Reserved4 = 0;
5660 pSMB->hdr.smb_buf_length += byte_count;
5661 pSMB->ByteCount = cpu_to_le16(byte_count);
5662 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5663 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005664 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005665 cFYI(1, ("SetPathInfo (EA) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005666
5667 cifs_buf_release(pSMB);
5668
5669 if (rc == -EAGAIN)
5670 goto SetEARetry;
5671
5672 return rc;
5673}
5674
5675#endif