• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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