linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/5] fs: add SEEK_HOLE and SEEK_DATA flags
@ 2011-07-18 17:21 Josef Bacik
  2011-07-18 17:21 ` [PATCH 2/5] Btrfs: implement our own ->llseek Josef Bacik
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Josef Bacik @ 2011-07-18 17:21 UTC (permalink / raw)
  To: linux-fsdevel, viro

This just gets us ready to support the SEEK_HOLE and SEEK_DATA flags.  Turns out
using fiemap in things like cp cause more problems than it solves, so lets try
and give userspace an interface that doesn't suck.  We need to match solaris
here, and the definitions are

*o* If /whence/ is SEEK_HOLE, the offset of the start of the
next hole greater than or equal to the supplied offset
is returned. The definition of a hole is provided near
the end of the DESCRIPTION.

*o* If /whence/ is SEEK_DATA, the file pointer is set to the
start of the next non-hole file region greater than or
equal to the supplied offset.

So in the generic case the entire file is data and there is a virtual hole at
the end.  That means we will just return i_size for SEEK_HOLE and will return
the same offset for SEEK_DATA.  This is how Solaris does it so we have to do it
the same way.

Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
---
 Documentation/filesystems/porting |    9 +++++++
 fs/read_write.c                   |   44 ++++++++++++++++++++++++++++++++++--
 include/linux/fs.h                |    4 ++-
 3 files changed, 53 insertions(+), 4 deletions(-)

diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index 6e29954..e24e7e9 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -407,3 +407,12 @@ a file off.
 a matter of switching from calling get_sb_... to mount_... and changing the
 function type.  If you were doing it manually, just switch from setting ->mnt_root
 to some pointer to returning that pointer.  On errors return ERR_PTR(...).
+
+[mandatory]
+	If you implement your own ->llseek() you must handle SEEK_HOLE and
+SEEK_DATA.  You can hanle this by returning -EINVAL, but it would be nicer to
+support it in some way.  The generic handler assumes that the entire file is
+data and there is a virtual hole at the end of the file.  So if the provided
+offset is less than i_size and SEEK_DATA is specified, return the same offset.
+If the above is true for the offset and you are given SEEK_HOLE, return the end
+of the file.  If the offset is i_size or greater return -ENXIO in either case.
diff --git a/fs/read_write.c b/fs/read_write.c
index 5520f8a..5907b49 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -64,6 +64,23 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin)
 			return file->f_pos;
 		offset += file->f_pos;
 		break;
+	case SEEK_DATA:
+		/*
+		 * In the generic case the entire file is data, so as long as
+		 * offset isn't at the end of the file then the offset is data.
+		 */
+		if (offset >= inode->i_size)
+			return -ENXIO;
+		break;
+	case SEEK_HOLE:
+		/*
+		 * There is a virtual hole at the end of the file, so as long as
+		 * offset isn't i_size or larger, return i_size.
+		 */
+		if (offset >= inode->i_size)
+			return -ENXIO;
+		offset = inode->i_size;
+		break;
 	}
 
 	if (offset < 0 && !unsigned_offsets(file))
@@ -128,12 +145,13 @@ EXPORT_SYMBOL(no_llseek);
 
 loff_t default_llseek(struct file *file, loff_t offset, int origin)
 {
+	struct inode *inode = file->f_path.dentry->d_inode;
 	loff_t retval;
 
-	mutex_lock(&file->f_dentry->d_inode->i_mutex);
+	mutex_lock(&inode->i_mutex);
 	switch (origin) {
 		case SEEK_END:
-			offset += i_size_read(file->f_path.dentry->d_inode);
+			offset += i_size_read(inode);
 			break;
 		case SEEK_CUR:
 			if (offset == 0) {
@@ -141,6 +159,26 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin)
 				goto out;
 			}
 			offset += file->f_pos;
+			break;
+		case SEEK_DATA:
+			/*
+			 * In the generic case the entire file is data, so as
+			 * long as offset isn't at the end of the file then the
+			 * offset is data.
+			 */
+			if (offset >= inode->i_size)
+				return -ENXIO;
+			break;
+		case SEEK_HOLE:
+			/*
+			 * There is a virtual hole at the end of the file, so
+			 * as long as offset isn't i_size or larger, return
+			 * i_size.
+			 */
+			if (offset >= inode->i_size)
+				return -ENXIO;
+			offset = inode->i_size;
+			break;
 	}
 	retval = -EINVAL;
 	if (offset >= 0 || unsigned_offsets(file)) {
@@ -151,7 +189,7 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin)
 		retval = offset;
 	}
 out:
-	mutex_unlock(&file->f_dentry->d_inode->i_mutex);
+	mutex_unlock(&inode->i_mutex);
 	return retval;
 }
 EXPORT_SYMBOL(default_llseek);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b5b9792..c9156f3 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -32,7 +32,9 @@
 #define SEEK_SET	0	/* seek relative to beginning of file */
 #define SEEK_CUR	1	/* seek relative to current file position */
 #define SEEK_END	2	/* seek relative to end of file */
-#define SEEK_MAX	SEEK_END
+#define SEEK_DATA	3	/* seek to the next data */
+#define SEEK_HOLE	4	/* seek to the next hole */
+#define SEEK_MAX	SEEK_HOLE
 
 struct fstrim_range {
 	__u64 start;
-- 
1.7.5.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/5] Btrfs: implement our own ->llseek
  2011-07-18 17:21 [PATCH 1/5] fs: add SEEK_HOLE and SEEK_DATA flags Josef Bacik
@ 2011-07-18 17:21 ` Josef Bacik
  2011-07-18 17:21 ` [PATCH 3/5] Ext4: handle SEEK_HOLE/SEEK_DATA generically Josef Bacik
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Josef Bacik @ 2011-07-18 17:21 UTC (permalink / raw)
  To: linux-fsdevel, viro

In order to handle SEEK_HOLE/SEEK_DATA we need to implement our own llseek.
Basically for the normal SEEK_*'s we will just defer to the generic helper, and
for SEEK_HOLE/SEEK_DATA we will use our fiemap helper to figure out the nearest
hole or data.  Currently this helper doesn't check for delalloc bytes for
prealloc space, so for now treat prealloc as data until that is fixed.  Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
---
 fs/btrfs/ctree.h |    3 +
 fs/btrfs/file.c  |  148 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 150 insertions(+), 1 deletions(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 3b859a3..13116f1 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -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)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index fa4ef18..bd4d061 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1664,8 +1664,154 @@ out:
 	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,
-- 
1.7.5.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/5] Ext4: handle SEEK_HOLE/SEEK_DATA generically
  2011-07-18 17:21 [PATCH 1/5] fs: add SEEK_HOLE and SEEK_DATA flags Josef Bacik
  2011-07-18 17:21 ` [PATCH 2/5] Btrfs: implement our own ->llseek Josef Bacik
@ 2011-07-18 17:21 ` Josef Bacik
  2011-07-18 17:21 ` [PATCH 4/5] fs: handle SEEK_HOLE/SEEK_DATA properly in all fs's that define their own llseek Josef Bacik
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Josef Bacik @ 2011-07-18 17:21 UTC (permalink / raw)
  To: linux-fsdevel, viro

Since Ext4 has its own lseek we need to make sure it handles
SEEK_HOLE/SEEK_DATA.  For now just do the same thing that is done in the generic
case, somebody else can come along and make it do fancy things later.  Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
---
 fs/ext4/file.c |   21 +++++++++++++++++++++
 1 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 2c09723..ce766f9 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -236,6 +236,27 @@ loff_t ext4_llseek(struct file *file, loff_t offset, int origin)
 		}
 		offset += file->f_pos;
 		break;
+	case SEEK_DATA:
+		/*
+		 * In the generic case the entire file is data, so as long as
+		 * offset isn't at the end of the file then the offset is data.
+		 */
+		if (offset >= inode->i_size) {
+			mutex_unlock(&inode->i_mutex);
+			return -ENXIO;
+		}
+		break;
+	case SEEK_HOLE:
+		/*
+		 * There is a virtual hole at the end of the file, so as long as
+		 * offset isn't i_size or larger, return i_size.
+		 */
+		if (offset >= inode->i_size) {
+			mutex_unlock(&inode->i_mutex);
+			return -ENXIO;
+		}
+		offset = inode->i_size;
+		break;
 	}
 
 	if (offset < 0 || offset > maxbytes) {
-- 
1.7.5.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/5] fs: handle SEEK_HOLE/SEEK_DATA properly in all fs's that define their own llseek
  2011-07-18 17:21 [PATCH 1/5] fs: add SEEK_HOLE and SEEK_DATA flags Josef Bacik
  2011-07-18 17:21 ` [PATCH 2/5] Btrfs: implement our own ->llseek Josef Bacik
  2011-07-18 17:21 ` [PATCH 3/5] Ext4: handle SEEK_HOLE/SEEK_DATA generically Josef Bacik
@ 2011-07-18 17:21 ` Josef Bacik
  2011-07-18 17:21 ` [PATCH 5/5] drivers: fix up various ->llseek() implementations Josef Bacik
  2011-07-19  4:30 ` [PATCH 1/5] fs: add SEEK_HOLE and SEEK_DATA flags Andreas Dilger
  4 siblings, 0 replies; 6+ messages in thread
From: Josef Bacik @ 2011-07-18 17:21 UTC (permalink / raw)
  To: linux-fsdevel, viro

This converts everybody to handle SEEK_HOLE/SEEK_DATA properly.  In some cases
we just return -EINVAL, in others we do the normal generic thing, and in others
we're simply making sure that the properly due-dilligence is done.  For example
in NFS/CIFS we need to make sure the file size is update properly for the
SEEK_HOLE and SEEK_DATA case, but since it calls the generic llseek stuff itself
that is all we have to do.  Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
---
 fs/block_dev.c   |   11 ++++++++---
 fs/ceph/dir.c    |    8 +++++++-
 fs/ceph/file.c   |   20 ++++++++++++++++++--
 fs/cifs/cifsfs.c |    7 +++++--
 fs/fuse/file.c   |   21 +++++++++++++++++++--
 fs/hpfs/dir.c    |    4 ++++
 fs/nfs/file.c    |    7 +++++--
 7 files changed, 66 insertions(+), 12 deletions(-)

diff --git a/fs/block_dev.c b/fs/block_dev.c
index 610e8e0..966617a 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -355,20 +355,25 @@ static loff_t block_llseek(struct file *file, loff_t offset, int origin)
 	mutex_lock(&bd_inode->i_mutex);
 	size = i_size_read(bd_inode);
 
+	retval = -EINVAL;
 	switch (origin) {
-		case 2:
+		case SEEK_END:
 			offset += size;
 			break;
-		case 1:
+		case SEEK_CUR:
 			offset += file->f_pos;
+		case SEEK_SET:
+			break;
+		default:
+			goto out;
 	}
-	retval = -EINVAL;
 	if (offset >= 0 && offset <= size) {
 		if (offset != file->f_pos) {
 			file->f_pos = offset;
 		}
 		retval = offset;
 	}
+out:
 	mutex_unlock(&bd_inode->i_mutex);
 	return retval;
 }
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index ef8f08c..79cd77c 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -446,14 +446,19 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)
 	loff_t retval;
 
 	mutex_lock(&inode->i_mutex);
+	retval = -EINVAL;
 	switch (origin) {
 	case SEEK_END:
 		offset += inode->i_size + 2;   /* FIXME */
 		break;
 	case SEEK_CUR:
 		offset += file->f_pos;
+	case SEEK_SET:
+		break;
+	default:
+		goto out;
 	}
-	retval = -EINVAL;
+
 	if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) {
 		if (offset != file->f_pos) {
 			file->f_pos = offset;
@@ -477,6 +482,7 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin)
 		if (offset > old_offset)
 			fi->dir_release_count--;
 	}
+out:
 	mutex_unlock(&inode->i_mutex);
 	return retval;
 }
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 4698a5c..7d5d06e 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -768,13 +768,16 @@ static loff_t ceph_llseek(struct file *file, loff_t offset, int origin)
 
 	mutex_lock(&inode->i_mutex);
 	__ceph_do_pending_vmtruncate(inode);
-	switch (origin) {
-	case SEEK_END:
+	if (origin != SEEK_CUR || origin != SEEK_SET) {
 		ret = ceph_do_getattr(inode, CEPH_STAT_CAP_SIZE);
 		if (ret < 0) {
 			offset = ret;
 			goto out;
 		}
+	}
+
+	switch (origin) {
+	case SEEK_END:
 		offset += inode->i_size;
 		break;
 	case SEEK_CUR:
@@ -790,6 +793,19 @@ static loff_t ceph_llseek(struct file *file, loff_t offset, int origin)
 		}
 		offset += file->f_pos;
 		break;
+	case SEEK_DATA:
+		if (offset >= inode->i_size) {
+			ret = -ENXIO;
+			goto out;
+		}
+		break;
+	case SEEK_HOLE:
+		if (offset >= inode->i_size) {
+			ret = -ENXIO;
+			goto out;
+		}
+		offset = inode->i_size;
+		break;
 	}
 
 	if (offset < 0 || offset > inode->i_sb->s_maxbytes) {
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 3e29899..fc05935 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -746,8 +746,11 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
 
 static loff_t cifs_llseek(struct file *file, loff_t offset, int origin)
 {
-	/* origin == SEEK_END => we must revalidate the cached file length */
-	if (origin == SEEK_END) {
+	/*
+	 * origin == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate
+	 * the cached file length
+	 */
+	if (origin != SEEK_SET || origin != SEEK_CUR) {
 		int rc;
 		struct inode *inode = file->f_path.dentry->d_inode;
 
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 82a6646..73b89df 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1600,15 +1600,32 @@ static loff_t fuse_file_llseek(struct file *file, loff_t offset, int origin)
 	struct inode *inode = file->f_path.dentry->d_inode;
 
 	mutex_lock(&inode->i_mutex);
-	switch (origin) {
-	case SEEK_END:
+	if (origin != SEEK_CUR || origin != SEEK_SET) {
 		retval = fuse_update_attributes(inode, NULL, file, NULL);
 		if (retval)
 			goto exit;
+	}
+
+	switch (origin) {
+	case SEEK_END:
 		offset += i_size_read(inode);
 		break;
 	case SEEK_CUR:
 		offset += file->f_pos;
+		break;
+	case SEEK_DATA:
+		if (offset >= i_size_read(inode)) {
+			retval = -ENXIO;
+			goto exit;
+		}
+		break;
+	case SEEK_HOLE:
+		if (offset >= i_size_read(inode)) {
+			retval = -ENXIO;
+			goto exit;
+		}
+		offset = i_size_read(inode);
+		break;
 	}
 	retval = -EINVAL;
 	if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) {
diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c
index f46ae02..96a8ed9 100644
--- a/fs/hpfs/dir.c
+++ b/fs/hpfs/dir.c
@@ -29,6 +29,10 @@ static loff_t hpfs_dir_lseek(struct file *filp, loff_t off, int whence)
 	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
 	struct super_block *s = i->i_sb;
 
+	/* Somebody else will have to figure out what to do here */
+	if (whence == SEEK_DATA || whence == SEEK_HOLE)
+		return -EINVAL;
+
 	hpfs_lock(s);
 
 	/*printk("dir lseek\n");*/
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 2f093ed..2c1705b 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -187,8 +187,11 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
 			filp->f_path.dentry->d_name.name,
 			offset, origin);
 
-	/* origin == SEEK_END => we must revalidate the cached file length */
-	if (origin == SEEK_END) {
+	/*
+	 * origin == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate
+	 * the cached file length
+	 */
+	if (origin != SEEK_SET || origin != SEEK_CUR) {
 		struct inode *inode = filp->f_mapping->host;
 
 		int retval = nfs_revalidate_file_size(inode, filp);
-- 
1.7.5.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 5/5] drivers: fix up various ->llseek() implementations
  2011-07-18 17:21 [PATCH 1/5] fs: add SEEK_HOLE and SEEK_DATA flags Josef Bacik
                   ` (2 preceding siblings ...)
  2011-07-18 17:21 ` [PATCH 4/5] fs: handle SEEK_HOLE/SEEK_DATA properly in all fs's that define their own llseek Josef Bacik
@ 2011-07-18 17:21 ` Josef Bacik
  2011-07-19  4:30 ` [PATCH 1/5] fs: add SEEK_HOLE and SEEK_DATA flags Andreas Dilger
  4 siblings, 0 replies; 6+ messages in thread
From: Josef Bacik @ 2011-07-18 17:21 UTC (permalink / raw)
  To: linux-fsdevel, viro

Fix up a few ->llseek() implementations that won't deal with SEEK_HOLE/SEEK_DATA
properly.  Make them future proof so that if we ever add new options they will
return -EINVAL.  Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
---
 drivers/char/generic_nvram.c |    4 ++++
 drivers/char/nvram.c         |    2 ++
 drivers/char/ps3flash.c      |    4 ++++
 drivers/macintosh/nvram.c    |    4 ++++
 4 files changed, 14 insertions(+), 0 deletions(-)

diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c
index 0e941b5..6c4f4b5 100644
--- a/drivers/char/generic_nvram.c
+++ b/drivers/char/generic_nvram.c
@@ -34,12 +34,16 @@ static ssize_t nvram_len;
 static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
 {
 	switch (origin) {
+	case 0:
+		break;
 	case 1:
 		offset += file->f_pos;
 		break;
 	case 2:
 		offset += nvram_len;
 		break;
+	default:
+		offset = -1;
 	}
 	if (offset < 0)
 		return -EINVAL;
diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c
index 166f1e7..da3cfee 100644
--- a/drivers/char/nvram.c
+++ b/drivers/char/nvram.c
@@ -224,6 +224,8 @@ static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
 	case 2:
 		offset += NVRAM_BYTES;
 		break;
+	default:
+		return -EINVAL;
 	}
 
 	return (offset >= 0) ? (file->f_pos = offset) : -EINVAL;
diff --git a/drivers/char/ps3flash.c b/drivers/char/ps3flash.c
index 85c004a..5a06787 100644
--- a/drivers/char/ps3flash.c
+++ b/drivers/char/ps3flash.c
@@ -101,12 +101,16 @@ static loff_t ps3flash_llseek(struct file *file, loff_t offset, int origin)
 
 	mutex_lock(&file->f_mapping->host->i_mutex);
 	switch (origin) {
+	case 0:
+		break;
 	case 1:
 		offset += file->f_pos;
 		break;
 	case 2:
 		offset += dev->regions[dev->region_idx].size*dev->blk_size;
 		break;
+	default:
+		offset = -1;
 	}
 	if (offset < 0) {
 		res = -EINVAL;
diff --git a/drivers/macintosh/nvram.c b/drivers/macintosh/nvram.c
index a271c82..f0e03e7 100644
--- a/drivers/macintosh/nvram.c
+++ b/drivers/macintosh/nvram.c
@@ -21,12 +21,16 @@
 static loff_t nvram_llseek(struct file *file, loff_t offset, int origin)
 {
 	switch (origin) {
+	case 0:
+		break;
 	case 1:
 		offset += file->f_pos;
 		break;
 	case 2:
 		offset += NVRAM_SIZE;
 		break;
+	default:
+		offset = -1;
 	}
 	if (offset < 0)
 		return -EINVAL;
-- 
1.7.5.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/5] fs: add SEEK_HOLE and SEEK_DATA flags
  2011-07-18 17:21 [PATCH 1/5] fs: add SEEK_HOLE and SEEK_DATA flags Josef Bacik
                   ` (3 preceding siblings ...)
  2011-07-18 17:21 ` [PATCH 5/5] drivers: fix up various ->llseek() implementations Josef Bacik
@ 2011-07-19  4:30 ` Andreas Dilger
  4 siblings, 0 replies; 6+ messages in thread
From: Andreas Dilger @ 2011-07-19  4:30 UTC (permalink / raw)
  To: Josef Bacik; +Cc: linux-fsdevel@vger.kernel.org, viro@ZenIV.linux.org.uk

On 2011-07-18, at 11:21 AM, Josef Bacik <josef@redhat.com> wrote:

> This just gets us ready to support the SEEK_HOLE and SEEK_DATA flags.  Turns out
> using fiemap in things like cp cause more problems than it solves, so lets try
> and give userspace an interface that doesn't suck.  We need to match solaris
> here, and the definitions are
> 
> *o* If /whence/ is SEEK_HOLE, the offset of the start of the
> next hole greater than or equal to the supplied offset
> is returned. The definition of a hole is provided near
> the end of the DESCRIPTION.
> 
> *o* If /whence/ is SEEK_DATA, the file pointer is set to the
> start of the next non-hole file region greater than or
> equal to the supplied offset.
> 
> So in the generic case the entire file is data and there is a virtual hole at
> the end.  That means we will just return i_size for SEEK_HOLE and will return
> the same offset for SEEK_DATA.  This is how Solaris does it so we have to do it
> the same way.
> 
> Thanks,
> 
> Signed-off-by: Josef Bacik <josef@redhat.com>
> ---
> Documentation/filesystems/porting |    9 +++++++
> fs/read_write.c                   |   44 ++++++++++++++++++++++++++++++++++--
> include/linux/fs.h                |    4 ++-
> 3 files changed, 53 insertions(+), 4 deletions(-)
> 
> diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
> index 6e29954..e24e7e9 100644
> --- a/Documentation/filesystems/porting
> +++ b/Documentation/filesystems/porting
> @@ -407,3 +407,12 @@ a file off.
> a matter of switching from calling get_sb_... to mount_... and changing the
> function type.  If you were doing it manually, just switch from setting ->mnt_root
> to some pointer to returning that pointer.  On errors return ERR_PTR(...).
> +
> +[mandatory]
> +    If you implement your own ->llseek() you must handle SEEK_HOLE and
> +SEEK_DATA.  You can hanle this by returning -EINVAL, but it would be nicer to
> +support it in some way.  The generic handler assumes that the entire file is
> +data and there is a virtual hole at the end of the file.  So if the provided
> +offset is less than i_size and SEEK_DATA is specified, return the same offset.
> +If the above is true for the offset and you are given SEEK_HOLE, return the end
> +of the file.  If the offset is i_size or greater return -ENXIO in either case.

Rather than documenting (only) the way to have a "compliant but not useful SEEK_{HOLE,DATA} implementation, it makes sense to document the desired implementation here as well (what is in the commit comment).

> diff --git a/fs/read_write.c b/fs/read_write.c
> index 5520f8a..5907b49 100644
> --- a/fs/read_write.c
> +++ b/fs/read_write.c
> @@ -64,6 +64,23 @@ generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin)
>            return file->f_pos;
>        offset += file->f_pos;
>        break;
> +    case SEEK_DATA:
> +        /*
> +         * In the generic case the entire file is data, so as long as
> +         * offset isn't at the end of the file then the offset is data.
> +         */
> +        if (offset >= inode->i_size)
> +            return -ENXIO;
> +        break;
> +    case SEEK_HOLE:
> +        /*
> +         * There is a virtual hole at the end of the file, so as long as
> +         * offset isn't i_size or larger, return i_size.
> +         */
> +        if (offset >= inode->i_size)
> +            return -ENXIO;
> +        offset = inode->i_size;
> +        break;
>    }
> 
>    if (offset < 0 && !unsigned_offsets(file))
> @@ -128,12 +145,13 @@ EXPORT_SYMBOL(no_llseek);
> 
> loff_t default_llseek(struct file *file, loff_t offset, int origin)
> {
> +    struct inode *inode = file->f_path.dentry->d_inode;
>    loff_t retval;
> 
> -    mutex_lock(&file->f_dentry->d_inode->i_mutex);
> +    mutex_lock(&inode->i_mutex);
>    switch (origin) {
>        case SEEK_END:
> -            offset += i_size_read(file->f_path.dentry->d_inode);
> +            offset += i_size_read(inode);
>            break;
>        case SEEK_CUR:
>            if (offset == 0) {
> @@ -141,6 +159,26 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin)
>                goto out;
>            }
>            offset += file->f_pos;
> +            break;
> +        case SEEK_DATA:
> +            /*
> +             * In the generic case the entire file is data, so as
> +             * long as offset isn't at the end of the file then the
> +             * offset is data.
> +             */
> +            if (offset >= inode->i_size)
> +                return -ENXIO;
> +            break;
> +        case SEEK_HOLE:
> +            /*
> +             * There is a virtual hole at the end of the file, so
> +             * as long as offset isn't i_size or larger, return
> +             * i_size.
> +             */
> +            if (offset >= inode->i_size)
> +                return -ENXIO;
> +            offset = inode->i_size;
> +            break;
>    }
>    retval = -EINVAL;
>    if (offset >= 0 || unsigned_offsets(file)) {
> @@ -151,7 +189,7 @@ loff_t default_llseek(struct file *file, loff_t offset, int origin)
>        retval = offset;
>    }
> out:
> -    mutex_unlock(&file->f_dentry->d_inode->i_mutex);
> +    mutex_unlock(&inode->i_mutex);
>    return retval;
> }
> EXPORT_SYMBOL(default_llseek);
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index b5b9792..c9156f3 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -32,7 +32,9 @@
> #define SEEK_SET    0    /* seek relative to beginning of file */
> #define SEEK_CUR    1    /* seek relative to current file position */
> #define SEEK_END    2    /* seek relative to end of file */
> -#define SEEK_MAX    SEEK_END
> +#define SEEK_DATA    3    /* seek to the next data */
> +#define SEEK_HOLE    4    /* seek to the next hole */
> +#define SEEK_MAX    SEEK_HOLE
> 
> struct fstrim_range {
>    __u64 start;
> -- 
> 1.7.5.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2011-07-19  4:40 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-07-18 17:21 [PATCH 1/5] fs: add SEEK_HOLE and SEEK_DATA flags Josef Bacik
2011-07-18 17:21 ` [PATCH 2/5] Btrfs: implement our own ->llseek Josef Bacik
2011-07-18 17:21 ` [PATCH 3/5] Ext4: handle SEEK_HOLE/SEEK_DATA generically Josef Bacik
2011-07-18 17:21 ` [PATCH 4/5] fs: handle SEEK_HOLE/SEEK_DATA properly in all fs's that define their own llseek Josef Bacik
2011-07-18 17:21 ` [PATCH 5/5] drivers: fix up various ->llseek() implementations Josef Bacik
2011-07-19  4:30 ` [PATCH 1/5] fs: add SEEK_HOLE and SEEK_DATA flags Andreas Dilger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).