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