[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

RE: Issue 3501: Repositories mounted on NFS don't work

From: Bert Huijben <bert_at_qqmail.nl>
Date: Thu, 7 Jan 2010 09:25:46 +0100

> -----Original Message-----
> From: Peter Samuelson [mailto:peter_at_p12n.org]
> Sent: donderdag 7 januari 2010 2:30
> To: Edgar Fuß
> Cc: dev_at_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)
Received on 2010-01-07 09:26:08 CET

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.