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