diff options
author | Chao Yu <chao2.yu@samsung.com> | 2014-04-23 02:10:24 -0400 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2014-05-06 21:21:57 -0400 |
commit | 267378d4de696d4397cd611e62957d19b2a61357 (patch) | |
tree | fdcc2b128e16060b5b451f9b8405a424534f7b17 | |
parent | 2163d19815b3dfdb243cee2de2478ae7efce1942 (diff) |
f2fs: introduce f2fs_seek_block to support SEEK_{DATA, HOLE} in llseek
In This patch we introduce f2fs_seek_block to support SEEK_{DATA,HOLE} of
lseek(2).
change log from v1:
o fix bug when lseek from middle of page and fix wrong calculation of
PGOFS_OF_NEXT_DNODE macro.
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
-rw-r--r-- | fs/f2fs/f2fs.h | 6 | ||||
-rw-r--r-- | fs/f2fs/file.c | 92 |
2 files changed, 97 insertions, 1 deletions
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 1ca958ab40e3..e77be7cb1e60 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h | |||
@@ -1079,6 +1079,12 @@ static inline void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi) | |||
1079 | ((is_inode_flag_set(F2FS_I(i), FI_ACL_MODE)) ? \ | 1079 | ((is_inode_flag_set(F2FS_I(i), FI_ACL_MODE)) ? \ |
1080 | (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) | 1080 | (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) |
1081 | 1081 | ||
1082 | /* get offset of first page in next direct node */ | ||
1083 | #define PGOFS_OF_NEXT_DNODE(pgofs, fi) \ | ||
1084 | ((pgofs < ADDRS_PER_INODE(fi)) ? ADDRS_PER_INODE(fi) : \ | ||
1085 | (pgofs - ADDRS_PER_INODE(fi) + ADDRS_PER_BLOCK) / \ | ||
1086 | ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(fi)) | ||
1087 | |||
1082 | /* | 1088 | /* |
1083 | * file.c | 1089 | * file.c |
1084 | */ | 1090 | */ |
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index bb365c932555..d99d17325046 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c | |||
@@ -194,6 +194,96 @@ out: | |||
194 | return ret; | 194 | return ret; |
195 | } | 195 | } |
196 | 196 | ||
197 | static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) | ||
198 | { | ||
199 | struct inode *inode = file->f_mapping->host; | ||
200 | loff_t maxbytes = inode->i_sb->s_maxbytes; | ||
201 | struct dnode_of_data dn; | ||
202 | pgoff_t pgofs, end_offset; | ||
203 | loff_t data_ofs = offset, isize; | ||
204 | int err = 0; | ||
205 | |||
206 | mutex_lock(&inode->i_mutex); | ||
207 | |||
208 | isize = i_size_read(inode); | ||
209 | if (offset >= isize) | ||
210 | goto fail; | ||
211 | |||
212 | /* handle inline data case */ | ||
213 | if (f2fs_has_inline_data(inode)) { | ||
214 | if (whence == SEEK_HOLE) | ||
215 | data_ofs = isize; | ||
216 | goto found; | ||
217 | } | ||
218 | |||
219 | pgofs = (pgoff_t)(offset >> PAGE_CACHE_SHIFT); | ||
220 | |||
221 | for (; data_ofs < isize; data_ofs = pgofs << PAGE_CACHE_SHIFT) { | ||
222 | set_new_dnode(&dn, inode, NULL, NULL, 0); | ||
223 | err = get_dnode_of_data(&dn, pgofs, LOOKUP_NODE_RA); | ||
224 | if (err && err != -ENOENT) { | ||
225 | goto fail; | ||
226 | } else if (err == -ENOENT) { | ||
227 | /* direct node is not exist */ | ||
228 | if (whence == SEEK_DATA) { | ||
229 | pgofs = PGOFS_OF_NEXT_DNODE(pgofs, | ||
230 | F2FS_I(inode)); | ||
231 | continue; | ||
232 | } else { | ||
233 | goto found; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | end_offset = IS_INODE(dn.node_page) ? | ||
238 | ADDRS_PER_INODE(F2FS_I(inode)) : ADDRS_PER_BLOCK; | ||
239 | |||
240 | /* find data/hole in dnode block */ | ||
241 | for (; dn.ofs_in_node < end_offset; | ||
242 | dn.ofs_in_node++, pgofs++, | ||
243 | data_ofs = pgofs << PAGE_CACHE_SHIFT) { | ||
244 | block_t blkaddr; | ||
245 | blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node); | ||
246 | |||
247 | if ((whence == SEEK_DATA && blkaddr != NULL_ADDR) || | ||
248 | (whence == SEEK_HOLE && blkaddr == NULL_ADDR)) { | ||
249 | f2fs_put_dnode(&dn); | ||
250 | goto found; | ||
251 | } | ||
252 | } | ||
253 | f2fs_put_dnode(&dn); | ||
254 | } | ||
255 | |||
256 | if (whence == SEEK_DATA) | ||
257 | goto fail; | ||
258 | else | ||
259 | data_ofs = isize; | ||
260 | found: | ||
261 | mutex_unlock(&inode->i_mutex); | ||
262 | return vfs_setpos(file, data_ofs, maxbytes); | ||
263 | fail: | ||
264 | mutex_unlock(&inode->i_mutex); | ||
265 | return -ENXIO; | ||
266 | } | ||
267 | |||
268 | static loff_t f2fs_llseek(struct file *file, loff_t offset, int whence) | ||
269 | { | ||
270 | struct inode *inode = file->f_mapping->host; | ||
271 | loff_t maxbytes = inode->i_sb->s_maxbytes; | ||
272 | |||
273 | switch (whence) { | ||
274 | case SEEK_SET: | ||
275 | case SEEK_CUR: | ||
276 | case SEEK_END: | ||
277 | return generic_file_llseek_size(file, offset, whence, | ||
278 | maxbytes, i_size_read(inode)); | ||
279 | case SEEK_DATA: | ||
280 | case SEEK_HOLE: | ||
281 | return f2fs_seek_block(file, offset, whence); | ||
282 | } | ||
283 | |||
284 | return -EINVAL; | ||
285 | } | ||
286 | |||
197 | static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) | 287 | static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) |
198 | { | 288 | { |
199 | file_accessed(file); | 289 | file_accessed(file); |
@@ -675,7 +765,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
675 | #endif | 765 | #endif |
676 | 766 | ||
677 | const struct file_operations f2fs_file_operations = { | 767 | const struct file_operations f2fs_file_operations = { |
678 | .llseek = generic_file_llseek, | 768 | .llseek = f2fs_llseek, |
679 | .read = do_sync_read, | 769 | .read = do_sync_read, |
680 | .write = do_sync_write, | 770 | .write = do_sync_write, |
681 | .aio_read = generic_file_aio_read, | 771 | .aio_read = generic_file_aio_read, |