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