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