blob: 30bbe448e260fa931a2a36098057bfdd8364fad3 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
85/* Mark as invalid, all open files on tree connections since they
86 were closed when session to server was lost */
Steve French790fe572007-07-07 19:25:05 +000087static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088{
89 struct cifsFileInfo *open_file = NULL;
Steve French790fe572007-07-07 19:25:05 +000090 struct list_head *tmp;
91 struct list_head *tmp1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
93/* list all files open on tree connection and mark them invalid */
94 write_lock(&GlobalSMBSeslock);
95 list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
Steve French790fe572007-07-07 19:25:05 +000096 open_file = list_entry(tmp, struct cifsFileInfo, tlist);
Steve French26f57362007-08-30 22:09:15 +000097 if (open_file)
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 open_file->invalidHandle = TRUE;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 }
100 write_unlock(&GlobalSMBSeslock);
Steve French09d1db52005-04-28 22:41:08 -0700101 /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
102 to this tcon */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103}
104
Steve Frenchad7a2922008-02-07 23:25:02 +0000105/* Allocate and return pointer to an SMB request buffer, and set basic
106 SMB information in the SMB header. If the return code is zero, this
107 function must have filled in request_buf pointer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108static int
109small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000110 void **request_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
112 int rc = 0;
113
114 /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
115 check for tcp and smb session status done differently
116 for those three - in the calling routine */
Steve French790fe572007-07-07 19:25:05 +0000117 if (tcon) {
118 if (tcon->tidStatus == CifsExiting) {
Steve French6ab16d22005-11-29 20:55:11 -0800119 /* only tree disconnect, open, and write,
120 (and ulogoff which does not have tcon)
121 are allowed as we start force umount */
Steve French790fe572007-07-07 19:25:05 +0000122 if ((smb_command != SMB_COM_WRITE_ANDX) &&
Steve French50c2f752007-07-13 00:33:32 +0000123 (smb_command != SMB_COM_OPEN_ANDX) &&
Steve French6ab16d22005-11-29 20:55:11 -0800124 (smb_command != SMB_COM_TREE_DISCONNECT)) {
Steve French790fe572007-07-07 19:25:05 +0000125 cFYI(1, ("can not send cmd %d while umounting",
Steve French6ab16d22005-11-29 20:55:11 -0800126 smb_command));
127 return -ENODEV;
128 }
129 }
Steve French790fe572007-07-07 19:25:05 +0000130 if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
Steve French50c2f752007-07-13 00:33:32 +0000131 (tcon->ses->server)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 struct nls_table *nls_codepage;
Steve French50c2f752007-07-13 00:33:32 +0000133 /* Give Demultiplex thread up to 10 seconds to
Steve French09d1db52005-04-28 22:41:08 -0700134 reconnect, should be greater than cifs socket
135 timeout which is 7 seconds */
Steve Frenchc18c8422007-07-18 23:21:09 +0000136 while (tcon->ses->server->tcpStatus ==
Steve French63135e02007-07-17 17:34:02 +0000137 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 wait_event_interruptible_timeout(tcon->ses->server->response_q,
Steve Frenchc18c8422007-07-18 23:21:09 +0000139 (tcon->ses->server->tcpStatus ==
Steve French63135e02007-07-17 17:34:02 +0000140 CifsGood), 10 * HZ);
141 if (tcon->ses->server->tcpStatus ==
142 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 /* on "soft" mounts we wait once */
Steve French790fe572007-07-07 19:25:05 +0000144 if ((tcon->retry == FALSE) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 (tcon->ses->status == CifsExiting)) {
Steve French50c2f752007-07-13 00:33:32 +0000146 cFYI(1, ("gave up waiting on "
147 "reconnect in smb_init"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 return -EHOSTDOWN;
Steve French09d1db52005-04-28 22:41:08 -0700149 } /* else "hard" mount - keep retrying
150 until process is killed or server
151 comes back on-line */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 } else /* TCP session is reestablished now */
153 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 }
Steve French50c2f752007-07-13 00:33:32 +0000155
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 nls_codepage = load_nls_default();
157 /* need to prevent multiple threads trying to
158 simultaneously reconnect the same SMB session */
159 down(&tcon->ses->sesSem);
Steve French790fe572007-07-07 19:25:05 +0000160 if (tcon->ses->status == CifsNeedReconnect)
Steve French50c2f752007-07-13 00:33:32 +0000161 rc = cifs_setup_session(0, tcon->ses,
Steve French09d1db52005-04-28 22:41:08 -0700162 nls_codepage);
Steve French790fe572007-07-07 19:25:05 +0000163 if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 mark_open_files_invalid(tcon);
Steve French50c2f752007-07-13 00:33:32 +0000165 rc = CIFSTCon(0, tcon->ses, tcon->treeName,
Steve French8af18972007-02-14 04:42:51 +0000166 tcon, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 up(&tcon->ses->sesSem);
Steve French8af18972007-02-14 04:42:51 +0000168 /* tell server which Unix caps we support */
169 if (tcon->ses->capabilities & CAP_UNIX)
170 reset_cifs_unix_caps(0 /* no xid */,
Steve French50c2f752007-07-13 00:33:32 +0000171 tcon,
Steve French8af18972007-02-14 04:42:51 +0000172 NULL /* we do not know sb */,
Steve French50c2f752007-07-13 00:33:32 +0000173 NULL /* no vol info */);
Steve French3e844692005-10-03 13:37:24 -0700174 /* BB FIXME add code to check if wsize needs
175 update due to negotiated smb buffer size
176 shrinking */
Steve French790fe572007-07-07 19:25:05 +0000177 if (rc == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 atomic_inc(&tconInfoReconnectCount);
179
180 cFYI(1, ("reconnect tcon rc = %d", rc));
Steve French50c2f752007-07-13 00:33:32 +0000181 /* Removed call to reopen open files here.
182 It is safer (and faster) to reopen files
Steve French09d1db52005-04-28 22:41:08 -0700183 one at a time as needed in read and write */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
Steve French50c2f752007-07-13 00:33:32 +0000185 /* Check if handle based operation so we
Steve French09d1db52005-04-28 22:41:08 -0700186 know whether we can continue or not without
187 returning to caller to reset file handle */
Steve French50c2f752007-07-13 00:33:32 +0000188 switch (smb_command) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 case SMB_COM_READ_ANDX:
190 case SMB_COM_WRITE_ANDX:
191 case SMB_COM_CLOSE:
192 case SMB_COM_FIND_CLOSE2:
193 case SMB_COM_LOCKING_ANDX: {
194 unload_nls(nls_codepage);
195 return -EAGAIN;
196 }
197 }
198 } else {
199 up(&tcon->ses->sesSem);
200 }
201 unload_nls(nls_codepage);
202
203 } else {
204 return -EIO;
205 }
206 }
Steve French790fe572007-07-07 19:25:05 +0000207 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 return rc;
209
210 *request_buf = cifs_small_buf_get();
211 if (*request_buf == NULL) {
212 /* BB should we add a retry in here if not a writepage? */
213 return -ENOMEM;
214 }
215
Steve French63135e02007-07-17 17:34:02 +0000216 header_assemble((struct smb_hdr *) *request_buf, smb_command,
Steve Frenchc18c8422007-07-18 23:21:09 +0000217 tcon, wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
Steve French790fe572007-07-07 19:25:05 +0000219 if (tcon != NULL)
220 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700221
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 return rc;
Steve French5815449d2006-02-14 01:36:20 +0000223}
224
Steve French12b3b8f2006-02-09 21:12:47 +0000225int
Steve French50c2f752007-07-13 00:33:32 +0000226small_smb_init_no_tc(const int smb_command, const int wct,
Steve French5815449d2006-02-14 01:36:20 +0000227 struct cifsSesInfo *ses, void **request_buf)
Steve French12b3b8f2006-02-09 21:12:47 +0000228{
229 int rc;
Steve French50c2f752007-07-13 00:33:32 +0000230 struct smb_hdr *buffer;
Steve French12b3b8f2006-02-09 21:12:47 +0000231
Steve French5815449d2006-02-14 01:36:20 +0000232 rc = small_smb_init(smb_command, wct, NULL, request_buf);
Steve French790fe572007-07-07 19:25:05 +0000233 if (rc)
Steve French12b3b8f2006-02-09 21:12:47 +0000234 return rc;
235
Steve French04fdabe2006-02-10 05:52:50 +0000236 buffer = (struct smb_hdr *)*request_buf;
Steve French12b3b8f2006-02-09 21:12:47 +0000237 buffer->Mid = GetNextMid(ses->server);
238 if (ses->capabilities & CAP_UNICODE)
239 buffer->Flags2 |= SMBFLG2_UNICODE;
Steve French04fdabe2006-02-10 05:52:50 +0000240 if (ses->capabilities & CAP_STATUS32)
Steve French12b3b8f2006-02-09 21:12:47 +0000241 buffer->Flags2 |= SMBFLG2_ERR_STATUS;
242
243 /* uid, tid can stay at zero as set in header assemble */
244
Steve French50c2f752007-07-13 00:33:32 +0000245 /* BB add support for turning on the signing when
Steve French12b3b8f2006-02-09 21:12:47 +0000246 this function is used after 1st of session setup requests */
247
248 return rc;
249}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
251/* If the return code is zero, this function must fill in request_buf pointer */
252static int
253smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
254 void **request_buf /* returned */ ,
255 void **response_buf /* returned */ )
256{
257 int rc = 0;
258
259 /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
260 check for tcp and smb session status done differently
261 for those three - in the calling routine */
Steve French790fe572007-07-07 19:25:05 +0000262 if (tcon) {
263 if (tcon->tidStatus == CifsExiting) {
Steve French6ab16d22005-11-29 20:55:11 -0800264 /* only tree disconnect, open, and write,
265 (and ulogoff which does not have tcon)
266 are allowed as we start force umount */
Steve French790fe572007-07-07 19:25:05 +0000267 if ((smb_command != SMB_COM_WRITE_ANDX) &&
Steve French6ab16d22005-11-29 20:55:11 -0800268 (smb_command != SMB_COM_OPEN_ANDX) &&
269 (smb_command != SMB_COM_TREE_DISCONNECT)) {
Steve French790fe572007-07-07 19:25:05 +0000270 cFYI(1, ("can not send cmd %d while umounting",
Steve French6ab16d22005-11-29 20:55:11 -0800271 smb_command));
272 return -ENODEV;
273 }
274 }
275
Steve French790fe572007-07-07 19:25:05 +0000276 if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
Steve French50c2f752007-07-13 00:33:32 +0000277 (tcon->ses->server)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 struct nls_table *nls_codepage;
Steve French09d1db52005-04-28 22:41:08 -0700279 /* Give Demultiplex thread up to 10 seconds to
280 reconnect, should be greater than cifs socket
281 timeout which is 7 seconds */
Steve French63135e02007-07-17 17:34:02 +0000282 while (tcon->ses->server->tcpStatus ==
283 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 wait_event_interruptible_timeout(tcon->ses->server->response_q,
Steve French63135e02007-07-17 17:34:02 +0000285 (tcon->ses->server->tcpStatus ==
286 CifsGood), 10 * HZ);
Steve French790fe572007-07-07 19:25:05 +0000287 if (tcon->ses->server->tcpStatus ==
Steve French09d1db52005-04-28 22:41:08 -0700288 CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 /* on "soft" mounts we wait once */
Steve French790fe572007-07-07 19:25:05 +0000290 if ((tcon->retry == FALSE) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 (tcon->ses->status == CifsExiting)) {
Steve French50c2f752007-07-13 00:33:32 +0000292 cFYI(1, ("gave up waiting on "
293 "reconnect in smb_init"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 return -EHOSTDOWN;
Steve French09d1db52005-04-28 22:41:08 -0700295 } /* else "hard" mount - keep retrying
296 until process is killed or server
297 comes on-line */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 } else /* TCP session is reestablished now */
299 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 nls_codepage = load_nls_default();
302 /* need to prevent multiple threads trying to
303 simultaneously reconnect the same SMB session */
304 down(&tcon->ses->sesSem);
Steve French790fe572007-07-07 19:25:05 +0000305 if (tcon->ses->status == CifsNeedReconnect)
Steve French50c2f752007-07-13 00:33:32 +0000306 rc = cifs_setup_session(0, tcon->ses,
Steve French09d1db52005-04-28 22:41:08 -0700307 nls_codepage);
Steve French790fe572007-07-07 19:25:05 +0000308 if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 mark_open_files_invalid(tcon);
Steve French09d1db52005-04-28 22:41:08 -0700310 rc = CIFSTCon(0, tcon->ses, tcon->treeName,
311 tcon, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 up(&tcon->ses->sesSem);
Steve French8af18972007-02-14 04:42:51 +0000313 /* tell server which Unix caps we support */
314 if (tcon->ses->capabilities & CAP_UNIX)
315 reset_cifs_unix_caps(0 /* no xid */,
Steve French50c2f752007-07-13 00:33:32 +0000316 tcon,
Steve French8af18972007-02-14 04:42:51 +0000317 NULL /* do not know sb */,
318 NULL /* no vol info */);
Steve French3e844692005-10-03 13:37:24 -0700319 /* BB FIXME add code to check if wsize needs
320 update due to negotiated smb buffer size
321 shrinking */
Steve French790fe572007-07-07 19:25:05 +0000322 if (rc == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 atomic_inc(&tconInfoReconnectCount);
324
325 cFYI(1, ("reconnect tcon rc = %d", rc));
Steve French50c2f752007-07-13 00:33:32 +0000326 /* Removed call to reopen open files here.
327 It is safer (and faster) to reopen files
Steve French09d1db52005-04-28 22:41:08 -0700328 one at a time as needed in read and write */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
Steve French50c2f752007-07-13 00:33:32 +0000330 /* Check if handle based operation so we
Steve French09d1db52005-04-28 22:41:08 -0700331 know whether we can continue or not without
332 returning to caller to reset file handle */
Steve French50c2f752007-07-13 00:33:32 +0000333 switch (smb_command) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 case SMB_COM_READ_ANDX:
335 case SMB_COM_WRITE_ANDX:
336 case SMB_COM_CLOSE:
337 case SMB_COM_FIND_CLOSE2:
338 case SMB_COM_LOCKING_ANDX: {
339 unload_nls(nls_codepage);
340 return -EAGAIN;
341 }
342 }
343 } else {
344 up(&tcon->ses->sesSem);
345 }
346 unload_nls(nls_codepage);
347
348 } else {
349 return -EIO;
350 }
351 }
Steve French790fe572007-07-07 19:25:05 +0000352 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 return rc;
354
355 *request_buf = cifs_buf_get();
356 if (*request_buf == NULL) {
357 /* BB should we add a retry in here if not a writepage? */
358 return -ENOMEM;
359 }
360 /* Although the original thought was we needed the response buf for */
361 /* potential retries of smb operations it turns out we can determine */
362 /* from the mid flags when the request buffer can be resent without */
363 /* having to use a second distinct buffer for the response */
Steve French790fe572007-07-07 19:25:05 +0000364 if (response_buf)
Steve French50c2f752007-07-13 00:33:32 +0000365 *response_buf = *request_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
367 header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000368 wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369
Steve French790fe572007-07-07 19:25:05 +0000370 if (tcon != NULL)
371 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700372
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 return rc;
374}
375
Steve French50c2f752007-07-13 00:33:32 +0000376static int validate_t2(struct smb_t2_rsp *pSMB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377{
378 int rc = -EINVAL;
379 int total_size;
Steve French50c2f752007-07-13 00:33:32 +0000380 char *pBCC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
382 /* check for plausible wct, bcc and t2 data and parm sizes */
383 /* check for parm and data offset going beyond end of smb */
Steve French790fe572007-07-07 19:25:05 +0000384 if (pSMB->hdr.WordCount >= 10) {
385 if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
387 /* check that bcc is at least as big as parms + data */
388 /* check that bcc is less than negotiated smb buffer */
389 total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
Steve French790fe572007-07-07 19:25:05 +0000390 if (total_size < 512) {
Steve Frenchc18c8422007-07-18 23:21:09 +0000391 total_size +=
Steve French63135e02007-07-17 17:34:02 +0000392 le16_to_cpu(pSMB->t2_rsp.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 /* BCC le converted in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +0000394 pBCC = (pSMB->hdr.WordCount * 2) +
Steve French09d1db52005-04-28 22:41:08 -0700395 sizeof(struct smb_hdr) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 (char *)pSMB;
Steve French790fe572007-07-07 19:25:05 +0000397 if ((total_size <= (*(u16 *)pBCC)) &&
Steve French50c2f752007-07-13 00:33:32 +0000398 (total_size <
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
400 return 0;
401 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 }
403 }
404 }
Steve French50c2f752007-07-13 00:33:32 +0000405 cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 sizeof(struct smb_t2_rsp) + 16);
407 return rc;
408}
409int
410CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
411{
412 NEGOTIATE_REQ *pSMB;
413 NEGOTIATE_RSP *pSMBr;
414 int rc = 0;
415 int bytes_returned;
Steve French39798772006-05-31 22:40:51 +0000416 int i;
Steve French50c2f752007-07-13 00:33:32 +0000417 struct TCP_Server_Info *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 u16 count;
Steve French750d1152006-06-27 06:28:30 +0000419 unsigned int secFlags;
Al Viro733f99a2006-10-14 16:48:26 +0100420 u16 dialect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
Steve French790fe572007-07-07 19:25:05 +0000422 if (ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 server = ses->server;
424 else {
425 rc = -EIO;
426 return rc;
427 }
428 rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
429 (void **) &pSMB, (void **) &pSMBr);
430 if (rc)
431 return rc;
Steve French750d1152006-06-27 06:28:30 +0000432
433 /* if any of auth flags (ie not sign or seal) are overriden use them */
Steve French790fe572007-07-07 19:25:05 +0000434 if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
Steve French762e5ab2007-06-28 18:41:42 +0000435 secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */
Steve French750d1152006-06-27 06:28:30 +0000436 else /* if override flags set only sign/seal OR them with global auth */
437 secFlags = extended_security | ses->overrideSecFlg;
438
Steve French762e5ab2007-06-28 18:41:42 +0000439 cFYI(1, ("secFlags 0x%x", secFlags));
Steve Frenchf40c5622006-06-28 00:13:38 +0000440
Steve French1982c342005-08-17 12:38:22 -0700441 pSMB->hdr.Mid = GetNextMid(server);
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000442 pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
Steve Frencha0136892007-10-04 20:05:09 +0000443
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000444 if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
Steve French254e55e2006-06-04 05:53:15 +0000445 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Steve Frencha0136892007-10-04 20:05:09 +0000446 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
447 cFYI(1, ("Kerberos only mechanism, enable extended security"));
448 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
449 }
Steve French50c2f752007-07-13 00:33:32 +0000450
Steve French39798772006-05-31 22:40:51 +0000451 count = 0;
Steve French50c2f752007-07-13 00:33:32 +0000452 for (i = 0; i < CIFS_NUM_PROT; i++) {
Steve French39798772006-05-31 22:40:51 +0000453 strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
454 count += strlen(protocols[i].name) + 1;
455 /* null at end of source and target buffers anyway */
456 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 pSMB->hdr.smb_buf_length += count;
458 pSMB->ByteCount = cpu_to_le16(count);
459
460 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
461 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French50c2f752007-07-13 00:33:32 +0000462 if (rc != 0)
Steve French254e55e2006-06-04 05:53:15 +0000463 goto neg_err_exit;
464
Al Viro733f99a2006-10-14 16:48:26 +0100465 dialect = le16_to_cpu(pSMBr->DialectIndex);
Steve French790fe572007-07-07 19:25:05 +0000466 cFYI(1, ("Dialect: %d", dialect));
Steve French254e55e2006-06-04 05:53:15 +0000467 /* Check wct = 1 error case */
Steve French790fe572007-07-07 19:25:05 +0000468 if ((pSMBr->hdr.WordCount < 13) || (dialect == BAD_PROT)) {
Steve French254e55e2006-06-04 05:53:15 +0000469 /* core returns wct = 1, but we do not ask for core - otherwise
Steve French50c2f752007-07-13 00:33:32 +0000470 small wct just comes when dialect index is -1 indicating we
Steve French254e55e2006-06-04 05:53:15 +0000471 could not negotiate a common dialect */
472 rc = -EOPNOTSUPP;
473 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000474#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French790fe572007-07-07 19:25:05 +0000475 } else if ((pSMBr->hdr.WordCount == 13)
Al Viro733f99a2006-10-14 16:48:26 +0100476 && ((dialect == LANMAN_PROT)
477 || (dialect == LANMAN2_PROT))) {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000478 __s16 tmp;
Steve French50c2f752007-07-13 00:33:32 +0000479 struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
Steve French254e55e2006-06-04 05:53:15 +0000480
Steve French790fe572007-07-07 19:25:05 +0000481 if ((secFlags & CIFSSEC_MAY_LANMAN) ||
Steve French750d1152006-06-27 06:28:30 +0000482 (secFlags & CIFSSEC_MAY_PLNTXT))
Steve French254e55e2006-06-04 05:53:15 +0000483 server->secType = LANMAN;
484 else {
485 cERROR(1, ("mount failed weak security disabled"
486 " in /proc/fs/cifs/SecurityFlags"));
Steve French39798772006-05-31 22:40:51 +0000487 rc = -EOPNOTSUPP;
488 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000489 }
Steve French254e55e2006-06-04 05:53:15 +0000490 server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
491 server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
492 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
Steve French39798772006-05-31 22:40:51 +0000493 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve French254e55e2006-06-04 05:53:15 +0000494 GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
495 /* even though we do not use raw we might as well set this
496 accurately, in case we ever find a need for it */
Steve French790fe572007-07-07 19:25:05 +0000497 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
Steve French254e55e2006-06-04 05:53:15 +0000498 server->maxRw = 0xFF00;
499 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
500 } else {
501 server->maxRw = 0;/* we do not need to use raw anyway */
502 server->capabilities = CAP_MPX_MODE;
503 }
Steve Frenchb815f1e52006-10-02 05:53:29 +0000504 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
Steve French1a70d652006-10-02 05:59:18 +0000505 if (tmp == -1) {
Steve French25ee4a92006-09-30 00:54:23 +0000506 /* OS/2 often does not set timezone therefore
507 * we must use server time to calc time zone.
Steve Frenchb815f1e52006-10-02 05:53:29 +0000508 * Could deviate slightly from the right zone.
509 * Smallest defined timezone difference is 15 minutes
510 * (i.e. Nepal). Rounding up/down is done to match
511 * this requirement.
Steve French25ee4a92006-09-30 00:54:23 +0000512 */
Steve Frenchb815f1e52006-10-02 05:53:29 +0000513 int val, seconds, remain, result;
Steve French25ee4a92006-09-30 00:54:23 +0000514 struct timespec ts, utc;
515 utc = CURRENT_TIME;
516 ts = cnvrtDosUnixTm(le16_to_cpu(rsp->SrvTime.Date),
517 le16_to_cpu(rsp->SrvTime.Time));
Steve French50c2f752007-07-13 00:33:32 +0000518 cFYI(1, ("SrvTime %d sec since 1970 (utc: %d) diff: %d",
519 (int)ts.tv_sec, (int)utc.tv_sec,
Steve French25ee4a92006-09-30 00:54:23 +0000520 (int)(utc.tv_sec - ts.tv_sec)));
Steve Frenchb815f1e52006-10-02 05:53:29 +0000521 val = (int)(utc.tv_sec - ts.tv_sec);
Andre Haupt8594c152007-08-30 20:18:41 +0000522 seconds = abs(val);
Steve French947a5062006-10-02 05:55:25 +0000523 result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000524 remain = seconds % MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000525 if (remain >= (MIN_TZ_ADJ / 2))
Steve Frenchb815f1e52006-10-02 05:53:29 +0000526 result += MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000527 if (val < 0)
Steve Frenchad7a2922008-02-07 23:25:02 +0000528 result = -result;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000529 server->timeAdj = result;
Steve French25ee4a92006-09-30 00:54:23 +0000530 } else {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000531 server->timeAdj = (int)tmp;
532 server->timeAdj *= 60; /* also in seconds */
Steve French25ee4a92006-09-30 00:54:23 +0000533 }
Steve French790fe572007-07-07 19:25:05 +0000534 cFYI(1, ("server->timeAdj: %d seconds", server->timeAdj));
Steve French25ee4a92006-09-30 00:54:23 +0000535
Steve French39798772006-05-31 22:40:51 +0000536
Steve French254e55e2006-06-04 05:53:15 +0000537 /* BB get server time for time conversions and add
Steve French50c2f752007-07-13 00:33:32 +0000538 code to use it and timezone since this is not UTC */
Steve French39798772006-05-31 22:40:51 +0000539
Steve French50c2f752007-07-13 00:33:32 +0000540 if (rsp->EncryptionKeyLength ==
Steve French25ee4a92006-09-30 00:54:23 +0000541 cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
Steve French254e55e2006-06-04 05:53:15 +0000542 memcpy(server->cryptKey, rsp->EncryptionKey,
543 CIFS_CRYPTO_KEY_SIZE);
544 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
545 rc = -EIO; /* need cryptkey unless plain text */
546 goto neg_err_exit;
547 }
Steve French39798772006-05-31 22:40:51 +0000548
Steve French790fe572007-07-07 19:25:05 +0000549 cFYI(1, ("LANMAN negotiated"));
Steve French254e55e2006-06-04 05:53:15 +0000550 /* we will not end up setting signing flags - as no signing
551 was in LANMAN and server did not return the flags on */
552 goto signing_check;
Steve French7c7b25b2006-06-01 19:20:10 +0000553#else /* weak security disabled */
Steve French790fe572007-07-07 19:25:05 +0000554 } else if (pSMBr->hdr.WordCount == 13) {
Steve French50c2f752007-07-13 00:33:32 +0000555 cERROR(1, ("mount failed, cifs module not built "
Steve French254e55e2006-06-04 05:53:15 +0000556 "with CIFS_WEAK_PW_HASH support"));
Steve French7c7b25b2006-06-01 19:20:10 +0000557 rc = -EOPNOTSUPP;
558#endif /* WEAK_PW_HASH */
Steve French254e55e2006-06-04 05:53:15 +0000559 goto neg_err_exit;
Steve French790fe572007-07-07 19:25:05 +0000560 } else if (pSMBr->hdr.WordCount != 17) {
Steve French254e55e2006-06-04 05:53:15 +0000561 /* unknown wct */
562 rc = -EOPNOTSUPP;
563 goto neg_err_exit;
564 }
565 /* else wct == 17 NTLM */
566 server->secMode = pSMBr->SecurityMode;
Steve French790fe572007-07-07 19:25:05 +0000567 if ((server->secMode & SECMODE_USER) == 0)
568 cFYI(1, ("share mode security"));
Steve French39798772006-05-31 22:40:51 +0000569
Steve French790fe572007-07-07 19:25:05 +0000570 if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000571#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French750d1152006-06-27 06:28:30 +0000572 if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000573#endif /* CIFS_WEAK_PW_HASH */
Steve French50c2f752007-07-13 00:33:32 +0000574 cERROR(1, ("Server requests plain text password"
Steve French254e55e2006-06-04 05:53:15 +0000575 " but client support disabled"));
Steve French9312f672006-06-04 22:21:07 +0000576
Steve French790fe572007-07-07 19:25:05 +0000577 if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
Steve French254e55e2006-06-04 05:53:15 +0000578 server->secType = NTLMv2;
Steve French790fe572007-07-07 19:25:05 +0000579 else if (secFlags & CIFSSEC_MAY_NTLM)
Steve French254e55e2006-06-04 05:53:15 +0000580 server->secType = NTLM;
Steve French790fe572007-07-07 19:25:05 +0000581 else if (secFlags & CIFSSEC_MAY_NTLMV2)
Steve Frenchf40c5622006-06-28 00:13:38 +0000582 server->secType = NTLMv2;
Steve Frencha0136892007-10-04 20:05:09 +0000583 else if (secFlags & CIFSSEC_MAY_KRB5)
584 server->secType = Kerberos;
585 else if (secFlags & CIFSSEC_MAY_LANMAN)
586 server->secType = LANMAN;
587/* #ifdef CONFIG_CIFS_EXPERIMENTAL
588 else if (secFlags & CIFSSEC_MAY_PLNTXT)
589 server->secType = ??
590#endif */
591 else {
592 rc = -EOPNOTSUPP;
593 cERROR(1, ("Invalid security type"));
594 goto neg_err_exit;
595 }
596 /* else ... any others ...? */
Steve French7c7b25b2006-06-01 19:20:10 +0000597
Steve French254e55e2006-06-04 05:53:15 +0000598 /* one byte, so no need to convert this or EncryptionKeyLen from
599 little endian */
600 server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
601 /* probably no need to store and check maxvcs */
602 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve French254e55e2006-06-04 05:53:15 +0000604 server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
Steve French90c81e02008-02-12 20:32:36 +0000605 cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf));
Steve French254e55e2006-06-04 05:53:15 +0000606 GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
607 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
Steve Frenchb815f1e52006-10-02 05:53:29 +0000608 server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
609 server->timeAdj *= 60;
Steve French254e55e2006-06-04 05:53:15 +0000610 if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
611 memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
612 CIFS_CRYPTO_KEY_SIZE);
613 } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
614 && (pSMBr->EncryptionKeyLength == 0)) {
615 /* decode security blob */
616 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
617 rc = -EIO; /* no crypt key only if plain text pwd */
618 goto neg_err_exit;
619 }
620
621 /* BB might be helpful to save off the domain of server here */
622
Steve French50c2f752007-07-13 00:33:32 +0000623 if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
Steve French254e55e2006-06-04 05:53:15 +0000624 (server->capabilities & CAP_EXTENDED_SECURITY)) {
625 count = pSMBr->ByteCount;
Jeff Laytone187e442007-10-16 17:10:44 +0000626 if (count < 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 rc = -EIO;
Jeff Laytone187e442007-10-16 17:10:44 +0000628 goto neg_err_exit;
629 }
630
631 if (server->socketUseCount.counter > 1) {
632 if (memcmp(server->server_GUID,
633 pSMBr->u.extended_response.
634 GUID, 16) != 0) {
635 cFYI(1, ("server UID changed"));
Steve French254e55e2006-06-04 05:53:15 +0000636 memcpy(server->server_GUID,
Jeff Laytone187e442007-10-16 17:10:44 +0000637 pSMBr->u.extended_response.GUID,
638 16);
639 }
640 } else
641 memcpy(server->server_GUID,
642 pSMBr->u.extended_response.GUID, 16);
643
644 if (count == 16) {
645 server->secType = RawNTLMSSP;
Steve French254e55e2006-06-04 05:53:15 +0000646 } else {
647 rc = decode_negTokenInit(pSMBr->u.extended_response.
648 SecurityBlob,
649 count - 16,
650 &server->secType);
Steve French790fe572007-07-07 19:25:05 +0000651 if (rc == 1) {
Jeff Laytone5459372007-11-03 05:11:06 +0000652 rc = 0;
Steve French254e55e2006-06-04 05:53:15 +0000653 } else {
654 rc = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 }
Steve French254e55e2006-06-04 05:53:15 +0000657 } else
658 server->capabilities &= ~CAP_EXTENDED_SECURITY;
659
Steve French6344a422006-06-12 04:18:35 +0000660#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French254e55e2006-06-04 05:53:15 +0000661signing_check:
Steve French6344a422006-06-12 04:18:35 +0000662#endif
Steve French762e5ab2007-06-28 18:41:42 +0000663 if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
664 /* MUST_SIGN already includes the MAY_SIGN FLAG
665 so if this is zero it means that signing is disabled */
666 cFYI(1, ("Signing disabled"));
Steve Frenchabb63d62007-10-18 02:58:40 +0000667 if (server->secMode & SECMODE_SIGN_REQUIRED) {
Steve French762e5ab2007-06-28 18:41:42 +0000668 cERROR(1, ("Server requires "
Jeff Layton7111d212007-10-16 16:50:25 +0000669 "packet signing to be enabled in "
670 "/proc/fs/cifs/SecurityFlags."));
Steve Frenchabb63d62007-10-18 02:58:40 +0000671 rc = -EOPNOTSUPP;
672 }
Steve French50c2f752007-07-13 00:33:32 +0000673 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000674 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Steve French762e5ab2007-06-28 18:41:42 +0000675 } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
676 /* signing required */
Jeff38c10a12007-07-06 21:10:07 +0000677 cFYI(1, ("Must sign - secFlags 0x%x", secFlags));
Steve French762e5ab2007-06-28 18:41:42 +0000678 if ((server->secMode &
679 (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
680 cERROR(1,
681 ("signing required but server lacks support"));
Jeff38c10a12007-07-06 21:10:07 +0000682 rc = -EOPNOTSUPP;
Steve French762e5ab2007-06-28 18:41:42 +0000683 } else
684 server->secMode |= SECMODE_SIGN_REQUIRED;
685 } else {
686 /* signing optional ie CIFSSEC_MAY_SIGN */
Steve French790fe572007-07-07 19:25:05 +0000687 if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
Steve French50c2f752007-07-13 00:33:32 +0000688 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000689 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 }
Steve French50c2f752007-07-13 00:33:32 +0000691
692neg_err_exit:
Steve French4a6d87f2005-08-13 08:15:54 -0700693 cifs_buf_release(pSMB);
Steve French254e55e2006-06-04 05:53:15 +0000694
Steve French790fe572007-07-07 19:25:05 +0000695 cFYI(1, ("negprot rc %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 return rc;
697}
698
699int
700CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
701{
702 struct smb_hdr *smb_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
705 cFYI(1, ("In tree disconnect"));
706 /*
707 * If last user of the connection and
708 * connection alive - disconnect it
709 * If this is the last connection on the server session disconnect it
Steve French50c2f752007-07-13 00:33:32 +0000710 * (and inside session disconnect we should check if tcp socket needs
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 * to be freed and kernel thread woken up).
712 */
713 if (tcon)
714 down(&tcon->tconSem);
715 else
716 return -EIO;
717
718 atomic_dec(&tcon->useCount);
719 if (atomic_read(&tcon->useCount) > 0) {
720 up(&tcon->tconSem);
721 return -EBUSY;
722 }
723
Steve French50c2f752007-07-13 00:33:32 +0000724 /* No need to return error on this operation if tid invalidated and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 closed on server already e.g. due to tcp session crashing */
Steve French790fe572007-07-07 19:25:05 +0000726 if (tcon->tidStatus == CifsNeedReconnect) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 up(&tcon->tconSem);
Steve French50c2f752007-07-13 00:33:32 +0000728 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 }
730
Steve French790fe572007-07-07 19:25:05 +0000731 if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 up(&tcon->tconSem);
733 return -EIO;
734 }
Steve French50c2f752007-07-13 00:33:32 +0000735 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
Steve French09d1db52005-04-28 22:41:08 -0700736 (void **)&smb_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 if (rc) {
738 up(&tcon->tconSem);
739 return rc;
Steve Frenchcd634992005-04-28 22:41:10 -0700740 }
Steve French133672e2007-11-13 22:41:37 +0000741
742 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 if (rc)
Steve French966ca922005-04-28 22:41:08 -0700744 cFYI(1, ("Tree disconnect failed %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 up(&tcon->tconSem);
747
Steve French50c2f752007-07-13 00:33:32 +0000748 /* No need to return error on this operation if tid invalidated and
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 closed on server already e.g. due to tcp session crashing */
750 if (rc == -EAGAIN)
751 rc = 0;
752
753 return rc;
754}
755
756int
757CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
758{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 LOGOFF_ANDX_REQ *pSMB;
760 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
762 cFYI(1, ("In SMBLogoff for session disconnect"));
763 if (ses)
764 down(&ses->sesSem);
765 else
766 return -EIO;
767
768 atomic_dec(&ses->inUse);
769 if (atomic_read(&ses->inUse) > 0) {
770 up(&ses->sesSem);
771 return -EBUSY;
772 }
773 rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
774 if (rc) {
775 up(&ses->sesSem);
776 return rc;
777 }
778
Steve French790fe572007-07-07 19:25:05 +0000779 if (ses->server) {
Steve French1982c342005-08-17 12:38:22 -0700780 pSMB->hdr.Mid = GetNextMid(ses->server);
781
Steve French790fe572007-07-07 19:25:05 +0000782 if (ses->server->secMode &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
784 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
785 }
786
787 pSMB->hdr.Uid = ses->Suid;
788
789 pSMB->AndXCommand = 0xFF;
Steve French133672e2007-11-13 22:41:37 +0000790 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 if (ses->server) {
792 atomic_dec(&ses->server->socketUseCount);
793 if (atomic_read(&ses->server->socketUseCount) == 0) {
794 spin_lock(&GlobalMid_Lock);
795 ses->server->tcpStatus = CifsExiting;
796 spin_unlock(&GlobalMid_Lock);
797 rc = -ESHUTDOWN;
798 }
799 }
Steve Frencha59c6582005-08-17 12:12:19 -0700800 up(&ses->sesSem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
802 /* if session dead then we do not need to do ulogoff,
Steve French50c2f752007-07-13 00:33:32 +0000803 since server closed smb session, no sense reporting
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 error */
805 if (rc == -EAGAIN)
806 rc = 0;
807 return rc;
808}
809
810int
Steve French2d785a52007-07-15 01:48:57 +0000811CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
812 __u16 type, const struct nls_table *nls_codepage, int remap)
813{
814 TRANSACTION2_SPI_REQ *pSMB = NULL;
815 TRANSACTION2_SPI_RSP *pSMBr = NULL;
816 struct unlink_psx_rq *pRqD;
817 int name_len;
818 int rc = 0;
819 int bytes_returned = 0;
820 __u16 params, param_offset, offset, byte_count;
821
822 cFYI(1, ("In POSIX delete"));
823PsxDelete:
824 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
825 (void **) &pSMBr);
826 if (rc)
827 return rc;
828
829 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
830 name_len =
831 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
832 PATH_MAX, nls_codepage, remap);
833 name_len++; /* trailing null */
834 name_len *= 2;
835 } else { /* BB add path length overrun check */
836 name_len = strnlen(fileName, PATH_MAX);
837 name_len++; /* trailing null */
838 strncpy(pSMB->FileName, fileName, name_len);
839 }
840
841 params = 6 + name_len;
842 pSMB->MaxParameterCount = cpu_to_le16(2);
843 pSMB->MaxDataCount = 0; /* BB double check this with jra */
844 pSMB->MaxSetupCount = 0;
845 pSMB->Reserved = 0;
846 pSMB->Flags = 0;
847 pSMB->Timeout = 0;
848 pSMB->Reserved2 = 0;
849 param_offset = offsetof(struct smb_com_transaction2_spi_req,
850 InformationLevel) - 4;
851 offset = param_offset + params;
852
853 /* Setup pointer to Request Data (inode type) */
854 pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
855 pRqD->type = cpu_to_le16(type);
856 pSMB->ParameterOffset = cpu_to_le16(param_offset);
857 pSMB->DataOffset = cpu_to_le16(offset);
858 pSMB->SetupCount = 1;
859 pSMB->Reserved3 = 0;
860 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
861 byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
862
863 pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
864 pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
865 pSMB->ParameterCount = cpu_to_le16(params);
866 pSMB->TotalParameterCount = pSMB->ParameterCount;
867 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
868 pSMB->Reserved4 = 0;
869 pSMB->hdr.smb_buf_length += byte_count;
870 pSMB->ByteCount = cpu_to_le16(byte_count);
871 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
872 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +0000873 if (rc)
Steve French2d785a52007-07-15 01:48:57 +0000874 cFYI(1, ("Posix delete returned %d", rc));
Steve French2d785a52007-07-15 01:48:57 +0000875 cifs_buf_release(pSMB);
876
877 cifs_stats_inc(&tcon->num_deletes);
878
879 if (rc == -EAGAIN)
880 goto PsxDelete;
881
882 return rc;
883}
884
885int
Steve French737b7582005-04-28 22:41:06 -0700886CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
887 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888{
889 DELETE_FILE_REQ *pSMB = NULL;
890 DELETE_FILE_RSP *pSMBr = NULL;
891 int rc = 0;
892 int bytes_returned;
893 int name_len;
894
895DelFileRetry:
896 rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
897 (void **) &pSMBr);
898 if (rc)
899 return rc;
900
901 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
902 name_len =
Steve French50c2f752007-07-13 00:33:32 +0000903 cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
Steve French737b7582005-04-28 22:41:06 -0700904 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 name_len++; /* trailing null */
906 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700907 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 name_len = strnlen(fileName, PATH_MAX);
909 name_len++; /* trailing null */
910 strncpy(pSMB->fileName, fileName, name_len);
911 }
912 pSMB->SearchAttributes =
913 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
914 pSMB->BufferFormat = 0x04;
915 pSMB->hdr.smb_buf_length += name_len + 1;
916 pSMB->ByteCount = cpu_to_le16(name_len + 1);
917 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
918 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700919 cifs_stats_inc(&tcon->num_deletes);
Steve Frenchad7a2922008-02-07 23:25:02 +0000920 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 cFYI(1, ("Error in RMFile = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
923 cifs_buf_release(pSMB);
924 if (rc == -EAGAIN)
925 goto DelFileRetry;
926
927 return rc;
928}
929
930int
Steve French50c2f752007-07-13 00:33:32 +0000931CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
Steve French737b7582005-04-28 22:41:06 -0700932 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933{
934 DELETE_DIRECTORY_REQ *pSMB = NULL;
935 DELETE_DIRECTORY_RSP *pSMBr = NULL;
936 int rc = 0;
937 int bytes_returned;
938 int name_len;
939
940 cFYI(1, ("In CIFSSMBRmDir"));
941RmDirRetry:
942 rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
943 (void **) &pSMBr);
944 if (rc)
945 return rc;
946
947 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French737b7582005-04-28 22:41:06 -0700948 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
949 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 name_len++; /* trailing null */
951 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700952 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 name_len = strnlen(dirName, PATH_MAX);
954 name_len++; /* trailing null */
955 strncpy(pSMB->DirName, dirName, name_len);
956 }
957
958 pSMB->BufferFormat = 0x04;
959 pSMB->hdr.smb_buf_length += name_len + 1;
960 pSMB->ByteCount = cpu_to_le16(name_len + 1);
961 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
962 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700963 cifs_stats_inc(&tcon->num_rmdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000964 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 cFYI(1, ("Error in RMDir = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
967 cifs_buf_release(pSMB);
968 if (rc == -EAGAIN)
969 goto RmDirRetry;
970 return rc;
971}
972
973int
974CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -0700975 const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976{
977 int rc = 0;
978 CREATE_DIRECTORY_REQ *pSMB = NULL;
979 CREATE_DIRECTORY_RSP *pSMBr = NULL;
980 int bytes_returned;
981 int name_len;
982
983 cFYI(1, ("In CIFSSMBMkDir"));
984MkDirRetry:
985 rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
986 (void **) &pSMBr);
987 if (rc)
988 return rc;
989
990 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +0000991 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
Steve French737b7582005-04-28 22:41:06 -0700992 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 name_len++; /* trailing null */
994 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700995 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 name_len = strnlen(name, PATH_MAX);
997 name_len++; /* trailing null */
998 strncpy(pSMB->DirName, name, name_len);
999 }
1000
1001 pSMB->BufferFormat = 0x04;
1002 pSMB->hdr.smb_buf_length += name_len + 1;
1003 pSMB->ByteCount = cpu_to_le16(name_len + 1);
1004 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1005 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001006 cifs_stats_inc(&tcon->num_mkdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +00001007 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 cFYI(1, ("Error in Mkdir = %d", rc));
Steve Frencha5a2b482005-08-20 21:42:53 -07001009
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 cifs_buf_release(pSMB);
1011 if (rc == -EAGAIN)
1012 goto MkDirRetry;
1013 return rc;
1014}
1015
Steve French2dd29d32007-04-23 22:07:35 +00001016int
1017CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
Steve Frenchad7a2922008-02-07 23:25:02 +00001018 __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
Steve French50c2f752007-07-13 00:33:32 +00001019 __u32 *pOplock, const char *name,
Steve French2dd29d32007-04-23 22:07:35 +00001020 const struct nls_table *nls_codepage, int remap)
1021{
1022 TRANSACTION2_SPI_REQ *pSMB = NULL;
1023 TRANSACTION2_SPI_RSP *pSMBr = NULL;
1024 int name_len;
1025 int rc = 0;
1026 int bytes_returned = 0;
Steve French2dd29d32007-04-23 22:07:35 +00001027 __u16 params, param_offset, offset, byte_count, count;
Steve Frenchad7a2922008-02-07 23:25:02 +00001028 OPEN_PSX_REQ *pdata;
1029 OPEN_PSX_RSP *psx_rsp;
Steve French2dd29d32007-04-23 22:07:35 +00001030
1031 cFYI(1, ("In POSIX Create"));
1032PsxCreat:
1033 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
1034 (void **) &pSMBr);
1035 if (rc)
1036 return rc;
1037
1038 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1039 name_len =
1040 cifsConvertToUCS((__le16 *) pSMB->FileName, name,
1041 PATH_MAX, nls_codepage, remap);
1042 name_len++; /* trailing null */
1043 name_len *= 2;
1044 } else { /* BB improve the check for buffer overruns BB */
1045 name_len = strnlen(name, PATH_MAX);
1046 name_len++; /* trailing null */
1047 strncpy(pSMB->FileName, name, name_len);
1048 }
1049
1050 params = 6 + name_len;
1051 count = sizeof(OPEN_PSX_REQ);
1052 pSMB->MaxParameterCount = cpu_to_le16(2);
1053 pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
1054 pSMB->MaxSetupCount = 0;
1055 pSMB->Reserved = 0;
1056 pSMB->Flags = 0;
1057 pSMB->Timeout = 0;
1058 pSMB->Reserved2 = 0;
1059 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00001060 InformationLevel) - 4;
Steve French2dd29d32007-04-23 22:07:35 +00001061 offset = param_offset + params;
Steve French2dd29d32007-04-23 22:07:35 +00001062 pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001063 pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
Steve French2dd29d32007-04-23 22:07:35 +00001064 pdata->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00001065 pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
Steve French2dd29d32007-04-23 22:07:35 +00001066 pdata->OpenFlags = cpu_to_le32(*pOplock);
1067 pSMB->ParameterOffset = cpu_to_le16(param_offset);
1068 pSMB->DataOffset = cpu_to_le16(offset);
1069 pSMB->SetupCount = 1;
1070 pSMB->Reserved3 = 0;
1071 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
1072 byte_count = 3 /* pad */ + params + count;
1073
1074 pSMB->DataCount = cpu_to_le16(count);
1075 pSMB->ParameterCount = cpu_to_le16(params);
1076 pSMB->TotalDataCount = pSMB->DataCount;
1077 pSMB->TotalParameterCount = pSMB->ParameterCount;
1078 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1079 pSMB->Reserved4 = 0;
Steve French50c2f752007-07-13 00:33:32 +00001080 pSMB->hdr.smb_buf_length += byte_count;
Steve French2dd29d32007-04-23 22:07:35 +00001081 pSMB->ByteCount = cpu_to_le16(byte_count);
1082 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1083 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1084 if (rc) {
1085 cFYI(1, ("Posix create returned %d", rc));
1086 goto psx_create_err;
1087 }
1088
Steve French790fe572007-07-07 19:25:05 +00001089 cFYI(1, ("copying inode info"));
Steve French2dd29d32007-04-23 22:07:35 +00001090 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1091
1092 if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
1093 rc = -EIO; /* bad smb */
1094 goto psx_create_err;
1095 }
1096
1097 /* copy return information to pRetData */
Steve French50c2f752007-07-13 00:33:32 +00001098 psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
Steve French2dd29d32007-04-23 22:07:35 +00001099 + le16_to_cpu(pSMBr->t2.DataOffset));
Steve French50c2f752007-07-13 00:33:32 +00001100
Steve French2dd29d32007-04-23 22:07:35 +00001101 *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
Steve French790fe572007-07-07 19:25:05 +00001102 if (netfid)
Steve French2dd29d32007-04-23 22:07:35 +00001103 *netfid = psx_rsp->Fid; /* cifs fid stays in le */
1104 /* Let caller know file was created so we can set the mode. */
1105 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001106 if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
Steve French2dd29d32007-04-23 22:07:35 +00001107 *pOplock |= CIFS_CREATE_ACTION;
1108 /* check to make sure response data is there */
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001109 if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1110 pRetData->Type = cpu_to_le32(-1); /* unknown */
Steve French90c81e02008-02-12 20:32:36 +00001111 cFYI(DBG2, ("unknown type"));
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001112 } else {
Steve French790fe572007-07-07 19:25:05 +00001113 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001114 + sizeof(FILE_UNIX_BASIC_INFO)) {
Steve French50c2f752007-07-13 00:33:32 +00001115 cERROR(1, ("Open response data too small"));
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001116 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001117 goto psx_create_err;
1118 }
Steve French50c2f752007-07-13 00:33:32 +00001119 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001120 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001121 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001122 }
Steve French2dd29d32007-04-23 22:07:35 +00001123
1124psx_create_err:
1125 cifs_buf_release(pSMB);
1126
1127 cifs_stats_inc(&tcon->num_mkdirs);
1128
1129 if (rc == -EAGAIN)
1130 goto PsxCreat;
1131
Steve French50c2f752007-07-13 00:33:32 +00001132 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001133}
1134
Steve Frencha9d02ad2005-08-24 23:06:05 -07001135static __u16 convert_disposition(int disposition)
1136{
1137 __u16 ofun = 0;
1138
1139 switch (disposition) {
1140 case FILE_SUPERSEDE:
1141 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1142 break;
1143 case FILE_OPEN:
1144 ofun = SMBOPEN_OAPPEND;
1145 break;
1146 case FILE_CREATE:
1147 ofun = SMBOPEN_OCREATE;
1148 break;
1149 case FILE_OPEN_IF:
1150 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1151 break;
1152 case FILE_OVERWRITE:
1153 ofun = SMBOPEN_OTRUNC;
1154 break;
1155 case FILE_OVERWRITE_IF:
1156 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1157 break;
1158 default:
Steve French790fe572007-07-07 19:25:05 +00001159 cFYI(1, ("unknown disposition %d", disposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001160 ofun = SMBOPEN_OAPPEND; /* regular open */
1161 }
1162 return ofun;
1163}
1164
1165int
1166SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1167 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001168 const int access_flags, const int create_options, __u16 *netfid,
1169 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001170 const struct nls_table *nls_codepage, int remap)
1171{
1172 int rc = -EACCES;
1173 OPENX_REQ *pSMB = NULL;
1174 OPENX_RSP *pSMBr = NULL;
1175 int bytes_returned;
1176 int name_len;
1177 __u16 count;
1178
1179OldOpenRetry:
1180 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1181 (void **) &pSMBr);
1182 if (rc)
1183 return rc;
1184
1185 pSMB->AndXCommand = 0xFF; /* none */
1186
1187 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1188 count = 1; /* account for one byte pad to word boundary */
1189 name_len =
1190 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1191 fileName, PATH_MAX, nls_codepage, remap);
1192 name_len++; /* trailing null */
1193 name_len *= 2;
1194 } else { /* BB improve check for buffer overruns BB */
1195 count = 0; /* no pad */
1196 name_len = strnlen(fileName, PATH_MAX);
1197 name_len++; /* trailing null */
1198 strncpy(pSMB->fileName, fileName, name_len);
1199 }
1200 if (*pOplock & REQ_OPLOCK)
1201 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001202 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001203 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001204
Steve Frencha9d02ad2005-08-24 23:06:05 -07001205 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
1206 /* BB fixme add conversion for access_flags to bits 0 - 2 of mode */
1207 /* 0 = read
1208 1 = write
1209 2 = rw
1210 3 = execute
Steve French50c2f752007-07-13 00:33:32 +00001211 */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001212 pSMB->Mode = cpu_to_le16(2);
1213 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1214 /* set file as system file if special file such
1215 as fifo and server expecting SFU style and
1216 no Unix extensions */
1217
Steve French790fe572007-07-07 19:25:05 +00001218 if (create_options & CREATE_OPTION_SPECIAL)
1219 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001220 else /* BB FIXME BB */
1221 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001222
1223 /* if ((omode & S_IWUGO) == 0)
1224 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/
1225 /* Above line causes problems due to vfs splitting create into two
1226 pieces - need to set mode after file created not while it is
1227 being created */
1228
1229 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001230/* pSMB->CreateOptions = cpu_to_le32(create_options &
1231 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001232 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001233
1234 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001235 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001236 count += name_len;
1237 pSMB->hdr.smb_buf_length += count;
1238
1239 pSMB->ByteCount = cpu_to_le16(count);
1240 /* long_op set to 1 to allow for oplock break timeouts */
1241 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001242 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001243 cifs_stats_inc(&tcon->num_opens);
1244 if (rc) {
1245 cFYI(1, ("Error in Open = %d", rc));
1246 } else {
1247 /* BB verify if wct == 15 */
1248
1249/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field BB */
1250
1251 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1252 /* Let caller know file was created so we can set the mode. */
1253 /* Do we care about the CreateAction in any other cases? */
1254 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001255/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001256 *pOplock |= CIFS_CREATE_ACTION; */
1257 /* BB FIXME END */
1258
Steve French790fe572007-07-07 19:25:05 +00001259 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001260 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1261 pfile_info->LastAccessTime = 0; /* BB fixme */
1262 pfile_info->LastWriteTime = 0; /* BB fixme */
1263 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001264 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001265 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001266 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001267 pfile_info->AllocationSize =
1268 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1269 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001270 pfile_info->NumberOfLinks = cpu_to_le32(1);
1271 }
1272 }
1273
1274 cifs_buf_release(pSMB);
1275 if (rc == -EAGAIN)
1276 goto OldOpenRetry;
1277 return rc;
1278}
1279
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280int
1281CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1282 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001283 const int access_flags, const int create_options, __u16 *netfid,
1284 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001285 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286{
1287 int rc = -EACCES;
1288 OPEN_REQ *pSMB = NULL;
1289 OPEN_RSP *pSMBr = NULL;
1290 int bytes_returned;
1291 int name_len;
1292 __u16 count;
1293
1294openRetry:
1295 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1296 (void **) &pSMBr);
1297 if (rc)
1298 return rc;
1299
1300 pSMB->AndXCommand = 0xFF; /* none */
1301
1302 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1303 count = 1; /* account for one byte pad to word boundary */
1304 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001305 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001306 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 name_len++; /* trailing null */
1308 name_len *= 2;
1309 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001310 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 count = 0; /* no pad */
1312 name_len = strnlen(fileName, PATH_MAX);
1313 name_len++; /* trailing null */
1314 pSMB->NameLength = cpu_to_le16(name_len);
1315 strncpy(pSMB->fileName, fileName, name_len);
1316 }
1317 if (*pOplock & REQ_OPLOCK)
1318 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001319 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1322 pSMB->AllocationSize = 0;
Steve Frencheda3c0292005-07-21 15:20:28 -07001323 /* set file as system file if special file such
1324 as fifo and server expecting SFU style and
1325 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001326 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c0292005-07-21 15:20:28 -07001327 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1328 else
1329 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 /* XP does not handle ATTR_POSIX_SEMANTICS */
1331 /* but it helps speed up case sensitive checks for other
1332 servers such as Samba */
1333 if (tcon->ses->capabilities & CAP_UNIX)
1334 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1335
1336 /* if ((omode & S_IWUGO) == 0)
1337 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/
1338 /* Above line causes problems due to vfs splitting create into two
1339 pieces - need to set mode after file created not while it is
1340 being created */
1341 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1342 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c0292005-07-21 15:20:28 -07001343 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001344 /* BB Expirement with various impersonation levels and verify */
1345 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 pSMB->SecurityFlags =
1347 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1348
1349 count += name_len;
1350 pSMB->hdr.smb_buf_length += count;
1351
1352 pSMB->ByteCount = cpu_to_le16(count);
1353 /* long_op set to 1 to allow for oplock break timeouts */
1354 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001355 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha4544342005-08-24 13:59:35 -07001356 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 if (rc) {
1358 cFYI(1, ("Error in Open = %d", rc));
1359 } else {
Steve French09d1db52005-04-28 22:41:08 -07001360 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1362 /* Let caller know file was created so we can set the mode. */
1363 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001364 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001365 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001366 if (pfile_info) {
Steve French50c2f752007-07-13 00:33:32 +00001367 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 36 /* CreationTime to Attributes */);
1369 /* the file_info buf is endian converted by caller */
1370 pfile_info->AllocationSize = pSMBr->AllocationSize;
1371 pfile_info->EndOfFile = pSMBr->EndOfFile;
1372 pfile_info->NumberOfLinks = cpu_to_le32(1);
1373 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001375
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 cifs_buf_release(pSMB);
1377 if (rc == -EAGAIN)
1378 goto openRetry;
1379 return rc;
1380}
1381
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382int
Steve French50c2f752007-07-13 00:33:32 +00001383CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1384 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1385 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386{
1387 int rc = -EACCES;
1388 READ_REQ *pSMB = NULL;
1389 READ_RSP *pSMBr = NULL;
1390 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001391 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001392 int resp_buf_type = 0;
1393 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394
Steve French790fe572007-07-07 19:25:05 +00001395 cFYI(1, ("Reading %d bytes on fid %d", count, netfid));
1396 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001397 wct = 12;
1398 else
1399 wct = 10; /* old style read */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400
1401 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001402 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 if (rc)
1404 return rc;
1405
1406 /* tcon and ses pointer are checked in smb_init */
1407 if (tcon->ses->server == NULL)
1408 return -ECONNABORTED;
1409
Steve Frenchec637e32005-12-12 20:53:18 -08001410 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 pSMB->Fid = netfid;
1412 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001413 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001414 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve French790fe572007-07-07 19:25:05 +00001415 else if ((lseek >> 32) > 0) /* can not handle this big offset for old */
Steve Frenchec637e32005-12-12 20:53:18 -08001416 return -EIO;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001417
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 pSMB->Remaining = 0;
1419 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1420 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001421 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001422 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1423 else {
1424 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001425 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001426 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001427 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001428 }
Steve Frenchec637e32005-12-12 20:53:18 -08001429
1430 iov[0].iov_base = (char *)pSMB;
1431 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001432 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Steve French133672e2007-11-13 22:41:37 +00001433 &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001434 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001435 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 if (rc) {
1437 cERROR(1, ("Send error in read = %d", rc));
1438 } else {
1439 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1440 data_length = data_length << 16;
1441 data_length += le16_to_cpu(pSMBr->DataLength);
1442 *nbytes = data_length;
1443
1444 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001445 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 || (data_length > count)) {
Steve French50c2f752007-07-13 00:33:32 +00001447 cFYI(1, ("bad length %d for count %d",
1448 data_length, count));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 rc = -EIO;
1450 *nbytes = 0;
1451 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001452 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001453 le16_to_cpu(pSMBr->DataOffset);
1454/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Steve French50c2f752007-07-13 00:33:32 +00001455 cERROR(1,("Faulting on read rc = %d",rc));
1456 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001457 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001458 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001459 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 }
1461 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
Steve French4b8f9302006-02-26 16:41:18 +00001463/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001464 if (*buf) {
1465 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001466 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001467 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001468 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001469 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001470 /* return buffer to caller to free */
1471 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001472 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001473 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001474 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001475 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001476 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001477
1478 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 since file handle passed in no longer valid */
1480 return rc;
1481}
1482
Steve Frenchec637e32005-12-12 20:53:18 -08001483
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484int
1485CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1486 const int netfid, const unsigned int count,
1487 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001488 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489{
1490 int rc = -EACCES;
1491 WRITE_REQ *pSMB = NULL;
1492 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001493 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 __u32 bytes_sent;
1495 __u16 byte_count;
1496
1497 /* cFYI(1,("write at %lld %d bytes",offset,count));*/
Steve French790fe572007-07-07 19:25:05 +00001498 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001499 return -ECONNABORTED;
1500
Steve French790fe572007-07-07 19:25:05 +00001501 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001502 wct = 14;
1503 else
1504 wct = 12;
1505
1506 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 (void **) &pSMBr);
1508 if (rc)
1509 return rc;
1510 /* tcon and ses pointer are checked in smb_init */
1511 if (tcon->ses->server == NULL)
1512 return -ECONNABORTED;
1513
1514 pSMB->AndXCommand = 0xFF; /* none */
1515 pSMB->Fid = netfid;
1516 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001517 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001518 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French790fe572007-07-07 19:25:05 +00001519 else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
Steve French1c955182005-08-30 20:58:07 -07001520 return -EIO;
Steve French50c2f752007-07-13 00:33:32 +00001521
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 pSMB->Reserved = 0xFFFFFFFF;
1523 pSMB->WriteMode = 0;
1524 pSMB->Remaining = 0;
1525
Steve French50c2f752007-07-13 00:33:32 +00001526 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 can send more if LARGE_WRITE_X capability returned by the server and if
1528 our buffer is big enough or if we convert to iovecs on socket writes
1529 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001530 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1532 } else {
1533 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1534 & ~0xFF;
1535 }
1536
1537 if (bytes_sent > count)
1538 bytes_sent = count;
1539 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001540 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001541 if (buf)
Steve French50c2f752007-07-13 00:33:32 +00001542 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001543 else if (ubuf) {
1544 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 cifs_buf_release(pSMB);
1546 return -EFAULT;
1547 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001548 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 /* No buffer */
1550 cifs_buf_release(pSMB);
1551 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001552 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001553 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001554 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001555 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001556 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001557
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1559 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001560 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001561
Steve French790fe572007-07-07 19:25:05 +00001562 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001563 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001564 else { /* old style write has byte count 4 bytes earlier
1565 so 4 bytes pad */
1566 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001567 (struct smb_com_writex_req *)pSMB;
1568 pSMBW->ByteCount = cpu_to_le16(byte_count);
1569 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570
1571 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1572 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001573 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 if (rc) {
1575 cFYI(1, ("Send error in write = %d", rc));
1576 *nbytes = 0;
1577 } else {
1578 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1579 *nbytes = (*nbytes) << 16;
1580 *nbytes += le16_to_cpu(pSMBr->Count);
1581 }
1582
1583 cifs_buf_release(pSMB);
1584
Steve French50c2f752007-07-13 00:33:32 +00001585 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586 since file handle passed in no longer valid */
1587
1588 return rc;
1589}
1590
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001591int
1592CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001594 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1595 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596{
1597 int rc = -EACCES;
1598 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001599 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001600 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001601 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
Steve French790fe572007-07-07 19:25:05 +00001603 cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count));
Steve Frenchff7feac2005-11-15 16:45:16 -08001604
Steve French790fe572007-07-07 19:25:05 +00001605 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French8cc64c62005-10-03 13:49:43 -07001606 wct = 14;
1607 else
1608 wct = 12;
1609 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 if (rc)
1611 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 /* tcon and ses pointer are checked in smb_init */
1613 if (tcon->ses->server == NULL)
1614 return -ECONNABORTED;
1615
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001616 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617 pSMB->Fid = netfid;
1618 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001619 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001620 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French790fe572007-07-07 19:25:05 +00001621 else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
Steve French8cc64c62005-10-03 13:49:43 -07001622 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 pSMB->Reserved = 0xFFFFFFFF;
1624 pSMB->WriteMode = 0;
1625 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001626
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001628 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629
Steve French3e844692005-10-03 13:37:24 -07001630 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1631 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001632 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001633 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001634 pSMB->hdr.smb_buf_length += count+1;
1635 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001636 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1637 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001638 pSMB->ByteCount = cpu_to_le16(count + 1);
1639 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001640 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001641 (struct smb_com_writex_req *)pSMB;
1642 pSMBW->ByteCount = cpu_to_le16(count + 5);
1643 }
Steve French3e844692005-10-03 13:37:24 -07001644 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001645 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001646 iov[0].iov_len = smb_hdr_len + 4;
1647 else /* wct == 12 pad bigger by four bytes */
1648 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001649
Steve French3e844692005-10-03 13:37:24 -07001650
Steve Frenchec637e32005-12-12 20:53:18 -08001651 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001652 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001653 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 if (rc) {
Steve French8cc64c62005-10-03 13:49:43 -07001655 cFYI(1, ("Send error Write2 = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656 *nbytes = 0;
Steve French790fe572007-07-07 19:25:05 +00001657 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001658 /* presumably this can not happen, but best to be safe */
1659 rc = -EIO;
1660 *nbytes = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001661 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001662 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001663 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1664 *nbytes = (*nbytes) << 16;
1665 *nbytes += le16_to_cpu(pSMBr->Count);
Steve French50c2f752007-07-13 00:33:32 +00001666 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667
Steve French4b8f9302006-02-26 16:41:18 +00001668/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001669 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001670 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001671 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001672 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673
Steve French50c2f752007-07-13 00:33:32 +00001674 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675 since file handle passed in no longer valid */
1676
1677 return rc;
1678}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001679
1680
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681int
1682CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1683 const __u16 smb_file_id, const __u64 len,
1684 const __u64 offset, const __u32 numUnlock,
1685 const __u32 numLock, const __u8 lockType, const int waitFlag)
1686{
1687 int rc = 0;
1688 LOCK_REQ *pSMB = NULL;
1689 LOCK_RSP *pSMBr = NULL;
1690 int bytes_returned;
1691 int timeout = 0;
1692 __u16 count;
1693
Steve French133672e2007-11-13 22:41:37 +00001694 cFYI(1, ("CIFSSMBLock timeout %d numLock %d", waitFlag, numLock));
Steve French46810cb2005-04-28 22:41:09 -07001695 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1696
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 if (rc)
1698 return rc;
1699
Steve French46810cb2005-04-28 22:41:09 -07001700 pSMBr = (LOCK_RSP *)pSMB; /* BB removeme BB */
1701
Steve French790fe572007-07-07 19:25:05 +00001702 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001703 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 pSMB->Timeout = 0;
1705 } else if (waitFlag == TRUE) {
Steve French133672e2007-11-13 22:41:37 +00001706 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1708 } else {
1709 pSMB->Timeout = 0;
1710 }
1711
1712 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1713 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1714 pSMB->LockType = lockType;
1715 pSMB->AndXCommand = 0xFF; /* none */
1716 pSMB->Fid = smb_file_id; /* netfid stays le */
1717
Steve French790fe572007-07-07 19:25:05 +00001718 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1720 /* BB where to store pid high? */
1721 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1722 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1723 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1724 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1725 count = sizeof(LOCKING_ANDX_RANGE);
1726 } else {
1727 /* oplock break */
1728 count = 0;
1729 }
1730 pSMB->hdr.smb_buf_length += count;
1731 pSMB->ByteCount = cpu_to_le16(count);
1732
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001733 if (waitFlag) {
1734 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1735 (struct smb_hdr *) pSMBr, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001736 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001737 } else {
Steve French133672e2007-11-13 22:41:37 +00001738 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1739 timeout);
1740 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001741 }
Steve Frencha4544342005-08-24 13:59:35 -07001742 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001743 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744 cFYI(1, ("Send error in Lock = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745
Steve French50c2f752007-07-13 00:33:32 +00001746 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 since file handle passed in no longer valid */
1748 return rc;
1749}
1750
1751int
Steve French08547b02006-02-28 22:39:25 +00001752CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1753 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001754 struct file_lock *pLockData, const __u16 lock_type,
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001755 const int waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001756{
1757 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1758 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001759 struct cifs_posix_lock *parm_data;
1760 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001761 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001762 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001763 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001764 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001765 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001766
1767 cFYI(1, ("Posix Lock"));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001768
Steve French790fe572007-07-07 19:25:05 +00001769 if (pLockData == NULL)
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001770 return EINVAL;
1771
Steve French08547b02006-02-28 22:39:25 +00001772 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1773
1774 if (rc)
1775 return rc;
1776
1777 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1778
Steve French50c2f752007-07-13 00:33:32 +00001779 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001780 pSMB->MaxSetupCount = 0;
1781 pSMB->Reserved = 0;
1782 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001783 pSMB->Reserved2 = 0;
1784 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1785 offset = param_offset + params;
1786
Steve French08547b02006-02-28 22:39:25 +00001787 count = sizeof(struct cifs_posix_lock);
1788 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001789 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001790 pSMB->SetupCount = 1;
1791 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001792 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001793 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1794 else
1795 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1796 byte_count = 3 /* pad */ + params + count;
1797 pSMB->DataCount = cpu_to_le16(count);
1798 pSMB->ParameterCount = cpu_to_le16(params);
1799 pSMB->TotalDataCount = pSMB->DataCount;
1800 pSMB->TotalParameterCount = pSMB->ParameterCount;
1801 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001802 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001803 (((char *) &pSMB->hdr.Protocol) + offset);
1804
1805 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001806 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001807 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001808 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001809 pSMB->Timeout = cpu_to_le32(-1);
1810 } else
1811 pSMB->Timeout = 0;
1812
Steve French08547b02006-02-28 22:39:25 +00001813 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001814 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001815 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001816
1817 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001818 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001819 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1820 pSMB->Reserved4 = 0;
1821 pSMB->hdr.smb_buf_length += byte_count;
1822 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001823 if (waitFlag) {
1824 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1825 (struct smb_hdr *) pSMBr, &bytes_returned);
1826 } else {
Steve French133672e2007-11-13 22:41:37 +00001827 iov[0].iov_base = (char *)pSMB;
1828 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1829 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1830 &resp_buf_type, timeout);
1831 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1832 not try to free it twice below on exit */
1833 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001834 }
1835
Steve French08547b02006-02-28 22:39:25 +00001836 if (rc) {
1837 cFYI(1, ("Send error in Posix Lock = %d", rc));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001838 } else if (get_flag) {
1839 /* lock structure can be returned on get */
1840 __u16 data_offset;
1841 __u16 data_count;
1842 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001843
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001844 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1845 rc = -EIO; /* bad smb */
1846 goto plk_err_exit;
1847 }
Steve French790fe572007-07-07 19:25:05 +00001848 if (pLockData == NULL) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001849 rc = -EINVAL;
1850 goto plk_err_exit;
1851 }
1852 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1853 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001854 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001855 rc = -EIO;
1856 goto plk_err_exit;
1857 }
1858 parm_data = (struct cifs_posix_lock *)
1859 ((char *)&pSMBr->hdr.Protocol + data_offset);
Steve French790fe572007-07-07 19:25:05 +00001860 if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001861 pLockData->fl_type = F_UNLCK;
1862 }
Steve French50c2f752007-07-13 00:33:32 +00001863
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001864plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001865 if (pSMB)
1866 cifs_small_buf_release(pSMB);
1867
Steve French133672e2007-11-13 22:41:37 +00001868 if (resp_buf_type == CIFS_SMALL_BUFFER)
1869 cifs_small_buf_release(iov[0].iov_base);
1870 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1871 cifs_buf_release(iov[0].iov_base);
1872
Steve French08547b02006-02-28 22:39:25 +00001873 /* Note: On -EAGAIN error only caller can retry on handle based calls
1874 since file handle passed in no longer valid */
1875
1876 return rc;
1877}
1878
1879
1880int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1882{
1883 int rc = 0;
1884 CLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 cFYI(1, ("In CIFSSMBClose"));
1886
1887/* do not retry on dead session on close */
1888 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001889 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890 return 0;
1891 if (rc)
1892 return rc;
1893
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e52006-10-02 05:53:29 +00001895 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001897 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001898 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001900 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 /* EINTR is expected when user ctl-c to kill app */
1902 cERROR(1, ("Send error in Close = %d", rc));
1903 }
1904 }
1905
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001907 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 rc = 0;
1909
1910 return rc;
1911}
1912
1913int
1914CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1915 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001916 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917{
1918 int rc = 0;
1919 RENAME_REQ *pSMB = NULL;
1920 RENAME_RSP *pSMBr = NULL;
1921 int bytes_returned;
1922 int name_len, name_len2;
1923 __u16 count;
1924
1925 cFYI(1, ("In CIFSSMBRename"));
1926renameRetry:
1927 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1928 (void **) &pSMBr);
1929 if (rc)
1930 return rc;
1931
1932 pSMB->BufferFormat = 0x04;
1933 pSMB->SearchAttributes =
1934 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1935 ATTR_DIRECTORY);
1936
1937 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1938 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001939 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001940 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941 name_len++; /* trailing null */
1942 name_len *= 2;
1943 pSMB->OldFileName[name_len] = 0x04; /* pad */
1944 /* protocol requires ASCII signature byte on Unicode string */
1945 pSMB->OldFileName[name_len + 1] = 0x00;
1946 name_len2 =
Steve Frenchb1a45692005-05-17 16:07:23 -05001947 cifsConvertToUCS((__le16 *) &pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07001948 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
1950 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00001951 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 name_len = strnlen(fromName, PATH_MAX);
1953 name_len++; /* trailing null */
1954 strncpy(pSMB->OldFileName, fromName, name_len);
1955 name_len2 = strnlen(toName, PATH_MAX);
1956 name_len2++; /* trailing null */
1957 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
1958 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
1959 name_len2++; /* trailing null */
1960 name_len2++; /* signature byte */
1961 }
1962
1963 count = 1 /* 1st signature byte */ + name_len + name_len2;
1964 pSMB->hdr.smb_buf_length += count;
1965 pSMB->ByteCount = cpu_to_le16(count);
1966
1967 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1968 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001969 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00001970 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971 cFYI(1, ("Send error in rename = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 cifs_buf_release(pSMB);
1974
1975 if (rc == -EAGAIN)
1976 goto renameRetry;
1977
1978 return rc;
1979}
1980
Steve French50c2f752007-07-13 00:33:32 +00001981int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
1982 int netfid, char *target_name,
1983 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984{
1985 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1986 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00001987 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988 char *data_offset;
1989 char dummy_string[30];
1990 int rc = 0;
1991 int bytes_returned = 0;
1992 int len_of_str;
1993 __u16 params, param_offset, offset, count, byte_count;
1994
1995 cFYI(1, ("Rename to File by handle"));
1996 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
1997 (void **) &pSMBr);
1998 if (rc)
1999 return rc;
2000
2001 params = 6;
2002 pSMB->MaxSetupCount = 0;
2003 pSMB->Reserved = 0;
2004 pSMB->Flags = 0;
2005 pSMB->Timeout = 0;
2006 pSMB->Reserved2 = 0;
2007 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2008 offset = param_offset + params;
2009
2010 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2011 rename_info = (struct set_file_rename *) data_offset;
2012 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002013 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 pSMB->SetupCount = 1;
2015 pSMB->Reserved3 = 0;
2016 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2017 byte_count = 3 /* pad */ + params;
2018 pSMB->ParameterCount = cpu_to_le16(params);
2019 pSMB->TotalParameterCount = pSMB->ParameterCount;
2020 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2021 pSMB->DataOffset = cpu_to_le16(offset);
2022 /* construct random name ".cifs_tmp<inodenum><mid>" */
2023 rename_info->overwrite = cpu_to_le32(1);
2024 rename_info->root_fid = 0;
2025 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002026 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002027 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2028 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002029 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002031 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002032 target_name, PATH_MAX, nls_codepage,
2033 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034 }
2035 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
2036 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str) + 2;
2037 byte_count += count;
2038 pSMB->DataCount = cpu_to_le16(count);
2039 pSMB->TotalDataCount = pSMB->DataCount;
2040 pSMB->Fid = netfid;
2041 pSMB->InformationLevel =
2042 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2043 pSMB->Reserved4 = 0;
2044 pSMB->hdr.smb_buf_length += byte_count;
2045 pSMB->ByteCount = cpu_to_le16(byte_count);
2046 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002047 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002048 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002049 if (rc)
Steve French790fe572007-07-07 19:25:05 +00002050 cFYI(1, ("Send error in Rename (by file handle) = %d", rc));
Steve Frencha5a2b482005-08-20 21:42:53 -07002051
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 cifs_buf_release(pSMB);
2053
2054 /* Note: On -EAGAIN error only caller can retry on handle based calls
2055 since file handle passed in no longer valid */
2056
2057 return rc;
2058}
2059
2060int
Steve French50c2f752007-07-13 00:33:32 +00002061CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2062 const __u16 target_tid, const char *toName, const int flags,
2063 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064{
2065 int rc = 0;
2066 COPY_REQ *pSMB = NULL;
2067 COPY_RSP *pSMBr = NULL;
2068 int bytes_returned;
2069 int name_len, name_len2;
2070 __u16 count;
2071
2072 cFYI(1, ("In CIFSSMBCopy"));
2073copyRetry:
2074 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2075 (void **) &pSMBr);
2076 if (rc)
2077 return rc;
2078
2079 pSMB->BufferFormat = 0x04;
2080 pSMB->Tid2 = target_tid;
2081
2082 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2083
2084 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002085 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002086 fromName, PATH_MAX, nls_codepage,
2087 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 name_len++; /* trailing null */
2089 name_len *= 2;
2090 pSMB->OldFileName[name_len] = 0x04; /* pad */
2091 /* protocol requires ASCII signature byte on Unicode string */
2092 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002093 name_len2 =
2094 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002095 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2097 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002098 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 name_len = strnlen(fromName, PATH_MAX);
2100 name_len++; /* trailing null */
2101 strncpy(pSMB->OldFileName, fromName, name_len);
2102 name_len2 = strnlen(toName, PATH_MAX);
2103 name_len2++; /* trailing null */
2104 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2105 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2106 name_len2++; /* trailing null */
2107 name_len2++; /* signature byte */
2108 }
2109
2110 count = 1 /* 1st signature byte */ + name_len + name_len2;
2111 pSMB->hdr.smb_buf_length += count;
2112 pSMB->ByteCount = cpu_to_le16(count);
2113
2114 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2115 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2116 if (rc) {
2117 cFYI(1, ("Send error in copy = %d with %d files copied",
2118 rc, le16_to_cpu(pSMBr->CopyCount)));
2119 }
2120 if (pSMB)
2121 cifs_buf_release(pSMB);
2122
2123 if (rc == -EAGAIN)
2124 goto copyRetry;
2125
2126 return rc;
2127}
2128
2129int
2130CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2131 const char *fromName, const char *toName,
2132 const struct nls_table *nls_codepage)
2133{
2134 TRANSACTION2_SPI_REQ *pSMB = NULL;
2135 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2136 char *data_offset;
2137 int name_len;
2138 int name_len_target;
2139 int rc = 0;
2140 int bytes_returned = 0;
2141 __u16 params, param_offset, offset, byte_count;
2142
2143 cFYI(1, ("In Symlink Unix style"));
2144createSymLinkRetry:
2145 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2146 (void **) &pSMBr);
2147 if (rc)
2148 return rc;
2149
2150 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2151 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002152 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153 /* find define for this maxpathcomponent */
2154 , nls_codepage);
2155 name_len++; /* trailing null */
2156 name_len *= 2;
2157
Steve French50c2f752007-07-13 00:33:32 +00002158 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 name_len = strnlen(fromName, PATH_MAX);
2160 name_len++; /* trailing null */
2161 strncpy(pSMB->FileName, fromName, name_len);
2162 }
2163 params = 6 + name_len;
2164 pSMB->MaxSetupCount = 0;
2165 pSMB->Reserved = 0;
2166 pSMB->Flags = 0;
2167 pSMB->Timeout = 0;
2168 pSMB->Reserved2 = 0;
2169 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002170 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 offset = param_offset + params;
2172
2173 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2174 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2175 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002176 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177 /* find define for this maxpathcomponent */
2178 , nls_codepage);
2179 name_len_target++; /* trailing null */
2180 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002181 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182 name_len_target = strnlen(toName, PATH_MAX);
2183 name_len_target++; /* trailing null */
2184 strncpy(data_offset, toName, name_len_target);
2185 }
2186
2187 pSMB->MaxParameterCount = cpu_to_le16(2);
2188 /* BB find exact max on data count below from sess */
2189 pSMB->MaxDataCount = cpu_to_le16(1000);
2190 pSMB->SetupCount = 1;
2191 pSMB->Reserved3 = 0;
2192 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2193 byte_count = 3 /* pad */ + params + name_len_target;
2194 pSMB->DataCount = cpu_to_le16(name_len_target);
2195 pSMB->ParameterCount = cpu_to_le16(params);
2196 pSMB->TotalDataCount = pSMB->DataCount;
2197 pSMB->TotalParameterCount = pSMB->ParameterCount;
2198 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2199 pSMB->DataOffset = cpu_to_le16(offset);
2200 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2201 pSMB->Reserved4 = 0;
2202 pSMB->hdr.smb_buf_length += byte_count;
2203 pSMB->ByteCount = cpu_to_le16(byte_count);
2204 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2205 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002206 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002207 if (rc)
Steve French2d785a52007-07-15 01:48:57 +00002208 cFYI(1, ("Send error in SetPathInfo create symlink = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209
2210 if (pSMB)
2211 cifs_buf_release(pSMB);
2212
2213 if (rc == -EAGAIN)
2214 goto createSymLinkRetry;
2215
2216 return rc;
2217}
2218
2219int
2220CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2221 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002222 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223{
2224 TRANSACTION2_SPI_REQ *pSMB = NULL;
2225 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2226 char *data_offset;
2227 int name_len;
2228 int name_len_target;
2229 int rc = 0;
2230 int bytes_returned = 0;
2231 __u16 params, param_offset, offset, byte_count;
2232
2233 cFYI(1, ("In Create Hard link Unix style"));
2234createHardLinkRetry:
2235 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2236 (void **) &pSMBr);
2237 if (rc)
2238 return rc;
2239
2240 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002241 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002242 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243 name_len++; /* trailing null */
2244 name_len *= 2;
2245
Steve French50c2f752007-07-13 00:33:32 +00002246 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247 name_len = strnlen(toName, PATH_MAX);
2248 name_len++; /* trailing null */
2249 strncpy(pSMB->FileName, toName, name_len);
2250 }
2251 params = 6 + name_len;
2252 pSMB->MaxSetupCount = 0;
2253 pSMB->Reserved = 0;
2254 pSMB->Flags = 0;
2255 pSMB->Timeout = 0;
2256 pSMB->Reserved2 = 0;
2257 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002258 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259 offset = param_offset + params;
2260
2261 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2262 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2263 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002264 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002265 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 name_len_target++; /* trailing null */
2267 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002268 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 name_len_target = strnlen(fromName, PATH_MAX);
2270 name_len_target++; /* trailing null */
2271 strncpy(data_offset, fromName, name_len_target);
2272 }
2273
2274 pSMB->MaxParameterCount = cpu_to_le16(2);
2275 /* BB find exact max on data count below from sess*/
2276 pSMB->MaxDataCount = cpu_to_le16(1000);
2277 pSMB->SetupCount = 1;
2278 pSMB->Reserved3 = 0;
2279 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2280 byte_count = 3 /* pad */ + params + name_len_target;
2281 pSMB->ParameterCount = cpu_to_le16(params);
2282 pSMB->TotalParameterCount = pSMB->ParameterCount;
2283 pSMB->DataCount = cpu_to_le16(name_len_target);
2284 pSMB->TotalDataCount = pSMB->DataCount;
2285 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2286 pSMB->DataOffset = cpu_to_le16(offset);
2287 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2288 pSMB->Reserved4 = 0;
2289 pSMB->hdr.smb_buf_length += byte_count;
2290 pSMB->ByteCount = cpu_to_le16(byte_count);
2291 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2292 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002293 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002294 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296
2297 cifs_buf_release(pSMB);
2298 if (rc == -EAGAIN)
2299 goto createHardLinkRetry;
2300
2301 return rc;
2302}
2303
2304int
2305CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2306 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002307 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308{
2309 int rc = 0;
2310 NT_RENAME_REQ *pSMB = NULL;
2311 RENAME_RSP *pSMBr = NULL;
2312 int bytes_returned;
2313 int name_len, name_len2;
2314 __u16 count;
2315
2316 cFYI(1, ("In CIFSCreateHardLink"));
2317winCreateHardLinkRetry:
2318
2319 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2320 (void **) &pSMBr);
2321 if (rc)
2322 return rc;
2323
2324 pSMB->SearchAttributes =
2325 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2326 ATTR_DIRECTORY);
2327 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2328 pSMB->ClusterCount = 0;
2329
2330 pSMB->BufferFormat = 0x04;
2331
2332 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2333 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002334 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002335 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336 name_len++; /* trailing null */
2337 name_len *= 2;
2338 pSMB->OldFileName[name_len] = 0; /* pad */
Steve French50c2f752007-07-13 00:33:32 +00002339 pSMB->OldFileName[name_len + 1] = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002341 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002342 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2344 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002345 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346 name_len = strnlen(fromName, PATH_MAX);
2347 name_len++; /* trailing null */
2348 strncpy(pSMB->OldFileName, fromName, name_len);
2349 name_len2 = strnlen(toName, PATH_MAX);
2350 name_len2++; /* trailing null */
2351 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2352 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2353 name_len2++; /* trailing null */
2354 name_len2++; /* signature byte */
2355 }
2356
2357 count = 1 /* string type byte */ + name_len + name_len2;
2358 pSMB->hdr.smb_buf_length += count;
2359 pSMB->ByteCount = cpu_to_le16(count);
2360
2361 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2362 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002363 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002364 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 cFYI(1, ("Send error in hard link (NT rename) = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00002366
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367 cifs_buf_release(pSMB);
2368 if (rc == -EAGAIN)
2369 goto winCreateHardLinkRetry;
2370
2371 return rc;
2372}
2373
2374int
2375CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
2376 const unsigned char *searchName,
2377 char *symlinkinfo, const int buflen,
2378 const struct nls_table *nls_codepage)
2379{
2380/* SMB_QUERY_FILE_UNIX_LINK */
2381 TRANSACTION2_QPI_REQ *pSMB = NULL;
2382 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2383 int rc = 0;
2384 int bytes_returned;
2385 int name_len;
2386 __u16 params, byte_count;
2387
2388 cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName));
2389
2390querySymLinkRetry:
2391 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2392 (void **) &pSMBr);
2393 if (rc)
2394 return rc;
2395
2396 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2397 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002398 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2399 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 name_len++; /* trailing null */
2401 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002402 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403 name_len = strnlen(searchName, PATH_MAX);
2404 name_len++; /* trailing null */
2405 strncpy(pSMB->FileName, searchName, name_len);
2406 }
2407
2408 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2409 pSMB->TotalDataCount = 0;
2410 pSMB->MaxParameterCount = cpu_to_le16(2);
2411 /* BB find exact max data count below from sess structure BB */
2412 pSMB->MaxDataCount = cpu_to_le16(4000);
2413 pSMB->MaxSetupCount = 0;
2414 pSMB->Reserved = 0;
2415 pSMB->Flags = 0;
2416 pSMB->Timeout = 0;
2417 pSMB->Reserved2 = 0;
2418 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002419 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420 pSMB->DataCount = 0;
2421 pSMB->DataOffset = 0;
2422 pSMB->SetupCount = 1;
2423 pSMB->Reserved3 = 0;
2424 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2425 byte_count = params + 1 /* pad */ ;
2426 pSMB->TotalParameterCount = cpu_to_le16(params);
2427 pSMB->ParameterCount = pSMB->TotalParameterCount;
2428 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2429 pSMB->Reserved4 = 0;
2430 pSMB->hdr.smb_buf_length += byte_count;
2431 pSMB->ByteCount = cpu_to_le16(byte_count);
2432
2433 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2434 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2435 if (rc) {
2436 cFYI(1, ("Send error in QuerySymLinkInfo = %d", rc));
2437 } else {
2438 /* decode response */
2439
2440 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2441 if (rc || (pSMBr->ByteCount < 2))
2442 /* BB also check enough total bytes returned */
2443 rc = -EIO; /* bad smb */
2444 else {
2445 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2446 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2447
2448 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2449 name_len = UniStrnlen((wchar_t *) ((char *)
Steve French50c2f752007-07-13 00:33:32 +00002450 &pSMBr->hdr.Protocol + data_offset),
2451 min_t(const int, buflen, count) / 2);
Steve French737b7582005-04-28 22:41:06 -07002452 /* BB FIXME investigate remapping reserved chars here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002454 (__le16 *) ((char *)&pSMBr->hdr.Protocol
2455 + data_offset),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456 name_len, nls_codepage);
2457 } else {
2458 strncpy(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002459 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460 data_offset,
2461 min_t(const int, buflen, count));
2462 }
2463 symlinkinfo[buflen] = 0;
2464 /* just in case so calling code does not go off the end of buffer */
2465 }
2466 }
2467 cifs_buf_release(pSMB);
2468 if (rc == -EAGAIN)
2469 goto querySymLinkRetry;
2470 return rc;
2471}
2472
Parag Warudkarc9489772007-10-23 18:09:48 +00002473#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08002474/* Initialize NT TRANSACT SMB into small smb request buffer.
2475 This assumes that all NT TRANSACTS that we init here have
2476 total parm and data under about 400 bytes (to fit in small cifs
2477 buffer size), which is the case so far, it easily fits. NB:
2478 Setup words themselves and ByteCount
2479 MaxSetupCount (size of returned setup area) and
2480 MaxParameterCount (returned parms size) must be set by caller */
Steve French50c2f752007-07-13 00:33:32 +00002481static int
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002482smb_init_nttransact(const __u16 sub_command, const int setup_count,
Steve French0a4b92c2006-01-12 15:44:21 -08002483 const int parm_len, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002484 void **ret_buf)
Steve French0a4b92c2006-01-12 15:44:21 -08002485{
2486 int rc;
2487 __u32 temp_offset;
Steve French50c2f752007-07-13 00:33:32 +00002488 struct smb_com_ntransact_req *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08002489
2490 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
2491 (void **)&pSMB);
2492 if (rc)
2493 return rc;
2494 *ret_buf = (void *)pSMB;
2495 pSMB->Reserved = 0;
2496 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
2497 pSMB->TotalDataCount = 0;
2498 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2499 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
2500 pSMB->ParameterCount = pSMB->TotalParameterCount;
2501 pSMB->DataCount = pSMB->TotalDataCount;
2502 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
2503 (setup_count * 2) - 4 /* for rfc1001 length itself */;
2504 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
2505 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
2506 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
2507 pSMB->SubCommand = cpu_to_le16(sub_command);
2508 return 0;
2509}
2510
2511static int
Steve French50c2f752007-07-13 00:33:32 +00002512validate_ntransact(char *buf, char **ppparm, char **ppdata,
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002513 __u32 *pparmlen, __u32 *pdatalen)
Steve French0a4b92c2006-01-12 15:44:21 -08002514{
Steve French50c2f752007-07-13 00:33:32 +00002515 char *end_of_smb;
Steve French0a4b92c2006-01-12 15:44:21 -08002516 __u32 data_count, data_offset, parm_count, parm_offset;
Steve French50c2f752007-07-13 00:33:32 +00002517 struct smb_com_ntransact_rsp *pSMBr;
Steve French0a4b92c2006-01-12 15:44:21 -08002518
Steve French630f3f0c2007-10-25 21:17:17 +00002519 *pdatalen = 0;
2520 *pparmlen = 0;
2521
Steve French790fe572007-07-07 19:25:05 +00002522 if (buf == NULL)
Steve French0a4b92c2006-01-12 15:44:21 -08002523 return -EINVAL;
2524
2525 pSMBr = (struct smb_com_ntransact_rsp *)buf;
2526
2527 /* ByteCount was converted from little endian in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +00002528 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
Steve French0a4b92c2006-01-12 15:44:21 -08002529 (char *)&pSMBr->ByteCount;
2530
Steve French0a4b92c2006-01-12 15:44:21 -08002531 data_offset = le32_to_cpu(pSMBr->DataOffset);
2532 data_count = le32_to_cpu(pSMBr->DataCount);
Steve French50c2f752007-07-13 00:33:32 +00002533 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
Steve French0a4b92c2006-01-12 15:44:21 -08002534 parm_count = le32_to_cpu(pSMBr->ParameterCount);
2535
2536 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
2537 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
2538
2539 /* should we also check that parm and data areas do not overlap? */
Steve French790fe572007-07-07 19:25:05 +00002540 if (*ppparm > end_of_smb) {
2541 cFYI(1, ("parms start after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002542 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002543 } else if (parm_count + *ppparm > end_of_smb) {
2544 cFYI(1, ("parm end after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002545 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002546 } else if (*ppdata > end_of_smb) {
2547 cFYI(1, ("data starts after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002548 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002549 } else if (data_count + *ppdata > end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002550 cFYI(1, ("data %p + count %d (%p) ends after end of smb %p start %p",
Steve French50c2f752007-07-13 00:33:32 +00002551 *ppdata, data_count, (data_count + *ppdata),
2552 end_of_smb, pSMBr));
Steve French0a4b92c2006-01-12 15:44:21 -08002553 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002554 } else if (parm_count + data_count > pSMBr->ByteCount) {
2555 cFYI(1, ("parm count and data count larger than SMB"));
Steve French0a4b92c2006-01-12 15:44:21 -08002556 return -EINVAL;
2557 }
Steve French630f3f0c2007-10-25 21:17:17 +00002558 *pdatalen = data_count;
2559 *pparmlen = parm_count;
Steve French0a4b92c2006-01-12 15:44:21 -08002560 return 0;
2561}
Parag Warudkarc9489772007-10-23 18:09:48 +00002562#endif /* CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08002563
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564int
2565CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2566 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002567 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568 const struct nls_table *nls_codepage)
2569{
2570 int rc = 0;
2571 int bytes_returned;
2572 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00002573 struct smb_com_transaction_ioctl_req *pSMB;
2574 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575
2576 cFYI(1, ("In Windows reparse style QueryLink for path %s", searchName));
2577 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2578 (void **) &pSMBr);
2579 if (rc)
2580 return rc;
2581
2582 pSMB->TotalParameterCount = 0 ;
2583 pSMB->TotalDataCount = 0;
2584 pSMB->MaxParameterCount = cpu_to_le32(2);
2585 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002586 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2587 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 pSMB->MaxSetupCount = 4;
2589 pSMB->Reserved = 0;
2590 pSMB->ParameterOffset = 0;
2591 pSMB->DataCount = 0;
2592 pSMB->DataOffset = 0;
2593 pSMB->SetupCount = 4;
2594 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2595 pSMB->ParameterCount = pSMB->TotalParameterCount;
2596 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2597 pSMB->IsFsctl = 1; /* FSCTL */
2598 pSMB->IsRootFlag = 0;
2599 pSMB->Fid = fid; /* file handle always le */
2600 pSMB->ByteCount = 0;
2601
2602 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2603 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2604 if (rc) {
2605 cFYI(1, ("Send error in QueryReparseLinkInfo = %d", rc));
2606 } else { /* decode response */
2607 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2608 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
2609 if ((pSMBr->ByteCount < 2) || (data_offset > 512))
2610 /* BB also check enough total bytes returned */
2611 rc = -EIO; /* bad smb */
2612 else {
Steve French790fe572007-07-07 19:25:05 +00002613 if (data_count && (data_count < 2048)) {
Steve French50c2f752007-07-13 00:33:32 +00002614 char *end_of_smb = 2 /* sizeof byte count */ +
Steve French0a4b92c2006-01-12 15:44:21 -08002615 pSMBr->ByteCount +
2616 (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617
Steve French50c2f752007-07-13 00:33:32 +00002618 struct reparse_data *reparse_buf =
2619 (struct reparse_data *)
2620 ((char *)&pSMBr->hdr.Protocol
2621 + data_offset);
Steve French790fe572007-07-07 19:25:05 +00002622 if ((char *)reparse_buf >= end_of_smb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623 rc = -EIO;
2624 goto qreparse_out;
2625 }
Steve French790fe572007-07-07 19:25:05 +00002626 if ((reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002627 reparse_buf->TargetNameOffset +
2628 reparse_buf->TargetNameLen) >
2629 end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002630 cFYI(1, ("reparse buf beyond SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631 rc = -EIO;
2632 goto qreparse_out;
2633 }
Steve French50c2f752007-07-13 00:33:32 +00002634
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2636 name_len = UniStrnlen((wchar_t *)
Steve French50c2f752007-07-13 00:33:32 +00002637 (reparse_buf->LinkNamesBuf +
2638 reparse_buf->TargetNameOffset),
2639 min(buflen/2,
2640 reparse_buf->TargetNameLen / 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002641 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002642 (__le16 *) (reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643 reparse_buf->TargetNameOffset),
2644 name_len, nls_codepage);
2645 } else { /* ASCII names */
Steve French50c2f752007-07-13 00:33:32 +00002646 strncpy(symlinkinfo,
2647 reparse_buf->LinkNamesBuf +
2648 reparse_buf->TargetNameOffset,
2649 min_t(const int, buflen,
2650 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 }
2652 } else {
2653 rc = -EIO;
Steve French63135e02007-07-17 17:34:02 +00002654 cFYI(1, ("Invalid return data count on "
2655 "get reparse info ioctl"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656 }
2657 symlinkinfo[buflen] = 0; /* just in case so the caller
2658 does not go off the end of the buffer */
Steve French50c2f752007-07-13 00:33:32 +00002659 cFYI(1, ("readlink result - %s", symlinkinfo));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002660 }
2661 }
2662qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002663 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664
2665 /* Note: On -EAGAIN error only caller can retry on handle based calls
2666 since file handle passed in no longer valid */
2667
2668 return rc;
2669}
2670
2671#ifdef CONFIG_CIFS_POSIX
2672
2673/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002674static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2675 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676{
2677 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002678 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2679 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2680 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681 /* cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id)); */
2682
2683 return;
2684}
2685
2686/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002687static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2688 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689{
2690 int size = 0;
2691 int i;
2692 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002693 struct cifs_posix_ace *pACE;
2694 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2695 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696
2697 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2698 return -EOPNOTSUPP;
2699
Steve French790fe572007-07-07 19:25:05 +00002700 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701 count = le16_to_cpu(cifs_acl->access_entry_count);
2702 pACE = &cifs_acl->ace_array[0];
2703 size = sizeof(struct cifs_posix_acl);
2704 size += sizeof(struct cifs_posix_ace) * count;
2705 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002706 if (size_of_data_area < size) {
Steve French50c2f752007-07-13 00:33:32 +00002707 cFYI(1, ("bad CIFS POSIX ACL size %d vs. %d",
2708 size_of_data_area, size));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709 return -EINVAL;
2710 }
Steve French790fe572007-07-07 19:25:05 +00002711 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712 count = le16_to_cpu(cifs_acl->access_entry_count);
2713 size = sizeof(struct cifs_posix_acl);
2714 size += sizeof(struct cifs_posix_ace) * count;
2715/* skip past access ACEs to get to default ACEs */
2716 pACE = &cifs_acl->ace_array[count];
2717 count = le16_to_cpu(cifs_acl->default_entry_count);
2718 size += sizeof(struct cifs_posix_ace) * count;
2719 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002720 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721 return -EINVAL;
2722 } else {
2723 /* illegal type */
2724 return -EINVAL;
2725 }
2726
2727 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002728 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002729 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002730 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731 return -ERANGE;
2732 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002733 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002734 for (i = 0; i < count ; i++) {
2735 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2736 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737 }
2738 }
2739 return size;
2740}
2741
Steve French50c2f752007-07-13 00:33:32 +00002742static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2743 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002744{
2745 __u16 rc = 0; /* 0 = ACL converted ok */
2746
Steve Frenchff7feac2005-11-15 16:45:16 -08002747 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2748 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002750 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751 /* Probably no need to le convert -1 on any arch but can not hurt */
2752 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002753 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002754 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Steve French50c2f752007-07-13 00:33:32 +00002755 /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 return rc;
2757}
2758
2759/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002760static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2761 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762{
2763 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002764 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2765 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766 int count;
2767 int i;
2768
Steve French790fe572007-07-07 19:25:05 +00002769 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770 return 0;
2771
2772 count = posix_acl_xattr_count((size_t)buflen);
Steve Frenchc18c8422007-07-18 23:21:09 +00002773 cFYI(1, ("setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002774 "version of %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002775 count, buflen, le32_to_cpu(local_acl->a_version)));
Steve French790fe572007-07-07 19:25:05 +00002776 if (le32_to_cpu(local_acl->a_version) != 2) {
Steve French50c2f752007-07-13 00:33:32 +00002777 cFYI(1, ("unknown POSIX ACL version %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002778 le32_to_cpu(local_acl->a_version)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779 return 0;
2780 }
2781 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002782 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002783 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002784 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002785 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 else {
Steve French50c2f752007-07-13 00:33:32 +00002787 cFYI(1, ("unknown ACL type %d", acl_type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 return 0;
2789 }
Steve French50c2f752007-07-13 00:33:32 +00002790 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2792 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002793 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 /* ACE not converted */
2795 break;
2796 }
2797 }
Steve French790fe572007-07-07 19:25:05 +00002798 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2800 rc += sizeof(struct cifs_posix_acl);
2801 /* BB add check to make sure ACL does not overflow SMB */
2802 }
2803 return rc;
2804}
2805
2806int
2807CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002808 const unsigned char *searchName,
2809 char *acl_inf, const int buflen, const int acl_type,
2810 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811{
2812/* SMB_QUERY_POSIX_ACL */
2813 TRANSACTION2_QPI_REQ *pSMB = NULL;
2814 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2815 int rc = 0;
2816 int bytes_returned;
2817 int name_len;
2818 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002819
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820 cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName));
2821
2822queryAclRetry:
2823 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2824 (void **) &pSMBr);
2825 if (rc)
2826 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002827
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2829 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002830 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002831 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 name_len++; /* trailing null */
2833 name_len *= 2;
2834 pSMB->FileName[name_len] = 0;
2835 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002836 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837 name_len = strnlen(searchName, PATH_MAX);
2838 name_len++; /* trailing null */
2839 strncpy(pSMB->FileName, searchName, name_len);
2840 }
2841
2842 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2843 pSMB->TotalDataCount = 0;
2844 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002845 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002846 pSMB->MaxDataCount = cpu_to_le16(4000);
2847 pSMB->MaxSetupCount = 0;
2848 pSMB->Reserved = 0;
2849 pSMB->Flags = 0;
2850 pSMB->Timeout = 0;
2851 pSMB->Reserved2 = 0;
2852 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002853 offsetof(struct smb_com_transaction2_qpi_req,
2854 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855 pSMB->DataCount = 0;
2856 pSMB->DataOffset = 0;
2857 pSMB->SetupCount = 1;
2858 pSMB->Reserved3 = 0;
2859 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2860 byte_count = params + 1 /* pad */ ;
2861 pSMB->TotalParameterCount = cpu_to_le16(params);
2862 pSMB->ParameterCount = pSMB->TotalParameterCount;
2863 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2864 pSMB->Reserved4 = 0;
2865 pSMB->hdr.smb_buf_length += byte_count;
2866 pSMB->ByteCount = cpu_to_le16(byte_count);
2867
2868 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2869 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002870 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871 if (rc) {
2872 cFYI(1, ("Send error in Query POSIX ACL = %d", rc));
2873 } else {
2874 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002875
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2877 if (rc || (pSMBr->ByteCount < 2))
2878 /* BB also check enough total bytes returned */
2879 rc = -EIO; /* bad smb */
2880 else {
2881 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2882 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2883 rc = cifs_copy_posix_acl(acl_inf,
2884 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002885 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002886 }
2887 }
2888 cifs_buf_release(pSMB);
2889 if (rc == -EAGAIN)
2890 goto queryAclRetry;
2891 return rc;
2892}
2893
2894int
2895CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002896 const unsigned char *fileName,
2897 const char *local_acl, const int buflen,
2898 const int acl_type,
2899 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900{
2901 struct smb_com_transaction2_spi_req *pSMB = NULL;
2902 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2903 char *parm_data;
2904 int name_len;
2905 int rc = 0;
2906 int bytes_returned = 0;
2907 __u16 params, byte_count, data_count, param_offset, offset;
2908
2909 cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName));
2910setAclRetry:
2911 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002912 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002913 if (rc)
2914 return rc;
2915 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2916 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002917 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002918 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002919 name_len++; /* trailing null */
2920 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002921 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002922 name_len = strnlen(fileName, PATH_MAX);
2923 name_len++; /* trailing null */
2924 strncpy(pSMB->FileName, fileName, name_len);
2925 }
2926 params = 6 + name_len;
2927 pSMB->MaxParameterCount = cpu_to_le16(2);
2928 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB size from sess */
2929 pSMB->MaxSetupCount = 0;
2930 pSMB->Reserved = 0;
2931 pSMB->Flags = 0;
2932 pSMB->Timeout = 0;
2933 pSMB->Reserved2 = 0;
2934 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002935 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002936 offset = param_offset + params;
2937 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2938 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2939
2940 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002941 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942
Steve French790fe572007-07-07 19:25:05 +00002943 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002944 rc = -EOPNOTSUPP;
2945 goto setACLerrorExit;
2946 }
2947 pSMB->DataOffset = cpu_to_le16(offset);
2948 pSMB->SetupCount = 1;
2949 pSMB->Reserved3 = 0;
2950 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2951 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2952 byte_count = 3 /* pad */ + params + data_count;
2953 pSMB->DataCount = cpu_to_le16(data_count);
2954 pSMB->TotalDataCount = pSMB->DataCount;
2955 pSMB->ParameterCount = cpu_to_le16(params);
2956 pSMB->TotalParameterCount = pSMB->ParameterCount;
2957 pSMB->Reserved4 = 0;
2958 pSMB->hdr.smb_buf_length += byte_count;
2959 pSMB->ByteCount = cpu_to_le16(byte_count);
2960 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002961 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00002962 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002963 cFYI(1, ("Set POSIX ACL returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964
2965setACLerrorExit:
2966 cifs_buf_release(pSMB);
2967 if (rc == -EAGAIN)
2968 goto setAclRetry;
2969 return rc;
2970}
2971
Steve Frenchf654bac2005-04-28 22:41:04 -07002972/* BB fix tabs in this function FIXME BB */
2973int
2974CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00002975 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07002976{
Steve French50c2f752007-07-13 00:33:32 +00002977 int rc = 0;
2978 struct smb_t2_qfi_req *pSMB = NULL;
2979 struct smb_t2_qfi_rsp *pSMBr = NULL;
2980 int bytes_returned;
2981 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07002982
Steve French790fe572007-07-07 19:25:05 +00002983 cFYI(1, ("In GetExtAttr"));
2984 if (tcon == NULL)
2985 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07002986
2987GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00002988 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2989 (void **) &pSMBr);
2990 if (rc)
2991 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07002992
Steve Frenchad7a2922008-02-07 23:25:02 +00002993 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00002994 pSMB->t2.TotalDataCount = 0;
2995 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
2996 /* BB find exact max data count below from sess structure BB */
2997 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
2998 pSMB->t2.MaxSetupCount = 0;
2999 pSMB->t2.Reserved = 0;
3000 pSMB->t2.Flags = 0;
3001 pSMB->t2.Timeout = 0;
3002 pSMB->t2.Reserved2 = 0;
3003 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3004 Fid) - 4);
3005 pSMB->t2.DataCount = 0;
3006 pSMB->t2.DataOffset = 0;
3007 pSMB->t2.SetupCount = 1;
3008 pSMB->t2.Reserved3 = 0;
3009 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3010 byte_count = params + 1 /* pad */ ;
3011 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3012 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3013 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
3014 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07003015 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00003016 pSMB->hdr.smb_buf_length += byte_count;
3017 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07003018
Steve French790fe572007-07-07 19:25:05 +00003019 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3020 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3021 if (rc) {
3022 cFYI(1, ("error %d in GetExtAttr", rc));
3023 } else {
3024 /* decode response */
3025 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3026 if (rc || (pSMBr->ByteCount < 2))
3027 /* BB also check enough total bytes returned */
3028 /* If rc should we check for EOPNOSUPP and
3029 disable the srvino flag? or in caller? */
3030 rc = -EIO; /* bad smb */
3031 else {
3032 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3033 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
3034 struct file_chattr_info *pfinfo;
3035 /* BB Do we need a cast or hash here ? */
3036 if (count != 16) {
3037 cFYI(1, ("Illegal size ret in GetExtAttr"));
3038 rc = -EIO;
3039 goto GetExtAttrOut;
3040 }
3041 pfinfo = (struct file_chattr_info *)
3042 (data_offset + (char *) &pSMBr->hdr.Protocol);
3043 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07003044 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00003045 }
3046 }
Steve Frenchf654bac2005-04-28 22:41:04 -07003047GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00003048 cifs_buf_release(pSMB);
3049 if (rc == -EAGAIN)
3050 goto GetExtAttrRetry;
3051 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003052}
3053
Steve Frenchf654bac2005-04-28 22:41:04 -07003054#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003055
Steve French297647c2007-10-12 04:11:59 +00003056#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08003057/* Get Security Descriptor (by handle) from remote server for a file or dir */
3058int
3059CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f0c2007-10-25 21:17:17 +00003060 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003061{
3062 int rc = 0;
3063 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003064 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003065 struct kvec iov[1];
3066
3067 cFYI(1, ("GetCifsACL"));
3068
Steve French630f3f0c2007-10-25 21:17:17 +00003069 *pbuflen = 0;
3070 *acl_inf = NULL;
3071
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003072 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003073 8 /* parm len */, tcon, (void **) &pSMB);
3074 if (rc)
3075 return rc;
3076
3077 pSMB->MaxParameterCount = cpu_to_le32(4);
3078 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3079 pSMB->MaxSetupCount = 0;
3080 pSMB->Fid = fid; /* file handle always le */
3081 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3082 CIFS_ACL_DACL);
3083 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3084 pSMB->hdr.smb_buf_length += 11;
3085 iov[0].iov_base = (char *)pSMB;
3086 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3087
Steve Frencha761ac52007-10-18 21:45:27 +00003088 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Steve French133672e2007-11-13 22:41:37 +00003089 CIFS_STD_OP);
Steve French0a4b92c2006-01-12 15:44:21 -08003090 cifs_stats_inc(&tcon->num_acl_get);
3091 if (rc) {
3092 cFYI(1, ("Send error in QuerySecDesc = %d", rc));
3093 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003094 __le32 *parm;
Steve French630f3f0c2007-10-25 21:17:17 +00003095 __u32 parm_len;
3096 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003097 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f0c2007-10-25 21:17:17 +00003098 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003099
3100/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003101 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f0c2007-10-25 21:17:17 +00003102 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003103 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003104 goto qsec_out;
3105 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3106
Steve French630f3f0c2007-10-25 21:17:17 +00003107 cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, *acl_inf));
Steve French0a4b92c2006-01-12 15:44:21 -08003108
3109 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3110 rc = -EIO; /* bad smb */
Steve French630f3f0c2007-10-25 21:17:17 +00003111 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003112 goto qsec_out;
3113 }
3114
3115/* BB check that data area is minimum length and as big as acl_len */
3116
Steve Frenchaf6f4612007-10-16 18:40:37 +00003117 acl_len = le32_to_cpu(*parm);
Steve French630f3f0c2007-10-25 21:17:17 +00003118 if (acl_len != *pbuflen) {
3119 cERROR(1, ("acl length %d does not match %d",
3120 acl_len, *pbuflen));
3121 if (*pbuflen > acl_len)
3122 *pbuflen = acl_len;
3123 }
Steve French0a4b92c2006-01-12 15:44:21 -08003124
Steve French630f3f0c2007-10-25 21:17:17 +00003125 /* check if buffer is big enough for the acl
3126 header followed by the smallest SID */
3127 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3128 (*pbuflen >= 64 * 1024)) {
3129 cERROR(1, ("bad acl length %d", *pbuflen));
3130 rc = -EINVAL;
3131 *pbuflen = 0;
3132 } else {
3133 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3134 if (*acl_inf == NULL) {
3135 *pbuflen = 0;
3136 rc = -ENOMEM;
3137 }
3138 memcpy(*acl_inf, pdata, *pbuflen);
3139 }
Steve French0a4b92c2006-01-12 15:44:21 -08003140 }
3141qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003142 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003143 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003144 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003145 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003146/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003147 return rc;
3148}
Steve French97837582007-12-31 07:47:21 +00003149
3150int
3151CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3152 struct cifs_ntsd *pntsd, __u32 acllen)
3153{
3154 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3155 int rc = 0;
3156 int bytes_returned = 0;
3157 SET_SEC_DESC_REQ *pSMB = NULL;
3158 NTRANSACT_RSP *pSMBr = NULL;
3159
3160setCifsAclRetry:
3161 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3162 (void **) &pSMBr);
3163 if (rc)
3164 return (rc);
3165
3166 pSMB->MaxSetupCount = 0;
3167 pSMB->Reserved = 0;
3168
3169 param_count = 8;
3170 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3171 data_count = acllen;
3172 data_offset = param_offset + param_count;
3173 byte_count = 3 /* pad */ + param_count;
3174
3175 pSMB->DataCount = cpu_to_le32(data_count);
3176 pSMB->TotalDataCount = pSMB->DataCount;
3177 pSMB->MaxParameterCount = cpu_to_le32(4);
3178 pSMB->MaxDataCount = cpu_to_le32(16384);
3179 pSMB->ParameterCount = cpu_to_le32(param_count);
3180 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3181 pSMB->TotalParameterCount = pSMB->ParameterCount;
3182 pSMB->DataOffset = cpu_to_le32(data_offset);
3183 pSMB->SetupCount = 0;
3184 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3185 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3186
3187 pSMB->Fid = fid; /* file handle always le */
3188 pSMB->Reserved2 = 0;
3189 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3190
3191 if (pntsd && acllen) {
3192 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3193 (char *) pntsd,
3194 acllen);
3195 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3196
3197 } else
3198 pSMB->hdr.smb_buf_length += byte_count;
3199
3200 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3201 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3202
3203 cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc));
3204 if (rc)
3205 cFYI(1, ("Set CIFS ACL returned %d", rc));
3206 cifs_buf_release(pSMB);
3207
3208 if (rc == -EAGAIN)
3209 goto setCifsAclRetry;
3210
3211 return (rc);
3212}
3213
Steve French297647c2007-10-12 04:11:59 +00003214#endif /* CONFIG_CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08003215
Steve French6b8edfe2005-08-23 20:26:03 -07003216/* Legacy Query Path Information call for lookup to old servers such
3217 as Win9x/WinME */
3218int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003219 const unsigned char *searchName,
3220 FILE_ALL_INFO *pFinfo,
3221 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003222{
Steve Frenchad7a2922008-02-07 23:25:02 +00003223 QUERY_INFORMATION_REQ *pSMB;
3224 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003225 int rc = 0;
3226 int bytes_returned;
3227 int name_len;
3228
Steve French50c2f752007-07-13 00:33:32 +00003229 cFYI(1, ("In SMBQPath path %s", searchName));
Steve French6b8edfe2005-08-23 20:26:03 -07003230QInfRetry:
3231 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003232 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003233 if (rc)
3234 return rc;
3235
3236 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3237 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003238 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3239 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003240 name_len++; /* trailing null */
3241 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003242 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003243 name_len = strnlen(searchName, PATH_MAX);
3244 name_len++; /* trailing null */
3245 strncpy(pSMB->FileName, searchName, name_len);
3246 }
3247 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003248 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003249 pSMB->hdr.smb_buf_length += (__u16) name_len;
3250 pSMB->ByteCount = cpu_to_le16(name_len);
3251
3252 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003253 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003254 if (rc) {
3255 cFYI(1, ("Send error in QueryInfo = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003256 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003257 struct timespec ts;
3258 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003259
3260 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003261 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003262 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003263 ts.tv_nsec = 0;
3264 ts.tv_sec = time;
3265 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003266 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003267 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3268 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003269 pFinfo->AllocationSize =
3270 cpu_to_le64(le32_to_cpu(pSMBr->size));
3271 pFinfo->EndOfFile = pFinfo->AllocationSize;
3272 pFinfo->Attributes =
3273 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003274 } else
3275 rc = -EIO; /* bad buffer passed in */
3276
3277 cifs_buf_release(pSMB);
3278
3279 if (rc == -EAGAIN)
3280 goto QInfRetry;
3281
3282 return rc;
3283}
3284
3285
3286
3287
Linus Torvalds1da177e2005-04-16 15:20:36 -07003288int
3289CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3290 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003291 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003292 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003293 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003294{
3295/* level 263 SMB_QUERY_FILE_ALL_INFO */
3296 TRANSACTION2_QPI_REQ *pSMB = NULL;
3297 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3298 int rc = 0;
3299 int bytes_returned;
3300 int name_len;
3301 __u16 params, byte_count;
3302
3303/* cFYI(1, ("In QPathInfo path %s", searchName)); */
3304QPathInfoRetry:
3305 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3306 (void **) &pSMBr);
3307 if (rc)
3308 return rc;
3309
3310 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3311 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003312 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003313 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003314 name_len++; /* trailing null */
3315 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003316 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003317 name_len = strnlen(searchName, PATH_MAX);
3318 name_len++; /* trailing null */
3319 strncpy(pSMB->FileName, searchName, name_len);
3320 }
3321
Steve French50c2f752007-07-13 00:33:32 +00003322 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003323 pSMB->TotalDataCount = 0;
3324 pSMB->MaxParameterCount = cpu_to_le16(2);
3325 pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
3326 pSMB->MaxSetupCount = 0;
3327 pSMB->Reserved = 0;
3328 pSMB->Flags = 0;
3329 pSMB->Timeout = 0;
3330 pSMB->Reserved2 = 0;
3331 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003332 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003333 pSMB->DataCount = 0;
3334 pSMB->DataOffset = 0;
3335 pSMB->SetupCount = 1;
3336 pSMB->Reserved3 = 0;
3337 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3338 byte_count = params + 1 /* pad */ ;
3339 pSMB->TotalParameterCount = cpu_to_le16(params);
3340 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003341 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003342 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3343 else
3344 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003345 pSMB->Reserved4 = 0;
3346 pSMB->hdr.smb_buf_length += byte_count;
3347 pSMB->ByteCount = cpu_to_le16(byte_count);
3348
3349 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3350 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3351 if (rc) {
3352 cFYI(1, ("Send error in QPathInfo = %d", rc));
3353 } else { /* decode response */
3354 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3355
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003356 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3357 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003358 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003359 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003360 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003361 rc = -EIO; /* 24 or 26 expected but we do not read
3362 last field */
3363 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003364 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003365 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003366
3367 /* On legacy responses we do not read the last field,
3368 EAsize, fortunately since it varies by subdialect and
3369 also note it differs on Set vs. Get, ie two bytes or 4
3370 bytes depending but we don't care here */
3371 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003372 size = sizeof(FILE_INFO_STANDARD);
3373 else
3374 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003375 memcpy((char *) pFindData,
3376 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003377 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003378 } else
3379 rc = -ENOMEM;
3380 }
3381 cifs_buf_release(pSMB);
3382 if (rc == -EAGAIN)
3383 goto QPathInfoRetry;
3384
3385 return rc;
3386}
3387
3388int
3389CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3390 const unsigned char *searchName,
3391 FILE_UNIX_BASIC_INFO * pFindData,
Steve French737b7582005-04-28 22:41:06 -07003392 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003393{
3394/* SMB_QUERY_FILE_UNIX_BASIC */
3395 TRANSACTION2_QPI_REQ *pSMB = NULL;
3396 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3397 int rc = 0;
3398 int bytes_returned = 0;
3399 int name_len;
3400 __u16 params, byte_count;
3401
3402 cFYI(1, ("In QPathInfo (Unix) the path %s", searchName));
3403UnixQPathInfoRetry:
3404 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3405 (void **) &pSMBr);
3406 if (rc)
3407 return rc;
3408
3409 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3410 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003411 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003412 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003413 name_len++; /* trailing null */
3414 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003415 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003416 name_len = strnlen(searchName, PATH_MAX);
3417 name_len++; /* trailing null */
3418 strncpy(pSMB->FileName, searchName, name_len);
3419 }
3420
Steve French50c2f752007-07-13 00:33:32 +00003421 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003422 pSMB->TotalDataCount = 0;
3423 pSMB->MaxParameterCount = cpu_to_le16(2);
3424 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003425 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003426 pSMB->MaxSetupCount = 0;
3427 pSMB->Reserved = 0;
3428 pSMB->Flags = 0;
3429 pSMB->Timeout = 0;
3430 pSMB->Reserved2 = 0;
3431 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003432 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003433 pSMB->DataCount = 0;
3434 pSMB->DataOffset = 0;
3435 pSMB->SetupCount = 1;
3436 pSMB->Reserved3 = 0;
3437 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3438 byte_count = params + 1 /* pad */ ;
3439 pSMB->TotalParameterCount = cpu_to_le16(params);
3440 pSMB->ParameterCount = pSMB->TotalParameterCount;
3441 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3442 pSMB->Reserved4 = 0;
3443 pSMB->hdr.smb_buf_length += byte_count;
3444 pSMB->ByteCount = cpu_to_le16(byte_count);
3445
3446 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3447 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3448 if (rc) {
3449 cFYI(1, ("Send error in QPathInfo = %d", rc));
3450 } else { /* decode response */
3451 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3452
3453 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve French1e71f252007-09-20 15:30:07 +00003454 cERROR(1, ("Malformed FILE_UNIX_BASIC_INFO response.\n"
3455 "Unix Extensions can be disabled on mount "
3456 "by specifying the nosfu mount option."));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003457 rc = -EIO; /* bad smb */
3458 } else {
3459 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3460 memcpy((char *) pFindData,
3461 (char *) &pSMBr->hdr.Protocol +
3462 data_offset,
Steve French630f3f0c2007-10-25 21:17:17 +00003463 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003464 }
3465 }
3466 cifs_buf_release(pSMB);
3467 if (rc == -EAGAIN)
3468 goto UnixQPathInfoRetry;
3469
3470 return rc;
3471}
3472
Linus Torvalds1da177e2005-04-16 15:20:36 -07003473/* xid, tcon, searchName and codepage are input parms, rest are returned */
3474int
3475CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003476 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003477 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003478 __u16 *pnetfid,
3479 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003480{
3481/* level 257 SMB_ */
3482 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3483 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003484 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003485 int rc = 0;
3486 int bytes_returned = 0;
3487 int name_len;
3488 __u16 params, byte_count;
3489
Steve French50c2f752007-07-13 00:33:32 +00003490 cFYI(1, ("In FindFirst for %s", searchName));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003491
3492findFirstRetry:
3493 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3494 (void **) &pSMBr);
3495 if (rc)
3496 return rc;
3497
3498 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3499 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003500 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003501 PATH_MAX, nls_codepage, remap);
3502 /* We can not add the asterik earlier in case
3503 it got remapped to 0xF03A as if it were part of the
3504 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003505 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003506 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003507 pSMB->FileName[name_len+1] = 0;
3508 pSMB->FileName[name_len+2] = '*';
3509 pSMB->FileName[name_len+3] = 0;
3510 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003511 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3512 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003513 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003514 } else { /* BB add check for overrun of SMB buf BB */
3515 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003517 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003518 free buffer exit; BB */
3519 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003520 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003521 pSMB->FileName[name_len+1] = '*';
3522 pSMB->FileName[name_len+2] = 0;
3523 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003524 }
3525
3526 params = 12 + name_len /* includes null */ ;
3527 pSMB->TotalDataCount = 0; /* no EAs */
3528 pSMB->MaxParameterCount = cpu_to_le16(10);
3529 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3530 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3531 pSMB->MaxSetupCount = 0;
3532 pSMB->Reserved = 0;
3533 pSMB->Flags = 0;
3534 pSMB->Timeout = 0;
3535 pSMB->Reserved2 = 0;
3536 byte_count = params + 1 /* pad */ ;
3537 pSMB->TotalParameterCount = cpu_to_le16(params);
3538 pSMB->ParameterCount = pSMB->TotalParameterCount;
3539 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003540 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3541 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003542 pSMB->DataCount = 0;
3543 pSMB->DataOffset = 0;
3544 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3545 pSMB->Reserved3 = 0;
3546 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3547 pSMB->SearchAttributes =
3548 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3549 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003550 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3551 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003552 CIFS_SEARCH_RETURN_RESUME);
3553 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3554
3555 /* BB what should we set StorageType to? Does it matter? BB */
3556 pSMB->SearchStorageType = 0;
3557 pSMB->hdr.smb_buf_length += byte_count;
3558 pSMB->ByteCount = cpu_to_le16(byte_count);
3559
3560 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3561 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003562 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003563
Steve French88274812006-03-09 22:21:45 +00003564 if (rc) {/* BB add logic to retry regular search if Unix search
3565 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003566 /* BB Add code to handle unsupported level rc */
3567 cFYI(1, ("Error in FindFirst = %d", rc));
Steve French1982c342005-08-17 12:38:22 -07003568
Steve French88274812006-03-09 22:21:45 +00003569 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003570
3571 /* BB eventually could optimize out free and realloc of buf */
3572 /* for this case */
3573 if (rc == -EAGAIN)
3574 goto findFirstRetry;
3575 } else { /* decode response */
3576 /* BB remember to free buffer if error BB */
3577 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003578 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003579 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
3580 psrch_inf->unicode = TRUE;
3581 else
3582 psrch_inf->unicode = FALSE;
3583
3584 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003585 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003586 psrch_inf->srch_entries_start =
3587 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003588 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003589 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3590 le16_to_cpu(pSMBr->t2.ParameterOffset));
3591
Steve French790fe572007-07-07 19:25:05 +00003592 if (parms->EndofSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003593 psrch_inf->endOfSearch = TRUE;
3594 else
3595 psrch_inf->endOfSearch = FALSE;
3596
Steve French50c2f752007-07-13 00:33:32 +00003597 psrch_inf->entries_in_buffer =
3598 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003599 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003600 psrch_inf->entries_in_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003601 *pnetfid = parms->SearchHandle;
3602 } else {
3603 cifs_buf_release(pSMB);
3604 }
3605 }
3606
3607 return rc;
3608}
3609
3610int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003611 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003612{
3613 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3614 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003615 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003616 char *response_data;
3617 int rc = 0;
3618 int bytes_returned, name_len;
3619 __u16 params, byte_count;
3620
3621 cFYI(1, ("In FindNext"));
3622
Steve French790fe572007-07-07 19:25:05 +00003623 if (psrch_inf->endOfSearch == TRUE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003624 return -ENOENT;
3625
3626 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3627 (void **) &pSMBr);
3628 if (rc)
3629 return rc;
3630
Steve French50c2f752007-07-13 00:33:32 +00003631 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003632 byte_count = 0;
3633 pSMB->TotalDataCount = 0; /* no EAs */
3634 pSMB->MaxParameterCount = cpu_to_le16(8);
3635 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003636 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3637 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003638 pSMB->MaxSetupCount = 0;
3639 pSMB->Reserved = 0;
3640 pSMB->Flags = 0;
3641 pSMB->Timeout = 0;
3642 pSMB->Reserved2 = 0;
3643 pSMB->ParameterOffset = cpu_to_le16(
3644 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3645 pSMB->DataCount = 0;
3646 pSMB->DataOffset = 0;
3647 pSMB->SetupCount = 1;
3648 pSMB->Reserved3 = 0;
3649 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3650 pSMB->SearchHandle = searchHandle; /* always kept as le */
3651 pSMB->SearchCount =
Steve French630f3f0c2007-10-25 21:17:17 +00003652 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003653 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3654 pSMB->ResumeKey = psrch_inf->resume_key;
3655 pSMB->SearchFlags =
3656 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3657
3658 name_len = psrch_inf->resume_name_len;
3659 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003660 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003661 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3662 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003663 /* 14 byte parm len above enough for 2 byte null terminator */
3664 pSMB->ResumeFileName[name_len] = 0;
3665 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003666 } else {
3667 rc = -EINVAL;
3668 goto FNext2_err_exit;
3669 }
3670 byte_count = params + 1 /* pad */ ;
3671 pSMB->TotalParameterCount = cpu_to_le16(params);
3672 pSMB->ParameterCount = pSMB->TotalParameterCount;
3673 pSMB->hdr.smb_buf_length += byte_count;
3674 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003675
Linus Torvalds1da177e2005-04-16 15:20:36 -07003676 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3677 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003678 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003679 if (rc) {
3680 if (rc == -EBADF) {
3681 psrch_inf->endOfSearch = TRUE;
Steve French50c2f752007-07-13 00:33:32 +00003682 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003683 } else
3684 cFYI(1, ("FindNext returned = %d", rc));
3685 } else { /* decode response */
3686 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003687
Steve French790fe572007-07-07 19:25:05 +00003688 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003689 /* BB fixme add lock for file (srch_info) struct here */
3690 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
3691 psrch_inf->unicode = TRUE;
3692 else
3693 psrch_inf->unicode = FALSE;
3694 response_data = (char *) &pSMBr->hdr.Protocol +
3695 le16_to_cpu(pSMBr->t2.ParameterOffset);
3696 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3697 response_data = (char *)&pSMBr->hdr.Protocol +
3698 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003699 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003700 cifs_small_buf_release(
3701 psrch_inf->ntwrk_buf_start);
3702 else
3703 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003704 psrch_inf->srch_entries_start = response_data;
3705 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003706 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003707 if (parms->EndofSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003708 psrch_inf->endOfSearch = TRUE;
3709 else
3710 psrch_inf->endOfSearch = FALSE;
Steve French50c2f752007-07-13 00:33:32 +00003711 psrch_inf->entries_in_buffer =
3712 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003713 psrch_inf->index_of_last_entry +=
3714 psrch_inf->entries_in_buffer;
Steve French50c2f752007-07-13 00:33:32 +00003715/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",
3716 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003717
3718 /* BB fixme add unlock here */
3719 }
3720
3721 }
3722
3723 /* BB On error, should we leave previous search buf (and count and
3724 last entry fields) intact or free the previous one? */
3725
3726 /* Note: On -EAGAIN error only caller can retry on handle based calls
3727 since file handle passed in no longer valid */
3728FNext2_err_exit:
3729 if (rc != 0)
3730 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003731 return rc;
3732}
3733
3734int
Steve French50c2f752007-07-13 00:33:32 +00003735CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3736 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003737{
3738 int rc = 0;
3739 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003740
3741 cFYI(1, ("In CIFSSMBFindClose"));
3742 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3743
3744 /* no sense returning error if session restarted
3745 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003746 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003747 return 0;
3748 if (rc)
3749 return rc;
3750
Linus Torvalds1da177e2005-04-16 15:20:36 -07003751 pSMB->FileID = searchHandle;
3752 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003753 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003754 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003755 cERROR(1, ("Send error in FindClose = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003756
Steve Frencha4544342005-08-24 13:59:35 -07003757 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003758
3759 /* Since session is dead, search handle closed on server already */
3760 if (rc == -EAGAIN)
3761 rc = 0;
3762
3763 return rc;
3764}
3765
Linus Torvalds1da177e2005-04-16 15:20:36 -07003766int
3767CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003768 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003769 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003770 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003771{
3772 int rc = 0;
3773 TRANSACTION2_QPI_REQ *pSMB = NULL;
3774 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3775 int name_len, bytes_returned;
3776 __u16 params, byte_count;
3777
Steve French50c2f752007-07-13 00:33:32 +00003778 cFYI(1, ("In GetSrvInodeNum for %s", searchName));
Steve French790fe572007-07-07 19:25:05 +00003779 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003780 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003781
3782GetInodeNumberRetry:
3783 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003784 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003785 if (rc)
3786 return rc;
3787
Linus Torvalds1da177e2005-04-16 15:20:36 -07003788 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3789 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003790 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003791 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003792 name_len++; /* trailing null */
3793 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003794 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003795 name_len = strnlen(searchName, PATH_MAX);
3796 name_len++; /* trailing null */
3797 strncpy(pSMB->FileName, searchName, name_len);
3798 }
3799
3800 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
3801 pSMB->TotalDataCount = 0;
3802 pSMB->MaxParameterCount = cpu_to_le16(2);
3803 /* BB find exact max data count below from sess structure BB */
3804 pSMB->MaxDataCount = cpu_to_le16(4000);
3805 pSMB->MaxSetupCount = 0;
3806 pSMB->Reserved = 0;
3807 pSMB->Flags = 0;
3808 pSMB->Timeout = 0;
3809 pSMB->Reserved2 = 0;
3810 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003811 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003812 pSMB->DataCount = 0;
3813 pSMB->DataOffset = 0;
3814 pSMB->SetupCount = 1;
3815 pSMB->Reserved3 = 0;
3816 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3817 byte_count = params + 1 /* pad */ ;
3818 pSMB->TotalParameterCount = cpu_to_le16(params);
3819 pSMB->ParameterCount = pSMB->TotalParameterCount;
3820 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
3821 pSMB->Reserved4 = 0;
3822 pSMB->hdr.smb_buf_length += byte_count;
3823 pSMB->ByteCount = cpu_to_le16(byte_count);
3824
3825 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3826 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3827 if (rc) {
3828 cFYI(1, ("error %d in QueryInternalInfo", rc));
3829 } else {
3830 /* decode response */
3831 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3832 if (rc || (pSMBr->ByteCount < 2))
3833 /* BB also check enough total bytes returned */
3834 /* If rc should we check for EOPNOSUPP and
3835 disable the srvino flag? or in caller? */
3836 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00003837 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003838 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3839 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00003840 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003841 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00003842 if (count < 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003843 cFYI(1, ("Illegal size ret in QryIntrnlInf"));
3844 rc = -EIO;
3845 goto GetInodeNumOut;
3846 }
3847 pfinfo = (struct file_internal_info *)
3848 (data_offset + (char *) &pSMBr->hdr.Protocol);
3849 *inode_number = pfinfo->UniqueId;
3850 }
3851 }
3852GetInodeNumOut:
3853 cifs_buf_release(pSMB);
3854 if (rc == -EAGAIN)
3855 goto GetInodeNumberRetry;
3856 return rc;
3857}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003858
3859int
3860CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
3861 const unsigned char *searchName,
3862 unsigned char **targetUNCs,
3863 unsigned int *number_of_UNC_in_array,
Steve French737b7582005-04-28 22:41:06 -07003864 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003865{
3866/* TRANS2_GET_DFS_REFERRAL */
3867 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
3868 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00003869 struct dfs_referral_level_3 *referrals = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003870 int rc = 0;
3871 int bytes_returned;
3872 int name_len;
3873 unsigned int i;
Steve French50c2f752007-07-13 00:33:32 +00003874 char *temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003875 __u16 params, byte_count;
3876 *number_of_UNC_in_array = 0;
3877 *targetUNCs = NULL;
3878
3879 cFYI(1, ("In GetDFSRefer the path %s", searchName));
3880 if (ses == NULL)
3881 return -ENODEV;
3882getDFSRetry:
3883 rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
3884 (void **) &pSMBr);
3885 if (rc)
3886 return rc;
Steve French50c2f752007-07-13 00:33:32 +00003887
3888 /* server pointer checked in called function,
Steve French1982c342005-08-17 12:38:22 -07003889 but should never be null here anyway */
3890 pSMB->hdr.Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003891 pSMB->hdr.Tid = ses->ipc_tid;
3892 pSMB->hdr.Uid = ses->Suid;
Steve French26f57362007-08-30 22:09:15 +00003893 if (ses->capabilities & CAP_STATUS32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003894 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
Steve French26f57362007-08-30 22:09:15 +00003895 if (ses->capabilities & CAP_DFS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003896 pSMB->hdr.Flags2 |= SMBFLG2_DFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003897
3898 if (ses->capabilities & CAP_UNICODE) {
3899 pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
3900 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003901 cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
Steve French737b7582005-04-28 22:41:06 -07003902 searchName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003903 name_len++; /* trailing null */
3904 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003905 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003906 name_len = strnlen(searchName, PATH_MAX);
3907 name_len++; /* trailing null */
3908 strncpy(pSMB->RequestFileName, searchName, name_len);
3909 }
3910
Steve French790fe572007-07-07 19:25:05 +00003911 if (ses->server) {
3912 if (ses->server->secMode &
Steve French1a4e15a2006-10-12 21:33:51 +00003913 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
3914 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
3915 }
3916
Steve French50c2f752007-07-13 00:33:32 +00003917 pSMB->hdr.Uid = ses->Suid;
Steve French1a4e15a2006-10-12 21:33:51 +00003918
Linus Torvalds1da177e2005-04-16 15:20:36 -07003919 params = 2 /* level */ + name_len /*includes null */ ;
3920 pSMB->TotalDataCount = 0;
3921 pSMB->DataCount = 0;
3922 pSMB->DataOffset = 0;
3923 pSMB->MaxParameterCount = 0;
3924 pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
3925 pSMB->MaxSetupCount = 0;
3926 pSMB->Reserved = 0;
3927 pSMB->Flags = 0;
3928 pSMB->Timeout = 0;
3929 pSMB->Reserved2 = 0;
3930 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003931 struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003932 pSMB->SetupCount = 1;
3933 pSMB->Reserved3 = 0;
3934 pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
3935 byte_count = params + 3 /* pad */ ;
3936 pSMB->ParameterCount = cpu_to_le16(params);
3937 pSMB->TotalParameterCount = pSMB->ParameterCount;
3938 pSMB->MaxReferralLevel = cpu_to_le16(3);
3939 pSMB->hdr.smb_buf_length += byte_count;
3940 pSMB->ByteCount = cpu_to_le16(byte_count);
3941
3942 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
3943 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3944 if (rc) {
3945 cFYI(1, ("Send error in GetDFSRefer = %d", rc));
3946 } else { /* decode response */
3947/* BB Add logic to parse referrals here */
3948 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3949
Steve French50c2f752007-07-13 00:33:32 +00003950 /* BB Also check if enough total bytes returned? */
3951 if (rc || (pSMBr->ByteCount < 17))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003952 rc = -EIO; /* bad smb */
3953 else {
Steve French50c2f752007-07-13 00:33:32 +00003954 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003955 __u16 data_count = le16_to_cpu(pSMBr->t2.DataCount);
3956
3957 cFYI(1,
Steve French50c2f752007-07-13 00:33:32 +00003958 ("Decoding GetDFSRefer response BCC: %d Offset %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003959 pSMBr->ByteCount, data_offset));
Steve French50c2f752007-07-13 00:33:32 +00003960 referrals =
3961 (struct dfs_referral_level_3 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003962 (8 /* sizeof start of data block */ +
3963 data_offset +
Steve French50c2f752007-07-13 00:33:32 +00003964 (char *) &pSMBr->hdr.Protocol);
Steve Frenchc18c8422007-07-18 23:21:09 +00003965 cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n"
Steve French63135e02007-07-17 17:34:02 +00003966 "for referral one refer size: 0x%x srv "
3967 "type: 0x%x refer flags: 0x%x ttl: 0x%x",
Steve French50c2f752007-07-13 00:33:32 +00003968 le16_to_cpu(pSMBr->NumberOfReferrals),
3969 le16_to_cpu(pSMBr->DFSFlags),
3970 le16_to_cpu(referrals->ReferralSize),
3971 le16_to_cpu(referrals->ServerType),
3972 le16_to_cpu(referrals->ReferralFlags),
3973 le16_to_cpu(referrals->TimeToLive)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003974 /* BB This field is actually two bytes in from start of
3975 data block so we could do safety check that DataBlock
3976 begins at address of pSMBr->NumberOfReferrals */
Steve French50c2f752007-07-13 00:33:32 +00003977 *number_of_UNC_in_array =
3978 le16_to_cpu(pSMBr->NumberOfReferrals);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003979
3980 /* BB Fix below so can return more than one referral */
Steve French790fe572007-07-07 19:25:05 +00003981 if (*number_of_UNC_in_array > 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003982 *number_of_UNC_in_array = 1;
3983
3984 /* get the length of the strings describing refs */
3985 name_len = 0;
Steve French50c2f752007-07-13 00:33:32 +00003986 for (i = 0; i < *number_of_UNC_in_array; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003987 /* make sure that DfsPathOffset not past end */
Steve French50c2f752007-07-13 00:33:32 +00003988 __u16 offset =
3989 le16_to_cpu(referrals->DfsPathOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003990 if (offset > data_count) {
Steve French50c2f752007-07-13 00:33:32 +00003991 /* if invalid referral, stop here and do
Linus Torvalds1da177e2005-04-16 15:20:36 -07003992 not try to copy any more */
3993 *number_of_UNC_in_array = i;
3994 break;
Steve French50c2f752007-07-13 00:33:32 +00003995 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003996 temp = ((char *)referrals) + offset;
3997
3998 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00003999 name_len += UniStrnlen((wchar_t *)temp,
4000 data_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004001 } else {
Steve French50c2f752007-07-13 00:33:32 +00004002 name_len += strnlen(temp, data_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004003 }
4004 referrals++;
Steve French50c2f752007-07-13 00:33:32 +00004005 /* BB add check that referral pointer does
4006 not fall off end PDU */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004007 }
4008 /* BB add check for name_len bigger than bcc */
Steve French50c2f752007-07-13 00:33:32 +00004009 *targetUNCs =
4010 kmalloc(name_len+1+(*number_of_UNC_in_array),
4011 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00004012 if (*targetUNCs == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004013 rc = -ENOMEM;
4014 goto GetDFSRefExit;
4015 }
4016 /* copy the ref strings */
Steve French50c2f752007-07-13 00:33:32 +00004017 referrals = (struct dfs_referral_level_3 *)
4018 (8 /* sizeof data hdr */ + data_offset +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004019 (char *) &pSMBr->hdr.Protocol);
4020
Steve French50c2f752007-07-13 00:33:32 +00004021 for (i = 0; i < *number_of_UNC_in_array; i++) {
4022 temp = ((char *)referrals) +
4023 le16_to_cpu(referrals->DfsPathOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004024 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
4025 cifs_strfromUCS_le(*targetUNCs,
Steve French50c2f752007-07-13 00:33:32 +00004026 (__le16 *) temp,
4027 name_len,
4028 nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004029 } else {
Steve French50c2f752007-07-13 00:33:32 +00004030 strncpy(*targetUNCs, temp, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004031 }
4032 /* BB update target_uncs pointers */
4033 referrals++;
4034 }
4035 temp = *targetUNCs;
4036 temp[name_len] = 0;
4037 }
4038
4039 }
4040GetDFSRefExit:
4041 if (pSMB)
4042 cifs_buf_release(pSMB);
4043
4044 if (rc == -EAGAIN)
4045 goto getDFSRetry;
4046
4047 return rc;
4048}
4049
Steve French20962432005-09-21 22:05:57 -07004050/* Query File System Info such as free space to old servers such as Win 9x */
4051int
4052SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4053{
4054/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4055 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4056 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4057 FILE_SYSTEM_ALLOC_INFO *response_data;
4058 int rc = 0;
4059 int bytes_returned = 0;
4060 __u16 params, byte_count;
4061
4062 cFYI(1, ("OldQFSInfo"));
4063oldQFSInfoRetry:
4064 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4065 (void **) &pSMBr);
4066 if (rc)
4067 return rc;
Steve French20962432005-09-21 22:05:57 -07004068
4069 params = 2; /* level */
4070 pSMB->TotalDataCount = 0;
4071 pSMB->MaxParameterCount = cpu_to_le16(2);
4072 pSMB->MaxDataCount = cpu_to_le16(1000);
4073 pSMB->MaxSetupCount = 0;
4074 pSMB->Reserved = 0;
4075 pSMB->Flags = 0;
4076 pSMB->Timeout = 0;
4077 pSMB->Reserved2 = 0;
4078 byte_count = params + 1 /* pad */ ;
4079 pSMB->TotalParameterCount = cpu_to_le16(params);
4080 pSMB->ParameterCount = pSMB->TotalParameterCount;
4081 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4082 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4083 pSMB->DataCount = 0;
4084 pSMB->DataOffset = 0;
4085 pSMB->SetupCount = 1;
4086 pSMB->Reserved3 = 0;
4087 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4088 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4089 pSMB->hdr.smb_buf_length += byte_count;
4090 pSMB->ByteCount = cpu_to_le16(byte_count);
4091
4092 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4093 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4094 if (rc) {
4095 cFYI(1, ("Send error in QFSInfo = %d", rc));
4096 } else { /* decode response */
4097 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4098
4099 if (rc || (pSMBr->ByteCount < 18))
4100 rc = -EIO; /* bad smb */
4101 else {
4102 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00004103 cFYI(1, ("qfsinf resp BCC: %d Offset %d",
Steve French20962432005-09-21 22:05:57 -07004104 pSMBr->ByteCount, data_offset));
4105
Steve French50c2f752007-07-13 00:33:32 +00004106 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004107 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4108 FSData->f_bsize =
4109 le16_to_cpu(response_data->BytesPerSector) *
4110 le32_to_cpu(response_data->
4111 SectorsPerAllocationUnit);
4112 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004113 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004114 FSData->f_bfree = FSData->f_bavail =
4115 le32_to_cpu(response_data->FreeAllocationUnits);
4116 cFYI(1,
4117 ("Blocks: %lld Free: %lld Block size %ld",
4118 (unsigned long long)FSData->f_blocks,
4119 (unsigned long long)FSData->f_bfree,
4120 FSData->f_bsize));
4121 }
4122 }
4123 cifs_buf_release(pSMB);
4124
4125 if (rc == -EAGAIN)
4126 goto oldQFSInfoRetry;
4127
4128 return rc;
4129}
4130
Linus Torvalds1da177e2005-04-16 15:20:36 -07004131int
Steve French737b7582005-04-28 22:41:06 -07004132CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004133{
4134/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4135 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4136 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4137 FILE_SYSTEM_INFO *response_data;
4138 int rc = 0;
4139 int bytes_returned = 0;
4140 __u16 params, byte_count;
4141
4142 cFYI(1, ("In QFSInfo"));
4143QFSInfoRetry:
4144 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4145 (void **) &pSMBr);
4146 if (rc)
4147 return rc;
4148
4149 params = 2; /* level */
4150 pSMB->TotalDataCount = 0;
4151 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004152 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004153 pSMB->MaxSetupCount = 0;
4154 pSMB->Reserved = 0;
4155 pSMB->Flags = 0;
4156 pSMB->Timeout = 0;
4157 pSMB->Reserved2 = 0;
4158 byte_count = params + 1 /* pad */ ;
4159 pSMB->TotalParameterCount = cpu_to_le16(params);
4160 pSMB->ParameterCount = pSMB->TotalParameterCount;
4161 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004162 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004163 pSMB->DataCount = 0;
4164 pSMB->DataOffset = 0;
4165 pSMB->SetupCount = 1;
4166 pSMB->Reserved3 = 0;
4167 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4168 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4169 pSMB->hdr.smb_buf_length += byte_count;
4170 pSMB->ByteCount = cpu_to_le16(byte_count);
4171
4172 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4173 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4174 if (rc) {
Steve French20962432005-09-21 22:05:57 -07004175 cFYI(1, ("Send error in QFSInfo = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004176 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004177 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004178
Steve French20962432005-09-21 22:05:57 -07004179 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004180 rc = -EIO; /* bad smb */
4181 else {
4182 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004183
4184 response_data =
4185 (FILE_SYSTEM_INFO
4186 *) (((char *) &pSMBr->hdr.Protocol) +
4187 data_offset);
4188 FSData->f_bsize =
4189 le32_to_cpu(response_data->BytesPerSector) *
4190 le32_to_cpu(response_data->
4191 SectorsPerAllocationUnit);
4192 FSData->f_blocks =
4193 le64_to_cpu(response_data->TotalAllocationUnits);
4194 FSData->f_bfree = FSData->f_bavail =
4195 le64_to_cpu(response_data->FreeAllocationUnits);
4196 cFYI(1,
4197 ("Blocks: %lld Free: %lld Block size %ld",
4198 (unsigned long long)FSData->f_blocks,
4199 (unsigned long long)FSData->f_bfree,
4200 FSData->f_bsize));
4201 }
4202 }
4203 cifs_buf_release(pSMB);
4204
4205 if (rc == -EAGAIN)
4206 goto QFSInfoRetry;
4207
4208 return rc;
4209}
4210
4211int
Steve French737b7582005-04-28 22:41:06 -07004212CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004213{
4214/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4215 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4216 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4217 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4218 int rc = 0;
4219 int bytes_returned = 0;
4220 __u16 params, byte_count;
4221
4222 cFYI(1, ("In QFSAttributeInfo"));
4223QFSAttributeRetry:
4224 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4225 (void **) &pSMBr);
4226 if (rc)
4227 return rc;
4228
4229 params = 2; /* level */
4230 pSMB->TotalDataCount = 0;
4231 pSMB->MaxParameterCount = cpu_to_le16(2);
4232 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
4233 pSMB->MaxSetupCount = 0;
4234 pSMB->Reserved = 0;
4235 pSMB->Flags = 0;
4236 pSMB->Timeout = 0;
4237 pSMB->Reserved2 = 0;
4238 byte_count = params + 1 /* pad */ ;
4239 pSMB->TotalParameterCount = cpu_to_le16(params);
4240 pSMB->ParameterCount = pSMB->TotalParameterCount;
4241 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004242 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004243 pSMB->DataCount = 0;
4244 pSMB->DataOffset = 0;
4245 pSMB->SetupCount = 1;
4246 pSMB->Reserved3 = 0;
4247 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4248 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4249 pSMB->hdr.smb_buf_length += byte_count;
4250 pSMB->ByteCount = cpu_to_le16(byte_count);
4251
4252 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4253 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4254 if (rc) {
4255 cERROR(1, ("Send error in QFSAttributeInfo = %d", rc));
4256 } else { /* decode response */
4257 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4258
Steve French50c2f752007-07-13 00:33:32 +00004259 if (rc || (pSMBr->ByteCount < 13)) {
4260 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004261 rc = -EIO; /* bad smb */
4262 } else {
4263 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4264 response_data =
4265 (FILE_SYSTEM_ATTRIBUTE_INFO
4266 *) (((char *) &pSMBr->hdr.Protocol) +
4267 data_offset);
4268 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004269 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004270 }
4271 }
4272 cifs_buf_release(pSMB);
4273
4274 if (rc == -EAGAIN)
4275 goto QFSAttributeRetry;
4276
4277 return rc;
4278}
4279
4280int
Steve French737b7582005-04-28 22:41:06 -07004281CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004282{
4283/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4284 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4285 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4286 FILE_SYSTEM_DEVICE_INFO *response_data;
4287 int rc = 0;
4288 int bytes_returned = 0;
4289 __u16 params, byte_count;
4290
4291 cFYI(1, ("In QFSDeviceInfo"));
4292QFSDeviceRetry:
4293 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4294 (void **) &pSMBr);
4295 if (rc)
4296 return rc;
4297
4298 params = 2; /* level */
4299 pSMB->TotalDataCount = 0;
4300 pSMB->MaxParameterCount = cpu_to_le16(2);
4301 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
4302 pSMB->MaxSetupCount = 0;
4303 pSMB->Reserved = 0;
4304 pSMB->Flags = 0;
4305 pSMB->Timeout = 0;
4306 pSMB->Reserved2 = 0;
4307 byte_count = params + 1 /* pad */ ;
4308 pSMB->TotalParameterCount = cpu_to_le16(params);
4309 pSMB->ParameterCount = pSMB->TotalParameterCount;
4310 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004311 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004312
4313 pSMB->DataCount = 0;
4314 pSMB->DataOffset = 0;
4315 pSMB->SetupCount = 1;
4316 pSMB->Reserved3 = 0;
4317 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4318 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4319 pSMB->hdr.smb_buf_length += byte_count;
4320 pSMB->ByteCount = cpu_to_le16(byte_count);
4321
4322 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4323 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4324 if (rc) {
4325 cFYI(1, ("Send error in QFSDeviceInfo = %d", rc));
4326 } else { /* decode response */
4327 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4328
Steve French630f3f0c2007-10-25 21:17:17 +00004329 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004330 rc = -EIO; /* bad smb */
4331 else {
4332 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4333 response_data =
Steve French737b7582005-04-28 22:41:06 -07004334 (FILE_SYSTEM_DEVICE_INFO *)
4335 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004336 data_offset);
4337 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004338 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004339 }
4340 }
4341 cifs_buf_release(pSMB);
4342
4343 if (rc == -EAGAIN)
4344 goto QFSDeviceRetry;
4345
4346 return rc;
4347}
4348
4349int
Steve French737b7582005-04-28 22:41:06 -07004350CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004351{
4352/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4353 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4354 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4355 FILE_SYSTEM_UNIX_INFO *response_data;
4356 int rc = 0;
4357 int bytes_returned = 0;
4358 __u16 params, byte_count;
4359
4360 cFYI(1, ("In QFSUnixInfo"));
4361QFSUnixRetry:
4362 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4363 (void **) &pSMBr);
4364 if (rc)
4365 return rc;
4366
4367 params = 2; /* level */
4368 pSMB->TotalDataCount = 0;
4369 pSMB->DataCount = 0;
4370 pSMB->DataOffset = 0;
4371 pSMB->MaxParameterCount = cpu_to_le16(2);
4372 pSMB->MaxDataCount = cpu_to_le16(100); /* BB find exact max SMB PDU from sess structure BB */
4373 pSMB->MaxSetupCount = 0;
4374 pSMB->Reserved = 0;
4375 pSMB->Flags = 0;
4376 pSMB->Timeout = 0;
4377 pSMB->Reserved2 = 0;
4378 byte_count = params + 1 /* pad */ ;
4379 pSMB->ParameterCount = cpu_to_le16(params);
4380 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004381 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4382 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004383 pSMB->SetupCount = 1;
4384 pSMB->Reserved3 = 0;
4385 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4386 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4387 pSMB->hdr.smb_buf_length += byte_count;
4388 pSMB->ByteCount = cpu_to_le16(byte_count);
4389
4390 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4391 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4392 if (rc) {
4393 cERROR(1, ("Send error in QFSUnixInfo = %d", rc));
4394 } else { /* decode response */
4395 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4396
4397 if (rc || (pSMBr->ByteCount < 13)) {
4398 rc = -EIO; /* bad smb */
4399 } else {
4400 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4401 response_data =
4402 (FILE_SYSTEM_UNIX_INFO
4403 *) (((char *) &pSMBr->hdr.Protocol) +
4404 data_offset);
4405 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004406 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004407 }
4408 }
4409 cifs_buf_release(pSMB);
4410
4411 if (rc == -EAGAIN)
4412 goto QFSUnixRetry;
4413
4414
4415 return rc;
4416}
4417
Jeremy Allisonac670552005-06-22 17:26:35 -07004418int
Steve French45abc6e2005-06-23 13:42:03 -05004419CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004420{
4421/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4422 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4423 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4424 int rc = 0;
4425 int bytes_returned = 0;
4426 __u16 params, param_offset, offset, byte_count;
4427
4428 cFYI(1, ("In SETFSUnixInfo"));
4429SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004430 /* BB switch to small buf init to save memory */
Jeremy Allisonac670552005-06-22 17:26:35 -07004431 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4432 (void **) &pSMBr);
4433 if (rc)
4434 return rc;
4435
4436 params = 4; /* 2 bytes zero followed by info level. */
4437 pSMB->MaxSetupCount = 0;
4438 pSMB->Reserved = 0;
4439 pSMB->Flags = 0;
4440 pSMB->Timeout = 0;
4441 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004442 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4443 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004444 offset = param_offset + params;
4445
4446 pSMB->MaxParameterCount = cpu_to_le16(4);
4447 pSMB->MaxDataCount = cpu_to_le16(100); /* BB find exact max SMB PDU from sess structure BB */
4448 pSMB->SetupCount = 1;
4449 pSMB->Reserved3 = 0;
4450 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4451 byte_count = 1 /* pad */ + params + 12;
4452
4453 pSMB->DataCount = cpu_to_le16(12);
4454 pSMB->ParameterCount = cpu_to_le16(params);
4455 pSMB->TotalDataCount = pSMB->DataCount;
4456 pSMB->TotalParameterCount = pSMB->ParameterCount;
4457 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4458 pSMB->DataOffset = cpu_to_le16(offset);
4459
4460 /* Params. */
4461 pSMB->FileNum = 0;
4462 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4463
4464 /* Data. */
4465 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4466 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4467 pSMB->ClientUnixCap = cpu_to_le64(cap);
4468
4469 pSMB->hdr.smb_buf_length += byte_count;
4470 pSMB->ByteCount = cpu_to_le16(byte_count);
4471
4472 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4473 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4474 if (rc) {
4475 cERROR(1, ("Send error in SETFSUnixInfo = %d", rc));
4476 } else { /* decode response */
4477 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004478 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004479 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004480 }
4481 cifs_buf_release(pSMB);
4482
4483 if (rc == -EAGAIN)
4484 goto SETFSUnixRetry;
4485
4486 return rc;
4487}
4488
4489
Linus Torvalds1da177e2005-04-16 15:20:36 -07004490
4491int
4492CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004493 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004494{
4495/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4496 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4497 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4498 FILE_SYSTEM_POSIX_INFO *response_data;
4499 int rc = 0;
4500 int bytes_returned = 0;
4501 __u16 params, byte_count;
4502
4503 cFYI(1, ("In QFSPosixInfo"));
4504QFSPosixRetry:
4505 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4506 (void **) &pSMBr);
4507 if (rc)
4508 return rc;
4509
4510 params = 2; /* level */
4511 pSMB->TotalDataCount = 0;
4512 pSMB->DataCount = 0;
4513 pSMB->DataOffset = 0;
4514 pSMB->MaxParameterCount = cpu_to_le16(2);
4515 pSMB->MaxDataCount = cpu_to_le16(100); /* BB find exact max SMB PDU from sess structure BB */
4516 pSMB->MaxSetupCount = 0;
4517 pSMB->Reserved = 0;
4518 pSMB->Flags = 0;
4519 pSMB->Timeout = 0;
4520 pSMB->Reserved2 = 0;
4521 byte_count = params + 1 /* pad */ ;
4522 pSMB->ParameterCount = cpu_to_le16(params);
4523 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004524 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4525 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004526 pSMB->SetupCount = 1;
4527 pSMB->Reserved3 = 0;
4528 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4529 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4530 pSMB->hdr.smb_buf_length += byte_count;
4531 pSMB->ByteCount = cpu_to_le16(byte_count);
4532
4533 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4534 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4535 if (rc) {
4536 cFYI(1, ("Send error in QFSUnixInfo = %d", rc));
4537 } else { /* decode response */
4538 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4539
4540 if (rc || (pSMBr->ByteCount < 13)) {
4541 rc = -EIO; /* bad smb */
4542 } else {
4543 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4544 response_data =
4545 (FILE_SYSTEM_POSIX_INFO
4546 *) (((char *) &pSMBr->hdr.Protocol) +
4547 data_offset);
4548 FSData->f_bsize =
4549 le32_to_cpu(response_data->BlockSize);
4550 FSData->f_blocks =
4551 le64_to_cpu(response_data->TotalBlocks);
4552 FSData->f_bfree =
4553 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004554 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004555 FSData->f_bavail = FSData->f_bfree;
4556 } else {
4557 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004558 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004559 }
Steve French790fe572007-07-07 19:25:05 +00004560 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004561 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004562 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004563 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004564 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004565 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004566 }
4567 }
4568 cifs_buf_release(pSMB);
4569
4570 if (rc == -EAGAIN)
4571 goto QFSPosixRetry;
4572
4573 return rc;
4574}
4575
4576
Steve French50c2f752007-07-13 00:33:32 +00004577/* We can not use write of zero bytes trick to
4578 set file size due to need for large file support. Also note that
4579 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004580 routine which is only needed to work around a sharing violation bug
4581 in Samba which this routine can run into */
4582
4583int
4584CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00004585 __u64 size, int SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004586 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004587{
4588 struct smb_com_transaction2_spi_req *pSMB = NULL;
4589 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4590 struct file_end_of_file_info *parm_data;
4591 int name_len;
4592 int rc = 0;
4593 int bytes_returned = 0;
4594 __u16 params, byte_count, data_count, param_offset, offset;
4595
4596 cFYI(1, ("In SetEOF"));
4597SetEOFRetry:
4598 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4599 (void **) &pSMBr);
4600 if (rc)
4601 return rc;
4602
4603 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4604 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004605 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004606 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004607 name_len++; /* trailing null */
4608 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004609 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004610 name_len = strnlen(fileName, PATH_MAX);
4611 name_len++; /* trailing null */
4612 strncpy(pSMB->FileName, fileName, name_len);
4613 }
4614 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004615 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004616 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004617 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004618 pSMB->MaxSetupCount = 0;
4619 pSMB->Reserved = 0;
4620 pSMB->Flags = 0;
4621 pSMB->Timeout = 0;
4622 pSMB->Reserved2 = 0;
4623 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004624 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004625 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004626 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004627 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4628 pSMB->InformationLevel =
4629 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4630 else
4631 pSMB->InformationLevel =
4632 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4633 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004634 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4635 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004636 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004637 else
4638 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004639 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004640 }
4641
4642 parm_data =
4643 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4644 offset);
4645 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4646 pSMB->DataOffset = cpu_to_le16(offset);
4647 pSMB->SetupCount = 1;
4648 pSMB->Reserved3 = 0;
4649 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4650 byte_count = 3 /* pad */ + params + data_count;
4651 pSMB->DataCount = cpu_to_le16(data_count);
4652 pSMB->TotalDataCount = pSMB->DataCount;
4653 pSMB->ParameterCount = cpu_to_le16(params);
4654 pSMB->TotalParameterCount = pSMB->ParameterCount;
4655 pSMB->Reserved4 = 0;
4656 pSMB->hdr.smb_buf_length += byte_count;
4657 parm_data->FileSize = cpu_to_le64(size);
4658 pSMB->ByteCount = cpu_to_le16(byte_count);
4659 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4660 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004661 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004662 cFYI(1, ("SetPathInfo (file size) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004663
4664 cifs_buf_release(pSMB);
4665
4666 if (rc == -EAGAIN)
4667 goto SetEOFRetry;
4668
4669 return rc;
4670}
4671
4672int
Steve French50c2f752007-07-13 00:33:32 +00004673CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
4674 __u16 fid, __u32 pid_of_opener, int SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004675{
4676 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004677 char *data_offset;
4678 struct file_end_of_file_info *parm_data;
4679 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004680 __u16 params, param_offset, offset, byte_count, count;
4681
4682 cFYI(1, ("SetFileSize (via SetFileInfo) %lld",
4683 (long long)size));
Steve Frenchcd634992005-04-28 22:41:10 -07004684 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4685
Linus Torvalds1da177e2005-04-16 15:20:36 -07004686 if (rc)
4687 return rc;
4688
4689 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4690 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004691
Linus Torvalds1da177e2005-04-16 15:20:36 -07004692 params = 6;
4693 pSMB->MaxSetupCount = 0;
4694 pSMB->Reserved = 0;
4695 pSMB->Flags = 0;
4696 pSMB->Timeout = 0;
4697 pSMB->Reserved2 = 0;
4698 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4699 offset = param_offset + params;
4700
Steve French50c2f752007-07-13 00:33:32 +00004701 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004702
4703 count = sizeof(struct file_end_of_file_info);
4704 pSMB->MaxParameterCount = cpu_to_le16(2);
4705 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB PDU from sess */
4706 pSMB->SetupCount = 1;
4707 pSMB->Reserved3 = 0;
4708 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4709 byte_count = 3 /* pad */ + params + count;
4710 pSMB->DataCount = cpu_to_le16(count);
4711 pSMB->ParameterCount = cpu_to_le16(params);
4712 pSMB->TotalDataCount = pSMB->DataCount;
4713 pSMB->TotalParameterCount = pSMB->ParameterCount;
4714 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4715 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004716 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4717 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004718 pSMB->DataOffset = cpu_to_le16(offset);
4719 parm_data->FileSize = cpu_to_le64(size);
4720 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004721 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004722 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4723 pSMB->InformationLevel =
4724 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4725 else
4726 pSMB->InformationLevel =
4727 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004728 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004729 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4730 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004731 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004732 else
4733 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004734 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004735 }
4736 pSMB->Reserved4 = 0;
4737 pSMB->hdr.smb_buf_length += byte_count;
4738 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004739 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004740 if (rc) {
4741 cFYI(1,
4742 ("Send error in SetFileInfo (SetFileSize) = %d",
4743 rc));
4744 }
4745
Steve French50c2f752007-07-13 00:33:32 +00004746 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004747 since file handle passed in no longer valid */
4748
4749 return rc;
4750}
4751
Steve French50c2f752007-07-13 00:33:32 +00004752/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004753 an open handle, rather than by pathname - this is awkward due to
4754 potential access conflicts on the open, but it is unavoidable for these
4755 old servers since the only other choice is to go from 100 nanosecond DCE
4756 time and resort to the original setpathinfo level which takes the ancient
4757 DOS time format with 2 second granularity */
4758int
Steve French50c2f752007-07-13 00:33:32 +00004759CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon,
4760 const FILE_BASIC_INFO *data, __u16 fid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004761{
4762 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004763 char *data_offset;
4764 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004765 __u16 params, param_offset, offset, byte_count, count;
4766
4767 cFYI(1, ("Set Times (via SetFileInfo)"));
Steve Frenchcd634992005-04-28 22:41:10 -07004768 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4769
Linus Torvalds1da177e2005-04-16 15:20:36 -07004770 if (rc)
4771 return rc;
4772
4773 /* At this point there is no need to override the current pid
4774 with the pid of the opener, but that could change if we someday
4775 use an existing handle (rather than opening one on the fly) */
4776 /* pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4777 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));*/
Steve French50c2f752007-07-13 00:33:32 +00004778
Linus Torvalds1da177e2005-04-16 15:20:36 -07004779 params = 6;
4780 pSMB->MaxSetupCount = 0;
4781 pSMB->Reserved = 0;
4782 pSMB->Flags = 0;
4783 pSMB->Timeout = 0;
4784 pSMB->Reserved2 = 0;
4785 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4786 offset = param_offset + params;
4787
Steve French50c2f752007-07-13 00:33:32 +00004788 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004789
Steve French26f57362007-08-30 22:09:15 +00004790 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004791 pSMB->MaxParameterCount = cpu_to_le16(2);
4792 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB PDU from sess */
4793 pSMB->SetupCount = 1;
4794 pSMB->Reserved3 = 0;
4795 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4796 byte_count = 3 /* pad */ + params + count;
4797 pSMB->DataCount = cpu_to_le16(count);
4798 pSMB->ParameterCount = cpu_to_le16(params);
4799 pSMB->TotalDataCount = pSMB->DataCount;
4800 pSMB->TotalParameterCount = pSMB->ParameterCount;
4801 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4802 pSMB->DataOffset = cpu_to_le16(offset);
4803 pSMB->Fid = fid;
4804 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4805 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4806 else
4807 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
4808 pSMB->Reserved4 = 0;
4809 pSMB->hdr.smb_buf_length += byte_count;
4810 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00004811 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00004812 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004813 if (rc)
Steve French50c2f752007-07-13 00:33:32 +00004814 cFYI(1, ("Send error in Set Time (SetFileInfo) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004815
Steve French50c2f752007-07-13 00:33:32 +00004816 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004817 since file handle passed in no longer valid */
4818
4819 return rc;
4820}
4821
4822
4823int
4824CIFSSMBSetTimes(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00004825 const FILE_BASIC_INFO *data,
Steve French737b7582005-04-28 22:41:06 -07004826 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004827{
4828 TRANSACTION2_SPI_REQ *pSMB = NULL;
4829 TRANSACTION2_SPI_RSP *pSMBr = NULL;
4830 int name_len;
4831 int rc = 0;
4832 int bytes_returned = 0;
4833 char *data_offset;
4834 __u16 params, param_offset, offset, byte_count, count;
4835
4836 cFYI(1, ("In SetTimes"));
4837
4838SetTimesRetry:
4839 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4840 (void **) &pSMBr);
4841 if (rc)
4842 return rc;
4843
4844 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4845 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004846 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004847 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004848 name_len++; /* trailing null */
4849 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004850 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004851 name_len = strnlen(fileName, PATH_MAX);
4852 name_len++; /* trailing null */
4853 strncpy(pSMB->FileName, fileName, name_len);
4854 }
4855
4856 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004857 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004858 pSMB->MaxParameterCount = cpu_to_le16(2);
4859 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
4860 pSMB->MaxSetupCount = 0;
4861 pSMB->Reserved = 0;
4862 pSMB->Flags = 0;
4863 pSMB->Timeout = 0;
4864 pSMB->Reserved2 = 0;
4865 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004866 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004867 offset = param_offset + params;
4868 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
4869 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4870 pSMB->DataOffset = cpu_to_le16(offset);
4871 pSMB->SetupCount = 1;
4872 pSMB->Reserved3 = 0;
4873 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4874 byte_count = 3 /* pad */ + params + count;
4875
4876 pSMB->DataCount = cpu_to_le16(count);
4877 pSMB->ParameterCount = cpu_to_le16(params);
4878 pSMB->TotalDataCount = pSMB->DataCount;
4879 pSMB->TotalParameterCount = pSMB->ParameterCount;
4880 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4881 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4882 else
4883 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
4884 pSMB->Reserved4 = 0;
4885 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00004886 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004887 pSMB->ByteCount = cpu_to_le16(byte_count);
4888 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4889 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004890 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004891 cFYI(1, ("SetPathInfo (times) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004892
4893 cifs_buf_release(pSMB);
4894
4895 if (rc == -EAGAIN)
4896 goto SetTimesRetry;
4897
4898 return rc;
4899}
4900
4901/* Can not be used to set time stamps yet (due to old DOS time format) */
4902/* Can be used to set attributes */
4903#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
4904 handling it anyway and NT4 was what we thought it would be needed for
4905 Do not delete it until we prove whether needed for Win9x though */
4906int
4907CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
4908 __u16 dos_attrs, const struct nls_table *nls_codepage)
4909{
4910 SETATTR_REQ *pSMB = NULL;
4911 SETATTR_RSP *pSMBr = NULL;
4912 int rc = 0;
4913 int bytes_returned;
4914 int name_len;
4915
4916 cFYI(1, ("In SetAttrLegacy"));
4917
4918SetAttrLgcyRetry:
4919 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
4920 (void **) &pSMBr);
4921 if (rc)
4922 return rc;
4923
4924 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4925 name_len =
Steve French50c2f752007-07-13 00:33:32 +00004926 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004927 PATH_MAX, nls_codepage);
4928 name_len++; /* trailing null */
4929 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004930 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004931 name_len = strnlen(fileName, PATH_MAX);
4932 name_len++; /* trailing null */
4933 strncpy(pSMB->fileName, fileName, name_len);
4934 }
4935 pSMB->attr = cpu_to_le16(dos_attrs);
4936 pSMB->BufferFormat = 0x04;
4937 pSMB->hdr.smb_buf_length += name_len + 1;
4938 pSMB->ByteCount = cpu_to_le16(name_len + 1);
4939 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4940 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004941 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004942 cFYI(1, ("Error in LegacySetAttr = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004943
4944 cifs_buf_release(pSMB);
4945
4946 if (rc == -EAGAIN)
4947 goto SetAttrLgcyRetry;
4948
4949 return rc;
4950}
4951#endif /* temporarily unneeded SetAttr legacy function */
4952
4953int
4954CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00004955 char *fileName, __u64 mode, __u64 uid, __u64 gid,
4956 dev_t device, const struct nls_table *nls_codepage,
Steve French737b7582005-04-28 22:41:06 -07004957 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004958{
4959 TRANSACTION2_SPI_REQ *pSMB = NULL;
4960 TRANSACTION2_SPI_RSP *pSMBr = NULL;
4961 int name_len;
4962 int rc = 0;
4963 int bytes_returned = 0;
4964 FILE_UNIX_BASIC_INFO *data_offset;
4965 __u16 params, param_offset, offset, count, byte_count;
4966
4967 cFYI(1, ("In SetUID/GID/Mode"));
4968setPermsRetry:
4969 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4970 (void **) &pSMBr);
4971 if (rc)
4972 return rc;
4973
4974 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4975 name_len =
Steve French50c2f752007-07-13 00:33:32 +00004976 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004977 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004978 name_len++; /* trailing null */
4979 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004980 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004981 name_len = strnlen(fileName, PATH_MAX);
4982 name_len++; /* trailing null */
4983 strncpy(pSMB->FileName, fileName, name_len);
4984 }
4985
4986 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004987 count = sizeof(FILE_UNIX_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004988 pSMB->MaxParameterCount = cpu_to_le16(2);
4989 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
4990 pSMB->MaxSetupCount = 0;
4991 pSMB->Reserved = 0;
4992 pSMB->Flags = 0;
4993 pSMB->Timeout = 0;
4994 pSMB->Reserved2 = 0;
4995 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004996 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004997 offset = param_offset + params;
4998 data_offset =
4999 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5000 offset);
5001 memset(data_offset, 0, count);
5002 pSMB->DataOffset = cpu_to_le16(offset);
5003 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5004 pSMB->SetupCount = 1;
5005 pSMB->Reserved3 = 0;
5006 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5007 byte_count = 3 /* pad */ + params + count;
5008 pSMB->ParameterCount = cpu_to_le16(params);
5009 pSMB->DataCount = cpu_to_le16(count);
5010 pSMB->TotalParameterCount = pSMB->ParameterCount;
5011 pSMB->TotalDataCount = pSMB->DataCount;
5012 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5013 pSMB->Reserved4 = 0;
5014 pSMB->hdr.smb_buf_length += byte_count;
Steve Frenchc7af1852007-03-01 04:11:22 +00005015 /* Samba server ignores set of file size to zero due to bugs in some
5016 older clients, but we should be precise - we use SetFileSize to
5017 set file size and do not want to truncate file size to zero
5018 accidently as happened on one Samba server beta by putting
Steve French50c2f752007-07-13 00:33:32 +00005019 zero instead of -1 here */
Steve Frenchc7af1852007-03-01 04:11:22 +00005020 data_offset->EndOfFile = NO_CHANGE_64;
5021 data_offset->NumOfBytes = NO_CHANGE_64;
5022 data_offset->LastStatusChange = NO_CHANGE_64;
5023 data_offset->LastAccessTime = NO_CHANGE_64;
5024 data_offset->LastModificationTime = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005025 data_offset->Uid = cpu_to_le64(uid);
5026 data_offset->Gid = cpu_to_le64(gid);
5027 /* better to leave device as zero when it is */
5028 data_offset->DevMajor = cpu_to_le64(MAJOR(device));
5029 data_offset->DevMinor = cpu_to_le64(MINOR(device));
5030 data_offset->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00005031
Steve French790fe572007-07-07 19:25:05 +00005032 if (S_ISREG(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005033 data_offset->Type = cpu_to_le32(UNIX_FILE);
Steve French790fe572007-07-07 19:25:05 +00005034 else if (S_ISDIR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005035 data_offset->Type = cpu_to_le32(UNIX_DIR);
Steve French790fe572007-07-07 19:25:05 +00005036 else if (S_ISLNK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005037 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
Steve French790fe572007-07-07 19:25:05 +00005038 else if (S_ISCHR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005039 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
Steve French790fe572007-07-07 19:25:05 +00005040 else if (S_ISBLK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005041 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
Steve French790fe572007-07-07 19:25:05 +00005042 else if (S_ISFIFO(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005043 data_offset->Type = cpu_to_le32(UNIX_FIFO);
Steve French790fe572007-07-07 19:25:05 +00005044 else if (S_ISSOCK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005045 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5046
5047
5048 pSMB->ByteCount = cpu_to_le16(byte_count);
5049 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5050 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005051 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005052 cFYI(1, ("SetPathInfo (perms) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005053
5054 if (pSMB)
5055 cifs_buf_release(pSMB);
5056 if (rc == -EAGAIN)
5057 goto setPermsRetry;
5058 return rc;
5059}
5060
Steve French50c2f752007-07-13 00:33:32 +00005061int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005062 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005063 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005064 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005065{
5066 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005067 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5068 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005069 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005070 int bytes_returned;
5071
Steve French50c2f752007-07-13 00:33:32 +00005072 cFYI(1, ("In CIFSSMBNotify for file handle %d", (int)netfid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005073 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005074 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005075 if (rc)
5076 return rc;
5077
5078 pSMB->TotalParameterCount = 0 ;
5079 pSMB->TotalDataCount = 0;
5080 pSMB->MaxParameterCount = cpu_to_le32(2);
5081 /* BB find exact data count max from sess structure BB */
5082 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005083/* BB VERIFY verify which is correct for above BB */
5084 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5085 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5086
Linus Torvalds1da177e2005-04-16 15:20:36 -07005087 pSMB->MaxSetupCount = 4;
5088 pSMB->Reserved = 0;
5089 pSMB->ParameterOffset = 0;
5090 pSMB->DataCount = 0;
5091 pSMB->DataOffset = 0;
5092 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5093 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5094 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005095 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005096 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5097 pSMB->Reserved2 = 0;
5098 pSMB->CompletionFilter = cpu_to_le32(filter);
5099 pSMB->Fid = netfid; /* file handle always le */
5100 pSMB->ByteCount = 0;
5101
5102 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005103 (struct smb_hdr *)pSMBr, &bytes_returned,
5104 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005105 if (rc) {
5106 cFYI(1, ("Error in Notify = %d", rc));
Steve Frenchff5dbd92005-08-24 17:10:36 -07005107 } else {
5108 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005109 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005110 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005111 sizeof(struct dir_notify_req),
5112 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005113 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005114 dnotify_req->Pid = pSMB->hdr.Pid;
5115 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5116 dnotify_req->Mid = pSMB->hdr.Mid;
5117 dnotify_req->Tid = pSMB->hdr.Tid;
5118 dnotify_req->Uid = pSMB->hdr.Uid;
5119 dnotify_req->netfid = netfid;
5120 dnotify_req->pfile = pfile;
5121 dnotify_req->filter = filter;
5122 dnotify_req->multishot = multishot;
5123 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005124 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005125 &GlobalDnotifyReqList);
5126 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005127 } else
Steve French47c786e2005-10-11 20:03:18 -07005128 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005129 }
5130 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005131 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005132}
5133#ifdef CONFIG_CIFS_XATTR
5134ssize_t
5135CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
5136 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00005137 char *EAData, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005138 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005139{
5140 /* BB assumes one setup word */
5141 TRANSACTION2_QPI_REQ *pSMB = NULL;
5142 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5143 int rc = 0;
5144 int bytes_returned;
5145 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005146 struct fea *temp_fea;
5147 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005148 __u16 params, byte_count;
5149
5150 cFYI(1, ("In Query All EAs path %s", searchName));
5151QAllEAsRetry:
5152 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5153 (void **) &pSMBr);
5154 if (rc)
5155 return rc;
5156
5157 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5158 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005159 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005160 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005161 name_len++; /* trailing null */
5162 name_len *= 2;
5163 } else { /* BB improve the check for buffer overruns BB */
5164 name_len = strnlen(searchName, PATH_MAX);
5165 name_len++; /* trailing null */
5166 strncpy(pSMB->FileName, searchName, name_len);
5167 }
5168
Steve French50c2f752007-07-13 00:33:32 +00005169 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005170 pSMB->TotalDataCount = 0;
5171 pSMB->MaxParameterCount = cpu_to_le16(2);
5172 pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
5173 pSMB->MaxSetupCount = 0;
5174 pSMB->Reserved = 0;
5175 pSMB->Flags = 0;
5176 pSMB->Timeout = 0;
5177 pSMB->Reserved2 = 0;
5178 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005179 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005180 pSMB->DataCount = 0;
5181 pSMB->DataOffset = 0;
5182 pSMB->SetupCount = 1;
5183 pSMB->Reserved3 = 0;
5184 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5185 byte_count = params + 1 /* pad */ ;
5186 pSMB->TotalParameterCount = cpu_to_le16(params);
5187 pSMB->ParameterCount = pSMB->TotalParameterCount;
5188 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5189 pSMB->Reserved4 = 0;
5190 pSMB->hdr.smb_buf_length += byte_count;
5191 pSMB->ByteCount = cpu_to_le16(byte_count);
5192
5193 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5194 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5195 if (rc) {
5196 cFYI(1, ("Send error in QueryAllEAs = %d", rc));
5197 } else { /* decode response */
5198 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5199
5200 /* BB also check enough total bytes returned */
5201 /* BB we need to improve the validity checking
5202 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005203 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005204 rc = -EIO; /* bad smb */
5205 /* else if (pFindData){
5206 memcpy((char *) pFindData,
5207 (char *) &pSMBr->hdr.Protocol +
5208 data_offset, kl);
5209 }*/ else {
5210 /* check that length of list is not more than bcc */
5211 /* check that each entry does not go beyond length
5212 of list */
5213 /* check that each element of each entry does not
5214 go beyond end of list */
5215 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005216 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005217 rc = 0;
5218 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005219 /* BB check if start of smb + data_offset > &bcc+ bcc */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005220 ea_response_data = (struct fealist *)
5221 (((char *) &pSMBr->hdr.Protocol) +
5222 data_offset);
5223 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005224 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005225 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005226 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005227 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005228 } else {
5229 /* account for ea list len */
5230 name_len -= 4;
5231 temp_fea = ea_response_data->list;
5232 temp_ptr = (char *)temp_fea;
Steve French50c2f752007-07-13 00:33:32 +00005233 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005234 __u16 value_len;
5235 name_len -= 4;
5236 temp_ptr += 4;
5237 rc += temp_fea->name_len;
5238 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005239 rc = rc + 5 + 1;
5240 if (rc < (int)buf_size) {
Steve French50c2f752007-07-13 00:33:32 +00005241 memcpy(EAData, "user.", 5);
5242 EAData += 5;
5243 memcpy(EAData, temp_ptr,
5244 temp_fea->name_len);
5245 EAData += temp_fea->name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005246 /* null terminate name */
5247 *EAData = 0;
5248 EAData = EAData + 1;
Steve French790fe572007-07-07 19:25:05 +00005249 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005250 /* skip copy - calc size only */
5251 } else {
5252 /* stop before overrun buffer */
5253 rc = -ERANGE;
5254 break;
5255 }
5256 name_len -= temp_fea->name_len;
5257 temp_ptr += temp_fea->name_len;
5258 /* account for trailing null */
5259 name_len--;
5260 temp_ptr++;
Steve French50c2f752007-07-13 00:33:32 +00005261 value_len =
5262 le16_to_cpu(temp_fea->value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005263 name_len -= value_len;
5264 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005265 /* BB check that temp_ptr is still
5266 within the SMB BB*/
5267
5268 /* no trailing null to account for
5269 in value len */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005270 /* go on to next EA */
5271 temp_fea = (struct fea *)temp_ptr;
5272 }
5273 }
5274 }
5275 }
5276 if (pSMB)
5277 cifs_buf_release(pSMB);
5278 if (rc == -EAGAIN)
5279 goto QAllEAsRetry;
5280
5281 return (ssize_t)rc;
5282}
5283
Steve French50c2f752007-07-13 00:33:32 +00005284ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon,
5285 const unsigned char *searchName, const unsigned char *ea_name,
5286 unsigned char *ea_value, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005287 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005288{
5289 TRANSACTION2_QPI_REQ *pSMB = NULL;
5290 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5291 int rc = 0;
5292 int bytes_returned;
5293 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005294 struct fea *temp_fea;
5295 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005296 __u16 params, byte_count;
5297
5298 cFYI(1, ("In Query EA path %s", searchName));
5299QEARetry:
5300 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5301 (void **) &pSMBr);
5302 if (rc)
5303 return rc;
5304
5305 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5306 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005307 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005308 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005309 name_len++; /* trailing null */
5310 name_len *= 2;
5311 } else { /* BB improve the check for buffer overruns BB */
5312 name_len = strnlen(searchName, PATH_MAX);
5313 name_len++; /* trailing null */
5314 strncpy(pSMB->FileName, searchName, name_len);
5315 }
5316
Steve French50c2f752007-07-13 00:33:32 +00005317 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005318 pSMB->TotalDataCount = 0;
5319 pSMB->MaxParameterCount = cpu_to_le16(2);
5320 pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
5321 pSMB->MaxSetupCount = 0;
5322 pSMB->Reserved = 0;
5323 pSMB->Flags = 0;
5324 pSMB->Timeout = 0;
5325 pSMB->Reserved2 = 0;
5326 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005327 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005328 pSMB->DataCount = 0;
5329 pSMB->DataOffset = 0;
5330 pSMB->SetupCount = 1;
5331 pSMB->Reserved3 = 0;
5332 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5333 byte_count = params + 1 /* pad */ ;
5334 pSMB->TotalParameterCount = cpu_to_le16(params);
5335 pSMB->ParameterCount = pSMB->TotalParameterCount;
5336 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5337 pSMB->Reserved4 = 0;
5338 pSMB->hdr.smb_buf_length += byte_count;
5339 pSMB->ByteCount = cpu_to_le16(byte_count);
5340
5341 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5342 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5343 if (rc) {
5344 cFYI(1, ("Send error in Query EA = %d", rc));
5345 } else { /* decode response */
5346 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5347
5348 /* BB also check enough total bytes returned */
5349 /* BB we need to improve the validity checking
5350 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005351 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005352 rc = -EIO; /* bad smb */
5353 /* else if (pFindData){
5354 memcpy((char *) pFindData,
5355 (char *) &pSMBr->hdr.Protocol +
5356 data_offset, kl);
5357 }*/ else {
5358 /* check that length of list is not more than bcc */
5359 /* check that each entry does not go beyond length
5360 of list */
5361 /* check that each element of each entry does not
5362 go beyond end of list */
5363 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005364 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005365 rc = -ENODATA;
5366 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005367 /* BB check if start of smb + data_offset > &bcc+ bcc*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07005368 ea_response_data = (struct fealist *)
5369 (((char *) &pSMBr->hdr.Protocol) +
5370 data_offset);
5371 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005372 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005373 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005374 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005375 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005376 } else {
5377 /* account for ea list len */
5378 name_len -= 4;
5379 temp_fea = ea_response_data->list;
5380 temp_ptr = (char *)temp_fea;
5381 /* loop through checking if we have a matching
5382 name and then return the associated value */
Steve French50c2f752007-07-13 00:33:32 +00005383 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005384 __u16 value_len;
5385 name_len -= 4;
5386 temp_ptr += 4;
Steve French50c2f752007-07-13 00:33:32 +00005387 value_len =
5388 le16_to_cpu(temp_fea->value_len);
5389 /* BB validate that value_len falls within SMB,
5390 even though maximum for name_len is 255 */
Steve French790fe572007-07-07 19:25:05 +00005391 if (memcmp(temp_fea->name, ea_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005392 temp_fea->name_len) == 0) {
5393 /* found a match */
5394 rc = value_len;
5395 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005396 if (rc <= (int)buf_size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005397 memcpy(ea_value,
5398 temp_fea->name+temp_fea->name_len+1,
5399 rc);
Steve French50c2f752007-07-13 00:33:32 +00005400 /* ea values, unlike ea
5401 names, are not null
5402 terminated */
Steve French790fe572007-07-07 19:25:05 +00005403 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005404 /* skip copy - calc size only */
5405 } else {
Steve French50c2f752007-07-13 00:33:32 +00005406 /* stop before overrun buffer */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005407 rc = -ERANGE;
5408 }
5409 break;
5410 }
5411 name_len -= temp_fea->name_len;
5412 temp_ptr += temp_fea->name_len;
5413 /* account for trailing null */
5414 name_len--;
5415 temp_ptr++;
5416 name_len -= value_len;
5417 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005418 /* No trailing null to account for in
5419 value_len. Go on to next EA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005420 temp_fea = (struct fea *)temp_ptr;
5421 }
Steve French50c2f752007-07-13 00:33:32 +00005422 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005423 }
5424 }
5425 if (pSMB)
5426 cifs_buf_release(pSMB);
5427 if (rc == -EAGAIN)
5428 goto QEARetry;
5429
5430 return (ssize_t)rc;
5431}
5432
5433int
5434CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005435 const char *ea_name, const void *ea_value,
5436 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5437 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005438{
5439 struct smb_com_transaction2_spi_req *pSMB = NULL;
5440 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5441 struct fealist *parm_data;
5442 int name_len;
5443 int rc = 0;
5444 int bytes_returned = 0;
5445 __u16 params, param_offset, byte_count, offset, count;
5446
5447 cFYI(1, ("In SetEA"));
5448SetEARetry:
5449 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5450 (void **) &pSMBr);
5451 if (rc)
5452 return rc;
5453
5454 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5455 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005456 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005457 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005458 name_len++; /* trailing null */
5459 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005460 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005461 name_len = strnlen(fileName, PATH_MAX);
5462 name_len++; /* trailing null */
5463 strncpy(pSMB->FileName, fileName, name_len);
5464 }
5465
5466 params = 6 + name_len;
5467
5468 /* done calculating parms using name_len of file name,
5469 now use name_len to calculate length of ea name
5470 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005471 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005472 name_len = 0;
5473 else
Steve French50c2f752007-07-13 00:33:32 +00005474 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005475
Steve Frenchdae5dbd2007-12-30 23:49:57 +00005476 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005477 pSMB->MaxParameterCount = cpu_to_le16(2);
5478 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB size from sess */
5479 pSMB->MaxSetupCount = 0;
5480 pSMB->Reserved = 0;
5481 pSMB->Flags = 0;
5482 pSMB->Timeout = 0;
5483 pSMB->Reserved2 = 0;
5484 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005485 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005486 offset = param_offset + params;
5487 pSMB->InformationLevel =
5488 cpu_to_le16(SMB_SET_FILE_EA);
5489
5490 parm_data =
5491 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5492 offset);
5493 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5494 pSMB->DataOffset = cpu_to_le16(offset);
5495 pSMB->SetupCount = 1;
5496 pSMB->Reserved3 = 0;
5497 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5498 byte_count = 3 /* pad */ + params + count;
5499 pSMB->DataCount = cpu_to_le16(count);
5500 parm_data->list_len = cpu_to_le32(count);
5501 parm_data->list[0].EA_flags = 0;
5502 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005503 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005504 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005505 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005506 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005507 parm_data->list[0].name[name_len] = 0;
5508 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5509 /* caller ensures that ea_value_len is less than 64K but
5510 we need to ensure that it fits within the smb */
5511
Steve French50c2f752007-07-13 00:33:32 +00005512 /*BB add length check to see if it would fit in
5513 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005514 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5515 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005516 memcpy(parm_data->list[0].name+name_len+1,
5517 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005518
5519 pSMB->TotalDataCount = pSMB->DataCount;
5520 pSMB->ParameterCount = cpu_to_le16(params);
5521 pSMB->TotalParameterCount = pSMB->ParameterCount;
5522 pSMB->Reserved4 = 0;
5523 pSMB->hdr.smb_buf_length += byte_count;
5524 pSMB->ByteCount = cpu_to_le16(byte_count);
5525 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5526 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005527 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005528 cFYI(1, ("SetPathInfo (EA) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005529
5530 cifs_buf_release(pSMB);
5531
5532 if (rc == -EAGAIN)
5533 goto SetEARetry;
5534
5535 return rc;
5536}
5537
5538#endif