Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions components/kaos/core/include/kaos.h
Original file line number Diff line number Diff line change
Expand Up @@ -3125,6 +3125,21 @@ void storage_cat_request(QActive *who, const char *path, uint32_t max);
void storage_path_request_wait(StorageWaiter *wait, uint32_t gen, uint8_t op,
const char *path);

/**
* @brief A STORAGE_PATH_LIST answered into @p wait, from the @p skip-th entry on.
*
* storage_path_request_wait() with STORAGE_PATH_LIST is this with a @p skip
* of 0. The entries left out are counted as the walk finds them, after the
* names it never lists, so a page starts where the one before it ended.
*
* @param wait See storage_path_request_wait().
* @param gen See storage_path_request_wait().
* @param path Absolute, normalised, virtual. Copied into the event.
* @param skip Entries to leave out: the ones earlier pages gave.
*/
void storage_list_request_wait(StorageWaiter *wait, uint32_t gen, const char *path,
uint32_t skip);

/**
* @brief storage_cat_request(), answered into @p wait.
*
Expand Down
53 changes: 44 additions & 9 deletions components/kaos/storage/src/storage_ao.c
Original file line number Diff line number Diff line change
Expand Up @@ -596,13 +596,13 @@ void storage_cat_request(QActive *who, const char *path, uint32_t max)
/**
* @brief Build and post one StoragePathEvt on behalf of a blocked task.
*
* Shared by the two public wait variants, which differ in one field.
* Shared by the public wait variants.
*
* @param wait Where the answer goes. Not NULL.
* @param gen This request's tag.
* @param op storage_path_op_t.
* @param path Absolute, normalised, virtual.
* @param offset CAT only; ignored otherwise.
* @param offset A CAT's first byte, or a LIST's entries to skip; ignored otherwise.
* @param max CAT only; ignored otherwise.
*/
static void path_request_wait(StorageWaiter *wait, uint32_t gen, uint8_t op,
Expand Down Expand Up @@ -636,6 +636,12 @@ void storage_path_request_wait(StorageWaiter *wait, uint32_t gen, uint8_t op,
path_request_wait(wait, gen, op, path, 0U, 0U);
}

void storage_list_request_wait(StorageWaiter *wait, uint32_t gen, const char *path,
uint32_t skip)
{
path_request_wait(wait, gen, (uint8_t)STORAGE_PATH_LIST, path, skip, 0U);
}

void storage_cat_request_wait(StorageWaiter *wait, uint32_t gen,
const char *path, uint32_t offset, uint32_t max)
{
Expand Down Expand Up @@ -1403,7 +1409,8 @@ typedef struct {
* publish the walk as events. */
QActive *ask; /**< An asking AO the page is posted to, or
* NULL. */
uint32_t skip; /**< An ask's entries still to leave out. */
uint32_t skip; /**< An ask's or a blocked task's entries
* still to leave out. */
uint32_t base; /**< An ask's first entry's position. */
} PathListCtx;

Expand Down Expand Up @@ -1482,7 +1489,13 @@ static bool publish_path_entry(const char *name, bool is_dir, uint32_t size,
{
/* A blocked task asked: the row goes straight into its own array and
* nothing is published. Its array's size is the ceiling, not
* KODE_STORAGE_LIST_MAX, and it is told when there was more. */
* KODE_STORAGE_LIST_MAX, and it is told when there was more. The
* entries earlier pages gave are left out first. */
if ( 0U < c->skip )
{
--c->skip;
return true;
}
if ( c->published >= c->wait->cap )
{
c->capped = true;
Expand Down Expand Up @@ -1564,13 +1577,14 @@ static void list_ask(const char *path, uint32_t skip)
* An ask's listing is list_ask()'s.
*
* @param path The directory.
* @param skip An ask's entries to skip; nobody else's.
* @param skip An ask's or a blocked task's entries to skip; 0 for a console
* listing, which asks for none.
*/
static void do_path_list(const char *path, uint32_t skip)
{
PathListCtx ctx = { .dir_path = path, .published = 0U,
.truncated = false, .capped = false,
.wait = s_waiter, .ask = NULL, .skip = 0U, .base = 0U };
.wait = s_waiter, .ask = NULL, .skip = skip, .base = 0U };

if ( NULL != s_ask_rsp )
{
Expand All @@ -1581,16 +1595,37 @@ static void do_path_list(const char *path, uint32_t skip)
kdrv_storage_list_path(path, &publish_path_entry, &ctx);
if ( ctx.capped )
{
KLOG_W(TAG, "listing stopped at %u entries (the ceiling)",
(unsigned)ctx.published);
/* A blocked task reads a long folder a page at a time and is told
* there is more (`cut`): a full page is no fault of its own. A
* listing published whole has met its ceiling, and that is said. */
if ( NULL == s_waiter )
{
KLOG_W(TAG, "listing stopped at %u entries (the ceiling)",
(unsigned)ctx.published);
}
}
else if ( ctx.truncated )
{
KLOG_W(TAG, "listing cut short: no event blocks left");
}
else if ( KODE_OK != rc )
{
KLOG_W(TAG, "cannot list '%s'", path);
/* A folder that is not there is an answer, not a fault: `appdata_ls`
* lists two that a board with no app data never made. */
kdrv_stat_t st = { .exists = false, .is_dir = false, .size = 0U };

if ( (kdrv_storage_stat(path, &st) == KODE_OK) && !st.exists )
{
KLOG_D(TAG, "nothing at '%s' to list", path);
}
else
{
KLOG_W(TAG, "cannot list '%s'", path);
}
}
else
{
/* Listed whole. */
}

if ( NULL != s_waiter )
Expand Down
25 changes: 23 additions & 2 deletions components/kode/include/kode.h
Original file line number Diff line number Diff line change
Expand Up @@ -588,11 +588,32 @@ bool kode_file_exists(const char *path);
* Blocks. KODE_FILE_LIST_MAX entries at most; `more` says there were others.
*
* @param folder "sd", "flash" or "sd/...", "flash/..." naming a folder.
* @return The listing, valid until this task's next kode_file_list(); never
* NULL: empty on failure, with kode_error() saying why.
* @return The listing, valid until this task's next kode_file_list() or
* kode_file_list_from(); never NULL: empty on failure, with
* kode_error() saying why.
*/
const kode_file_list_t *kode_file_list(const char *folder);

/**
* @brief List one folder from its @p skip-th entry on.
*
* Blocks. kode_file_list(folder) is kode_file_list_from(folder, 0). A folder
* of more than KODE_FILE_LIST_MAX entries is read a page at a time: while
* `more` is set, ask again with the entries read so far. A @p skip at or past
* the last entry lists nothing and is not an error. Every page walks the
* folder from its first entry, so a page deep into a large folder takes
* longer than the first. An entry created or deleted between two pages moves
* the ones after it, so a page can then repeat an entry or miss one. An
* Active Object lists with kode_sys_file_list().
*
* @param folder As kode_file_list().
* @param skip The entries to leave out: the ones earlier pages gave.
* @return The listing, valid until this task's next kode_file_list() or
* kode_file_list_from(); never NULL: empty on failure, with
* kode_error() saying why.
*/
const kode_file_list_t *kode_file_list_from(const char *folder, uint32_t skip);

/**
* @brief Delete a file, or a folder with nothing in it.
*
Expand Down
36 changes: 28 additions & 8 deletions components/kode/src/kode_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ _Static_assert(KODE_FILE_NAME_MAX == KODE_APP_FILE_MAX,
struct kode_file_state {
char *text; /**< The last kode_file_read() text. */
uint8_t *bytes; /**< The last kode_file_read_bytes() data. */
kode_file_list_t *list; /**< The last kode_file_list() listing. */
kode_file_list_t *list; /**< The last listing, of either call. */
};

/** @brief What one try of kode_file_sys() carries. */
Expand Down Expand Up @@ -1160,6 +1160,7 @@ bool kode_file_append_bytes(const char *path, const void *data, uint32_t len)
/** @brief What one list try carries. */
typedef struct {
const char *path; /**< The volume path. */
uint32_t skip; /**< Entries to leave out. */
kode_file_list_t *list; /**< Where the entries go. */
} list_try_t;

Expand Down Expand Up @@ -1357,17 +1358,18 @@ static kode_status_t list_op(void *ctx)
const list_try_t *const t = (const list_try_t *)ctx;

(void)memset(t->list, 0, sizeof(*t->list));
return kode_block_file_list_each(t->path, &list_collect, t->list);
return kode_block_file_list_each(t->path, t->skip, &list_collect, t->list);
}

/**
* @brief Fill @p list with the entries of a folder.
* @brief Fill @p list with the entries of a folder, from the @p skip-th on.
* @param user The folder as typed.
* @param skip Entries to leave out.
* @param list Out: the entries; zeroed first.
* @return true on success, `more` set when there were others; false with the
* error set.
*/
static bool list_fill(const char *user, kode_file_list_t *list)
static bool list_fill(const char *user, uint32_t skip, kode_file_list_t *list)
{
char vol[KODE_PATH_MAX];
kode_sys_file_status_t st;
Expand All @@ -1388,7 +1390,7 @@ static bool list_fill(const char *user, kode_file_list_t *list)
}
else
{
list_try_t t = { vol, list };
list_try_t t = { vol, skip, list };
const kode_status_t rc = kode_file_sys(&list_op, &t);

/* The STAT above took the sys buffer: NO_RESOURCES here means more entries only. */
Expand All @@ -1411,12 +1413,19 @@ static bool list_fill(const char *user, kode_file_list_t *list)
return ok;
}

const kode_file_list_t *kode_file_list(const char *folder)
/**
* @brief The body of the two listing calls.
* @param name The public name of the call.
* @param folder The folder as typed.
* @param skip Entries to leave out.
* @return The task's listing; an empty one on failure, with the error set.
*/
static const kode_file_list_t *list_call(const char *name, const char *folder, uint32_t skip)
{
static const kode_file_list_t no_list = { 0U }; /* a failed listing */
const kode_file_list_t *list = &no_list;

if ( files_enter("kode_file_list") )
if ( files_enter(name) )
{
struct kode_file_state *const s = kode_file_state_ensure();

Expand All @@ -1431,14 +1440,25 @@ const kode_file_list_t *kode_file_list(const char *folder)
}
else
{
(void)list_fill(folder, s->list);
(void)list_fill(folder, skip, s->list);
list = s->list;
}
files_leave();
}
return list;
}

const kode_file_list_t *kode_file_list(const char *folder)
{
return list_call("kode_file_list", folder, 0U);
}

/* cppcheck-suppress misra-c2012-8.7 ; a facade call: the apps outside this tree call it */
const kode_file_list_t *kode_file_list_from(const char *folder, uint32_t skip)
{
return list_call("kode_file_list_from", folder, skip);
}

/** @brief Whether @p vol is a volume itself, "/sdcard" or "/storage". */
static bool is_volume(const char *vol)
{
Expand Down
12 changes: 8 additions & 4 deletions components/kode_sys/internal/include/kode_sys_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -419,13 +419,16 @@ typedef bool (*kode_sys_file_entry_cb)(const char *name, bool is_dir, uint32_t s
void *ctx);

/**
* @brief List one directory's own entries, one level.
* @brief List one directory's own entries, one level, from the @p skip-th on.
*
* More than KODE_SYS_FILE_LIST_MAX entries: the first ones are all offered to
* @p cb, then KODE_NO_RESOURCES says that was a page, not the whole.
* More than KODE_SYS_FILE_LIST_MAX entries after the ones left out: the first
* ones are all offered to @p cb, then KODE_NO_RESOURCES says that was a page,
* not the whole. A @p skip at or past the last entry offers nothing and is
* KODE_OK.
* @note Blocks. Two round trips (a STAT, then the list); the claim is held
* across the callbacks.
* @param path Absolute, virtual, and a directory.
* @param skip Entries to leave out: the ones earlier pages offered.
* @param cb Called once per entry, in AO_Storage's order.
* @param ctx Passed to every call of @p cb.
* @return KODE_OK once every entry has been offered and there were no more.
Expand All @@ -434,7 +437,8 @@ typedef bool (*kode_sys_file_entry_cb)(const char *name, bool is_dir, uint32_t s
* call waiting. KODE_ERROR when the volume will not mount, or for a NULL
* @p path or @p cb. KODE_TIMEOUT when AO_Storage did not answer.
*/
kode_status_t kode_block_file_list_each(const char *path, kode_sys_file_entry_cb cb, void *ctx);
kode_status_t kode_block_file_list_each(const char *path, uint32_t skip, kode_sys_file_entry_cb cb,
void *ctx);

/**
* @brief Make one directory at @p path.
Expand Down
6 changes: 3 additions & 3 deletions components/kode_sys/src/kode_sys_files.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,8 @@ kode_status_t kode_block_file_space(const char *path, kode_sys_file_space_t *out
return rc;
}

kode_status_t kode_block_file_list_each(const char *path, kode_sys_file_entry_cb cb, void *ctx)
kode_status_t kode_block_file_list_each(const char *path, uint32_t skip, kode_sys_file_entry_cb cb,
void *ctx)
{
kode_sys_file_status_t st;

Expand Down Expand Up @@ -274,8 +275,7 @@ kode_status_t kode_block_file_list_each(const char *path, kode_sys_file_entry_cb
s_fs_wait.cut = false;

uint32_t const gen = fs_next_gen();
storage_path_request_wait(&s_fs_wait, gen, (uint8_t)STORAGE_PATH_LIST,
path);
storage_list_request_wait(&s_fs_wait, gen, path, skip);

rc = fs_await(gen);
if ( KODE_OK != rc )
Expand Down
7 changes: 4 additions & 3 deletions components/kqp/include/kqp_signals.h
Original file line number Diff line number Diff line change
Expand Up @@ -6347,16 +6347,17 @@ typedef struct {
uint32_t max; /**< CAT only: ceiling on bytes read,
* already bounded by the console's own
* key spec. Ignored by every other op. */
uint32_t offset; /**< CAT only: first byte to read. */
uint32_t offset; /**< CAT: first byte to read. An ask's or
* a blocked task's LIST: the entries
* to skip. Ignored by every other op. */
StorageWaiter *wait; /**< A blocked task's own storage, or NULL
* when `who` is an Active Object. */
uint32_t gen; /**< This request's tag, echoed into
* `wait->ack`. Meaningless without
* `wait`. */
FileRspEvt *rsp; /**< An ask's answer, taken with the
* request and posted to `who`; NULL
* otherwise. An ask's LIST counts
* `offset` as the entries to skip. */
* otherwise. */
void *buf; /**< An ask's CAT: the lent buffer, `max`
* bytes, written before the answer. */
} StoragePathEvt;
Expand Down
Loading
Loading