> -----Original Message-----
> From: Peter Samuelson [mailto:pe...@p12n.org]
> Sent: donderdag 7 januari 2010 2:30
> To: Edgar Fuß
> Cc: dev@subversion.apache.org
> Subject: Re: Issue 3501: Repositories mounted on NFS don't work
> 
> 
> [Edgar Fuß]
> > To summarise the problem: recursive directory deletion fails because
> entries
> > re-appear after dir rewinding. The fix may be as easy as ignoring
ENOENT.
> 
> Or not calling rewind.  Something like the following (untested) against
> 1.6.x.  Same patch applies to trunk except for some conflicts involving
> path vs. dirent functions.
> --
> Peter Samuelson | org-tld!p12n!peter | http://p12n.org/
> 
> [[[
> * subversion/libsvn_subr/io.c
>   (svn_io_remove_dir2): Remove rewinddir() magic (see Issues 1896 and
>    3501).  Instead, build linked lists of files and subdirs to delete,
>    then delete them in separate loops.
> ]]]
> 
> Index: subversion/libsvn_subr/io.c
> ==========================================================
> =========
> --- subversion/libsvn_subr/io.c       (revisione 887406)
> +++ subversion/libsvn_subr/io.c       (copia locale)
> @@ -1862,17 +1862,13 @@
> 
>   Similar problem has been observed on FreeBSD.
> 
> - See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 for more
> - discussion and an initial solution.
> + The workaround is to collect filenames in the readdir loop, then
> + delete them in a separate loop (two, actually, one for subdirs and one
> + for other files).  A previous workaround involving rewinddir is
> + problematic on Win32 and some NFS clients, notably NetBSD.
> 
> - To work around the problem, we do a rewinddir after we delete all files
> - and see if there's anything left. We repeat the steps untill there's
> - nothing left to delete.
> -
> - This workaround causes issues on Windows where delete's are
> asynchronous,
> - however, so we never rewind if we're on Windows (the delete says it is
> - complete, we rewind, we see the same file and try to delete it again,
> - we fail.
> + See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
> + http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
>  */
> 
>  /* Neither windows nor unix allows us to delete a non-empty
> @@ -1890,7 +1886,8 @@
>    apr_pool_t *subpool;
>    apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME;
>    const char *path_apr;
> -  int need_rewind;
> +  struct path_list { char *path; struct path_list *next; }
> +    *del_files = NULL, *del_dirs = NULL, *del_tmp;
> 
>    /* Check for pending cancellation request.
>       If we need to bail out, do so early. */
> @@ -1922,72 +1919,36 @@
> 
>    subpool = svn_pool_create(pool);
> 
> -  do
> +  while ((status = apr_dir_read(&this_entry, flags, this_dir)) ==
> APR_SUCCESS)
>      {
> -      need_rewind = FALSE;
> +      const char *entry_utf8;
> +      if ((this_entry.filetype == APR_DIR)
> +       && ((this_entry.name[0] == '.')
> +           && ((this_entry.name[1] == '\0')
> +               || ((this_entry.name[1] == '.')
> +                   && (this_entry.name[2] == '\0')))))
> +     {
> +       /* skip "." and ".." */
> +       continue;
> +     }
> +      SVN_ERR(entry_name_to_utf8(&entry_utf8, this_entry.name,
> +                              path_apr, subpool));
> 
> -      for (status = apr_dir_read(&this_entry, flags, this_dir);
> -           status == APR_SUCCESS;
> -           status = apr_dir_read(&this_entry, flags, this_dir))
> -        {
> -          svn_pool_clear(subpool);
> -          if ((this_entry.filetype == APR_DIR)
> -              && ((this_entry.name[0] == '.')
> -                  && ((this_entry.name[1] == '\0')
> -                      || ((this_entry.name[1] == '.')
> -                          && (this_entry.name[2] == '\0')))))
> -            {
> -              continue;
> -            }
> -          else  /* something other than "." or "..", so proceed */
> -            {
> -              const char *fullpath, *entry_utf8;
> +      del_tmp = apr_palloc(subpool, sizeof *del_tmp);
> +      del_tmp->path = svn_path_join(path, entry_utf8, subpool);
> 
> -#ifndef WIN32
> -              need_rewind = TRUE;
> -#endif
> -
> -              SVN_ERR(entry_name_to_utf8(&entry_utf8, this_entry.name,
> -                                         path_apr, subpool));
> -
> -              fullpath = svn_path_join(path, entry_utf8, subpool);
> -
> -              if (this_entry.filetype == APR_DIR)
> -                {
> -                  /* Don't check for cancellation, the callee
> -                     will immediately do so */
> -                  SVN_ERR(svn_io_remove_dir2(fullpath, FALSE,
> -                                             cancel_func, cancel_baton,
> -                                             subpool));
> -                }
> -              else
> -                {
> -                  svn_error_t *err;
> -
> -                  if (cancel_func)
> -                    SVN_ERR((*cancel_func)(cancel_baton));
> -
> -                  err = svn_io_remove_file(fullpath, subpool);
> -                  if (err)
> -                    return svn_error_createf
> -                      (err->apr_err, err, _("Can't remove '%s'"),
> -                       svn_path_local_style(fullpath, subpool));
> -                }
> -            }
> -        }
> -
> -      if (need_rewind)
> -        {
> -          status = apr_dir_rewind(this_dir);
> -          if (status)
> -            return svn_error_wrap_apr(status, _("Can't rewind directory
'%s'"),
> -                                      svn_path_local_style (path, pool));
> -        }

I think it is easier to use svn_io_get_dirents2() then this block
replicating its implementation.

/** Read all of the disk entries in directory @a path, a utf8-encoded
 * path.  Set @a *dirents to a hash mapping dirent names (<tt>char *</tt>)
to
 * #svn_io_dirent_t structures, allocated in @a pool.
 *
 * @note The `.' and `..' directories normally returned by
 * apr_dir_read() are NOT returned in the hash.
 *
 * @note The kind field in the @a dirents is set according to the mapping
 *       as documented for svn_io_check_path()
 *
 * @since New in 1.3.
 */
svn_error_t *
svn_io_get_dirents2(apr_hash_t **dirents,
                    const char *path,
                    apr_pool_t *pool);


> +      if (this_entry.filetype == APR_DIR)
> +     {
> +       del_tmp->next = del_dirs;
> +       del_dirs = del_tmp;
> +     }
> +      else
> +     {
> +       del_tmp->next = del_files;
> +       del_files = del_tmp;
> +     }
>      }
> -  while (need_rewind);
> 
> -  svn_pool_destroy(subpool);
> -
>    if (!APR_STATUS_IS_ENOENT(status))
>      return svn_error_wrap_apr(status, _("Can't read directory '%s'"),
>                                svn_path_local_style(path, pool));
> @@ -1997,6 +1958,31 @@
>      return svn_error_wrap_apr(status, _("Error closing directory '%s'"),
>                                svn_path_local_style(path, pool));
> 
> +  for (; del_dirs; del_dirs = del_dirs->next)
> +    {
> +      /* Don't check for cancellation, the callee will immediately do so
*/
> +      SVN_ERR(svn_io_remove_dir2(del_dirs->path, FALSE,
> +                              cancel_func, cancel_baton,
> +                              subpool));
> +    }
> +  for (; del_files; del_files = del_files->next)
> +    {
> +      svn_error_t *err;
> +
> +      if (cancel_func)
> +     SVN_ERR((*cancel_func)(cancel_baton));
> +
> +      err = svn_io_remove_file2(del_files->path, FALSE, subpool);
> +      if (err)
> +     {
> +       return svn_error_createf
> +         (err->apr_err, err, _("Can't remove '%s'"),
> +          svn_path_local_style(del_files->path, subpool));
> +     }
> +    }
> +
> +  svn_pool_destroy(subpool);
> +
>    status = apr_dir_remove(path_apr, pool);
>    WIN32_RETRY_LOOP(status, apr_dir_remove(path_apr, pool));
>    if (status)

Reply via email to