smbfs_smb.c revision 138490
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 138490 2004-12-06 20:31:08Z phk $
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_flag & NOPEN) == 0 || !SMBTOV(np) ||
367	    SMBTOV(np)->v_type != VREG)
368		return 0; /* not a regular open file */
369	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_FLUSH, scred);
370	if (error)
371		return (error);
372	smb_rq_getrequest(rqp, &mbp);
373	smb_rq_wstart(rqp);
374	mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
375	smb_rq_wend(rqp);
376	smb_rq_bstart(rqp);
377	smb_rq_bend(rqp);
378	error = smb_rq_simple(rqp);
379	smb_rq_done(rqp);
380	if (!error)
381		np->n_flag &= ~NFLUSHWIRE;
382	return (error);
383}
384
385int
386smbfs_smb_flush(struct smbnode *np, struct smb_cred *scred)
387{
388	if (np->n_flag & NFLUSHWIRE)
389		return (smb_smb_flush(np, scred));
390	return (0);
391}
392
393int
394smbfs_smb_setfsize(struct smbnode *np, int newsize, struct smb_cred *scred)
395{
396	struct smb_share *ssp = np->n_mount->sm_share;
397	struct smb_rq rq, *rqp = &rq;
398	struct mbchain *mbp;
399	int error;
400
401	if (!smbfs_smb_seteof(np, (int64_t) newsize, scred)) {
402		np->n_flag |= NFLUSHWIRE;
403		return (0);
404	}
405
406	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_WRITE, scred);
407	if (error)
408		return error;
409	smb_rq_getrequest(rqp, &mbp);
410	smb_rq_wstart(rqp);
411	mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
412	mb_put_uint16le(mbp, 0);
413	mb_put_uint32le(mbp, newsize);
414	mb_put_uint16le(mbp, 0);
415	smb_rq_wend(rqp);
416	smb_rq_bstart(rqp);
417	mb_put_uint8(mbp, SMB_DT_DATA);
418	mb_put_uint16le(mbp, 0);
419	smb_rq_bend(rqp);
420	error = smb_rq_simple(rqp);
421	smb_rq_done(rqp);
422	return error;
423}
424
425int
426smbfs_smb_query_info(struct smbnode *np, const char *name, int len,
427		     struct smbfattr *fap, struct smb_cred *scred)
428{
429	struct smb_rq rq, *rqp = &rq;
430	struct smb_share *ssp = np->n_mount->sm_share;
431	struct mbchain *mbp;
432	struct mdchain *mdp;
433	u_int8_t wc;
434	int error;
435	u_int16_t wattr;
436	u_int32_t lint;
437
438	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_QUERY_INFORMATION, scred);
439	if (error)
440		return error;
441	smb_rq_getrequest(rqp, &mbp);
442	smb_rq_wstart(rqp);
443	smb_rq_wend(rqp);
444	smb_rq_bstart(rqp);
445	mb_put_uint8(mbp, SMB_DT_ASCII);
446	do {
447		error = smbfs_fullpath(mbp, SSTOVC(ssp), np, name, len);
448		if (error)
449			break;
450		smb_rq_bend(rqp);
451		error = smb_rq_simple(rqp);
452		if (error)
453			break;
454		smb_rq_getreply(rqp, &mdp);
455		if (md_get_uint8(mdp, &wc) != 0 || wc != 10) {
456			error = EBADRPC;
457			break;
458		}
459		md_get_uint16le(mdp, &wattr);
460		fap->fa_attr = wattr;
461		/*
462		 * Be careful using the time returned here, as
463		 * with FAT on NT4SP6, at least, the time returned is low
464		 * 32 bits of 100s of nanoseconds (since 1601) so it rolls
465		 * over about every seven minutes!
466		 */
467		md_get_uint32le(mdp, &lint); /* specs: secs since 1970 */
468		if (lint)	/* avoid bogus zero returns */
469			smb_time_server2local(lint, SSTOVC(ssp)->vc_sopt.sv_tz,
470					      &fap->fa_mtime);
471		md_get_uint32le(mdp, &lint);
472		fap->fa_size = lint;
473	} while(0);
474	smb_rq_done(rqp);
475	return error;
476}
477
478/*
479 * Set DOS file attributes. mtime should be NULL for dialects above lm10
480 */
481int
482smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, struct timespec *mtime,
483	struct smb_cred *scred)
484{
485	struct smb_rq rq, *rqp = &rq;
486	struct smb_share *ssp = np->n_mount->sm_share;
487	struct mbchain *mbp;
488	u_long time;
489	int error, svtz;
490
491	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION, scred);
492	if (error)
493		return error;
494	svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
495	smb_rq_getrequest(rqp, &mbp);
496	smb_rq_wstart(rqp);
497	mb_put_uint16le(mbp, attr);
498	if (mtime) {
499		smb_time_local2server(mtime, svtz, &time);
500	} else
501		time = 0;
502	mb_put_uint32le(mbp, time);		/* mtime */
503	mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO);
504	smb_rq_wend(rqp);
505	smb_rq_bstart(rqp);
506	mb_put_uint8(mbp, SMB_DT_ASCII);
507	do {
508		error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
509		if (error)
510			break;
511		mb_put_uint8(mbp, SMB_DT_ASCII);
512		mb_put_uint8(mbp, 0);
513		smb_rq_bend(rqp);
514		error = smb_rq_simple(rqp);
515		SMBERROR("%d\n", error);
516		if (error)
517			break;
518	} while(0);
519	smb_rq_done(rqp);
520	return error;
521}
522
523/*
524 * Note, win95 doesn't support this call.
525 */
526int
527smbfs_smb_setptime2(struct smbnode *np, struct timespec *mtime,
528	struct timespec *atime, int attr, struct smb_cred *scred)
529{
530	struct smb_t2rq *t2p;
531	struct smb_share *ssp = np->n_mount->sm_share;
532	struct smb_vc *vcp = SSTOVC(ssp);
533	struct mbchain *mbp;
534	u_int16_t date, time;
535	int error, tzoff;
536
537	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
538	    scred, &t2p);
539	if (error)
540		return error;
541	mbp = &t2p->t2_tparam;
542	mb_init(mbp);
543	mb_put_uint16le(mbp, SMB_INFO_STANDARD);
544	mb_put_uint32le(mbp, 0);		/* MBZ */
545	/* mb_put_uint8(mbp, SMB_DT_ASCII); specs incorrect */
546	error = smbfs_fullpath(mbp, vcp, np, NULL, 0);
547	if (error) {
548		smb_t2_done(t2p);
549		return error;
550	}
551	tzoff = vcp->vc_sopt.sv_tz;
552	mbp = &t2p->t2_tdata;
553	mb_init(mbp);
554	mb_put_uint32le(mbp, 0);		/* creation time */
555	if (atime)
556		smb_time_unix2dos(atime, tzoff, &date, &time, NULL);
557	else
558		time = date = 0;
559	mb_put_uint16le(mbp, date);
560	mb_put_uint16le(mbp, time);
561	if (mtime)
562		smb_time_unix2dos(mtime, tzoff, &date, &time, NULL);
563	else
564		time = date = 0;
565	mb_put_uint16le(mbp, date);
566	mb_put_uint16le(mbp, time);
567	mb_put_uint32le(mbp, 0);		/* file size */
568	mb_put_uint32le(mbp, 0);		/* allocation unit size */
569	mb_put_uint16le(mbp, attr);	/* DOS attr */
570	mb_put_uint32le(mbp, 0);		/* EA size */
571	t2p->t2_maxpcount = 5 * 2;
572	t2p->t2_maxdcount = vcp->vc_txmax;
573	error = smb_t2_request(t2p);
574	smb_t2_done(t2p);
575	return error;
576}
577
578/*
579 * NT level. Specially for win9x
580 */
581int
582smbfs_smb_setpattrNT(struct smbnode *np, u_short attr, struct timespec *mtime,
583	struct timespec *atime, struct smb_cred *scred)
584{
585	struct smb_t2rq *t2p;
586	struct smb_share *ssp = np->n_mount->sm_share;
587	struct smb_vc *vcp = SSTOVC(ssp);
588	struct mbchain *mbp;
589	int64_t tm;
590	int error, tzoff;
591
592	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
593	    scred, &t2p);
594	if (error)
595		return error;
596	mbp = &t2p->t2_tparam;
597	mb_init(mbp);
598	mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFO);
599	mb_put_uint32le(mbp, 0);		/* MBZ */
600	/* mb_put_uint8(mbp, SMB_DT_ASCII); specs incorrect */
601	error = smbfs_fullpath(mbp, vcp, np, NULL, 0);
602	if (error) {
603		smb_t2_done(t2p);
604		return error;
605	}
606	tzoff = vcp->vc_sopt.sv_tz;
607	mbp = &t2p->t2_tdata;
608	mb_init(mbp);
609	mb_put_int64le(mbp, 0);		/* creation time */
610	if (atime) {
611		smb_time_local2NT(atime, tzoff, &tm);
612	} else
613		tm = 0;
614	mb_put_int64le(mbp, tm);
615	if (mtime) {
616		smb_time_local2NT(mtime, tzoff, &tm);
617	} else
618		tm = 0;
619	mb_put_int64le(mbp, tm);
620	mb_put_int64le(mbp, tm);		/* change time */
621	mb_put_uint32le(mbp, attr);		/* attr */
622	t2p->t2_maxpcount = 24;
623	t2p->t2_maxdcount = 56;
624	error = smb_t2_request(t2p);
625	smb_t2_done(t2p);
626	return error;
627}
628
629/*
630 * Set file atime and mtime. Doesn't supported by core dialect.
631 */
632int
633smbfs_smb_setftime(struct smbnode *np, struct timespec *mtime,
634	struct timespec *atime, struct smb_cred *scred)
635{
636	struct smb_rq rq, *rqp = &rq;
637	struct smb_share *ssp = np->n_mount->sm_share;
638	struct mbchain *mbp;
639	u_int16_t date, time;
640	int error, tzoff;
641
642	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION2, scred);
643	if (error)
644		return error;
645	tzoff = SSTOVC(ssp)->vc_sopt.sv_tz;
646	smb_rq_getrequest(rqp, &mbp);
647	smb_rq_wstart(rqp);
648	mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
649	mb_put_uint32le(mbp, 0);		/* creation time */
650
651	if (atime)
652		smb_time_unix2dos(atime, tzoff, &date, &time, NULL);
653	else
654		time = date = 0;
655	mb_put_uint16le(mbp, date);
656	mb_put_uint16le(mbp, time);
657	if (mtime)
658		smb_time_unix2dos(mtime, tzoff, &date, &time, NULL);
659	else
660		time = date = 0;
661	mb_put_uint16le(mbp, date);
662	mb_put_uint16le(mbp, time);
663	smb_rq_wend(rqp);
664	smb_rq_bstart(rqp);
665	smb_rq_bend(rqp);
666	error = smb_rq_simple(rqp);
667	SMBSDEBUG("%d\n", error);
668	smb_rq_done(rqp);
669	return error;
670}
671
672/*
673 * Set DOS file attributes.
674 * Looks like this call can be used only if CAP_NT_SMBS bit is on.
675 */
676int
677smbfs_smb_setfattrNT(struct smbnode *np, u_int16_t attr, struct timespec *mtime,
678	struct timespec *atime, struct smb_cred *scred)
679{
680	struct smb_t2rq *t2p;
681	struct smb_share *ssp = np->n_mount->sm_share;
682	struct mbchain *mbp;
683	int64_t tm;
684	int error, svtz;
685
686	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION,
687	    scred, &t2p);
688	if (error)
689		return error;
690	svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
691	mbp = &t2p->t2_tparam;
692	mb_init(mbp);
693	mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
694	mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFO);
695	mb_put_uint32le(mbp, 0);
696	mbp = &t2p->t2_tdata;
697	mb_init(mbp);
698	mb_put_int64le(mbp, 0);		/* creation time */
699	if (atime) {
700		smb_time_local2NT(atime, svtz, &tm);
701	} else
702		tm = 0;
703	mb_put_int64le(mbp, tm);
704	if (mtime) {
705		smb_time_local2NT(mtime, svtz, &tm);
706	} else
707		tm = 0;
708	mb_put_int64le(mbp, tm);
709	mb_put_int64le(mbp, tm);		/* change time */
710	mb_put_uint16le(mbp, attr);
711	mb_put_uint32le(mbp, 0);			/* padding */
712	mb_put_uint16le(mbp, 0);
713	t2p->t2_maxpcount = 2;
714	t2p->t2_maxdcount = 0;
715	error = smb_t2_request(t2p);
716	smb_t2_done(t2p);
717	return error;
718}
719
720
721int
722smbfs_smb_open(struct smbnode *np, int accmode, struct smb_cred *scred)
723{
724	struct smb_rq rq, *rqp = &rq;
725	struct smb_share *ssp = np->n_mount->sm_share;
726	struct mbchain *mbp;
727	struct mdchain *mdp;
728	u_int8_t wc;
729	u_int16_t fid, wattr, grantedmode;
730	int error;
731
732	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_OPEN, scred);
733	if (error)
734		return error;
735	smb_rq_getrequest(rqp, &mbp);
736	smb_rq_wstart(rqp);
737	mb_put_uint16le(mbp, accmode);
738	mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
739	smb_rq_wend(rqp);
740	smb_rq_bstart(rqp);
741	mb_put_uint8(mbp, SMB_DT_ASCII);
742	do {
743		error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
744		if (error)
745			break;
746		smb_rq_bend(rqp);
747		error = smb_rq_simple(rqp);
748		if (error)
749			break;
750		smb_rq_getreply(rqp, &mdp);
751		if (md_get_uint8(mdp, &wc) != 0 || wc != 7) {
752			error = EBADRPC;
753			break;
754		}
755		md_get_uint16(mdp, &fid);
756		md_get_uint16le(mdp, &wattr);
757		md_get_uint32(mdp, NULL);	/* mtime */
758		md_get_uint32(mdp, NULL);	/* fsize */
759		md_get_uint16le(mdp, &grantedmode);
760		/*
761		 * TODO: refresh attributes from this reply
762		 */
763	} while(0);
764	smb_rq_done(rqp);
765	if (error)
766		return error;
767	np->n_fid = fid;
768	np->n_rwstate = grantedmode;
769	return 0;
770}
771
772
773int
774smbfs_smb_close(struct smb_share *ssp, u_int16_t fid, struct timespec *mtime,
775	struct smb_cred *scred)
776{
777	struct smb_rq rq, *rqp = &rq;
778	struct mbchain *mbp;
779	u_long time;
780	int error;
781
782	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CLOSE, scred);
783	if (error)
784		return error;
785	smb_rq_getrequest(rqp, &mbp);
786	smb_rq_wstart(rqp);
787	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
788	if (mtime) {
789		smb_time_local2server(mtime, SSTOVC(ssp)->vc_sopt.sv_tz, &time);
790	} else
791		time = 0;
792	mb_put_uint32le(mbp, time);
793	smb_rq_wend(rqp);
794	smb_rq_bstart(rqp);
795	smb_rq_bend(rqp);
796	error = smb_rq_simple(rqp);
797	smb_rq_done(rqp);
798	return error;
799}
800
801int
802smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen,
803	struct smb_cred *scred)
804{
805	struct smb_rq rq, *rqp = &rq;
806	struct smb_share *ssp = dnp->n_mount->sm_share;
807	struct mbchain *mbp;
808	struct mdchain *mdp;
809	struct timespec ctime;
810	u_int8_t wc;
811	u_int16_t fid;
812	u_long tm;
813	int error;
814
815	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE, scred);
816	if (error)
817		return error;
818	smb_rq_getrequest(rqp, &mbp);
819	smb_rq_wstart(rqp);
820	mb_put_uint16le(mbp, SMB_FA_ARCHIVE);		/* attributes  */
821	nanotime(&ctime);
822	smb_time_local2server(&ctime, SSTOVC(ssp)->vc_sopt.sv_tz, &tm);
823	mb_put_uint32le(mbp, tm);
824	smb_rq_wend(rqp);
825	smb_rq_bstart(rqp);
826	mb_put_uint8(mbp, SMB_DT_ASCII);
827	error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, nmlen);
828	if (!error) {
829		smb_rq_bend(rqp);
830		error = smb_rq_simple(rqp);
831		if (!error) {
832			smb_rq_getreply(rqp, &mdp);
833			md_get_uint8(mdp, &wc);
834			if (wc == 1)
835				md_get_uint16(mdp, &fid);
836			else
837				error = EBADRPC;
838		}
839	}
840	smb_rq_done(rqp);
841	if (error)
842		return error;
843	smbfs_smb_close(ssp, fid, &ctime, scred);
844	return error;
845}
846
847int
848smbfs_smb_delete(struct smbnode *np, struct smb_cred *scred)
849{
850	struct smb_rq rq, *rqp = &rq;
851	struct smb_share *ssp = np->n_mount->sm_share;
852	struct mbchain *mbp;
853	int error;
854
855	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE, scred);
856	if (error)
857		return error;
858	smb_rq_getrequest(rqp, &mbp);
859	smb_rq_wstart(rqp);
860	mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
861	smb_rq_wend(rqp);
862	smb_rq_bstart(rqp);
863	mb_put_uint8(mbp, SMB_DT_ASCII);
864	error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
865	if (!error) {
866		smb_rq_bend(rqp);
867		error = smb_rq_simple(rqp);
868	}
869	smb_rq_done(rqp);
870	return error;
871}
872
873int
874smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp,
875	const char *tname, int tnmlen, struct smb_cred *scred)
876{
877	struct smb_rq rq, *rqp = &rq;
878	struct smb_share *ssp = src->n_mount->sm_share;
879	struct mbchain *mbp;
880	int error;
881
882	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_RENAME, scred);
883	if (error)
884		return error;
885	smb_rq_getrequest(rqp, &mbp);
886	smb_rq_wstart(rqp);
887	mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
888	smb_rq_wend(rqp);
889	smb_rq_bstart(rqp);
890	mb_put_uint8(mbp, SMB_DT_ASCII);
891	do {
892		error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0);
893		if (error)
894			break;
895		mb_put_uint8(mbp, SMB_DT_ASCII);
896		error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen);
897		if (error)
898			break;
899		smb_rq_bend(rqp);
900		error = smb_rq_simple(rqp);
901	} while(0);
902	smb_rq_done(rqp);
903	return error;
904}
905
906int
907smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp,
908	const char *tname, int tnmlen, u_int16_t flags, struct smb_cred *scred)
909{
910	struct smb_rq rq, *rqp = &rq;
911	struct smb_share *ssp = src->n_mount->sm_share;
912	struct mbchain *mbp;
913	int error;
914
915	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_MOVE, scred);
916	if (error)
917		return error;
918	smb_rq_getrequest(rqp, &mbp);
919	smb_rq_wstart(rqp);
920	mb_put_uint16le(mbp, SMB_TID_UNKNOWN);
921	mb_put_uint16le(mbp, 0x20);	/* delete target file */
922	mb_put_uint16le(mbp, flags);
923	smb_rq_wend(rqp);
924	smb_rq_bstart(rqp);
925	mb_put_uint8(mbp, SMB_DT_ASCII);
926	do {
927		error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0);
928		if (error)
929			break;
930		mb_put_uint8(mbp, SMB_DT_ASCII);
931		error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen);
932		if (error)
933			break;
934		smb_rq_bend(rqp);
935		error = smb_rq_simple(rqp);
936	} while(0);
937	smb_rq_done(rqp);
938	return error;
939}
940
941int
942smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len,
943	struct smb_cred *scred)
944{
945	struct smb_rq rq, *rqp = &rq;
946	struct smb_share *ssp = dnp->n_mount->sm_share;
947	struct mbchain *mbp;
948	int error;
949
950	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE_DIRECTORY, scred);
951	if (error)
952		return error;
953	smb_rq_getrequest(rqp, &mbp);
954	smb_rq_wstart(rqp);
955	smb_rq_wend(rqp);
956	smb_rq_bstart(rqp);
957	mb_put_uint8(mbp, SMB_DT_ASCII);
958	error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, len);
959	if (!error) {
960		smb_rq_bend(rqp);
961		error = smb_rq_simple(rqp);
962	}
963	smb_rq_done(rqp);
964	return error;
965}
966
967int
968smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scred)
969{
970	struct smb_rq rq, *rqp = &rq;
971	struct smb_share *ssp = np->n_mount->sm_share;
972	struct mbchain *mbp;
973	int error;
974
975	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE_DIRECTORY, scred);
976	if (error)
977		return error;
978	smb_rq_getrequest(rqp, &mbp);
979	smb_rq_wstart(rqp);
980	smb_rq_wend(rqp);
981	smb_rq_bstart(rqp);
982	mb_put_uint8(mbp, SMB_DT_ASCII);
983	error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
984	if (!error) {
985		smb_rq_bend(rqp);
986		error = smb_rq_simple(rqp);
987	}
988	smb_rq_done(rqp);
989	return error;
990}
991
992static int
993smbfs_smb_search(struct smbfs_fctx *ctx)
994{
995	struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
996	struct smb_rq *rqp;
997	struct mbchain *mbp;
998	struct mdchain *mdp;
999	u_int8_t wc, bt;
1000	u_int16_t ec, dlen, bc;
1001	int maxent, error, iseof = 0;
1002
1003	maxent = min(ctx->f_left, (vcp->vc_txmax - SMB_HDRLEN - 3) / SMB_DENTRYLEN);
1004	if (ctx->f_rq) {
1005		smb_rq_done(ctx->f_rq);
1006		ctx->f_rq = NULL;
1007	}
1008	error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_SEARCH, ctx->f_scred, &rqp);
1009	if (error)
1010		return error;
1011	ctx->f_rq = rqp;
1012	smb_rq_getrequest(rqp, &mbp);
1013	smb_rq_wstart(rqp);
1014	mb_put_uint16le(mbp, maxent);	/* max entries to return */
1015	mb_put_uint16le(mbp, ctx->f_attrmask);
1016	smb_rq_wend(rqp);
1017	smb_rq_bstart(rqp);
1018	mb_put_uint8(mbp, SMB_DT_ASCII);	/* buffer format */
1019	if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
1020		error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen);
1021		if (error)
1022			return error;
1023		mb_put_uint8(mbp, SMB_DT_VARIABLE);
1024		mb_put_uint16le(mbp, 0);	/* context length */
1025		ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
1026	} else {
1027		mb_put_uint8(mbp, 0);	/* file name length */
1028		mb_put_uint8(mbp, SMB_DT_VARIABLE);
1029		mb_put_uint16le(mbp, SMB_SKEYLEN);
1030		mb_put_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM);
1031	}
1032	smb_rq_bend(rqp);
1033	error = smb_rq_simple(rqp);
1034	if (error) {
1035		if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnofiles) {
1036			error = 0;
1037			iseof = 1;
1038			ctx->f_flags |= SMBFS_RDD_EOF;
1039		} else
1040			return error;
1041	}
1042	smb_rq_getreply(rqp, &mdp);
1043	md_get_uint8(mdp, &wc);
1044	if (wc != 1)
1045		return iseof ? ENOENT : EBADRPC;
1046	md_get_uint16le(mdp, &ec);
1047	if (ec == 0)
1048		return ENOENT;
1049	ctx->f_ecnt = ec;
1050	md_get_uint16le(mdp, &bc);
1051	if (bc < 3)
1052		return EBADRPC;
1053	bc -= 3;
1054	md_get_uint8(mdp, &bt);
1055	if (bt != SMB_DT_VARIABLE)
1056		return EBADRPC;
1057	md_get_uint16le(mdp, &dlen);
1058	if (dlen != bc || dlen % SMB_DENTRYLEN != 0)
1059		return EBADRPC;
1060	return 0;
1061}
1062
1063static int
1064smbfs_findopenLM1(struct smbfs_fctx *ctx, struct smbnode *dnp,
1065	const char *wildcard, int wclen, int attr, struct smb_cred *scred)
1066{
1067	ctx->f_attrmask = attr;
1068	if (wildcard) {
1069		if (wclen == 1 && wildcard[0] == '*') {
1070			ctx->f_wildcard = "*.*";
1071			ctx->f_wclen = 3;
1072		} else {
1073			ctx->f_wildcard = wildcard;
1074			ctx->f_wclen = wclen;
1075		}
1076	} else {
1077		ctx->f_wildcard = NULL;
1078		ctx->f_wclen = 0;
1079	}
1080	ctx->f_name = ctx->f_fname;
1081	return 0;
1082}
1083
1084static int
1085smbfs_findnextLM1(struct smbfs_fctx *ctx, int limit)
1086{
1087	struct mdchain *mbp;
1088	struct smb_rq *rqp;
1089	char *cp;
1090	u_int8_t battr;
1091	u_int16_t date, time;
1092	u_int32_t size;
1093	int error;
1094
1095	if (ctx->f_ecnt == 0) {
1096		if (ctx->f_flags & SMBFS_RDD_EOF)
1097			return ENOENT;
1098		ctx->f_left = ctx->f_limit = limit;
1099		error = smbfs_smb_search(ctx);
1100		if (error)
1101			return error;
1102	}
1103	rqp = ctx->f_rq;
1104	smb_rq_getreply(rqp, &mbp);
1105	md_get_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM);
1106	md_get_uint8(mbp, &battr);
1107	md_get_uint16le(mbp, &time);
1108	md_get_uint16le(mbp, &date);
1109	md_get_uint32le(mbp, &size);
1110	cp = ctx->f_name;
1111	md_get_mem(mbp, cp, sizeof(ctx->f_fname), MB_MSYSTEM);
1112	cp[sizeof(ctx->f_fname) - 1] = 0;
1113	cp += strlen(cp) - 1;
1114	while (*cp == ' ' && cp >= ctx->f_name)
1115		*cp-- = 0;
1116	ctx->f_attr.fa_attr = battr;
1117	smb_dos2unixtime(date, time, 0, rqp->sr_vc->vc_sopt.sv_tz,
1118	    &ctx->f_attr.fa_mtime);
1119	ctx->f_attr.fa_size = size;
1120	ctx->f_nmlen = strlen(ctx->f_name);
1121	ctx->f_ecnt--;
1122	ctx->f_left--;
1123	return 0;
1124}
1125
1126static int
1127smbfs_findcloseLM1(struct smbfs_fctx *ctx)
1128{
1129	if (ctx->f_rq)
1130		smb_rq_done(ctx->f_rq);
1131	return 0;
1132}
1133
1134/*
1135 * TRANS2_FIND_FIRST2/NEXT2, used for NT LM12 dialect
1136 */
1137static int
1138smbfs_smb_trans2find2(struct smbfs_fctx *ctx)
1139{
1140	struct smb_t2rq *t2p;
1141	struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
1142	struct mbchain *mbp;
1143	struct mdchain *mdp;
1144	u_int16_t tw, flags;
1145	int error;
1146
1147	if (ctx->f_t2) {
1148		smb_t2_done(ctx->f_t2);
1149		ctx->f_t2 = NULL;
1150	}
1151	ctx->f_flags &= ~SMBFS_RDD_GOTRNAME;
1152	flags = 8 | 2;			/* <resume> | <close if EOS> */
1153	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
1154		flags |= 1;		/* close search after this request */
1155		ctx->f_flags |= SMBFS_RDD_NOCLOSE;
1156	}
1157	if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
1158		error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_FIRST2,
1159		    ctx->f_scred, &t2p);
1160		if (error)
1161			return error;
1162		ctx->f_t2 = t2p;
1163		mbp = &t2p->t2_tparam;
1164		mb_init(mbp);
1165		mb_put_uint16le(mbp, ctx->f_attrmask);
1166		mb_put_uint16le(mbp, ctx->f_limit);
1167		mb_put_uint16le(mbp, flags);
1168		mb_put_uint16le(mbp, ctx->f_infolevel);
1169		mb_put_uint32le(mbp, 0);
1170		error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen);
1171		if (error)
1172			return error;
1173	} else	{
1174		error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_NEXT2,
1175		    ctx->f_scred, &t2p);
1176		if (error)
1177			return error;
1178		ctx->f_t2 = t2p;
1179		mbp = &t2p->t2_tparam;
1180		mb_init(mbp);
1181		mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM);
1182		mb_put_uint16le(mbp, ctx->f_limit);
1183		mb_put_uint16le(mbp, ctx->f_infolevel);
1184		mb_put_uint32le(mbp, 0);		/* resume key */
1185		mb_put_uint16le(mbp, flags);
1186		if (ctx->f_rname)
1187			mb_put_mem(mbp, ctx->f_rname, strlen(ctx->f_rname) + 1, MB_MSYSTEM);
1188		else
1189			mb_put_uint8(mbp, 0);	/* resume file name */
1190#if 0
1191	struct timeval tv;
1192	tv.tv_sec = 0;
1193	tv.tv_usec = 200 * 1000;	/* 200ms */
1194		if (vcp->vc_flags & SMBC_WIN95) {
1195			/*
1196			 * some implementations suggests to sleep here
1197			 * for 200ms, due to the bug in the Win95.
1198			 * I've didn't notice any problem, but put code
1199			 * for it.
1200			 */
1201			 tsleep(&flags, PVFS, "fix95", tvtohz(&tv));
1202		}
1203#endif
1204	}
1205	t2p->t2_maxpcount = 5 * 2;
1206	t2p->t2_maxdcount = vcp->vc_txmax;
1207	error = smb_t2_request(t2p);
1208	if (error)
1209		return error;
1210	mdp = &t2p->t2_rparam;
1211	if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
1212		if ((error = md_get_uint16(mdp, &ctx->f_Sid)) != 0)
1213			return error;
1214		ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
1215	}
1216	if ((error = md_get_uint16le(mdp, &tw)) != 0)
1217		return error;
1218	ctx->f_ecnt = tw;
1219	if ((error = md_get_uint16le(mdp, &tw)) != 0)
1220		return error;
1221	if (tw)
1222		ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE;
1223	if ((error = md_get_uint16le(mdp, &tw)) != 0)
1224		return error;
1225	if ((error = md_get_uint16le(mdp, &tw)) != 0)
1226		return error;
1227	if (ctx->f_ecnt == 0)
1228		return ENOENT;
1229	ctx->f_rnameofs = tw;
1230	mdp = &t2p->t2_rdata;
1231	if (mdp->md_top == NULL) {
1232		printf("bug: ecnt = %d, but data is NULL (please report)\n", ctx->f_ecnt);
1233		return ENOENT;
1234	}
1235	if (mdp->md_top->m_len == 0) {
1236		printf("bug: ecnt = %d, but m_len = 0 and m_next = %p (please report)\n", ctx->f_ecnt,mbp->mb_top->m_next);
1237		return ENOENT;
1238	}
1239	ctx->f_eofs = 0;
1240	return 0;
1241}
1242
1243static int
1244smbfs_smb_findclose2(struct smbfs_fctx *ctx)
1245{
1246	struct smb_rq rq, *rqp = &rq;
1247	struct mbchain *mbp;
1248	int error;
1249
1250	error = smb_rq_init(rqp, SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2, ctx->f_scred);
1251	if (error)
1252		return error;
1253	smb_rq_getrequest(rqp, &mbp);
1254	smb_rq_wstart(rqp);
1255	mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM);
1256	smb_rq_wend(rqp);
1257	smb_rq_bstart(rqp);
1258	smb_rq_bend(rqp);
1259	error = smb_rq_simple(rqp);
1260	smb_rq_done(rqp);
1261	return error;
1262}
1263
1264static int
1265smbfs_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp,
1266	const char *wildcard, int wclen, int attr, struct smb_cred *scred)
1267{
1268	ctx->f_name = malloc(SMB_MAXFNAMELEN, M_SMBFSDATA, M_WAITOK);
1269	if (ctx->f_name == NULL)
1270		return ENOMEM;
1271	ctx->f_infolevel = SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_NTLM0_12 ?
1272	    SMB_INFO_STANDARD : SMB_FIND_FILE_DIRECTORY_INFO;
1273	ctx->f_attrmask = attr;
1274	ctx->f_wildcard = wildcard;
1275	ctx->f_wclen = wclen;
1276	return 0;
1277}
1278
1279static int
1280smbfs_findnextLM2(struct smbfs_fctx *ctx, int limit)
1281{
1282	struct mdchain *mbp;
1283	struct smb_t2rq *t2p;
1284	char *cp;
1285	u_int8_t tb;
1286	u_int16_t date, time, wattr;
1287	u_int32_t size, next, dattr;
1288	int64_t lint;
1289	int error, svtz, cnt, fxsz, nmlen, recsz;
1290
1291	if (ctx->f_ecnt == 0) {
1292		if (ctx->f_flags & SMBFS_RDD_EOF)
1293			return ENOENT;
1294		ctx->f_left = ctx->f_limit = limit;
1295		error = smbfs_smb_trans2find2(ctx);
1296		if (error)
1297			return error;
1298	}
1299	t2p = ctx->f_t2;
1300	mbp = &t2p->t2_rdata;
1301	svtz = SSTOVC(ctx->f_ssp)->vc_sopt.sv_tz;
1302	switch (ctx->f_infolevel) {
1303	    case SMB_INFO_STANDARD:
1304		next = 0;
1305		fxsz = 0;
1306		md_get_uint16le(mbp, &date);
1307		md_get_uint16le(mbp, &time);	/* creation time */
1308		md_get_uint16le(mbp, &date);
1309		md_get_uint16le(mbp, &time);	/* access time */
1310		smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_atime);
1311		md_get_uint16le(mbp, &date);
1312		md_get_uint16le(mbp, &time);	/* access time */
1313		smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_mtime);
1314		md_get_uint32le(mbp, &size);
1315		ctx->f_attr.fa_size = size;
1316		md_get_uint32(mbp, NULL);	/* allocation size */
1317		md_get_uint16le(mbp, &wattr);
1318		ctx->f_attr.fa_attr = wattr;
1319		md_get_uint8(mbp, &tb);
1320		size = nmlen = tb;
1321		fxsz = 23;
1322		recsz = next = 24 + nmlen;	/* docs misses zero byte at end */
1323		break;
1324	    case SMB_FIND_FILE_DIRECTORY_INFO:
1325		md_get_uint32le(mbp, &next);
1326		md_get_uint32(mbp, NULL);	/* file index */
1327		md_get_int64(mbp, NULL);	/* creation time */
1328		md_get_int64le(mbp, &lint);
1329		smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_atime);
1330		md_get_int64le(mbp, &lint);
1331		smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_mtime);
1332		md_get_int64le(mbp, &lint);
1333		smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_ctime);
1334		md_get_int64le(mbp, &lint);	/* file size */
1335		ctx->f_attr.fa_size = lint;
1336		md_get_int64(mbp, NULL);	/* real size (should use) */
1337		md_get_uint32le(mbp, &dattr);	/* EA */
1338		ctx->f_attr.fa_attr = dattr;
1339		md_get_uint32le(mbp, &size);	/* name len */
1340		fxsz = 64;
1341		recsz = next ? next : fxsz + size;
1342		break;
1343	    default:
1344		SMBERROR("unexpected info level %d\n", ctx->f_infolevel);
1345		return EINVAL;
1346	}
1347	nmlen = min(size, SMB_MAXFNAMELEN);
1348	cp = ctx->f_name;
1349	error = md_get_mem(mbp, cp, nmlen, MB_MSYSTEM);
1350	if (error)
1351		return error;
1352	if (next) {
1353		cnt = next - nmlen - fxsz;
1354		if (cnt > 0)
1355			md_get_mem(mbp, NULL, cnt, MB_MSYSTEM);
1356		else if (cnt < 0) {
1357			SMBERROR("out of sync\n");
1358			return EBADRPC;
1359		}
1360	}
1361	if (nmlen && cp[nmlen - 1] == 0)
1362		nmlen--;
1363	if (nmlen == 0)
1364		return EBADRPC;
1365
1366	next = ctx->f_eofs + recsz;
1367	if (ctx->f_rnameofs && (ctx->f_flags & SMBFS_RDD_GOTRNAME) == 0 &&
1368	    (ctx->f_rnameofs >= ctx->f_eofs && ctx->f_rnameofs < next)) {
1369		/*
1370		 * Server needs a resume filename.
1371		 */
1372		if (ctx->f_rnamelen <= nmlen) {
1373			if (ctx->f_rname)
1374				free(ctx->f_rname, M_SMBFSDATA);
1375			ctx->f_rname = malloc(nmlen + 1, M_SMBFSDATA, M_WAITOK);
1376			ctx->f_rnamelen = nmlen;
1377		}
1378		bcopy(ctx->f_name, ctx->f_rname, nmlen);
1379		ctx->f_rname[nmlen] = 0;
1380		ctx->f_flags |= SMBFS_RDD_GOTRNAME;
1381	}
1382	ctx->f_nmlen = nmlen;
1383	ctx->f_eofs = next;
1384	ctx->f_ecnt--;
1385	ctx->f_left--;
1386	return 0;
1387}
1388
1389static int
1390smbfs_findcloseLM2(struct smbfs_fctx *ctx)
1391{
1392	if (ctx->f_name)
1393		free(ctx->f_name, M_SMBFSDATA);
1394	if (ctx->f_t2)
1395		smb_t2_done(ctx->f_t2);
1396	if ((ctx->f_flags & SMBFS_RDD_NOCLOSE) == 0)
1397		smbfs_smb_findclose2(ctx);
1398	return 0;
1399}
1400
1401int
1402smbfs_findopen(struct smbnode *dnp, const char *wildcard, int wclen, int attr,
1403	struct smb_cred *scred, struct smbfs_fctx **ctxpp)
1404{
1405	struct smbfs_fctx *ctx;
1406	int error;
1407
1408	ctx = malloc(sizeof(*ctx), M_SMBFSDATA, M_WAITOK);
1409	if (ctx == NULL)
1410		return ENOMEM;
1411	bzero(ctx, sizeof(*ctx));
1412	ctx->f_ssp = dnp->n_mount->sm_share;
1413	ctx->f_dnp = dnp;
1414	ctx->f_flags = SMBFS_RDD_FINDFIRST;
1415	ctx->f_scred = scred;
1416	if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0 ||
1417	    (dnp->n_mount->sm_flags & SMBFS_MOUNT_NO_LONG)) {
1418		ctx->f_flags |= SMBFS_RDD_USESEARCH;
1419		error = smbfs_findopenLM1(ctx, dnp, wildcard, wclen, attr, scred);
1420	} else
1421		error = smbfs_findopenLM2(ctx, dnp, wildcard, wclen, attr, scred);
1422	if (error)
1423		smbfs_findclose(ctx, scred);
1424	else
1425		*ctxpp = ctx;
1426	return error;
1427}
1428
1429int
1430smbfs_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scred)
1431{
1432	int error;
1433
1434	if (limit == 0)
1435		limit = 1000000;
1436	else if (limit > 1)
1437		limit *= 4;	/* imperical */
1438	ctx->f_scred = scred;
1439	for (;;) {
1440		if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
1441			error = smbfs_findnextLM1(ctx, limit);
1442		} else
1443			error = smbfs_findnextLM2(ctx, limit);
1444		if (error)
1445			return error;
1446		if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') ||
1447		    (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' &&
1448		     ctx->f_name[1] == '.'))
1449			continue;
1450		break;
1451	}
1452	smbfs_fname_tolocal(SSTOVC(ctx->f_ssp), ctx->f_name, ctx->f_nmlen,
1453	    ctx->f_dnp->n_mount->sm_caseopt);
1454	ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name, ctx->f_nmlen);
1455	return 0;
1456}
1457
1458int
1459smbfs_findclose(struct smbfs_fctx *ctx, struct smb_cred *scred)
1460{
1461	ctx->f_scred = scred;
1462	if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
1463		smbfs_findcloseLM1(ctx);
1464	} else
1465		smbfs_findcloseLM2(ctx);
1466	if (ctx->f_rname)
1467		free(ctx->f_rname, M_SMBFSDATA);
1468	free(ctx, M_SMBFSDATA);
1469	return 0;
1470}
1471
1472int
1473smbfs_smb_lookup(struct smbnode *dnp, const char *name, int nmlen,
1474	struct smbfattr *fap, struct smb_cred *scred)
1475{
1476	struct smbfs_fctx *ctx;
1477	int error;
1478
1479	if (dnp == NULL || (dnp->n_ino == 2 && name == NULL)) {
1480		bzero(fap, sizeof(*fap));
1481		fap->fa_attr = SMB_FA_DIR;
1482		fap->fa_ino = 2;
1483		return 0;
1484	}
1485	if (nmlen == 1 && name[0] == '.') {
1486		error = smbfs_smb_lookup(dnp, NULL, 0, fap, scred);
1487		return error;
1488	} else if (nmlen == 2 && name[0] == '.' && name[1] == '.') {
1489		error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0, fap,
1490		    scred);
1491		printf("%s: knows NOTHING about '..'\n", __func__);
1492		return error;
1493	}
1494	error = smbfs_findopen(dnp, name, nmlen,
1495	    SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, scred, &ctx);
1496	if (error)
1497		return error;
1498	ctx->f_flags |= SMBFS_RDD_FINDSINGLE;
1499	error = smbfs_findnext(ctx, 1, scred);
1500	if (error == 0) {
1501		*fap = ctx->f_attr;
1502		if (name == NULL)
1503			fap->fa_ino = dnp->n_ino;
1504	}
1505	smbfs_findclose(ctx, scred);
1506	return error;
1507}
1508