diff options
author | Jeff Liu <jeff.liu@oracle.com> | 2012-05-10 09:29:17 -0400 |
---|---|---|
committer | Ben Myers <bpm@sgi.com> | 2012-05-14 17:21:05 -0400 |
commit | 3fe3e6b18216c1247497dfd51c35484338856e1b (patch) | |
tree | 4ebbec83f37d2b7bd7d2aaab3800ed5b335b42b9 | |
parent | e700a06c71dbbc0879a5d15881cca7b772282484 (diff) |
xfs: introduce SEEK_DATA/SEEK_HOLE support
This patch adds lseek(2) SEEK_DATA/SEEK_HOLE functionality to xfs.
Signed-off-by: Jie Liu <jeff.liu@oracle.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
-rw-r--r-- | fs/xfs/xfs_file.c | 143 |
1 files changed, 142 insertions, 1 deletions
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 51fc2cf07d5e..8d214b87f6bb 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c | |||
@@ -963,8 +963,149 @@ xfs_vm_page_mkwrite( | |||
963 | return block_page_mkwrite(vma, vmf, xfs_get_blocks); | 963 | return block_page_mkwrite(vma, vmf, xfs_get_blocks); |
964 | } | 964 | } |
965 | 965 | ||
966 | STATIC loff_t | ||
967 | xfs_seek_data( | ||
968 | struct file *file, | ||
969 | loff_t start, | ||
970 | u32 type) | ||
971 | { | ||
972 | struct inode *inode = file->f_mapping->host; | ||
973 | struct xfs_inode *ip = XFS_I(inode); | ||
974 | struct xfs_mount *mp = ip->i_mount; | ||
975 | struct xfs_bmbt_irec map[2]; | ||
976 | int nmap = 2; | ||
977 | loff_t uninitialized_var(offset); | ||
978 | xfs_fsize_t isize; | ||
979 | xfs_fileoff_t fsbno; | ||
980 | xfs_filblks_t end; | ||
981 | uint lock; | ||
982 | int error; | ||
983 | |||
984 | lock = xfs_ilock_map_shared(ip); | ||
985 | |||
986 | isize = i_size_read(inode); | ||
987 | if (start >= isize) { | ||
988 | error = ENXIO; | ||
989 | goto out_unlock; | ||
990 | } | ||
991 | |||
992 | fsbno = XFS_B_TO_FSBT(mp, start); | ||
993 | |||
994 | /* | ||
995 | * Try to read extents from the first block indicated | ||
996 | * by fsbno to the end block of the file. | ||
997 | */ | ||
998 | end = XFS_B_TO_FSB(mp, isize); | ||
999 | |||
1000 | error = xfs_bmapi_read(ip, fsbno, end - fsbno, map, &nmap, | ||
1001 | XFS_BMAPI_ENTIRE); | ||
1002 | if (error) | ||
1003 | goto out_unlock; | ||
1004 | |||
1005 | /* | ||
1006 | * Treat unwritten extent as data extent since it might | ||
1007 | * contains dirty data in page cache. | ||
1008 | */ | ||
1009 | if (map[0].br_startblock != HOLESTARTBLOCK) { | ||
1010 | offset = max_t(loff_t, start, | ||
1011 | XFS_FSB_TO_B(mp, map[0].br_startoff)); | ||
1012 | } else { | ||
1013 | if (nmap == 1) { | ||
1014 | error = ENXIO; | ||
1015 | goto out_unlock; | ||
1016 | } | ||
1017 | |||
1018 | offset = max_t(loff_t, start, | ||
1019 | XFS_FSB_TO_B(mp, map[1].br_startoff)); | ||
1020 | } | ||
1021 | |||
1022 | if (offset != file->f_pos) | ||
1023 | file->f_pos = offset; | ||
1024 | |||
1025 | out_unlock: | ||
1026 | xfs_iunlock_map_shared(ip, lock); | ||
1027 | |||
1028 | if (error) | ||
1029 | return -error; | ||
1030 | return offset; | ||
1031 | } | ||
1032 | |||
1033 | STATIC loff_t | ||
1034 | xfs_seek_hole( | ||
1035 | struct file *file, | ||
1036 | loff_t start, | ||
1037 | u32 type) | ||
1038 | { | ||
1039 | struct inode *inode = file->f_mapping->host; | ||
1040 | struct xfs_inode *ip = XFS_I(inode); | ||
1041 | struct xfs_mount *mp = ip->i_mount; | ||
1042 | loff_t uninitialized_var(offset); | ||
1043 | loff_t holeoff; | ||
1044 | xfs_fsize_t isize; | ||
1045 | xfs_fileoff_t fsbno; | ||
1046 | uint lock; | ||
1047 | int error; | ||
1048 | |||
1049 | if (XFS_FORCED_SHUTDOWN(mp)) | ||
1050 | return -XFS_ERROR(EIO); | ||
1051 | |||
1052 | lock = xfs_ilock_map_shared(ip); | ||
1053 | |||
1054 | isize = i_size_read(inode); | ||
1055 | if (start >= isize) { | ||
1056 | error = ENXIO; | ||
1057 | goto out_unlock; | ||
1058 | } | ||
1059 | |||
1060 | fsbno = XFS_B_TO_FSBT(mp, start); | ||
1061 | error = xfs_bmap_first_unused(NULL, ip, 1, &fsbno, XFS_DATA_FORK); | ||
1062 | if (error) | ||
1063 | goto out_unlock; | ||
1064 | |||
1065 | holeoff = XFS_FSB_TO_B(mp, fsbno); | ||
1066 | if (holeoff <= start) | ||
1067 | offset = start; | ||
1068 | else { | ||
1069 | /* | ||
1070 | * xfs_bmap_first_unused() could return a value bigger than | ||
1071 | * isize if there are no more holes past the supplied offset. | ||
1072 | */ | ||
1073 | offset = min_t(loff_t, holeoff, isize); | ||
1074 | } | ||
1075 | |||
1076 | if (offset != file->f_pos) | ||
1077 | file->f_pos = offset; | ||
1078 | |||
1079 | out_unlock: | ||
1080 | xfs_iunlock_map_shared(ip, lock); | ||
1081 | |||
1082 | if (error) | ||
1083 | return -error; | ||
1084 | return offset; | ||
1085 | } | ||
1086 | |||
1087 | STATIC loff_t | ||
1088 | xfs_file_llseek( | ||
1089 | struct file *file, | ||
1090 | loff_t offset, | ||
1091 | int origin) | ||
1092 | { | ||
1093 | switch (origin) { | ||
1094 | case SEEK_END: | ||
1095 | case SEEK_CUR: | ||
1096 | case SEEK_SET: | ||
1097 | return generic_file_llseek(file, offset, origin); | ||
1098 | case SEEK_DATA: | ||
1099 | return xfs_seek_data(file, offset, origin); | ||
1100 | case SEEK_HOLE: | ||
1101 | return xfs_seek_hole(file, offset, origin); | ||
1102 | default: | ||
1103 | return -EINVAL; | ||
1104 | } | ||
1105 | } | ||
1106 | |||
966 | const struct file_operations xfs_file_operations = { | 1107 | const struct file_operations xfs_file_operations = { |
967 | .llseek = generic_file_llseek, | 1108 | .llseek = xfs_file_llseek, |
968 | .read = do_sync_read, | 1109 | .read = do_sync_read, |
969 | .write = do_sync_write, | 1110 | .write = do_sync_write, |
970 | .aio_read = xfs_file_aio_read, | 1111 | .aio_read = xfs_file_aio_read, |