import.c revision f79b2dff1024db4f6326f3422236bed169dd902f
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 "errors.h"
27#include "extent.h"
28#include "fat.h"
29#include "fdpool.h"
30#include "filedir.h"
31#include "fs.h"
32#include "import.h"
33#include "utils.h"
34
35static inline int valid_char(int c)
36{
37	return (isalnum(c) || strchr("!#$%'()-@^_`{}~", c) || ((c >= 128) && (c < 256)));
38}
39
40static int convert_name(char *short_name, const char *long_name)
41{
42	int i;
43
44	const char *s;
45	const char *dot;
46	int c;
47
48	dot = NULL;
49
50	for (s = long_name; *s; s++) {
51		if (*s == '.') {
52			if (dot) {
53				goto short_fail;
54			} else {
55				dot = s;
56			}
57		} else if (!valid_char(*s)) {
58			goto short_fail;
59		}
60	}
61
62	if (dot - long_name > 8) {
63		goto short_fail;
64	}
65
66	if (dot && (s - (dot + 1) > 3)) {
67		goto short_fail;
68	}
69
70	memset(short_name, ' ', 11);
71
72	if (!dot) {
73		dot = s;
74	}
75
76	for (i = 0; i < dot - long_name; i++) {
77		short_name[i] = toupper(long_name[i]);
78	}
79
80	for (i = 0; i < s - dot; i++) {
81		short_name[8 + i] = toupper(dot[1 + i]);
82	}
83
84	return 0;
85
86short_fail:
87	return 1;
88}
89
90struct imported {
91	cluster_t first_cluster;
92	uint32_t size;
93	struct fat_dirent *dot_dot_dirent;
94};
95
96static int import_file(struct fs *fs, char *path, struct imported *out)
97{
98	struct stat st;
99	struct file *f = NULL;
100	char *path_copy = NULL;
101	int ret;
102
103	ret = stat(path, &st);
104	if (ret < 0) {
105		WARN("importing %s: stat failed: %s\n", path, strerror(errno));
106		goto fail;
107	}
108
109	f = malloc(sizeof(struct file));
110	if (!f) {
111		WARN("importing %s: couldn't allocate file struct: 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", path);
119		ret = MALLOC_FAIL;
120		goto fail;
121	}
122
123	f->path = path_copy;
124	f->size = st.st_size;
125	f->dev = st.st_dev;
126	f->ino = st.st_ino;
127	f->mtime = st.st_mtime;
128	fdpool_init(&f->pfd);
129
130	ret = fs_alloc_extent(fs, &f->extent,
131                              f->size, EXTENT_TYPE_FILE, &f->first_cluster);
132	if (ret) {
133		WARN("importing %s: couldn't allocate data extent\n", path);
134		goto fail;
135	}
136
137	out->first_cluster = f->first_cluster;
138	out->size = f->size;
139	out->dot_dot_dirent = NULL;
140
141	return 0;
142
143fail:
144	if (path_copy)
145		free(path_copy);
146	if (f)
147		free(f);
148	return ret;
149}
150
151struct item {
152	char name[11];
153	struct imported imp;
154	struct item *next;
155	int is_dir;
156};
157
158static struct item *free_items_head;
159
160static 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
175static void free_item(struct item *item)
176{
177	item->next = free_items_head;
178	free_items_head = item;
179}
180
181static 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. */
193static int import_dir(struct fs *fs, char *path, int is_root, struct imported *out)
194{
195	struct dir *d;
196	cluster_t first_cluster;
197
198	DIR *dir;
199	struct dirent *de;
200
201	char ch_path[PATH_MAX];
202	struct imported *ch_imp;
203	cluster_t ch_first_cluster;
204	struct fat_dirent *ch_dirent;
205
206	int ret;
207
208	struct item *items;
209	struct item *item;
210	int count;
211
212	int i;
213
214	dir = opendir(path);
215	if (!dir) {
216		WARN("importing %s: opendir failed: %s\n", path, strerror(errno));
217		return -1;
218	}
219
220	d = malloc(sizeof(struct dir));
221	if (!d) {
222		WARN("importing %s: couldn't allocate dir struct: out of memory\n", path);
223		closedir(dir);
224		return MALLOC_FAIL;
225	}
226
227	d->path = strdup(path);
228	if (!d->path) {
229		WARN("importing %s: couldn't strdup path: out of memory\n", path);
230		closedir(dir);
231		free(d);
232		return MALLOC_FAIL;
233	}
234
235	items = NULL;
236	item = NULL;
237	count = 0;
238
239	while ((de = readdir(dir))) {
240		if (de->d_name[0] == '.') {
241			goto skip_item;
242		}
243
244		ret = snprintf(ch_path, PATH_MAX, "%s/%s", path, de->d_name);
245		if (ret < 0 || ret >= PATH_MAX) {
246			goto skip_item;
247		}
248
249		item = alloc_item();
250		if (!item) {
251			WARN("importing %s: couldn't allocate item struct: out of memory\n", path);
252			ret = MALLOC_FAIL;
253			goto free_items;
254		}
255
256		if (convert_name(item->name, de->d_name)) {
257			goto skip_item;
258		}
259
260		switch (de->d_type) {
261			case DT_REG:
262				import_file(fs, ch_path, &item->imp);
263				item->is_dir = 0;
264				break;
265			case DT_DIR:
266				import_dir(fs, ch_path, 0, &item->imp);
267				item->is_dir = 1;
268				break;
269			default:
270				goto skip_item;
271		}
272
273		item->next = items;
274		items = item;
275
276		count++;
277
278		item = NULL;
279
280		continue;
281
282skip_item:
283		if (item)
284			free_item(item);
285	}
286
287	closedir(dir);
288
289	d->size = sizeof(struct fat_dirent) * (count + (is_root ? 0 : 2));
290	ret = fs_alloc_extent(fs, &d->extent, d->size, EXTENT_TYPE_DIR, &d->first_cluster);
291	if (ret) {
292		WARN("importing %s: couldn't allocate directory table extent: out of space\n", path);
293		goto free_items;
294	}
295
296	first_cluster = is_root ? 0 : d->first_cluster;
297
298	d->entries = malloc(sizeof(struct fat_dirent) * (count + (is_root ? 0 : 2)));
299	assert(d->entries);
300	for (i = count - 1; i >= 0; i--) {
301		item = items;
302		items = item->next;
303
304		ch_dirent = &d->entries[i + (is_root ? 0 : 2)];
305
306		fat_dirent_set(ch_dirent,
307                               item->name, item->is_dir ? FAT_ATTR_SUBDIR : 0,
308                               item->imp.first_cluster, item->imp.size);
309
310		if (item->imp.dot_dot_dirent) {
311			fat_dirent_set_first_cluster(item->imp.dot_dot_dirent, first_cluster);
312		}
313
314		free_item(item);
315	}
316
317	if (!is_root) {
318		fat_dirent_set(&d->entries[0],
319                               "..         ", FAT_ATTR_SUBDIR,
320                               (cluster_t)-1, 0);
321		out->dot_dot_dirent = &d->entries[0]; /* will set first_cluster */
322
323		fat_dirent_set(&d->entries[1],
324                               ".          ", FAT_ATTR_SUBDIR,
325                               first_cluster, 0);
326	} else {
327		out->dot_dot_dirent = NULL;
328	}
329
330	out->first_cluster = d->first_cluster;
331	out->size = 0;
332
333	return 0;
334
335free_items:
336	free_items(items);
337	free(d->path);
338	free(d);
339
340	return ret;
341}
342
343int import_tree(struct fs *fs, char *path)
344{
345	struct imported imp;
346	int ret;
347
348	ret = import_dir(fs, path, 0, &imp);
349	if (ret)
350		return ret;
351
352	fs_set_rootdir_start(fs, imp.first_cluster);
353	fs_update_free_clusters(fs);
354
355	return 0;
356}
357