#pypp 0 #include #include #define SECTOR_BITS 9 #define BLOCK_MASK (~((1 << SECTOR_BITS) - 1)) #define ROOT_CLUSTER 0x7fffffff static unsigned _free extern unsigned _end void init_alloc (): _free = ((unsigned)&_end + PAGE_SIZE - 1) & PAGE_MASK char *alloc_space (unsigned pages): unsigned ret = (_free + PAGE_SIZE - 1) & PAGE_MASK _free = ret + (pages << PAGE_BITS) return (char *)ret void *operator new[] (unsigned size): //kdebug ("new ") void *ret = (void *)_free size = (size + 3) & ~3 unsigned rest = PAGE_SIZE - (((_free - 1) & ~PAGE_MASK) + 1) if rest < size: unsigned pages = ((size - rest) + PAGE_SIZE - 1) >> PAGE_BITS for unsigned p = 0; p < pages; ++p: Iris::Page page = Iris::my_memory.create_page () page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME) Iris::my_memory.map (page, _free + rest + (p << PAGE_BITS)) Iris::free_cap (page) _free += size //kdebug_num ((unsigned)ret) //kdebug ("+") //kdebug_num (size) //kdebug ("\n") return ret void *operator new (unsigned size): return new char[size] static Iris::WString dev static Iris::Num device_size static Iris::Page page static char *data static Iris::Num current_block static void read_block (Iris::Num idx, Iris::Page p = page, unsigned size = 1 << SECTOR_BITS, unsigned offset = 0): if p.code == page.code: if idx.value () == current_block.value () && offset == 0 && size == 1 << SECTOR_BITS: return current_block = idx //kdebug ("fat getting block: ") //kdebug_num (idx.h) //kdebug (":") //kdebug_num (idx.l) //kdebug ("+") //kdebug_num (size) //kdebug ("@") //kdebug_num (offset) //kdebug ("\n") dev.get_block (idx, size, offset, p) struct Fat: char oem[8] unsigned sector_size_bits unsigned sectors_per_cluster_bits unsigned reserved_sectors unsigned num_fats unsigned root_entries unsigned sectors unsigned media unsigned sectors_per_fat unsigned sectors_per_track unsigned heads unsigned hidden_sectors unsigned active_fat bool write_all_fats unsigned fs_version unsigned root_cluster unsigned fsinfo_sector unsigned boot_backup_sector unsigned drive unsigned current_head unsigned volume_id char label[0xb] unsigned bits unsigned clusters unsigned cluster_size_bits unsigned root_sectors unsigned header_sectors unsigned bad_clusters unsigned free_clusters unsigned last_alloced unsigned *fat unsigned first_free_cluster, first_bad_cluster void print_num (char const *pre, unsigned data): kdebug ("\t") kdebug (pre) unsigned bytes = 1 while bytes < 8 && data >> (bytes * 4): ++bytes kdebug_num (data, bytes) kdebug ("\n") void print_br (): kdebug ("\tOEM: '") for unsigned i = 0; i < 8; ++i: kdebug_char (oem[i]) kdebug ("'\n") print_num ("bytes per sector: ", 1 << sector_size_bits) print_num ("sectors per cluster: ", 1 << sectors_per_cluster_bits) print_num ("reserved sectors: ", reserved_sectors) print_num ("number of fats: ", num_fats) print_num ("entries in root directory: ", root_entries) print_num ("sectors: ", sectors) print_num ("media descriptor: ", media) print_num ("sectors per fat: ", sectors_per_fat) print_num ("sectors per track: ", sectors_per_track) print_num ("heads: ", heads) print_num ("hidden sectors: ", hidden_sectors) print_num ("active_fat: ", active_fat) kdebug ("\twrite all: ") kdebug (write_all_fats ? "yes\n" : "no\n") print_num ("fs version: ", fs_version) print_num ("root cluster: ", root_cluster) print_num ("fsinfo sector: ", fsinfo_sector) print_num ("boot sector backup sector: ", boot_backup_sector) print_num ("drive: ", drive) print_num ("current head: ", current_head) print_num ("volume id: ", volume_id) kdebug ("\tlabel: '") for unsigned i = 0; i < 0xb; ++i: kdebug_char (label[i]) kdebug ("'\n") print_num ("bits: ", bits) print_num ("clusters: ", clusters) print_num ("header sectors: ", header_sectors) unsigned read_num (char *data, unsigned bytes): unsigned ret = 0 for unsigned i = 0; i < bytes; ++i: ret |= (data[i] & 0xff) << (i * 8) return ret void map_fat_cluster (unsigned c, unsigned offset = 0): //unsigned b = current_block.l read_block ((reserved_sectors + ((c * bits + 8 * offset) >> (sector_size_bits + 3))) << sector_size_bits) //if b != current_block.l: //for unsigned i = 0; i < 0x20; ++i: //kdebug (" ") //kdebug_num (data[i], 2) //kdebug ("\n") unsigned make_bits (unsigned orig): unsigned ret for ret = 0; ret < 32; ++ret: if orig == 1 << ret: return ret //Iris::panic (orig, "non-power of 2") kdebug ("not a power of two, using 16\n") return 16 void reset (): read_block (0) if data[0x1fe] != 0x55 || (data[0x1ff] & 0xff) != 0xaa: kdebug ("invalid boot record signature in fat device\n") for unsigned i = 0; i < 8; ++i: oem[i] = data[3 + i] sector_size_bits = make_bits (read_num (data + 0xb, 2)) sectors_per_cluster_bits = make_bits (read_num (data + 0xd, 1)) cluster_size_bits = sector_size_bits + sectors_per_cluster_bits reserved_sectors = read_num (data + 0xe, 2) num_fats = read_num (data + 0x10, 1) root_entries = read_num (data + 0x11, 2) sectors = read_num (data + 0x13, 2) media = read_num (data + 0x15, 1) sectors_per_fat = read_num (data + 0x16, 2) sectors_per_track = read_num (data + 0x18, 2) heads = read_num (data + 0x1a, 2) hidden_sectors = read_num (data + 0x1c, 4) if !sectors: sectors = read_num (data + 0x20, 4) if Iris::Num (sectors).value () << sector_size_bits > device_size.value (): sectors = device_size.value () >> sector_size_bits kdebug ("warning: limiting sectors because of limited device size\n") root_sectors = (root_entries * 32 + (1 << sector_size_bits) - 1) >> sector_size_bits header_sectors = reserved_sectors + sectors_per_fat * num_fats + root_sectors clusters = (sectors - header_sectors) >> sectors_per_cluster_bits unsigned skip if clusters >= 65525: bits = 32 sectors_per_fat = read_num (data + 0x24, 4) active_fat = read_num (data + 0x28, 2) write_all_fats = active_fat & 0x80 active_fat &= 0xf fs_version = read_num (data + 0x2a, 2) root_cluster = read_num (data + 0x2c, 4) fsinfo_sector = read_num (data + 0x30, 2) boot_backup_sector = read_num (data + 0x32, 2) skip = 0x40 - 0x24 else: if clusters < 4085: bits = 12 else: bits = 16 skip = 0 active_fat = 0 write_all_fats = true fs_version = 0 root_cluster = 0 fsinfo_sector = 0 boot_backup_sector = 0 unsigned fat_entries_per_sector = (8 << sector_size_bits) / bits unsigned fat_entries = sectors_per_fat * fat_entries_per_sector if clusters + 2 > fat_entries: clusters = fat_entries - 2 kdebug ("warning: limiting clusters because of limited sector count\n") drive = read_num (data + skip + 0x24, 1) current_head = read_num (data + skip + 0x25, 1) if data[skip + 0x26] == 0x29: volume_id = read_num (data + skip + 0x27, 4) for unsigned i = 0; i < 0xb; ++i: label[i] = data[skip + 0x2b + i] char *id = data + skip + 0x36 if id[0] != 'F' || id[1] != 'A' || id[2] != 'T' || id[5] != ' ' || id[6] != ' ' || id[7] != ' ': kdebug ("warning: file system type field was not 'FATxx '\n") else: switch bits: case 12: if id[3] != '1' || id[4] != '2': kdebug ("warning: id for fat12 is not FAT12\n") break case 16: if id[3] != '1' || id[4] != '6': kdebug ("warning: id for fat16 is not FAT16\n") break case 32: if id[3] != '3' || id[4] != '2': kdebug ("warning: id for fat32 wat not FAT32") break else: volume_id = 0 for unsigned i = 0; i < 0xb; ++i: label[i] = 0 if fsinfo_sector: read_block (fsinfo_sector << sector_size_bits) if (data[0] & 0xff) != 0x52 || (data[1] & 0xff) != 0x52 || (data[2] & 0xff) != 0x6a || (data[3] & 0xff) != 0x41 || (data[0x1e4] & 0xff) != 0x72 || (data[0x1e5] & 0xff) != 0x72 || (data[0x1e6] & 0xff) != 0x4a || (data[0x1e7] & 0xff) != 0x61 || (data[0x1fe] & 0xff) != 0x55 || (data[0x1ff] & 0xff) != 0xaa: kdebug ("invalid signature in fsinfo structure\n") free_clusters = read_num (data + 0x1e8, 4) last_alloced = read_num (data + 0x1ec, 4) else: free_clusters = ~0 last_alloced = ~0 // Now read the FAT. bad_clusters = 0 fat = new unsigned[clusters] unsigned counted_free_clusters = 0 first_free_cluster = ~0 for unsigned c = 0; c < clusters; ++c: // reduced cluster. unsigned rc = c & (1 << sector_size_bits) - 1 // The next line does nothing most of the time. map_fat_cluster (c) switch bits: case 12: fat[c] = data[(rc + 2) * 2] & 0xff // There may be a sector boundary in the middle of the entry, so optionally reread. map_fat_cluster (c, 1) fat[c] |= (data[(rc + 2) * 2 + 1] & 0xff) << 8 if c & 1: fat[c] >>= 4 else: fat[c] &= 0xfff break case 16: fat[c] = read_num (data + (rc + 2) * 2, 2) break case 32: fat[c] = read_num (data + (rc + 2) * 4, 4) break // Correct for the crazy +2 offset, and keep a list of bad and free clusters. if fat[c] == 0: // Free cluster. fat[c] = first_free_cluster first_free_cluster = c ++counted_free_clusters else if fat[c] == 1: // Invalid value. Iris::panic (0, "entry is '1' in fat.") else if bits == 12 && fat[c] == 0xfff || bits == 16 && fat[c] == 0xffff || bits == 32 && fat[c] == 0xfffffff: // Last cluster in chain. fat[c] = ~0 //kdebug ("last cluster: ") //kdebug_num (c) //kdebug ("\n") else if bits == 12 && fat[c] == 0xff7 || bits == 16 && fat[c] == 0xfff7 || bits == 32 && fat[c] == 0xffffff7: // Bad cluster. fat[c] = first_bad_cluster first_bad_cluster = c ++bad_clusters else: // Non-last cluster in chain. fat[c] -= 2 //kdebug_num (c) //kdebug (" -> ") //kdebug_num (fat[c]) //kdebug ("\n") unsigned fat_lookup (unsigned first_cluster, unsigned cluster): //kdebug ("looking up ") //kdebug_num (first_cluster) //kdebug ("+") //kdebug_num (cluster) //kdebug (":") while cluster--: first_cluster = fat[first_cluster] //kdebug ("->") //kdebug_num (first_cluster) if first_cluster == ~0: //kdebug ("sector beyond end of file requested\n") return ~0 //kdebug ("\n") return first_cluster struct File: Fat *fat unsigned size unsigned first_cluster char name[11] bool archive, readonly, system, hidden, directory, volume unsigned create_second, create_minute_hour, create_date, access_date, time, date unsigned checksum void load_cluster (unsigned idx, unsigned offset_in_cluster, Iris::Page p = page, unsigned offset = 0): unsigned cluster = fat->fat_lookup (first_cluster, idx >> fat->cluster_size_bits) //kdebug ("loading cluster ") //kdebug_num (idx) //kdebug ("+") //kdebug_num (offset_in_cluster) //kdebug ("@") //kdebug_num (cluster) //kdebug (" from file\n") if cluster == ~0: kdebug ("invalid cluster requested from file\n") return read_block (((fat->header_sectors + (Iris::Num (cluster).value () << fat->sectors_per_cluster_bits)) << fat->sector_size_bits) + offset_in_cluster, p, fat->cluster_size_bits < PAGE_BITS ? 1 << fat->cluster_size_bits : PAGE_SIZE, offset) char *load_dir_entry (unsigned dir, unsigned idx): unsigned sector = idx >> (sector_size_bits - 5) unsigned num = (idx << 5) & ~BLOCK_MASK Iris::Num hwsector if dir == ROOT_CLUSTER: if sector < root_sectors: hwsector = header_sectors - root_sectors + sector else: return NULL else: unsigned entry = fat_lookup (dir, sector) if entry == ~0: return NULL hwsector = header_sectors + (Iris::Num (entry).value () << sectors_per_cluster_bits) read_block (hwsector.value () << sector_size_bits) return &data[num] char *find_idx (unsigned dir, unsigned *idx, unsigned *count = NULL): unsigned todo = *idx + 1 char *e if count: *count = 0 for *idx = 0; todo; ++*idx: e = load_dir_entry (dir, *idx) if !e: return NULL if (e[0xb] & 0xff) == 0xf: // This is part of a long filename. continue if (e[0] & 0xff) == 0xe5: // This is a deleted file. continue if !e[0]: // This is a free entry. continue if count: ++*count --todo --*idx return e unsigned get_dir_size (unsigned dir): unsigned num = 0 - 2 unsigned ret find_idx (dir, &num, &ret) return ret bool get_dir_entry (unsigned dir, unsigned idx, File *f): char *e = load_dir_entry (dir, idx) if !e: kdebug ("unable to load dir entry\n") return false f->fat = this //kdebug ("loading dir entry for ") for unsigned i = 0; i < 11; ++i: f->name[i] = e[i] //kdebug_char (f->name[i]) //kdebug ("\n") f->readonly = e[0xb] & 0x1 f->system = e[0xb] & 0x2 f->hidden = e[0xb] & 0x4 f->volume = e[0xb] & 0x8 f->directory = e[0xb] & 0x10 f->archive = e[0xb] & 0x20 f->create_second = read_num (e + 0xd, 1) f->create_minute_hour = read_num (e + 0xe, 2) f->create_date = read_num (e + 0x10, 2) f->access_date = read_num (e + 0x12, 2) f->time = read_num (e + 0x16, 1) f->date = read_num (e + 0x18, 1) f->size = read_num (e + 0x1c, 4) f->first_cluster = (read_num (e + 0x14, 2) << 16 | read_num (e + 0x1a, 2)) - 2 f->checksum = 0 for unsigned i = 0; i < 11; ++i: f->checksum = ((((f->checksum & 1) << 7) | ((f->checksum & 0xfe) >> 1)) + f->name[i]) & 0xff //kdebug ("loaded dir entry, first cluster = ") //kdebug_num (f->first_cluster) //kdebug ("\n") return true struct LFN: unsigned ordinal unsigned name[13] unsigned checksum bool load_lfn (unsigned dir, unsigned idx, unsigned t, unsigned checksum, LFN *lfn): if t >= idx: return false char *e = load_dir_entry (dir, idx - t - 1) if (e[0xb] & 0xff) != 0xf: return false lfn->ordinal = read_num (e + 0x00, 1) for unsigned i = 0; i < 5; ++i: lfn->name[i] = read_num (e + 0x01 + 2 * i, 2) lfn->checksum = read_num (e + 0xd, 1) for unsigned i = 0; i < 6; ++i: lfn->name[i + 5] = read_num (e + 0xe + 2 * i, 2) for unsigned i = 0; i < 2; ++i: lfn->name[i + 11] = read_num (e + 0x1c + 2 * i, 2) return true unsigned parse_shortname (File const &f, char *name): if f.name[0] == ' ': Iris::panic (0, "fat name starts with space") unsigned len = 8 while f.name[len - 1] == ' ': --len char *ptr = name for unsigned i = 0; i < len; ++i: *ptr++ = f.name[i] if f.name[8] == ' ': return len *ptr++ = '.' len = 3 while f.name[8 + len - 1] == ' ': --len for unsigned i = 0; i < len; ++i: *ptr++ = f.name[8 + i] return ptr - name unsigned get_name_size (unsigned dir, unsigned idx, File const &f): LFN lfn unsigned num = 0 if !load_lfn (dir, idx, 0, f.checksum, &lfn): // Not a long filename. char n[12] return parse_shortname (f, n) unsigned ordinal = 0 while true: if !load_lfn (dir, idx, num, f.checksum, &lfn): Iris::panic (0, "error parsing long filename") if (lfn.ordinal & 0x3f) != ++ordinal: Iris::panic (lfn.ordinal, "error in sequence for long filename") if lfn.ordinal & 0x40: break ++num unsigned i for i = 0; i < 13; ++i: if !lfn.name[i]: break return num * 13 + i // Capability encoding. // 0:ROOT_CLUSTER = non fat-32 root directory. // 0:cluster = other directory. // cluster:index = file index from directory at cluster. // cluster|0x80000000:index = filename for file with index from directory at cluster. Iris::Num start (): init_alloc () current_block = ~0 dev = Iris::my_parent.get_capability () if dev.get_align_bits () > SECTOR_BITS: kdebug ("fat device doesn't support 512 byte access") return 1 device_size = dev.get_size () data = (char *)0x15000; //alloc_space (1) page = Iris::my_memory.create_page () page.set_flags (Iris::Page::PAYING) Iris::my_memory.map (page, (unsigned)data) Fat fat fat.reset () fat.print_br () Iris::Cap root if fat.root_cluster: root = Iris::my_receiver.create_capability (Iris::Num (fat.root_cluster, 0)) else: root = Iris::my_receiver.create_capability (Iris::Num (ROOT_CLUSTER, 0)) Iris::my_parent.provide_capability (root.copy ()) Iris::free_cap (root) while true: Iris::wait () unsigned dir = Iris::recv.protected_data.h if dir & 0x80000000: dir &= ~0x80000000 // File name. unsigned idx = Iris::recv.protected_data.l Iris::Cap reply = Iris::get_reply () unsigned num = Iris::recv.data[1].l unsigned size = Iris::recv.data[0].h >> 16 unsigned cmd = Iris::recv.data[0].l Fat::File f if !fat.find_idx (dir, &idx): Iris::panic (Iris::recv.protected_data.l, "invalid index") if !fat.get_dir_entry (dir, idx, &f): Iris::panic (Iris::recv.protected_data.l, "invalid dir entry requested for filename") switch cmd: case Iris::String::GET_SIZE: //kdebug ("filename size requested\n") reply.invoke (fat.get_name_size (dir, idx, f)) break case Iris::String::GET_CHARS: //kdebug ("filename chars requested\n") //kdebug ("flags: ") //kdebug_char (f.readonly ? 'R' : 'r') //kdebug_char (f.system ? 'S' : 's') //kdebug_char (f.hidden ? 'H' : 'h') //kdebug_char (f.volume ? 'V' : 'v') //kdebug_char (f.directory ? 'D' : 'd') //kdebug_char (f.archive ? 'A' : 'a') //kdebug_char ('\n') /**/union { unsigned u[4]; char c[16]; } u for unsigned k = 0; k < 4; ++k: u.u[k] = 0 Fat::LFN lfn if !fat.load_lfn (dir, idx, 0, f.checksum, &lfn): // Not a long filename. char n[12] unsigned len = fat.parse_shortname (f, n) //kdebug ("short filename: ") for unsigned k = 0; k + num < len; ++k: u.c[k] = n[k + num] //kdebug_char (u.c[k]) //kdebug ("\n") else: // Very inefficient, but it works: reload everything for every character. //kdebug ("filename: ") for unsigned c = 0; c < 16; ++c: if !fat.load_lfn (dir, idx, (num + c) / 13, f.checksum, &lfn): // Filename isn't this long: keep the rest at 0. break u.c[c] = lfn.name[(num + c) % 13] if u.c[c] == 0: break //kdebug_char (u.c[c]) //kdebug ("\n") reply.invoke (Iris::Num (u.u[0], u.u[1]), Iris::Num (u.u[2], u.u[3])) break case Iris::String::GET_ALIGN_BITS: //kdebug ("filename align requested\n") reply.invoke (0) break case Iris::String::GET_BLOCK: default: Iris::panic (Iris::recv.data[0].l, "invalid request for fat filename") Iris::free_cap (reply) else if dir: // If it *has* a directory, it *is* a file. unsigned idx = Iris::recv.protected_data.l Iris::Cap reply = Iris::get_reply () Iris::Cap arg = Iris::get_arg () Iris::Num num = Iris::recv.data[1] unsigned size = Iris::recv.data[0].h >> 16 unsigned offset = Iris::recv.data[0].h & 0xffff unsigned cmd = Iris::recv.data[0].l Fat::File f fat.get_dir_entry (dir, idx, &f) switch cmd: case Iris::String::GET_SIZE: //kdebug ("file size requested\n") reply.invoke (f.size) break case Iris::String::GET_CHARS: //kdebug ("file chars requested\n") unsigned mask = (1 << fat.cluster_size_bits) - 1 f.load_cluster (num.l & ~mask, num.l & mask) unsigned n = num.l & mask & ~0xf unsigned *dat = (unsigned *)(data + n) reply.invoke (Iris::Num (dat[0], dat[1]), Iris::Num (dat[2], dat[3])) break case Iris::String::GET_ALIGN_BITS: //kdebug ("file align requested\n") reply.invoke (fat.cluster_size_bits <= PAGE_BITS ? fat.cluster_size_bits : PAGE_BITS) break case Iris::String::GET_BLOCK: //kdebug ("file block requested\n") unsigned mask = (1 << fat.cluster_size_bits) - 1 //kdebug ("mask = ") //kdebug_num (mask) //kdebug ("\n") if offset > PAGE_SIZE: //kdebug ("invalid offset requested\n") break if size + offset > PAGE_SIZE: Iris::panic (size, "invalid size requested") size = PAGE_SIZE - offset for unsigned i = 0; i < size; i += 1 << fat.cluster_size_bits: f.load_cluster ((num.l & ~mask) + i, num.l & mask, arg, i + offset) reply.invoke () break case Iris::WString::TRUNCATE: case Iris::WString::SET_CHARS: case Iris::WString::SET_BLOCK: Iris::panic (Iris::recv.data[0].l, "writing to files not supported yet") default: Iris::panic (Iris::recv.data[0].l, "invalid request for fat file") Iris::free_cap (reply) Iris::free_cap (arg) else: // Directory. switch Iris::recv.data[0].l: case Iris::Directory::GET_SIZE: //kdebug ("dir size requested\n") Iris::Cap reply = Iris::get_reply () reply.invoke (fat.get_dir_size (Iris::recv.protected_data.l)) Iris::free_cap (reply) break case Iris::Directory::GET_NAME: //kdebug ("dir name requested\n") Iris::Cap reply = Iris::get_reply () Iris::Cap ret = Iris::my_receiver.create_capability (Iris::Num (Iris::recv.data[1].l, Iris::recv.protected_data.l | 0x80000000)) reply.invoke (0, 0, ret.copy ()) Iris::free_cap (reply) Iris::free_cap (ret) break case Iris::Directory::GET_FILE_RO: //kdebug ("dir file requested\n") Iris::Cap reply = Iris::get_reply () dir = Iris::recv.protected_data.l unsigned idx = Iris::recv.data[1].l unsigned oldidx = idx if !fat.find_idx (dir, &idx): kdebug_num (oldidx) kdebug ("\n") Iris::panic (1, "file not found") Fat::File f fat.get_dir_entry (dir, idx, &f) Iris::Cap ret if f.directory: //kdebug ("dir provided: ") //kdebug_num (f.first_cluster) //kdebug ("\n") ret = Iris::my_receiver.create_capability (Iris::Num (f.first_cluster, 0)) else: ret = Iris::my_receiver.create_capability (Iris::Num (idx, dir)) reply.invoke (0, 0, ret.copy ()) Iris::free_cap (reply) Iris::free_cap (ret) break case Iris::Directory::GET_FILE_INFO: //kdebug ("dir file info requested\n") Iris::Cap reply = Iris::get_reply () dir = Iris::recv.protected_data.l unsigned idx = Iris::recv.data[1].l unsigned oldidx = idx if !fat.find_idx (dir, &idx): kdebug_num (oldidx) kdebug ("\n") Iris::panic (2, "file not found") unsigned type = Iris::recv.data[0].h Fat::File f fat.get_dir_entry (dir, idx, &f) reply.invoke (f.directory ? 1 : 0) Iris::free_cap (reply) break case Iris::Directory::LOCK_RO: case Iris::Directory::UNLOCK_RO: //kdebug ("dir lock or unlock requested\n") Iris::recv.reply.invoke () break default: //kdebug ("invalid dir operation requested\n") Iris::recv.reply.invoke () break