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