Esempio n. 1
0
ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
		const void* buffer, size_t size, off64_t offset)
{
	cluster_t cluster;
	const char* bufp = buffer;
	off64_t lsize, loffset, remainder;

	if (offset > node->size)
		if (exfat_truncate(ef, node, offset, true) != 0)
			return -1;
	if (offset + size > node->size)
		if (exfat_truncate(ef, node, offset + size, false) != 0)
			return -1;
	if (size == 0)
		return 0;

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

	loffset = offset % CLUSTER_SIZE(*ef->sb);
	remainder = size;
	while (remainder > 0)
	{
		if (CLUSTER_INVALID(cluster))
		{
			exfat_error("invalid cluster 0x%x while writing", cluster);
			return -1;
		}
		lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
		if (exfat_pwrite(ef->dev, bufp, lsize,
				exfat_c2o(ef, cluster) + loffset) < 0)
		{
			exfat_error("failed to write cluster %#x", cluster);
			ef->was_dirty = true; // Leaves the volume "mounted", to force chkdsk on it.
			return -1;
		}
		bufp += lsize;
		loffset = 0;
		remainder -= lsize;
		cluster = exfat_next_cluster(ef, node, cluster);
	}
	exfat_update_mtime(node);
	return size - remainder;
}
void exfat_put_node(struct exfat* ef, struct exfat_node* node)
{
	if (--node->references < 0)
	{
		char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
		exfat_get_name(node, buffer, sizeof(buffer) - 1);
		exfat_bug("reference counter of `%s' is below zero", buffer);
	}

	if (node->references == 0)
	{
		/* FIXME handle I/O error */
		if (exfat_flush_node(ef, node) != 0)
			exfat_bug("node flush failed");
		if (node->flags & EXFAT_ATTRIB_UNLINKED)
		{
			/* free all clusters and node structure itself */
			exfat_truncate(ef, node, 0, true);
			free(node);
		}
		/* FIXME handle I/O error */
		if (exfat_flush(ef) != 0)
			exfat_bug("flush failed");
	}
}
Esempio n. 3
0
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;
}
static int fuse_exfat_truncate(const char* path, off64_t size)
{
	struct exfat_node* node;
	int rc;

	exfat_debug("[%s] %s, %"PRId64, __func__, path, size);

	rc = exfat_lookup(&ef, &node, path);
	if (rc != 0)
		return rc;

	rc = exfat_truncate(&ef, node, size);
	exfat_put_node(&ef, node);
	return rc;
}
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 = 0;
	uint64_t new_size;
	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 == 0) /* directory always has at least 1 cluster */
		new_size = CLUSTER_SIZE(*ef->sb);
	if (new_size == dir->size)
		return 0;
	rc = exfat_truncate(ef, dir, new_size, true);
	if (rc != 0)
		return rc;
	return 0;
}
Esempio n. 6
0
/**
 * This function must be called on rmdir and unlink (after the last
 * exfat_put_node()) to free clusters.
 */
int exfat_cleanup_node(struct exfat* ef, struct exfat_node* node)
{
	int rc = 0;

	if (node->references != 0)
		exfat_bug("unable to cleanup a node with %d references",
				node->references);

	if (node->flags & EXFAT_ATTRIB_UNLINKED)
	{
		/* free all clusters and node structure itself */
		rc = exfat_truncate(ef, node, 0, true);
		/* free the node even in case of error or its memory will be lost */
		free(node);
	}
	return rc;
}
Esempio n. 7
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;
}
Esempio n. 8
0
static int fuse_exfat_truncate(const char* path, fbx_off_t size)
{
	struct exfat_node* node;
	int rc;

	exfat_debug("[%s] %s, %"PRId64, __func__, path, size);

	rc = exfat_lookup(&ef, &node, path);
	if (rc != 0)
		return rc;

	rc = exfat_truncate(&ef, node, size, true);
	if (rc != 0)
	{
		exfat_flush_node(&ef, node);	/* ignore return code */
		exfat_put_node(&ef, node);
		return rc;
	}
	rc = exfat_flush_node(&ef, node);
	exfat_put_node(&ef, node);
	return rc;
}
Esempio n. 9
0
void exfat_put_node(struct exfat* ef, struct exfat_node* node)
{
    if (--node->references < 0)
    {
        char buffer[EXFAT_NAME_MAX + 1];
        exfat_get_name(node, buffer, EXFAT_NAME_MAX);
        exfat_bug("reference counter of `%s' is below zero", buffer);
    }

    if (node->references == 0)
    {
        if (node->flags & EXFAT_ATTRIB_DIRTY)
            exfat_flush_node(ef, node);
        if (node->flags & EXFAT_ATTRIB_UNLINKED)
        {
            /* free all clusters and node structure itself */
            exfat_truncate(ef, node, 0);
            free(node);
        }
        if (ef->cmap.dirty)
            exfat_flush_cmap(ef);
    }
}