Commit 93862d5e1ab875664c6cc95254fc365028a48bb1
1 parent
5cffff9e29
Exists in
master
and in
6 other branches
ocfs2: Implement llseek()
ocfs2 implements its own llseek() to provide the SEEK_HOLE/SEEK_DATA functionality. SEEK_HOLE sets the file pointer to the start of either a hole or an unwritten (preallocated) extent, that is greater than or equal to the supplied offset. SEEK_DATA sets the file pointer to the start of an allocated extent (not unwritten) that is greater than or equal to the supplied offset. If the supplied offset is on a desired region, then the file pointer is set to it. Offsets greater than or equal to the file size return -ENXIO. Unwritten (preallocated) extents are considered holes because the file system treats reads to such regions in the same way as it does to holes. Signed-off-by: Sunil Mushran <sunil.mushran@oracle.com>
Showing 3 changed files with 151 additions and 2 deletions Side-by-side Diff
fs/ocfs2/extent_map.c
... | ... | @@ -832,6 +832,102 @@ |
832 | 832 | return ret; |
833 | 833 | } |
834 | 834 | |
835 | +int ocfs2_seek_data_hole_offset(struct file *file, loff_t *offset, int origin) | |
836 | +{ | |
837 | + struct inode *inode = file->f_mapping->host; | |
838 | + int ret; | |
839 | + unsigned int is_last = 0, is_data = 0; | |
840 | + u16 cs_bits = OCFS2_SB(inode->i_sb)->s_clustersize_bits; | |
841 | + u32 cpos, cend, clen, hole_size; | |
842 | + u64 extoff, extlen; | |
843 | + struct buffer_head *di_bh = NULL; | |
844 | + struct ocfs2_extent_rec rec; | |
845 | + | |
846 | + BUG_ON(origin != SEEK_DATA && origin != SEEK_HOLE); | |
847 | + | |
848 | + ret = ocfs2_inode_lock(inode, &di_bh, 0); | |
849 | + if (ret) { | |
850 | + mlog_errno(ret); | |
851 | + goto out; | |
852 | + } | |
853 | + | |
854 | + down_read(&OCFS2_I(inode)->ip_alloc_sem); | |
855 | + | |
856 | + if (*offset >= inode->i_size) { | |
857 | + ret = -ENXIO; | |
858 | + goto out_unlock; | |
859 | + } | |
860 | + | |
861 | + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_INLINE_DATA_FL) { | |
862 | + if (origin == SEEK_HOLE) | |
863 | + *offset = inode->i_size; | |
864 | + goto out_unlock; | |
865 | + } | |
866 | + | |
867 | + clen = 0; | |
868 | + cpos = *offset >> cs_bits; | |
869 | + cend = ocfs2_clusters_for_bytes(inode->i_sb, inode->i_size); | |
870 | + | |
871 | + while (cpos < cend && !is_last) { | |
872 | + ret = ocfs2_get_clusters_nocache(inode, di_bh, cpos, &hole_size, | |
873 | + &rec, &is_last); | |
874 | + if (ret) { | |
875 | + mlog_errno(ret); | |
876 | + goto out_unlock; | |
877 | + } | |
878 | + | |
879 | + extoff = cpos; | |
880 | + extoff <<= cs_bits; | |
881 | + | |
882 | + if (rec.e_blkno == 0ULL) { | |
883 | + clen = hole_size; | |
884 | + is_data = 0; | |
885 | + } else { | |
886 | + clen = le16_to_cpu(rec.e_leaf_clusters) - | |
887 | + (cpos - le32_to_cpu(rec.e_cpos)); | |
888 | + is_data = (rec.e_flags & OCFS2_EXT_UNWRITTEN) ? 0 : 1; | |
889 | + } | |
890 | + | |
891 | + if ((!is_data && origin == SEEK_HOLE) || | |
892 | + (is_data && origin == SEEK_DATA)) { | |
893 | + if (extoff > *offset) | |
894 | + *offset = extoff; | |
895 | + goto out_unlock; | |
896 | + } | |
897 | + | |
898 | + if (!is_last) | |
899 | + cpos += clen; | |
900 | + } | |
901 | + | |
902 | + if (origin == SEEK_HOLE) { | |
903 | + extoff = cpos; | |
904 | + extoff <<= cs_bits; | |
905 | + extlen = clen; | |
906 | + extlen <<= cs_bits; | |
907 | + | |
908 | + if ((extoff + extlen) > inode->i_size) | |
909 | + extlen = inode->i_size - extoff; | |
910 | + extoff += extlen; | |
911 | + if (extoff > *offset) | |
912 | + *offset = extoff; | |
913 | + goto out_unlock; | |
914 | + } | |
915 | + | |
916 | + ret = -ENXIO; | |
917 | + | |
918 | +out_unlock: | |
919 | + | |
920 | + brelse(di_bh); | |
921 | + | |
922 | + up_read(&OCFS2_I(inode)->ip_alloc_sem); | |
923 | + | |
924 | + ocfs2_inode_unlock(inode, 0); | |
925 | +out: | |
926 | + if (ret && ret != -ENXIO) | |
927 | + ret = -ENXIO; | |
928 | + return ret; | |
929 | +} | |
930 | + | |
835 | 931 | int ocfs2_read_virt_blocks(struct inode *inode, u64 v_block, int nr, |
836 | 932 | struct buffer_head *bhs[], int flags, |
837 | 933 | int (*validate)(struct super_block *sb, |
fs/ocfs2/extent_map.h
... | ... | @@ -53,6 +53,8 @@ |
53 | 53 | int ocfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, |
54 | 54 | u64 map_start, u64 map_len); |
55 | 55 | |
56 | +int ocfs2_seek_data_hole_offset(struct file *file, loff_t *offset, int origin); | |
57 | + | |
56 | 58 | int ocfs2_xattr_get_clusters(struct inode *inode, u32 v_cluster, |
57 | 59 | u32 *p_cluster, u32 *num_clusters, |
58 | 60 | struct ocfs2_extent_list *el, |
fs/ocfs2/file.c
... | ... | @@ -2591,6 +2591,57 @@ |
2591 | 2591 | return ret; |
2592 | 2592 | } |
2593 | 2593 | |
2594 | +/* Refer generic_file_llseek_unlocked() */ | |
2595 | +static loff_t ocfs2_file_llseek(struct file *file, loff_t offset, int origin) | |
2596 | +{ | |
2597 | + struct inode *inode = file->f_mapping->host; | |
2598 | + int ret = 0; | |
2599 | + | |
2600 | + mutex_lock(&inode->i_mutex); | |
2601 | + | |
2602 | + switch (origin) { | |
2603 | + case SEEK_SET: | |
2604 | + break; | |
2605 | + case SEEK_END: | |
2606 | + offset += inode->i_size; | |
2607 | + break; | |
2608 | + case SEEK_CUR: | |
2609 | + if (offset == 0) { | |
2610 | + offset = file->f_pos; | |
2611 | + goto out; | |
2612 | + } | |
2613 | + offset += file->f_pos; | |
2614 | + break; | |
2615 | + case SEEK_DATA: | |
2616 | + case SEEK_HOLE: | |
2617 | + ret = ocfs2_seek_data_hole_offset(file, &offset, origin); | |
2618 | + if (ret) | |
2619 | + goto out; | |
2620 | + break; | |
2621 | + default: | |
2622 | + ret = -EINVAL; | |
2623 | + goto out; | |
2624 | + } | |
2625 | + | |
2626 | + if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET)) | |
2627 | + ret = -EINVAL; | |
2628 | + if (!ret && offset > inode->i_sb->s_maxbytes) | |
2629 | + ret = -EINVAL; | |
2630 | + if (ret) | |
2631 | + goto out; | |
2632 | + | |
2633 | + if (offset != file->f_pos) { | |
2634 | + file->f_pos = offset; | |
2635 | + file->f_version = 0; | |
2636 | + } | |
2637 | + | |
2638 | +out: | |
2639 | + mutex_unlock(&inode->i_mutex); | |
2640 | + if (ret) | |
2641 | + return ret; | |
2642 | + return offset; | |
2643 | +} | |
2644 | + | |
2594 | 2645 | const struct inode_operations ocfs2_file_iops = { |
2595 | 2646 | .setattr = ocfs2_setattr, |
2596 | 2647 | .getattr = ocfs2_getattr, |
... | ... | @@ -2615,7 +2666,7 @@ |
2615 | 2666 | * ocfs2_fops_no_plocks and ocfs2_dops_no_plocks! |
2616 | 2667 | */ |
2617 | 2668 | const struct file_operations ocfs2_fops = { |
2618 | - .llseek = generic_file_llseek, | |
2669 | + .llseek = ocfs2_file_llseek, | |
2619 | 2670 | .read = do_sync_read, |
2620 | 2671 | .write = do_sync_write, |
2621 | 2672 | .mmap = ocfs2_mmap, |
... | ... | @@ -2663,7 +2714,7 @@ |
2663 | 2714 | * the cluster. |
2664 | 2715 | */ |
2665 | 2716 | const struct file_operations ocfs2_fops_no_plocks = { |
2666 | - .llseek = generic_file_llseek, | |
2717 | + .llseek = ocfs2_file_llseek, | |
2667 | 2718 | .read = do_sync_read, |
2668 | 2719 | .write = do_sync_write, |
2669 | 2720 | .mmap = ocfs2_mmap, |