smbfs_smb.c revision 82039
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 82039 2001-08-21 08:27:47Z bp $
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, (u_int32_t)id, start, end, scred);
140}
141
142int
143smbfs_smb_statfs2(struct smb_share *ssp, struct statfs *sbp,
144	struct smb_cred *scred)
145{
146	struct smb_t2rq *t2p;
147	struct mbchain *mbp;
148	struct mdchain *mdp;
149	u_int16_t bsize;
150	u_int32_t units, bpu, funits;
151	int error;
152
153	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FS_INFORMATION,
154	    scred, &t2p);
155	if (error)
156		return error;
157	mbp = &t2p->t2_tparam;
158	mb_init(mbp);
159	mb_put_uint16le(mbp, SMB_INFO_ALLOCATION);
160	t2p->t2_maxpcount = 4;
161	t2p->t2_maxdcount = 4 * 4 + 2;
162	error = smb_t2_request(t2p);
163	if (error) {
164		smb_t2_done(t2p);
165		return error;
166	}
167	mdp = &t2p->t2_rdata;
168	md_get_uint32(mdp, NULL);	/* fs id */
169	md_get_uint32le(mdp, &bpu);
170	md_get_uint32le(mdp, &units);
171	md_get_uint32le(mdp, &funits);
172	md_get_uint16le(mdp, &bsize);
173	sbp->f_bsize = bpu * bsize;	/* fundamental file system block size */
174	sbp->f_blocks= units;		/* total data blocks in file system */
175	sbp->f_bfree = funits;		/* free blocks in fs */
176	sbp->f_bavail= funits;		/* free blocks avail to non-superuser */
177	sbp->f_files = 0xffff;		/* total file nodes in file system */
178	sbp->f_ffree = 0xffff;		/* free file nodes in fs */
179	smb_t2_done(t2p);
180	return 0;
181}
182
183int
184smbfs_smb_statfs(struct smb_share *ssp, struct statfs *sbp,
185	struct smb_cred *scred)
186{
187	struct smb_rq rq, *rqp = &rq;
188	struct mdchain *mdp;
189	u_int16_t units, bpu, bsize, funits;
190	int error;
191
192	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_QUERY_INFORMATION_DISK, scred);
193	if (error)
194		return error;
195	smb_rq_wstart(rqp);
196	smb_rq_wend(rqp);
197	smb_rq_bstart(rqp);
198	smb_rq_bend(rqp);
199	error = smb_rq_simple(rqp);
200	if (error) {
201		smb_rq_done(rqp);
202		return error;
203	}
204	smb_rq_getreply(rqp, &mdp);
205	md_get_uint16le(mdp, &units);
206	md_get_uint16le(mdp, &bpu);
207	md_get_uint16le(mdp, &bsize);
208	md_get_uint16le(mdp, &funits);
209	sbp->f_bsize = bpu * bsize;	/* fundamental file system block size */
210	sbp->f_blocks= units;		/* total data blocks in file system */
211	sbp->f_bfree = funits;		/* free blocks in fs */
212	sbp->f_bavail= funits;		/* free blocks avail to non-superuser */
213	sbp->f_files = 0xffff;		/* total file nodes in file system */
214	sbp->f_ffree = 0xffff;		/* free file nodes in fs */
215	smb_rq_done(rqp);
216	return 0;
217}
218
219int
220smbfs_smb_setfsize(struct smbnode *np, int newsize, struct smb_cred *scred)
221{
222	struct smb_share *ssp = np->n_mount->sm_share;
223	struct smb_rq rq, *rqp = &rq;
224	struct mbchain *mbp;
225	int error;
226
227	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_WRITE, scred);
228	if (error)
229		return error;
230	smb_rq_getrequest(rqp, &mbp);
231	smb_rq_wstart(rqp);
232	mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
233	mb_put_uint16le(mbp, 0);
234	mb_put_uint32le(mbp, newsize);
235	mb_put_uint16le(mbp, 0);
236	smb_rq_wend(rqp);
237	smb_rq_bstart(rqp);
238	mb_put_uint8(mbp, SMB_DT_DATA);
239	mb_put_uint16le(mbp, 0);
240	smb_rq_bend(rqp);
241	error = smb_rq_simple(rqp);
242	smb_rq_done(rqp);
243	return error;
244}
245
246
247/*
248 * Set DOS file attributes. mtime should be NULL for dialects above lm10
249 */
250int
251smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, struct timespec *mtime,
252	struct smb_cred *scred)
253{
254	struct smb_rq rq, *rqp = &rq;
255	struct smb_share *ssp = np->n_mount->sm_share;
256	struct mbchain *mbp;
257	u_long time;
258	int error, svtz;
259
260	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION, scred);
261	if (error)
262		return error;
263	svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
264	smb_rq_getrequest(rqp, &mbp);
265	smb_rq_wstart(rqp);
266	mb_put_uint16le(mbp, attr);
267	if (mtime) {
268		smb_time_local2server(mtime, svtz, &time);
269	} else
270		time = 0;
271	mb_put_uint32le(mbp, time);		/* mtime */
272	mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO);
273	smb_rq_wend(rqp);
274	smb_rq_bstart(rqp);
275	mb_put_uint8(mbp, SMB_DT_ASCII);
276	do {
277		error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
278		if (error)
279			break;
280		mb_put_uint8(mbp, SMB_DT_ASCII);
281		mb_put_uint8(mbp, 0);
282		smb_rq_bend(rqp);
283		error = smb_rq_simple(rqp);
284		SMBERROR("%d\n", error);
285		if (error)
286			break;
287	} while(0);
288	smb_rq_done(rqp);
289	return error;
290}
291
292/*
293 * Note, win95 doesn't support this call.
294 */
295int
296smbfs_smb_setptime2(struct smbnode *np, struct timespec *mtime,
297	struct timespec *atime, int attr, struct smb_cred *scred)
298{
299	struct smb_t2rq *t2p;
300	struct smb_share *ssp = np->n_mount->sm_share;
301	struct smb_vc *vcp = SSTOVC(ssp);
302	struct mbchain *mbp;
303	u_int16_t date, time;
304	int error, tzoff;
305
306	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
307	    scred, &t2p);
308	if (error)
309		return error;
310	mbp = &t2p->t2_tparam;
311	mb_init(mbp);
312	mb_put_uint16le(mbp, SMB_INFO_STANDARD);
313	mb_put_uint32le(mbp, 0);		/* MBZ */
314	error = smbfs_fullpath(mbp, vcp, np, NULL, 0);
315	if (error) {
316		smb_t2_done(t2p);
317		return error;
318	}
319	tzoff = vcp->vc_sopt.sv_tz;
320	mbp = &t2p->t2_tdata;
321	mb_init(mbp);
322	mb_put_uint32le(mbp, 0);		/* creation time */
323	if (atime)
324		smb_time_unix2dos(atime, tzoff, &date, &time, NULL);
325	else
326		time = date = 0;
327	mb_put_uint16le(mbp, date);
328	mb_put_uint16le(mbp, time);
329	if (mtime)
330		smb_time_unix2dos(mtime, tzoff, &date, &time, NULL);
331	else
332		time = date = 0;
333	mb_put_uint16le(mbp, date);
334	mb_put_uint16le(mbp, time);
335	mb_put_uint32le(mbp, 0);		/* file size */
336	mb_put_uint32le(mbp, 0);		/* allocation unit size */
337	mb_put_uint16le(mbp, attr);	/* DOS attr */
338	mb_put_uint32le(mbp, 0);		/* EA size */
339	t2p->t2_maxpcount = 5 * 2;
340	t2p->t2_maxdcount = vcp->vc_txmax;
341	error = smb_t2_request(t2p);
342	smb_t2_done(t2p);
343	return error;
344}
345
346/*
347 * NT level. Specially for win9x
348 */
349int
350smbfs_smb_setpattrNT(struct smbnode *np, u_short attr, struct timespec *mtime,
351	struct timespec *atime, struct smb_cred *scred)
352{
353	struct smb_t2rq *t2p;
354	struct smb_share *ssp = np->n_mount->sm_share;
355	struct smb_vc *vcp = SSTOVC(ssp);
356	struct mbchain *mbp;
357	int64_t tm;
358	int error, tzoff;
359
360	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
361	    scred, &t2p);
362	if (error)
363		return error;
364	mbp = &t2p->t2_tparam;
365	mb_init(mbp);
366	mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFO);
367	mb_put_uint32le(mbp, 0);		/* MBZ */
368	error = smbfs_fullpath(mbp, vcp, np, NULL, 0);
369	if (error) {
370		smb_t2_done(t2p);
371		return error;
372	}
373	tzoff = vcp->vc_sopt.sv_tz;
374	mbp = &t2p->t2_tdata;
375	mb_init(mbp);
376	mb_put_int64le(mbp, 0);		/* creation time */
377	if (atime) {
378		smb_time_local2NT(atime, tzoff, &tm);
379	} else
380		tm = 0;
381	mb_put_int64le(mbp, tm);
382	if (mtime) {
383		smb_time_local2NT(mtime, tzoff, &tm);
384	} else
385		tm = 0;
386	mb_put_int64le(mbp, tm);
387	mb_put_int64le(mbp, tm);		/* change time */
388	mb_put_uint32le(mbp, attr);		/* attr */
389	t2p->t2_maxpcount = 24;
390	t2p->t2_maxdcount = 56;
391	error = smb_t2_request(t2p);
392	smb_t2_done(t2p);
393	return error;
394}
395
396/*
397 * Set file atime and mtime. Doesn't supported by core dialect.
398 */
399int
400smbfs_smb_setftime(struct smbnode *np, struct timespec *mtime,
401	struct timespec *atime, struct smb_cred *scred)
402{
403	struct smb_rq rq, *rqp = &rq;
404	struct smb_share *ssp = np->n_mount->sm_share;
405	struct mbchain *mbp;
406	u_int16_t date, time;
407	int error, tzoff;
408
409	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_SET_INFORMATION2, scred);
410	if (error)
411		return error;
412	tzoff = SSTOVC(ssp)->vc_sopt.sv_tz;
413	smb_rq_getrequest(rqp, &mbp);
414	smb_rq_wstart(rqp);
415	mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
416	mb_put_uint32le(mbp, 0);		/* creation time */
417
418	if (atime)
419		smb_time_unix2dos(atime, tzoff, &date, &time, NULL);
420	else
421		time = date = 0;
422	mb_put_uint16le(mbp, date);
423	mb_put_uint16le(mbp, time);
424	if (mtime)
425		smb_time_unix2dos(mtime, tzoff, &date, &time, NULL);
426	else
427		time = date = 0;
428	mb_put_uint16le(mbp, date);
429	mb_put_uint16le(mbp, time);
430	smb_rq_wend(rqp);
431	smb_rq_bstart(rqp);
432	smb_rq_bend(rqp);
433	error = smb_rq_simple(rqp);
434	SMBSDEBUG("%d\n", error);
435	smb_rq_done(rqp);
436	return error;
437}
438
439/*
440 * Set DOS file attributes.
441 * Looks like this call can be used only if CAP_NT_SMBS bit is on.
442 */
443int
444smbfs_smb_setfattrNT(struct smbnode *np, u_int16_t attr, struct timespec *mtime,
445	struct timespec *atime, struct smb_cred *scred)
446{
447	struct smb_t2rq *t2p;
448	struct smb_share *ssp = np->n_mount->sm_share;
449	struct mbchain *mbp;
450	int64_t tm;
451	int error, svtz;
452
453	error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION,
454	    scred, &t2p);
455	if (error)
456		return error;
457	svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
458	mbp = &t2p->t2_tparam;
459	mb_init(mbp);
460	mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM);
461	mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFO);
462	mb_put_uint32le(mbp, 0);
463	mbp = &t2p->t2_tdata;
464	mb_init(mbp);
465	mb_put_int64le(mbp, 0);		/* creation time */
466	if (atime) {
467		smb_time_local2NT(atime, svtz, &tm);
468	} else
469		tm = 0;
470	mb_put_int64le(mbp, tm);
471	if (mtime) {
472		smb_time_local2NT(mtime, svtz, &tm);
473	} else
474		tm = 0;
475	mb_put_int64le(mbp, tm);
476	mb_put_int64le(mbp, tm);		/* change time */
477	mb_put_uint16le(mbp, attr);
478	mb_put_uint32le(mbp, 0);			/* padding */
479	mb_put_uint16le(mbp, 0);
480	t2p->t2_maxpcount = 2;
481	t2p->t2_maxdcount = 0;
482	error = smb_t2_request(t2p);
483	smb_t2_done(t2p);
484	return error;
485}
486
487
488int
489smbfs_smb_open(struct smbnode *np, int accmode, struct smb_cred *scred)
490{
491	struct smb_rq rq, *rqp = &rq;
492	struct smb_share *ssp = np->n_mount->sm_share;
493	struct mbchain *mbp;
494	struct mdchain *mdp;
495	u_int8_t wc;
496	u_int16_t fid, wattr, grantedmode;
497	int error;
498
499	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_OPEN, scred);
500	if (error)
501		return error;
502	smb_rq_getrequest(rqp, &mbp);
503	smb_rq_wstart(rqp);
504	mb_put_uint16le(mbp, accmode);
505	mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
506	smb_rq_wend(rqp);
507	smb_rq_bstart(rqp);
508	mb_put_uint8(mbp, SMB_DT_ASCII);
509	do {
510		error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
511		if (error)
512			break;
513		smb_rq_bend(rqp);
514		error = smb_rq_simple(rqp);
515		if (error)
516			break;
517		smb_rq_getreply(rqp, &mdp);
518		if (md_get_uint8(mdp, &wc) != 0 || wc != 7) {
519			error = EBADRPC;
520			break;
521		}
522		md_get_uint16(mdp, &fid);
523		md_get_uint16le(mdp, &wattr);
524		md_get_uint32(mdp, NULL);	/* mtime */
525		md_get_uint32(mdp, NULL);	/* fsize */
526		md_get_uint16le(mdp, &grantedmode);
527		/*
528		 * TODO: refresh attributes from this reply
529		 */
530	} while(0);
531	smb_rq_done(rqp);
532	if (error)
533		return error;
534	np->n_fid = fid;
535	np->n_rwstate = grantedmode;
536	return 0;
537}
538
539
540int
541smbfs_smb_close(struct smb_share *ssp, u_int16_t fid, struct timespec *mtime,
542	struct smb_cred *scred)
543{
544	struct smb_rq rq, *rqp = &rq;
545	struct mbchain *mbp;
546	u_long time;
547	int error;
548
549	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CLOSE, scred);
550	if (error)
551		return error;
552	smb_rq_getrequest(rqp, &mbp);
553	smb_rq_wstart(rqp);
554	mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
555	if (mtime) {
556		smb_time_local2server(mtime, SSTOVC(ssp)->vc_sopt.sv_tz, &time);
557	} else
558		time = 0;
559	mb_put_uint32le(mbp, time);
560	smb_rq_wend(rqp);
561	smb_rq_bstart(rqp);
562	smb_rq_bend(rqp);
563	error = smb_rq_simple(rqp);
564	smb_rq_done(rqp);
565	return error;
566}
567
568int
569smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen,
570	struct smb_cred *scred)
571{
572	struct smb_rq rq, *rqp = &rq;
573	struct smb_share *ssp = dnp->n_mount->sm_share;
574	struct mbchain *mbp;
575	struct mdchain *mdp;
576	struct timespec ctime;
577	u_int8_t wc;
578	u_int16_t fid;
579	u_long tm;
580	int error;
581
582	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE, scred);
583	if (error)
584		return error;
585	smb_rq_getrequest(rqp, &mbp);
586	smb_rq_wstart(rqp);
587	mb_put_uint16le(mbp, SMB_FA_ARCHIVE);		/* attributes  */
588	nanotime(&ctime);
589	smb_time_local2server(&ctime, SSTOVC(ssp)->vc_sopt.sv_tz, &tm);
590	mb_put_uint32le(mbp, tm);
591	smb_rq_wend(rqp);
592	smb_rq_bstart(rqp);
593	mb_put_uint8(mbp, SMB_DT_ASCII);
594	error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, nmlen);
595	if (!error) {
596		smb_rq_bend(rqp);
597		error = smb_rq_simple(rqp);
598		if (!error) {
599			smb_rq_getreply(rqp, &mdp);
600			md_get_uint8(mdp, &wc);
601			if (wc == 1)
602				md_get_uint16(mdp, &fid);
603			else
604				error = EBADRPC;
605		}
606	}
607	smb_rq_done(rqp);
608	if (error)
609		return error;
610	smbfs_smb_close(ssp, fid, &ctime, scred);
611	return error;
612}
613
614int
615smbfs_smb_delete(struct smbnode *np, struct smb_cred *scred)
616{
617	struct smb_rq rq, *rqp = &rq;
618	struct smb_share *ssp = np->n_mount->sm_share;
619	struct mbchain *mbp;
620	int error;
621
622	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE, scred);
623	if (error)
624		return error;
625	smb_rq_getrequest(rqp, &mbp);
626	smb_rq_wstart(rqp);
627	mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
628	smb_rq_wend(rqp);
629	smb_rq_bstart(rqp);
630	mb_put_uint8(mbp, SMB_DT_ASCII);
631	error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
632	if (!error) {
633		smb_rq_bend(rqp);
634		error = smb_rq_simple(rqp);
635	}
636	smb_rq_done(rqp);
637	return error;
638}
639
640int
641smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp,
642	const char *tname, int tnmlen, struct smb_cred *scred)
643{
644	struct smb_rq rq, *rqp = &rq;
645	struct smb_share *ssp = src->n_mount->sm_share;
646	struct mbchain *mbp;
647	int error;
648
649	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_RENAME, scred);
650	if (error)
651		return error;
652	smb_rq_getrequest(rqp, &mbp);
653	smb_rq_wstart(rqp);
654	mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
655	smb_rq_wend(rqp);
656	smb_rq_bstart(rqp);
657	mb_put_uint8(mbp, SMB_DT_ASCII);
658	do {
659		error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0);
660		if (error)
661			break;
662		mb_put_uint8(mbp, SMB_DT_ASCII);
663		error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen);
664		if (error)
665			break;
666		smb_rq_bend(rqp);
667		error = smb_rq_simple(rqp);
668	} while(0);
669	smb_rq_done(rqp);
670	return error;
671}
672
673int
674smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp,
675	const char *tname, int tnmlen, u_int16_t flags, struct smb_cred *scred)
676{
677	struct smb_rq rq, *rqp = &rq;
678	struct smb_share *ssp = src->n_mount->sm_share;
679	struct mbchain *mbp;
680	int error;
681
682	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_MOVE, scred);
683	if (error)
684		return error;
685	smb_rq_getrequest(rqp, &mbp);
686	smb_rq_wstart(rqp);
687	mb_put_uint16le(mbp, SMB_TID_UNKNOWN);
688	mb_put_uint16le(mbp, 0x20);	/* delete target file */
689	mb_put_uint16le(mbp, flags);
690	smb_rq_wend(rqp);
691	smb_rq_bstart(rqp);
692	mb_put_uint8(mbp, SMB_DT_ASCII);
693	do {
694		error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0);
695		if (error)
696			break;
697		mb_put_uint8(mbp, SMB_DT_ASCII);
698		error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen);
699		if (error)
700			break;
701		smb_rq_bend(rqp);
702		error = smb_rq_simple(rqp);
703	} while(0);
704	smb_rq_done(rqp);
705	return error;
706}
707
708int
709smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len,
710	struct smb_cred *scred)
711{
712	struct smb_rq rq, *rqp = &rq;
713	struct smb_share *ssp = dnp->n_mount->sm_share;
714	struct mbchain *mbp;
715	int error;
716
717	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CREATE_DIRECTORY, scred);
718	if (error)
719		return error;
720	smb_rq_getrequest(rqp, &mbp);
721	smb_rq_wstart(rqp);
722	smb_rq_wend(rqp);
723	smb_rq_bstart(rqp);
724	mb_put_uint8(mbp, SMB_DT_ASCII);
725	error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, len);
726	if (!error) {
727		smb_rq_bend(rqp);
728		error = smb_rq_simple(rqp);
729	}
730	smb_rq_done(rqp);
731	return error;
732}
733
734int
735smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scred)
736{
737	struct smb_rq rq, *rqp = &rq;
738	struct smb_share *ssp = np->n_mount->sm_share;
739	struct mbchain *mbp;
740	int error;
741
742	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_DELETE_DIRECTORY, scred);
743	if (error)
744		return error;
745	smb_rq_getrequest(rqp, &mbp);
746	smb_rq_wstart(rqp);
747	smb_rq_wend(rqp);
748	smb_rq_bstart(rqp);
749	mb_put_uint8(mbp, SMB_DT_ASCII);
750	error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
751	if (!error) {
752		smb_rq_bend(rqp);
753		error = smb_rq_simple(rqp);
754	}
755	smb_rq_done(rqp);
756	return error;
757}
758
759static int
760smbfs_smb_search(struct smbfs_fctx *ctx)
761{
762	struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
763	struct smb_rq *rqp;
764	struct mbchain *mbp;
765	struct mdchain *mdp;
766	u_int8_t wc, bt;
767	u_int16_t ec, dlen, bc;
768	int maxent, error, iseof = 0;
769
770	maxent = min(ctx->f_left, (vcp->vc_txmax - SMB_HDRLEN - 3) / SMB_DENTRYLEN);
771	if (ctx->f_rq) {
772		smb_rq_done(ctx->f_rq);
773		ctx->f_rq = NULL;
774	}
775	error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_SEARCH, ctx->f_scred, &rqp);
776	if (error)
777		return error;
778	ctx->f_rq = rqp;
779	smb_rq_getrequest(rqp, &mbp);
780	smb_rq_wstart(rqp);
781	mb_put_uint16le(mbp, maxent);	/* max entries to return */
782	mb_put_uint16le(mbp, ctx->f_attrmask);
783	smb_rq_wend(rqp);
784	smb_rq_bstart(rqp);
785	mb_put_uint8(mbp, SMB_DT_ASCII);	/* buffer format */
786	if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
787		error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen);
788		if (error)
789			return error;
790		mb_put_uint8(mbp, SMB_DT_VARIABLE);
791		mb_put_uint16le(mbp, 0);	/* context length */
792		ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
793	} else {
794		mb_put_uint8(mbp, 0);	/* file name length */
795		mb_put_uint8(mbp, SMB_DT_VARIABLE);
796		mb_put_uint16le(mbp, SMB_SKEYLEN);
797		mb_put_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM);
798	}
799	smb_rq_bend(rqp);
800	error = smb_rq_simple(rqp);
801	if (error) {
802		if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnofiles) {
803			error = 0;
804			iseof = 1;
805			ctx->f_flags |= SMBFS_RDD_EOF;
806		} else
807			return error;
808	}
809	smb_rq_getreply(rqp, &mdp);
810	md_get_uint8(mdp, &wc);
811	if (wc != 1)
812		return iseof ? ENOENT : EBADRPC;
813	md_get_uint16le(mdp, &ec);
814	if (ec == 0)
815		return ENOENT;
816	ctx->f_ecnt = ec;
817	md_get_uint16le(mdp, &bc);
818	if (bc < 3)
819		return EBADRPC;
820	bc -= 3;
821	md_get_uint8(mdp, &bt);
822	if (bt != SMB_DT_VARIABLE)
823		return EBADRPC;
824	md_get_uint16le(mdp, &dlen);
825	if (dlen != bc || dlen % SMB_DENTRYLEN != 0)
826		return EBADRPC;
827	return 0;
828}
829
830static int
831smbfs_findopenLM1(struct smbfs_fctx *ctx, struct smbnode *dnp,
832	const char *wildcard, int wclen, int attr, struct smb_cred *scred)
833{
834	ctx->f_attrmask = attr;
835	if (wildcard) {
836		if (wclen == 1 && wildcard[0] == '*') {
837			ctx->f_wildcard = "*.*";
838			ctx->f_wclen = 3;
839		} else {
840			ctx->f_wildcard = wildcard;
841			ctx->f_wclen = wclen;
842		}
843	} else {
844		ctx->f_wildcard = NULL;
845		ctx->f_wclen = 0;
846	}
847	ctx->f_name = ctx->f_fname;
848	return 0;
849}
850
851static int
852smbfs_findnextLM1(struct smbfs_fctx *ctx, int limit)
853{
854	struct mdchain *mbp;
855	struct smb_rq *rqp;
856	char *cp;
857	u_int8_t battr;
858	u_int16_t date, time;
859	u_int32_t size;
860	int error;
861
862	if (ctx->f_ecnt == 0) {
863		if (ctx->f_flags & SMBFS_RDD_EOF)
864			return ENOENT;
865		ctx->f_left = ctx->f_limit = limit;
866		error = smbfs_smb_search(ctx);
867		if (error)
868			return error;
869	}
870	rqp = ctx->f_rq;
871	smb_rq_getreply(rqp, &mbp);
872	md_get_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM);
873	md_get_uint8(mbp, &battr);
874	md_get_uint16le(mbp, &time);
875	md_get_uint16le(mbp, &date);
876	md_get_uint32le(mbp, &size);
877	cp = ctx->f_name;
878	md_get_mem(mbp, cp, sizeof(ctx->f_fname), MB_MSYSTEM);
879	cp[sizeof(ctx->f_fname) - 1] = 0;
880	cp += strlen(cp) - 1;
881	while (*cp == ' ' && cp >= ctx->f_name)
882		*cp-- = 0;
883	ctx->f_attr.fa_attr = battr;
884	smb_dos2unixtime(date, time, 0, rqp->sr_vc->vc_sopt.sv_tz,
885	    &ctx->f_attr.fa_mtime);
886	ctx->f_attr.fa_size = size;
887	ctx->f_nmlen = strlen(ctx->f_name);
888	ctx->f_ecnt--;
889	ctx->f_left--;
890	return 0;
891}
892
893static int
894smbfs_findcloseLM1(struct smbfs_fctx *ctx)
895{
896	if (ctx->f_rq)
897		smb_rq_done(ctx->f_rq);
898	return 0;
899}
900
901/*
902 * TRANS2_FIND_FIRST2/NEXT2, used for NT LM12 dialect
903 */
904static int
905smbfs_smb_trans2find2(struct smbfs_fctx *ctx)
906{
907	struct smb_t2rq *t2p;
908	struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
909	struct mbchain *mbp;
910	struct mdchain *mdp;
911	u_int16_t tw, flags;
912	int error;
913
914	if (ctx->f_t2) {
915		smb_t2_done(ctx->f_t2);
916		ctx->f_t2 = NULL;
917	}
918	ctx->f_flags &= ~SMBFS_RDD_GOTRNAME;
919	flags = 8 | 2;			/* <resume> | <close if EOS> */
920	if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
921		flags |= 1;		/* close search after this request */
922		ctx->f_flags |= SMBFS_RDD_NOCLOSE;
923	}
924	if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
925		error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_FIRST2,
926		    ctx->f_scred, &t2p);
927		if (error)
928			return error;
929		ctx->f_t2 = t2p;
930		mbp = &t2p->t2_tparam;
931		mb_init(mbp);
932		mb_put_uint16le(mbp, ctx->f_attrmask);
933		mb_put_uint16le(mbp, ctx->f_limit);
934		mb_put_uint16le(mbp, flags);
935		mb_put_uint16le(mbp, ctx->f_infolevel);
936		mb_put_uint32le(mbp, 0);
937		error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen);
938		if (error)
939			return error;
940	} else	{
941		error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_NEXT2,
942		    ctx->f_scred, &t2p);
943		if (error)
944			return error;
945		ctx->f_t2 = t2p;
946		mbp = &t2p->t2_tparam;
947		mb_init(mbp);
948		mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM);
949		mb_put_uint16le(mbp, ctx->f_limit);
950		mb_put_uint16le(mbp, ctx->f_infolevel);
951		mb_put_uint32le(mbp, 0);		/* resume key */
952		mb_put_uint16le(mbp, flags);
953		if (ctx->f_rname)
954			mb_put_mem(mbp, ctx->f_rname, strlen(ctx->f_rname) + 1, MB_MSYSTEM);
955		else
956			mb_put_uint8(mbp, 0);	/* resume file name */
957#if 0
958	struct timeval tv;
959	tv.tv_sec = 0;
960	tv.tv_usec = 200 * 1000;	/* 200ms */
961		if (vcp->vc_flags & SMBC_WIN95) {
962			/*
963			 * some implementations suggests to sleep here
964			 * for 200ms, due to the bug in the Win95.
965			 * I've didn't notice any problem, but put code
966			 * for it.
967			 */
968			 tsleep(&flags, PVFS, "fix95", tvtohz(&tv));
969		}
970#endif
971	}
972	t2p->t2_maxpcount = 5 * 2;
973	t2p->t2_maxdcount = vcp->vc_txmax;
974	error = smb_t2_request(t2p);
975	if (error)
976		return error;
977	mdp = &t2p->t2_rparam;
978	if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
979		if ((error = md_get_uint16(mdp, &ctx->f_Sid)) != 0)
980			return error;
981		ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
982	}
983	if ((error = md_get_uint16le(mdp, &tw)) != 0)
984		return error;
985	ctx->f_ecnt = tw;
986	if ((error = md_get_uint16le(mdp, &tw)) != 0)
987		return error;
988	if (tw)
989		ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE;
990	if ((error = md_get_uint16le(mdp, &tw)) != 0)
991		return error;
992	if ((error = md_get_uint16le(mdp, &tw)) != 0)
993		return error;
994	if (ctx->f_ecnt == 0)
995		return ENOENT;
996	ctx->f_rnameofs = tw;
997	mdp = &t2p->t2_rdata;
998	if (mdp->md_top == NULL) {
999		printf("bug: ecnt = %d, but data is NULL (please report)\n", ctx->f_ecnt);
1000		return ENOENT;
1001	}
1002	if (mdp->md_top->m_len == 0) {
1003		printf("bug: ecnt = %d, but m_len = 0 and m_next = %p (please report)\n", ctx->f_ecnt,mbp->mb_top->m_next);
1004		return ENOENT;
1005	}
1006	ctx->f_eofs = 0;
1007	return 0;
1008}
1009
1010static int
1011smbfs_smb_findclose2(struct smbfs_fctx *ctx)
1012{
1013	struct smb_rq rq, *rqp = &rq;
1014	struct mbchain *mbp;
1015	int error;
1016
1017	error = smb_rq_init(rqp, SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2, ctx->f_scred);
1018	if (error)
1019		return error;
1020	smb_rq_getrequest(rqp, &mbp);
1021	smb_rq_wstart(rqp);
1022	mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM);
1023	smb_rq_wend(rqp);
1024	smb_rq_bstart(rqp);
1025	smb_rq_bend(rqp);
1026	error = smb_rq_simple(rqp);
1027	smb_rq_done(rqp);
1028	return error;
1029}
1030
1031static int
1032smbfs_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp,
1033	const char *wildcard, int wclen, int attr, struct smb_cred *scred)
1034{
1035	ctx->f_name = malloc(SMB_MAXFNAMELEN, M_SMBFSDATA, M_WAITOK);
1036	if (ctx->f_name == NULL)
1037		return ENOMEM;
1038	ctx->f_infolevel = SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_NTLM0_12 ?
1039	    SMB_INFO_STANDARD : SMB_FIND_FILE_DIRECTORY_INFO;
1040	ctx->f_attrmask = attr;
1041	ctx->f_wildcard = wildcard;
1042	ctx->f_wclen = wclen;
1043	return 0;
1044}
1045
1046static int
1047smbfs_findnextLM2(struct smbfs_fctx *ctx, int limit)
1048{
1049	struct mdchain *mbp;
1050	struct smb_t2rq *t2p;
1051	char *cp;
1052	u_int8_t tb;
1053	u_int16_t date, time, wattr;
1054	u_int32_t size, next, dattr;
1055	int64_t lint;
1056	int error, svtz, cnt, fxsz, nmlen, recsz;
1057
1058	if (ctx->f_ecnt == 0) {
1059		if (ctx->f_flags & SMBFS_RDD_EOF)
1060			return ENOENT;
1061		ctx->f_left = ctx->f_limit = limit;
1062		error = smbfs_smb_trans2find2(ctx);
1063		if (error)
1064			return error;
1065	}
1066	t2p = ctx->f_t2;
1067	mbp = &t2p->t2_rdata;
1068	svtz = SSTOVC(ctx->f_ssp)->vc_sopt.sv_tz;
1069	switch (ctx->f_infolevel) {
1070	    case SMB_INFO_STANDARD:
1071		next = 0;
1072		fxsz = 0;
1073		md_get_uint16le(mbp, &date);
1074		md_get_uint16le(mbp, &time);	/* creation time */
1075		md_get_uint16le(mbp, &date);
1076		md_get_uint16le(mbp, &time);	/* access time */
1077		smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_atime);
1078		md_get_uint16le(mbp, &date);
1079		md_get_uint16le(mbp, &time);	/* access time */
1080		smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_mtime);
1081		md_get_uint32le(mbp, &size);
1082		ctx->f_attr.fa_size = size;
1083		md_get_uint32(mbp, NULL);	/* allocation size */
1084		md_get_uint16le(mbp, &wattr);
1085		ctx->f_attr.fa_attr = wattr;
1086		md_get_uint8(mbp, &tb);
1087		size = nmlen = tb;
1088		fxsz = 23;
1089		recsz = next = 24 + nmlen;	/* docs misses zero byte at end */
1090		break;
1091	    case SMB_FIND_FILE_DIRECTORY_INFO:
1092		md_get_uint32le(mbp, &next);
1093		md_get_uint32(mbp, NULL);	/* file index */
1094		md_get_int64(mbp, NULL);	/* creation time */
1095		md_get_int64le(mbp, &lint);
1096		smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_atime);
1097		md_get_int64le(mbp, &lint);
1098		smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_mtime);
1099		md_get_int64le(mbp, &lint);
1100		smb_time_NT2local(lint, svtz, &ctx->f_attr.fa_ctime);
1101		md_get_int64le(mbp, &lint);	/* file size */
1102		ctx->f_attr.fa_size = lint;
1103		md_get_int64(mbp, NULL);	/* real size (should use) */
1104		md_get_uint32le(mbp, &dattr);	/* EA */
1105		ctx->f_attr.fa_attr = dattr;
1106		md_get_uint32le(mbp, &size);	/* name len */
1107		fxsz = 64;
1108		recsz = next ? next : fxsz + size;
1109		break;
1110	    default:
1111		SMBERROR("unexpected info level %d\n", ctx->f_infolevel);
1112		return EINVAL;
1113	}
1114	nmlen = min(size, SMB_MAXFNAMELEN);
1115	cp = ctx->f_name;
1116	error = md_get_mem(mbp, cp, nmlen, MB_MSYSTEM);
1117	if (error)
1118		return error;
1119	if (next) {
1120		cnt = next - nmlen - fxsz;
1121		if (cnt > 0)
1122			md_get_mem(mbp, NULL, cnt, MB_MSYSTEM);
1123		else if (cnt < 0) {
1124			SMBERROR("out of sync\n");
1125			return EBADRPC;
1126		}
1127	}
1128	if (nmlen && cp[nmlen - 1] == 0)
1129		nmlen--;
1130	if (nmlen == 0)
1131		return EBADRPC;
1132
1133	next = ctx->f_eofs + recsz;
1134	if (ctx->f_rnameofs && (ctx->f_flags & SMBFS_RDD_GOTRNAME) == 0 &&
1135	    (ctx->f_rnameofs >= ctx->f_eofs && ctx->f_rnameofs < next)) {
1136		/*
1137		 * Server needs a resume filename.
1138		 */
1139		if (ctx->f_rnamelen <= nmlen) {
1140			if (ctx->f_rname)
1141				free(ctx->f_rname, M_SMBFSDATA);
1142			ctx->f_rname = malloc(nmlen + 1, M_SMBFSDATA, M_WAITOK);
1143			ctx->f_rnamelen = nmlen;
1144		}
1145		bcopy(ctx->f_name, ctx->f_rname, nmlen);
1146		ctx->f_rname[nmlen] = 0;
1147		ctx->f_flags |= SMBFS_RDD_GOTRNAME;
1148	}
1149	ctx->f_nmlen = nmlen;
1150	ctx->f_eofs = next;
1151	ctx->f_ecnt--;
1152	ctx->f_left--;
1153	return 0;
1154}
1155
1156static int
1157smbfs_findcloseLM2(struct smbfs_fctx *ctx)
1158{
1159	if (ctx->f_name)
1160		free(ctx->f_name, M_SMBFSDATA);
1161	if (ctx->f_t2)
1162		smb_t2_done(ctx->f_t2);
1163	if ((ctx->f_flags & SMBFS_RDD_NOCLOSE) == 0)
1164		smbfs_smb_findclose2(ctx);
1165	return 0;
1166}
1167
1168int
1169smbfs_findopen(struct smbnode *dnp, const char *wildcard, int wclen, int attr,
1170	struct smb_cred *scred, struct smbfs_fctx **ctxpp)
1171{
1172	struct smbfs_fctx *ctx;
1173	int error;
1174
1175	ctx = malloc(sizeof(*ctx), M_SMBFSDATA, M_WAITOK);
1176	if (ctx == NULL)
1177		return ENOMEM;
1178	bzero(ctx, sizeof(*ctx));
1179	ctx->f_ssp = dnp->n_mount->sm_share;
1180	ctx->f_dnp = dnp;
1181	ctx->f_flags = SMBFS_RDD_FINDFIRST;
1182	ctx->f_scred = scred;
1183	if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0 ||
1184	    (dnp->n_mount->sm_args.flags & SMBFS_MOUNT_NO_LONG)) {
1185		ctx->f_flags |= SMBFS_RDD_USESEARCH;
1186		error = smbfs_findopenLM1(ctx, dnp, wildcard, wclen, attr, scred);
1187	} else
1188		error = smbfs_findopenLM2(ctx, dnp, wildcard, wclen, attr, scred);
1189	if (error)
1190		smbfs_findclose(ctx, scred);
1191	else
1192		*ctxpp = ctx;
1193	return error;
1194}
1195
1196int
1197smbfs_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scred)
1198{
1199	int error;
1200
1201	if (limit == 0)
1202		limit = 1000000;
1203	else if (limit > 1)
1204		limit *= 4;	/* imperical */
1205	ctx->f_scred = scred;
1206	for (;;) {
1207		if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
1208			error = smbfs_findnextLM1(ctx, limit);
1209		} else
1210			error = smbfs_findnextLM2(ctx, limit);
1211		if (error)
1212			return error;
1213		if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') ||
1214		    (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' &&
1215		     ctx->f_name[1] == '.'))
1216			continue;
1217		break;
1218	}
1219	smbfs_fname_tolocal(SSTOVC(ctx->f_ssp), ctx->f_name, ctx->f_nmlen,
1220	    ctx->f_dnp->n_mount->sm_caseopt);
1221	ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name, ctx->f_nmlen);
1222	return 0;
1223}
1224
1225int
1226smbfs_findclose(struct smbfs_fctx *ctx, struct smb_cred *scred)
1227{
1228	ctx->f_scred = scred;
1229	if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
1230		smbfs_findcloseLM1(ctx);
1231	} else
1232		smbfs_findcloseLM2(ctx);
1233	if (ctx->f_rname)
1234		free(ctx->f_rname, M_SMBFSDATA);
1235	free(ctx, M_SMBFSDATA);
1236	return 0;
1237}
1238
1239int
1240smbfs_smb_lookup(struct smbnode *dnp, const char *name, int nmlen,
1241	struct smbfattr *fap, struct smb_cred *scred)
1242{
1243	struct smbfs_fctx *ctx;
1244	int error;
1245
1246	if (dnp == NULL || (dnp->n_ino == 2 && name == NULL)) {
1247		bzero(fap, sizeof(*fap));
1248		fap->fa_attr = SMB_FA_DIR;
1249		fap->fa_ino = 2;
1250		return 0;
1251	}
1252	if (nmlen == 1 && name[0] == '.') {
1253		error = smbfs_smb_lookup(dnp, NULL, 0, fap, scred);
1254		return error;
1255	} else if (nmlen == 2 && name[0] == '.' && name[1] == '.') {
1256		error = smbfs_smb_lookup(dnp->n_parent, NULL, 0, fap, scred);
1257		printf("%s: knows NOTHING about '..'\n", __FUNCTION__);
1258		return error;
1259	}
1260	error = smbfs_findopen(dnp, name, nmlen,
1261	    SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, scred, &ctx);
1262	if (error)
1263		return error;
1264	ctx->f_flags |= SMBFS_RDD_FINDSINGLE;
1265	error = smbfs_findnext(ctx, 1, scred);
1266	if (error == 0) {
1267		*fap = ctx->f_attr;
1268		if (name == NULL)
1269			fap->fa_ino = dnp->n_ino;
1270	}
1271	smbfs_findclose(ctx, scred);
1272	return error;
1273}
1274