]> git.sven.stormbind.net Git - sven/exfatprogs.git/blobdiff - fsck/fsck.c
New upstream version 1.2.2
[sven/exfatprogs.git] / fsck / fsck.c
index cd9ee9a193e344af59482af52256cbe98b4c5896..77272aa0bd991c0e1f9ae1b537e22a89eb4d7fa3 100644 (file)
@@ -103,31 +103,21 @@ static void usage(char *name)
 })
 
 static int check_clus_chain(struct exfat_de_iter *de_iter,
-                               struct exfat_inode *node)
+                           struct exfat_inode *node)
 {
        struct exfat *exfat = de_iter->exfat;
        struct exfat_dentry *stream_de;
-       clus_t clus, prev, next, new_clus;
+       clus_t clus, prev, next;
        uint64_t count, max_count;
-       int err;
 
        clus = node->first_clus;
        prev = EXFAT_EOF_CLUSTER;
        count = 0;
        max_count = DIV_ROUND_UP(node->size, exfat->clus_size);
 
-       if (node->size == 0 && node->first_clus == EXFAT_FREE_CLUSTER) {
-               /* locate a cluster for the empty dir if the dir starts with EXFAT_FREE_CLUSTER */
-               if (node->attr & ATTR_SUBDIR) {
-                       if (repair_file_ask(de_iter, node,
-                                       ER_DE_FIRST_CLUS,
-                                       "size %#" PRIx64 ", but the first cluster %#x",
-                                       node->size, node->first_clus))
-                               goto allocate_cluster;
-                       return -EINVAL;
-               }
+       if (node->size == 0 && node->first_clus == EXFAT_FREE_CLUSTER)
                return 0;
-       }
+
        /* the first cluster is wrong */
        if ((node->size == 0 && node->first_clus != EXFAT_FREE_CLUSTER) ||
            (node->size > 0 && !exfat_heap_clus(exfat, node->first_clus))) {
@@ -225,38 +215,6 @@ static int check_clus_chain(struct exfat_de_iter *de_iter,
        }
 
        return 0;
-allocate_cluster:
-       exfat_de_iter_get_dirty(de_iter, 1, &stream_de);
-       err = exfat_find_free_cluster(exfat, exfat->start_clu, &new_clus);
-       if (err) {
-               exfat->start_clu = EXFAT_FIRST_CLUSTER;
-               exfat_err("failed to find a free cluster\n");
-               return -ENOSPC;
-       }
-       exfat->start_clu = new_clus;
-
-       if (exfat_set_fat(exfat, new_clus, EXFAT_EOF_CLUSTER))
-               return -EIO;
-
-       /* zero out the new cluster */
-       if (exfat_write(exfat->blk_dev->dev_fd, exfat->zero_cluster,
-                       exfat->clus_size, exfat_c2o(exfat, new_clus)) !=
-                       (ssize_t)exfat->clus_size) {
-               exfat_err("failed to fill new cluster with zeroes\n");
-               return -EIO;
-       }
-
-       /* modify the number of cluster form 0 to 1 */
-       count = 1;
-       stream_de->stream_start_clu = cpu_to_le32(new_clus);
-       stream_de->stream_size = cpu_to_le64(count * exfat->clus_size);
-       stream_de->stream_valid_size = cpu_to_le64(count * exfat->clus_size);
-       stream_de->dentry.stream.flags |= EXFAT_SF_CONTIGUOUS;
-       node->first_clus = new_clus;
-       node->size = count * exfat->clus_size;
-       node->is_contiguous = true;
-       exfat_bitmap_set(exfat->alloc_bitmap, new_clus);
-       return 1;
 truncate_file:
        node->size = count * exfat->clus_size;
        if (!exfat_heap_clus(exfat, prev))
@@ -677,6 +635,35 @@ static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node)
        return valid ? ret : -EINVAL;
 }
 
+static int handle_duplicated_filename(struct exfat_de_iter *iter,
+               struct exfat_inode *inode)
+{
+       int ret;
+       struct exfat_lookup_filter filter;
+       char filename[PATH_MAX + 1] = {0};
+
+       ret = exfat_lookup_file_by_utf16name(iter->exfat, iter->parent,
+                       inode->name, &filter);
+       if (ret)
+               return ret;
+
+       free(filter.out.dentry_set);
+
+       /* Hash is same, but filename is not same */
+       if (exfat_de_iter_device_offset(iter) == filter.out.dev_offset)
+               return 0;
+
+       ret = exfat_utf16_dec(inode->name, NAME_BUFFER_SIZE, filename,
+                       PATH_MAX);
+       if (ret < 0) {
+               exfat_err("failed to decode filename\n");
+               return ret;
+       }
+
+       return exfat_repair_rename_ask(&exfat_fsck, iter, filename,
+                       ER_DE_DUPLICATED_NAME, "filename is duplicated");
+}
+
 static int check_name_dentry_set(struct exfat_de_iter *iter,
                                 struct exfat_inode *inode)
 {
@@ -707,77 +694,16 @@ static int check_name_dentry_set(struct exfat_de_iter *iter,
                        return -EINVAL;
                }
        }
-       return 0;
-}
 
-static int check_bad_char(char w)
-{
-       return (w < 0x0020) || (w == '*') || (w == '?') || (w == '<') ||
-               (w == '>') || (w == '|') || (w == '"') || (w == ':') ||
-               (w == '/') || (w == '\\');
-}
+       if (BITMAP_GET(iter->name_hash_bitmap, hash)) {
+               int ret = handle_duplicated_filename(iter, inode);
 
-static char *get_rename_from_user(struct exfat_de_iter *iter)
-{
-       char *rename = malloc(ENTRY_NAME_MAX + 2);
-
-       if (!rename)
-               return NULL;
-
-retry:
-       /* +2 means LF(Line Feed) and NULL terminator */
-       memset(rename, 0x1, ENTRY_NAME_MAX + 2);
-       printf("New name: ");
-       if (fgets(rename, ENTRY_NAME_MAX + 2, stdin)) {
-               int i, len, err;
-               struct exfat_lookup_filter filter;
-
-               len = strlen(rename);
-               /* Remove LF in filename */
-               rename[len - 1] = '\0';
-               for (i = 0; i < len - 1; i++) {
-                       if (check_bad_char(rename[i])) {
-                               printf("filename contain invalid character(%c)\n", rename[i]);
-                               goto retry;
-                       }
-               }
-
-               exfat_de_iter_flush(iter);
-               err = exfat_lookup_file(iter->exfat, iter->parent, rename, &filter);
-               if (!err) {
-                       printf("file(%s) already exists, retry to insert name\n", rename);
-                       goto retry;
-               }
-       }
-
-       return rename;
-}
-
-static char *generate_rename(struct exfat_de_iter *iter)
-{
-       char *rename;
-
-       if (iter->dot_name_num > DOT_NAME_NUM_MAX)
-               return NULL;
-
-       rename = malloc(ENTRY_NAME_MAX + 1);
-       if (!rename)
-               return NULL;
-
-       while (1) {
-               struct exfat_lookup_filter filter;
-               int err;
-
-               snprintf(rename, ENTRY_NAME_MAX + 1, "FILE%07d.CHK",
-                        iter->dot_name_num++);
-               err = exfat_lookup_file(iter->exfat, iter->parent, rename,
-                                       &filter);
-               if (!err)
-                       continue;
-               break;
-       }
+               if (ret)
+                       return ret;
+       } else
+               BITMAP_SET(iter->name_hash_bitmap, hash);
 
-       return rename;
+       return 0;
 }
 
 const __le16 MSDOS_DOT[ENTRY_NAME_MAX] = {cpu_to_le16(46), 0, };
@@ -788,8 +714,6 @@ static int handle_dot_dotdot_filename(struct exfat_de_iter *iter,
                                      int strm_name_len)
 {
        char *filename;
-       char error_msg[150];
-       int num;
 
        if (!memcmp(dentry->name_unicode, MSDOS_DOT, strm_name_len * 2))
                filename = ".";
@@ -799,56 +723,8 @@ static int handle_dot_dotdot_filename(struct exfat_de_iter *iter,
        else
                return 0;
 
-       sprintf(error_msg, "ERROR: '%s' filename is not allowed.\n"
-                       " [1] Insert the name you want to rename.\n"
-                       " [2] Automatically renames filename.\n"
-                       " [3] Bypass this check(No repair)\n", filename);
-ask_again:
-       num = exfat_repair_ask(&exfat_fsck, ER_DE_DOT_NAME,
-                              error_msg);
-       if (num) {
-               __le16 utf16_name[ENTRY_NAME_MAX];
-               char *rename = NULL;
-               __u16 hash;
-               struct exfat_dentry *stream_de;
-               int name_len, ret;
-
-               switch (num) {
-               case 1:
-                       rename = get_rename_from_user(iter);
-                       break;
-               case 2:
-                       rename = generate_rename(iter);
-                       break;
-               case 3:
-                       break;
-               default:
-                       exfat_info("select 1 or 2 number instead of %d\n", num);
-                       goto ask_again;
-               }
-
-               if (!rename)
-                       return -EINVAL;
-
-               exfat_info("%s filename is renamed to %s\n", filename, rename);
-
-               exfat_de_iter_get_dirty(iter, 2, &dentry);
-
-               memset(utf16_name, 0, sizeof(utf16_name));
-               ret = exfat_utf16_enc(rename, utf16_name, sizeof(utf16_name));
-               free(rename);
-               if (ret < 0)
-                       return ret;
-
-               memcpy(dentry->name_unicode, utf16_name, ENTRY_NAME_MAX * 2);
-               name_len = exfat_utf16_len(utf16_name, ENTRY_NAME_MAX * 2);
-               hash = exfat_calc_name_hash(iter->exfat, utf16_name, (int)name_len);
-               exfat_de_iter_get_dirty(iter, 1, &stream_de);
-               stream_de->stream_name_len = (__u8)name_len;
-               stream_de->stream_name_hash = cpu_to_le16(hash);
-       }
-
-       return 0;
+       return exfat_repair_rename_ask(&exfat_fsck, iter, filename,
+                       ER_DE_DOT_NAME, "filename is not allowed");
 }
 
 static int read_file_dentry_set(struct exfat_de_iter *iter,
@@ -898,7 +774,7 @@ static int read_file_dentry_set(struct exfat_de_iter *iter,
        if (!node)
                return -ENOMEM;
 
-       for (i = 2; i <= file_de->file_num_ext; i++) {
+       for (i = 2; i <= MIN(file_de->file_num_ext, 1 + MAX_NAME_DENTRIES); i++) {
                ret = exfat_de_iter_get(iter, i, &dentry);
                if (ret || dentry->type != EXFAT_NAME) {
                        if (i > 2 && repair_file_ask(iter, NULL, ER_DE_NAME,
@@ -1004,6 +880,7 @@ static int read_bitmap(struct exfat *exfat)
 {
        struct exfat_lookup_filter filter = {
                .in.type        = EXFAT_BITMAP,
+               .in.dentry_count = 0,
                .in.filter      = NULL,
                .in.param       = NULL,
        };
@@ -1078,6 +955,7 @@ static int read_upcase_table(struct exfat *exfat)
 {
        struct exfat_lookup_filter filter = {
                .in.type        = EXFAT_UPCASE,
+               .in.dentry_count = 0,
                .in.filter      = NULL,
                .in.param       = NULL,
        };
@@ -1171,6 +1049,10 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir)
        else if (ret)
                return ret;
 
+       de_iter->name_hash_bitmap = fsck->name_hash_bitmap;
+       memset(fsck->name_hash_bitmap, 0,
+                       EXFAT_BITMAP_SIZE(EXFAT_MAX_HASH_COUNT));
+
        while (1) {
                ret = exfat_de_iter_get(de_iter, 0, &dentry);
                if (ret == EOF) {
@@ -1211,6 +1093,7 @@ static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir)
                case EXFAT_VOLUME:
                case EXFAT_BITMAP:
                case EXFAT_UPCASE:
+               case EXFAT_GUID:
                        if (dir == exfat->root)
                                break;
                        /* fallthrough */
@@ -1300,6 +1183,12 @@ static int exfat_filesystem_check(struct exfat_fsck *fsck)
                return -ENOENT;
        }
 
+       fsck->name_hash_bitmap = malloc(EXFAT_BITMAP_SIZE(EXFAT_MAX_HASH_COUNT));
+       if (!fsck->name_hash_bitmap) {
+               exfat_err("failed to allocate name hash bitmap\n");
+               return -ENOMEM;
+       }
+
        list_add(&exfat->root->list, &exfat->dir_list);
 
        while (!list_empty(&exfat->dir_list)) {
@@ -1328,6 +1217,7 @@ static int exfat_filesystem_check(struct exfat_fsck *fsck)
        }
 out:
        exfat_free_dir_list(exfat);
+       free(fsck->name_hash_bitmap);
        return ret;
 }
 
@@ -1427,9 +1317,40 @@ static int rescue_orphan_clusters(struct exfat_fsck *fsck)
        struct exfat_dentry_loc loc;
        struct exfat_lookup_filter lf = {
                .in.type = EXFAT_INVAL,
+               .in.dentry_count = 0,
                .in.filter = NULL,
        };
 
+       clu_count = le32_to_cpu(exfat->bs->bsx.clu_count);
+
+       /* find clusters which are not marked as free, but not allocated to
+        * any files.
+        */
+       disk_b = (bitmap_t *)exfat->disk_bitmap;
+       alloc_b = (bitmap_t *)exfat->alloc_bitmap;
+       ohead_b = (bitmap_t *)exfat->ohead_bitmap;
+       for (i = 0; i < EXFAT_BITMAP_SIZE(clu_count) / sizeof(bitmap_t); i++)
+               ohead_b[i] = disk_b[i] & ~alloc_b[i];
+
+       /* no orphan clusters */
+       if (exfat_bitmap_find_one(exfat, exfat->ohead_bitmap,
+                               EXFAT_FIRST_CLUSTER, &s_clu))
+               return 0;
+
+       err = exfat_create_file(exfat_fsck.exfat,
+                               exfat_fsck.exfat->root,
+                               "LOST+FOUND",
+                               ATTR_SUBDIR);
+       if (err) {
+               exfat_err("failed to create LOST+FOUND directory\n");
+               return err;
+       }
+
+       if (fsync(exfat_fsck.exfat->blk_dev->dev_fd) != 0) {
+               exfat_err("failed to sync()\n");
+               return -EIO;
+       }
+
        err = read_lostfound(exfat, &lostfound);
        if (err) {
                exfat_err("failed to find LOST+FOUND\n");
@@ -1455,17 +1376,6 @@ static int rescue_orphan_clusters(struct exfat_fsck *fsck)
        }
        dset[1].dentry.stream.flags |= EXFAT_SF_CONTIGUOUS;
 
-       clu_count = le32_to_cpu(exfat->bs->bsx.clu_count);
-
-       /* find clusters which are not marked as free, but not allocated to
-        * any files.
-        */
-       disk_b = (bitmap_t *)exfat->disk_bitmap;
-       alloc_b = (bitmap_t *)exfat->alloc_bitmap;
-       ohead_b = (bitmap_t *)exfat->ohead_bitmap;
-       for (i = 0; i < EXFAT_BITMAP_SIZE(clu_count) / sizeof(bitmap_t); i++)
-               ohead_b[i] = disk_b[i] & ~alloc_b[i];
-
        /* create temporary files and allocate contiguous orphan clusters
         * to each file.
         */
@@ -1657,23 +1567,6 @@ int main(int argc, char * const argv[])
                goto out;
        }
 
-       if (exfat_fsck.options & FSCK_OPTS_RESCUE_CLUS) {
-               ret = exfat_create_file(exfat_fsck.exfat,
-                                       exfat_fsck.exfat->root,
-                                       "LOST+FOUND",
-                                       ATTR_SUBDIR);
-               if (ret) {
-                       exfat_err("failed to create lost+found directory\n");
-                       goto out;
-               }
-
-               if (fsync(exfat_fsck.exfat->blk_dev->dev_fd) != 0) {
-                       ret = -EIO;
-                       exfat_err("failed to sync()\n");
-                       goto out;
-               }
-       }
-
        exfat_debug("verifying directory entries...\n");
        ret = exfat_filesystem_check(&exfat_fsck);
        if (ret)