return fat__get_entry(dir, pos, bh, de);
}
+/*
+ * Like fat_get_entry(), but honour the FAT end-of-directory marker:
+ * a dirent whose first name byte is NUL terminates iteration per the
+ * spec, which also guarantees that every following slot is zeroed.
+ * Skip straight to the end of the directory so the next call returns
+ * -1 from fat_bmap() without re-reading the trailing zero slots, and
+ * so callers that persist *pos across invocations (e.g. readdir's
+ * ctx->pos) keep reporting EOD. Release *bh and set it to NULL to
+ * match fat_get_entry()'s contract that *bh is NULL on the -1 return.
+ */
+static int fat_get_entry_eod(struct inode *dir, loff_t *pos,
+ struct buffer_head **bh,
+ struct msdos_dir_entry **de)
+{
+ int err = fat_get_entry(dir, pos, bh, de);
+
+ if (err == 0 && (*de)->name[0] == 0) {
+ brelse(*bh);
+ *bh = NULL;
+ *pos = dir->i_size;
+ return -1;
+ }
+ return err;
+}
+
/*
* Convert Unicode 16 to UTF-8, translated Unicode, or ASCII.
* If uni_xlate is enabled and we can't get a 1:1 conversion, use a
if (ds->id & 0x40)
(*unicode)[offset + 13] = 0;
- if (fat_get_entry(dir, pos, bh, de) < 0)
+ if (fat_get_entry_eod(dir, pos, bh, de) < 0)
return PARSE_EOF;
if (slot == 0)
break;
err = -ENOENT;
while (1) {
- if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
+ if (fat_get_entry_eod(inode, &cpos, &bh, &de) == -1)
goto end_of_dir;
parse_record:
nr_slots = 0;
bh = NULL;
get_new:
- if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
+ if (fat_get_entry_eod(inode, &cpos, &bh, &de) == -1)
goto end_of_dir;
parse_record:
nr_slots = 0;
struct buffer_head **bh,
struct msdos_dir_entry **de)
{
- while (fat_get_entry(dir, pos, bh, de) >= 0) {
+ while (fat_get_entry_eod(dir, pos, bh, de) >= 0) {
/* free entry or long name entry or volume label */
if (!IS_FREE((*de)->name) && !((*de)->attr & ATTR_VOLUME))
return 0;
struct msdos_dir_entry *de;
int err, free_slots, i, nr_bhs;
loff_t pos;
+ bool saw_eod;
sinfo->nr_slots = nr_slots;
bh = prev = NULL;
pos = 0;
err = -ENOSPC;
+ saw_eod = false;
while (fat_get_entry(dir, &pos, &bh, &de) > -1) {
/* check the maximum size of directory */
if (pos >= FAT_MAX_DIR_SIZE)
goto error;
if (IS_FREE(de->name)) {
+ if (de->name[0] == 0)
+ saw_eod = true;
if (prev != bh) {
get_bh(bh);
bhs[nr_bhs] = prev = bh;
if (free_slots == nr_slots)
goto found;
} else {
+ if (saw_eod) {
+ fat_fs_error_ratelimit(sb,
+ "allocated dir entry found after end-of-directory marker (i_pos %lld)",
+ MSDOS_I(dir)->i_pos);
+ err = -EIO;
+ goto error;
+ }
for (i = 0; i < nr_bhs; i++)
brelse(bhs[i]);
prev = NULL;