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