Revert "ipc.c: Use ftok() instead of SuperFastHash."
authorAndre Noll <maan@tuebingen.mpg.de>
Wed, 6 Sep 2017 12:57:28 +0000 (14:57 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Fri, 6 Oct 2017 09:43:30 +0000 (11:43 +0200)
This reverts commit c92370affe722f38a85a41d1b5524e4a102b8f4d.

This was not a good idea because ftok(3) hashes, among other
information, the inode number of the file, and this number changes
every time the configuration file is edited.

The revert conflicted slightly to the commit which renamed
get_key_or_die() to get_key() and changed the type of the return
value to key_t, but the conflict was easy to resolve.

NEWS
ipc.c

diff --git a/NEWS b/NEWS
index 1532320..c87e411 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+-----------------------
+x.y.z (to be announced)
+-----------------------
+
+ - Improved error diagnostics for the kill subcommand.
+
 ------------------
 0.1.7 (2017-04-17)
 ------------------
diff --git a/ipc.c b/ipc.c
index 0f295a9..183c76e 100644 (file)
--- a/ipc.c
+++ b/ipc.c
 #include "err.h"
 #include "ipc.h"
 
-static key_t get_key(const char *config_file)
+#if (defined(__GNUC__) && defined(__i386__))
+#define get16bits(d) (*((const uint16_t *) (d)))
+#else
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+               +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+/*
+ * SuperFastHash, by Paul Hsieh.
+ * http://www.azillionmonkeys.com/qed/hash.html
+ */
+static uint32_t super_fast_hash(const uint8_t *data, uint32_t len, uint32_t hash)
 {
-       key_t ret;
+       uint32_t tmp;
+       int rem = len & 3;
 
-       assert(config_file);
-       ret = ftok(config_file, 'D');
-       if (ret >= 0)
-               return ret;
+       len >>= 2;
+
+       for (;len > 0; len--) {
+               hash  += get16bits (data);
+               tmp    = (get16bits (data+2) << 11) ^ hash;
+               hash   = (hash << 16) ^ tmp;
+               data  += 2*sizeof (uint16_t);
+               hash  += hash >> 11;
+       }
+
+       /* Handle end cases */
+       switch (rem) {
+       case 3:
+               hash += get16bits (data);
+               hash ^= hash << 16;
+               hash ^= data[sizeof (uint16_t)] << 18;
+               hash += hash >> 11;
+               break;
+       case 2:
+               hash += get16bits (data);
+               hash ^= hash << 11;
+               hash += hash >> 17;
+               break;
+       case 1:
+               hash += *data;
+               hash ^= hash << 10;
+               hash += hash >> 1;
+       }
+       /* Force "avalanching" of final 127 bits */
+       hash ^= hash << 3;
+       hash += hash >> 5;
+       hash ^= hash << 4;
+       hash += hash >> 17;
+       hash ^= hash << 25;
+       hash += hash >> 6;
+       return hash;
+}
+
+/*
+ * Return the canonical absolute name of a given file name.
+ *
+ * Slightly modified version of glibc's realpath, Copyright (C)
+ * 1996-2002,2004,2005,2006,2008 Free Software Foundation, Inc.
+ *
+ * A canonical name does not contain any `.', `..' components nor any repeated
+ * path separators ('/') or symlinks. All path components must exist. The
+ * result is malloc'd and must be freed by the caller.
+ */
+static int dss_realpath(const char *name, char **resolved_path)
+{
+       char *rpath = NULL, *dest, *extra_buf = NULL;
+       const char *start, *end, *rpath_limit;
+       long int path_max;
+       int ret, num_links = 0;
+
+       if (name[0] == '\0') {
+               /*
+                * As per Single Unix Specification V2 we must return an error
+                * if the name argument points to an empty string.
+                */
+               ret = -ERRNO_TO_DSS_ERROR(ENOENT);
+               goto error;
+       }
+#ifdef PATH_MAX
+       path_max = PATH_MAX;
+#else
        /*
-        * This happens if the user did not specify a config file, and the
-        * default config file does not exist.  Another (unlikely) possibility
-        * is that the config file was removed between startup and this call.
-        * We don't care about these corner cases too much and just return a
-        * fixed key in this case.
+        * From realpath(3): Asking pathconf(3) does not really help, since on
+        * the one hand POSIX warns that the result of pathconf(3) may be
+        * huge and unsuitable for mallocing memory. And on the other hand
+        * pathconf(3) may return -1 to signify that PATH_MAX is not bounded.
         */
-       return 0x0D55; /* no magic, this number just looks a bit like DSS */
+       path_max = pathconf(name, _PC_PATH_MAX);
+       if (path_max <= 0 || path_max >= 4096)
+               path_max = 4096;
+#endif
+       rpath = dss_malloc(path_max);
+       rpath_limit = rpath + path_max;
+
+       if (name[0] != '/') {
+               if (!getcwd(rpath, path_max)) {
+                       ret = -ERRNO_TO_DSS_ERROR(errno);
+                       goto error;
+               }
+               dest = memchr(rpath, '\0', path_max);
+       } else {
+               rpath[0] = '/';
+               dest = rpath + 1;
+       }
+
+       for (start = end = name; *start; start = end) {
+               struct stat st;
+               int n;
+
+               /* Skip sequence of multiple path-separators.  */
+               while (*start == '/')
+                       ++start;
+
+               /* Find end of path component.  */
+               for (end = start; *end && *end != '/'; ++end)
+                       /* Nothing.  */ ;
+
+               if (end - start == 0)
+                       break;
+               else if (end - start == 1 && start[0] == '.')
+                       /* nothing */ ;
+               else if (end - start == 2 && start[0] == '.' && start[1] == '.') {
+                       /* Back up to previous component, ignore if at root already.  */
+                       if (dest > rpath + 1)
+                               while ((--dest)[-1] != '/') ;
+               } else {
+                       size_t new_size;
+
+                       if (dest[-1] != '/')
+                               *dest++ = '/';
+
+                       if (dest + (end - start) >= rpath_limit) {
+                               ptrdiff_t dest_offset = dest - rpath;
+
+                               new_size = rpath_limit - rpath;
+                               if (end - start + 1 > path_max)
+                                       new_size += end - start + 1;
+                               else
+                                       new_size += path_max;
+                               rpath = dss_realloc(rpath, new_size);
+                               rpath_limit = rpath + new_size;
+                               dest = rpath + dest_offset;
+                       }
+
+                       memcpy(dest, start, end - start);
+                       dest += end - start;
+                       *dest = '\0';
+
+                       if (stat(rpath, &st) < 0) {
+                               ret = -ERRNO_TO_DSS_ERROR(errno);
+                               goto error;
+                       }
+
+                       if (S_ISLNK(st.st_mode)) {
+                               char *buf = alloca(path_max);
+                               size_t len;
+
+                               if (++num_links > MAXSYMLINKS) {
+                                       ret = -ERRNO_TO_DSS_ERROR(ELOOP);
+                                       goto error;
+                               }
+
+                               n = readlink(rpath, buf, path_max - 1);
+                               if (n < 0) {
+                                       ret = -ERRNO_TO_DSS_ERROR(errno);
+                                       goto error;
+                               }
+                               buf[n] = '\0';
+
+                               if (!extra_buf)
+                                       extra_buf = alloca(path_max);
+
+                               len = strlen(end);
+                               if ((long int) (n + len) >= path_max) {
+                                       ret = -ERRNO_TO_DSS_ERROR(ENAMETOOLONG);
+                                       goto error;
+                               }
+
+                               /* Careful here, end may be a pointer into extra_buf... */
+                               memmove(&extra_buf[n], end, len + 1);
+                               name = end = memcpy(extra_buf, buf, n);
+
+                               if (buf[0] == '/') /* It's an absolute symlink */
+                                       dest = rpath + 1;
+                               else /* Back up to previous component, ignore if at root already: */
+                                       if (dest > rpath + 1)
+                                               while ((--dest)[-1] != '/')
+                                                       ; /* nothing */
+                       } else if (!S_ISDIR(st.st_mode) && *end != '\0') {
+                               ret = -ERRNO_TO_DSS_ERROR(ENOTDIR);
+                               goto error;
+                       }
+               }
+       }
+       if (dest > rpath + 1 && dest[-1] == '/')
+               --dest;
+       *dest = '\0';
+       *resolved_path = rpath;
+       return 1;
+error:
+       free(rpath);
+       *resolved_path = NULL;
+       return ret;
+}
+
+static int get_key_or_die(const char *config_file)
+{
+       int ret;
+       struct stat statbuf;
+       char *rpath;
+
+       assert(config_file);
+       if (stat(config_file, &statbuf) == 0) {
+               ret = dss_realpath(config_file, &rpath);
+               if (ret < 0) {
+                       DSS_EMERG_LOG(("could not resolve path %s: %s\n", config_file,
+                               dss_strerror(-ret)));
+                       exit(EXIT_FAILURE);
+               }
+               DSS_DEBUG_LOG(("resolved path: %s\n", rpath));
+       } else
+               /*
+                * This happens if the user did not specify a config file, and
+                * the default config file does not exist.  Another (unlikely)
+                * possibility is that the config file was removed between
+                * startup and this call. We don't care about these corner
+                * cases too much and just use the unresolved path in this
+                * case.
+                */
+               rpath = dss_strdup(config_file);
+       ret = super_fast_hash((uint8_t *)rpath, strlen(rpath), 0) >> 1;
+       free(rpath);
+       return ret;
 }
 
 static int mutex_get(key_t key, int flags)
@@ -85,7 +303,7 @@ int lock_dss(char *config_file)
 {
        int ret, id;
        struct sembuf sops[4];
-       key_t key = get_key(config_file);
+       key_t key = get_key_or_die(config_file);
 
        ret = mutex_get(key, IPC_CREAT | 0600);
        if (ret < 0)
@@ -114,7 +332,7 @@ int lock_dss(char *config_file)
 int get_dss_pid(char *config_file, pid_t *pid)
 {
        int ret, semid;
-       key_t key = get_key(config_file);
+       key_t key = get_key_or_die(config_file);
 
        if (pid)
                *pid = 0;