Subject: Second patch for lseek() sparse file extension
To: None <tech-kern@NetBSD.org>
From: Reinoud Zandijk <reinoud@netbsd.org>
List: tech-kern
Date: 09/21/2006 20:47:49
--tsOsTdHNUZQcU9Ye
Content-Type: multipart/mixed; boundary="3MwIy2ne0vdjdPXF"
Content-Disposition: inline
--3MwIy2ne0vdjdPXF
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Dear folks,
2nd try that ought to be complete. I had to change the VOP_SEEK() a bit
since i was worried about FS layering and got wierd results at times; thus
i pass the vattr down so it reflects the top vnode and not the bottom
vnode. This might be important for some file systems and it saves
duplicating and thus error-prone ness.
I've also added and updated src/regress/os/fs/lseek to test for normal
lseek() behaviour and to test the SEEK_DATA and SEEK_HOLE extension if it
is defined.
If all is ok, i'd like to commit this.
With regards,
Reinoud
--3MwIy2ne0vdjdPXF
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=DIFFS
Index: include/stdio.h
===================================================================
RCS file: /cvsroot/src/include/stdio.h,v
retrieving revision 1.64
diff -u -r1.64 stdio.h
--- include/stdio.h 10 May 2006 21:09:45 -0000 1.64
+++ include/stdio.h 21 Sep 2006 18:37:52 -0000
@@ -203,6 +203,12 @@
#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 -r1.22 lseek.2
--- lib/libc/sys/lseek.2 13 May 2004 10:20:58 -0000 1.22
+++ lib/libc/sys/lseek.2 21 Sep 2006 18:37:53 -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 @@
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 @@
.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 @@
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 -r1.51 vnodeops.9
--- share/man/man9/vnodeops.9 16 Sep 2006 08:54:22 -0000 1.51
+++ share/man/man9/vnodeops.9 21 Sep 2006 18:37:54 -0000
@@ -146,7 +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" \
+.Fn VOP_SEEK "struct vnode *vp" "off_t oldoffset" "int whence" \
+"off_t givenoffset" "off_t *newoffset" "struct vattr *vattr" \
"struct ucred *cred"
.Ft int
.Fn VOP_REMOVE "struct vnode *vp" "struct vnode *vp" \
@@ -828,18 +829,33 @@
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" "*vattr" \
+"*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 +1478,9 @@
.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 -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 21 Sep 2006 18:37:57 -0000
@@ -128,7 +128,7 @@
#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 -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 21 Sep 2006 18:37:57 -0000
@@ -1207,8 +1207,11 @@
{
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;
+ struct vattr *a_vattr;
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 -r1.270 vfs_syscalls.c
--- sys/kern/vfs_syscalls.c 13 Sep 2006 10:07:42 -0000 1.270
+++ sys/kern/vfs_syscalls.c 21 Sep 2006 18:38:00 -0000
@@ -2118,7 +2118,7 @@
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)
@@ -2131,29 +2131,17 @@
error = ESPIPE;
goto out;
}
-
- 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)
+ error = VOP_GETATTR(vp, &vattr, cred, l);
+ if (error)
goto out;
- *(off_t *)retval = fp->f_offset = newoff;
- out:
+ error = VOP_SEEK(vp, fp->f_offset, SCARG(uap, whence),
+ SCARG(uap, offset), &newoffset, &vattr, cred);
+
+ if (error == 0)
+ *(off_t *)retval = fp->f_offset = newoffset;
+
+out:
FILE_UNUSE(fp, l);
return (error);
}
@@ -2199,7 +2187,8 @@
* 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, NULL, fp->f_cred);
+ if (error != 0)
goto out;
/* dofileread() will unuse the descriptor for us */
@@ -2252,7 +2241,8 @@
* 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, NULL, fp->f_cred);
+ if (error != 0)
goto out;
/* dofilereadv() will unuse the descriptor for us */
@@ -2305,7 +2295,8 @@
* 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, NULL, fp->f_cred);
+ if (error != 0)
goto out;
/* dofilewrite() will unuse the descriptor for us */
@@ -2358,7 +2349,8 @@
* 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, NULL, fp->f_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 -r1.66 vnode_if.c
--- sys/kern/vnode_if.c 13 Jul 2006 12:00:25 -0000 1.66
+++ sys/kern/vnode_if.c 21 Sep 2006 18:38:00 -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,11 @@
};
int
VOP_SEEK(struct vnode *vp,
- off_t oldoff,
- off_t newoff,
+ off_t oldoffset,
+ int whence,
+ off_t givenoffset,
+ off_t *newoffset,
+ struct vattr *vattr,
kauth_cred_t cred)
{
struct vop_seek_args a;
@@ -750,8 +753,11 @@
#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_vattr = vattr;
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 -r1.50 vnode_if.src
--- sys/kern/vnode_if.src 14 May 2006 21:15:12 -0000 1.50
+++ sys/kern/vnode_if.src 21 Sep 2006 18:38:00 -0000
@@ -266,13 +266,16 @@
};
#
-# Needs work: Is newoff right? What's it mean?
# XXX Locking protocol?
+# XXX kauth_cred_t cred is not used
#
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 struct vattr *vattr;
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 -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 21 Sep 2006 18:38:00 -0000
@@ -64,7 +64,7 @@
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 -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 21 Sep 2006 18:38:01 -0000
@@ -50,6 +50,7 @@
#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,50 @@
{
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;
+ struct vattr *a_vattr;
+ kauth_cred_t a_cred;
+ }; */ *ap = v;
+ off_t newoffset;
- if (ap->a_newoff < 0)
- return (EINVAL);
+ /* 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 + ap->a_vattr->va_size;
+ break;
+ case SEEK_SET:
+ newoffset = ap->a_givenoffset;
+ break;
+ case SEEK_DATA:
+ /* if at start, there is one data block */
+ if (ap->a_givenoffset != 0)
+ return ENXIO;
+ newoffset = 0;
+ break;
+ case SEEK_HOLE:
+ /* there exists one virtual hole at the end of the file */
+ if (ap->a_givenoffset > ap->a_vattr->va_size)
+ return ENXIO;
+ newoffset = ap->a_vattr->va_size;
+ break;
+ default:
+ return EINVAL;
+ }
- return (0);
+ if (newoffset < 0)
+ return EINVAL;
+
+ 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 -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 21 Sep 2006 18:38:01 -0000
@@ -190,7 +190,7 @@
#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 -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 21 Sep 2006 18:38:01 -0000
@@ -184,7 +184,7 @@
#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 -r1.30 specdev.h
--- sys/miscfs/specfs/specdev.h 14 May 2006 21:32:21 -0000 1.30
+++ sys/miscfs/specfs/specdev.h 21 Sep 2006 18:38:01 -0000
@@ -120,7 +120,7 @@
#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 -r1.33 fcntl.h
--- sys/sys/fcntl.h 29 Nov 2005 22:52:02 -0000 1.33
+++ sys/sys/fcntl.h 21 Sep 2006 18:38:02 -0000
@@ -247,15 +247,21 @@
#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 -r1.35 unistd.h
--- sys/sys/unistd.h 14 Aug 2006 18:17:48 -0000 1.35
+++ sys/sys/unistd.h 21 Sep 2006 18:38:02 -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 -r1.62 vnode_if.h
--- sys/sys/vnode_if.h 13 Jul 2006 12:00:26 -0000 1.62
+++ sys/sys/vnode_if.h 21 Sep 2006 18:38:02 -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,16 @@
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;
+ struct vattr *a_vattr;
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 *, struct vattr *,
+ kauth_cred_t);
struct vop_remove_args {
const struct vnodeop_desc *a_desc;
--3MwIy2ne0vdjdPXF--
--tsOsTdHNUZQcU9Ye
Content-Type: application/pgp-signature
Content-Disposition: inline
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.1 (NetBSD)
iQEVAwUBRRLeToKcNwBDyKpoAQJikQgAmf39PAO5dGdO9Wv1nYdUtWemaTtwKg9B
lCsxQX1NbN2rZGdU5pTI5rl3jgWe5pyUD2L6SjWqjjluW531NBEIGfEWNfEByI7M
LWq0rtLXrO3sfYxqowlrQonqVRpH5PhC5o5mMEYrLAepme7RuApp4YL3ZeSzU+My
6Bf3OcY4nX/A90lo2noUku1IkuJ2HuB2Ggr0+PmYM247cB88nbCAfjY8VGaQ4fj9
TRaqn04gAhQgMVEQFukNa+tP8WkccA/bl5zZr/nDKh1Mb9otMybdN/2fWDyZOIcP
hrYhI1ds7I0bjOB7LVskMduP32Ev/p9vSa5sgdJOLme3dFaWGGPr3w==
=ZJDa
-----END PGP SIGNATURE-----
--tsOsTdHNUZQcU9Ye--