OSDN Git Service

Add a mode to the open(2) call when creating the output file.
[android-x86/system-extras.git] / fatblock / import.c
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
32 static inline int valid_char(int c)
33 {
34         return (isalnum(c) ||
35                 strchr("!#$%'()-@^_`{}~", c) ||
36                 ((c >= 128) && (c < 256)));
37 }
38
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
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
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
175 static void free_item(struct item *item)
176 {
177         item->next = free_items_head;
178         free_items_head = item;
179 }
180
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. */
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
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 }