diff --git a/supervisor/shared/bluetooth/file_transfer.c b/supervisor/shared/bluetooth/file_transfer.c index 216cf3d567..159c639e4d 100644 --- a/supervisor/shared/bluetooth/file_transfer.c +++ b/supervisor/shared/bluetooth/file_transfer.c @@ -47,11 +47,11 @@ #include "supervisor/shared/reload.h" #include "supervisor/shared/bluetooth/file_transfer.h" #include "supervisor/shared/bluetooth/file_transfer_protocol.h" +#include "supervisor/shared/workflow.h" #include "supervisor/shared/tick.h" #include "supervisor/usb.h" #include "py/mpstate.h" -#include "py/stackctrl.h" STATIC bleio_service_obj_t supervisor_ble_service; STATIC bleio_uuid_obj_t supervisor_ble_service_uuid; @@ -387,39 +387,6 @@ STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) { return WRITE_DATA; } -STATIC FRESULT _delete_directory_contents(FATFS *fs, const TCHAR *path) { - FF_DIR dir; - FRESULT res = f_opendir(fs, &dir, path); - FILINFO file_info; - // Check the stack since we're putting paths on it. - if (mp_stack_usage() >= MP_STATE_THREAD(stack_limit)) { - return FR_INT_ERR; - } - while (res == FR_OK) { - res = f_readdir(&dir, &file_info); - if (res != FR_OK || file_info.fname[0] == '\0') { - break; - } - size_t pathlen = strlen(path); - size_t fnlen = strlen(file_info.fname); - TCHAR full_path[pathlen + 1 + fnlen]; - memcpy(full_path, path, pathlen); - full_path[pathlen] = '/'; - size_t full_pathlen = pathlen + 1 + fnlen; - memcpy(full_path + pathlen + 1, file_info.fname, fnlen); - full_path[full_pathlen] = '\0'; - if ((file_info.fattrib & AM_DIR) != 0) { - res = _delete_directory_contents(fs, full_path); - } - if (res != FR_OK) { - break; - } - res = f_unlink(fs, full_path); - } - f_closedir(&dir); - return res; -} - STATIC uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) { const struct delete_command *command = (struct delete_command *)raw_buf; size_t header_size = sizeof(struct delete_command); @@ -446,7 +413,7 @@ STATIC uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) { FRESULT result = f_stat(fs, path, &file); if (result == FR_OK) { if ((file.fattrib & AM_DIR) != 0) { - result = _delete_directory_contents(fs, path); + result = supervisor_workflow_delete_directory_contents(fs, path); } if (result == FR_OK) { result = f_unlink(fs, path); @@ -503,7 +470,7 @@ STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) { DWORD fattime; response.truncated_time = truncate_time(command->modification_time, &fattime); override_fattime(fattime); - FRESULT result = f_mkdir(fs, path); + FRESULT result = supervisor_workflow_mkdir_parents(fs, path); override_fattime(0); #if CIRCUITPY_USB_MSC usb_msc_unlock(); diff --git a/supervisor/shared/web_workflow/static/directory.html b/supervisor/shared/web_workflow/static/directory.html index 011d56fa61..ebe4b03010 100644 --- a/supervisor/shared/web_workflow/static/directory.html +++ b/supervisor/shared/web_workflow/static/directory.html @@ -18,7 +18,6 @@
-
+📁  diff --git a/supervisor/shared/web_workflow/static/directory.js b/supervisor/shared/web_workflow/static/directory.js index 0047435016..06ce7274b3 100644 --- a/supervisor/shared/web_workflow/static/directory.js +++ b/supervisor/shared/web_workflow/static/directory.js @@ -6,6 +6,11 @@ var url_base = window.location; var current_path; var editable = undefined; +function set_upload_enabled(enabled) { + files.disabled = !enabled; + dirs.disabled = !enabled; +} + async function refresh_list() { function compareValues(a, b) { @@ -46,7 +51,7 @@ async function refresh_list() { ); editable = status.headers.get("Access-Control-Allow-Methods").includes("DELETE"); new_directory_name.disabled = !editable; - files.disabled = !editable; + set_upload_enabled(editable); if (!editable) { let usbwarning = document.querySelector("#usbwarning"); usbwarning.style.display = "block"; @@ -149,6 +154,7 @@ async function mkdir(e) { } async function upload(e) { + set_upload_enabled(false); let progress = document.querySelector("progress"); let made_dirs = new Set(); progress.max = files.files.length + dirs.files.length; @@ -185,7 +191,7 @@ async function upload(e) { files.value = ""; dirs.value = ""; progress.value = 0; - upload_button.disabled = true; + set_upload_enabled(true); } async function del(e) { @@ -213,18 +219,8 @@ find_devices(); let mkdir_button = document.getElementById("mkdir"); mkdir_button.onclick = mkdir; -let upload_button = document.getElementById("upload"); -upload_button.onclick = upload; - -upload_button.disabled = files.files.length == 0 && dirs.files.length == 0; - -files.onchange = () => { - upload_button.disabled = files.files.length == 0 && dirs.files.length == 0; -} - -dirs.onchange = () => { - upload_button.disabled = files.files.length == 0 && dirs.files.length == 0; -} +files.onchange = upload; +dirs.onchange = upload; mkdir_button.disabled = new_directory_name.value.length == 0; diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index 7dbe6dbf92..bfc899a3fd 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -42,6 +42,7 @@ #include "supervisor/shared/translate/translate.h" #include "supervisor/shared/web_workflow/web_workflow.h" #include "supervisor/shared/web_workflow/websocket.h" +#include "supervisor/shared/workflow.h" #include "supervisor/usb.h" #include "shared-bindings/hashlib/__init__.h" @@ -763,47 +764,6 @@ static void _reply_with_version_json(socketpool_socket_obj_t *socket, _request * _send_chunk(socket, ""); } -// Copied from ble file_transfer.c. We should share it. -STATIC FRESULT _delete_directory_contents(FATFS *fs, const TCHAR *path) { - FF_DIR dir; - FILINFO file_info; - // Check the stack since we're putting paths on it. - if (mp_stack_usage() >= MP_STATE_THREAD(stack_limit)) { - return FR_INT_ERR; - } - FRESULT res = FR_OK; - while (res == FR_OK) { - res = f_opendir(fs, &dir, path); - if (res != FR_OK) { - break; - } - res = f_readdir(&dir, &file_info); - // We close and reopen the directory every time since we're deleting - // entries and it may invalidate the directory handle. - f_closedir(&dir); - if (res != FR_OK || file_info.fname[0] == '\0') { - break; - } - size_t pathlen = strlen(path); - size_t fnlen = strlen(file_info.fname); - TCHAR full_path[pathlen + 1 + fnlen]; - memcpy(full_path, path, pathlen); - full_path[pathlen] = '/'; - size_t full_pathlen = pathlen + 1 + fnlen; - memcpy(full_path + pathlen + 1, file_info.fname, fnlen); - full_path[full_pathlen] = '\0'; - if ((file_info.fattrib & AM_DIR) != 0) { - res = _delete_directory_contents(fs, full_path); - } - if (res != FR_OK) { - break; - } - res = f_unlink(fs, full_path); - } - f_closedir(&dir); - return res; -} - // FATFS has a two second timestamp resolution but the BLE API allows for nanosecond resolution. // This function truncates the time the time to a resolution storable by FATFS and fills in the // FATFS encoded version into fattime. @@ -1054,7 +1014,7 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) { FRESULT result = f_stat(fs, path, &file); if (result == FR_OK) { if ((file.fattrib & AM_DIR) != 0) { - result = _delete_directory_contents(fs, path); + result = supervisor_workflow_delete_directory_contents(fs, path); } if (result == FR_OK) { result = f_unlink(fs, path); @@ -1105,7 +1065,7 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) { truncate_time(request->timestamp_ms * 1000000, &fattime); override_fattime(fattime); } - FRESULT result = f_mkdir(fs, path); + FRESULT result = supervisor_workflow_mkdir_parents(fs, path); override_fattime(0); #if CIRCUITPY_USB_MSC usb_msc_unlock(); diff --git a/supervisor/shared/workflow.c b/supervisor/shared/workflow.c index 7b14c663c4..40a095720c 100644 --- a/supervisor/shared/workflow.c +++ b/supervisor/shared/workflow.c @@ -26,6 +26,7 @@ #include #include "py/mpconfig.h" +#include "py/stackctrl.h" #include "supervisor/background_callback.h" #include "supervisor/workflow.h" #include "supervisor/serial.h" @@ -115,3 +116,60 @@ void supervisor_workflow_start(void) { supervisor_start_web_workflow(); #endif } + +FRESULT supervisor_workflow_mkdir_parents(FATFS *fs, char *path) { + FRESULT result = FR_OK; + // Make parent directories. + for (size_t j = 1; j < strlen(path); j++) { + if (path[j] == '/') { + path[j] = '\0'; + result = f_mkdir(fs, path); + path[j] = '/'; + if (result != FR_OK && result != FR_EXIST) { + return result; + } + } + } + // Make the target directory. + return f_mkdir(fs, path); +} + +FRESULT supervisor_workflow_delete_directory_contents(FATFS *fs, const TCHAR *path) { + FF_DIR dir; + FILINFO file_info; + // Check the stack since we're putting paths on it. + if (mp_stack_usage() >= MP_STATE_THREAD(stack_limit)) { + return FR_INT_ERR; + } + FRESULT res = FR_OK; + while (res == FR_OK) { + res = f_opendir(fs, &dir, path); + if (res != FR_OK) { + break; + } + res = f_readdir(&dir, &file_info); + // We close and reopen the directory every time since we're deleting + // entries and it may invalidate the directory handle. + f_closedir(&dir); + if (res != FR_OK || file_info.fname[0] == '\0') { + break; + } + size_t pathlen = strlen(path); + size_t fnlen = strlen(file_info.fname); + TCHAR full_path[pathlen + 1 + fnlen]; + memcpy(full_path, path, pathlen); + full_path[pathlen] = '/'; + size_t full_pathlen = pathlen + 1 + fnlen; + memcpy(full_path + pathlen + 1, file_info.fname, fnlen); + full_path[full_pathlen] = '\0'; + if ((file_info.fattrib & AM_DIR) != 0) { + res = supervisor_workflow_delete_directory_contents(fs, full_path); + } + if (res != FR_OK) { + break; + } + res = f_unlink(fs, full_path); + } + f_closedir(&dir); + return res; +} diff --git a/supervisor/shared/workflow.h b/supervisor/shared/workflow.h index b3c817fb8b..df483e0ebc 100644 --- a/supervisor/shared/workflow.h +++ b/supervisor/shared/workflow.h @@ -26,4 +26,10 @@ #pragma once +#include "lib/oofatfs/ff.h" + extern bool supervisor_workflow_connecting(void); + +// File system helpers for workflow code. +FRESULT supervisor_workflow_mkdir_parents(FATFS *fs, char *path); +FRESULT supervisor_workflow_delete_directory_contents(FATFS *fs, const TCHAR *path);