@@ -476,4 +476,7 @@ ec EXT2_ET_MMP_CSUM_INVALID,
ec EXT2_ET_FILE_EXISTS,
"Ext2 file already exists"
+ec EXT2_ET_SEEK_BEYOND_EOF,
+ "lseek beyond the EOF"
+
end
@@ -160,6 +160,8 @@ typedef struct ext2_file *ext2_file_t;
#define EXT2_SEEK_SET 0
#define EXT2_SEEK_CUR 1
#define EXT2_SEEK_END 2
+#define EXT2_SEEK_DATA 3
+#define EXT2_SEEK_HOLE 4
/*
* Flags for the ext2_filsys structure and for ext2fs_open()
@@ -312,10 +312,201 @@ fail:
return retval;
}
+static errcode_t ext2fs_file_llseek_data_ext(ext2_file_t file, __u64 offset)
+{
+ ext2_extent_handle_t handle = 0;
+ struct ext2fs_extent extent;
+ ext2_filsys fs = file->fs;
+ errcode_t retval = 0;
+ unsigned int dataoff;
+
+ retval = ext2fs_extent_open2(fs, file->ino, &file->inode, &handle);
+ if (retval)
+ return retval;
+
+ while (file->blockno * fs->blocksize < EXT2_I_SIZE(&file->inode)) {
+ retval = ext2fs_extent_goto(handle, file->blockno);
+ if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND)
+ goto done;
+ if (retval == EXT2_ET_EXTENT_NOT_FOUND) {
+ file->blockno++;
+ } else {
+ retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
+ &extent);
+ if (retval)
+ goto done;
+ /*
+ * Here we skip uninit block because uninit block is
+ * as a hole.
+ */
+ if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) {
+ file->blockno++;
+ continue;
+ }
+ dataoff = file->blockno - extent.e_lblk;
+ if (file->blockno >= extent.e_lblk &&
+ dataoff <= extent.e_len) {
+ if (file->blockno == offset / fs->blocksize)
+ file->pos = offset;
+ else
+ file->pos = file->blockno *
+ fs->blocksize;
+ break;
+ }
+ }
+ }
+
+done:
+ ext2fs_extent_free(handle);
+ return retval;
+}
+
+static errcode_t ext2fs_file_llseek_data_ind(ext2_file_t file, __u64 offset)
+{
+ ext2_filsys fs = file->fs;
+ errcode_t retval = 0;
+
+ while (file->blockno * fs->blocksize < EXT2_I_SIZE(&file->inode)) {
+ retval = ext2fs_bmap2(fs, file->ino, &file->inode, BMAP_BUFFER,
+ 0, file->blockno, 0, &file->physblock);
+ if (retval)
+ return retval;
+ /*
+ * Here we don't care about uninit block because indirect-based
+ * file doesn't support it.
+ */
+ if (file->physblock == 0) {
+ file->blockno++;
+ } else {
+ if (file->blockno == offset / fs->blocksize)
+ file->pos = offset;
+ else
+ file->pos = file->blockno * fs->blocksize;
+ break;
+ }
+ }
+
+ return retval;
+}
+
+static errcode_t ext2fs_file_llseek_data(ext2_file_t file, __u64 offset)
+{
+ ext2_filsys fs = file->fs;
+ errcode_t retval;
+
+ file->blockno = offset / fs->blocksize;
+
+ if (file->inode.i_flags & EXT4_EXTENTS_FL)
+ retval = ext2fs_file_llseek_data_ext(file, offset);
+ else
+ retval = ext2fs_file_llseek_data_ind(file, offset);
+
+ /* notify the caller that there is no any data */
+ if (file->blockno * fs->blocksize >= EXT2_I_SIZE(&file->inode))
+ return EXT2_ET_SEEK_BEYOND_EOF;
+
+ return retval;
+}
+
+static errcode_t ext2fs_file_llseek_hole_ext(ext2_file_t file, __u64 offset)
+{
+ ext2_extent_handle_t handle = 0;
+ struct ext2fs_extent extent;
+ ext2_filsys fs = file->fs;
+ errcode_t retval = 0;
+ unsigned int dataoff;
+
+ retval = ext2fs_extent_open2(fs, file->ino, &file->inode, &handle);
+ if (retval)
+ return retval;
+
+ while (file->blockno * fs->blocksize < EXT2_I_SIZE(&file->inode)) {
+ retval = ext2fs_extent_goto(handle, file->blockno);
+ if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND)
+ goto done;
+ if (retval == EXT2_ET_EXTENT_NOT_FOUND) {
+ if (file->blockno == offset / fs->blocksize)
+ file->pos = offset;
+ else
+ file->pos = file->blockno * fs->blocksize;
+ break;
+ } else {
+ retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT,
+ &extent);
+ if (retval)
+ goto done;
+ /* Here uninit block is as a hole */
+ if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) {
+ if (file->blockno == offset / fs->blocksize)
+ file->pos = offset;
+ else
+ file->pos = file->blockno *
+ fs->blocksize;
+ break;
+ }
+ dataoff = file->blockno - extent.e_lblk;
+ if (file->blockno >= extent.e_lblk &&
+ dataoff <= extent.e_len)
+ file->blockno = extent.e_lblk + extent.e_len;
+ }
+ }
+
+done:
+ ext2fs_extent_free(handle);
+ return retval;
+}
+
+static errcode_t ext2fs_file_llseek_hole_ind(ext2_file_t file, __u64 offset)
+{
+ ext2_filsys fs = file->fs;
+ errcode_t retval = 0;
+
+ while (file->blockno * fs->blocksize < EXT2_I_SIZE(&file->inode)) {
+ retval = ext2fs_bmap2(fs, file->ino, &file->inode, BMAP_BUFFER,
+ 0, file->blockno, 0, &file->physblock);
+ if (retval)
+ return retval;
+ /*
+ * Here we don't care about uninit block because indirect-based
+ * file doesn't support it.
+ */
+ if (file->physblock == 0) {
+ if (file->blockno == offset / fs->blocksize)
+ file->pos = offset;
+ else
+ file->pos = file->blockno * fs->blocksize;
+ break;
+ } else {
+ file->blockno++;
+ }
+ }
+
+ return retval;
+}
+
+static errcode_t ext2fs_file_llseek_hole(ext2_file_t file, __u64 offset)
+{
+ ext2_filsys fs = file->fs;
+ errcode_t retval;
+
+ if (offset >= EXT2_I_SIZE(&file->inode))
+ return EXT2_ET_SEEK_BEYOND_EOF;
+
+ file->blockno = offset / fs->blocksize;
+
+ if (file->inode.i_flags & EXT4_EXTENTS_FL)
+ retval = ext2fs_file_llseek_hole_ext(file, offset);
+ else
+ retval = ext2fs_file_llseek_hole_ind(file, offset);
+
+ return retval;
+}
+
errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
int whence, __u64 *ret_pos)
{
EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+ errcode_t retval = 0;
if (whence == EXT2_SEEK_SET)
file->pos = offset;
@@ -323,13 +514,17 @@ errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
file->pos += offset;
else if (whence == EXT2_SEEK_END)
file->pos = EXT2_I_SIZE(&file->inode) + offset;
+ else if (whence == EXT2_SEEK_DATA)
+ retval = ext2fs_file_llseek_data(file, offset);
+ else if (whence == EXT2_SEEK_HOLE)
+ retval = ext2fs_file_llseek_hole(file, offset);
else
return EXT2_ET_INVALID_ARGUMENT;
if (ret_pos)
*ret_pos = file->pos;
- return 0;
+ return retval;
}
errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,