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