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