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