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