Beispiel #1
0
static void dirck(struct exfat* ef, const char* path)
{
	struct exfat_node* parent;
	struct exfat_node* node;
	struct exfat_iterator it;
	int rc;
	size_t path_length;
	char* entry_path;

	if (exfat_lookup(ef, &parent, path) != 0)
		exfat_bug("directory '%s' is not found", path);
	if (!(parent->flags & EXFAT_ATTRIB_DIR))
		exfat_bug("'%s' is not a directory (0x%x)", path, parent->flags);
	if (nodeck(ef, parent) != 0)
	{
		exfat_put_node(ef, parent);
		return;
	}

	path_length = strlen(path);
	entry_path = malloc(path_length + 1 + UTF8_BYTES(EXFAT_NAME_MAX) + 1);
	if (entry_path == NULL)
	{
		exfat_put_node(ef, parent);
		exfat_error("out of memory");
		return;
	}
	strcpy(entry_path, path);
	strcat(entry_path, "/");

	rc = exfat_opendir(ef, parent, &it);
	if (rc != 0)
	{
		free(entry_path);
		exfat_put_node(ef, parent);
		return;
	}
	while ((node = exfat_readdir(ef, &it)))
	{
		exfat_get_name(node, entry_path + path_length + 1,
				UTF8_BYTES(EXFAT_NAME_MAX));
		exfat_debug("%s: %s, %"PRIu64" bytes, cluster %u", entry_path,
				IS_CONTIGUOUS(*node) ? "contiguous" : "fragmented",
				node->size, node->start_cluster);
		if (node->flags & EXFAT_ATTRIB_DIR)
		{
			directories_count++;
			dirck(ef, entry_path);
		}
		else
		{
			files_count++;
			nodeck(ef, node);
		}
		exfat_put_node(ef, node);
	}
	exfat_closedir(ef, &it);
	exfat_put_node(ef, parent);
	free(entry_path);
}
Beispiel #2
0
static void reset_cache(struct exfat* ef, struct exfat_node* node)
{
	char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];

	while (node->child)
	{
		struct exfat_node* p = node->child;
		reset_cache(ef, p);
		tree_detach(p);
		free(p);
	}
	node->flags &= ~EXFAT_ATTRIB_CACHED;
	if (node->references != 0)
	{
		exfat_get_name(node, buffer, sizeof(buffer) - 1);
		exfat_warn("non-zero reference counter (%d) for '%s'",
				node->references, buffer);
	}
	if (node != ef->root && (node->flags & EXFAT_ATTRIB_DIRTY))
	{
		exfat_get_name(node, buffer, sizeof(buffer) - 1);
		exfat_bug("node '%s' is dirty", buffer);
	}
	while (node->references)
		exfat_put_node(ef, node);
}
static int fuse_exfat_readdir(const char* path, void* buffer,
		fuse_fill_dir_t filler, off_t offset, struct fuse_file_info* fi)
{
	struct exfat_node* parent;
	struct exfat_node* node;
	struct exfat_iterator it;
	int rc;
	char name[UTF8_BYTES(EXFAT_NAME_MAX) + 1];

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

	rc = exfat_lookup(&ef, &parent, path);
	if (rc != 0)
		return rc;
	if (!(parent->flags & EXFAT_ATTRIB_DIR))
	{
		exfat_put_node(&ef, parent);
		exfat_error("'%s' is not a directory (0x%x)", path, parent->flags);
		return -ENOTDIR;
	}

	filler(buffer, ".", NULL, 0);
	filler(buffer, "..", NULL, 0);

	rc = exfat_opendir(&ef, parent, &it);
	if (rc != 0)
	{
		exfat_put_node(&ef, parent);
		exfat_error("failed to open directory '%s'", path);
		return rc;
	}
	while ((node = exfat_readdir(&ef, &it)))
	{
		exfat_get_name(node, name, sizeof(name) - 1);
		exfat_debug("[%s] %s: %s, %"PRId64" bytes, cluster 0x%x", __func__,
				name, IS_CONTIGUOUS(*node) ? "contiguous" : "fragmented",
				node->size, node->start_cluster);
		filler(buffer, name, NULL, 0);
		exfat_put_node(&ef, node);
	}
	exfat_closedir(&ef, &it);
	exfat_put_node(&ef, parent);
	return 0;
}
static int fuse_exfat_truncate(const char* path, 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;
}
static int fuse_exfat_release(const char* path, struct fuse_file_info* fi)
{
	/*
	   This handler is called by FUSE on close() syscall. If the FUSE
	   implementation does not call flush handler, we will flush node here.
	   But in this case we will not be able to return an error to the caller.
	   See fuse_exfat_flush() below.
	*/
	exfat_debug("[%s] %s", __func__, path);
 	exfat_flush_node(&ef, get_node(fi));
	exfat_put_node(&ef, get_node(fi));
	return 0; /* FUSE ignores this return value */
}
static int fuse_exfat_getattr(const char* path, struct stat* stbuf)
{
	struct exfat_node* node;
	int rc;

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

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

	exfat_stat(&ef, node, stbuf);
	exfat_put_node(&ef, node);
	return 0;
}
static int fuse_exfat_utimens(const char* path, const struct timespec tv[2])
{
	struct exfat_node* node;
	int rc;

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

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

	exfat_utimes(node, tv);
	exfat_put_node(&ef, node);
	return 0;
}
static int fuse_exfat_rmdir(const char* path)
{
	struct exfat_node* node;
	int rc;

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

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

	rc = exfat_rmdir(&ef, node);
	exfat_put_node(&ef, node);
	return rc;
}
Beispiel #9
0
static void reset_cache(struct exfat* ef, struct exfat_node* node)
{
	while (node->child)
	{
		struct exfat_node* p = node->child;
		reset_cache(ef, p);
		tree_detach(p);
		free(p);
	}
	node->flags &= ~EXFAT_ATTRIB_CACHED;
	if (node->references != 0)
	{
		char buffer[EXFAT_NAME_MAX + 1];
		exfat_get_name(node, buffer, EXFAT_NAME_MAX);
		exfat_warn("non-zero reference counter (%d) for `%s'",
				node->references, buffer);
	}
	while (node->references)
		exfat_put_node(ef, node);
}
Beispiel #10
0
void exfat_unmount(struct exfat* ef)
{
	exfat_flush_nodes(ef);	/* ignore return code */
	exfat_flush(ef);		/* ignore return code */
	exfat_put_node(ef, ef->root);
	exfat_reset_cache(ef);
	free(ef->root);
	ef->root = NULL;
	finalize_super_block(ef);
	exfat_close(ef->dev);	/* close descriptor immediately after fsync */
	ef->dev = NULL;
	free(ef->zero_cluster);
	ef->zero_cluster = NULL;
	free(ef->cmap.chunk);
	ef->cmap.chunk = NULL;
	free(ef->sb);
	ef->sb = NULL;
	free(ef->upcase);
	ef->upcase = NULL;
	ef->upcase_chars = 0;
}
static void reset_cache(struct exfat* ef, struct exfat_node* node)
{
    struct exfat_node* child;
    struct exfat_node* next;

    for (child = node->child; child; child = next)
    {
        reset_cache(ef, child);
        next = child->next;
        free(child);
    }
    if (node->references != 0)
    {
        char buffer[EXFAT_NAME_MAX + 1];
        exfat_get_name(node, buffer, EXFAT_NAME_MAX);
        exfat_warn("non-zero reference counter (%d) for `%s'",
                   node->references, buffer);
    }
    while (node->references--)
        exfat_put_node(ef, node);
    node->child = NULL;
    node->flags &= ~EXFAT_ATTRIB_CACHED;
}
static int fuse_exfat_release(const char* path, struct fuse_file_info* fi)
{
	exfat_debug("[%s] %s", __func__, path);
	exfat_put_node(&ef, get_node(fi));
	return 0;
}
Beispiel #13
0
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;
}