xref: /netbsd-src/sys/fs/ntfs/ntfs_vnops.c (revision 1ffa7b76c40339c17a0fb2a09fac93f287cfc046)
1 /*	$NetBSD: ntfs_vnops.c,v 1.6 2003/04/10 21:57:26 jdolecek Exp $	*/
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * John Heidemann of the UCLA Ficus project.
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  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *	Id: ntfs_vnops.c,v 1.5 1999/05/12 09:43:06 semenu Exp
39  *
40  */
41 
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: ntfs_vnops.c,v 1.6 2003/04/10 21:57:26 jdolecek Exp $");
44 
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/time.h>
49 #include <sys/stat.h>
50 #include <sys/vnode.h>
51 #include <sys/mount.h>
52 #include <sys/namei.h>
53 #include <sys/malloc.h>
54 #include <sys/buf.h>
55 #include <sys/dirent.h>
56 
57 #if !defined(__NetBSD__)
58 #include <vm/vm.h>
59 #endif
60 
61 #if defined(__FreeBSD__)
62 #include <vm/vnode_pager.h>
63 #endif
64 
65 #include <sys/sysctl.h>
66 
67 
68 /*#define NTFS_DEBUG 1*/
69 #include <fs/ntfs/ntfs.h>
70 #include <fs/ntfs/ntfs_inode.h>
71 #include <fs/ntfs/ntfs_subr.h>
72 #include <miscfs/specfs/specdev.h>
73 #include <miscfs/genfs/genfs.h>
74 
75 #include <sys/unistd.h> /* for pathconf(2) constants */
76 
77 static int	ntfs_bypass __P((struct vop_generic_args *ap));
78 static int	ntfs_read __P((struct vop_read_args *));
79 static int	ntfs_write __P((struct vop_write_args *ap));
80 static int	ntfs_getattr __P((struct vop_getattr_args *ap));
81 static int	ntfs_inactive __P((struct vop_inactive_args *ap));
82 static int	ntfs_print __P((struct vop_print_args *ap));
83 static int	ntfs_reclaim __P((struct vop_reclaim_args *ap));
84 static int	ntfs_strategy __P((struct vop_strategy_args *ap));
85 static int	ntfs_access __P((struct vop_access_args *ap));
86 static int	ntfs_open __P((struct vop_open_args *ap));
87 static int	ntfs_close __P((struct vop_close_args *ap));
88 static int	ntfs_readdir __P((struct vop_readdir_args *ap));
89 static int	ntfs_lookup __P((struct vop_lookup_args *ap));
90 static int	ntfs_bmap __P((struct vop_bmap_args *ap));
91 #if defined(__FreeBSD__)
92 static int	ntfs_getpages __P((struct vop_getpages_args *ap));
93 static int	ntfs_putpages __P((struct vop_putpages_args *));
94 static int	ntfs_fsync __P((struct vop_fsync_args *ap));
95 #endif
96 static int	ntfs_pathconf __P((void *));
97 
98 int	ntfs_prtactive = 1;	/* 1 => print out reclaim of active vnodes */
99 
100 #if defined(__FreeBSD__)
101 int
102 ntfs_getpages(ap)
103 	struct vop_getpages_args *ap;
104 {
105 	return vnode_pager_generic_getpages(ap->a_vp, ap->a_m, ap->a_count,
106 		ap->a_reqpage);
107 }
108 
109 int
110 ntfs_putpages(ap)
111 	struct vop_putpages_args *ap;
112 {
113 	return vnode_pager_generic_putpages(ap->a_vp, ap->a_m, ap->a_count,
114 		ap->a_sync, ap->a_rtvals);
115 }
116 #endif
117 
118 /*
119  * This is a noop, simply returning what one has been given.
120  */
121 int
122 ntfs_bmap(ap)
123 	struct vop_bmap_args /* {
124 		struct vnode *a_vp;
125 		daddr_t  a_bn;
126 		struct vnode **a_vpp;
127 		daddr_t *a_bnp;
128 		int *a_runp;
129 		int *a_runb;
130 	} */ *ap;
131 {
132 	dprintf(("ntfs_bmap: vn: %p, blk: %d\n", ap->a_vp,(u_int32_t)ap->a_bn));
133 	if (ap->a_vpp != NULL)
134 		*ap->a_vpp = ap->a_vp;
135 	if (ap->a_bnp != NULL)
136 		*ap->a_bnp = ap->a_bn;
137 	if (ap->a_runp != NULL)
138 		*ap->a_runp = 0;
139 #if !defined(__NetBSD__)
140 	if (ap->a_runb != NULL)
141 		*ap->a_runb = 0;
142 #endif
143 	return (0);
144 }
145 
146 static int
147 ntfs_read(ap)
148 	struct vop_read_args /* {
149 		struct vnode *a_vp;
150 		struct uio *a_uio;
151 		int a_ioflag;
152 		struct ucred *a_cred;
153 	} */ *ap;
154 {
155 	struct vnode *vp = ap->a_vp;
156 	struct fnode *fp = VTOF(vp);
157 	struct ntnode *ip = FTONT(fp);
158 	struct uio *uio = ap->a_uio;
159 	struct ntfsmount *ntmp = ip->i_mp;
160 	u_int64_t toread;
161 	int error;
162 
163 	dprintf(("ntfs_read: ino: %d, off: %d resid: %d, segflg: %d\n",ip->i_number,(u_int32_t)uio->uio_offset,uio->uio_resid,uio->uio_segflg));
164 
165 	dprintf(("ntfs_read: filesize: %d",(u_int32_t)fp->f_size));
166 
167 	/* don't allow reading after end of file */
168 	if (uio->uio_offset > fp->f_size)
169 		toread = 0;
170 	else
171 		toread = min( uio->uio_resid, fp->f_size - uio->uio_offset );
172 
173 	dprintf((", toread: %d\n",(u_int32_t)toread));
174 
175 	if (toread == 0)
176 		return (0);
177 
178 	error = ntfs_readattr(ntmp, ip, fp->f_attrtype,
179 		fp->f_attrname, uio->uio_offset, toread, NULL, uio);
180 	if (error) {
181 		printf("ntfs_read: ntfs_readattr failed: %d\n",error);
182 		return (error);
183 	}
184 
185 	return (0);
186 }
187 
188 static int
189 ntfs_bypass(ap)
190 	struct vop_generic_args /* {
191 		struct vnodeop_desc *a_desc;
192 		<other random data follows, presumably>
193 	} */ *ap;
194 {
195 	int error = ENOTTY;
196 	dprintf(("ntfs_bypass: %s\n", ap->a_desc->vdesc_name));
197 	return (error);
198 }
199 
200 
201 static int
202 ntfs_getattr(ap)
203 	struct vop_getattr_args /* {
204 		struct vnode *a_vp;
205 		struct vattr *a_vap;
206 		struct ucred *a_cred;
207 		struct proc *a_p;
208 	} */ *ap;
209 {
210 	struct vnode *vp = ap->a_vp;
211 	struct fnode *fp = VTOF(vp);
212 	struct ntnode *ip = FTONT(fp);
213 	struct vattr *vap = ap->a_vap;
214 
215 	dprintf(("ntfs_getattr: %d, flags: %d\n",ip->i_number,ip->i_flag));
216 
217 #if defined(__FreeBSD__)
218 	vap->va_fsid = dev2udev(ip->i_dev);
219 #else /* NetBSD */
220 	vap->va_fsid = ip->i_dev;
221 #endif
222 	vap->va_fileid = ip->i_number;
223 	vap->va_mode = ip->i_mp->ntm_mode;
224 	vap->va_nlink = ip->i_nlink;
225 	vap->va_uid = ip->i_mp->ntm_uid;
226 	vap->va_gid = ip->i_mp->ntm_gid;
227 	vap->va_rdev = 0;				/* XXX UNODEV ? */
228 	vap->va_size = fp->f_size;
229 	vap->va_bytes = fp->f_allocated;
230 	vap->va_atime = ntfs_nttimetounix(fp->f_times.t_access);
231 	vap->va_mtime = ntfs_nttimetounix(fp->f_times.t_write);
232 	vap->va_ctime = ntfs_nttimetounix(fp->f_times.t_create);
233 	vap->va_flags = ip->i_flag;
234 	vap->va_gen = 0;
235 	vap->va_blocksize = ip->i_mp->ntm_spc * ip->i_mp->ntm_bps;
236 	vap->va_type = vp->v_type;
237 	vap->va_filerev = 0;
238 	return (0);
239 }
240 
241 
242 /*
243  * Last reference to an ntnode.  If necessary, write or delete it.
244  */
245 int
246 ntfs_inactive(ap)
247 	struct vop_inactive_args /* {
248 		struct vnode *a_vp;
249 	} */ *ap;
250 {
251 	struct vnode *vp = ap->a_vp;
252 #ifdef NTFS_DEBUG
253 	struct ntnode *ip = VTONT(vp);
254 #endif
255 
256 	dprintf(("ntfs_inactive: vnode: %p, ntnode: %d\n", vp, ip->i_number));
257 
258 	if (ntfs_prtactive && vp->v_usecount != 0)
259 		vprint("ntfs_inactive: pushing active", vp);
260 
261 	VOP_UNLOCK(vp, 0);
262 
263 	/* XXX since we don't support any filesystem changes
264 	 * right now, nothing more needs to be done
265 	 */
266 	return (0);
267 }
268 
269 /*
270  * Reclaim an fnode/ntnode so that it can be used for other purposes.
271  */
272 int
273 ntfs_reclaim(ap)
274 	struct vop_reclaim_args /* {
275 		struct vnode *a_vp;
276 	} */ *ap;
277 {
278 	struct vnode *vp = ap->a_vp;
279 	struct fnode *fp = VTOF(vp);
280 	struct ntnode *ip = FTONT(fp);
281 	int error;
282 
283 	dprintf(("ntfs_reclaim: vnode: %p, ntnode: %d\n", vp, ip->i_number));
284 
285 	if (ntfs_prtactive && vp->v_usecount != 0)
286 		vprint("ntfs_reclaim: pushing active", vp);
287 
288 	if ((error = ntfs_ntget(ip)) != 0)
289 		return (error);
290 
291 	/* Purge old data structures associated with the inode. */
292 	cache_purge(vp);
293 	if (ip->i_devvp) {
294 		vrele(ip->i_devvp);
295 		ip->i_devvp = NULL;
296 	}
297 
298 	ntfs_frele(fp);
299 	ntfs_ntput(ip);
300 	vp->v_data = NULL;
301 
302 	return (0);
303 }
304 
305 static int
306 ntfs_print(ap)
307 	struct vop_print_args /* {
308 		struct vnode *a_vp;
309 	} */ *ap;
310 {
311 	struct ntnode *ip = VTONT(ap->a_vp);
312 
313 	printf("tag VT_NTFS, ino %u, flag %#x, usecount %d, nlink %ld\n",
314 	    ip->i_number, ip->i_flag, ip->i_usecount, ip->i_nlink);
315 	printf("       ");
316 	lockmgr_printinfo(ap->a_vp->v_vnlock);
317 	printf("\n");
318 	return (0);
319 }
320 
321 /*
322  * Calculate the logical to physical mapping if not done already,
323  * then call the device strategy routine.
324  */
325 int
326 ntfs_strategy(ap)
327 	struct vop_strategy_args /* {
328 		struct buf *a_bp;
329 	} */ *ap;
330 {
331 	struct buf *bp = ap->a_bp;
332 	struct vnode *vp = bp->b_vp;
333 	struct fnode *fp = VTOF(vp);
334 	struct ntnode *ip = FTONT(fp);
335 	struct ntfsmount *ntmp = ip->i_mp;
336 	int error;
337 
338 #ifdef __FreeBSD__
339 	dprintf(("ntfs_strategy: offset: %d, blkno: %d, lblkno: %d\n",
340 		(u_int32_t)bp->b_offset,(u_int32_t)bp->b_blkno,
341 		(u_int32_t)bp->b_lblkno));
342 #else
343 	dprintf(("ntfs_strategy: blkno: %d, lblkno: %d\n",
344 		(u_int32_t)bp->b_blkno,
345 		(u_int32_t)bp->b_lblkno));
346 #endif
347 
348 	dprintf(("strategy: bcount: %d flags: 0x%lx\n",
349 		(u_int32_t)bp->b_bcount,bp->b_flags));
350 
351 	if (bp->b_flags & B_READ) {
352 		u_int32_t toread;
353 
354 		if (ntfs_cntob(bp->b_blkno) >= fp->f_size) {
355 			clrbuf(bp);
356 			error = 0;
357 		} else {
358 			toread = min(bp->b_bcount,
359 				 fp->f_size-ntfs_cntob(bp->b_blkno));
360 			dprintf(("ntfs_strategy: toread: %d, fsize: %d\n",
361 				toread,(u_int32_t)fp->f_size));
362 
363 			error = ntfs_readattr(ntmp, ip, fp->f_attrtype,
364 				fp->f_attrname, ntfs_cntob(bp->b_blkno),
365 				toread, bp->b_data, NULL);
366 
367 			if (error) {
368 				printf("ntfs_strategy: ntfs_readattr failed\n");
369 				bp->b_error = error;
370 				bp->b_flags |= B_ERROR;
371 			}
372 
373 			bzero(bp->b_data + toread, bp->b_bcount - toread);
374 		}
375 	} else {
376 		size_t tmp;
377 		u_int32_t towrite;
378 
379 		if (ntfs_cntob(bp->b_blkno) + bp->b_bcount >= fp->f_size) {
380 			printf("ntfs_strategy: CAN'T EXTEND FILE\n");
381 			bp->b_error = error = EFBIG;
382 			bp->b_flags |= B_ERROR;
383 		} else {
384 			towrite = min(bp->b_bcount,
385 				fp->f_size-ntfs_cntob(bp->b_blkno));
386 			dprintf(("ntfs_strategy: towrite: %d, fsize: %d\n",
387 				towrite,(u_int32_t)fp->f_size));
388 
389 			error = ntfs_writeattr_plain(ntmp, ip, fp->f_attrtype,
390 				fp->f_attrname, ntfs_cntob(bp->b_blkno),towrite,
391 				bp->b_data, &tmp, NULL);
392 
393 			if (error) {
394 				printf("ntfs_strategy: ntfs_writeattr fail\n");
395 				bp->b_error = error;
396 				bp->b_flags |= B_ERROR;
397 			}
398 		}
399 	}
400 	biodone(bp);
401 	return (error);
402 }
403 
404 static int
405 ntfs_write(ap)
406 	struct vop_write_args /* {
407 		struct vnode *a_vp;
408 		struct uio *a_uio;
409 		int  a_ioflag;
410 		struct ucred *a_cred;
411 	} */ *ap;
412 {
413 	struct vnode *vp = ap->a_vp;
414 	struct fnode *fp = VTOF(vp);
415 	struct ntnode *ip = FTONT(fp);
416 	struct uio *uio = ap->a_uio;
417 	struct ntfsmount *ntmp = ip->i_mp;
418 	u_int64_t towrite;
419 	size_t written;
420 	int error;
421 
422 	dprintf(("ntfs_write: ino: %d, off: %d resid: %d, segflg: %d\n",ip->i_number,(u_int32_t)uio->uio_offset,uio->uio_resid,uio->uio_segflg));
423 	dprintf(("ntfs_write: filesize: %d",(u_int32_t)fp->f_size));
424 
425 	if (uio->uio_resid + uio->uio_offset > fp->f_size) {
426 		printf("ntfs_write: CAN'T WRITE BEYOND END OF FILE\n");
427 		return (EFBIG);
428 	}
429 
430 	towrite = min(uio->uio_resid, fp->f_size - uio->uio_offset);
431 
432 	dprintf((", towrite: %d\n",(u_int32_t)towrite));
433 
434 	error = ntfs_writeattr_plain(ntmp, ip, fp->f_attrtype,
435 		fp->f_attrname, uio->uio_offset, towrite, NULL, &written, uio);
436 #ifdef NTFS_DEBUG
437 	if (error)
438 		printf("ntfs_write: ntfs_writeattr failed: %d\n", error);
439 #endif
440 
441 	return (error);
442 }
443 
444 int
445 ntfs_access(ap)
446 	struct vop_access_args /* {
447 		struct vnode *a_vp;
448 		int  a_mode;
449 		struct ucred *a_cred;
450 		struct proc *a_p;
451 	} */ *ap;
452 {
453 	struct vnode *vp = ap->a_vp;
454 	struct ntnode *ip = VTONT(vp);
455 	struct ucred *cred = ap->a_cred;
456 	mode_t mask, mode = ap->a_mode;
457 	gid_t *gp;
458 	int i;
459 #ifdef QUOTA
460 	int error;
461 #endif
462 
463 	dprintf(("ntfs_access: %d\n",ip->i_number));
464 
465 	/*
466 	 * Disallow write attempts on read-only file systems;
467 	 * unless the file is a socket, fifo, or a block or
468 	 * character device resident on the file system.
469 	 */
470 	if (mode & VWRITE) {
471 		switch ((int)vp->v_type) {
472 		case VDIR:
473 		case VLNK:
474 		case VREG:
475 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
476 				return (EROFS);
477 #ifdef QUOTA
478 			if (error = getinoquota(ip))
479 				return (error);
480 #endif
481 			break;
482 		}
483 	}
484 
485 	/* Otherwise, user id 0 always gets access. */
486 	if (cred->cr_uid == 0)
487 		return (0);
488 
489 	mask = 0;
490 
491 	/* Otherwise, check the owner. */
492 	if (cred->cr_uid == ip->i_mp->ntm_uid) {
493 		if (mode & VEXEC)
494 			mask |= S_IXUSR;
495 		if (mode & VREAD)
496 			mask |= S_IRUSR;
497 		if (mode & VWRITE)
498 			mask |= S_IWUSR;
499 		return ((ip->i_mp->ntm_mode & mask) == mask ? 0 : EACCES);
500 	}
501 
502 	/* Otherwise, check the groups. */
503 	for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
504 		if (ip->i_mp->ntm_gid == *gp) {
505 			if (mode & VEXEC)
506 				mask |= S_IXGRP;
507 			if (mode & VREAD)
508 				mask |= S_IRGRP;
509 			if (mode & VWRITE)
510 				mask |= S_IWGRP;
511 			return ((ip->i_mp->ntm_mode&mask) == mask ? 0 : EACCES);
512 		}
513 
514 	/* Otherwise, check everyone else. */
515 	if (mode & VEXEC)
516 		mask |= S_IXOTH;
517 	if (mode & VREAD)
518 		mask |= S_IROTH;
519 	if (mode & VWRITE)
520 		mask |= S_IWOTH;
521 	return ((ip->i_mp->ntm_mode & mask) == mask ? 0 : EACCES);
522 }
523 
524 /*
525  * Open called.
526  *
527  * Nothing to do.
528  */
529 /* ARGSUSED */
530 static int
531 ntfs_open(ap)
532 	struct vop_open_args /* {
533 		struct vnode *a_vp;
534 		int  a_mode;
535 		struct ucred *a_cred;
536 		struct proc *a_p;
537 	} */ *ap;
538 {
539 #if NTFS_DEBUG
540 	struct vnode *vp = ap->a_vp;
541 	struct ntnode *ip = VTONT(vp);
542 
543 	printf("ntfs_open: %d\n",ip->i_number);
544 #endif
545 
546 	/*
547 	 * Files marked append-only must be opened for appending.
548 	 */
549 
550 	return (0);
551 }
552 
553 /*
554  * Close called.
555  *
556  * Update the times on the inode.
557  */
558 /* ARGSUSED */
559 static int
560 ntfs_close(ap)
561 	struct vop_close_args /* {
562 		struct vnode *a_vp;
563 		int  a_fflag;
564 		struct ucred *a_cred;
565 		struct proc *a_p;
566 	} */ *ap;
567 {
568 #if NTFS_DEBUG
569 	struct vnode *vp = ap->a_vp;
570 	struct ntnode *ip = VTONT(vp);
571 
572 	printf("ntfs_close: %d\n",ip->i_number);
573 #endif
574 
575 	return (0);
576 }
577 
578 int
579 ntfs_readdir(ap)
580 	struct vop_readdir_args /* {
581 		struct vnode *a_vp;
582 		struct uio *a_uio;
583 		struct ucred *a_cred;
584 		int *a_ncookies;
585 		u_int **cookies;
586 	} */ *ap;
587 {
588 	struct vnode *vp = ap->a_vp;
589 	struct fnode *fp = VTOF(vp);
590 	struct ntnode *ip = FTONT(fp);
591 	struct uio *uio = ap->a_uio;
592 	struct ntfsmount *ntmp = ip->i_mp;
593 	int i, error = 0;
594 	u_int32_t faked = 0, num;
595 	int ncookies = 0;
596 	struct dirent *cde;
597 	off_t off;
598 
599 	dprintf(("ntfs_readdir %d off: %d resid: %d\n",ip->i_number,(u_int32_t)uio->uio_offset,uio->uio_resid));
600 
601 	off = uio->uio_offset;
602 
603 	MALLOC(cde, struct dirent *, sizeof(struct dirent), M_TEMP, M_WAITOK);
604 
605 	/* Simulate . in every dir except ROOT */
606 	if (ip->i_number != NTFS_ROOTINO
607 	    && uio->uio_offset < sizeof(struct dirent)) {
608 		cde->d_fileno = ip->i_number;
609 		cde->d_reclen = sizeof(struct dirent);
610 		cde->d_type = DT_DIR;
611 		cde->d_namlen = 1;
612 		strncpy(cde->d_name, ".", 2);
613 		error = uiomove((void *)cde, sizeof(struct dirent), uio);
614 		if (error)
615 			goto out;
616 
617 		ncookies++;
618 	}
619 
620 	/* Simulate .. in every dir including ROOT */
621 	if (uio->uio_offset < 2 * sizeof(struct dirent)) {
622 		cde->d_fileno = NTFS_ROOTINO;	/* XXX */
623 		cde->d_reclen = sizeof(struct dirent);
624 		cde->d_type = DT_DIR;
625 		cde->d_namlen = 2;
626 		strncpy(cde->d_name, "..", 3);
627 
628 		error = uiomove((void *) cde, sizeof(struct dirent), uio);
629 		if (error)
630 			goto out;
631 
632 		ncookies++;
633 	}
634 
635 	faked = (ip->i_number == NTFS_ROOTINO) ? 1 : 2;
636 	num = uio->uio_offset / sizeof(struct dirent) - faked;
637 
638 	while (uio->uio_resid >= sizeof(struct dirent)) {
639 		struct attr_indexentry *iep;
640 		char *fname;
641 		size_t remains;
642 		int sz;
643 
644 		error = ntfs_ntreaddir(ntmp, fp, num, &iep);
645 		if (error)
646 			goto out;
647 
648 		if (NULL == iep)
649 			break;
650 
651 		for(; !(iep->ie_flag & NTFS_IEFLAG_LAST) && (uio->uio_resid >= sizeof(struct dirent));
652 			iep = NTFS_NEXTREC(iep, struct attr_indexentry *))
653 		{
654 			if(!ntfs_isnamepermitted(ntmp,iep))
655 				continue;
656 
657 			remains = sizeof(cde->d_name) - 1;
658 			fname = cde->d_name;
659 			for(i=0; i<iep->ie_fnamelen; i++) {
660 				sz = (*ntmp->ntm_wput)(fname, remains,
661 						iep->ie_fname[i]);
662 				fname += sz;
663 				remains -= sz;
664 			}
665 			*fname = '\0';
666 			dprintf(("ntfs_readdir: elem: %d, fname:[%s] type: %d, flag: %d, ",
667 				num, cde->d_name, iep->ie_fnametype,
668 				iep->ie_flag));
669 			cde->d_namlen = fname - (char *) cde->d_name;
670 			cde->d_fileno = iep->ie_number;
671 			cde->d_type = (iep->ie_fflag & NTFS_FFLAG_DIR) ? DT_DIR : DT_REG;
672 			cde->d_reclen = sizeof(struct dirent);
673 			dprintf(("%s\n", (cde->d_type == DT_DIR) ? "dir":"reg"));
674 
675 			error = uiomove((void *)cde, sizeof(struct dirent), uio);
676 			if (error)
677 				goto out;
678 
679 			ncookies++;
680 			num++;
681 		}
682 	}
683 
684 	dprintf(("ntfs_readdir: %d entries (%d bytes) read\n",
685 		ncookies,(u_int)(uio->uio_offset - off)));
686 	dprintf(("ntfs_readdir: off: %d resid: %d\n",
687 		(u_int32_t)uio->uio_offset,uio->uio_resid));
688 
689 	if (!error && ap->a_ncookies != NULL) {
690 		struct dirent* dpStart;
691 		struct dirent* dp;
692 #if defined(__FreeBSD__)
693 		u_long *cookies;
694 		u_long *cookiep;
695 #else /* defined(__NetBSD__) */
696 		off_t *cookies;
697 		off_t *cookiep;
698 #endif
699 
700 		printf("ntfs_readdir: %d cookies\n",ncookies);
701 		if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
702 			panic("ntfs_readdir: unexpected uio from NFS server");
703 		dpStart = (struct dirent *)
704 		     ((caddr_t)uio->uio_iov->iov_base -
705 			 (uio->uio_offset - off));
706 #if defined(__FreeBSD__)
707 		MALLOC(cookies, u_long *, ncookies * sizeof(u_long),
708 		       M_TEMP, M_WAITOK);
709 #else /* defined(__NetBSD__) */
710 		cookies = malloc(ncookies * sizeof(off_t), M_TEMP, M_WAITOK);
711 #endif
712 		for (dp = dpStart, cookiep = cookies, i=0;
713 		     i < ncookies;
714 		     dp = (struct dirent *)((caddr_t) dp + dp->d_reclen), i++) {
715 			off += dp->d_reclen;
716 			*cookiep++ = (u_int) off;
717 		}
718 		*ap->a_ncookies = ncookies;
719 		*ap->a_cookies = cookies;
720 	}
721 /*
722 	if (ap->a_eofflag)
723 	    *ap->a_eofflag = VTONT(ap->a_vp)->i_size <= uio->uio_offset;
724 */
725     out:
726 	FREE(cde, M_TEMP);
727 	return (error);
728 }
729 
730 int
731 ntfs_lookup(ap)
732 	struct vop_lookup_args /* {
733 		struct vnode *a_dvp;
734 		struct vnode **a_vpp;
735 		struct componentname *a_cnp;
736 	} */ *ap;
737 {
738 	struct vnode *dvp = ap->a_dvp;
739 	struct ntnode *dip = VTONT(dvp);
740 	struct ntfsmount *ntmp = dip->i_mp;
741 	struct componentname *cnp = ap->a_cnp;
742 	struct ucred *cred = cnp->cn_cred;
743 	int error;
744 	int lockparent = cnp->cn_flags & LOCKPARENT;
745 #if NTFS_DEBUG
746 	int wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
747 #endif
748 	dprintf(("ntfs_lookup: \"%.*s\" (%ld bytes) in %d, lp: %d, wp: %d \n",
749 		(int)cnp->cn_namelen, cnp->cn_nameptr, cnp->cn_namelen,
750 		dip->i_number, lockparent, wantparent));
751 
752 	error = VOP_ACCESS(dvp, VEXEC, cred, cnp->cn_proc);
753 	if(error)
754 		return (error);
755 
756 	if ((cnp->cn_flags & ISLASTCN) &&
757 	    (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
758 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
759 		return (EROFS);
760 
761 #ifdef __NetBSD__
762 	/*
763 	 * We now have a segment name to search for, and a directory
764 	 * to search.
765 	 *
766 	 * Before tediously performing a linear scan of the directory,
767 	 * check the name cache to see if the directory/name pair
768 	 * we are looking for is known already.
769 	 */
770 	if ((error = cache_lookup(ap->a_dvp, ap->a_vpp, cnp)) >= 0)
771 		return (error);
772 #endif
773 
774 	if(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
775 		dprintf(("ntfs_lookup: faking . directory in %d\n",
776 			dip->i_number));
777 
778 		VREF(dvp);
779 		*ap->a_vpp = dvp;
780 		error = 0;
781 	} else if (cnp->cn_flags & ISDOTDOT) {
782 		struct ntvattr *vap;
783 
784 		dprintf(("ntfs_lookup: faking .. directory in %d\n",
785 			 dip->i_number));
786 
787 		VOP_UNLOCK(dvp, 0);
788 		cnp->cn_flags |= PDIRUNLOCK;
789 
790 		error = ntfs_ntvattrget(ntmp, dip, NTFS_A_NAME, NULL, 0, &vap);
791 		if(error)
792 			return (error);
793 
794 		dprintf(("ntfs_lookup: parentdir: %d\n",
795 			 vap->va_a_name->n_pnumber));
796 		error = VFS_VGET(ntmp->ntm_mountp,
797 				 vap->va_a_name->n_pnumber,ap->a_vpp);
798 		ntfs_ntvattrrele(vap);
799 		if (error) {
800 			if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY) == 0)
801 				cnp->cn_flags &= ~PDIRUNLOCK;
802 			return (error);
803 		}
804 
805 		if (lockparent && (cnp->cn_flags & ISLASTCN)) {
806 			error = vn_lock(dvp, LK_EXCLUSIVE);
807 			if (error) {
808 				vput( *(ap->a_vpp) );
809 				return (error);
810 			}
811 			cnp->cn_flags &= ~PDIRUNLOCK;
812 		}
813 	} else {
814 		error = ntfs_ntlookupfile(ntmp, dvp, cnp, ap->a_vpp);
815 		if (error) {
816 			dprintf(("ntfs_ntlookupfile: returned %d\n", error));
817 			return (error);
818 		}
819 
820 		dprintf(("ntfs_lookup: found ino: %d\n",
821 			VTONT(*ap->a_vpp)->i_number));
822 
823 		if(!lockparent || (cnp->cn_flags & ISLASTCN) == 0) {
824 			VOP_UNLOCK(dvp, 0);
825 			cnp->cn_flags |= PDIRUNLOCK;
826 		}
827 	}
828 
829 	if (cnp->cn_flags & MAKEENTRY)
830 		cache_enter(dvp, *ap->a_vpp, cnp);
831 
832 	return (error);
833 }
834 
835 #if defined(__FreeBSD__)
836 /*
837  * Flush the blocks of a file to disk.
838  *
839  * This function is worthless for vnodes that represent directories. Maybe we
840  * could just do a sync if they try an fsync on a directory file.
841  */
842 static int
843 ntfs_fsync(ap)
844 	struct vop_fsync_args /* {
845 		struct vnode *a_vp;
846 		struct ucred *a_cred;
847 		int a_waitfor;
848 		off_t offlo;
849 		off_t offhi;
850 		struct proc *a_p;
851 	} */ *ap;
852 {
853 	return (0);
854 }
855 #endif
856 
857 /*
858  * Return POSIX pathconf information applicable to NTFS filesystem
859  */
860 static int
861 ntfs_pathconf(v)
862 	void *v;
863 {
864 	struct vop_pathconf_args /* {
865 		struct vnode *a_vp;
866 		int a_name;
867 		register_t *a_retval;
868 	} */ *ap = v;
869 
870 	switch (ap->a_name) {
871 	case _PC_LINK_MAX:
872 		*ap->a_retval = 1;
873 		return (0);
874 	case _PC_NAME_MAX:
875 		*ap->a_retval = NTFS_MAXFILENAME;
876 		return (0);
877 	case _PC_PATH_MAX:
878 		*ap->a_retval = PATH_MAX;
879 		return (0);
880 	case _PC_CHOWN_RESTRICTED:
881 		*ap->a_retval = 1;
882 		return (0);
883 	case _PC_NO_TRUNC:
884 		*ap->a_retval = 0;
885 		return (0);
886 	case _PC_SYNC_IO:
887 		*ap->a_retval = 1;
888 		return (0);
889 	case _PC_FILESIZEBITS:
890 		*ap->a_retval = 64;
891 		return (0);
892 	default:
893 		return (EINVAL);
894 	}
895 	/* NOTREACHED */
896 }
897 
898 /*
899  * Global vfs data structures
900  */
901 vop_t **ntfs_vnodeop_p;
902 #if defined(__FreeBSD__)
903 static
904 struct vnodeopv_entry_desc ntfs_vnodeop_entries[] = {
905 	{ &vop_default_desc, (vop_t *)ntfs_bypass },
906 
907 	{ &vop_getattr_desc, (vop_t *)ntfs_getattr },
908 	{ &vop_inactive_desc, (vop_t *)ntfs_inactive },
909 	{ &vop_reclaim_desc, (vop_t *)ntfs_reclaim },
910 	{ &vop_print_desc, (vop_t *)ntfs_print },
911 	{ &vop_pathconf_desc, ntfs_pathconf },
912 
913 	{ &vop_islocked_desc, (vop_t *)vop_stdislocked },
914 	{ &vop_unlock_desc, (vop_t *)vop_stdunlock },
915 	{ &vop_lock_desc, (vop_t *)vop_stdlock },
916 	{ &vop_cachedlookup_desc, (vop_t *)ntfs_lookup },
917 	{ &vop_lookup_desc, (vop_t *)vfs_cache_lookup },
918 
919 	{ &vop_access_desc, (vop_t *)ntfs_access },
920 	{ &vop_close_desc, (vop_t *)ntfs_close },
921 	{ &vop_open_desc, (vop_t *)ntfs_open },
922 	{ &vop_readdir_desc, (vop_t *)ntfs_readdir },
923 	{ &vop_fsync_desc, (vop_t *)ntfs_fsync },
924 
925 	{ &vop_bmap_desc, (vop_t *)ntfs_bmap },
926 	{ &vop_getpages_desc, (vop_t *) ntfs_getpages },
927 	{ &vop_putpages_desc, (vop_t *) ntfs_putpages },
928 	{ &vop_strategy_desc, (vop_t *)ntfs_strategy },
929 	{ &vop_bwrite_desc, (vop_t *)vop_stdbwrite },
930 	{ &vop_read_desc, (vop_t *)ntfs_read },
931 	{ &vop_write_desc, (vop_t *)ntfs_write },
932 
933 	{ NULL, NULL }
934 };
935 
936 static
937 struct vnodeopv_desc ntfs_vnodeop_opv_desc =
938 	{ &ntfs_vnodeop_p, ntfs_vnodeop_entries };
939 
940 VNODEOP_SET(ntfs_vnodeop_opv_desc);
941 
942 #else /* !FreeBSD */
943 
944 const struct vnodeopv_entry_desc ntfs_vnodeop_entries[] = {
945 	{ &vop_default_desc, (vop_t *) ntfs_bypass },
946 	{ &vop_lookup_desc, (vop_t *) ntfs_lookup },	/* lookup */
947 	{ &vop_create_desc, genfs_eopnotsupp },		/* create */
948 	{ &vop_mknod_desc, genfs_eopnotsupp },		/* mknod */
949 	{ &vop_open_desc, (vop_t *) ntfs_open },	/* open */
950 	{ &vop_close_desc,(vop_t *)  ntfs_close },	/* close */
951 	{ &vop_access_desc, (vop_t *) ntfs_access },	/* access */
952 	{ &vop_getattr_desc, (vop_t *) ntfs_getattr },	/* getattr */
953 	{ &vop_setattr_desc, genfs_eopnotsupp },	/* setattr */
954 	{ &vop_read_desc, (vop_t *) ntfs_read },	/* read */
955 	{ &vop_write_desc, (vop_t *) ntfs_write },	/* write */
956 	{ &vop_lease_desc, genfs_lease_check },		/* lease */
957 	{ &vop_fcntl_desc, genfs_fcntl },		/* fcntl */
958 	{ &vop_ioctl_desc, genfs_enoioctl },		/* ioctl */
959 	{ &vop_poll_desc, genfs_poll },			/* poll */
960 	{ &vop_kqfilter_desc, genfs_kqfilter },		/* kqfilter */
961 	{ &vop_revoke_desc, genfs_revoke },		/* revoke */
962 	{ &vop_mmap_desc, genfs_mmap },			/* mmap */
963 	{ &vop_fsync_desc, genfs_fsync },		/* fsync */
964 	{ &vop_seek_desc, genfs_seek },			/* seek */
965 	{ &vop_remove_desc, genfs_eopnotsupp },		/* remove */
966 	{ &vop_link_desc, genfs_eopnotsupp },		/* link */
967 	{ &vop_rename_desc, genfs_eopnotsupp },		/* rename */
968 	{ &vop_mkdir_desc, genfs_eopnotsupp },		/* mkdir */
969 	{ &vop_rmdir_desc, genfs_eopnotsupp },		/* rmdir */
970 	{ &vop_symlink_desc, genfs_eopnotsupp },	/* symlink */
971 	{ &vop_readdir_desc, (vop_t *) ntfs_readdir },	/* readdir */
972 	{ &vop_readlink_desc, genfs_eopnotsupp },	/* readlink */
973 	{ &vop_abortop_desc, genfs_abortop },		/* abortop */
974 	{ &vop_inactive_desc, (vop_t *) ntfs_inactive },	/* inactive */
975 	{ &vop_reclaim_desc, (vop_t *) ntfs_reclaim },	/* reclaim */
976 	{ &vop_lock_desc, genfs_lock },			/* lock */
977 	{ &vop_unlock_desc, genfs_unlock },		/* unlock */
978 	{ &vop_bmap_desc, (vop_t *) ntfs_bmap },	/* bmap */
979 	{ &vop_strategy_desc, (vop_t *) ntfs_strategy },	/* strategy */
980 	{ &vop_print_desc, (vop_t *) ntfs_print },	/* print */
981 	{ &vop_islocked_desc, genfs_islocked },		/* islocked */
982 	{ &vop_pathconf_desc, ntfs_pathconf },		/* pathconf */
983 	{ &vop_advlock_desc, genfs_nullop },		/* advlock */
984 	{ &vop_blkatoff_desc, genfs_eopnotsupp },	/* blkatoff */
985 	{ &vop_valloc_desc, genfs_eopnotsupp },		/* valloc */
986 	{ &vop_reallocblks_desc, genfs_eopnotsupp },	/* reallocblks */
987 	{ &vop_vfree_desc, genfs_eopnotsupp },		/* vfree */
988 	{ &vop_truncate_desc, genfs_eopnotsupp },	/* truncate */
989 	{ &vop_update_desc, genfs_eopnotsupp },		/* update */
990 	{ &vop_bwrite_desc, vn_bwrite },		/* bwrite */
991 	{ &vop_getpages_desc, genfs_compat_getpages },	/* getpages */
992 	{ &vop_putpages_desc, genfs_putpages },		/* putpages */
993 	{ NULL, NULL }
994 };
995 const struct vnodeopv_desc ntfs_vnodeop_opv_desc =
996 	{ &ntfs_vnodeop_p, ntfs_vnodeop_entries };
997 
998 #endif
999