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