s!/etc/slackware-release!/etc/slackware-version! (reported by bubulak)
[util-vserver.git] / util-vserver / src / vhashify.c
index 7d2b072..5e37887 100644 (file)
 #define ENSC_WRAPPERS_UNISTD    1
 #define ENSC_WRAPPERS_FCNTL     1
 #define ENSC_WRAPPERS_DIRENT    1
+#define ENSC_WRAPPERS_IO       1
 #include <wrappers.h>
 
 
 #define HASH_BLOCKSIZE         0x10000000u
 #define HASH_MINSIZE           0x10
+#define HASH_MAXBITS           256             // we have to take care about
+                                               // max filename-length...
 
 #if HASH_MINSIZE<=0
 #  error HASH_MINSIZE must be not '0'
@@ -82,13 +85,23 @@ CMDLINE_OPTIONS[] = {
   { 0,0,0,0 }
 };
 
-  // SHA1, grouped by 4 digits + hash-collision counter + 2* '/' + NULL
-typedef char                   HashPath[160/4 + (160/4/4) + sizeof(unsigned int)*2 + 3];
+  // hash digest grouped by 2 digits + hash-collision counter + 2* '/' + NULL
+typedef char                   HashPath[HASH_MAXBITS/4 + (HASH_MAXBITS/4/2) +
+                                        sizeof(unsigned int)*2 + 3];
+
+struct HashDirConfiguration
+{
+    hashFunction const                         *method;
+    enum { hshALL=0, hshSTART = 1, hshMIDDLE=2,
+          hshEND = 4, hshINVALID = -1 }        blocks;
+    size_t                                     blocksize;
+};
 
 struct WalkdownInfo
 {
     PathInfo                   state;
     struct MatchList           dst_list;
+    struct HashDirConfiguration        hash_conf;
     HashDirCollection          hash_dirs;
     size_t                     hash_dirs_max_size;
 
@@ -97,9 +110,14 @@ struct WalkdownInfo
 
 int                            wrapper_exit_code = 1;
 struct Arguments const         *global_args;
-struct WalkdownInfo            global_info;
 static struct SkipReason       skip_reason;
 
+struct WalkdownInfo            global_info = {
+  .hash_conf = { .method     = 0,
+                .blocks     = hshALL,
+                .blocksize  = 0x10000 }
+};
+
 #include "vhashify-init.hc"
 
 int Global_getVerbosity() {
@@ -215,14 +233,14 @@ checkFstat(PathInfo const * const basename,
   return true;
 }
 
-static jmp_buf                 bus_error_restore;
+static sigjmp_buf              bus_error_restore;
 static volatile sig_atomic_t   bus_error;
 
 static void
 handlerSIGBUS(int UNUSED num)
 {
   bus_error = 1;
-  longjmp(bus_error_restore, 1);
+  siglongjmp(bus_error_restore, 1);
 }
 
 static bool
@@ -241,15 +259,17 @@ convertDigest(HashPath d_path)
   for (size_t in=0;
        out+1<sizeof(HashPath)-(sizeof(unsigned int)*2 + 2) && in<d_size;
        ++in) {
-    if (in%2 == 0 && in>0) d_path[out++]='/';
-    d_path[out++] = HEX_DIGIT[digest[in] >>    4];
-    d_path[out++] = HEX_DIGIT[digest[in] &  0x0f];
+    if ((in+254)%(in<=2 ? 1 : 256) == 0 && in>0)
+      d_path[out++]='/';
+    d_path[out++]  = HEX_DIGIT[digest[in] >>    4];
+    d_path[out++]  = HEX_DIGIT[digest[in] &  0x0f];
   }
   d_path[out++] = '\0';
   
   return true;
 }
 
+#ifndef ENSC_TESTSUITE
 static bool
 addStatHash(hashFunctionContext *h_ctx, struct stat const * const st)
 {
@@ -272,18 +292,27 @@ addStatHash(hashFunctionContext *h_ctx, struct stat const * const st)
     SET_ATTR(mtime)
   };
 
+#undef SET_ATTR
+#undef DECL_ATTR
+
+  
   return hashFunctionContextUpdate(h_ctx, (void *)&tmp, sizeof tmp)!=-1;
 }
-
+#else
+static bool
+addStatHash(hashFunctionContext UNUSED *h_ctx, struct stat const UNUSED * const st)
+{
+  return true;
+}
+#endif
+  
 static bool
 calculateHashFromFD(int fd, HashPath d_path, struct stat const * const st)
 {
   hashFunctionContext * const  h_ctx    = &global_info.hash_context;
-  bool                         res      = false;
-  loff_t                       offset   = 0;
-  void                         *buf     = 0;
-  off_t                                size     = st->st_size;
-  loff_t                       cur_size = 0;
+  void const * volatile                buf      = 0;
+  loff_t volatile              buf_size = 0;
+  bool   volatile              res      = false;
 
 
   if (hashFunctionContextReset(h_ctx)==-1 ||
@@ -291,30 +320,33 @@ calculateHashFromFD(int fd, HashPath d_path, struct stat const * const st)
     return false;
 
   bus_error = 0;
-  if (setjmp(bus_error_restore)!=0) goto out;
+  if (sigsetjmp(bus_error_restore,1)==0) {
+    loff_t                     offset   = 0;
+    off_t                      size     = st->st_size;
 
-  while (offset < size) {
-    cur_size = size-offset;
-    if (cur_size>HASH_BLOCKSIZE) cur_size = HASH_BLOCKSIZE;
+    while (offset < size) {
+      buf_size = size-offset;
+      if (buf_size>HASH_BLOCKSIZE) buf_size = HASH_BLOCKSIZE;
 
-    buf     = mmap(0, cur_size, PROT_READ, MAP_SHARED, fd, offset);
-    if (buf==0) goto out;
-    
-    offset += cur_size;
-    madvise(buf, cur_size, MADV_SEQUENTIAL);   // ignore error...
+      if ((buf=mmap(0, buf_size, PROT_READ, MAP_SHARED, fd, offset))==0) {
+       perror("mmap(<hash>)");
+       goto out;
+      }
 
-    if (hashFunctionContextUpdate(h_ctx, buf, cur_size)==-1) goto out;
+      offset += buf_size;
+      madvise(const_cast(void *)(buf), buf_size, MADV_SEQUENTIAL);     // ignore error...
 
-    munmap(buf, cur_size);
-    buf = 0;
-  }
+      if (hashFunctionContextUpdate(h_ctx, buf, buf_size)==-1) goto out;
 
-  if (!convertDigest(d_path)) goto out;
-    
-  res = true;
+      munmap(const_cast(void *)(buf), buf_size);
+      buf = 0;
+    }
+
+    res = convertDigest(d_path);
+  }
 
   out:
-  if (buf!=0) munmap(buf, cur_size);
+  if (buf!=0) munmap(const_cast(void *)(buf), buf_size);
   return res;
 }
 
@@ -357,31 +389,64 @@ calculateHash(PathInfo const *filename, HashPath d_path, struct stat const * con
   return res;
 }
 
+static enum { mkdirFAIL, mkdirSUCCESS, mkdirSKIP }
+mkdirSingle(char const *path, char *end_ptr, int good_err)
+{
+  *end_ptr = '\0';
+  if (mkdir(path, 0700)!=-1 || errno==EEXIST) {
+    *end_ptr = '/';
+    return mkdirSUCCESS;
+  }
+  else if (errno==good_err) {
+    *end_ptr = '/';
+    return mkdirSKIP;
+  }
+  else {
+    int                old_errno = errno;
+    WRITE_MSG(2, "mkdir('");
+    WRITE_STR(2, path);
+    errno = old_errno;
+    perror("')");
+    return mkdirFAIL;
+  }
+}
+
+static char *
+rstrchr(char *str, char c)
+{
+  while (*str!=c) --str;
+  return str;
+}
+
 static bool
 mkdirRecursive(char const *path)
 {
-  struct stat          st;
-
-  if (path[0]!='/')        return false; // only absolute paths
-  if (lstat(path,&st)!=-1) return true;
+  if (path[0]!='/')      return false; // only absolute paths
 
   char                 buf[strlen(path)+1];
-  char *               ptr = buf+1;
-  
+  char *               ptr = buf + sizeof(buf) - 2;
+
   strcpy(buf, path);
 
-  while ((ptr = strchr(ptr, '/'))!=0) {
-    *ptr = '\0';
-    if (mkdir(buf, 0700)==-1 && errno!=EEXIST) {
-      int              old_errno = errno;
-      WRITE_MSG(2, "mkdir('");
-      WRITE_STR(2, buf);
-      errno = old_errno;
-      perror("')");
-      return false;
+  while (ptr>buf && (ptr = rstrchr(ptr, '/'))!=0) {
+    switch (mkdirSingle(buf, ptr, ENOENT)) {
+      case mkdirSUCCESS                :  break;
+      case mkdirSKIP           :  --ptr; continue;
+      case mkdirFAIL           :  return false;
+    }
+
+    break;     // implied by mkdirSUCCESS
+  }
+
+  assert(ptr!=0);
+  ++ptr;
+
+  while ((ptr=strchr(ptr, '/'))!=0) {
+    switch (mkdirSingle(buf, ptr, 0)) {
+      case mkdirSKIP           :
+      case mkdirFAIL           :  return false;
+      case mkdirSUCCESS                :  ++ptr; continue;
     }
-    *ptr = '/';
-    ++ptr;
   }
 
   return true;
@@ -399,18 +464,13 @@ resolveCollisions(char *result, PathInfo const *root, HashPath d_path,
   char                 buf[sizeof(int)*2 + 1];
   size_t               len;
 
-  *ptr++             = '/';
-  *ptr               = '\0';
-  ptr[sizeof(int)*2] = '\0';
-
-  if (!global_args->dry_run &&
-      !mkdirRecursive(result))
-    return false;
+  *ptr                 = '-';
+  ptr[sizeof(int)*2+1] = '\0';
 
   for (;; ++idx) {
     len = utilvserver_fmt_xuint(buf, idx);
-    memset(ptr, '0', sizeof(int)*2 - len);
-    memcpy(ptr + sizeof(int)*2 - len, buf, len);
+    memset(ptr+1, '0', sizeof(int)*2 - len);
+    memcpy(ptr+1 + sizeof(int)*2 - len, buf, len);
 
     if (lstat(result, hash_st)==-1) {
       if (global_args->dry_run && errno!=ENOENT) {
@@ -432,9 +492,14 @@ resolveCollisions(char *result, PathInfo const *root, HashPath d_path,
       break;           // ok, we finish here
 
     if (!global_args->dry_run) {
+      *ptr = '\0';
+      if (!mkdirRecursive(result))
+       return false;
+      *ptr = '-';
+
       int              fd = open(result, O_NOFOLLOW|O_EXCL|O_CREAT|O_WRONLY, 0200);
 
-      if (global_args->dry_run && fd==-1) {
+      if (fd==-1) {
        int             old_errno = errno;
        WRITE_MSG(2, "open('");
        WRITE_STR(2, buf);
@@ -627,11 +692,6 @@ int main(int argc, char *argv[])
 
   Vector_init(&global_info.hash_dirs, sizeof(struct HashDirInfo));
 
-  if (hashFunctionContextInit(&global_info.hash_context,
-                             hashFunctionDefault())==-1)
-    return EXIT_FAILURE;
-
-
   global_args = &args;
   while (1) {
     int                c = getopt_long(argc, argv, "+nv",
@@ -673,6 +733,12 @@ int main(int argc, char *argv[])
     default            :  assert(false); return EXIT_FAILURE;
   };
 
+  if (hashFunctionContextInit(&global_info.hash_context,
+                             global_info.hash_conf.method)==-1) {
+    WRITE_MSG(2, "Failed to initialize hash-context\n");
+    return EXIT_FAILURE;
+  }
+
   if (Global_getVerbosity()>=1)
     WRITE_MSG(1, "Starting to traverse directories...\n");
 
@@ -686,4 +752,6 @@ int main(int argc, char *argv[])
   freeHashList(&global_info.hash_dirs);
   hashFunctionContextFree(&global_info.hash_context);
 #endif
+
+  return EXIT_SUCCESS;
 }