1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <sys/conf.h> 26 #include <sys/stat.h> 27 #include <sys/file.h> 28 #include <sys/types.h> 29 #include <sys/pathname.h> 30 #include <sys/proc.h> 31 #include <sys/mode.h> 32 #include <sys/vnode.h> 33 #include <sys/ddi.h> 34 #include <sys/sunddi.h> 35 #include <sys/sunldi.h> 36 #include <sys/uio.h> 37 #include <sys/attr.h> 38 #include <sys/acl.h> 39 #include <sys/fs/zut.h> 40 41 ldi_ident_t zut_li = NULL; 42 dev_info_t *zut_dip; 43 44 static int 45 zut_open_dir(char *path, vnode_t *startvp, cred_t *cr, int flags, 46 pathname_t *realpn, vnode_t **dvn) 47 { 48 pathname_t pn; 49 vnode_t *vp; 50 vnode_t *rootvp; 51 proc_t *p = curproc; 52 int error; 53 54 pn_alloc(&pn); 55 (void) strlcpy(pn.pn_buf, path, MAXPATHLEN); 56 pn.pn_pathlen = strlen(path); 57 58 mutex_enter(&p->p_lock); /* for u_rdir and u_cdir */ 59 if ((rootvp = PTOU(p)->u_rdir) == NULL) 60 rootvp = rootdir; 61 else if (rootvp != rootdir) /* no need to VN_HOLD rootdir */ 62 VN_HOLD(rootvp); 63 64 if (pn.pn_path[0] == '/') { 65 vp = rootvp; 66 } else { 67 vp = (startvp == NULL) ? PTOU(p)->u_cdir : startvp; 68 } 69 VN_HOLD(vp); 70 mutex_exit(&p->p_lock); 71 72 /* 73 * Skip over leading slashes 74 */ 75 while (pn.pn_path[0] == '/') { 76 pn.pn_path++; 77 pn.pn_pathlen--; 78 } 79 80 error = lookuppnvp(&pn, realpn, flags | FOLLOW, NULL, 81 dvn, rootvp, vp, cr); 82 83 /* 84 * If we lack read access to the directory, we should error out. 85 */ 86 if (!error) { 87 if (vfs_has_feature((*dvn)->v_vfsp, VFSFT_ACEMASKONACCESS)) { 88 error = VOP_ACCESS(*dvn, ACE_LIST_DIRECTORY, 89 V_ACE_MASK, cr, NULL); 90 } else { 91 error = VOP_ACCESS(*dvn, VREAD, 0, cr, NULL); 92 } 93 } 94 95 pn_free(&pn); 96 97 return (error); 98 } 99 100 static int 101 zut_readdir(intptr_t arg, cred_t *cr, int iflag, int *rvalp) 102 { 103 zut_readdir_t *zr; 104 struct iovec aiov; 105 struct uio auio; 106 vnode_t *dvn = NULL; 107 vnode_t *fvn = NULL; 108 char *kbuf; 109 int flags = 0; 110 int error, rc; 111 112 zr = kmem_zalloc(sizeof (zut_readdir_t), KM_SLEEP); 113 error = ddi_copyin((void *)arg, zr, sizeof (zut_readdir_t), iflag); 114 if (error) 115 goto zutr_bail; 116 117 kbuf = kmem_zalloc(zr->zr_buflen, KM_SLEEP); 118 119 zr->zr_retcode = zut_open_dir(zr->zr_dir, NULL, cr, flags, NULL, &dvn); 120 if (zr->zr_retcode) 121 goto zutr_done; 122 123 if (zr->zr_reqflags & ZUT_XATTR) { 124 vattr_t vattr; 125 126 zr->zr_retcode = VOP_LOOKUP(dvn, zr->zr_file, &fvn, 127 NULL, flags, NULL, cr, NULL, NULL, NULL); 128 VN_RELE(dvn); 129 dvn = NULL; 130 if (zr->zr_retcode) 131 goto zutr_done; 132 133 /* 134 * In order to access hidden attribute directory the 135 * user must have appropriate read access and be able 136 * to stat() the file 137 */ 138 if (vfs_has_feature(fvn->v_vfsp, VFSFT_ACEMASKONACCESS)) { 139 zr->zr_retcode = VOP_ACCESS(fvn, ACE_READ_NAMED_ATTRS, 140 V_ACE_MASK, cr, NULL); 141 } else { 142 zr->zr_retcode = VOP_ACCESS(fvn, VREAD, 0, cr, NULL); 143 } 144 if (zr->zr_retcode) 145 goto zutr_done; 146 147 vattr.va_mask = AT_ALL; 148 zr->zr_retcode = VOP_GETATTR(fvn, &vattr, 0, cr, NULL); 149 if (zr->zr_retcode) 150 goto zutr_done; 151 152 zr->zr_retcode = VOP_LOOKUP(fvn, "", &dvn, NULL, 153 flags | LOOKUP_XATTR, NULL, cr, NULL, NULL, NULL); 154 VN_RELE(fvn); 155 if (zr->zr_retcode) 156 goto zutr_done; 157 } 158 159 aiov.iov_base = kbuf; 160 aiov.iov_len = zr->zr_buflen; 161 auio.uio_iov = &aiov; 162 auio.uio_iovcnt = 1; 163 auio.uio_loffset = zr->zr_loffset; 164 auio.uio_segflg = UIO_SYSSPACE; 165 auio.uio_resid = zr->zr_buflen; 166 auio.uio_fmode = 0; 167 auio.uio_extflg = UIO_COPY_CACHED; 168 169 if (zr->zr_reqflags & ZUT_EXTRDDIR) 170 flags |= V_RDDIR_ENTFLAGS; 171 if (zr->zr_reqflags & ZUT_ACCFILTER) 172 flags |= V_RDDIR_ACCFILTER; 173 174 (void) VOP_RWLOCK(dvn, V_WRITELOCK_FALSE, NULL); 175 zr->zr_retcode = VOP_READDIR(dvn, &auio, cr, &zr->zr_eof, 176 NULL, flags); 177 VOP_RWUNLOCK(dvn, V_WRITELOCK_FALSE, NULL); 178 VN_RELE(dvn); 179 180 zr->zr_bytes = aiov.iov_base - kbuf; 181 zr->zr_loffset = auio.uio_loffset; 182 183 error = ddi_copyout(kbuf, (void *)(uintptr_t)zr->zr_buf, 184 zr->zr_buflen, iflag); 185 186 zutr_done: 187 kmem_free(kbuf, zr->zr_buflen); 188 rc = ddi_copyout(zr, (void *)arg, sizeof (zut_readdir_t), iflag); 189 if (error == 0) 190 error = rc; 191 192 zutr_bail: 193 kmem_free(zr, sizeof (zut_readdir_t)); 194 if (rvalp) 195 *rvalp = error; 196 return (error); 197 } 198 199 static int 200 zut_stat64(vnode_t *vp, struct stat64 *sb, uint64_t *xvs, int flag, cred_t *cr) 201 { 202 xoptattr_t *xoap = NULL; 203 xvattr_t xv = {{ 0 }}; 204 int error; 205 206 xva_init(&xv); 207 208 XVA_SET_REQ(&xv, XAT_ARCHIVE); 209 XVA_SET_REQ(&xv, XAT_SYSTEM); 210 XVA_SET_REQ(&xv, XAT_READONLY); 211 XVA_SET_REQ(&xv, XAT_HIDDEN); 212 XVA_SET_REQ(&xv, XAT_NOUNLINK); 213 XVA_SET_REQ(&xv, XAT_IMMUTABLE); 214 XVA_SET_REQ(&xv, XAT_APPENDONLY); 215 XVA_SET_REQ(&xv, XAT_NODUMP); 216 XVA_SET_REQ(&xv, XAT_OPAQUE); 217 XVA_SET_REQ(&xv, XAT_AV_QUARANTINED); 218 XVA_SET_REQ(&xv, XAT_AV_MODIFIED); 219 XVA_SET_REQ(&xv, XAT_REPARSE); 220 XVA_SET_REQ(&xv, XAT_OFFLINE); 221 XVA_SET_REQ(&xv, XAT_SPARSE); 222 223 xv.xva_vattr.va_mask |= AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE; 224 if (error = VOP_GETATTR(vp, &xv.xva_vattr, flag, cr, NULL)) 225 return (error); 226 227 bzero(sb, sizeof (sb)); 228 sb->st_dev = xv.xva_vattr.va_fsid; 229 sb->st_ino = xv.xva_vattr.va_nodeid; 230 sb->st_mode = VTTOIF(xv.xva_vattr.va_type) | xv.xva_vattr.va_mode; 231 sb->st_nlink = xv.xva_vattr.va_nlink; 232 sb->st_uid = xv.xva_vattr.va_uid; 233 sb->st_gid = xv.xva_vattr.va_gid; 234 sb->st_rdev = xv.xva_vattr.va_rdev; 235 sb->st_size = xv.xva_vattr.va_size; 236 sb->st_atim = xv.xva_vattr.va_atime; 237 sb->st_mtim = xv.xva_vattr.va_mtime; 238 sb->st_ctim = xv.xva_vattr.va_ctime; 239 sb->st_blksize = xv.xva_vattr.va_blksize; 240 sb->st_blocks = xv.xva_vattr.va_nblocks; 241 sb->st_fstype[0] = 0; 242 243 if ((xoap = xva_getxoptattr(&xv)) == NULL) 244 return (0); 245 246 if (XVA_ISSET_RTN(&xv, XAT_ARCHIVE) && xoap->xoa_archive) 247 *xvs |= (1 << F_ARCHIVE); 248 if (XVA_ISSET_RTN(&xv, XAT_SYSTEM) && xoap->xoa_system) 249 *xvs |= (1 << F_SYSTEM); 250 if (XVA_ISSET_RTN(&xv, XAT_READONLY) && xoap->xoa_readonly) 251 *xvs |= (1 << F_READONLY); 252 if (XVA_ISSET_RTN(&xv, XAT_HIDDEN) && xoap->xoa_hidden) 253 *xvs |= (1 << F_HIDDEN); 254 if (XVA_ISSET_RTN(&xv, XAT_NOUNLINK) && xoap->xoa_nounlink) 255 *xvs |= (1 << F_NOUNLINK); 256 if (XVA_ISSET_RTN(&xv, XAT_IMMUTABLE) && xoap->xoa_immutable) 257 *xvs |= (1 << F_IMMUTABLE); 258 if (XVA_ISSET_RTN(&xv, XAT_APPENDONLY) && xoap->xoa_appendonly) 259 *xvs |= (1 << F_APPENDONLY); 260 if (XVA_ISSET_RTN(&xv, XAT_NODUMP) && xoap->xoa_nodump) 261 *xvs |= (1 << F_NODUMP); 262 if (XVA_ISSET_RTN(&xv, XAT_OPAQUE) && xoap->xoa_opaque) 263 *xvs |= (1 << F_OPAQUE); 264 if (XVA_ISSET_RTN(&xv, XAT_AV_QUARANTINED) && xoap->xoa_av_quarantined) 265 *xvs |= (1 << F_AV_QUARANTINED); 266 if (XVA_ISSET_RTN(&xv, XAT_AV_MODIFIED) && xoap->xoa_av_modified) 267 *xvs |= (1 << F_AV_MODIFIED); 268 if (XVA_ISSET_RTN(&xv, XAT_REPARSE) && xoap->xoa_reparse) 269 *xvs |= (1 << F_REPARSE); 270 if (XVA_ISSET_RTN(&xv, XAT_OFFLINE) && xoap->xoa_offline) 271 *xvs |= (1 << F_OFFLINE); 272 if (XVA_ISSET_RTN(&xv, XAT_SPARSE) && xoap->xoa_sparse) 273 *xvs |= (1 << F_SPARSE); 274 275 return (0); 276 } 277 278 /*ARGSUSED*/ 279 static int 280 zut_lookup(intptr_t arg, cred_t *cr, int iflag, int *rvalp) 281 { 282 zut_lookup_t *zl; 283 pathname_t rpn; 284 vnode_t *dvn = NULL; 285 vnode_t *fvn = NULL; 286 vnode_t *xdvn = NULL; 287 vnode_t *xfvn = NULL; 288 vnode_t *release = NULL; 289 int flags = 0; 290 int error, rc; 291 292 zl = kmem_zalloc(sizeof (zut_lookup_t), KM_SLEEP); 293 294 error = ddi_copyin((void *)arg, zl, sizeof (zut_lookup_t), iflag); 295 if (error) 296 goto zutl_bail; 297 298 pn_alloc(&rpn); 299 bzero(rpn.pn_buf, MAXPATHLEN); 300 301 zl->zl_retcode = zut_open_dir(zl->zl_dir, NULL, cr, flags, &rpn, &dvn); 302 if (zl->zl_retcode) 303 goto zutl_done; 304 305 if (zl->zl_reqflags & ZUT_IGNORECASE) 306 flags |= FIGNORECASE; 307 308 zl->zl_retcode = VOP_LOOKUP(dvn, zl->zl_file, &fvn, NULL, flags, NULL, 309 cr, NULL, &zl->zl_deflags, &rpn); 310 if (zl->zl_retcode) 311 goto zutl_done; 312 313 release = fvn; 314 315 if (zl->zl_reqflags & ZUT_XATTR) { 316 vattr_t vattr; 317 318 /* 319 * In order to access hidden attribute directory the 320 * user must have appropriate read access and be able 321 * to stat() the file 322 */ 323 if (vfs_has_feature(fvn->v_vfsp, VFSFT_ACEMASKONACCESS)) { 324 zl->zl_retcode = VOP_ACCESS(fvn, ACE_READ_NAMED_ATTRS, 325 V_ACE_MASK, cr, NULL); 326 } else { 327 zl->zl_retcode = VOP_ACCESS(fvn, VREAD, 0, cr, NULL); 328 } 329 if (zl->zl_retcode) 330 goto zutl_done; 331 332 vattr.va_mask = AT_ALL; 333 zl->zl_retcode = VOP_GETATTR(fvn, &vattr, 0, cr, NULL); 334 if (zl->zl_retcode) 335 goto zutl_done; 336 337 zl->zl_retcode = VOP_LOOKUP(fvn, "", &xdvn, NULL, 338 flags | LOOKUP_XATTR, NULL, cr, NULL, NULL, NULL); 339 if (zl->zl_retcode) 340 goto zutl_done; 341 VN_RELE(fvn); 342 release = xdvn; 343 344 zl->zl_retcode = VOP_LOOKUP(xdvn, zl->zl_xfile, &xfvn, 345 NULL, flags, NULL, cr, NULL, &zl->zl_deflags, &rpn); 346 if (zl->zl_retcode) 347 goto zutl_done; 348 VN_RELE(xdvn); 349 release = xfvn; 350 } 351 352 if (zl->zl_reqflags & ZUT_GETSTAT) { 353 zl->zl_retcode = zut_stat64(release, 354 &zl->zl_statbuf, &zl->zl_xvattrs, 0, cr); 355 } 356 357 zutl_done: 358 (void) strlcpy(zl->zl_real, rpn.pn_path, MAXPATHLEN); 359 360 rc = ddi_copyout(zl, (void *)arg, sizeof (zut_lookup_t), iflag); 361 if (error == 0) 362 error = rc; 363 364 if (release) 365 VN_RELE(release); 366 if (dvn) 367 VN_RELE(dvn); 368 pn_free(&rpn); 369 370 zutl_bail: 371 kmem_free(zl, sizeof (zut_lookup_t)); 372 if (rvalp) 373 *rvalp = error; 374 return (error); 375 } 376 377 /*ARGSUSED*/ 378 static int 379 zut_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 380 { 381 int error; 382 383 if (getminor(dev) != 0) 384 return (ENXIO); 385 386 if (cmd <= ZUT_IOC_MIN_CMD || cmd >= ZUT_IOC_MAX_CMD) 387 return (EINVAL); 388 389 switch (cmd) { 390 case ZUT_IOC_LOOKUP: 391 error = zut_lookup(arg, cr, flag, rvalp); 392 break; 393 case ZUT_IOC_READDIR: 394 error = zut_readdir(arg, cr, flag, rvalp); 395 default: 396 break; 397 } 398 399 return (error); 400 } 401 402 static int 403 zut_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 404 { 405 if (cmd != DDI_ATTACH) 406 return (DDI_FAILURE); 407 408 if (ddi_create_minor_node(dip, "zut", S_IFCHR, 0, 409 DDI_PSEUDO, 0) == DDI_FAILURE) 410 return (DDI_FAILURE); 411 412 zut_dip = dip; 413 414 ddi_report_dev(dip); 415 416 return (DDI_SUCCESS); 417 } 418 419 static int 420 zut_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 421 { 422 if (cmd != DDI_DETACH) 423 return (DDI_FAILURE); 424 425 zut_dip = NULL; 426 427 ddi_prop_remove_all(dip); 428 ddi_remove_minor_node(dip, NULL); 429 430 return (DDI_SUCCESS); 431 } 432 433 /*ARGSUSED*/ 434 static int 435 zut_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 436 { 437 switch (infocmd) { 438 case DDI_INFO_DEVT2DEVINFO: 439 *result = zut_dip; 440 return (DDI_SUCCESS); 441 442 case DDI_INFO_DEVT2INSTANCE: 443 *result = (void *)0; 444 return (DDI_SUCCESS); 445 } 446 447 return (DDI_FAILURE); 448 } 449 450 /*ARGSUSED*/ 451 int 452 zut_open(dev_t *devp, int flag, int otyp, cred_t *cr) 453 { 454 minor_t minor = getminor(*devp); 455 456 if (minor == 0) /* This is the control device */ 457 return (0); 458 459 return (ENXIO); 460 } 461 462 /*ARGSUSED*/ 463 int 464 zut_close(dev_t dev, int flag, int otyp, cred_t *cr) 465 { 466 minor_t minor = getminor(dev); 467 468 if (minor == 0) /* This is the control device */ 469 return (0); 470 471 return (ENXIO); 472 } 473 474 /* 475 * /dev/zut is the control node, i.e. minor 0. 476 * 477 * There are no other minor nodes, and /dev/zut basically does nothing 478 * other than serve up ioctls. 479 */ 480 static struct cb_ops zut_cb_ops = { 481 zut_open, /* open */ 482 zut_close, /* close */ 483 nodev, /* strategy */ 484 nodev, /* print */ 485 nodev, /* dump */ 486 nodev, /* read */ 487 nodev, /* write */ 488 zut_ioctl, /* ioctl */ 489 nodev, /* devmap */ 490 nodev, /* mmap */ 491 nodev, /* segmap */ 492 nochpoll, /* poll */ 493 ddi_prop_op, /* prop_op */ 494 NULL, /* streamtab */ 495 D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 496 CB_REV, /* version */ 497 nodev, /* async read */ 498 nodev, /* async write */ 499 }; 500 501 static struct dev_ops zut_dev_ops = { 502 DEVO_REV, /* version */ 503 0, /* refcnt */ 504 zut_info, /* info */ 505 nulldev, /* identify */ 506 nulldev, /* probe */ 507 zut_attach, /* attach */ 508 zut_detach, /* detach */ 509 nodev, /* reset */ 510 &zut_cb_ops, /* driver operations */ 511 NULL /* no bus operations */ 512 }; 513 514 static struct modldrv zut_modldrv = { 515 &mod_driverops, "ZFS unit test " ZUT_VERSION_STRING, 516 &zut_dev_ops 517 }; 518 519 static struct modlinkage modlinkage = { 520 MODREV_1, 521 { (void *)&zut_modldrv, 522 NULL } 523 }; 524 525 int 526 _init(void) 527 { 528 int error; 529 530 if ((error = mod_install(&modlinkage)) != 0) { 531 return (error); 532 } 533 534 error = ldi_ident_from_mod(&modlinkage, &zut_li); 535 ASSERT(error == 0); 536 537 return (0); 538 } 539 540 int 541 _fini(void) 542 { 543 int error; 544 545 if ((error = mod_remove(&modlinkage)) != 0) 546 return (error); 547 548 ldi_ident_release(zut_li); 549 zut_li = NULL; 550 551 return (error); 552 } 553 554 int 555 _info(struct modinfo *modinfop) 556 { 557 return (mod_info(&modlinkage, modinfop)); 558 }