blob: 4ecb361640bf3dbbe4fa93cc5667fc15b632fcf8 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/cifssmb.c
3 *
Steve Frenchf19159d2010-04-21 04:12:10 +00004 * Copyright (C) International Business Machines Corp., 2002,2010
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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090033#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include <linux/posix_acl_xattr.h>
35#include <asm/uaccess.h>
36#include "cifspdu.h"
37#include "cifsglob.h"
Shirish Pargaonkard0d66c42007-10-03 18:22:19 +000038#include "cifsacl.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include "cifsproto.h"
40#include "cifs_unicode.h"
41#include "cifs_debug.h"
42
43#ifdef CONFIG_CIFS_POSIX
44static struct {
45 int index;
46 char *name;
47} protocols[] = {
Steve French39798772006-05-31 22:40:51 +000048#ifdef CONFIG_CIFS_WEAK_PW_HASH
49 {LANMAN_PROT, "\2LM1.2X002"},
Steve French9ac00b72006-09-30 04:13:17 +000050 {LANMAN2_PROT, "\2LANMAN2.1"},
Steve French39798772006-05-31 22:40:51 +000051#endif /* weak password hashing for legacy clients */
Steve French50c2f752007-07-13 00:33:32 +000052 {CIFS_PROT, "\2NT LM 0.12"},
Steve French39798772006-05-31 22:40:51 +000053 {POSIX_PROT, "\2POSIX 2"},
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 {BAD_PROT, "\2"}
55};
56#else
57static struct {
58 int index;
59 char *name;
60} protocols[] = {
Steve French39798772006-05-31 22:40:51 +000061#ifdef CONFIG_CIFS_WEAK_PW_HASH
62 {LANMAN_PROT, "\2LM1.2X002"},
Steve French18f75ca2006-10-01 03:13:01 +000063 {LANMAN2_PROT, "\2LANMAN2.1"},
Steve French39798772006-05-31 22:40:51 +000064#endif /* weak password hashing for legacy clients */
Steve French790fe572007-07-07 19:25:05 +000065 {CIFS_PROT, "\2NT LM 0.12"},
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 {BAD_PROT, "\2"}
67};
68#endif
69
Steve French39798772006-05-31 22:40:51 +000070/* define the number of elements in the cifs dialect array */
71#ifdef CONFIG_CIFS_POSIX
72#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French9ac00b72006-09-30 04:13:17 +000073#define CIFS_NUM_PROT 4
Steve French39798772006-05-31 22:40:51 +000074#else
75#define CIFS_NUM_PROT 2
76#endif /* CIFS_WEAK_PW_HASH */
77#else /* not posix */
78#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French9ac00b72006-09-30 04:13:17 +000079#define CIFS_NUM_PROT 3
Steve French39798772006-05-31 22:40:51 +000080#else
81#define CIFS_NUM_PROT 1
82#endif /* CONFIG_CIFS_WEAK_PW_HASH */
83#endif /* CIFS_POSIX */
84
Linus Torvalds1da177e2005-04-16 15:20:36 -070085/* 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 Frenchad8b15f2008-08-08 21:10:16 +000097 open_file->invalidHandle = true;
Jeff Layton3bc303c2009-09-21 06:47:50 -040098 open_file->oplock_break_cancelled = 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
Jeff Layton9162ab22009-09-03 12:07:17 -0400105/* reconnect the socket, tcon, and smb session if needed */
106static int
107cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)
108{
109 int rc = 0;
110 struct cifsSesInfo *ses;
111 struct TCP_Server_Info *server;
112 struct nls_table *nls_codepage;
113
114 /*
115 * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
116 * tcp and smb session status done differently for those three - in the
117 * calling routine
118 */
119 if (!tcon)
120 return 0;
121
122 ses = tcon->ses;
123 server = ses->server;
124
125 /*
126 * only tree disconnect, open, and write, (and ulogoff which does not
127 * have tcon) are allowed as we start force umount
128 */
129 if (tcon->tidStatus == CifsExiting) {
130 if (smb_command != SMB_COM_WRITE_ANDX &&
131 smb_command != SMB_COM_OPEN_ANDX &&
132 smb_command != SMB_COM_TREE_DISCONNECT) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000133 cFYI(1, "can not send cmd %d while umounting",
134 smb_command);
Jeff Layton9162ab22009-09-03 12:07:17 -0400135 return -ENODEV;
136 }
137 }
138
139 if (ses->status == CifsExiting)
140 return -EIO;
141
142 /*
143 * Give demultiplex thread up to 10 seconds to reconnect, should be
144 * greater than cifs socket timeout which is 7 seconds
145 */
146 while (server->tcpStatus == CifsNeedReconnect) {
147 wait_event_interruptible_timeout(server->response_q,
148 (server->tcpStatus == CifsGood), 10 * HZ);
149
150 /* is TCP session is reestablished now ?*/
151 if (server->tcpStatus != CifsNeedReconnect)
152 break;
153
154 /*
155 * on "soft" mounts we wait once. Hard mounts keep
156 * retrying until process is killed or server comes
157 * back on-line
158 */
159 if (!tcon->retry || ses->status == CifsExiting) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000160 cFYI(1, "gave up waiting on reconnect in smb_init");
Jeff Layton9162ab22009-09-03 12:07:17 -0400161 return -EHOSTDOWN;
162 }
163 }
164
165 if (!ses->need_reconnect && !tcon->need_reconnect)
166 return 0;
167
168 nls_codepage = load_nls_default();
169
170 /*
171 * need to prevent multiple threads trying to simultaneously
172 * reconnect the same SMB session
173 */
Steve Frenchd7b619c2010-02-25 05:36:46 +0000174 mutex_lock(&ses->session_mutex);
Jeff Layton9162ab22009-09-03 12:07:17 -0400175 if (ses->need_reconnect)
176 rc = cifs_setup_session(0, ses, nls_codepage);
177
178 /* do we need to reconnect tcon? */
179 if (rc || !tcon->need_reconnect) {
Steve Frenchd7b619c2010-02-25 05:36:46 +0000180 mutex_unlock(&ses->session_mutex);
Jeff Layton9162ab22009-09-03 12:07:17 -0400181 goto out;
182 }
183
184 mark_open_files_invalid(tcon);
185 rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage);
Steve Frenchd7b619c2010-02-25 05:36:46 +0000186 mutex_unlock(&ses->session_mutex);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000187 cFYI(1, "reconnect tcon rc = %d", rc);
Jeff Layton9162ab22009-09-03 12:07:17 -0400188
189 if (rc)
190 goto out;
191
192 /*
193 * FIXME: check if wsize needs updated due to negotiated smb buffer
194 * size shrinking
195 */
196 atomic_inc(&tconInfoReconnectCount);
197
198 /* tell server Unix caps we support */
199 if (ses->capabilities & CAP_UNIX)
200 reset_cifs_unix_caps(0, tcon, NULL, NULL);
201
202 /*
203 * Removed call to reopen open files here. It is safer (and faster) to
204 * reopen files one at a time as needed in read and write.
205 *
206 * FIXME: what about file locks? don't we need to reclaim them ASAP?
207 */
208
209out:
210 /*
211 * Check if handle based operation so we know whether we can continue
212 * or not without returning to caller to reset file handle
213 */
214 switch (smb_command) {
215 case SMB_COM_READ_ANDX:
216 case SMB_COM_WRITE_ANDX:
217 case SMB_COM_CLOSE:
218 case SMB_COM_FIND_CLOSE2:
219 case SMB_COM_LOCKING_ANDX:
220 rc = -EAGAIN;
221 }
222
223 unload_nls(nls_codepage);
224 return rc;
225}
226
Steve Frenchad7a2922008-02-07 23:25:02 +0000227/* Allocate and return pointer to an SMB request buffer, and set basic
228 SMB information in the SMB header. If the return code is zero, this
229 function must have filled in request_buf pointer */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230static int
231small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000232 void **request_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233{
234 int rc = 0;
235
Jeff Layton9162ab22009-09-03 12:07:17 -0400236 rc = cifs_reconnect_tcon(tcon, smb_command);
Steve French790fe572007-07-07 19:25:05 +0000237 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 return rc;
239
240 *request_buf = cifs_small_buf_get();
241 if (*request_buf == NULL) {
242 /* BB should we add a retry in here if not a writepage? */
243 return -ENOMEM;
244 }
245
Steve French63135e02007-07-17 17:34:02 +0000246 header_assemble((struct smb_hdr *) *request_buf, smb_command,
Steve Frenchc18c8422007-07-18 23:21:09 +0000247 tcon, wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
Steve French790fe572007-07-07 19:25:05 +0000249 if (tcon != NULL)
250 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700251
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 return rc;
Steve French5815449d2006-02-14 01:36:20 +0000253}
254
Steve French12b3b8f2006-02-09 21:12:47 +0000255int
Steve French50c2f752007-07-13 00:33:32 +0000256small_smb_init_no_tc(const int smb_command, const int wct,
Steve French5815449d2006-02-14 01:36:20 +0000257 struct cifsSesInfo *ses, void **request_buf)
Steve French12b3b8f2006-02-09 21:12:47 +0000258{
259 int rc;
Steve French50c2f752007-07-13 00:33:32 +0000260 struct smb_hdr *buffer;
Steve French12b3b8f2006-02-09 21:12:47 +0000261
Steve French5815449d2006-02-14 01:36:20 +0000262 rc = small_smb_init(smb_command, wct, NULL, request_buf);
Steve French790fe572007-07-07 19:25:05 +0000263 if (rc)
Steve French12b3b8f2006-02-09 21:12:47 +0000264 return rc;
265
Steve French04fdabe2006-02-10 05:52:50 +0000266 buffer = (struct smb_hdr *)*request_buf;
Steve French12b3b8f2006-02-09 21:12:47 +0000267 buffer->Mid = GetNextMid(ses->server);
268 if (ses->capabilities & CAP_UNICODE)
269 buffer->Flags2 |= SMBFLG2_UNICODE;
Steve French04fdabe2006-02-10 05:52:50 +0000270 if (ses->capabilities & CAP_STATUS32)
Steve French12b3b8f2006-02-09 21:12:47 +0000271 buffer->Flags2 |= SMBFLG2_ERR_STATUS;
272
273 /* uid, tid can stay at zero as set in header assemble */
274
Steve French50c2f752007-07-13 00:33:32 +0000275 /* BB add support for turning on the signing when
Steve French12b3b8f2006-02-09 21:12:47 +0000276 this function is used after 1st of session setup requests */
277
278 return rc;
279}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280
281/* If the return code is zero, this function must fill in request_buf pointer */
282static int
283smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
284 void **request_buf /* returned */ ,
285 void **response_buf /* returned */ )
286{
287 int rc = 0;
288
Jeff Layton9162ab22009-09-03 12:07:17 -0400289 rc = cifs_reconnect_tcon(tcon, smb_command);
Steve French790fe572007-07-07 19:25:05 +0000290 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 return rc;
292
293 *request_buf = cifs_buf_get();
294 if (*request_buf == NULL) {
295 /* BB should we add a retry in here if not a writepage? */
296 return -ENOMEM;
297 }
298 /* Although the original thought was we needed the response buf for */
299 /* potential retries of smb operations it turns out we can determine */
300 /* from the mid flags when the request buffer can be resent without */
301 /* having to use a second distinct buffer for the response */
Steve French790fe572007-07-07 19:25:05 +0000302 if (response_buf)
Steve French50c2f752007-07-13 00:33:32 +0000303 *response_buf = *request_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
305 header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +0000306 wct);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
Steve French790fe572007-07-07 19:25:05 +0000308 if (tcon != NULL)
309 cifs_stats_inc(&tcon->num_smbs_sent);
Steve Frencha4544342005-08-24 13:59:35 -0700310
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 return rc;
312}
313
Steve French50c2f752007-07-13 00:33:32 +0000314static int validate_t2(struct smb_t2_rsp *pSMB)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315{
316 int rc = -EINVAL;
317 int total_size;
Steve French50c2f752007-07-13 00:33:32 +0000318 char *pBCC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
320 /* check for plausible wct, bcc and t2 data and parm sizes */
321 /* check for parm and data offset going beyond end of smb */
Steve French790fe572007-07-07 19:25:05 +0000322 if (pSMB->hdr.WordCount >= 10) {
323 if ((le16_to_cpu(pSMB->t2_rsp.ParameterOffset) <= 1024) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 (le16_to_cpu(pSMB->t2_rsp.DataOffset) <= 1024)) {
325 /* check that bcc is at least as big as parms + data */
326 /* check that bcc is less than negotiated smb buffer */
327 total_size = le16_to_cpu(pSMB->t2_rsp.ParameterCount);
Steve French790fe572007-07-07 19:25:05 +0000328 if (total_size < 512) {
Steve Frenchc18c8422007-07-18 23:21:09 +0000329 total_size +=
Steve French63135e02007-07-17 17:34:02 +0000330 le16_to_cpu(pSMB->t2_rsp.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 /* BCC le converted in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +0000332 pBCC = (pSMB->hdr.WordCount * 2) +
Steve French09d1db52005-04-28 22:41:08 -0700333 sizeof(struct smb_hdr) +
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 (char *)pSMB;
Steve French790fe572007-07-07 19:25:05 +0000335 if ((total_size <= (*(u16 *)pBCC)) &&
Steve French50c2f752007-07-13 00:33:32 +0000336 (total_size <
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 CIFSMaxBufSize+MAX_CIFS_HDR_SIZE)) {
338 return 0;
339 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 }
341 }
342 }
Steve French50c2f752007-07-13 00:33:32 +0000343 cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 sizeof(struct smb_t2_rsp) + 16);
345 return rc;
346}
347int
348CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
349{
350 NEGOTIATE_REQ *pSMB;
351 NEGOTIATE_RSP *pSMBr;
352 int rc = 0;
353 int bytes_returned;
Steve French39798772006-05-31 22:40:51 +0000354 int i;
Steve French50c2f752007-07-13 00:33:32 +0000355 struct TCP_Server_Info *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 u16 count;
Steve French750d1152006-06-27 06:28:30 +0000357 unsigned int secFlags;
Al Viro733f99a2006-10-14 16:48:26 +0100358 u16 dialect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
Steve French790fe572007-07-07 19:25:05 +0000360 if (ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 server = ses->server;
362 else {
363 rc = -EIO;
364 return rc;
365 }
366 rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
367 (void **) &pSMB, (void **) &pSMBr);
368 if (rc)
369 return rc;
Steve French750d1152006-06-27 06:28:30 +0000370
371 /* if any of auth flags (ie not sign or seal) are overriden use them */
Steve French790fe572007-07-07 19:25:05 +0000372 if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
Steve French762e5ab2007-06-28 18:41:42 +0000373 secFlags = ses->overrideSecFlg; /* BB FIXME fix sign flags? */
Steve French750d1152006-06-27 06:28:30 +0000374 else /* if override flags set only sign/seal OR them with global auth */
Jeff Layton04912d62010-04-24 07:57:45 -0400375 secFlags = global_secflags | ses->overrideSecFlg;
Steve French750d1152006-06-27 06:28:30 +0000376
Joe Perchesb6b38f72010-04-21 03:50:45 +0000377 cFYI(1, "secFlags 0x%x", secFlags);
Steve Frenchf40c5622006-06-28 00:13:38 +0000378
Steve French1982c342005-08-17 12:38:22 -0700379 pSMB->hdr.Mid = GetNextMid(server);
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000380 pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
Steve Frencha0136892007-10-04 20:05:09 +0000381
Yehuda Sadeh Weinraub100c1dd2007-06-05 21:31:16 +0000382 if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
Steve French254e55e2006-06-04 05:53:15 +0000383 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
Steve Frencha0136892007-10-04 20:05:09 +0000384 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000385 cFYI(1, "Kerberos only mechanism, enable extended security");
Steve Frencha0136892007-10-04 20:05:09 +0000386 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
387 }
Steve Frenchac683922009-05-06 04:16:04 +0000388#ifdef CONFIG_CIFS_EXPERIMENTAL
389 else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
390 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
391 else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000392 cFYI(1, "NTLMSSP only mechanism, enable extended security");
Steve Frenchac683922009-05-06 04:16:04 +0000393 pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
394 }
395#endif
Steve French50c2f752007-07-13 00:33:32 +0000396
Steve French39798772006-05-31 22:40:51 +0000397 count = 0;
Steve French50c2f752007-07-13 00:33:32 +0000398 for (i = 0; i < CIFS_NUM_PROT; i++) {
Steve French39798772006-05-31 22:40:51 +0000399 strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
400 count += strlen(protocols[i].name) + 1;
401 /* null at end of source and target buffers anyway */
402 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 pSMB->hdr.smb_buf_length += count;
404 pSMB->ByteCount = cpu_to_le16(count);
405
406 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
407 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French50c2f752007-07-13 00:33:32 +0000408 if (rc != 0)
Steve French254e55e2006-06-04 05:53:15 +0000409 goto neg_err_exit;
410
Al Viro733f99a2006-10-14 16:48:26 +0100411 dialect = le16_to_cpu(pSMBr->DialectIndex);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000412 cFYI(1, "Dialect: %d", dialect);
Steve French254e55e2006-06-04 05:53:15 +0000413 /* Check wct = 1 error case */
Steve French790fe572007-07-07 19:25:05 +0000414 if ((pSMBr->hdr.WordCount < 13) || (dialect == BAD_PROT)) {
Steve French254e55e2006-06-04 05:53:15 +0000415 /* core returns wct = 1, but we do not ask for core - otherwise
Steve French50c2f752007-07-13 00:33:32 +0000416 small wct just comes when dialect index is -1 indicating we
Steve French254e55e2006-06-04 05:53:15 +0000417 could not negotiate a common dialect */
418 rc = -EOPNOTSUPP;
419 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000420#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French790fe572007-07-07 19:25:05 +0000421 } else if ((pSMBr->hdr.WordCount == 13)
Al Viro733f99a2006-10-14 16:48:26 +0100422 && ((dialect == LANMAN_PROT)
423 || (dialect == LANMAN2_PROT))) {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000424 __s16 tmp;
Steve French50c2f752007-07-13 00:33:32 +0000425 struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
Steve French254e55e2006-06-04 05:53:15 +0000426
Steve French790fe572007-07-07 19:25:05 +0000427 if ((secFlags & CIFSSEC_MAY_LANMAN) ||
Steve French750d1152006-06-27 06:28:30 +0000428 (secFlags & CIFSSEC_MAY_PLNTXT))
Steve French254e55e2006-06-04 05:53:15 +0000429 server->secType = LANMAN;
430 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000431 cERROR(1, "mount failed weak security disabled"
432 " in /proc/fs/cifs/SecurityFlags");
Steve French39798772006-05-31 22:40:51 +0000433 rc = -EOPNOTSUPP;
434 goto neg_err_exit;
Steve French50c2f752007-07-13 00:33:32 +0000435 }
Steve French254e55e2006-06-04 05:53:15 +0000436 server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
437 server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
438 server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
Steve French39798772006-05-31 22:40:51 +0000439 (__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000440 server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
Steve French254e55e2006-06-04 05:53:15 +0000441 GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
442 /* even though we do not use raw we might as well set this
443 accurately, in case we ever find a need for it */
Steve French790fe572007-07-07 19:25:05 +0000444 if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
Steve Frencheca6acf2009-02-20 05:43:09 +0000445 server->max_rw = 0xFF00;
Steve French254e55e2006-06-04 05:53:15 +0000446 server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
447 } else {
Steve Frencheca6acf2009-02-20 05:43:09 +0000448 server->max_rw = 0;/* do not need to use raw anyway */
Steve French254e55e2006-06-04 05:53:15 +0000449 server->capabilities = CAP_MPX_MODE;
450 }
Steve Frenchb815f1e52006-10-02 05:53:29 +0000451 tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
Steve French1a70d652006-10-02 05:59:18 +0000452 if (tmp == -1) {
Steve French25ee4a92006-09-30 00:54:23 +0000453 /* OS/2 often does not set timezone therefore
454 * we must use server time to calc time zone.
Steve Frenchb815f1e52006-10-02 05:53:29 +0000455 * Could deviate slightly from the right zone.
456 * Smallest defined timezone difference is 15 minutes
457 * (i.e. Nepal). Rounding up/down is done to match
458 * this requirement.
Steve French25ee4a92006-09-30 00:54:23 +0000459 */
Steve Frenchb815f1e52006-10-02 05:53:29 +0000460 int val, seconds, remain, result;
Steve French25ee4a92006-09-30 00:54:23 +0000461 struct timespec ts, utc;
462 utc = CURRENT_TIME;
Jeff Laytonc4a2c082009-05-27 09:37:33 -0400463 ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
464 rsp->SrvTime.Time, 0);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000465 cFYI(1, "SrvTime %d sec since 1970 (utc: %d) diff: %d",
Steve French50c2f752007-07-13 00:33:32 +0000466 (int)ts.tv_sec, (int)utc.tv_sec,
Joe Perchesb6b38f72010-04-21 03:50:45 +0000467 (int)(utc.tv_sec - ts.tv_sec));
Steve Frenchb815f1e52006-10-02 05:53:29 +0000468 val = (int)(utc.tv_sec - ts.tv_sec);
Andre Haupt8594c152007-08-30 20:18:41 +0000469 seconds = abs(val);
Steve French947a5062006-10-02 05:55:25 +0000470 result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000471 remain = seconds % MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000472 if (remain >= (MIN_TZ_ADJ / 2))
Steve Frenchb815f1e52006-10-02 05:53:29 +0000473 result += MIN_TZ_ADJ;
Steve French790fe572007-07-07 19:25:05 +0000474 if (val < 0)
Steve Frenchad7a2922008-02-07 23:25:02 +0000475 result = -result;
Steve Frenchb815f1e52006-10-02 05:53:29 +0000476 server->timeAdj = result;
Steve French25ee4a92006-09-30 00:54:23 +0000477 } else {
Steve Frenchb815f1e52006-10-02 05:53:29 +0000478 server->timeAdj = (int)tmp;
479 server->timeAdj *= 60; /* also in seconds */
Steve French25ee4a92006-09-30 00:54:23 +0000480 }
Joe Perchesb6b38f72010-04-21 03:50:45 +0000481 cFYI(1, "server->timeAdj: %d seconds", server->timeAdj);
Steve French25ee4a92006-09-30 00:54:23 +0000482
Steve French39798772006-05-31 22:40:51 +0000483
Steve French254e55e2006-06-04 05:53:15 +0000484 /* BB get server time for time conversions and add
Steve French50c2f752007-07-13 00:33:32 +0000485 code to use it and timezone since this is not UTC */
Steve French39798772006-05-31 22:40:51 +0000486
Steve French50c2f752007-07-13 00:33:32 +0000487 if (rsp->EncryptionKeyLength ==
Steve French25ee4a92006-09-30 00:54:23 +0000488 cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
Steve French254e55e2006-06-04 05:53:15 +0000489 memcpy(server->cryptKey, rsp->EncryptionKey,
490 CIFS_CRYPTO_KEY_SIZE);
491 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
492 rc = -EIO; /* need cryptkey unless plain text */
493 goto neg_err_exit;
494 }
Steve French39798772006-05-31 22:40:51 +0000495
Steve Frenchf19159d2010-04-21 04:12:10 +0000496 cFYI(1, "LANMAN negotiated");
Steve French254e55e2006-06-04 05:53:15 +0000497 /* we will not end up setting signing flags - as no signing
498 was in LANMAN and server did not return the flags on */
499 goto signing_check;
Steve French7c7b25b2006-06-01 19:20:10 +0000500#else /* weak security disabled */
Steve French790fe572007-07-07 19:25:05 +0000501 } else if (pSMBr->hdr.WordCount == 13) {
Steve Frenchf19159d2010-04-21 04:12:10 +0000502 cERROR(1, "mount failed, cifs module not built "
503 "with CIFS_WEAK_PW_HASH support");
Dan Carpenter8212cf72010-03-15 11:22:26 +0300504 rc = -EOPNOTSUPP;
Steve French7c7b25b2006-06-01 19:20:10 +0000505#endif /* WEAK_PW_HASH */
Steve French254e55e2006-06-04 05:53:15 +0000506 goto neg_err_exit;
Steve French790fe572007-07-07 19:25:05 +0000507 } else if (pSMBr->hdr.WordCount != 17) {
Steve French254e55e2006-06-04 05:53:15 +0000508 /* unknown wct */
509 rc = -EOPNOTSUPP;
510 goto neg_err_exit;
511 }
512 /* else wct == 17 NTLM */
513 server->secMode = pSMBr->SecurityMode;
Steve French790fe572007-07-07 19:25:05 +0000514 if ((server->secMode & SECMODE_USER) == 0)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000515 cFYI(1, "share mode security");
Steve French39798772006-05-31 22:40:51 +0000516
Steve French790fe572007-07-07 19:25:05 +0000517 if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000518#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French750d1152006-06-27 06:28:30 +0000519 if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
Steve Frenchbdc4bf6e2006-06-02 22:57:13 +0000520#endif /* CIFS_WEAK_PW_HASH */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000521 cERROR(1, "Server requests plain text password"
522 " but client support disabled");
Steve French9312f672006-06-04 22:21:07 +0000523
Steve French790fe572007-07-07 19:25:05 +0000524 if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
Steve French254e55e2006-06-04 05:53:15 +0000525 server->secType = NTLMv2;
Steve French790fe572007-07-07 19:25:05 +0000526 else if (secFlags & CIFSSEC_MAY_NTLM)
Steve French254e55e2006-06-04 05:53:15 +0000527 server->secType = NTLM;
Steve French790fe572007-07-07 19:25:05 +0000528 else if (secFlags & CIFSSEC_MAY_NTLMV2)
Steve Frenchf40c5622006-06-28 00:13:38 +0000529 server->secType = NTLMv2;
Steve Frencha0136892007-10-04 20:05:09 +0000530 else if (secFlags & CIFSSEC_MAY_KRB5)
531 server->secType = Kerberos;
Steve Frenchac683922009-05-06 04:16:04 +0000532 else if (secFlags & CIFSSEC_MAY_NTLMSSP)
Steve Frenchf46c7232009-06-25 03:04:20 +0000533 server->secType = RawNTLMSSP;
Steve Frencha0136892007-10-04 20:05:09 +0000534 else if (secFlags & CIFSSEC_MAY_LANMAN)
535 server->secType = LANMAN;
536/* #ifdef CONFIG_CIFS_EXPERIMENTAL
537 else if (secFlags & CIFSSEC_MAY_PLNTXT)
538 server->secType = ??
539#endif */
540 else {
541 rc = -EOPNOTSUPP;
Joe Perchesb6b38f72010-04-21 03:50:45 +0000542 cERROR(1, "Invalid security type");
Steve Frencha0136892007-10-04 20:05:09 +0000543 goto neg_err_exit;
544 }
545 /* else ... any others ...? */
Steve French7c7b25b2006-06-01 19:20:10 +0000546
Steve French254e55e2006-06-04 05:53:15 +0000547 /* one byte, so no need to convert this or EncryptionKeyLen from
548 little endian */
549 server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
550 /* probably no need to store and check maxvcs */
551 server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 (__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
Steve Frencheca6acf2009-02-20 05:43:09 +0000553 server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
Joe Perchesb6b38f72010-04-21 03:50:45 +0000554 cFYI(DBG2, "Max buf = %d", ses->server->maxBuf);
Steve French254e55e2006-06-04 05:53:15 +0000555 GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
556 server->capabilities = le32_to_cpu(pSMBr->Capabilities);
Steve Frenchb815f1e52006-10-02 05:53:29 +0000557 server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
558 server->timeAdj *= 60;
Steve French254e55e2006-06-04 05:53:15 +0000559 if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
560 memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
561 CIFS_CRYPTO_KEY_SIZE);
562 } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
563 && (pSMBr->EncryptionKeyLength == 0)) {
564 /* decode security blob */
565 } else if (server->secMode & SECMODE_PW_ENCRYPT) {
566 rc = -EIO; /* no crypt key only if plain text pwd */
567 goto neg_err_exit;
568 }
569
570 /* BB might be helpful to save off the domain of server here */
571
Steve French50c2f752007-07-13 00:33:32 +0000572 if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
Steve French254e55e2006-06-04 05:53:15 +0000573 (server->capabilities & CAP_EXTENDED_SECURITY)) {
574 count = pSMBr->ByteCount;
Jeff Laytone187e442007-10-16 17:10:44 +0000575 if (count < 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 rc = -EIO;
Jeff Laytone187e442007-10-16 17:10:44 +0000577 goto neg_err_exit;
578 }
Jeff Laytone7ddee92008-11-14 13:44:38 -0500579 read_lock(&cifs_tcp_ses_lock);
580 if (server->srv_count > 1) {
581 read_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000582 if (memcmp(server->server_GUID,
583 pSMBr->u.extended_response.
584 GUID, 16) != 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000585 cFYI(1, "server UID changed");
Steve French254e55e2006-06-04 05:53:15 +0000586 memcpy(server->server_GUID,
Jeff Laytone187e442007-10-16 17:10:44 +0000587 pSMBr->u.extended_response.GUID,
588 16);
589 }
Jeff Laytone7ddee92008-11-14 13:44:38 -0500590 } else {
591 read_unlock(&cifs_tcp_ses_lock);
Jeff Laytone187e442007-10-16 17:10:44 +0000592 memcpy(server->server_GUID,
593 pSMBr->u.extended_response.GUID, 16);
Jeff Laytone7ddee92008-11-14 13:44:38 -0500594 }
Jeff Laytone187e442007-10-16 17:10:44 +0000595
596 if (count == 16) {
597 server->secType = RawNTLMSSP;
Steve French254e55e2006-06-04 05:53:15 +0000598 } else {
599 rc = decode_negTokenInit(pSMBr->u.extended_response.
600 SecurityBlob,
601 count - 16,
602 &server->secType);
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000603 if (rc == 1)
Jeff Laytone5459372007-11-03 05:11:06 +0000604 rc = 0;
Shirish Pargaonkaref571ca2008-07-24 15:56:05 +0000605 else
Steve French254e55e2006-06-04 05:53:15 +0000606 rc = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 }
Steve French254e55e2006-06-04 05:53:15 +0000608 } else
609 server->capabilities &= ~CAP_EXTENDED_SECURITY;
610
Steve French6344a422006-06-12 04:18:35 +0000611#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French254e55e2006-06-04 05:53:15 +0000612signing_check:
Steve French6344a422006-06-12 04:18:35 +0000613#endif
Steve French762e5ab2007-06-28 18:41:42 +0000614 if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
615 /* MUST_SIGN already includes the MAY_SIGN FLAG
616 so if this is zero it means that signing is disabled */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000617 cFYI(1, "Signing disabled");
Steve Frenchabb63d62007-10-18 02:58:40 +0000618 if (server->secMode & SECMODE_SIGN_REQUIRED) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000619 cERROR(1, "Server requires "
Jeff Layton7111d212007-10-16 16:50:25 +0000620 "packet signing to be enabled in "
Joe Perchesb6b38f72010-04-21 03:50:45 +0000621 "/proc/fs/cifs/SecurityFlags.");
Steve Frenchabb63d62007-10-18 02:58:40 +0000622 rc = -EOPNOTSUPP;
623 }
Steve French50c2f752007-07-13 00:33:32 +0000624 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000625 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Steve French762e5ab2007-06-28 18:41:42 +0000626 } else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
627 /* signing required */
Joe Perchesb6b38f72010-04-21 03:50:45 +0000628 cFYI(1, "Must sign - secFlags 0x%x", secFlags);
Steve French762e5ab2007-06-28 18:41:42 +0000629 if ((server->secMode &
630 (SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +0000631 cERROR(1, "signing required but server lacks support");
Jeff38c10a12007-07-06 21:10:07 +0000632 rc = -EOPNOTSUPP;
Steve French762e5ab2007-06-28 18:41:42 +0000633 } else
634 server->secMode |= SECMODE_SIGN_REQUIRED;
635 } else {
636 /* signing optional ie CIFSSEC_MAY_SIGN */
Steve French790fe572007-07-07 19:25:05 +0000637 if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
Steve French50c2f752007-07-13 00:33:32 +0000638 server->secMode &=
Steve French254e55e2006-06-04 05:53:15 +0000639 ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 }
Steve French50c2f752007-07-13 00:33:32 +0000641
642neg_err_exit:
Steve French4a6d87f2005-08-13 08:15:54 -0700643 cifs_buf_release(pSMB);
Steve French254e55e2006-06-04 05:53:15 +0000644
Joe Perchesb6b38f72010-04-21 03:50:45 +0000645 cFYI(1, "negprot rc %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 return rc;
647}
648
649int
650CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
651{
652 struct smb_hdr *smb_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
Joe Perchesb6b38f72010-04-21 03:50:45 +0000655 cFYI(1, "In tree disconnect");
Jeff Laytonf1987b42008-11-15 11:12:47 -0500656
657 /* BB: do we need to check this? These should never be NULL. */
658 if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
659 return -EIO;
660
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 /*
Jeff Laytonf1987b42008-11-15 11:12:47 -0500662 * No need to return error on this operation if tid invalidated and
663 * closed on server already e.g. due to tcp session crashing. Also,
664 * the tcon is no longer on the list, so no need to take lock before
665 * checking this.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 */
Steve French268875b2009-06-25 00:29:21 +0000667 if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
Steve French50c2f752007-07-13 00:33:32 +0000668 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
Steve French50c2f752007-07-13 00:33:32 +0000670 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
Steve French09d1db52005-04-28 22:41:08 -0700671 (void **)&smb_buffer);
Jeff Laytonf1987b42008-11-15 11:12:47 -0500672 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 return rc;
Steve French133672e2007-11-13 22:41:37 +0000674
675 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000677 cFYI(1, "Tree disconnect failed %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
Steve French50c2f752007-07-13 00:33:32 +0000679 /* No need to return error on this operation if tid invalidated and
Jeff Laytonf1987b42008-11-15 11:12:47 -0500680 closed on server already e.g. due to tcp session crashing */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 if (rc == -EAGAIN)
682 rc = 0;
683
684 return rc;
685}
686
687int
688CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
689{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 LOGOFF_ANDX_REQ *pSMB;
691 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
Joe Perchesb6b38f72010-04-21 03:50:45 +0000693 cFYI(1, "In SMBLogoff for session disconnect");
Jeff Layton14fbf502008-11-14 13:53:46 -0500694
695 /*
696 * BB: do we need to check validity of ses and server? They should
697 * always be valid since we have an active reference. If not, that
698 * should probably be a BUG()
699 */
700 if (!ses || !ses->server)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 return -EIO;
702
Steve Frenchd7b619c2010-02-25 05:36:46 +0000703 mutex_lock(&ses->session_mutex);
Steve French3b795212008-11-13 19:45:32 +0000704 if (ses->need_reconnect)
705 goto session_already_dead; /* no need to send SMBlogoff if uid
706 already closed due to reconnect */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
708 if (rc) {
Steve Frenchd7b619c2010-02-25 05:36:46 +0000709 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 return rc;
711 }
712
Steve French3b795212008-11-13 19:45:32 +0000713 pSMB->hdr.Mid = GetNextMid(ses->server);
Steve French1982c342005-08-17 12:38:22 -0700714
Steve French3b795212008-11-13 19:45:32 +0000715 if (ses->server->secMode &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
717 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
719 pSMB->hdr.Uid = ses->Suid;
720
721 pSMB->AndXCommand = 0xFF;
Steve French133672e2007-11-13 22:41:37 +0000722 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
Steve French3b795212008-11-13 19:45:32 +0000723session_already_dead:
Steve Frenchd7b619c2010-02-25 05:36:46 +0000724 mutex_unlock(&ses->session_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725
726 /* if session dead then we do not need to do ulogoff,
Steve French50c2f752007-07-13 00:33:32 +0000727 since server closed smb session, no sense reporting
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 error */
729 if (rc == -EAGAIN)
730 rc = 0;
731 return rc;
732}
733
734int
Steve French2d785a52007-07-15 01:48:57 +0000735CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
736 __u16 type, const struct nls_table *nls_codepage, int remap)
737{
738 TRANSACTION2_SPI_REQ *pSMB = NULL;
739 TRANSACTION2_SPI_RSP *pSMBr = NULL;
740 struct unlink_psx_rq *pRqD;
741 int name_len;
742 int rc = 0;
743 int bytes_returned = 0;
744 __u16 params, param_offset, offset, byte_count;
745
Joe Perchesb6b38f72010-04-21 03:50:45 +0000746 cFYI(1, "In POSIX delete");
Steve French2d785a52007-07-15 01:48:57 +0000747PsxDelete:
748 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
749 (void **) &pSMBr);
750 if (rc)
751 return rc;
752
753 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
754 name_len =
755 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
756 PATH_MAX, nls_codepage, remap);
757 name_len++; /* trailing null */
758 name_len *= 2;
759 } else { /* BB add path length overrun check */
760 name_len = strnlen(fileName, PATH_MAX);
761 name_len++; /* trailing null */
762 strncpy(pSMB->FileName, fileName, name_len);
763 }
764
765 params = 6 + name_len;
766 pSMB->MaxParameterCount = cpu_to_le16(2);
767 pSMB->MaxDataCount = 0; /* BB double check this with jra */
768 pSMB->MaxSetupCount = 0;
769 pSMB->Reserved = 0;
770 pSMB->Flags = 0;
771 pSMB->Timeout = 0;
772 pSMB->Reserved2 = 0;
773 param_offset = offsetof(struct smb_com_transaction2_spi_req,
774 InformationLevel) - 4;
775 offset = param_offset + params;
776
777 /* Setup pointer to Request Data (inode type) */
778 pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
779 pRqD->type = cpu_to_le16(type);
780 pSMB->ParameterOffset = cpu_to_le16(param_offset);
781 pSMB->DataOffset = cpu_to_le16(offset);
782 pSMB->SetupCount = 1;
783 pSMB->Reserved3 = 0;
784 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
785 byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq);
786
787 pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
788 pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
789 pSMB->ParameterCount = cpu_to_le16(params);
790 pSMB->TotalParameterCount = pSMB->ParameterCount;
791 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
792 pSMB->Reserved4 = 0;
793 pSMB->hdr.smb_buf_length += byte_count;
794 pSMB->ByteCount = cpu_to_le16(byte_count);
795 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
796 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +0000797 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000798 cFYI(1, "Posix delete returned %d", rc);
Steve French2d785a52007-07-15 01:48:57 +0000799 cifs_buf_release(pSMB);
800
801 cifs_stats_inc(&tcon->num_deletes);
802
803 if (rc == -EAGAIN)
804 goto PsxDelete;
805
806 return rc;
807}
808
809int
Steve French737b7582005-04-28 22:41:06 -0700810CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
811 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812{
813 DELETE_FILE_REQ *pSMB = NULL;
814 DELETE_FILE_RSP *pSMBr = NULL;
815 int rc = 0;
816 int bytes_returned;
817 int name_len;
818
819DelFileRetry:
820 rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
821 (void **) &pSMBr);
822 if (rc)
823 return rc;
824
825 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
826 name_len =
Steve French50c2f752007-07-13 00:33:32 +0000827 cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
Steve French737b7582005-04-28 22:41:06 -0700828 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 name_len++; /* trailing null */
830 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700831 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 name_len = strnlen(fileName, PATH_MAX);
833 name_len++; /* trailing null */
834 strncpy(pSMB->fileName, fileName, name_len);
835 }
836 pSMB->SearchAttributes =
837 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
838 pSMB->BufferFormat = 0x04;
839 pSMB->hdr.smb_buf_length += name_len + 1;
840 pSMB->ByteCount = cpu_to_le16(name_len + 1);
841 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
842 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700843 cifs_stats_inc(&tcon->num_deletes);
Steve Frenchad7a2922008-02-07 23:25:02 +0000844 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000845 cFYI(1, "Error in RMFile = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
847 cifs_buf_release(pSMB);
848 if (rc == -EAGAIN)
849 goto DelFileRetry;
850
851 return rc;
852}
853
854int
Steve French50c2f752007-07-13 00:33:32 +0000855CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
Steve French737b7582005-04-28 22:41:06 -0700856 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857{
858 DELETE_DIRECTORY_REQ *pSMB = NULL;
859 DELETE_DIRECTORY_RSP *pSMBr = NULL;
860 int rc = 0;
861 int bytes_returned;
862 int name_len;
863
Joe Perchesb6b38f72010-04-21 03:50:45 +0000864 cFYI(1, "In CIFSSMBRmDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865RmDirRetry:
866 rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
867 (void **) &pSMBr);
868 if (rc)
869 return rc;
870
871 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French737b7582005-04-28 22:41:06 -0700872 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
873 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 name_len++; /* trailing null */
875 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700876 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 name_len = strnlen(dirName, PATH_MAX);
878 name_len++; /* trailing null */
879 strncpy(pSMB->DirName, dirName, name_len);
880 }
881
882 pSMB->BufferFormat = 0x04;
883 pSMB->hdr.smb_buf_length += name_len + 1;
884 pSMB->ByteCount = cpu_to_le16(name_len + 1);
885 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
886 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700887 cifs_stats_inc(&tcon->num_rmdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000888 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000889 cFYI(1, "Error in RMDir = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890
891 cifs_buf_release(pSMB);
892 if (rc == -EAGAIN)
893 goto RmDirRetry;
894 return rc;
895}
896
897int
898CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -0700899 const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900{
901 int rc = 0;
902 CREATE_DIRECTORY_REQ *pSMB = NULL;
903 CREATE_DIRECTORY_RSP *pSMBr = NULL;
904 int bytes_returned;
905 int name_len;
906
Joe Perchesb6b38f72010-04-21 03:50:45 +0000907 cFYI(1, "In CIFSSMBMkDir");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908MkDirRetry:
909 rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
910 (void **) &pSMBr);
911 if (rc)
912 return rc;
913
914 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +0000915 name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
Steve French737b7582005-04-28 22:41:06 -0700916 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 name_len++; /* trailing null */
918 name_len *= 2;
Steve French09d1db52005-04-28 22:41:08 -0700919 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 name_len = strnlen(name, PATH_MAX);
921 name_len++; /* trailing null */
922 strncpy(pSMB->DirName, name, name_len);
923 }
924
925 pSMB->BufferFormat = 0x04;
926 pSMB->hdr.smb_buf_length += name_len + 1;
927 pSMB->ByteCount = cpu_to_le16(name_len + 1);
928 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
929 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -0700930 cifs_stats_inc(&tcon->num_mkdirs);
Steve Frenchad7a2922008-02-07 23:25:02 +0000931 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +0000932 cFYI(1, "Error in Mkdir = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -0700933
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 cifs_buf_release(pSMB);
935 if (rc == -EAGAIN)
936 goto MkDirRetry;
937 return rc;
938}
939
Steve French2dd29d32007-04-23 22:07:35 +0000940int
941CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags,
Steve Frenchad7a2922008-02-07 23:25:02 +0000942 __u64 mode, __u16 *netfid, FILE_UNIX_BASIC_INFO *pRetData,
Steve French50c2f752007-07-13 00:33:32 +0000943 __u32 *pOplock, const char *name,
Steve French2dd29d32007-04-23 22:07:35 +0000944 const struct nls_table *nls_codepage, int remap)
945{
946 TRANSACTION2_SPI_REQ *pSMB = NULL;
947 TRANSACTION2_SPI_RSP *pSMBr = NULL;
948 int name_len;
949 int rc = 0;
950 int bytes_returned = 0;
Steve French2dd29d32007-04-23 22:07:35 +0000951 __u16 params, param_offset, offset, byte_count, count;
Steve Frenchad7a2922008-02-07 23:25:02 +0000952 OPEN_PSX_REQ *pdata;
953 OPEN_PSX_RSP *psx_rsp;
Steve French2dd29d32007-04-23 22:07:35 +0000954
Joe Perchesb6b38f72010-04-21 03:50:45 +0000955 cFYI(1, "In POSIX Create");
Steve French2dd29d32007-04-23 22:07:35 +0000956PsxCreat:
957 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
958 (void **) &pSMBr);
959 if (rc)
960 return rc;
961
962 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
963 name_len =
964 cifsConvertToUCS((__le16 *) pSMB->FileName, name,
965 PATH_MAX, nls_codepage, remap);
966 name_len++; /* trailing null */
967 name_len *= 2;
968 } else { /* BB improve the check for buffer overruns BB */
969 name_len = strnlen(name, PATH_MAX);
970 name_len++; /* trailing null */
971 strncpy(pSMB->FileName, name, name_len);
972 }
973
974 params = 6 + name_len;
975 count = sizeof(OPEN_PSX_REQ);
976 pSMB->MaxParameterCount = cpu_to_le16(2);
977 pSMB->MaxDataCount = cpu_to_le16(1000); /* large enough */
978 pSMB->MaxSetupCount = 0;
979 pSMB->Reserved = 0;
980 pSMB->Flags = 0;
981 pSMB->Timeout = 0;
982 pSMB->Reserved2 = 0;
983 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +0000984 InformationLevel) - 4;
Steve French2dd29d32007-04-23 22:07:35 +0000985 offset = param_offset + params;
Steve French2dd29d32007-04-23 22:07:35 +0000986 pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset);
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +0000987 pdata->Level = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
Steve French2dd29d32007-04-23 22:07:35 +0000988 pdata->Permissions = cpu_to_le64(mode);
Steve French50c2f752007-07-13 00:33:32 +0000989 pdata->PosixOpenFlags = cpu_to_le32(posix_flags);
Steve French2dd29d32007-04-23 22:07:35 +0000990 pdata->OpenFlags = cpu_to_le32(*pOplock);
991 pSMB->ParameterOffset = cpu_to_le16(param_offset);
992 pSMB->DataOffset = cpu_to_le16(offset);
993 pSMB->SetupCount = 1;
994 pSMB->Reserved3 = 0;
995 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
996 byte_count = 3 /* pad */ + params + count;
997
998 pSMB->DataCount = cpu_to_le16(count);
999 pSMB->ParameterCount = cpu_to_le16(params);
1000 pSMB->TotalDataCount = pSMB->DataCount;
1001 pSMB->TotalParameterCount = pSMB->ParameterCount;
1002 pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_OPEN);
1003 pSMB->Reserved4 = 0;
Steve French50c2f752007-07-13 00:33:32 +00001004 pSMB->hdr.smb_buf_length += byte_count;
Steve French2dd29d32007-04-23 22:07:35 +00001005 pSMB->ByteCount = cpu_to_le16(byte_count);
1006 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1007 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
1008 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001009 cFYI(1, "Posix create returned %d", rc);
Steve French2dd29d32007-04-23 22:07:35 +00001010 goto psx_create_err;
1011 }
1012
Joe Perchesb6b38f72010-04-21 03:50:45 +00001013 cFYI(1, "copying inode info");
Steve French2dd29d32007-04-23 22:07:35 +00001014 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
1015
1016 if (rc || (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP))) {
1017 rc = -EIO; /* bad smb */
1018 goto psx_create_err;
1019 }
1020
1021 /* copy return information to pRetData */
Steve French50c2f752007-07-13 00:33:32 +00001022 psx_rsp = (OPEN_PSX_RSP *)((char *) &pSMBr->hdr.Protocol
Steve French2dd29d32007-04-23 22:07:35 +00001023 + le16_to_cpu(pSMBr->t2.DataOffset));
Steve French50c2f752007-07-13 00:33:32 +00001024
Steve French2dd29d32007-04-23 22:07:35 +00001025 *pOplock = le16_to_cpu(psx_rsp->OplockFlags);
Steve French790fe572007-07-07 19:25:05 +00001026 if (netfid)
Steve French2dd29d32007-04-23 22:07:35 +00001027 *netfid = psx_rsp->Fid; /* cifs fid stays in le */
1028 /* Let caller know file was created so we can set the mode. */
1029 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001030 if (cpu_to_le32(FILE_CREATE) == psx_rsp->CreateAction)
Steve French2dd29d32007-04-23 22:07:35 +00001031 *pOplock |= CIFS_CREATE_ACTION;
1032 /* check to make sure response data is there */
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001033 if (psx_rsp->ReturnedLevel != cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC)) {
1034 pRetData->Type = cpu_to_le32(-1); /* unknown */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001035 cFYI(DBG2, "unknown type");
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001036 } else {
Steve French790fe572007-07-07 19:25:05 +00001037 if (pSMBr->ByteCount < sizeof(OPEN_PSX_RSP)
Steve French2dd29d32007-04-23 22:07:35 +00001038 + sizeof(FILE_UNIX_BASIC_INFO)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001039 cERROR(1, "Open response data too small");
Cyril Gorcunov8f2376a2007-10-14 17:58:43 +00001040 pRetData->Type = cpu_to_le32(-1);
Steve French2dd29d32007-04-23 22:07:35 +00001041 goto psx_create_err;
1042 }
Steve French50c2f752007-07-13 00:33:32 +00001043 memcpy((char *) pRetData,
Steve Frenchcbac3cb2007-04-25 11:46:06 +00001044 (char *)psx_rsp + sizeof(OPEN_PSX_RSP),
Steve French26f57362007-08-30 22:09:15 +00001045 sizeof(FILE_UNIX_BASIC_INFO));
Steve French2dd29d32007-04-23 22:07:35 +00001046 }
Steve French2dd29d32007-04-23 22:07:35 +00001047
1048psx_create_err:
1049 cifs_buf_release(pSMB);
1050
Steve French65bc98b2009-07-10 15:27:25 +00001051 if (posix_flags & SMB_O_DIRECTORY)
1052 cifs_stats_inc(&tcon->num_posixmkdirs);
1053 else
1054 cifs_stats_inc(&tcon->num_posixopens);
Steve French2dd29d32007-04-23 22:07:35 +00001055
1056 if (rc == -EAGAIN)
1057 goto PsxCreat;
1058
Steve French50c2f752007-07-13 00:33:32 +00001059 return rc;
Steve French2dd29d32007-04-23 22:07:35 +00001060}
1061
Steve Frencha9d02ad2005-08-24 23:06:05 -07001062static __u16 convert_disposition(int disposition)
1063{
1064 __u16 ofun = 0;
1065
1066 switch (disposition) {
1067 case FILE_SUPERSEDE:
1068 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1069 break;
1070 case FILE_OPEN:
1071 ofun = SMBOPEN_OAPPEND;
1072 break;
1073 case FILE_CREATE:
1074 ofun = SMBOPEN_OCREATE;
1075 break;
1076 case FILE_OPEN_IF:
1077 ofun = SMBOPEN_OCREATE | SMBOPEN_OAPPEND;
1078 break;
1079 case FILE_OVERWRITE:
1080 ofun = SMBOPEN_OTRUNC;
1081 break;
1082 case FILE_OVERWRITE_IF:
1083 ofun = SMBOPEN_OCREATE | SMBOPEN_OTRUNC;
1084 break;
1085 default:
Joe Perchesb6b38f72010-04-21 03:50:45 +00001086 cFYI(1, "unknown disposition %d", disposition);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001087 ofun = SMBOPEN_OAPPEND; /* regular open */
1088 }
1089 return ofun;
1090}
1091
Jeff Layton35fc37d2008-05-14 10:22:03 -07001092static int
1093access_flags_to_smbopen_mode(const int access_flags)
1094{
1095 int masked_flags = access_flags & (GENERIC_READ | GENERIC_WRITE);
1096
1097 if (masked_flags == GENERIC_READ)
1098 return SMBOPEN_READ;
1099 else if (masked_flags == GENERIC_WRITE)
1100 return SMBOPEN_WRITE;
1101
1102 /* just go for read/write */
1103 return SMBOPEN_READWRITE;
1104}
1105
Steve Frencha9d02ad2005-08-24 23:06:05 -07001106int
1107SMBLegacyOpen(const int xid, struct cifsTconInfo *tcon,
1108 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001109 const int access_flags, const int create_options, __u16 *netfid,
1110 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve Frencha9d02ad2005-08-24 23:06:05 -07001111 const struct nls_table *nls_codepage, int remap)
1112{
1113 int rc = -EACCES;
1114 OPENX_REQ *pSMB = NULL;
1115 OPENX_RSP *pSMBr = NULL;
1116 int bytes_returned;
1117 int name_len;
1118 __u16 count;
1119
1120OldOpenRetry:
1121 rc = smb_init(SMB_COM_OPEN_ANDX, 15, tcon, (void **) &pSMB,
1122 (void **) &pSMBr);
1123 if (rc)
1124 return rc;
1125
1126 pSMB->AndXCommand = 0xFF; /* none */
1127
1128 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1129 count = 1; /* account for one byte pad to word boundary */
1130 name_len =
1131 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
1132 fileName, PATH_MAX, nls_codepage, remap);
1133 name_len++; /* trailing null */
1134 name_len *= 2;
1135 } else { /* BB improve check for buffer overruns BB */
1136 count = 0; /* no pad */
1137 name_len = strnlen(fileName, PATH_MAX);
1138 name_len++; /* trailing null */
1139 strncpy(pSMB->fileName, fileName, name_len);
1140 }
1141 if (*pOplock & REQ_OPLOCK)
1142 pSMB->OpenFlags = cpu_to_le16(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001143 else if (*pOplock & REQ_BATCHOPLOCK)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001144 pSMB->OpenFlags = cpu_to_le16(REQ_BATCHOPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001145
Steve Frencha9d02ad2005-08-24 23:06:05 -07001146 pSMB->OpenFlags |= cpu_to_le16(REQ_MORE_INFO);
Jeff Layton35fc37d2008-05-14 10:22:03 -07001147 pSMB->Mode = cpu_to_le16(access_flags_to_smbopen_mode(access_flags));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001148 pSMB->Mode |= cpu_to_le16(0x40); /* deny none */
1149 /* set file as system file if special file such
1150 as fifo and server expecting SFU style and
1151 no Unix extensions */
1152
Steve French790fe572007-07-07 19:25:05 +00001153 if (create_options & CREATE_OPTION_SPECIAL)
1154 pSMB->FileAttributes = cpu_to_le16(ATTR_SYSTEM);
Steve Frenchad7a2922008-02-07 23:25:02 +00001155 else /* BB FIXME BB */
1156 pSMB->FileAttributes = cpu_to_le16(0/*ATTR_NORMAL*/);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001157
Jeff Layton67750fb2008-05-09 22:28:02 +00001158 if (create_options & CREATE_OPTION_READONLY)
1159 pSMB->FileAttributes |= cpu_to_le16(ATTR_READONLY);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001160
1161 /* BB FIXME BB */
Steve French50c2f752007-07-13 00:33:32 +00001162/* pSMB->CreateOptions = cpu_to_le32(create_options &
1163 CREATE_OPTIONS_MASK); */
Steve Frencha9d02ad2005-08-24 23:06:05 -07001164 /* BB FIXME END BB */
Steve French3e87d802005-09-18 20:49:21 -07001165
1166 pSMB->Sattr = cpu_to_le16(ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY);
Steve French70ca7342005-09-22 16:32:06 -07001167 pSMB->OpenFunction = cpu_to_le16(convert_disposition(openDisposition));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001168 count += name_len;
1169 pSMB->hdr.smb_buf_length += count;
1170
1171 pSMB->ByteCount = cpu_to_le16(count);
1172 /* long_op set to 1 to allow for oplock break timeouts */
1173 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001174 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001175 cifs_stats_inc(&tcon->num_opens);
1176 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001177 cFYI(1, "Error in Open = %d", rc);
Steve Frencha9d02ad2005-08-24 23:06:05 -07001178 } else {
1179 /* BB verify if wct == 15 */
1180
Steve French582d21e2008-05-13 04:54:12 +00001181/* *pOplock = pSMBr->OplockLevel; */ /* BB take from action field*/
Steve Frencha9d02ad2005-08-24 23:06:05 -07001182
1183 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1184 /* Let caller know file was created so we can set the mode. */
1185 /* Do we care about the CreateAction in any other cases? */
1186 /* BB FIXME BB */
Steve French790fe572007-07-07 19:25:05 +00001187/* if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve Frencha9d02ad2005-08-24 23:06:05 -07001188 *pOplock |= CIFS_CREATE_ACTION; */
1189 /* BB FIXME END */
1190
Steve French790fe572007-07-07 19:25:05 +00001191 if (pfile_info) {
Steve Frencha9d02ad2005-08-24 23:06:05 -07001192 pfile_info->CreationTime = 0; /* BB convert CreateTime*/
1193 pfile_info->LastAccessTime = 0; /* BB fixme */
1194 pfile_info->LastWriteTime = 0; /* BB fixme */
1195 pfile_info->ChangeTime = 0; /* BB fixme */
Steve French70ca7342005-09-22 16:32:06 -07001196 pfile_info->Attributes =
Steve French50c2f752007-07-13 00:33:32 +00001197 cpu_to_le32(le16_to_cpu(pSMBr->FileAttributes));
Steve Frencha9d02ad2005-08-24 23:06:05 -07001198 /* the file_info buf is endian converted by caller */
Steve French70ca7342005-09-22 16:32:06 -07001199 pfile_info->AllocationSize =
1200 cpu_to_le64(le32_to_cpu(pSMBr->EndOfFile));
1201 pfile_info->EndOfFile = pfile_info->AllocationSize;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001202 pfile_info->NumberOfLinks = cpu_to_le32(1);
Jeff Layton9a8165f2008-10-17 21:03:20 -04001203 pfile_info->DeletePending = 0;
Steve Frencha9d02ad2005-08-24 23:06:05 -07001204 }
1205 }
1206
1207 cifs_buf_release(pSMB);
1208 if (rc == -EAGAIN)
1209 goto OldOpenRetry;
1210 return rc;
1211}
1212
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213int
1214CIFSSMBOpen(const int xid, struct cifsTconInfo *tcon,
1215 const char *fileName, const int openDisposition,
Steve Frenchad7a2922008-02-07 23:25:02 +00001216 const int access_flags, const int create_options, __u16 *netfid,
1217 int *pOplock, FILE_ALL_INFO *pfile_info,
Steve French737b7582005-04-28 22:41:06 -07001218 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219{
1220 int rc = -EACCES;
1221 OPEN_REQ *pSMB = NULL;
1222 OPEN_RSP *pSMBr = NULL;
1223 int bytes_returned;
1224 int name_len;
1225 __u16 count;
1226
1227openRetry:
1228 rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB,
1229 (void **) &pSMBr);
1230 if (rc)
1231 return rc;
1232
1233 pSMB->AndXCommand = 0xFF; /* none */
1234
1235 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1236 count = 1; /* account for one byte pad to word boundary */
1237 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05001238 cifsConvertToUCS((__le16 *) (pSMB->fileName + 1),
Steve French737b7582005-04-28 22:41:06 -07001239 fileName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 name_len++; /* trailing null */
1241 name_len *= 2;
1242 pSMB->NameLength = cpu_to_le16(name_len);
Steve French09d1db52005-04-28 22:41:08 -07001243 } else { /* BB improve check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 count = 0; /* no pad */
1245 name_len = strnlen(fileName, PATH_MAX);
1246 name_len++; /* trailing null */
1247 pSMB->NameLength = cpu_to_le16(name_len);
1248 strncpy(pSMB->fileName, fileName, name_len);
1249 }
1250 if (*pOplock & REQ_OPLOCK)
1251 pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK);
Steve French26f57362007-08-30 22:09:15 +00001252 else if (*pOplock & REQ_BATCHOPLOCK)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 pSMB->DesiredAccess = cpu_to_le32(access_flags);
1255 pSMB->AllocationSize = 0;
Steve Frencheda3c0292005-07-21 15:20:28 -07001256 /* set file as system file if special file such
1257 as fifo and server expecting SFU style and
1258 no Unix extensions */
Steve French790fe572007-07-07 19:25:05 +00001259 if (create_options & CREATE_OPTION_SPECIAL)
Steve Frencheda3c0292005-07-21 15:20:28 -07001260 pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM);
1261 else
1262 pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL);
Jeff Layton67750fb2008-05-09 22:28:02 +00001263
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 /* XP does not handle ATTR_POSIX_SEMANTICS */
1265 /* but it helps speed up case sensitive checks for other
1266 servers such as Samba */
1267 if (tcon->ses->capabilities & CAP_UNIX)
1268 pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);
1269
Jeff Layton67750fb2008-05-09 22:28:02 +00001270 if (create_options & CREATE_OPTION_READONLY)
1271 pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY);
1272
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL);
1274 pSMB->CreateDisposition = cpu_to_le32(openDisposition);
Steve Frencheda3c0292005-07-21 15:20:28 -07001275 pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);
Steve French09d1db52005-04-28 22:41:08 -07001276 /* BB Expirement with various impersonation levels and verify */
1277 pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 pSMB->SecurityFlags =
1279 SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY;
1280
1281 count += name_len;
1282 pSMB->hdr.smb_buf_length += count;
1283
1284 pSMB->ByteCount = cpu_to_le16(count);
1285 /* long_op set to 1 to allow for oplock break timeouts */
1286 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00001287 (struct smb_hdr *)pSMBr, &bytes_returned, CIFS_LONG_OP);
Steve Frencha4544342005-08-24 13:59:35 -07001288 cifs_stats_inc(&tcon->num_opens);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001290 cFYI(1, "Error in Open = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 } else {
Steve French09d1db52005-04-28 22:41:08 -07001292 *pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 *netfid = pSMBr->Fid; /* cifs fid stays in le */
1294 /* Let caller know file was created so we can set the mode. */
1295 /* Do we care about the CreateAction in any other cases? */
Steve French790fe572007-07-07 19:25:05 +00001296 if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction)
Steve French50c2f752007-07-13 00:33:32 +00001297 *pOplock |= CIFS_CREATE_ACTION;
Steve French790fe572007-07-07 19:25:05 +00001298 if (pfile_info) {
Steve French61e74802008-12-03 00:57:54 +00001299 memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime,
1300 36 /* CreationTime to Attributes */);
1301 /* the file_info buf is endian converted by caller */
1302 pfile_info->AllocationSize = pSMBr->AllocationSize;
1303 pfile_info->EndOfFile = pSMBr->EndOfFile;
1304 pfile_info->NumberOfLinks = cpu_to_le32(1);
1305 pfile_info->DeletePending = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 }
Steve Frencha5a2b482005-08-20 21:42:53 -07001308
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 cifs_buf_release(pSMB);
1310 if (rc == -EAGAIN)
1311 goto openRetry;
1312 return rc;
1313}
1314
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315int
Steve French50c2f752007-07-13 00:33:32 +00001316CIFSSMBRead(const int xid, struct cifsTconInfo *tcon, const int netfid,
1317 const unsigned int count, const __u64 lseek, unsigned int *nbytes,
1318 char **buf, int *pbuf_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319{
1320 int rc = -EACCES;
1321 READ_REQ *pSMB = NULL;
1322 READ_RSP *pSMBr = NULL;
1323 char *pReadData = NULL;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001324 int wct;
Steve Frenchec637e32005-12-12 20:53:18 -08001325 int resp_buf_type = 0;
1326 struct kvec iov[1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327
Joe Perchesb6b38f72010-04-21 03:50:45 +00001328 cFYI(1, "Reading %d bytes on fid %d", count, netfid);
Steve French790fe572007-07-07 19:25:05 +00001329 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001330 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001331 else {
Steve Frenchbfa0d752005-08-31 21:50:37 -07001332 wct = 10; /* old style read */
Steve French4c3130e2008-12-09 00:28:16 +00001333 if ((lseek >> 32) > 0) {
1334 /* can not handle this big offset for old */
1335 return -EIO;
1336 }
1337 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338
1339 *nbytes = 0;
Steve Frenchec637e32005-12-12 20:53:18 -08001340 rc = small_smb_init(SMB_COM_READ_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341 if (rc)
1342 return rc;
1343
1344 /* tcon and ses pointer are checked in smb_init */
1345 if (tcon->ses->server == NULL)
1346 return -ECONNABORTED;
1347
Steve Frenchec637e32005-12-12 20:53:18 -08001348 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 pSMB->Fid = netfid;
1350 pSMB->OffsetLow = cpu_to_le32(lseek & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001351 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001352 pSMB->OffsetHigh = cpu_to_le32(lseek >> 32);
Steve Frenchbfa0d752005-08-31 21:50:37 -07001353
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 pSMB->Remaining = 0;
1355 pSMB->MaxCount = cpu_to_le16(count & 0xFFFF);
1356 pSMB->MaxCountHigh = cpu_to_le32(count >> 16);
Steve French790fe572007-07-07 19:25:05 +00001357 if (wct == 12)
Steve Frenchbfa0d752005-08-31 21:50:37 -07001358 pSMB->ByteCount = 0; /* no need to do le conversion since 0 */
1359 else {
1360 /* old style read */
Steve French50c2f752007-07-13 00:33:32 +00001361 struct smb_com_readx_req *pSMBW =
Steve Frenchbfa0d752005-08-31 21:50:37 -07001362 (struct smb_com_readx_req *)pSMB;
Steve Frenchec637e32005-12-12 20:53:18 -08001363 pSMBW->ByteCount = 0;
Steve Frenchbfa0d752005-08-31 21:50:37 -07001364 }
Steve Frenchec637e32005-12-12 20:53:18 -08001365
1366 iov[0].iov_base = (char *)pSMB;
1367 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
Steve Frencha761ac52007-10-18 21:45:27 +00001368 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
Steve French133672e2007-11-13 22:41:37 +00001369 &resp_buf_type, CIFS_STD_OP | CIFS_LOG_ERROR);
Steve Frencha4544342005-08-24 13:59:35 -07001370 cifs_stats_inc(&tcon->num_reads);
Steve Frenchec637e32005-12-12 20:53:18 -08001371 pSMBr = (READ_RSP *)iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001373 cERROR(1, "Send error in read = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 } else {
1375 int data_length = le16_to_cpu(pSMBr->DataLengthHigh);
1376 data_length = data_length << 16;
1377 data_length += le16_to_cpu(pSMBr->DataLength);
1378 *nbytes = data_length;
1379
1380 /*check that DataLength would not go beyond end of SMB */
Steve Frenchec637e32005-12-12 20:53:18 -08001381 if ((data_length > CIFSMaxBufSize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382 || (data_length > count)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001383 cFYI(1, "bad length %d for count %d",
1384 data_length, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 rc = -EIO;
1386 *nbytes = 0;
1387 } else {
Steve Frenchec637e32005-12-12 20:53:18 -08001388 pReadData = (char *) (&pSMBr->hdr.Protocol) +
Steve French26f57362007-08-30 22:09:15 +00001389 le16_to_cpu(pSMBr->DataOffset);
1390/* if (rc = copy_to_user(buf, pReadData, data_length)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001391 cERROR(1, "Faulting on read rc = %d",rc);
Steve French50c2f752007-07-13 00:33:32 +00001392 rc = -EFAULT;
Steve French26f57362007-08-30 22:09:15 +00001393 }*/ /* can not use copy_to_user when using page cache*/
Steve French790fe572007-07-07 19:25:05 +00001394 if (*buf)
Steve French50c2f752007-07-13 00:33:32 +00001395 memcpy(*buf, pReadData, data_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396 }
1397 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
Steve French4b8f9302006-02-26 16:41:18 +00001399/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001400 if (*buf) {
1401 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001402 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001403 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001404 cifs_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001405 } else if (resp_buf_type != CIFS_NO_BUFFER) {
Steve French50c2f752007-07-13 00:33:32 +00001406 /* return buffer to caller to free */
1407 *buf = iov[0].iov_base;
Steve French790fe572007-07-07 19:25:05 +00001408 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001409 *pbuf_type = CIFS_SMALL_BUFFER;
Steve French790fe572007-07-07 19:25:05 +00001410 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001411 *pbuf_type = CIFS_LARGE_BUFFER;
Steve French6cec2ae2006-02-22 17:31:52 -06001412 } /* else no valid buffer on return - leave as null */
Steve Frenchec637e32005-12-12 20:53:18 -08001413
1414 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 since file handle passed in no longer valid */
1416 return rc;
1417}
1418
Steve Frenchec637e32005-12-12 20:53:18 -08001419
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420int
1421CIFSSMBWrite(const int xid, struct cifsTconInfo *tcon,
1422 const int netfid, const unsigned int count,
1423 const __u64 offset, unsigned int *nbytes, const char *buf,
Steve French50c2f752007-07-13 00:33:32 +00001424 const char __user *ubuf, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425{
1426 int rc = -EACCES;
1427 WRITE_REQ *pSMB = NULL;
1428 WRITE_RSP *pSMBr = NULL;
Steve French1c955182005-08-30 20:58:07 -07001429 int bytes_returned, wct;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 __u32 bytes_sent;
1431 __u16 byte_count;
1432
Steve Frencha24e2d72010-04-03 17:20:21 +00001433 *nbytes = 0;
1434
Joe Perchesb6b38f72010-04-21 03:50:45 +00001435 /* cFYI(1, "write at %lld %d bytes", offset, count);*/
Steve French790fe572007-07-07 19:25:05 +00001436 if (tcon->ses == NULL)
Steve French1c955182005-08-30 20:58:07 -07001437 return -ECONNABORTED;
1438
Steve French790fe572007-07-07 19:25:05 +00001439 if (tcon->ses->capabilities & CAP_LARGE_FILES)
Steve French1c955182005-08-30 20:58:07 -07001440 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001441 else {
Steve French1c955182005-08-30 20:58:07 -07001442 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001443 if ((offset >> 32) > 0) {
1444 /* can not handle big offset for old srv */
1445 return -EIO;
1446 }
1447 }
Steve French1c955182005-08-30 20:58:07 -07001448
1449 rc = smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 (void **) &pSMBr);
1451 if (rc)
1452 return rc;
1453 /* tcon and ses pointer are checked in smb_init */
1454 if (tcon->ses->server == NULL)
1455 return -ECONNABORTED;
1456
1457 pSMB->AndXCommand = 0xFF; /* none */
1458 pSMB->Fid = netfid;
1459 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001460 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001461 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Steve French50c2f752007-07-13 00:33:32 +00001462
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 pSMB->Reserved = 0xFFFFFFFF;
1464 pSMB->WriteMode = 0;
1465 pSMB->Remaining = 0;
1466
Steve French50c2f752007-07-13 00:33:32 +00001467 /* Can increase buffer size if buffer is big enough in some cases ie we
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468 can send more if LARGE_WRITE_X capability returned by the server and if
1469 our buffer is big enough or if we convert to iovecs on socket writes
1470 and eliminate the copy to the CIFS buffer */
Steve French790fe572007-07-07 19:25:05 +00001471 if (tcon->ses->capabilities & CAP_LARGE_WRITE_X) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 bytes_sent = min_t(const unsigned int, CIFSMaxBufSize, count);
1473 } else {
1474 bytes_sent = (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE)
1475 & ~0xFF;
1476 }
1477
1478 if (bytes_sent > count)
1479 bytes_sent = count;
1480 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001481 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Steve French790fe572007-07-07 19:25:05 +00001482 if (buf)
Steve French61e74802008-12-03 00:57:54 +00001483 memcpy(pSMB->Data, buf, bytes_sent);
Steve French790fe572007-07-07 19:25:05 +00001484 else if (ubuf) {
1485 if (copy_from_user(pSMB->Data, ubuf, bytes_sent)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 cifs_buf_release(pSMB);
1487 return -EFAULT;
1488 }
Steve Frenche30dcf32005-09-20 20:49:16 -07001489 } else if (count != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 /* No buffer */
1491 cifs_buf_release(pSMB);
1492 return -EINVAL;
Steve Frenche30dcf32005-09-20 20:49:16 -07001493 } /* else setting file size with write of zero bytes */
Steve French790fe572007-07-07 19:25:05 +00001494 if (wct == 14)
Steve Frenche30dcf32005-09-20 20:49:16 -07001495 byte_count = bytes_sent + 1; /* pad */
Steve Frenchad7a2922008-02-07 23:25:02 +00001496 else /* wct == 12 */
Steve Frenche30dcf32005-09-20 20:49:16 -07001497 byte_count = bytes_sent + 5; /* bigger pad, smaller smb hdr */
Steve Frenchad7a2922008-02-07 23:25:02 +00001498
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 pSMB->DataLengthLow = cpu_to_le16(bytes_sent & 0xFFFF);
1500 pSMB->DataLengthHigh = cpu_to_le16(bytes_sent >> 16);
Steve Frenche30dcf32005-09-20 20:49:16 -07001501 pSMB->hdr.smb_buf_length += byte_count;
Steve French1c955182005-08-30 20:58:07 -07001502
Steve French790fe572007-07-07 19:25:05 +00001503 if (wct == 14)
Steve French1c955182005-08-30 20:58:07 -07001504 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00001505 else { /* old style write has byte count 4 bytes earlier
1506 so 4 bytes pad */
1507 struct smb_com_writex_req *pSMBW =
Steve French1c955182005-08-30 20:58:07 -07001508 (struct smb_com_writex_req *)pSMB;
1509 pSMBW->ByteCount = cpu_to_le16(byte_count);
1510 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511
1512 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1513 (struct smb_hdr *) pSMBr, &bytes_returned, long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001514 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00001516 cFYI(1, "Send error in write = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 } else {
1518 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1519 *nbytes = (*nbytes) << 16;
1520 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301521
1522 /*
1523 * Mask off high 16 bits when bytes written as returned by the
1524 * server is greater than bytes requested by the client. Some
1525 * OS/2 servers are known to set incorrect CountHigh values.
1526 */
1527 if (*nbytes > count)
1528 *nbytes &= 0xFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 }
1530
1531 cifs_buf_release(pSMB);
1532
Steve French50c2f752007-07-13 00:33:32 +00001533 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 since file handle passed in no longer valid */
1535
1536 return rc;
1537}
1538
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001539int
1540CIFSSMBWrite2(const int xid, struct cifsTconInfo *tcon,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 const int netfid, const unsigned int count,
Steve French3e844692005-10-03 13:37:24 -07001542 const __u64 offset, unsigned int *nbytes, struct kvec *iov,
1543 int n_vec, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544{
1545 int rc = -EACCES;
1546 WRITE_REQ *pSMB = NULL;
Steve Frenchec637e32005-12-12 20:53:18 -08001547 int wct;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001548 int smb_hdr_len;
Steve Frenchec637e32005-12-12 20:53:18 -08001549 int resp_buf_type = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550
Jeff Laytonfbec9ab2009-04-03 13:44:00 -04001551 *nbytes = 0;
1552
Joe Perchesb6b38f72010-04-21 03:50:45 +00001553 cFYI(1, "write2 at %lld %d bytes", (long long)offset, count);
Steve Frenchff7feac2005-11-15 16:45:16 -08001554
Steve French4c3130e2008-12-09 00:28:16 +00001555 if (tcon->ses->capabilities & CAP_LARGE_FILES) {
Steve French8cc64c62005-10-03 13:49:43 -07001556 wct = 14;
Steve French4c3130e2008-12-09 00:28:16 +00001557 } else {
Steve French8cc64c62005-10-03 13:49:43 -07001558 wct = 12;
Steve French4c3130e2008-12-09 00:28:16 +00001559 if ((offset >> 32) > 0) {
1560 /* can not handle big offset for old srv */
1561 return -EIO;
1562 }
1563 }
Steve French8cc64c62005-10-03 13:49:43 -07001564 rc = small_smb_init(SMB_COM_WRITE_ANDX, wct, tcon, (void **) &pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 if (rc)
1566 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 /* tcon and ses pointer are checked in smb_init */
1568 if (tcon->ses->server == NULL)
1569 return -ECONNABORTED;
1570
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001571 pSMB->AndXCommand = 0xFF; /* none */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 pSMB->Fid = netfid;
1573 pSMB->OffsetLow = cpu_to_le32(offset & 0xFFFFFFFF);
Steve French790fe572007-07-07 19:25:05 +00001574 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001575 pSMB->OffsetHigh = cpu_to_le32(offset >> 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 pSMB->Reserved = 0xFFFFFFFF;
1577 pSMB->WriteMode = 0;
1578 pSMB->Remaining = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001579
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 pSMB->DataOffset =
Steve French50c2f752007-07-13 00:33:32 +00001581 cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582
Steve French3e844692005-10-03 13:37:24 -07001583 pSMB->DataLengthLow = cpu_to_le16(count & 0xFFFF);
1584 pSMB->DataLengthHigh = cpu_to_le16(count >> 16);
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001585 smb_hdr_len = pSMB->hdr.smb_buf_length + 1; /* hdr + 1 byte pad */
Steve French790fe572007-07-07 19:25:05 +00001586 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001587 pSMB->hdr.smb_buf_length += count+1;
1588 else /* wct == 12 */
Steve French790fe572007-07-07 19:25:05 +00001589 pSMB->hdr.smb_buf_length += count+5; /* smb data starts later */
1590 if (wct == 14)
Steve French8cc64c62005-10-03 13:49:43 -07001591 pSMB->ByteCount = cpu_to_le16(count + 1);
1592 else /* wct == 12 */ /* bigger pad, smaller smb hdr, keep offset ok */ {
Steve French50c2f752007-07-13 00:33:32 +00001593 struct smb_com_writex_req *pSMBW =
Steve French8cc64c62005-10-03 13:49:43 -07001594 (struct smb_com_writex_req *)pSMB;
1595 pSMBW->ByteCount = cpu_to_le16(count + 5);
1596 }
Steve French3e844692005-10-03 13:37:24 -07001597 iov[0].iov_base = pSMB;
Steve French790fe572007-07-07 19:25:05 +00001598 if (wct == 14)
Steve Frenchec637e32005-12-12 20:53:18 -08001599 iov[0].iov_len = smb_hdr_len + 4;
1600 else /* wct == 12 pad bigger by four bytes */
1601 iov[0].iov_len = smb_hdr_len + 8;
Steve French50c2f752007-07-13 00:33:32 +00001602
Steve French3e844692005-10-03 13:37:24 -07001603
Steve Frenchec637e32005-12-12 20:53:18 -08001604 rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
Steve French133672e2007-11-13 22:41:37 +00001605 long_op);
Steve Frencha4544342005-08-24 13:59:35 -07001606 cifs_stats_inc(&tcon->num_writes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001608 cFYI(1, "Send error Write2 = %d", rc);
Steve French790fe572007-07-07 19:25:05 +00001609 } else if (resp_buf_type == 0) {
Steve Frenchec637e32005-12-12 20:53:18 -08001610 /* presumably this can not happen, but best to be safe */
1611 rc = -EIO;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001612 } else {
Steve Frenchad7a2922008-02-07 23:25:02 +00001613 WRITE_RSP *pSMBr = (WRITE_RSP *)iov[0].iov_base;
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001614 *nbytes = le16_to_cpu(pSMBr->CountHigh);
1615 *nbytes = (*nbytes) << 16;
1616 *nbytes += le16_to_cpu(pSMBr->Count);
Suresh Jayaraman6513a812010-03-31 12:00:03 +05301617
1618 /*
1619 * Mask off high 16 bits when bytes written as returned by the
1620 * server is greater than bytes requested by the client. OS/2
1621 * servers are known to set incorrect CountHigh values.
1622 */
1623 if (*nbytes > count)
1624 *nbytes &= 0xFFFF;
Steve French50c2f752007-07-13 00:33:32 +00001625 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626
Steve French4b8f9302006-02-26 16:41:18 +00001627/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French790fe572007-07-07 19:25:05 +00001628 if (resp_buf_type == CIFS_SMALL_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001629 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00001630 else if (resp_buf_type == CIFS_LARGE_BUFFER)
Steve Frenchec637e32005-12-12 20:53:18 -08001631 cifs_buf_release(iov[0].iov_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632
Steve French50c2f752007-07-13 00:33:32 +00001633 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 since file handle passed in no longer valid */
1635
1636 return rc;
1637}
Steve Frenchd6e04ae2005-06-13 13:24:43 -05001638
1639
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640int
1641CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
1642 const __u16 smb_file_id, const __u64 len,
1643 const __u64 offset, const __u32 numUnlock,
Steve French4b18f2a2008-04-29 00:06:05 +00001644 const __u32 numLock, const __u8 lockType, const bool waitFlag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645{
1646 int rc = 0;
1647 LOCK_REQ *pSMB = NULL;
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001648/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 int bytes_returned;
1650 int timeout = 0;
1651 __u16 count;
1652
Joe Perchesb6b38f72010-04-21 03:50:45 +00001653 cFYI(1, "CIFSSMBLock timeout %d numLock %d", (int)waitFlag, numLock);
Steve French46810cb2005-04-28 22:41:09 -07001654 rc = small_smb_init(SMB_COM_LOCKING_ANDX, 8, tcon, (void **) &pSMB);
1655
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656 if (rc)
1657 return rc;
1658
Steve French790fe572007-07-07 19:25:05 +00001659 if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
Steve French133672e2007-11-13 22:41:37 +00001660 timeout = CIFS_ASYNC_OP; /* no response expected */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 pSMB->Timeout = 0;
Steve French4b18f2a2008-04-29 00:06:05 +00001662 } else if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001663 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 pSMB->Timeout = cpu_to_le32(-1);/* blocking - do not time out */
1665 } else {
1666 pSMB->Timeout = 0;
1667 }
1668
1669 pSMB->NumberOfLocks = cpu_to_le16(numLock);
1670 pSMB->NumberOfUnlocks = cpu_to_le16(numUnlock);
1671 pSMB->LockType = lockType;
1672 pSMB->AndXCommand = 0xFF; /* none */
1673 pSMB->Fid = smb_file_id; /* netfid stays le */
1674
Steve French790fe572007-07-07 19:25:05 +00001675 if ((numLock != 0) || (numUnlock != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 pSMB->Locks[0].Pid = cpu_to_le16(current->tgid);
1677 /* BB where to store pid high? */
1678 pSMB->Locks[0].LengthLow = cpu_to_le32((u32)len);
1679 pSMB->Locks[0].LengthHigh = cpu_to_le32((u32)(len>>32));
1680 pSMB->Locks[0].OffsetLow = cpu_to_le32((u32)offset);
1681 pSMB->Locks[0].OffsetHigh = cpu_to_le32((u32)(offset>>32));
1682 count = sizeof(LOCKING_ANDX_RANGE);
1683 } else {
1684 /* oplock break */
1685 count = 0;
1686 }
1687 pSMB->hdr.smb_buf_length += count;
1688 pSMB->ByteCount = cpu_to_le16(count);
1689
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001690 if (waitFlag) {
1691 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
Steve Frenchaaa9bbe2008-05-23 17:38:32 +00001692 (struct smb_hdr *) pSMB, &bytes_returned);
Steve French133672e2007-11-13 22:41:37 +00001693 cifs_small_buf_release(pSMB);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001694 } else {
Steve French133672e2007-11-13 22:41:37 +00001695 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
1696 timeout);
1697 /* SMB buffer freed by function above */
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001698 }
Steve Frencha4544342005-08-24 13:59:35 -07001699 cifs_stats_inc(&tcon->num_locks);
Steve Frenchad7a2922008-02-07 23:25:02 +00001700 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001701 cFYI(1, "Send error in Lock = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702
Steve French50c2f752007-07-13 00:33:32 +00001703 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 since file handle passed in no longer valid */
1705 return rc;
1706}
1707
1708int
Steve French08547b02006-02-28 22:39:25 +00001709CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon,
1710 const __u16 smb_file_id, const int get_flag, const __u64 len,
Steve French50c2f752007-07-13 00:33:32 +00001711 struct file_lock *pLockData, const __u16 lock_type,
Steve French4b18f2a2008-04-29 00:06:05 +00001712 const bool waitFlag)
Steve French08547b02006-02-28 22:39:25 +00001713{
1714 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1715 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French08547b02006-02-28 22:39:25 +00001716 struct cifs_posix_lock *parm_data;
1717 int rc = 0;
Steve French3a5ff612006-07-14 22:37:11 +00001718 int timeout = 0;
Steve French08547b02006-02-28 22:39:25 +00001719 int bytes_returned = 0;
Steve French133672e2007-11-13 22:41:37 +00001720 int resp_buf_type = 0;
Steve French08547b02006-02-28 22:39:25 +00001721 __u16 params, param_offset, offset, byte_count, count;
Steve French133672e2007-11-13 22:41:37 +00001722 struct kvec iov[1];
Steve French08547b02006-02-28 22:39:25 +00001723
Joe Perchesb6b38f72010-04-21 03:50:45 +00001724 cFYI(1, "Posix Lock");
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001725
Steve French790fe572007-07-07 19:25:05 +00001726 if (pLockData == NULL)
Marcin Slusarzed5f0372008-05-13 04:01:01 +00001727 return -EINVAL;
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001728
Steve French08547b02006-02-28 22:39:25 +00001729 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
1730
1731 if (rc)
1732 return rc;
1733
1734 pSMBr = (struct smb_com_transaction2_sfi_rsp *)pSMB;
1735
Steve French50c2f752007-07-13 00:33:32 +00001736 params = 6;
Steve French08547b02006-02-28 22:39:25 +00001737 pSMB->MaxSetupCount = 0;
1738 pSMB->Reserved = 0;
1739 pSMB->Flags = 0;
Steve French08547b02006-02-28 22:39:25 +00001740 pSMB->Reserved2 = 0;
1741 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1742 offset = param_offset + params;
1743
Steve French08547b02006-02-28 22:39:25 +00001744 count = sizeof(struct cifs_posix_lock);
1745 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00001746 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Steve French08547b02006-02-28 22:39:25 +00001747 pSMB->SetupCount = 1;
1748 pSMB->Reserved3 = 0;
Steve French790fe572007-07-07 19:25:05 +00001749 if (get_flag)
Steve French08547b02006-02-28 22:39:25 +00001750 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
1751 else
1752 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
1753 byte_count = 3 /* pad */ + params + count;
1754 pSMB->DataCount = cpu_to_le16(count);
1755 pSMB->ParameterCount = cpu_to_le16(params);
1756 pSMB->TotalDataCount = pSMB->DataCount;
1757 pSMB->TotalParameterCount = pSMB->ParameterCount;
1758 pSMB->ParameterOffset = cpu_to_le16(param_offset);
Steve French50c2f752007-07-13 00:33:32 +00001759 parm_data = (struct cifs_posix_lock *)
Steve French08547b02006-02-28 22:39:25 +00001760 (((char *) &pSMB->hdr.Protocol) + offset);
1761
1762 parm_data->lock_type = cpu_to_le16(lock_type);
Steve French790fe572007-07-07 19:25:05 +00001763 if (waitFlag) {
Steve French133672e2007-11-13 22:41:37 +00001764 timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
Steve Frenchcec6815a2006-05-30 18:07:17 +00001765 parm_data->lock_flags = cpu_to_le16(1);
Steve French3a5ff612006-07-14 22:37:11 +00001766 pSMB->Timeout = cpu_to_le32(-1);
1767 } else
1768 pSMB->Timeout = 0;
1769
Steve French08547b02006-02-28 22:39:25 +00001770 parm_data->pid = cpu_to_le32(current->tgid);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001771 parm_data->start = cpu_to_le64(pLockData->fl_start);
Steve Frenchcec6815a2006-05-30 18:07:17 +00001772 parm_data->length = cpu_to_le64(len); /* normalize negative numbers */
Steve French08547b02006-02-28 22:39:25 +00001773
1774 pSMB->DataOffset = cpu_to_le16(offset);
Steve Frenchf26282c2006-03-01 09:17:37 +00001775 pSMB->Fid = smb_file_id;
Steve French08547b02006-02-28 22:39:25 +00001776 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_LOCK);
1777 pSMB->Reserved4 = 0;
1778 pSMB->hdr.smb_buf_length += byte_count;
1779 pSMB->ByteCount = cpu_to_le16(byte_count);
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001780 if (waitFlag) {
1781 rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
1782 (struct smb_hdr *) pSMBr, &bytes_returned);
1783 } else {
Steve French133672e2007-11-13 22:41:37 +00001784 iov[0].iov_base = (char *)pSMB;
1785 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
1786 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
1787 &resp_buf_type, timeout);
1788 pSMB = NULL; /* request buf already freed by SendReceive2. Do
1789 not try to free it twice below on exit */
1790 pSMBr = (struct smb_com_transaction2_sfi_rsp *)iov[0].iov_base;
Jeremy Allison7ee1af72006-08-02 21:56:33 +00001791 }
1792
Steve French08547b02006-02-28 22:39:25 +00001793 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00001794 cFYI(1, "Send error in Posix Lock = %d", rc);
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001795 } else if (get_flag) {
1796 /* lock structure can be returned on get */
1797 __u16 data_offset;
1798 __u16 data_count;
1799 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French08547b02006-02-28 22:39:25 +00001800
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001801 if (rc || (pSMBr->ByteCount < sizeof(struct cifs_posix_lock))) {
1802 rc = -EIO; /* bad smb */
1803 goto plk_err_exit;
1804 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001805 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
1806 data_count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French790fe572007-07-07 19:25:05 +00001807 if (data_count < sizeof(struct cifs_posix_lock)) {
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001808 rc = -EIO;
1809 goto plk_err_exit;
1810 }
1811 parm_data = (struct cifs_posix_lock *)
1812 ((char *)&pSMBr->hdr.Protocol + data_offset);
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001813 if (parm_data->lock_type == __constant_cpu_to_le16(CIFS_UNLCK))
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001814 pLockData->fl_type = F_UNLCK;
Pavel Shilovskyf05337c2010-04-05 09:59:14 +04001815 else {
1816 if (parm_data->lock_type ==
1817 __constant_cpu_to_le16(CIFS_RDLCK))
1818 pLockData->fl_type = F_RDLCK;
1819 else if (parm_data->lock_type ==
1820 __constant_cpu_to_le16(CIFS_WRLCK))
1821 pLockData->fl_type = F_WRLCK;
1822
1823 pLockData->fl_start = parm_data->start;
1824 pLockData->fl_end = parm_data->start +
1825 parm_data->length - 1;
1826 pLockData->fl_pid = parm_data->pid;
1827 }
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001828 }
Steve French50c2f752007-07-13 00:33:32 +00001829
Steve Frenchfc94cdb2006-05-30 18:03:32 +00001830plk_err_exit:
Steve French08547b02006-02-28 22:39:25 +00001831 if (pSMB)
1832 cifs_small_buf_release(pSMB);
1833
Steve French133672e2007-11-13 22:41:37 +00001834 if (resp_buf_type == CIFS_SMALL_BUFFER)
1835 cifs_small_buf_release(iov[0].iov_base);
1836 else if (resp_buf_type == CIFS_LARGE_BUFFER)
1837 cifs_buf_release(iov[0].iov_base);
1838
Steve French08547b02006-02-28 22:39:25 +00001839 /* Note: On -EAGAIN error only caller can retry on handle based calls
1840 since file handle passed in no longer valid */
1841
1842 return rc;
1843}
1844
1845
1846int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847CIFSSMBClose(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1848{
1849 int rc = 0;
1850 CLOSE_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001851 cFYI(1, "In CIFSSMBClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852
1853/* do not retry on dead session on close */
1854 rc = small_smb_init(SMB_COM_CLOSE, 3, tcon, (void **) &pSMB);
Steve French790fe572007-07-07 19:25:05 +00001855 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856 return 0;
1857 if (rc)
1858 return rc;
1859
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 pSMB->FileID = (__u16) smb_file_id;
Steve Frenchb815f1e52006-10-02 05:53:29 +00001861 pSMB->LastWriteTime = 0xFFFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00001863 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001864 cifs_stats_inc(&tcon->num_closes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 if (rc) {
Steve French790fe572007-07-07 19:25:05 +00001866 if (rc != -EINTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867 /* EINTR is expected when user ctl-c to kill app */
Joe Perchesb6b38f72010-04-21 03:50:45 +00001868 cERROR(1, "Send error in Close = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869 }
1870 }
1871
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872 /* Since session is dead, file will be closed on server already */
Steve French790fe572007-07-07 19:25:05 +00001873 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874 rc = 0;
1875
1876 return rc;
1877}
1878
1879int
Steve Frenchb298f222009-02-21 21:17:43 +00001880CIFSSMBFlush(const int xid, struct cifsTconInfo *tcon, int smb_file_id)
1881{
1882 int rc = 0;
1883 FLUSH_REQ *pSMB = NULL;
Joe Perchesb6b38f72010-04-21 03:50:45 +00001884 cFYI(1, "In CIFSSMBFlush");
Steve Frenchb298f222009-02-21 21:17:43 +00001885
1886 rc = small_smb_init(SMB_COM_FLUSH, 1, tcon, (void **) &pSMB);
1887 if (rc)
1888 return rc;
1889
1890 pSMB->FileID = (__u16) smb_file_id;
1891 pSMB->ByteCount = 0;
1892 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
1893 cifs_stats_inc(&tcon->num_flushes);
1894 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001895 cERROR(1, "Send error in Flush = %d", rc);
Steve Frenchb298f222009-02-21 21:17:43 +00001896
1897 return rc;
1898}
1899
1900int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
1902 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07001903 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904{
1905 int rc = 0;
1906 RENAME_REQ *pSMB = NULL;
1907 RENAME_RSP *pSMBr = NULL;
1908 int bytes_returned;
1909 int name_len, name_len2;
1910 __u16 count;
1911
Joe Perchesb6b38f72010-04-21 03:50:45 +00001912 cFYI(1, "In CIFSSMBRename");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913renameRetry:
1914 rc = smb_init(SMB_COM_RENAME, 1, tcon, (void **) &pSMB,
1915 (void **) &pSMBr);
1916 if (rc)
1917 return rc;
1918
1919 pSMB->BufferFormat = 0x04;
1920 pSMB->SearchAttributes =
1921 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
1922 ATTR_DIRECTORY);
1923
1924 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
1925 name_len =
Steve French50c2f752007-07-13 00:33:32 +00001926 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07001927 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 name_len++; /* trailing null */
1929 name_len *= 2;
1930 pSMB->OldFileName[name_len] = 0x04; /* pad */
1931 /* protocol requires ASCII signature byte on Unicode string */
1932 pSMB->OldFileName[name_len + 1] = 0x00;
1933 name_len2 =
Steve French582d21e2008-05-13 04:54:12 +00001934 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07001935 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
1937 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00001938 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 name_len = strnlen(fromName, PATH_MAX);
1940 name_len++; /* trailing null */
1941 strncpy(pSMB->OldFileName, fromName, name_len);
1942 name_len2 = strnlen(toName, PATH_MAX);
1943 name_len2++; /* trailing null */
1944 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
1945 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
1946 name_len2++; /* trailing null */
1947 name_len2++; /* signature byte */
1948 }
1949
1950 count = 1 /* 1st signature byte */ + name_len + name_len2;
1951 pSMB->hdr.smb_buf_length += count;
1952 pSMB->ByteCount = cpu_to_le16(count);
1953
1954 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
1955 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07001956 cifs_stats_inc(&tcon->num_renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00001957 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00001958 cFYI(1, "Send error in rename = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 cifs_buf_release(pSMB);
1961
1962 if (rc == -EAGAIN)
1963 goto renameRetry;
1964
1965 return rc;
1966}
1967
Steve French50c2f752007-07-13 00:33:32 +00001968int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
Jeff Layton391e5752008-09-24 11:32:59 -04001969 int netfid, const char *target_name,
Steve French50c2f752007-07-13 00:33:32 +00001970 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971{
1972 struct smb_com_transaction2_sfi_req *pSMB = NULL;
1973 struct smb_com_transaction2_sfi_rsp *pSMBr = NULL;
Steve French50c2f752007-07-13 00:33:32 +00001974 struct set_file_rename *rename_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 char *data_offset;
1976 char dummy_string[30];
1977 int rc = 0;
1978 int bytes_returned = 0;
1979 int len_of_str;
1980 __u16 params, param_offset, offset, count, byte_count;
1981
Joe Perchesb6b38f72010-04-21 03:50:45 +00001982 cFYI(1, "Rename to File by handle");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983 rc = smb_init(SMB_COM_TRANSACTION2, 15, pTcon, (void **) &pSMB,
1984 (void **) &pSMBr);
1985 if (rc)
1986 return rc;
1987
1988 params = 6;
1989 pSMB->MaxSetupCount = 0;
1990 pSMB->Reserved = 0;
1991 pSMB->Flags = 0;
1992 pSMB->Timeout = 0;
1993 pSMB->Reserved2 = 0;
1994 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
1995 offset = param_offset + params;
1996
1997 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
1998 rename_info = (struct set_file_rename *) data_offset;
1999 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve Frenchad7a2922008-02-07 23:25:02 +00002000 pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB from sess */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 pSMB->SetupCount = 1;
2002 pSMB->Reserved3 = 0;
2003 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
2004 byte_count = 3 /* pad */ + params;
2005 pSMB->ParameterCount = cpu_to_le16(params);
2006 pSMB->TotalParameterCount = pSMB->ParameterCount;
2007 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2008 pSMB->DataOffset = cpu_to_le16(offset);
2009 /* construct random name ".cifs_tmp<inodenum><mid>" */
2010 rename_info->overwrite = cpu_to_le32(1);
2011 rename_info->root_fid = 0;
2012 /* unicode only call */
Steve French790fe572007-07-07 19:25:05 +00002013 if (target_name == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00002014 sprintf(dummy_string, "cifs%x", pSMB->hdr.Mid);
2015 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French737b7582005-04-28 22:41:06 -07002016 dummy_string, 24, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 } else {
Steve Frenchb1a45692005-05-17 16:07:23 -05002018 len_of_str = cifsConvertToUCS((__le16 *)rename_info->target_name,
Steve French50c2f752007-07-13 00:33:32 +00002019 target_name, PATH_MAX, nls_codepage,
2020 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021 }
2022 rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
Jeff Layton391e5752008-09-24 11:32:59 -04002023 count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 byte_count += count;
2025 pSMB->DataCount = cpu_to_le16(count);
2026 pSMB->TotalDataCount = pSMB->DataCount;
2027 pSMB->Fid = netfid;
2028 pSMB->InformationLevel =
2029 cpu_to_le16(SMB_SET_FILE_RENAME_INFORMATION);
2030 pSMB->Reserved4 = 0;
2031 pSMB->hdr.smb_buf_length += byte_count;
2032 pSMB->ByteCount = cpu_to_le16(byte_count);
2033 rc = SendReceive(xid, pTcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002034 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002035 cifs_stats_inc(&pTcon->num_t2renames);
Steve Frenchad7a2922008-02-07 23:25:02 +00002036 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002037 cFYI(1, "Send error in Rename (by file handle) = %d", rc);
Steve Frencha5a2b482005-08-20 21:42:53 -07002038
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 cifs_buf_release(pSMB);
2040
2041 /* Note: On -EAGAIN error only caller can retry on handle based calls
2042 since file handle passed in no longer valid */
2043
2044 return rc;
2045}
2046
2047int
Steve French50c2f752007-07-13 00:33:32 +00002048CIFSSMBCopy(const int xid, struct cifsTconInfo *tcon, const char *fromName,
2049 const __u16 target_tid, const char *toName, const int flags,
2050 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051{
2052 int rc = 0;
2053 COPY_REQ *pSMB = NULL;
2054 COPY_RSP *pSMBr = NULL;
2055 int bytes_returned;
2056 int name_len, name_len2;
2057 __u16 count;
2058
Joe Perchesb6b38f72010-04-21 03:50:45 +00002059 cFYI(1, "In CIFSSMBCopy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060copyRetry:
2061 rc = smb_init(SMB_COM_COPY, 1, tcon, (void **) &pSMB,
2062 (void **) &pSMBr);
2063 if (rc)
2064 return rc;
2065
2066 pSMB->BufferFormat = 0x04;
2067 pSMB->Tid2 = target_tid;
2068
2069 pSMB->Flags = cpu_to_le16(flags & COPY_TREE);
2070
2071 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve French50c2f752007-07-13 00:33:32 +00002072 name_len = cifsConvertToUCS((__le16 *) pSMB->OldFileName,
Steve French737b7582005-04-28 22:41:06 -07002073 fromName, PATH_MAX, nls_codepage,
2074 remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 name_len++; /* trailing null */
2076 name_len *= 2;
2077 pSMB->OldFileName[name_len] = 0x04; /* pad */
2078 /* protocol requires ASCII signature byte on Unicode string */
2079 pSMB->OldFileName[name_len + 1] = 0x00;
Steve French50c2f752007-07-13 00:33:32 +00002080 name_len2 =
2081 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002082 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2084 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002085 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 name_len = strnlen(fromName, PATH_MAX);
2087 name_len++; /* trailing null */
2088 strncpy(pSMB->OldFileName, fromName, name_len);
2089 name_len2 = strnlen(toName, PATH_MAX);
2090 name_len2++; /* trailing null */
2091 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2092 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2093 name_len2++; /* trailing null */
2094 name_len2++; /* signature byte */
2095 }
2096
2097 count = 1 /* 1st signature byte */ + name_len + name_len2;
2098 pSMB->hdr.smb_buf_length += count;
2099 pSMB->ByteCount = cpu_to_le16(count);
2100
2101 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2102 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2103 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002104 cFYI(1, "Send error in copy = %d with %d files copied",
2105 rc, le16_to_cpu(pSMBr->CopyCount));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 }
Steve French0d817bc2008-05-22 02:02:03 +00002107 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108
2109 if (rc == -EAGAIN)
2110 goto copyRetry;
2111
2112 return rc;
2113}
2114
2115int
2116CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon,
2117 const char *fromName, const char *toName,
2118 const struct nls_table *nls_codepage)
2119{
2120 TRANSACTION2_SPI_REQ *pSMB = NULL;
2121 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2122 char *data_offset;
2123 int name_len;
2124 int name_len_target;
2125 int rc = 0;
2126 int bytes_returned = 0;
2127 __u16 params, param_offset, offset, byte_count;
2128
Joe Perchesb6b38f72010-04-21 03:50:45 +00002129 cFYI(1, "In Symlink Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130createSymLinkRetry:
2131 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2132 (void **) &pSMBr);
2133 if (rc)
2134 return rc;
2135
2136 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2137 name_len =
Steve Frenche89dc922005-11-11 15:18:19 -08002138 cifs_strtoUCS((__le16 *) pSMB->FileName, fromName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139 /* find define for this maxpathcomponent */
2140 , nls_codepage);
2141 name_len++; /* trailing null */
2142 name_len *= 2;
2143
Steve French50c2f752007-07-13 00:33:32 +00002144 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 name_len = strnlen(fromName, PATH_MAX);
2146 name_len++; /* trailing null */
2147 strncpy(pSMB->FileName, fromName, name_len);
2148 }
2149 params = 6 + name_len;
2150 pSMB->MaxSetupCount = 0;
2151 pSMB->Reserved = 0;
2152 pSMB->Flags = 0;
2153 pSMB->Timeout = 0;
2154 pSMB->Reserved2 = 0;
2155 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002156 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 offset = param_offset + params;
2158
2159 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2160 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2161 name_len_target =
Steve Frenche89dc922005-11-11 15:18:19 -08002162 cifs_strtoUCS((__le16 *) data_offset, toName, PATH_MAX
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 /* find define for this maxpathcomponent */
2164 , nls_codepage);
2165 name_len_target++; /* trailing null */
2166 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002167 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168 name_len_target = strnlen(toName, PATH_MAX);
2169 name_len_target++; /* trailing null */
2170 strncpy(data_offset, toName, name_len_target);
2171 }
2172
2173 pSMB->MaxParameterCount = cpu_to_le16(2);
2174 /* BB find exact max on data count below from sess */
2175 pSMB->MaxDataCount = cpu_to_le16(1000);
2176 pSMB->SetupCount = 1;
2177 pSMB->Reserved3 = 0;
2178 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2179 byte_count = 3 /* pad */ + params + name_len_target;
2180 pSMB->DataCount = cpu_to_le16(name_len_target);
2181 pSMB->ParameterCount = cpu_to_le16(params);
2182 pSMB->TotalDataCount = pSMB->DataCount;
2183 pSMB->TotalParameterCount = pSMB->ParameterCount;
2184 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2185 pSMB->DataOffset = cpu_to_le16(offset);
2186 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_LINK);
2187 pSMB->Reserved4 = 0;
2188 pSMB->hdr.smb_buf_length += byte_count;
2189 pSMB->ByteCount = cpu_to_le16(byte_count);
2190 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2191 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002192 cifs_stats_inc(&tcon->num_symlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002193 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002194 cFYI(1, "Send error in SetPathInfo create symlink = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195
Steve French0d817bc2008-05-22 02:02:03 +00002196 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197
2198 if (rc == -EAGAIN)
2199 goto createSymLinkRetry;
2200
2201 return rc;
2202}
2203
2204int
2205CIFSUnixCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2206 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002207 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208{
2209 TRANSACTION2_SPI_REQ *pSMB = NULL;
2210 TRANSACTION2_SPI_RSP *pSMBr = NULL;
2211 char *data_offset;
2212 int name_len;
2213 int name_len_target;
2214 int rc = 0;
2215 int bytes_returned = 0;
2216 __u16 params, param_offset, offset, byte_count;
2217
Joe Perchesb6b38f72010-04-21 03:50:45 +00002218 cFYI(1, "In Create Hard link Unix style");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219createHardLinkRetry:
2220 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2221 (void **) &pSMBr);
2222 if (rc)
2223 return rc;
2224
2225 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Steve Frenchb1a45692005-05-17 16:07:23 -05002226 name_len = cifsConvertToUCS((__le16 *) pSMB->FileName, toName,
Steve French737b7582005-04-28 22:41:06 -07002227 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228 name_len++; /* trailing null */
2229 name_len *= 2;
2230
Steve French50c2f752007-07-13 00:33:32 +00002231 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232 name_len = strnlen(toName, PATH_MAX);
2233 name_len++; /* trailing null */
2234 strncpy(pSMB->FileName, toName, name_len);
2235 }
2236 params = 6 + name_len;
2237 pSMB->MaxSetupCount = 0;
2238 pSMB->Reserved = 0;
2239 pSMB->Flags = 0;
2240 pSMB->Timeout = 0;
2241 pSMB->Reserved2 = 0;
2242 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002243 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244 offset = param_offset + params;
2245
2246 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
2247 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2248 name_len_target =
Steve Frenchb1a45692005-05-17 16:07:23 -05002249 cifsConvertToUCS((__le16 *) data_offset, fromName, PATH_MAX,
Steve French737b7582005-04-28 22:41:06 -07002250 nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251 name_len_target++; /* trailing null */
2252 name_len_target *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002253 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 name_len_target = strnlen(fromName, PATH_MAX);
2255 name_len_target++; /* trailing null */
2256 strncpy(data_offset, fromName, name_len_target);
2257 }
2258
2259 pSMB->MaxParameterCount = cpu_to_le16(2);
2260 /* BB find exact max on data count below from sess*/
2261 pSMB->MaxDataCount = cpu_to_le16(1000);
2262 pSMB->SetupCount = 1;
2263 pSMB->Reserved3 = 0;
2264 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2265 byte_count = 3 /* pad */ + params + name_len_target;
2266 pSMB->ParameterCount = cpu_to_le16(params);
2267 pSMB->TotalParameterCount = pSMB->ParameterCount;
2268 pSMB->DataCount = cpu_to_le16(name_len_target);
2269 pSMB->TotalDataCount = pSMB->DataCount;
2270 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2271 pSMB->DataOffset = cpu_to_le16(offset);
2272 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_HLINK);
2273 pSMB->Reserved4 = 0;
2274 pSMB->hdr.smb_buf_length += byte_count;
2275 pSMB->ByteCount = cpu_to_le16(byte_count);
2276 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2277 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002278 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002279 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002280 cFYI(1, "Send error in SetPathInfo (hard link) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281
2282 cifs_buf_release(pSMB);
2283 if (rc == -EAGAIN)
2284 goto createHardLinkRetry;
2285
2286 return rc;
2287}
2288
2289int
2290CIFSCreateHardLink(const int xid, struct cifsTconInfo *tcon,
2291 const char *fromName, const char *toName,
Steve French737b7582005-04-28 22:41:06 -07002292 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293{
2294 int rc = 0;
2295 NT_RENAME_REQ *pSMB = NULL;
2296 RENAME_RSP *pSMBr = NULL;
2297 int bytes_returned;
2298 int name_len, name_len2;
2299 __u16 count;
2300
Joe Perchesb6b38f72010-04-21 03:50:45 +00002301 cFYI(1, "In CIFSCreateHardLink");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302winCreateHardLinkRetry:
2303
2304 rc = smb_init(SMB_COM_NT_RENAME, 4, tcon, (void **) &pSMB,
2305 (void **) &pSMBr);
2306 if (rc)
2307 return rc;
2308
2309 pSMB->SearchAttributes =
2310 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
2311 ATTR_DIRECTORY);
2312 pSMB->Flags = cpu_to_le16(CREATE_HARD_LINK);
2313 pSMB->ClusterCount = 0;
2314
2315 pSMB->BufferFormat = 0x04;
2316
2317 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2318 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05002319 cifsConvertToUCS((__le16 *) pSMB->OldFileName, fromName,
Steve French737b7582005-04-28 22:41:06 -07002320 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 name_len++; /* trailing null */
2322 name_len *= 2;
Jeff Laytonfcc7c092009-02-28 12:59:03 -05002323
2324 /* protocol specifies ASCII buffer format (0x04) for unicode */
2325 pSMB->OldFileName[name_len] = 0x04;
2326 pSMB->OldFileName[name_len + 1] = 0x00; /* pad */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327 name_len2 =
Steve French50c2f752007-07-13 00:33:32 +00002328 cifsConvertToUCS((__le16 *)&pSMB->OldFileName[name_len + 2],
Steve French737b7582005-04-28 22:41:06 -07002329 toName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330 name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
2331 name_len2 *= 2; /* convert to bytes */
Steve French50c2f752007-07-13 00:33:32 +00002332 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333 name_len = strnlen(fromName, PATH_MAX);
2334 name_len++; /* trailing null */
2335 strncpy(pSMB->OldFileName, fromName, name_len);
2336 name_len2 = strnlen(toName, PATH_MAX);
2337 name_len2++; /* trailing null */
2338 pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
2339 strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
2340 name_len2++; /* trailing null */
2341 name_len2++; /* signature byte */
2342 }
2343
2344 count = 1 /* string type byte */ + name_len + name_len2;
2345 pSMB->hdr.smb_buf_length += count;
2346 pSMB->ByteCount = cpu_to_le16(count);
2347
2348 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2349 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07002350 cifs_stats_inc(&tcon->num_hardlinks);
Steve Frenchad7a2922008-02-07 23:25:02 +00002351 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002352 cFYI(1, "Send error in hard link (NT rename) = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00002353
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 cifs_buf_release(pSMB);
2355 if (rc == -EAGAIN)
2356 goto winCreateHardLinkRetry;
2357
2358 return rc;
2359}
2360
2361int
2362CIFSSMBUnixQuerySymLink(const int xid, struct cifsTconInfo *tcon,
Jeff Layton460b9692009-04-30 07:17:56 -04002363 const unsigned char *searchName, char **symlinkinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364 const struct nls_table *nls_codepage)
2365{
2366/* SMB_QUERY_FILE_UNIX_LINK */
2367 TRANSACTION2_QPI_REQ *pSMB = NULL;
2368 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2369 int rc = 0;
2370 int bytes_returned;
2371 int name_len;
2372 __u16 params, byte_count;
Jeff Layton460b9692009-04-30 07:17:56 -04002373 char *data_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374
Joe Perchesb6b38f72010-04-21 03:50:45 +00002375 cFYI(1, "In QPathSymLinkInfo (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376
2377querySymLinkRetry:
2378 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2379 (void **) &pSMBr);
2380 if (rc)
2381 return rc;
2382
2383 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2384 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002385 cifs_strtoUCS((__le16 *) pSMB->FileName, searchName,
2386 PATH_MAX, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387 name_len++; /* trailing null */
2388 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002389 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390 name_len = strnlen(searchName, PATH_MAX);
2391 name_len++; /* trailing null */
2392 strncpy(pSMB->FileName, searchName, name_len);
2393 }
2394
2395 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2396 pSMB->TotalDataCount = 0;
2397 pSMB->MaxParameterCount = cpu_to_le16(2);
Jeff Layton46a75742009-05-24 18:45:17 -04002398 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399 pSMB->MaxSetupCount = 0;
2400 pSMB->Reserved = 0;
2401 pSMB->Flags = 0;
2402 pSMB->Timeout = 0;
2403 pSMB->Reserved2 = 0;
2404 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00002405 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406 pSMB->DataCount = 0;
2407 pSMB->DataOffset = 0;
2408 pSMB->SetupCount = 1;
2409 pSMB->Reserved3 = 0;
2410 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2411 byte_count = params + 1 /* pad */ ;
2412 pSMB->TotalParameterCount = cpu_to_le16(params);
2413 pSMB->ParameterCount = pSMB->TotalParameterCount;
2414 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_LINK);
2415 pSMB->Reserved4 = 0;
2416 pSMB->hdr.smb_buf_length += byte_count;
2417 pSMB->ByteCount = cpu_to_le16(byte_count);
2418
2419 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2420 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2421 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002422 cFYI(1, "Send error in QuerySymLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 } else {
2424 /* decode response */
2425
2426 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427 /* BB also check enough total bytes returned */
Jeff Layton460b9692009-04-30 07:17:56 -04002428 if (rc || (pSMBr->ByteCount < 2))
2429 rc = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002430 else {
Steve French0e0d2cf2009-05-01 05:27:32 +00002431 bool is_unicode;
Jeff Layton460b9692009-04-30 07:17:56 -04002432 u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433
Jeff Layton460b9692009-04-30 07:17:56 -04002434 data_start = ((char *) &pSMBr->hdr.Protocol) +
2435 le16_to_cpu(pSMBr->t2.DataOffset);
2436
Steve French0e0d2cf2009-05-01 05:27:32 +00002437 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
2438 is_unicode = true;
2439 else
2440 is_unicode = false;
2441
Steve French737b7582005-04-28 22:41:06 -07002442 /* BB FIXME investigate remapping reserved chars here */
Steve Frenchd185cda2009-04-30 17:45:10 +00002443 *symlinkinfo = cifs_strndup_from_ucs(data_start, count,
Steve French0e0d2cf2009-05-01 05:27:32 +00002444 is_unicode, nls_codepage);
Jeff Layton8b6427a2009-05-19 09:57:03 -04002445 if (!*symlinkinfo)
Jeff Layton460b9692009-04-30 07:17:56 -04002446 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 }
2448 }
2449 cifs_buf_release(pSMB);
2450 if (rc == -EAGAIN)
2451 goto querySymLinkRetry;
2452 return rc;
2453}
2454
Parag Warudkarc9489772007-10-23 18:09:48 +00002455#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08002456/* Initialize NT TRANSACT SMB into small smb request buffer.
2457 This assumes that all NT TRANSACTS that we init here have
2458 total parm and data under about 400 bytes (to fit in small cifs
2459 buffer size), which is the case so far, it easily fits. NB:
2460 Setup words themselves and ByteCount
2461 MaxSetupCount (size of returned setup area) and
2462 MaxParameterCount (returned parms size) must be set by caller */
Steve French50c2f752007-07-13 00:33:32 +00002463static int
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002464smb_init_nttransact(const __u16 sub_command, const int setup_count,
Steve French0a4b92c2006-01-12 15:44:21 -08002465 const int parm_len, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002466 void **ret_buf)
Steve French0a4b92c2006-01-12 15:44:21 -08002467{
2468 int rc;
2469 __u32 temp_offset;
Steve French50c2f752007-07-13 00:33:32 +00002470 struct smb_com_ntransact_req *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08002471
2472 rc = small_smb_init(SMB_COM_NT_TRANSACT, 19 + setup_count, tcon,
2473 (void **)&pSMB);
2474 if (rc)
2475 return rc;
2476 *ret_buf = (void *)pSMB;
2477 pSMB->Reserved = 0;
2478 pSMB->TotalParameterCount = cpu_to_le32(parm_len);
2479 pSMB->TotalDataCount = 0;
2480 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2481 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
2482 pSMB->ParameterCount = pSMB->TotalParameterCount;
2483 pSMB->DataCount = pSMB->TotalDataCount;
2484 temp_offset = offsetof(struct smb_com_ntransact_req, Parms) +
2485 (setup_count * 2) - 4 /* for rfc1001 length itself */;
2486 pSMB->ParameterOffset = cpu_to_le32(temp_offset);
2487 pSMB->DataOffset = cpu_to_le32(temp_offset + parm_len);
2488 pSMB->SetupCount = setup_count; /* no need to le convert byte fields */
2489 pSMB->SubCommand = cpu_to_le16(sub_command);
2490 return 0;
2491}
2492
2493static int
Steve French50c2f752007-07-13 00:33:32 +00002494validate_ntransact(char *buf, char **ppparm, char **ppdata,
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00002495 __u32 *pparmlen, __u32 *pdatalen)
Steve French0a4b92c2006-01-12 15:44:21 -08002496{
Steve French50c2f752007-07-13 00:33:32 +00002497 char *end_of_smb;
Steve French0a4b92c2006-01-12 15:44:21 -08002498 __u32 data_count, data_offset, parm_count, parm_offset;
Steve French50c2f752007-07-13 00:33:32 +00002499 struct smb_com_ntransact_rsp *pSMBr;
Steve French0a4b92c2006-01-12 15:44:21 -08002500
Steve French630f3f0c2007-10-25 21:17:17 +00002501 *pdatalen = 0;
2502 *pparmlen = 0;
2503
Steve French790fe572007-07-07 19:25:05 +00002504 if (buf == NULL)
Steve French0a4b92c2006-01-12 15:44:21 -08002505 return -EINVAL;
2506
2507 pSMBr = (struct smb_com_ntransact_rsp *)buf;
2508
2509 /* ByteCount was converted from little endian in SendReceive */
Steve French50c2f752007-07-13 00:33:32 +00002510 end_of_smb = 2 /* sizeof byte count */ + pSMBr->ByteCount +
Steve French0a4b92c2006-01-12 15:44:21 -08002511 (char *)&pSMBr->ByteCount;
2512
Steve French0a4b92c2006-01-12 15:44:21 -08002513 data_offset = le32_to_cpu(pSMBr->DataOffset);
2514 data_count = le32_to_cpu(pSMBr->DataCount);
Steve French50c2f752007-07-13 00:33:32 +00002515 parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
Steve French0a4b92c2006-01-12 15:44:21 -08002516 parm_count = le32_to_cpu(pSMBr->ParameterCount);
2517
2518 *ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
2519 *ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
2520
2521 /* should we also check that parm and data areas do not overlap? */
Steve French790fe572007-07-07 19:25:05 +00002522 if (*ppparm > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002523 cFYI(1, "parms start after end of smb");
Steve French0a4b92c2006-01-12 15:44:21 -08002524 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002525 } else if (parm_count + *ppparm > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002526 cFYI(1, "parm end after end of smb");
Steve French0a4b92c2006-01-12 15:44:21 -08002527 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002528 } else if (*ppdata > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002529 cFYI(1, "data starts after end of smb");
Steve French0a4b92c2006-01-12 15:44:21 -08002530 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002531 } else if (data_count + *ppdata > end_of_smb) {
Steve Frenchf19159d2010-04-21 04:12:10 +00002532 cFYI(1, "data %p + count %d (%p) past smb end %p start %p",
Steve French50c2f752007-07-13 00:33:32 +00002533 *ppdata, data_count, (data_count + *ppdata),
Joe Perchesb6b38f72010-04-21 03:50:45 +00002534 end_of_smb, pSMBr);
Steve French0a4b92c2006-01-12 15:44:21 -08002535 return -EINVAL;
Steve French790fe572007-07-07 19:25:05 +00002536 } else if (parm_count + data_count > pSMBr->ByteCount) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002537 cFYI(1, "parm count and data count larger than SMB");
Steve French0a4b92c2006-01-12 15:44:21 -08002538 return -EINVAL;
2539 }
Steve French630f3f0c2007-10-25 21:17:17 +00002540 *pdatalen = data_count;
2541 *pparmlen = parm_count;
Steve French0a4b92c2006-01-12 15:44:21 -08002542 return 0;
2543}
2544
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545int
2546CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon,
2547 const unsigned char *searchName,
Steve French50c2f752007-07-13 00:33:32 +00002548 char *symlinkinfo, const int buflen, __u16 fid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 const struct nls_table *nls_codepage)
2550{
2551 int rc = 0;
2552 int bytes_returned;
Steve French50c2f752007-07-13 00:33:32 +00002553 struct smb_com_transaction_ioctl_req *pSMB;
2554 struct smb_com_transaction_ioctl_rsp *pSMBr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555
Joe Perchesb6b38f72010-04-21 03:50:45 +00002556 cFYI(1, "In Windows reparse style QueryLink for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
2558 (void **) &pSMBr);
2559 if (rc)
2560 return rc;
2561
2562 pSMB->TotalParameterCount = 0 ;
2563 pSMB->TotalDataCount = 0;
2564 pSMB->MaxParameterCount = cpu_to_le32(2);
2565 /* BB find exact data count max from sess structure BB */
Steve French0a4b92c2006-01-12 15:44:21 -08002566 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
2567 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568 pSMB->MaxSetupCount = 4;
2569 pSMB->Reserved = 0;
2570 pSMB->ParameterOffset = 0;
2571 pSMB->DataCount = 0;
2572 pSMB->DataOffset = 0;
2573 pSMB->SetupCount = 4;
2574 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
2575 pSMB->ParameterCount = pSMB->TotalParameterCount;
2576 pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
2577 pSMB->IsFsctl = 1; /* FSCTL */
2578 pSMB->IsRootFlag = 0;
2579 pSMB->Fid = fid; /* file handle always le */
2580 pSMB->ByteCount = 0;
2581
2582 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2583 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2584 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002585 cFYI(1, "Send error in QueryReparseLinkInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586 } else { /* decode response */
2587 __u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
2588 __u32 data_count = le32_to_cpu(pSMBr->DataCount);
Steve Frenchafe48c32009-05-02 05:25:46 +00002589 if ((pSMBr->ByteCount < 2) || (data_offset > 512)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590 /* BB also check enough total bytes returned */
2591 rc = -EIO; /* bad smb */
Steve Frenchafe48c32009-05-02 05:25:46 +00002592 goto qreparse_out;
2593 }
2594 if (data_count && (data_count < 2048)) {
2595 char *end_of_smb = 2 /* sizeof byte count */ +
2596 pSMBr->ByteCount + (char *)&pSMBr->ByteCount;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597
Steve Frenchafe48c32009-05-02 05:25:46 +00002598 struct reparse_data *reparse_buf =
Steve French50c2f752007-07-13 00:33:32 +00002599 (struct reparse_data *)
2600 ((char *)&pSMBr->hdr.Protocol
2601 + data_offset);
Steve Frenchafe48c32009-05-02 05:25:46 +00002602 if ((char *)reparse_buf >= end_of_smb) {
2603 rc = -EIO;
2604 goto qreparse_out;
2605 }
2606 if ((reparse_buf->LinkNamesBuf +
2607 reparse_buf->TargetNameOffset +
2608 reparse_buf->TargetNameLen) > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002609 cFYI(1, "reparse buf beyond SMB");
Steve Frenchafe48c32009-05-02 05:25:46 +00002610 rc = -EIO;
2611 goto qreparse_out;
2612 }
Steve French50c2f752007-07-13 00:33:32 +00002613
Steve Frenchafe48c32009-05-02 05:25:46 +00002614 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
2615 cifs_from_ucs2(symlinkinfo, (__le16 *)
Steve French50c2f752007-07-13 00:33:32 +00002616 (reparse_buf->LinkNamesBuf +
2617 reparse_buf->TargetNameOffset),
Steve Frenchafe48c32009-05-02 05:25:46 +00002618 buflen,
2619 reparse_buf->TargetNameLen,
2620 nls_codepage, 0);
2621 } else { /* ASCII names */
2622 strncpy(symlinkinfo,
2623 reparse_buf->LinkNamesBuf +
2624 reparse_buf->TargetNameOffset,
2625 min_t(const int, buflen,
2626 reparse_buf->TargetNameLen));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002627 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002628 } else {
2629 rc = -EIO;
Joe Perchesb6b38f72010-04-21 03:50:45 +00002630 cFYI(1, "Invalid return data count on "
2631 "get reparse info ioctl");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632 }
Steve Frenchafe48c32009-05-02 05:25:46 +00002633 symlinkinfo[buflen] = 0; /* just in case so the caller
2634 does not go off the end of the buffer */
Joe Perchesb6b38f72010-04-21 03:50:45 +00002635 cFYI(1, "readlink result - %s", symlinkinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002636 }
Steve French989c7e52009-05-02 05:32:20 +00002637
Linus Torvalds1da177e2005-04-16 15:20:36 -07002638qreparse_out:
Steve French4a6d87f2005-08-13 08:15:54 -07002639 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002640
2641 /* Note: On -EAGAIN error only caller can retry on handle based calls
2642 since file handle passed in no longer valid */
2643
2644 return rc;
2645}
Steve Frenchafe48c32009-05-02 05:25:46 +00002646#endif /* CIFS_EXPERIMENTAL */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647
2648#ifdef CONFIG_CIFS_POSIX
2649
2650/*Convert an Access Control Entry from wire format to local POSIX xattr format*/
Steve French50c2f752007-07-13 00:33:32 +00002651static void cifs_convert_ace(posix_acl_xattr_entry *ace,
2652 struct cifs_posix_ace *cifs_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653{
2654 /* u8 cifs fields do not need le conversion */
Steve Frenchff7feac2005-11-15 16:45:16 -08002655 ace->e_perm = cpu_to_le16(cifs_ace->cifs_e_perm);
2656 ace->e_tag = cpu_to_le16(cifs_ace->cifs_e_tag);
2657 ace->e_id = cpu_to_le32(le64_to_cpu(cifs_ace->cifs_uid));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002658 /* cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659
2660 return;
2661}
2662
2663/* Convert ACL from CIFS POSIX wire format to local Linux POSIX ACL xattr */
Steve French50c2f752007-07-13 00:33:32 +00002664static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen,
2665 const int acl_type, const int size_of_data_area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666{
2667 int size = 0;
2668 int i;
2669 __u16 count;
Steve French50c2f752007-07-13 00:33:32 +00002670 struct cifs_posix_ace *pACE;
2671 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)src;
2672 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)trgt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673
2674 if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION)
2675 return -EOPNOTSUPP;
2676
Steve French790fe572007-07-07 19:25:05 +00002677 if (acl_type & ACL_TYPE_ACCESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678 count = le16_to_cpu(cifs_acl->access_entry_count);
2679 pACE = &cifs_acl->ace_array[0];
2680 size = sizeof(struct cifs_posix_acl);
2681 size += sizeof(struct cifs_posix_ace) * count;
2682 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002683 if (size_of_data_area < size) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002684 cFYI(1, "bad CIFS POSIX ACL size %d vs. %d",
2685 size_of_data_area, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686 return -EINVAL;
2687 }
Steve French790fe572007-07-07 19:25:05 +00002688 } else if (acl_type & ACL_TYPE_DEFAULT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 count = le16_to_cpu(cifs_acl->access_entry_count);
2690 size = sizeof(struct cifs_posix_acl);
2691 size += sizeof(struct cifs_posix_ace) * count;
2692/* skip past access ACEs to get to default ACEs */
2693 pACE = &cifs_acl->ace_array[count];
2694 count = le16_to_cpu(cifs_acl->default_entry_count);
2695 size += sizeof(struct cifs_posix_ace) * count;
2696 /* check if we would go beyond end of SMB */
Steve French790fe572007-07-07 19:25:05 +00002697 if (size_of_data_area < size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698 return -EINVAL;
2699 } else {
2700 /* illegal type */
2701 return -EINVAL;
2702 }
2703
2704 size = posix_acl_xattr_size(count);
Steve French790fe572007-07-07 19:25:05 +00002705 if ((buflen == 0) || (local_acl == NULL)) {
Steve French50c2f752007-07-13 00:33:32 +00002706 /* used to query ACL EA size */
Steve French790fe572007-07-07 19:25:05 +00002707 } else if (size > buflen) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708 return -ERANGE;
2709 } else /* buffer big enough */ {
Steve Frenchff7feac2005-11-15 16:45:16 -08002710 local_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
Steve French50c2f752007-07-13 00:33:32 +00002711 for (i = 0; i < count ; i++) {
2712 cifs_convert_ace(&local_acl->a_entries[i], pACE);
2713 pACE++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714 }
2715 }
2716 return size;
2717}
2718
Steve French50c2f752007-07-13 00:33:32 +00002719static __u16 convert_ace_to_cifs_ace(struct cifs_posix_ace *cifs_ace,
2720 const posix_acl_xattr_entry *local_ace)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721{
2722 __u16 rc = 0; /* 0 = ACL converted ok */
2723
Steve Frenchff7feac2005-11-15 16:45:16 -08002724 cifs_ace->cifs_e_perm = le16_to_cpu(local_ace->e_perm);
2725 cifs_ace->cifs_e_tag = le16_to_cpu(local_ace->e_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002726 /* BB is there a better way to handle the large uid? */
Steve French790fe572007-07-07 19:25:05 +00002727 if (local_ace->e_id == cpu_to_le32(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728 /* Probably no need to le convert -1 on any arch but can not hurt */
2729 cifs_ace->cifs_uid = cpu_to_le64(-1);
Steve French50c2f752007-07-13 00:33:32 +00002730 } else
Steve Frenchff7feac2005-11-15 16:45:16 -08002731 cifs_ace->cifs_uid = cpu_to_le64(le32_to_cpu(local_ace->e_id));
Joe Perchesb6b38f72010-04-21 03:50:45 +00002732 /*cFYI(1, "perm %d tag %d id %d",ace->e_perm,ace->e_tag,ace->e_id);*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733 return rc;
2734}
2735
2736/* Convert ACL from local Linux POSIX xattr to CIFS POSIX ACL wire format */
Steve French50c2f752007-07-13 00:33:32 +00002737static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
2738 const int buflen, const int acl_type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739{
2740 __u16 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002741 struct cifs_posix_acl *cifs_acl = (struct cifs_posix_acl *)parm_data;
2742 posix_acl_xattr_header *local_acl = (posix_acl_xattr_header *)pACL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743 int count;
2744 int i;
2745
Steve French790fe572007-07-07 19:25:05 +00002746 if ((buflen == 0) || (pACL == NULL) || (cifs_acl == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747 return 0;
2748
2749 count = posix_acl_xattr_count((size_t)buflen);
Joe Perchesb6b38f72010-04-21 03:50:45 +00002750 cFYI(1, "setting acl with %d entries from buf of length %d and "
Steve French63135e02007-07-17 17:34:02 +00002751 "version of %d",
Joe Perchesb6b38f72010-04-21 03:50:45 +00002752 count, buflen, le32_to_cpu(local_acl->a_version));
Steve French790fe572007-07-07 19:25:05 +00002753 if (le32_to_cpu(local_acl->a_version) != 2) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002754 cFYI(1, "unknown POSIX ACL version %d",
2755 le32_to_cpu(local_acl->a_version));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 return 0;
2757 }
2758 cifs_acl->version = cpu_to_le16(1);
Steve French790fe572007-07-07 19:25:05 +00002759 if (acl_type == ACL_TYPE_ACCESS)
Steve Frenchff7feac2005-11-15 16:45:16 -08002760 cifs_acl->access_entry_count = cpu_to_le16(count);
Steve French790fe572007-07-07 19:25:05 +00002761 else if (acl_type == ACL_TYPE_DEFAULT)
Steve Frenchff7feac2005-11-15 16:45:16 -08002762 cifs_acl->default_entry_count = cpu_to_le16(count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763 else {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002764 cFYI(1, "unknown ACL type %d", acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 return 0;
2766 }
Steve French50c2f752007-07-13 00:33:32 +00002767 for (i = 0; i < count; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768 rc = convert_ace_to_cifs_ace(&cifs_acl->ace_array[i],
2769 &local_acl->a_entries[i]);
Steve French790fe572007-07-07 19:25:05 +00002770 if (rc != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 /* ACE not converted */
2772 break;
2773 }
2774 }
Steve French790fe572007-07-07 19:25:05 +00002775 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776 rc = (__u16)(count * sizeof(struct cifs_posix_ace));
2777 rc += sizeof(struct cifs_posix_acl);
2778 /* BB add check to make sure ACL does not overflow SMB */
2779 }
2780 return rc;
2781}
2782
2783int
2784CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002785 const unsigned char *searchName,
2786 char *acl_inf, const int buflen, const int acl_type,
2787 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788{
2789/* SMB_QUERY_POSIX_ACL */
2790 TRANSACTION2_QPI_REQ *pSMB = NULL;
2791 TRANSACTION2_QPI_RSP *pSMBr = NULL;
2792 int rc = 0;
2793 int bytes_returned;
2794 int name_len;
2795 __u16 params, byte_count;
Steve French50c2f752007-07-13 00:33:32 +00002796
Joe Perchesb6b38f72010-04-21 03:50:45 +00002797 cFYI(1, "In GetPosixACL (Unix) for path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798
2799queryAclRetry:
2800 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2801 (void **) &pSMBr);
2802 if (rc)
2803 return rc;
Steve French50c2f752007-07-13 00:33:32 +00002804
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2806 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002807 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07002808 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 name_len++; /* trailing null */
2810 name_len *= 2;
2811 pSMB->FileName[name_len] = 0;
2812 pSMB->FileName[name_len+1] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002813 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 name_len = strnlen(searchName, PATH_MAX);
2815 name_len++; /* trailing null */
2816 strncpy(pSMB->FileName, searchName, name_len);
2817 }
2818
2819 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
2820 pSMB->TotalDataCount = 0;
2821 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French50c2f752007-07-13 00:33:32 +00002822 /* BB find exact max data count below from sess structure BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823 pSMB->MaxDataCount = cpu_to_le16(4000);
2824 pSMB->MaxSetupCount = 0;
2825 pSMB->Reserved = 0;
2826 pSMB->Flags = 0;
2827 pSMB->Timeout = 0;
2828 pSMB->Reserved2 = 0;
2829 pSMB->ParameterOffset = cpu_to_le16(
Steve French50c2f752007-07-13 00:33:32 +00002830 offsetof(struct smb_com_transaction2_qpi_req,
2831 InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 pSMB->DataCount = 0;
2833 pSMB->DataOffset = 0;
2834 pSMB->SetupCount = 1;
2835 pSMB->Reserved3 = 0;
2836 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
2837 byte_count = params + 1 /* pad */ ;
2838 pSMB->TotalParameterCount = cpu_to_le16(params);
2839 pSMB->ParameterCount = pSMB->TotalParameterCount;
2840 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_ACL);
2841 pSMB->Reserved4 = 0;
2842 pSMB->hdr.smb_buf_length += byte_count;
2843 pSMB->ByteCount = cpu_to_le16(byte_count);
2844
2845 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2846 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French0a4b92c2006-01-12 15:44:21 -08002847 cifs_stats_inc(&tcon->num_acl_get);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00002849 cFYI(1, "Send error in Query POSIX ACL = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850 } else {
2851 /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00002852
Linus Torvalds1da177e2005-04-16 15:20:36 -07002853 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
2854 if (rc || (pSMBr->ByteCount < 2))
2855 /* BB also check enough total bytes returned */
2856 rc = -EIO; /* bad smb */
2857 else {
2858 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
2859 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
2860 rc = cifs_copy_posix_acl(acl_inf,
2861 (char *)&pSMBr->hdr.Protocol+data_offset,
Steve French50c2f752007-07-13 00:33:32 +00002862 buflen, acl_type, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863 }
2864 }
2865 cifs_buf_release(pSMB);
2866 if (rc == -EAGAIN)
2867 goto queryAclRetry;
2868 return rc;
2869}
2870
2871int
2872CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00002873 const unsigned char *fileName,
2874 const char *local_acl, const int buflen,
2875 const int acl_type,
2876 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877{
2878 struct smb_com_transaction2_spi_req *pSMB = NULL;
2879 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
2880 char *parm_data;
2881 int name_len;
2882 int rc = 0;
2883 int bytes_returned = 0;
2884 __u16 params, byte_count, data_count, param_offset, offset;
2885
Joe Perchesb6b38f72010-04-21 03:50:45 +00002886 cFYI(1, "In SetPosixACL (Unix) for path %s", fileName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002887setAclRetry:
2888 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002889 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890 if (rc)
2891 return rc;
2892 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
2893 name_len =
Steve French50c2f752007-07-13 00:33:32 +00002894 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07002895 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896 name_len++; /* trailing null */
2897 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00002898 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002899 name_len = strnlen(fileName, PATH_MAX);
2900 name_len++; /* trailing null */
2901 strncpy(pSMB->FileName, fileName, name_len);
2902 }
2903 params = 6 + name_len;
2904 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00002905 /* BB find max SMB size from sess */
2906 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002907 pSMB->MaxSetupCount = 0;
2908 pSMB->Reserved = 0;
2909 pSMB->Flags = 0;
2910 pSMB->Timeout = 0;
2911 pSMB->Reserved2 = 0;
2912 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00002913 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914 offset = param_offset + params;
2915 parm_data = ((char *) &pSMB->hdr.Protocol) + offset;
2916 pSMB->ParameterOffset = cpu_to_le16(param_offset);
2917
2918 /* convert to on the wire format for POSIX ACL */
Steve French50c2f752007-07-13 00:33:32 +00002919 data_count = ACL_to_cifs_posix(parm_data, local_acl, buflen, acl_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002920
Steve French790fe572007-07-07 19:25:05 +00002921 if (data_count == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002922 rc = -EOPNOTSUPP;
2923 goto setACLerrorExit;
2924 }
2925 pSMB->DataOffset = cpu_to_le16(offset);
2926 pSMB->SetupCount = 1;
2927 pSMB->Reserved3 = 0;
2928 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
2929 pSMB->InformationLevel = cpu_to_le16(SMB_SET_POSIX_ACL);
2930 byte_count = 3 /* pad */ + params + data_count;
2931 pSMB->DataCount = cpu_to_le16(data_count);
2932 pSMB->TotalDataCount = pSMB->DataCount;
2933 pSMB->ParameterCount = cpu_to_le16(params);
2934 pSMB->TotalParameterCount = pSMB->ParameterCount;
2935 pSMB->Reserved4 = 0;
2936 pSMB->hdr.smb_buf_length += byte_count;
2937 pSMB->ByteCount = cpu_to_le16(byte_count);
2938 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00002939 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00002940 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00002941 cFYI(1, "Set POSIX ACL returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942
2943setACLerrorExit:
2944 cifs_buf_release(pSMB);
2945 if (rc == -EAGAIN)
2946 goto setAclRetry;
2947 return rc;
2948}
2949
Steve Frenchf654bac2005-04-28 22:41:04 -07002950/* BB fix tabs in this function FIXME BB */
2951int
2952CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
Steve Frenchad7a2922008-02-07 23:25:02 +00002953 const int netfid, __u64 *pExtAttrBits, __u64 *pMask)
Steve Frenchf654bac2005-04-28 22:41:04 -07002954{
Steve French50c2f752007-07-13 00:33:32 +00002955 int rc = 0;
2956 struct smb_t2_qfi_req *pSMB = NULL;
2957 struct smb_t2_qfi_rsp *pSMBr = NULL;
2958 int bytes_returned;
2959 __u16 params, byte_count;
Steve Frenchf654bac2005-04-28 22:41:04 -07002960
Joe Perchesb6b38f72010-04-21 03:50:45 +00002961 cFYI(1, "In GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00002962 if (tcon == NULL)
2963 return -ENODEV;
Steve Frenchf654bac2005-04-28 22:41:04 -07002964
2965GetExtAttrRetry:
Steve French790fe572007-07-07 19:25:05 +00002966 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
2967 (void **) &pSMBr);
2968 if (rc)
2969 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07002970
Steve Frenchad7a2922008-02-07 23:25:02 +00002971 params = 2 /* level */ + 2 /* fid */;
Steve French790fe572007-07-07 19:25:05 +00002972 pSMB->t2.TotalDataCount = 0;
2973 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
2974 /* BB find exact max data count below from sess structure BB */
2975 pSMB->t2.MaxDataCount = cpu_to_le16(4000);
2976 pSMB->t2.MaxSetupCount = 0;
2977 pSMB->t2.Reserved = 0;
2978 pSMB->t2.Flags = 0;
2979 pSMB->t2.Timeout = 0;
2980 pSMB->t2.Reserved2 = 0;
2981 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
2982 Fid) - 4);
2983 pSMB->t2.DataCount = 0;
2984 pSMB->t2.DataOffset = 0;
2985 pSMB->t2.SetupCount = 1;
2986 pSMB->t2.Reserved3 = 0;
2987 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
2988 byte_count = params + 1 /* pad */ ;
2989 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
2990 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
2991 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_ATTR_FLAGS);
2992 pSMB->Pad = 0;
Steve Frenchf654bac2005-04-28 22:41:04 -07002993 pSMB->Fid = netfid;
Steve French790fe572007-07-07 19:25:05 +00002994 pSMB->hdr.smb_buf_length += byte_count;
2995 pSMB->t2.ByteCount = cpu_to_le16(byte_count);
Steve Frenchf654bac2005-04-28 22:41:04 -07002996
Steve French790fe572007-07-07 19:25:05 +00002997 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
2998 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
2999 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003000 cFYI(1, "error %d in GetExtAttr", rc);
Steve French790fe572007-07-07 19:25:05 +00003001 } else {
3002 /* decode response */
3003 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3004 if (rc || (pSMBr->ByteCount < 2))
3005 /* BB also check enough total bytes returned */
3006 /* If rc should we check for EOPNOSUPP and
3007 disable the srvino flag? or in caller? */
3008 rc = -EIO; /* bad smb */
3009 else {
3010 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3011 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
3012 struct file_chattr_info *pfinfo;
3013 /* BB Do we need a cast or hash here ? */
3014 if (count != 16) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003015 cFYI(1, "Illegal size ret in GetExtAttr");
Steve French790fe572007-07-07 19:25:05 +00003016 rc = -EIO;
3017 goto GetExtAttrOut;
3018 }
3019 pfinfo = (struct file_chattr_info *)
3020 (data_offset + (char *) &pSMBr->hdr.Protocol);
3021 *pExtAttrBits = le64_to_cpu(pfinfo->mode);
Steve Frenchf654bac2005-04-28 22:41:04 -07003022 *pMask = le64_to_cpu(pfinfo->mask);
Steve French790fe572007-07-07 19:25:05 +00003023 }
3024 }
Steve Frenchf654bac2005-04-28 22:41:04 -07003025GetExtAttrOut:
Steve French790fe572007-07-07 19:25:05 +00003026 cifs_buf_release(pSMB);
3027 if (rc == -EAGAIN)
3028 goto GetExtAttrRetry;
3029 return rc;
Steve Frenchf654bac2005-04-28 22:41:04 -07003030}
3031
Steve Frenchf654bac2005-04-28 22:41:04 -07003032#endif /* CONFIG_POSIX */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003033
Steve French297647c2007-10-12 04:11:59 +00003034#ifdef CONFIG_CIFS_EXPERIMENTAL
Steve French0a4b92c2006-01-12 15:44:21 -08003035/* Get Security Descriptor (by handle) from remote server for a file or dir */
3036int
3037CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
Steve French630f3f0c2007-10-25 21:17:17 +00003038 struct cifs_ntsd **acl_inf, __u32 *pbuflen)
Steve French0a4b92c2006-01-12 15:44:21 -08003039{
3040 int rc = 0;
3041 int buf_type = 0;
Steve Frenchad7a2922008-02-07 23:25:02 +00003042 QUERY_SEC_DESC_REQ *pSMB;
Steve French0a4b92c2006-01-12 15:44:21 -08003043 struct kvec iov[1];
3044
Joe Perchesb6b38f72010-04-21 03:50:45 +00003045 cFYI(1, "GetCifsACL");
Steve French0a4b92c2006-01-12 15:44:21 -08003046
Steve French630f3f0c2007-10-25 21:17:17 +00003047 *pbuflen = 0;
3048 *acl_inf = NULL;
3049
Steve Frenchb9c7a2b2007-10-26 23:40:20 +00003050 rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
Steve French0a4b92c2006-01-12 15:44:21 -08003051 8 /* parm len */, tcon, (void **) &pSMB);
3052 if (rc)
3053 return rc;
3054
3055 pSMB->MaxParameterCount = cpu_to_le32(4);
3056 /* BB TEST with big acls that might need to be e.g. larger than 16K */
3057 pSMB->MaxSetupCount = 0;
3058 pSMB->Fid = fid; /* file handle always le */
3059 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_OWNER | CIFS_ACL_GROUP |
3060 CIFS_ACL_DACL);
3061 pSMB->ByteCount = cpu_to_le16(11); /* 3 bytes pad + 8 bytes parm */
3062 pSMB->hdr.smb_buf_length += 11;
3063 iov[0].iov_base = (char *)pSMB;
3064 iov[0].iov_len = pSMB->hdr.smb_buf_length + 4;
3065
Steve Frencha761ac52007-10-18 21:45:27 +00003066 rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovec */, &buf_type,
Steve French133672e2007-11-13 22:41:37 +00003067 CIFS_STD_OP);
Steve French0a4b92c2006-01-12 15:44:21 -08003068 cifs_stats_inc(&tcon->num_acl_get);
3069 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003070 cFYI(1, "Send error in QuerySecDesc = %d", rc);
Steve French0a4b92c2006-01-12 15:44:21 -08003071 } else { /* decode response */
Steve Frenchad7a2922008-02-07 23:25:02 +00003072 __le32 *parm;
Steve French630f3f0c2007-10-25 21:17:17 +00003073 __u32 parm_len;
3074 __u32 acl_len;
Steve French50c2f752007-07-13 00:33:32 +00003075 struct smb_com_ntransact_rsp *pSMBr;
Steve French630f3f0c2007-10-25 21:17:17 +00003076 char *pdata;
Steve French0a4b92c2006-01-12 15:44:21 -08003077
3078/* validate_nttransact */
Steve French50c2f752007-07-13 00:33:32 +00003079 rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
Steve French630f3f0c2007-10-25 21:17:17 +00003080 &pdata, &parm_len, pbuflen);
Steve French790fe572007-07-07 19:25:05 +00003081 if (rc)
Steve French0a4b92c2006-01-12 15:44:21 -08003082 goto qsec_out;
3083 pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
3084
Joe Perchesb6b38f72010-04-21 03:50:45 +00003085 cFYI(1, "smb %p parm %p data %p", pSMBr, parm, *acl_inf);
Steve French0a4b92c2006-01-12 15:44:21 -08003086
3087 if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
3088 rc = -EIO; /* bad smb */
Steve French630f3f0c2007-10-25 21:17:17 +00003089 *pbuflen = 0;
Steve French0a4b92c2006-01-12 15:44:21 -08003090 goto qsec_out;
3091 }
3092
3093/* BB check that data area is minimum length and as big as acl_len */
3094
Steve Frenchaf6f4612007-10-16 18:40:37 +00003095 acl_len = le32_to_cpu(*parm);
Steve French630f3f0c2007-10-25 21:17:17 +00003096 if (acl_len != *pbuflen) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003097 cERROR(1, "acl length %d does not match %d",
3098 acl_len, *pbuflen);
Steve French630f3f0c2007-10-25 21:17:17 +00003099 if (*pbuflen > acl_len)
3100 *pbuflen = acl_len;
3101 }
Steve French0a4b92c2006-01-12 15:44:21 -08003102
Steve French630f3f0c2007-10-25 21:17:17 +00003103 /* check if buffer is big enough for the acl
3104 header followed by the smallest SID */
3105 if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
3106 (*pbuflen >= 64 * 1024)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003107 cERROR(1, "bad acl length %d", *pbuflen);
Steve French630f3f0c2007-10-25 21:17:17 +00003108 rc = -EINVAL;
3109 *pbuflen = 0;
3110 } else {
3111 *acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
3112 if (*acl_inf == NULL) {
3113 *pbuflen = 0;
3114 rc = -ENOMEM;
3115 }
3116 memcpy(*acl_inf, pdata, *pbuflen);
3117 }
Steve French0a4b92c2006-01-12 15:44:21 -08003118 }
3119qsec_out:
Steve French790fe572007-07-07 19:25:05 +00003120 if (buf_type == CIFS_SMALL_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003121 cifs_small_buf_release(iov[0].iov_base);
Steve French790fe572007-07-07 19:25:05 +00003122 else if (buf_type == CIFS_LARGE_BUFFER)
Steve French0a4b92c2006-01-12 15:44:21 -08003123 cifs_buf_release(iov[0].iov_base);
Steve French4b8f9302006-02-26 16:41:18 +00003124/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
Steve French0a4b92c2006-01-12 15:44:21 -08003125 return rc;
3126}
Steve French97837582007-12-31 07:47:21 +00003127
3128int
3129CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
3130 struct cifs_ntsd *pntsd, __u32 acllen)
3131{
3132 __u16 byte_count, param_count, data_count, param_offset, data_offset;
3133 int rc = 0;
3134 int bytes_returned = 0;
3135 SET_SEC_DESC_REQ *pSMB = NULL;
3136 NTRANSACT_RSP *pSMBr = NULL;
3137
3138setCifsAclRetry:
3139 rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
3140 (void **) &pSMBr);
3141 if (rc)
3142 return (rc);
3143
3144 pSMB->MaxSetupCount = 0;
3145 pSMB->Reserved = 0;
3146
3147 param_count = 8;
3148 param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
3149 data_count = acllen;
3150 data_offset = param_offset + param_count;
3151 byte_count = 3 /* pad */ + param_count;
3152
3153 pSMB->DataCount = cpu_to_le32(data_count);
3154 pSMB->TotalDataCount = pSMB->DataCount;
3155 pSMB->MaxParameterCount = cpu_to_le32(4);
3156 pSMB->MaxDataCount = cpu_to_le32(16384);
3157 pSMB->ParameterCount = cpu_to_le32(param_count);
3158 pSMB->ParameterOffset = cpu_to_le32(param_offset);
3159 pSMB->TotalParameterCount = pSMB->ParameterCount;
3160 pSMB->DataOffset = cpu_to_le32(data_offset);
3161 pSMB->SetupCount = 0;
3162 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
3163 pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
3164
3165 pSMB->Fid = fid; /* file handle always le */
3166 pSMB->Reserved2 = 0;
3167 pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
3168
3169 if (pntsd && acllen) {
3170 memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
3171 (char *) pntsd,
3172 acllen);
3173 pSMB->hdr.smb_buf_length += (byte_count + data_count);
3174
3175 } else
3176 pSMB->hdr.smb_buf_length += byte_count;
3177
3178 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3179 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3180
Joe Perchesb6b38f72010-04-21 03:50:45 +00003181 cFYI(1, "SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc);
Steve French97837582007-12-31 07:47:21 +00003182 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003183 cFYI(1, "Set CIFS ACL returned %d", rc);
Steve French97837582007-12-31 07:47:21 +00003184 cifs_buf_release(pSMB);
3185
3186 if (rc == -EAGAIN)
3187 goto setCifsAclRetry;
3188
3189 return (rc);
3190}
3191
Steve French297647c2007-10-12 04:11:59 +00003192#endif /* CONFIG_CIFS_EXPERIMENTAL */
Steve French0a4b92c2006-01-12 15:44:21 -08003193
Steve French6b8edfe2005-08-23 20:26:03 -07003194/* Legacy Query Path Information call for lookup to old servers such
3195 as Win9x/WinME */
3196int SMBQueryInformation(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003197 const unsigned char *searchName,
3198 FILE_ALL_INFO *pFinfo,
3199 const struct nls_table *nls_codepage, int remap)
Steve French6b8edfe2005-08-23 20:26:03 -07003200{
Steve Frenchad7a2922008-02-07 23:25:02 +00003201 QUERY_INFORMATION_REQ *pSMB;
3202 QUERY_INFORMATION_RSP *pSMBr;
Steve French6b8edfe2005-08-23 20:26:03 -07003203 int rc = 0;
3204 int bytes_returned;
3205 int name_len;
3206
Joe Perchesb6b38f72010-04-21 03:50:45 +00003207 cFYI(1, "In SMBQPath path %s", searchName);
Steve French6b8edfe2005-08-23 20:26:03 -07003208QInfRetry:
3209 rc = smb_init(SMB_COM_QUERY_INFORMATION, 0, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003210 (void **) &pSMBr);
Steve French6b8edfe2005-08-23 20:26:03 -07003211 if (rc)
3212 return rc;
3213
3214 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3215 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003216 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
3217 PATH_MAX, nls_codepage, remap);
Steve French6b8edfe2005-08-23 20:26:03 -07003218 name_len++; /* trailing null */
3219 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003220 } else {
Steve French6b8edfe2005-08-23 20:26:03 -07003221 name_len = strnlen(searchName, PATH_MAX);
3222 name_len++; /* trailing null */
3223 strncpy(pSMB->FileName, searchName, name_len);
3224 }
3225 pSMB->BufferFormat = 0x04;
Steve French50c2f752007-07-13 00:33:32 +00003226 name_len++; /* account for buffer type byte */
Steve French6b8edfe2005-08-23 20:26:03 -07003227 pSMB->hdr.smb_buf_length += (__u16) name_len;
3228 pSMB->ByteCount = cpu_to_le16(name_len);
3229
3230 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003231 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve French6b8edfe2005-08-23 20:26:03 -07003232 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003233 cFYI(1, "Send error in QueryInfo = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003234 } else if (pFinfo) {
Steve French1bd5bbc2006-09-28 03:35:57 +00003235 struct timespec ts;
3236 __u32 time = le32_to_cpu(pSMBr->last_write_time);
Steve Frenchad7a2922008-02-07 23:25:02 +00003237
3238 /* decode response */
Steve French1bd5bbc2006-09-28 03:35:57 +00003239 /* BB FIXME - add time zone adjustment BB */
Steve French6b8edfe2005-08-23 20:26:03 -07003240 memset(pFinfo, 0, sizeof(FILE_ALL_INFO));
Steve French1bd5bbc2006-09-28 03:35:57 +00003241 ts.tv_nsec = 0;
3242 ts.tv_sec = time;
3243 /* decode time fields */
Al Viro733f99a2006-10-14 16:48:26 +01003244 pFinfo->ChangeTime = cpu_to_le64(cifs_UnixTimeToNT(ts));
Steve French1bd5bbc2006-09-28 03:35:57 +00003245 pFinfo->LastWriteTime = pFinfo->ChangeTime;
3246 pFinfo->LastAccessTime = 0;
Steve French70ca7342005-09-22 16:32:06 -07003247 pFinfo->AllocationSize =
3248 cpu_to_le64(le32_to_cpu(pSMBr->size));
3249 pFinfo->EndOfFile = pFinfo->AllocationSize;
3250 pFinfo->Attributes =
3251 cpu_to_le32(le16_to_cpu(pSMBr->attr));
Steve French6b8edfe2005-08-23 20:26:03 -07003252 } else
3253 rc = -EIO; /* bad buffer passed in */
3254
3255 cifs_buf_release(pSMB);
3256
3257 if (rc == -EAGAIN)
3258 goto QInfRetry;
3259
3260 return rc;
3261}
3262
Jeff Laytonbcd53572010-02-12 07:44:16 -05003263int
3264CIFSSMBQFileInfo(const int xid, struct cifsTconInfo *tcon,
3265 u16 netfid, FILE_ALL_INFO *pFindData)
3266{
3267 struct smb_t2_qfi_req *pSMB = NULL;
3268 struct smb_t2_qfi_rsp *pSMBr = NULL;
3269 int rc = 0;
3270 int bytes_returned;
3271 __u16 params, byte_count;
Steve French6b8edfe2005-08-23 20:26:03 -07003272
Jeff Laytonbcd53572010-02-12 07:44:16 -05003273QFileInfoRetry:
3274 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3275 (void **) &pSMBr);
3276 if (rc)
3277 return rc;
Steve French6b8edfe2005-08-23 20:26:03 -07003278
Jeff Laytonbcd53572010-02-12 07:44:16 -05003279 params = 2 /* level */ + 2 /* fid */;
3280 pSMB->t2.TotalDataCount = 0;
3281 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3282 /* BB find exact max data count below from sess structure BB */
3283 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3284 pSMB->t2.MaxSetupCount = 0;
3285 pSMB->t2.Reserved = 0;
3286 pSMB->t2.Flags = 0;
3287 pSMB->t2.Timeout = 0;
3288 pSMB->t2.Reserved2 = 0;
3289 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3290 Fid) - 4);
3291 pSMB->t2.DataCount = 0;
3292 pSMB->t2.DataOffset = 0;
3293 pSMB->t2.SetupCount = 1;
3294 pSMB->t2.Reserved3 = 0;
3295 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3296 byte_count = params + 1 /* pad */ ;
3297 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3298 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3299 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
3300 pSMB->Pad = 0;
3301 pSMB->Fid = netfid;
3302 pSMB->hdr.smb_buf_length += byte_count;
3303
3304 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3305 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3306 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003307 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonbcd53572010-02-12 07:44:16 -05003308 } else { /* decode response */
3309 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3310
3311 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3312 rc = -EIO;
3313 else if (pSMBr->ByteCount < 40)
3314 rc = -EIO; /* bad smb */
3315 else if (pFindData) {
3316 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3317 memcpy((char *) pFindData,
3318 (char *) &pSMBr->hdr.Protocol +
3319 data_offset, sizeof(FILE_ALL_INFO));
3320 } else
3321 rc = -ENOMEM;
3322 }
3323 cifs_buf_release(pSMB);
3324 if (rc == -EAGAIN)
3325 goto QFileInfoRetry;
3326
3327 return rc;
3328}
Steve French6b8edfe2005-08-23 20:26:03 -07003329
Linus Torvalds1da177e2005-04-16 15:20:36 -07003330int
3331CIFSSMBQPathInfo(const int xid, struct cifsTconInfo *tcon,
3332 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003333 FILE_ALL_INFO *pFindData,
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003334 int legacy /* old style infolevel */,
Steve French737b7582005-04-28 22:41:06 -07003335 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003336{
3337/* level 263 SMB_QUERY_FILE_ALL_INFO */
3338 TRANSACTION2_QPI_REQ *pSMB = NULL;
3339 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3340 int rc = 0;
3341 int bytes_returned;
3342 int name_len;
3343 __u16 params, byte_count;
3344
Joe Perchesb6b38f72010-04-21 03:50:45 +00003345/* cFYI(1, "In QPathInfo path %s", searchName); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003346QPathInfoRetry:
3347 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3348 (void **) &pSMBr);
3349 if (rc)
3350 return rc;
3351
3352 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3353 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003354 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003355 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003356 name_len++; /* trailing null */
3357 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003358 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003359 name_len = strnlen(searchName, PATH_MAX);
3360 name_len++; /* trailing null */
3361 strncpy(pSMB->FileName, searchName, name_len);
3362 }
3363
Steve French50c2f752007-07-13 00:33:32 +00003364 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003365 pSMB->TotalDataCount = 0;
3366 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00003367 /* BB find exact max SMB PDU from sess structure BB */
3368 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003369 pSMB->MaxSetupCount = 0;
3370 pSMB->Reserved = 0;
3371 pSMB->Flags = 0;
3372 pSMB->Timeout = 0;
3373 pSMB->Reserved2 = 0;
3374 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003375 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003376 pSMB->DataCount = 0;
3377 pSMB->DataOffset = 0;
3378 pSMB->SetupCount = 1;
3379 pSMB->Reserved3 = 0;
3380 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3381 byte_count = params + 1 /* pad */ ;
3382 pSMB->TotalParameterCount = cpu_to_le16(params);
3383 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00003384 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003385 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD);
3386 else
3387 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388 pSMB->Reserved4 = 0;
3389 pSMB->hdr.smb_buf_length += byte_count;
3390 pSMB->ByteCount = cpu_to_le16(byte_count);
3391
3392 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3393 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3394 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003395 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003396 } else { /* decode response */
3397 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3398
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003399 if (rc) /* BB add auto retry on EOPNOTSUPP? */
3400 rc = -EIO;
Steve French50c2f752007-07-13 00:33:32 +00003401 else if (!legacy && (pSMBr->ByteCount < 40))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003402 rc = -EIO; /* bad smb */
Steve French790fe572007-07-07 19:25:05 +00003403 else if (legacy && (pSMBr->ByteCount < 24))
Steve French50c2f752007-07-13 00:33:32 +00003404 rc = -EIO; /* 24 or 26 expected but we do not read
3405 last field */
3406 else if (pFindData) {
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003407 int size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003408 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Steve Frenchad7a2922008-02-07 23:25:02 +00003409
3410 /* On legacy responses we do not read the last field,
3411 EAsize, fortunately since it varies by subdialect and
3412 also note it differs on Set vs. Get, ie two bytes or 4
3413 bytes depending but we don't care here */
3414 if (legacy)
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003415 size = sizeof(FILE_INFO_STANDARD);
3416 else
3417 size = sizeof(FILE_ALL_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003418 memcpy((char *) pFindData,
3419 (char *) &pSMBr->hdr.Protocol +
Steve Frenchacf1a1b2006-10-12 03:28:28 +00003420 data_offset, size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003421 } else
3422 rc = -ENOMEM;
3423 }
3424 cifs_buf_release(pSMB);
3425 if (rc == -EAGAIN)
3426 goto QPathInfoRetry;
3427
3428 return rc;
3429}
3430
3431int
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003432CIFSSMBUnixQFileInfo(const int xid, struct cifsTconInfo *tcon,
3433 u16 netfid, FILE_UNIX_BASIC_INFO *pFindData)
3434{
3435 struct smb_t2_qfi_req *pSMB = NULL;
3436 struct smb_t2_qfi_rsp *pSMBr = NULL;
3437 int rc = 0;
3438 int bytes_returned;
3439 __u16 params, byte_count;
3440
3441UnixQFileInfoRetry:
3442 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3443 (void **) &pSMBr);
3444 if (rc)
3445 return rc;
3446
3447 params = 2 /* level */ + 2 /* fid */;
3448 pSMB->t2.TotalDataCount = 0;
3449 pSMB->t2.MaxParameterCount = cpu_to_le16(4);
3450 /* BB find exact max data count below from sess structure BB */
3451 pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
3452 pSMB->t2.MaxSetupCount = 0;
3453 pSMB->t2.Reserved = 0;
3454 pSMB->t2.Flags = 0;
3455 pSMB->t2.Timeout = 0;
3456 pSMB->t2.Reserved2 = 0;
3457 pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(struct smb_t2_qfi_req,
3458 Fid) - 4);
3459 pSMB->t2.DataCount = 0;
3460 pSMB->t2.DataOffset = 0;
3461 pSMB->t2.SetupCount = 1;
3462 pSMB->t2.Reserved3 = 0;
3463 pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
3464 byte_count = params + 1 /* pad */ ;
3465 pSMB->t2.TotalParameterCount = cpu_to_le16(params);
3466 pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
3467 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3468 pSMB->Pad = 0;
3469 pSMB->Fid = netfid;
3470 pSMB->hdr.smb_buf_length += byte_count;
3471
3472 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3473 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3474 if (rc) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003475 cFYI(1, "Send error in QPathInfo = %d", rc);
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003476 } else { /* decode response */
3477 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3478
3479 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Steve Frenchf19159d2010-04-21 04:12:10 +00003480 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003481 "Unix Extensions can be disabled on mount "
Steve Frenchf19159d2010-04-21 04:12:10 +00003482 "by specifying the nosfu mount option.");
Jeff Laytonc8634fd2010-02-12 07:44:17 -05003483 rc = -EIO; /* bad smb */
3484 } else {
3485 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3486 memcpy((char *) pFindData,
3487 (char *) &pSMBr->hdr.Protocol +
3488 data_offset,
3489 sizeof(FILE_UNIX_BASIC_INFO));
3490 }
3491 }
3492
3493 cifs_buf_release(pSMB);
3494 if (rc == -EAGAIN)
3495 goto UnixQFileInfoRetry;
3496
3497 return rc;
3498}
3499
3500int
Linus Torvalds1da177e2005-04-16 15:20:36 -07003501CIFSSMBUnixQPathInfo(const int xid, struct cifsTconInfo *tcon,
3502 const unsigned char *searchName,
Steve French582d21e2008-05-13 04:54:12 +00003503 FILE_UNIX_BASIC_INFO *pFindData,
Steve French737b7582005-04-28 22:41:06 -07003504 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003505{
3506/* SMB_QUERY_FILE_UNIX_BASIC */
3507 TRANSACTION2_QPI_REQ *pSMB = NULL;
3508 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3509 int rc = 0;
3510 int bytes_returned = 0;
3511 int name_len;
3512 __u16 params, byte_count;
3513
Joe Perchesb6b38f72010-04-21 03:50:45 +00003514 cFYI(1, "In QPathInfo (Unix) the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003515UnixQPathInfoRetry:
3516 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3517 (void **) &pSMBr);
3518 if (rc)
3519 return rc;
3520
3521 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3522 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003523 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003524 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003525 name_len++; /* trailing null */
3526 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003527 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003528 name_len = strnlen(searchName, PATH_MAX);
3529 name_len++; /* trailing null */
3530 strncpy(pSMB->FileName, searchName, name_len);
3531 }
3532
Steve French50c2f752007-07-13 00:33:32 +00003533 params = 2 /* level */ + 4 /* reserved */ + name_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003534 pSMB->TotalDataCount = 0;
3535 pSMB->MaxParameterCount = cpu_to_le16(2);
3536 /* BB find exact max SMB PDU from sess structure BB */
Steve French50c2f752007-07-13 00:33:32 +00003537 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003538 pSMB->MaxSetupCount = 0;
3539 pSMB->Reserved = 0;
3540 pSMB->Flags = 0;
3541 pSMB->Timeout = 0;
3542 pSMB->Reserved2 = 0;
3543 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003544 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003545 pSMB->DataCount = 0;
3546 pSMB->DataOffset = 0;
3547 pSMB->SetupCount = 1;
3548 pSMB->Reserved3 = 0;
3549 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3550 byte_count = params + 1 /* pad */ ;
3551 pSMB->TotalParameterCount = cpu_to_le16(params);
3552 pSMB->ParameterCount = pSMB->TotalParameterCount;
3553 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_UNIX_BASIC);
3554 pSMB->Reserved4 = 0;
3555 pSMB->hdr.smb_buf_length += byte_count;
3556 pSMB->ByteCount = cpu_to_le16(byte_count);
3557
3558 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3559 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3560 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003561 cFYI(1, "Send error in QPathInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003562 } else { /* decode response */
3563 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3564
3565 if (rc || (pSMBr->ByteCount < sizeof(FILE_UNIX_BASIC_INFO))) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003566 cERROR(1, "Malformed FILE_UNIX_BASIC_INFO response.\n"
Steve French1e71f252007-09-20 15:30:07 +00003567 "Unix Extensions can be disabled on mount "
Joe Perchesb6b38f72010-04-21 03:50:45 +00003568 "by specifying the nosfu mount option.");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003569 rc = -EIO; /* bad smb */
3570 } else {
3571 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3572 memcpy((char *) pFindData,
3573 (char *) &pSMBr->hdr.Protocol +
3574 data_offset,
Steve French630f3f0c2007-10-25 21:17:17 +00003575 sizeof(FILE_UNIX_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003576 }
3577 }
3578 cifs_buf_release(pSMB);
3579 if (rc == -EAGAIN)
3580 goto UnixQPathInfoRetry;
3581
3582 return rc;
3583}
3584
Linus Torvalds1da177e2005-04-16 15:20:36 -07003585/* xid, tcon, searchName and codepage are input parms, rest are returned */
3586int
3587CIFSFindFirst(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003588 const char *searchName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003589 const struct nls_table *nls_codepage,
Steve French50c2f752007-07-13 00:33:32 +00003590 __u16 *pnetfid,
3591 struct cifs_search_info *psrch_inf, int remap, const char dirsep)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003592{
3593/* level 257 SMB_ */
3594 TRANSACTION2_FFIRST_REQ *pSMB = NULL;
3595 TRANSACTION2_FFIRST_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003596 T2_FFIRST_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003597 int rc = 0;
3598 int bytes_returned = 0;
3599 int name_len;
3600 __u16 params, byte_count;
3601
Joe Perchesb6b38f72010-04-21 03:50:45 +00003602 cFYI(1, "In FindFirst for %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003603
3604findFirstRetry:
3605 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3606 (void **) &pSMBr);
3607 if (rc)
3608 return rc;
3609
3610 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3611 name_len =
Steve French50c2f752007-07-13 00:33:32 +00003612 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07003613 PATH_MAX, nls_codepage, remap);
3614 /* We can not add the asterik earlier in case
3615 it got remapped to 0xF03A as if it were part of the
3616 directory name instead of a wildcard */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003617 name_len *= 2;
Jeremy Allisonac670552005-06-22 17:26:35 -07003618 pSMB->FileName[name_len] = dirsep;
Steve French737b7582005-04-28 22:41:06 -07003619 pSMB->FileName[name_len+1] = 0;
3620 pSMB->FileName[name_len+2] = '*';
3621 pSMB->FileName[name_len+3] = 0;
3622 name_len += 4; /* now the trailing null */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003623 pSMB->FileName[name_len] = 0; /* null terminate just in case */
3624 pSMB->FileName[name_len+1] = 0;
Steve French737b7582005-04-28 22:41:06 -07003625 name_len += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003626 } else { /* BB add check for overrun of SMB buf BB */
3627 name_len = strnlen(searchName, PATH_MAX);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003628/* BB fix here and in unicode clause above ie
Steve French790fe572007-07-07 19:25:05 +00003629 if (name_len > buffersize-header)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003630 free buffer exit; BB */
3631 strncpy(pSMB->FileName, searchName, name_len);
Jeremy Allisonac670552005-06-22 17:26:35 -07003632 pSMB->FileName[name_len] = dirsep;
Steve French68575472005-04-30 11:10:57 -07003633 pSMB->FileName[name_len+1] = '*';
3634 pSMB->FileName[name_len+2] = 0;
3635 name_len += 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003636 }
3637
3638 params = 12 + name_len /* includes null */ ;
3639 pSMB->TotalDataCount = 0; /* no EAs */
3640 pSMB->MaxParameterCount = cpu_to_le16(10);
3641 pSMB->MaxDataCount = cpu_to_le16((tcon->ses->server->maxBuf -
3642 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
3643 pSMB->MaxSetupCount = 0;
3644 pSMB->Reserved = 0;
3645 pSMB->Flags = 0;
3646 pSMB->Timeout = 0;
3647 pSMB->Reserved2 = 0;
3648 byte_count = params + 1 /* pad */ ;
3649 pSMB->TotalParameterCount = cpu_to_le16(params);
3650 pSMB->ParameterCount = pSMB->TotalParameterCount;
3651 pSMB->ParameterOffset = cpu_to_le16(
Steve French88274812006-03-09 22:21:45 +00003652 offsetof(struct smb_com_transaction2_ffirst_req, SearchAttributes)
3653 - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003654 pSMB->DataCount = 0;
3655 pSMB->DataOffset = 0;
3656 pSMB->SetupCount = 1; /* one byte, no need to make endian neutral */
3657 pSMB->Reserved3 = 0;
3658 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_FIRST);
3659 pSMB->SearchAttributes =
3660 cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM |
3661 ATTR_DIRECTORY);
Steve French50c2f752007-07-13 00:33:32 +00003662 pSMB->SearchCount = cpu_to_le16(CIFSMaxBufSize/sizeof(FILE_UNIX_INFO));
3663 pSMB->SearchFlags = cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END |
Linus Torvalds1da177e2005-04-16 15:20:36 -07003664 CIFS_SEARCH_RETURN_RESUME);
3665 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3666
3667 /* BB what should we set StorageType to? Does it matter? BB */
3668 pSMB->SearchStorageType = 0;
3669 pSMB->hdr.smb_buf_length += byte_count;
3670 pSMB->ByteCount = cpu_to_le16(byte_count);
3671
3672 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3673 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003674 cifs_stats_inc(&tcon->num_ffirst);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003675
Steve French88274812006-03-09 22:21:45 +00003676 if (rc) {/* BB add logic to retry regular search if Unix search
3677 rejected unexpectedly by server */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003678 /* BB Add code to handle unsupported level rc */
Joe Perchesb6b38f72010-04-21 03:50:45 +00003679 cFYI(1, "Error in FindFirst = %d", rc);
Steve French1982c342005-08-17 12:38:22 -07003680
Steve French88274812006-03-09 22:21:45 +00003681 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003682
3683 /* BB eventually could optimize out free and realloc of buf */
3684 /* for this case */
3685 if (rc == -EAGAIN)
3686 goto findFirstRetry;
3687 } else { /* decode response */
3688 /* BB remember to free buffer if error BB */
3689 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French790fe572007-07-07 19:25:05 +00003690 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003691 unsigned int lnoff;
3692
Linus Torvalds1da177e2005-04-16 15:20:36 -07003693 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003694 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003695 else
Steve French4b18f2a2008-04-29 00:06:05 +00003696 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003697
3698 psrch_inf->ntwrk_buf_start = (char *)pSMBr;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003699 psrch_inf->smallBuf = 0;
Steve French50c2f752007-07-13 00:33:32 +00003700 psrch_inf->srch_entries_start =
3701 (char *) &pSMBr->hdr.Protocol +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003702 le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003703 parms = (T2_FFIRST_RSP_PARMS *)((char *) &pSMBr->hdr.Protocol +
3704 le16_to_cpu(pSMBr->t2.ParameterOffset));
3705
Steve French790fe572007-07-07 19:25:05 +00003706 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003707 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003708 else
Steve French4b18f2a2008-04-29 00:06:05 +00003709 psrch_inf->endOfSearch = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003710
Steve French50c2f752007-07-13 00:33:32 +00003711 psrch_inf->entries_in_buffer =
3712 le16_to_cpu(parms->SearchCount);
Steve French60808232006-04-22 15:53:05 +00003713 psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
Linus Torvalds1da177e2005-04-16 15:20:36 -07003714 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003715 lnoff = le16_to_cpu(parms->LastNameOffset);
3716 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3717 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003718 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003719 psrch_inf->last_entry = NULL;
3720 return rc;
3721 }
3722
Steve French0752f152008-10-07 20:03:33 +00003723 psrch_inf->last_entry = psrch_inf->srch_entries_start +
Steve Frenchb77d7532008-10-08 19:13:46 +00003724 lnoff;
3725
Linus Torvalds1da177e2005-04-16 15:20:36 -07003726 *pnetfid = parms->SearchHandle;
3727 } else {
3728 cifs_buf_release(pSMB);
3729 }
3730 }
3731
3732 return rc;
3733}
3734
3735int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003736 __u16 searchHandle, struct cifs_search_info *psrch_inf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003737{
3738 TRANSACTION2_FNEXT_REQ *pSMB = NULL;
3739 TRANSACTION2_FNEXT_RSP *pSMBr = NULL;
Steve Frenchad7a2922008-02-07 23:25:02 +00003740 T2_FNEXT_RSP_PARMS *parms;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003741 char *response_data;
3742 int rc = 0;
3743 int bytes_returned, name_len;
3744 __u16 params, byte_count;
3745
Joe Perchesb6b38f72010-04-21 03:50:45 +00003746 cFYI(1, "In FindNext");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003747
Steve French4b18f2a2008-04-29 00:06:05 +00003748 if (psrch_inf->endOfSearch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003749 return -ENOENT;
3750
3751 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
3752 (void **) &pSMBr);
3753 if (rc)
3754 return rc;
3755
Steve French50c2f752007-07-13 00:33:32 +00003756 params = 14; /* includes 2 bytes of null string, converted to LE below*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003757 byte_count = 0;
3758 pSMB->TotalDataCount = 0; /* no EAs */
3759 pSMB->MaxParameterCount = cpu_to_le16(8);
3760 pSMB->MaxDataCount =
Steve French50c2f752007-07-13 00:33:32 +00003761 cpu_to_le16((tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) &
3762 0xFFFFFF00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003763 pSMB->MaxSetupCount = 0;
3764 pSMB->Reserved = 0;
3765 pSMB->Flags = 0;
3766 pSMB->Timeout = 0;
3767 pSMB->Reserved2 = 0;
3768 pSMB->ParameterOffset = cpu_to_le16(
3769 offsetof(struct smb_com_transaction2_fnext_req,SearchHandle) - 4);
3770 pSMB->DataCount = 0;
3771 pSMB->DataOffset = 0;
3772 pSMB->SetupCount = 1;
3773 pSMB->Reserved3 = 0;
3774 pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT);
3775 pSMB->SearchHandle = searchHandle; /* always kept as le */
3776 pSMB->SearchCount =
Steve French630f3f0c2007-10-25 21:17:17 +00003777 cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003778 pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level);
3779 pSMB->ResumeKey = psrch_inf->resume_key;
3780 pSMB->SearchFlags =
3781 cpu_to_le16(CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME);
3782
3783 name_len = psrch_inf->resume_name_len;
3784 params += name_len;
Steve French790fe572007-07-07 19:25:05 +00003785 if (name_len < PATH_MAX) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003786 memcpy(pSMB->ResumeFileName, psrch_inf->presume_name, name_len);
3787 byte_count += name_len;
Steve Frenchef6724e2005-08-02 21:31:05 -07003788 /* 14 byte parm len above enough for 2 byte null terminator */
3789 pSMB->ResumeFileName[name_len] = 0;
3790 pSMB->ResumeFileName[name_len+1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003791 } else {
3792 rc = -EINVAL;
3793 goto FNext2_err_exit;
3794 }
3795 byte_count = params + 1 /* pad */ ;
3796 pSMB->TotalParameterCount = cpu_to_le16(params);
3797 pSMB->ParameterCount = pSMB->TotalParameterCount;
3798 pSMB->hdr.smb_buf_length += byte_count;
3799 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00003800
Linus Torvalds1da177e2005-04-16 15:20:36 -07003801 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3802 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frencha4544342005-08-24 13:59:35 -07003803 cifs_stats_inc(&tcon->num_fnext);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003804 if (rc) {
3805 if (rc == -EBADF) {
Steve French4b18f2a2008-04-29 00:06:05 +00003806 psrch_inf->endOfSearch = true;
Jeff Layton63534502008-05-12 19:56:05 -07003807 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00003808 rc = 0; /* search probably was closed at end of search*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003809 } else
Joe Perchesb6b38f72010-04-21 03:50:45 +00003810 cFYI(1, "FindNext returned = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003811 } else { /* decode response */
3812 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve French50c2f752007-07-13 00:33:32 +00003813
Steve French790fe572007-07-07 19:25:05 +00003814 if (rc == 0) {
Steve Frenchb77d7532008-10-08 19:13:46 +00003815 unsigned int lnoff;
3816
Linus Torvalds1da177e2005-04-16 15:20:36 -07003817 /* BB fixme add lock for file (srch_info) struct here */
3818 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
Steve French4b18f2a2008-04-29 00:06:05 +00003819 psrch_inf->unicode = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003820 else
Steve French4b18f2a2008-04-29 00:06:05 +00003821 psrch_inf->unicode = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003822 response_data = (char *) &pSMBr->hdr.Protocol +
3823 le16_to_cpu(pSMBr->t2.ParameterOffset);
3824 parms = (T2_FNEXT_RSP_PARMS *)response_data;
3825 response_data = (char *)&pSMBr->hdr.Protocol +
3826 le16_to_cpu(pSMBr->t2.DataOffset);
Steve French790fe572007-07-07 19:25:05 +00003827 if (psrch_inf->smallBuf)
Steve Frenchd47d7c12006-02-28 03:45:48 +00003828 cifs_small_buf_release(
3829 psrch_inf->ntwrk_buf_start);
3830 else
3831 cifs_buf_release(psrch_inf->ntwrk_buf_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003832 psrch_inf->srch_entries_start = response_data;
3833 psrch_inf->ntwrk_buf_start = (char *)pSMB;
Steve Frenchd47d7c12006-02-28 03:45:48 +00003834 psrch_inf->smallBuf = 0;
Steve French790fe572007-07-07 19:25:05 +00003835 if (parms->EndofSearch)
Steve French4b18f2a2008-04-29 00:06:05 +00003836 psrch_inf->endOfSearch = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003837 else
Steve French4b18f2a2008-04-29 00:06:05 +00003838 psrch_inf->endOfSearch = false;
Steve French50c2f752007-07-13 00:33:32 +00003839 psrch_inf->entries_in_buffer =
3840 le16_to_cpu(parms->SearchCount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003841 psrch_inf->index_of_last_entry +=
3842 psrch_inf->entries_in_buffer;
Steve Frenchb77d7532008-10-08 19:13:46 +00003843 lnoff = le16_to_cpu(parms->LastNameOffset);
3844 if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
3845 lnoff) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003846 cERROR(1, "ignoring corrupt resume name");
Steve Frenchb77d7532008-10-08 19:13:46 +00003847 psrch_inf->last_entry = NULL;
3848 return rc;
3849 } else
3850 psrch_inf->last_entry =
3851 psrch_inf->srch_entries_start + lnoff;
3852
Joe Perchesb6b38f72010-04-21 03:50:45 +00003853/* cFYI(1, "fnxt2 entries in buf %d index_of_last %d",
3854 psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003855
3856 /* BB fixme add unlock here */
3857 }
3858
3859 }
3860
3861 /* BB On error, should we leave previous search buf (and count and
3862 last entry fields) intact or free the previous one? */
3863
3864 /* Note: On -EAGAIN error only caller can retry on handle based calls
3865 since file handle passed in no longer valid */
3866FNext2_err_exit:
3867 if (rc != 0)
3868 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003869 return rc;
3870}
3871
3872int
Steve French50c2f752007-07-13 00:33:32 +00003873CIFSFindClose(const int xid, struct cifsTconInfo *tcon,
3874 const __u16 searchHandle)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003875{
3876 int rc = 0;
3877 FINDCLOSE_REQ *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003878
Joe Perchesb6b38f72010-04-21 03:50:45 +00003879 cFYI(1, "In CIFSSMBFindClose");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003880 rc = small_smb_init(SMB_COM_FIND_CLOSE2, 1, tcon, (void **)&pSMB);
3881
3882 /* no sense returning error if session restarted
3883 as file handle has been closed */
Steve French790fe572007-07-07 19:25:05 +00003884 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003885 return 0;
3886 if (rc)
3887 return rc;
3888
Linus Torvalds1da177e2005-04-16 15:20:36 -07003889 pSMB->FileID = searchHandle;
3890 pSMB->ByteCount = 0;
Steve French133672e2007-11-13 22:41:37 +00003891 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00003892 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00003893 cERROR(1, "Send error in FindClose = %d", rc);
Steve Frenchad7a2922008-02-07 23:25:02 +00003894
Steve Frencha4544342005-08-24 13:59:35 -07003895 cifs_stats_inc(&tcon->num_fclose);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003896
3897 /* Since session is dead, search handle closed on server already */
3898 if (rc == -EAGAIN)
3899 rc = 0;
3900
3901 return rc;
3902}
3903
Linus Torvalds1da177e2005-04-16 15:20:36 -07003904int
3905CIFSGetSrvInodeNumber(const int xid, struct cifsTconInfo *tcon,
Steve French50c2f752007-07-13 00:33:32 +00003906 const unsigned char *searchName,
Steve Frenchad7a2922008-02-07 23:25:02 +00003907 __u64 *inode_number,
Steve French50c2f752007-07-13 00:33:32 +00003908 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003909{
3910 int rc = 0;
3911 TRANSACTION2_QPI_REQ *pSMB = NULL;
3912 TRANSACTION2_QPI_RSP *pSMBr = NULL;
3913 int name_len, bytes_returned;
3914 __u16 params, byte_count;
3915
Joe Perchesb6b38f72010-04-21 03:50:45 +00003916 cFYI(1, "In GetSrvInodeNum for %s", searchName);
Steve French790fe572007-07-07 19:25:05 +00003917 if (tcon == NULL)
Steve French50c2f752007-07-13 00:33:32 +00003918 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003919
3920GetInodeNumberRetry:
3921 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00003922 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003923 if (rc)
3924 return rc;
3925
Linus Torvalds1da177e2005-04-16 15:20:36 -07003926 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
3927 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05003928 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French50c2f752007-07-13 00:33:32 +00003929 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003930 name_len++; /* trailing null */
3931 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00003932 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003933 name_len = strnlen(searchName, PATH_MAX);
3934 name_len++; /* trailing null */
3935 strncpy(pSMB->FileName, searchName, name_len);
3936 }
3937
3938 params = 2 /* level */ + 4 /* rsrvd */ + name_len /* incl null */ ;
3939 pSMB->TotalDataCount = 0;
3940 pSMB->MaxParameterCount = cpu_to_le16(2);
3941 /* BB find exact max data count below from sess structure BB */
3942 pSMB->MaxDataCount = cpu_to_le16(4000);
3943 pSMB->MaxSetupCount = 0;
3944 pSMB->Reserved = 0;
3945 pSMB->Flags = 0;
3946 pSMB->Timeout = 0;
3947 pSMB->Reserved2 = 0;
3948 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00003949 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003950 pSMB->DataCount = 0;
3951 pSMB->DataOffset = 0;
3952 pSMB->SetupCount = 1;
3953 pSMB->Reserved3 = 0;
3954 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
3955 byte_count = params + 1 /* pad */ ;
3956 pSMB->TotalParameterCount = cpu_to_le16(params);
3957 pSMB->ParameterCount = pSMB->TotalParameterCount;
3958 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
3959 pSMB->Reserved4 = 0;
3960 pSMB->hdr.smb_buf_length += byte_count;
3961 pSMB->ByteCount = cpu_to_le16(byte_count);
3962
3963 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
3964 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
3965 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003966 cFYI(1, "error %d in QueryInternalInfo", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003967 } else {
3968 /* decode response */
3969 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
3970 if (rc || (pSMBr->ByteCount < 2))
3971 /* BB also check enough total bytes returned */
3972 /* If rc should we check for EOPNOSUPP and
3973 disable the srvino flag? or in caller? */
3974 rc = -EIO; /* bad smb */
Steve French50c2f752007-07-13 00:33:32 +00003975 else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003976 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
3977 __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
Steve French50c2f752007-07-13 00:33:32 +00003978 struct file_internal_info *pfinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003979 /* BB Do we need a cast or hash here ? */
Steve French790fe572007-07-07 19:25:05 +00003980 if (count < 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00003981 cFYI(1, "Illegal size ret in QryIntrnlInf");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003982 rc = -EIO;
3983 goto GetInodeNumOut;
3984 }
3985 pfinfo = (struct file_internal_info *)
3986 (data_offset + (char *) &pSMBr->hdr.Protocol);
Steve French85a6dac2009-04-01 05:22:00 +00003987 *inode_number = le64_to_cpu(pfinfo->UniqueId);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003988 }
3989 }
3990GetInodeNumOut:
3991 cifs_buf_release(pSMB);
3992 if (rc == -EAGAIN)
3993 goto GetInodeNumberRetry;
3994 return rc;
3995}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003996
Igor Mammedovfec45852008-05-16 13:06:30 +04003997/* parses DFS refferal V3 structure
3998 * caller is responsible for freeing target_nodes
3999 * returns:
4000 * on success - 0
4001 * on failure - errno
4002 */
4003static int
Steve Frencha1fe78f2008-05-16 18:48:38 +00004004parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
Igor Mammedovfec45852008-05-16 13:06:30 +04004005 unsigned int *num_of_nodes,
4006 struct dfs_info3_param **target_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004007 const struct nls_table *nls_codepage, int remap,
4008 const char *searchName)
Igor Mammedovfec45852008-05-16 13:06:30 +04004009{
4010 int i, rc = 0;
4011 char *data_end;
4012 bool is_unicode;
4013 struct dfs_referral_level_3 *ref;
4014
Harvey Harrison5ca33c62008-07-23 17:45:58 -07004015 if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
4016 is_unicode = true;
4017 else
4018 is_unicode = false;
Igor Mammedovfec45852008-05-16 13:06:30 +04004019 *num_of_nodes = le16_to_cpu(pSMBr->NumberOfReferrals);
4020
4021 if (*num_of_nodes < 1) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004022 cERROR(1, "num_referrals: must be at least > 0,"
4023 "but we get num_referrals = %d\n", *num_of_nodes);
Igor Mammedovfec45852008-05-16 13:06:30 +04004024 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004025 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004026 }
4027
4028 ref = (struct dfs_referral_level_3 *) &(pSMBr->referrals);
Al Viro1d92cfd2008-06-02 10:59:02 +01004029 if (ref->VersionNumber != cpu_to_le16(3)) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004030 cERROR(1, "Referrals of V%d version are not supported,"
4031 "should be V3", le16_to_cpu(ref->VersionNumber));
Igor Mammedovfec45852008-05-16 13:06:30 +04004032 rc = -EINVAL;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004033 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004034 }
4035
4036 /* get the upper boundary of the resp buffer */
4037 data_end = (char *)(&(pSMBr->PathConsumed)) +
4038 le16_to_cpu(pSMBr->t2.DataCount);
4039
Steve Frenchf19159d2010-04-21 04:12:10 +00004040 cFYI(1, "num_referrals: %d dfs flags: 0x%x ...\n",
Igor Mammedovfec45852008-05-16 13:06:30 +04004041 *num_of_nodes,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004042 le32_to_cpu(pSMBr->DFSFlags));
Igor Mammedovfec45852008-05-16 13:06:30 +04004043
4044 *target_nodes = kzalloc(sizeof(struct dfs_info3_param) *
4045 *num_of_nodes, GFP_KERNEL);
4046 if (*target_nodes == NULL) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004047 cERROR(1, "Failed to allocate buffer for target_nodes\n");
Igor Mammedovfec45852008-05-16 13:06:30 +04004048 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004049 goto parse_DFS_referrals_exit;
Igor Mammedovfec45852008-05-16 13:06:30 +04004050 }
4051
Daniel Mack3ad2f3f2010-02-03 08:01:28 +08004052 /* collect necessary data from referrals */
Igor Mammedovfec45852008-05-16 13:06:30 +04004053 for (i = 0; i < *num_of_nodes; i++) {
4054 char *temp;
4055 int max_len;
4056 struct dfs_info3_param *node = (*target_nodes)+i;
4057
Steve French0e0d2cf2009-05-01 05:27:32 +00004058 node->flags = le32_to_cpu(pSMBr->DFSFlags);
Igor Mammedov2c556082008-10-23 13:58:42 +04004059 if (is_unicode) {
Jeff Layton331c3132008-12-17 06:31:53 -05004060 __le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
4061 GFP_KERNEL);
Steve French2920ee22009-08-31 15:27:26 +00004062 if (tmp == NULL) {
4063 rc = -ENOMEM;
4064 goto parse_DFS_referrals_exit;
4065 }
Igor Mammedov2c556082008-10-23 13:58:42 +04004066 cifsConvertToUCS((__le16 *) tmp, searchName,
4067 PATH_MAX, nls_codepage, remap);
Jeff Layton69f801f2009-04-30 06:46:32 -04004068 node->path_consumed = cifs_ucs2_bytes(tmp,
4069 le16_to_cpu(pSMBr->PathConsumed),
Igor Mammedov2c556082008-10-23 13:58:42 +04004070 nls_codepage);
4071 kfree(tmp);
4072 } else
4073 node->path_consumed = le16_to_cpu(pSMBr->PathConsumed);
4074
Igor Mammedovfec45852008-05-16 13:06:30 +04004075 node->server_type = le16_to_cpu(ref->ServerType);
4076 node->ref_flag = le16_to_cpu(ref->ReferralEntryFlags);
4077
4078 /* copy DfsPath */
4079 temp = (char *)ref + le16_to_cpu(ref->DfsPathOffset);
4080 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004081 node->path_name = cifs_strndup_from_ucs(temp, max_len,
4082 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004083 if (!node->path_name) {
4084 rc = -ENOMEM;
Steve Frencha1fe78f2008-05-16 18:48:38 +00004085 goto parse_DFS_referrals_exit;
Jeff Layton066ce682009-04-30 07:16:14 -04004086 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004087
4088 /* copy link target UNC */
4089 temp = (char *)ref + le16_to_cpu(ref->NetworkAddressOffset);
4090 max_len = data_end - temp;
Steve Frenchd185cda2009-04-30 17:45:10 +00004091 node->node_name = cifs_strndup_from_ucs(temp, max_len,
4092 is_unicode, nls_codepage);
Jeff Laytond8e2f532009-05-14 07:46:59 -04004093 if (!node->node_name)
4094 rc = -ENOMEM;
Igor Mammedovfec45852008-05-16 13:06:30 +04004095 }
4096
Steve Frencha1fe78f2008-05-16 18:48:38 +00004097parse_DFS_referrals_exit:
Igor Mammedovfec45852008-05-16 13:06:30 +04004098 if (rc) {
4099 free_dfs_info_array(*target_nodes, *num_of_nodes);
4100 *target_nodes = NULL;
4101 *num_of_nodes = 0;
4102 }
4103 return rc;
4104}
4105
Linus Torvalds1da177e2005-04-16 15:20:36 -07004106int
4107CIFSGetDFSRefer(const int xid, struct cifsSesInfo *ses,
4108 const unsigned char *searchName,
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004109 struct dfs_info3_param **target_nodes,
4110 unsigned int *num_of_nodes,
Steve French737b7582005-04-28 22:41:06 -07004111 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004112{
4113/* TRANS2_GET_DFS_REFERRAL */
4114 TRANSACTION2_GET_DFS_REFER_REQ *pSMB = NULL;
4115 TRANSACTION2_GET_DFS_REFER_RSP *pSMBr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004116 int rc = 0;
4117 int bytes_returned;
4118 int name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004119 __u16 params, byte_count;
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004120 *num_of_nodes = 0;
4121 *target_nodes = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004122
Joe Perchesb6b38f72010-04-21 03:50:45 +00004123 cFYI(1, "In GetDFSRefer the path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004124 if (ses == NULL)
4125 return -ENODEV;
4126getDFSRetry:
4127 rc = smb_init(SMB_COM_TRANSACTION2, 15, NULL, (void **) &pSMB,
4128 (void **) &pSMBr);
4129 if (rc)
4130 return rc;
Steve French50c2f752007-07-13 00:33:32 +00004131
4132 /* server pointer checked in called function,
Steve French1982c342005-08-17 12:38:22 -07004133 but should never be null here anyway */
4134 pSMB->hdr.Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004135 pSMB->hdr.Tid = ses->ipc_tid;
4136 pSMB->hdr.Uid = ses->Suid;
Steve French26f57362007-08-30 22:09:15 +00004137 if (ses->capabilities & CAP_STATUS32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004138 pSMB->hdr.Flags2 |= SMBFLG2_ERR_STATUS;
Steve French26f57362007-08-30 22:09:15 +00004139 if (ses->capabilities & CAP_DFS)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004140 pSMB->hdr.Flags2 |= SMBFLG2_DFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004141
4142 if (ses->capabilities & CAP_UNICODE) {
4143 pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
4144 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004145 cifsConvertToUCS((__le16 *) pSMB->RequestFileName,
Steve French737b7582005-04-28 22:41:06 -07004146 searchName, PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004147 name_len++; /* trailing null */
4148 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00004149 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004150 name_len = strnlen(searchName, PATH_MAX);
4151 name_len++; /* trailing null */
4152 strncpy(pSMB->RequestFileName, searchName, name_len);
4153 }
4154
Steve French790fe572007-07-07 19:25:05 +00004155 if (ses->server) {
4156 if (ses->server->secMode &
Steve French1a4e15a2006-10-12 21:33:51 +00004157 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
4158 pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
4159 }
4160
Steve French50c2f752007-07-13 00:33:32 +00004161 pSMB->hdr.Uid = ses->Suid;
Steve French1a4e15a2006-10-12 21:33:51 +00004162
Linus Torvalds1da177e2005-04-16 15:20:36 -07004163 params = 2 /* level */ + name_len /*includes null */ ;
4164 pSMB->TotalDataCount = 0;
4165 pSMB->DataCount = 0;
4166 pSMB->DataOffset = 0;
4167 pSMB->MaxParameterCount = 0;
Steve French582d21e2008-05-13 04:54:12 +00004168 /* BB find exact max SMB PDU from sess structure BB */
4169 pSMB->MaxDataCount = cpu_to_le16(4000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004170 pSMB->MaxSetupCount = 0;
4171 pSMB->Reserved = 0;
4172 pSMB->Flags = 0;
4173 pSMB->Timeout = 0;
4174 pSMB->Reserved2 = 0;
4175 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004176 struct smb_com_transaction2_get_dfs_refer_req, MaxReferralLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004177 pSMB->SetupCount = 1;
4178 pSMB->Reserved3 = 0;
4179 pSMB->SubCommand = cpu_to_le16(TRANS2_GET_DFS_REFERRAL);
4180 byte_count = params + 3 /* pad */ ;
4181 pSMB->ParameterCount = cpu_to_le16(params);
4182 pSMB->TotalParameterCount = pSMB->ParameterCount;
4183 pSMB->MaxReferralLevel = cpu_to_le16(3);
4184 pSMB->hdr.smb_buf_length += byte_count;
4185 pSMB->ByteCount = cpu_to_le16(byte_count);
4186
4187 rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
4188 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4189 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004190 cFYI(1, "Send error in GetDFSRefer = %d", rc);
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004191 goto GetDFSRefExit;
4192 }
4193 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004194
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004195 /* BB Also check if enough total bytes returned? */
Igor Mammedovfec45852008-05-16 13:06:30 +04004196 if (rc || (pSMBr->ByteCount < 17)) {
Steve Frenchc2cf07d2008-05-15 06:20:02 +00004197 rc = -EIO; /* bad smb */
Igor Mammedovfec45852008-05-16 13:06:30 +04004198 goto GetDFSRefExit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004199 }
Igor Mammedovfec45852008-05-16 13:06:30 +04004200
Joe Perchesb6b38f72010-04-21 03:50:45 +00004201 cFYI(1, "Decoding GetDFSRefer response BCC: %d Offset %d",
Igor Mammedovfec45852008-05-16 13:06:30 +04004202 pSMBr->ByteCount,
Joe Perchesb6b38f72010-04-21 03:50:45 +00004203 le16_to_cpu(pSMBr->t2.DataOffset));
Igor Mammedovfec45852008-05-16 13:06:30 +04004204
4205 /* parse returned result into more usable form */
Steve Frencha1fe78f2008-05-16 18:48:38 +00004206 rc = parse_DFS_referrals(pSMBr, num_of_nodes,
Igor Mammedov2c556082008-10-23 13:58:42 +04004207 target_nodes, nls_codepage, remap,
4208 searchName);
Igor Mammedovfec45852008-05-16 13:06:30 +04004209
Linus Torvalds1da177e2005-04-16 15:20:36 -07004210GetDFSRefExit:
Steve French0d817bc2008-05-22 02:02:03 +00004211 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004212
4213 if (rc == -EAGAIN)
4214 goto getDFSRetry;
4215
4216 return rc;
4217}
4218
Steve French20962432005-09-21 22:05:57 -07004219/* Query File System Info such as free space to old servers such as Win 9x */
4220int
4221SMBOldQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
4222{
4223/* level 0x01 SMB_QUERY_FILE_SYSTEM_INFO */
4224 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4225 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4226 FILE_SYSTEM_ALLOC_INFO *response_data;
4227 int rc = 0;
4228 int bytes_returned = 0;
4229 __u16 params, byte_count;
4230
Joe Perchesb6b38f72010-04-21 03:50:45 +00004231 cFYI(1, "OldQFSInfo");
Steve French20962432005-09-21 22:05:57 -07004232oldQFSInfoRetry:
4233 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4234 (void **) &pSMBr);
4235 if (rc)
4236 return rc;
Steve French20962432005-09-21 22:05:57 -07004237
4238 params = 2; /* level */
4239 pSMB->TotalDataCount = 0;
4240 pSMB->MaxParameterCount = cpu_to_le16(2);
4241 pSMB->MaxDataCount = cpu_to_le16(1000);
4242 pSMB->MaxSetupCount = 0;
4243 pSMB->Reserved = 0;
4244 pSMB->Flags = 0;
4245 pSMB->Timeout = 0;
4246 pSMB->Reserved2 = 0;
4247 byte_count = params + 1 /* pad */ ;
4248 pSMB->TotalParameterCount = cpu_to_le16(params);
4249 pSMB->ParameterCount = pSMB->TotalParameterCount;
4250 pSMB->ParameterOffset = cpu_to_le16(offsetof(
4251 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
4252 pSMB->DataCount = 0;
4253 pSMB->DataOffset = 0;
4254 pSMB->SetupCount = 1;
4255 pSMB->Reserved3 = 0;
4256 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4257 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_ALLOCATION);
4258 pSMB->hdr.smb_buf_length += byte_count;
4259 pSMB->ByteCount = cpu_to_le16(byte_count);
4260
4261 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4262 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4263 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004264 cFYI(1, "Send error in QFSInfo = %d", rc);
Steve French20962432005-09-21 22:05:57 -07004265 } else { /* decode response */
4266 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4267
4268 if (rc || (pSMBr->ByteCount < 18))
4269 rc = -EIO; /* bad smb */
4270 else {
4271 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004272 cFYI(1, "qfsinf resp BCC: %d Offset %d",
4273 pSMBr->ByteCount, data_offset);
Steve French20962432005-09-21 22:05:57 -07004274
Steve French50c2f752007-07-13 00:33:32 +00004275 response_data = (FILE_SYSTEM_ALLOC_INFO *)
Steve French20962432005-09-21 22:05:57 -07004276 (((char *) &pSMBr->hdr.Protocol) + data_offset);
4277 FSData->f_bsize =
4278 le16_to_cpu(response_data->BytesPerSector) *
4279 le32_to_cpu(response_data->
4280 SectorsPerAllocationUnit);
4281 FSData->f_blocks =
Steve French50c2f752007-07-13 00:33:32 +00004282 le32_to_cpu(response_data->TotalAllocationUnits);
Steve French20962432005-09-21 22:05:57 -07004283 FSData->f_bfree = FSData->f_bavail =
4284 le32_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004285 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4286 (unsigned long long)FSData->f_blocks,
4287 (unsigned long long)FSData->f_bfree,
4288 FSData->f_bsize);
Steve French20962432005-09-21 22:05:57 -07004289 }
4290 }
4291 cifs_buf_release(pSMB);
4292
4293 if (rc == -EAGAIN)
4294 goto oldQFSInfoRetry;
4295
4296 return rc;
4297}
4298
Linus Torvalds1da177e2005-04-16 15:20:36 -07004299int
Steve French737b7582005-04-28 22:41:06 -07004300CIFSSMBQFSInfo(const int xid, struct cifsTconInfo *tcon, struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004301{
4302/* level 0x103 SMB_QUERY_FILE_SYSTEM_INFO */
4303 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4304 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4305 FILE_SYSTEM_INFO *response_data;
4306 int rc = 0;
4307 int bytes_returned = 0;
4308 __u16 params, byte_count;
4309
Joe Perchesb6b38f72010-04-21 03:50:45 +00004310 cFYI(1, "In QFSInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004311QFSInfoRetry:
4312 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4313 (void **) &pSMBr);
4314 if (rc)
4315 return rc;
4316
4317 params = 2; /* level */
4318 pSMB->TotalDataCount = 0;
4319 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French20962432005-09-21 22:05:57 -07004320 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004321 pSMB->MaxSetupCount = 0;
4322 pSMB->Reserved = 0;
4323 pSMB->Flags = 0;
4324 pSMB->Timeout = 0;
4325 pSMB->Reserved2 = 0;
4326 byte_count = params + 1 /* pad */ ;
4327 pSMB->TotalParameterCount = cpu_to_le16(params);
4328 pSMB->ParameterCount = pSMB->TotalParameterCount;
4329 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004330 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004331 pSMB->DataCount = 0;
4332 pSMB->DataOffset = 0;
4333 pSMB->SetupCount = 1;
4334 pSMB->Reserved3 = 0;
4335 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4336 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_SIZE_INFO);
4337 pSMB->hdr.smb_buf_length += byte_count;
4338 pSMB->ByteCount = cpu_to_le16(byte_count);
4339
4340 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4341 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4342 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004343 cFYI(1, "Send error in QFSInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004344 } else { /* decode response */
Steve French50c2f752007-07-13 00:33:32 +00004345 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004346
Steve French20962432005-09-21 22:05:57 -07004347 if (rc || (pSMBr->ByteCount < 24))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004348 rc = -EIO; /* bad smb */
4349 else {
4350 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004351
4352 response_data =
4353 (FILE_SYSTEM_INFO
4354 *) (((char *) &pSMBr->hdr.Protocol) +
4355 data_offset);
4356 FSData->f_bsize =
4357 le32_to_cpu(response_data->BytesPerSector) *
4358 le32_to_cpu(response_data->
4359 SectorsPerAllocationUnit);
4360 FSData->f_blocks =
4361 le64_to_cpu(response_data->TotalAllocationUnits);
4362 FSData->f_bfree = FSData->f_bavail =
4363 le64_to_cpu(response_data->FreeAllocationUnits);
Joe Perchesb6b38f72010-04-21 03:50:45 +00004364 cFYI(1, "Blocks: %lld Free: %lld Block size %ld",
4365 (unsigned long long)FSData->f_blocks,
4366 (unsigned long long)FSData->f_bfree,
4367 FSData->f_bsize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004368 }
4369 }
4370 cifs_buf_release(pSMB);
4371
4372 if (rc == -EAGAIN)
4373 goto QFSInfoRetry;
4374
4375 return rc;
4376}
4377
4378int
Steve French737b7582005-04-28 22:41:06 -07004379CIFSSMBQFSAttributeInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004380{
4381/* level 0x105 SMB_QUERY_FILE_SYSTEM_INFO */
4382 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4383 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4384 FILE_SYSTEM_ATTRIBUTE_INFO *response_data;
4385 int rc = 0;
4386 int bytes_returned = 0;
4387 __u16 params, byte_count;
4388
Joe Perchesb6b38f72010-04-21 03:50:45 +00004389 cFYI(1, "In QFSAttributeInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004390QFSAttributeRetry:
4391 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4392 (void **) &pSMBr);
4393 if (rc)
4394 return rc;
4395
4396 params = 2; /* level */
4397 pSMB->TotalDataCount = 0;
4398 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004399 /* BB find exact max SMB PDU from sess structure BB */
4400 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004401 pSMB->MaxSetupCount = 0;
4402 pSMB->Reserved = 0;
4403 pSMB->Flags = 0;
4404 pSMB->Timeout = 0;
4405 pSMB->Reserved2 = 0;
4406 byte_count = params + 1 /* pad */ ;
4407 pSMB->TotalParameterCount = cpu_to_le16(params);
4408 pSMB->ParameterCount = pSMB->TotalParameterCount;
4409 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004410 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004411 pSMB->DataCount = 0;
4412 pSMB->DataOffset = 0;
4413 pSMB->SetupCount = 1;
4414 pSMB->Reserved3 = 0;
4415 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4416 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_ATTRIBUTE_INFO);
4417 pSMB->hdr.smb_buf_length += byte_count;
4418 pSMB->ByteCount = cpu_to_le16(byte_count);
4419
4420 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4421 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4422 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004423 cERROR(1, "Send error in QFSAttributeInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004424 } else { /* decode response */
4425 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4426
Steve French50c2f752007-07-13 00:33:32 +00004427 if (rc || (pSMBr->ByteCount < 13)) {
4428 /* BB also check if enough bytes returned */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004429 rc = -EIO; /* bad smb */
4430 } else {
4431 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4432 response_data =
4433 (FILE_SYSTEM_ATTRIBUTE_INFO
4434 *) (((char *) &pSMBr->hdr.Protocol) +
4435 data_offset);
4436 memcpy(&tcon->fsAttrInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004437 sizeof(FILE_SYSTEM_ATTRIBUTE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004438 }
4439 }
4440 cifs_buf_release(pSMB);
4441
4442 if (rc == -EAGAIN)
4443 goto QFSAttributeRetry;
4444
4445 return rc;
4446}
4447
4448int
Steve French737b7582005-04-28 22:41:06 -07004449CIFSSMBQFSDeviceInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004450{
4451/* level 0x104 SMB_QUERY_FILE_SYSTEM_INFO */
4452 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4453 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4454 FILE_SYSTEM_DEVICE_INFO *response_data;
4455 int rc = 0;
4456 int bytes_returned = 0;
4457 __u16 params, byte_count;
4458
Joe Perchesb6b38f72010-04-21 03:50:45 +00004459 cFYI(1, "In QFSDeviceInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004460QFSDeviceRetry:
4461 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4462 (void **) &pSMBr);
4463 if (rc)
4464 return rc;
4465
4466 params = 2; /* level */
4467 pSMB->TotalDataCount = 0;
4468 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004469 /* BB find exact max SMB PDU from sess structure BB */
4470 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004471 pSMB->MaxSetupCount = 0;
4472 pSMB->Reserved = 0;
4473 pSMB->Flags = 0;
4474 pSMB->Timeout = 0;
4475 pSMB->Reserved2 = 0;
4476 byte_count = params + 1 /* pad */ ;
4477 pSMB->TotalParameterCount = cpu_to_le16(params);
4478 pSMB->ParameterCount = pSMB->TotalParameterCount;
4479 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00004480 struct smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004481
4482 pSMB->DataCount = 0;
4483 pSMB->DataOffset = 0;
4484 pSMB->SetupCount = 1;
4485 pSMB->Reserved3 = 0;
4486 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4487 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FS_DEVICE_INFO);
4488 pSMB->hdr.smb_buf_length += byte_count;
4489 pSMB->ByteCount = cpu_to_le16(byte_count);
4490
4491 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4492 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4493 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004494 cFYI(1, "Send error in QFSDeviceInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004495 } else { /* decode response */
4496 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4497
Steve French630f3f0c2007-10-25 21:17:17 +00004498 if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004499 rc = -EIO; /* bad smb */
4500 else {
4501 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4502 response_data =
Steve French737b7582005-04-28 22:41:06 -07004503 (FILE_SYSTEM_DEVICE_INFO *)
4504 (((char *) &pSMBr->hdr.Protocol) +
Linus Torvalds1da177e2005-04-16 15:20:36 -07004505 data_offset);
4506 memcpy(&tcon->fsDevInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004507 sizeof(FILE_SYSTEM_DEVICE_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004508 }
4509 }
4510 cifs_buf_release(pSMB);
4511
4512 if (rc == -EAGAIN)
4513 goto QFSDeviceRetry;
4514
4515 return rc;
4516}
4517
4518int
Steve French737b7582005-04-28 22:41:06 -07004519CIFSSMBQFSUnixInfo(const int xid, struct cifsTconInfo *tcon)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004520{
4521/* level 0x200 SMB_QUERY_CIFS_UNIX_INFO */
4522 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4523 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4524 FILE_SYSTEM_UNIX_INFO *response_data;
4525 int rc = 0;
4526 int bytes_returned = 0;
4527 __u16 params, byte_count;
4528
Joe Perchesb6b38f72010-04-21 03:50:45 +00004529 cFYI(1, "In QFSUnixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004530QFSUnixRetry:
4531 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4532 (void **) &pSMBr);
4533 if (rc)
4534 return rc;
4535
4536 params = 2; /* level */
4537 pSMB->TotalDataCount = 0;
4538 pSMB->DataCount = 0;
4539 pSMB->DataOffset = 0;
4540 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004541 /* BB find exact max SMB PDU from sess structure BB */
4542 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004543 pSMB->MaxSetupCount = 0;
4544 pSMB->Reserved = 0;
4545 pSMB->Flags = 0;
4546 pSMB->Timeout = 0;
4547 pSMB->Reserved2 = 0;
4548 byte_count = params + 1 /* pad */ ;
4549 pSMB->ParameterCount = cpu_to_le16(params);
4550 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004551 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4552 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004553 pSMB->SetupCount = 1;
4554 pSMB->Reserved3 = 0;
4555 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4556 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_CIFS_UNIX_INFO);
4557 pSMB->hdr.smb_buf_length += byte_count;
4558 pSMB->ByteCount = cpu_to_le16(byte_count);
4559
4560 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4561 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4562 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004563 cERROR(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004564 } else { /* decode response */
4565 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4566
4567 if (rc || (pSMBr->ByteCount < 13)) {
4568 rc = -EIO; /* bad smb */
4569 } else {
4570 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4571 response_data =
4572 (FILE_SYSTEM_UNIX_INFO
4573 *) (((char *) &pSMBr->hdr.Protocol) +
4574 data_offset);
4575 memcpy(&tcon->fsUnixInfo, response_data,
Steve French26f57362007-08-30 22:09:15 +00004576 sizeof(FILE_SYSTEM_UNIX_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07004577 }
4578 }
4579 cifs_buf_release(pSMB);
4580
4581 if (rc == -EAGAIN)
4582 goto QFSUnixRetry;
4583
4584
4585 return rc;
4586}
4587
Jeremy Allisonac670552005-06-22 17:26:35 -07004588int
Steve French45abc6e2005-06-23 13:42:03 -05004589CIFSSMBSetFSUnixInfo(const int xid, struct cifsTconInfo *tcon, __u64 cap)
Jeremy Allisonac670552005-06-22 17:26:35 -07004590{
4591/* level 0x200 SMB_SET_CIFS_UNIX_INFO */
4592 TRANSACTION2_SETFSI_REQ *pSMB = NULL;
4593 TRANSACTION2_SETFSI_RSP *pSMBr = NULL;
4594 int rc = 0;
4595 int bytes_returned = 0;
4596 __u16 params, param_offset, offset, byte_count;
4597
Joe Perchesb6b38f72010-04-21 03:50:45 +00004598 cFYI(1, "In SETFSUnixInfo");
Jeremy Allisonac670552005-06-22 17:26:35 -07004599SETFSUnixRetry:
Steve Frenchf26282c2006-03-01 09:17:37 +00004600 /* BB switch to small buf init to save memory */
Jeremy Allisonac670552005-06-22 17:26:35 -07004601 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4602 (void **) &pSMBr);
4603 if (rc)
4604 return rc;
4605
4606 params = 4; /* 2 bytes zero followed by info level. */
4607 pSMB->MaxSetupCount = 0;
4608 pSMB->Reserved = 0;
4609 pSMB->Flags = 0;
4610 pSMB->Timeout = 0;
4611 pSMB->Reserved2 = 0;
Steve French50c2f752007-07-13 00:33:32 +00004612 param_offset = offsetof(struct smb_com_transaction2_setfsi_req, FileNum)
4613 - 4;
Jeremy Allisonac670552005-06-22 17:26:35 -07004614 offset = param_offset + params;
4615
4616 pSMB->MaxParameterCount = cpu_to_le16(4);
Steve French582d21e2008-05-13 04:54:12 +00004617 /* BB find exact max SMB PDU from sess structure BB */
4618 pSMB->MaxDataCount = cpu_to_le16(100);
Jeremy Allisonac670552005-06-22 17:26:35 -07004619 pSMB->SetupCount = 1;
4620 pSMB->Reserved3 = 0;
4621 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FS_INFORMATION);
4622 byte_count = 1 /* pad */ + params + 12;
4623
4624 pSMB->DataCount = cpu_to_le16(12);
4625 pSMB->ParameterCount = cpu_to_le16(params);
4626 pSMB->TotalDataCount = pSMB->DataCount;
4627 pSMB->TotalParameterCount = pSMB->ParameterCount;
4628 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4629 pSMB->DataOffset = cpu_to_le16(offset);
4630
4631 /* Params. */
4632 pSMB->FileNum = 0;
4633 pSMB->InformationLevel = cpu_to_le16(SMB_SET_CIFS_UNIX_INFO);
4634
4635 /* Data. */
4636 pSMB->ClientUnixMajor = cpu_to_le16(CIFS_UNIX_MAJOR_VERSION);
4637 pSMB->ClientUnixMinor = cpu_to_le16(CIFS_UNIX_MINOR_VERSION);
4638 pSMB->ClientUnixCap = cpu_to_le64(cap);
4639
4640 pSMB->hdr.smb_buf_length += byte_count;
4641 pSMB->ByteCount = cpu_to_le16(byte_count);
4642
4643 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4644 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4645 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004646 cERROR(1, "Send error in SETFSUnixInfo = %d", rc);
Jeremy Allisonac670552005-06-22 17:26:35 -07004647 } else { /* decode response */
4648 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
Steve Frenchad7a2922008-02-07 23:25:02 +00004649 if (rc)
Jeremy Allisonac670552005-06-22 17:26:35 -07004650 rc = -EIO; /* bad smb */
Jeremy Allisonac670552005-06-22 17:26:35 -07004651 }
4652 cifs_buf_release(pSMB);
4653
4654 if (rc == -EAGAIN)
4655 goto SETFSUnixRetry;
4656
4657 return rc;
4658}
4659
4660
Linus Torvalds1da177e2005-04-16 15:20:36 -07004661
4662int
4663CIFSSMBQFSPosixInfo(const int xid, struct cifsTconInfo *tcon,
Steve French737b7582005-04-28 22:41:06 -07004664 struct kstatfs *FSData)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004665{
4666/* level 0x201 SMB_QUERY_CIFS_POSIX_INFO */
4667 TRANSACTION2_QFSI_REQ *pSMB = NULL;
4668 TRANSACTION2_QFSI_RSP *pSMBr = NULL;
4669 FILE_SYSTEM_POSIX_INFO *response_data;
4670 int rc = 0;
4671 int bytes_returned = 0;
4672 __u16 params, byte_count;
4673
Joe Perchesb6b38f72010-04-21 03:50:45 +00004674 cFYI(1, "In QFSPosixInfo");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004675QFSPosixRetry:
4676 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4677 (void **) &pSMBr);
4678 if (rc)
4679 return rc;
4680
4681 params = 2; /* level */
4682 pSMB->TotalDataCount = 0;
4683 pSMB->DataCount = 0;
4684 pSMB->DataOffset = 0;
4685 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004686 /* BB find exact max SMB PDU from sess structure BB */
4687 pSMB->MaxDataCount = cpu_to_le16(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004688 pSMB->MaxSetupCount = 0;
4689 pSMB->Reserved = 0;
4690 pSMB->Flags = 0;
4691 pSMB->Timeout = 0;
4692 pSMB->Reserved2 = 0;
4693 byte_count = params + 1 /* pad */ ;
4694 pSMB->ParameterCount = cpu_to_le16(params);
4695 pSMB->TotalParameterCount = pSMB->ParameterCount;
Steve French50c2f752007-07-13 00:33:32 +00004696 pSMB->ParameterOffset = cpu_to_le16(offsetof(struct
4697 smb_com_transaction2_qfsi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004698 pSMB->SetupCount = 1;
4699 pSMB->Reserved3 = 0;
4700 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_FS_INFORMATION);
4701 pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_POSIX_FS_INFO);
4702 pSMB->hdr.smb_buf_length += byte_count;
4703 pSMB->ByteCount = cpu_to_le16(byte_count);
4704
4705 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4706 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
4707 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004708 cFYI(1, "Send error in QFSUnixInfo = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004709 } else { /* decode response */
4710 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
4711
4712 if (rc || (pSMBr->ByteCount < 13)) {
4713 rc = -EIO; /* bad smb */
4714 } else {
4715 __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
4716 response_data =
4717 (FILE_SYSTEM_POSIX_INFO
4718 *) (((char *) &pSMBr->hdr.Protocol) +
4719 data_offset);
4720 FSData->f_bsize =
4721 le32_to_cpu(response_data->BlockSize);
4722 FSData->f_blocks =
4723 le64_to_cpu(response_data->TotalBlocks);
4724 FSData->f_bfree =
4725 le64_to_cpu(response_data->BlocksAvail);
Steve French790fe572007-07-07 19:25:05 +00004726 if (response_data->UserBlocksAvail == cpu_to_le64(-1)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004727 FSData->f_bavail = FSData->f_bfree;
4728 } else {
4729 FSData->f_bavail =
Steve French50c2f752007-07-13 00:33:32 +00004730 le64_to_cpu(response_data->UserBlocksAvail);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004731 }
Steve French790fe572007-07-07 19:25:05 +00004732 if (response_data->TotalFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004733 FSData->f_files =
Steve French50c2f752007-07-13 00:33:32 +00004734 le64_to_cpu(response_data->TotalFileNodes);
Steve French790fe572007-07-07 19:25:05 +00004735 if (response_data->FreeFileNodes != cpu_to_le64(-1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004736 FSData->f_ffree =
Steve French50c2f752007-07-13 00:33:32 +00004737 le64_to_cpu(response_data->FreeFileNodes);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004738 }
4739 }
4740 cifs_buf_release(pSMB);
4741
4742 if (rc == -EAGAIN)
4743 goto QFSPosixRetry;
4744
4745 return rc;
4746}
4747
4748
Steve French50c2f752007-07-13 00:33:32 +00004749/* We can not use write of zero bytes trick to
4750 set file size due to need for large file support. Also note that
4751 this SetPathInfo is preferred to SetFileInfo based method in next
Linus Torvalds1da177e2005-04-16 15:20:36 -07004752 routine which is only needed to work around a sharing violation bug
4753 in Samba which this routine can run into */
4754
4755int
4756CIFSSMBSetEOF(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French4b18f2a2008-04-29 00:06:05 +00004757 __u64 size, bool SetAllocation,
Steve French737b7582005-04-28 22:41:06 -07004758 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004759{
4760 struct smb_com_transaction2_spi_req *pSMB = NULL;
4761 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
4762 struct file_end_of_file_info *parm_data;
4763 int name_len;
4764 int rc = 0;
4765 int bytes_returned = 0;
4766 __u16 params, byte_count, data_count, param_offset, offset;
4767
Joe Perchesb6b38f72010-04-21 03:50:45 +00004768 cFYI(1, "In SetEOF");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004769SetEOFRetry:
4770 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
4771 (void **) &pSMBr);
4772 if (rc)
4773 return rc;
4774
4775 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
4776 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05004777 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07004778 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004779 name_len++; /* trailing null */
4780 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07004781 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004782 name_len = strnlen(fileName, PATH_MAX);
4783 name_len++; /* trailing null */
4784 strncpy(pSMB->FileName, fileName, name_len);
4785 }
4786 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00004787 data_count = sizeof(struct file_end_of_file_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004788 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French3e87d802005-09-18 20:49:21 -07004789 pSMB->MaxDataCount = cpu_to_le16(4100);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004790 pSMB->MaxSetupCount = 0;
4791 pSMB->Reserved = 0;
4792 pSMB->Flags = 0;
4793 pSMB->Timeout = 0;
4794 pSMB->Reserved2 = 0;
4795 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00004796 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004797 offset = param_offset + params;
Steve French790fe572007-07-07 19:25:05 +00004798 if (SetAllocation) {
Steve French50c2f752007-07-13 00:33:32 +00004799 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4800 pSMB->InformationLevel =
4801 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4802 else
4803 pSMB->InformationLevel =
4804 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
4805 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004806 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4807 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004808 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004809 else
4810 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004811 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004812 }
4813
4814 parm_data =
4815 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol) +
4816 offset);
4817 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4818 pSMB->DataOffset = cpu_to_le16(offset);
4819 pSMB->SetupCount = 1;
4820 pSMB->Reserved3 = 0;
4821 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
4822 byte_count = 3 /* pad */ + params + data_count;
4823 pSMB->DataCount = cpu_to_le16(data_count);
4824 pSMB->TotalDataCount = pSMB->DataCount;
4825 pSMB->ParameterCount = cpu_to_le16(params);
4826 pSMB->TotalParameterCount = pSMB->ParameterCount;
4827 pSMB->Reserved4 = 0;
4828 pSMB->hdr.smb_buf_length += byte_count;
4829 parm_data->FileSize = cpu_to_le64(size);
4830 pSMB->ByteCount = cpu_to_le16(byte_count);
4831 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
4832 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004833 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00004834 cFYI(1, "SetPathInfo (file size) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004835
4836 cifs_buf_release(pSMB);
4837
4838 if (rc == -EAGAIN)
4839 goto SetEOFRetry;
4840
4841 return rc;
4842}
4843
4844int
Steve French50c2f752007-07-13 00:33:32 +00004845CIFSSMBSetFileSize(const int xid, struct cifsTconInfo *tcon, __u64 size,
Steve French4b18f2a2008-04-29 00:06:05 +00004846 __u16 fid, __u32 pid_of_opener, bool SetAllocation)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004847{
4848 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004849 char *data_offset;
4850 struct file_end_of_file_info *parm_data;
4851 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004852 __u16 params, param_offset, offset, byte_count, count;
4853
Joe Perchesb6b38f72010-04-21 03:50:45 +00004854 cFYI(1, "SetFileSize (via SetFileInfo) %lld",
4855 (long long)size);
Steve Frenchcd634992005-04-28 22:41:10 -07004856 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4857
Linus Torvalds1da177e2005-04-16 15:20:36 -07004858 if (rc)
4859 return rc;
4860
4861 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4862 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004863
Linus Torvalds1da177e2005-04-16 15:20:36 -07004864 params = 6;
4865 pSMB->MaxSetupCount = 0;
4866 pSMB->Reserved = 0;
4867 pSMB->Flags = 0;
4868 pSMB->Timeout = 0;
4869 pSMB->Reserved2 = 0;
4870 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4871 offset = param_offset + params;
4872
Steve French50c2f752007-07-13 00:33:32 +00004873 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004874
4875 count = sizeof(struct file_end_of_file_info);
4876 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004877 /* BB find exact max SMB PDU from sess structure BB */
4878 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004879 pSMB->SetupCount = 1;
4880 pSMB->Reserved3 = 0;
4881 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4882 byte_count = 3 /* pad */ + params + count;
4883 pSMB->DataCount = cpu_to_le16(count);
4884 pSMB->ParameterCount = cpu_to_le16(params);
4885 pSMB->TotalDataCount = pSMB->DataCount;
4886 pSMB->TotalParameterCount = pSMB->ParameterCount;
4887 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4888 parm_data =
Steve French50c2f752007-07-13 00:33:32 +00004889 (struct file_end_of_file_info *) (((char *) &pSMB->hdr.Protocol)
4890 + offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004891 pSMB->DataOffset = cpu_to_le16(offset);
4892 parm_data->FileSize = cpu_to_le64(size);
4893 pSMB->Fid = fid;
Steve French790fe572007-07-07 19:25:05 +00004894 if (SetAllocation) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004895 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4896 pSMB->InformationLevel =
4897 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
4898 else
4899 pSMB->InformationLevel =
4900 cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO);
Steve French50c2f752007-07-13 00:33:32 +00004901 } else /* Set File Size */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004902 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4903 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004904 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004905 else
4906 pSMB->InformationLevel =
Steve French50c2f752007-07-13 00:33:32 +00004907 cpu_to_le16(SMB_SET_FILE_END_OF_FILE_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004908 }
4909 pSMB->Reserved4 = 0;
4910 pSMB->hdr.smb_buf_length += byte_count;
4911 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French133672e2007-11-13 22:41:37 +00004912 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004913 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00004914 cFYI(1, "Send error in SetFileInfo (SetFileSize) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004915 }
4916
Steve French50c2f752007-07-13 00:33:32 +00004917 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004918 since file handle passed in no longer valid */
4919
4920 return rc;
4921}
4922
Steve French50c2f752007-07-13 00:33:32 +00004923/* Some legacy servers such as NT4 require that the file times be set on
Linus Torvalds1da177e2005-04-16 15:20:36 -07004924 an open handle, rather than by pathname - this is awkward due to
4925 potential access conflicts on the open, but it is unavoidable for these
4926 old servers since the only other choice is to go from 100 nanosecond DCE
4927 time and resort to the original setpathinfo level which takes the ancient
4928 DOS time format with 2 second granularity */
4929int
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004930CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
4931 const FILE_BASIC_INFO *data, __u16 fid, __u32 pid_of_opener)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004932{
4933 struct smb_com_transaction2_sfi_req *pSMB = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004934 char *data_offset;
4935 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004936 __u16 params, param_offset, offset, byte_count, count;
4937
Joe Perchesb6b38f72010-04-21 03:50:45 +00004938 cFYI(1, "Set Times (via SetFileInfo)");
Steve Frenchcd634992005-04-28 22:41:10 -07004939 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
4940
Linus Torvalds1da177e2005-04-16 15:20:36 -07004941 if (rc)
4942 return rc;
4943
Jeff Layton2dd2dfa2008-08-02 07:26:12 -04004944 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
4945 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
Steve French50c2f752007-07-13 00:33:32 +00004946
Linus Torvalds1da177e2005-04-16 15:20:36 -07004947 params = 6;
4948 pSMB->MaxSetupCount = 0;
4949 pSMB->Reserved = 0;
4950 pSMB->Flags = 0;
4951 pSMB->Timeout = 0;
4952 pSMB->Reserved2 = 0;
4953 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
4954 offset = param_offset + params;
4955
Steve French50c2f752007-07-13 00:33:32 +00004956 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004957
Steve French26f57362007-08-30 22:09:15 +00004958 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004959 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00004960 /* BB find max SMB PDU from sess */
4961 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004962 pSMB->SetupCount = 1;
4963 pSMB->Reserved3 = 0;
4964 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
4965 byte_count = 3 /* pad */ + params + count;
4966 pSMB->DataCount = cpu_to_le16(count);
4967 pSMB->ParameterCount = cpu_to_le16(params);
4968 pSMB->TotalDataCount = pSMB->DataCount;
4969 pSMB->TotalParameterCount = pSMB->ParameterCount;
4970 pSMB->ParameterOffset = cpu_to_le16(param_offset);
4971 pSMB->DataOffset = cpu_to_le16(offset);
4972 pSMB->Fid = fid;
4973 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
4974 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
4975 else
4976 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
4977 pSMB->Reserved4 = 0;
4978 pSMB->hdr.smb_buf_length += byte_count;
4979 pSMB->ByteCount = cpu_to_le16(byte_count);
Steve French50c2f752007-07-13 00:33:32 +00004980 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Steve French133672e2007-11-13 22:41:37 +00004981 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00004982 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00004983 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004984
Steve French50c2f752007-07-13 00:33:32 +00004985 /* Note: On -EAGAIN error only caller can retry on handle based calls
Linus Torvalds1da177e2005-04-16 15:20:36 -07004986 since file handle passed in no longer valid */
4987
4988 return rc;
4989}
4990
Jeff Layton6d22f092008-09-23 11:48:35 -04004991int
4992CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
4993 bool delete_file, __u16 fid, __u32 pid_of_opener)
4994{
4995 struct smb_com_transaction2_sfi_req *pSMB = NULL;
4996 char *data_offset;
4997 int rc = 0;
4998 __u16 params, param_offset, offset, byte_count, count;
4999
Joe Perchesb6b38f72010-04-21 03:50:45 +00005000 cFYI(1, "Set File Disposition (via SetFileInfo)");
Jeff Layton6d22f092008-09-23 11:48:35 -04005001 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5002
5003 if (rc)
5004 return rc;
5005
5006 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5007 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5008
5009 params = 6;
5010 pSMB->MaxSetupCount = 0;
5011 pSMB->Reserved = 0;
5012 pSMB->Flags = 0;
5013 pSMB->Timeout = 0;
5014 pSMB->Reserved2 = 0;
5015 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5016 offset = param_offset + params;
5017
5018 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5019
5020 count = 1;
5021 pSMB->MaxParameterCount = cpu_to_le16(2);
5022 /* BB find max SMB PDU from sess */
5023 pSMB->MaxDataCount = cpu_to_le16(1000);
5024 pSMB->SetupCount = 1;
5025 pSMB->Reserved3 = 0;
5026 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5027 byte_count = 3 /* pad */ + params + count;
5028 pSMB->DataCount = cpu_to_le16(count);
5029 pSMB->ParameterCount = cpu_to_le16(params);
5030 pSMB->TotalDataCount = pSMB->DataCount;
5031 pSMB->TotalParameterCount = pSMB->ParameterCount;
5032 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5033 pSMB->DataOffset = cpu_to_le16(offset);
5034 pSMB->Fid = fid;
5035 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
5036 pSMB->Reserved4 = 0;
5037 pSMB->hdr.smb_buf_length += byte_count;
5038 pSMB->ByteCount = cpu_to_le16(byte_count);
5039 *data_offset = delete_file ? 1 : 0;
5040 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5041 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005042 cFYI(1, "Send error in SetFileDisposition = %d", rc);
Jeff Layton6d22f092008-09-23 11:48:35 -04005043
5044 return rc;
5045}
Linus Torvalds1da177e2005-04-16 15:20:36 -07005046
5047int
Jeff Layton6fc000e2008-08-02 07:26:12 -04005048CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
5049 const char *fileName, const FILE_BASIC_INFO *data,
5050 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005051{
5052 TRANSACTION2_SPI_REQ *pSMB = NULL;
5053 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5054 int name_len;
5055 int rc = 0;
5056 int bytes_returned = 0;
5057 char *data_offset;
5058 __u16 params, param_offset, offset, byte_count, count;
5059
Joe Perchesb6b38f72010-04-21 03:50:45 +00005060 cFYI(1, "In SetTimes");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005061
5062SetTimesRetry:
5063 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5064 (void **) &pSMBr);
5065 if (rc)
5066 return rc;
5067
5068 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5069 name_len =
Steve Frenchb1a45692005-05-17 16:07:23 -05005070 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005071 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005072 name_len++; /* trailing null */
5073 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005074 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005075 name_len = strnlen(fileName, PATH_MAX);
5076 name_len++; /* trailing null */
5077 strncpy(pSMB->FileName, fileName, name_len);
5078 }
5079
5080 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005081 count = sizeof(FILE_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005082 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005083 /* BB find max SMB PDU from sess structure BB */
5084 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005085 pSMB->MaxSetupCount = 0;
5086 pSMB->Reserved = 0;
5087 pSMB->Flags = 0;
5088 pSMB->Timeout = 0;
5089 pSMB->Reserved2 = 0;
5090 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005091 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005092 offset = param_offset + params;
5093 data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
5094 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5095 pSMB->DataOffset = cpu_to_le16(offset);
5096 pSMB->SetupCount = 1;
5097 pSMB->Reserved3 = 0;
5098 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5099 byte_count = 3 /* pad */ + params + count;
5100
5101 pSMB->DataCount = cpu_to_le16(count);
5102 pSMB->ParameterCount = cpu_to_le16(params);
5103 pSMB->TotalDataCount = pSMB->DataCount;
5104 pSMB->TotalParameterCount = pSMB->ParameterCount;
5105 if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
5106 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO2);
5107 else
5108 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_BASIC_INFO);
5109 pSMB->Reserved4 = 0;
5110 pSMB->hdr.smb_buf_length += byte_count;
Steve French26f57362007-08-30 22:09:15 +00005111 memcpy(data_offset, data, sizeof(FILE_BASIC_INFO));
Linus Torvalds1da177e2005-04-16 15:20:36 -07005112 pSMB->ByteCount = cpu_to_le16(byte_count);
5113 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5114 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005115 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005116 cFYI(1, "SetPathInfo (times) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005117
5118 cifs_buf_release(pSMB);
5119
5120 if (rc == -EAGAIN)
5121 goto SetTimesRetry;
5122
5123 return rc;
5124}
5125
5126/* Can not be used to set time stamps yet (due to old DOS time format) */
5127/* Can be used to set attributes */
5128#if 0 /* Possibly not needed - since it turns out that strangely NT4 has a bug
5129 handling it anyway and NT4 was what we thought it would be needed for
5130 Do not delete it until we prove whether needed for Win9x though */
5131int
5132CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon, char *fileName,
5133 __u16 dos_attrs, const struct nls_table *nls_codepage)
5134{
5135 SETATTR_REQ *pSMB = NULL;
5136 SETATTR_RSP *pSMBr = NULL;
5137 int rc = 0;
5138 int bytes_returned;
5139 int name_len;
5140
Joe Perchesb6b38f72010-04-21 03:50:45 +00005141 cFYI(1, "In SetAttrLegacy");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005142
5143SetAttrLgcyRetry:
5144 rc = smb_init(SMB_COM_SETATTR, 8, tcon, (void **) &pSMB,
5145 (void **) &pSMBr);
5146 if (rc)
5147 return rc;
5148
5149 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5150 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005151 ConvertToUCS((__le16 *) pSMB->fileName, fileName,
Linus Torvalds1da177e2005-04-16 15:20:36 -07005152 PATH_MAX, nls_codepage);
5153 name_len++; /* trailing null */
5154 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005155 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005156 name_len = strnlen(fileName, PATH_MAX);
5157 name_len++; /* trailing null */
5158 strncpy(pSMB->fileName, fileName, name_len);
5159 }
5160 pSMB->attr = cpu_to_le16(dos_attrs);
5161 pSMB->BufferFormat = 0x04;
5162 pSMB->hdr.smb_buf_length += name_len + 1;
5163 pSMB->ByteCount = cpu_to_le16(name_len + 1);
5164 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5165 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005166 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005167 cFYI(1, "Error in LegacySetAttr = %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005168
5169 cifs_buf_release(pSMB);
5170
5171 if (rc == -EAGAIN)
5172 goto SetAttrLgcyRetry;
5173
5174 return rc;
5175}
5176#endif /* temporarily unneeded SetAttr legacy function */
5177
Jeff Layton654cf142009-07-09 20:02:49 -04005178static void
5179cifs_fill_unix_set_info(FILE_UNIX_BASIC_INFO *data_offset,
5180 const struct cifs_unix_set_info_args *args)
5181{
5182 u64 mode = args->mode;
5183
5184 /*
5185 * Samba server ignores set of file size to zero due to bugs in some
5186 * older clients, but we should be precise - we use SetFileSize to
5187 * set file size and do not want to truncate file size to zero
5188 * accidently as happened on one Samba server beta by putting
5189 * zero instead of -1 here
5190 */
5191 data_offset->EndOfFile = cpu_to_le64(NO_CHANGE_64);
5192 data_offset->NumOfBytes = cpu_to_le64(NO_CHANGE_64);
5193 data_offset->LastStatusChange = cpu_to_le64(args->ctime);
5194 data_offset->LastAccessTime = cpu_to_le64(args->atime);
5195 data_offset->LastModificationTime = cpu_to_le64(args->mtime);
5196 data_offset->Uid = cpu_to_le64(args->uid);
5197 data_offset->Gid = cpu_to_le64(args->gid);
5198 /* better to leave device as zero when it is */
5199 data_offset->DevMajor = cpu_to_le64(MAJOR(args->device));
5200 data_offset->DevMinor = cpu_to_le64(MINOR(args->device));
5201 data_offset->Permissions = cpu_to_le64(mode);
5202
5203 if (S_ISREG(mode))
5204 data_offset->Type = cpu_to_le32(UNIX_FILE);
5205 else if (S_ISDIR(mode))
5206 data_offset->Type = cpu_to_le32(UNIX_DIR);
5207 else if (S_ISLNK(mode))
5208 data_offset->Type = cpu_to_le32(UNIX_SYMLINK);
5209 else if (S_ISCHR(mode))
5210 data_offset->Type = cpu_to_le32(UNIX_CHARDEV);
5211 else if (S_ISBLK(mode))
5212 data_offset->Type = cpu_to_le32(UNIX_BLOCKDEV);
5213 else if (S_ISFIFO(mode))
5214 data_offset->Type = cpu_to_le32(UNIX_FIFO);
5215 else if (S_ISSOCK(mode))
5216 data_offset->Type = cpu_to_le32(UNIX_SOCKET);
5217}
5218
Linus Torvalds1da177e2005-04-16 15:20:36 -07005219int
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005220CIFSSMBUnixSetFileInfo(const int xid, struct cifsTconInfo *tcon,
5221 const struct cifs_unix_set_info_args *args,
5222 u16 fid, u32 pid_of_opener)
5223{
5224 struct smb_com_transaction2_sfi_req *pSMB = NULL;
5225 FILE_UNIX_BASIC_INFO *data_offset;
5226 int rc = 0;
5227 u16 params, param_offset, offset, byte_count, count;
5228
Joe Perchesb6b38f72010-04-21 03:50:45 +00005229 cFYI(1, "Set Unix Info (via SetFileInfo)");
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005230 rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
5231
5232 if (rc)
5233 return rc;
5234
5235 pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
5236 pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
5237
5238 params = 6;
5239 pSMB->MaxSetupCount = 0;
5240 pSMB->Reserved = 0;
5241 pSMB->Flags = 0;
5242 pSMB->Timeout = 0;
5243 pSMB->Reserved2 = 0;
5244 param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
5245 offset = param_offset + params;
5246
5247 data_offset = (FILE_UNIX_BASIC_INFO *)
5248 ((char *)(&pSMB->hdr.Protocol) + offset);
5249 count = sizeof(FILE_UNIX_BASIC_INFO);
5250
5251 pSMB->MaxParameterCount = cpu_to_le16(2);
5252 /* BB find max SMB PDU from sess */
5253 pSMB->MaxDataCount = cpu_to_le16(1000);
5254 pSMB->SetupCount = 1;
5255 pSMB->Reserved3 = 0;
5256 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
5257 byte_count = 3 /* pad */ + params + count;
5258 pSMB->DataCount = cpu_to_le16(count);
5259 pSMB->ParameterCount = cpu_to_le16(params);
5260 pSMB->TotalDataCount = pSMB->DataCount;
5261 pSMB->TotalParameterCount = pSMB->ParameterCount;
5262 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5263 pSMB->DataOffset = cpu_to_le16(offset);
5264 pSMB->Fid = fid;
5265 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5266 pSMB->Reserved4 = 0;
5267 pSMB->hdr.smb_buf_length += byte_count;
5268 pSMB->ByteCount = cpu_to_le16(byte_count);
5269
5270 cifs_fill_unix_set_info(data_offset, args);
5271
5272 rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
5273 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005274 cFYI(1, "Send error in Set Time (SetFileInfo) = %d", rc);
Jeff Layton3bbeeb32009-07-09 20:02:50 -04005275
5276 /* Note: On -EAGAIN error only caller can retry on handle based calls
5277 since file handle passed in no longer valid */
5278
5279 return rc;
5280}
5281
5282int
Jeff Layton01ea95e2009-07-09 20:02:49 -04005283CIFSSMBUnixSetPathInfo(const int xid, struct cifsTconInfo *tcon, char *fileName,
5284 const struct cifs_unix_set_info_args *args,
5285 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005286{
5287 TRANSACTION2_SPI_REQ *pSMB = NULL;
5288 TRANSACTION2_SPI_RSP *pSMBr = NULL;
5289 int name_len;
5290 int rc = 0;
5291 int bytes_returned = 0;
5292 FILE_UNIX_BASIC_INFO *data_offset;
5293 __u16 params, param_offset, offset, count, byte_count;
5294
Joe Perchesb6b38f72010-04-21 03:50:45 +00005295 cFYI(1, "In SetUID/GID/Mode");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005296setPermsRetry:
5297 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5298 (void **) &pSMBr);
5299 if (rc)
5300 return rc;
5301
5302 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5303 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005304 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005305 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005306 name_len++; /* trailing null */
5307 name_len *= 2;
Steve French3e87d802005-09-18 20:49:21 -07005308 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005309 name_len = strnlen(fileName, PATH_MAX);
5310 name_len++; /* trailing null */
5311 strncpy(pSMB->FileName, fileName, name_len);
5312 }
5313
5314 params = 6 + name_len;
Steve French26f57362007-08-30 22:09:15 +00005315 count = sizeof(FILE_UNIX_BASIC_INFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005316 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005317 /* BB find max SMB PDU from sess structure BB */
5318 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005319 pSMB->MaxSetupCount = 0;
5320 pSMB->Reserved = 0;
5321 pSMB->Flags = 0;
5322 pSMB->Timeout = 0;
5323 pSMB->Reserved2 = 0;
5324 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005325 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005326 offset = param_offset + params;
5327 data_offset =
5328 (FILE_UNIX_BASIC_INFO *) ((char *) &pSMB->hdr.Protocol +
5329 offset);
5330 memset(data_offset, 0, count);
5331 pSMB->DataOffset = cpu_to_le16(offset);
5332 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5333 pSMB->SetupCount = 1;
5334 pSMB->Reserved3 = 0;
5335 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5336 byte_count = 3 /* pad */ + params + count;
5337 pSMB->ParameterCount = cpu_to_le16(params);
5338 pSMB->DataCount = cpu_to_le16(count);
5339 pSMB->TotalParameterCount = pSMB->ParameterCount;
5340 pSMB->TotalDataCount = pSMB->DataCount;
5341 pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_UNIX_BASIC);
5342 pSMB->Reserved4 = 0;
5343 pSMB->hdr.smb_buf_length += byte_count;
Steve French50c2f752007-07-13 00:33:32 +00005344
Jeff Layton654cf142009-07-09 20:02:49 -04005345 cifs_fill_unix_set_info(data_offset, args);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005346
5347 pSMB->ByteCount = cpu_to_le16(byte_count);
5348 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5349 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005350 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005351 cFYI(1, "SetPathInfo (perms) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005352
Steve French0d817bc2008-05-22 02:02:03 +00005353 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005354 if (rc == -EAGAIN)
5355 goto setPermsRetry;
5356 return rc;
5357}
5358
Steve French50c2f752007-07-13 00:33:32 +00005359int CIFSSMBNotify(const int xid, struct cifsTconInfo *tcon,
Steve French167a2512005-08-24 20:03:11 -07005360 const int notify_subdirs, const __u16 netfid,
Steve French50c2f752007-07-13 00:33:32 +00005361 __u32 filter, struct file *pfile, int multishot,
Steve French167a2512005-08-24 20:03:11 -07005362 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005363{
5364 int rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00005365 struct smb_com_transaction_change_notify_req *pSMB = NULL;
5366 struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL;
Steve Frenchabb15b82005-08-24 18:51:02 -07005367 struct dir_notify_req *dnotify_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005368 int bytes_returned;
5369
Joe Perchesb6b38f72010-04-21 03:50:45 +00005370 cFYI(1, "In CIFSSMBNotify for file handle %d", (int)netfid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005371 rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
Steve French50c2f752007-07-13 00:33:32 +00005372 (void **) &pSMBr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005373 if (rc)
5374 return rc;
5375
5376 pSMB->TotalParameterCount = 0 ;
5377 pSMB->TotalDataCount = 0;
5378 pSMB->MaxParameterCount = cpu_to_le32(2);
5379 /* BB find exact data count max from sess structure BB */
5380 pSMB->MaxDataCount = 0; /* same in little endian or be */
Steve French0a4b92c2006-01-12 15:44:21 -08005381/* BB VERIFY verify which is correct for above BB */
5382 pSMB->MaxDataCount = cpu_to_le32((tcon->ses->server->maxBuf -
5383 MAX_CIFS_HDR_SIZE) & 0xFFFFFF00);
5384
Linus Torvalds1da177e2005-04-16 15:20:36 -07005385 pSMB->MaxSetupCount = 4;
5386 pSMB->Reserved = 0;
5387 pSMB->ParameterOffset = 0;
5388 pSMB->DataCount = 0;
5389 pSMB->DataOffset = 0;
5390 pSMB->SetupCount = 4; /* single byte does not need le conversion */
5391 pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE);
5392 pSMB->ParameterCount = pSMB->TotalParameterCount;
Steve French790fe572007-07-07 19:25:05 +00005393 if (notify_subdirs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005394 pSMB->WatchTree = 1; /* one byte - no le conversion needed */
5395 pSMB->Reserved2 = 0;
5396 pSMB->CompletionFilter = cpu_to_le32(filter);
5397 pSMB->Fid = netfid; /* file handle always le */
5398 pSMB->ByteCount = 0;
5399
5400 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
Steve French133672e2007-11-13 22:41:37 +00005401 (struct smb_hdr *)pSMBr, &bytes_returned,
5402 CIFS_ASYNC_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005403 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005404 cFYI(1, "Error in Notify = %d", rc);
Steve Frenchff5dbd92005-08-24 17:10:36 -07005405 } else {
5406 /* Add file to outstanding requests */
Steve French50c2f752007-07-13 00:33:32 +00005407 /* BB change to kmem cache alloc */
Robert P. J. Day5cbded52006-12-13 00:35:56 -08005408 dnotify_req = kmalloc(
Steve French47c786e2005-10-11 20:03:18 -07005409 sizeof(struct dir_notify_req),
5410 GFP_KERNEL);
Steve French790fe572007-07-07 19:25:05 +00005411 if (dnotify_req) {
Steve French47c786e2005-10-11 20:03:18 -07005412 dnotify_req->Pid = pSMB->hdr.Pid;
5413 dnotify_req->PidHigh = pSMB->hdr.PidHigh;
5414 dnotify_req->Mid = pSMB->hdr.Mid;
5415 dnotify_req->Tid = pSMB->hdr.Tid;
5416 dnotify_req->Uid = pSMB->hdr.Uid;
5417 dnotify_req->netfid = netfid;
5418 dnotify_req->pfile = pfile;
5419 dnotify_req->filter = filter;
5420 dnotify_req->multishot = multishot;
5421 spin_lock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005422 list_add_tail(&dnotify_req->lhead,
Steve French47c786e2005-10-11 20:03:18 -07005423 &GlobalDnotifyReqList);
5424 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +00005425 } else
Steve French47c786e2005-10-11 20:03:18 -07005426 rc = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005427 }
5428 cifs_buf_release(pSMB);
Steve French50c2f752007-07-13 00:33:32 +00005429 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005430}
Jeff Layton31c05192010-02-10 16:18:26 -05005431
Linus Torvalds1da177e2005-04-16 15:20:36 -07005432#ifdef CONFIG_CIFS_XATTR
Jeff Layton31c05192010-02-10 16:18:26 -05005433/*
5434 * Do a path-based QUERY_ALL_EAS call and parse the result. This is a common
5435 * function used by listxattr and getxattr type calls. When ea_name is set,
5436 * it looks for that attribute name and stuffs that value into the EAData
5437 * buffer. When ea_name is NULL, it stuffs a list of attribute names into the
5438 * buffer. In both cases, the return value is either the length of the
5439 * resulting data or a negative error code. If EAData is a NULL pointer then
5440 * the data isn't copied to it, but the length is returned.
5441 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005442ssize_t
5443CIFSSMBQAllEAs(const int xid, struct cifsTconInfo *tcon,
Jeff Layton31c05192010-02-10 16:18:26 -05005444 const unsigned char *searchName, const unsigned char *ea_name,
5445 char *EAData, size_t buf_size,
5446 const struct nls_table *nls_codepage, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005447{
5448 /* BB assumes one setup word */
5449 TRANSACTION2_QPI_REQ *pSMB = NULL;
5450 TRANSACTION2_QPI_RSP *pSMBr = NULL;
5451 int rc = 0;
5452 int bytes_returned;
Jeff Layton6e462b92010-02-10 16:18:26 -05005453 int list_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005454 struct fealist *ea_response_data;
Steve French50c2f752007-07-13 00:33:32 +00005455 struct fea *temp_fea;
5456 char *temp_ptr;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005457 char *end_of_smb;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005458 __u16 params, byte_count, data_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005459
Joe Perchesb6b38f72010-04-21 03:50:45 +00005460 cFYI(1, "In Query All EAs path %s", searchName);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005461QAllEAsRetry:
5462 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5463 (void **) &pSMBr);
5464 if (rc)
5465 return rc;
5466
5467 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
Jeff Layton6e462b92010-02-10 16:18:26 -05005468 list_len =
Steve French50c2f752007-07-13 00:33:32 +00005469 cifsConvertToUCS((__le16 *) pSMB->FileName, searchName,
Steve French737b7582005-04-28 22:41:06 -07005470 PATH_MAX, nls_codepage, remap);
Jeff Layton6e462b92010-02-10 16:18:26 -05005471 list_len++; /* trailing null */
5472 list_len *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005473 } else { /* BB improve the check for buffer overruns BB */
Jeff Layton6e462b92010-02-10 16:18:26 -05005474 list_len = strnlen(searchName, PATH_MAX);
5475 list_len++; /* trailing null */
5476 strncpy(pSMB->FileName, searchName, list_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005477 }
5478
Jeff Layton6e462b92010-02-10 16:18:26 -05005479 params = 2 /* level */ + 4 /* reserved */ + list_len /* includes NUL */;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005480 pSMB->TotalDataCount = 0;
5481 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005482 /* BB find exact max SMB PDU from sess structure BB */
Jeff Laytone5296142010-02-10 16:18:26 -05005483 pSMB->MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005484 pSMB->MaxSetupCount = 0;
5485 pSMB->Reserved = 0;
5486 pSMB->Flags = 0;
5487 pSMB->Timeout = 0;
5488 pSMB->Reserved2 = 0;
5489 pSMB->ParameterOffset = cpu_to_le16(offsetof(
Steve French50c2f752007-07-13 00:33:32 +00005490 struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005491 pSMB->DataCount = 0;
5492 pSMB->DataOffset = 0;
5493 pSMB->SetupCount = 1;
5494 pSMB->Reserved3 = 0;
5495 pSMB->SubCommand = cpu_to_le16(TRANS2_QUERY_PATH_INFORMATION);
5496 byte_count = params + 1 /* pad */ ;
5497 pSMB->TotalParameterCount = cpu_to_le16(params);
5498 pSMB->ParameterCount = pSMB->TotalParameterCount;
5499 pSMB->InformationLevel = cpu_to_le16(SMB_INFO_QUERY_ALL_EAS);
5500 pSMB->Reserved4 = 0;
5501 pSMB->hdr.smb_buf_length += byte_count;
5502 pSMB->ByteCount = cpu_to_le16(byte_count);
5503
5504 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5505 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
5506 if (rc) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005507 cFYI(1, "Send error in QueryAllEAs = %d", rc);
Jeff Laytonf0d38682010-02-10 16:18:26 -05005508 goto QAllEAsOut;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005509 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005510
5511
5512 /* BB also check enough total bytes returned */
5513 /* BB we need to improve the validity checking
5514 of these trans2 responses */
5515
5516 rc = validate_t2((struct smb_t2_rsp *)pSMBr);
5517 if (rc || (pSMBr->ByteCount < 4)) {
5518 rc = -EIO; /* bad smb */
5519 goto QAllEAsOut;
5520 }
5521
5522 /* check that length of list is not more than bcc */
5523 /* check that each entry does not go beyond length
5524 of list */
5525 /* check that each element of each entry does not
5526 go beyond end of list */
5527 /* validate_trans2_offsets() */
5528 /* BB check if start of smb + data_offset > &bcc+ bcc */
5529
5530 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
5531 ea_response_data = (struct fealist *)
5532 (((char *) &pSMBr->hdr.Protocol) + data_offset);
5533
Jeff Layton6e462b92010-02-10 16:18:26 -05005534 list_len = le32_to_cpu(ea_response_data->list_len);
Joe Perchesb6b38f72010-04-21 03:50:45 +00005535 cFYI(1, "ea length %d", list_len);
Jeff Layton6e462b92010-02-10 16:18:26 -05005536 if (list_len <= 8) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005537 cFYI(1, "empty EA list returned from server");
Jeff Laytonf0d38682010-02-10 16:18:26 -05005538 goto QAllEAsOut;
5539 }
5540
Jeff Layton0cd126b2010-02-10 16:18:26 -05005541 /* make sure list_len doesn't go past end of SMB */
5542 end_of_smb = (char *)pByteArea(&pSMBr->hdr) + BCC(&pSMBr->hdr);
5543 if ((char *)ea_response_data + list_len > end_of_smb) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005544 cFYI(1, "EA list appears to go beyond SMB");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005545 rc = -EIO;
5546 goto QAllEAsOut;
5547 }
5548
Jeff Laytonf0d38682010-02-10 16:18:26 -05005549 /* account for ea list len */
Jeff Layton6e462b92010-02-10 16:18:26 -05005550 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005551 temp_fea = ea_response_data->list;
5552 temp_ptr = (char *)temp_fea;
Jeff Layton6e462b92010-02-10 16:18:26 -05005553 while (list_len > 0) {
Steve French122ca002010-02-24 21:56:48 +00005554 unsigned int name_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005555 __u16 value_len;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005556
Jeff Layton6e462b92010-02-10 16:18:26 -05005557 list_len -= 4;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005558 temp_ptr += 4;
Jeff Layton0cd126b2010-02-10 16:18:26 -05005559 /* make sure we can read name_len and value_len */
5560 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005561 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005562 rc = -EIO;
5563 goto QAllEAsOut;
5564 }
5565
5566 name_len = temp_fea->name_len;
5567 value_len = le16_to_cpu(temp_fea->value_len);
5568 list_len -= name_len + 1 + value_len;
5569 if (list_len < 0) {
Joe Perchesb6b38f72010-04-21 03:50:45 +00005570 cFYI(1, "EA entry goes beyond length of list");
Jeff Layton0cd126b2010-02-10 16:18:26 -05005571 rc = -EIO;
5572 goto QAllEAsOut;
5573 }
5574
Jeff Layton31c05192010-02-10 16:18:26 -05005575 if (ea_name) {
5576 if (strncmp(ea_name, temp_ptr, name_len) == 0) {
5577 temp_ptr += name_len + 1;
5578 rc = value_len;
5579 if (buf_size == 0)
5580 goto QAllEAsOut;
5581 if ((size_t)value_len > buf_size) {
5582 rc = -ERANGE;
5583 goto QAllEAsOut;
5584 }
5585 memcpy(EAData, temp_ptr, value_len);
5586 goto QAllEAsOut;
5587 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005588 } else {
Jeff Layton31c05192010-02-10 16:18:26 -05005589 /* account for prefix user. and trailing null */
5590 rc += (5 + 1 + name_len);
5591 if (rc < (int) buf_size) {
5592 memcpy(EAData, "user.", 5);
5593 EAData += 5;
5594 memcpy(EAData, temp_ptr, name_len);
5595 EAData += name_len;
5596 /* null terminate name */
5597 *EAData = 0;
5598 ++EAData;
5599 } else if (buf_size == 0) {
5600 /* skip copy - calc size only */
5601 } else {
5602 /* stop before overrun buffer */
5603 rc = -ERANGE;
5604 break;
5605 }
Jeff Laytonf0d38682010-02-10 16:18:26 -05005606 }
Jeff Layton0cd126b2010-02-10 16:18:26 -05005607 temp_ptr += name_len + 1 + value_len;
Jeff Laytonf0d38682010-02-10 16:18:26 -05005608 temp_fea = (struct fea *)temp_ptr;
5609 }
5610
Jeff Layton31c05192010-02-10 16:18:26 -05005611 /* didn't find the named attribute */
5612 if (ea_name)
5613 rc = -ENODATA;
5614
Jeff Laytonf0d38682010-02-10 16:18:26 -05005615QAllEAsOut:
Steve French0d817bc2008-05-22 02:02:03 +00005616 cifs_buf_release(pSMB);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005617 if (rc == -EAGAIN)
5618 goto QAllEAsRetry;
5619
5620 return (ssize_t)rc;
5621}
5622
Linus Torvalds1da177e2005-04-16 15:20:36 -07005623int
5624CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const char *fileName,
Steve French50c2f752007-07-13 00:33:32 +00005625 const char *ea_name, const void *ea_value,
5626 const __u16 ea_value_len, const struct nls_table *nls_codepage,
5627 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005628{
5629 struct smb_com_transaction2_spi_req *pSMB = NULL;
5630 struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
5631 struct fealist *parm_data;
5632 int name_len;
5633 int rc = 0;
5634 int bytes_returned = 0;
5635 __u16 params, param_offset, byte_count, offset, count;
5636
Joe Perchesb6b38f72010-04-21 03:50:45 +00005637 cFYI(1, "In SetEA");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005638SetEARetry:
5639 rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
5640 (void **) &pSMBr);
5641 if (rc)
5642 return rc;
5643
5644 if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
5645 name_len =
Steve French50c2f752007-07-13 00:33:32 +00005646 cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
Steve French737b7582005-04-28 22:41:06 -07005647 PATH_MAX, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005648 name_len++; /* trailing null */
5649 name_len *= 2;
Steve French50c2f752007-07-13 00:33:32 +00005650 } else { /* BB improve the check for buffer overruns BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07005651 name_len = strnlen(fileName, PATH_MAX);
5652 name_len++; /* trailing null */
5653 strncpy(pSMB->FileName, fileName, name_len);
5654 }
5655
5656 params = 6 + name_len;
5657
5658 /* done calculating parms using name_len of file name,
5659 now use name_len to calculate length of ea name
5660 we are going to create in the inode xattrs */
Steve French790fe572007-07-07 19:25:05 +00005661 if (ea_name == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005662 name_len = 0;
5663 else
Steve French50c2f752007-07-13 00:33:32 +00005664 name_len = strnlen(ea_name, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005665
Steve Frenchdae5dbdb2007-12-30 23:49:57 +00005666 count = sizeof(*parm_data) + ea_value_len + name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005667 pSMB->MaxParameterCount = cpu_to_le16(2);
Steve French582d21e2008-05-13 04:54:12 +00005668 /* BB find max SMB PDU from sess */
5669 pSMB->MaxDataCount = cpu_to_le16(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005670 pSMB->MaxSetupCount = 0;
5671 pSMB->Reserved = 0;
5672 pSMB->Flags = 0;
5673 pSMB->Timeout = 0;
5674 pSMB->Reserved2 = 0;
5675 param_offset = offsetof(struct smb_com_transaction2_spi_req,
Steve French50c2f752007-07-13 00:33:32 +00005676 InformationLevel) - 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005677 offset = param_offset + params;
5678 pSMB->InformationLevel =
5679 cpu_to_le16(SMB_SET_FILE_EA);
5680
5681 parm_data =
5682 (struct fealist *) (((char *) &pSMB->hdr.Protocol) +
5683 offset);
5684 pSMB->ParameterOffset = cpu_to_le16(param_offset);
5685 pSMB->DataOffset = cpu_to_le16(offset);
5686 pSMB->SetupCount = 1;
5687 pSMB->Reserved3 = 0;
5688 pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
5689 byte_count = 3 /* pad */ + params + count;
5690 pSMB->DataCount = cpu_to_le16(count);
5691 parm_data->list_len = cpu_to_le32(count);
5692 parm_data->list[0].EA_flags = 0;
5693 /* we checked above that name len is less than 255 */
Alexey Dobriyan53b35312006-03-24 03:16:13 -08005694 parm_data->list[0].name_len = (__u8)name_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07005695 /* EA names are always ASCII */
Steve French790fe572007-07-07 19:25:05 +00005696 if (ea_name)
Steve French50c2f752007-07-13 00:33:32 +00005697 strncpy(parm_data->list[0].name, ea_name, name_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005698 parm_data->list[0].name[name_len] = 0;
5699 parm_data->list[0].value_len = cpu_to_le16(ea_value_len);
5700 /* caller ensures that ea_value_len is less than 64K but
5701 we need to ensure that it fits within the smb */
5702
Steve French50c2f752007-07-13 00:33:32 +00005703 /*BB add length check to see if it would fit in
5704 negotiated SMB buffer size BB */
Steve French790fe572007-07-07 19:25:05 +00005705 /* if (ea_value_len > buffer_size - 512 (enough for header)) */
5706 if (ea_value_len)
Steve French50c2f752007-07-13 00:33:32 +00005707 memcpy(parm_data->list[0].name+name_len+1,
5708 ea_value, ea_value_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005709
5710 pSMB->TotalDataCount = pSMB->DataCount;
5711 pSMB->ParameterCount = cpu_to_le16(params);
5712 pSMB->TotalParameterCount = pSMB->ParameterCount;
5713 pSMB->Reserved4 = 0;
5714 pSMB->hdr.smb_buf_length += byte_count;
5715 pSMB->ByteCount = cpu_to_le16(byte_count);
5716 rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
5717 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
Steve Frenchad7a2922008-02-07 23:25:02 +00005718 if (rc)
Joe Perchesb6b38f72010-04-21 03:50:45 +00005719 cFYI(1, "SetPathInfo (EA) returned %d", rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005720
5721 cifs_buf_release(pSMB);
5722
5723 if (rc == -EAGAIN)
5724 goto SetEARetry;
5725
5726 return rc;
5727}
5728
5729#endif