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 221 xv.xva_vattr.va_mask |= AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE; 222 if (error = VOP_GETATTR(vp, &xv.xva_vattr, flag, cr, NULL)) 223 return (error); 224 225 bzero(sb, sizeof (sb)); 226 sb->st_dev = xv.xva_vattr.va_fsid; 227 sb->st_ino = xv.xva_vattr.va_nodeid; 228 sb->st_mode = VTTOIF(xv.xva_vattr.va_type) | xv.xva_vattr.va_mode; 229 sb->st_nlink = xv.xva_vattr.va_nlink; 230 sb->st_uid = xv.xva_vattr.va_uid; 231 sb->st_gid = xv.xva_vattr.va_gid; 232 sb->st_rdev = xv.xva_vattr.va_rdev; 233 sb->st_size = xv.xva_vattr.va_size; 234 sb->st_atim = xv.xva_vattr.va_atime; 235 sb->st_mtim = xv.xva_vattr.va_mtime; 236 sb->st_ctim = xv.xva_vattr.va_ctime; 237 sb->st_blksize = xv.xva_vattr.va_blksize; 238 sb->st_blocks = xv.xva_vattr.va_nblocks; 239 sb->st_fstype[0] = 0; 240 241 if ((xoap = xva_getxoptattr(&xv)) == NULL) 242 return (0); 243 244 if (XVA_ISSET_RTN(&xv, XAT_ARCHIVE) && xoap->xoa_archive) 245 *xvs |= (1 << F_ARCHIVE); 246 if (XVA_ISSET_RTN(&xv, XAT_SYSTEM) && xoap->xoa_system) 247 *xvs |= (1 << F_SYSTEM); 248 if (XVA_ISSET_RTN(&xv, XAT_READONLY) && xoap->xoa_readonly) 249 *xvs |= (1 << F_READONLY); 250 if (XVA_ISSET_RTN(&xv, XAT_HIDDEN) && xoap->xoa_hidden) 251 *xvs |= (1 << F_HIDDEN); 252 if (XVA_ISSET_RTN(&xv, XAT_NOUNLINK) && xoap->xoa_nounlink) 253 *xvs |= (1 << F_NOUNLINK); 254 if (XVA_ISSET_RTN(&xv, XAT_IMMUTABLE) && xoap->xoa_immutable) 255 *xvs |= (1 << F_IMMUTABLE); 256 if (XVA_ISSET_RTN(&xv, XAT_APPENDONLY) && xoap->xoa_appendonly) 257 *xvs |= (1 << F_APPENDONLY); 258 if (XVA_ISSET_RTN(&xv, XAT_NODUMP) && xoap->xoa_nodump) 259 *xvs |= (1 << F_NODUMP); 260 if (XVA_ISSET_RTN(&xv, XAT_OPAQUE) && xoap->xoa_opaque) 261 *xvs |= (1 << F_OPAQUE); 262 if (XVA_ISSET_RTN(&xv, XAT_AV_QUARANTINED) && xoap->xoa_av_quarantined) 263 *xvs |= (1 << F_AV_QUARANTINED); 264 if (XVA_ISSET_RTN(&xv, XAT_AV_MODIFIED) && xoap->xoa_av_modified) 265 *xvs |= (1 << F_AV_MODIFIED); 266 267 return (0); 268 } 269 270 /*ARGSUSED*/ 271 static int 272 zut_lookup(intptr_t arg, cred_t *cr, int iflag, int *rvalp) 273 { 274 zut_lookup_t *zl; 275 pathname_t rpn; 276 vnode_t *dvn = NULL; 277 vnode_t *fvn = NULL; 278 vnode_t *xdvn = NULL; 279 vnode_t *xfvn = NULL; 280 vnode_t *release = NULL; 281 int flags = 0; 282 int error, rc; 283 284 zl = kmem_zalloc(sizeof (zut_lookup_t), KM_SLEEP); 285 286 error = ddi_copyin((void *)arg, zl, sizeof (zut_lookup_t), iflag); 287 if (error) 288 goto zutl_bail; 289 290 pn_alloc(&rpn); 291 bzero(rpn.pn_buf, MAXPATHLEN); 292 293 zl->zl_retcode = zut_open_dir(zl->zl_dir, NULL, cr, flags, &rpn, &dvn); 294 if (zl->zl_retcode) 295 goto zutl_done; 296 297 if (zl->zl_reqflags & ZUT_IGNORECASE) 298 flags |= FIGNORECASE; 299 300 zl->zl_retcode = VOP_LOOKUP(dvn, zl->zl_file, &fvn, NULL, flags, NULL, 301 cr, NULL, &zl->zl_deflags, &rpn); 302 if (zl->zl_retcode) 303 goto zutl_done; 304 305 release = fvn; 306 307 if (zl->zl_reqflags & ZUT_XATTR) { 308 vattr_t vattr; 309 310 /* 311 * In order to access hidden attribute directory the 312 * user must have appropriate read access and be able 313 * to stat() the file 314 */ 315 if (vfs_has_feature(fvn->v_vfsp, VFSFT_ACEMASKONACCESS)) { 316 zl->zl_retcode = VOP_ACCESS(fvn, ACE_READ_NAMED_ATTRS, 317 V_ACE_MASK, cr, NULL); 318 } else { 319 zl->zl_retcode = VOP_ACCESS(fvn, VREAD, 0, cr, NULL); 320 } 321 if (zl->zl_retcode) 322 goto zutl_done; 323 324 vattr.va_mask = AT_ALL; 325 zl->zl_retcode = VOP_GETATTR(fvn, &vattr, 0, cr, NULL); 326 if (zl->zl_retcode) 327 goto zutl_done; 328 329 zl->zl_retcode = VOP_LOOKUP(fvn, "", &xdvn, NULL, 330 flags | LOOKUP_XATTR, NULL, cr, NULL, NULL, NULL); 331 if (zl->zl_retcode) 332 goto zutl_done; 333 VN_RELE(fvn); 334 release = xdvn; 335 336 zl->zl_retcode = VOP_LOOKUP(xdvn, zl->zl_xfile, &xfvn, 337 NULL, flags, NULL, cr, NULL, &zl->zl_deflags, &rpn); 338 if (zl->zl_retcode) 339 goto zutl_done; 340 VN_RELE(xdvn); 341 release = xfvn; 342 } 343 344 if (zl->zl_reqflags & ZUT_GETSTAT) { 345 zl->zl_retcode = zut_stat64(release, 346 &zl->zl_statbuf, &zl->zl_xvattrs, 0, cr); 347 } 348 349 zutl_done: 350 (void) strlcpy(zl->zl_real, rpn.pn_path, MAXPATHLEN); 351 352 rc = ddi_copyout(zl, (void *)arg, sizeof (zut_lookup_t), iflag); 353 if (error == 0) 354 error = rc; 355 356 if (release) 357 VN_RELE(release); 358 if (dvn) 359 VN_RELE(dvn); 360 pn_free(&rpn); 361 362 zutl_bail: 363 kmem_free(zl, sizeof (zut_lookup_t)); 364 if (rvalp) 365 *rvalp = error; 366 return (error); 367 } 368 369 /*ARGSUSED*/ 370 static int 371 zut_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) 372 { 373 int error; 374 375 if (getminor(dev) != 0) 376 return (ENXIO); 377 378 if (cmd <= ZUT_IOC_MIN_CMD || cmd >= ZUT_IOC_MAX_CMD) 379 return (EINVAL); 380 381 switch (cmd) { 382 case ZUT_IOC_LOOKUP: 383 error = zut_lookup(arg, cr, flag, rvalp); 384 break; 385 case ZUT_IOC_READDIR: 386 error = zut_readdir(arg, cr, flag, rvalp); 387 default: 388 break; 389 } 390 391 return (error); 392 } 393 394 static int 395 zut_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 396 { 397 if (cmd != DDI_ATTACH) 398 return (DDI_FAILURE); 399 400 if (ddi_create_minor_node(dip, "zut", S_IFCHR, 0, 401 DDI_PSEUDO, 0) == DDI_FAILURE) 402 return (DDI_FAILURE); 403 404 zut_dip = dip; 405 406 ddi_report_dev(dip); 407 408 return (DDI_SUCCESS); 409 } 410 411 static int 412 zut_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 413 { 414 if (cmd != DDI_DETACH) 415 return (DDI_FAILURE); 416 417 zut_dip = NULL; 418 419 ddi_prop_remove_all(dip); 420 ddi_remove_minor_node(dip, NULL); 421 422 return (DDI_SUCCESS); 423 } 424 425 /*ARGSUSED*/ 426 static int 427 zut_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 428 { 429 switch (infocmd) { 430 case DDI_INFO_DEVT2DEVINFO: 431 *result = zut_dip; 432 return (DDI_SUCCESS); 433 434 case DDI_INFO_DEVT2INSTANCE: 435 *result = (void *)0; 436 return (DDI_SUCCESS); 437 } 438 439 return (DDI_FAILURE); 440 } 441 442 /*ARGSUSED*/ 443 int 444 zut_open(dev_t *devp, int flag, int otyp, cred_t *cr) 445 { 446 minor_t minor = getminor(*devp); 447 448 if (minor == 0) /* This is the control device */ 449 return (0); 450 451 return (ENXIO); 452 } 453 454 /*ARGSUSED*/ 455 int 456 zut_close(dev_t dev, int flag, int otyp, cred_t *cr) 457 { 458 minor_t minor = getminor(dev); 459 460 if (minor == 0) /* This is the control device */ 461 return (0); 462 463 return (ENXIO); 464 } 465 466 /* 467 * /dev/zut is the control node, i.e. minor 0. 468 * 469 * There are no other minor nodes, and /dev/zut basically does nothing 470 * other than serve up ioctls. 471 */ 472 static struct cb_ops zut_cb_ops = { 473 zut_open, /* open */ 474 zut_close, /* close */ 475 nodev, /* strategy */ 476 nodev, /* print */ 477 nodev, /* dump */ 478 nodev, /* read */ 479 nodev, /* write */ 480 zut_ioctl, /* ioctl */ 481 nodev, /* devmap */ 482 nodev, /* mmap */ 483 nodev, /* segmap */ 484 nochpoll, /* poll */ 485 ddi_prop_op, /* prop_op */ 486 NULL, /* streamtab */ 487 D_NEW | D_MP | D_64BIT, /* Driver compatibility flag */ 488 CB_REV, /* version */ 489 nodev, /* async read */ 490 nodev, /* async write */ 491 }; 492 493 static struct dev_ops zut_dev_ops = { 494 DEVO_REV, /* version */ 495 0, /* refcnt */ 496 zut_info, /* info */ 497 nulldev, /* identify */ 498 nulldev, /* probe */ 499 zut_attach, /* attach */ 500 zut_detach, /* detach */ 501 nodev, /* reset */ 502 &zut_cb_ops, /* driver operations */ 503 NULL /* no bus operations */ 504 }; 505 506 static struct modldrv zut_modldrv = { 507 &mod_driverops, "ZFS unit test " ZUT_VERSION_STRING, 508 &zut_dev_ops 509 }; 510 511 static struct modlinkage modlinkage = { 512 MODREV_1, 513 (void *)&zut_modldrv, 514 NULL 515 }; 516 517 int 518 _init(void) 519 { 520 int error; 521 522 if ((error = mod_install(&modlinkage)) != 0) { 523 return (error); 524 } 525 526 error = ldi_ident_from_mod(&modlinkage, &zut_li); 527 ASSERT(error == 0); 528 529 return (0); 530 } 531 532 int 533 _fini(void) 534 { 535 int error; 536 537 if ((error = mod_remove(&modlinkage)) != 0) 538 return (error); 539 540 ldi_ident_release(zut_li); 541 zut_li = NULL; 542 543 return (error); 544 } 545 546 int 547 _info(struct modinfo *modinfop) 548 { 549 return (mod_info(&modlinkage, modinfop)); 550 } 551