static void zfsctl_rename_snap(zfsctl_snapdir_t *sdp, zfs_snapentry_t *sep, const char *nm) { avl_index_t where; vfs_t *vfsp; refstr_t *pathref; char newpath[MAXNAMELEN]; char *tail; ASSERT(MUTEX_HELD(&sdp->sd_lock)); ASSERT(sep != NULL); vfsp = vn_mountedvfs(sep->se_root); ASSERT(vfsp != NULL); vfs_lock_wait(vfsp); /* * Change the name in the AVL tree. */ avl_remove(&sdp->sd_snaps, sep); kmem_free(sep->se_name, strlen(sep->se_name) + 1); sep->se_name = kmem_alloc(strlen(nm) + 1, KM_SLEEP); (void) strcpy(sep->se_name, nm); VERIFY(avl_find(&sdp->sd_snaps, sep, &where) == NULL); avl_insert(&sdp->sd_snaps, sep, where); /* * Change the current mountpoint info: * - update the tail of the mntpoint path * - update the tail of the resource path */ pathref = vfs_getmntpoint(vfsp); (void) strncpy(newpath, refstr_value(pathref), sizeof (newpath)); VERIFY((tail = strrchr(newpath, '/')) != NULL); *(tail+1) = '\0'; ASSERT3U(strlen(newpath) + strlen(nm), <, sizeof (newpath)); (void) strcat(newpath, nm); refstr_rele(pathref); vfs_setmntpoint(vfsp, newpath); pathref = vfs_getresource(vfsp); (void) strncpy(newpath, refstr_value(pathref), sizeof (newpath)); VERIFY((tail = strrchr(newpath, '@')) != NULL); *(tail+1) = '\0'; ASSERT3U(strlen(newpath) + strlen(nm), <, sizeof (newpath)); (void) strcat(newpath, nm); refstr_rele(pathref); vfs_setresource(vfsp, newpath); vfs_unlock(vfsp); }
/* * Extract the volume name. */ static void smb_tree_get_volname(vfs_t *vfsp, smb_tree_t *tree) { refstr_t *vfs_mntpoint; const char *s; char *name; vfs_mntpoint = vfs_getmntpoint(vfsp); s = vfs_mntpoint->rs_string; s += strspn(s, "/"); (void) strlcpy(tree->t_volume, s, SMB_VOLNAMELEN); refstr_rele(vfs_mntpoint); name = tree->t_volume; (void) strsep((char **)&name, "/"); }
/* ARGSUSED */ int ufs_fioffs( struct vnode *vp, char *vap, /* must be NULL - reserved */ struct cred *cr) /* credentials from ufs_ioctl */ { int error; struct ufsvfs *ufsvfsp; struct ulockfs *ulp; /* file system has been forcibly unmounted */ ufsvfsp = VTOI(vp)->i_ufsvfs; if (ufsvfsp == NULL) return (EIO); ulp = &ufsvfsp->vfs_ulockfs; /* * suspend the delete thread * this must be done outside the lockfs locking protocol */ vfs_lock_wait(vp->v_vfsp); ufs_thread_suspend(&ufsvfsp->vfs_delete); /* hold the mutex to prevent race with a lockfs request */ mutex_enter(&ulp->ul_lock); atomic_inc_ulong(&ufs_quiesce_pend); if (ULOCKFS_IS_HLOCK(ulp)) { error = EIO; goto out; } if (ULOCKFS_IS_ELOCK(ulp)) { error = EBUSY; goto out; } /* wait for outstanding accesses to finish */ if (error = ufs_quiesce(ulp)) goto out; /* * If logging, and the logmap was marked as not rollable, * make it rollable now, and start the trans_roll thread and * the reclaim thread. The log at this point is safe to write to. */ if (ufsvfsp->vfs_log) { ml_unit_t *ul = ufsvfsp->vfs_log; struct fs *fsp = ufsvfsp->vfs_fs; int err; if (ul->un_flags & LDL_NOROLL) { ul->un_flags &= ~LDL_NOROLL; logmap_start_roll(ul); if (!fsp->fs_ronly && (fsp->fs_reclaim & (FS_RECLAIM|FS_RECLAIMING))) { fsp->fs_reclaim &= ~FS_RECLAIM; fsp->fs_reclaim |= FS_RECLAIMING; ufs_thread_start(&ufsvfsp->vfs_reclaim, ufs_thread_reclaim, vp->v_vfsp); if (!fsp->fs_ronly) { TRANS_SBWRITE(ufsvfsp, TOP_SBUPDATE_UPDATE); if (err = geterror(ufsvfsp->vfs_bufp)) { refstr_t *mntpt; mntpt = vfs_getmntpoint( vp->v_vfsp); cmn_err(CE_NOTE, "Filesystem Flush " "Failed to update " "Reclaim Status for " " %s, Write failed to " "update superblock, " "error %d", refstr_value(mntpt), err); refstr_rele(mntpt); } } } } } /* synchronously flush dirty data and metadata */ error = ufs_flush(vp->v_vfsp); out: atomic_dec_ulong(&ufs_quiesce_pend); cv_broadcast(&ulp->ul_cv); mutex_exit(&ulp->ul_lock); vfs_unlock(vp->v_vfsp); /* * allow the delete thread to continue */ ufs_thread_continue(&ufsvfsp->vfs_delete); return (error); }
/* * Given a directory, return the full, resolved path. This looks up "..", * searches for the given vnode in the parent, appends the component, etc. It * is used to implement vnodetopath() and getcwd() when the cached path fails. */ static int dirtopath(vnode_t *vrootp, vnode_t *vp, char *buf, size_t buflen, int flags, cred_t *cr) { pathname_t pn, rpn, emptypn; vnode_t *cmpvp, *pvp = NULL; vnode_t *startvp = vp; int err = 0, vprivs; size_t complen; char *dbuf; dirent64_t *dp; char *bufloc; size_t dlen = DIRENT64_RECLEN(MAXPATHLEN); refstr_t *mntpt; /* Operation only allowed on directories */ ASSERT(vp->v_type == VDIR); /* We must have at least enough space for "/" */ if (buflen < 2) return (ENAMETOOLONG); /* Start at end of string with terminating null */ bufloc = &buf[buflen - 1]; *bufloc = '\0'; pn_alloc(&pn); pn_alloc(&rpn); dbuf = kmem_alloc(dlen, KM_SLEEP); bzero(&emptypn, sizeof (emptypn)); /* * Begin with an additional reference on vp. This will be decremented * during the loop. */ VN_HOLD(vp); for (;;) { /* * Return if we've reached the root. If the buffer is empty, * return '/'. We explicitly don't use vn_compare(), since it * compares the real vnodes. A lofs mount of '/' would produce * incorrect results otherwise. */ if (VN_CMP(vrootp, vp)) { if (*bufloc == '\0') *--bufloc = '/'; break; } /* * If we've reached the VFS root, something has gone wrong. We * should have reached the root in the above check. The only * explantation is that 'vp' is not contained withing the given * root, in which case we return EPERM. */ if (VN_CMP(rootdir, vp)) { err = EPERM; goto out; } /* * Shortcut: see if this vnode is a mountpoint. If so, * grab the path information from the vfs_t. */ if (vp->v_flag & VROOT) { mntpt = vfs_getmntpoint(vp->v_vfsp); if ((err = pn_set(&pn, (char *)refstr_value(mntpt))) == 0) { refstr_rele(mntpt); rpn.pn_path = rpn.pn_buf; /* * Ensure the mountpoint still exists. */ VN_HOLD(vrootp); if (vrootp != rootdir) VN_HOLD(vrootp); if (lookuppnvp(&pn, &rpn, flags, NULL, &cmpvp, vrootp, vrootp, cr) == 0) { if (VN_CMP(vp, cmpvp)) { VN_RELE(cmpvp); complen = strlen(rpn.pn_path); bufloc -= complen; if (bufloc < buf) { err = ERANGE; goto out; } bcopy(rpn.pn_path, bufloc, complen); break; } else { VN_RELE(cmpvp); } } } else { refstr_rele(mntpt); } } /* * Shortcut: see if this vnode has correct v_path. If so, * we have the work done. */ mutex_enter(&vp->v_lock); if (vp->v_path != NULL) { if ((err = pn_set(&pn, vp->v_path)) == 0) { mutex_exit(&vp->v_lock); rpn.pn_path = rpn.pn_buf; /* * Ensure the v_path pointing to correct vnode */ VN_HOLD(vrootp); if (vrootp != rootdir) VN_HOLD(vrootp); if (lookuppnvp(&pn, &rpn, flags, NULL, &cmpvp, vrootp, vrootp, cr) == 0) { if (VN_CMP(vp, cmpvp)) { VN_RELE(cmpvp); complen = strlen(rpn.pn_path); bufloc -= complen; if (bufloc < buf) { err = ERANGE; goto out; } bcopy(rpn.pn_path, bufloc, complen); break; } else { VN_RELE(cmpvp); } } } else { mutex_exit(&vp->v_lock); } } else { mutex_exit(&vp->v_lock); } /* * Shortcuts failed, search for this vnode in its parent. If * this is a mountpoint, then get the vnode underneath. */ if (vp->v_flag & VROOT) vp = vn_under(vp); if ((err = VOP_LOOKUP(vp, "..", &pvp, &emptypn, 0, vrootp, cr, NULL, NULL, NULL)) != 0) goto out; /* * With extended attributes, it's possible for a directory to * have a parent that is a regular file. Check for that here. */ if (pvp->v_type != VDIR) { err = ENOTDIR; goto out; } /* * If this is true, something strange has happened. This is * only true if we are the root of a filesystem, which should * have been caught by the check above. */ if (VN_CMP(pvp, vp)) { err = ENOENT; goto out; } /* * Check if we have read and search privilege so, that * we can lookup the path in the directory */ vprivs = (flags & LOOKUP_CHECKREAD) ? VREAD | VEXEC : VEXEC; if ((err = VOP_ACCESS(pvp, vprivs, 0, cr, NULL)) != 0) { goto out; } /* * Try to obtain the path component from dnlc cache * before searching through the directory. */ if ((cmpvp = dnlc_reverse_lookup(vp, dbuf, dlen)) != NULL) { /* * If we got parent vnode as a result, * then the answered path is correct. */ if (VN_CMP(cmpvp, pvp)) { VN_RELE(cmpvp); complen = strlen(dbuf); bufloc -= complen; if (bufloc <= buf) { err = ENAMETOOLONG; goto out; } bcopy(dbuf, bufloc, complen); /* Prepend a slash to the current path */ *--bufloc = '/'; /* And continue with the next component */ VN_RELE(vp); vp = pvp; pvp = NULL; continue; } else { VN_RELE(cmpvp); } } /* * Search the parent directory for the entry corresponding to * this vnode. */ if ((err = dirfindvp(vrootp, pvp, vp, cr, dbuf, dlen, &dp)) != 0) goto out; complen = strlen(dp->d_name); bufloc -= complen; if (bufloc <= buf) { err = ENAMETOOLONG; goto out; } bcopy(dp->d_name, bufloc, complen); /* Prepend a slash to the current path. */ *--bufloc = '/'; /* And continue with the next component */ VN_RELE(vp); vp = pvp; pvp = NULL; } /* * Place the path at the beginning of the buffer. */ if (bufloc != buf) ovbcopy(bufloc, buf, buflen - (bufloc - buf)); out: /* * If the error was ESTALE and the current directory to look in * was the root for this lookup, the root for a mounted file * system, or the starting directory for lookups, then * return ENOENT instead of ESTALE. In this case, no recovery * is possible by the higher level. If ESTALE was returned for * some intermediate directory along the path, then recovery * is potentially possible and retrying from the higher level * will either correct the situation by purging stale cache * entries or eventually get back to the point where no recovery * is possible. */ if (err == ESTALE && (VN_CMP(vp, vrootp) || (vp->v_flag & VROOT) || vp == startvp)) err = ENOENT; kmem_free(dbuf, dlen); VN_RELE(vp); if (pvp) VN_RELE(pvp); pn_free(&pn); pn_free(&rpn); return (err); }
/* * getflabel - * * Return pointer to the ts_label associated with the specified file, * or returns NULL if error occurs. Caller is responsible for doing * a label_rele of the ts_label. */ ts_label_t * getflabel(vnode_t *vp) { vfs_t *vfsp, *rvfsp; vnode_t *rvp, *rvp2; zone_t *zone; ts_label_t *zl; int err; boolean_t vfs_is_held = B_FALSE; char vpath[MAXPATHLEN]; ASSERT(vp); vfsp = vp->v_vfsp; if (vfsp == NULL) return (NULL); rvp = vp; /* * Traverse lofs mounts and fattach'es to get the real vnode */ if (VOP_REALVP(rvp, &rvp2, NULL) == 0) rvp = rvp2; rvfsp = rvp->v_vfsp; /* rvp/rvfsp now represent the real vnode/vfs we will be using */ /* Go elsewhere to handle all nfs files. */ if (strncmp(vfssw[rvfsp->vfs_fstype].vsw_name, "nfs", 3) == 0) return (getflabel_nfs(rvfsp)); /* * Fast path, for objects in a labeled zone: everything except * for lofs/nfs will be just the label of that zone. */ if ((rvfsp->vfs_zone != NULL) && (rvfsp->vfs_zone != global_zone)) { if ((strcmp(vfssw[rvfsp->vfs_fstype].vsw_name, "lofs") != 0)) { zone = rvfsp->vfs_zone; zone_hold(zone); goto zone_out; /* return this label */ } } /* * Get the vnode path -- it may be missing or weird for some * cases, like devices. In those cases use the label of the * current zone. */ err = vnodetopath(rootdir, rvp, vpath, sizeof (vpath), kcred); if ((err != 0) || (*vpath != '/')) { zone = curproc->p_zone; zone_hold(zone); goto zone_out; } /* * For zfs filesystem, return the explicit label property if a * meaningful one exists. */ if (strncmp(vfssw[rvfsp->vfs_fstype].vsw_name, "zfs", 3) == 0) { ts_label_t *tsl; tsl = getflabel_zfs(rvfsp); /* if label found, return it, otherwise continue... */ if (tsl != NULL) return (tsl); } /* * If a mountpoint exists, hold the vfs while we reference it. * Otherwise if mountpoint is NULL it should not be held (e.g., * a hold/release on spec_vfs would result in an attempted free * and panic.) */ if (vfsp->vfs_mntpt != NULL) { VFS_HOLD(vfsp); vfs_is_held = B_TRUE; } zone = zone_find_by_any_path(vpath, B_FALSE); /* * If the vnode source zone is properly set to a non-global zone, or * any zone if the mount is R/W, then use the label of that zone. */ if ((zone != global_zone) || ((vfsp->vfs_flag & VFS_RDONLY) != 0)) goto zone_out; /* return this label */ /* * Otherwise, if we're not in the global zone, use the label of * our zone. */ if ((zone = curproc->p_zone) != global_zone) { zone_hold(zone); goto zone_out; /* return this label */ } /* * We're in the global zone and the mount is R/W ... so the file * may actually be in the global zone -- or in the root of any zone. * Always build our own path for the file, to be sure it's simplified * (i.e., no ".", "..", "//", and so on). */ zone_rele(zone); zone = zone_find_by_any_path(vpath, B_FALSE); zone_out: if ((curproc->p_zone == global_zone) && (zone == global_zone)) { vfs_t *nvfs; boolean_t exported = B_FALSE; refstr_t *mntpt_ref; char *mntpt; /* * File is in the global zone - check whether it's admin_high. * If it's in a filesys that was exported from the global zone, * it's admin_low by definition. Otherwise, if it's in a * filesys that's NOT exported to any zone, it's admin_high. * * And for these files if there wasn't a valid mount resource, * the file must be admin_high (not exported, probably a global * zone device). */ if (!vfs_is_held) goto out_high; mntpt_ref = vfs_getmntpoint(vfsp); mntpt = (char *)refstr_value(mntpt_ref); if ((mntpt != NULL) && (*mntpt == '/')) { zone_t *to_zone; to_zone = zone_find_by_any_path(mntpt, B_FALSE); zone_rele(to_zone); if (to_zone != global_zone) { /* force admin_low */ exported = B_TRUE; } } if (mntpt_ref) refstr_rele(mntpt_ref); if (!exported) { size_t plen = strlen(vpath); vfs_list_read_lock(); nvfs = vfsp->vfs_next; while (nvfs != vfsp) { const char *rstr; size_t rlen = 0; /* * Skip checking this vfs if it's not lofs * (the only way to export from the global * zone to a zone). */ if (strncmp(vfssw[nvfs->vfs_fstype].vsw_name, "lofs", 4) != 0) { nvfs = nvfs->vfs_next; continue; } rstr = refstr_value(nvfs->vfs_resource); if (rstr != NULL) rlen = strlen(rstr); /* * Check for a match: does this vfs correspond * to our global zone file path? I.e., check * if the resource string of this vfs is a * prefix of our path. */ if ((rlen > 0) && (rlen <= plen) && (strncmp(rstr, vpath, rlen) == 0) && (vpath[rlen] == '/' || vpath[rlen] == '\0')) { /* force admin_low */ exported = B_TRUE; break; } nvfs = nvfs->vfs_next; } vfs_list_unlock(); } if (!exported) goto out_high; } if (vfs_is_held) VFS_RELE(vfsp); /* * Now that we have the "home" zone for the file, return the slabel * of that zone. */ zl = zone->zone_slabel; label_hold(zl); zone_rele(zone); return (zl); out_high: if (vfs_is_held) VFS_RELE(vfsp); label_hold(l_admin_high); zone_rele(zone); return (l_admin_high); }
/* * Get/Make vfs structure for given real vfs */ static struct vfs * makelfsnode(struct vfs *vfsp, struct loinfo *li) { struct lfsnode *lfs; struct lfsnode *tlfs; /* * Don't grab any locks for the fast (common) case. */ if (vfsp == li->li_realvfs) return (li->li_mountvfs); ASSERT(li->li_refct > 0); mutex_enter(&li->li_lfslock); if ((lfs = lfsfind(vfsp, li)) == NULL) { mutex_exit(&li->li_lfslock); lfs = kmem_zalloc(sizeof (*lfs), KM_SLEEP); mutex_enter(&li->li_lfslock); if ((tlfs = lfsfind(vfsp, li)) != NULL) { kmem_free(lfs, sizeof (*lfs)); lfs = tlfs; goto found_lfs; } lfs->lfs_realvfs = vfsp; /* * Even though the lfsnode is strictly speaking a private * implementation detail of lofs, it should behave as a regular * vfs_t for the benefit of the rest of the kernel. */ VFS_INIT(&lfs->lfs_vfs, lo_vfsops, (caddr_t)li); lfs->lfs_vfs.vfs_fstype = li->li_mountvfs->vfs_fstype; lfs->lfs_vfs.vfs_flag = ((vfsp->vfs_flag | li->li_mflag) & ~li->li_dflag) & INHERIT_VFS_FLAG; lfs->lfs_vfs.vfs_bsize = vfsp->vfs_bsize; lfs->lfs_vfs.vfs_dev = vfsp->vfs_dev; lfs->lfs_vfs.vfs_fsid = vfsp->vfs_fsid; if (vfsp->vfs_mntpt != NULL) { lfs->lfs_vfs.vfs_mntpt = vfs_getmntpoint(vfsp); /* Leave a reference to the mountpoint */ } (void) VFS_ROOT(vfsp, &lfs->lfs_realrootvp); /* * We use 1 instead of 0 as the value to associate with * an idle lfs_vfs. This is to prevent VFS_RELE() * trying to kmem_free() our lfs_t (which is the wrong * size). */ VFS_HOLD(&lfs->lfs_vfs); lfs->lfs_next = li->li_lfs; li->li_lfs = lfs; vfs_propagate_features(vfsp, &lfs->lfs_vfs); } found_lfs: VFS_HOLD(&lfs->lfs_vfs); mutex_exit(&li->li_lfslock); return (&lfs->lfs_vfs); }