Re: [PATCH] race between util_create_path() and util_delete_path()?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 9/7/09, Florian Zumbiehl <florz@xxxxxxxx> wrote:
> Hi,
>
>> >  int util_create_path(struct udev *udev, const char *path)
>> >  {
>> >  	char p[UTIL_PATH_SIZE];
>> > @@ -32,6 +55,8 @@ int util_create_path(struct udev *udev, const char
>> > *path)
>> >  	struct stat stats;
>> >  	int ret;
>> >
>> > +	if (util_path_lock())
>> > +		return -1;
>>
>> I think the patch missed some call sites.  Each create_path() needs to
>> be followed by a create_path_done(), but I can see nine calls to
>> create_path() calls and only about six to create_path_done().
>
> Hu? I see 8 call sites, and those should all be covered - maybe it's just
> because I'm working on a somewhat old tree?

The ninth site was the recursive call within create_path().  I see
you've added unlock()s there now.  I re-counted on a per-file basis
and everything else looks covered; I probably miscounted before.

I'm still concerned/interested about the effect on performance. But
since I read the patch, here are some style suggestions.

> [...]
>> I think there's locking recursion here, because we end up calling
>> name_index()  between create_path() and create_path_done().  Looking
>> at the definition of unlock(), that's not going to work.
>
> Below is a new patch that should work with recursive locking (of limited
> depth - I hope 2^31-1 levels of recursion is sufficient? :-)
>
> Untested, uncompiled, etc., as usual ...
>
> Florian
>
> diff --git a/libudev/libudev-device-private.c
> b/libudev/libudev-device-private.c
> index 68dc0a5..d386135 100644
> --- a/libudev/libudev-device-private.c
> +++ b/libudev/libudev-device-private.c
> @@ -44,7 +44,8 @@ int udev_device_update_db(struct udev_device *udev_device)
>  	int ret;
>
>  	devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename,
> sizeof(filename));
> -	util_create_path(udev, filename);
> +	if (util_create_path(udev, filename))
> +		return -1;
>  	unlink(filename);
>
>  	udev_list_entry_foreach(list_entry,
> udev_device_get_properties_list_entry(udev_device))
> @@ -86,6 +87,7 @@ file:
>  	f = fopen(filename, "w");
>  	if (f == NULL) {
>  		err(udev, "unable to create db file '%s': %m\n", filename);
> +		util_create_path_done();
>  		return -1;
>  		}
>  	info(udev, "created db file for '%s' in '%s'\n",
> udev_device_get_devpath(udev_device), filename);
> @@ -114,6 +116,7 @@ file:
>  	}
>  	fclose(f);
>  out:
> +	util_create_path_done();
>  	return 0;
>  }
>
> diff --git a/libudev/libudev-queue-private.c
> b/libudev/libudev-queue-private.c
> index 0427b65..f2f3905 100644
> --- a/libudev/libudev-queue-private.c
> +++ b/libudev/libudev-queue-private.c
> @@ -417,6 +417,7 @@ static void update_failed(struct udev_queue_export
> *udev_queue_export,
>  		udev_selinux_setfscreatecon(udev, filename, S_IFLNK);
>  		symlink(udev_device_get_devpath(udev_device), filename);
>  		udev_selinux_resetfscreatecon(udev);
> +		util_create_path_done();
>  		break;
>
>  	case DEVICE_QUEUED:
> diff --git a/libudev/libudev-util-private.c b/libudev/libudev-util-private.c
> index c309945..26fbb5c 100644
> --- a/libudev/libudev-util-private.c
> +++ b/libudev/libudev-util-private.c
> @@ -25,6 +25,28 @@
>  #include "libudev.h"
>  #include "libudev-private.h"
>
> +static int util_path_lock_cnt,util_path_lock_fd;

static int util_path_lock_cnt;
static int util_path_lock_fd;

> +static int util_path_lock(void)
> +{
> +	if (!util_path_lock_cnt) {
> +		if ((util_path_lock_fd = open("/dev/udev_util_path_lock", O_WRONLY |
> O_CREAT)) < 0)
> +			return -1;
> +		if (flock(util_path_lock_fd, LOCK_EX)) {
> +			close(util_create_path_lock_fd);
> +			return -1;
> +		}
> +		util_path_lock_cnt++;
> +	}
> +	return 0;
> +}
> +
> +static void util_path_unlock(void)
> +{
> +	if (!--util_path_lock_cnt)
> +		close(util_create_path_lock_fd);
> +}
> +
>  int util_create_path(struct udev *udev, const char *path)
>  {
>  	char p[UTIL_PATH_SIZE];
> @@ -32,6 +54,8 @@ int util_create_path(struct udev *udev, const char *path)
>  	struct stat stats;
>  	int ret;
>
> +	if (util_path_lock())
> +		return -1;
>
>  	util_strscpy(p, sizeof(p), path);
>  	pos = strrchr(p, '/');
>  	if (pos == NULL)
> @@ -47,8 +71,10 @@ int util_create_path(struct udev *udev, const char *path)
>  	if (stat(p, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR)
>  		return 0;
>

> -	if (util_create_path(udev, p) != 0)
> +	if (util_create_path(udev, p) != 0) {
> +		util_path_unlock();
>  		return -1;
> +	}

Looks suspicious.  It'll try to unlock even If the recursive
util_create_path() bailed because it failed at util_path_lock().

I suspect that can't happen, but this seems needlessly tricky.  How
about just renaming the old create_path() to create_path_unlocked() or
something (updating the recursive call accordingly), and then

int util_create_path(struct udev *udev, const char *path)
{
	if (!util_path_lock())
		return -1;
	return util_path_create_unlocked(udev, path);
}

>  	dbg(udev, "mkdir '%s'\n", p);
>  	udev_selinux_setfscreatecon(udev, p, S_IFDIR|0755);
> @@ -60,9 +86,15 @@ int util_create_path(struct udev *udev, const char *path)
>  	if (ret == 0)
>  		return 0;
>
> +	util_path_unlock();
>  	return -1;
>  }
>
> +void util_create_path_done(void)
> +{
> +	util_path_unlock();
> +}
> +
>  int util_delete_path(struct udev *udev, const char *path)
>  {
>  	char p[UTIL_PATH_SIZE];
> @@ -75,6 +107,8 @@ int util_delete_path(struct udev *udev, const char *path)
>  	if (pos == p || pos == NULL)
>  		return 0;
>
> +	if (util_path_lock())
> +		return 0;
>  	while (1) {
>  		*pos = '\0';
>  		pos = strrchr(p, '/');
> @@ -88,13 +122,13 @@ int util_delete_path(struct udev *udev, const char
> *path)
>  		if (errno == ENOENT)
>  			retval = 0;
>  		if (retval) {
> -			if (errno == ENOTEMPTY)
> -				return 0;
> -			err(udev, "rmdir(%s) failed: %m\n", p);
> +			if (errno != ENOTEMPTY)
> +				err(udev, "rmdir(%s) failed: %m\n", p);
>  			break;
>  		}
>  		dbg(udev, "removed '%s'\n", p);
>  	}
> +	util_path_unlock();
>  	return 0;
>  }

Hmm, does this function ever return non-zero?  Is it time to admit
that it returns void?

Regards
Alan
--
To unsubscribe from this list: send the line "unsubscribe linux-hotplug" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel]     [Linux DVB]     [Asterisk Internet PBX]     [DCCP]     [Netdev]     [X.org]     [Util Linux NG]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux