1/*	$NetBSD: smbfs_smb.c,v 1.42.8.1 2012/11/29 00:04:37 riz Exp $	*/
2
3/*-
4 * Copyright (c) 2003 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jaromir Dolecek.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Copyright (c) 2000-2001 Boris Popov
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 *    notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 *    notice, this list of conditions and the following disclaimer in the
43 *    documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 *    must display the following acknowledgement:
46 *    This product includes software developed by Boris Popov.
47 * 4. Neither the name of the author nor the names of any co-contributors
48 *    may be used to endorse or promote products derived from this software
49 *    without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 *
63 * FreeBSD: src/sys/fs/smbfs/smbfs_smb.c,v 1.3 2001/12/10 08:09:46 obrien Exp
64 */
65
66#include <sys/cdefs.h>
67__KERNEL_RCSID(0, "$NetBSD: smbfs_smb.c,v 1.42.8.1 2012/11/29 00:04:37 riz Exp $");
68
69#include <sys/param.h>
70#include <sys/systm.h>
71#include <sys/kernel.h>
72#include <sys/malloc.h>
73#include <sys/proc.h>
74#include <sys/lock.h>
75#include <sys/vnode.h>
76#include <sys/mbuf.h>
77#include <sys/mount.h>
78
79#ifdef USE_MD5_HASH
80#include <sys/md5.h>
81#endif
82
83#include <netsmb/smb.h>
84#include <netsmb/smb_subr.h>
85#include <netsmb/smb_rq.h>
86#include <netsmb/smb_conn.h>
87
88#include <fs/smbfs/smbfs.h>
89#include <fs/smbfs/smbfs_node.h>
90#include <fs/smbfs/smbfs_subr.h>
91
92/*
93 * Lack of inode numbers leads us to the problem of generating them.
94 * Partially this problem can be solved by having a dir/file cache
95 * with inode numbers generated from the incremented by one counter.
96 * However this way will require too much kernel memory, gives all
97 * sorts of locking and consistency problems, not to mentinon counter overflows.
98 * So, I'm decided to use a hash function to generate pseudo random (and unique)
99 * inode numbers.
100 */
101static long
102smbfs_getino(struct smbnode *dnp, const char *name, int nmlen)
103{
104#ifdef USE_MD5_HASH
105	MD5_CTX md5;
106	u_int32_t state[4];
107	long ino;
108	int i;
109
110	MD5Init(&md5);
111	MD5Update(&md5, name, nmlen);
112	MD5Final((u_char *)state, &md5);
113	for (i = 0, ino = 0; i < 4; i++)
114		ino += state[i];
115	return dnp->n_ino + ino;
116#endif
117	u_int32_t ino;
118
119	ino = dnp->n_ino + smbfs_hash(name, nmlen);
120	if (ino <= 2)
121		ino += 3;
122	return ino;
123}
124
125static int
126smbfs_smb_lockandx(struct smbnode *np, int op, void *id, off_t start, off_t end,
127	struct smb_cred *scred)
128{
129	struct smb_share *ssp = np->n_mount->sm_share;
130	struct smb_rq *rqp;
131	struct mbchain *mbp;
132	u_char ltype = 0;
133	int error;
134
135	if (op == SMB_LOCK_SHARED)
136		ltype |= SMB_LOCKING_ANDX_SHARED_LOCK;
137	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_LOCKING_ANDX, scred, &rqp);
138	if (error)
139		return error;
140	smb_rq_getrequest(rqp, &mbp);
141	smb_rq_wstart(rqp);
142	mb_put_uint8(mbp, 0xff);	/* secondary command */
143	mb_put_uint8(mbp, 0);		/* MBZ */
144	mb_put_uint16le(mbp, 0);
145	mb_put_mem(mbp, (void *)&np->n_fid, 2, MB_MSYSTEM);
146	mb_put_uint8(mbp, ltype);	/* locktype */
147	mb_put_uint8(mbp, 0);		/* oplocklevel - 0 seems is NO_OPLOCK */
148	mb_put_uint32le(mbp, 0);	/* timeout - break immediately */
149	mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 1 : 0);
150	mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 0 : 1);
151	smb_rq_wend(rqp);
152	smb_rq_bstart(rqp);
153	mb_put_uint16le(mbp, (((long) id) & 0xffff));	/* process ID */
154	mb_put_uint32le(mbp, start);
155	mb_put_uint32le(mbp, end - start);
156	smb_rq_bend(rqp);
157	error = smb_rq_simple(rqp);
158	smb_rq_done(rqp);
159	return error;
160}
161
162int
163smbfs_smb_lock(struct smbnode *np, int op, void *id,
164	off_t start, off_t end,	struct smb_cred *scred)
165{
166	struct smb_share *ssp = np->n_mount->sm_share;
167
168	if (SMB_DIALECT(SSTOVC(ssp)) < SMB_DIALECT_LANMAN1_0)
169		/*
170		 * TODO: use LOCK_BYTE_RANGE here.
171		 */
172		return EINVAL;
173	else
174		return smbfs_smb_lockandx(np, op, id, start, end, scred);
175}
176
177int
178smbfs_smb_statvfs(struct smb_share *ssp, struct statvfs *sbp,
179	struct smb_cred *scred)
180{
181	unsigned long bsize;	/* Block (allocation unit) size */
182	unsigned long bavail, bfree;
183
184	/*
185	 * The SMB request work with notion of sector size and
186	 * allocation units. Allocation unit is what 'block'
187	 * means in Unix context, sector size might be HW sector size.
188	 */
189
190	if (SMB_DIALECT(SSTOVC(ssp)) >= SMB_DIALECT_LANMAN2_0) {
191		struct smb_t2rq *t2p;
192		struct mbchain *mbp;
193		struct mdchain *mdp;
194		u_int16_t secsz;
195		u_int32_t units, bpu, funits;
196		int error;
197
198		error = smb_t2_alloc(SSTOCP(ssp),
199		    SMB_TRANS2_QUERY_FS_INFORMATION, scred, &t2p);
200		if (error)
201			return error;
202		mbp = &t2p->t2_tparam;
203		mb_init(mbp);
204		mb_put_uint16le(mbp, SMB_INFO_ALLOCATION);
205		t2p->t2_maxpcount = 4;
206		t2p->t2_maxdcount = 4 * 4 + 2;
207		error = smb_t2_request(t2p);
208		if (error) {
209			smb_t2_done(t2p);
210			return error;
211		}
212		mdp = &t2p->t2_rdata;
213		md_get_uint32(mdp, NULL);	/* fs id */
214		md_get_uint32le(mdp, &bpu);	/* Number of sectors per unit */
215		md_get_uint32le(mdp, &units);	/* Total number of units */
216		md_get_uint32le(mdp, &funits);	/* Number of available units */
217		md_get_uint16le(mdp, &secsz);	/* Number of bytes per sector */
218		smb_t2_done(t2p);
219
220		bsize = bpu * secsz;
221		bavail = units;
222		bfree = funits;
223	} else {
224		struct smb_rq *rqp;
225		struct mdchain *mdp;
226		u_int16_t units, bpu, secsz, funits;
227		int error;
228
229		error = smb_rq_alloc(SSTOCP(ssp),
230		    SMB_COM_QUERY_INFORMATION_DISK, scred, &rqp);
231		if (error)
232			return error;
233		smb_rq_wstart(rqp);
234		smb_rq_wend(rqp);
235		smb_rq_bstart(rqp);
236		smb_rq_bend(rqp);
237		error = smb_rq_simple(rqp);
238		if (error) {
239			smb_rq_done(rqp);
240			return error;
241		}
242		smb_rq_getreply(rqp, &mdp);
243		md_get_uint16le(mdp, &units);	/* Total units per server */
244		md_get_uint16le(mdp, &bpu);	/* Blocks per allocation unit */
245		md_get_uint16le(mdp, &secsz);	/* Block size (in bytes) */
246		md_get_uint16le(mdp, &funits);	/* Number of free units */
247		smb_rq_done(rqp);
248
249		bsize = bpu * secsz;
250		bavail = units;
251		bfree = funits;
252	}
253
254	sbp->f_bsize = bsize;		/* fundamental file system block size */
255	sbp->f_frsize = bsize;		/* fundamental file system frag size */
256	sbp->f_iosize = bsize;		/* optimal I/O size */
257	sbp->f_blocks = bavail;		/* total data blocks in file system */
258	sbp->f_bfree = bfree;		/* free blocks in fs */
259	sbp->f_bresvd = 0;		/* reserved blocks in fs */
260	sbp->f_bavail= bfree;		/* free blocks avail to non-superuser */
261	sbp->f_files = 0xffff;		/* total file nodes in file system */
262	sbp->f_ffree = 0xffff;		/* free file nodes to non-superuser */
263	sbp->f_favail = 0xffff;		/* free file nodes in fs */
264	sbp->f_fresvd = 0;		/* reserved file nodes in fs */
265	return 0;
266}
267
268static int
269smbfs_smb_seteof(struct smbnode *np, int64_t newsize, struct smb_cred *scred)
270{
271	struct smb_t2rq *t2p;
272	struct smb_share *ssp = np->n_mount->sm_share;
273	struct mbchain *mbp;
274	int error;
275
276	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION,
277	    scred, &t2p);
278	if (error)
279		return error;
280	mbp = &t2p->t2_tparam;
281	mb_init(mbp);
282	mb_put_mem(mbp, (void *)&np->n_fid, 2, MB_MSYSTEM);
283	mb_put_uint16le(mbp, SMB_SET_FILE_END_OF_FILE_INFO);
284	mb_put_uint32le(mbp, 0);
285	mbp = &t2p->t2_tdata;
286	mb_init(mbp);
287	mb_put_int64le(mbp, newsize);
288	mb_put_uint32le(mbp, 0);			/* padding */
289	mb_put_uint16le(mbp, 0);
290	t2p->t2_maxpcount = 2;
291	t2p->t2_maxdcount = 0;
292	error = smb_t2_request(t2p);
293	smb_t2_done(t2p);
294	return error;
295}
296
297int
298smbfs_smb_setfsize(struct smbnode *np, u_quad_t newsize,
299		   struct smb_cred *scred)
300{
301	struct smb_share *ssp = np->n_mount->sm_share;
302	struct smb_rq *rqp;
303	struct mbchain *mbp;
304	int error;
305
306	if (newsize >= (1LL << 32)) {
307		if (!(SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_FILES))
308			return EFBIG;
309		return smbfs_smb_seteof(np, (int64_t)newsize, scred);
310	}
311
312	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
313	if (error)
314		return error;
315	smb_rq_getrequest(rqp, &mbp);
316	smb_rq_wstart(rqp);
317	mb_put_mem(mbp, (void *)&np->n_fid, 2, MB_MSYSTEM);
318	mb_put_uint16le(mbp, 0);
319	mb_put_uint32le(mbp, newsize);
320	mb_put_uint16le(mbp, 0);
321	smb_rq_wend(rqp);
322	smb_rq_bstart(rqp);
323	mb_put_uint8(mbp, SMB_DT_DATA);
324	mb_put_uint16le(mbp, 0);
325	smb_rq_bend(rqp);
326	error = smb_rq_simple(rqp);
327	smb_rq_done(rqp);
328	return error;
329}
330
331
332/*
333 * Set DOS file attributes. mtime should be NULL for dialects above lm10
334 */
335int
336smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, struct timespec *mtime,
337	struct smb_cred *scred)
338{
339	struct smb_rq *rqp;
340	struct smb_share *ssp = np->n_mount->sm_share;
341	struct mbchain *mbp;
342	u_long xtime;
343	int error, svtz;
344
345	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_SET_INFORMATION, scred, &rqp);
346	if (error)
347		return error;
348	svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
349	smb_rq_getrequest(rqp, &mbp);
350	smb_rq_wstart(rqp);
351	mb_put_uint16le(mbp, attr);
352	if (mtime) {
353		smb_time_local2server(mtime, svtz, &xtime);
354	} else
355		xtime = 0;
356	mb_put_uint32le(mbp, xtime);		/* mtime */
357	mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO);
358	smb_rq_wend(rqp);
359	smb_rq_bstart(rqp);
360	mb_put_uint8(mbp, SMB_DT_ASCII);
361	do {
362		error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
363		if (error)
364			break;
365		mb_put_uint8(mbp, SMB_DT_ASCII);
366		mb_put_uint8(mbp, 0);
367		smb_rq_bend(rqp);
368		error = smb_rq_simple(rqp);
369		if (error)
370			break;
371	} while(0);
372	smb_rq_done(rqp);
373	return error;
374}
375
376/*
377 * Note, win95 doesn't support this call.
378 */
379int
380smbfs_smb_setptime2(struct smbnode *np, struct timespec *mtime,
381	struct timespec *atime, int attr, struct smb_cred *scred)
382{
383	struct smb_t2rq *t2p;
384	struct smb_share *ssp = np->n_mount->sm_share;
385	struct smb_vc *vcp = SSTOVC(ssp);
386	struct mbchain *mbp;
387	u_int16_t xdate, xtime;
388	int error, tzoff;
389
390	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
391	    scred, &t2p);
392	if (error)
393		return error;
394	mbp = &t2p->t2_tparam;
395	mb_init(mbp);
396	mb_put_uint16le(mbp, SMB_INFO_STANDARD);
397	mb_put_uint32le(mbp, 0);		/* MBZ */
398	error = smbfs_fullpath(mbp, vcp, np, NULL, 0);
399	if (error) {
400		smb_t2_done(t2p);
401		return error;
402	}
403	tzoff = vcp->vc_sopt.sv_tz;
404	mbp = &t2p->t2_tdata;
405	mb_init(mbp);
406	mb_put_uint32le(mbp, 0);		/* creation time */
407	if (atime)
408		smb_time_unix2dos(atime, tzoff, &xdate, &xtime, NULL);
409	else
410		xtime = xdate = 0;
411	mb_put_uint16le(mbp, xdate);
412	mb_put_uint16le(mbp, xtime);
413	if (mtime)
414		smb_time_unix2dos(mtime, tzoff, &xdate, &xtime, NULL);
415	else
416		xtime = xdate = 0;
417	mb_put_uint16le(mbp, xdate);
418	mb_put_uint16le(mbp, xtime);
419	mb_put_uint32le(mbp, 0);		/* file size */
420	mb_put_uint32le(mbp, 0);		/* allocation unit size */
421	mb_put_uint16le(mbp, attr);	/* DOS attr */
422	mb_put_uint32le(mbp, 0);		/* EA size */
423	t2p->t2_maxpcount = 5 * 2;
424	t2p->t2_maxdcount = vcp->vc_txmax;
425	error = smb_t2_request(t2p);
426	smb_t2_done(t2p);
427	return error;
428}
429
430/*
431 * NT level. Specially for win9x
432 */
433int
434smbfs_smb_setpattrNT(struct smbnode *np, u_short attr, struct timespec *mtime,
435	struct timespec *atime, struct smb_cred *scred)
436{
437	struct smb_t2rq *t2p;
438	struct smb_share *ssp = np->n_mount->sm_share;
439	struct smb_vc *vcp = SSTOVC(ssp);
440	struct mbchain *mbp;
441	int64_t tm;
442	int error, tzoff;
443
444	/*
445	 * SMB_SET_FILE_BASIC_INFO isn't supported for
446	 * SMB_TRANS2_SET_PATH_INFORMATION,
447	 * so use SMB_SET_FILE_BASIC_INFORMATION instead,
448	 * but it requires SMB_CAP_INFOLEVEL_PASSTHRU capability.
449	 */
450	if ((SMB_CAPS(vcp) & SMB_CAP_INFOLEVEL_PASSTHRU) == 0)
451		return smbfs_smb_setptime2(np, mtime, atime, attr, scred);
452
453	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
454	    scred, &t2p);
455	if (error)
456		return error;
457	mbp = &t2p->t2_tparam;
458	mb_init(mbp);
459	mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFORMATION);
460	mb_put_uint32le(mbp, 0);		/* MBZ */
461	error = smbfs_fullpath(mbp, vcp, np, NULL, 0);
462	if (error) {
463		smb_t2_done(t2p);
464		return error;
465	}
466	tzoff = vcp->vc_sopt.sv_tz;
467	mbp = &t2p->t2_tdata;
468	mb_init(mbp);
469	mb_put_int64le(mbp, 0);		/* creation time */
470	if (atime) {
471		smb_time_local2NT(atime, tzoff, &tm);
472	} else
473		tm = 0;
474	mb_put_int64le(mbp, tm);
475	if (mtime) {
476		smb_time_local2NT(mtime, tzoff, &tm);
477	} else
478		tm = 0;
479	mb_put_int64le(mbp, tm);
480	mb_put_int64le(mbp, tm);		/* change time */
481	mb_put_uint32le(mbp, attr);		/* attr */
482	mb_put_uint32le(mbp, 0);		/* padding */
483	t2p->t2_maxpcount = 2;
484	t2p->t2_maxdcount = 0;
485	error = smb_t2_request(t2p);
486	smb_t2_done(t2p);
487	return error;
488}
489
490/*
491 * Set file atime and mtime. Doesn't supported by core dialect.
492 */
493int
494smbfs_smb_setftime(struct smbnode *np, struct timespec *mtime,
495	struct timespec *atime, struct smb_cred *scred)
496{
497	struct smb_rq *rqp;
498	struct smb_share *ssp = np->n_mount->sm_share;
499	struct mbchain *mbp;
500	u_int16_t xdate, xtime;
501	int error, tzoff;
502
503	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_SET_INFORMATION2, scred, &rqp);
504	if (error)
505		return error;
506	tzoff = SSTOVC(ssp)->vc_sopt.sv_tz;
507	smb_rq_getrequest(rqp, &mbp);
508	smb_rq_wstart(rqp);
509	mb_put_mem(mbp, (void *)&np->n_fid, 2, MB_MSYSTEM);
510	mb_put_uint32le(mbp, 0);		/* creation time */
511
512	if (atime)
513		smb_time_unix2dos(atime, tzoff, &xdate, &xtime, NULL);
514	else
515		xtime = xdate = 0;
516	mb_put_uint16le(mbp, xdate);
517	mb_put_uint16le(mbp, xtime);
518	if (mtime)
519		smb_time_unix2dos(mtime, tzoff, &xdate, &xtime, NULL);
520	else
521		xtime = xdate = 0;
522	mb_put_uint16le(mbp, xdate);
523	mb_put_uint16le(mbp, xtime);
524	smb_rq_wend(rqp);
525	smb_rq_bstart(rqp);
526	smb_rq_bend(rqp);
527	error = smb_rq_simple(rqp);
528	SMBSDEBUG(("%d\n", error));
529	smb_rq_done(rqp);
530	return error;
531}
532
533/*
534 * Set DOS file attributes.
535 * Looks like this call can be used only if CAP_NT_SMBS bit is on.
536 */
537int
538smbfs_smb_setfattrNT(struct smbnode *np, u_int16_t attr, struct timespec *mtime,
539	struct timespec *atime, struct smb_cred *scred)
540{
541	struct smb_t2rq *t2p;
542	struct smb_share *ssp = np->n_mount->sm_share;
543	struct mbchain *mbp;
544	int64_t tm;
545	int error, svtz;
546
547	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION,
548	    scred, &t2p);
549	if (error)
550		return error;
551	svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
552	mbp = &t2p->t2_tparam;
553	mb_init(mbp);
554	mb_put_mem(mbp, (void *)&np->n_fid, 2, MB_MSYSTEM); 	/* FID */
555	mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFO);		/* info level */
556	mb_put_uint32le(mbp, 0);				/* reserved */
557	mbp = &t2p->t2_tdata;
558	mb_init(mbp);
559	mb_put_int64le(mbp, 0);		/* creation time */
560	if (atime) {
561		smb_time_local2NT(atime, svtz, &tm);
562	} else
563		tm = 0;
564	mb_put_int64le(mbp, tm);
565	if (mtime) {
566		smb_time_local2NT(mtime, svtz, &tm);
567	} else
568		tm = 0;
569	mb_put_int64le(mbp, tm);
570	mb_put_int64le(mbp, tm);		/* change time */
571	mb_put_uint32le(mbp, attr);		/* attr */
572	mb_put_uint32le(mbp, 0);		/* padding */
573	t2p->t2_maxpcount = 2;
574	t2p->t2_maxdcount = 0;
575	error = smb_t2_request(t2p);
576	smb_t2_done(t2p);
577	return error;
578}
579
580
581int
582smbfs_smb_open(struct smbnode *np, int accmode, struct smb_cred *scred)
583{
584	struct smb_rq *rqp;
585	struct smb_share *ssp = np->n_mount->sm_share;
586	struct mbchain *mbp;
587	struct mdchain *mdp;
588	u_int8_t wc;
589	u_int16_t fid, wattr, grantedmode;
590	int error;
591
592	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_OPEN, scred, &rqp);
593	if (error)
594		return error;
595	smb_rq_getrequest(rqp, &mbp);
596	smb_rq_wstart(rqp);
597	mb_put_uint16le(mbp, accmode);
598	mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
599	smb_rq_wend(rqp);
600	smb_rq_bstart(rqp);
601	mb_put_uint8(mbp, SMB_DT_ASCII);
602	do {
603		error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
604		if (error)
605			break;
606		smb_rq_bend(rqp);
607		error = smb_rq_simple(rqp);
608		if (error)
609			break;
610		smb_rq_getreply(rqp, &mdp);
611		if (md_get_uint8(mdp, &wc) != 0 || wc != 7) {
612			error = EBADRPC;
613			break;
614		}
615		md_get_uint16(mdp, &fid);
616		md_get_uint16le(mdp, &wattr);
617		md_get_uint32(mdp, NULL);	/* mtime */
618		md_get_uint32(mdp, NULL);	/* fsize */
619		md_get_uint16le(mdp, &grantedmode);
620		/*
621		 * TODO: refresh attributes from this reply
622		 */
623	} while(0);
624	smb_rq_done(rqp);
625	if (error)
626		return error;
627	np->n_fid = fid;
628	np->n_rwstate = grantedmode;
629	return 0;
630}
631
632
633int
634smbfs_smb_close(struct smb_share *ssp, u_int16_t fid, struct timespec *mtime,
635	struct smb_cred *scred)
636{
637	struct smb_rq *rqp;
638	struct mbchain *mbp;
639	u_long xtime;
640	int error;
641
642	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CLOSE, scred, &rqp);
643	if (error)
644		return error;
645	smb_rq_getrequest(rqp, &mbp);
646	smb_rq_wstart(rqp);
647	mb_put_mem(mbp, (void *)&fid, sizeof(fid), MB_MSYSTEM);
648	if (mtime) {
649		smb_time_local2server(mtime, SSTOVC(ssp)->vc_sopt.sv_tz, &xtime);
650	} else
651		xtime = 0;
652	mb_put_uint32le(mbp, xtime);
653	smb_rq_wend(rqp);
654	smb_rq_bstart(rqp);
655	smb_rq_bend(rqp);
656	error = smb_rq_simple(rqp);
657	smb_rq_done(rqp);
658	return error;
659}
660
661int
662smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen,
663	struct smb_cred *scred)
664{
665	struct smb_rq *rqp;
666	struct smb_share *ssp = dnp->n_mount->sm_share;
667	struct mbchain *mbp;
668	struct mdchain *mdp;
669	struct timespec ctime;
670	u_int8_t wc;
671	u_int16_t fid;
672	u_long tm;
673	int error;
674
675	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CREATE_NEW, scred, &rqp);
676	if (error)
677		return error;
678	smb_rq_getrequest(rqp, &mbp);
679
680	/* get current time */
681	getnanotime(&ctime);
682	smb_time_local2server(&ctime, SSTOVC(ssp)->vc_sopt.sv_tz, &tm);
683
684	smb_rq_wstart(rqp);
685	mb_put_uint16le(mbp, SMB_FA_ARCHIVE);	/* attributes  */
686	mb_put_uint32le(mbp, tm);
687	smb_rq_wend(rqp);
688
689	smb_rq_bstart(rqp);
690	mb_put_uint8(mbp, SMB_DT_ASCII);
691	error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, nmlen);
692	if (!error) {
693		smb_rq_bend(rqp);
694		error = smb_rq_simple(rqp);
695		if (!error) {
696			smb_rq_getreply(rqp, &mdp);
697			md_get_uint8(mdp, &wc);
698			if (wc == 1)
699				md_get_uint16(mdp, &fid);
700			else
701				error = EBADRPC;
702		}
703	}
704
705	smb_rq_done(rqp);
706	if (!error)
707		smbfs_smb_close(ssp, fid, &ctime, scred);
708
709	return (error);
710}
711
712int
713smbfs_smb_delete(struct smbnode *np, struct smb_cred *scred)
714{
715	struct smb_rq *rqp;
716	struct smb_share *ssp = np->n_mount->sm_share;
717	struct mbchain *mbp;
718	int error;
719
720	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_DELETE, scred, &rqp);
721	if (error)
722		return error;
723	smb_rq_getrequest(rqp, &mbp);
724	smb_rq_wstart(rqp);
725	mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
726	smb_rq_wend(rqp);
727	smb_rq_bstart(rqp);
728	mb_put_uint8(mbp, SMB_DT_ASCII);
729	error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
730	if (!error) {
731		smb_rq_bend(rqp);
732		error = smb_rq_simple(rqp);
733	}
734	smb_rq_done(rqp);
735	return error;
736}
737
738int
739smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp,
740	const char *tname, int tnmlen, struct smb_cred *scred)
741{
742	struct smb_rq *rqp;
743	struct smb_share *ssp = src->n_mount->sm_share;
744	struct mbchain *mbp;
745	int error;
746
747	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_RENAME, scred, &rqp);
748	if (error)
749		return error;
750	smb_rq_getrequest(rqp, &mbp);
751	smb_rq_wstart(rqp);
752	mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
753	smb_rq_wend(rqp);
754	smb_rq_bstart(rqp);
755	mb_put_uint8(mbp, SMB_DT_ASCII);
756	do {
757		error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0);
758		if (error)
759			break;
760		mb_put_uint8(mbp, SMB_DT_ASCII);
761		error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen);
762		if (error)
763			break;
764		smb_rq_bend(rqp);
765		error = smb_rq_simple(rqp);
766	} while(0);
767	smb_rq_done(rqp);
768	return error;
769}
770
771#ifdef notnow
772int
773smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp,
774	const char *tname, int tnmlen, u_int16_t flags, struct smb_cred *scred)
775{
776	struct smb_rq *rqp;
777	struct smb_share *ssp = src->n_mount->sm_share;
778	struct mbchain *mbp;
779	int error;
780
781	error = smb_rq_alloc(rqp, SSTOCP(ssp), SMB_COM_MOVE, scred, &rqp);
782	if (error)
783		return error;
784	smb_rq_getrequest(rqp, &mbp);
785	smb_rq_wstart(rqp);
786	mb_put_uint16le(mbp, SMB_TID_UNKNOWN);
787	mb_put_uint16le(mbp, 0x20);	/* delete target file */
788	mb_put_uint16le(mbp, flags);
789	smb_rq_wend(rqp);
790	smb_rq_bstart(rqp);
791	mb_put_uint8(mbp, SMB_DT_ASCII);
792	do {
793		error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0);
794		if (error)
795			break;
796		mb_put_uint8(mbp, SMB_DT_ASCII);
797		error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen);
798		if (error)
799			break;
800		smb_rq_bend(rqp);
801		error = smb_rq_simple(rqp);
802	} while(0);
803	smb_rq_done(rqp);
804	return error;
805}
806#endif
807
808int
809smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len,
810	struct smb_cred *scred)
811{
812	struct smb_rq *rqp;
813	struct smb_share *ssp = dnp->n_mount->sm_share;
814	struct mbchain *mbp;
815	int error;
816
817	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CREATE_DIRECTORY, scred, &rqp);
818	if (error)
819		return error;
820	smb_rq_getrequest(rqp, &mbp);
821	smb_rq_wstart(rqp);
822	smb_rq_wend(rqp);
823	smb_rq_bstart(rqp);
824	mb_put_uint8(mbp, SMB_DT_ASCII);
825	error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, len);
826	if (!error) {
827		smb_rq_bend(rqp);
828		error = smb_rq_simple(rqp);
829	}
830	smb_rq_done(rqp);
831	return error;
832}
833
834int
835smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scred)
836{
837	struct smb_rq *rqp;
838	struct smb_share *ssp = np->n_mount->sm_share;
839	struct mbchain *mbp;
840	int error;
841
842	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_DELETE_DIRECTORY, scred, &rqp);
843	if (error)
844		return error;
845	smb_rq_getrequest(rqp, &mbp);
846	smb_rq_wstart(rqp);
847	smb_rq_wend(rqp);
848	smb_rq_bstart(rqp);
849	mb_put_uint8(mbp, SMB_DT_ASCII);
850	error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
851	if (!error) {
852		smb_rq_bend(rqp);
853		error = smb_rq_simple(rqp);
854	}
855	smb_rq_done(rqp);
856	return error;
857}
858
859static int
860smbfs_smb_search(struct smbfs_fctx *ctx)
861{
862	struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
863	struct smb_rq *rqp;
864	struct mbchain *mbp;
865	struct mdchain *mdp;
866	u_int8_t wc, bt;
867	u_int16_t ec, dlen, bc;
868	int maxent, error;
869
870	maxent = min(ctx->f_left, (vcp->vc_txmax - SMB_HDRLEN - 3) / SMB_DENTRYLEN);
871	if (ctx->f_rq) {
872		smb_rq_done(ctx->f_rq);
873		ctx->f_rq = NULL;
874	}
875	error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_SEARCH, ctx->f_scred, &rqp);
876	if (error)
877		return error;
878	ctx->f_rq = rqp;
879	smb_rq_getrequest(rqp, &mbp);
880	smb_rq_wstart(rqp);
881	mb_put_uint16le(mbp, maxent);	/* max entries to return */
882	mb_put_uint16le(mbp, ctx->f_attrmask);
883	smb_rq_wend(rqp);
884	smb_rq_bstart(rqp);
885	mb_put_uint8(mbp, SMB_DT_ASCII);	/* buffer format */
886	if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
887		error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen);
888		if (error)
889			return error;
890		mb_put_uint8(mbp, SMB_DT_VARIABLE);
891		mb_put_uint16le(mbp, 0);	/* context length */
892		ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
893	} else {
894		mb_put_uint8(mbp, 0);	/* file name length */
895		mb_put_uint8(mbp, SMB_DT_VARIABLE);
896		mb_put_uint16le(mbp, SMB_SKEYLEN);
897		mb_put_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM);
898	}
899	smb_rq_bend(rqp);
900	error = smb_rq_simple(rqp);
901	if (error) {
902		if (error == ENOENT)
903			ctx->f_flags |= SMBFS_RDD_EOF;
904
905		return error;
906	}
907	smb_rq_getreply(rqp, &mdp);
908	md_get_uint8(mdp, &wc);
909	if (wc != 1)
910		return EBADRPC;
911	md_get_uint16le(mdp, &ec);
912	if (ec == 0)
913		return ENOENT;
914	ctx->f_ecnt = ec;
915	md_get_uint16le(mdp, &bc);
916	if (bc < 3)
917		return EBADRPC;
918	bc -= 3;
919	md_get_uint8(mdp, &bt);
920	if (bt != SMB_DT_VARIABLE)
921		return EBADRPC;
922	md_get_uint16le(mdp, &dlen);
923	if (dlen != bc || dlen % SMB_DENTRYLEN != 0)
924		return EBADRPC;
925	return 0;
926}
927
928static int
929smbfs_findopenLM1(struct smbfs_fctx *ctx, struct smbnode *dnp,
930    const char *wildcard, int wclen, int attr, struct smb_cred *scred)
931{
932	ctx->f_attrmask = attr;
933	if (wildcard) {
934		if (wclen == 1 && wildcard[0] == '*') {
935			ctx->f_wildcard = "*.*";
936			ctx->f_wclen = 3;
937		} else {
938			ctx->f_wildcard = wildcard;
939			ctx->f_wclen = wclen;
940		}
941	} else {
942		ctx->f_wildcard = NULL;
943		ctx->f_wclen = 0;
944	}
945	ctx->f_name = ctx->f_fname;
946	return 0;
947}
948
949static int
950smbfs_findnextLM1(struct smbfs_fctx *ctx, int limit)
951{
952	struct mdchain *mbp;
953	struct smb_rq *rqp;
954	char *cp;
955	u_int8_t battr;
956	u_int16_t xdate, xtime;
957	u_int32_t size;
958	int error;
959
960	if (ctx->f_ecnt == 0) {
961		if (ctx->f_flags & SMBFS_RDD_EOF)
962			return ENOENT;
963		ctx->f_left = ctx->f_limit = limit;
964		error = smbfs_smb_search(ctx);
965		if (error)
966			return error;
967	}
968	rqp = ctx->f_rq;
969	smb_rq_getreply(rqp, &mbp);
970	md_get_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM);
971	md_get_uint8(mbp, &battr);
972	md_get_uint16le(mbp, &xtime);
973	md_get_uint16le(mbp, &xdate);
974	md_get_uint32le(mbp, &size);
975	KASSERT(ctx->f_name == ctx->f_fname);
976	cp = ctx->f_name;
977	md_get_mem(mbp, cp, sizeof(ctx->f_fname), MB_MSYSTEM);
978	cp[sizeof(ctx->f_fname) - 1] = '\0';
979	cp += strlen(cp) - 1;
980	while(*cp == ' ' && cp > ctx->f_name)
981		*cp-- = '\0';
982	ctx->f_attr.fa_attr = battr;
983	smb_dos2unixtime(xdate, xtime, 0, rqp->sr_vc->vc_sopt.sv_tz,
984	    &ctx->f_attr.fa_mtime);
985	ctx->f_attr.fa_size = size;
986	ctx->f_nmlen = strlen(ctx->f_name);
987	ctx->f_ecnt--;
988	ctx->f_left--;
989	return 0;
990}
991
992static int
993smbfs_findcloseLM1(struct smbfs_fctx *ctx)
994{
995	if (ctx->f_rq)
996		smb_rq_done(ctx->f_rq);
997	return 0;
998}
999
1000/*
1001 * TRANS2_FIND_FIRST2/NEXT2, used for NT LM12 dialect
1002 */
1003static int
1004smbfs_smb_trans2find2(struct smbfs_fctx *ctx)
1005{
1006	struct smb_t2rq *t2p;
1007	struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
1008	struct mbchain *mbp;
1009	struct mdchain *mdp;
1010	u_int16_t tw, flags;
1011	int error;
1012
1013	if (ctx->f_t2) {
1014		smb_t2_done(ctx->f_t2);
1015		ctx->f_t2 = NULL;
1016	}
1017	ctx->f_flags &= ~SMBFS_RDD_GOTRNAME;
1018	flags = 8 | 2;			/* <resume> | <close if EOS> */
1019	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
1020		flags |= 1;		/* close search after this request */
1021		ctx->f_flags |= SMBFS_RDD_NOCLOSE;
1022	}
1023	if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
1024		error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_FIRST2,
1025		    ctx->f_scred, &t2p);
1026		if (error)
1027			return error;
1028		ctx->f_t2 = t2p;
1029		mbp = &t2p->t2_tparam;
1030		mb_init(mbp);
1031		mb_put_uint16le(mbp, ctx->f_attrmask);
1032		mb_put_uint16le(mbp, ctx->f_limit);
1033		mb_put_uint16le(mbp, flags);
1034		mb_put_uint16le(mbp, ctx->f_infolevel);
1035		mb_put_uint32le(mbp, 0);
1036		error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen);
1037		if (error)
1038			return error;
1039	} else	{
1040		error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_NEXT2,
1041		    ctx->f_scred, &t2p);
1042		if (error)
1043			return error;
1044		ctx->f_t2 = t2p;
1045		mbp = &t2p->t2_tparam;
1046		mb_init(mbp);
1047		mb_put_mem(mbp, (void *)&ctx->f_Sid, 2, MB_MSYSTEM);
1048		mb_put_uint16le(mbp, ctx->f_limit);
1049		mb_put_uint16le(mbp, ctx->f_infolevel);
1050		mb_put_uint32le(mbp, 0);		/* resume key */
1051		mb_put_uint16le(mbp, flags);
1052		if (ctx->f_rname)
1053			mb_put_mem(mbp, ctx->f_rname, strlen(ctx->f_rname) + 1, MB_MSYSTEM);
1054		else
1055			mb_put_uint8(mbp, 0);	/* resume file name */
1056#if 0
1057	struct timeval tv;
1058	tv.tv_sec = 0;
1059	tv.tv_usec = 200 * 1000;	/* 200ms */
1060		if (vcp->vc_flags & SMBC_WIN95) {
1061			/*
1062			 * some implementations suggests to sleep here
1063			 * for 200ms, due to the bug in the Win95.
1064			 * I've didn't notice any problem, but put code
1065			 * for it.
1066			 */
1067			 tsleep(&flags, PVFS, "fix95", tvtohz(&tv));
1068		}
1069#endif
1070	}
1071	t2p->t2_maxpcount = 5 * 2;
1072	t2p->t2_maxdcount = vcp->vc_txmax;
1073	error = smb_t2_request(t2p);
1074	if (error)
1075		return error;
1076	mdp = &t2p->t2_rparam;
1077	if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
1078		if ((error = md_get_uint16(mdp, &ctx->f_Sid)) != 0)
1079			return error;
1080		ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
1081	}
1082	if ((error = md_get_uint16le(mdp, &tw)) != 0)
1083		return error;
1084	ctx->f_ecnt = tw;
1085	if ((error = md_get_uint16le(mdp, &tw)) != 0)
1086		return error;
1087	if (tw)
1088		ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE;
1089	if ((error = md_get_uint16le(mdp, &tw)) != 0)
1090		return error;
1091	if ((error = md_get_uint16le(mdp, &tw)) != 0)
1092		return error;
1093	if (ctx->f_ecnt == 0) {
1094		ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE;
1095		return ENOENT;
1096	}
1097	ctx->f_rnameofs = tw;
1098	mdp = &t2p->t2_rdata;
1099
1100	KASSERT(mdp->md_top != NULL);
1101	KASSERT(mdp->md_top->m_len != 0);
1102
1103	ctx->f_eofs = 0;
1104	return 0;
1105}
1106
1107static int
1108smbfs_smb_findclose2(struct smbfs_fctx *ctx)
1109{
1110	struct smb_rq *rqp;
1111	struct mbchain *mbp;
1112	int error;
1113
1114	error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2, ctx->f_scred, &rqp);
1115	if (error)
1116		return error;
1117	smb_rq_getrequest(rqp, &mbp);
1118	smb_rq_wstart(rqp);
1119	mb_put_mem(mbp, (void *)&ctx->f_Sid, 2, MB_MSYSTEM);
1120	smb_rq_wend(rqp);
1121	smb_rq_bstart(rqp);
1122	smb_rq_bend(rqp);
1123	error = smb_rq_simple(rqp);
1124	smb_rq_done(rqp);
1125	return error;
1126}
1127
1128static int
1129smbfs_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp,
1130    const char *wildcard, int wclen, int attr, struct smb_cred *scred)
1131{
1132	ctx->f_name = malloc(SMB_MAXNAMLEN, M_SMBFSDATA, M_WAITOK);
1133	if (ctx->f_name == NULL)
1134		return ENOMEM;
1135	ctx->f_infolevel = SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_NTLM0_12 ?
1136	    SMB_INFO_STANDARD : SMB_FIND_FILE_DIRECTORY_INFO;
1137	ctx->f_attrmask = attr;
1138	ctx->f_wildcard = wildcard;
1139	ctx->f_wclen = wclen;
1140	return 0;
1141}
1142
1143static int
1144smbfs_findnextLM2(struct smbfs_fctx *ctx, int limit)
1145{
1146	struct mdchain *mbp;
1147	struct smb_t2rq *t2p;
1148	char *cp;
1149	u_int8_t tb;
1150	u_int16_t xdate, xtime, wattr;
1151	u_int32_t size, next, dattr;
1152	int64_t tmp;
1153	int error, svtz, cnt, fxsz, nmlen, recsz;
1154
1155	if (ctx->f_ecnt == 0) {
1156		if (ctx->f_flags & SMBFS_RDD_EOF)
1157			return ENOENT;
1158		ctx->f_left = ctx->f_limit = limit;
1159		error = smbfs_smb_trans2find2(ctx);
1160		if (error)
1161			return error;
1162	}
1163	t2p = ctx->f_t2;
1164	mbp = &t2p->t2_rdata;
1165	svtz = SSTOVC(ctx->f_ssp)->vc_sopt.sv_tz;
1166	switch (ctx->f_infolevel) {
1167	case SMB_INFO_STANDARD:
1168		next = 0;
1169		fxsz = 0;
1170		md_get_uint16le(mbp, &xdate);
1171		md_get_uint16le(mbp, &xtime);	/* creation time */
1172		md_get_uint16le(mbp, &xdate);
1173		md_get_uint16le(mbp, &xtime);	/* access time */
1174		smb_dos2unixtime(xdate, xtime, 0, svtz, &ctx->f_attr.fa_atime);
1175		md_get_uint16le(mbp, &xdate);
1176		md_get_uint16le(mbp, &xtime);	/* access time */
1177		smb_dos2unixtime(xdate, xtime, 0, svtz, &ctx->f_attr.fa_mtime);
1178		md_get_uint32le(mbp, &size);
1179		ctx->f_attr.fa_size = size;
1180		md_get_uint32(mbp, NULL);	/* allocation size */
1181		md_get_uint16le(mbp, &wattr);
1182		ctx->f_attr.fa_attr = wattr;
1183		md_get_uint8(mbp, &tb);
1184		size = nmlen = tb;
1185		fxsz = 23;
1186		recsz = next = 24 + nmlen;	/* docs misses zero byte at end */
1187		break;
1188	case SMB_FIND_FILE_DIRECTORY_INFO:
1189		md_get_uint32le(mbp, &next);
1190		md_get_uint32(mbp, NULL);	/* file index */
1191		md_get_int64(mbp, NULL);	/* creation time */
1192		md_get_int64le(mbp, &tmp);
1193		smb_time_NT2local(tmp, svtz, &ctx->f_attr.fa_atime);
1194		md_get_int64le(mbp, &tmp);
1195		smb_time_NT2local(tmp, svtz, &ctx->f_attr.fa_mtime);
1196		md_get_int64le(mbp, &tmp);
1197		smb_time_NT2local(tmp, svtz, &ctx->f_attr.fa_ctime);
1198		md_get_int64le(mbp, &tmp);	/* file size */
1199		ctx->f_attr.fa_size = tmp;
1200		md_get_int64(mbp, NULL);	/* real size (should use) */
1201		md_get_uint32le(mbp, &dattr);	/* EA */
1202		ctx->f_attr.fa_attr = dattr;
1203		md_get_uint32le(mbp, &size);	/* name len */
1204		fxsz = 64;
1205		recsz = next ? next : fxsz + size;
1206		break;
1207	default:
1208#ifdef DIAGNOSTIC
1209		panic("smbfs_findnextLM2: unexpected info level %d\n",
1210		    ctx->f_infolevel);
1211#else
1212		return EINVAL;
1213#endif
1214	}
1215	nmlen = min(size, SMB_MAXNAMLEN);
1216	cp = ctx->f_name;
1217	error = md_get_mem(mbp, cp, nmlen, MB_MSYSTEM);
1218	if (error)
1219		return error;
1220	if (next) {
1221		cnt = next - nmlen - fxsz;
1222		if (cnt > 0)
1223			md_get_mem(mbp, NULL, cnt, MB_MSYSTEM);
1224#ifdef DIAGNOSTIC
1225		else if (cnt < 0)
1226			panic("smbfs_findnextLM2: out of sync");
1227#endif
1228	}
1229	if (nmlen && cp[nmlen - 1] == 0)
1230		nmlen--;
1231	if (nmlen == 0)
1232		return EBADRPC;
1233
1234	next = ctx->f_eofs + recsz;
1235	if (ctx->f_rnameofs && (ctx->f_flags & SMBFS_RDD_GOTRNAME) == 0 &&
1236	    (ctx->f_rnameofs >= ctx->f_eofs && ctx->f_rnameofs < next)) {
1237		/*
1238		 * Server needs a resume filename.
1239		 */
1240		if (ctx->f_rnamelen <= nmlen) {
1241			if (ctx->f_rname)
1242				free(ctx->f_rname, M_SMBFSDATA);
1243			ctx->f_rname = malloc(nmlen + 1, M_SMBFSDATA, M_WAITOK);
1244			ctx->f_rnamelen = nmlen;
1245		}
1246		memcpy(ctx->f_rname, ctx->f_name, nmlen);
1247		ctx->f_rname[nmlen] = 0;
1248		ctx->f_flags |= SMBFS_RDD_GOTRNAME;
1249	}
1250	ctx->f_nmlen = nmlen;
1251	ctx->f_eofs = next;
1252	ctx->f_ecnt--;
1253	ctx->f_left--;
1254	return 0;
1255}
1256
1257static int
1258smbfs_findcloseLM2(struct smbfs_fctx *ctx)
1259{
1260	if (ctx->f_name)
1261		free(ctx->f_name, M_SMBFSDATA);
1262	if (ctx->f_t2)
1263		smb_t2_done(ctx->f_t2);
1264	if ((ctx->f_flags & SMBFS_RDD_NOCLOSE) == 0)
1265		smbfs_smb_findclose2(ctx);
1266	return 0;
1267}
1268
1269int
1270smbfs_findopen(struct smbnode *dnp, const char *wildcard, int wclen, int attr,
1271	struct smb_cred *scred, struct smbfs_fctx **ctxpp)
1272{
1273	struct smbfs_fctx *ctx;
1274	int error;
1275
1276	ctx = malloc(sizeof(*ctx), M_SMBFSDATA, M_WAITOK|M_ZERO);
1277	ctx->f_ssp = dnp->n_mount->sm_share;
1278	ctx->f_dnp = dnp;
1279	ctx->f_flags = SMBFS_RDD_FINDFIRST;
1280	ctx->f_scred = scred;
1281	if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0 ||
1282	    (dnp->n_mount->sm_args.flags & SMBFS_MOUNT_NO_LONG)) {
1283		ctx->f_flags |= SMBFS_RDD_USESEARCH;
1284		error = smbfs_findopenLM1(ctx, dnp, wildcard, wclen, attr, scred);
1285	} else
1286		error = smbfs_findopenLM2(ctx, dnp, wildcard, wclen, attr, scred);
1287	if (error)
1288		smbfs_findclose(ctx, scred);
1289	else
1290		*ctxpp = ctx;
1291	return error;
1292}
1293
1294int
1295smbfs_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scred)
1296{
1297	int error;
1298
1299	if (limit == 0)
1300		limit = 1000000;
1301	else if (limit > 1)
1302		limit *= 4;	/* empirical */
1303	ctx->f_scred = scred;
1304	for (;;) {
1305		if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
1306			error = smbfs_findnextLM1(ctx, limit);
1307		} else
1308			error = smbfs_findnextLM2(ctx, limit);
1309		if (error)
1310			return error;
1311
1312		/* Skip '.' and '..' */
1313		if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') ||
1314		    (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' &&
1315		     ctx->f_name[1] == '.'))
1316			continue;
1317		break;
1318	}
1319	smbfs_fname_tolocal(SSTOVC(ctx->f_ssp), ctx->f_name, ctx->f_nmlen,
1320	    ctx->f_dnp->n_mount->sm_caseopt);
1321	ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name, ctx->f_nmlen);
1322	return 0;
1323}
1324
1325int
1326smbfs_findclose(struct smbfs_fctx *ctx, struct smb_cred *scred)
1327{
1328	ctx->f_scred = scred;
1329	if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
1330		smbfs_findcloseLM1(ctx);
1331	} else
1332		smbfs_findcloseLM2(ctx);
1333	if (ctx->f_rname)
1334		free(ctx->f_rname, M_SMBFSDATA);
1335	free(ctx, M_SMBFSDATA);
1336	return 0;
1337}
1338
1339int
1340smbfs_smb_lookup(struct smbnode *dnp, const char *name, int nmlen,
1341	struct smbfattr *fap, struct smb_cred *scred)
1342{
1343	struct smbfs_fctx *ctx;
1344	int error;
1345
1346	if (dnp == NULL || (dnp->n_ino == 2 && name == NULL)) {
1347		memset(fap, 0, sizeof(*fap));
1348		fap->fa_attr = SMB_FA_DIR;
1349		fap->fa_ino = 2;
1350		return 0;
1351	}
1352	if (nmlen == 1 && name[0] == '.') {
1353		error = smbfs_smb_lookup(dnp, NULL, 0, fap, scred);
1354		return error;
1355	} else if (nmlen == 2 && name[0] == '.' && name[1] == '.') {
1356		error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0,
1357		    fap, scred);
1358		printf("%s: knows NOTHING about '..'\n", __func__);
1359		return error;
1360	}
1361	error = smbfs_findopen(dnp, name, nmlen,
1362	    SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, scred, &ctx);
1363	if (error)
1364		return error;
1365	ctx->f_flags |= SMBFS_RDD_FINDSINGLE;
1366	error = smbfs_findnext(ctx, 1, scred);
1367	if (error == 0) {
1368		*fap = ctx->f_attr;
1369		if (name == NULL)
1370			fap->fa_ino = dnp->n_ino;
1371
1372		/*
1373		 * Check the returned file name case exactly
1374		 * matches requested file name. ctx->f_nmlen is
1375		 * guaranteed to always match nmlen.
1376		 */
1377		if (nmlen > 0 && strncmp(name, ctx->f_name, nmlen) != 0)
1378			error = ENOENT;
1379	}
1380	smbfs_findclose(ctx, scred);
1381	return error;
1382}
1383
1384/*
1385 * This call is used to fetch FID for directories. For normal files,
1386 * SMB_COM_OPEN is used.
1387 */
1388int
1389smbfs_smb_ntcreatex(struct smbnode *np, int accmode,
1390    struct smb_cred *scred)
1391{
1392	struct smb_rq *rqp;
1393	struct smb_share *ssp = np->n_mount->sm_share;
1394	struct mbchain *mbp;
1395	struct mdchain *mdp;
1396	int error;
1397	u_int8_t wc;
1398	u_int8_t *nmlen;
1399	u_int16_t flen;
1400
1401	KASSERT(SMBTOV(np)->v_type == VDIR);
1402
1403	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_NT_CREATE_ANDX, scred, &rqp);
1404	if (error)
1405		return error;
1406	smb_rq_getrequest(rqp, &mbp);
1407	smb_rq_wstart(rqp);
1408	mb_put_uint8(mbp, 0xff);	/* Secondary command; 0xFF = None */
1409	mb_put_uint8(mbp, 0);		/* Reserved (must be 0) */
1410	mb_put_uint16le(mbp, 0);	/* Off to next cmd WordCount */
1411	mb_put_uint8(mbp, 0);		/* Reserved (must be 0) */
1412	nmlen = mb_reserve(mbp, sizeof(u_int16_t));
1413					/* Length of Name[] in bytes */
1414	mb_put_uint32le(mbp, SMB_FL_CANONICAL_PATHNAMES);
1415					/* Flags - Create bit set */
1416	mb_put_uint32le(mbp, 0);	/* If nonzero, open relative to this */
1417	mb_put_uint32le(mbp, NT_FILE_LIST_DIRECTORY);	/* Access mask */
1418	mb_put_uint32le(mbp, 0);	/* Low 32bit */
1419	mb_put_uint32le(mbp, 0);	/* Hi 32bit */
1420					/* Initial allocation size */
1421	mb_put_uint32le(mbp, 0);	/* File attributes */
1422	mb_put_uint32le(mbp, NT_FILE_SHARE_READ|NT_FILE_SHARE_WRITE);
1423					/* Type of share access */
1424	mb_put_uint32le(mbp, NT_OPEN_EXISTING);
1425					/* Create disposition - just open */
1426	mb_put_uint32le(mbp, NT_FILE_DIRECTORY_FILE);
1427					/* Options to use if creating a file */
1428	mb_put_uint32le(mbp, 0);	/* Security QOS information */
1429	mb_put_uint8(mbp, 0);		/* Security tracking mode flags */
1430	smb_rq_wend(rqp);
1431	smb_rq_bstart(rqp);
1432
1433	error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
1434	if (error)
1435		return error;
1436
1437	/* Windows XP seems to include the final zero. Better do that too. */
1438	mb_put_uint8(mbp, 0);
1439
1440	flen = mbp->mb_count;
1441	SMBRQ_PUTLE16(nmlen, flen);
1442
1443	smb_rq_bend(rqp);
1444	error = smb_rq_simple(rqp);
1445	if (error)
1446		goto bad;
1447
1448	smb_rq_getreply(rqp, &mdp);
1449	md_get_uint8(mdp, &wc);		/* WordCount - check? */
1450	md_get_uint8(mdp, NULL);	/* AndXCommand */
1451	md_get_uint8(mdp, NULL);	/* Reserved - must be zero */
1452	md_get_uint16(mdp, NULL);	/* Offset to next cmd WordCount */
1453	md_get_uint8(mdp, NULL);	/* Oplock level granted */
1454	md_get_uint16(mdp, &np->n_fid);	/* FID */
1455	/* ignore rest */
1456
1457bad:
1458	smb_rq_done(rqp);
1459	return (error);
1460}
1461
1462/*
1463 * Setup a request for NT DIRECTORY CHANGE NOTIFY.
1464 */
1465int
1466smbfs_smb_nt_dirnotify_setup(struct smbnode *dnp, struct smb_rq **rqpp, struct smb_cred *scred, void (*notifyhook)(void *), void *notifyarg)
1467{
1468	struct smb_rq *rqp;
1469	struct smb_share *ssp = dnp->n_mount->sm_share;
1470	struct mbchain *mbp;
1471	int error;
1472
1473	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_NT_TRANSACT, scred, &rqp);
1474	if (error)
1475		return error;
1476	smb_rq_getrequest(rqp, &mbp);
1477	smb_rq_wstart(rqp);
1478	mb_put_uint8(mbp, 0xff);	/* Max setup words to return */
1479	mb_put_uint16le(mbp, 0);	/* Flags (according to Samba) */
1480	mb_put_uint32le(mbp, 0);	/* Total parameter bytes being sent*/
1481	mb_put_uint32le(mbp, 0);	/* Total data bytes being sent */
1482	mb_put_uint32le(mbp, 10*1024); /* Max parameter bytes to return */
1483	mb_put_uint32le(mbp, 0);	/* Max data bytes to return */
1484	mb_put_uint32le(mbp, 0);	/* Parameter bytes sent this buffer */
1485	mb_put_uint32le(mbp, 0);	/* Offset (from h. start) to Param */
1486	mb_put_uint32le(mbp, 0);	/* Data bytes sent this buffer */
1487	mb_put_uint32le(mbp, 0);	/* Offset (from h. start) to Data */
1488	mb_put_uint8(mbp, 4);		/* Count of setup words */
1489	mb_put_uint16le(mbp, SMB_NTTRANS_NOTIFY_CHANGE); /* Trans func code */
1490
1491	/* NT TRANSACT NOTIFY CHANGE: Request Change Notification */
1492	mb_put_uint32le(mbp,
1493		FILE_NOTIFY_CHANGE_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|
1494		FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE|
1495		FILE_NOTIFY_CHANGE_CREATION);	/* CompletionFilter */
1496	mb_put_mem(mbp, (void *)&dnp->n_fid, 2, MB_MSYSTEM);	/* FID */
1497	mb_put_uint8(mbp, 0);		/* WatchTree - Watch all subdirs too */
1498	mb_put_uint8(mbp, 0);		/* Reserved - must be zero */
1499	smb_rq_wend(rqp);
1500	smb_rq_bstart(rqp);
1501	smb_rq_bend(rqp);
1502
1503	/* No timeout */
1504	rqp->sr_timo = -1;
1505	smb_rq_setcallback(rqp, notifyhook, notifyarg);
1506
1507	error = smb_rq_enqueue(rqp);
1508	if (!error)
1509		*rqpp = rqp;
1510	else
1511		smb_rq_done(rqp);
1512
1513	return (error);
1514}
1515
1516int
1517smbfs_smb_nt_dirnotify_fetch(struct smb_rq *rqp, int *hint)
1518{
1519	int error;
1520	struct mdchain *mdp;
1521	u_int8_t sc;
1522	u_int32_t nextentry;
1523
1524	error = smb_rq_reply(rqp);
1525	if (error) {
1526		/*
1527		 * If we get EMSGSIZE, there is already too many notifications
1528		 * available for the directory, and the internal buffer
1529		 * overflew. Just flag any possible relevant change.
1530		 */
1531		if (error == EMSGSIZE) {
1532			*hint = NOTE_ATTRIB | NOTE_WRITE;
1533			error = 0;
1534		}
1535
1536		goto bad;
1537	}
1538
1539	smb_rq_getreply(rqp, &mdp);
1540
1541	/* Parse reply */
1542	error = md_get_mem(mdp, NULL, 4 + (8*4), MB_MZERO);	/* skip */
1543	if (error)
1544		goto bad;
1545
1546	md_get_uint8(mdp, &sc);			/* SetupCount */
1547	if (sc > 0)
1548		md_get_mem(mdp, NULL, sc * sizeof(u_int16_t), MB_MZERO);
1549	md_get_uint16(mdp, NULL);		/* ByteCount */
1550	md_get_mem(mdp, NULL, 1 + (sc % 2) * 2, MB_MZERO);	/* Pad */
1551
1552	/*
1553	 * The notify data are blocks of
1554	 *   ULONG nextentry - offset of next entry from start of this one
1555	 *   ULONG action - type of notification
1556	 *   ULONG filenamelen - length of filename in bytes
1557	 *   WCHAR filename[filenamelen/2] - Unicode filename
1558	 * nexentry == 0 means last notification, filename is in 16bit LE
1559	 * unicode
1560	 */
1561	*hint = 0;
1562	do {
1563		u_int32_t action;
1564#if 0
1565		u_int32_t fnlen;
1566		u_int16_t fnc;
1567#endif
1568
1569		md_get_uint32le(mdp, &nextentry);
1570		md_get_uint32le(mdp, &action);
1571		if (nextentry)
1572			md_get_mem(mdp, NULL, nextentry - 2 * 4, MB_MZERO);
1573#if 0
1574		md_get_uint32le(mdp, &fnlen);
1575
1576		printf("notify: next %u act %u fnlen %u fname '",
1577			nextentry, action, fnlen);
1578		for(; fnlen > 0; fnlen -= 2) {
1579			md_get_uint16le(mdp, &fnc);
1580			printf("%c", fnc&0xff);
1581		}
1582		printf("'\n");
1583#endif
1584
1585		switch(action) {
1586		case FILE_ACTION_ADDED:
1587		case FILE_ACTION_REMOVED:
1588		case FILE_ACTION_RENAMED_OLD_NAME:
1589		case FILE_ACTION_RENAMED_NEW_NAME:
1590			*hint |= NOTE_ATTRIB | NOTE_WRITE;
1591			break;
1592
1593		case FILE_ACTION_MODIFIED:
1594			*hint |= NOTE_ATTRIB;
1595			break;
1596		}
1597	} while(nextentry > 0);
1598
1599bad:
1600	smb_rq_done(rqp);
1601	return error;
1602}
1603
1604/*
1605 * Cancel previous SMB, with message ID mid. No reply is generated
1606 * to this one (only the previous message returns with error).
1607 */
1608int
1609smbfs_smb_ntcancel(struct smb_connobj *layer, u_int16_t mid, struct smb_cred *scred)
1610{
1611	struct smb_rq *rqp;
1612	struct mbchain *mbp;
1613	struct mbuf *m;
1614	u_int8_t *mp;
1615	int error;
1616
1617	error = smb_rq_alloc(layer, SMB_COM_NT_CANCEL, scred, &rqp);
1618	if (error)
1619		return (error);
1620	rqp->sr_flags |= SMBR_NOWAIT;	/* do not wait for reply */
1621	smb_rq_getrequest(rqp, &mbp);
1622
1623	/*
1624	 * This is nonstandard. We need to rewrite the just written
1625	 * mid to different one. Access underlying mbuf directly.
1626	 * We assume mid is the last thing written smb_rq_alloc()
1627	 * to request buffer.
1628	 */
1629	m = mbp->mb_cur;
1630	mp = mtod(m, u_int8_t *) + m->m_len - 2;
1631	SMBRQ_PUTLE16(mp, mid);
1632	rqp->sr_mid = mid;
1633
1634	smb_rq_wstart(rqp);
1635	smb_rq_wend(rqp);
1636	smb_rq_bstart(rqp);
1637	smb_rq_bend(rqp);
1638
1639	error = (smb_rq_simple(rqp));
1640
1641	/* Discard, there is no real reply */
1642	smb_rq_done(rqp);
1643
1644	return (error);
1645}
1646