+static int cb_common_fini(char *path, DIR *parent, DIR *d, void *data,
+ cfs_dirent_t *de)
+{
+ struct find_param *param = (struct find_param *)data;
+ param->depth--;
+ return 0;
+}
+
+static DIR *opendir_parent(char *path)
+{
+ DIR *parent;
+ char *fname;
+ char c;
+
+ fname = strrchr(path, '/');
+ if (fname == NULL)
+ return opendir(".");
+
+ c = fname[1];
+ fname[1] = '\0';
+ parent = opendir(path);
+ fname[1] = c;
+ return parent;
+}
+
+int llapi_mds_getfileinfo(char *path, DIR *parent,
+ struct lov_user_mds_data *lmd)
+{
+ lstat_t *st = &lmd->lmd_st;
+ char *fname = strrchr(path, '/');
+ int ret = 0;
+
+ if (parent == NULL)
+ return -EINVAL;
+
+ fname = (fname == NULL ? path : fname + 1);
+ /* retrieve needed file info */
+ strncpy((char *)lmd, fname,
+ lov_mds_md_size(MAX_LOV_UUID_COUNT, LOV_MAGIC));
+ ret = ioctl(dirfd(parent), IOC_MDC_GETFILEINFO, (void *)lmd);
+
+ if (ret) {
+ if (errno == ENOTTY) {
+ /* ioctl is not supported, it is not a lustre fs.
+ * Do the regular lstat(2) instead. */
+ ret = lstat_f(path, st);
+ if (ret) {
+ llapi_err(LLAPI_MSG_ERROR,
+ "error: %s: lstat failed for %s",
+ __FUNCTION__, path);
+ return ret;
+ }
+ } else if (errno == ENOENT) {
+ llapi_err(LLAPI_MSG_WARN,
+ "warning: %s: %s does not exist",
+ __FUNCTION__, path);
+ return -ENOENT;
+ } else {
+ llapi_err(LLAPI_MSG_ERROR,
+ "error: %s: IOC_MDC_GETFILEINFO failed for %s",
+ __FUNCTION__, path);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int llapi_semantic_traverse(char *path, int size, DIR *parent,
+ semantic_func_t sem_init,
+ semantic_func_t sem_fini, void *data,
+ cfs_dirent_t *de)
+{
+ cfs_dirent_t *dent;
+ int len, ret;
+ DIR *d, *p = NULL;
+
+ ret = 0;
+ len = strlen(path);
+
+ d = opendir(path);
+ if (!d && errno != ENOTDIR) {
+ llapi_err(LLAPI_MSG_ERROR, "%s: Failed to open '%s'",
+ __FUNCTION__, path);
+ return -EINVAL;
+ } else if (!d && !parent) {
+ /* ENOTDIR. Open the parent dir. */
+ p = opendir_parent(path);
+ if (!p)
+ GOTO(out, ret = -EINVAL);
+ }
+
+ if (sem_init && (ret = sem_init(path, parent ?: p, d, data, de)))
+ goto err;
+
+ if (!d)
+ GOTO(out, ret = 0);
+
+ while ((dent = readdir64(d)) != NULL) {
+ ((struct find_param *)data)->have_fileinfo = 0;
+
+ if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
+ continue;
+
+ /* Don't traverse .lustre directory */
+ if (!(strcmp(dent->d_name, dot_lustre_name)))
+ continue;
+
+ path[len] = 0;
+ if ((len + dent->d_reclen + 2) > size) {
+ llapi_err(LLAPI_MSG_ERROR,
+ "error: %s: string buffer is too small",
+ __FUNCTION__);
+ break;
+ }
+ strcat(path, "/");
+ strcat(path, dent->d_name);
+
+ if (dent->d_type == DT_UNKNOWN) {
+ lstat_t *st = &((struct find_param *)data)->lmd->lmd_st;
+
+ ret = llapi_mds_getfileinfo(path, d,
+ ((struct find_param *)data)->lmd);
+ if (ret == 0) {
+ ((struct find_param *)data)->have_fileinfo = 1;
+ dent->d_type =
+ llapi_filetype_dir_table[st->st_mode &
+ S_IFMT];
+ }
+ if (ret == -ENOENT)
+ continue;
+ }
+
+ switch (dent->d_type) {
+ case DT_UNKNOWN:
+ llapi_err(LLAPI_MSG_ERROR,
+ "error: %s: '%s' is UNKNOWN type %d",
+ __FUNCTION__, dent->d_name, dent->d_type);
+ break;
+ case DT_DIR:
+ ret = llapi_semantic_traverse(path, size, d, sem_init,
+ sem_fini, data, dent);
+ if (ret < 0)
+ goto out;
+ break;
+ default:
+ ret = 0;
+ if (sem_init) {
+ ret = sem_init(path, d, NULL, data, dent);
+ if (ret < 0)
+ goto out;
+ }
+ if (sem_fini && ret == 0)
+ sem_fini(path, d, NULL, data, dent);
+ }
+ }
+
+out:
+ path[len] = 0;
+
+ if (sem_fini)
+ sem_fini(path, parent, d, data, de);
+err:
+ if (d)
+ closedir(d);
+ if (p)
+ closedir(p);
+ return ret;
+}
+
+static int param_callback(char *path, semantic_func_t sem_init,
+ semantic_func_t sem_fini, struct find_param *param)
+{
+ int ret, len = strlen(path);
+ char *buf;
+
+ if (len > PATH_MAX) {
+ llapi_err(LLAPI_MSG_ERROR, "Path name '%s' is too long", path);
+ return -EINVAL;
+ }
+
+ buf = (char *)malloc(PATH_MAX + 1);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = common_param_init(param);
+ if (ret)
+ goto out;
+ param->depth = 0;
+
+ strncpy(buf, path, PATH_MAX + 1);
+ ret = llapi_semantic_traverse(buf, PATH_MAX + 1, NULL, sem_init,
+ sem_fini, param, NULL);
+out:
+ find_param_fini(param);
+ free(buf);
+ return ret < 0 ? ret : 0;
+}
+