On 18/10/17(Wed) 14:19, Helg Bredow wrote:
> On Wed, 18 Oct 2017 10:47:59 +0200
> Martin Pieuchot <[email protected]> wrote:
> 
> > On 16/10/17(Mon) 14:37, Helg Bredow wrote:
> > > [...] 
> > > Patch is below.
> > 
> > Do you mind explaining the problem and the solution you implemented?
> > 
> 
> There is a regression introduced in 6.2 that can cause a call to getcwd(3) to 
> result in an endless loop. fusefs_readdir() has a TODO that returns success 
> even if a directory is not open, so getcwd(3) keeps calling it to fetch more 
> dirents but none ever get supplied. This patch opens and closes the directory 
> if it's not already open. The check for an invalid file handle was inside the 
> loop but I've moved it outside since it's not going to become invalid once 
> it's been opened.

Nice.  You can remove a call to fb_delete() in the first error path. 

> This doesn't resolve the problem however. This is because ifuse_ops_readdir() 
> does not support being called across multiple calls to VOP_READDIR(9). This 
> occurs if the buffer is too small to contain all the entries and getcwd(3) 
> only supplies a 512 byte buffer. If the current directory is not returned in 
> the first call to VOP_READDIR(9) then getcwd(3) will continue to call it but 
> the eofflag is never set. ifuse_ops_readdir() now correctly starts scanning 
> at the next dirent even across multiple VOP_READDIR(9) calls.

Why did you decide to put the logic in the library?  Now the library
needs to remember some "state".

Would it be possible to keep a dumb ifuse_ops_readdir() and make the
kernel ask for the next dirent?  How often does that happen in
practise?  Only when strlen(dirent) > 512?

> In addition, if the entries don't evenly fit into the buffer then 
> fusefs_readdir() would prematurely detect that the entire directory had been 
> scanned and set the eofflag. This fix handles this condition so the entire 
> directory is always scanned.

That's good.

> ifuse_fill_readdir() returns the ino of the file system instead of its own 
> ino, which it returns from other calls such as stat(2). getcwd(3) compares 
> these inos when scanning the directory and will never find a match since they 
> are sourced from different file systems. fuse has the use_ino option to allow 
> a file system to use its own inos but this is not implemented in OpenBSD yet. 
> This patch forces the fuse ino to be used in ifuse_fill_readdir(). This is 
> still only paritally implemented though, it will only set a valid ino if it 
> can find the file or directory in the name cache. This is sufficient for 
> getcwd(3) since the parent directory will always be in the cache.

How much work would it be to support the `use_ino' option?

> Index: sys/miscfs/fuse/fuse_vnops.c
> ===================================================================
> RCS file: /cvs/src/sys/miscfs/fuse/fuse_vnops.c,v
> retrieving revision 1.33
> diff -u -p -r1.33 fuse_vnops.c
> --- sys/miscfs/fuse/fuse_vnops.c      7 Sep 2016 17:53:35 -0000       1.33
> +++ sys/miscfs/fuse/fuse_vnops.c      18 Oct 2017 13:25:04 -0000
> @@ -680,7 +680,7 @@ fusefs_readdir(void *v)
>       struct vnode *vp;
>       struct proc *p;
>       struct uio *uio;
> -     int error = 0, eofflag = 0;
> +     int error = 0, eofflag = 0, diropen = 0;
>  
>       vp = ap->a_vp;
>       uio = ap->a_uio;
> @@ -695,14 +695,18 @@ fusefs_readdir(void *v)
>       if (uio->uio_resid < sizeof(struct dirent))
>               return (EINVAL);
>  
> -     while (uio->uio_resid > 0) {
> -             fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_READDIR, p);
> -
> -             if (ip->fufh[FUFH_RDONLY].fh_type == FUFH_INVALID) {
> -                     /* TODO open the file */
> +     if (ip->fufh[FUFH_RDONLY].fh_type == FUFH_INVALID) {
> +             error = fusefs_file_open(fmp, ip, FUFH_RDONLY, O_RDONLY, 1, p);
> +             if (error) {
>                       fb_delete(fbuf);

No need for fb_delete() here.

>                       return (error);
>               }
> +             diropen = 1;
> +     }
> +
> +     while (uio->uio_resid > 0) {
> +             fbuf = fb_setup(0, ip->ufs_ino.i_number, FBT_READDIR, p);
> +
>               fbuf->fb_io_fd = ip->fufh[FUFH_RDONLY].fh_id;
>               fbuf->fb_io_off = uio->uio_offset;
>               fbuf->fb_io_len = MIN(uio->uio_resid, fmp->max_read);
> @@ -710,6 +714,13 @@ fusefs_readdir(void *v)
>               error = fb_queue(fmp->dev, fbuf);
>  
>               if (error) {
> +                     /*
> +                      * dirent was larger than residual space left in
> +                      * buffer.
> +                      */
> +                     if (error == ENOBUFS && fbuf->fb_len == 0)
> +                             error = 0;
> +
>                       fb_delete(fbuf);
>                       break;
>               }
> @@ -731,6 +742,9 @@ fusefs_readdir(void *v)
>  
>       if (!error && ap->a_eofflag != NULL)
>               *ap->a_eofflag = eofflag;
> +
> +     if (diropen)
> +             fusefs_file_close(fmp, ip, FUFH_RDONLY, O_RDONLY, 1, p);
>  
>       return (error);
>  }
> 

Reply via email to