xref: /openbsd-src/sys/ntfs/ntfs_vnops.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: ntfs_vnops.c,v 1.24 2011/07/04 20:35:35 deraadt Exp $	*/
2 /*	$NetBSD: ntfs_vnops.c,v 1.6 2003/04/10 21:57:26 jdolecek Exp $	*/
3 
4 /*
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * John Heidemann of the UCLA Ficus project.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *	Id: ntfs_vnops.c,v 1.5 1999/05/12 09:43:06 semenu Exp
36  *
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/time.h>
43 #include <sys/stat.h>
44 #include <sys/vnode.h>
45 #include <sys/mount.h>
46 #include <sys/namei.h>
47 #include <sys/malloc.h>
48 #include <sys/buf.h>
49 #include <sys/dirent.h>
50 #include <sys/specdev.h>
51 
52 /*#define NTFS_DEBUG 1*/
53 #include <ntfs/ntfs.h>
54 #include <ntfs/ntfs_inode.h>
55 #include <ntfs/ntfs_subr.h>
56 
57 #include <sys/unistd.h> /* for pathconf(2) constants */
58 
59 static int	ntfs_read(void *);
60 static int	ntfs_write(void *);
61 static int	ntfs_getattr(void *);
62 static int	ntfs_inactive(void *);
63 static int	ntfs_print(void *);
64 static int	ntfs_reclaim(void *);
65 static int	ntfs_strategy(void *);
66 static int	ntfs_access(void *);
67 static int	ntfs_open(void *);
68 static int	ntfs_close(void *);
69 static int	ntfs_readdir(void *);
70 static int	ntfs_lookup(void *);
71 static int	ntfs_bmap(void *);
72 static int	ntfs_fsync(void *);
73 static int	ntfs_pathconf(void *);
74 
75 int	ntfs_prtactive = 1;	/* 1 => print out reclaim of active vnodes */
76 
77 /*
78  * This is a noop, simply returning what one has been given.
79  */
80 int
81 ntfs_bmap(void *v)
82 {
83 	struct vop_bmap_args *ap = v;
84 	dprintf(("ntfs_bmap: vn: %p, blk: %d\n", ap->a_vp,(u_int32_t)ap->a_bn));
85 	if (ap->a_vpp != NULL)
86 		*ap->a_vpp = ap->a_vp;
87 	if (ap->a_bnp != NULL)
88 		*ap->a_bnp = ap->a_bn;
89 	if (ap->a_runp != NULL)
90 		*ap->a_runp = 0;
91 	return (0);
92 }
93 
94 static int
95 ntfs_read(void *v)
96 {
97 	struct vop_read_args *ap = v;
98 	struct vnode *vp = ap->a_vp;
99 	struct fnode *fp = VTOF(vp);
100 	struct ntnode *ip = FTONT(fp);
101 	struct uio *uio = ap->a_uio;
102 	struct ntfsmount *ntmp = ip->i_mp;
103 	u_int64_t toread;
104 	int error;
105 
106 	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));
107 
108 	dprintf(("ntfs_read: filesize: %d",(u_int32_t)fp->f_size));
109 
110 	/* don't allow reading after end of file */
111 	if (uio->uio_offset > fp->f_size)
112 		toread = 0;
113 	else
114 		toread = MIN(uio->uio_resid, fp->f_size - uio->uio_offset );
115 
116 	dprintf((", toread: %d\n",(u_int32_t)toread));
117 
118 	if (toread == 0)
119 		return (0);
120 
121 	error = ntfs_readattr(ntmp, ip, fp->f_attrtype,
122 		fp->f_attrname, uio->uio_offset, toread, NULL, uio);
123 	if (error) {
124 		printf("ntfs_read: ntfs_readattr failed: %d\n",error);
125 		return (error);
126 	}
127 
128 	return (0);
129 }
130 
131 static int
132 ntfs_getattr(void *v)
133 {
134 	struct vop_getattr_args *ap = v;
135 	struct vnode *vp = ap->a_vp;
136 	struct fnode *fp = VTOF(vp);
137 	struct ntnode *ip = FTONT(fp);
138 	struct vattr *vap = ap->a_vap;
139 
140 	dprintf(("ntfs_getattr: %d, flags: %d\n",ip->i_number,ip->i_flag));
141 
142 	vap->va_fsid = ip->i_dev;
143 	vap->va_fileid = ip->i_number;
144 	vap->va_mode = ip->i_mp->ntm_mode;
145 	vap->va_nlink = ip->i_nlink;
146 	vap->va_uid = ip->i_mp->ntm_uid;
147 	vap->va_gid = ip->i_mp->ntm_gid;
148 	vap->va_rdev = 0;				/* XXX UNODEV ? */
149 	vap->va_size = fp->f_size;
150 	vap->va_bytes = fp->f_allocated;
151 	vap->va_atime = ntfs_nttimetounix(fp->f_times.t_access);
152 	vap->va_mtime = ntfs_nttimetounix(fp->f_times.t_write);
153 	vap->va_ctime = ntfs_nttimetounix(fp->f_times.t_create);
154 	vap->va_flags = ip->i_flag;
155 	vap->va_gen = 0;
156 	vap->va_blocksize = ip->i_mp->ntm_spc * ip->i_mp->ntm_bps;
157 	vap->va_type = vp->v_type;
158 	vap->va_filerev = 0;
159 	return (0);
160 }
161 
162 
163 /*
164  * Last reference to an ntnode.  If necessary, write or delete it.
165  */
166 int
167 ntfs_inactive(void *v)
168 {
169 	struct vop_inactive_args *ap = v;
170 	struct vnode *vp = ap->a_vp;
171 	struct proc *p = ap->a_p;
172 #ifdef NTFS_DEBUG
173 	struct ntnode *ip = VTONT(vp);
174 #endif
175 
176 	dprintf(("ntfs_inactive: vnode: %p, ntnode: %d\n", vp, ip->i_number));
177 
178 #ifdef DIAGNOSTIC
179 	if (ntfs_prtactive && vp->v_usecount != 0)
180 		vprint("ntfs_inactive: pushing active", vp);
181 #endif
182 
183 	VOP_UNLOCK(vp, 0, p);
184 
185 	/* XXX since we don't support any filesystem changes
186 	 * right now, nothing more needs to be done
187 	 */
188 	return (0);
189 }
190 
191 /*
192  * Reclaim an fnode/ntnode so that it can be used for other purposes.
193  */
194 int
195 ntfs_reclaim(void *v)
196 {
197 	struct vop_reclaim_args *ap = v;
198 	struct vnode *vp = ap->a_vp;
199 	struct fnode *fp = VTOF(vp);
200 	struct ntnode *ip = FTONT(fp);
201 	struct proc *p = ap->a_p;
202 	int error;
203 
204 	dprintf(("ntfs_reclaim: vnode: %p, ntnode: %d\n", vp, ip->i_number));
205 
206 #ifdef DIAGNOSTIC
207 	if (ntfs_prtactive && vp->v_usecount != 0)
208 		vprint("ntfs_reclaim: pushing active", vp);
209 #endif
210 
211 	if ((error = ntfs_ntget(ip, p)) != 0)
212 		return (error);
213 
214 	/* Purge old data structures associated with the inode. */
215 	cache_purge(vp);
216 
217 	ntfs_frele(fp);
218 	ntfs_ntput(ip, p);
219 
220 	vp->v_data = NULL;
221 
222 	return (0);
223 }
224 
225 static int
226 ntfs_print(void *v)
227 {
228 	struct vop_print_args *ap = v;
229 	struct ntnode *ip = VTONT(ap->a_vp);
230 
231 	printf("tag VT_NTFS, ino %u, flag %#x, usecount %d, nlink %ld\n",
232 	    ip->i_number, ip->i_flag, ip->i_usecount, ip->i_nlink);
233 
234 	return (0);
235 }
236 
237 /*
238  * Calculate the logical to physical mapping if not done already,
239  * then call the device strategy routine.
240  */
241 int
242 ntfs_strategy(void *v)
243 {
244 	struct vop_strategy_args *ap = v;
245 	struct buf *bp = ap->a_bp;
246 	struct vnode *vp = bp->b_vp;
247 	struct fnode *fp = VTOF(vp);
248 	struct ntnode *ip = FTONT(fp);
249 	struct ntfsmount *ntmp = ip->i_mp;
250 	int error, s;
251 
252 	dprintf(("ntfs_strategy: blkno: %d, lblkno: %d\n",
253 		(u_int32_t)bp->b_blkno,
254 		(u_int32_t)bp->b_lblkno));
255 
256 	dprintf(("strategy: bcount: %u flags: 0x%x\n",
257 		(u_int32_t)bp->b_bcount,bp->b_flags));
258 
259 	if (bp->b_flags & B_READ) {
260 		u_int32_t toread;
261 
262 		if (ntfs_cntob(bp->b_blkno) >= fp->f_size) {
263 			clrbuf(bp);
264 			error = 0;
265 		} else {
266 			toread = MIN(bp->b_bcount,
267 				 fp->f_size - ntfs_cntob(bp->b_blkno));
268 			dprintf(("ntfs_strategy: toread: %d, fsize: %d\n",
269 				toread,(u_int32_t)fp->f_size));
270 
271 			error = ntfs_readattr(ntmp, ip, fp->f_attrtype,
272 				fp->f_attrname, ntfs_cntob(bp->b_blkno),
273 				toread, bp->b_data, NULL);
274 
275 			if (error) {
276 				printf("ntfs_strategy: ntfs_readattr failed\n");
277 				bp->b_error = error;
278 				bp->b_flags |= B_ERROR;
279 			}
280 
281 			bzero(bp->b_data + toread, bp->b_bcount - toread);
282 		}
283 	} else {
284 		size_t tmp;
285 		u_int32_t towrite;
286 
287 		if (ntfs_cntob(bp->b_blkno) + bp->b_bcount >= fp->f_size) {
288 			printf("ntfs_strategy: CAN'T EXTEND FILE\n");
289 			bp->b_error = error = EFBIG;
290 			bp->b_flags |= B_ERROR;
291 		} else {
292 			towrite = MIN(bp->b_bcount,
293 				fp->f_size - ntfs_cntob(bp->b_blkno));
294 			dprintf(("ntfs_strategy: towrite: %d, fsize: %d\n",
295 				towrite,(u_int32_t)fp->f_size));
296 
297 			error = ntfs_writeattr_plain(ntmp, ip, fp->f_attrtype,
298 				fp->f_attrname, ntfs_cntob(bp->b_blkno),towrite,
299 				bp->b_data, &tmp, NULL);
300 
301 			if (error) {
302 				printf("ntfs_strategy: ntfs_writeattr fail\n");
303 				bp->b_error = error;
304 				bp->b_flags |= B_ERROR;
305 			}
306 		}
307 	}
308 	s = splbio();
309 	biodone(bp);
310 	splx(s);
311 	return (error);
312 }
313 
314 static int
315 ntfs_write(void *v)
316 {
317 	struct vop_write_args *ap = v;
318 	struct vnode *vp = ap->a_vp;
319 	struct fnode *fp = VTOF(vp);
320 	struct ntnode *ip = FTONT(fp);
321 	struct uio *uio = ap->a_uio;
322 	struct ntfsmount *ntmp = ip->i_mp;
323 	u_int64_t towrite;
324 	size_t written;
325 	int error;
326 
327 	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));
328 	dprintf(("ntfs_write: filesize: %d",(u_int32_t)fp->f_size));
329 
330 	if (uio->uio_resid + uio->uio_offset > fp->f_size) {
331 		printf("ntfs_write: CAN'T WRITE BEYOND END OF FILE\n");
332 		return (EFBIG);
333 	}
334 
335 	towrite = MIN(uio->uio_resid, fp->f_size - uio->uio_offset);
336 
337 	dprintf((", towrite: %d\n",(u_int32_t)towrite));
338 
339 	error = ntfs_writeattr_plain(ntmp, ip, fp->f_attrtype,
340 		fp->f_attrname, uio->uio_offset, towrite, NULL, &written, uio);
341 #ifdef NTFS_DEBUG
342 	if (error)
343 		printf("ntfs_write: ntfs_writeattr failed: %d\n", error);
344 #endif
345 
346 	return (error);
347 }
348 
349 int
350 ntfs_access(void *v)
351 {
352 	struct vop_access_args *ap = v;
353 	struct vnode *vp = ap->a_vp;
354 	struct ntnode *ip = VTONT(vp);
355 	struct ucred *cred = ap->a_cred;
356 	mode_t mask, mode = ap->a_mode;
357 	gid_t *gp;
358 	int i;
359 
360 	dprintf(("ntfs_access: %d\n",ip->i_number));
361 
362 	/*
363 	 * Disallow write attempts on read-only file systems;
364 	 * unless the file is a socket, fifo, or a block or
365 	 * character device resident on the file system.
366 	 */
367 	if (mode & VWRITE) {
368 		switch ((int)vp->v_type) {
369 		case VDIR:
370 		case VLNK:
371 		case VREG:
372 			if (vp->v_mount->mnt_flag & MNT_RDONLY)
373 				return (EROFS);
374 			break;
375 		}
376 	}
377 
378 	/* Otherwise, user id 0 always gets access. */
379 	if (cred->cr_uid == 0)
380 		return (0);
381 
382 	mask = 0;
383 
384 	/* Otherwise, check the owner. */
385 	if (cred->cr_uid == ip->i_mp->ntm_uid) {
386 		if (mode & VEXEC)
387 			mask |= S_IXUSR;
388 		if (mode & VREAD)
389 			mask |= S_IRUSR;
390 		if (mode & VWRITE)
391 			mask |= S_IWUSR;
392 		return ((ip->i_mp->ntm_mode & mask) == mask ? 0 : EACCES);
393 	}
394 
395 	/* Otherwise, check the groups. */
396 	for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
397 		if (ip->i_mp->ntm_gid == *gp) {
398 			if (mode & VEXEC)
399 				mask |= S_IXGRP;
400 			if (mode & VREAD)
401 				mask |= S_IRGRP;
402 			if (mode & VWRITE)
403 				mask |= S_IWGRP;
404 			return ((ip->i_mp->ntm_mode&mask) == mask ? 0 : EACCES);
405 		}
406 
407 	/* Otherwise, check everyone else. */
408 	if (mode & VEXEC)
409 		mask |= S_IXOTH;
410 	if (mode & VREAD)
411 		mask |= S_IROTH;
412 	if (mode & VWRITE)
413 		mask |= S_IWOTH;
414 	return ((ip->i_mp->ntm_mode & mask) == mask ? 0 : EACCES);
415 }
416 
417 /*
418  * Open called.
419  *
420  * Nothing to do.
421  */
422 /* ARGSUSED */
423 static int
424 ntfs_open(void *v)
425 {
426 #if NTFS_DEBUG
427 	struct vop_open_args *ap = v;
428 	struct vnode *vp = ap->a_vp;
429 	struct ntnode *ip = VTONT(vp);
430 
431 	printf("ntfs_open: %d\n",ip->i_number);
432 #endif
433 
434 	/*
435 	 * Files marked append-only must be opened for appending.
436 	 */
437 
438 	return (0);
439 }
440 
441 /*
442  * Close called.
443  *
444  * Update the times on the inode.
445  */
446 /* ARGSUSED */
447 static int
448 ntfs_close(void *v)
449 {
450 #if NTFS_DEBUG
451 	struct vop_close_args *ap = v;
452 	struct vnode *vp = ap->a_vp;
453 	struct ntnode *ip = VTONT(vp);
454 
455 	printf("ntfs_close: %d\n",ip->i_number);
456 #endif
457 
458 	return (0);
459 }
460 
461 int
462 ntfs_readdir(void *v)
463 {
464 	struct vop_readdir_args *ap = v;
465 	struct vnode *vp = ap->a_vp;
466 	struct fnode *fp = VTOF(vp);
467 	struct ntnode *ip = FTONT(fp);
468 	struct uio *uio = ap->a_uio;
469 	struct ntfsmount *ntmp = ip->i_mp;
470 	int i, error = 0;
471 	u_int32_t faked = 0, num;
472 	int ncookies = 0;
473 	struct dirent *cde;
474 	off_t off;
475 
476 	dprintf(("ntfs_readdir %d off: %d resid: %d\n",ip->i_number,(u_int32_t)uio->uio_offset,uio->uio_resid));
477 
478 	off = uio->uio_offset;
479 
480 	cde = malloc(sizeof(struct dirent), M_TEMP, M_WAITOK);
481 
482 	/* Simulate . in every dir except ROOT */
483 	if (ip->i_number != NTFS_ROOTINO
484 	    && uio->uio_offset < sizeof(struct dirent)) {
485 		cde->d_fileno = ip->i_number;
486 		cde->d_reclen = sizeof(struct dirent);
487 		cde->d_type = DT_DIR;
488 		cde->d_namlen = 1;
489 		strncpy(cde->d_name, ".", 2);
490 		error = uiomove((void *)cde, sizeof(struct dirent), uio);
491 		if (error)
492 			goto out;
493 
494 		ncookies++;
495 	}
496 
497 	/* Simulate .. in every dir including ROOT */
498 	if (uio->uio_offset < 2 * sizeof(struct dirent)) {
499 		cde->d_fileno = NTFS_ROOTINO;	/* XXX */
500 		cde->d_reclen = sizeof(struct dirent);
501 		cde->d_type = DT_DIR;
502 		cde->d_namlen = 2;
503 		strncpy(cde->d_name, "..", 3);
504 
505 		error = uiomove((void *) cde, sizeof(struct dirent), uio);
506 		if (error)
507 			goto out;
508 
509 		ncookies++;
510 	}
511 
512 	faked = (ip->i_number == NTFS_ROOTINO) ? 1 : 2;
513 	num = uio->uio_offset / sizeof(struct dirent) - faked;
514 
515 	while (uio->uio_resid >= sizeof(struct dirent)) {
516 		struct attr_indexentry *iep;
517 		char *fname;
518 		size_t remains;
519 		int sz;
520 
521 		error = ntfs_ntreaddir(ntmp, fp, num, &iep, uio->uio_procp);
522 		if (error)
523 			goto out;
524 
525 		if (NULL == iep)
526 			break;
527 
528 		for(; !(iep->ie_flag & NTFS_IEFLAG_LAST) && (uio->uio_resid >= sizeof(struct dirent));
529 			iep = NTFS_NEXTREC(iep, struct attr_indexentry *))
530 		{
531 			if(!ntfs_isnamepermitted(ntmp,iep))
532 				continue;
533 
534 			remains = sizeof(cde->d_name) - 1;
535 			fname = cde->d_name;
536 			for(i=0; i<iep->ie_fnamelen; i++) {
537 				sz = (*ntmp->ntm_wput)(fname, remains,
538 						iep->ie_fname[i]);
539 				fname += sz;
540 				remains -= sz;
541 			}
542 			*fname = '\0';
543 			dprintf(("ntfs_readdir: elem: %d, fname:[%s] type: %d, flag: %d, ",
544 				num, cde->d_name, iep->ie_fnametype,
545 				iep->ie_flag));
546 			cde->d_namlen = fname - (char *) cde->d_name;
547 			cde->d_fileno = iep->ie_number;
548 			cde->d_type = (iep->ie_fflag & NTFS_FFLAG_DIR) ? DT_DIR : DT_REG;
549 			cde->d_reclen = sizeof(struct dirent);
550 			dprintf(("%s\n", (cde->d_type == DT_DIR) ? "dir":"reg"));
551 
552 			error = uiomove((void *)cde, sizeof(struct dirent), uio);
553 			if (error)
554 				goto out;
555 
556 			ncookies++;
557 			num++;
558 		}
559 	}
560 
561 	dprintf(("ntfs_readdir: %d entries (%d bytes) read\n",
562 		ncookies,(u_int)(uio->uio_offset - off)));
563 	dprintf(("ntfs_readdir: off: %d resid: %d\n",
564 		(u_int32_t)uio->uio_offset,uio->uio_resid));
565 
566 	if (!error && ap->a_ncookies != NULL) {
567 		struct dirent* dpStart;
568 		struct dirent* dp;
569 		u_long *cookies;
570 		u_long *cookiep;
571 
572 		dprintf(("ntfs_readdir: %d cookies\n",ncookies));
573 		if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
574 			panic("ntfs_readdir: unexpected uio from NFS server");
575 		dpStart = (struct dirent *)
576 		     ((caddr_t)uio->uio_iov->iov_base -
577 			 (uio->uio_offset - off));
578 		cookies = malloc(ncookies * sizeof(*cookies), M_TEMP, M_WAITOK);
579 		for (dp = dpStart, cookiep = cookies, i=0;
580 		     i < ncookies;
581 		     dp = (struct dirent *)((caddr_t) dp + dp->d_reclen), i++) {
582 			off += dp->d_reclen;
583 			*cookiep++ = off;
584 		}
585 		*ap->a_ncookies = ncookies;
586 		*ap->a_cookies = cookies;
587 	}
588 /*
589 	if (ap->a_eofflag)
590 	    *ap->a_eofflag = VTONT(ap->a_vp)->i_size <= uio->uio_offset;
591 */
592     out:
593 	free(cde, M_TEMP);
594 	return (error);
595 }
596 
597 int
598 ntfs_lookup(void *v)
599 {
600 	struct vop_lookup_args *ap = v;
601 	struct vnode *dvp = ap->a_dvp;
602 	struct ntnode *dip = VTONT(dvp);
603 	struct ntfsmount *ntmp = dip->i_mp;
604 	struct componentname *cnp = ap->a_cnp;
605 	struct ucred *cred = cnp->cn_cred;
606 	int error;
607 	int lockparent = cnp->cn_flags & LOCKPARENT;
608 	struct proc *p = cnp->cn_proc;
609 #if NTFS_DEBUG
610 	int wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
611 #endif
612 	dprintf(("ntfs_lookup: \"%.*s\" (%ld bytes) in %d, lp: %d, wp: %d \n",
613 		(int)cnp->cn_namelen, cnp->cn_nameptr, cnp->cn_namelen,
614 		dip->i_number, lockparent, wantparent));
615 
616 	error = VOP_ACCESS(dvp, VEXEC, cred, cnp->cn_proc);
617 	if(error)
618 		return (error);
619 
620 	if ((cnp->cn_flags & ISLASTCN) &&
621 	    (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
622 	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
623 		return (EROFS);
624 
625 	/*
626 	 * We now have a segment name to search for, and a directory
627 	 * to search.
628 	 *
629 	 * Before tediously performing a linear scan of the directory,
630 	 * check the name cache to see if the directory/name pair
631 	 * we are looking for is known already.
632 	 */
633 	if ((error = cache_lookup(ap->a_dvp, ap->a_vpp, cnp)) >= 0)
634 		return (error);
635 
636 	if(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
637 		dprintf(("ntfs_lookup: faking . directory in %d\n",
638 			dip->i_number));
639 
640 		vref(dvp);
641 		*ap->a_vpp = dvp;
642 		error = 0;
643 	} else if (cnp->cn_flags & ISDOTDOT) {
644 		struct ntvattr *vap;
645 
646 		dprintf(("ntfs_lookup: faking .. directory in %d\n",
647 			 dip->i_number));
648 
649 		VOP_UNLOCK(dvp, 0, p);
650 		cnp->cn_flags |= PDIRUNLOCK;
651 
652 		error = ntfs_ntvattrget(ntmp, dip, NTFS_A_NAME, NULL, 0, &vap);
653 		if(error)
654 			return (error);
655 
656 		dprintf(("ntfs_lookup: parentdir: %d\n",
657 			 vap->va_a_name->n_pnumber));
658 		error = VFS_VGET(ntmp->ntm_mountp,
659 				 vap->va_a_name->n_pnumber,ap->a_vpp);
660 		ntfs_ntvattrrele(vap);
661 		if (error) {
662 			if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
663 				cnp->cn_flags &= ~PDIRUNLOCK;
664 			return (error);
665 		}
666 
667 		if (lockparent && (cnp->cn_flags & ISLASTCN)) {
668 			error = vn_lock(dvp, LK_EXCLUSIVE, p);
669 			if (error) {
670 				vput( *(ap->a_vpp) );
671 				return (error);
672 			}
673 			cnp->cn_flags &= ~PDIRUNLOCK;
674 		}
675 	} else {
676 		error = ntfs_ntlookupfile(ntmp, dvp, cnp, ap->a_vpp, p);
677 		if (error) {
678 			dprintf(("ntfs_ntlookupfile: returned %d\n", error));
679 			return (error);
680 		}
681 
682 		dprintf(("ntfs_lookup: found ino: %d\n",
683 			VTONT(*ap->a_vpp)->i_number));
684 
685 		if(!lockparent || (cnp->cn_flags & ISLASTCN) == 0) {
686 			VOP_UNLOCK(dvp, 0, p);
687 			cnp->cn_flags |= PDIRUNLOCK;
688 		}
689 	}
690 
691 	if (cnp->cn_flags & MAKEENTRY)
692 		cache_enter(dvp, *ap->a_vpp, cnp);
693 
694 	return (error);
695 }
696 
697 /*
698  * Flush the blocks of a file to disk.
699  *
700  * This function is worthless for vnodes that represent directories. Maybe we
701  * could just do a sync if they try an fsync on a directory file.
702  */
703 static int
704 ntfs_fsync(void *v)
705 {
706 	return (0);
707 }
708 
709 /*
710  * Return POSIX pathconf information applicable to NTFS filesystem
711  */
712 static int
713 ntfs_pathconf(void *v)
714 {
715 	struct vop_pathconf_args *ap = v;
716 
717 	switch (ap->a_name) {
718 	case _PC_LINK_MAX:
719 		*ap->a_retval = 1;
720 		return (0);
721 	case _PC_NAME_MAX:
722 		*ap->a_retval = NTFS_MAXFILENAME;
723 		return (0);
724 	case _PC_PATH_MAX:
725 		*ap->a_retval = PATH_MAX;
726 		return (0);
727 	case _PC_CHOWN_RESTRICTED:
728 		*ap->a_retval = 1;
729 		return (0);
730 	case _PC_NO_TRUNC:
731 		*ap->a_retval = 0;
732 		return (0);
733 	default:
734 		return (EINVAL);
735 	}
736 	/* NOTREACHED */
737 }
738 
739 /*
740  * Global vfs data structures
741  */
742 struct vops ntfs_vops = {
743 	.vop_getattr	= ntfs_getattr,
744 	.vop_inactive	= ntfs_inactive,
745 	.vop_reclaim	= ntfs_reclaim,
746 	.vop_print	= ntfs_print,
747 	.vop_pathconf	= ntfs_pathconf,
748 	.vop_islocked	= vop_generic_islocked,
749 	.vop_unlock	= vop_generic_unlock,
750 	.vop_lock	= vop_generic_lock,
751 	.vop_lookup	= ntfs_lookup,
752 	.vop_access	= ntfs_access,
753 	.vop_close	= ntfs_close,
754 	.vop_open	= ntfs_open,
755 	.vop_readdir	= ntfs_readdir,
756 	.vop_fsync	= ntfs_fsync,
757 	.vop_bmap	= ntfs_bmap,
758 	.vop_strategy	= ntfs_strategy,
759 	.vop_bwrite	= vop_generic_bwrite,
760 	.vop_read	= ntfs_read,
761 	.vop_write	= ntfs_write,
762 };
763