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