#include "mount.h" #include "../MassStorage.h" #include "../fs/fat32/fat32.h" #include "../mbr/mbr.h" #include #include #define AUTOMOUNT_SECTOR_SIZE (512) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) static Mnt_MountTable mtab = {0}; static Mnt_FileTable ftab = {0}; /** * Attempts to mount a volume. * @param mstg pointer to Mass Storage object * @param part pointer to partition entry * @return 0 if concluded without error (even if partition of unknown type could not be mounted) */ static int mnt_mount_volume(MassStorage *mstg, const PartEntry *part) { // fetch common partition details Mnt_Volume *next_entry = mtab.vols + mtab.mounted_vols; // pointer to control block area uint8_t *cbk = next_entry->fs_control_block_area; // control block area to be written next_entry->mnt_name[MNT_MAX_MOUNT_NAME_LENGTH] = '\0'; // write terminating zero in advance int mount_success = -1; // attempt to mount partition switch (part->part_type) { case 0: // empty slot break; case FAT32_CHS_PART_ID: // FAT32 case FAT32_LBA_PART_ID: { Fat32_CtrlBlock *ctrl = (Fat32_CtrlBlock *)cbk; mount_success = fat32_load(ctrl, part->lba_fa, mstg); if (mount_success == 0) { fat32_get_volume_label(ctrl, next_entry->mnt_name, MNT_MAX_MOUNT_NAME_LENGTH); // get volume name next_entry->driver = fat32_driver; // assign driver } } break; default: // unmountable file system's found on the disk MSG("Unknown file system type: %d, cannot mount!\n", part->part_type); break; } // if succeeded, then maintain mount table if (mount_success > -1) { next_entry->part_size = part->sector_count * AUTOMOUNT_SECTOR_SIZE; // save volume size mtab.mounted_vols++; // increase number of mounted partitions } return 0; } /** * Attempt to automount disk volumes. * @param mstg pointer to Mass Storage object * @return 0 if successful */ int mnt_automount_disk(MassStorage *mstg) { // load the first sector uint8_t buf[AUTOMOUNT_SECTOR_SIZE]; mstg->read_sector(mstg, 0, buf); // fetch partitions from the disk const PartEntry *partList = mbr_get_partitions(buf); if (partList == NULL) { // not an MBR disk return 0; } // if it's an MBR disk, iterate over all four partition slots mtab.mounted_vols = 0; PartEntry parts[MBR_MAX_PARTITIONS]; memcpy(parts, partList, sizeof(PartEntry) * MBR_MAX_PARTITIONS); // copy partition table for (uint8_t i = 0; (i < MBR_MAX_PARTITIONS) && (mtab.mounted_vols < MNT_MAX_MOUNTED_VOLUMES); i++) { const PartEntry *part = parts + i; // fetch a partition mnt_mount_volume(mstg, part); // attempt mounting a volume } return 0; } /** * Get volume by name. * @param name volume name * @return pointer to volume, if found OR NULL */ Mnt_Volume *mnt_get_volume_by_name(const char *name) { Mnt_Volume *vol = NULL; for (uint8_t i = 0; i < mtab.mounted_vols; i++) { if (!strncmp(mtab.vols[i].mnt_name, name, MNT_MAX_MOUNT_NAME_LENGTH)) { vol = mtab.vols + i; break; } } return vol; } #define MNT_DIRSEP '/' /** * Separate volume name and the path relative to the volume's root. * @param volpath string of /volume/path * @param volname pointer to free area up to max_volname_len bytes * @param max_volname_len size of free area not including terminating zero * @return pointer to the relative path */ const char *mnt_separate_volume_path(const char *volpath, char *volname, uint16_t max_volname_len) { const char *iter = volpath; // strip first '/' if (*iter == MNT_DIRSEP) { iter++; } // search the end of volume name uint16_t i = 0; while ((*iter != MNT_DIRSEP) && (*iter != '\0')) { if (i < max_volname_len) { // copy only so many that we can store volname[i++] = *iter; } iter++; } volname[i] = '\0'; // skip '/' at the end of the volume name if (*iter == MNT_DIRSEP) { iter++; } return iter; } /** * Get volume by name extracted from a file's path stored on the volume. * @param volpath path beginning with the /volume name/ * @param path path of the file on the volume (relative to volume root) * @return pointer to a mounted volume OR NULL if not found */ Mnt_Volume *mnt_get_volume_by_path(const char *volpath, const char **path) { const char *ret; char volname[MNT_MAX_MOUNT_NAME_LENGTH + 1]; ret = mnt_separate_volume_path(volpath, volname, MNT_MAX_MOUNT_NAME_LENGTH); if (path != NULL) { *path = ret; } return mnt_get_volume_by_name(volname); } #define MNT_FTAB_BLOCK_FREE (0) #define MNT_FTAB_BLOCK_OCCUPIED (1) /** * Allocate space for a newly opened file's helper object. * @return allocated slot OR NULL is allocation was not possible */ Mnt_File *mnt_alloc_file_helper() { Mnt_File *slot = NULL; for (uint8_t i = 0; (i < MNT_MAX_OPEN_FILES) && (ftab.open_files < MNT_MAX_OPEN_FILES); i++) { if (ftab.files[i].ctrl_word == MNT_FTAB_BLOCK_FREE) { ftab.files[i].ctrl_word = MNT_FTAB_BLOCK_OCCUPIED; slot = ftab.files + i; ftab.open_files++; break; } } return slot; } /** * Release file helper. * @param helper pointer to helper */ void mnt_free_file_helper(Mnt_File *file) { if (ftab.open_files > 0) { file->ctrl_word = MNT_FTAB_BLOCK_FREE; ftab.open_files--; } } // ----------------------- /** * List directory. * @param dir path to the directory */ void mnt_list(const char *dir) { // if if ((dir[0] == '\0') || ((dir[0] == MNT_DIRSEP) && (dir[1] == '\0'))) { for (uint8_t i = 0; i < mtab.mounted_vols; i++) { MSG("/%s %.3f MB VOLUME\n", mtab.vols[i].mnt_name, mtab.vols[i].part_size * 1E-06); } return; } // if not empty path is specified const char *relpath; Mnt_Volume *vol = mnt_get_volume_by_path(dir, &relpath); if (vol != NULL) { (vol->driver).list((void *)vol->fs_control_block_area, relpath); } } /** * Open file. * @param path full path to file * @return pointer to file handle */ Mnt_File *mnt_open(const char *path) { Mnt_File *file = NULL; const char *relpath; Mnt_Volume *vol = mnt_get_volume_by_path(path, &relpath); if (vol != NULL) { file = mnt_alloc_file_helper(); if (file != NULL) { file->vol = vol; int success = (vol->driver).open(vol->fs_control_block_area, relpath, file->helper); if (success != 0) { // release file helper if could not open file mnt_free_file_helper(file); file = NULL; } } } return file; } /** * Read from the file. * @param file pointer to Mnt_File object * @param len (desired) read length * @param buf destination buffer * @return actual read length */ int mnt_read(Mnt_File *file, unsigned long len, void *buf) { Mnt_Volume *vol = file->vol; return vol->driver.read(vol->fs_control_block_area, (void *)file->helper, len, buf); } /** * Seek the file. * @param file pointer to Mnt_File object * @param pos position from the beginning * @return ??? */ int mnt_seek(Mnt_File *file, unsigned long pos) { Mnt_Volume *vol = file->vol; return vol->driver.seek(vol->fs_control_block_area, (void *)file->helper, pos); } void mnt_close(Mnt_File *file) { mnt_free_file_helper(file); } // -------------- /** * Get file system name from type ID. * @param fst file system type ID * @return file system name */ static inline const char *mnt_fs_type_to_str(uint8_t fst) { switch (fst) { case FAT32_CHS_PART_ID: case FAT32_LBA_PART_ID: return "FAT32"; default: return "N/A"; } } /** * Print state and statistical infromation. */ void mnt_print_info() { MSG("\n\n---- MOUNT/FILES stats ----\n"); MSG(" Mounted volumes: %u\n", mtab.mounted_vols); for (uint32_t i = 0; i < mtab.mounted_vols; i++) { MSG(" /%s %.3f MB\n", mtab.vols[i].mnt_name, mtab.vols[i].part_size * 1E-06); } MSG(" Open files: %u\n\n", ftab.open_files); MSG("---------------------------\n\n"); }