1/*	$OpenBSD: ntfs_vnops.c,v 1.49 2024/05/13 11:17:40 semarie 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
59int	ntfs_read(void *);
60int	ntfs_getattr(void *);
61int	ntfs_inactive(void *);
62int	ntfs_print(void *);
63int	ntfs_reclaim(void *);
64int	ntfs_strategy(void *);
65int	ntfs_access(void *v);
66int	ntfs_open(void *v);
67int	ntfs_close(void *);
68int	ntfs_readdir(void *);
69int	ntfs_lookup(void *);
70int	ntfs_bmap(void *);
71int	ntfs_fsync(void *);
72int	ntfs_pathconf(void *);
73
74int	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 */
79int
80ntfs_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
94int
95ntfs_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
132int
133ntfs_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 */
177int
178ntfs_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 */
204int
205ntfs_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
234int
235ntfs_print(void *v)
236{
237	struct vop_print_args *ap = v;
238	struct ntnode *ip = VTONT(ap->a_vp);
239
240	printf("tag VT_NTFS, ino %u, flag %#x, usecount %d, nlink %ld\n",
241	    ip->i_number, ip->i_flag, ip->i_usecount, ip->i_nlink);
242
243	return (0);
244}
245
246/*
247 * Calculate the logical to physical mapping if not done already,
248 * then call the device strategy routine.
249 */
250int
251ntfs_strategy(void *v)
252{
253	struct vop_strategy_args *ap = v;
254	struct buf *bp = ap->a_bp;
255	struct vnode *vp = bp->b_vp;
256	struct fnode *fp = VTOF(vp);
257	struct ntnode *ip = FTONT(fp);
258	struct ntfsmount *ntmp = ip->i_mp;
259	int error, s;
260
261	DPRINTF("ntfs_strategy: blkno: %lld, lblkno: %lld\n",
262	    (long long)bp->b_blkno, (long long)bp->b_lblkno);
263
264	DPRINTF("strategy: bcount: %ld flags: 0x%lx\n",
265	    bp->b_bcount, bp->b_flags);
266
267	if (bp->b_flags & B_READ) {
268		u_int32_t toread;
269
270		if (ntfs_cntob(bp->b_blkno) >= fp->f_size) {
271			clrbuf(bp);
272			error = 0;
273		} else {
274			toread = MIN(bp->b_bcount,
275				 fp->f_size - ntfs_cntob(bp->b_blkno));
276			DPRINTF("ntfs_strategy: toread: %u, fsize: %llu\n",
277			    toread, fp->f_size);
278
279			error = ntfs_readattr(ntmp, ip, fp->f_attrtype,
280				fp->f_attrname, ntfs_cntob(bp->b_blkno),
281				toread, bp->b_data, NULL);
282
283			if (error) {
284				printf("ntfs_strategy: ntfs_readattr failed\n");
285				bp->b_error = error;
286				bp->b_flags |= B_ERROR;
287			}
288
289			bzero(bp->b_data + toread, bp->b_bcount - toread);
290		}
291	} else {
292		bp->b_error = error = EROFS;
293		bp->b_flags |= B_ERROR;
294	}
295	s = splbio();
296	biodone(bp);
297	splx(s);
298	return (error);
299}
300
301int
302ntfs_access(void *v)
303{
304	struct vop_access_args *ap = v;
305	struct vnode *vp = ap->a_vp;
306	struct ntnode *ip = VTONT(vp);
307	struct ucred *cred = ap->a_cred;
308	mode_t mask, mode = ap->a_mode;
309	gid_t *gp;
310	int i;
311
312	DPRINTF("ntfs_access: %u\n", ip->i_number);
313
314	/*
315	 * Disallow write attempts unless the file is a socket, fifo, or
316	 * a block or character device resident on the file system.
317	 */
318	if (mode & VWRITE) {
319		switch ((int)vp->v_type) {
320		case VDIR:
321		case VLNK:
322		case VREG:
323			return (EROFS);
324		}
325	}
326
327	/* Otherwise, user id 0 always gets access. */
328	if (cred->cr_uid == 0)
329		return (0);
330
331	mask = 0;
332
333	/* Otherwise, check the owner. */
334	if (cred->cr_uid == ip->i_mp->ntm_uid) {
335		if (mode & VEXEC)
336			mask |= S_IXUSR;
337		if (mode & VREAD)
338			mask |= S_IRUSR;
339		if (mode & VWRITE)
340			mask |= S_IWUSR;
341		return ((ip->i_mp->ntm_mode & mask) == mask ? 0 : EACCES);
342	}
343
344	/* Otherwise, check the groups. */
345	for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
346		if (ip->i_mp->ntm_gid == *gp) {
347			if (mode & VEXEC)
348				mask |= S_IXGRP;
349			if (mode & VREAD)
350				mask |= S_IRGRP;
351			if (mode & VWRITE)
352				mask |= S_IWGRP;
353			return ((ip->i_mp->ntm_mode&mask) == mask ? 0 : EACCES);
354		}
355
356	/* Otherwise, check everyone else. */
357	if (mode & VEXEC)
358		mask |= S_IXOTH;
359	if (mode & VREAD)
360		mask |= S_IROTH;
361	if (mode & VWRITE)
362		mask |= S_IWOTH;
363	return ((ip->i_mp->ntm_mode & mask) == mask ? 0 : EACCES);
364}
365
366/*
367 * Open called.
368 *
369 * Nothing to do.
370 */
371int
372ntfs_open(void *v)
373{
374#if NTFS_DEBUG
375	struct vop_open_args *ap = v;
376	struct vnode *vp = ap->a_vp;
377	struct ntnode *ip = VTONT(vp);
378
379	printf("ntfs_open: %d\n",ip->i_number);
380#endif
381
382	/*
383	 * Files marked append-only must be opened for appending.
384	 */
385
386	return (0);
387}
388
389/*
390 * Close called.
391 *
392 * Update the times on the inode.
393 */
394int
395ntfs_close(void *v)
396{
397#if NTFS_DEBUG
398	struct vop_close_args *ap = v;
399	struct vnode *vp = ap->a_vp;
400	struct ntnode *ip = VTONT(vp);
401
402	printf("ntfs_close: %d\n",ip->i_number);
403#endif
404
405	return (0);
406}
407
408int
409ntfs_readdir(void *v)
410{
411	struct vop_readdir_args *ap = v;
412	struct vnode *vp = ap->a_vp;
413	struct fnode *fp = VTOF(vp);
414	struct ntnode *ip = FTONT(fp);
415	struct uio *uio = ap->a_uio;
416	struct ntfsmount *ntmp = ip->i_mp;
417	int i, error = 0;
418	u_int32_t faked = 0, num;
419	struct dirent cde;
420	off_t off;
421
422	DPRINTF("ntfs_readdir %u off: %lld resid: %zu\n", ip->i_number,
423	    uio->uio_offset, uio->uio_resid);
424
425	off = uio->uio_offset;
426	memset(&cde, 0, sizeof(cde));
427
428	/* Simulate . in every dir except ROOT */
429	if (ip->i_number != NTFS_ROOTINO && uio->uio_offset == 0) {
430		cde.d_fileno = ip->i_number;
431		cde.d_reclen = sizeof(struct dirent);
432		cde.d_type = DT_DIR;
433		cde.d_namlen = 1;
434		cde.d_off = sizeof(struct dirent);
435		cde.d_name[0] = '.';
436		cde.d_name[1] = '\0';
437		error = uiomove(&cde, sizeof(struct dirent), uio);
438		if (error)
439			goto out;
440	}
441
442	/* Simulate .. in every dir including ROOT */
443	if (uio->uio_offset < 2 * sizeof(struct dirent)) {
444		cde.d_fileno = NTFS_ROOTINO;	/* XXX */
445		cde.d_reclen = sizeof(struct dirent);
446		cde.d_type = DT_DIR;
447		cde.d_namlen = 2;
448		cde.d_off = 2 * sizeof(struct dirent);
449		cde.d_name[0] = '.';
450		cde.d_name[1] = '.';
451		cde.d_name[2] = '\0';
452		error = uiomove(&cde, sizeof(struct dirent), uio);
453		if (error)
454			goto out;
455	}
456
457	faked = (ip->i_number == NTFS_ROOTINO) ? 1 : 2;
458	num = uio->uio_offset / sizeof(struct dirent) - faked;
459
460	while (uio->uio_resid >= sizeof(struct dirent)) {
461		struct attr_indexentry *iep;
462		char *fname;
463		size_t remains;
464		int sz;
465
466		error = ntfs_ntreaddir(ntmp, fp, num, &iep, uio->uio_procp);
467		if (error)
468			goto out;
469
470		if (NULL == iep)
471			break;
472
473		for(; !(iep->ie_flag & NTFS_IEFLAG_LAST) && (uio->uio_resid >= sizeof(struct dirent));
474			iep = NTFS_NEXTREC(iep, struct attr_indexentry *))
475		{
476			if(!ntfs_isnamepermitted(ntmp,iep))
477				continue;
478
479			remains = sizeof(cde.d_name) - 1;
480			fname = cde.d_name;
481			for(i=0; i<iep->ie_fnamelen; i++) {
482				sz = (*ntmp->ntm_wput)(fname, remains,
483						iep->ie_fname[i]);
484				fname += sz;
485				remains -= sz;
486			}
487			*fname = '\0';
488			DPRINTF("ntfs_readdir: elem: %u, fname:[%s] type: %u, "
489			    "flag: %u, ",
490			    num, cde.d_name, iep->ie_fnametype, iep->ie_flag);
491			cde.d_namlen = fname - (char *) cde.d_name;
492			cde.d_fileno = iep->ie_number;
493			cde.d_type = (iep->ie_fflag & NTFS_FFLAG_DIR) ? DT_DIR : DT_REG;
494			cde.d_reclen = sizeof(struct dirent);
495			cde.d_off = uio->uio_offset + sizeof(struct dirent);
496			DPRINTF("%s\n", cde.d_type == DT_DIR ? "dir" : "reg");
497
498			error = uiomove(&cde, sizeof(struct dirent), uio);
499			if (error)
500				goto out;
501			num++;
502		}
503	}
504
505	DPRINTF("ntfs_readdir: %u entries (%lld bytes) read\n",
506	    num, uio->uio_offset - off);
507	DPRINTF("ntfs_readdir: off: %lld resid: %zu\n",
508	    uio->uio_offset, uio->uio_resid);
509
510/*
511	if (ap->a_eofflag)
512	    *ap->a_eofflag = VTONT(ap->a_vp)->i_size <= uio->uio_offset;
513*/
514out:
515	if (fp->f_dirblbuf != NULL) {
516		free(fp->f_dirblbuf, M_NTFSDIR, 0);
517		fp->f_dirblbuf = NULL;
518	}
519	return (error);
520}
521
522int
523ntfs_lookup(void *v)
524{
525	struct vop_lookup_args *ap = v;
526	struct vnode *dvp = ap->a_dvp;
527	struct ntnode *dip = VTONT(dvp);
528	struct ntfsmount *ntmp = dip->i_mp;
529	struct componentname *cnp = ap->a_cnp;
530	struct ucred *cred = cnp->cn_cred;
531	int error;
532	int lockparent = cnp->cn_flags & LOCKPARENT;
533#if NTFS_DEBUG
534	int wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
535#endif
536	DPRINTF("ntfs_lookup: \"%.*s\" (%ld bytes) in %u, lp: %d, wp: %d \n",
537	    (unsigned int)cnp->cn_namelen, cnp->cn_nameptr, cnp->cn_namelen,
538	    dip->i_number, lockparent, wantparent);
539
540	error = VOP_ACCESS(dvp, VEXEC, cred, cnp->cn_proc);
541	if(error)
542		return (error);
543
544	if ((cnp->cn_flags & ISLASTCN) &&
545	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
546		return (EROFS);
547
548	/*
549	 * We now have a segment name to search for, and a directory
550	 * to search.
551	 *
552	 * Before tediously performing a linear scan of the directory,
553	 * check the name cache to see if the directory/name pair
554	 * we are looking for is known already.
555	 */
556	if ((error = cache_lookup(ap->a_dvp, ap->a_vpp, cnp)) >= 0)
557		return (error);
558
559	if(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') {
560		DPRINTF("ntfs_lookup: faking . directory in %u\n",
561		    dip->i_number);
562
563		vref(dvp);
564		*ap->a_vpp = dvp;
565		error = 0;
566	} else if (cnp->cn_flags & ISDOTDOT) {
567		struct ntvattr *vap;
568
569		DPRINTF("ntfs_lookup: faking .. directory in %u\n",
570		    dip->i_number);
571
572		VOP_UNLOCK(dvp);
573		cnp->cn_flags |= PDIRUNLOCK;
574
575		error = ntfs_ntvattrget(ntmp, dip, NTFS_A_NAME, NULL, 0, &vap);
576		if(error)
577			return (error);
578
579		DPRINTF("ntfs_lookup: parentdir: %u\n",
580		    vap->va_a_name->n_pnumber);
581		error = VFS_VGET(ntmp->ntm_mountp,
582				 vap->va_a_name->n_pnumber,ap->a_vpp);
583		ntfs_ntvattrrele(vap);
584		if (error) {
585			if (vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY) == 0)
586				cnp->cn_flags &= ~PDIRUNLOCK;
587			return (error);
588		}
589
590		if (lockparent && (cnp->cn_flags & ISLASTCN)) {
591			error = vn_lock(dvp, LK_EXCLUSIVE);
592			if (error) {
593				vput( *(ap->a_vpp) );
594				return (error);
595			}
596			cnp->cn_flags &= ~PDIRUNLOCK;
597		}
598	} else {
599		error = ntfs_ntlookupfile(ntmp, dvp, cnp, ap->a_vpp);
600		if (error) {
601			DPRINTF("ntfs_ntlookupfile: returned %d\n", error);
602			return (error);
603		}
604
605		DPRINTF("ntfs_lookup: found ino: %u\n",
606		    VTONT(*ap->a_vpp)->i_number);
607
608		if(!lockparent || (cnp->cn_flags & ISLASTCN) == 0) {
609			VOP_UNLOCK(dvp);
610			cnp->cn_flags |= PDIRUNLOCK;
611		}
612	}
613
614	if (cnp->cn_flags & MAKEENTRY)
615		cache_enter(dvp, *ap->a_vpp, cnp);
616
617	return (error);
618}
619
620/*
621 * Flush the blocks of a file to disk.
622 *
623 * This function is worthless for vnodes that represent directories. Maybe we
624 * could just do a sync if they try an fsync on a directory file.
625 */
626int
627ntfs_fsync(void *v)
628{
629	return (0);
630}
631
632/*
633 * Return POSIX pathconf information applicable to NTFS filesystem
634 */
635int
636ntfs_pathconf(void *v)
637{
638	struct vop_pathconf_args *ap = v;
639	int error = 0;
640
641	switch (ap->a_name) {
642	case _PC_LINK_MAX:
643		*ap->a_retval = 1;
644		break;
645	case _PC_NAME_MAX:
646		*ap->a_retval = NTFS_MAXFILENAME;
647		break;
648	case _PC_CHOWN_RESTRICTED:
649		*ap->a_retval = 1;
650		break;
651	case _PC_NO_TRUNC:
652		*ap->a_retval = 0;
653		break;
654	default:
655		error = EINVAL;
656		break;
657	}
658
659	return (error);
660}
661
662/*
663 * Global vfs data structures
664 */
665const struct vops ntfs_vops = {
666	.vop_getattr	= ntfs_getattr,
667	.vop_inactive	= ntfs_inactive,
668	.vop_reclaim	= ntfs_reclaim,
669	.vop_print	= ntfs_print,
670	.vop_pathconf	= ntfs_pathconf,
671	.vop_lock	= nullop,
672	.vop_unlock	= nullop,
673	.vop_islocked	= nullop,
674	.vop_lookup	= ntfs_lookup,
675	.vop_access	= ntfs_access,
676	.vop_close	= ntfs_close,
677	.vop_open	= ntfs_open,
678	.vop_readdir	= ntfs_readdir,
679	.vop_fsync	= ntfs_fsync,
680	.vop_bmap	= ntfs_bmap,
681	.vop_strategy	= ntfs_strategy,
682	.vop_bwrite	= vop_generic_bwrite,
683	.vop_read	= ntfs_read,
684
685	.vop_abortop	= NULL,
686	.vop_advlock	= NULL,
687	.vop_create	= NULL,
688	.vop_ioctl	= NULL,
689	.vop_link	= NULL,
690	.vop_mknod	= NULL,
691	.vop_readlink	= NULL,
692	.vop_remove	= eopnotsupp,
693	.vop_rename	= NULL,
694	.vop_revoke	= NULL,
695	.vop_mkdir	= NULL,
696	.vop_rmdir	= NULL,
697	.vop_setattr	= NULL,
698	.vop_symlink	= NULL,
699	.vop_write	= NULL,
700	.vop_kqfilter	= NULL
701};
702