error_t FAT::traverse(Path path, dirvector& ents, const Dirent* const start) const { // start with given entry (defaults to root) uint32_t cluster = start ? start->block() : 0; Dirent found(this, INVALID_ENTITY); while (!path.empty()) { auto S = this->cl_to_sector(cluster); ents.clear(); // mui importante // sync read entire directory auto err = int_ls(S, ents); if (UNLIKELY(err)) return err; // the name we are looking for const std::string name = path.front(); path.pop_front(); // check for matches in dirents for (auto& e : ents) if (UNLIKELY(e.name() == name)) { // go to this directory, unless its the last name FS_PRINT("traverse_sync: Found match for %s", name.c_str()); // enter the matching directory FS_PRINT("\t\t cluster: %lu\n", e.block()); // only follow if the name is a directory if (e.type() == DIR) { found = e; break; } else { // not dir = error, for now return { error_t::E_NOTDIR, "Cannot list non-directory" }; } } // for (ents) // validate result if (found.type() == INVALID_ENTITY) { FS_PRINT("traverse_sync: NO MATCH for %s\n", name.c_str()); return { error_t::E_NOENT, name }; } // set next cluster cluster = found.block(); } auto S = this->cl_to_sector(cluster); // read result directory entries into ents ents.clear(); // mui importante! return int_ls(S, ents); }
void FAT::init(uint64_t base, uint64_t size, on_init_func on_init) { this->lba_base = base; this->lba_size = size; // read Partition block device.read( base, hw::Block_device::on_read_func::make_packed( [this, on_init] (buffer_t data) { auto* mbr = (MBR::mbr*) data->data(); if (mbr == nullptr) { on_init({ error_t::E_IO, "Could not read MBR" }, *this); return; } // verify image signature FS_PRINT("OEM name: \t%s\n", mbr->oem_name); FS_PRINT("MBR signature: \t0x%x\n", mbr->magic); if (UNLIKELY(mbr->magic != 0xAA55)) { on_init({ error_t::E_MNT, "Missing or invalid MBR signature" }, *this); return; } // initialize FAT16 or FAT32 filesystem init(mbr); // determine which FAT version is initialized switch (this->fat_type) { case FAT::T_FAT12: INFO("FAT", "Initializing FAT12 filesystem"); break; case FAT::T_FAT16: INFO("FAT", "Initializing FAT16 filesystem"); break; case FAT::T_FAT32: INFO("FAT", "Initializing FAT32 filesystem"); break; } INFO2("[ofs=%u size=%u (%u bytes)]\n", this->lba_base, this->lba_size, this->lba_size * 512); // on_init callback on_init(no_error, *this); }) ); }
Dirent FAT::stat(Path path, const Dirent* const start) const { if (UNLIKELY(path.empty())) { return Dirent(this, Enttype::DIR, "/", 0); } FS_PRINT("stat_sync: %s\n", path.back().c_str()); // extract file we are looking for const std::string filename = path.back(); path.pop_back(); // result directory entries are put into @dirents dirvector dirents; auto err = traverse(path, dirents, start); if (UNLIKELY(err)) return Dirent(this, INVALID_ENTITY); // for now // find the matching filename in directory for (auto& e : dirents) if (UNLIKELY(e.name() == filename)) { // return this directory entry return e; } // entry not found return Dirent(this, INVALID_ENTITY); }
static void fs_print(struct sbuf *sb, struct flowtable_stats *fs) { FS_PRINT(sb, collisions); FS_PRINT(sb, allocated); FS_PRINT(sb, misses); FS_PRINT(sb, max_depth); FS_PRINT(sb, free_checks); FS_PRINT(sb, frees); FS_PRINT(sb, hits); FS_PRINT(sb, lookups); }
void FAT::int_ls( uint32_t sector, Dirvec_ptr dirents, on_internal_ls_func callback) const { // list contents of meme sector by sector typedef delegate<void(uint32_t)> next_func_t; auto next = std::make_shared<next_func_t> (); auto weak_next = std::weak_ptr<next_func_t>(next); *next = next_func_t::make_packed( [this, callback, dirents, weak_next] (uint32_t sector) { FS_PRINT("int_ls: sec=%u\n", sector); auto next = weak_next.lock(); device.read( sector, hw::Block_device::on_read_func::make_packed( [this, sector, callback, dirents, next] (buffer_t data) { if (data == nullptr) { // could not read sector callback({ error_t::E_IO, "Unable to read directory" }, dirents); return; } // parse entries in sector bool done = int_dirent(sector, data->data(), *dirents); if (done) // execute callback callback(no_error, dirents); else // go to next sector (*next)(sector+1); }) ); // read root dir }); // start reading sectors asynchronously (*next)(sector); }
void FAT::cstat(const std::string& strpath, on_stat_func func) { // cache lookup auto it = stat_cache.find(strpath); if (it != stat_cache.end()) { FS_PRINT("used cached stat for %s\n", strpath.c_str()); func(no_error, it->second); return; } File_system::stat( strpath, fs::on_stat_func::make_packed( [this, strpath, func] (error_t error, const Dirent& ent) { stat_cache.emplace(strpath, ent); func(error, ent); }) ); }
void FAT::stat(Path_ptr path, on_stat_func func, const Dirent* const start) const { // manual lookup if (UNLIKELY(path->empty())) { // Note: could use ATTR_VOLUME_ID in FAT func(no_error, Dirent(this, DIR, "/", 0)); return; } FS_PRINT("stat: %s\n", path->back().c_str()); // extract file we are looking for std::string filename = path->back(); path->pop_back(); traverse( path, cluster_func::make_packed( [this, filename, func] (error_t error, Dirvec_ptr dirents) { if (UNLIKELY(error)) { // no path, no file! func(error, Dirent(this, INVALID_ENTITY, filename)); return; } // find the matching filename in directory for (auto& e : *dirents) { if (UNLIKELY(e.name() == filename)) { // return this dir entry func(no_error, e); return; } } // not found func({ error_t::E_NOENT, filename }, Dirent(this, INVALID_ENTITY, filename)); }), start ); }
void FAT::traverse(std::shared_ptr<Path> path, cluster_func callback, const Dirent* const start) const { // parse this path into a stack of memes typedef delegate<void(uint32_t)> next_func_t; // asynch stack traversal auto next = std::make_shared<next_func_t> (); auto weak_next = std::weak_ptr<next_func_t>(next); *next = next_func_t::make_packed( [this, path, weak_next, callback] (uint32_t cluster) { if (path->empty()) { // attempt to read directory uint32_t S = this->cl_to_sector(cluster); // result allocated on heap auto dirents = std::make_shared<std::vector<Dirent>> (); int_ls(S, dirents, on_internal_ls_func::make_packed( [callback] (error_t error, Dirvec_ptr ents) { callback(error, ents);} )); return; } // retrieve next name std::string name = path->front(); path->pop_front(); uint32_t S = this->cl_to_sector(cluster); FS_PRINT("Current target: %s on cluster %u (sector %u)\n", name.c_str(), cluster, S); // result allocated on heap auto dirents = std::make_shared<std::vector<Dirent>> (); auto next = weak_next.lock(); // list directory contents int_ls( S, dirents, on_internal_ls_func::make_packed( [name, dirents, next, callback] (error_t err, Dirvec_ptr ents) { if (UNLIKELY(err)) { FS_PRINT("Could not find: %s\n", name.c_str()); callback(err, dirents); return; } // look for name in directory for (auto& e : *ents) { if (UNLIKELY(e.name() == name)) { // go to this directory, unless its the last name FS_PRINT("Found match for %s", name.c_str()); // enter the matching directory FS_PRINT("\t\t cluster: %lu\n", e.block()); // only follow directories if (e.type() == DIR) (*next)(e.block()); else callback({ error_t::E_NOTDIR, e.name() }, dirents); return; } } // for (ents) FS_PRINT("NO MATCH for %s\n", name.c_str()); callback({ error_t::E_NOENT, name }, dirents); }) ); }); // start by reading provided dirent or root (*next)(start ? start->block() : 0); }
void FAT::init(const void* base_sector) { // assume its the master boot record for now auto* mbr = (MBR::mbr*) base_sector; MBR::BPB* bpb = mbr->bpb(); this->sector_size = bpb->bytes_per_sector; if (UNLIKELY(this->sector_size < 512)) { fprintf(stderr, "Invalid sector size (%u) for FAT32 partition\n", sector_size); fprintf(stderr, "Are you initializing the correct partition?\n"); panic("FAT32: Invalid sector size"); } // Let's begin our incantation // To drive out the demons of old DOS we have to read some PBP values FS_PRINT("Bytes per sector: \t%u\n", bpb->bytes_per_sector); FS_PRINT("Sectors per cluster: \t%u\n", bpb->sectors_per_cluster); FS_PRINT("Reserved sectors: \t%u\n", bpb->reserved_sectors); FS_PRINT("Number of FATs: \t%u\n", bpb->fa_tables); FS_PRINT("Small sectors (FAT16): \t%u\n", bpb->small_sectors); FS_PRINT("Sectors per FAT: \t%u\n", bpb->sectors_per_fat); FS_PRINT("Sectors per Track: \t%u\n", bpb->sectors_per_track); FS_PRINT("Number of Heads: \t%u\n", bpb->num_heads); FS_PRINT("Hidden sectors: \t%u\n", bpb->hidden_sectors); FS_PRINT("Large sectors: \t%u\n", bpb->large_sectors); FS_PRINT("Disk number: \t0x%x\n", bpb->disk_number); FS_PRINT("Signature: \t0x%x\n", bpb->signature); FS_PRINT("System ID: \t%.8s\n", bpb->system_id); // sector count if (bpb->small_sectors) this->sectors = bpb->small_sectors; else this->sectors = bpb->large_sectors; // sectors per FAT (not sure about the rule here) this->sectors_per_fat = bpb->sectors_per_fat; if (this->sectors_per_fat == 0) this->sectors_per_fat = *(uint32_t*) &mbr->boot[25]; // root dir sectors from root entries this->root_dir_sectors = ((bpb->root_entries * 32) + (sector_size - 1)) / sector_size; // calculate index of first data sector this->data_index = bpb->reserved_sectors + (bpb->fa_tables * this->sectors_per_fat) + this->root_dir_sectors; FS_PRINT("First data sector: %u\n", this->data_index); // number of reserved sectors is needed constantly this->reserved = bpb->reserved_sectors; FS_PRINT("Reserved sectors: %u\n", this->reserved); // number of sectors per cluster is important for calculating entry offsets this->sectors_per_cluster = bpb->sectors_per_cluster; FS_PRINT("Sectors per cluster: %u\n", this->sectors_per_cluster); // calculate number of data sectors this->data_sectors = this->sectors - this->data_index; FS_PRINT("Data sectors: %u\n", this->data_sectors); // calculate total cluster count this->clusters = this->data_sectors / this->sectors_per_cluster; FS_PRINT("Total clusters: %u\n", this->clusters); // now that we're here, we can determine the actual FAT type // using the official method: if (this->clusters < 4085) { this->fat_type = FAT::T_FAT12; this->root_cluster = 2; FS_PRINT("The image is type FAT12, with %u clusters\n", this->clusters); } else if (this->clusters < 65525) { this->fat_type = FAT::T_FAT16; this->root_cluster = 2; FS_PRINT("The image is type FAT16, with %u clusters\n", this->clusters); } else { this->fat_type = FAT::T_FAT32; this->root_cluster = *(uint32_t*) &mbr->boot[33]; this->root_cluster = 2; FS_PRINT("The image is type FAT32, with %u clusters\n", this->clusters); FS_PRINT("Root dir entries: %u clusters\n", bpb->root_entries); //assert(bpb->root_entries == 0); //this->root_dir_sectors = 0; //this->data_index = bpb->reserved_sectors + bpb->fa_tables * this->sectors_per_fat; } FS_PRINT("Root cluster index: %u (sector %u)\n", this->root_cluster, cl_to_sector(root_cluster)); FS_PRINT("System ID: %.8s\n", bpb->system_id); }
bool FAT::int_dirent(uint32_t sector, const void* data, dirvector& dirents) const { auto* root = (cl_dir*) data; bool found_last = false; for (int i = 0; i < 16; i++) { if (UNLIKELY(root[i].shortname[0] == 0x0)) { found_last = true; // end of directory break; } else if (UNLIKELY(root[i].shortname[0] == 0xE5)) { // unused index } else { // traverse long names, then final cluster // to read all the relevant info if (LIKELY(root[i].is_longname())) { auto* L = (cl_long*) &root[i]; // the last long index is part of a chain of entries if (L->is_last()) { // buffer for long filename char final_name[256]; int final_count = 0; int total = L->long_index(); // ignore names we can't complete inside of one sector if (i + total >= 16) return false; // go to the last entry and work backwards i += total-1; L += total-1; for (int idx = total; idx > 0; idx--) { uint16_t longname[13]; memcpy(longname+ 0, L->first, 10); memcpy(longname+ 5, L->second, 12); memcpy(longname+11, L->third, 4); for (int j = 0; j < 13; j++) { // 0xFFFF indicates end of name if (UNLIKELY(longname[j] == 0xFFFF)) break; // sometimes, invalid stuff are snuck into filenames if (UNLIKELY(longname[j] == 0x0)) break; final_name[final_count] = longname[j] & 0xFF; final_count++; } L--; if (UNLIKELY(final_count > 240)) { FS_PRINT("Suspicious long name length, breaking...\n"); break; } } final_name[final_count] = 0; FS_PRINT("Long name: %s\n", final_name); i++; // skip over the long version // to the short version for the stats and cluster auto* D = &root[i]; std::string dirname(final_name, final_count); dirents.emplace_back( this, D->type(), std::move(dirname), D->dir_cluster(root_cluster), sector, // parent block D->size(), D->attrib, D->get_modified()); } } else { auto* D = &root[i]; FS_PRINT("Short name: %.11s\n", D->shortname); std::string dirname((char*) D->shortname, 11); dirname = trim_right_copy(dirname); dirents.emplace_back( this, D->type(), std::move(dirname), D->dir_cluster(root_cluster), sector, // parent block D->size(), D->attrib, D->get_modified()); } } // entry is long name } // directory list return found_last; }