+static struct dt_it *osd_dir_it_init(const struct lu_env *env,
+ struct dt_object *dt,
+ __u32 unused,
+ struct lustre_capa *capa)
+{
+ struct osd_zap_it *it;
+
+ it = (struct osd_zap_it *)osd_index_it_init(env, dt, unused, capa);
+ if (!IS_ERR(it))
+ it->ozi_pos = 0;
+
+ RETURN((struct dt_it *)it);
+}
+
+/**
+ * Move Iterator to record specified by \a key
+ *
+ * \param di osd iterator
+ * \param key key for index
+ *
+ * \retval +ve di points to record with least key not larger than key
+ * \retval 0 di points to exact matched key
+ * \retval -ve failure
+ */
+static int osd_dir_it_get(const struct lu_env *env,
+ struct dt_it *di, const struct dt_key *key)
+{
+ struct osd_zap_it *it = (struct osd_zap_it *)di;
+ struct osd_object *obj = it->ozi_obj;
+ struct osd_device *osd = osd_obj2dev(obj);
+ char *name = (char *)key;
+ int rc;
+ ENTRY;
+
+ LASSERT(it);
+ LASSERT(it->ozi_zc);
+
+ udmu_zap_cursor_fini(it->ozi_zc);
+
+ if (udmu_zap_cursor_init(&it->ozi_zc, &osd->od_objset,
+ obj->oo_db->db_object, 0))
+ RETURN(-ENOMEM);
+
+ /* XXX: implementation of the API is broken at the moment */
+ LASSERT(((const char *)key)[0] == 0);
+
+ if (name[0] == 0) {
+ it->ozi_pos = 0;
+ RETURN(1);
+ }
+
+ if (name[0] == '.') {
+ if (name[1] == 0) {
+ it->ozi_pos = 1;
+ GOTO(out, rc = 1);
+ } else if (name[1] == '.' && name[2] == 0) {
+ it->ozi_pos = 2;
+ GOTO(out, rc = 1);
+ }
+ }
+
+ /* neither . nor .. - some real record */
+ it->ozi_pos = 3;
+ rc = +1;
+
+out:
+ RETURN(rc);
+}
+
+static void osd_dir_it_put(const struct lu_env *env, struct dt_it *di)
+{
+ /* PBS: do nothing : ref are incremented at retrive and decreamented
+ * next/finish. */
+}
+
+/*
+ * in Orion . and .. were stored in the directory, while ZPL
+ * and current osd-zfs generate them up on request. so, we
+ * need to ignore previously stored . and ..
+ */
+static int osd_index_retrieve_skip_dots(struct osd_zap_it *it,
+ zap_attribute_t *za)
+{
+ int rc, isdot;
+
+ do {
+ rc = -zap_cursor_retrieve(it->ozi_zc, za);
+
+ isdot = 0;
+ if (unlikely(rc == 0 && za->za_name[0] == '.')) {
+ if (za->za_name[1] == 0) {
+ isdot = 1;
+ } else if (za->za_name[1] == '.' &&
+ za->za_name[2] == 0) {
+ isdot = 1;
+ }
+ if (unlikely(isdot))
+ zap_cursor_advance(it->ozi_zc);
+ }
+ } while (unlikely(rc == 0 && isdot));
+
+ return rc;
+}
+
+/**
+ * to load a directory entry at a time and stored it in
+ * iterator's in-memory data structure.
+ *
+ * \param di, struct osd_it_ea, iterator's in memory structure
+ *
+ * \retval +ve, iterator reached to end
+ * \retval 0, iterator not reached to end
+ * \retval -ve, on error
+ */
+static int osd_dir_it_next(const struct lu_env *env, struct dt_it *di)
+{
+ struct osd_zap_it *it = (struct osd_zap_it *)di;
+ zap_attribute_t *za = &osd_oti_get(env)->oti_za;
+ int rc;
+
+ /* temp. storage should be enough for any key supported by ZFS */
+ CLASSERT(sizeof(za->za_name) <= sizeof(it->ozi_name));
+
+ /*
+ * the first ->next() moves the cursor to .
+ * the second ->next() moves the cursor to ..
+ * then we get to the real records and have to verify any exist
+ */
+ if (it->ozi_pos <= 2) {
+ it->ozi_pos++;
+ if (it->ozi_pos <=2)
+ RETURN(0);
+ }
+
+ zap_cursor_advance(it->ozi_zc);
+
+ /*
+ * According to current API we need to return error if its last entry.
+ * zap_cursor_advance() does not return any value. So we need to call
+ * retrieve to check if there is any record. We should make
+ * changes to Iterator API to not return status for this API
+ */
+ rc = osd_index_retrieve_skip_dots(it, za);
+
+ if (rc == -ENOENT) /* end of dir */
+ RETURN(+1);
+
+ RETURN(rc);
+}
+
+static struct dt_key *osd_dir_it_key(const struct lu_env *env,
+ const struct dt_it *di)
+{
+ struct osd_zap_it *it = (struct osd_zap_it *)di;
+ zap_attribute_t *za = &osd_oti_get(env)->oti_za;
+ int rc = 0;
+ ENTRY;
+
+ if (it->ozi_pos <= 1) {
+ it->ozi_pos = 1;
+ RETURN((struct dt_key *)".");
+ } else if (it->ozi_pos == 2) {
+ RETURN((struct dt_key *)"..");
+ }
+
+ if ((rc = -zap_cursor_retrieve(it->ozi_zc, za)))
+ RETURN(ERR_PTR(rc));
+
+ strcpy(it->ozi_name, za->za_name);
+
+#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 3, 91, 0)
+ if (za->za_name[0] == '.') {
+ if (za->za_name[1] == 0 || (za->za_name[1] == '.' &&
+ za->za_name[2] == 0)) {
+ /* we should not get onto . and ..
+ * stored in the directory. ->next() and
+ * other methods should prevent this
+ */
+ LBUG();
+ }
+ }
+#endif
+
+ RETURN((struct dt_key *)it->ozi_name);
+}
+
+static int osd_dir_it_key_size(const struct lu_env *env, const struct dt_it *di)
+{
+ struct osd_zap_it *it = (struct osd_zap_it *)di;
+ zap_attribute_t *za = &osd_oti_get(env)->oti_za;
+ int rc;
+ ENTRY;
+
+ if (it->ozi_pos <= 1) {
+ it->ozi_pos = 1;
+ RETURN(2);
+ } else if (it->ozi_pos == 2) {
+ RETURN(3);
+ }
+
+ if ((rc = -zap_cursor_retrieve(it->ozi_zc, za)) == 0)
+ rc = strlen(za->za_name);
+
+#if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 3, 99, 0)
+ if (rc == 0 && za->za_name[0] == '.') {
+ if (za->za_name[1] == 0 || (za->za_name[1] == '.' &&
+ za->za_name[2] == 0)) {
+ /* we should not get onto . and ..
+ * stored in the directory. ->next() and
+ * other methods should prevent this
+ */
+ LBUG();
+ }
+ }
+#endif
+ RETURN(rc);
+}
+
+static int osd_dir_it_rec(const struct lu_env *env, const struct dt_it *di,
+ struct dt_rec *dtrec, __u32 attr)
+{
+ struct osd_zap_it *it = (struct osd_zap_it *)di;
+ struct lu_dirent *lde = (struct lu_dirent *)dtrec;
+ struct luz_direntry *zde = &osd_oti_get(env)->oti_zde;
+ zap_attribute_t *za = &osd_oti_get(env)->oti_za;
+ int rc, namelen;
+ ENTRY;
+
+ if (it->ozi_pos <= 1) {
+ lde->lde_hash = cpu_to_le64(1);
+ strcpy(lde->lde_name, ".");
+ lde->lde_namelen = cpu_to_le16(1);
+ lde->lde_fid = *lu_object_fid(&it->ozi_obj->oo_dt.do_lu);
+ lde->lde_attrs = LUDA_FID;
+ /* append lustre attributes */
+ osd_it_append_attrs(lde, attr, 1, IFTODT(S_IFDIR));
+ lde->lde_reclen = cpu_to_le16(lu_dirent_calc_size(1, attr));
+ it->ozi_pos = 1;
+ GOTO(out, rc = 0);
+
+ } else if (it->ozi_pos == 2) {
+ lde->lde_hash = cpu_to_le64(2);
+ strcpy(lde->lde_name, "..");
+ lde->lde_namelen = cpu_to_le16(2);
+ lde->lde_attrs = LUDA_FID;
+ /* append lustre attributes */
+ osd_it_append_attrs(lde, attr, 2, IFTODT(S_IFDIR));
+ lde->lde_reclen = cpu_to_le16(lu_dirent_calc_size(2, attr));
+ rc = osd_find_parent_fid(env, &it->ozi_obj->oo_dt, &lde->lde_fid);
+ /*
+ * early Orion code was not setting LinkEA, so it's possible
+ * some setups still have objects with no LinkEA set.
+ * but at that time .. was a real record in the directory
+ * so we should try to lookup .. in ZAP
+ */
+ if (rc != -ENOENT)
+ GOTO(out, rc);
+ }
+
+ LASSERT(lde);
+
+ lde->lde_hash = cpu_to_le64(udmu_zap_cursor_serialize(it->ozi_zc));
+
+ if ((rc = -zap_cursor_retrieve(it->ozi_zc, za)))
+ GOTO(out, rc);
+
+ namelen = strlen(za->za_name);
+ if (namelen > NAME_MAX)
+ GOTO(out, rc = -EOVERFLOW);
+ strcpy(lde->lde_name, za->za_name);
+ lde->lde_namelen = cpu_to_le16(namelen);
+
+ if (za->za_integer_length != 8 || za->za_num_integers < 3) {
+ CERROR("%s: unsupported direntry format: %d %d\n",
+ osd_obj2dev(it->ozi_obj)->od_svname,
+ za->za_integer_length, (int)za->za_num_integers);
+
+ GOTO(out, rc = -EIO);
+ }
+
+ rc = -zap_lookup(it->ozi_zc->zc_objset, it->ozi_zc->zc_zapobj,
+ za->za_name, za->za_integer_length, 3, zde);
+ if (rc)
+ GOTO(out, rc);
+
+ lde->lde_fid = zde->lzd_fid;
+ lde->lde_attrs = LUDA_FID;
+
+ /* append lustre attributes */
+ osd_it_append_attrs(lde, attr, namelen, zde->lzd_reg.zde_type);
+
+ lde->lde_reclen = cpu_to_le16(lu_dirent_calc_size(namelen, attr));
+
+out:
+ RETURN(rc);
+}
+
+static __u64 osd_dir_it_store(const struct lu_env *env, const struct dt_it *di)
+{
+ struct osd_zap_it *it = (struct osd_zap_it *)di;
+ __u64 pos;
+ ENTRY;
+
+ if (it->ozi_pos <= 2)
+ pos = it->ozi_pos;
+ else
+ pos = udmu_zap_cursor_serialize(it->ozi_zc);
+
+ RETURN(pos);
+}
+
+/*
+ * return status :
+ * rc == 0 -> end of directory.
+ * rc > 0 -> ok, proceed.
+ * rc < 0 -> error. ( EOVERFLOW can be masked.)
+ */
+static int osd_dir_it_load(const struct lu_env *env,
+ const struct dt_it *di, __u64 hash)
+{
+ struct osd_zap_it *it = (struct osd_zap_it *)di;
+ struct osd_object *obj = it->ozi_obj;
+ struct osd_device *osd = osd_obj2dev(obj);
+ zap_attribute_t *za = &osd_oti_get(env)->oti_za;
+ int rc;
+ ENTRY;
+
+ if (it->ozi_pos != 0) {
+ /* the cursor wasn't at the beginning
+ * so we should reset ZAP cursor as well */
+ udmu_zap_cursor_fini(it->ozi_zc);
+ if (udmu_zap_cursor_init(&it->ozi_zc, &osd->od_objset,
+ obj->oo_db->db_object, hash))
+ RETURN(-ENOMEM);
+ }
+
+ if (hash <= 2) {
+ it->ozi_pos = hash;
+ rc = +1;
+ } else {
+ it->ozi_pos = 3;
+ /* to return whether the end has been reached */
+ rc = osd_index_retrieve_skip_dots(it, za);
+ if (rc == 0)
+ rc = +1;
+ else if (rc == -ENOENT)
+ rc = 0;
+ }
+
+ RETURN(rc);
+}
+