+ /*
+ * No parent, go get it if supported.
+ */
+ struct vnode_attr va;
+ vnode_t dvp;
+
+ /*
+ * Make sure file system supports obtaining a path from id.
+ */
+ if (!(vp->v_mount->mnt_kern_flag & MNTK_PATH_FROM_ID)) {
+ ret = ENOENT;
+ goto out_unlock;
+ }
+ vid = vp->v_id;
+
+ NAME_CACHE_UNLOCK();
+
+ if (vp != first_vp && vp != vp_with_iocount) {
+ if (vp_with_iocount) {
+ vnode_put(vp_with_iocount);
+ vp_with_iocount = NULLVP;
+ }
+ if (vnode_getwithvid(vp, vid))
+ goto again;
+ vp_with_iocount = vp;
+ }
+ VATTR_INIT(&va);
+ VATTR_WANTED(&va, va_parentid);
+
+ if (fixhardlink) {
+ VATTR_WANTED(&va, va_name);
+ MALLOC_ZONE(va.va_name, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
+ } else {
+ va.va_name = NULL;
+ }
+ /*
+ * Ask the file system for its parent id and for its name (optional).
+ */
+ ret = vnode_getattr(vp, &va, ctx);
+
+ if (fixhardlink) {
+ if ((ret == 0) && (VATTR_IS_SUPPORTED(&va, va_name))) {
+ str = va.va_name;
+ vnode_update_identity(vp, NULL, str, strlen(str), 0, VNODE_UPDATE_NAME);
+ } else if (vp->v_name) {
+ str = vp->v_name;
+ ret = 0;
+ } else {
+ ret = ENOENT;
+ goto bad_news;
+ }
+ len = strlen(str);
+
+ /*
+ * Check that there's enough space.
+ */
+ if ((end - buff) < (len + 1)) {
+ ret = ENOSPC;
+ } else {
+ /* Copy the name backwards. */
+ str += len;
+
+ for (; len > 0; len--) {
+ *--end = *--str;
+ }
+ /*
+ * Add a path separator.
+ */
+ *--end = '/';
+ }
+bad_news:
+ FREE_ZONE(va.va_name, MAXPATHLEN, M_NAMEI);
+ }
+ if (ret || !VATTR_IS_SUPPORTED(&va, va_parentid)) {
+ ret = ENOENT;
+ goto out;
+ }
+ /*
+ * Ask the file system for the parent vnode.
+ */
+ if ((ret = VFS_VGET(vp->v_mount, (ino64_t)va.va_parentid, &dvp, ctx)))
+ goto out;
+
+ if (!fixhardlink && (vp->v_parent != dvp))
+ vnode_update_identity(vp, dvp, NULL, 0, 0, VNODE_UPDATE_PARENT);
+
+ if (vp_with_iocount)
+ vnode_put(vp_with_iocount);
+ vp = dvp;
+ vp_with_iocount = vp;
+
+ NAME_CACHE_LOCK_SHARED();
+
+ /*
+ * if the vnode we have in hand isn't a directory and it
+ * has a v_parent, then we started with the resource fork
+ * so skip up to avoid getting a duplicate copy of the
+ * file name in the path.
+ */
+ if (vp && !vnode_isdir(vp) && vp->v_parent)
+ vp = vp->v_parent;