1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <assert.h>
18 #include <ctype.h>
19 #include <dirent.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/stat.h>
25
26 #include "fatblock.h"
27 #include "fat.h"
28 #include "fdpool.h"
29 #include "fs.h"
30 #include "utils.h"
31
valid_char(int c)32 static inline int valid_char(int c)
33 {
34 return (isalnum(c) ||
35 strchr("!#$%'()-@^_`{}~", c) ||
36 ((c >= 128) && (c < 256)));
37 }
38
convert_name(char * short_name,const char * long_name)39 static int convert_name(char *short_name, const char *long_name)
40 {
41 int i;
42
43 const char *s;
44 const char *dot;
45 int c;
46
47 dot = NULL;
48
49 for (s = long_name; *s; s++) {
50 if (*s == '.') {
51 if (dot) {
52 goto short_fail;
53 } else {
54 dot = s;
55 }
56 } else if (!valid_char(*s)) {
57 goto short_fail;
58 }
59 }
60
61 if (dot - long_name > 8) {
62 goto short_fail;
63 }
64
65 if (dot && (s - (dot + 1) > 3)) {
66 goto short_fail;
67 }
68
69 memset(short_name, ' ', 11);
70
71 if (!dot) {
72 dot = s;
73 }
74
75 for (i = 0; i < dot - long_name; i++) {
76 short_name[i] = toupper(long_name[i]);
77 }
78
79 for (i = 0; i < s - dot; i++) {
80 short_name[8 + i] = toupper(dot[1 + i]);
81 }
82
83 return 0;
84
85 short_fail:
86 return 1;
87 }
88
89 struct imported {
90 cluster_t first_cluster;
91 uint32_t size;
92 struct fat_dirent *dot_dot_dirent;
93 };
94
import_file(struct fs * fs,char * path,struct imported * out)95 static int import_file(struct fs *fs, char *path, struct imported *out)
96 {
97 struct stat st;
98 struct file *f = NULL;
99 char *path_copy = NULL;
100 int ret;
101
102 ret = stat(path, &st);
103 if (ret < 0) {
104 WARN("importing %s: stat failed: %s\n", path, strerror(errno));
105 goto fail;
106 }
107
108 f = malloc(sizeof(struct file));
109 if (!f) {
110 WARN("importing %s: couldn't allocate file struct: "
111 "out of memory\n", path);
112 ret = MALLOC_FAIL;
113 goto fail;
114 }
115
116 path_copy = strdup(path);
117 if (!path_copy) {
118 WARN("importing %s: couldn't strdup path: out of memory\n",
119 path);
120 ret = MALLOC_FAIL;
121 goto fail;
122 }
123
124 f->path = path_copy;
125 f->size = st.st_size;
126 f->dev = st.st_dev;
127 f->ino = st.st_ino;
128 f->mtime = st.st_mtime;
129 fdpool_init(&f->pfd);
130
131 ret = fs_alloc_extent(fs, &f->extent,
132 f->size, EXTENT_TYPE_FILE, &out->first_cluster);
133 if (ret) {
134 WARN("importing %s: couldn't allocate data extent\n", path);
135 goto fail;
136 }
137
138 out->size = f->size;
139 out->dot_dot_dirent = NULL;
140
141 return 0;
142
143 fail:
144 if (path_copy)
145 free(path_copy);
146 if (f)
147 free(f);
148 return ret;
149 }
150
151 struct item {
152 char name[11];
153 struct imported imp;
154 struct item *next;
155 int is_dir;
156 };
157
158 static struct item *free_items_head;
159
alloc_item(void)160 static struct item *alloc_item(void)
161 {
162 struct item *item;
163
164 if (free_items_head) {
165 item = free_items_head;
166 free_items_head = item->next;
167 } else {
168 item = malloc(sizeof(struct item));
169 /* May return NULL if item couldn't be allocated. */
170 }
171
172 return item;
173 }
174
free_item(struct item * item)175 static void free_item(struct item *item)
176 {
177 item->next = free_items_head;
178 free_items_head = item;
179 }
180
free_items(struct item * head)181 static void free_items(struct item *head)
182 {
183 struct item *tail;
184
185 for (tail = head; tail->next; tail = tail->next);
186
187 tail->next = free_items_head;
188 free_items_head = head;
189 }
190
191 /* TODO: With some work, this can be rewritten so we don't recurse
192 * until all memory is allocated. */
import_dir(struct fs * fs,char * path,int is_root,struct imported * out)193 static int import_dir(struct fs *fs, char *path, int is_root,
194 struct imported *out)
195 {
196 struct dir *d;
197 cluster_t my_first_cluster;
198
199 DIR *dir;
200 struct dirent *de;
201
202 char ch_path[PATH_MAX];
203 struct imported *ch_imp;
204 cluster_t ch_first_cluster;
205 struct fat_dirent *ch_dirent;
206
207 int ret;
208
209 struct item *items;
210 struct item *item;
211 int count;
212
213 int i;
214
215 dir = opendir(path);
216 if (!dir) {
217 WARN("importing %s: opendir failed: %s\n", path,
218 strerror(errno));
219 return -1;
220 }
221
222 d = malloc(sizeof(struct dir));
223 if (!d) {
224 WARN("importing %s: couldn't allocate dir struct: "
225 "out of memory\n", path);
226 closedir(dir);
227 return MALLOC_FAIL;
228 }
229
230 d->path = strdup(path);
231 if (!d->path) {
232 WARN("importing %s: couldn't strdup path: out of memory\n",
233 path);
234 closedir(dir);
235 free(d);
236 return MALLOC_FAIL;
237 }
238
239 items = NULL;
240 item = NULL;
241 count = 0;
242
243 while ((de = readdir(dir))) {
244 if (de->d_name[0] == '.') {
245 goto skip_item;
246 }
247
248 ret = snprintf(ch_path, PATH_MAX, "%s/%s", path, de->d_name);
249 if (ret < 0 || ret >= PATH_MAX) {
250 goto skip_item;
251 }
252
253 item = alloc_item();
254 if (!item) {
255 WARN("importing %s: couldn't allocate item struct: "
256 "out of memory\n", path);
257 ret = MALLOC_FAIL;
258 goto free_items;
259 }
260
261 if (convert_name(item->name, de->d_name)) {
262 goto skip_item;
263 }
264
265 switch (de->d_type) {
266 case DT_REG:
267 import_file(fs, ch_path, &item->imp);
268 item->is_dir = 0;
269 break;
270 case DT_DIR:
271 import_dir(fs, ch_path, 0, &item->imp);
272 item->is_dir = 1;
273 break;
274 default:
275 goto skip_item;
276 }
277
278 item->next = items;
279 items = item;
280
281 count++;
282
283 item = NULL;
284
285 continue;
286
287 skip_item:
288 if (item)
289 free_item(item);
290 }
291
292 closedir(dir);
293
294 d->size = sizeof(struct fat_dirent) * (count + (is_root ? 0 : 2));
295 ret = fs_alloc_extent(fs, &d->extent, d->size, EXTENT_TYPE_DIR, &out->first_cluster);
296 if (ret) {
297 WARN("importing %s: couldn't allocate directory table extent: "
298 "out of space\n", path);
299 goto free_items;
300 }
301
302 if (is_root)
303 out->first_cluster = 0;
304
305 my_first_cluster = is_root ? 0 : out->first_cluster;
306
307 d->entries = malloc(sizeof(struct fat_dirent) * (count + (is_root ? 0 : 2)));
308 assert(d->entries);
309 for (i = count - 1; i >= 0; i--) {
310 item = items;
311 items = item->next;
312
313 ch_dirent = &d->entries[i + (is_root ? 0 : 2)];
314
315 fat_dirent_set(ch_dirent,
316 item->name, item->is_dir ? FAT_ATTR_SUBDIR : 0,
317 item->imp.first_cluster, item->imp.size);
318
319 if (item->imp.dot_dot_dirent) {
320 fat_dirent_set_first_cluster(item->imp.dot_dot_dirent,
321 my_first_cluster);
322 }
323
324 free_item(item);
325 }
326
327 if (!is_root) {
328 fat_dirent_set(&d->entries[0],
329 ".. ", FAT_ATTR_SUBDIR,
330 (cluster_t)-1, 0);
331 out->dot_dot_dirent = &d->entries[0]; /* will set first_cluster */
332
333 fat_dirent_set(&d->entries[1],
334 ". ", FAT_ATTR_SUBDIR,
335 my_first_cluster, 0);
336 } else {
337 out->dot_dot_dirent = NULL;
338 }
339
340 out->size = 0;
341
342 return 0;
343
344 free_items:
345 free_items(items);
346 free(d->path);
347 free(d);
348
349 return ret;
350 }
351
import_tree(struct fs * fs,char * path)352 int import_tree(struct fs *fs, char *path)
353 {
354 struct imported imp;
355 int ret;
356
357 ret = import_dir(fs, path, 0, &imp);
358 if (ret)
359 return ret;
360
361 fs_set_rootdir_start(fs, imp.first_cluster);
362 fs_update_free_clusters(fs);
363
364 return 0;
365 }
366