Merge branch 'bpf: Allow reads from uninit stack'
[linux-block.git] / fs / exfat / dir.c
index 1dfa67f307f1737cfcd39eada98f4dbf272c4484..957574180a5e3e0e80b7830ecf8ec51b989a3e60 100644 (file)
@@ -29,14 +29,15 @@ static int exfat_extract_uni_name(struct exfat_dentry *ep,
 
 }
 
-static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
+static int exfat_get_uniname_from_ext_entry(struct super_block *sb,
                struct exfat_chain *p_dir, int entry, unsigned short *uniname)
 {
-       int i;
+       int i, err;
        struct exfat_entry_set_cache es;
 
-       if (exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES))
-               return;
+       err = exfat_get_dentry_set(&es, sb, p_dir, entry, ES_ALL_ENTRIES);
+       if (err)
+               return err;
 
        /*
         * First entry  : file entry
@@ -56,12 +57,13 @@ static void exfat_get_uniname_from_ext_entry(struct super_block *sb,
        }
 
        exfat_put_dentry_set(&es, false);
+       return 0;
 }
 
 /* read a directory entry from the opened directory */
 static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_entry *dir_entry)
 {
-       int i, dentries_per_clu, num_ext;
+       int i, dentries_per_clu, num_ext, err;
        unsigned int type, clu_offset, max_dentries;
        struct exfat_chain dir, clu;
        struct exfat_uni_name uni_name;
@@ -100,7 +102,7 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
                        clu.dir = ei->hint_bmap.clu;
                }
 
-               while (clu_offset > 0) {
+               while (clu_offset > 0 && clu.dir != EXFAT_EOF_CLUSTER) {
                        if (exfat_get_next_cluster(sb, &(clu.dir)))
                                return -EIO;
 
@@ -146,8 +148,12 @@ static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_ent
                                        0);
 
                        *uni_name.name = 0x0;
-                       exfat_get_uniname_from_ext_entry(sb, &clu, i,
+                       err = exfat_get_uniname_from_ext_entry(sb, &clu, i,
                                uni_name.name);
+                       if (err) {
+                               brelse(bh);
+                               continue;
+                       }
                        exfat_utf16_to_nls(sb, &uni_name,
                                dir_entry->namebuf.lfn,
                                dir_entry->namebuf.lfnbuf_len);
@@ -234,10 +240,7 @@ static int exfat_iterate(struct file *file, struct dir_context *ctx)
                fake_offset = 1;
        }
 
-       if (cpos & (DENTRY_SIZE - 1)) {
-               err = -ENOENT;
-               goto unlock;
-       }
+       cpos = round_up(cpos, DENTRY_SIZE);
 
        /* name buffer should be allocated before use */
        err = exfat_alloc_namebuf(nb);
@@ -378,6 +381,12 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
                        return TYPE_ACL;
                return TYPE_CRITICAL_SEC;
        }
+
+       if (ep->type == EXFAT_VENDOR_EXT)
+               return TYPE_VENDOR_EXT;
+       if (ep->type == EXFAT_VENDOR_ALLOC)
+               return TYPE_VENDOR_ALLOC;
+
        return TYPE_BENIGN_SEC;
 }
 
@@ -521,6 +530,25 @@ release_fbh:
        return ret;
 }
 
+static void exfat_free_benign_secondary_clusters(struct inode *inode,
+               struct exfat_dentry *ep)
+{
+       struct super_block *sb = inode->i_sb;
+       struct exfat_chain dir;
+       unsigned int start_clu =
+               le32_to_cpu(ep->dentry.generic_secondary.start_clu);
+       u64 size = le64_to_cpu(ep->dentry.generic_secondary.size);
+       unsigned char flags = ep->dentry.generic_secondary.flags;
+
+       if (!(flags & ALLOC_POSSIBLE) || !start_clu || !size)
+               return;
+
+       exfat_chain_set(&dir, start_clu,
+                       EXFAT_B_TO_CLU_ROUND_UP(size, EXFAT_SB(sb)),
+                       flags);
+       exfat_free_cluster(inode, &dir);
+}
+
 int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
                int entry, int num_entries, struct exfat_uni_name *p_uniname)
 {
@@ -553,6 +581,9 @@ int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir,
                if (!ep)
                        return -EIO;
 
+               if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
+                       exfat_free_benign_secondary_clusters(inode, ep);
+
                exfat_init_name_entry(ep, uniname);
                exfat_update_bh(bh, sync);
                brelse(bh);
@@ -576,6 +607,9 @@ int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir,
                if (!ep)
                        return -EIO;
 
+               if (exfat_get_entry_type(ep) & TYPE_BENIGN_SEC)
+                       exfat_free_benign_secondary_clusters(inode, ep);
+
                exfat_set_entry_type(ep, TYPE_DELETED);
                exfat_update_bh(bh, IS_DIRSYNC(inode));
                brelse(bh);
@@ -744,6 +778,7 @@ enum exfat_validate_dentry_mode {
        ES_MODE_GET_STRM_ENTRY,
        ES_MODE_GET_NAME_ENTRY,
        ES_MODE_GET_CRITICAL_SEC_ENTRY,
+       ES_MODE_GET_BENIGN_SEC_ENTRY,
 };
 
 static bool exfat_validate_entry(unsigned int type,
@@ -757,36 +792,33 @@ static bool exfat_validate_entry(unsigned int type,
                if  (type != TYPE_FILE && type != TYPE_DIR)
                        return false;
                *mode = ES_MODE_GET_FILE_ENTRY;
-               return true;
+               break;
        case ES_MODE_GET_FILE_ENTRY:
                if (type != TYPE_STREAM)
                        return false;
                *mode = ES_MODE_GET_STRM_ENTRY;
-               return true;
+               break;
        case ES_MODE_GET_STRM_ENTRY:
                if (type != TYPE_EXTEND)
                        return false;
                *mode = ES_MODE_GET_NAME_ENTRY;
-               return true;
+               break;
        case ES_MODE_GET_NAME_ENTRY:
-               if (type == TYPE_STREAM)
-                       return false;
-               if (type != TYPE_EXTEND) {
-                       if (!(type & TYPE_CRITICAL_SEC))
-                               return false;
-                       *mode = ES_MODE_GET_CRITICAL_SEC_ENTRY;
-               }
-               return true;
-       case ES_MODE_GET_CRITICAL_SEC_ENTRY:
-               if (type == TYPE_EXTEND || type == TYPE_STREAM)
+               if (type & TYPE_BENIGN_SEC)
+                       *mode = ES_MODE_GET_BENIGN_SEC_ENTRY;
+               else if (type != TYPE_EXTEND)
                        return false;
-               if ((type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC)
+               break;
+       case ES_MODE_GET_BENIGN_SEC_ENTRY:
+               /* Assume unreconized benign secondary entry */
+               if (!(type & TYPE_BENIGN_SEC))
                        return false;
-               return true;
+               break;
        default:
-               WARN_ON_ONCE(1);
                return false;
        }
+
+       return true;
 }
 
 struct exfat_dentry *exfat_get_dentry_cached(
@@ -1167,10 +1199,8 @@ int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir,
 
                type = exfat_get_entry_type(ext_ep);
                brelse(bh);
-               if (type == TYPE_EXTEND || type == TYPE_STREAM)
+               if (type & TYPE_CRITICAL_SEC || type & TYPE_BENIGN_SEC)
                        count++;
-               else
-                       break;
        }
        return count;
 }