Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 257393
b: refs/heads/master
c: b267515
h: refs/heads/master
i:
  257391: e1839dd
v: v3
  • Loading branch information
Josef Bacik authored and Al Viro committed Jul 21, 2011
1 parent 7ed529d commit b9b489e
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 982d816581eeeacfe5b2b7c6d47d13a157616eff
refs/heads/master: b26751575a9aa55fd6dbf3febde3ff06dfadc44f
3 changes: 3 additions & 0 deletions trunk/fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -2510,6 +2510,9 @@ int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
struct list_head *list, int search_commit);
/* inode.c */
struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *page,
size_t pg_offset, u64 start, u64 len,
int create);

/* RHEL and EL kernels have a patch that renames PG_checked to FsMisc */
#if defined(ClearPageFsMisc) && !defined(ClearPageChecked)
Expand Down
148 changes: 147 additions & 1 deletion trunk/fs/btrfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -1664,8 +1664,154 @@ static long btrfs_fallocate(struct file *file, int mode,
return ret;
}

static int find_desired_extent(struct inode *inode, loff_t *offset, int origin)
{
struct btrfs_root *root = BTRFS_I(inode)->root;
struct extent_map *em;
struct extent_state *cached_state = NULL;
u64 lockstart = *offset;
u64 lockend = i_size_read(inode);
u64 start = *offset;
u64 orig_start = *offset;
u64 len = i_size_read(inode);
u64 last_end = 0;
int ret = 0;

lockend = max_t(u64, root->sectorsize, lockend);
if (lockend <= lockstart)
lockend = lockstart + root->sectorsize;

len = lockend - lockstart + 1;

len = max_t(u64, len, root->sectorsize);
if (inode->i_size == 0)
return -ENXIO;

lock_extent_bits(&BTRFS_I(inode)->io_tree, lockstart, lockend, 0,
&cached_state, GFP_NOFS);

/*
* Delalloc is such a pain. If we have a hole and we have pending
* delalloc for a portion of the hole we will get back a hole that
* exists for the entire range since it hasn't been actually written
* yet. So to take care of this case we need to look for an extent just
* before the position we want in case there is outstanding delalloc
* going on here.
*/
if (origin == SEEK_HOLE && start != 0) {
if (start <= root->sectorsize)
em = btrfs_get_extent_fiemap(inode, NULL, 0, 0,
root->sectorsize, 0);
else
em = btrfs_get_extent_fiemap(inode, NULL, 0,
start - root->sectorsize,
root->sectorsize, 0);
if (IS_ERR(em)) {
ret = -ENXIO;
goto out;
}
last_end = em->start + em->len;
if (em->block_start == EXTENT_MAP_DELALLOC)
last_end = min_t(u64, last_end, inode->i_size);
free_extent_map(em);
}

while (1) {
em = btrfs_get_extent_fiemap(inode, NULL, 0, start, len, 0);
if (IS_ERR(em)) {
ret = -ENXIO;
break;
}

if (em->block_start == EXTENT_MAP_HOLE) {
if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) {
if (last_end <= orig_start) {
free_extent_map(em);
ret = -ENXIO;
break;
}
}

if (origin == SEEK_HOLE) {
*offset = start;
free_extent_map(em);
break;
}
} else {
if (origin == SEEK_DATA) {
if (em->block_start == EXTENT_MAP_DELALLOC) {
if (start >= inode->i_size) {
free_extent_map(em);
ret = -ENXIO;
break;
}
}

*offset = start;
free_extent_map(em);
break;
}
}

start = em->start + em->len;
last_end = em->start + em->len;

if (em->block_start == EXTENT_MAP_DELALLOC)
last_end = min_t(u64, last_end, inode->i_size);

if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) {
free_extent_map(em);
ret = -ENXIO;
break;
}
free_extent_map(em);
cond_resched();
}
if (!ret)
*offset = min(*offset, inode->i_size);
out:
unlock_extent_cached(&BTRFS_I(inode)->io_tree, lockstart, lockend,
&cached_state, GFP_NOFS);
return ret;
}

static loff_t btrfs_file_llseek(struct file *file, loff_t offset, int origin)
{
struct inode *inode = file->f_mapping->host;
int ret;

mutex_lock(&inode->i_mutex);
switch (origin) {
case SEEK_END:
case SEEK_CUR:
offset = generic_file_llseek_unlocked(file, offset, origin);
goto out;
case SEEK_DATA:
case SEEK_HOLE:
ret = find_desired_extent(inode, &offset, origin);
if (ret) {
mutex_unlock(&inode->i_mutex);
return ret;
}
}

if (offset < 0 && !(file->f_mode & FMODE_UNSIGNED_OFFSET))
return -EINVAL;
if (offset > inode->i_sb->s_maxbytes)
return -EINVAL;

/* Special lock needed here? */
if (offset != file->f_pos) {
file->f_pos = offset;
file->f_version = 0;
}
out:
mutex_unlock(&inode->i_mutex);
return offset;
}

const struct file_operations btrfs_file_operations = {
.llseek = generic_file_llseek,
.llseek = btrfs_file_llseek,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
Expand Down

0 comments on commit b9b489e

Please sign in to comment.