/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <assert.h> #include <ctype.h> #include <dirent.h> #include <errno.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include "fatblock.h" #include "fat.h" #include "fdpool.h" #include "fs.h" #include "utils.h" static inline int valid_char(int c) { return (isalnum(c) || strchr("!#$%'()-@^_`{}~", c) || ((c >= 128) && (c < 256))); } static int convert_name(char *short_name, const char *long_name) { int i; const char *s; const char *dot; int c; dot = NULL; for (s = long_name; *s; s++) { if (*s == '.') { if (dot) { goto short_fail; } else { dot = s; } } else if (!valid_char(*s)) { goto short_fail; } } if (dot - long_name > 8) { goto short_fail; } if (dot && (s - (dot + 1) > 3)) { goto short_fail; } memset(short_name, ' ', 11); if (!dot) { dot = s; } for (i = 0; i < dot - long_name; i++) { short_name[i] = toupper(long_name[i]); } for (i = 0; i < s - dot; i++) { short_name[8 + i] = toupper(dot[1 + i]); } return 0; short_fail: return 1; } struct imported { cluster_t first_cluster; uint32_t size; struct fat_dirent *dot_dot_dirent; }; static int import_file(struct fs *fs, char *path, struct imported *out) { struct stat st; struct file *f = NULL; char *path_copy = NULL; int ret; ret = stat(path, &st); if (ret < 0) { WARN("importing %s: stat failed: %s\n", path, strerror(errno)); goto fail; } f = malloc(sizeof(struct file)); if (!f) { WARN("importing %s: couldn't allocate file struct: " "out of memory\n", path); ret = MALLOC_FAIL; goto fail; } path_copy = strdup(path); if (!path_copy) { WARN("importing %s: couldn't strdup path: out of memory\n", path); ret = MALLOC_FAIL; goto fail; } f->path = path_copy; f->size = st.st_size; f->dev = st.st_dev; f->ino = st.st_ino; f->mtime = st.st_mtime; fdpool_init(&f->pfd); ret = fs_alloc_extent(fs, &f->extent, f->size, EXTENT_TYPE_FILE, &out->first_cluster); if (ret) { WARN("importing %s: couldn't allocate data extent\n", path); goto fail; } out->size = f->size; out->dot_dot_dirent = NULL; return 0; fail: if (path_copy) free(path_copy); if (f) free(f); return ret; } struct item { char name[11]; struct imported imp; struct item *next; int is_dir; }; static struct item *free_items_head; static struct item *alloc_item(void) { struct item *item; if (free_items_head) { item = free_items_head; free_items_head = item->next; } else { item = malloc(sizeof(struct item)); /* May return NULL if item couldn't be allocated. */ } return item; } static void free_item(struct item *item) { item->next = free_items_head; free_items_head = item; } static void free_items(struct item *head) { struct item *tail; for (tail = head; tail->next; tail = tail->next); tail->next = free_items_head; free_items_head = head; } /* TODO: With some work, this can be rewritten so we don't recurse * until all memory is allocated. */ static int import_dir(struct fs *fs, char *path, int is_root, struct imported *out) { struct dir *d; cluster_t my_first_cluster; DIR *dir; struct dirent *de; char ch_path[PATH_MAX]; struct imported *ch_imp; cluster_t ch_first_cluster; struct fat_dirent *ch_dirent; int ret; struct item *items; struct item *item; int count; int i; dir = opendir(path); if (!dir) { WARN("importing %s: opendir failed: %s\n", path, strerror(errno)); return -1; } d = malloc(sizeof(struct dir)); if (!d) { WARN("importing %s: couldn't allocate dir struct: " "out of memory\n", path); closedir(dir); return MALLOC_FAIL; } d->path = strdup(path); if (!d->path) { WARN("importing %s: couldn't strdup path: out of memory\n", path); closedir(dir); free(d); return MALLOC_FAIL; } items = NULL; item = NULL; count = 0; while ((de = readdir(dir))) { if (de->d_name[0] == '.') { goto skip_item; } ret = snprintf(ch_path, PATH_MAX, "%s/%s", path, de->d_name); if (ret < 0 || ret >= PATH_MAX) { goto skip_item; } item = alloc_item(); if (!item) { WARN("importing %s: couldn't allocate item struct: " "out of memory\n", path); ret = MALLOC_FAIL; goto free_items; } if (convert_name(item->name, de->d_name)) { goto skip_item; } switch (de->d_type) { case DT_REG: import_file(fs, ch_path, &item->imp); item->is_dir = 0; break; case DT_DIR: import_dir(fs, ch_path, 0, &item->imp); item->is_dir = 1; break; default: goto skip_item; } item->next = items; items = item; count++; item = NULL; continue; skip_item: if (item) free_item(item); } closedir(dir); d->size = sizeof(struct fat_dirent) * (count + (is_root ? 0 : 2)); ret = fs_alloc_extent(fs, &d->extent, d->size, EXTENT_TYPE_DIR, &out->first_cluster); if (ret) { WARN("importing %s: couldn't allocate directory table extent: " "out of space\n", path); goto free_items; } if (is_root) out->first_cluster = 0; my_first_cluster = is_root ? 0 : out->first_cluster; d->entries = malloc(sizeof(struct fat_dirent) * (count + (is_root ? 0 : 2))); assert(d->entries); for (i = count - 1; i >= 0; i--) { item = items; items = item->next; ch_dirent = &d->entries[i + (is_root ? 0 : 2)]; fat_dirent_set(ch_dirent, item->name, item->is_dir ? FAT_ATTR_SUBDIR : 0, item->imp.first_cluster, item->imp.size); if (item->imp.dot_dot_dirent) { fat_dirent_set_first_cluster(item->imp.dot_dot_dirent, my_first_cluster); } free_item(item); } if (!is_root) { fat_dirent_set(&d->entries[0], ".. ", FAT_ATTR_SUBDIR, (cluster_t)-1, 0); out->dot_dot_dirent = &d->entries[0]; /* will set first_cluster */ fat_dirent_set(&d->entries[1], ". ", FAT_ATTR_SUBDIR, my_first_cluster, 0); } else { out->dot_dot_dirent = NULL; } out->size = 0; return 0; free_items: free_items(items); free(d->path); free(d); return ret; } int import_tree(struct fs *fs, char *path) { struct imported imp; int ret; ret = import_dir(fs, path, 0, &imp); if (ret) return ret; fs_set_rootdir_start(fs, imp.first_cluster); fs_update_free_clusters(fs); return 0; }