#include "fsctl/fugue/dirent.h" #include "fsctl/fugue/cluster.h" #include "fsctl/fugue/logger.h" #include "fsctl/fugue/bits/fat.h" //--- // Internals //--- /* _fugue_dirent_checksum() : Fugue checksum handling */ static uint8_t _fugue_dirent_checksum(uintptr_t directory) { uint8_t *block; uint8_t checksum; int a; int b; checksum = 0; block = (void *)directory; for (int i = 0 ; i < 11 ; ++i) { checksum = checksum & 0xff; a = checksum / 2; b = checksum * 128; checksum = a | b; checksum = checksum + block[i]; } checksum = checksum & 0xff; return checksum; } /* _fugue_dirent_frag_dump() : decode name fragment */ static int _fugue_dirent_frag_dump(fugue_file_t *file, uint8_t *frag, int n) { while (--n >= 0) { file->name[file->name_len] = frag[0]; if ((frag[0] & 0x80) != 0) { file->name[file->name_len] = frag[1]; file->name_len += 1; } file->name_len += 1; frag = &frag[2]; } file->name[file->name_len] = '\0'; return 0; } //--- // Public //--- /* fugue_dirent_init() : init dirent object */ int fugue_dirent_init(fugue_dirent_t *dirent, fugue_fs_t *fs) { if (fs == NULL || dirent == NULL) return -1; memset(dirent, 0x00, sizeof(fugue_dirent_t)); memcpy(&dirent->_private.fs, fs, sizeof(fugue_fs_t)); dirent->current_dir_addr = (uintptr_t)fs->_private.root; dirent->cluster_addr_start = (uintptr_t)fs->_private.root; dirent->cluster_addr_end = (uintptr_t)fs->_private.root; dirent->cluster_addr_end += fs->props.sector_root_nb * 512; return 0; } /* fugue_dirent_dir_fetch() : fetch the current dir blob and walk to next */ void *fugue_dirent_dir_fetch(fugue_dirent_t *dirent) { void *dir; dir = (void*)dirent->current_dir_addr; if (dir == NULL) return NULL; dirent->current_dir_addr += 32; if (dirent->current_dir_addr < dirent->cluster_addr_end) return dir; dirent->cluster_addr_start = fugue_cluster_find_next( &dirent->_private.fs, dirent->cluster_idx ); dirent->cluster_addr_end = dirent->cluster_addr_start; if (dirent->cluster_addr_start != 0x00000000) dirent->cluster_addr_end += dirent->_private.fs.props.cluster_size; return dir; } /* fugue_dirent_name_fetch() : fetch fragment */ int fugue_dirent_name_fetch( fugue_dirent_t *dirent, fugue_file_t *file, void *dir ) { fugue_logger_t *log; struct fugue_fat_dir_name *lfn; lfn = (void *)dir; log = dirent->_private.fs._private.logger; //--- // check directory block validity //--- if (lfn->DIR_Attr != FUGUE_DIR_ATTR_LFN) { fugue_logger_write(log, "[WARN] %p: LFN type error\n", lfn); return -1; } if (lfn->DIR_Type != 0x00) { fugue_logger_write(log, "[WARN] %p: LFN attribute error\n", lfn); return -2; } if (lfn->DIR_FstClus != 0x0000) { fugue_logger_write(log, "[WARN] %p: cluster info error\n", lfn); return -3; } if (lfn->DIR_Sequence._low0 != 0 || lfn->DIR_Sequence._low1 != 0) { fugue_logger_write(log, "[WARN] %p: dirty sequence\n", lfn); return -4; } if (lfn->checksum != _fugue_dirent_checksum(dirent->current_dir_addr)) { fugue_logger_write(log, "[WARN] %p: checksum error\n", lfn); return -5; } //--- // check sequence validity //--- if (lfn->DIR_Sequence.last != 0) { if (dirent->_private.lfn_prev_idx != 0) fugue_logger_write(log, "[WARN] %p: multiple LFN entry\n", lfn); dirent->_private.lfn_prev_idx = lfn->DIR_Sequence.id + 1; } if (dirent->_private.lfn_prev_idx != lfn->DIR_Sequence.id + 1) { fugue_logger_write(log, "[WARN] %p: LFN sequence error\n", lfn); return -6; } dirent->_private.lfn_prev_idx = lfn->DIR_Sequence.id; //--- // Dump name fragment //--- _fugue_dirent_frag_dump(file, (uint8_t*)&lfn->DIR_Char0, 5); _fugue_dirent_frag_dump(file, (uint8_t*)&lfn->DIR_Char5, 5); _fugue_dirent_frag_dump(file, (uint8_t*)&lfn->DIR_Char11,2); return -1; }