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