Print this page
6975745 xattr directories should be more transparent

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/xattr.c
          +++ new/usr/src/uts/common/fs/xattr.c
↓ open down ↓ 46 lines elided ↑ open up ↑
  47   47  #include <fs/fs_subr.h>
  48   48  #include <sys/kidmap.h>
  49   49  
  50   50  typedef struct {
  51   51          gfs_file_t      xattr_gfs_private;
  52   52          xattr_view_t    xattr_view;
  53   53  } xattr_file_t;
  54   54  
  55   55  typedef struct {
  56   56          gfs_dir_t       xattr_gfs_private;
  57      -        vnode_t         *xattr_realvp;  /* Only used for VOP_REALVP */
       57 +        vnode_t         *xattr_realvp;
  58   58  } xattr_dir_t;
  59   59  
  60      -/*
  61      - * xattr_realvp is only used for VOP_REALVP, this is so we don't
  62      - * keep an unnecessary hold on the *real* xattr dir unless we have
  63      - * no other choice.
  64      - */
  65      -
  66   60  /* ARGSUSED */
  67   61  static int
  68   62  xattr_file_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
  69   63  {
  70   64          xattr_file_t *np = (*vpp)->v_data;
  71   65  
  72   66          if ((np->xattr_view == XATTR_VIEW_READONLY) && (flags & FWRITE))
  73   67                  return (EACCES);
  74   68  
  75   69          return (0);
↓ open down ↓ 796 lines elided ↑ open up ↑
 872  866  
 873  867          pdvp = gfs_file_parent(tdvp);
 874  868          error = VOP_SETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
 875  869          return (error);
 876  870  }
 877  871  
 878  872  static int
 879  873  xattr_dir_realdir(vnode_t *dvp, vnode_t **realdvp, int lookup_flags,
 880  874      cred_t *cr, caller_context_t *ct)
 881  875  {
 882      -        vnode_t *pvp;
      876 +        xattr_dir_t *xattr_dir;
 883  877          int error;
 884      -        struct pathname pn;
 885      -        char *startnm = "";
 886  878  
 887  879          *realdvp = NULL;
 888  880  
 889      -        pvp = gfs_file_parent(dvp);
      881 +        if (dvp->v_type != VDIR)
      882 +                return (EINVAL);
 890  883  
 891      -        error = pn_get(startnm, UIO_SYSSPACE, &pn);
 892      -        if (error) {
 893      -                VN_RELE(pvp);
 894      -                return (error);
 895      -        }
      884 +        mutex_enter(&dvp->v_lock);
      885 +        xattr_dir = dvp->v_data;
      886 +        *realdvp = xattr_dir->xattr_realvp;
      887 +        mutex_exit(&dvp->v_lock);
 896  888  
 897      -        /*
 898      -         * Set the LOOKUP_HAVE_SYSATTR_DIR flag so that we don't get into an
 899      -         * infinite loop with fop_lookup calling back to xattr_dir_lookup.
 900      -         */
 901      -        lookup_flags |= LOOKUP_HAVE_SYSATTR_DIR;
 902      -        error = VOP_LOOKUP(pvp, startnm, realdvp, &pn, lookup_flags,
 903      -            rootvp, cr, ct, NULL, NULL);
 904      -        pn_free(&pn);
      889 +        if (*realdvp != NULL) {
      890 +                VN_HOLD(*realdvp);
      891 +                error = 0;
      892 +        } else
      893 +                error = ENOENT;
 905  894  
 906  895          return (error);
 907  896  }
 908  897  
 909  898  /* ARGSUSED */
 910  899  static int
 911  900  xattr_dir_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
 912  901  {
      902 +        vnode_t *realvp;
      903 +        int error;
      904 +
 913  905          if (flags & FWRITE) {
 914  906                  return (EACCES);
 915  907          }
 916  908  
      909 +        /*
      910 +         * The underlying FS may need this VOP call.
      911 +         */
      912 +        error = xattr_dir_realdir(*vpp, &realvp, LOOKUP_XATTR, cr, ct);
      913 +        if (error == 0) {
      914 +                error = VOP_OPEN(&realvp, flags, cr, ct);
      915 +                VN_RELE(realvp);
      916 +                if (error)
      917 +                        return (error);
      918 +        } /* else ignore this error */
      919 +
 917  920          return (0);
 918  921  }
 919  922  
 920  923  /* ARGSUSED */
 921  924  static int
 922      -xattr_dir_close(vnode_t *vpp, int flags, int count, offset_t off, cred_t *cr,
      925 +xattr_dir_close(vnode_t *vp, int flags, int count, offset_t off, cred_t *cr,
 923  926      caller_context_t *ct)
 924  927  {
      928 +        vnode_t *realvp;
      929 +        int error;
      930 +
      931 +        /*
      932 +         * The underlying FS may need this VOP call.
      933 +         */
      934 +        error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
      935 +        if (error == 0) {
      936 +                error = VOP_CLOSE(realvp, flags, count, off, cr, ct);
      937 +                VN_RELE(realvp);
      938 +                if (error)
      939 +                        return (error);
      940 +        } /* else ignore this error */
      941 +
 925  942          return (0);
 926  943  }
 927  944  
 928  945  /*
 929  946   * Retrieve the attributes on an xattr directory.  If there is a "real"
 930  947   * xattr directory, use that.  Otherwise, get the attributes (represented
 931  948   * by PARENT_ATTRMASK) from the "parent" node and fill in the rest.  Note
 932  949   * that VOP_GETATTR() could turn off bits in the va_mask.
 933  950   */
 934  951  
↓ open down ↓ 414 lines elided ↑ open up ↑
1349 1366                  return (0);
1350 1367          default:
1351 1368                  return (fs_pathconf(vp, cmd, valp, cr, ct));
1352 1369          }
1353 1370  }
1354 1371  
1355 1372  /* ARGSUSED */
1356 1373  static int
1357 1374  xattr_dir_realvp(vnode_t *vp, vnode_t **realvp, caller_context_t *ct)
1358 1375  {
1359      -        xattr_dir_t *xattr_dir;
     1376 +        int error;
1360 1377  
1361      -        mutex_enter(&vp->v_lock);
1362      -        xattr_dir = vp->v_data;
1363      -        if (xattr_dir->xattr_realvp) {
1364      -                *realvp = xattr_dir->xattr_realvp;
1365      -                mutex_exit(&vp->v_lock);
1366      -                return (0);
1367      -        } else {
1368      -                vnode_t *xdvp;
1369      -                int error;
     1378 +        error = xattr_dir_realdir(vp, realvp, LOOKUP_XATTR, kcred, NULL);
     1379 +        return (error);
1370 1380  
1371      -                mutex_exit(&vp->v_lock);
1372      -                if ((error = xattr_dir_realdir(vp, &xdvp,
1373      -                    LOOKUP_XATTR, kcred, NULL)) == 0) {
1374      -                        /*
1375      -                         * verify we aren't racing with another thread
1376      -                         * to find the xattr_realvp
1377      -                         */
1378      -                        mutex_enter(&vp->v_lock);
1379      -                        if (xattr_dir->xattr_realvp == NULL) {
1380      -                                xattr_dir->xattr_realvp = xdvp;
1381      -                                *realvp = xdvp;
1382      -                                mutex_exit(&vp->v_lock);
1383      -                        } else {
1384      -                                *realvp = xattr_dir->xattr_realvp;
1385      -                                mutex_exit(&vp->v_lock);
1386      -                                VN_RELE(xdvp);
1387      -                        }
1388      -                }
1389      -                return (error);
1390      -        }
1391 1381  }
1392 1382  
1393 1383  static const fs_operation_def_t xattr_dir_tops[] = {
1394 1384          { VOPNAME_OPEN,         { .vop_open = xattr_dir_open }          },
1395 1385          { VOPNAME_CLOSE,        { .vop_close = xattr_dir_close }        },
1396 1386          { VOPNAME_IOCTL,        { .error = fs_inval }                   },
1397 1387          { VOPNAME_GETATTR,      { .vop_getattr = xattr_dir_getattr }    },
1398 1388          { VOPNAME_SETATTR,      { .vop_setattr = xattr_dir_setattr }    },
1399 1389          { VOPNAME_ACCESS,       { .vop_access = xattr_dir_access }      },
1400 1390          { VOPNAME_READDIR,      { .vop_readdir = xattr_dir_readdir }    },
↓ open down ↓ 62 lines elided ↑ open up ↑
1463 1453           */
1464 1454          return ((ino64_t)index+1);
1465 1455  }
1466 1456  
1467 1457  void
1468 1458  xattr_init(void)
1469 1459  {
1470 1460          VERIFY(gfs_make_opsvec(xattr_opsvec) == 0);
1471 1461  }
1472 1462  
     1463 +/* See vnode.c: fop_lookup() */
1473 1464  int
1474 1465  xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr)
1475 1466  {
1476 1467          int error = 0;
     1468 +        vnode_t *gfs_vp = NULL;
     1469 +        vnode_t *real_vp = NULL;
     1470 +        xattr_dir_t *xattr_dir;
     1471 +        struct pathname pn;
     1472 +        char *nm = "";
1477 1473  
1478 1474          *vpp = NULL;
1479 1475  
1480 1476          if (dvp->v_type != VDIR && dvp->v_type != VREG)
1481 1477                  return (EINVAL);
1482 1478  
1483 1479          mutex_enter(&dvp->v_lock);
1484 1480  
1485 1481          /*
1486 1482           * If we're already in sysattr space, don't allow creation
1487 1483           * of another level of sysattrs.
1488 1484           */
1489 1485          if (dvp->v_flag & V_SYSATTR) {
1490 1486                  mutex_exit(&dvp->v_lock);
1491 1487                  return (EINVAL);
1492 1488          }
1493 1489  
1494 1490          if (dvp->v_xattrdir != NULL) {
1495      -                *vpp = dvp->v_xattrdir;
1496      -                VN_HOLD(*vpp);
     1491 +                gfs_vp = dvp->v_xattrdir;
     1492 +                VN_HOLD(gfs_vp);
1497 1493          } else {
1498 1494                  ulong_t val;
1499 1495                  int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR;
1500 1496                  int sysattrs_allowed = 1;
1501 1497  
1502 1498                  /*
1503 1499                   * We have to drop the lock on dvp.  gfs_dir_create will
1504 1500                   * grab it for a VN_HOLD.
1505 1501                   */
1506 1502                  mutex_exit(&dvp->v_lock);
↓ open down ↓ 5 lines elided ↑ open up ↑
1512 1508                   * has vnode-level granularity (e.g. .zfs).
1513 1509                   */
1514 1510                  error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL);
1515 1511                  if (error != 0 || val == 0)
1516 1512                          sysattrs_allowed = 0;
1517 1513  
1518 1514                  if (!xattrs_allowed && !sysattrs_allowed)
1519 1515                          return (EINVAL);
1520 1516  
1521 1517                  if (!sysattrs_allowed) {
1522      -                        struct pathname pn;
1523      -                        char *nm = "";
1524      -
1525 1518                          error = pn_get(nm, UIO_SYSSPACE, &pn);
1526 1519                          if (error)
1527 1520                                  return (error);
1528 1521                          error = VOP_LOOKUP(dvp, nm, vpp, &pn,
1529 1522                              flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
1530 1523                              NULL, NULL);
1531 1524                          pn_free(&pn);
1532 1525                          return (error);
1533 1526                  }
1534 1527  
1535 1528                  /*
1536 1529                   * Note that we act as if we were given CREATE_XATTR_DIR,
1537 1530                   * but only for creation of the GFS directory.
1538 1531                   */
1539      -                *vpp = gfs_dir_create(
     1532 +                gfs_vp = gfs_dir_create(
1540 1533                      sizeof (xattr_dir_t), dvp, xattr_dir_ops, xattr_dirents,
1541 1534                      xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb);
1542 1535                  mutex_enter(&dvp->v_lock);
1543 1536                  if (dvp->v_xattrdir != NULL) {
1544 1537                          /*
1545 1538                           * We lost the race to create the xattr dir.
1546 1539                           * Destroy this one, use the winner.  We can't
1547 1540                           * just call VN_RELE(*vpp), because the vnode
1548 1541                           * is only partially initialized.
1549 1542                           */
1550      -                        gfs_dir_t *dp = (*vpp)->v_data;
     1543 +                        gfs_dir_t *dp = gfs_vp->v_data;
1551 1544  
1552      -                        ASSERT((*vpp)->v_count == 1);
1553      -                        vn_free(*vpp);
     1545 +                        ASSERT(gfs_vp->v_count == 1);
     1546 +                        vn_free(gfs_vp);
1554 1547  
1555 1548                          mutex_destroy(&dp->gfsd_lock);
1556 1549                          kmem_free(dp->gfsd_static,
1557 1550                              dp->gfsd_nstatic * sizeof (gfs_dirent_t));
1558 1551                          kmem_free(dp, dp->gfsd_file.gfs_size);
1559 1552  
1560 1553                          /*
1561 1554                           * There is an implied VN_HOLD(dvp) here.  We should
1562 1555                           * be doing a VN_RELE(dvp) to clean up the reference
1563      -                         * from *vpp, and then a VN_HOLD(dvp) for the new
     1556 +                         * from gfs_vp, and then a VN_HOLD(dvp) for the new
1564 1557                           * reference.  Instead, we just leave the count alone.
1565 1558                           */
1566 1559  
1567      -                        *vpp = dvp->v_xattrdir;
1568      -                        VN_HOLD(*vpp);
     1560 +                        gfs_vp = dvp->v_xattrdir;
     1561 +                        VN_HOLD(gfs_vp);
1569 1562                  } else {
1570      -                        (*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR);
1571      -                        dvp->v_xattrdir = *vpp;
     1563 +                        gfs_vp->v_flag |= (V_XATTRDIR|V_SYSATTR);
     1564 +                        dvp->v_xattrdir = gfs_vp;
1572 1565                  }
1573 1566          }
1574 1567          mutex_exit(&dvp->v_lock);
1575 1568  
1576      -        return (error);
     1569 +        /*
     1570 +         * In order to make this module relatively transparent
     1571 +         * to the underlying filesystem, we need to lookup the
     1572 +         * xattr dir in the lower filesystem and (if found)
     1573 +         * keep a hold on it for as long as there is a hold
     1574 +         * on the gfs_vp we're about to return.  This hold is
     1575 +         * released in xattr_dir_inactive.
     1576 +         */
     1577 +        xattr_dir = gfs_vp->v_data;
     1578 +        if ((dvp->v_vfsp->vfs_flag & VFS_XATTR) &&
     1579 +            (xattr_dir->xattr_realvp == NULL)) {
     1580 +                error = pn_get(nm, UIO_SYSSPACE, &pn);
     1581 +                if (error == 0) {
     1582 +                        error = VOP_LOOKUP(dvp, nm, &real_vp, &pn,
     1583 +                            flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
     1584 +                            NULL, NULL);
     1585 +                        pn_free(&pn);
     1586 +                }
     1587 +                if (error == 0) {
     1588 +                        mutex_enter(&gfs_vp->v_lock);
     1589 +                        if (xattr_dir->xattr_realvp == NULL)
     1590 +                                xattr_dir->xattr_realvp = real_vp;
     1591 +                        else
     1592 +                                VN_RELE(real_vp);
     1593 +                        mutex_exit(&gfs_vp->v_lock);
     1594 +                }
     1595 +        }
     1596 +
     1597 +        *vpp = gfs_vp;
     1598 +        return (0);
1577 1599  }
1578 1600  
1579 1601  int
1580 1602  xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
1581 1603  {
1582 1604          int error;
1583 1605          vnode_t *pvp, *dvp;
1584 1606          xattr_fid_t *xfidp;
1585 1607          struct pathname pn;
1586 1608          char *nm;
↓ open down ↓ 63 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX