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