[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