[ovs-dev] [PATCH] socket-util: try to deal with long path using a temporary symlink
YAMAMOTO Takashi
yamamoto at valinux.co.jp
Wed Oct 16 04:44:37 UTC 2013
the existing procfs stuff is not always usable.
stop skipping some relevant tests even if procfs is not available.
Signed-off-by: YAMAMOTO Takashi <yamamoto at valinux.co.jp>
---
lib/socket-util.c | 76 +++++++++++++++++++++++++++++++++++++++++++++--
python/ovs/socket_util.py | 46 +++++++++++++++++++++++++++-
tests/library.at | 2 --
3 files changed, 118 insertions(+), 6 deletions(-)
diff --git a/lib/socket-util.c b/lib/socket-util.c
index 7f34ea2..0d5fd89 100644
--- a/lib/socket-util.c
+++ b/lib/socket-util.c
@@ -337,6 +337,62 @@ drain_fd(int fd, size_t n_packets)
}
}
+/* A helper for make_sockaddr_un. returns malloc'ed string or NULL. */
+static const char *
+make_short_name(const char *long_name)
+{
+ const char *tmpdir;
+ char *long_absname;
+ char *long_dirname;
+ unsigned int i;
+
+ long_absname = abs_file_name(NULL, long_name);
+ long_dirname = dir_name(long_absname);
+ tmpdir = getenv("TMPDIR");
+ if (tmpdir == NULL) {
+ tmpdir = "/tmp";
+ }
+ for (i = 0; i < 1000; i++) {
+ char link_name[PATH_MAX];
+
+ snprintf(link_name, sizeof(link_name),
+ "%s/ovs-un-c-%" PRIu32 "-%u", tmpdir, random_uint32(), i);
+ if (symlink(long_dirname, link_name) == 0) {
+ char short_name[PATH_MAX];
+ char *long_basename;
+
+ fatal_signal_add_file_to_unlink(link_name);
+ long_basename = base_name(long_absname);
+ snprintf(short_name, sizeof(short_name),
+ "%s/%s", link_name, long_basename);
+ free(long_absname);
+ free(long_dirname);
+ free(long_basename);
+ return xstrdup(short_name);
+ }
+ if (errno != EEXIST) {
+ break;
+ }
+ }
+ free(long_absname);
+ free(long_dirname);
+ return NULL;
+}
+
+/* Clean up the name created by make_short_name */
+static void
+unlink_short_name(const char *short_name)
+{
+ char *link_name;
+
+ if (short_name == NULL) {
+ return;
+ }
+ link_name = dir_name(short_name);
+ fatal_signal_unlink_file_now(link_name);
+ free(CONST_CAST(void *, short_name));
+}
+
/* Stores in '*un' a sockaddr_un that refers to file 'name'. Stores in
* '*un_len' the size of the sockaddr_un. */
static void
@@ -357,11 +413,12 @@ make_sockaddr_un__(const char *name, struct sockaddr_un *un, socklen_t *un_len)
* -1. */
static int
make_sockaddr_un(const char *name, struct sockaddr_un *un, socklen_t *un_len,
- int *dirfdp)
+ int *dirfdp, const char **short_namep)
{
enum { MAX_UN_LEN = sizeof un->sun_path - 1 };
*dirfdp = -1;
+ *short_namep = NULL;
if (strlen(name) > MAX_UN_LEN) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
@@ -400,6 +457,14 @@ make_sockaddr_un(const char *name, struct sockaddr_un *un, socklen_t *un_len,
VLOG_WARN_RL(&rl, "Unix socket name %s is longer than maximum "
"%d bytes (even shortened)", name, MAX_UN_LEN);
} else {
+ const char *short_name = make_short_name(name);
+
+ if (short_name != NULL && strlen(short_name) <= MAX_UN_LEN) {
+ make_sockaddr_un__(short_name, un, un_len);
+ *short_namep = short_name;
+ return 0;
+ }
+ unlink_short_name(short_name);
/* 'name' is too long and we have no workaround. */
VLOG_WARN_RL(&rl, "Unix socket name %s is longer than maximum "
"%d bytes", name, MAX_UN_LEN);
@@ -456,6 +521,7 @@ make_unix_socket(int style, bool nonblock,
struct sockaddr_un un;
socklen_t un_len;
int dirfd;
+ const char *short_name;
if (unlink(bind_path) && errno != ENOENT) {
VLOG_WARN("unlinking \"%s\": %s\n",
@@ -463,13 +529,14 @@ make_unix_socket(int style, bool nonblock,
}
fatal_signal_add_file_to_unlink(bind_path);
- error = make_sockaddr_un(bind_path, &un, &un_len, &dirfd);
+ error = make_sockaddr_un(bind_path, &un, &un_len, &dirfd, &short_name);
if (!error) {
error = bind_unix_socket(fd, (struct sockaddr *) &un, un_len);
}
if (dirfd >= 0) {
close(dirfd);
}
+ unlink_short_name(short_name);
if (error) {
goto error;
}
@@ -479,8 +546,10 @@ make_unix_socket(int style, bool nonblock,
struct sockaddr_un un;
socklen_t un_len;
int dirfd;
+ const char *short_name;
- error = make_sockaddr_un(connect_path, &un, &un_len, &dirfd);
+ error = make_sockaddr_un(connect_path, &un, &un_len, &dirfd,
+ &short_name);
if (!error
&& connect(fd, (struct sockaddr*) &un, un_len)
&& errno != EINPROGRESS) {
@@ -489,6 +558,7 @@ make_unix_socket(int style, bool nonblock,
if (dirfd >= 0) {
close(dirfd);
}
+ unlink_short_name(short_name);
if (error) {
goto error;
}
diff --git a/python/ovs/socket_util.py b/python/ovs/socket_util.py
index 7dac5bb..c657047 100644
--- a/python/ovs/socket_util.py
+++ b/python/ovs/socket_util.py
@@ -14,6 +14,8 @@
import errno
import os
+import os.path
+import random
import select
import socket
import sys
@@ -25,7 +27,33 @@ import ovs.vlog
vlog = ovs.vlog.Vlog("socket_util")
-def make_unix_socket(style, nonblock, bind_path, connect_path):
+def make_short_name(long_name):
+ if long_name is None:
+ return None
+ long_name = os.path.abspath(long_name)
+ long_dirname = os.path.dirname(long_name)
+ tmpdir = os.getenv('TMPDIR', '/tmp')
+ for x in xrange(0, 1000):
+ link_name = \
+ '%s/ovs-un-py-%d-%d' % (tmpdir, random.randint(0, 10000), x)
+ try:
+ os.symlink(long_dirname, link_name)
+ ovs.fatal_signal.add_file_to_unlink(link_name)
+ return os.path.join(link_name, os.path.basename(long_name))
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ break
+ raise Exception("Failed to create temporary symlink")
+
+
+def free_short_name(short_name):
+ if short_name is None:
+ return
+ link_name = os.path.dirname(short_name)
+ ovs.fatal_signal.unlink_file_now(link_name)
+
+
+def make_unix_socket(style, nonblock, bind_path, connect_path, short=False):
"""Creates a Unix domain socket in the given 'style' (either
socket.SOCK_DGRAM or socket.SOCK_STREAM) that is bound to 'bind_path' (if
'bind_path' is not None) and connected to 'connect_path' (if 'connect_path'
@@ -106,6 +134,22 @@ def make_unix_socket(style, nonblock, bind_path, connect_path):
os.close(connect_dirfd)
if bind_dirfd is not None:
os.close(bind_dirfd)
+ elif (eno == "AF_UNIX path too long"):
+ if short:
+ return get_exception_errno(e), None
+ short_bind_path = None
+ try:
+ short_bind_path = make_short_name(bind_path)
+ short_connect_path = make_short_name(connect_path)
+ except:
+ free_short_name(short_bind_path)
+ return errno.ENAMETOOLONG, None
+ try:
+ return make_unix_socket(style, nonblock, short_bind_path,
+ short_connect_path, short=True)
+ finally:
+ free_short_name(short_bind_path)
+ free_short_name(short_connect_path)
else:
return get_exception_errno(e), None
diff --git a/tests/library.at b/tests/library.at
index a978c13..6d1c6da 100644
--- a/tests/library.at
+++ b/tests/library.at
@@ -135,7 +135,6 @@ dnl is about 100 bytes. On Linux, we work around this by indirecting through
dnl a directory fd using /proc/self/fd/<dirfd>. We do not have a workaround
dnl for other platforms, so we skip the test there.
AT_SETUP([test unix socket, long pathname - C])
-AT_SKIP_IF([test ! -d /proc/self/fd])
dnl Linux has a 108 byte limit; this is 150 bytes long.
longname=012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
mkdir $longname
@@ -155,7 +154,6 @@ dnl a directory fd using /proc/self/fd/<dirfd>. We do not have a workaround
dnl for other platforms, so we skip the test there.
AT_SETUP([test unix socket, long pathname - Python])
AT_SKIP_IF([test $HAVE_PYTHON = no])
-AT_SKIP_IF([test ! -d /proc/self/fd])
dnl Linux has a 108 byte limit; this is 150 bytes long.
longname=012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
mkdir $longname
--
1.8.3.1
More information about the dev
mailing list