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

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

 



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?

[...]
> 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(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;
+	}
 
 	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;
 }
 
diff --git a/udev/udev-node.c b/udev/udev-node.c
index d73bc6c..4124485 100644
--- a/udev/udev-node.c
+++ b/udev/udev-node.c
@@ -46,10 +46,12 @@ static int name_index(struct udev *udev, const char *devpath, const char *name,
 
 	if (add) {
 		dbg(udev, "creating index: '%s'\n", filename);
-		util_create_path(udev, filename);
-		fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
-		if (fd > 0)
-			close(fd);
+		if (!util_create_path(udev, filename)) {
+			fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644);
+			util_create_path_done();
+			if (fd > 0)
+				close(fd);
+		}
 	} else {
 		dbg(udev, "removing index: '%s'\n", filename);
 		unlink(filename);
@@ -354,8 +356,10 @@ static int update_link(struct udev_device *dev, const char *slink)
 
 	/* create symlink to the target with the highest priority */
 	info(udev, "'%s' with target '%s' has the highest priority %i, create it\n", slink, target, priority);
-	util_create_path(udev, slink);
-	node_symlink(udev, target, slink);
+	if (!util_create_path(udev, slink)) {
+		node_symlink(udev, target, slink);
+		util_create_path_done();
+	}
 out:
 	return rc;
 }
@@ -424,7 +428,8 @@ int udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid)
 	     major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)),
 	     mode, uid, gid);
 
-	util_create_path(udev, udev_device_get_devnode(dev));
+	if (util_create_path(udev, udev_device_get_devnode(dev)))
+		return -1;
 	if (udev_node_mknod(dev, NULL, makedev(0,0), mode, uid, gid) != 0) {
 		err = -1;
 		goto exit;
@@ -456,6 +461,7 @@ int udev_node_add(struct udev_device *dev, mode_t mode, uid_t uid, gid_t gid)
 		update_link(dev, udev_list_entry_get_name(list_entry));
 	}
 exit:
+	util_create_path_done();
 	return err;
 }
 
diff --git a/udev/udev-rules.c b/udev/udev-rules.c
index 54a54c3..d8f3e7c 100644
--- a/udev/udev-rules.c
+++ b/udev/udev-rules.c
@@ -1729,10 +1729,12 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names)
 		/* read dynamic/temporary rules */
 		util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/rules.d", NULL);
 		if (stat(filename, &statbuf) != 0) {
-			util_create_path(udev, filename);
-			udev_selinux_setfscreatecon(udev, filename, S_IFDIR|0755);
-			mkdir(filename, 0755);
-			udev_selinux_resetfscreatecon(udev);
+			if (!util_create_path(udev, filename)) {
+				udev_selinux_setfscreatecon(udev, filename, S_IFDIR|0755);
+				mkdir(filename, 0755);
+				udev_selinux_resetfscreatecon(udev);
+				util_create_path_done();
+			}
 		}
 		udev_list_init(&sort_list);
 		add_matching_files(udev, &sort_list, filename, ".rules");
diff --git a/udev/udev-watch.c b/udev/udev-watch.c
index 5a49c96..744d9cd 100644
--- a/udev/udev-watch.c
+++ b/udev/udev-watch.c
@@ -124,9 +124,11 @@ void udev_watch_begin(struct udev *udev, struct udev_device *dev)
 	}
 
 	snprintf(filename, sizeof(filename), "%s/.udev/watch/%d", udev_get_dev_path(udev), wd);
-	util_create_path(udev, filename);
-	unlink(filename);
-	symlink(udev_device_get_devpath(dev), filename);
+	if (!util_create_path(udev, filename)) {
+		unlink(filename);
+		symlink(udev_device_get_devpath(dev), filename);
+		util_create_path_done();
+	}
 
 	udev_device_set_watch_handle(dev, wd);
 }
--
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