From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 23746 invoked by alias); 12 Aug 2008 13:33:07 -0000 Received: (qmail 23740 invoked by alias); 12 Aug 2008 13:33:06 -0000 X-Spam-Status: No, hits=-1.6 required=5.0 tests=AWL,BAYES_00,KAM_MX,SPF_HELO_PASS X-Spam-Check-By: sourceware.org X-Spam-Checker-Version: SpamAssassin 3.2.4 (2008-01-01) on bastion.fedora.phx.redhat.com X-Spam-Level: Subject: master - libdlm: handle truncated device names To: cluster-cvs-relay@redhat.com X-Project: Cluster Project X-Git-Module: cluster.git X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: 674aea11d7fdddc382d01839f631b17385916f38 X-Git-Newrev: 116708143fb9492c1f9b99e3837bacc590317173 From: David Teigland Message-Id: <20080812133143.94C1412002B@lists.fedorahosted.org> Date: Tue, 12 Aug 2008 14:14:00 -0000 X-Scanned-By: MIMEDefang 2.58 on 172.16.52.254 Mailing-List: contact cluster-cvs-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Post: List-Help: , Sender: cluster-cvs-owner@sourceware.org X-SW-Source: 2008-q3/txt/msg00243.txt.bz2 Gitweb: http://git.fedorahosted.org/git/cluster.git?p=cluster.git;a=commitdiff;h=116708143fb9492c1f9b99e3837bacc590317173 Commit: 116708143fb9492c1f9b99e3837bacc590317173 Parent: 674aea11d7fdddc382d01839f631b17385916f38 Author: David Teigland AuthorDate: Mon Aug 11 12:07:25 2008 -0500 Committer: David Teigland CommitterDate: Tue Aug 12 08:23:26 2008 -0500 libdlm: handle truncated device names When lockspace names are over 15 characters long, they result in a device name that's over 19 characters long, e.g. dlm_0123456789ABCDEF. Sysfs truncates device names at 19 characters, so the device name for this lockspace is /sys/class/misc/dlm_0123456789ABCDE, which udev also uses to create the device node, /dev/misc/dlm_0123456789ABCDE. So, when libdlm waits for udev to create the device node, it needs to look for this truncated name. It then creates and removes symlinks with the full lockspace name. Joel Becker identified the problem and came up with this solution. Signed-off-by: David Teigland --- dlm/libdlm/libdlm.c | 134 +++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 114 insertions(+), 20 deletions(-) diff --git a/dlm/libdlm/libdlm.c b/dlm/libdlm/libdlm.c index f88d824..9a32fb7 100644 --- a/dlm/libdlm/libdlm.c +++ b/dlm/libdlm/libdlm.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #ifdef HAVE_SELINUX #include @@ -469,6 +470,64 @@ static int open_control_device(void) return 0; } +/* the max number of characters in a sysfs device name, not including \0 */ +#define MAX_SYSFS_NAME 19 + +static int find_udev_device(const char *lockspace, int minor, char *udev_path) +{ + char basename[PATH_MAX]; + char tmp_path[PATH_MAX]; + DIR *d; + struct dirent *de; + struct stat st; + size_t basename_len; + int i; + + ls_dev_name(lockspace, udev_path, PATH_MAX); + snprintf(basename, PATH_MAX, DLM_PREFIX "%s", lockspace); + basename_len = strlen(basename); + + for (i = 0; i < 10; i++) { + + /* look for a device with the full name */ + + if (stat(udev_path, &st) == 0 && minor(st.st_rdev) == minor) + return 0; + + if (basename_len < MAX_SYSFS_NAME) { + sleep(1); + continue; + } + + /* look for a device with a truncated name */ + + d = opendir(MISC_PREFIX); + while ((de = readdir(d))) { + if (de->d_name[0] == '.') + continue; + if (strlen(de->d_name) < MAX_SYSFS_NAME) + continue; + if (strncmp(de->d_name, basename, MAX_SYSFS_NAME)) + continue; + snprintf(tmp_path, PATH_MAX, MISC_PREFIX "%s", + de->d_name); + if (stat(tmp_path, &st)) + continue; + if (minor(st.st_rdev) != minor) + continue; + + /* truncated name */ + strncpy(udev_path, tmp_path, PATH_MAX); + closedir(d); + return 0; + } + closedir(d); + sleep(1); + } + + return -1; +} + /* * do_dlm_dispatch() * Read an ast from the kernel. @@ -1290,10 +1349,10 @@ static int create_lockspace_v6(const char *name, uint32_t flags) static dlm_lshandle_t create_lockspace(const char *name, mode_t mode, uint32_t flags) { - int minor, i; - struct stat st; - char dev_name[PATH_MAX]; + char dev_path[PATH_MAX]; + char udev_path[PATH_MAX]; struct dlm_ls_info *newls; + int error, saved_errno, minor; /* We use the control device for creating lockspaces. */ if (open_control_device()) @@ -1303,39 +1362,48 @@ static dlm_lshandle_t create_lockspace(const char *name, mode_t mode, if (!newls) return NULL; - ls_dev_name(name, dev_name, sizeof(dev_name)); + ls_dev_name(name, dev_path, sizeof(dev_path)); if (kernel_version.version[0] == 5) minor = create_lockspace_v5(name, flags); else minor = create_lockspace_v6(name, flags); - if (minor < 0) { - free(newls); - return NULL; - } + if (minor < 0) + goto fail; - /* Wait for udev to create the device */ - for (i = 1; i < 10; i++) { - if (stat(dev_name, &st) == 0) - break; - sleep(1); + /* Wait for udev to create the device; the device it creates may + have a truncated name due to the sysfs device name limit. */ + + error = find_udev_device(name, minor, udev_path); + if (error) + goto fail; + + /* If the symlink already exists, find_udev_device() will return + it and we'll skip this. */ + + if (strcmp(dev_path, udev_path)) { + error = symlink(udev_path, dev_path); + if (error) + goto fail; } /* Open it and return the struct as a handle */ - newls->fd = open(dev_name, O_RDWR); - if (newls->fd == -1) { - int saved_errno = errno; - free(newls); - errno = saved_errno; - return NULL; - } + newls->fd = open(dev_path, O_RDWR); + if (newls->fd == -1) + goto fail; if (mode) fchmod(newls->fd, mode); newls->tid = 0; fcntl(newls->fd, F_SETFD, 1); return (dlm_lshandle_t)newls; + + fail: + saved_errno = errno; + free(newls); + errno = saved_errno; + return NULL; } dlm_lshandle_t dlm_new_lockspace(const char *name, mode_t mode, uint32_t flags) @@ -1382,9 +1450,15 @@ static int release_lockspace(uint32_t minor, uint32_t flags) int dlm_release_lockspace(const char *name, dlm_lshandle_t ls, int force) { + char dev_path[PATH_MAX]; struct stat st; struct dlm_ls_info *lsinfo = (struct dlm_ls_info *)ls; uint32_t flags = 0; + int fd, is_symlink = 0; + + ls_dev_name(name, dev_path, sizeof(dev_path)); + if (!lstat(dev_path, &st) && S_ISLNK(st.st_mode)) + is_symlink = 1; /* We need the minor number */ if (fstat(lsinfo->fd, &st)) @@ -1400,6 +1474,26 @@ int dlm_release_lockspace(const char *name, dlm_lshandle_t ls, int force) flags = DLM_USER_LSFLG_FORCEFREE; release_lockspace(minor(st.st_rdev), flags); + + if (!is_symlink) + return 0; + + /* The following open is used to detect if our release was the last. + It will fail if our release was the last, because either: + . udev has already removed the truncated sysfs device name (ENOENT) + . the misc device has been deregistered in the kernel (ENODEV) + (the deregister completes before release returns) + + So, if the open fails, we know that our release was the last, + udev will be removing the device with the truncated name (if it + hasn't already), and we should remove the symlink. */ + + fd = open(dev_path, O_RDWR); + if (fd < 0) + unlink(dev_path); + else + close(fd); /* our release was not the last */ + return 0; }