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