1 /* $NetBSD: ntfs_vnops.c,v 1.45 2009/07/03 21:17:41 elad Exp $ */
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * John Heidemann of the UCLA Ficus project.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * Id: ntfs_vnops.c,v 1.5 1999/05/12 09:43:06 semenu Exp
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: ntfs_vnops.c,v 1.45 2009/07/03 21:17:41 elad Exp $");
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
46 #include <sys/vnode.h>
47 #include <sys/mount.h>
48 #include <sys/namei.h>
49 #include <sys/malloc.h>
51 #include <sys/dirent.h>
52 #include <sys/kauth.h>
53 #include <sys/sysctl.h>
56 #include <fs/ntfs/ntfs.h>
57 #include <fs/ntfs/ntfs_inode.h>
58 #include <fs/ntfs/ntfs_subr.h>
59 #include <miscfs/specfs/specdev.h>
60 #include <miscfs/genfs/genfs.h>
62 #include <sys/unistd.h> /* for pathconf(2) constants */
64 static int ntfs_bypass(void *);
65 static int ntfs_read(void *);
66 static int ntfs_write(void *);
67 static int ntfs_getattr(void *);
68 static int ntfs_inactive(void *);
69 static int ntfs_print(void *);
70 static int ntfs_reclaim(void *);
71 static int ntfs_strategy(void *);
72 static int ntfs_access(void *);
73 static int ntfs_open(void *);
74 static int ntfs_close(void *);
75 static int ntfs_readdir(void *);
76 static int ntfs_lookup(void *);
77 static int ntfs_bmap(void *);
78 static int ntfs_fsync(void *);
79 static int ntfs_pathconf(void *);
84 * This is a noop, simply returning what one has been given.
89 struct vop_bmap_args
/* {
97 dprintf(("ntfs_bmap: vn: %p, blk: %d\n", ap
->a_vp
,(u_int32_t
)ap
->a_bn
));
98 if (ap
->a_vpp
!= NULL
)
99 *ap
->a_vpp
= ap
->a_vp
;
100 if (ap
->a_bnp
!= NULL
)
101 *ap
->a_bnp
= ap
->a_bn
;
102 if (ap
->a_runp
!= NULL
)
110 struct vop_read_args
/* {
116 struct vnode
*vp
= ap
->a_vp
;
117 struct fnode
*fp
= VTOF(vp
);
118 struct ntnode
*ip
= FTONT(fp
);
119 struct uio
*uio
= ap
->a_uio
;
120 struct ntfsmount
*ntmp
= ip
->i_mp
;
124 dprintf(("ntfs_read: ino: %llu, off: %qd resid: %qd\n",
125 (unsigned long long)ip
->i_number
, (long long)uio
->uio_offset
,
126 (long long)uio
->uio_resid
));
128 dprintf(("ntfs_read: filesize: %qu",(long long)fp
->f_size
));
130 /* don't allow reading after end of file */
131 if (uio
->uio_offset
> fp
->f_size
)
134 toread
= MIN(uio
->uio_resid
, fp
->f_size
- uio
->uio_offset
);
136 dprintf((", toread: %qu\n",(long long)toread
));
141 error
= ntfs_readattr(ntmp
, ip
, fp
->f_attrtype
,
142 fp
->f_attrname
, uio
->uio_offset
, toread
, NULL
, uio
);
144 printf("ntfs_read: ntfs_readattr failed: %d\n",error
);
154 struct vop_generic_args
/* {
155 struct vnodeop_desc *a_desc;
156 <other random data follows, presumably>
157 } */ *ap __unused
= v
;
159 dprintf(("ntfs_bypass: %s\n", ap
->a_desc
->vdesc_name
));
165 ntfs_getattr(void *v
)
167 struct vop_getattr_args
/* {
172 struct vnode
*vp
= ap
->a_vp
;
173 struct fnode
*fp
= VTOF(vp
);
174 struct ntnode
*ip
= FTONT(fp
);
175 struct vattr
*vap
= ap
->a_vap
;
177 dprintf(("ntfs_getattr: %llu, flags: %d\n",
178 (unsigned long long)ip
->i_number
, ip
->i_flag
));
180 vap
->va_fsid
= ip
->i_dev
;
181 vap
->va_fileid
= ip
->i_number
;
182 vap
->va_mode
= ip
->i_mp
->ntm_mode
;
183 vap
->va_nlink
= ip
->i_nlink
;
184 vap
->va_uid
= ip
->i_mp
->ntm_uid
;
185 vap
->va_gid
= ip
->i_mp
->ntm_gid
;
186 vap
->va_rdev
= 0; /* XXX UNODEV ? */
187 vap
->va_size
= fp
->f_size
;
188 vap
->va_bytes
= fp
->f_allocated
;
189 vap
->va_atime
= ntfs_nttimetounix(fp
->f_times
.t_access
);
190 vap
->va_mtime
= ntfs_nttimetounix(fp
->f_times
.t_write
);
191 vap
->va_ctime
= ntfs_nttimetounix(fp
->f_times
.t_create
);
192 vap
->va_flags
= ip
->i_flag
;
194 vap
->va_blocksize
= ip
->i_mp
->ntm_spc
* ip
->i_mp
->ntm_bps
;
195 vap
->va_type
= vp
->v_type
;
202 * Last reference to an ntnode. If necessary, write or delete it.
205 ntfs_inactive(void *v
)
207 struct vop_inactive_args
/* {
210 struct vnode
*vp
= ap
->a_vp
;
212 struct ntnode
*ip
= VTONT(vp
);
215 dprintf(("ntfs_inactive: vnode: %p, ntnode: %llu\n", vp
,
216 (unsigned long long)ip
->i_number
));
220 /* XXX since we don't support any filesystem changes
221 * right now, nothing more needs to be done
227 * Reclaim an fnode/ntnode so that it can be used for other purposes.
230 ntfs_reclaim(void *v
)
232 struct vop_reclaim_args
/* {
235 struct vnode
*vp
= ap
->a_vp
;
236 struct fnode
*fp
= VTOF(vp
);
237 struct ntnode
*ip
= FTONT(fp
);
240 dprintf(("ntfs_reclaim: vnode: %p, ntnode: %llu\n", vp
,
241 (unsigned long long)ip
->i_number
));
243 if (prtactive
&& vp
->v_usecount
> 1)
244 vprint("ntfs_reclaim: pushing active", vp
);
246 if ((error
= ntfs_ntget(ip
)) != 0)
249 /* Purge old data structures associated with the inode. */
256 genfs_node_destroy(vp
);
267 struct vop_print_args
/* {
270 struct ntnode
*ip
= VTONT(ap
->a_vp
);
272 printf("tag VT_NTFS, ino %llu, flag %#x, usecount %d, nlink %ld\n",
273 (unsigned long long)ip
->i_number
, ip
->i_flag
, ip
->i_usecount
,
281 * Calculate the logical to physical mapping if not done already,
282 * then call the device strategy routine.
285 ntfs_strategy(void *v
)
287 struct vop_strategy_args
/* {
291 struct buf
*bp
= ap
->a_bp
;
292 struct vnode
*vp
= ap
->a_vp
;
293 struct fnode
*fp
= VTOF(vp
);
294 struct ntnode
*ip
= FTONT(fp
);
295 struct ntfsmount
*ntmp
= ip
->i_mp
;
298 dprintf(("ntfs_strategy: blkno: %d, lblkno: %d\n",
299 (u_int32_t
)bp
->b_blkno
,
300 (u_int32_t
)bp
->b_lblkno
));
302 dprintf(("strategy: bcount: %u flags: 0x%x\n",
303 (u_int32_t
)bp
->b_bcount
,bp
->b_flags
));
305 if (bp
->b_flags
& B_READ
) {
308 if (ntfs_cntob(bp
->b_blkno
) >= fp
->f_size
) {
312 toread
= MIN(bp
->b_bcount
,
313 fp
->f_size
- ntfs_cntob(bp
->b_blkno
));
314 dprintf(("ntfs_strategy: toread: %d, fsize: %d\n",
315 toread
,(u_int32_t
)fp
->f_size
));
317 error
= ntfs_readattr(ntmp
, ip
, fp
->f_attrtype
,
318 fp
->f_attrname
, ntfs_cntob(bp
->b_blkno
),
319 toread
, bp
->b_data
, NULL
);
322 printf("ntfs_strategy: ntfs_readattr failed\n");
326 memset((char *)bp
->b_data
+ toread
, 0,
327 bp
->b_bcount
- toread
);
333 if (ntfs_cntob(bp
->b_blkno
) + bp
->b_bcount
>= fp
->f_size
) {
334 printf("ntfs_strategy: CAN'T EXTEND FILE\n");
335 bp
->b_error
= error
= EFBIG
;
337 towrite
= MIN(bp
->b_bcount
,
338 fp
->f_size
- ntfs_cntob(bp
->b_blkno
));
339 dprintf(("ntfs_strategy: towrite: %d, fsize: %d\n",
340 towrite
,(u_int32_t
)fp
->f_size
));
342 error
= ntfs_writeattr_plain(ntmp
, ip
, fp
->f_attrtype
,
343 fp
->f_attrname
, ntfs_cntob(bp
->b_blkno
),towrite
,
344 bp
->b_data
, &tmp
, NULL
);
347 printf("ntfs_strategy: ntfs_writeattr fail\n");
359 struct vop_write_args
/* {
365 struct vnode
*vp
= ap
->a_vp
;
366 struct fnode
*fp
= VTOF(vp
);
367 struct ntnode
*ip
= FTONT(fp
);
368 struct uio
*uio
= ap
->a_uio
;
369 struct ntfsmount
*ntmp
= ip
->i_mp
;
374 dprintf(("ntfs_write: ino: %llu, off: %qd resid: %qd\n",
375 (unsigned long long)ip
->i_number
, (long long)uio
->uio_offset
,
376 (long long)uio
->uio_resid
));
377 dprintf(("ntfs_write: filesize: %qu",(long long)fp
->f_size
));
379 if (uio
->uio_resid
+ uio
->uio_offset
> fp
->f_size
) {
380 printf("ntfs_write: CAN'T WRITE BEYOND END OF FILE\n");
384 towrite
= MIN(uio
->uio_resid
, fp
->f_size
- uio
->uio_offset
);
386 dprintf((", towrite: %qu\n",(long long)towrite
));
388 error
= ntfs_writeattr_plain(ntmp
, ip
, fp
->f_attrtype
,
389 fp
->f_attrname
, uio
->uio_offset
, towrite
, NULL
, &written
, uio
);
392 printf("ntfs_write: ntfs_writeattr failed: %d\n", error
);
399 ntfs_check_possible(struct vnode
*vp
, struct ntnode
*ip
, mode_t mode
)
403 * Disallow write attempts on read-only file systems;
404 * unless the file is a socket, fifo, or a block or
405 * character device resident on the file system.
408 switch ((int)vp
->v_type
) {
412 if (vp
->v_mount
->mnt_flag
& MNT_RDONLY
)
422 ntfs_check_permitted(struct vnode
*vp
, struct ntnode
*ip
, mode_t mode
,
427 file_mode
= ip
->i_mp
->ntm_mode
| (S_IXUSR
|S_IXGRP
|S_IXOTH
);
429 return genfs_can_access(vp
->v_type
, file_mode
, ip
->i_mp
->ntm_uid
,
430 ip
->i_mp
->ntm_gid
, mode
, cred
);
436 struct vop_access_args
/* {
441 struct vnode
*vp
= ap
->a_vp
;
442 struct ntnode
*ip
= VTONT(vp
);
445 dprintf(("ntfs_access: %llu\n", (unsigned long long)ip
->i_number
));
447 error
= ntfs_check_possible(vp
, ip
, ap
->a_mode
);
451 error
= ntfs_check_permitted(vp
, ip
, ap
->a_mode
, ap
->a_cred
);
465 struct vop_open_args
/* {
469 } */ *ap __unused
= v
;
471 struct vnode
*vp
= ap
->a_vp
;
472 struct ntnode
*ip
= VTONT(vp
);
474 printf("ntfs_open: %llu\n", (unsigned long long)ip
->i_number
);
478 * Files marked append-only must be opened for appending.
487 * Update the times on the inode.
493 struct vop_close_args
/* {
497 } */ *ap __unused
= v
;
499 struct vnode
*vp
= ap
->a_vp
;
500 struct ntnode
*ip
= VTONT(vp
);
502 printf("ntfs_close: %llu\n", (unsigned long long)ip
->i_number
);
509 ntfs_readdir(void *v
)
511 struct vop_readdir_args
/* {
518 struct vnode
*vp
= ap
->a_vp
;
519 struct fnode
*fp
= VTOF(vp
);
520 struct ntnode
*ip
= FTONT(fp
);
521 struct uio
*uio
= ap
->a_uio
;
522 struct ntfsmount
*ntmp
= ip
->i_mp
;
524 u_int32_t faked
= 0, num
;
529 dprintf(("ntfs_readdir %llu off: %qd resid: %qd\n",
530 (unsigned long long)ip
->i_number
, (long long)uio
->uio_offset
,
531 (long long)uio
->uio_resid
));
533 off
= uio
->uio_offset
;
535 cde
= malloc(sizeof(struct dirent
), M_TEMP
, M_WAITOK
);
537 /* Simulate . in every dir except ROOT */
538 if (ip
->i_number
!= NTFS_ROOTINO
539 && uio
->uio_offset
< sizeof(struct dirent
)) {
540 cde
->d_fileno
= ip
->i_number
;
541 cde
->d_reclen
= sizeof(struct dirent
);
542 cde
->d_type
= DT_DIR
;
544 strncpy(cde
->d_name
, ".", 2);
545 error
= uiomove((void *)cde
, sizeof(struct dirent
), uio
);
552 /* Simulate .. in every dir including ROOT */
553 if (uio
->uio_offset
< 2 * sizeof(struct dirent
)) {
554 cde
->d_fileno
= NTFS_ROOTINO
; /* XXX */
555 cde
->d_reclen
= sizeof(struct dirent
);
556 cde
->d_type
= DT_DIR
;
558 strncpy(cde
->d_name
, "..", 3);
560 error
= uiomove((void *) cde
, sizeof(struct dirent
), uio
);
567 faked
= (ip
->i_number
== NTFS_ROOTINO
) ? 1 : 2;
568 num
= uio
->uio_offset
/ sizeof(struct dirent
) - faked
;
570 while (uio
->uio_resid
>= sizeof(struct dirent
)) {
571 struct attr_indexentry
*iep
;
576 error
= ntfs_ntreaddir(ntmp
, fp
, num
, &iep
);
583 for(; !(iep
->ie_flag
& NTFS_IEFLAG_LAST
) && (uio
->uio_resid
>= sizeof(struct dirent
));
584 iep
= NTFS_NEXTREC(iep
, struct attr_indexentry
*))
586 if(!ntfs_isnamepermitted(ntmp
,iep
))
589 remains
= sizeof(cde
->d_name
) - 1;
591 for(i
=0; i
<iep
->ie_fnamelen
; i
++) {
592 sz
= (*ntmp
->ntm_wput
)(fname
, remains
,
598 dprintf(("ntfs_readdir: elem: %d, fname:[%s] type: %d, flag: %d, ",
599 num
, cde
->d_name
, iep
->ie_fnametype
,
601 cde
->d_namlen
= fname
- (char *) cde
->d_name
;
602 cde
->d_fileno
= iep
->ie_number
;
603 cde
->d_type
= (iep
->ie_fflag
& NTFS_FFLAG_DIR
) ? DT_DIR
: DT_REG
;
604 cde
->d_reclen
= sizeof(struct dirent
);
605 dprintf(("%s\n", (cde
->d_type
== DT_DIR
) ? "dir":"reg"));
607 error
= uiomove((void *)cde
, sizeof(struct dirent
), uio
);
616 dprintf(("ntfs_readdir: %d entries (%d bytes) read\n",
617 ncookies
,(u_int
)(uio
->uio_offset
- off
)));
618 dprintf(("ntfs_readdir: off: %qd resid: %qu\n",
619 (long long)uio
->uio_offset
,(long long)uio
->uio_resid
));
621 if (!error
&& ap
->a_ncookies
!= NULL
) {
622 struct dirent
* dpStart
;
627 dprintf(("ntfs_readdir: %d cookies\n",ncookies
));
628 dpStart
= (struct dirent
*)
629 ((char *)uio
->uio_iov
->iov_base
-
630 (uio
->uio_offset
- off
));
631 cookies
= malloc(ncookies
* sizeof(off_t
), M_TEMP
, M_WAITOK
);
632 for (dp
= dpStart
, cookiep
= cookies
, i
=0;
634 dp
= (struct dirent
*)((char *) dp
+ dp
->d_reclen
), i
++) {
636 *cookiep
++ = (u_int
) off
;
638 *ap
->a_ncookies
= ncookies
;
639 *ap
->a_cookies
= cookies
;
643 *ap->a_eofflag = VTONT(ap->a_vp)->i_size <= uio->uio_offset;
653 struct vop_lookup_args
/* {
655 struct vnode **a_vpp;
656 struct componentname *a_cnp;
658 struct vnode
*dvp
= ap
->a_dvp
;
659 struct ntnode
*dip
= VTONT(dvp
);
660 struct ntfsmount
*ntmp
= dip
->i_mp
;
661 struct componentname
*cnp
= ap
->a_cnp
;
662 kauth_cred_t cred
= cnp
->cn_cred
;
665 dprintf(("ntfs_lookup: \"%.*s\" (%lld bytes) in %llu\n",
666 (int)cnp
->cn_namelen
, cnp
->cn_nameptr
, (long long)cnp
->cn_namelen
,
667 (unsigned long long)dip
->i_number
));
669 error
= VOP_ACCESS(dvp
, VEXEC
, cred
);
673 if ((cnp
->cn_flags
& ISLASTCN
) &&
674 (dvp
->v_mount
->mnt_flag
& MNT_RDONLY
) &&
675 (cnp
->cn_nameiop
== DELETE
|| cnp
->cn_nameiop
== RENAME
))
679 * We now have a segment name to search for, and a directory
682 * Before tediously performing a linear scan of the directory,
683 * check the name cache to see if the directory/name pair
684 * we are looking for is known already.
686 if ((error
= cache_lookup(ap
->a_dvp
, ap
->a_vpp
, cnp
)) >= 0)
689 if(cnp
->cn_namelen
== 1 && cnp
->cn_nameptr
[0] == '.') {
690 dprintf(("ntfs_lookup: faking . directory in %llu\n",
691 (unsigned long long)dip
->i_number
));
696 } else if (cnp
->cn_flags
& ISDOTDOT
) {
699 dprintf(("ntfs_lookup: faking .. directory in %llu\n",
700 (unsigned long long)dip
->i_number
));
703 error
= ntfs_ntvattrget(ntmp
, dip
, NTFS_A_NAME
, NULL
, 0, &vap
);
705 vn_lock(dvp
, LK_EXCLUSIVE
| LK_RETRY
);
709 dprintf(("ntfs_lookup: parentdir: %d\n",
710 vap
->va_a_name
->n_pnumber
));
711 error
= VFS_VGET(ntmp
->ntm_mountp
,
712 vap
->va_a_name
->n_pnumber
,ap
->a_vpp
);
713 ntfs_ntvattrrele(vap
);
714 vn_lock(dvp
, LK_EXCLUSIVE
| LK_RETRY
);
719 error
= ntfs_ntlookupfile(ntmp
, dvp
, cnp
, ap
->a_vpp
);
721 dprintf(("ntfs_ntlookupfile: returned %d\n", error
));
725 dprintf(("ntfs_lookup: found ino: %llu\n",
726 (unsigned long long)VTONT(*ap
->a_vpp
)->i_number
));
729 if (cnp
->cn_flags
& MAKEENTRY
)
730 cache_enter(dvp
, *ap
->a_vpp
, cnp
);
736 * Flush the blocks of a file to disk.
738 * This function is worthless for vnodes that represent directories. Maybe we
739 * could just do a sync if they try an fsync on a directory file.
744 struct vop_fsync_args
/* {
751 struct vnode
*vp
= ap
->a_vp
;
754 if (ap
->a_flags
& FSYNC_CACHE
) {
758 wait
= (ap
->a_flags
& FSYNC_WAIT
) != 0;
765 * Return POSIX pathconf information applicable to NTFS filesystem
768 ntfs_pathconf(void *v
)
770 struct vop_pathconf_args
/* {
773 register_t *a_retval;
776 switch (ap
->a_name
) {
781 *ap
->a_retval
= ap
->a_vp
->v_mount
->mnt_stat
.f_namemax
;
784 *ap
->a_retval
= PATH_MAX
;
786 case _PC_CHOWN_RESTRICTED
:
795 case _PC_FILESIZEBITS
:
805 * Global vfs data structures
807 vop_t
**ntfs_vnodeop_p
;
809 const struct vnodeopv_entry_desc ntfs_vnodeop_entries
[] = {
810 { &vop_default_desc
, (vop_t
*) ntfs_bypass
},
811 { &vop_lookup_desc
, (vop_t
*) ntfs_lookup
}, /* lookup */
812 { &vop_create_desc
, genfs_eopnotsupp
}, /* create */
813 { &vop_mknod_desc
, genfs_eopnotsupp
}, /* mknod */
814 { &vop_open_desc
, (vop_t
*) ntfs_open
}, /* open */
815 { &vop_close_desc
,(vop_t
*) ntfs_close
}, /* close */
816 { &vop_access_desc
, (vop_t
*) ntfs_access
}, /* access */
817 { &vop_getattr_desc
, (vop_t
*) ntfs_getattr
}, /* getattr */
818 { &vop_setattr_desc
, genfs_eopnotsupp
}, /* setattr */
819 { &vop_read_desc
, (vop_t
*) ntfs_read
}, /* read */
820 { &vop_write_desc
, (vop_t
*) ntfs_write
}, /* write */
821 { &vop_fcntl_desc
, genfs_fcntl
}, /* fcntl */
822 { &vop_ioctl_desc
, genfs_enoioctl
}, /* ioctl */
823 { &vop_poll_desc
, genfs_poll
}, /* poll */
824 { &vop_kqfilter_desc
, genfs_kqfilter
}, /* kqfilter */
825 { &vop_revoke_desc
, genfs_revoke
}, /* revoke */
826 { &vop_mmap_desc
, genfs_mmap
}, /* mmap */
827 { &vop_fsync_desc
, (vop_t
*) ntfs_fsync
}, /* fsync */
828 { &vop_seek_desc
, genfs_seek
}, /* seek */
829 { &vop_remove_desc
, genfs_eopnotsupp
}, /* remove */
830 { &vop_link_desc
, genfs_eopnotsupp
}, /* link */
831 { &vop_rename_desc
, genfs_eopnotsupp
}, /* rename */
832 { &vop_mkdir_desc
, genfs_eopnotsupp
}, /* mkdir */
833 { &vop_rmdir_desc
, genfs_eopnotsupp
}, /* rmdir */
834 { &vop_symlink_desc
, genfs_eopnotsupp
}, /* symlink */
835 { &vop_readdir_desc
, (vop_t
*) ntfs_readdir
}, /* readdir */
836 { &vop_readlink_desc
, genfs_eopnotsupp
}, /* readlink */
837 { &vop_abortop_desc
, genfs_abortop
}, /* abortop */
838 { &vop_inactive_desc
, (vop_t
*) ntfs_inactive
}, /* inactive */
839 { &vop_reclaim_desc
, (vop_t
*) ntfs_reclaim
}, /* reclaim */
840 { &vop_lock_desc
, genfs_lock
}, /* lock */
841 { &vop_unlock_desc
, genfs_unlock
}, /* unlock */
842 { &vop_bmap_desc
, (vop_t
*) ntfs_bmap
}, /* bmap */
843 { &vop_strategy_desc
, (vop_t
*) ntfs_strategy
}, /* strategy */
844 { &vop_print_desc
, (vop_t
*) ntfs_print
}, /* print */
845 { &vop_islocked_desc
, genfs_islocked
}, /* islocked */
846 { &vop_pathconf_desc
, ntfs_pathconf
}, /* pathconf */
847 { &vop_advlock_desc
, genfs_nullop
}, /* advlock */
848 { &vop_bwrite_desc
, vn_bwrite
}, /* bwrite */
849 { &vop_getpages_desc
, genfs_compat_getpages
}, /* getpages */
850 { &vop_putpages_desc
, genfs_putpages
}, /* putpages */
853 const struct vnodeopv_desc ntfs_vnodeop_opv_desc
=
854 { &ntfs_vnodeop_p
, ntfs_vnodeop_entries
};