Skip to content
Open
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
78 changes: 76 additions & 2 deletions mettle/src/stdapi/fs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,12 +543,85 @@ fs_cb(eio_req *req)
struct tlv_packet *
fs_mkdir(struct tlv_handler_ctx *ctx)
{
const char *path = tlv_packet_get_str(ctx->req, TLV_TYPE_DIRECTORY_PATH);
char * dir;
char * base_dir;
char * tmp;
struct stat f_info;
char * path_dup;
char *path = tlv_packet_get_str(ctx->req, TLV_TYPE_DIRECTORY_PATH);

if (path == NULL) {
return tlv_packet_response_result(ctx, TLV_RESULT_EINVAL);
}

eio_mkdir(path, 0777, 0, fs_cb, ctx);
path_dup = strdup(path);

if (path_dup == NULL) {
return tlv_packet_response_result(ctx, TLV_RESULT_ENOMEM);
}

// take into account null byte at the end of path and the one we add in sprintf
base_dir = malloc(strlen(path_dup)+2);

if(base_dir == NULL)
{
free(path_dup);
return tlv_packet_response_result(ctx, TLV_RESULT_ENOMEM);
}

tmp = malloc(strlen(path_dup)+2);

if(tmp == NULL)
{
free(path_dup);
free(base_dir);
return tlv_packet_response_result(ctx, TLV_RESULT_ENOMEM);
}

dir = strtok(path_dup, "/");

//address absolute paths — check original path since strtok modifies path_dup
if (path[0] == '/')
{
sprintf(base_dir, "/%s/", dir);
}
else
{
sprintf(base_dir, "%s/", dir);
}
Comment on lines +557 to +591
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fs_mkdir doesn’t validate allocation/tokenization results: strdup()/malloc() can return NULL, and strtok() returns NULL for inputs like "" or "/" (or all-slash paths). In those cases the subsequent sprintf(base_dir, ... , dir) dereferences a NULL dir and will crash. Add checks for allocation failures and for dir == NULL before formatting, and return an appropriate error code (e.g., TLV_RESULT_ENOMEM / TLV_RESULT_EINVAL) instead of proceeding.

Copilot uses AI. Check for mistakes.

while(dir != NULL)
{
#ifdef _WIN32
if(stat(base_dir, &f_info) != 0)
#else
if(lstat(base_dir, &f_info) != 0)
#endif
{
if(!eio_mkdir(base_dir, 0777, 0, NULL, NULL))
{
free(path_dup);
free(base_dir);
free(tmp);
return tlv_packet_response_result(ctx, errno);
}
}
memset(&f_info, 0, sizeof(struct stat));
dir = strtok(NULL, "/");
if(dir != NULL)
{
sprintf(tmp, "%s%s/", base_dir, dir);
strcpy(base_dir, tmp);
}
}

struct tlv_packet *p = tlv_packet_response_result(ctx,TLV_RESULT_SUCCESS);

tlv_dispatcher_enqueue_response(ctx->td, p);
tlv_handler_ctx_free(ctx);
Comment on lines +593 to +621
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fs_mkdir now enqueues a TLV_RESULT_SUCCESS response immediately and frees ctx without waiting for any mkdir operations to complete or checking their results. Additionally, each missing path component is created via eio_mkdir with a NULL callback, which both prevents error reporting and can schedule nested directory creations out-of-order (e.g., "a/b" mkdir can run before "a"), causing intermittent failures while still returning success. Consider moving the whole recursive mkdir logic into a single eio_custom worker (or chaining eio_mkdir calls with a callback) so directory creation is ordered and the response reflects the actual outcome (including EEXIST/ENOTDIR cases).

Copilot uses AI. Check for mistakes.
free(tmp);
free(base_dir);
free(path_dup);
return NULL;
}

Expand Down Expand Up @@ -887,3 +960,4 @@ void file_register_handlers(struct mettle *m)
};
channelmgr_add_channel_type(cm, "stdapi_fs_file", &cbs);
}

Loading