Home | History | Annotate | Download | only in fatblock
      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 }
    366