+ ptr = pli->pli_path + pli->pli_pathlen - 1;
+ *ptr = 0;
+ --ptr;
+ pli->pli_fidcount = 0;
+ pli->pli_fids[0] = *(struct lu_fid *)mdd_object_fid(pli->pli_mdd_obj);
+
+ while (!mdd_is_root(mdd, &pli->pli_fids[pli->pli_fidcount])) {
+ mdd_obj = mdd_object_find(env, mdd,
+ &pli->pli_fids[pli->pli_fidcount]);
+ if (mdd_obj == NULL)
+ GOTO(out, rc = -EREMOTE);
+ if (IS_ERR(mdd_obj))
+ GOTO(out, rc = -PTR_ERR(mdd_obj));
+ rc = lu_object_exists(&mdd_obj->mod_obj.mo_lu);
+ if (rc <= 0) {
+ mdd_object_put(env, mdd_obj);
+ if (rc == -1)
+ rc = -EREMOTE;
+ else if (rc == 0)
+ /* Do I need to error out here? */
+ rc = -ENOENT;
+ GOTO(out, rc);
+ }
+
+ /* Get parent fid and object name */
+ mdd_read_lock(env, mdd_obj, MOR_TGT_CHILD);
+ buf = mdd_links_get(env, mdd_obj);
+ mdd_read_unlock(env, mdd_obj);
+ mdd_object_put(env, mdd_obj);
+ if (IS_ERR(buf))
+ GOTO(out, rc = PTR_ERR(buf));
+
+ leh = buf->lb_buf;
+ lee = (struct link_ea_entry *)(leh + 1); /* link #0 */
+ mdd_lee_unpack(lee, &reclen, tmpname, tmpfid);
+
+ /* If set, use link #linkno for path lookup, otherwise use
+ link #0. Only do this for the final path element. */
+ if ((pli->pli_fidcount == 0) &&
+ (pli->pli_linkno < leh->leh_reccount)) {
+ int count;
+ for (count = 0; count < pli->pli_linkno; count++) {
+ lee = (struct link_ea_entry *)
+ ((char *)lee + reclen);
+ mdd_lee_unpack(lee, &reclen, tmpname, tmpfid);
+ }
+ if (pli->pli_linkno < leh->leh_reccount - 1)
+ /* indicate to user there are more links */
+ pli->pli_linkno++;
+ }
+
+ /* Pack the name in the end of the buffer */
+ ptr -= tmpname->ln_namelen;
+ if (ptr - 1 <= pli->pli_path)
+ GOTO(out, rc = -EOVERFLOW);
+ strncpy(ptr, tmpname->ln_name, tmpname->ln_namelen);
+ *(--ptr) = '/';
+
+ /* Store the parent fid for historic lookup */
+ if (++pli->pli_fidcount >= MAX_PATH_DEPTH)
+ GOTO(out, rc = -EOVERFLOW);
+ pli->pli_fids[pli->pli_fidcount] = *tmpfid;
+ }
+
+ /* Verify that our path hasn't changed since we started the lookup.
+ Record the current index, and verify the path resolves to the
+ same fid. If it does, then the path is correct as of this index. */
+ spin_lock(&mdd->mdd_cl.mc_lock);
+ pli->pli_currec = mdd->mdd_cl.mc_index;
+ spin_unlock(&mdd->mdd_cl.mc_lock);
+ rc = mdd_path2fid(env, mdd, ptr, &pli->pli_fid);
+ if (rc) {
+ CDEBUG(D_INFO, "mdd_path2fid(%s) failed %d\n", ptr, rc);
+ GOTO (out, rc = -EAGAIN);
+ }
+ if (!lu_fid_eq(&pli->pli_fids[0], &pli->pli_fid)) {
+ CDEBUG(D_INFO, "mdd_path2fid(%s) found another FID o="DFID
+ " n="DFID"\n", ptr, PFID(&pli->pli_fids[0]),
+ PFID(&pli->pli_fid));
+ GOTO(out, rc = -EAGAIN);
+ }
+
+ memmove(pli->pli_path, ptr, pli->pli_path + pli->pli_pathlen - ptr);
+
+ EXIT;
+out:
+ if (buf && !IS_ERR(buf) && buf->lb_vmalloc)
+ /* if we vmalloced a large buffer drop it */
+ mdd_buf_put(buf);
+
+ return rc;
+}
+
+static int mdd_path_historic(const struct lu_env *env,
+ struct path_lookup_info *pli)
+{
+ return 0;
+}
+
+/* Returns the full path to this fid, as of changelog record recno. */
+static int mdd_path(const struct lu_env *env, struct md_object *obj,
+ char *path, int pathlen, __u64 *recno, int *linkno)
+{
+ struct path_lookup_info *pli;
+ int tries = 3;
+ int rc = -EAGAIN;
+ ENTRY;
+
+ if (pathlen < 3)
+ RETURN(-EOVERFLOW);
+
+ if (mdd_is_root(mdo2mdd(obj), mdd_object_fid(md2mdd_obj(obj)))) {
+ path[0] = '/';
+ path[1] = '\0';
+ RETURN(0);
+ }
+
+ OBD_ALLOC_PTR(pli);
+ if (pli == NULL)
+ RETURN(-ENOMEM);
+
+ pli->pli_mdd_obj = md2mdd_obj(obj);
+ pli->pli_recno = *recno;
+ pli->pli_path = path;
+ pli->pli_pathlen = pathlen;
+ pli->pli_linkno = *linkno;
+
+ /* Retry multiple times in case file is being moved */
+ while (tries-- && rc == -EAGAIN)
+ rc = mdd_path_current(env, pli);
+
+ /* For historical path lookup, the current links may not have existed
+ * at "recno" time. We must switch over to earlier links/parents
+ * by using the changelog records. If the earlier parent doesn't
+ * exist, we must search back through the changelog to reconstruct
+ * its parents, then check if it exists, etc.
+ * We may ignore this problem for the initial implementation and
+ * state that an "original" hardlink must still exist for us to find
+ * historic path name. */
+ if (pli->pli_recno != -1) {
+ rc = mdd_path_historic(env, pli);
+ } else {
+ *recno = pli->pli_currec;
+ /* Return next link index to caller */
+ *linkno = pli->pli_linkno;