static void print_generic_info(const struct exfat_super_block* sb) { printf("Volume serial number 0x%08x\n", le32_to_cpu(sb->volume_serial)); printf("FS version %hhu.%hhu\n", sb->version.major, sb->version.minor); printf("Sector size %10u\n", SECTOR_SIZE(*sb)); printf("Cluster size %10u\n", CLUSTER_SIZE(*sb)); }
int exfat_find_used_sectors(const struct exfat* ef, off_t* a, off_t* b) { cluster_t ca, cb; if (*a == 0 && *b == 0) ca = cb = EXFAT_FIRST_DATA_CLUSTER - 1; else { ca = s2c(ef, *a); cb = s2c(ef, *b); } if (find_used_clusters(ef, &ca, &cb) != 0) return 1; if (*a != 0 || *b != 0) *a = c2s(ef, ca); *b = c2s(ef, cb) + (CLUSTER_SIZE(*ef->sb) - 1) / SECTOR_SIZE(*ef->sb); return 0; }
void pm_open_file(com32sys_t *regs) { int rv; struct file *file; const char *name = MK_PTR(regs->es, regs->esi.w[0]); char mangled_name[FILENAME_MAX]; dprintf("pm_open_file %s\n", name); mangle_name(mangled_name, name); rv = searchdir(mangled_name); if (rv < 0) { regs->eflags.l |= EFLAGS_CF; } else { file = handle_to_file(rv); regs->eflags.l &= ~EFLAGS_CF; regs->eax.l = file->inode->size; regs->ecx.w[0] = SECTOR_SIZE(file->fs); regs->esi.w[0] = rv; } }
void pm_load_high(com32sys_t *regs) { struct fs_info *fs; uint32_t bytes; uint32_t zero_mask; bool have_more; uint32_t bytes_read; char *buf, *limit; struct file *file; uint32_t sector_mask; size_t pad; uint32_t retflags = 0; bytes = regs->eax.l; zero_mask = regs->edx.w[0]; buf = (char *)regs->edi.l; limit = (char *)(regs->ebp.l & ~zero_mask); file = handle_to_file(regs->esi.w[0]); fs = file->fs; sector_mask = SECTOR_SIZE(fs) - 1; while (bytes) { uint32_t sectors; uint32_t chunk; if (buf + SECTOR_SIZE(fs) > limit) { /* Can't fit even one more sector in... */ retflags = EFLAGS_OF; break; } chunk = bytes; if (regs->ebx.w[0]) { call16((void (*)(void))(size_t)regs->ebx.w[0], &zero_regs, NULL); chunk = min(chunk, MAX_CHUNK); } if (chunk > (((char *)limit - buf) & ~sector_mask)) chunk = ((char *)limit - buf) & ~sector_mask; sectors = (chunk + sector_mask) >> SECTOR_SHIFT(fs); bytes_read = fs->fs_ops->getfssec(file, buf, sectors, &have_more); if (bytes_read > chunk) bytes_read = chunk; buf += bytes_read; bytes -= bytes_read; if (!have_more) { /* * If we reach EOF, the filesystem driver will have already closed * the underlying file... this really should be cleaner. */ _close_file(file); regs->esi.w[0] = 0; retflags = EFLAGS_CF; break; } } pad = (size_t)buf & zero_mask; if (pad) memset(buf, 0, pad); regs->ebx.l = (size_t)buf; regs->edi.l = (size_t)buf + pad; set_flags(regs, retflags); }
int exfat_mount(struct exfat* ef, const char* spec, const char* options) { int rc; enum exfat_mode mode; exfat_tzset(); memset(ef, 0, sizeof(struct exfat)); parse_options(ef, options); if (match_option(options, "ro")) mode = EXFAT_MODE_RO; else if (match_option(options, "ro_fallback")) mode = EXFAT_MODE_ANY; else mode = EXFAT_MODE_RW; ef->dev = exfat_open(spec, mode); if (ef->dev == NULL) return -EIO; if (exfat_get_mode(ef->dev) == EXFAT_MODE_RO) { if (mode == EXFAT_MODE_ANY) ef->ro = -1; else ef->ro = 1; } ef->sb = malloc(sizeof(struct exfat_super_block)); if (ef->sb == NULL) { exfat_close(ef->dev); exfat_error("failed to allocate memory for the super block"); return -ENOMEM; } memset(ef->sb, 0, sizeof(struct exfat_super_block)); if (exfat_pread(ef->dev, ef->sb, sizeof(struct exfat_super_block), 0) < 0) { exfat_close(ef->dev); free(ef->sb); exfat_error("failed to read boot sector"); return -EIO; } if (memcmp(ef->sb->oem_name, "EXFAT ", 8) != 0) { exfat_close(ef->dev); free(ef->sb); exfat_error("exFAT file system is not found"); return -EIO; } /* sector cannot be smaller than 512 bytes */ if (ef->sb->sector_bits < 9) { exfat_close(ef->dev); exfat_error("too small sector size: 2^%hhd", ef->sb->sector_bits); free(ef->sb); return -EIO; } /* officially exFAT supports cluster size up to 32 MB */ if ((int) ef->sb->sector_bits + (int) ef->sb->spc_bits > 25) { exfat_close(ef->dev); exfat_error("too big cluster size: 2^(%hhd+%hhd)", ef->sb->sector_bits, ef->sb->spc_bits); free(ef->sb); return -EIO; } ef->zero_cluster = malloc(CLUSTER_SIZE(*ef->sb)); if (ef->zero_cluster == NULL) { exfat_close(ef->dev); free(ef->sb); exfat_error("failed to allocate zero sector"); return -ENOMEM; } /* use zero_cluster as a temporary buffer for VBR checksum verification */ if (!verify_vbr_checksum(ef->dev, ef->zero_cluster, SECTOR_SIZE(*ef->sb))) { free(ef->zero_cluster); exfat_close(ef->dev); free(ef->sb); return -EIO; } memset(ef->zero_cluster, 0, CLUSTER_SIZE(*ef->sb)); if (ef->sb->version.major != 1 || ef->sb->version.minor != 0) { free(ef->zero_cluster); exfat_close(ef->dev); exfat_error("unsupported exFAT version: %hhu.%hhu", ef->sb->version.major, ef->sb->version.minor); free(ef->sb); return -EIO; } if (ef->sb->fat_count != 1) { free(ef->zero_cluster); exfat_close(ef->dev); exfat_error("unsupported FAT count: %hhu", ef->sb->fat_count); free(ef->sb); return -EIO; } if (le64_to_cpu(ef->sb->sector_count) * SECTOR_SIZE(*ef->sb) > exfat_get_size(ef->dev)) { /* this can cause I/O errors later but we don't fail mounting to let user rescue data */ exfat_warn("file system is larger than underlying device: " "%"PRIu64" > %"PRIu64, le64_to_cpu(ef->sb->sector_count) * SECTOR_SIZE(*ef->sb), exfat_get_size(ef->dev)); } ef->root = malloc(sizeof(struct exfat_node)); if (ef->root == NULL) { free(ef->zero_cluster); exfat_close(ef->dev); free(ef->sb); exfat_error("failed to allocate root node"); return -ENOMEM; } memset(ef->root, 0, sizeof(struct exfat_node)); ef->root->flags = EXFAT_ATTRIB_DIR; ef->root->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster); ef->root->fptr_cluster = ef->root->start_cluster; ef->root->name[0] = cpu_to_le16('\0'); ef->root->size = rootdir_size(ef); if (ef->root->size == 0) { free(ef->root); free(ef->zero_cluster); exfat_close(ef->dev); free(ef->sb); return -EIO; } /* exFAT does not have time attributes for the root directory */ ef->root->mtime = 0; ef->root->atime = 0; /* always keep at least 1 reference to the root node */ exfat_get_node(ef->root); rc = exfat_cache_directory(ef, ef->root); if (rc != 0) goto error; if (ef->upcase == NULL) { exfat_error("upcase table is not found"); goto error; } if (ef->cmap.chunk == NULL) { exfat_error("clusters bitmap is not found"); goto error; } if (prepare_super_block(ef) != 0) goto error; return 0; error: exfat_put_node(ef, ef->root); exfat_reset_cache(ef); free(ef->root); free(ef->zero_cluster); exfat_close(ef->dev); free(ef->sb); return -EIO; }
uint32_t generic_getfssec(struct file *file, char *buf, int sectors, bool *have_more) { struct inode *inode = file->inode; struct fs_info *fs = file->fs; struct disk *disk = fs->fs_dev->disk; uint32_t bytes_read = 0; uint32_t bytes_left = inode->size - file->offset; uint32_t sectors_left = (bytes_left + SECTOR_SIZE(fs) - 1) >> SECTOR_SHIFT(fs); uint32_t lsector; if (sectors > sectors_left) sectors = sectors_left; if (!sectors) return 0; lsector = file->offset >> SECTOR_SHIFT(fs); dprintf("Offset: %u lsector: %u\n", file->offset, lsector); if (lsector < inode->this_extent.lstart || lsector >= inode->this_extent.lstart + inode->this_extent.len) { /* inode->this_extent unusable, maybe next_extent is... */ inode->this_extent = inode->next_extent; } if (lsector < inode->this_extent.lstart || lsector >= inode->this_extent.lstart + inode->this_extent.len) { /* Still nothing useful... */ inode->this_extent.lstart = lsector; inode->this_extent.len = 0; } else { /* We have some usable information */ uint32_t delta = lsector - inode->this_extent.lstart; inode->this_extent.lstart = lsector; inode->this_extent.len -= delta; inode->this_extent.pstart = next_psector(inode->this_extent.pstart, delta); } dprintf("this_extent: lstart %u pstart %llu len %u\n", inode->this_extent.lstart, inode->this_extent.pstart, inode->this_extent.len); while (sectors) { uint32_t chunk; size_t len; while (sectors > inode->this_extent.len) { if (!inode->next_extent.len || inode->next_extent.lstart != inode->this_extent.lstart + inode->this_extent.len) get_next_extent(inode); if (!inode->this_extent.len) { /* Doesn't matter if it's contiguous... */ inode->this_extent = inode->next_extent; if (!inode->next_extent.len) { sectors = 0; /* Failed to get anything... we're dead */ break; } } else if (inode->next_extent.len && inode->next_extent.pstart == next_pstart(&inode->this_extent)) { /* Coalesce extents and loop */ inode->this_extent.len += inode->next_extent.len; } else { /* Discontiguous extents */ break; } } dprintf("this_extent: lstart %u pstart %llu len %u\n", inode->this_extent.lstart, inode->this_extent.pstart, inode->this_extent.len); chunk = min(sectors, inode->this_extent.len); len = chunk << SECTOR_SHIFT(fs); dprintf(" I/O: inode %p @ %u start %llu len %u\n", inode, inode->this_extent.lstart, inode->this_extent.pstart, chunk); if (inode->this_extent.pstart == EXTENT_ZERO) { memset(buf, 0, len); } else { disk->rdwr_sectors(disk, buf, inode->this_extent.pstart, chunk, 0); inode->this_extent.pstart += chunk; } buf += len; sectors -= chunk; bytes_read += len; inode->this_extent.lstart += chunk; inode->this_extent.len -= chunk; } bytes_read = min(bytes_read, bytes_left); file->offset += bytes_read; if (have_more) *have_more = bytes_read < bytes_left; return bytes_read; }