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