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