]> git.sven.stormbind.net Git - sven/exfatprogs.git/blob - lib/exfat_fs.c
b24f5321b9b9d88dcfb85d5d815d64e6d73c7449
[sven/exfatprogs.git] / lib / exfat_fs.c
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
19 struct 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 = 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
40 void 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
49 void 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
67 void 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  */
75 void 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
92 void 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
105 void 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->lookup_buffer)
121                         free(exfat->lookup_buffer);
122                 free(exfat);
123         }
124 }
125
126 struct exfat *exfat_alloc_exfat(struct exfat_blk_dev *blk_dev, struct pbr *bs)
127 {
128         struct exfat *exfat;
129
130         exfat = calloc(1, sizeof(*exfat));
131         if (!exfat) {
132                 free(bs);
133                 return NULL;
134         }
135
136         INIT_LIST_HEAD(&exfat->dir_list);
137         exfat->blk_dev = blk_dev;
138         exfat->bs = bs;
139         exfat->clus_count = le32_to_cpu(bs->bsx.clu_count);
140         exfat->clus_size = EXFAT_CLUSTER_SIZE(bs);
141         exfat->sect_size = EXFAT_SECTOR_SIZE(bs);
142
143         /* TODO: bitmap could be very large. */
144         exfat->alloc_bitmap = calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count));
145         if (!exfat->alloc_bitmap) {
146                 exfat_err("failed to allocate bitmap\n");
147                 goto err;
148         }
149
150         exfat->ohead_bitmap = 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 = calloc(1, EXFAT_BITMAP_SIZE(exfat->clus_count));
157         if (!exfat->disk_bitmap) {
158                 exfat_err("failed to allocate bitmap\n");
159                 goto err;
160         }
161
162         exfat->buffer_count = ((MAX_EXT_DENTRIES + 1) * DENTRY_SIZE) /
163                 exfat_get_read_size(exfat) + 1;
164
165         exfat->start_clu = EXFAT_FIRST_CLUSTER;
166         return exfat;
167 err:
168         exfat_free_exfat(exfat);
169         return NULL;
170 }
171
172 struct buffer_desc *exfat_alloc_buffer(struct exfat *exfat)
173 {
174         struct buffer_desc *bd;
175         unsigned int i;
176         unsigned int read_size = exfat_get_read_size(exfat);
177
178         bd = calloc(exfat->buffer_count, sizeof(*bd));
179         if (!bd)
180                 return NULL;
181
182         for (i = 0; i < exfat->buffer_count; i++) {
183                 bd[i].buffer = malloc(read_size);
184                 if (!bd[i].buffer)
185                         goto err;
186
187                 memset(&bd[i].dirty, 0, sizeof(bd[i].dirty));
188         }
189         return bd;
190 err:
191         exfat_free_buffer(exfat, bd);
192         return NULL;
193 }
194
195 void exfat_free_buffer(const struct exfat *exfat, struct buffer_desc *bd)
196 {
197         unsigned int i;
198
199         for (i = 0; i < exfat->buffer_count; i++) {
200                 if (bd[i].buffer)
201                         free(bd[i].buffer);
202         }
203         free(bd);
204 }
205
206 /*
207  * get references of ancestors that include @child until the count of
208  * ancesters is not larger than @count and the count of characters of
209  * their names is not larger than @max_char_len.
210  * return true if root is reached.
211  */
212 static bool get_ancestors(struct exfat_inode *child,
213                           struct exfat_inode **ancestors, int count,
214                           int max_char_len,
215                           int *ancestor_count)
216 {
217         struct exfat_inode *dir;
218         int name_len, char_len;
219         int root_depth, depth, i;
220
221         root_depth = 0;
222         char_len = 0;
223         max_char_len += 1;
224
225         dir = child;
226         while (dir) {
227                 name_len = exfat_utf16_len(dir->name, NAME_BUFFER_SIZE);
228                 if (char_len + name_len > max_char_len)
229                         break;
230
231                 /* include '/' */
232                 char_len += name_len + 1;
233                 root_depth++;
234
235                 dir = dir->parent;
236         }
237
238         depth = MIN(root_depth, count);
239
240         for (dir = child, i = depth - 1; i >= 0; dir = dir->parent, i--)
241                 ancestors[i] = dir;
242
243         *ancestor_count = depth;
244         return !dir;
245 }
246
247 int exfat_resolve_path(struct path_resolve_ctx *ctx, struct exfat_inode *child)
248 {
249         int depth, i;
250         int name_len;
251         __le16 *utf16_path;
252         static const __le16 utf16_slash = cpu_to_le16(0x002F);
253         static const __le16 utf16_null = cpu_to_le16(0x0000);
254         size_t in_size;
255
256         ctx->local_path[0] = '\0';
257
258         get_ancestors(child,
259                       ctx->ancestors,
260                       sizeof(ctx->ancestors) / sizeof(ctx->ancestors[0]),
261                       PATH_MAX,
262                       &depth);
263
264         utf16_path = ctx->utf16_path;
265         for (i = 0; i < depth; i++) {
266                 name_len = exfat_utf16_len(ctx->ancestors[i]->name,
267                                            NAME_BUFFER_SIZE);
268                 memcpy((char *)utf16_path, (char *)ctx->ancestors[i]->name,
269                        name_len * 2);
270                 utf16_path += name_len;
271                 memcpy((char *)utf16_path, &utf16_slash, sizeof(utf16_slash));
272                 utf16_path++;
273         }
274
275         if (depth > 1)
276                 utf16_path--;
277         memcpy((char *)utf16_path, &utf16_null, sizeof(utf16_null));
278         utf16_path++;
279
280         in_size = (utf16_path - ctx->utf16_path) * sizeof(__le16);
281         return exfat_utf16_dec(ctx->utf16_path, in_size,
282                                 ctx->local_path, sizeof(ctx->local_path));
283 }
284
285 int exfat_resolve_path_parent(struct path_resolve_ctx *ctx,
286                               struct exfat_inode *parent, struct exfat_inode *child)
287 {
288         int ret;
289         struct exfat_inode *old;
290
291         old = child->parent;
292         child->parent = parent;
293
294         ret = exfat_resolve_path(ctx, child);
295         child->parent = old;
296         return ret;
297 }