blob: 4b69d1cea65ee6f8b606b6aeb46ad0f99283073f [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);
605 cFYI(0, ("Max buf = %d", ses->server->maxBuf));
606 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 Frenchcbac3cb2007-04-25 11:46:06 +00001111#ifdef CONFIG_CIFS_DEBUG2
Steve French790fe572007-07-07 19:25:05 +00001112 cFYI(1, ("unknown type"));
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001113#endif
1114 } else {
Steve French790fe572007-07-07 19:25:05 +00001115 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001116 + sizeof(FILE_UNIX_BASIC_INFO)) {
Steve French50c2f752007-07-13 00:33:32 +00001117 cERROR(1, ("Open response data too small"));
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001118 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001119 goto psx_create_err;
1120 }
Steve French50c2f752007-07-13 00:33:32 +00001121 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001122 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001123 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001124 }
Steve French2dd29d32007-04-23 22:07:35 +00001125
1126psx_create_err:
1127 cifs_buf_release(pSMB);
1128
1129 cifs_stats_inc(&tcon->num_mkdirs);
1130
1131 if (rc == -EAGAIN)
1132 goto PsxCreat;
1133
Steve French50c2f752007-07-13 00:33:32 +00001134 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001135}
1136
Steve Frencha9d02ad2005-08-24 23:06:05 -07001137static __u16 convert_disposition(int disposition)
1138{
1139 __u16 ofun = 0;
1140
1141 switch (disposition) {
1142 case FILE_SUPERSEDE:
1143 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1144 break;
1145 case FILE_OPEN:
1146 ofun = SMBOPEN_OAPPEND;
1147 break;
1148 case FILE_CREATE:
1149 ofun = SMBOPEN_OCREATE;
1150 break;
1151 case FILE_OPEN_IF:
1152 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1153 break;
1154 case FILE_OVERWRITE:
1155 ofun = SMBOPEN_OTRUNC;
1156 break;
1157 case FILE_OVERWRITE_IF:
1158 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1159 break;
1160 default:
Steve French790fe572007-07-07 19:25:05 +00001161 cFYI(1, ("unknown disposition %d", disposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001162 ofun = SMBOPEN_OAPPEND; /* regular open */
1163 }
1164 return ofun;
1165}
1166
1167int
1168SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1169 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001170 const int access_flags, const int create_options, __u16 *netfid,
1171 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001172 const struct nls_table *nls_codepage, int remap)
1173{
1174 int rc = -EACCES;
1175 OPENX_REQ *pSMB = NULL;
1176 OPENX_RSP *pSMBr = NULL;
1177 int bytes_returned;
1178 int name_len;
1179 __u16 count;
1180
1181OldOpenRetry:
1182 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1183 (void **) &pSMBr);
1184 if (rc)
1185 return rc;
1186
1187 pSMB->AndXCommand = 0xFF; /* none */
1188
1189 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1190 count = 1; /* account for one byte pad to word boundary */
1191 name_len =
1192 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1193 fileName, PATH_MAX, nls_codepage, remap);
1194 name_len++; /* trailing null */
1195 name_len *= 2;
1196 } else { /* BB improve check for buffer overruns BB */
1197 count = 0; /* no pad */
1198 name_len = strnlen(fileName, PATH_MAX);
1199 name_len++; /* trailing null */
1200 strncpy(pSMB->fileName, fileName, name_len);
1201 }
1202 if (*pOplock & REQ_OPLOCK)
1203 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001204 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001205 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001206
Steve Frencha9d02ad2005-08-24 23:06:05 -07001207 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
1208 /* BB fixme add conversion for access_flags to bits 0 - 2 of mode */
1209 /* 0 = read
1210 1 = write
1211 2 = rw
1212 3 = execute
Steve French50c2f752007-07-13 00:33:32 +00001213 */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001214 pSMB->Mode = cpu_to_le16(2);
1215 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1216 /* set file as system file if special file such
1217 as fifo and server expecting SFU style and
1218 no Unix extensions */
1219
Steve French790fe572007-07-07 19:25:05 +00001220 if (create_options & CREATE_OPTION_SPECIAL)
1221 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001222 else /* BB FIXME BB */
1223 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001224
1225 /* if ((omode & S_IWUGO) == 0)
1226 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/
1227 /* Above line causes problems due to vfs splitting create into two
1228 pieces - need to set mode after file created not while it is
1229 being created */
1230
1231 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001232/* pSMB->CreateOptions = cpu_to_le32(create_options &
1233 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001234 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001235
1236 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001237 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001238 count += name_len;
1239 pSMB->hdr.smb_buf_length += count;
1240
1241 pSMB->ByteCount = cpu_to_le16(count);
1242 /* long_op set to 1 to allow for oplock break timeouts */
1243 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001244 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001245 cifs_stats_inc(&tcon->num_opens);
1246 if (rc) {
1247 cFYI(1, ("Error in Open = %d", rc));
1248 } else {
1249 /* BB verify if wct == 15 */
1250
1251/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field BB */
1252
1253 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1254 /* Let caller know file was created so we can set the mode. */
1255 /* Do we care about the CreateAction in any other cases? */
1256 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001257/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001258 *pOplock |= CIFS_CREATE_ACTION; */
1259 /* BB FIXME END */
1260
Steve French790fe572007-07-07 19:25:05 +00001261 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001262 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1263 pfile_info->LastAccessTime = 0; /* BB fixme */
1264 pfile_info->LastWriteTime = 0; /* BB fixme */
1265 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001266 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001267 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001268 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001269 pfile_info->AllocationSize =
1270 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1271 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001272 pfile_info->NumberOfLinks = cpu_to_le32(1);
1273 }
1274 }
1275
1276 cifs_buf_release(pSMB);
1277 if (rc == -EAGAIN)
1278 goto OldOpenRetry;
1279 return rc;
1280}
1281
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282int
1283CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1284 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001285 const int access_flags, const int create_options, __u16 *netfid,
1286 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001287 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288{
1289 int rc = -EACCES;
1290 OPEN_REQ *pSMB = NULL;
1291 OPEN_RSP *pSMBr = NULL;
1292 int bytes_returned;
1293 int name_len;
1294 __u16 count;
1295
1296openRetry:
1297 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1298 (void **) &pSMBr);
1299 if (rc)
1300 return rc;
1301
1302 pSMB->AndXCommand = 0xFF; /* none */
1303
1304 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1305 count = 1; /* account for one byte pad to word boundary */
1306 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001307 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001308 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 name_len++; /* trailing null */
1310 name_len *= 2;
1311 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001312 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 count = 0; /* no pad */
1314 name_len = strnlen(fileName, PATH_MAX);
1315 name_len++; /* trailing null */
1316 pSMB->NameLength = cpu_to_le16(name_len);
1317 strncpy(pSMB->fileName, fileName, name_len);
1318 }
1319 if (*pOplock & REQ_OPLOCK)
1320 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001321 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1324 pSMB->AllocationSize = 0;
Steve Frencheda3c0292005-07-21 15:20:28 -07001325 /* set file as system file if special file such
1326 as fifo and server expecting SFU style and
1327 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001328 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c0292005-07-21 15:20:28 -07001329 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1330 else
1331 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 /* XP does not handle ATTR_POSIX_SEMANTICS */
1333 /* but it helps speed up case sensitive checks for other
1334 servers such as Samba */
1335 if (tcon->ses->capabilities & CAP_UNIX)
1336 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1337
1338 /* if ((omode & S_IWUGO) == 0)
1339 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);*/
1340 /* Above line causes problems due to vfs splitting create into two
1341 pieces - need to set mode after file created not while it is
1342 being created */
1343 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1344 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c0292005-07-21 15:20:28 -07001345 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001346 /* BB Expirement with various impersonation levels and verify */
1347 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 pSMB->SecurityFlags =
1349 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1350
1351 count += name_len;
1352 pSMB->hdr.smb_buf_length += count;
1353
1354 pSMB->ByteCount = cpu_to_le16(count);
1355 /* long_op set to 1 to allow for oplock break timeouts */
1356 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001357 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha4544342005-08-24 13:59:35 -07001358 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 if (rc) {
1360 cFYI(1, ("Error in Open = %d", rc));
1361 } else {
Steve French09d1db52005-04-28 22:41:08 -07001362 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1364 /* Let caller know file was created so we can set the mode. */
1365 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001366 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001367 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001368 if (pfile_info) {
Steve French50c2f752007-07-13 00:33:32 +00001369 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 36 /* CreationTime to Attributes */);
1371 /* the file_info buf is endian converted by caller */
1372 pfile_info->AllocationSize = pSMBr->AllocationSize;
1373 pfile_info->EndOfFile = pSMBr->EndOfFile;
1374 pfile_info->NumberOfLinks = cpu_to_le32(1);
1375 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001377
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 cifs_buf_release(pSMB);
1379 if (rc == -EAGAIN)
1380 goto openRetry;
1381 return rc;
1382}
1383
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384int
Steve French50c2f752007-07-13 00:33:32 +00001385CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1386 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1387 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388{
1389 int rc = -EACCES;
1390 READ_REQ *pSMB = NULL;
1391 READ_RSP *pSMBr = NULL;
1392 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001393 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001394 int resp_buf_type = 0;
1395 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396
Steve French790fe572007-07-07 19:25:05 +00001397 cFYI(1, ("Reading %d bytes on fid %d", count, netfid));
1398 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001399 wct = 12;
1400 else
1401 wct = 10; /* old style read */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402
1403 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001404 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 if (rc)
1406 return rc;
1407
1408 /* tcon and ses pointer are checked in smb_init */
1409 if (tcon->ses->server == NULL)
1410 return -ECONNABORTED;
1411
Steve Frenchec637e32005-12-12 20:53:18 -08001412 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 pSMB->Fid = netfid;
1414 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001415 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001416 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve French790fe572007-07-07 19:25:05 +00001417 else if ((lseek >> 32) > 0) /* can not handle this big offset for old */
Steve Frenchec637e32005-12-12 20:53:18 -08001418 return -EIO;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001419
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 pSMB->Remaining = 0;
1421 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1422 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001423 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001424 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1425 else {
1426 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001427 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001428 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001429 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001430 }
Steve Frenchec637e32005-12-12 20:53:18 -08001431
1432 iov[0].iov_base = (char *)pSMB;
1433 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001434 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Steve French133672e2007-11-13 22:41:37 +00001435 &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001436 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001437 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 if (rc) {
1439 cERROR(1, ("Send error in read = %d", rc));
1440 } else {
1441 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1442 data_length = data_length << 16;
1443 data_length += le16_to_cpu(pSMBr->DataLength);
1444 *nbytes = data_length;
1445
1446 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001447 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 || (data_length > count)) {
Steve French50c2f752007-07-13 00:33:32 +00001449 cFYI(1, ("bad length %d for count %d",
1450 data_length, count));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 rc = -EIO;
1452 *nbytes = 0;
1453 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001454 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001455 le16_to_cpu(pSMBr->DataOffset);
1456/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Steve French50c2f752007-07-13 00:33:32 +00001457 cERROR(1,("Faulting on read rc = %d",rc));
1458 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001459 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001460 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001461 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 }
1463 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464
Steve French4b8f9302006-02-26 16:41:18 +00001465/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001466 if (*buf) {
1467 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001468 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001469 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001470 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001471 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001472 /* return buffer to caller to free */
1473 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001474 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001475 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001476 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001477 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001478 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001479
1480 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 since file handle passed in no longer valid */
1482 return rc;
1483}
1484
Steve Frenchec637e32005-12-12 20:53:18 -08001485
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486int
1487CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1488 const int netfid, const unsigned int count,
1489 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001490 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491{
1492 int rc = -EACCES;
1493 WRITE_REQ *pSMB = NULL;
1494 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001495 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 __u32 bytes_sent;
1497 __u16 byte_count;
1498
1499 /* cFYI(1,("write at %lld %d bytes",offset,count));*/
Steve French790fe572007-07-07 19:25:05 +00001500 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001501 return -ECONNABORTED;
1502
Steve French790fe572007-07-07 19:25:05 +00001503 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001504 wct = 14;
1505 else
1506 wct = 12;
1507
1508 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 (void **) &pSMBr);
1510 if (rc)
1511 return rc;
1512 /* tcon and ses pointer are checked in smb_init */
1513 if (tcon->ses->server == NULL)
1514 return -ECONNABORTED;
1515
1516 pSMB->AndXCommand = 0xFF; /* none */
1517 pSMB->Fid = netfid;
1518 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001519 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001520 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French790fe572007-07-07 19:25:05 +00001521 else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
Steve French1c955182005-08-30 20:58:07 -07001522 return -EIO;
Steve French50c2f752007-07-13 00:33:32 +00001523
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 pSMB->Reserved = 0xFFFFFFFF;
1525 pSMB->WriteMode = 0;
1526 pSMB->Remaining = 0;
1527
Steve French50c2f752007-07-13 00:33:32 +00001528 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 can send more if LARGE_WRITE_X capability returned by the server and if
1530 our buffer is big enough or if we convert to iovecs on socket writes
1531 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001532 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1534 } else {
1535 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1536 & ~0xFF;
1537 }
1538
1539 if (bytes_sent > count)
1540 bytes_sent = count;
1541 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001542 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001543 if (buf)
Steve French50c2f752007-07-13 00:33:32 +00001544 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001545 else if (ubuf) {
1546 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 cifs_buf_release(pSMB);
1548 return -EFAULT;
1549 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001550 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 /* No buffer */
1552 cifs_buf_release(pSMB);
1553 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001554 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001555 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001556 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001557 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001558 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001559
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1561 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001562 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001563
Steve French790fe572007-07-07 19:25:05 +00001564 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001565 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001566 else { /* old style write has byte count 4 bytes earlier
1567 so 4 bytes pad */
1568 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001569 (struct smb_com_writex_req *)pSMB;
1570 pSMBW->ByteCount = cpu_to_le16(byte_count);
1571 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572
1573 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1574 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001575 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 if (rc) {
1577 cFYI(1, ("Send error in write = %d", rc));
1578 *nbytes = 0;
1579 } else {
1580 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1581 *nbytes = (*nbytes) << 16;
1582 *nbytes += le16_to_cpu(pSMBr->Count);
1583 }
1584
1585 cifs_buf_release(pSMB);
1586
Steve French50c2f752007-07-13 00:33:32 +00001587 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 since file handle passed in no longer valid */
1589
1590 return rc;
1591}
1592
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001593int
1594CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001596 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1597 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598{
1599 int rc = -EACCES;
1600 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001601 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001602 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001603 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
Steve French790fe572007-07-07 19:25:05 +00001605 cFYI(1, ("write2 at %lld %d bytes", (long long)offset, count));
Steve Frenchff7feac2005-11-15 16:45:16 -08001606
Steve French790fe572007-07-07 19:25:05 +00001607 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French8cc64c62005-10-03 13:49:43 -07001608 wct = 14;
1609 else
1610 wct = 12;
1611 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 if (rc)
1613 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 /* tcon and ses pointer are checked in smb_init */
1615 if (tcon->ses->server == NULL)
1616 return -ECONNABORTED;
1617
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001618 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 pSMB->Fid = netfid;
1620 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001621 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001622 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French790fe572007-07-07 19:25:05 +00001623 else if ((offset >> 32) > 0) /* can not handle big offset for old srv */
Steve French8cc64c62005-10-03 13:49:43 -07001624 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 pSMB->Reserved = 0xFFFFFFFF;
1626 pSMB->WriteMode = 0;
1627 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001628
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001630 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631
Steve French3e844692005-10-03 13:37:24 -07001632 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1633 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001634 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001635 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001636 pSMB->hdr.smb_buf_length += count+1;
1637 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001638 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1639 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001640 pSMB->ByteCount = cpu_to_le16(count + 1);
1641 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001642 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001643 (struct smb_com_writex_req *)pSMB;
1644 pSMBW->ByteCount = cpu_to_le16(count + 5);
1645 }
Steve French3e844692005-10-03 13:37:24 -07001646 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001647 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001648 iov[0].iov_len = smb_hdr_len + 4;
1649 else /* wct == 12 pad bigger by four bytes */
1650 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001651
Steve French3e844692005-10-03 13:37:24 -07001652
Steve Frenchec637e32005-12-12 20:53:18 -08001653 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001654 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001655 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656 if (rc) {
Steve French8cc64c62005-10-03 13:49:43 -07001657 cFYI(1, ("Send error Write2 = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658 *nbytes = 0;
Steve French790fe572007-07-07 19:25:05 +00001659 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001660 /* presumably this can not happen, but best to be safe */
1661 rc = -EIO;
1662 *nbytes = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001663 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001664 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001665 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1666 *nbytes = (*nbytes) << 16;
1667 *nbytes += le16_to_cpu(pSMBr->Count);
Steve French50c2f752007-07-13 00:33:32 +00001668 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669
Steve French4b8f9302006-02-26 16:41:18 +00001670/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001671 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001672 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001673 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001674 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675
Steve French50c2f752007-07-13 00:33:32 +00001676 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 since file handle passed in no longer valid */
1678
1679 return rc;
1680}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001681
1682
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683int
1684CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1685 const __u16 smb_file_id, const __u64 len,
1686 const __u64 offset, const __u32 numUnlock,
1687 const __u32 numLock, const __u8 lockType, const int waitFlag)
1688{
1689 int rc = 0;
1690 LOCK_REQ *pSMB = NULL;
1691 LOCK_RSP *pSMBr = NULL;
1692 int bytes_returned;
1693 int timeout = 0;
1694 __u16 count;
1695
Steve French133672e2007-11-13 22:41:37 +00001696 cFYI(1, ("CIFSSMBLock timeout %d numLock %d", waitFlag, numLock));
Steve French46810cb2005-04-28 22:41:09 -07001697 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1698
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 if (rc)
1700 return rc;
1701
Steve French46810cb2005-04-28 22:41:09 -07001702 pSMBr = (LOCK_RSP *)pSMB; /* BB removeme BB */
1703
Steve French790fe572007-07-07 19:25:05 +00001704 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001705 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706 pSMB->Timeout = 0;
1707 } else if (waitFlag == TRUE) {
Steve French133672e2007-11-13 22:41:37 +00001708 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1710 } else {
1711 pSMB->Timeout = 0;
1712 }
1713
1714 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1715 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1716 pSMB->LockType = lockType;
1717 pSMB->AndXCommand = 0xFF; /* none */
1718 pSMB->Fid = smb_file_id; /* netfid stays le */
1719
Steve French790fe572007-07-07 19:25:05 +00001720 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001721 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1722 /* BB where to store pid high? */
1723 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1724 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1725 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1726 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1727 count = sizeof(LOCKING_ANDX_RANGE);
1728 } else {
1729 /* oplock break */
1730 count = 0;
1731 }
1732 pSMB->hdr.smb_buf_length += count;
1733 pSMB->ByteCount = cpu_to_le16(count);
1734
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001735 if (waitFlag) {
1736 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1737 (struct smb_hdr *) pSMBr, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001738 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001739 } else {
Steve French133672e2007-11-13 22:41:37 +00001740 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1741 timeout);
1742 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001743 }
Steve Frencha4544342005-08-24 13:59:35 -07001744 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001745 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746 cFYI(1, ("Send error in Lock = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747
Steve French50c2f752007-07-13 00:33:32 +00001748 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 since file handle passed in no longer valid */
1750 return rc;
1751}
1752
1753int
Steve French08547b02006-02-28 22:39:25 +00001754CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1755 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001756 struct file_lock *pLockData, const __u16 lock_type,
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001757 const int waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001758{
1759 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1760 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001761 struct cifs_posix_lock *parm_data;
1762 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001763 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001764 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001765 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001766 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001767 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001768
1769 cFYI(1, ("Posix Lock"));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001770
Steve French790fe572007-07-07 19:25:05 +00001771 if (pLockData == NULL)
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001772 return EINVAL;
1773
Steve French08547b02006-02-28 22:39:25 +00001774 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1775
1776 if (rc)
1777 return rc;
1778
1779 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1780
Steve French50c2f752007-07-13 00:33:32 +00001781 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001782 pSMB->MaxSetupCount = 0;
1783 pSMB->Reserved = 0;
1784 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001785 pSMB->Reserved2 = 0;
1786 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1787 offset = param_offset + params;
1788
Steve French08547b02006-02-28 22:39:25 +00001789 count = sizeof(struct cifs_posix_lock);
1790 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001791 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001792 pSMB->SetupCount = 1;
1793 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001794 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001795 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1796 else
1797 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1798 byte_count = 3 /* pad */ + params + count;
1799 pSMB->DataCount = cpu_to_le16(count);
1800 pSMB->ParameterCount = cpu_to_le16(params);
1801 pSMB->TotalDataCount = pSMB->DataCount;
1802 pSMB->TotalParameterCount = pSMB->ParameterCount;
1803 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001804 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001805 (((char *) &pSMB->hdr.Protocol) + offset);
1806
1807 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001808 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001809 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001810 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001811 pSMB->Timeout = cpu_to_le32(-1);
1812 } else
1813 pSMB->Timeout = 0;
1814
Steve French08547b02006-02-28 22:39:25 +00001815 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001816 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001817 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001818
1819 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001820 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001821 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1822 pSMB->Reserved4 = 0;
1823 pSMB->hdr.smb_buf_length += byte_count;
1824 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001825 if (waitFlag) {
1826 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1827 (struct smb_hdr *) pSMBr, &bytes_returned);
1828 } else {
Steve French133672e2007-11-13 22:41:37 +00001829 iov[0].iov_base = (char *)pSMB;
1830 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1831 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1832 &resp_buf_type, timeout);
1833 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1834 not try to free it twice below on exit */
1835 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001836 }
1837
Steve French08547b02006-02-28 22:39:25 +00001838 if (rc) {
1839 cFYI(1, ("Send error in Posix Lock = %d", rc));
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001840 } else if (get_flag) {
1841 /* lock structure can be returned on get */
1842 __u16 data_offset;
1843 __u16 data_count;
1844 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001845
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001846 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1847 rc = -EIO; /* bad smb */
1848 goto plk_err_exit;
1849 }
Steve French790fe572007-07-07 19:25:05 +00001850 if (pLockData == NULL) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001851 rc = -EINVAL;
1852 goto plk_err_exit;
1853 }
1854 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1855 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001856 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001857 rc = -EIO;
1858 goto plk_err_exit;
1859 }
1860 parm_data = (struct cifs_posix_lock *)
1861 ((char *)&pSMBr->hdr.Protocol + data_offset);
Steve French790fe572007-07-07 19:25:05 +00001862 if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001863 pLockData->fl_type = F_UNLCK;
1864 }
Steve French50c2f752007-07-13 00:33:32 +00001865
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001866plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001867 if (pSMB)
1868 cifs_small_buf_release(pSMB);
1869
Steve French133672e2007-11-13 22:41:37 +00001870 if (resp_buf_type == CIFS_SMALL_BUFFER)
1871 cifs_small_buf_release(iov[0].iov_base);
1872 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1873 cifs_buf_release(iov[0].iov_base);
1874
Steve French08547b02006-02-28 22:39:25 +00001875 /* Note: On -EAGAIN error only caller can retry on handle based calls
1876 since file handle passed in no longer valid */
1877
1878 return rc;
1879}
1880
1881
1882int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1884{
1885 int rc = 0;
1886 CLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 cFYI(1, ("In CIFSSMBClose"));
1888
1889/* do not retry on dead session on close */
1890 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001891 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 return 0;
1893 if (rc)
1894 return rc;
1895
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e52006-10-02 05:53:29 +00001897 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001899 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001900 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001902 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903 /* EINTR is expected when user ctl-c to kill app */
1904 cERROR(1, ("Send error in Close = %d", rc));
1905 }
1906 }
1907
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001909 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910 rc = 0;
1911
1912 return rc;
1913}
1914
1915int
1916CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1917 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001918 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919{
1920 int rc = 0;
1921 RENAME_REQ *pSMB = NULL;
1922 RENAME_RSP *pSMBr = NULL;
1923 int bytes_returned;
1924 int name_len, name_len2;
1925 __u16 count;
1926
1927 cFYI(1, ("In CIFSSMBRename"));
1928renameRetry:
1929 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1930 (void **) &pSMBr);
1931 if (rc)
1932 return rc;
1933
1934 pSMB->BufferFormat = 0x04;
1935 pSMB->SearchAttributes =
1936 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1937 ATTR_DIRECTORY);
1938
1939 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1940 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001941 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001942 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 name_len++; /* trailing null */
1944 name_len *= 2;
1945 pSMB->OldFileName[name_len] = 0x04; /* pad */
1946 /* protocol requires ASCII signature byte on Unicode string */
1947 pSMB->OldFileName[name_len + 1] = 0x00;
1948 name_len2 =
Steve Frenchb1a45692005-05-17 16:07:23 -05001949 cifsConvertToUCS((__le16 *) &pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07001950 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
1952 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00001953 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 name_len = strnlen(fromName, PATH_MAX);
1955 name_len++; /* trailing null */
1956 strncpy(pSMB->OldFileName, fromName, name_len);
1957 name_len2 = strnlen(toName, PATH_MAX);
1958 name_len2++; /* trailing null */
1959 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
1960 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
1961 name_len2++; /* trailing null */
1962 name_len2++; /* signature byte */
1963 }
1964
1965 count = 1 /* 1st signature byte */ + name_len + name_len2;
1966 pSMB->hdr.smb_buf_length += count;
1967 pSMB->ByteCount = cpu_to_le16(count);
1968
1969 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1970 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001971 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00001972 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 cFYI(1, ("Send error in rename = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 cifs_buf_release(pSMB);
1976
1977 if (rc == -EAGAIN)
1978 goto renameRetry;
1979
1980 return rc;
1981}
1982
Steve French50c2f752007-07-13 00:33:32 +00001983int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
1984 int netfid, char *target_name,
1985 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986{
1987 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1988 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00001989 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 char *data_offset;
1991 char dummy_string[30];
1992 int rc = 0;
1993 int bytes_returned = 0;
1994 int len_of_str;
1995 __u16 params, param_offset, offset, count, byte_count;
1996
1997 cFYI(1, ("Rename to File by handle"));
1998 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
1999 (void **) &pSMBr);
2000 if (rc)
2001 return rc;
2002
2003 params = 6;
2004 pSMB->MaxSetupCount = 0;
2005 pSMB->Reserved = 0;
2006 pSMB->Flags = 0;
2007 pSMB->Timeout = 0;
2008 pSMB->Reserved2 = 0;
2009 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
2010 offset = param_offset + params;
2011
2012 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2013 rename_info = (struct set_file_rename *) data_offset;
2014 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002015 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016 pSMB->SetupCount = 1;
2017 pSMB->Reserved3 = 0;
2018 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2019 byte_count = 3 /* pad */ + params;
2020 pSMB->ParameterCount = cpu_to_le16(params);
2021 pSMB->TotalParameterCount = pSMB->ParameterCount;
2022 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2023 pSMB->DataOffset = cpu_to_le16(offset);
2024 /* construct random name ".cifs_tmp<inodenum><mid>" */
2025 rename_info->overwrite = cpu_to_le32(1);
2026 rename_info->root_fid = 0;
2027 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002028 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002029 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2030 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002031 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002033 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002034 target_name, PATH_MAX, nls_codepage,
2035 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 }
2037 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
2038 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str) + 2;
2039 byte_count += count;
2040 pSMB->DataCount = cpu_to_le16(count);
2041 pSMB->TotalDataCount = pSMB->DataCount;
2042 pSMB->Fid = netfid;
2043 pSMB->InformationLevel =
2044 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2045 pSMB->Reserved4 = 0;
2046 pSMB->hdr.smb_buf_length += byte_count;
2047 pSMB->ByteCount = cpu_to_le16(byte_count);
2048 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002049 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002050 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002051 if (rc)
Steve French790fe572007-07-07 19:25:05 +00002052 cFYI(1, ("Send error in Rename (by file handle) = %d", rc));
Steve Frencha5a2b482005-08-20 21:42:53 -07002053
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 cifs_buf_release(pSMB);
2055
2056 /* Note: On -EAGAIN error only caller can retry on handle based calls
2057 since file handle passed in no longer valid */
2058
2059 return rc;
2060}
2061
2062int
Steve French50c2f752007-07-13 00:33:32 +00002063CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2064 const __u16 target_tid, const char *toName, const int flags,
2065 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066{
2067 int rc = 0;
2068 COPY_REQ *pSMB = NULL;
2069 COPY_RSP *pSMBr = NULL;
2070 int bytes_returned;
2071 int name_len, name_len2;
2072 __u16 count;
2073
2074 cFYI(1, ("In CIFSSMBCopy"));
2075copyRetry:
2076 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2077 (void **) &pSMBr);
2078 if (rc)
2079 return rc;
2080
2081 pSMB->BufferFormat = 0x04;
2082 pSMB->Tid2 = target_tid;
2083
2084 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2085
2086 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002087 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002088 fromName, PATH_MAX, nls_codepage,
2089 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090 name_len++; /* trailing null */
2091 name_len *= 2;
2092 pSMB->OldFileName[name_len] = 0x04; /* pad */
2093 /* protocol requires ASCII signature byte on Unicode string */
2094 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002095 name_len2 =
2096 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002097 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2099 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002100 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 name_len = strnlen(fromName, PATH_MAX);
2102 name_len++; /* trailing null */
2103 strncpy(pSMB->OldFileName, fromName, name_len);
2104 name_len2 = strnlen(toName, PATH_MAX);
2105 name_len2++; /* trailing null */
2106 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2107 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2108 name_len2++; /* trailing null */
2109 name_len2++; /* signature byte */
2110 }
2111
2112 count = 1 /* 1st signature byte */ + name_len + name_len2;
2113 pSMB->hdr.smb_buf_length += count;
2114 pSMB->ByteCount = cpu_to_le16(count);
2115
2116 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2117 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2118 if (rc) {
2119 cFYI(1, ("Send error in copy = %d with %d files copied",
2120 rc, le16_to_cpu(pSMBr->CopyCount)));
2121 }
2122 if (pSMB)
2123 cifs_buf_release(pSMB);
2124
2125 if (rc == -EAGAIN)
2126 goto copyRetry;
2127
2128 return rc;
2129}
2130
2131int
2132CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2133 const char *fromName, const char *toName,
2134 const struct nls_table *nls_codepage)
2135{
2136 TRANSACTION2_SPI_REQ *pSMB = NULL;
2137 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2138 char *data_offset;
2139 int name_len;
2140 int name_len_target;
2141 int rc = 0;
2142 int bytes_returned = 0;
2143 __u16 params, param_offset, offset, byte_count;
2144
2145 cFYI(1, ("In Symlink Unix style"));
2146createSymLinkRetry:
2147 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2148 (void **) &pSMBr);
2149 if (rc)
2150 return rc;
2151
2152 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2153 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002154 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 /* find define for this maxpathcomponent */
2156 , nls_codepage);
2157 name_len++; /* trailing null */
2158 name_len *= 2;
2159
Steve French50c2f752007-07-13 00:33:32 +00002160 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 name_len = strnlen(fromName, PATH_MAX);
2162 name_len++; /* trailing null */
2163 strncpy(pSMB->FileName, fromName, name_len);
2164 }
2165 params = 6 + name_len;
2166 pSMB->MaxSetupCount = 0;
2167 pSMB->Reserved = 0;
2168 pSMB->Flags = 0;
2169 pSMB->Timeout = 0;
2170 pSMB->Reserved2 = 0;
2171 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002172 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 offset = param_offset + params;
2174
2175 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2176 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2177 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002178 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 /* find define for this maxpathcomponent */
2180 , nls_codepage);
2181 name_len_target++; /* trailing null */
2182 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002183 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184 name_len_target = strnlen(toName, PATH_MAX);
2185 name_len_target++; /* trailing null */
2186 strncpy(data_offset, toName, name_len_target);
2187 }
2188
2189 pSMB->MaxParameterCount = cpu_to_le16(2);
2190 /* BB find exact max on data count below from sess */
2191 pSMB->MaxDataCount = cpu_to_le16(1000);
2192 pSMB->SetupCount = 1;
2193 pSMB->Reserved3 = 0;
2194 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2195 byte_count = 3 /* pad */ + params + name_len_target;
2196 pSMB->DataCount = cpu_to_le16(name_len_target);
2197 pSMB->ParameterCount = cpu_to_le16(params);
2198 pSMB->TotalDataCount = pSMB->DataCount;
2199 pSMB->TotalParameterCount = pSMB->ParameterCount;
2200 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2201 pSMB->DataOffset = cpu_to_le16(offset);
2202 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2203 pSMB->Reserved4 = 0;
2204 pSMB->hdr.smb_buf_length += byte_count;
2205 pSMB->ByteCount = cpu_to_le16(byte_count);
2206 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2207 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002208 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002209 if (rc)
Steve French2d785a52007-07-15 01:48:57 +00002210 cFYI(1, ("Send error in SetPathInfo create symlink = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211
2212 if (pSMB)
2213 cifs_buf_release(pSMB);
2214
2215 if (rc == -EAGAIN)
2216 goto createSymLinkRetry;
2217
2218 return rc;
2219}
2220
2221int
2222CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2223 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002224 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225{
2226 TRANSACTION2_SPI_REQ *pSMB = NULL;
2227 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2228 char *data_offset;
2229 int name_len;
2230 int name_len_target;
2231 int rc = 0;
2232 int bytes_returned = 0;
2233 __u16 params, param_offset, offset, byte_count;
2234
2235 cFYI(1, ("In Create Hard link Unix style"));
2236createHardLinkRetry:
2237 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2238 (void **) &pSMBr);
2239 if (rc)
2240 return rc;
2241
2242 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002243 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002244 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245 name_len++; /* trailing null */
2246 name_len *= 2;
2247
Steve French50c2f752007-07-13 00:33:32 +00002248 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249 name_len = strnlen(toName, PATH_MAX);
2250 name_len++; /* trailing null */
2251 strncpy(pSMB->FileName, toName, name_len);
2252 }
2253 params = 6 + name_len;
2254 pSMB->MaxSetupCount = 0;
2255 pSMB->Reserved = 0;
2256 pSMB->Flags = 0;
2257 pSMB->Timeout = 0;
2258 pSMB->Reserved2 = 0;
2259 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002260 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261 offset = param_offset + params;
2262
2263 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2264 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2265 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002266 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002267 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268 name_len_target++; /* trailing null */
2269 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002270 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271 name_len_target = strnlen(fromName, PATH_MAX);
2272 name_len_target++; /* trailing null */
2273 strncpy(data_offset, fromName, name_len_target);
2274 }
2275
2276 pSMB->MaxParameterCount = cpu_to_le16(2);
2277 /* BB find exact max on data count below from sess*/
2278 pSMB->MaxDataCount = cpu_to_le16(1000);
2279 pSMB->SetupCount = 1;
2280 pSMB->Reserved3 = 0;
2281 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2282 byte_count = 3 /* pad */ + params + name_len_target;
2283 pSMB->ParameterCount = cpu_to_le16(params);
2284 pSMB->TotalParameterCount = pSMB->ParameterCount;
2285 pSMB->DataCount = cpu_to_le16(name_len_target);
2286 pSMB->TotalDataCount = pSMB->DataCount;
2287 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2288 pSMB->DataOffset = cpu_to_le16(offset);
2289 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2290 pSMB->Reserved4 = 0;
2291 pSMB->hdr.smb_buf_length += byte_count;
2292 pSMB->ByteCount = cpu_to_le16(byte_count);
2293 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2294 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002295 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002296 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297 cFYI(1, ("Send error in SetPathInfo (hard link) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298
2299 cifs_buf_release(pSMB);
2300 if (rc == -EAGAIN)
2301 goto createHardLinkRetry;
2302
2303 return rc;
2304}
2305
2306int
2307CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2308 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002309 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310{
2311 int rc = 0;
2312 NT_RENAME_REQ *pSMB = NULL;
2313 RENAME_RSP *pSMBr = NULL;
2314 int bytes_returned;
2315 int name_len, name_len2;
2316 __u16 count;
2317
2318 cFYI(1, ("In CIFSCreateHardLink"));
2319winCreateHardLinkRetry:
2320
2321 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2322 (void **) &pSMBr);
2323 if (rc)
2324 return rc;
2325
2326 pSMB->SearchAttributes =
2327 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2328 ATTR_DIRECTORY);
2329 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2330 pSMB->ClusterCount = 0;
2331
2332 pSMB->BufferFormat = 0x04;
2333
2334 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2335 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002336 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002337 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 name_len++; /* trailing null */
2339 name_len *= 2;
2340 pSMB->OldFileName[name_len] = 0; /* pad */
Steve French50c2f752007-07-13 00:33:32 +00002341 pSMB->OldFileName[name_len + 1] = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002343 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002344 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2346 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002347 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348 name_len = strnlen(fromName, PATH_MAX);
2349 name_len++; /* trailing null */
2350 strncpy(pSMB->OldFileName, fromName, name_len);
2351 name_len2 = strnlen(toName, PATH_MAX);
2352 name_len2++; /* trailing null */
2353 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2354 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2355 name_len2++; /* trailing null */
2356 name_len2++; /* signature byte */
2357 }
2358
2359 count = 1 /* string type byte */ + name_len + name_len2;
2360 pSMB->hdr.smb_buf_length += count;
2361 pSMB->ByteCount = cpu_to_le16(count);
2362
2363 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2364 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002365 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002366 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367 cFYI(1, ("Send error in hard link (NT rename) = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00002368
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369 cifs_buf_release(pSMB);
2370 if (rc == -EAGAIN)
2371 goto winCreateHardLinkRetry;
2372
2373 return rc;
2374}
2375
2376int
2377CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
2378 const unsigned char *searchName,
2379 char *symlinkinfo, const int buflen,
2380 const struct nls_table *nls_codepage)
2381{
2382/* SMB_QUERY_FILE_UNIX_LINK */
2383 TRANSACTION2_QPI_REQ *pSMB = NULL;
2384 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2385 int rc = 0;
2386 int bytes_returned;
2387 int name_len;
2388 __u16 params, byte_count;
2389
2390 cFYI(1, ("In QPathSymLinkInfo (Unix) for path %s", searchName));
2391
2392querySymLinkRetry:
2393 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2394 (void **) &pSMBr);
2395 if (rc)
2396 return rc;
2397
2398 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2399 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002400 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2401 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402 name_len++; /* trailing null */
2403 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002404 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 name_len = strnlen(searchName, PATH_MAX);
2406 name_len++; /* trailing null */
2407 strncpy(pSMB->FileName, searchName, name_len);
2408 }
2409
2410 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2411 pSMB->TotalDataCount = 0;
2412 pSMB->MaxParameterCount = cpu_to_le16(2);
2413 /* BB find exact max data count below from sess structure BB */
2414 pSMB->MaxDataCount = cpu_to_le16(4000);
2415 pSMB->MaxSetupCount = 0;
2416 pSMB->Reserved = 0;
2417 pSMB->Flags = 0;
2418 pSMB->Timeout = 0;
2419 pSMB->Reserved2 = 0;
2420 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002421 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422 pSMB->DataCount = 0;
2423 pSMB->DataOffset = 0;
2424 pSMB->SetupCount = 1;
2425 pSMB->Reserved3 = 0;
2426 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2427 byte_count = params + 1 /* pad */ ;
2428 pSMB->TotalParameterCount = cpu_to_le16(params);
2429 pSMB->ParameterCount = pSMB->TotalParameterCount;
2430 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2431 pSMB->Reserved4 = 0;
2432 pSMB->hdr.smb_buf_length += byte_count;
2433 pSMB->ByteCount = cpu_to_le16(byte_count);
2434
2435 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2436 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2437 if (rc) {
2438 cFYI(1, ("Send error in QuerySymLinkInfo = %d", rc));
2439 } else {
2440 /* decode response */
2441
2442 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2443 if (rc || (pSMBr->ByteCount < 2))
2444 /* BB also check enough total bytes returned */
2445 rc = -EIO; /* bad smb */
2446 else {
2447 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2448 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2449
2450 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2451 name_len = UniStrnlen((wchar_t *) ((char *)
Steve French50c2f752007-07-13 00:33:32 +00002452 &pSMBr->hdr.Protocol + data_offset),
2453 min_t(const int, buflen, count) / 2);
Steve French737b7582005-04-28 22:41:06 -07002454 /* BB FIXME investigate remapping reserved chars here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002456 (__le16 *) ((char *)&pSMBr->hdr.Protocol
2457 + data_offset),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458 name_len, nls_codepage);
2459 } else {
2460 strncpy(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002461 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462 data_offset,
2463 min_t(const int, buflen, count));
2464 }
2465 symlinkinfo[buflen] = 0;
2466 /* just in case so calling code does not go off the end of buffer */
2467 }
2468 }
2469 cifs_buf_release(pSMB);
2470 if (rc == -EAGAIN)
2471 goto querySymLinkRetry;
2472 return rc;
2473}
2474
Parag Warudkarc9489772007-10-23 18:09:48 +00002475#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08002476/* Initialize NT TRANSACT SMB into small smb request buffer.
2477 This assumes that all NT TRANSACTS that we init here have
2478 total parm and data under about 400 bytes (to fit in small cifs
2479 buffer size), which is the case so far, it easily fits. NB:
2480 Setup words themselves and ByteCount
2481 MaxSetupCount (size of returned setup area) and
2482 MaxParameterCount (returned parms size) must be set by caller */
Steve French50c2f752007-07-13 00:33:32 +00002483static int
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002484smb_init_nttransact(const __u16 sub_command, const int setup_count,
Steve French0a4b92c2006-01-12 15:44:21 -08002485 const int parm_len, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002486 void **ret_buf)
Steve French0a4b92c2006-01-12 15:44:21 -08002487{
2488 int rc;
2489 __u32 temp_offset;
Steve French50c2f752007-07-13 00:33:32 +00002490 struct smb_com_ntransact_req *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08002491
2492 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
2493 (void **)&pSMB);
2494 if (rc)
2495 return rc;
2496 *ret_buf = (void *)pSMB;
2497 pSMB->Reserved = 0;
2498 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
2499 pSMB->TotalDataCount = 0;
2500 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2501 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
2502 pSMB->ParameterCount = pSMB->TotalParameterCount;
2503 pSMB->DataCount = pSMB->TotalDataCount;
2504 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
2505 (setup_count * 2) - 4 /* for rfc1001 length itself */;
2506 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
2507 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
2508 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
2509 pSMB->SubCommand = cpu_to_le16(sub_command);
2510 return 0;
2511}
2512
2513static int
Steve French50c2f752007-07-13 00:33:32 +00002514validate_ntransact(char *buf, char **ppparm, char **ppdata,
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002515 __u32 *pparmlen, __u32 *pdatalen)
Steve French0a4b92c2006-01-12 15:44:21 -08002516{
Steve French50c2f752007-07-13 00:33:32 +00002517 char *end_of_smb;
Steve French0a4b92c2006-01-12 15:44:21 -08002518 __u32 data_count, data_offset, parm_count, parm_offset;
Steve French50c2f752007-07-13 00:33:32 +00002519 struct smb_com_ntransact_rsp *pSMBr;
Steve French0a4b92c2006-01-12 15:44:21 -08002520
Steve French630f3f0c2007-10-25 21:17:17 +00002521 *pdatalen = 0;
2522 *pparmlen = 0;
2523
Steve French790fe572007-07-07 19:25:05 +00002524 if (buf == NULL)
Steve French0a4b92c2006-01-12 15:44:21 -08002525 return -EINVAL;
2526
2527 pSMBr = (struct smb_com_ntransact_rsp *)buf;
2528
2529 /* ByteCount was converted from little endian in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +00002530 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
Steve French0a4b92c2006-01-12 15:44:21 -08002531 (char *)&pSMBr->ByteCount;
2532
Steve French0a4b92c2006-01-12 15:44:21 -08002533 data_offset = le32_to_cpu(pSMBr->DataOffset);
2534 data_count = le32_to_cpu(pSMBr->DataCount);
Steve French50c2f752007-07-13 00:33:32 +00002535 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
Steve French0a4b92c2006-01-12 15:44:21 -08002536 parm_count = le32_to_cpu(pSMBr->ParameterCount);
2537
2538 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
2539 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
2540
2541 /* should we also check that parm and data areas do not overlap? */
Steve French790fe572007-07-07 19:25:05 +00002542 if (*ppparm > end_of_smb) {
2543 cFYI(1, ("parms start after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002544 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002545 } else if (parm_count + *ppparm > end_of_smb) {
2546 cFYI(1, ("parm end after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002547 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002548 } else if (*ppdata > end_of_smb) {
2549 cFYI(1, ("data starts after end of smb"));
Steve French0a4b92c2006-01-12 15:44:21 -08002550 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002551 } else if (data_count + *ppdata > end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002552 cFYI(1, ("data %p + count %d (%p) ends after end of smb %p start %p",
Steve French50c2f752007-07-13 00:33:32 +00002553 *ppdata, data_count, (data_count + *ppdata),
2554 end_of_smb, pSMBr));
Steve French0a4b92c2006-01-12 15:44:21 -08002555 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002556 } else if (parm_count + data_count > pSMBr->ByteCount) {
2557 cFYI(1, ("parm count and data count larger than SMB"));
Steve French0a4b92c2006-01-12 15:44:21 -08002558 return -EINVAL;
2559 }
Steve French630f3f0c2007-10-25 21:17:17 +00002560 *pdatalen = data_count;
2561 *pparmlen = parm_count;
Steve French0a4b92c2006-01-12 15:44:21 -08002562 return 0;
2563}
Parag Warudkarc9489772007-10-23 18:09:48 +00002564#endif /* CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08002565
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566int
2567CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2568 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002569 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570 const struct nls_table *nls_codepage)
2571{
2572 int rc = 0;
2573 int bytes_returned;
2574 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00002575 struct smb_com_transaction_ioctl_req *pSMB;
2576 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577
2578 cFYI(1, ("In Windows reparse style QueryLink for path %s", searchName));
2579 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2580 (void **) &pSMBr);
2581 if (rc)
2582 return rc;
2583
2584 pSMB->TotalParameterCount = 0 ;
2585 pSMB->TotalDataCount = 0;
2586 pSMB->MaxParameterCount = cpu_to_le32(2);
2587 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002588 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2589 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590 pSMB->MaxSetupCount = 4;
2591 pSMB->Reserved = 0;
2592 pSMB->ParameterOffset = 0;
2593 pSMB->DataCount = 0;
2594 pSMB->DataOffset = 0;
2595 pSMB->SetupCount = 4;
2596 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2597 pSMB->ParameterCount = pSMB->TotalParameterCount;
2598 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2599 pSMB->IsFsctl = 1; /* FSCTL */
2600 pSMB->IsRootFlag = 0;
2601 pSMB->Fid = fid; /* file handle always le */
2602 pSMB->ByteCount = 0;
2603
2604 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2605 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2606 if (rc) {
2607 cFYI(1, ("Send error in QueryReparseLinkInfo = %d", rc));
2608 } else { /* decode response */
2609 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2610 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
2611 if ((pSMBr->ByteCount < 2) || (data_offset > 512))
2612 /* BB also check enough total bytes returned */
2613 rc = -EIO; /* bad smb */
2614 else {
Steve French790fe572007-07-07 19:25:05 +00002615 if (data_count && (data_count < 2048)) {
Steve French50c2f752007-07-13 00:33:32 +00002616 char *end_of_smb = 2 /* sizeof byte count */ +
Steve French0a4b92c2006-01-12 15:44:21 -08002617 pSMBr->ByteCount +
2618 (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619
Steve French50c2f752007-07-13 00:33:32 +00002620 struct reparse_data *reparse_buf =
2621 (struct reparse_data *)
2622 ((char *)&pSMBr->hdr.Protocol
2623 + data_offset);
Steve French790fe572007-07-07 19:25:05 +00002624 if ((char *)reparse_buf >= end_of_smb) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625 rc = -EIO;
2626 goto qreparse_out;
2627 }
Steve French790fe572007-07-07 19:25:05 +00002628 if ((reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 reparse_buf->TargetNameOffset +
2630 reparse_buf->TargetNameLen) >
2631 end_of_smb) {
Steve French26f57362007-08-30 22:09:15 +00002632 cFYI(1, ("reparse buf beyond SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633 rc = -EIO;
2634 goto qreparse_out;
2635 }
Steve French50c2f752007-07-13 00:33:32 +00002636
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2638 name_len = UniStrnlen((wchar_t *)
Steve French50c2f752007-07-13 00:33:32 +00002639 (reparse_buf->LinkNamesBuf +
2640 reparse_buf->TargetNameOffset),
2641 min(buflen/2,
2642 reparse_buf->TargetNameLen / 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643 cifs_strfromUCS_le(symlinkinfo,
Steve French50c2f752007-07-13 00:33:32 +00002644 (__le16 *) (reparse_buf->LinkNamesBuf +
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645 reparse_buf->TargetNameOffset),
2646 name_len, nls_codepage);
2647 } else { /* ASCII names */
Steve French50c2f752007-07-13 00:33:32 +00002648 strncpy(symlinkinfo,
2649 reparse_buf->LinkNamesBuf +
2650 reparse_buf->TargetNameOffset,
2651 min_t(const int, buflen,
2652 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653 }
2654 } else {
2655 rc = -EIO;
Steve French63135e02007-07-17 17:34:02 +00002656 cFYI(1, ("Invalid return data count on "
2657 "get reparse info ioctl"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658 }
2659 symlinkinfo[buflen] = 0; /* just in case so the caller
2660 does not go off the end of the buffer */
Steve French50c2f752007-07-13 00:33:32 +00002661 cFYI(1, ("readlink result - %s", symlinkinfo));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662 }
2663 }
2664qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002665 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666
2667 /* Note: On -EAGAIN error only caller can retry on handle based calls
2668 since file handle passed in no longer valid */
2669
2670 return rc;
2671}
2672
2673#ifdef CONFIG_CIFS_POSIX
2674
2675/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002676static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2677 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678{
2679 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002680 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2681 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2682 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683 /* cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id)); */
2684
2685 return;
2686}
2687
2688/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002689static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2690 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002691{
2692 int size = 0;
2693 int i;
2694 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002695 struct cifs_posix_ace *pACE;
2696 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2697 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698
2699 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2700 return -EOPNOTSUPP;
2701
Steve French790fe572007-07-07 19:25:05 +00002702 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 count = le16_to_cpu(cifs_acl->access_entry_count);
2704 pACE = &cifs_acl->ace_array[0];
2705 size = sizeof(struct cifs_posix_acl);
2706 size += sizeof(struct cifs_posix_ace) * count;
2707 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002708 if (size_of_data_area < size) {
Steve French50c2f752007-07-13 00:33:32 +00002709 cFYI(1, ("bad CIFS POSIX ACL size %d vs. %d",
2710 size_of_data_area, size));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711 return -EINVAL;
2712 }
Steve French790fe572007-07-07 19:25:05 +00002713 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714 count = le16_to_cpu(cifs_acl->access_entry_count);
2715 size = sizeof(struct cifs_posix_acl);
2716 size += sizeof(struct cifs_posix_ace) * count;
2717/* skip past access ACEs to get to default ACEs */
2718 pACE = &cifs_acl->ace_array[count];
2719 count = le16_to_cpu(cifs_acl->default_entry_count);
2720 size += sizeof(struct cifs_posix_ace) * count;
2721 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002722 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723 return -EINVAL;
2724 } else {
2725 /* illegal type */
2726 return -EINVAL;
2727 }
2728
2729 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002730 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002731 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002732 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733 return -ERANGE;
2734 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002735 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002736 for (i = 0; i < count ; i++) {
2737 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2738 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739 }
2740 }
2741 return size;
2742}
2743
Steve French50c2f752007-07-13 00:33:32 +00002744static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2745 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746{
2747 __u16 rc = 0; /* 0 = ACL converted ok */
2748
Steve Frenchff7feac2005-11-15 16:45:16 -08002749 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2750 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002752 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753 /* Probably no need to le convert -1 on any arch but can not hurt */
2754 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002755 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002756 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Steve French50c2f752007-07-13 00:33:32 +00002757 /*cFYI(1,("perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id));*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758 return rc;
2759}
2760
2761/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002762static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2763 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764{
2765 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002766 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2767 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768 int count;
2769 int i;
2770
Steve French790fe572007-07-07 19:25:05 +00002771 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772 return 0;
2773
2774 count = posix_acl_xattr_count((size_t)buflen);
Steve Frenchc18c8422007-07-18 23:21:09 +00002775 cFYI(1, ("setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002776 "version of %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002777 count, buflen, le32_to_cpu(local_acl->a_version)));
Steve French790fe572007-07-07 19:25:05 +00002778 if (le32_to_cpu(local_acl->a_version) != 2) {
Steve French50c2f752007-07-13 00:33:32 +00002779 cFYI(1, ("unknown POSIX ACL version %d",
Steve Frenchff7feac2005-11-15 16:45:16 -08002780 le32_to_cpu(local_acl->a_version)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 return 0;
2782 }
2783 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002784 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002785 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002786 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002787 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 else {
Steve French50c2f752007-07-13 00:33:32 +00002789 cFYI(1, ("unknown ACL type %d", acl_type));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 return 0;
2791 }
Steve French50c2f752007-07-13 00:33:32 +00002792 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2794 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002795 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 /* ACE not converted */
2797 break;
2798 }
2799 }
Steve French790fe572007-07-07 19:25:05 +00002800 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2802 rc += sizeof(struct cifs_posix_acl);
2803 /* BB add check to make sure ACL does not overflow SMB */
2804 }
2805 return rc;
2806}
2807
2808int
2809CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002810 const unsigned char *searchName,
2811 char *acl_inf, const int buflen, const int acl_type,
2812 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813{
2814/* SMB_QUERY_POSIX_ACL */
2815 TRANSACTION2_QPI_REQ *pSMB = NULL;
2816 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2817 int rc = 0;
2818 int bytes_returned;
2819 int name_len;
2820 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002821
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 cFYI(1, ("In GetPosixACL (Unix) for path %s", searchName));
2823
2824queryAclRetry:
2825 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2826 (void **) &pSMBr);
2827 if (rc)
2828 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002829
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2831 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002832 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002833 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834 name_len++; /* trailing null */
2835 name_len *= 2;
2836 pSMB->FileName[name_len] = 0;
2837 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002838 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839 name_len = strnlen(searchName, PATH_MAX);
2840 name_len++; /* trailing null */
2841 strncpy(pSMB->FileName, searchName, name_len);
2842 }
2843
2844 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2845 pSMB->TotalDataCount = 0;
2846 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002847 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848 pSMB->MaxDataCount = cpu_to_le16(4000);
2849 pSMB->MaxSetupCount = 0;
2850 pSMB->Reserved = 0;
2851 pSMB->Flags = 0;
2852 pSMB->Timeout = 0;
2853 pSMB->Reserved2 = 0;
2854 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002855 offsetof(struct smb_com_transaction2_qpi_req,
2856 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857 pSMB->DataCount = 0;
2858 pSMB->DataOffset = 0;
2859 pSMB->SetupCount = 1;
2860 pSMB->Reserved3 = 0;
2861 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2862 byte_count = params + 1 /* pad */ ;
2863 pSMB->TotalParameterCount = cpu_to_le16(params);
2864 pSMB->ParameterCount = pSMB->TotalParameterCount;
2865 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2866 pSMB->Reserved4 = 0;
2867 pSMB->hdr.smb_buf_length += byte_count;
2868 pSMB->ByteCount = cpu_to_le16(byte_count);
2869
2870 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2871 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002872 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873 if (rc) {
2874 cFYI(1, ("Send error in Query POSIX ACL = %d", rc));
2875 } else {
2876 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002877
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2879 if (rc || (pSMBr->ByteCount < 2))
2880 /* BB also check enough total bytes returned */
2881 rc = -EIO; /* bad smb */
2882 else {
2883 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2884 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2885 rc = cifs_copy_posix_acl(acl_inf,
2886 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002887 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 }
2889 }
2890 cifs_buf_release(pSMB);
2891 if (rc == -EAGAIN)
2892 goto queryAclRetry;
2893 return rc;
2894}
2895
2896int
2897CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002898 const unsigned char *fileName,
2899 const char *local_acl, const int buflen,
2900 const int acl_type,
2901 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002902{
2903 struct smb_com_transaction2_spi_req *pSMB = NULL;
2904 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2905 char *parm_data;
2906 int name_len;
2907 int rc = 0;
2908 int bytes_returned = 0;
2909 __u16 params, byte_count, data_count, param_offset, offset;
2910
2911 cFYI(1, ("In SetPosixACL (Unix) for path %s", fileName));
2912setAclRetry:
2913 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002914 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002915 if (rc)
2916 return rc;
2917 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2918 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002919 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002920 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002921 name_len++; /* trailing null */
2922 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002923 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002924 name_len = strnlen(fileName, PATH_MAX);
2925 name_len++; /* trailing null */
2926 strncpy(pSMB->FileName, fileName, name_len);
2927 }
2928 params = 6 + name_len;
2929 pSMB->MaxParameterCount = cpu_to_le16(2);
2930 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB size from sess */
2931 pSMB->MaxSetupCount = 0;
2932 pSMB->Reserved = 0;
2933 pSMB->Flags = 0;
2934 pSMB->Timeout = 0;
2935 pSMB->Reserved2 = 0;
2936 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002937 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002938 offset = param_offset + params;
2939 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2940 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2941
2942 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002943 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002944
Steve French790fe572007-07-07 19:25:05 +00002945 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946 rc = -EOPNOTSUPP;
2947 goto setACLerrorExit;
2948 }
2949 pSMB->DataOffset = cpu_to_le16(offset);
2950 pSMB->SetupCount = 1;
2951 pSMB->Reserved3 = 0;
2952 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2953 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2954 byte_count = 3 /* pad */ + params + data_count;
2955 pSMB->DataCount = cpu_to_le16(data_count);
2956 pSMB->TotalDataCount = pSMB->DataCount;
2957 pSMB->ParameterCount = cpu_to_le16(params);
2958 pSMB->TotalParameterCount = pSMB->ParameterCount;
2959 pSMB->Reserved4 = 0;
2960 pSMB->hdr.smb_buf_length += byte_count;
2961 pSMB->ByteCount = cpu_to_le16(byte_count);
2962 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002963 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00002964 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002965 cFYI(1, ("Set POSIX ACL returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966
2967setACLerrorExit:
2968 cifs_buf_release(pSMB);
2969 if (rc == -EAGAIN)
2970 goto setAclRetry;
2971 return rc;
2972}
2973
Steve Frenchf654bac2005-04-28 22:41:04 -07002974/* BB fix tabs in this function FIXME BB */
2975int
2976CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00002977 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07002978{
Steve French50c2f752007-07-13 00:33:32 +00002979 int rc = 0;
2980 struct smb_t2_qfi_req *pSMB = NULL;
2981 struct smb_t2_qfi_rsp *pSMBr = NULL;
2982 int bytes_returned;
2983 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07002984
Steve French790fe572007-07-07 19:25:05 +00002985 cFYI(1, ("In GetExtAttr"));
2986 if (tcon == NULL)
2987 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07002988
2989GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00002990 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2991 (void **) &pSMBr);
2992 if (rc)
2993 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07002994
Steve Frenchad7a2922008-02-07 23:25:02 +00002995 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00002996 pSMB->t2.TotalDataCount = 0;
2997 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
2998 /* BB find exact max data count below from sess structure BB */
2999 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
3000 pSMB->t2.MaxSetupCount = 0;
3001 pSMB->t2.Reserved = 0;
3002 pSMB->t2.Flags = 0;
3003 pSMB->t2.Timeout = 0;
3004 pSMB->t2.Reserved2 = 0;
3005 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3006 Fid) - 4);
3007 pSMB->t2.DataCount = 0;
3008 pSMB->t2.DataOffset = 0;
3009 pSMB->t2.SetupCount = 1;
3010 pSMB->t2.Reserved3 = 0;
3011 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3012 byte_count = params + 1 /* pad */ ;
3013 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3014 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3015 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
3016 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07003017 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00003018 pSMB->hdr.smb_buf_length += byte_count;
3019 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07003020
Steve French790fe572007-07-07 19:25:05 +00003021 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3022 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3023 if (rc) {
3024 cFYI(1, ("error %d in GetExtAttr", rc));
3025 } else {
3026 /* decode response */
3027 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3028 if (rc || (pSMBr->ByteCount < 2))
3029 /* BB also check enough total bytes returned */
3030 /* If rc should we check for EOPNOSUPP and
3031 disable the srvino flag? or in caller? */
3032 rc = -EIO; /* bad smb */
3033 else {
3034 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3035 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
3036 struct file_chattr_info *pfinfo;
3037 /* BB Do we need a cast or hash here ? */
3038 if (count != 16) {
3039 cFYI(1, ("Illegal size ret in GetExtAttr"));
3040 rc = -EIO;
3041 goto GetExtAttrOut;
3042 }
3043 pfinfo = (struct file_chattr_info *)
3044 (data_offset + (char *) &pSMBr->hdr.Protocol);
3045 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07003046 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00003047 }
3048 }
Steve Frenchf654bac2005-04-28 22:41:04 -07003049GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00003050 cifs_buf_release(pSMB);
3051 if (rc == -EAGAIN)
3052 goto GetExtAttrRetry;
3053 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003054}
3055
Steve Frenchf654bac2005-04-28 22:41:04 -07003056#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003057
Steve French297647c2007-10-12 04:11:59 +00003058#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08003059/* Get Security Descriptor (by handle) from remote server for a file or dir */
3060int
3061CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f0c2007-10-25 21:17:17 +00003062 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003063{
3064 int rc = 0;
3065 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003066 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003067 struct kvec iov[1];
3068
3069 cFYI(1, ("GetCifsACL"));
3070
Steve French630f3f0c2007-10-25 21:17:17 +00003071 *pbuflen = 0;
3072 *acl_inf = NULL;
3073
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003074 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003075 8 /* parm len */, tcon, (void **) &pSMB);
3076 if (rc)
3077 return rc;
3078
3079 pSMB->MaxParameterCount = cpu_to_le32(4);
3080 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3081 pSMB->MaxSetupCount = 0;
3082 pSMB->Fid = fid; /* file handle always le */
3083 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3084 CIFS_ACL_DACL);
3085 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3086 pSMB->hdr.smb_buf_length += 11;
3087 iov[0].iov_base = (char *)pSMB;
3088 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3089
Steve Frencha761ac52007-10-18 21:45:27 +00003090 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Steve French133672e2007-11-13 22:41:37 +00003091 CIFS_STD_OP);
Steve French0a4b92c2006-01-12 15:44:21 -08003092 cifs_stats_inc(&tcon->num_acl_get);
3093 if (rc) {
3094 cFYI(1, ("Send error in QuerySecDesc = %d", rc));
3095 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003096 __le32 *parm;
Steve French630f3f0c2007-10-25 21:17:17 +00003097 __u32 parm_len;
3098 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003099 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f0c2007-10-25 21:17:17 +00003100 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003101
3102/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003103 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f0c2007-10-25 21:17:17 +00003104 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003105 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003106 goto qsec_out;
3107 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3108
Steve French630f3f0c2007-10-25 21:17:17 +00003109 cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, *acl_inf));
Steve French0a4b92c2006-01-12 15:44:21 -08003110
3111 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3112 rc = -EIO; /* bad smb */
Steve French630f3f0c2007-10-25 21:17:17 +00003113 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003114 goto qsec_out;
3115 }
3116
3117/* BB check that data area is minimum length and as big as acl_len */
3118
Steve Frenchaf6f4612007-10-16 18:40:37 +00003119 acl_len = le32_to_cpu(*parm);
Steve French630f3f0c2007-10-25 21:17:17 +00003120 if (acl_len != *pbuflen) {
3121 cERROR(1, ("acl length %d does not match %d",
3122 acl_len, *pbuflen));
3123 if (*pbuflen > acl_len)
3124 *pbuflen = acl_len;
3125 }
Steve French0a4b92c2006-01-12 15:44:21 -08003126
Steve French630f3f0c2007-10-25 21:17:17 +00003127 /* check if buffer is big enough for the acl
3128 header followed by the smallest SID */
3129 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3130 (*pbuflen >= 64 * 1024)) {
3131 cERROR(1, ("bad acl length %d", *pbuflen));
3132 rc = -EINVAL;
3133 *pbuflen = 0;
3134 } else {
3135 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3136 if (*acl_inf == NULL) {
3137 *pbuflen = 0;
3138 rc = -ENOMEM;
3139 }
3140 memcpy(*acl_inf, pdata, *pbuflen);
3141 }
Steve French0a4b92c2006-01-12 15:44:21 -08003142 }
3143qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003144 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003145 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003146 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003147 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003148/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003149 return rc;
3150}
Steve French97837582007-12-31 07:47:21 +00003151
3152int
3153CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3154 struct cifs_ntsd *pntsd, __u32 acllen)
3155{
3156 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3157 int rc = 0;
3158 int bytes_returned = 0;
3159 SET_SEC_DESC_REQ *pSMB = NULL;
3160 NTRANSACT_RSP *pSMBr = NULL;
3161
3162setCifsAclRetry:
3163 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3164 (void **) &pSMBr);
3165 if (rc)
3166 return (rc);
3167
3168 pSMB->MaxSetupCount = 0;
3169 pSMB->Reserved = 0;
3170
3171 param_count = 8;
3172 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3173 data_count = acllen;
3174 data_offset = param_offset + param_count;
3175 byte_count = 3 /* pad */ + param_count;
3176
3177 pSMB->DataCount = cpu_to_le32(data_count);
3178 pSMB->TotalDataCount = pSMB->DataCount;
3179 pSMB->MaxParameterCount = cpu_to_le32(4);
3180 pSMB->MaxDataCount = cpu_to_le32(16384);
3181 pSMB->ParameterCount = cpu_to_le32(param_count);
3182 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3183 pSMB->TotalParameterCount = pSMB->ParameterCount;
3184 pSMB->DataOffset = cpu_to_le32(data_offset);
3185 pSMB->SetupCount = 0;
3186 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3187 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3188
3189 pSMB->Fid = fid; /* file handle always le */
3190 pSMB->Reserved2 = 0;
3191 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3192
3193 if (pntsd && acllen) {
3194 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3195 (char *) pntsd,
3196 acllen);
3197 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3198
3199 } else
3200 pSMB->hdr.smb_buf_length += byte_count;
3201
3202 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3203 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3204
3205 cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc));
3206 if (rc)
3207 cFYI(1, ("Set CIFS ACL returned %d", rc));
3208 cifs_buf_release(pSMB);
3209
3210 if (rc == -EAGAIN)
3211 goto setCifsAclRetry;
3212
3213 return (rc);
3214}
3215
Steve French297647c2007-10-12 04:11:59 +00003216#endif /* CONFIG_CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08003217
Steve French6b8edfe2005-08-23 20:26:03 -07003218/* Legacy Query Path Information call for lookup to old servers such
3219 as Win9x/WinME */
3220int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003221 const unsigned char *searchName,
3222 FILE_ALL_INFO *pFinfo,
3223 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003224{
Steve Frenchad7a2922008-02-07 23:25:02 +00003225 QUERY_INFORMATION_REQ *pSMB;
3226 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003227 int rc = 0;
3228 int bytes_returned;
3229 int name_len;
3230
Steve French50c2f752007-07-13 00:33:32 +00003231 cFYI(1, ("In SMBQPath path %s", searchName));
Steve French6b8edfe2005-08-23 20:26:03 -07003232QInfRetry:
3233 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003234 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003235 if (rc)
3236 return rc;
3237
3238 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3239 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003240 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3241 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003242 name_len++; /* trailing null */
3243 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003244 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003245 name_len = strnlen(searchName, PATH_MAX);
3246 name_len++; /* trailing null */
3247 strncpy(pSMB->FileName, searchName, name_len);
3248 }
3249 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003250 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003251 pSMB->hdr.smb_buf_length += (__u16) name_len;
3252 pSMB->ByteCount = cpu_to_le16(name_len);
3253
3254 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003255 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003256 if (rc) {
3257 cFYI(1, ("Send error in QueryInfo = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003258 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003259 struct timespec ts;
3260 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003261
3262 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003263 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003264 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003265 ts.tv_nsec = 0;
3266 ts.tv_sec = time;
3267 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003268 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003269 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3270 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003271 pFinfo->AllocationSize =
3272 cpu_to_le64(le32_to_cpu(pSMBr->size));
3273 pFinfo->EndOfFile = pFinfo->AllocationSize;
3274 pFinfo->Attributes =
3275 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003276 } else
3277 rc = -EIO; /* bad buffer passed in */
3278
3279 cifs_buf_release(pSMB);
3280
3281 if (rc == -EAGAIN)
3282 goto QInfRetry;
3283
3284 return rc;
3285}
3286
3287
3288
3289
Linus Torvalds1da177e2005-04-16 15:20:36 -07003290int
3291CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3292 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003293 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003294 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003295 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003296{
3297/* level 263 SMB_QUERY_FILE_ALL_INFO */
3298 TRANSACTION2_QPI_REQ *pSMB = NULL;
3299 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3300 int rc = 0;
3301 int bytes_returned;
3302 int name_len;
3303 __u16 params, byte_count;
3304
3305/* cFYI(1, ("In QPathInfo path %s", searchName)); */
3306QPathInfoRetry:
3307 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3308 (void **) &pSMBr);
3309 if (rc)
3310 return rc;
3311
3312 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3313 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003314 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003315 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003316 name_len++; /* trailing null */
3317 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003318 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003319 name_len = strnlen(searchName, PATH_MAX);
3320 name_len++; /* trailing null */
3321 strncpy(pSMB->FileName, searchName, name_len);
3322 }
3323
Steve French50c2f752007-07-13 00:33:32 +00003324 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003325 pSMB->TotalDataCount = 0;
3326 pSMB->MaxParameterCount = cpu_to_le16(2);
3327 pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
3328 pSMB->MaxSetupCount = 0;
3329 pSMB->Reserved = 0;
3330 pSMB->Flags = 0;
3331 pSMB->Timeout = 0;
3332 pSMB->Reserved2 = 0;
3333 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003334 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003335 pSMB->DataCount = 0;
3336 pSMB->DataOffset = 0;
3337 pSMB->SetupCount = 1;
3338 pSMB->Reserved3 = 0;
3339 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3340 byte_count = params + 1 /* pad */ ;
3341 pSMB->TotalParameterCount = cpu_to_le16(params);
3342 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003343 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003344 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3345 else
3346 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003347 pSMB->Reserved4 = 0;
3348 pSMB->hdr.smb_buf_length += byte_count;
3349 pSMB->ByteCount = cpu_to_le16(byte_count);
3350
3351 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3352 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3353 if (rc) {
3354 cFYI(1, ("Send error in QPathInfo = %d", rc));
3355 } else { /* decode response */
3356 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3357
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003358 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3359 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003360 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003361 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003362 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003363 rc = -EIO; /* 24 or 26 expected but we do not read
3364 last field */
3365 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003366 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003367 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003368
3369 /* On legacy responses we do not read the last field,
3370 EAsize, fortunately since it varies by subdialect and
3371 also note it differs on Set vs. Get, ie two bytes or 4
3372 bytes depending but we don't care here */
3373 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003374 size = sizeof(FILE_INFO_STANDARD);
3375 else
3376 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003377 memcpy((char *) pFindData,
3378 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003379 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003380 } else
3381 rc = -ENOMEM;
3382 }
3383 cifs_buf_release(pSMB);
3384 if (rc == -EAGAIN)
3385 goto QPathInfoRetry;
3386
3387 return rc;
3388}
3389
3390int
3391CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3392 const unsigned char *searchName,
3393 FILE_UNIX_BASIC_INFO * pFindData,
Steve French737b7582005-04-28 22:41:06 -07003394 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003395{
3396/* SMB_QUERY_FILE_UNIX_BASIC */
3397 TRANSACTION2_QPI_REQ *pSMB = NULL;
3398 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3399 int rc = 0;
3400 int bytes_returned = 0;
3401 int name_len;
3402 __u16 params, byte_count;
3403
3404 cFYI(1, ("In QPathInfo (Unix) the path %s", searchName));
3405UnixQPathInfoRetry:
3406 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3407 (void **) &pSMBr);
3408 if (rc)
3409 return rc;
3410
3411 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3412 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003413 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003414 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003415 name_len++; /* trailing null */
3416 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003417 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003418 name_len = strnlen(searchName, PATH_MAX);
3419 name_len++; /* trailing null */
3420 strncpy(pSMB->FileName, searchName, name_len);
3421 }
3422
Steve French50c2f752007-07-13 00:33:32 +00003423 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003424 pSMB->TotalDataCount = 0;
3425 pSMB->MaxParameterCount = cpu_to_le16(2);
3426 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003427 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003428 pSMB->MaxSetupCount = 0;
3429 pSMB->Reserved = 0;
3430 pSMB->Flags = 0;
3431 pSMB->Timeout = 0;
3432 pSMB->Reserved2 = 0;
3433 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003434 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003435 pSMB->DataCount = 0;
3436 pSMB->DataOffset = 0;
3437 pSMB->SetupCount = 1;
3438 pSMB->Reserved3 = 0;
3439 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3440 byte_count = params + 1 /* pad */ ;
3441 pSMB->TotalParameterCount = cpu_to_le16(params);
3442 pSMB->ParameterCount = pSMB->TotalParameterCount;
3443 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3444 pSMB->Reserved4 = 0;
3445 pSMB->hdr.smb_buf_length += byte_count;
3446 pSMB->ByteCount = cpu_to_le16(byte_count);
3447
3448 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3449 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3450 if (rc) {
3451 cFYI(1, ("Send error in QPathInfo = %d", rc));
3452 } else { /* decode response */
3453 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3454
3455 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve French1e71f252007-09-20 15:30:07 +00003456 cERROR(1, ("Malformed FILE_UNIX_BASIC_INFO response.\n"
3457 "Unix Extensions can be disabled on mount "
3458 "by specifying the nosfu mount option."));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003459 rc = -EIO; /* bad smb */
3460 } else {
3461 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3462 memcpy((char *) pFindData,
3463 (char *) &pSMBr->hdr.Protocol +
3464 data_offset,
Steve French630f3f0c2007-10-25 21:17:17 +00003465 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003466 }
3467 }
3468 cifs_buf_release(pSMB);
3469 if (rc == -EAGAIN)
3470 goto UnixQPathInfoRetry;
3471
3472 return rc;
3473}
3474
Linus Torvalds1da177e2005-04-16 15:20:36 -07003475/* xid, tcon, searchName and codepage are input parms, rest are returned */
3476int
3477CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003478 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003479 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003480 __u16 *pnetfid,
3481 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003482{
3483/* level 257 SMB_ */
3484 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3485 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003486 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003487 int rc = 0;
3488 int bytes_returned = 0;
3489 int name_len;
3490 __u16 params, byte_count;
3491
Steve French50c2f752007-07-13 00:33:32 +00003492 cFYI(1, ("In FindFirst for %s", searchName));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003493
3494findFirstRetry:
3495 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3496 (void **) &pSMBr);
3497 if (rc)
3498 return rc;
3499
3500 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3501 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003502 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003503 PATH_MAX, nls_codepage, remap);
3504 /* We can not add the asterik earlier in case
3505 it got remapped to 0xF03A as if it were part of the
3506 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003507 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003508 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003509 pSMB->FileName[name_len+1] = 0;
3510 pSMB->FileName[name_len+2] = '*';
3511 pSMB->FileName[name_len+3] = 0;
3512 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003513 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3514 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003515 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516 } else { /* BB add check for overrun of SMB buf BB */
3517 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003518/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003519 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003520 free buffer exit; BB */
3521 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003522 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003523 pSMB->FileName[name_len+1] = '*';
3524 pSMB->FileName[name_len+2] = 0;
3525 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003526 }
3527
3528 params = 12 + name_len /* includes null */ ;
3529 pSMB->TotalDataCount = 0; /* no EAs */
3530 pSMB->MaxParameterCount = cpu_to_le16(10);
3531 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3532 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3533 pSMB->MaxSetupCount = 0;
3534 pSMB->Reserved = 0;
3535 pSMB->Flags = 0;
3536 pSMB->Timeout = 0;
3537 pSMB->Reserved2 = 0;
3538 byte_count = params + 1 /* pad */ ;
3539 pSMB->TotalParameterCount = cpu_to_le16(params);
3540 pSMB->ParameterCount = pSMB->TotalParameterCount;
3541 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003542 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3543 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003544 pSMB->DataCount = 0;
3545 pSMB->DataOffset = 0;
3546 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3547 pSMB->Reserved3 = 0;
3548 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3549 pSMB->SearchAttributes =
3550 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3551 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003552 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3553 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003554 CIFS_SEARCH_RETURN_RESUME);
3555 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3556
3557 /* BB what should we set StorageType to? Does it matter? BB */
3558 pSMB->SearchStorageType = 0;
3559 pSMB->hdr.smb_buf_length += byte_count;
3560 pSMB->ByteCount = cpu_to_le16(byte_count);
3561
3562 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3563 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003564 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003565
Steve French88274812006-03-09 22:21:45 +00003566 if (rc) {/* BB add logic to retry regular search if Unix search
3567 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003568 /* BB Add code to handle unsupported level rc */
3569 cFYI(1, ("Error in FindFirst = %d", rc));
Steve French1982c342005-08-17 12:38:22 -07003570
Steve French88274812006-03-09 22:21:45 +00003571 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003572
3573 /* BB eventually could optimize out free and realloc of buf */
3574 /* for this case */
3575 if (rc == -EAGAIN)
3576 goto findFirstRetry;
3577 } else { /* decode response */
3578 /* BB remember to free buffer if error BB */
3579 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003580 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003581 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
3582 psrch_inf->unicode = TRUE;
3583 else
3584 psrch_inf->unicode = FALSE;
3585
3586 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003587 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003588 psrch_inf->srch_entries_start =
3589 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003590 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003591 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3592 le16_to_cpu(pSMBr->t2.ParameterOffset));
3593
Steve French790fe572007-07-07 19:25:05 +00003594 if (parms->EndofSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003595 psrch_inf->endOfSearch = TRUE;
3596 else
3597 psrch_inf->endOfSearch = FALSE;
3598
Steve French50c2f752007-07-13 00:33:32 +00003599 psrch_inf->entries_in_buffer =
3600 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003601 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003602 psrch_inf->entries_in_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003603 *pnetfid = parms->SearchHandle;
3604 } else {
3605 cifs_buf_release(pSMB);
3606 }
3607 }
3608
3609 return rc;
3610}
3611
3612int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003613 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003614{
3615 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3616 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003617 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003618 char *response_data;
3619 int rc = 0;
3620 int bytes_returned, name_len;
3621 __u16 params, byte_count;
3622
3623 cFYI(1, ("In FindNext"));
3624
Steve French790fe572007-07-07 19:25:05 +00003625 if (psrch_inf->endOfSearch == TRUE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003626 return -ENOENT;
3627
3628 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3629 (void **) &pSMBr);
3630 if (rc)
3631 return rc;
3632
Steve French50c2f752007-07-13 00:33:32 +00003633 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003634 byte_count = 0;
3635 pSMB->TotalDataCount = 0; /* no EAs */
3636 pSMB->MaxParameterCount = cpu_to_le16(8);
3637 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003638 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3639 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003640 pSMB->MaxSetupCount = 0;
3641 pSMB->Reserved = 0;
3642 pSMB->Flags = 0;
3643 pSMB->Timeout = 0;
3644 pSMB->Reserved2 = 0;
3645 pSMB->ParameterOffset = cpu_to_le16(
3646 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3647 pSMB->DataCount = 0;
3648 pSMB->DataOffset = 0;
3649 pSMB->SetupCount = 1;
3650 pSMB->Reserved3 = 0;
3651 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3652 pSMB->SearchHandle = searchHandle; /* always kept as le */
3653 pSMB->SearchCount =
Steve French630f3f0c2007-10-25 21:17:17 +00003654 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003655 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3656 pSMB->ResumeKey = psrch_inf->resume_key;
3657 pSMB->SearchFlags =
3658 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3659
3660 name_len = psrch_inf->resume_name_len;
3661 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003662 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003663 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3664 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003665 /* 14 byte parm len above enough for 2 byte null terminator */
3666 pSMB->ResumeFileName[name_len] = 0;
3667 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003668 } else {
3669 rc = -EINVAL;
3670 goto FNext2_err_exit;
3671 }
3672 byte_count = params + 1 /* pad */ ;
3673 pSMB->TotalParameterCount = cpu_to_le16(params);
3674 pSMB->ParameterCount = pSMB->TotalParameterCount;
3675 pSMB->hdr.smb_buf_length += byte_count;
3676 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003677
Linus Torvalds1da177e2005-04-16 15:20:36 -07003678 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3679 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003680 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003681 if (rc) {
3682 if (rc == -EBADF) {
3683 psrch_inf->endOfSearch = TRUE;
Steve French50c2f752007-07-13 00:33:32 +00003684 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003685 } else
3686 cFYI(1, ("FindNext returned = %d", rc));
3687 } else { /* decode response */
3688 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003689
Steve French790fe572007-07-07 19:25:05 +00003690 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003691 /* BB fixme add lock for file (srch_info) struct here */
3692 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
3693 psrch_inf->unicode = TRUE;
3694 else
3695 psrch_inf->unicode = FALSE;
3696 response_data = (char *) &pSMBr->hdr.Protocol +
3697 le16_to_cpu(pSMBr->t2.ParameterOffset);
3698 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3699 response_data = (char *)&pSMBr->hdr.Protocol +
3700 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003701 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003702 cifs_small_buf_release(
3703 psrch_inf->ntwrk_buf_start);
3704 else
3705 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003706 psrch_inf->srch_entries_start = response_data;
3707 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003708 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003709 if (parms->EndofSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003710 psrch_inf->endOfSearch = TRUE;
3711 else
3712 psrch_inf->endOfSearch = FALSE;
Steve French50c2f752007-07-13 00:33:32 +00003713 psrch_inf->entries_in_buffer =
3714 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003715 psrch_inf->index_of_last_entry +=
3716 psrch_inf->entries_in_buffer;
Steve French50c2f752007-07-13 00:33:32 +00003717/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",
3718 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719
3720 /* BB fixme add unlock here */
3721 }
3722
3723 }
3724
3725 /* BB On error, should we leave previous search buf (and count and
3726 last entry fields) intact or free the previous one? */
3727
3728 /* Note: On -EAGAIN error only caller can retry on handle based calls
3729 since file handle passed in no longer valid */
3730FNext2_err_exit:
3731 if (rc != 0)
3732 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733 return rc;
3734}
3735
3736int
Steve French50c2f752007-07-13 00:33:32 +00003737CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3738 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003739{
3740 int rc = 0;
3741 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003742
3743 cFYI(1, ("In CIFSSMBFindClose"));
3744 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3745
3746 /* no sense returning error if session restarted
3747 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003748 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003749 return 0;
3750 if (rc)
3751 return rc;
3752
Linus Torvalds1da177e2005-04-16 15:20:36 -07003753 pSMB->FileID = searchHandle;
3754 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003755 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003756 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003757 cERROR(1, ("Send error in FindClose = %d", rc));
Steve Frenchad7a2922008-02-07 23:25:02 +00003758
Steve Frencha4544342005-08-24 13:59:35 -07003759 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003760
3761 /* Since session is dead, search handle closed on server already */
3762 if (rc == -EAGAIN)
3763 rc = 0;
3764
3765 return rc;
3766}
3767
Linus Torvalds1da177e2005-04-16 15:20:36 -07003768int
3769CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003770 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003771 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003772 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003773{
3774 int rc = 0;
3775 TRANSACTION2_QPI_REQ *pSMB = NULL;
3776 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3777 int name_len, bytes_returned;
3778 __u16 params, byte_count;
3779
Steve French50c2f752007-07-13 00:33:32 +00003780 cFYI(1, ("In GetSrvInodeNum for %s", searchName));
Steve French790fe572007-07-07 19:25:05 +00003781 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003782 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003783
3784GetInodeNumberRetry:
3785 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003786 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003787 if (rc)
3788 return rc;
3789
Linus Torvalds1da177e2005-04-16 15:20:36 -07003790 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3791 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003792 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003793 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003794 name_len++; /* trailing null */
3795 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003796 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003797 name_len = strnlen(searchName, PATH_MAX);
3798 name_len++; /* trailing null */
3799 strncpy(pSMB->FileName, searchName, name_len);
3800 }
3801
3802 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
3803 pSMB->TotalDataCount = 0;
3804 pSMB->MaxParameterCount = cpu_to_le16(2);
3805 /* BB find exact max data count below from sess structure BB */
3806 pSMB->MaxDataCount = cpu_to_le16(4000);
3807 pSMB->MaxSetupCount = 0;
3808 pSMB->Reserved = 0;
3809 pSMB->Flags = 0;
3810 pSMB->Timeout = 0;
3811 pSMB->Reserved2 = 0;
3812 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003813 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003814 pSMB->DataCount = 0;
3815 pSMB->DataOffset = 0;
3816 pSMB->SetupCount = 1;
3817 pSMB->Reserved3 = 0;
3818 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3819 byte_count = params + 1 /* pad */ ;
3820 pSMB->TotalParameterCount = cpu_to_le16(params);
3821 pSMB->ParameterCount = pSMB->TotalParameterCount;
3822 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
3823 pSMB->Reserved4 = 0;
3824 pSMB->hdr.smb_buf_length += byte_count;
3825 pSMB->ByteCount = cpu_to_le16(byte_count);
3826
3827 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3828 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3829 if (rc) {
3830 cFYI(1, ("error %d in QueryInternalInfo", rc));
3831 } else {
3832 /* decode response */
3833 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3834 if (rc || (pSMBr->ByteCount < 2))
3835 /* BB also check enough total bytes returned */
3836 /* If rc should we check for EOPNOSUPP and
3837 disable the srvino flag? or in caller? */
3838 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00003839 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003840 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3841 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00003842 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003843 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00003844 if (count < 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003845 cFYI(1, ("Illegal size ret in QryIntrnlInf"));
3846 rc = -EIO;
3847 goto GetInodeNumOut;
3848 }
3849 pfinfo = (struct file_internal_info *)
3850 (data_offset + (char *) &pSMBr->hdr.Protocol);
3851 *inode_number = pfinfo->UniqueId;
3852 }
3853 }
3854GetInodeNumOut:
3855 cifs_buf_release(pSMB);
3856 if (rc == -EAGAIN)
3857 goto GetInodeNumberRetry;
3858 return rc;
3859}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003860
3861int
3862CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
3863 const unsigned char *searchName,
3864 unsigned char **targetUNCs,
3865 unsigned int *number_of_UNC_in_array,
Steve French737b7582005-04-28 22:41:06 -07003866 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003867{
3868/* TRANS2_GET_DFS_REFERRAL */
3869 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
3870 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00003871 struct dfs_referral_level_3 *referrals = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003872 int rc = 0;
3873 int bytes_returned;
3874 int name_len;
3875 unsigned int i;
Steve French50c2f752007-07-13 00:33:32 +00003876 char *temp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003877 __u16 params, byte_count;
3878 *number_of_UNC_in_array = 0;
3879 *targetUNCs = NULL;
3880
3881 cFYI(1, ("In GetDFSRefer the path %s", searchName));
3882 if (ses == NULL)
3883 return -ENODEV;
3884getDFSRetry:
3885 rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
3886 (void **) &pSMBr);
3887 if (rc)
3888 return rc;
Steve French50c2f752007-07-13 00:33:32 +00003889
3890 /* server pointer checked in called function,
Steve French1982c342005-08-17 12:38:22 -07003891 but should never be null here anyway */
3892 pSMB->hdr.Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003893 pSMB->hdr.Tid = ses->ipc_tid;
3894 pSMB->hdr.Uid = ses->Suid;
Steve French26f57362007-08-30 22:09:15 +00003895 if (ses->capabilities & CAP_STATUS32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003896 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
Steve French26f57362007-08-30 22:09:15 +00003897 if (ses->capabilities & CAP_DFS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003898 pSMB->hdr.Flags2 |= SMBFLG2_DFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003899
3900 if (ses->capabilities & CAP_UNICODE) {
3901 pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
3902 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003903 cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
Steve French737b7582005-04-28 22:41:06 -07003904 searchName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003905 name_len++; /* trailing null */
3906 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003907 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003908 name_len = strnlen(searchName, PATH_MAX);
3909 name_len++; /* trailing null */
3910 strncpy(pSMB->RequestFileName, searchName, name_len);
3911 }
3912
Steve French790fe572007-07-07 19:25:05 +00003913 if (ses->server) {
3914 if (ses->server->secMode &
Steve French1a4e15a2006-10-12 21:33:51 +00003915 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
3916 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
3917 }
3918
Steve French50c2f752007-07-13 00:33:32 +00003919 pSMB->hdr.Uid = ses->Suid;
Steve French1a4e15a2006-10-12 21:33:51 +00003920
Linus Torvalds1da177e2005-04-16 15:20:36 -07003921 params = 2 /* level */ + name_len /*includes null */ ;
3922 pSMB->TotalDataCount = 0;
3923 pSMB->DataCount = 0;
3924 pSMB->DataOffset = 0;
3925 pSMB->MaxParameterCount = 0;
3926 pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
3927 pSMB->MaxSetupCount = 0;
3928 pSMB->Reserved = 0;
3929 pSMB->Flags = 0;
3930 pSMB->Timeout = 0;
3931 pSMB->Reserved2 = 0;
3932 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003933 struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003934 pSMB->SetupCount = 1;
3935 pSMB->Reserved3 = 0;
3936 pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
3937 byte_count = params + 3 /* pad */ ;
3938 pSMB->ParameterCount = cpu_to_le16(params);
3939 pSMB->TotalParameterCount = pSMB->ParameterCount;
3940 pSMB->MaxReferralLevel = cpu_to_le16(3);
3941 pSMB->hdr.smb_buf_length += byte_count;
3942 pSMB->ByteCount = cpu_to_le16(byte_count);
3943
3944 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
3945 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3946 if (rc) {
3947 cFYI(1, ("Send error in GetDFSRefer = %d", rc));
3948 } else { /* decode response */
3949/* BB Add logic to parse referrals here */
3950 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3951
Steve French50c2f752007-07-13 00:33:32 +00003952 /* BB Also check if enough total bytes returned? */
3953 if (rc || (pSMBr->ByteCount < 17))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003954 rc = -EIO; /* bad smb */
3955 else {
Steve French50c2f752007-07-13 00:33:32 +00003956 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003957 __u16 data_count = le16_to_cpu(pSMBr->t2.DataCount);
3958
3959 cFYI(1,
Steve French50c2f752007-07-13 00:33:32 +00003960 ("Decoding GetDFSRefer response BCC: %d Offset %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003961 pSMBr->ByteCount, data_offset));
Steve French50c2f752007-07-13 00:33:32 +00003962 referrals =
3963 (struct dfs_referral_level_3 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003964 (8 /* sizeof start of data block */ +
3965 data_offset +
Steve French50c2f752007-07-13 00:33:32 +00003966 (char *) &pSMBr->hdr.Protocol);
Steve Frenchc18c8422007-07-18 23:21:09 +00003967 cFYI(1, ("num_referrals: %d dfs flags: 0x%x ... \n"
Steve French63135e02007-07-17 17:34:02 +00003968 "for referral one refer size: 0x%x srv "
3969 "type: 0x%x refer flags: 0x%x ttl: 0x%x",
Steve French50c2f752007-07-13 00:33:32 +00003970 le16_to_cpu(pSMBr->NumberOfReferrals),
3971 le16_to_cpu(pSMBr->DFSFlags),
3972 le16_to_cpu(referrals->ReferralSize),
3973 le16_to_cpu(referrals->ServerType),
3974 le16_to_cpu(referrals->ReferralFlags),
3975 le16_to_cpu(referrals->TimeToLive)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003976 /* BB This field is actually two bytes in from start of
3977 data block so we could do safety check that DataBlock
3978 begins at address of pSMBr->NumberOfReferrals */
Steve French50c2f752007-07-13 00:33:32 +00003979 *number_of_UNC_in_array =
3980 le16_to_cpu(pSMBr->NumberOfReferrals);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003981
3982 /* BB Fix below so can return more than one referral */
Steve French790fe572007-07-07 19:25:05 +00003983 if (*number_of_UNC_in_array > 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003984 *number_of_UNC_in_array = 1;
3985
3986 /* get the length of the strings describing refs */
3987 name_len = 0;
Steve French50c2f752007-07-13 00:33:32 +00003988 for (i = 0; i < *number_of_UNC_in_array; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003989 /* make sure that DfsPathOffset not past end */
Steve French50c2f752007-07-13 00:33:32 +00003990 __u16 offset =
3991 le16_to_cpu(referrals->DfsPathOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003992 if (offset > data_count) {
Steve French50c2f752007-07-13 00:33:32 +00003993 /* if invalid referral, stop here and do
Linus Torvalds1da177e2005-04-16 15:20:36 -07003994 not try to copy any more */
3995 *number_of_UNC_in_array = i;
3996 break;
Steve French50c2f752007-07-13 00:33:32 +00003997 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003998 temp = ((char *)referrals) + offset;
3999
4000 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00004001 name_len += UniStrnlen((wchar_t *)temp,
4002 data_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004003 } else {
Steve French50c2f752007-07-13 00:33:32 +00004004 name_len += strnlen(temp, data_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004005 }
4006 referrals++;
Steve French50c2f752007-07-13 00:33:32 +00004007 /* BB add check that referral pointer does
4008 not fall off end PDU */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004009 }
4010 /* BB add check for name_len bigger than bcc */
Steve French50c2f752007-07-13 00:33:32 +00004011 *targetUNCs =
4012 kmalloc(name_len+1+(*number_of_UNC_in_array),
4013 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00004014 if (*targetUNCs == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004015 rc = -ENOMEM;
4016 goto GetDFSRefExit;
4017 }
4018 /* copy the ref strings */
Steve French50c2f752007-07-13 00:33:32 +00004019 referrals = (struct dfs_referral_level_3 *)
4020 (8 /* sizeof data hdr */ + data_offset +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004021 (char *) &pSMBr->hdr.Protocol);
4022
Steve French50c2f752007-07-13 00:33:32 +00004023 for (i = 0; i < *number_of_UNC_in_array; i++) {
4024 temp = ((char *)referrals) +
4025 le16_to_cpu(referrals->DfsPathOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004026 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
4027 cifs_strfromUCS_le(*targetUNCs,
Steve French50c2f752007-07-13 00:33:32 +00004028 (__le16 *) temp,
4029 name_len,
4030 nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004031 } else {
Steve French50c2f752007-07-13 00:33:32 +00004032 strncpy(*targetUNCs, temp, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004033 }
4034 /* BB update target_uncs pointers */
4035 referrals++;
4036 }
4037 temp = *targetUNCs;
4038 temp[name_len] = 0;
4039 }
4040
4041 }
4042GetDFSRefExit:
4043 if (pSMB)
4044 cifs_buf_release(pSMB);
4045
4046 if (rc == -EAGAIN)
4047 goto getDFSRetry;
4048
4049 return rc;
4050}
4051
Steve French20962432005-09-21 22:05:57 -07004052/* Query File System Info such as free space to old servers such as Win 9x */
4053int
4054SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4055{
4056/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4057 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4058 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4059 FILE_SYSTEM_ALLOC_INFO *response_data;
4060 int rc = 0;
4061 int bytes_returned = 0;
4062 __u16 params, byte_count;
4063
4064 cFYI(1, ("OldQFSInfo"));
4065oldQFSInfoRetry:
4066 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4067 (void **) &pSMBr);
4068 if (rc)
4069 return rc;
Steve French20962432005-09-21 22:05:57 -07004070
4071 params = 2; /* level */
4072 pSMB->TotalDataCount = 0;
4073 pSMB->MaxParameterCount = cpu_to_le16(2);
4074 pSMB->MaxDataCount = cpu_to_le16(1000);
4075 pSMB->MaxSetupCount = 0;
4076 pSMB->Reserved = 0;
4077 pSMB->Flags = 0;
4078 pSMB->Timeout = 0;
4079 pSMB->Reserved2 = 0;
4080 byte_count = params + 1 /* pad */ ;
4081 pSMB->TotalParameterCount = cpu_to_le16(params);
4082 pSMB->ParameterCount = pSMB->TotalParameterCount;
4083 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4084 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4085 pSMB->DataCount = 0;
4086 pSMB->DataOffset = 0;
4087 pSMB->SetupCount = 1;
4088 pSMB->Reserved3 = 0;
4089 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4090 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4091 pSMB->hdr.smb_buf_length += byte_count;
4092 pSMB->ByteCount = cpu_to_le16(byte_count);
4093
4094 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4095 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4096 if (rc) {
4097 cFYI(1, ("Send error in QFSInfo = %d", rc));
4098 } else { /* decode response */
4099 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4100
4101 if (rc || (pSMBr->ByteCount < 18))
4102 rc = -EIO; /* bad smb */
4103 else {
4104 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00004105 cFYI(1, ("qfsinf resp BCC: %d Offset %d",
Steve French20962432005-09-21 22:05:57 -07004106 pSMBr->ByteCount, data_offset));
4107
Steve French50c2f752007-07-13 00:33:32 +00004108 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004109 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4110 FSData->f_bsize =
4111 le16_to_cpu(response_data->BytesPerSector) *
4112 le32_to_cpu(response_data->
4113 SectorsPerAllocationUnit);
4114 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004115 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004116 FSData->f_bfree = FSData->f_bavail =
4117 le32_to_cpu(response_data->FreeAllocationUnits);
4118 cFYI(1,
4119 ("Blocks: %lld Free: %lld Block size %ld",
4120 (unsigned long long)FSData->f_blocks,
4121 (unsigned long long)FSData->f_bfree,
4122 FSData->f_bsize));
4123 }
4124 }
4125 cifs_buf_release(pSMB);
4126
4127 if (rc == -EAGAIN)
4128 goto oldQFSInfoRetry;
4129
4130 return rc;
4131}
4132
Linus Torvalds1da177e2005-04-16 15:20:36 -07004133int
Steve French737b7582005-04-28 22:41:06 -07004134CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004135{
4136/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4137 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4138 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4139 FILE_SYSTEM_INFO *response_data;
4140 int rc = 0;
4141 int bytes_returned = 0;
4142 __u16 params, byte_count;
4143
4144 cFYI(1, ("In QFSInfo"));
4145QFSInfoRetry:
4146 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4147 (void **) &pSMBr);
4148 if (rc)
4149 return rc;
4150
4151 params = 2; /* level */
4152 pSMB->TotalDataCount = 0;
4153 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004154 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004155 pSMB->MaxSetupCount = 0;
4156 pSMB->Reserved = 0;
4157 pSMB->Flags = 0;
4158 pSMB->Timeout = 0;
4159 pSMB->Reserved2 = 0;
4160 byte_count = params + 1 /* pad */ ;
4161 pSMB->TotalParameterCount = cpu_to_le16(params);
4162 pSMB->ParameterCount = pSMB->TotalParameterCount;
4163 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004164 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004165 pSMB->DataCount = 0;
4166 pSMB->DataOffset = 0;
4167 pSMB->SetupCount = 1;
4168 pSMB->Reserved3 = 0;
4169 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4170 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4171 pSMB->hdr.smb_buf_length += byte_count;
4172 pSMB->ByteCount = cpu_to_le16(byte_count);
4173
4174 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4175 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4176 if (rc) {
Steve French20962432005-09-21 22:05:57 -07004177 cFYI(1, ("Send error in QFSInfo = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004178 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004179 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004180
Steve French20962432005-09-21 22:05:57 -07004181 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004182 rc = -EIO; /* bad smb */
4183 else {
4184 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004185
4186 response_data =
4187 (FILE_SYSTEM_INFO
4188 *) (((char *) &pSMBr->hdr.Protocol) +
4189 data_offset);
4190 FSData->f_bsize =
4191 le32_to_cpu(response_data->BytesPerSector) *
4192 le32_to_cpu(response_data->
4193 SectorsPerAllocationUnit);
4194 FSData->f_blocks =
4195 le64_to_cpu(response_data->TotalAllocationUnits);
4196 FSData->f_bfree = FSData->f_bavail =
4197 le64_to_cpu(response_data->FreeAllocationUnits);
4198 cFYI(1,
4199 ("Blocks: %lld Free: %lld Block size %ld",
4200 (unsigned long long)FSData->f_blocks,
4201 (unsigned long long)FSData->f_bfree,
4202 FSData->f_bsize));
4203 }
4204 }
4205 cifs_buf_release(pSMB);
4206
4207 if (rc == -EAGAIN)
4208 goto QFSInfoRetry;
4209
4210 return rc;
4211}
4212
4213int
Steve French737b7582005-04-28 22:41:06 -07004214CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004215{
4216/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4217 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4218 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4219 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4220 int rc = 0;
4221 int bytes_returned = 0;
4222 __u16 params, byte_count;
4223
4224 cFYI(1, ("In QFSAttributeInfo"));
4225QFSAttributeRetry:
4226 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4227 (void **) &pSMBr);
4228 if (rc)
4229 return rc;
4230
4231 params = 2; /* level */
4232 pSMB->TotalDataCount = 0;
4233 pSMB->MaxParameterCount = cpu_to_le16(2);
4234 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
4235 pSMB->MaxSetupCount = 0;
4236 pSMB->Reserved = 0;
4237 pSMB->Flags = 0;
4238 pSMB->Timeout = 0;
4239 pSMB->Reserved2 = 0;
4240 byte_count = params + 1 /* pad */ ;
4241 pSMB->TotalParameterCount = cpu_to_le16(params);
4242 pSMB->ParameterCount = pSMB->TotalParameterCount;
4243 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004244 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004245 pSMB->DataCount = 0;
4246 pSMB->DataOffset = 0;
4247 pSMB->SetupCount = 1;
4248 pSMB->Reserved3 = 0;
4249 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4250 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4251 pSMB->hdr.smb_buf_length += byte_count;
4252 pSMB->ByteCount = cpu_to_le16(byte_count);
4253
4254 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4255 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4256 if (rc) {
4257 cERROR(1, ("Send error in QFSAttributeInfo = %d", rc));
4258 } else { /* decode response */
4259 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4260
Steve French50c2f752007-07-13 00:33:32 +00004261 if (rc || (pSMBr->ByteCount < 13)) {
4262 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004263 rc = -EIO; /* bad smb */
4264 } else {
4265 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4266 response_data =
4267 (FILE_SYSTEM_ATTRIBUTE_INFO
4268 *) (((char *) &pSMBr->hdr.Protocol) +
4269 data_offset);
4270 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004271 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004272 }
4273 }
4274 cifs_buf_release(pSMB);
4275
4276 if (rc == -EAGAIN)
4277 goto QFSAttributeRetry;
4278
4279 return rc;
4280}
4281
4282int
Steve French737b7582005-04-28 22:41:06 -07004283CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004284{
4285/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4286 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4287 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4288 FILE_SYSTEM_DEVICE_INFO *response_data;
4289 int rc = 0;
4290 int bytes_returned = 0;
4291 __u16 params, byte_count;
4292
4293 cFYI(1, ("In QFSDeviceInfo"));
4294QFSDeviceRetry:
4295 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4296 (void **) &pSMBr);
4297 if (rc)
4298 return rc;
4299
4300 params = 2; /* level */
4301 pSMB->TotalDataCount = 0;
4302 pSMB->MaxParameterCount = cpu_to_le16(2);
4303 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
4304 pSMB->MaxSetupCount = 0;
4305 pSMB->Reserved = 0;
4306 pSMB->Flags = 0;
4307 pSMB->Timeout = 0;
4308 pSMB->Reserved2 = 0;
4309 byte_count = params + 1 /* pad */ ;
4310 pSMB->TotalParameterCount = cpu_to_le16(params);
4311 pSMB->ParameterCount = pSMB->TotalParameterCount;
4312 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004313 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004314
4315 pSMB->DataCount = 0;
4316 pSMB->DataOffset = 0;
4317 pSMB->SetupCount = 1;
4318 pSMB->Reserved3 = 0;
4319 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4320 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4321 pSMB->hdr.smb_buf_length += byte_count;
4322 pSMB->ByteCount = cpu_to_le16(byte_count);
4323
4324 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4325 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4326 if (rc) {
4327 cFYI(1, ("Send error in QFSDeviceInfo = %d", rc));
4328 } else { /* decode response */
4329 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4330
Steve French630f3f0c2007-10-25 21:17:17 +00004331 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004332 rc = -EIO; /* bad smb */
4333 else {
4334 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4335 response_data =
Steve French737b7582005-04-28 22:41:06 -07004336 (FILE_SYSTEM_DEVICE_INFO *)
4337 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004338 data_offset);
4339 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004340 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004341 }
4342 }
4343 cifs_buf_release(pSMB);
4344
4345 if (rc == -EAGAIN)
4346 goto QFSDeviceRetry;
4347
4348 return rc;
4349}
4350
4351int
Steve French737b7582005-04-28 22:41:06 -07004352CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004353{
4354/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4355 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4356 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4357 FILE_SYSTEM_UNIX_INFO *response_data;
4358 int rc = 0;
4359 int bytes_returned = 0;
4360 __u16 params, byte_count;
4361
4362 cFYI(1, ("In QFSUnixInfo"));
4363QFSUnixRetry:
4364 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4365 (void **) &pSMBr);
4366 if (rc)
4367 return rc;
4368
4369 params = 2; /* level */
4370 pSMB->TotalDataCount = 0;
4371 pSMB->DataCount = 0;
4372 pSMB->DataOffset = 0;
4373 pSMB->MaxParameterCount = cpu_to_le16(2);
4374 pSMB->MaxDataCount = cpu_to_le16(100); /* BB find exact max SMB PDU from sess structure BB */
4375 pSMB->MaxSetupCount = 0;
4376 pSMB->Reserved = 0;
4377 pSMB->Flags = 0;
4378 pSMB->Timeout = 0;
4379 pSMB->Reserved2 = 0;
4380 byte_count = params + 1 /* pad */ ;
4381 pSMB->ParameterCount = cpu_to_le16(params);
4382 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004383 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4384 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004385 pSMB->SetupCount = 1;
4386 pSMB->Reserved3 = 0;
4387 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4388 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4389 pSMB->hdr.smb_buf_length += byte_count;
4390 pSMB->ByteCount = cpu_to_le16(byte_count);
4391
4392 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4393 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4394 if (rc) {
4395 cERROR(1, ("Send error in QFSUnixInfo = %d", rc));
4396 } else { /* decode response */
4397 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4398
4399 if (rc || (pSMBr->ByteCount < 13)) {
4400 rc = -EIO; /* bad smb */
4401 } else {
4402 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4403 response_data =
4404 (FILE_SYSTEM_UNIX_INFO
4405 *) (((char *) &pSMBr->hdr.Protocol) +
4406 data_offset);
4407 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004408 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004409 }
4410 }
4411 cifs_buf_release(pSMB);
4412
4413 if (rc == -EAGAIN)
4414 goto QFSUnixRetry;
4415
4416
4417 return rc;
4418}
4419
Jeremy Allisonac670552005-06-22 17:26:35 -07004420int
Steve French45abc6e2005-06-23 13:42:03 -05004421CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004422{
4423/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4424 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4425 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4426 int rc = 0;
4427 int bytes_returned = 0;
4428 __u16 params, param_offset, offset, byte_count;
4429
4430 cFYI(1, ("In SETFSUnixInfo"));
4431SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004432 /* BB switch to small buf init to save memory */
Jeremy Allisonac670552005-06-22 17:26:35 -07004433 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4434 (void **) &pSMBr);
4435 if (rc)
4436 return rc;
4437
4438 params = 4; /* 2 bytes zero followed by info level. */
4439 pSMB->MaxSetupCount = 0;
4440 pSMB->Reserved = 0;
4441 pSMB->Flags = 0;
4442 pSMB->Timeout = 0;
4443 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004444 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4445 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004446 offset = param_offset + params;
4447
4448 pSMB->MaxParameterCount = cpu_to_le16(4);
4449 pSMB->MaxDataCount = cpu_to_le16(100); /* BB find exact max SMB PDU from sess structure BB */
4450 pSMB->SetupCount = 1;
4451 pSMB->Reserved3 = 0;
4452 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4453 byte_count = 1 /* pad */ + params + 12;
4454
4455 pSMB->DataCount = cpu_to_le16(12);
4456 pSMB->ParameterCount = cpu_to_le16(params);
4457 pSMB->TotalDataCount = pSMB->DataCount;
4458 pSMB->TotalParameterCount = pSMB->ParameterCount;
4459 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4460 pSMB->DataOffset = cpu_to_le16(offset);
4461
4462 /* Params. */
4463 pSMB->FileNum = 0;
4464 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4465
4466 /* Data. */
4467 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4468 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4469 pSMB->ClientUnixCap = cpu_to_le64(cap);
4470
4471 pSMB->hdr.smb_buf_length += byte_count;
4472 pSMB->ByteCount = cpu_to_le16(byte_count);
4473
4474 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4475 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4476 if (rc) {
4477 cERROR(1, ("Send error in SETFSUnixInfo = %d", rc));
4478 } else { /* decode response */
4479 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004480 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004481 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004482 }
4483 cifs_buf_release(pSMB);
4484
4485 if (rc == -EAGAIN)
4486 goto SETFSUnixRetry;
4487
4488 return rc;
4489}
4490
4491
Linus Torvalds1da177e2005-04-16 15:20:36 -07004492
4493int
4494CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004495 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004496{
4497/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4498 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4499 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4500 FILE_SYSTEM_POSIX_INFO *response_data;
4501 int rc = 0;
4502 int bytes_returned = 0;
4503 __u16 params, byte_count;
4504
4505 cFYI(1, ("In QFSPosixInfo"));
4506QFSPosixRetry:
4507 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4508 (void **) &pSMBr);
4509 if (rc)
4510 return rc;
4511
4512 params = 2; /* level */
4513 pSMB->TotalDataCount = 0;
4514 pSMB->DataCount = 0;
4515 pSMB->DataOffset = 0;
4516 pSMB->MaxParameterCount = cpu_to_le16(2);
4517 pSMB->MaxDataCount = cpu_to_le16(100); /* BB find exact max SMB PDU from sess structure BB */
4518 pSMB->MaxSetupCount = 0;
4519 pSMB->Reserved = 0;
4520 pSMB->Flags = 0;
4521 pSMB->Timeout = 0;
4522 pSMB->Reserved2 = 0;
4523 byte_count = params + 1 /* pad */ ;
4524 pSMB->ParameterCount = cpu_to_le16(params);
4525 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004526 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4527 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004528 pSMB->SetupCount = 1;
4529 pSMB->Reserved3 = 0;
4530 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4531 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4532 pSMB->hdr.smb_buf_length += byte_count;
4533 pSMB->ByteCount = cpu_to_le16(byte_count);
4534
4535 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4536 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4537 if (rc) {
4538 cFYI(1, ("Send error in QFSUnixInfo = %d", rc));
4539 } else { /* decode response */
4540 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4541
4542 if (rc || (pSMBr->ByteCount < 13)) {
4543 rc = -EIO; /* bad smb */
4544 } else {
4545 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4546 response_data =
4547 (FILE_SYSTEM_POSIX_INFO
4548 *) (((char *) &pSMBr->hdr.Protocol) +
4549 data_offset);
4550 FSData->f_bsize =
4551 le32_to_cpu(response_data->BlockSize);
4552 FSData->f_blocks =
4553 le64_to_cpu(response_data->TotalBlocks);
4554 FSData->f_bfree =
4555 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004556 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004557 FSData->f_bavail = FSData->f_bfree;
4558 } else {
4559 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004560 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004561 }
Steve French790fe572007-07-07 19:25:05 +00004562 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004563 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004564 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004565 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004566 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004567 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004568 }
4569 }
4570 cifs_buf_release(pSMB);
4571
4572 if (rc == -EAGAIN)
4573 goto QFSPosixRetry;
4574
4575 return rc;
4576}
4577
4578
Steve French50c2f752007-07-13 00:33:32 +00004579/* We can not use write of zero bytes trick to
4580 set file size due to need for large file support. Also note that
4581 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004582 routine which is only needed to work around a sharing violation bug
4583 in Samba which this routine can run into */
4584
4585int
4586CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00004587 __u64 size, int SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004588 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004589{
4590 struct smb_com_transaction2_spi_req *pSMB = NULL;
4591 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4592 struct file_end_of_file_info *parm_data;
4593 int name_len;
4594 int rc = 0;
4595 int bytes_returned = 0;
4596 __u16 params, byte_count, data_count, param_offset, offset;
4597
4598 cFYI(1, ("In SetEOF"));
4599SetEOFRetry:
4600 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4601 (void **) &pSMBr);
4602 if (rc)
4603 return rc;
4604
4605 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4606 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004607 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004608 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004609 name_len++; /* trailing null */
4610 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004611 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004612 name_len = strnlen(fileName, PATH_MAX);
4613 name_len++; /* trailing null */
4614 strncpy(pSMB->FileName, fileName, name_len);
4615 }
4616 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004617 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004618 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004619 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004620 pSMB->MaxSetupCount = 0;
4621 pSMB->Reserved = 0;
4622 pSMB->Flags = 0;
4623 pSMB->Timeout = 0;
4624 pSMB->Reserved2 = 0;
4625 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004626 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004627 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004628 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004629 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4630 pSMB->InformationLevel =
4631 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4632 else
4633 pSMB->InformationLevel =
4634 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4635 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004636 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4637 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004638 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004639 else
4640 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004641 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004642 }
4643
4644 parm_data =
4645 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4646 offset);
4647 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4648 pSMB->DataOffset = cpu_to_le16(offset);
4649 pSMB->SetupCount = 1;
4650 pSMB->Reserved3 = 0;
4651 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4652 byte_count = 3 /* pad */ + params + data_count;
4653 pSMB->DataCount = cpu_to_le16(data_count);
4654 pSMB->TotalDataCount = pSMB->DataCount;
4655 pSMB->ParameterCount = cpu_to_le16(params);
4656 pSMB->TotalParameterCount = pSMB->ParameterCount;
4657 pSMB->Reserved4 = 0;
4658 pSMB->hdr.smb_buf_length += byte_count;
4659 parm_data->FileSize = cpu_to_le64(size);
4660 pSMB->ByteCount = cpu_to_le16(byte_count);
4661 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4662 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004663 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004664 cFYI(1, ("SetPathInfo (file size) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004665
4666 cifs_buf_release(pSMB);
4667
4668 if (rc == -EAGAIN)
4669 goto SetEOFRetry;
4670
4671 return rc;
4672}
4673
4674int
Steve French50c2f752007-07-13 00:33:32 +00004675CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
4676 __u16 fid, __u32 pid_of_opener, int SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004677{
4678 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004679 char *data_offset;
4680 struct file_end_of_file_info *parm_data;
4681 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004682 __u16 params, param_offset, offset, byte_count, count;
4683
4684 cFYI(1, ("SetFileSize (via SetFileInfo) %lld",
4685 (long long)size));
Steve Frenchcd634992005-04-28 22:41:10 -07004686 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4687
Linus Torvalds1da177e2005-04-16 15:20:36 -07004688 if (rc)
4689 return rc;
4690
4691 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4692 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004693
Linus Torvalds1da177e2005-04-16 15:20:36 -07004694 params = 6;
4695 pSMB->MaxSetupCount = 0;
4696 pSMB->Reserved = 0;
4697 pSMB->Flags = 0;
4698 pSMB->Timeout = 0;
4699 pSMB->Reserved2 = 0;
4700 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4701 offset = param_offset + params;
4702
Steve French50c2f752007-07-13 00:33:32 +00004703 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004704
4705 count = sizeof(struct file_end_of_file_info);
4706 pSMB->MaxParameterCount = cpu_to_le16(2);
4707 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB PDU from sess */
4708 pSMB->SetupCount = 1;
4709 pSMB->Reserved3 = 0;
4710 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4711 byte_count = 3 /* pad */ + params + count;
4712 pSMB->DataCount = cpu_to_le16(count);
4713 pSMB->ParameterCount = cpu_to_le16(params);
4714 pSMB->TotalDataCount = pSMB->DataCount;
4715 pSMB->TotalParameterCount = pSMB->ParameterCount;
4716 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4717 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004718 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4719 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004720 pSMB->DataOffset = cpu_to_le16(offset);
4721 parm_data->FileSize = cpu_to_le64(size);
4722 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004723 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004724 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4725 pSMB->InformationLevel =
4726 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4727 else
4728 pSMB->InformationLevel =
4729 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004730 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004731 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4732 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004733 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004734 else
4735 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004736 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004737 }
4738 pSMB->Reserved4 = 0;
4739 pSMB->hdr.smb_buf_length += byte_count;
4740 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004741 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004742 if (rc) {
4743 cFYI(1,
4744 ("Send error in SetFileInfo (SetFileSize) = %d",
4745 rc));
4746 }
4747
Steve French50c2f752007-07-13 00:33:32 +00004748 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004749 since file handle passed in no longer valid */
4750
4751 return rc;
4752}
4753
Steve French50c2f752007-07-13 00:33:32 +00004754/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004755 an open handle, rather than by pathname - this is awkward due to
4756 potential access conflicts on the open, but it is unavoidable for these
4757 old servers since the only other choice is to go from 100 nanosecond DCE
4758 time and resort to the original setpathinfo level which takes the ancient
4759 DOS time format with 2 second granularity */
4760int
Steve French50c2f752007-07-13 00:33:32 +00004761CIFSSMBSetFileTimes(const int xid, struct cifsTconInfo *tcon,
4762 const FILE_BASIC_INFO *data, __u16 fid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004763{
4764 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004765 char *data_offset;
4766 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004767 __u16 params, param_offset, offset, byte_count, count;
4768
4769 cFYI(1, ("Set Times (via SetFileInfo)"));
Steve Frenchcd634992005-04-28 22:41:10 -07004770 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4771
Linus Torvalds1da177e2005-04-16 15:20:36 -07004772 if (rc)
4773 return rc;
4774
4775 /* At this point there is no need to override the current pid
4776 with the pid of the opener, but that could change if we someday
4777 use an existing handle (rather than opening one on the fly) */
4778 /* pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4779 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));*/
Steve French50c2f752007-07-13 00:33:32 +00004780
Linus Torvalds1da177e2005-04-16 15:20:36 -07004781 params = 6;
4782 pSMB->MaxSetupCount = 0;
4783 pSMB->Reserved = 0;
4784 pSMB->Flags = 0;
4785 pSMB->Timeout = 0;
4786 pSMB->Reserved2 = 0;
4787 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4788 offset = param_offset + params;
4789
Steve French50c2f752007-07-13 00:33:32 +00004790 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004791
Steve French26f57362007-08-30 22:09:15 +00004792 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004793 pSMB->MaxParameterCount = cpu_to_le16(2);
4794 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB PDU from sess */
4795 pSMB->SetupCount = 1;
4796 pSMB->Reserved3 = 0;
4797 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4798 byte_count = 3 /* pad */ + params + count;
4799 pSMB->DataCount = cpu_to_le16(count);
4800 pSMB->ParameterCount = cpu_to_le16(params);
4801 pSMB->TotalDataCount = pSMB->DataCount;
4802 pSMB->TotalParameterCount = pSMB->ParameterCount;
4803 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4804 pSMB->DataOffset = cpu_to_le16(offset);
4805 pSMB->Fid = fid;
4806 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4807 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4808 else
4809 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
4810 pSMB->Reserved4 = 0;
4811 pSMB->hdr.smb_buf_length += byte_count;
4812 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00004813 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00004814 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004815 if (rc)
Steve French50c2f752007-07-13 00:33:32 +00004816 cFYI(1, ("Send error in Set Time (SetFileInfo) = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004817
Steve French50c2f752007-07-13 00:33:32 +00004818 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004819 since file handle passed in no longer valid */
4820
4821 return rc;
4822}
4823
4824
4825int
4826CIFSSMBSetTimes(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00004827 const FILE_BASIC_INFO *data,
Steve French737b7582005-04-28 22:41:06 -07004828 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004829{
4830 TRANSACTION2_SPI_REQ *pSMB = NULL;
4831 TRANSACTION2_SPI_RSP *pSMBr = NULL;
4832 int name_len;
4833 int rc = 0;
4834 int bytes_returned = 0;
4835 char *data_offset;
4836 __u16 params, param_offset, offset, byte_count, count;
4837
4838 cFYI(1, ("In SetTimes"));
4839
4840SetTimesRetry:
4841 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4842 (void **) &pSMBr);
4843 if (rc)
4844 return rc;
4845
4846 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4847 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004848 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004849 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004850 name_len++; /* trailing null */
4851 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004852 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004853 name_len = strnlen(fileName, PATH_MAX);
4854 name_len++; /* trailing null */
4855 strncpy(pSMB->FileName, fileName, name_len);
4856 }
4857
4858 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004859 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004860 pSMB->MaxParameterCount = cpu_to_le16(2);
4861 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
4862 pSMB->MaxSetupCount = 0;
4863 pSMB->Reserved = 0;
4864 pSMB->Flags = 0;
4865 pSMB->Timeout = 0;
4866 pSMB->Reserved2 = 0;
4867 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004868 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004869 offset = param_offset + params;
4870 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
4871 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4872 pSMB->DataOffset = cpu_to_le16(offset);
4873 pSMB->SetupCount = 1;
4874 pSMB->Reserved3 = 0;
4875 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4876 byte_count = 3 /* pad */ + params + count;
4877
4878 pSMB->DataCount = cpu_to_le16(count);
4879 pSMB->ParameterCount = cpu_to_le16(params);
4880 pSMB->TotalDataCount = pSMB->DataCount;
4881 pSMB->TotalParameterCount = pSMB->ParameterCount;
4882 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4883 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4884 else
4885 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
4886 pSMB->Reserved4 = 0;
4887 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00004888 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004889 pSMB->ByteCount = cpu_to_le16(byte_count);
4890 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4891 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004892 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004893 cFYI(1, ("SetPathInfo (times) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004894
4895 cifs_buf_release(pSMB);
4896
4897 if (rc == -EAGAIN)
4898 goto SetTimesRetry;
4899
4900 return rc;
4901}
4902
4903/* Can not be used to set time stamps yet (due to old DOS time format) */
4904/* Can be used to set attributes */
4905#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
4906 handling it anyway and NT4 was what we thought it would be needed for
4907 Do not delete it until we prove whether needed for Win9x though */
4908int
4909CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
4910 __u16 dos_attrs, const struct nls_table *nls_codepage)
4911{
4912 SETATTR_REQ *pSMB = NULL;
4913 SETATTR_RSP *pSMBr = NULL;
4914 int rc = 0;
4915 int bytes_returned;
4916 int name_len;
4917
4918 cFYI(1, ("In SetAttrLegacy"));
4919
4920SetAttrLgcyRetry:
4921 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
4922 (void **) &pSMBr);
4923 if (rc)
4924 return rc;
4925
4926 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4927 name_len =
Steve French50c2f752007-07-13 00:33:32 +00004928 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07004929 PATH_MAX, nls_codepage);
4930 name_len++; /* trailing null */
4931 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004932 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004933 name_len = strnlen(fileName, PATH_MAX);
4934 name_len++; /* trailing null */
4935 strncpy(pSMB->fileName, fileName, name_len);
4936 }
4937 pSMB->attr = cpu_to_le16(dos_attrs);
4938 pSMB->BufferFormat = 0x04;
4939 pSMB->hdr.smb_buf_length += name_len + 1;
4940 pSMB->ByteCount = cpu_to_le16(name_len + 1);
4941 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4942 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004943 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004944 cFYI(1, ("Error in LegacySetAttr = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004945
4946 cifs_buf_release(pSMB);
4947
4948 if (rc == -EAGAIN)
4949 goto SetAttrLgcyRetry;
4950
4951 return rc;
4952}
4953#endif /* temporarily unneeded SetAttr legacy function */
4954
4955int
4956CIFSSMBUnixSetPerms(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00004957 char *fileName, __u64 mode, __u64 uid, __u64 gid,
4958 dev_t device, const struct nls_table *nls_codepage,
Steve French737b7582005-04-28 22:41:06 -07004959 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004960{
4961 TRANSACTION2_SPI_REQ *pSMB = NULL;
4962 TRANSACTION2_SPI_RSP *pSMBr = NULL;
4963 int name_len;
4964 int rc = 0;
4965 int bytes_returned = 0;
4966 FILE_UNIX_BASIC_INFO *data_offset;
4967 __u16 params, param_offset, offset, count, byte_count;
4968
4969 cFYI(1, ("In SetUID/GID/Mode"));
4970setPermsRetry:
4971 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4972 (void **) &pSMBr);
4973 if (rc)
4974 return rc;
4975
4976 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4977 name_len =
Steve French50c2f752007-07-13 00:33:32 +00004978 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004979 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004980 name_len++; /* trailing null */
4981 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004982 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004983 name_len = strnlen(fileName, PATH_MAX);
4984 name_len++; /* trailing null */
4985 strncpy(pSMB->FileName, fileName, name_len);
4986 }
4987
4988 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004989 count = sizeof(FILE_UNIX_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004990 pSMB->MaxParameterCount = cpu_to_le16(2);
4991 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find exact max SMB PDU from sess structure BB */
4992 pSMB->MaxSetupCount = 0;
4993 pSMB->Reserved = 0;
4994 pSMB->Flags = 0;
4995 pSMB->Timeout = 0;
4996 pSMB->Reserved2 = 0;
4997 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004998 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004999 offset = param_offset + params;
5000 data_offset =
5001 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5002 offset);
5003 memset(data_offset, 0, count);
5004 pSMB->DataOffset = cpu_to_le16(offset);
5005 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5006 pSMB->SetupCount = 1;
5007 pSMB->Reserved3 = 0;
5008 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5009 byte_count = 3 /* pad */ + params + count;
5010 pSMB->ParameterCount = cpu_to_le16(params);
5011 pSMB->DataCount = cpu_to_le16(count);
5012 pSMB->TotalParameterCount = pSMB->ParameterCount;
5013 pSMB->TotalDataCount = pSMB->DataCount;
5014 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5015 pSMB->Reserved4 = 0;
5016 pSMB->hdr.smb_buf_length += byte_count;
Steve Frenchc7af1852007-03-01 04:11:22 +00005017 /* Samba server ignores set of file size to zero due to bugs in some
5018 older clients, but we should be precise - we use SetFileSize to
5019 set file size and do not want to truncate file size to zero
5020 accidently as happened on one Samba server beta by putting
Steve French50c2f752007-07-13 00:33:32 +00005021 zero instead of -1 here */
Steve Frenchc7af1852007-03-01 04:11:22 +00005022 data_offset->EndOfFile = NO_CHANGE_64;
5023 data_offset->NumOfBytes = NO_CHANGE_64;
5024 data_offset->LastStatusChange = NO_CHANGE_64;
5025 data_offset->LastAccessTime = NO_CHANGE_64;
5026 data_offset->LastModificationTime = NO_CHANGE_64;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005027 data_offset->Uid = cpu_to_le64(uid);
5028 data_offset->Gid = cpu_to_le64(gid);
5029 /* better to leave device as zero when it is */
5030 data_offset->DevMajor = cpu_to_le64(MAJOR(device));
5031 data_offset->DevMinor = cpu_to_le64(MINOR(device));
5032 data_offset->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +00005033
Steve French790fe572007-07-07 19:25:05 +00005034 if (S_ISREG(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005035 data_offset->Type = cpu_to_le32(UNIX_FILE);
Steve French790fe572007-07-07 19:25:05 +00005036 else if (S_ISDIR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005037 data_offset->Type = cpu_to_le32(UNIX_DIR);
Steve French790fe572007-07-07 19:25:05 +00005038 else if (S_ISLNK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005039 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
Steve French790fe572007-07-07 19:25:05 +00005040 else if (S_ISCHR(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005041 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
Steve French790fe572007-07-07 19:25:05 +00005042 else if (S_ISBLK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005043 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
Steve French790fe572007-07-07 19:25:05 +00005044 else if (S_ISFIFO(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005045 data_offset->Type = cpu_to_le32(UNIX_FIFO);
Steve French790fe572007-07-07 19:25:05 +00005046 else if (S_ISSOCK(mode))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005047 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5048
5049
5050 pSMB->ByteCount = cpu_to_le16(byte_count);
5051 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5052 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005053 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005054 cFYI(1, ("SetPathInfo (perms) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005055
5056 if (pSMB)
5057 cifs_buf_release(pSMB);
5058 if (rc == -EAGAIN)
5059 goto setPermsRetry;
5060 return rc;
5061}
5062
Steve French50c2f752007-07-13 00:33:32 +00005063int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005064 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005065 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005066 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005067{
5068 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005069 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5070 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005071 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005072 int bytes_returned;
5073
Steve French50c2f752007-07-13 00:33:32 +00005074 cFYI(1, ("In CIFSSMBNotify for file handle %d", (int)netfid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005075 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005076 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005077 if (rc)
5078 return rc;
5079
5080 pSMB->TotalParameterCount = 0 ;
5081 pSMB->TotalDataCount = 0;
5082 pSMB->MaxParameterCount = cpu_to_le32(2);
5083 /* BB find exact data count max from sess structure BB */
5084 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005085/* BB VERIFY verify which is correct for above BB */
5086 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5087 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5088
Linus Torvalds1da177e2005-04-16 15:20:36 -07005089 pSMB->MaxSetupCount = 4;
5090 pSMB->Reserved = 0;
5091 pSMB->ParameterOffset = 0;
5092 pSMB->DataCount = 0;
5093 pSMB->DataOffset = 0;
5094 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5095 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5096 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005097 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005098 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5099 pSMB->Reserved2 = 0;
5100 pSMB->CompletionFilter = cpu_to_le32(filter);
5101 pSMB->Fid = netfid; /* file handle always le */
5102 pSMB->ByteCount = 0;
5103
5104 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005105 (struct smb_hdr *)pSMBr, &bytes_returned,
5106 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005107 if (rc) {
5108 cFYI(1, ("Error in Notify = %d", rc));
Steve Frenchff5dbd92005-08-24 17:10:36 -07005109 } else {
5110 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005111 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005112 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005113 sizeof(struct dir_notify_req),
5114 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005115 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005116 dnotify_req->Pid = pSMB->hdr.Pid;
5117 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5118 dnotify_req->Mid = pSMB->hdr.Mid;
5119 dnotify_req->Tid = pSMB->hdr.Tid;
5120 dnotify_req->Uid = pSMB->hdr.Uid;
5121 dnotify_req->netfid = netfid;
5122 dnotify_req->pfile = pfile;
5123 dnotify_req->filter = filter;
5124 dnotify_req->multishot = multishot;
5125 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005126 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005127 &GlobalDnotifyReqList);
5128 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005129 } else
Steve French47c786e2005-10-11 20:03:18 -07005130 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005131 }
5132 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005133 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005134}
5135#ifdef CONFIG_CIFS_XATTR
5136ssize_t
5137CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
5138 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00005139 char *EAData, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005140 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005141{
5142 /* BB assumes one setup word */
5143 TRANSACTION2_QPI_REQ *pSMB = NULL;
5144 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5145 int rc = 0;
5146 int bytes_returned;
5147 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005148 struct fea *temp_fea;
5149 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005150 __u16 params, byte_count;
5151
5152 cFYI(1, ("In Query All EAs path %s", searchName));
5153QAllEAsRetry:
5154 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5155 (void **) &pSMBr);
5156 if (rc)
5157 return rc;
5158
5159 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5160 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005161 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005162 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005163 name_len++; /* trailing null */
5164 name_len *= 2;
5165 } else { /* BB improve the check for buffer overruns BB */
5166 name_len = strnlen(searchName, PATH_MAX);
5167 name_len++; /* trailing null */
5168 strncpy(pSMB->FileName, searchName, name_len);
5169 }
5170
Steve French50c2f752007-07-13 00:33:32 +00005171 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005172 pSMB->TotalDataCount = 0;
5173 pSMB->MaxParameterCount = cpu_to_le16(2);
5174 pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
5175 pSMB->MaxSetupCount = 0;
5176 pSMB->Reserved = 0;
5177 pSMB->Flags = 0;
5178 pSMB->Timeout = 0;
5179 pSMB->Reserved2 = 0;
5180 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005181 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005182 pSMB->DataCount = 0;
5183 pSMB->DataOffset = 0;
5184 pSMB->SetupCount = 1;
5185 pSMB->Reserved3 = 0;
5186 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5187 byte_count = params + 1 /* pad */ ;
5188 pSMB->TotalParameterCount = cpu_to_le16(params);
5189 pSMB->ParameterCount = pSMB->TotalParameterCount;
5190 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5191 pSMB->Reserved4 = 0;
5192 pSMB->hdr.smb_buf_length += byte_count;
5193 pSMB->ByteCount = cpu_to_le16(byte_count);
5194
5195 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5196 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5197 if (rc) {
5198 cFYI(1, ("Send error in QueryAllEAs = %d", rc));
5199 } else { /* decode response */
5200 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5201
5202 /* BB also check enough total bytes returned */
5203 /* BB we need to improve the validity checking
5204 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005205 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005206 rc = -EIO; /* bad smb */
5207 /* else if (pFindData){
5208 memcpy((char *) pFindData,
5209 (char *) &pSMBr->hdr.Protocol +
5210 data_offset, kl);
5211 }*/ else {
5212 /* check that length of list is not more than bcc */
5213 /* check that each entry does not go beyond length
5214 of list */
5215 /* check that each element of each entry does not
5216 go beyond end of list */
5217 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005218 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005219 rc = 0;
5220 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005221 /* BB check if start of smb + data_offset > &bcc+ bcc */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005222 ea_response_data = (struct fealist *)
5223 (((char *) &pSMBr->hdr.Protocol) +
5224 data_offset);
5225 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005226 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005227 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005228 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005229 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005230 } else {
5231 /* account for ea list len */
5232 name_len -= 4;
5233 temp_fea = ea_response_data->list;
5234 temp_ptr = (char *)temp_fea;
Steve French50c2f752007-07-13 00:33:32 +00005235 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005236 __u16 value_len;
5237 name_len -= 4;
5238 temp_ptr += 4;
5239 rc += temp_fea->name_len;
5240 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005241 rc = rc + 5 + 1;
5242 if (rc < (int)buf_size) {
Steve French50c2f752007-07-13 00:33:32 +00005243 memcpy(EAData, "user.", 5);
5244 EAData += 5;
5245 memcpy(EAData, temp_ptr,
5246 temp_fea->name_len);
5247 EAData += temp_fea->name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005248 /* null terminate name */
5249 *EAData = 0;
5250 EAData = EAData + 1;
Steve French790fe572007-07-07 19:25:05 +00005251 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005252 /* skip copy - calc size only */
5253 } else {
5254 /* stop before overrun buffer */
5255 rc = -ERANGE;
5256 break;
5257 }
5258 name_len -= temp_fea->name_len;
5259 temp_ptr += temp_fea->name_len;
5260 /* account for trailing null */
5261 name_len--;
5262 temp_ptr++;
Steve French50c2f752007-07-13 00:33:32 +00005263 value_len =
5264 le16_to_cpu(temp_fea->value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005265 name_len -= value_len;
5266 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005267 /* BB check that temp_ptr is still
5268 within the SMB BB*/
5269
5270 /* no trailing null to account for
5271 in value len */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005272 /* go on to next EA */
5273 temp_fea = (struct fea *)temp_ptr;
5274 }
5275 }
5276 }
5277 }
5278 if (pSMB)
5279 cifs_buf_release(pSMB);
5280 if (rc == -EAGAIN)
5281 goto QAllEAsRetry;
5282
5283 return (ssize_t)rc;
5284}
5285
Steve French50c2f752007-07-13 00:33:32 +00005286ssize_t CIFSSMBQueryEA(const int xid, struct cifsTconInfo *tcon,
5287 const unsigned char *searchName, const unsigned char *ea_name,
5288 unsigned char *ea_value, size_t buf_size,
Steve French737b7582005-04-28 22:41:06 -07005289 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005290{
5291 TRANSACTION2_QPI_REQ *pSMB = NULL;
5292 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5293 int rc = 0;
5294 int bytes_returned;
5295 int name_len;
Steve French50c2f752007-07-13 00:33:32 +00005296 struct fea *temp_fea;
5297 char *temp_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005298 __u16 params, byte_count;
5299
5300 cFYI(1, ("In Query EA path %s", searchName));
5301QEARetry:
5302 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5303 (void **) &pSMBr);
5304 if (rc)
5305 return rc;
5306
5307 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5308 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005309 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005310 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005311 name_len++; /* trailing null */
5312 name_len *= 2;
5313 } else { /* BB improve the check for buffer overruns BB */
5314 name_len = strnlen(searchName, PATH_MAX);
5315 name_len++; /* trailing null */
5316 strncpy(pSMB->FileName, searchName, name_len);
5317 }
5318
Steve French50c2f752007-07-13 00:33:32 +00005319 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005320 pSMB->TotalDataCount = 0;
5321 pSMB->MaxParameterCount = cpu_to_le16(2);
5322 pSMB->MaxDataCount = cpu_to_le16(4000); /* BB find exact max SMB PDU from sess structure BB */
5323 pSMB->MaxSetupCount = 0;
5324 pSMB->Reserved = 0;
5325 pSMB->Flags = 0;
5326 pSMB->Timeout = 0;
5327 pSMB->Reserved2 = 0;
5328 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005329 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005330 pSMB->DataCount = 0;
5331 pSMB->DataOffset = 0;
5332 pSMB->SetupCount = 1;
5333 pSMB->Reserved3 = 0;
5334 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5335 byte_count = params + 1 /* pad */ ;
5336 pSMB->TotalParameterCount = cpu_to_le16(params);
5337 pSMB->ParameterCount = pSMB->TotalParameterCount;
5338 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5339 pSMB->Reserved4 = 0;
5340 pSMB->hdr.smb_buf_length += byte_count;
5341 pSMB->ByteCount = cpu_to_le16(byte_count);
5342
5343 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5344 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5345 if (rc) {
5346 cFYI(1, ("Send error in Query EA = %d", rc));
5347 } else { /* decode response */
5348 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5349
5350 /* BB also check enough total bytes returned */
5351 /* BB we need to improve the validity checking
5352 of these trans2 responses */
Steve French50c2f752007-07-13 00:33:32 +00005353 if (rc || (pSMBr->ByteCount < 4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005354 rc = -EIO; /* bad smb */
5355 /* else if (pFindData){
5356 memcpy((char *) pFindData,
5357 (char *) &pSMBr->hdr.Protocol +
5358 data_offset, kl);
5359 }*/ else {
5360 /* check that length of list is not more than bcc */
5361 /* check that each entry does not go beyond length
5362 of list */
5363 /* check that each element of each entry does not
5364 go beyond end of list */
5365 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve French50c2f752007-07-13 00:33:32 +00005366 struct fealist *ea_response_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005367 rc = -ENODATA;
5368 /* validate_trans2_offsets() */
Steve French790fe572007-07-07 19:25:05 +00005369 /* BB check if start of smb + data_offset > &bcc+ bcc*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07005370 ea_response_data = (struct fealist *)
5371 (((char *) &pSMBr->hdr.Protocol) +
5372 data_offset);
5373 name_len = le32_to_cpu(ea_response_data->list_len);
Steve French50c2f752007-07-13 00:33:32 +00005374 cFYI(1, ("ea length %d", name_len));
Steve French790fe572007-07-07 19:25:05 +00005375 if (name_len <= 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005376 /* returned EA size zeroed at top of function */
Steve French50c2f752007-07-13 00:33:32 +00005377 cFYI(1, ("empty EA list returned from server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005378 } else {
5379 /* account for ea list len */
5380 name_len -= 4;
5381 temp_fea = ea_response_data->list;
5382 temp_ptr = (char *)temp_fea;
5383 /* loop through checking if we have a matching
5384 name and then return the associated value */
Steve French50c2f752007-07-13 00:33:32 +00005385 while (name_len > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005386 __u16 value_len;
5387 name_len -= 4;
5388 temp_ptr += 4;
Steve French50c2f752007-07-13 00:33:32 +00005389 value_len =
5390 le16_to_cpu(temp_fea->value_len);
5391 /* BB validate that value_len falls within SMB,
5392 even though maximum for name_len is 255 */
Steve French790fe572007-07-07 19:25:05 +00005393 if (memcmp(temp_fea->name, ea_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005394 temp_fea->name_len) == 0) {
5395 /* found a match */
5396 rc = value_len;
5397 /* account for prefix user. and trailing null */
Steve French790fe572007-07-07 19:25:05 +00005398 if (rc <= (int)buf_size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005399 memcpy(ea_value,
5400 temp_fea->name+temp_fea->name_len+1,
5401 rc);
Steve French50c2f752007-07-13 00:33:32 +00005402 /* ea values, unlike ea
5403 names, are not null
5404 terminated */
Steve French790fe572007-07-07 19:25:05 +00005405 } else if (buf_size == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005406 /* skip copy - calc size only */
5407 } else {
Steve French50c2f752007-07-13 00:33:32 +00005408 /* stop before overrun buffer */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005409 rc = -ERANGE;
5410 }
5411 break;
5412 }
5413 name_len -= temp_fea->name_len;
5414 temp_ptr += temp_fea->name_len;
5415 /* account for trailing null */
5416 name_len--;
5417 temp_ptr++;
5418 name_len -= value_len;
5419 temp_ptr += value_len;
Steve French50c2f752007-07-13 00:33:32 +00005420 /* No trailing null to account for in
5421 value_len. Go on to next EA */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005422 temp_fea = (struct fea *)temp_ptr;
5423 }
Steve French50c2f752007-07-13 00:33:32 +00005424 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005425 }
5426 }
5427 if (pSMB)
5428 cifs_buf_release(pSMB);
5429 if (rc == -EAGAIN)
5430 goto QEARetry;
5431
5432 return (ssize_t)rc;
5433}
5434
5435int
5436CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005437 const char *ea_name, const void *ea_value,
5438 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5439 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005440{
5441 struct smb_com_transaction2_spi_req *pSMB = NULL;
5442 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5443 struct fealist *parm_data;
5444 int name_len;
5445 int rc = 0;
5446 int bytes_returned = 0;
5447 __u16 params, param_offset, byte_count, offset, count;
5448
5449 cFYI(1, ("In SetEA"));
5450SetEARetry:
5451 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5452 (void **) &pSMBr);
5453 if (rc)
5454 return rc;
5455
5456 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5457 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005458 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005459 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005460 name_len++; /* trailing null */
5461 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005462 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005463 name_len = strnlen(fileName, PATH_MAX);
5464 name_len++; /* trailing null */
5465 strncpy(pSMB->FileName, fileName, name_len);
5466 }
5467
5468 params = 6 + name_len;
5469
5470 /* done calculating parms using name_len of file name,
5471 now use name_len to calculate length of ea name
5472 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005473 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005474 name_len = 0;
5475 else
Steve French50c2f752007-07-13 00:33:32 +00005476 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005477
Steve Frenchdae5dbd2007-12-30 23:49:57 +00005478 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005479 pSMB->MaxParameterCount = cpu_to_le16(2);
5480 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB size from sess */
5481 pSMB->MaxSetupCount = 0;
5482 pSMB->Reserved = 0;
5483 pSMB->Flags = 0;
5484 pSMB->Timeout = 0;
5485 pSMB->Reserved2 = 0;
5486 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005487 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005488 offset = param_offset + params;
5489 pSMB->InformationLevel =
5490 cpu_to_le16(SMB_SET_FILE_EA);
5491
5492 parm_data =
5493 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5494 offset);
5495 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5496 pSMB->DataOffset = cpu_to_le16(offset);
5497 pSMB->SetupCount = 1;
5498 pSMB->Reserved3 = 0;
5499 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5500 byte_count = 3 /* pad */ + params + count;
5501 pSMB->DataCount = cpu_to_le16(count);
5502 parm_data->list_len = cpu_to_le32(count);
5503 parm_data->list[0].EA_flags = 0;
5504 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005505 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005506 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005507 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005508 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005509 parm_data->list[0].name[name_len] = 0;
5510 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5511 /* caller ensures that ea_value_len is less than 64K but
5512 we need to ensure that it fits within the smb */
5513
Steve French50c2f752007-07-13 00:33:32 +00005514 /*BB add length check to see if it would fit in
5515 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005516 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5517 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005518 memcpy(parm_data->list[0].name+name_len+1,
5519 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005520
5521 pSMB->TotalDataCount = pSMB->DataCount;
5522 pSMB->ParameterCount = cpu_to_le16(params);
5523 pSMB->TotalParameterCount = pSMB->ParameterCount;
5524 pSMB->Reserved4 = 0;
5525 pSMB->hdr.smb_buf_length += byte_count;
5526 pSMB->ByteCount = cpu_to_le16(byte_count);
5527 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5528 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005529 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005530 cFYI(1, ("SetPathInfo (EA) returned %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005531
5532 cifs_buf_release(pSMB);
5533
5534 if (rc == -EAGAIN)
5535 goto SetEARetry;
5536
5537 return rc;
5538}
5539
5540#endif