static int shrink_directory(struct exfat* ef, struct exfat_node* dir,
                            off64_t deleted_offset)
{
    const struct exfat_node* node;
    const struct exfat_node* last_node;
    uint64_t entries = 1; /* a directory always has at leat 1 entry (EOD) */
    uint64_t new_size;
    struct exfat_entry eod;
    off64_t eod_offset;
    int rc;

    if (!(dir->flags & EXFAT_ATTRIB_DIR))
        exfat_bug("attempted to shrink a file");
    if (!(dir->flags & EXFAT_ATTRIB_CACHED))
        exfat_bug("attempted to shrink uncached directory");

    for (last_node = node = dir->child; node; node = node->next)
    {
        if (deleted_offset < node->entry_offset)
        {
            /* there are other entries after the removed one, no way to shrink
               this directory */
            return 0;
        }
        if (last_node->entry_offset < node->entry_offset)
            last_node = node;
    }

    if (last_node)
    {
        /* offset of the last entry */
        entries += last_node->entry_offset / sizeof(struct exfat_entry);
        /* two subentries with meta info */
        entries += 2;
        /* subentries with file name */
        entries += DIV_ROUND_UP(utf16_length(last_node->name),
                                EXFAT_ENAME_MAX);
    }

    new_size = DIV_ROUND_UP(entries * sizeof(struct exfat_entry),
                            CLUSTER_SIZE(*ef->sb)) * CLUSTER_SIZE(*ef->sb);
    if (new_size == dir->size)
        return 0;
    rc = exfat_truncate(ef, dir, new_size);
    if (rc != 0)
        return rc;

    /* put EOD entry at the end of the last cluster */
    memset(&eod, 0, sizeof(eod));
    eod_offset = new_size - sizeof(struct exfat_entry);
    if (last_node)
        exfat_write_raw(&eod, sizeof(eod),
                        co2o(ef, last_node->entry_cluster, eod_offset), ef->fd);
    else
        exfat_write_raw(&eod, sizeof(eod),
                        co2o(ef, dir->start_cluster, eod_offset), ef->fd);
    return 0;
}
void exfat_flush_node(struct exfat* ef, struct exfat_node* node)
{
    cluster_t cluster;
    off64_t offset;
    off64_t meta1_offset, meta2_offset;
    struct exfat_entry_meta1 meta1;
    struct exfat_entry_meta2 meta2;

    if (ef->ro)
        exfat_bug("unable to flush node to read-only FS");

    if (node->parent == NULL)
        return; /* do not flush unlinked node */

    cluster = node->entry_cluster;
    offset = node->entry_offset;
    meta1_offset = co2o(ef, cluster, offset);
    next_entry(ef, node->parent, &cluster, &offset);
    meta2_offset = co2o(ef, cluster, offset);

    exfat_read_raw(&meta1, sizeof(meta1), meta1_offset, ef->fd);
    if (meta1.type != EXFAT_ENTRY_FILE)
        exfat_bug("invalid type of meta1: 0x%hhx", meta1.type);
    meta1.attrib = cpu_to_le16(node->flags);
    exfat_unix2exfat(node->mtime, &meta1.mdate, &meta1.mtime, &meta1.mtime_cs);
    exfat_unix2exfat(node->atime, &meta1.adate, &meta1.atime, NULL);

    exfat_read_raw(&meta2, sizeof(meta2), meta2_offset, ef->fd);
    if (meta2.type != EXFAT_ENTRY_FILE_INFO)
        exfat_bug("invalid type of meta2: 0x%hhx", meta2.type);
    meta2.size = meta2.real_size = cpu_to_le64(node->size);
    meta2.start_cluster = cpu_to_le32(node->start_cluster);
    meta2.flags = EXFAT_FLAG_ALWAYS1;
    /* empty files must not be marked as contiguous */
    if (node->size != 0 && IS_CONTIGUOUS(*node))
        meta2.flags |= EXFAT_FLAG_CONTIGUOUS;
    /* name hash remains unchanged, no need to recalculate it */

    meta1.checksum = exfat_calc_checksum(&meta1, &meta2, node->name);

    exfat_write_raw(&meta1, sizeof(meta1), meta1_offset, ef->fd);
    exfat_write_raw(&meta2, sizeof(meta2), meta2_offset, ef->fd);

    node->flags &= ~EXFAT_ATTRIB_DIRTY;
}
static void erase_entry(struct exfat* ef, struct exfat_node* node)
{
    cluster_t cluster = node->entry_cluster;
    off64_t offset = node->entry_offset;
    int name_entries = DIV_ROUND_UP(utf16_length(node->name), EXFAT_ENAME_MAX);
    uint8_t entry_type;

    entry_type = EXFAT_ENTRY_FILE & ~EXFAT_ENTRY_VALID;
    exfat_write_raw(&entry_type, 1, co2o(ef, cluster, offset), ef->fd);

    next_entry(ef, node->parent, &cluster, &offset);
    entry_type = EXFAT_ENTRY_FILE_INFO & ~EXFAT_ENTRY_VALID;
    exfat_write_raw(&entry_type, 1, co2o(ef, cluster, offset), ef->fd);

    while (name_entries--)
    {
        next_entry(ef, node->parent, &cluster, &offset);
        entry_type = EXFAT_ENTRY_FILE_NAME & ~EXFAT_ENTRY_VALID;
        exfat_write_raw(&entry_type, 1, co2o(ef, cluster, offset), ef->fd);
    }
}
Example #4
0
ssize_t exfat_write(struct exfat* ef, struct exfat_node* node,
		const void* buffer, size_t size, off_t offset)
{
	cluster_t cluster;
	const char* bufp = buffer;
	off_t lsize, loffset, remainder;

	if (offset + size > node->size)
	{
		int rc = exfat_truncate(ef, node, offset + size);
		if (rc != 0)
			return rc;
	}
	if (size == 0)
		return 0;

	cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
	if (CLUSTER_INVALID(cluster))
	{
		exfat_error("got invalid cluster");
		return -1;
	}

	loffset = offset % CLUSTER_SIZE(*ef->sb);
	remainder = size;
	while (remainder > 0)
	{
		if (CLUSTER_INVALID(cluster))
		{
			exfat_error("got invalid cluster");
			return -1;
		}
		lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
		exfat_write_raw(bufp, lsize, exfat_c2o(ef, cluster) + loffset, ef->fd);
		bufp += lsize;
		loffset = 0;
		remainder -= lsize;
		cluster = exfat_next_cluster(ef, node, cluster);
	}
	exfat_update_mtime(node);
	return size - remainder;
}