Subject: RFC: lseek() extension for sparse files version 3 + UFS implementation
To: None <tech-kern@NetBSD.org>
From: Reinoud Zandijk <reinoud@netbsd.org>
List: tech-kern
Date: 09/26/2006 21:36:04
--f2QGlHpHGjS2mn6Y
Content-Type: multipart/mixed; boundary="pWyiEgJYm5f9v55/"
Content-Disposition: inline


--pWyiEgJYm5f9v55/
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Dear folks,

thanks for the feedback on the former two patches. I've created a hopefully 
final patch for RFC to implement the Solaris lseek() extension to handle 
sparse files.

I've also included an UFS implementation at request that is thus 
implemented for ffs, lfs and ext2fs. The patch only works on the inode 
datastructure and is FFS agnostic and thus should work for all UFS since 
neither lfs nor ext2fs uses a special routine.

If there are no objections i'd like to submit it. So please try it out! :) 
Attached is also a test-program that uses the new lseek() primitives to 
copy a sparse file.

With regards,
Reinoud


--pWyiEgJYm5f9v55/
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=DIFFS-sparse-20060926b

Index: include/stdio.h
===================================================================
RCS file: /cvsroot/src/include/stdio.h,v
retrieving revision 1.64
diff -u -p -r1.64 stdio.h
--- include/stdio.h	10 May 2006 21:09:45 -0000	1.64
+++ include/stdio.h	26 Sep 2006 16:59:02 -0000
@@ -203,6 +203,12 @@ __END_DECLS
 #ifndef SEEK_END
 #define	SEEK_END	2	/* set file offset to EOF plus offset */
 #endif
+#ifndef SEEK_DATA
+#define	SEEK_DATA	3	/* Set file pointer to next data past offset */
+#endif
+#ifndef SEEK_HOLE
+#define	SEEK_HOLE	4	/* Set file pointer to next hole past offset */
+#endif
 
 #define	stdin	(&__sF[0])
 #define	stdout	(&__sF[1])
Index: lib/libc/sys/lseek.2
===================================================================
RCS file: /cvsroot/src/lib/libc/sys/lseek.2,v
retrieving revision 1.22
diff -u -p -r1.22 lseek.2
--- lib/libc/sys/lseek.2	13 May 2004 10:20:58 -0000	1.22
+++ lib/libc/sys/lseek.2	26 Sep 2006 16:59:06 -0000
@@ -29,7 +29,7 @@
 .\"
 .\"     @(#)lseek.2	8.3 (Berkeley) 4/19/94
 .\"
-.Dd April 19, 1994
+.Dd September 21, 2006
 .Dt LSEEK 2
 .Os
 .Sh NAME
@@ -86,6 +86,29 @@ the offset is set to the size of the
 file plus
 .Fa offset
 bytes.
+.It
+If
+.Fa whence
+is
+.Dv SEEK_DATA ,
+the offset is set to the next non-hole region which file offset is
+greater or equal to the provided
+.Fa offset
+in bytes. If specifying an
+.Fa offset
+of 0 bytes, there is guaranteed to be a data region for easy programming.
+.It
+If
+.Fa whence
+is
+.Dv SEEK_HOLE ,
+the offset is set to the next hole region which file offset is
+greater or equal to the provided
+.Fa offset
+in bytes. If specifying an
+.Fa offset
+within the boundaries of the file, there is a guaranteed virtual hole at the
+end of the file for easy programming.
 .El
 .Pp
 The
@@ -121,6 +144,9 @@ is associated with a pipe, socket, or FI
 .It Bq Er EINVAL
 .Fa whence
 is not a proper value, or the resulting file offset would be invalid.
+.It Bq Er ENXIO
+No more data regions or hole regions are present past the supplied
+.Fa offset
 .El
 .Sh SEE ALSO
 .Xr dup 2 ,
@@ -129,8 +155,18 @@ is not a proper value, or the resulting 
 The
 .Fn lseek
 function conforms to
-.St -p1003.1-90 .
+.St -p1003.1-90 . The
+.Dv SEEK_DATA
+and
+.Dv SEEK_HOLE
+conform to the Solaris 10 implemention.
 .Sh BUGS
 This document's use of
 .Fa whence
 is incorrect English, but is maintained for historical reasons.
+.Sh HISTORY
+The
+.Dv SEEK_DATA
+and
+.Dv SEEK_HOLE
+functionality was added in NetBSD 5.0
Index: share/man/man9/vnodeops.9
===================================================================
RCS file: /cvsroot/src/share/man/man9/vnodeops.9,v
retrieving revision 1.51
diff -u -p -r1.51 vnodeops.9
--- share/man/man9/vnodeops.9	16 Sep 2006 08:54:22 -0000	1.51
+++ share/man/man9/vnodeops.9	26 Sep 2006 16:59:14 -0000
@@ -146,8 +146,8 @@
 .Fn VOP_FSYNC "struct vnode *vp" "struct ucred *cred" "int flags" \
 "off_t offlo" "off_t offhi" "struct lwp *l"
 .Ft int
-.Fn VOP_SEEK "struct vnode *vp" "off_t oldoff" "off_t newoff" \
-"struct ucred *cred"
+.Fn VOP_SEEK "struct vnode *vp" "off_t oldoffset" "int whence" \
+"off_t givenoffset" "off_t *newoffset" "struct ucred *cred"
 .Ft int
 .Fn VOP_REMOVE "struct vnode *vp" "struct vnode *vp" \
 "struct componentname *cnp"
@@ -828,18 +828,32 @@ This function implements the
 and
 .Xr fsync 2
 system calls.
-.It Fn VOP_SEEK "vp" "oldoff" "newoff" "cred"
-Test if the file is seekable for the specified offset
-.Fa newoff .
+.It Fn VOP_SEEK "vp" "oldoffset" "whence" "givenoffset" "*newoffset" "cred"
+Implements the
+.Xr lseek 1
+function on a node. It tests if the file is seekable for the specified offset
+.Fa givenoffset
+with the method 
+.Fa whence .
 The argument
 .Fa vp
 is the locked vnode of the file to test.
-For most filesystems this function simply tests if
-.Fa newoff
-is valid.
-If the specified
-.Fa newoff
-is less than zero, the function returns error code EINVAL.
+Filesystems should return the new offset in
+.Fa *newoffset
+and return EINVAL if the resulting offset is invalid. If
+.Fa *newoffset
+is
+.Dv NULL
+no value needs to be returned. If
+.Fa *vattr
+is
+.Dv NULL
+, only
+.Dv SEEK_SET
+and
+.Dv SEEK_CUR
+are valid operations for
+.Fa whence .
 .It Fn VOP_REMOVE "dvp" "vp" "cnp"
 Remove a file.
 The argument
@@ -1462,6 +1476,9 @@ input
 .It Bq Er EIO
 a read error occurred while reading the directory or reading the
 contents of a symbolic link
+.It Bq Er ENXIO
+a seek for a data segment or hole segment in a file indicated no more segments
+from the given offset are defined.
 .It Bq Er EROFS
 the filesystem is read-only
 .El
Index: sys/fs/ptyfs/ptyfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/fs/ptyfs/ptyfs_vnops.c,v
retrieving revision 1.16
diff -u -p -r1.16 ptyfs_vnops.c
--- sys/fs/ptyfs/ptyfs_vnops.c	23 Jul 2006 22:06:10 -0000	1.16
+++ sys/fs/ptyfs/ptyfs_vnops.c	26 Sep 2006 16:59:32 -0000
@@ -128,7 +128,7 @@ int	ptyfs_kqfilter	(void *);
 #define ptyfs_revoke	genfs_revoke
 #define	ptyfs_mmap	genfs_eopnotsupp
 #define	ptyfs_fsync	genfs_nullop
-#define	ptyfs_seek	genfs_nullop
+#define	ptyfs_seek	genfs_seek
 #define	ptyfs_remove	genfs_eopnotsupp
 #define	ptyfs_link	genfs_abortop
 #define	ptyfs_rename	genfs_eopnotsupp
Index: sys/fs/union/union_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/fs/union/union_vnops.c,v
retrieving revision 1.16
diff -u -p -r1.16 union_vnops.c
--- sys/fs/union/union_vnops.c	14 May 2006 21:31:52 -0000	1.16
+++ sys/fs/union/union_vnops.c	26 Sep 2006 16:59:33 -0000
@@ -1207,8 +1207,10 @@ union_seek(v)
 {
 	struct vop_seek_args /* {
 		struct vnode *a_vp;
-		off_t  a_oldoff;
-		off_t  a_newoff;
+		off_t a_oldoffset;
+		int a_whence;
+		off_t a_givenoffset;
+		off_t *a_newoffset;
 		kauth_cred_t a_cred;
 	} */ *ap = v;
 	struct vnode *ovp = OTHERVP(ap->a_vp);
Index: sys/kern/vfs_syscalls.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_syscalls.c,v
retrieving revision 1.270
diff -u -p -r1.270 vfs_syscalls.c
--- sys/kern/vfs_syscalls.c	13 Sep 2006 10:07:42 -0000	1.270
+++ sys/kern/vfs_syscalls.c	26 Sep 2006 16:59:33 -0000
@@ -2117,8 +2117,7 @@ sys_lseek(struct lwp *l, void *v, regist
 	struct filedesc *fdp = p->p_fd;
 	struct file *fp;
 	struct vnode *vp;
-	struct vattr vattr;
-	off_t newoff;
+	off_t newoffset;
 	int error;
 
 	if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
@@ -2129,31 +2128,14 @@ sys_lseek(struct lwp *l, void *v, regist
 	vp = (struct vnode *)fp->f_data;
 	if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) {
 		error = ESPIPE;
-		goto out;
+	} else {
+		error = VOP_SEEK(vp, fp->f_offset, SCARG(uap, whence),
+		    SCARG(uap, offset), &newoffset, cred);
 	}
 
-	switch (SCARG(uap, whence)) {
-	case SEEK_CUR:
-		newoff = fp->f_offset + SCARG(uap, offset);
-		break;
-	case SEEK_END:
-		error = VOP_GETATTR(vp, &vattr, cred, l);
-		if (error)
-			goto out;
-		newoff = SCARG(uap, offset) + vattr.va_size;
-		break;
-	case SEEK_SET:
-		newoff = SCARG(uap, offset);
-		break;
-	default:
-		error = EINVAL;
-		goto out;
-	}
-	if ((error = VOP_SEEK(vp, fp->f_offset, newoff, cred)) != 0)
-		goto out;
+	if (error == 0)
+		*(off_t *)retval = fp->f_offset = newoffset;
 
-	*(off_t *)retval = fp->f_offset = newoff;
- out:
 	FILE_UNUSE(fp, l);
 	return (error);
 }
@@ -2170,6 +2152,7 @@ sys_pread(struct lwp *l, void *v, regist
 		syscallarg(size_t) nbyte;
 		syscallarg(off_t) offset;
 	} */ *uap = v;
+	kauth_cred_t cred = l->l_cred;
 	struct proc *p = l->l_proc;
 	struct filedesc *fdp = p->p_fd;
 	struct file *fp;
@@ -2199,7 +2182,8 @@ sys_pread(struct lwp *l, void *v, regist
 	 * XXX This works because no file systems actually
 	 * XXX take any action on the seek operation.
 	 */
-	if ((error = VOP_SEEK(vp, fp->f_offset, offset, fp->f_cred)) != 0)
+	error = VOP_SEEK(vp, fp->f_offset, SEEK_SET, offset, NULL, cred);
+	if (error != 0)
 		goto out;
 
 	/* dofileread() will unuse the descriptor for us */
@@ -2223,6 +2207,7 @@ sys_preadv(struct lwp *l, void *v, regis
 		syscallarg(int) iovcnt;
 		syscallarg(off_t) offset;
 	} */ *uap = v;
+	kauth_cred_t cred = l->l_cred;
 	struct proc *p = l->l_proc;
 	struct filedesc *fdp = p->p_fd;
 	struct file *fp;
@@ -2252,7 +2237,8 @@ sys_preadv(struct lwp *l, void *v, regis
 	 * XXX This works because no file systems actually
 	 * XXX take any action on the seek operation.
 	 */
-	if ((error = VOP_SEEK(vp, fp->f_offset, offset, fp->f_cred)) != 0)
+	error = VOP_SEEK(vp, fp->f_offset, SEEK_SET, offset, NULL, cred);
+	if (error != 0)
 		goto out;
 
 	/* dofilereadv() will unuse the descriptor for us */
@@ -2276,6 +2262,7 @@ sys_pwrite(struct lwp *l, void *v, regis
 		syscallarg(size_t) nbyte;
 		syscallarg(off_t) offset;
 	} */ *uap = v;
+	kauth_cred_t cred = l->l_cred;
 	struct proc *p = l->l_proc;
 	struct filedesc *fdp = p->p_fd;
 	struct file *fp;
@@ -2305,7 +2292,8 @@ sys_pwrite(struct lwp *l, void *v, regis
 	 * XXX This works because no file systems actually
 	 * XXX take any action on the seek operation.
 	 */
-	if ((error = VOP_SEEK(vp, fp->f_offset, offset, fp->f_cred)) != 0)
+	error = VOP_SEEK(vp, fp->f_offset, SEEK_SET, offset, NULL, cred);
+	if (error != 0)
 		goto out;
 
 	/* dofilewrite() will unuse the descriptor for us */
@@ -2329,6 +2317,7 @@ sys_pwritev(struct lwp *l, void *v, regi
 		syscallarg(int) iovcnt;
 		syscallarg(off_t) offset;
 	} */ *uap = v;
+	kauth_cred_t cred = l->l_cred;
 	struct proc *p = l->l_proc;
 	struct filedesc *fdp = p->p_fd;
 	struct file *fp;
@@ -2358,7 +2347,8 @@ sys_pwritev(struct lwp *l, void *v, regi
 	 * XXX This works because no file systems actually
 	 * XXX take any action on the seek operation.
 	 */
-	if ((error = VOP_SEEK(vp, fp->f_offset, offset, fp->f_cred)) != 0)
+	error = VOP_SEEK(vp, fp->f_offset, SEEK_SET, offset, NULL, cred);
+	if (error != 0)
 		goto out;
 
 	/* dofilewritev() will unuse the descriptor for us */
Index: sys/kern/vnode_if.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vnode_if.c,v
retrieving revision 1.66
diff -u -p -r1.66 vnode_if.c
--- sys/kern/vnode_if.c	13 Jul 2006 12:00:25 -0000	1.66
+++ sys/kern/vnode_if.c	26 Sep 2006 16:59:33 -0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: vnode_if.c,v 1.66 2006/07/13 12:00:25 martin Exp $	*/
+/*	$NetBSD$	*/
 
 /*
  * Warning: DO NOT EDIT! This file is automatically generated!
@@ -40,7 +40,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vnode_if.c,v 1.66 2006/07/13 12:00:25 martin Exp $");
+__KERNEL_RCSID(0, "$NetBSD$");
 
 
 /*
@@ -741,8 +741,10 @@ const struct vnodeop_desc vop_seek_desc 
 };
 int
 VOP_SEEK(struct vnode *vp,
-    off_t oldoff,
-    off_t newoff,
+    off_t oldoffset,
+    int whence,
+    off_t givenoffset,
+    off_t *newoffset,
     kauth_cred_t cred)
 {
 	struct vop_seek_args a;
@@ -750,8 +752,10 @@ VOP_SEEK(struct vnode *vp,
 #endif
 	a.a_desc = VDESC(vop_seek);
 	a.a_vp = vp;
-	a.a_oldoff = oldoff;
-	a.a_newoff = newoff;
+	a.a_oldoffset = oldoffset;
+	a.a_whence = whence;
+	a.a_givenoffset = givenoffset;
+	a.a_newoffset = newoffset;
 	a.a_cred = cred;
 	return (VCALL(vp, VOFFSET(vop_seek), &a));
 }
Index: sys/kern/vnode_if.src
===================================================================
RCS file: /cvsroot/src/sys/kern/vnode_if.src,v
retrieving revision 1.50
diff -u -p -r1.50 vnode_if.src
--- sys/kern/vnode_if.src	14 May 2006 21:15:12 -0000	1.50
+++ sys/kern/vnode_if.src	26 Sep 2006 16:59:34 -0000
@@ -266,13 +266,14 @@ vop_fsync {
 };
 
 #
-# Needs work: Is newoff right?  What's it mean?
 # XXX Locking protocol?
 #
 vop_seek {
 	IN struct vnode *vp;
-	IN off_t oldoff;
-	IN off_t newoff;
+	IN off_t oldoffset;
+	IN int whence;
+	IN off_t givenoffset;
+	OUT off_t *newoffset;
 	IN kauth_cred_t cred;
 };
 
Index: sys/miscfs/deadfs/dead_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/deadfs/dead_vnops.c,v
retrieving revision 1.39
diff -u -p -r1.39 dead_vnops.c
--- sys/miscfs/deadfs/dead_vnops.c	14 May 2006 21:31:52 -0000	1.39
+++ sys/miscfs/deadfs/dead_vnops.c	26 Sep 2006 16:59:34 -0000
@@ -64,7 +64,7 @@ int	dead_ioctl(void *);
 int	dead_poll(void *);
 #define dead_mmap	genfs_badop
 #define dead_fsync	genfs_nullop
-#define dead_seek	genfs_nullop
+#define dead_seek	genfs_nullop	/* no size -> genfs_nullop allowed */
 #define dead_remove	genfs_badop
 #define dead_link	genfs_badop
 #define dead_rename	genfs_badop
Index: sys/miscfs/genfs/genfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/genfs/genfs_vnops.c,v
retrieving revision 1.129
diff -u -p -r1.129 genfs_vnops.c
--- sys/miscfs/genfs/genfs_vnops.c	15 Sep 2006 15:51:12 -0000	1.129
+++ sys/miscfs/genfs/genfs_vnops.c	26 Sep 2006 16:59:34 -0000
@@ -50,6 +50,7 @@ __KERNEL_RCSID(0, "$NetBSD: genfs_vnops.
 #include <sys/mman.h>
 #include <sys/file.h>
 #include <sys/kauth.h>
+#include <sys/unistd.h>
 
 #include <miscfs/genfs/genfs.h>
 #include <miscfs/genfs/genfs_node.h>
@@ -90,15 +91,55 @@ genfs_seek(void *v)
 {
 	struct vop_seek_args /* {
 		struct vnode *a_vp;
-		off_t a_oldoff;
-		off_t a_newoff;
-		kauth_cred_t cred;
-	} */ *ap = v;
+		off_t a_oldoffset;
+		int a_whence;
+		off_t a_givenoffset;
+		off_t *a_newoffset;
+		kauth_cred_t a_cred;
+	}; */ *ap = v;
+	off_t newoffset;
+	struct vattr vattr;
+	int error;
+
+	error = VOP_GETATTR(ap->a_vp, &vattr, ap->a_cred, curlwp);
+	if (error)
+		return error;
+
+	/* initialise return value with old offset */
+	newoffset = ap->a_oldoffset;
+	switch (ap->a_whence) {
+	case SEEK_CUR:
+		newoffset = ap->a_oldoffset + ap->a_givenoffset;
+		break;
+	case SEEK_END:
+		newoffset = ap->a_givenoffset + vattr.va_size;
+		break;
+	case SEEK_SET:
+		newoffset = ap->a_givenoffset;
+		break;
+	case SEEK_DATA:
+		/* if in the file space, there is one data block */
+		if (ap->a_givenoffset >= vattr.va_size)
+			return ENXIO;
+		newoffset = ap->a_givenoffset;
+		break;
+	case SEEK_HOLE:
+		/* there exists one virtual hole at the end of the file */
+		if (ap->a_givenoffset > vattr.va_size)
+			return ENXIO;
+		newoffset = vattr.va_size;
+		break;
+	default:
+		return EINVAL;
+	}
 
-	if (ap->a_newoff < 0)
-		return (EINVAL);
+	if (newoffset < 0)
+		return EINVAL;
 
-	return (0);
+	if (ap->a_newoffset)
+		*(ap->a_newoffset) = newoffset;
+
+	return 0;
 }
 
 int
Index: sys/miscfs/kernfs/kernfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/kernfs/kernfs_vnops.c,v
retrieving revision 1.125
diff -u -p -r1.125 kernfs_vnops.c
--- sys/miscfs/kernfs/kernfs_vnops.c	23 Jun 2006 20:54:21 -0000	1.125
+++ sys/miscfs/kernfs/kernfs_vnops.c	26 Sep 2006 16:59:34 -0000
@@ -190,7 +190,7 @@ int	kernfs_ioctl(void *);
 #define	kernfs_poll	genfs_poll
 #define kernfs_revoke	genfs_revoke
 #define	kernfs_fsync	genfs_nullop
-#define	kernfs_seek	genfs_nullop
+#define	kernfs_seek	genfs_seek
 #define	kernfs_remove	genfs_eopnotsupp
 int	kernfs_link(void *);
 #define	kernfs_rename	genfs_eopnotsupp
Index: sys/miscfs/procfs/procfs_vnops.c
===================================================================
RCS file: /cvsroot/src/sys/miscfs/procfs/procfs_vnops.c,v
retrieving revision 1.134
diff -u -p -r1.134 procfs_vnops.c
--- sys/miscfs/procfs/procfs_vnops.c	20 Sep 2006 08:09:05 -0000	1.134
+++ sys/miscfs/procfs/procfs_vnops.c	26 Sep 2006 16:59:35 -0000
@@ -184,7 +184,7 @@ int	procfs_setattr(void *);
 #define	procfs_poll	genfs_poll
 #define procfs_revoke	genfs_revoke
 #define	procfs_fsync	genfs_nullop
-#define	procfs_seek	genfs_nullop
+#define	procfs_seek	genfs_seek
 #define	procfs_remove	genfs_eopnotsupp
 int	procfs_link(void *);
 #define	procfs_rename	genfs_eopnotsupp
Index: sys/miscfs/specfs/specdev.h
===================================================================
RCS file: /cvsroot/src/sys/miscfs/specfs/specdev.h,v
retrieving revision 1.30
diff -u -p -r1.30 specdev.h
--- sys/miscfs/specfs/specdev.h	14 May 2006 21:32:21 -0000	1.30
+++ sys/miscfs/specfs/specdev.h	26 Sep 2006 16:59:35 -0000
@@ -120,7 +120,7 @@ int	spec_kqfilter(void *);
 #define spec_revoke	genfs_revoke
 #define	spec_mmap	genfs_mmap
 int	spec_fsync(void *);
-#define	spec_seek	genfs_nullop		/* XXX should query device */
+#define	spec_seek	genfs_seek		/* XXX should query device */
 #define	spec_remove	genfs_badop
 #define	spec_link	genfs_badop
 #define	spec_rename	genfs_badop
Index: sys/sys/fcntl.h
===================================================================
RCS file: /cvsroot/src/sys/sys/fcntl.h,v
retrieving revision 1.33
diff -u -p -r1.33 fcntl.h
--- sys/sys/fcntl.h	29 Nov 2005 22:52:02 -0000	1.33
+++ sys/sys/fcntl.h	26 Sep 2006 16:59:35 -0000
@@ -247,15 +247,21 @@ struct flock {
 #endif
 
 /* Always ensure that these are consistent with <stdio.h> and <unistd.h>! */
-#ifndef	SEEK_SET
+#ifndef SEEK_SET
 #define	SEEK_SET	0	/* set file offset to offset */
 #endif
-#ifndef	SEEK_CUR
+#ifndef SEEK_CUR
 #define	SEEK_CUR	1	/* set file offset to current plus offset */
 #endif
-#ifndef	SEEK_END
+#ifndef SEEK_END
 #define	SEEK_END	2	/* set file offset to EOF plus offset */
 #endif
+#ifndef SEEK_DATA
+#define	SEEK_DATA	3	/* Set file pointer to next data past offset */
+#endif
+#ifndef SEEK_HOLE
+#define	SEEK_HOLE	4	/* Set file pointer to next hole past offset */
+#endif
 
 /*
  * posix_advise advisories.
Index: sys/sys/unistd.h
===================================================================
RCS file: /cvsroot/src/sys/sys/unistd.h,v
retrieving revision 1.35
diff -u -p -r1.35 unistd.h
--- sys/sys/unistd.h	14 Aug 2006 18:17:48 -0000	1.35
+++ sys/sys/unistd.h	26 Sep 2006 16:59:35 -0000
@@ -108,6 +108,8 @@
 #define	SEEK_SET	0	/* set file offset to offset */
 #define	SEEK_CUR	1	/* set file offset to current plus offset */
 #define	SEEK_END	2	/* set file offset to EOF plus offset */
+#define	SEEK_DATA	3	/* Set file pointer to next data past offset */
+#define	SEEK_HOLE	4	/* Set file pointer to next hole past offset */
 
 #if defined(_NETBSD_SOURCE)
 /* whence values for lseek(2); renamed by POSIX 1003.1 */
Index: sys/sys/vnode_if.h
===================================================================
RCS file: /cvsroot/src/sys/sys/vnode_if.h,v
retrieving revision 1.62
diff -u -p -r1.62 vnode_if.h
--- sys/sys/vnode_if.h	13 Jul 2006 12:00:26 -0000	1.62
+++ sys/sys/vnode_if.h	26 Sep 2006 16:59:35 -0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: vnode_if.h,v 1.62 2006/07/13 12:00:26 martin Exp $	*/
+/*	$NetBSD$	*/
 
 /*
  * Warning: DO NOT EDIT! This file is automatically generated!
@@ -224,12 +224,14 @@ int VOP_FSYNC(struct vnode *, kauth_cred
 struct vop_seek_args {
 	const struct vnodeop_desc *a_desc;
 	struct vnode *a_vp;
-	off_t a_oldoff;
-	off_t a_newoff;
+	off_t a_oldoffset;
+	int a_whence;
+	off_t a_givenoffset;
+	off_t *a_newoffset;
 	kauth_cred_t a_cred;
 };
 extern const struct vnodeop_desc vop_seek_desc;
-int VOP_SEEK(struct vnode *, off_t, off_t, kauth_cred_t);
+int VOP_SEEK(struct vnode *, off_t, int, off_t, off_t *, kauth_cred_t);
 
 struct vop_remove_args {
 	const struct vnodeop_desc *a_desc;
Index: sys/ufs/files.ufs
===================================================================
RCS file: /cvsroot/src/sys/ufs/files.ufs,v
retrieving revision 1.15
diff -u -p -r1.15 files.ufs
--- sys/ufs/files.ufs	20 Jul 2006 23:49:07 -0000	1.15
+++ sys/ufs/files.ufs	26 Sep 2006 16:59:36 -0000
@@ -60,3 +60,5 @@ file	ufs/ufs/ufs_lookup.c		ffs | lfs | m
 file	ufs/ufs/ufs_quota.c		quota & (ffs | lfs | mfs)
 file	ufs/ufs/ufs_vfsops.c		ffs | lfs | mfs | ext2fs
 file	ufs/ufs/ufs_vnops.c		ffs | lfs | mfs | ext2fs
+file	ufs/ufs/ufs_seek.c		ffs | lfs | mfs | ext2fs
+
Index: sys/ufs/ufs/ufs_extern.h
===================================================================
RCS file: /cvsroot/src/sys/ufs/ufs/ufs_extern.h,v
retrieving revision 1.49
diff -u -p -r1.49 ufs_extern.h
--- sys/ufs/ufs/ufs_extern.h	14 May 2006 21:33:39 -0000	1.49
+++ sys/ufs/ufs/ufs_extern.h	26 Sep 2006 16:59:36 -0000
@@ -83,7 +83,7 @@ int	ufs_readlink(void *);
 int	ufs_remove(void *);
 int	ufs_rename(void *);
 int	ufs_rmdir(void *);
-#define	ufs_seek	genfs_seek
+int	ufs_seek(void *);
 #define	ufs_poll	genfs_poll
 int	ufs_setattr(void *);
 int	ufs_strategy(void *);

--pWyiEgJYm5f9v55/
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="sparsecopy.c"

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <inttypes.h>

#define BLOB_SIZE 32*1024

char *blob[BLOB_SIZE];
int fdi, fdo;

#define MIN(a,b) ((a) < (b) ? (a) : (b))

void filedatacopy(off_t start, off_t len)
{
	off_t cplen;

	printf("copy from %"PRIu64" to %"PRIu64"\n", start, start+len);
	cplen = MIN(BLOB_SIZE, len);
	while (len) {
		 pread(fdi, blob, cplen, start);
		pwrite(fdo, blob, cplen, start);
		start += cplen;
		len   -= cplen;
		cplen = MIN(BLOB_SIZE, len);
	}
}


int main(int argc, char **argv)
{
	off_t extent, data, hole, pos;
	struct stat stat;
	int error;
	char *filen;

	if (argc != 2) {
		printf("Use %s fname\n", argv[0]);
		return EXIT_FAILURE;
	}
	filen = argv[1];

	if ((fdi = open(filen, O_RDONLY, 0)) == -1) {
		printf("Can't open my input file\n");
		return EXIT_FAILURE;
	}
	if (fstat(fdi, &stat)) {
		printf("can't stat file\n");
		return EXIT_FAILURE;
	}

	if ((fdo = open("file-out", O_WRONLY | O_TRUNC | O_CREAT, 0666)) == -1) {
		printf("Can't open output file\n");
		return EXIT_FAILURE;
	}

	/* sparse copy routine */
	extent = stat.st_size;

	error= 0;
	pos  = 0;

	while (pos < extent) {
		data = lseek(fdi, pos, SEEK_DATA);
		hole = lseek(fdi, data, SEEK_HOLE);
printf("DATA = %"PRIi64"\n", data);
printf("HOLE = %"PRIi64"\n", hole);
		if (data == -1) {
			error = errno;
			break;
		}
		filedatacopy(data, hole-data);
		pos = hole;
	}

	if (error && (error != ENXIO)) {
		printf("error occured in transfer\n");
		return EXIT_FAILURE;
	}

	close(fdi);
	close(fdo);

	return EXIT_SUCCESS;
}


--pWyiEgJYm5f9v55/--

--f2QGlHpHGjS2mn6Y
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.1 (NetBSD)

iQEVAwUBRRmBHIKcNwBDyKpoAQJb5ggAxOSw24+MqaWyr216mxA/1L4IYTauBl2a
Ldq7Azp55Ta5VdkDwjGbjdqeSJjrX2fYS9RcbHDoCQ2GJG5PXVKp9C7TiECd3Buk
tlDhv4H/2trwUHgM66u00+jftk1RslfysDVEonzdhmpuqOM1mc1AW431dc9MHZHU
3Z8cROEKwHt4NihiNsZ/NGJ1LGOJScexuDYm5jxhLNF2cbzqSPTotEb2LPvX3qj3
8OgK0wz5G2qVhTVbKBa9ADfdWmbPIj5peFeZh0Uxtslg578H5VO+sYlXJas7MJTX
RKwKnukORXa7oXA1O/JwXbylQTZA238YcQvfpLxBwxC0NvlKgJD/3A==
=ymjk
-----END PGP SIGNATURE-----

--f2QGlHpHGjS2mn6Y--