|
@@ -1396,6 +1396,14 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * So this function is called when the volume is mkfsed with
|
|
|
+ * dir_index disabled. In order to keep f_pos persistent
|
|
|
+ * after we convert from an inlined dir to a blocked based,
|
|
|
+ * we just pretend that we are a normal dir and return the
|
|
|
+ * offset as if '.' and '..' really take place.
|
|
|
+ *
|
|
|
+ */
|
|
|
int ext4_read_inline_dir(struct file *filp,
|
|
|
void *dirent, filldir_t filldir,
|
|
|
int *has_inline_data)
|
|
@@ -1409,6 +1417,7 @@ int ext4_read_inline_dir(struct file *filp,
|
|
|
int ret, inline_size = 0;
|
|
|
struct ext4_iloc iloc;
|
|
|
void *dir_buf = NULL;
|
|
|
+ int dotdot_offset, dotdot_size, extra_offset, extra_size;
|
|
|
|
|
|
ret = ext4_get_inode_loc(inode, &iloc);
|
|
|
if (ret)
|
|
@@ -1437,8 +1446,21 @@ int ext4_read_inline_dir(struct file *filp,
|
|
|
sb = inode->i_sb;
|
|
|
stored = 0;
|
|
|
parent_ino = le32_to_cpu(((struct ext4_dir_entry_2 *)dir_buf)->inode);
|
|
|
+ offset = filp->f_pos;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * dotdot_offset and dotdot_size is the real offset and
|
|
|
+ * size for ".." and "." if the dir is block based while
|
|
|
+ * the real size for them are only EXT4_INLINE_DOTDOT_SIZE.
|
|
|
+ * So we will use extra_offset and extra_size to indicate them
|
|
|
+ * during the inline dir iteration.
|
|
|
+ */
|
|
|
+ dotdot_offset = EXT4_DIR_REC_LEN(1);
|
|
|
+ dotdot_size = dotdot_offset + EXT4_DIR_REC_LEN(2);
|
|
|
+ extra_offset = dotdot_size - EXT4_INLINE_DOTDOT_SIZE;
|
|
|
+ extra_size = extra_offset + inline_size;
|
|
|
|
|
|
- while (!error && !stored && filp->f_pos < inode->i_size) {
|
|
|
+ while (!error && !stored && filp->f_pos < extra_size) {
|
|
|
revalidate:
|
|
|
/*
|
|
|
* If the version has changed since the last call to
|
|
@@ -1447,15 +1469,23 @@ revalidate:
|
|
|
* dir to make sure.
|
|
|
*/
|
|
|
if (filp->f_version != inode->i_version) {
|
|
|
- for (i = 0;
|
|
|
- i < inode->i_size && i < offset;) {
|
|
|
+ for (i = 0; i < extra_size && i < offset;) {
|
|
|
+ /*
|
|
|
+ * "." is with offset 0 and
|
|
|
+ * ".." is dotdot_offset.
|
|
|
+ */
|
|
|
if (!i) {
|
|
|
- /* skip "." and ".." if needed. */
|
|
|
- i += EXT4_INLINE_DOTDOT_SIZE;
|
|
|
+ i = dotdot_offset;
|
|
|
+ continue;
|
|
|
+ } else if (i == dotdot_offset) {
|
|
|
+ i = dotdot_size;
|
|
|
continue;
|
|
|
}
|
|
|
+ /* for other entry, the real offset in
|
|
|
+ * the buf has to be tuned accordingly.
|
|
|
+ */
|
|
|
de = (struct ext4_dir_entry_2 *)
|
|
|
- (dir_buf + i);
|
|
|
+ (dir_buf + i - extra_offset);
|
|
|
/* It's too expensive to do a full
|
|
|
* dirent test each time round this
|
|
|
* loop, but we do have to test at
|
|
@@ -1463,43 +1493,47 @@ revalidate:
|
|
|
* failure will be detected in the
|
|
|
* dirent test below. */
|
|
|
if (ext4_rec_len_from_disk(de->rec_len,
|
|
|
- inline_size) < EXT4_DIR_REC_LEN(1))
|
|
|
+ extra_size) < EXT4_DIR_REC_LEN(1))
|
|
|
break;
|
|
|
i += ext4_rec_len_from_disk(de->rec_len,
|
|
|
- inline_size);
|
|
|
+ extra_size);
|
|
|
}
|
|
|
offset = i;
|
|
|
filp->f_pos = offset;
|
|
|
filp->f_version = inode->i_version;
|
|
|
}
|
|
|
|
|
|
- while (!error && filp->f_pos < inode->i_size) {
|
|
|
+ while (!error && filp->f_pos < extra_size) {
|
|
|
if (filp->f_pos == 0) {
|
|
|
error = filldir(dirent, ".", 1, 0, inode->i_ino,
|
|
|
DT_DIR);
|
|
|
if (error)
|
|
|
break;
|
|
|
stored++;
|
|
|
+ filp->f_pos = dotdot_offset;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- error = filldir(dirent, "..", 2, 0, parent_ino,
|
|
|
- DT_DIR);
|
|
|
+ if (filp->f_pos == dotdot_offset) {
|
|
|
+ error = filldir(dirent, "..", 2,
|
|
|
+ dotdot_offset,
|
|
|
+ parent_ino, DT_DIR);
|
|
|
if (error)
|
|
|
break;
|
|
|
stored++;
|
|
|
|
|
|
- filp->f_pos = offset = EXT4_INLINE_DOTDOT_SIZE;
|
|
|
+ filp->f_pos = dotdot_size;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- de = (struct ext4_dir_entry_2 *)(dir_buf + offset);
|
|
|
+ de = (struct ext4_dir_entry_2 *)
|
|
|
+ (dir_buf + filp->f_pos - extra_offset);
|
|
|
if (ext4_check_dir_entry(inode, filp, de,
|
|
|
iloc.bh, dir_buf,
|
|
|
- inline_size, offset)) {
|
|
|
+ extra_size, filp->f_pos)) {
|
|
|
ret = stored;
|
|
|
goto out;
|
|
|
}
|
|
|
- offset += ext4_rec_len_from_disk(de->rec_len,
|
|
|
- inline_size);
|
|
|
if (le32_to_cpu(de->inode)) {
|
|
|
/* We might block in the next section
|
|
|
* if the data destination is
|
|
@@ -1522,9 +1556,8 @@ revalidate:
|
|
|
stored++;
|
|
|
}
|
|
|
filp->f_pos += ext4_rec_len_from_disk(de->rec_len,
|
|
|
- inline_size);
|
|
|
+ extra_size);
|
|
|
}
|
|
|
- offset = 0;
|
|
|
}
|
|
|
out:
|
|
|
kfree(dir_buf);
|