1/* $NetBSD: smbfs_smb.c,v 1.47 2014/12/21 10:48:53 hannken Exp $ */
2
3/*-
4 * Copyright (c) 2003 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jaromir Dolecek.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Copyright (c) 2000-2001 Boris Popov
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by Boris Popov.
47 * 4. Neither the name of the author nor the names of any co-contributors
48 * may be used to endorse or promote products derived from this software
49 * without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 *
63 * FreeBSD: src/sys/fs/smbfs/smbfs_smb.c,v 1.3 2001/12/10 08:09:46 obrien Exp
64 */
65
66#include <sys/cdefs.h>
67__KERNEL_RCSID(0, "$NetBSD: smbfs_smb.c,v 1.47 2014/12/21 10:48:53 hannken Exp $");
68
69#include <sys/param.h>
70#include <sys/systm.h>
71#include <sys/kernel.h>
72#include <sys/malloc.h>
73#include <sys/proc.h>
74#include <sys/lock.h>
75#include <sys/vnode.h>
76#include <sys/mbuf.h>
77#include <sys/mount.h>
78
79#ifdef USE_MD5_HASH
80#include <sys/md5.h>
81#endif
82
83#include <netsmb/smb.h>
84#include <netsmb/smb_subr.h>
85#include <netsmb/smb_rq.h>
86#include <netsmb/smb_conn.h>
87
88#include <fs/smbfs/smbfs.h>
89#include <fs/smbfs/smbfs_node.h>
90#include <fs/smbfs/smbfs_subr.h>
91
92/*
93 * Lack of inode numbers leads us to the problem of generating them.
94 * Partially this problem can be solved by having a dir/file cache
95 * with inode numbers generated from the incremented by one counter.
96 * However this way will require too much kernel memory, gives all
97 * sorts of locking and consistency problems, not to mentinon counter overflows.
98 * So, I'm decided to use a hash function to generate pseudo random (and unique)
99 * inode numbers.
100 */
101static long
102smbfs_getino(struct smbnode *dnp, const char *name, int nmlen)
103{
104#ifdef USE_MD5_HASH
105 MD5_CTX md5;
106 u_int32_t state[4];
107 long ino;
108 int i;
109
110 MD5Init(&md5);
111 MD5Update(&md5, name, nmlen);
112 MD5Final((u_char *)state, &md5);
113 for (i = 0, ino = 0; i < 4; i++)
114 ino += state[i];
115 return dnp->n_ino + ino;
116#endif
117 u_int32_t ino;
118
119 ino = dnp->n_ino + hash32_strn(name, nmlen, HASH32_STR_INIT);
120 if (ino <= 2)
121 ino += 3;
122 return ino;
123}
124
125static int
126smbfs_smb_lockandx(struct smbnode *np, int op, void *id, off_t start, off_t end,
127 struct smb_cred *scred)
128{
129 struct smb_share *ssp = np->n_mount->sm_share;
130 struct smb_rq *rqp;
131 struct mbchain *mbp;
132 u_char ltype = 0;
133 int error;
134
135 if (op == SMB_LOCK_SHARED)
136 ltype |= SMB_LOCKING_ANDX_SHARED_LOCK;
137 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_LOCKING_ANDX, scred, &rqp);
138 if (error)
139 return error;
140 smb_rq_getrequest(rqp, &mbp);
141 smb_rq_wstart(rqp);
142 mb_put_uint8(mbp, 0xff); /* secondary command */
143 mb_put_uint8(mbp, 0); /* MBZ */
144 mb_put_uint16le(mbp, 0);
145 mb_put_mem(mbp, (void *)&np->n_fid, 2, MB_MSYSTEM);
146 mb_put_uint8(mbp, ltype); /* locktype */
147 mb_put_uint8(mbp, 0); /* oplocklevel - 0 seems is NO_OPLOCK */
148 mb_put_uint32le(mbp, 0); /* timeout - break immediately */
149 mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 1 : 0);
150 mb_put_uint16le(mbp, op == SMB_LOCK_RELEASE ? 0 : 1);
151 smb_rq_wend(rqp);
152 smb_rq_bstart(rqp);
153 mb_put_uint16le(mbp, (((long) id) & 0xffff)); /* process ID */
154 mb_put_uint32le(mbp, start);
155 mb_put_uint32le(mbp, end - start);
156 smb_rq_bend(rqp);
157 error = smb_rq_simple(rqp);
158 smb_rq_done(rqp);
159 return error;
160}
161
162int
163smbfs_smb_lock(struct smbnode *np, int op, void *id,
164 off_t start, off_t end, struct smb_cred *scred)
165{
166 struct smb_share *ssp = np->n_mount->sm_share;
167
168 if (SMB_DIALECT(SSTOVC(ssp)) < SMB_DIALECT_LANMAN1_0)
169 /*
170 * TODO: use LOCK_BYTE_RANGE here.
171 */
172 return EINVAL;
173 else
174 return smbfs_smb_lockandx(np, op, id, start, end, scred);
175}
176
177int
178smbfs_smb_statvfs(struct smb_share *ssp, struct statvfs *sbp,
179 struct smb_cred *scred)
180{
181 unsigned long bsize; /* Block (allocation unit) size */
182 unsigned long bavail, bfree;
183
184 /*
185 * The SMB request work with notion of sector size and
186 * allocation units. Allocation unit is what 'block'
187 * means in Unix context, sector size might be HW sector size.
188 */
189
190 if (SMB_DIALECT(SSTOVC(ssp)) >= SMB_DIALECT_LANMAN2_0) {
191 struct smb_t2rq *t2p;
192 struct mbchain *mbp;
193 struct mdchain *mdp;
194 u_int16_t secsz;
195 u_int32_t units, bpu, funits;
196 int error;
197
198 error = smb_t2_alloc(SSTOCP(ssp),
199 SMB_TRANS2_QUERY_FS_INFORMATION, scred, &t2p);
200 if (error)
201 return error;
202 mbp = &t2p->t2_tparam;
203 mb_init(mbp);
204 mb_put_uint16le(mbp, SMB_INFO_ALLOCATION);
205 t2p->t2_maxpcount = 4;
206 t2p->t2_maxdcount = 4 * 4 + 2;
207 error = smb_t2_request(t2p);
208 if (error) {
209 smb_t2_done(t2p);
210 return error;
211 }
212 mdp = &t2p->t2_rdata;
213 md_get_uint32(mdp, NULL); /* fs id */
214 md_get_uint32le(mdp, &bpu); /* Number of sectors per unit */
215 md_get_uint32le(mdp, &units); /* Total number of units */
216 md_get_uint32le(mdp, &funits); /* Number of available units */
217 md_get_uint16le(mdp, &secsz); /* Number of bytes per sector */
218 smb_t2_done(t2p);
219
220 bsize = bpu * secsz;
221 bavail = units;
222 bfree = funits;
223 } else {
224 struct smb_rq *rqp;
225 struct mdchain *mdp;
226 u_int16_t units, bpu, secsz, funits;
227 int error;
228
229 error = smb_rq_alloc(SSTOCP(ssp),
230 SMB_COM_QUERY_INFORMATION_DISK, 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); /* Total units per server */
244 md_get_uint16le(mdp, &bpu); /* Blocks per allocation unit */
245 md_get_uint16le(mdp, &secsz); /* Block size (in bytes) */
246 md_get_uint16le(mdp, &funits); /* Number of free units */
247 smb_rq_done(rqp);
248
249 bsize = bpu * secsz;
250 bavail = units;
251 bfree = funits;
252 }
253
254 sbp->f_bsize = bsize; /* fundamental file system block size */
255 sbp->f_frsize = bsize; /* fundamental file system frag size */
256 sbp->f_iosize = bsize; /* optimal I/O size */
257 sbp->f_blocks = bavail; /* total data blocks in file system */
258 sbp->f_bfree = bfree; /* free blocks in fs */
259 sbp->f_bresvd = 0; /* reserved blocks in fs */
260 sbp->f_bavail= bfree; /* free blocks avail to non-superuser */
261 sbp->f_files = 0xffff; /* total file nodes in file system */
262 sbp->f_ffree = 0xffff; /* free file nodes to non-superuser */
263 sbp->f_favail = 0xffff; /* free file nodes in fs */
264 sbp->f_fresvd = 0; /* reserved file nodes in fs */
265 return 0;
266}
267
268static int
269smbfs_smb_seteof(struct smbnode *np, int64_t newsize, struct smb_cred *scred)
270{
271 struct smb_t2rq *t2p;
272 struct smb_share *ssp = np->n_mount->sm_share;
273 struct mbchain *mbp;
274 int error;
275
276 error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION,
277 scred, &t2p);
278 if (error)
279 return error;
280 mbp = &t2p->t2_tparam;
281 mb_init(mbp);
282 mb_put_mem(mbp, (void *)&np->n_fid, 2, MB_MSYSTEM);
283 mb_put_uint16le(mbp, SMB_SET_FILE_END_OF_FILE_INFO);
284 mb_put_uint32le(mbp, 0);
285 mbp = &t2p->t2_tdata;
286 mb_init(mbp);
287 mb_put_int64le(mbp, newsize);
288 mb_put_uint32le(mbp, 0); /* padding */
289 mb_put_uint16le(mbp, 0);
290 t2p->t2_maxpcount = 2;
291 t2p->t2_maxdcount = 0;
292 error = smb_t2_request(t2p);
293 smb_t2_done(t2p);
294 return error;
295}
296
297int
298smbfs_smb_setfsize(struct smbnode *np, u_quad_t newsize,
299 struct smb_cred *scred)
300{
301 struct smb_share *ssp = np->n_mount->sm_share;
302 struct smb_rq *rqp;
303 struct mbchain *mbp;
304 int error;
305
306 if (newsize >= (1LL << 32)) {
307 if (!(SMB_CAPS(SSTOVC(ssp)) & SMB_CAP_LARGE_FILES))
308 return EFBIG;
309 return smbfs_smb_seteof(np, (int64_t)newsize, scred);
310 }
311
312 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
313 if (error)
314 return error;
315 smb_rq_getrequest(rqp, &mbp);
316 smb_rq_wstart(rqp);
317 mb_put_mem(mbp, (void *)&np->n_fid, 2, MB_MSYSTEM);
318 mb_put_uint16le(mbp, 0);
319 mb_put_uint32le(mbp, newsize);
320 mb_put_uint16le(mbp, 0);
321 smb_rq_wend(rqp);
322 smb_rq_bstart(rqp);
323 mb_put_uint8(mbp, SMB_DT_DATA);
324 mb_put_uint16le(mbp, 0);
325 smb_rq_bend(rqp);
326 error = smb_rq_simple(rqp);
327 smb_rq_done(rqp);
328 return error;
329}
330
331
332/*
333 * Set DOS file attributes. mtime should be NULL for dialects above lm10
334 */
335int
336smbfs_smb_setpattr(struct smbnode *np, u_int16_t attr, struct timespec *mtime,
337 struct smb_cred *scred)
338{
339 struct smb_rq *rqp;
340 struct smb_share *ssp = np->n_mount->sm_share;
341 struct mbchain *mbp;
342 u_long xtime;
343 int error, svtz;
344
345 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_SET_INFORMATION, scred, &rqp);
346 if (error)
347 return error;
348 svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
349 smb_rq_getrequest(rqp, &mbp);
350 smb_rq_wstart(rqp);
351 mb_put_uint16le(mbp, attr);
352 if (mtime) {
353 smb_time_local2server(mtime, svtz, &xtime);
354 } else
355 xtime = 0;
356 mb_put_uint32le(mbp, xtime); /* mtime */
357 mb_put_mem(mbp, NULL, 5 * 2, MB_MZERO);
358 smb_rq_wend(rqp);
359 smb_rq_bstart(rqp);
360 mb_put_uint8(mbp, SMB_DT_ASCII);
361 do {
362 error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
363 if (error)
364 break;
365 mb_put_uint8(mbp, SMB_DT_ASCII);
366 mb_put_uint8(mbp, 0);
367 smb_rq_bend(rqp);
368 error = smb_rq_simple(rqp);
369 if (error)
370 break;
371 } while(0);
372 smb_rq_done(rqp);
373 return error;
374}
375
376/*
377 * Note, win95 doesn't support this call.
378 */
379int
380smbfs_smb_setptime2(struct smbnode *np, struct timespec *mtime,
381 struct timespec *atime, int attr, struct smb_cred *scred)
382{
383 struct smb_t2rq *t2p;
384 struct smb_share *ssp = np->n_mount->sm_share;
385 struct smb_vc *vcp = SSTOVC(ssp);
386 struct mbchain *mbp;
387 u_int16_t xdate, xtime;
388 int error, tzoff;
389
390 error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
391 scred, &t2p);
392 if (error)
393 return error;
394 mbp = &t2p->t2_tparam;
395 mb_init(mbp);
396 mb_put_uint16le(mbp, SMB_INFO_STANDARD);
397 mb_put_uint32le(mbp, 0); /* MBZ */
398 error = smbfs_fullpath(mbp, vcp, np, NULL, 0);
399 if (error) {
400 smb_t2_done(t2p);
401 return error;
402 }
403 tzoff = vcp->vc_sopt.sv_tz;
404 mbp = &t2p->t2_tdata;
405 mb_init(mbp);
406 mb_put_uint32le(mbp, 0); /* creation time */
407 if (atime)
408 smb_time_unix2dos(atime, tzoff, &xdate, &xtime, NULL);
409 else
410 xtime = xdate = 0;
411 mb_put_uint16le(mbp, xdate);
412 mb_put_uint16le(mbp, xtime);
413 if (mtime)
414 smb_time_unix2dos(mtime, tzoff, &xdate, &xtime, NULL);
415 else
416 xtime = xdate = 0;
417 mb_put_uint16le(mbp, xdate);
418 mb_put_uint16le(mbp, xtime);
419 mb_put_uint32le(mbp, 0); /* file size */
420 mb_put_uint32le(mbp, 0); /* allocation unit size */
421 mb_put_uint16le(mbp, attr); /* DOS attr */
422 mb_put_uint32le(mbp, 0); /* EA size */
423 t2p->t2_maxpcount = 5 * 2;
424 t2p->t2_maxdcount = vcp->vc_txmax;
425 error = smb_t2_request(t2p);
426 smb_t2_done(t2p);
427 return error;
428}
429
430/*
431 * NT level. Specially for win9x
432 */
433int
434smbfs_smb_setpattrNT(struct smbnode *np, u_short attr, struct timespec *mtime,
435 struct timespec *atime, struct smb_cred *scred)
436{
437 struct smb_t2rq *t2p;
438 struct smb_share *ssp = np->n_mount->sm_share;
439 struct smb_vc *vcp = SSTOVC(ssp);
440 struct mbchain *mbp;
441 int64_t tm;
442 int error, tzoff;
443
444 /*
445 * SMB_SET_FILE_BASIC_INFO isn't supported for
446 * SMB_TRANS2_SET_PATH_INFORMATION,
447 * so use SMB_SET_FILE_BASIC_INFORMATION instead,
448 * but it requires SMB_CAP_INFOLEVEL_PASSTHRU capability.
449 */
450 if ((SMB_CAPS(vcp) & SMB_CAP_INFOLEVEL_PASSTHRU) == 0)
451 return smbfs_smb_setptime2(np, mtime, atime, attr, scred);
452
453 error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_PATH_INFORMATION,
454 scred, &t2p);
455 if (error)
456 return error;
457 mbp = &t2p->t2_tparam;
458 mb_init(mbp);
459 mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFORMATION);
460 mb_put_uint32le(mbp, 0); /* MBZ */
461 error = smbfs_fullpath(mbp, vcp, np, NULL, 0);
462 if (error) {
463 smb_t2_done(t2p);
464 return error;
465 }
466 tzoff = vcp->vc_sopt.sv_tz;
467 mbp = &t2p->t2_tdata;
468 mb_init(mbp);
469 mb_put_int64le(mbp, 0); /* creation time */
470 if (atime) {
471 smb_time_local2NT(atime, tzoff, &tm);
472 } else
473 tm = 0;
474 mb_put_int64le(mbp, tm);
475 if (mtime) {
476 smb_time_local2NT(mtime, tzoff, &tm);
477 } else
478 tm = 0;
479 mb_put_int64le(mbp, tm);
480 mb_put_int64le(mbp, tm); /* change time */
481 mb_put_uint32le(mbp, attr); /* attr */
482 mb_put_uint32le(mbp, 0); /* padding */
483 t2p->t2_maxpcount = 2;
484 t2p->t2_maxdcount = 0;
485 error = smb_t2_request(t2p);
486 smb_t2_done(t2p);
487 return error;
488}
489
490/*
491 * Set file atime and mtime. Doesn't supported by core dialect.
492 */
493int
494smbfs_smb_setftime(struct smbnode *np, struct timespec *mtime,
495 struct timespec *atime, struct smb_cred *scred)
496{
497 struct smb_rq *rqp;
498 struct smb_share *ssp = np->n_mount->sm_share;
499 struct mbchain *mbp;
500 u_int16_t xdate, xtime;
501 int error, tzoff;
502
503 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_SET_INFORMATION2, scred, &rqp);
504 if (error)
505 return error;
506 tzoff = SSTOVC(ssp)->vc_sopt.sv_tz;
507 smb_rq_getrequest(rqp, &mbp);
508 smb_rq_wstart(rqp);
509 mb_put_mem(mbp, (void *)&np->n_fid, 2, MB_MSYSTEM);
510 mb_put_uint32le(mbp, 0); /* creation time */
511
512 if (atime)
513 smb_time_unix2dos(atime, tzoff, &xdate, &xtime, NULL);
514 else
515 xtime = xdate = 0;
516 mb_put_uint16le(mbp, xdate);
517 mb_put_uint16le(mbp, xtime);
518 if (mtime)
519 smb_time_unix2dos(mtime, tzoff, &xdate, &xtime, NULL);
520 else
521 xtime = xdate = 0;
522 mb_put_uint16le(mbp, xdate);
523 mb_put_uint16le(mbp, xtime);
524 smb_rq_wend(rqp);
525 smb_rq_bstart(rqp);
526 smb_rq_bend(rqp);
527 error = smb_rq_simple(rqp);
528 SMBSDEBUG(("%d\n", error));
529 smb_rq_done(rqp);
530 return error;
531}
532
533/*
534 * Set DOS file attributes.
535 * Looks like this call can be used only if CAP_NT_SMBS bit is on.
536 */
537int
538smbfs_smb_setfattrNT(struct smbnode *np, u_int16_t attr, struct timespec *mtime,
539 struct timespec *atime, struct smb_cred *scred)
540{
541 struct smb_t2rq *t2p;
542 struct smb_share *ssp = np->n_mount->sm_share;
543 struct mbchain *mbp;
544 int64_t tm;
545 int error, svtz;
546
547 error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION,
548 scred, &t2p);
549 if (error)
550 return error;
551 svtz = SSTOVC(ssp)->vc_sopt.sv_tz;
552 mbp = &t2p->t2_tparam;
553 mb_init(mbp);
554 mb_put_mem(mbp, (void *)&np->n_fid, 2, MB_MSYSTEM); /* FID */
555 mb_put_uint16le(mbp, SMB_SET_FILE_BASIC_INFO); /* info level */
556 mb_put_uint32le(mbp, 0); /* reserved */
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, svtz, &tm);
562 } else
563 tm = 0;
564 mb_put_int64le(mbp, tm);
565 if (mtime) {
566 smb_time_local2NT(mtime, svtz, &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 mb_put_uint32le(mbp, 0); /* padding */
573 t2p->t2_maxpcount = 2;
574 t2p->t2_maxdcount = 0;
575 error = smb_t2_request(t2p);
576 smb_t2_done(t2p);
577 return error;
578}
579
580
581int
582smbfs_smb_open(struct smbnode *np, int accmode, struct smb_cred *scred)
583{
584 struct smb_rq *rqp;
585 struct smb_share *ssp = np->n_mount->sm_share;
586 struct mbchain *mbp;
587 struct mdchain *mdp;
588 u_int8_t wc;
589 u_int16_t fid, wattr, grantedmode;
590 int error;
591
592 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_OPEN, scred, &rqp);
593 if (error)
594 return error;
595 smb_rq_getrequest(rqp, &mbp);
596 smb_rq_wstart(rqp);
597 mb_put_uint16le(mbp, accmode);
598 mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
599 smb_rq_wend(rqp);
600 smb_rq_bstart(rqp);
601 mb_put_uint8(mbp, SMB_DT_ASCII);
602 do {
603 error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
604 if (error)
605 break;
606 smb_rq_bend(rqp);
607 error = smb_rq_simple(rqp);
608 if (error)
609 break;
610 smb_rq_getreply(rqp, &mdp);
611 if (md_get_uint8(mdp, &wc) != 0 || wc != 7) {
612 error = EBADRPC;
613 break;
614 }
615 md_get_uint16(mdp, &fid);
616 md_get_uint16le(mdp, &wattr);
617 md_get_uint32(mdp, NULL); /* mtime */
618 md_get_uint32(mdp, NULL); /* fsize */
619 md_get_uint16le(mdp, &grantedmode);
620 /*
621 * TODO: refresh attributes from this reply
622 */
623 } while(0);
624 smb_rq_done(rqp);
625 if (error)
626 return error;
627 np->n_fid = fid;
628 np->n_rwstate = grantedmode;
629 return 0;
630}
631
632
633int
634smbfs_smb_close(struct smb_share *ssp, u_int16_t fid, struct timespec *mtime,
635 struct smb_cred *scred)
636{
637 struct smb_rq *rqp;
638 struct mbchain *mbp;
639 u_long xtime;
640 int error;
641
642 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CLOSE, scred, &rqp);
643 if (error)
644 return error;
645 smb_rq_getrequest(rqp, &mbp);
646 smb_rq_wstart(rqp);
647 mb_put_mem(mbp, (void *)&fid, sizeof(fid), MB_MSYSTEM);
648 if (mtime) {
649 smb_time_local2server(mtime, SSTOVC(ssp)->vc_sopt.sv_tz, &xtime);
650 } else
651 xtime = 0;
652 mb_put_uint32le(mbp, xtime);
653 smb_rq_wend(rqp);
654 smb_rq_bstart(rqp);
655 smb_rq_bend(rqp);
656 error = smb_rq_simple(rqp);
657 smb_rq_done(rqp);
658 return error;
659}
660
661int
662smbfs_smb_create(struct smbnode *dnp, const char *name, int nmlen,
663 struct smb_cred *scred)
664{
665 struct smb_rq *rqp;
666 struct smb_share *ssp = dnp->n_mount->sm_share;
667 struct mbchain *mbp;
668 struct mdchain *mdp;
669 struct timespec ctime;
670 u_int8_t wc;
671 u_int16_t fid;
672 u_long tm;
673 int error;
674
675 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CREATE_NEW, scred, &rqp);
676 if (error)
677 return error;
678 smb_rq_getrequest(rqp, &mbp);
679
680 /* get current time */
681 getnanotime(&ctime);
682 smb_time_local2server(&ctime, SSTOVC(ssp)->vc_sopt.sv_tz, &tm);
683
684 smb_rq_wstart(rqp);
685 mb_put_uint16le(mbp, SMB_FA_ARCHIVE); /* attributes */
686 mb_put_uint32le(mbp, tm);
687 smb_rq_wend(rqp);
688
689 smb_rq_bstart(rqp);
690 mb_put_uint8(mbp, SMB_DT_ASCII);
691 error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, nmlen);
692 if (!error) {
693 smb_rq_bend(rqp);
694 error = smb_rq_simple(rqp);
695 if (!error) {
696 smb_rq_getreply(rqp, &mdp);
697 md_get_uint8(mdp, &wc);
698 if (wc == 1)
699 md_get_uint16(mdp, &fid);
700 else
701 error = EBADRPC;
702 }
703 }
704
705 smb_rq_done(rqp);
706 if (!error)
707 smbfs_smb_close(ssp, fid, &ctime, scred);
708
709 return (error);
710}
711
712int
713smbfs_smb_delete(struct smbnode *np, struct smb_cred *scred)
714{
715 struct smb_rq *rqp;
716 struct smb_share *ssp = np->n_mount->sm_share;
717 struct mbchain *mbp;
718 int error;
719
720 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_DELETE, scred, &rqp);
721 if (error)
722 return error;
723 smb_rq_getrequest(rqp, &mbp);
724 smb_rq_wstart(rqp);
725 mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
726 smb_rq_wend(rqp);
727 smb_rq_bstart(rqp);
728 mb_put_uint8(mbp, SMB_DT_ASCII);
729 error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
730 if (!error) {
731 smb_rq_bend(rqp);
732 error = smb_rq_simple(rqp);
733 }
734 smb_rq_done(rqp);
735 return error;
736}
737
738int
739smbfs_smb_rename(struct smbnode *src, struct smbnode *tdnp,
740 const char *tname, int tnmlen, struct smb_cred *scred)
741{
742 struct smb_rq *rqp;
743 struct smb_share *ssp = src->n_mount->sm_share;
744 struct mbchain *mbp;
745 int error;
746
747 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_RENAME, scred, &rqp);
748 if (error)
749 return error;
750 smb_rq_getrequest(rqp, &mbp);
751 smb_rq_wstart(rqp);
752 mb_put_uint16le(mbp, SMB_FA_SYSTEM | SMB_FA_HIDDEN);
753 smb_rq_wend(rqp);
754 smb_rq_bstart(rqp);
755 mb_put_uint8(mbp, SMB_DT_ASCII);
756 do {
757 error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0);
758 if (error)
759 break;
760 mb_put_uint8(mbp, SMB_DT_ASCII);
761 error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen);
762 if (error)
763 break;
764 smb_rq_bend(rqp);
765 error = smb_rq_simple(rqp);
766 } while(0);
767 smb_rq_done(rqp);
768 return error;
769}
770
771#ifdef notnow
772int
773smbfs_smb_move(struct smbnode *src, struct smbnode *tdnp,
774 const char *tname, int tnmlen, u_int16_t flags, struct smb_cred *scred)
775{
776 struct smb_rq *rqp;
777 struct smb_share *ssp = src->n_mount->sm_share;
778 struct mbchain *mbp;
779 int error;
780
781 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_MOVE, scred, &rqp);
782 if (error)
783 return error;
784 smb_rq_getrequest(rqp, &mbp);
785 smb_rq_wstart(rqp);
786 mb_put_uint16le(mbp, SMB_TID_UNKNOWN);
787 mb_put_uint16le(mbp, 0x20); /* delete target file */
788 mb_put_uint16le(mbp, flags);
789 smb_rq_wend(rqp);
790 smb_rq_bstart(rqp);
791 mb_put_uint8(mbp, SMB_DT_ASCII);
792 do {
793 error = smbfs_fullpath(mbp, SSTOVC(ssp), src, NULL, 0);
794 if (error)
795 break;
796 mb_put_uint8(mbp, SMB_DT_ASCII);
797 error = smbfs_fullpath(mbp, SSTOVC(ssp), tdnp, tname, tnmlen);
798 if (error)
799 break;
800 smb_rq_bend(rqp);
801 error = smb_rq_simple(rqp);
802 } while(0);
803 smb_rq_done(rqp);
804 return error;
805}
806#endif
807
808int
809smbfs_smb_mkdir(struct smbnode *dnp, const char *name, int len,
810 struct smb_cred *scred)
811{
812 struct smb_rq *rqp;
813 struct smb_share *ssp = dnp->n_mount->sm_share;
814 struct mbchain *mbp;
815 int error;
816
817 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CREATE_DIRECTORY, scred, &rqp);
818 if (error)
819 return error;
820 smb_rq_getrequest(rqp, &mbp);
821 smb_rq_wstart(rqp);
822 smb_rq_wend(rqp);
823 smb_rq_bstart(rqp);
824 mb_put_uint8(mbp, SMB_DT_ASCII);
825 error = smbfs_fullpath(mbp, SSTOVC(ssp), dnp, name, len);
826 if (!error) {
827 smb_rq_bend(rqp);
828 error = smb_rq_simple(rqp);
829 }
830 smb_rq_done(rqp);
831 return error;
832}
833
834int
835smbfs_smb_rmdir(struct smbnode *np, struct smb_cred *scred)
836{
837 struct smb_rq *rqp;
838 struct smb_share *ssp = np->n_mount->sm_share;
839 struct mbchain *mbp;
840 int error;
841
842 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_DELETE_DIRECTORY, scred, &rqp);
843 if (error)
844 return error;
845 smb_rq_getrequest(rqp, &mbp);
846 smb_rq_wstart(rqp);
847 smb_rq_wend(rqp);
848 smb_rq_bstart(rqp);
849 mb_put_uint8(mbp, SMB_DT_ASCII);
850 error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
851 if (!error) {
852 smb_rq_bend(rqp);
853 error = smb_rq_simple(rqp);
854 }
855 smb_rq_done(rqp);
856 return error;
857}
858
859static int
860smbfs_smb_search(struct smbfs_fctx *ctx)
861{
862 struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
863 struct smb_rq *rqp;
864 struct mbchain *mbp;
865 struct mdchain *mdp;
866 u_int8_t wc, bt;
867 u_int16_t ec, dlen, bc;
868 int maxent, error;
869
870 maxent = min(ctx->f_left, (vcp->vc_txmax - SMB_HDRLEN - 3) / SMB_DENTRYLEN);
871 if (ctx->f_rq) {
872 smb_rq_done(ctx->f_rq);
873 ctx->f_rq = NULL;
874 }
875 error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_SEARCH, ctx->f_scred, &rqp);
876 if (error)
877 return error;
878 ctx->f_rq = rqp;
879 smb_rq_getrequest(rqp, &mbp);
880 smb_rq_wstart(rqp);
881 mb_put_uint16le(mbp, maxent); /* max entries to return */
882 mb_put_uint16le(mbp, ctx->f_attrmask);
883 smb_rq_wend(rqp);
884 smb_rq_bstart(rqp);
885 mb_put_uint8(mbp, SMB_DT_ASCII); /* buffer format */
886 if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
887 error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen);
888 if (error)
889 return error;
890 mb_put_uint8(mbp, SMB_DT_VARIABLE);
891 mb_put_uint16le(mbp, 0); /* context length */
892 ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
893 } else {
894 mb_put_uint8(mbp, 0); /* file name length */
895 mb_put_uint8(mbp, SMB_DT_VARIABLE);
896 mb_put_uint16le(mbp, SMB_SKEYLEN);
897 mb_put_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM);
898 }
899 smb_rq_bend(rqp);
900 error = smb_rq_simple(rqp);
901 if (error) {
902 if (error == ENOENT)
903 ctx->f_flags |= SMBFS_RDD_EOF;
904
905 return error;
906 }
907 smb_rq_getreply(rqp, &mdp);
908 md_get_uint8(mdp, &wc);
909 if (wc != 1)
910 return EBADRPC;
911 md_get_uint16le(mdp, &ec);
912 if (ec == 0)
913 return ENOENT;
914 ctx->f_ecnt = ec;
915 md_get_uint16le(mdp, &bc);
916 if (bc < 3)
917 return EBADRPC;
918 bc -= 3;
919 md_get_uint8(mdp, &bt);
920 if (bt != SMB_DT_VARIABLE)
921 return EBADRPC;
922 md_get_uint16le(mdp, &dlen);
923 if (dlen != bc || dlen % SMB_DENTRYLEN != 0)
924 return EBADRPC;
925 return 0;
926}
927
928static int
929smbfs_findopenLM1(struct smbfs_fctx *ctx, struct smbnode *dnp,
930 const char *wildcard, int wclen, int attr, struct smb_cred *scred)
931{
932 ctx->f_attrmask = attr;
933 if (wildcard) {
934 if (wclen == 1 && wildcard[0] == '*') {
935 ctx->f_wildcard = "*.*";
936 ctx->f_wclen = 3;
937 } else {
938 ctx->f_wildcard = wildcard;
939 ctx->f_wclen = wclen;
940 }
941 } else {
942 ctx->f_wildcard = NULL;
943 ctx->f_wclen = 0;
944 }
945 ctx->f_name = ctx->f_fname;
946 return 0;
947}
948
949static int
950smbfs_findnextLM1(struct smbfs_fctx *ctx, int limit)
951{
952 struct mdchain *mbp;
953 struct smb_rq *rqp;
954 char *cp;
955 u_int8_t battr;
956 u_int16_t xdate, xtime;
957 u_int32_t size;
958 int error;
959
960 if (ctx->f_ecnt == 0) {
961 if (ctx->f_flags & SMBFS_RDD_EOF)
962 return ENOENT;
963 ctx->f_left = ctx->f_limit = limit;
964 error = smbfs_smb_search(ctx);
965 if (error)
966 return error;
967 }
968 rqp = ctx->f_rq;
969 smb_rq_getreply(rqp, &mbp);
970 md_get_mem(mbp, ctx->f_skey, SMB_SKEYLEN, MB_MSYSTEM);
971 md_get_uint8(mbp, &battr);
972 md_get_uint16le(mbp, &xtime);
973 md_get_uint16le(mbp, &xdate);
974 md_get_uint32le(mbp, &size);
975 KASSERT(ctx->f_name == ctx->f_fname);
976 cp = ctx->f_name;
977 md_get_mem(mbp, cp, sizeof(ctx->f_fname), MB_MSYSTEM);
978 cp[sizeof(ctx->f_fname) - 1] = '\0';
979 cp += strlen(cp) - 1;
980 while(*cp == ' ' && cp > ctx->f_name)
981 *cp-- = '\0';
982 ctx->f_attr.fa_attr = battr;
983 smb_dos2unixtime(xdate, xtime, 0, rqp->sr_vc->vc_sopt.sv_tz,
984 &ctx->f_attr.fa_mtime);
985 ctx->f_attr.fa_size = size;
986 ctx->f_nmlen = strlen(ctx->f_name);
987 ctx->f_ecnt--;
988 ctx->f_left--;
989 return 0;
990}
991
992static int
993smbfs_findcloseLM1(struct smbfs_fctx *ctx)
994{
995 if (ctx->f_rq)
996 smb_rq_done(ctx->f_rq);
997 return 0;
998}
999
1000/*
1001 * TRANS2_FIND_FIRST2/NEXT2, used for NT LM12 dialect
1002 */
1003static int
1004smbfs_smb_trans2find2(struct smbfs_fctx *ctx)
1005{
1006 struct smb_t2rq *t2p;
1007 struct smb_vc *vcp = SSTOVC(ctx->f_ssp);
1008 struct mbchain *mbp;
1009 struct mdchain *mdp;
1010 u_int16_t tw, flags;
1011 int error;
1012
1013 if (ctx->f_t2) {
1014 smb_t2_done(ctx->f_t2);
1015 ctx->f_t2 = NULL;
1016 }
1017 ctx->f_flags &= ~SMBFS_RDD_GOTRNAME;
1018 flags = 8 | 2; /* <resume> | <close if EOS> */
1019 if (ctx->f_flags & SMBFS_RDD_FINDSINGLE) {
1020 flags |= 1; /* close search after this request */
1021 ctx->f_flags |= SMBFS_RDD_NOCLOSE;
1022 }
1023 if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
1024 error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_FIRST2,
1025 ctx->f_scred, &t2p);
1026 if (error)
1027 return error;
1028 ctx->f_t2 = t2p;
1029 mbp = &t2p->t2_tparam;
1030 mb_init(mbp);
1031 mb_put_uint16le(mbp, ctx->f_attrmask);
1032 mb_put_uint16le(mbp, ctx->f_limit);
1033 mb_put_uint16le(mbp, flags);
1034 mb_put_uint16le(mbp, ctx->f_infolevel);
1035 mb_put_uint32le(mbp, 0);
1036 error = smbfs_fullpath(mbp, vcp, ctx->f_dnp, ctx->f_wildcard, ctx->f_wclen);
1037 if (error)
1038 return error;
1039 } else {
1040 error = smb_t2_alloc(SSTOCP(ctx->f_ssp), SMB_TRANS2_FIND_NEXT2,
1041 ctx->f_scred, &t2p);
1042 if (error)
1043 return error;
1044 ctx->f_t2 = t2p;
1045 mbp = &t2p->t2_tparam;
1046 mb_init(mbp);
1047 mb_put_mem(mbp, (void *)&ctx->f_Sid, 2, MB_MSYSTEM);
1048 mb_put_uint16le(mbp, ctx->f_limit);
1049 mb_put_uint16le(mbp, ctx->f_infolevel);
1050 mb_put_uint32le(mbp, 0); /* resume key */
1051 mb_put_uint16le(mbp, flags);
1052 if (ctx->f_rname)
1053 mb_put_mem(mbp, ctx->f_rname, strlen(ctx->f_rname) + 1, MB_MSYSTEM);
1054 else
1055 mb_put_uint8(mbp, 0); /* resume file name */
1056#if 0
1057 struct timeval tv;
1058 tv.tv_sec = 0;
1059 tv.tv_usec = 200 * 1000; /* 200ms */
1060 if (vcp->vc_flags & SMBC_WIN95) {
1061 /*
1062 * some implementations suggests to sleep here
1063 * for 200ms, due to the bug in the Win95.
1064 * I've didn't notice any problem, but put code
1065 * for it.
1066 */
1067 tsleep(&flags, PVFS, "fix95", tvtohz(&tv));
1068 }
1069#endif
1070 }
1071 t2p->t2_maxpcount = 5 * 2;
1072 t2p->t2_maxdcount = vcp->vc_txmax;
1073 error = smb_t2_request(t2p);
1074 if (error)
1075 return error;
1076 mdp = &t2p->t2_rparam;
1077 if (ctx->f_flags & SMBFS_RDD_FINDFIRST) {
1078 if ((error = md_get_uint16(mdp, &ctx->f_Sid)) != 0)
1079 return error;
1080 ctx->f_flags &= ~SMBFS_RDD_FINDFIRST;
1081 }
1082 if ((error = md_get_uint16le(mdp, &tw)) != 0)
1083 return error;
1084 ctx->f_ecnt = tw;
1085 if ((error = md_get_uint16le(mdp, &tw)) != 0)
1086 return error;
1087 if (tw)
1088 ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE;
1089 if ((error = md_get_uint16le(mdp, &tw)) != 0)
1090 return error;
1091 if ((error = md_get_uint16le(mdp, &tw)) != 0)
1092 return error;
1093 if (ctx->f_ecnt == 0) {
1094 ctx->f_flags |= SMBFS_RDD_EOF | SMBFS_RDD_NOCLOSE;
1095 return ENOENT;
1096 }
1097 ctx->f_rnameofs = tw;
1098 mdp = &t2p->t2_rdata;
1099
1100 KASSERT(mdp->md_top != NULL);
1101 KASSERT(mdp->md_top->m_len != 0);
1102
1103 ctx->f_eofs = 0;
1104 return 0;
1105}
1106
1107static int
1108smbfs_smb_findclose2(struct smbfs_fctx *ctx)
1109{
1110 struct smb_rq *rqp;
1111 struct mbchain *mbp;
1112 int error;
1113
1114 error = smb_rq_alloc(SSTOCP(ctx->f_ssp), SMB_COM_FIND_CLOSE2, ctx->f_scred, &rqp);
1115 if (error)
1116 return error;
1117 smb_rq_getrequest(rqp, &mbp);
1118 smb_rq_wstart(rqp);
1119 mb_put_mem(mbp, (void *)&ctx->f_Sid, 2, MB_MSYSTEM);
1120 smb_rq_wend(rqp);
1121 smb_rq_bstart(rqp);
1122 smb_rq_bend(rqp);
1123 error = smb_rq_simple(rqp);
1124 smb_rq_done(rqp);
1125 return error;
1126}
1127
1128static int
1129smbfs_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp,
1130 const char *wildcard, int wclen, int attr, struct smb_cred *scred)
1131{
1132 ctx->f_name = malloc(SMB_MAXNAMLEN * 2, M_SMBFSDATA, M_WAITOK);
1133 if (ctx->f_name == NULL)
1134 return ENOMEM;
1135 ctx->f_infolevel = SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_NTLM0_12 ?
1136 SMB_INFO_STANDARD : SMB_FIND_FILE_DIRECTORY_INFO;
1137 ctx->f_attrmask = attr;
1138 ctx->f_wildcard = wildcard;
1139 ctx->f_wclen = wclen;
1140 return 0;
1141}
1142
1143static int
1144smbfs_findnextLM2(struct smbfs_fctx *ctx, int limit)
1145{
1146 struct mdchain *mbp;
1147 struct smb_t2rq *t2p;
1148 char *cp;
1149 u_int8_t tb;
1150 u_int16_t xdate, xtime, wattr;
1151 u_int32_t size, next, dattr;
1152 int64_t tmp;
1153 int error, svtz, cnt, fxsz, nmlen, recsz;
1154
1155 if (ctx->f_ecnt == 0) {
1156 if (ctx->f_flags & SMBFS_RDD_EOF)
1157 return ENOENT;
1158 ctx->f_left = ctx->f_limit = limit;
1159 error = smbfs_smb_trans2find2(ctx);
1160 if (error)
1161 return error;
1162 }
1163 t2p = ctx->f_t2;
1164 mbp = &t2p->t2_rdata;
1165 svtz = SSTOVC(ctx->f_ssp)->vc_sopt.sv_tz;
1166 switch (ctx->f_infolevel) {
1167 case SMB_INFO_STANDARD:
1168 next = 0;
1169 fxsz = 0;
1170 md_get_uint16le(mbp, &xdate);
1171 md_get_uint16le(mbp, &xtime); /* creation time */
1172 md_get_uint16le(mbp, &xdate);
1173 md_get_uint16le(mbp, &xtime); /* access time */
1174 smb_dos2unixtime(xdate, xtime, 0, svtz, &ctx->f_attr.fa_atime);
1175 md_get_uint16le(mbp, &xdate);
1176 md_get_uint16le(mbp, &xtime); /* access time */
1177 smb_dos2unixtime(xdate, xtime, 0, svtz, &ctx->f_attr.fa_mtime);
1178 md_get_uint32le(mbp, &size);
1179 ctx->f_attr.fa_size = size;
1180 md_get_uint32(mbp, NULL); /* allocation size */
1181 md_get_uint16le(mbp, &wattr);
1182 ctx->f_attr.fa_attr = wattr;
1183 md_get_uint8(mbp, &tb);
1184 size = nmlen = tb;
1185 fxsz = 23;
1186 recsz = next = 24 + nmlen; /* docs misses zero byte at end */
1187 break;
1188 case SMB_FIND_FILE_DIRECTORY_INFO:
1189 md_get_uint32le(mbp, &next);
1190 md_get_uint32(mbp, NULL); /* file index */
1191 md_get_int64(mbp, NULL); /* creation time */
1192 md_get_int64le(mbp, &tmp);
1193 smb_time_NT2local(tmp, svtz, &ctx->f_attr.fa_atime);
1194 md_get_int64le(mbp, &tmp);
1195 smb_time_NT2local(tmp, svtz, &ctx->f_attr.fa_mtime);
1196 md_get_int64le(mbp, &tmp);
1197 smb_time_NT2local(tmp, svtz, &ctx->f_attr.fa_ctime);
1198 md_get_int64le(mbp, &tmp); /* file size */
1199 ctx->f_attr.fa_size = tmp;
1200 md_get_int64(mbp, NULL); /* real size (should use) */
1201 md_get_uint32le(mbp, &dattr); /* EA */
1202 ctx->f_attr.fa_attr = dattr;
1203 md_get_uint32le(mbp, &size); /* name len */
1204 fxsz = 64;
1205 recsz = next ? next : fxsz + size;
1206 break;
1207 default:
1208#ifdef DIAGNOSTIC
1209 panic("smbfs_findnextLM2: unexpected info level %d\n",
1210 ctx->f_infolevel);
1211#else
1212 return EINVAL;
1213#endif
1214 }
1215 nmlen = min(size, SMB_MAXNAMLEN * 2);
1216 cp = ctx->f_name;
1217 error = md_get_mem(mbp, cp, nmlen, MB_MSYSTEM);
1218 if (error)
1219 return error;
1220 if (next) {
1221 cnt = next - nmlen - fxsz;
1222 if (cnt > 0)
1223 md_get_mem(mbp, NULL, cnt, MB_MSYSTEM);
1224#ifdef DIAGNOSTIC
1225 else if (cnt < 0)
1226 panic("smbfs_findnextLM2: out of sync");
1227#endif
1228 }
1229 if (nmlen && cp[nmlen - 1] == 0)
1230 nmlen--;
1231 if (nmlen == 0)
1232 return EBADRPC;
1233
1234 next = ctx->f_eofs + recsz;
1235 if (ctx->f_rnameofs && (ctx->f_flags & SMBFS_RDD_GOTRNAME) == 0 &&
1236 (ctx->f_rnameofs >= ctx->f_eofs && ctx->f_rnameofs < next)) {
1237 /*
1238 * Server needs a resume filename.
1239 */
1240 if (ctx->f_rnamelen <= nmlen) {
1241 if (ctx->f_rname)
1242 free(ctx->f_rname, M_SMBFSDATA);
1243 ctx->f_rname = malloc(nmlen + 1, M_SMBFSDATA, M_WAITOK);
1244 ctx->f_rnamelen = nmlen;
1245 }
1246 memcpy(ctx->f_rname, ctx->f_name, nmlen);
1247 ctx->f_rname[nmlen] = 0;
1248 ctx->f_flags |= SMBFS_RDD_GOTRNAME;
1249 }
1250 ctx->f_nmlen = nmlen;
1251 ctx->f_eofs = next;
1252 ctx->f_ecnt--;
1253 ctx->f_left--;
1254 return 0;
1255}
1256
1257static int
1258smbfs_findcloseLM2(struct smbfs_fctx *ctx)
1259{
1260 if (ctx->f_name)
1261 free(ctx->f_name, M_SMBFSDATA);
1262 if (ctx->f_t2)
1263 smb_t2_done(ctx->f_t2);
1264 if ((ctx->f_flags & SMBFS_RDD_NOCLOSE) == 0)
1265 smbfs_smb_findclose2(ctx);
1266 return 0;
1267}
1268
1269int
1270smbfs_findopen(struct smbnode *dnp, const char *wildcard, int wclen, int attr,
1271 struct smb_cred *scred, struct smbfs_fctx **ctxpp)
1272{
1273 struct smbfs_fctx *ctx;
1274 int error;
1275
1276 ctx = malloc(sizeof(*ctx), M_SMBFSDATA, M_WAITOK|M_ZERO);
1277 ctx->f_ssp = dnp->n_mount->sm_share;
1278 ctx->f_dnp = dnp;
1279 ctx->f_flags = SMBFS_RDD_FINDFIRST;
1280 ctx->f_scred = scred;
1281 if (SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_LANMAN2_0 ||
1282 (dnp->n_mount->sm_args.flags & SMBFS_MOUNT_NO_LONG)) {
1283 ctx->f_flags |= SMBFS_RDD_USESEARCH;
1284 error = smbfs_findopenLM1(ctx, dnp, wildcard, wclen, attr, scred);
1285 } else
1286 error = smbfs_findopenLM2(ctx, dnp, wildcard, wclen, attr, scred);
1287 if (error)
1288 smbfs_findclose(ctx, scred);
1289 else
1290 *ctxpp = ctx;
1291 return error;
1292}
1293
1294int
1295smbfs_findnext(struct smbfs_fctx *ctx, int limit, struct smb_cred *scred)
1296{
1297 int error;
1298
1299 if (limit == 0)
1300 limit = 1000000;
1301 else if (limit > 1)
1302 limit *= 4; /* empirical */
1303 ctx->f_scred = scred;
1304 for (;;) {
1305 if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
1306 error = smbfs_findnextLM1(ctx, limit);
1307 } else
1308 error = smbfs_findnextLM2(ctx, limit);
1309 if (error)
1310 return error;
1311
1312 /* Skip '.' and '..' */
1313 if ((ctx->f_nmlen == 1 && ctx->f_name[0] == '.') ||
1314 (ctx->f_nmlen == 2 && ctx->f_name[0] == '.' &&
1315 ctx->f_name[1] == '.'))
1316 continue;
1317 break;
1318 }
1319 smbfs_fname_tolocal(SSTOVC(ctx->f_ssp), ctx->f_name, &ctx->f_nmlen,
1320 ctx->f_dnp->n_mount->sm_caseopt);
1321 ctx->f_attr.fa_ino = smbfs_getino(ctx->f_dnp, ctx->f_name, ctx->f_nmlen);
1322 return 0;
1323}
1324
1325int
1326smbfs_findclose(struct smbfs_fctx *ctx, struct smb_cred *scred)
1327{
1328 ctx->f_scred = scred;
1329 if (ctx->f_flags & SMBFS_RDD_USESEARCH) {
1330 smbfs_findcloseLM1(ctx);
1331 } else
1332 smbfs_findcloseLM2(ctx);
1333 if (ctx->f_rname)
1334 free(ctx->f_rname, M_SMBFSDATA);
1335 free(ctx, M_SMBFSDATA);
1336 return 0;
1337}
1338
1339int
1340smbfs_smb_lookup(struct smbnode *dnp, const char *name, int nmlen,
1341 struct smbfattr *fap, struct smb_cred *scred)
1342{
1343 struct smbfs_fctx *ctx;
1344 int error;
1345
1346 if (dnp == NULL || (dnp->n_ino == 2 && name == NULL)) {
1347 memset(fap, 0, sizeof(*fap));
1348 fap->fa_attr = SMB_FA_DIR;
1349 fap->fa_ino = 2;
1350 return 0;
1351 }
1352 if (nmlen == 1 && name[0] == '.') {
1353 error = smbfs_smb_lookup(dnp, NULL, 0, fap, scred);
1354 return error;
1355 } else if (nmlen == 2 && name[0] == '.' && name[1] == '.') {
1356 error = smbfs_smb_lookup(VTOSMB(dnp->n_parent), NULL, 0,
1357 fap, scred);
1358 printf("%s: knows NOTHING about '..'\n", __func__);
1359 return error;
1360 }
1361 error = smbfs_findopen(dnp, name, nmlen,
1362 SMB_FA_SYSTEM | SMB_FA_HIDDEN | SMB_FA_DIR, scred, &ctx);
1363 if (error)
1364 return error;
1365 ctx->f_flags |= SMBFS_RDD_FINDSINGLE;
1366 error = smbfs_findnext(ctx, 1, scred);
1367 if (error == 0) {
1368 *fap = ctx->f_attr;
1369 if (name == NULL)
1370 fap->fa_ino = dnp->n_ino;
1371
1372 /*
1373 * Check the returned file name case exactly
1374 * matches requested file name. ctx->f_nmlen is
1375 * guaranteed to always match nmlen.
1376 */
1377 if (nmlen > 0 && strncmp(name, ctx->f_name, nmlen) != 0)
1378 error = ENOENT;
1379 }
1380 smbfs_findclose(ctx, scred);
1381 return error;
1382}
1383
1384/*
1385 * This call is used to fetch FID for directories. For normal files,
1386 * SMB_COM_OPEN is used.
1387 */
1388int
1389smbfs_smb_ntcreatex(struct smbnode *np, int accmode,
1390 struct smb_cred *scred)
1391{
1392 struct smb_rq *rqp;
1393 struct smb_share *ssp = np->n_mount->sm_share;
1394 struct mbchain *mbp;
1395 struct mdchain *mdp;
1396 int error;
1397 u_int8_t wc;
1398 u_int8_t *nmlen;
1399 u_int16_t flen;
1400
1401 KASSERT(SMBTOV(np)->v_type == VDIR);
1402
1403 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_NT_CREATE_ANDX, scred, &rqp);
1404 if (error)
1405 return error;
1406 smb_rq_getrequest(rqp, &mbp);
1407 smb_rq_wstart(rqp);
1408 mb_put_uint8(mbp, 0xff); /* Secondary command; 0xFF = None */
1409 mb_put_uint8(mbp, 0); /* Reserved (must be 0) */
1410 mb_put_uint16le(mbp, 0); /* Off to next cmd WordCount */
1411 mb_put_uint8(mbp, 0); /* Reserved (must be 0) */
1412 nmlen = mb_reserve(mbp, sizeof(u_int16_t));
1413 /* Length of Name[] in bytes */
1414 mb_put_uint32le(mbp, SMB_FL_CANONICAL_PATHNAMES);
1415 /* Flags - Create bit set */
1416 mb_put_uint32le(mbp, 0); /* If nonzero, open relative to this */
1417 mb_put_uint32le(mbp, NT_FILE_LIST_DIRECTORY); /* Access mask */
1418 mb_put_uint32le(mbp, 0); /* Low 32bit */
1419 mb_put_uint32le(mbp, 0); /* Hi 32bit */
1420 /* Initial allocation size */
1421 mb_put_uint32le(mbp, 0); /* File attributes */
1422 mb_put_uint32le(mbp, NT_FILE_SHARE_READ|NT_FILE_SHARE_WRITE);
1423 /* Type of share access */
1424 mb_put_uint32le(mbp, NT_OPEN_EXISTING);
1425 /* Create disposition - just open */
1426 mb_put_uint32le(mbp, NT_FILE_DIRECTORY_FILE);
1427 /* Options to use if creating a file */
1428 mb_put_uint32le(mbp, 0); /* Security QOS information */
1429 mb_put_uint8(mbp, 0); /* Security tracking mode flags */
1430 smb_rq_wend(rqp);
1431 smb_rq_bstart(rqp);
1432
1433 error = smbfs_fullpath(mbp, SSTOVC(ssp), np, NULL, 0);
1434 if (error)
1435 return error;
1436
1437 /* Windows XP seems to include the final zero. Better do that too. */
1438 mb_put_uint8(mbp, 0);
1439
1440 flen = mbp->mb_count;
1441 SMBRQ_PUTLE16(nmlen, flen);
1442
1443 smb_rq_bend(rqp);
1444 error = smb_rq_simple(rqp);
1445 if (error)
1446 goto bad;
1447
1448 smb_rq_getreply(rqp, &mdp);
1449 md_get_uint8(mdp, &wc); /* WordCount - check? */
1450 md_get_uint8(mdp, NULL); /* AndXCommand */
1451 md_get_uint8(mdp, NULL); /* Reserved - must be zero */
1452 md_get_uint16(mdp, NULL); /* Offset to next cmd WordCount */
1453 md_get_uint8(mdp, NULL); /* Oplock level granted */
1454 md_get_uint16(mdp, &np->n_fid); /* FID */
1455 /* ignore rest */
1456
1457bad:
1458 smb_rq_done(rqp);
1459 return (error);
1460}
1461
1462/*
1463 * Setup a request for NT DIRECTORY CHANGE NOTIFY.
1464 */
1465int
1466smbfs_smb_nt_dirnotify_setup(struct smbnode *dnp, struct smb_rq **rqpp, struct smb_cred *scred, void (*notifyhook)(void *), void *notifyarg)
1467{
1468 struct smb_rq *rqp;
1469 struct smb_share *ssp = dnp->n_mount->sm_share;
1470 struct mbchain *mbp;
1471 int error;
1472
1473 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_NT_TRANSACT, scred, &rqp);
1474 if (error)
1475 return error;
1476 smb_rq_getrequest(rqp, &mbp);
1477 smb_rq_wstart(rqp);
1478 mb_put_uint8(mbp, 0xff); /* Max setup words to return */
1479 mb_put_uint16le(mbp, 0); /* Flags (according to Samba) */
1480 mb_put_uint32le(mbp, 0); /* Total parameter bytes being sent*/
1481 mb_put_uint32le(mbp, 0); /* Total data bytes being sent */
1482 mb_put_uint32le(mbp, 10*1024); /* Max parameter bytes to return */
1483 mb_put_uint32le(mbp, 0); /* Max data bytes to return */
1484 mb_put_uint32le(mbp, 0); /* Parameter bytes sent this buffer */
1485 mb_put_uint32le(mbp, 0); /* Offset (from h. start) to Param */
1486 mb_put_uint32le(mbp, 0); /* Data bytes sent this buffer */
1487 mb_put_uint32le(mbp, 0); /* Offset (from h. start) to Data */
1488 mb_put_uint8(mbp, 4); /* Count of setup words */
1489 mb_put_uint16le(mbp, SMB_NTTRANS_NOTIFY_CHANGE); /* Trans func code */
1490
1491 /* NT TRANSACT NOTIFY CHANGE: Request Change Notification */
1492 mb_put_uint32le(mbp,
1493 FILE_NOTIFY_CHANGE_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES|
1494 FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE|
1495 FILE_NOTIFY_CHANGE_CREATION); /* CompletionFilter */
1496 mb_put_mem(mbp, (void *)&dnp->n_fid, 2, MB_MSYSTEM); /* FID */
1497 mb_put_uint8(mbp, 0); /* WatchTree - Watch all subdirs too */
1498 mb_put_uint8(mbp, 0); /* Reserved - must be zero */
1499 smb_rq_wend(rqp);
1500 smb_rq_bstart(rqp);
1501 smb_rq_bend(rqp);
1502
1503 /* No timeout */
1504 rqp->sr_timo = -1;
1505 smb_rq_setcallback(rqp, notifyhook, notifyarg);
1506
1507 error = smb_rq_enqueue(rqp);
1508 if (!error)
1509 *rqpp = rqp;
1510 else
1511 smb_rq_done(rqp);
1512
1513 return (error);
1514}
1515
1516int
1517smbfs_smb_nt_dirnotify_fetch(struct smb_rq *rqp, int *hint)
1518{
1519 int error;
1520 struct mdchain *mdp;
1521 u_int8_t sc;
1522 u_int32_t nextentry;
1523
1524 error = smb_rq_reply(rqp);
1525 if (error) {
1526 /*
1527 * If we get EMSGSIZE, there is already too many notifications
1528 * available for the directory, and the internal buffer
1529 * overflew. Just flag any possible relevant change.
1530 */
1531 if (error == EMSGSIZE) {
1532 *hint = NOTE_ATTRIB | NOTE_WRITE;
1533 error = 0;
1534 }
1535
1536 goto bad;
1537 }
1538
1539 smb_rq_getreply(rqp, &mdp);
1540
1541 /* Parse reply */
1542 error = md_get_mem(mdp, NULL, 4 + (8*4), MB_MZERO); /* skip */
1543 if (error)
1544 goto bad;
1545
1546 md_get_uint8(mdp, &sc); /* SetupCount */
1547 if (sc > 0)
1548 md_get_mem(mdp, NULL, sc * sizeof(u_int16_t), MB_MZERO);
1549 md_get_uint16(mdp, NULL); /* ByteCount */
1550 md_get_mem(mdp, NULL, 1 + (sc % 2) * 2, MB_MZERO); /* Pad */
1551
1552 /*
1553 * The notify data are blocks of
1554 * ULONG nextentry - offset of next entry from start of this one
1555 * ULONG action - type of notification
1556 * ULONG filenamelen - length of filename in bytes
1557 * WCHAR filename[filenamelen/2] - Unicode filename
1558 * nexentry == 0 means last notification, filename is in 16bit LE
1559 * unicode
1560 */
1561 *hint = 0;
1562 do {
1563 u_int32_t action;
1564#if 0
1565 u_int32_t fnlen;
1566 u_int16_t fnc;
1567#endif
1568
1569 md_get_uint32le(mdp, &nextentry);
1570 md_get_uint32le(mdp, &action);
1571 if (nextentry)
1572 md_get_mem(mdp, NULL, nextentry - 2 * 4, MB_MZERO);
1573#if 0
1574 md_get_uint32le(mdp, &fnlen);
1575
1576 printf("notify: next %u act %u fnlen %u fname '",
1577 nextentry, action, fnlen);
1578 for(; fnlen > 0; fnlen -= 2) {
1579 md_get_uint16le(mdp, &fnc);
1580 printf("%c", fnc&0xff);
1581 }
1582 printf("'\n");
1583#endif
1584
1585 switch(action) {
1586 case FILE_ACTION_ADDED:
1587 case FILE_ACTION_REMOVED:
1588 case FILE_ACTION_RENAMED_OLD_NAME:
1589 case FILE_ACTION_RENAMED_NEW_NAME:
1590 *hint |= NOTE_ATTRIB | NOTE_WRITE;
1591 break;
1592
1593 case FILE_ACTION_MODIFIED:
1594 *hint |= NOTE_ATTRIB;
1595 break;
1596 }
1597 } while(nextentry > 0);
1598
1599bad:
1600 smb_rq_done(rqp);
1601 return error;
1602}
1603
1604/*
1605 * Cancel previous SMB, with message ID mid. No reply is generated
1606 * to this one (only the previous message returns with error).
1607 */
1608int
1609smbfs_smb_ntcancel(struct smb_connobj *layer, u_int16_t mid, struct smb_cred *scred)
1610{
1611 struct smb_rq *rqp;
1612 struct mbchain *mbp;
1613 struct mbuf *m;
1614 u_int8_t *mp;
1615 int error;
1616
1617 error = smb_rq_alloc(layer, SMB_COM_NT_CANCEL, scred, &rqp);
1618 if (error)
1619 return (error);
1620 rqp->sr_flags |= SMBR_NOWAIT; /* do not wait for reply */
1621 smb_rq_getrequest(rqp, &mbp);
1622
1623 /*
1624 * This is nonstandard. We need to rewrite the just written
1625 * mid to different one. Access underlying mbuf directly.
1626 * We assume mid is the last thing written smb_rq_alloc()
1627 * to request buffer.
1628 */
1629 m = mbp->mb_cur;
1630 mp = mtod(m, u_int8_t *) + m->m_len - 2;
1631 SMBRQ_PUTLE16(mp, mid);
1632 rqp->sr_mid = mid;
1633
1634 smb_rq_wstart(rqp);
1635 smb_rq_wend(rqp);
1636 smb_rq_bstart(rqp);
1637 smb_rq_bend(rqp);
1638
1639 error = (smb_rq_simple(rqp));
1640
1641 /* Discard, there is no real reply */
1642 smb_rq_done(rqp);
1643
1644 return (error);
1645}
1646