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