xref: /third_party/exfatprogs/lib/exfat_fs.c (revision a7ce5b29)
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 *   Copyright (C) 2021 LG Electronics.
4 *
5 *   Author(s): Hyunchul Lee <hyc.lee@gmail.com>
6 */
7
8#include <stdlib.h>
9#include <stdio.h>
10#include <string.h>
11#include <errno.h>
12
13#include "exfat_ondisk.h"
14#include "libexfat.h"
15
16#include "exfat_fs.h"
17#include "exfat_dir.h"
18
19struct exfat_inode *exfat_alloc_inode(__u16 attr)
20{
21	struct exfat_inode *node;
22	int size;
23
24	size = offsetof(struct exfat_inode, name) + NAME_BUFFER_SIZE;
25	node = (struct exfat_inode *)calloc(1, size);
26	if (!node) {
27		exfat_err("failed to allocate exfat_node\n");
28		return NULL;
29	}
30
31	node->parent = NULL;
32	INIT_LIST_HEAD(&node->children);
33	INIT_LIST_HEAD(&node->sibling);
34	INIT_LIST_HEAD(&node->list);
35
36	node->attr = attr;
37	return node;
38}
39
40void exfat_free_inode(struct exfat_inode *node)
41{
42	if (node) {
43		if (node->dentry_set)
44			free(node->dentry_set);
45		free(node);
46	}
47}
48
49void exfat_free_children(struct exfat_inode *dir, bool file_only)
50{
51	struct exfat_inode *node, *i;
52
53	list_for_each_entry_safe(node, i, &dir->children, sibling) {
54		if (file_only) {
55			if (!(node->attr & ATTR_SUBDIR)) {
56				list_del(&node->sibling);
57				exfat_free_inode(node);
58			}
59		} else {
60			list_del(&node->sibling);
61			list_del(&node->list);
62			exfat_free_inode(node);
63		}
64	}
65}
66
67void exfat_free_file_children(struct exfat_inode *dir)
68{
69	exfat_free_children(dir, true);
70}
71
72/* delete @child and all ancestors that does not have
73 * children
74 */
75void exfat_free_ancestors(struct exfat_inode *child)
76{
77	struct exfat_inode *parent;
78
79	while (child && list_empty(&child->children)) {
80		if (!child->parent || !(child->attr & ATTR_SUBDIR))
81			return;
82
83		parent = child->parent;
84		list_del(&child->sibling);
85		exfat_free_inode(child);
86
87		child = parent;
88	}
89	return;
90}
91
92void exfat_free_dir_list(struct exfat *exfat)
93{
94	struct exfat_inode *dir, *i;
95
96	list_for_each_entry_safe(dir, i, &exfat->dir_list, list) {
97		if (!dir->parent)
98			continue;
99		exfat_free_file_children(dir);
100		list_del(&dir->list);
101		exfat_free_inode(dir);
102	}
103}
104
105void exfat_free_exfat(struct exfat *exfat)
106{
107	if (exfat) {
108		if (exfat->bs)
109			free(exfat->bs);
110		if (exfat->alloc_bitmap)
111			free(exfat->alloc_bitmap);
112		if (exfat->disk_bitmap)
113			free(exfat->disk_bitmap);
114		if (exfat->ohead_bitmap)
115			free(exfat->ohead_bitmap);
116		if (exfat->upcase_table)
117			free(exfat->upcase_table);
118		if (exfat->root)
119			exfat_free_inode(exfat->root);
120		if (exfat->zero_cluster)
121			free(exfat->zero_cluster);
122		free(exfat);
123	}
124}
125
126struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs)
127{
128	struct exfat *exfat;
129
130	exfat = (struct exfat *)calloc(1, sizeof(*exfat));
131	if (!exfat)
132		return NULL;
133
134	INIT_LIST_HEAD(&exfat->dir_list);
135	exfat->blk_dev = blk_dev;
136	exfat->bs = bs;
137	exfat->clus_count = le32_to_cpu(bs->bsx.clu_count);
138	exfat->clus_size = EXFAT_CLUSTER_SIZE(bs);
139	exfat->sect_size = EXFAT_SECTOR_SIZE(bs);
140
141	/* TODO: bitmap could be very large. */
142	exfat->alloc_bitmap = (char *)calloc(1,
143			EXFAT_BITMAP_SIZE(exfat->clus_count));
144	if (!exfat->alloc_bitmap) {
145		exfat_err("failed to allocate bitmap\n");
146		goto err;
147	}
148
149	exfat->ohead_bitmap =
150		calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count));
151	if (!exfat->ohead_bitmap) {
152		exfat_err("failed to allocate bitmap\n");
153		goto err;
154	}
155
156	exfat->disk_bitmap =
157		calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count));
158	if (!exfat->disk_bitmap) {
159		exfat_err("failed to allocate bitmap\n");
160		goto err;
161	}
162
163	exfat->zero_cluster = calloc(1, exfat->clus_size);
164	if (!exfat->zero_cluster) {
165		exfat_err("failed to allocate a zero-filled cluster buffer\n");
166		goto err;
167	}
168
169	exfat->start_clu = EXFAT_FIRST_CLUSTER;
170	return exfat;
171err:
172	exfat_free_exfat(exfat);
173	return NULL;
174}
175
176struct buffer_desc *exfat_alloc_buffer(int count,
177				       unsigned int clu_size, unsigned int sect_size)
178{
179	struct buffer_desc *bd;
180	int i;
181
182	bd = (struct buffer_desc *)calloc(sizeof(*bd), count);
183	if (!bd)
184		return NULL;
185
186	for (i = 0; i < count; i++) {
187		bd[i].buffer = (char *)malloc(clu_size);
188		if (!bd[i].buffer)
189			goto err;
190		bd[i].dirty = (char *)calloc(clu_size / sect_size, 1);
191		if (!bd[i].dirty)
192			goto err;
193	}
194	return bd;
195err:
196	exfat_free_buffer(bd, count);
197	return NULL;
198}
199
200void exfat_free_buffer(struct buffer_desc *bd, int count)
201{
202	int i;
203
204	for (i = 0; i < count; i++) {
205		if (bd[i].buffer)
206			free(bd[i].buffer);
207		if (bd[i].dirty)
208			free(bd[i].dirty);
209	}
210	free(bd);
211}
212
213/*
214 * get references of ancestors that include @child until the count of
215 * ancesters is not larger than @count and the count of characters of
216 * their names is not larger than @max_char_len.
217 * return true if root is reached.
218 */
219static bool get_ancestors(struct exfat_inode *child,
220			  struct exfat_inode **ancestors, int count,
221			  int max_char_len,
222			  int *ancestor_count)
223{
224	struct exfat_inode *dir;
225	int name_len, char_len;
226	int root_depth, depth, i;
227
228	root_depth = 0;
229	char_len = 0;
230	max_char_len += 1;
231
232	dir = child;
233	while (dir) {
234		name_len = exfat_utf16_len(dir->name, NAME_BUFFER_SIZE);
235		if (char_len + name_len > max_char_len)
236			break;
237
238		/* include '/' */
239		char_len += name_len + 1;
240		root_depth++;
241
242		dir = dir->parent;
243	}
244
245	depth = MIN(root_depth, count);
246
247	for (dir = child, i = depth - 1; i >= 0; dir = dir->parent, i--)
248		ancestors[i] = dir;
249
250	*ancestor_count = depth;
251	return !dir;
252}
253
254int exfat_resolve_path(struct path_resolve_ctx *ctx, struct exfat_inode *child)
255{
256	int depth, i;
257	int name_len;
258	__le16 *utf16_path;
259	static const __le16 utf16_slash = cpu_to_le16(0x002F);
260	static const __le16 utf16_null = cpu_to_le16(0x0000);
261	size_t in_size;
262
263	ctx->local_path[0] = '\0';
264
265	get_ancestors(child,
266		      ctx->ancestors,
267		      sizeof(ctx->ancestors) / sizeof(ctx->ancestors[0]),
268		      PATH_MAX,
269		      &depth);
270
271	utf16_path = ctx->utf16_path;
272	for (i = 0; i < depth; i++) {
273		name_len = exfat_utf16_len(ctx->ancestors[i]->name,
274					   NAME_BUFFER_SIZE);
275		memcpy((char *)utf16_path, (char *)ctx->ancestors[i]->name,
276		       name_len * 2);
277		utf16_path += name_len;
278		memcpy((char *)utf16_path, &utf16_slash, sizeof(utf16_slash));
279		utf16_path++;
280	}
281
282	if (depth > 1)
283		utf16_path--;
284	memcpy((char *)utf16_path, &utf16_null, sizeof(utf16_null));
285	utf16_path++;
286
287	in_size = (utf16_path - ctx->utf16_path) * sizeof(__le16);
288	return exfat_utf16_dec(ctx->utf16_path, in_size,
289				ctx->local_path, sizeof(ctx->local_path));
290}
291
292int exfat_resolve_path_parent(struct path_resolve_ctx *ctx,
293			      struct exfat_inode *parent, struct exfat_inode *child)
294{
295	int ret;
296	struct exfat_inode *old;
297
298	old = child->parent;
299	child->parent = parent;
300
301	ret = exfat_resolve_path(ctx, child);
302	child->parent = old;
303	return ret;
304}
305