ssize_t fs_getdents(ino_t ino_nr, struct fsdriver_data *data, size_t bytes, off_t *pos) { struct fsdriver_dentry fsdentry; struct inode *i_node, *i_node_tmp; size_t cur_pos, new_pos; int r, len; char *cp; if ((i_node = find_inode(ino_nr)) == NULL) return EINVAL; if (*pos < 0 || *pos > SSIZE_MAX) return EINVAL; fsdriver_dentry_init(&fsdentry, data, bytes, getdents_buf, sizeof(getdents_buf)); r = OK; for (cur_pos = (size_t)*pos; ; cur_pos = new_pos) { i_node_tmp = alloc_inode(); r = read_inode(i_node_tmp, i_node->extent, cur_pos, &new_pos); if ((r != OK) || (new_pos >= i_node->i_stat.st_size)) { put_inode(i_node_tmp); break; } /* Compute the length of the name */ cp = memchr(i_node_tmp->i_name, '\0', NAME_MAX); if (cp == NULL) len = NAME_MAX; else len = cp - i_node_tmp->i_name; r = fsdriver_dentry_add(&fsdentry, i_node_tmp->i_stat.st_ino, i_node_tmp->i_name, len, IFTODT(i_node_tmp->i_stat.st_mode)); put_inode(i_node_tmp); if (r <= 0) break; } if (r >= 0 && (r = fsdriver_dentry_finish(&fsdentry)) >= 0) *pos = cur_pos; return r; }
/*===========================================================================* * fs_getdents * *===========================================================================*/ ssize_t fs_getdents(ino_t ino_nr, struct fsdriver_data *data, size_t bytes, off_t *posp) { /* Retrieve directory entries. */ struct fsdriver_dentry fsdentry; struct inode *node, *child; const char *name; off_t pos; int r, skip, get_next, indexed; static char buf[GETDENTS_BUFSIZ]; if (*posp >= ULONG_MAX) return EIO; if ((node = find_inode(ino_nr)) == NULL) return EINVAL; indexed = node->i_indexed; get_next = FALSE; child = NULL; /* Call the getdents hook, if any, to "refresh" the directory. */ if (!is_inode_deleted(node) && vtreefs_hooks->getdents_hook != NULL) { r = vtreefs_hooks->getdents_hook(node, get_inode_cbdata(node)); if (r != OK) return r; } fsdriver_dentry_init(&fsdentry, data, bytes, buf, sizeof(buf)); do { /* Determine which inode and name to use for this entry. */ pos = (*posp)++; if (pos == 0) { /* The "." entry. */ child = node; name = "."; } else if (pos == 1) { /* The ".." entry. */ child = get_parent_inode(node); if (child == NULL) child = node; name = ".."; } else if (pos - 2 < indexed) { /* All indexed entries. */ child = get_inode_by_index(node, pos - 2); /* If there is no inode with this particular index, * continue with the next index number. */ if (child == NULL) continue; name = child->i_name; } else { /* All non-indexed entries. */ /* If this is the first loop iteration, first get to * the non-indexed child identified by the current * position. */ if (get_next == FALSE) { skip = pos - indexed - 2; child = get_first_inode(node); /* Skip indexed children. */ while (child != NULL && child->i_index != NO_INDEX) child = get_next_inode(child); /* Skip to the right position. */ while (child != NULL && skip-- > 0) child = get_next_inode(child); get_next = TRUE; } else { child = get_next_inode(child); } /* No more children? Then stop. */ if (child == NULL) break; assert(!is_inode_deleted(child)); name = child->i_name; } /* Add the directory entry to the output. */ r = fsdriver_dentry_add(&fsdentry, (ino_t) get_inode_number(child), name, strlen(name), IFTODT(child->i_stat.mode)); if (r < 0) return r; } while (r > 0); return fsdriver_dentry_finish(&fsdentry); }
/*===========================================================================* * do_getdents * *===========================================================================*/ ssize_t do_getdents(ino_t ino_nr, struct fsdriver_data *data, size_t bytes, off_t *posp) { /* Retrieve directory entries. */ struct fsdriver_dentry fsdentry; char name[NAME_MAX+1]; struct inode *ino, *child; struct sffs_attr attr; off_t pos; int r; /* must be at least sizeof(struct dirent) + NAME_MAX */ static char buf[BLOCK_SIZE]; if ((ino = find_inode(ino_nr)) == NULL) return EINVAL; if (!IS_DIR(ino)) return ENOTDIR; if (*posp < 0 || *posp >= ULONG_MAX) return EINVAL; /* We are going to need at least one free inode to store children in. */ if (!have_free_inode()) return ENFILE; /* If we don't have a directory handle yet, get one now. */ if ((r = get_handle(ino)) != OK) return r; fsdriver_dentry_init(&fsdentry, data, bytes, buf, sizeof(buf)); /* We use the seek position as file index number. The first position is for * the "." entry, the second position is for the ".." entry, and the next * position numbers each represent a file in the directory. */ do { /* Determine which inode and name to use for this entry. * We have no idea whether the host will give us "." and/or "..", * so generate our own and skip those from the host. */ pos = (*posp)++; if (pos == 0) { /* Entry for ".". */ child = ino; strcpy(name, "."); get_inode(child); } else if (pos == 1) { /* Entry for "..", but only when there is a parent. */ if (ino->i_parent == NULL) continue; child = ino->i_parent; strcpy(name, ".."); get_inode(child); } else { /* Any other entry, not being "." or "..". */ attr.a_mask = SFFS_ATTR_MODE; r = sffs_table->t_readdir(ino->i_dir, pos - 2, name, sizeof(name), &attr); if (r != OK) { /* No more entries? Then close the handle and stop. */ if (r == ENOENT) { put_handle(ino); break; } /* FIXME: what if the error is ENAMETOOLONG? */ return r; } if (!strcmp(name, ".") || !strcmp(name, "..")) continue; if ((child = lookup_dentry(ino, name)) == NULL) { child = get_free_inode(); /* We were promised a free inode! */ assert(child != NULL); child->i_flags = MODE_TO_DIRFLAG(attr.a_mode); add_dentry(ino, name, child); } } r = fsdriver_dentry_add(&fsdentry, INODE_NR(child), name, strlen(name), IS_DIR(child) ? DT_DIR : DT_REG); put_inode(child); if (r < 0) return r; } while (r > 0); return fsdriver_dentry_finish(&fsdentry); }