On Thu, Apr 04, 2013 at 11:56:29AM -0600, Eric Blake wrote:
On 04/04/2013 07:40 AM, Daniel P. Berrange wrote:
> From: "Daniel P. Berrange" <berrange(a)redhat.com>
>
> Introduce a method virFileDeleteTree for recursively deleting
> an entire directory tree
>
> Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
> ---
> src/libvirt_private.syms | 1 +
> src/util/virfile.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
> src/util/virfile.h | 2 ++
> 3 files changed, 81 insertions(+)
Don't we already have something like this?
/me goes and looks...
yep, very similar to virCgroupRemoveRecursively - hopefully a later
patch drops that function to use this instead. I like the idea of a
generalized interface.
> + * virFileDeleteTree:
> + *
> + * Recursively deletes all files / directories
> + * starting from the directory @dir. Does not
> + * follow symlinks
> + */
> +int virFileDeleteTree(const char *dir)
> +{
> + DIR *dh = opendir(dir);
> + struct dirent *de;
> + char *filepath = NULL;
> + int ret = -1;
> +
> + if (!dh) {
> + virReportSystemError(errno, _("Cannot open dir '%s'"),
> + dir);
> + return -1;
> + }
> +
> + errno = 0;
> + while ((de = readdir(dh)) != NULL) {
> + struct stat sb;
> +
> + if (STREQ(de->d_name, ".") ||
> + STREQ(de->d_name, ".."))
> + continue;
> +
> + if (virAsprintf(&filepath, "%s/%s",
> + dir, de->d_name) < 0) {
> + virReportOOMError();
> + goto cleanup;
> + }
We should use gnulib's LGPL unlinkat. On capable kernels, it avoids
O(n^2) behavior that is inherent in computing filenames in a deep
hierarchy. On less capable kernels, it still makes this code simpler to
write (no virAsprintf needed here).
I looked into unlinkat() but that gnulib module is GPL only.
> +
> + if (lstat(filepath, &sb) < 0) {
> + virReportSystemError(errno, _("Cannot access '%s'"),
> + filepath);
> + goto cleanup;
> + }
Potentially wasteful on systems like Linux that have d_type. If d_type
exists, and is not DT_UNKNOWN, then the difference between DT_DIR and
other types can save some system call efforts.
Potential TOCTTOU race. POSIX allows unlink("dir") to succeed (although
most platforms reject it, either always [Linux], or based on
capabilities [Solaris, which also has code to give up that capability,
and where gnulib also exposes that]). If we ever manage to unlink() a
directory, because we lost the TOCTTOU race, then we have done bad
things to the file system.
But you are guaranteed that rmdir() on a non-directory will gracefully
fail; so you can minimize the race window by attempting to treat _every_
name as a directory first, then gracefully fall back to unlink() if the
opendir() fails with ENOTDIR, without ever having to waste time
lstat()ing things. Hmm, except that you don't want to follow symlinks,
but opendir() follows them by default; so you would have to use
open(O_DIRECTORY)/fdopendir() instead of raw opendir().
I also looked at fdopendir() but we'd want gnulib for portability
of that, and again it is GPL only.
Very weak ACK, depending on what you answer to my commentary.
I would like to make some of the enhancements you suggest, but doing
so would require we implement a bunch of portability code since all
the things you suggest are Linux specific and the gnulib modules are
GPL only. So I think in the immediate term, we'll just have to go
with that I have here.
Daniel
--
|:
http://berrange.com -o-
http://www.flickr.com/photos/dberrange/ :|
|:
http://libvirt.org -o-
http://virt-manager.org :|
|:
http://autobuild.org -o-
http://search.cpan.org/~danberr/ :|
|:
http://entangle-photo.org -o-
http://live.gnome.org/gtk-vnc :|