+ char *dir, *base;
+ int dirfd;
+ int len;
+
+ if (!LINUX) {
+ return ENAMETOOLONG;
+ }
+
+ dir = dir_name(name);
+ dirfd = open(dir, O_DIRECTORY | O_RDONLY);
+ if (dirfd < 0) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+ int error = errno;
+
+ VLOG_WARN_RL(&rl, "%s: open failed (%s)", dir, ovs_strerror(error));
+ free(dir);
+
+ return error;
+ }
+ free(dir);
+
+ base = base_name(name);
+ len = snprintf(short_name, MAX_UN_LEN + 1,
+ "/proc/self/fd/%d/%s", dirfd, base);
+ free(base);
+
+ if (len >= 0 && len <= MAX_UN_LEN) {
+ *dirfdp = dirfd;
+ return 0;
+ } else {
+ close(dirfd);
+ return ENAMETOOLONG;
+ }
+}
+
+/* Attempts to shorten 'name' by creating a symlink for the directory part of
+ * the name and indirecting through <symlink>/<basename>. This works on
+ * systems that support symlinks, as long as <basename> isn't too long.
+ *
+ * On success, returns 0 and stores the short name in 'short_name' and the
+ * symbolic link to eventually delete in 'linkname'. */
+static int
+shorten_name_via_symlink(const char *name, char short_name[MAX_UN_LEN + 1],
+ char linkname[MAX_UN_LEN + 1])
+{
+ char *abs, *dir, *base;
+ const char *tmpdir;
+ int error;
+ int i;
+
+ abs = abs_file_name(NULL, name);
+ dir = dir_name(abs);
+ base = base_name(abs);
+ free(abs);
+
+ tmpdir = getenv("TMPDIR");
+ if (tmpdir == NULL) {
+ tmpdir = "/tmp";
+ }
+
+ for (i = 0; i < 1000; i++) {
+ int len;
+
+ len = snprintf(linkname, MAX_UN_LEN + 1,
+ "%s/ovs-un-c-%"PRIu32, tmpdir, random_uint32());
+ error = (len < 0 || len > MAX_UN_LEN ? ENAMETOOLONG
+ : symlink(dir, linkname) ? errno
+ : 0);
+ if (error != EEXIST) {
+ break;
+ }
+ }
+
+ if (!error) {
+ int len;
+
+ fatal_signal_add_file_to_unlink(linkname);
+
+ len = snprintf(short_name, MAX_UN_LEN + 1, "%s/%s", linkname, base);
+ if (len < 0 || len > MAX_UN_LEN) {
+ fatal_signal_unlink_file_now(linkname);
+ error = ENAMETOOLONG;
+ }
+ }
+
+ if (error) {
+ linkname[0] = '\0';
+ }
+ free(dir);
+ free(base);
+
+ return error;