#include "cmd_base.h" #include "utils.h" #include "fatfs/ff.h" #include #include #include /* Set error status for failed file system command (status 451) */ void cmd_fs_fail(cmdargs_t *args, FRESULT status) { const char *error = NULL; switch (status) { case FR_DISK_ERR: error = "FR_DISK_ERR"; break; case FR_NOT_READY: error = "FR_NOT_READY"; break; case FR_NO_FILE: error = "FR_NO_FILE"; break; case FR_NO_PATH: error = "FR_NO_PATH"; break; case FR_INVALID_NAME: error = "FR_INVALID_NAME"; break; case FR_TIMEOUT: error = "FR_TIMEOUT"; break; default: cmd_resultf(args, 450, "FR_%d", status); return; } cmd_result(args, 450, error); } /* Report free space on file system. */ bool cmd_fs_free(cmdargs_t *args) { DWORD free; FATFS *fsptr; FRESULT status = f_getfree("", &free, &fsptr); if (status != FR_OK) { cmd_fs_fail(args, status); } else { fprintf(args->out, "212-sector total free(kB)\n"); cmd_resultf(args, 212, "%6u %7lu %8lu\n", fsptr->csize, (fsptr->n_fatent - 2) * fsptr->csize, free * fsptr->csize ); } return true; } /* Print a single "ls" output line. */ static void print_fileinfo(FILE* stream, FILINFO *file) { // Mode: drwsha char mode[7] = { (file->fattrib & AM_DIR) ? 'd' : '-', 'r', (file->fattrib & AM_RDO) ? '-' : 'w', (file->fattrib & AM_SYS) ? 's' : '-', (file->fattrib & AM_HID) ? 'h' : '-', (file->fattrib & AM_ARC) ? 'a' : '-', '\0' }; // Make the filename lowercase char *p = file->fname; while (*p != '\0') { *p = tolower(*p); p++; } fprintf(stream, "%s %8ld %04d-%02d-%02dT%02d:%02d:%02d %s\n", mode, file->fsize, 1980 + (file->fdate >> 9), (file->fdate >> 5) & 0xF, file->fdate & 0x1F, file->ftime >> 11, (file->ftime >> 5) & 0x3F, (file->ftime & 0x1F) << 1, file->fname); } /* List directory */ bool cmd_fs_ls(cmdargs_t *args) { char *path; FRESULT status; DIR directory; FILINFO file; if (!cmd_string_arg(args, &path)) { path = "/"; } else if (!cmd_last_arg(args)) { return false; } status = f_opendir(&directory, path); if (status != FR_OK) { cmd_fs_fail(args, status); return true; } fprintf(args->out, "100-\n"); // Multi-line response for (;;) { status = f_readdir(&directory, &file); if (status != FR_OK || file.fname[0] == '\0') break; print_fileinfo(args->out, &file); } fprintf(args->out, "100 \n"); if (status != FR_OK) cmd_fs_fail(args, status); else cmd_result(args, 200, "OK"); return true; } /* Send file to client */ bool cmd_fs_get(cmdargs_t *args) { char *path; size_t filesize; FRESULT status; if (!cmd_string_arg(args, &path) || !cmd_last_arg(args)) return false; // Get file size FIXME: could use f_size() directly { FILINFO fileinfo; status = f_stat(path, &fileinfo); if (status != FR_OK) { cmd_fs_fail(args, status); return true; } filesize = fileinfo.fsize; } // Get file contents { FIL file; char *buffer = malloc(512); if (!buffer) { cmd_result(args, 400, "malloc"); return true; } status = f_open(&file, path, FA_READ); if (status != FR_OK) { free(buffer); cmd_fs_fail(args, status); return true; } fprintf(args->out, "100-%d\n", filesize); UINT read_count = 0; bool error = false; while (filesize) { status = f_read(&file, buffer, 512, &read_count); if (status != FR_OK || read_count == 0) { error = true; break; // EOF or error } fwrite(buffer, 1, read_count, args->out); filesize -= read_count; } while (filesize--) fputc(0, args->out); // Pad with zeros if there was an error fprintf(args->out, "100 \n"); f_close(&file); free(buffer); if (status != FR_OK || error) { cmd_fs_fail(args, status); } else { cmd_result(args, 200, "OK"); } return true; } } bool cmd_fs_put(cmdargs_t *args) { char *path; unsigned filesize; FIL file; FRESULT status; if (!cmd_string_arg(args, &path) || !cmd_unsigned_arg(args, &filesize) || !cmd_last_arg(args)) return false; char *buffer = malloc(512); if (!buffer) { cmd_result(args, 400, "malloc"); return true; } status = f_open(&file, path, FA_WRITE | FA_CREATE_ALWAYS); if (status != FR_OK) { free(buffer); cmd_fs_fail(args, status); return true; } unsigned int readsize = 0, writesize = 0, count = 0; while (count < filesize) { fprintf(args->out, "100 %d/%d\n", count, filesize); readsize = fread(buffer, 1, MIN(512, filesize - count), args->in); status = f_write(&file, buffer, readsize, &writesize); count += readsize; if (status != FR_OK || writesize != readsize || readsize == 0) { break; } } free(buffer); FRESULT closestatus = f_close(&file); if (status != FR_OK) cmd_fs_fail(args, status); else if (closestatus != FR_OK) cmd_fs_fail(args, closestatus); else if (writesize != readsize) cmd_result(args, 451, "disk full"); else if (readsize == 0) cmd_result(args, 451, "timeout"); else cmd_result(args, 200, "OK"); return true; } // Copy file bool cmd_fs_cp(cmdargs_t *args) { char *path1, *path2; FIL file1, file2; FRESULT status; if (!cmd_string_arg(args, &path1) || !cmd_string_arg(args, &path2) || !cmd_last_arg(args)) return false; char *buffer = malloc(512); if (!buffer) { cmd_result(args, 400, "malloc"); return true; } status = f_open(&file1, path1, FA_READ); if (status != FR_OK) { free(buffer); cmd_fs_fail(args, status); return true; } status = f_open(&file2, path2, FA_WRITE | FA_CREATE_ALWAYS); if (status != FR_OK) { free(buffer); f_close(&file1); cmd_fs_fail(args, status); return true; } UINT readcount, writecount; do { status = f_read(&file1, buffer, 512, &readcount); if (status != FR_OK) break; status = f_write(&file2, buffer, readcount, &writecount); } while (status == FR_OK && readcount != 0 && writecount == readcount); free(buffer); f_close(&file1); f_close(&file2); if (status != FR_OK) { cmd_fs_fail(args, status); } else { cmd_result(args, 200, "OK"); } return true; } // Remove file bool cmd_fs_rm(cmdargs_t *args) { char *path; FRESULT status; if (!cmd_string_arg(args, &path) || !cmd_last_arg(args)) return false; status = f_unlink(path); if (status != FR_OK) { cmd_fs_fail(args, status); } else { cmd_result(args, 200, "OK"); } return true; } // Create directory bool cmd_fs_mkdir(cmdargs_t *args) { char *path; FRESULT status; if (!cmd_string_arg(args, &path) || !cmd_last_arg(args)) return false; status = f_mkdir(path); if (status != FR_OK) { cmd_fs_fail(args, status); } else { cmd_result(args, 200, "OK"); } return true; } // Move file bool cmd_fs_mv(cmdargs_t *args) { char *path1, *path2; FRESULT status; if (!cmd_string_arg(args, &path1) || !cmd_string_arg(args, &path2) || !cmd_last_arg(args)) return false; status = f_rename(path1, path2); if (status != FR_OK) { cmd_fs_fail(args, status); } else { cmd_result(args, 200, "OK"); } return true; } // Show the last n lines of file bool cmd_fs_tail(cmdargs_t *args) { char *path; unsigned count; FIL file; FRESULT status; if (!cmd_string_arg(args, &path)) return false; if (!cmd_unsigned_arg(args, &count)) count = 10; if (!cmd_last_arg(args)) return false; status = f_open(&file, path, FA_READ); if (status != FR_OK) goto error; status = f_lseek(&file, f_size(&file)); if (status != FR_OK) goto error; // Read backwards until we have seen 'count' newlines char byte; UINT readcount; while (count && f_tell(&file) > 1) { status = f_lseek(&file, f_tell(&file) - 2); if (status != FR_OK) goto error; status = f_read(&file, &byte, 1, &readcount); if (status != FR_OK || readcount != 1) goto error; if (byte == '\n') count--; } if (byte != '\n') // Terminated because of start of file f_lseek(&file, 0); // Read and print forwards fprintf(args->out, "200-\n"); do { status = f_read(&file, &byte, 1, &readcount); if (status != FR_OK) goto error; if (readcount == 1) fputc(byte, args->out); } while (readcount == 1); if (byte != '\n') fputc('\n', args->out); cmd_result(args, 200, "OK"); return true; error: cmd_fs_fail(args, status); return true; } // Calculate BSD checksum for a file bool cmd_fs_sum(cmdargs_t *args) { char *path; FIL file; FRESULT status; if (!cmd_string_arg(args, &path) || !cmd_last_arg(args)) return false; char *buffer = malloc(512); if (!buffer) { cmd_result(args, 400, "malloc"); return true; } status = f_open(&file, path, FA_READ); if (status != FR_OK) { free(buffer); cmd_fs_fail(args, status); return true; } unsigned filesize = 0; uint16_t checksum = 0; UINT readcount; do { status = f_read(&file, buffer, 512, &readcount); for (int i = 0; i < readcount; i++) { checksum = (checksum >> 1) + ((checksum & 1) << 15); checksum += buffer[i]; } filesize += readcount; } while (status == FR_OK && readcount != 0); f_close(&file); free(buffer); if (status != FR_OK) { cmd_fs_fail(args, status); } else { // Print checksum and number of blocks, like /usr/bin/sum. fprintf(args->out, "213-Checksum Blocks\n"); cmd_resultf(args, 213, "%8d %6u", checksum, ceil_div(filesize, 1024)); } return true; }