-fixing #3034
[oweals/gnunet.git] / src / fs / gnunet-helper-fs-publish.c
index b96559cd14d62fdede6e84b0c8aa63d256eccecb..2aa9df0e40ecbdeee7b392eae7bf1677ce15106d 100644 (file)
@@ -87,6 +87,11 @@ struct ScanTreeNode
  */
 static struct EXTRACTOR_PluginList *plugins;
 
+/**
+ * File descriptor we use for IPC with the parent.
+ */
+static int output_stream;
+
 
 /**
  * Add meta data that libextractor finds to our meta data
@@ -140,11 +145,11 @@ free_tree (struct ScanTreeNode *tree)
 
 
 /**
- * Write 'size' bytes from 'buf' into 'out'.
+ * Write @a size bytes from @a buf into the #output_stream.
  *
  * @param buf buffer with data to write
  * @param size number of bytes to write
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
 write_all (const void *buf,
@@ -157,7 +162,7 @@ write_all (const void *buf,
   total = 0;
   do
   {
-    wr = write (1,
+    wr = write (output_stream,
                &cbuf[total],
                size - total);
     if (wr > 0)
@@ -176,8 +181,8 @@ write_all (const void *buf,
  *
  * @param message_type message type to use
  * @param data data to append, NULL for none
- * @param data_length number of bytes in data
- * @return GNUNET_SYSERR to stop scanning (the pipe was broken somehow)
+ * @param data_length number of bytes in @a data
+ * @return #GNUNET_SYSERR to stop scanning (the pipe was broken somehow)
  */
 static int
 write_message (uint16_t message_type,
@@ -186,6 +191,12 @@ write_message (uint16_t message_type,
 {
   struct GNUNET_MessageHeader hdr;
 
+#if 0
+  fprintf (stderr, 
+          "Helper sends %u-byte message of type %u\n",
+          (unsigned int) (sizeof (struct GNUNET_MessageHeader) + data_length),
+          (unsigned int) message_type);
+#endif
   hdr.type = htons (message_type);
   hdr.size = htons (sizeof (struct GNUNET_MessageHeader) + data_length);
   if ( (GNUNET_OK !=
@@ -205,8 +216,9 @@ write_message (uint16_t message_type,
  * the scan.  Does NOT yet add any metadata.
  *
  * @param filename file or directory to scan
- * @param dst where to store the resulting share tree item
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ * @param dst where to store the resulting share tree item;
+ *         NULL is stored in @a dst upon recoverable errors (#GNUNET_OK is returned)
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
 preprocess_file (const char *filename,
@@ -235,9 +247,9 @@ struct RecursionContext
  * of the files in the directory to the tree.  Called by the directory
  * scanner to initiate the scan.  Does NOT yet add any metadata.
  *
- * @param cls the 'struct RecursionContext'
+ * @param cls the `struct RecursionContext`
  * @param filename file or directory to scan
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
 scan_callback (void *cls,
@@ -253,6 +265,8 @@ scan_callback (void *cls,
     rc->stop = GNUNET_YES;
     return GNUNET_SYSERR;
   }
+  if (NULL == chld)
+    return GNUNET_OK;
   chld->parent = rc->parent;
   GNUNET_CONTAINER_DLL_insert (rc->parent->children_head,
                               rc->parent->children_tail,
@@ -267,8 +281,9 @@ scan_callback (void *cls,
  * the scan.  Does NOT yet add any metadata.
  *
  * @param filename file or directory to scan
- * @param dst where to store the resulting share tree item
- * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ * @param dst where to store the resulting share tree item;
+ *         NULL is stored in @a dst upon recoverable errors (#GNUNET_OK is returned) 
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
 preprocess_file (const char *filename,
@@ -288,6 +303,8 @@ preprocess_file (const char *filename,
        write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_SKIP_FILE,
                       filename, strlen (filename) + 1))
       return GNUNET_SYSERR;
+    /* recoverable error, store 'NULL' in *dst */
+    *dst = NULL;
     return GNUNET_OK;
   }
 
@@ -302,7 +319,7 @@ preprocess_file (const char *filename,
   item->filename = GNUNET_strdup (filename);
   item->is_directory = (S_ISDIR (sbuf.st_mode)) ? GNUNET_YES : GNUNET_NO;
   item->file_size = fsize;
-  if (item->is_directory == GNUNET_YES)
+  if (GNUNET_YES == item->is_directory)
   {
     struct RecursionContext rc;
 
@@ -311,7 +328,7 @@ preprocess_file (const char *filename,
     GNUNET_DISK_directory_scan (filename, 
                                &scan_callback, 
                                &rc);    
-    if ( (rc.stop == GNUNET_YES) ||
+    if ( (GNUNET_YES == rc.stop) ||
         (GNUNET_OK !=
          write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_PROGRESS_DIRECTORY,
                         "..", 3)) )
@@ -329,7 +346,7 @@ preprocess_file (const char *filename,
  * Extract metadata from files.
  *
  * @param item entry we are processing
- * @return GNUNET_OK on success, GNUNET_SYSERR on fatal errors
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on fatal errors
  */
 static int
 extract_files (struct ScanTreeNode *item)
@@ -338,7 +355,7 @@ extract_files (struct ScanTreeNode *item)
   ssize_t size;
   size_t slen;
 
-  if (item->is_directory == GNUNET_YES)
+  if (GNUNET_YES == item->is_directory)
   {
     /* for directories, we simply only descent, no extraction, no
        progress reporting */
@@ -353,8 +370,7 @@ extract_files (struct ScanTreeNode *item)
   
   /* this is the expensive operation, *afterwards* we'll check for aborts */
   meta = GNUNET_CONTAINER_meta_data_create ();
-  if (NULL != plugins)
-    EXTRACTOR_extract (plugins, item->filename, NULL, 0, &add_to_md, meta);
+  EXTRACTOR_extract (plugins, item->filename, NULL, 0, &add_to_md, meta);
   slen = strlen (item->filename) + 1;
   size = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
   if (-1 == size)
@@ -367,6 +383,11 @@ extract_files (struct ScanTreeNode *item)
       return GNUNET_SYSERR;    
     return GNUNET_OK;
   }
+  else if (size > (UINT16_MAX - sizeof (struct GNUNET_MessageHeader) - slen))
+  {
+    /* We can't transfer more than 64k bytes in one message. */
+    size = UINT16_MAX - sizeof (struct GNUNET_MessageHeader) - slen;
+  }
   {
     char buf[size + slen];
     char *dst = &buf[slen];
@@ -391,6 +412,54 @@ extract_files (struct ScanTreeNode *item)
 }
 
 
+#ifndef WINDOWS
+/**
+ * Install a signal handler to ignore SIGPIPE.
+ */
+static void
+ignore_sigpipe ()
+{
+  struct sigaction oldsig;
+  struct sigaction sig;
+
+  memset (&sig, 0, sizeof (struct sigaction));
+  sig.sa_handler = SIG_IGN;
+  sigemptyset (&sig.sa_mask);
+#ifdef SA_INTERRUPT
+  sig.sa_flags = SA_INTERRUPT;  /* SunOS */
+#else
+  sig.sa_flags = SA_RESTART;
+#endif
+  if (0 != sigaction (SIGPIPE, &sig, &oldsig))
+    fprintf (stderr,
+             "Failed to install SIGPIPE handler: %s\n", strerror (errno));
+}
+
+
+/**
+ * Turn the given file descriptor in to '/dev/null'.
+ *
+ * @param fd fd to bind to /dev/null
+ * @param flags flags to use (O_RDONLY or O_WRONLY)
+ */
+static void
+make_dev_zero (int fd, 
+              int flags)
+{
+  int z;
+
+  GNUNET_assert (0 == close (fd));
+  z = open ("/dev/null", flags);
+  GNUNET_assert (-1 != z);
+  if (z == fd)
+    return;
+  dup2 (z, fd);
+  GNUNET_assert (0 == close (z));
+}
+
+#endif
+
+
 /**
  * Main function of the helper process to extract meta data.
  *
@@ -401,8 +470,9 @@ extract_files (struct ScanTreeNode *item)
  *                 otherwise custom plugins to load from LE
  * @return 0 on success
  */
-int main(int argc,
-        char **argv)
+int 
+main (int argc,
+      char *const *argv)
 {
   const char *filename_expanded;
   const char *ex;
@@ -413,19 +483,33 @@ int main(int argc,
    * binary mode.
    */
   _setmode (1, _O_BINARY);
+  /* Get utf-8-encoded arguments */
+  if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+    return 5;
+  output_stream = 1; /* stdout */
+#else
+  ignore_sigpipe ();
+  /* move stdout to some other FD for IPC, bind 
+     stdout/stderr to /dev/null */
+  output_stream = dup (1);
+  make_dev_zero (1, O_WRONLY);
+  make_dev_zero (2, O_WRONLY);
 #endif
 
   /* parse command line */
-  if ( (argc != 3) && (argc != 2) )
+  if ( (3 != argc) && (2 != argc) )
   {
     FPRINTF (stderr, 
             "%s",
             "gnunet-helper-fs-publish needs exactly one or two arguments\n");
+#if WINDOWS
+    GNUNET_free ((void*) argv);
+#endif
     return 1;
   }
   filename_expanded = argv[1];
   ex = argv[2];
-  if ( (ex == NULL) ||
+  if ( (NULL == ex) ||
        (0 != strcmp (ex, "-")) )
   {
     plugins = EXTRACTOR_plugin_add_defaults (EXTRACTOR_OPTION_DEFAULT_POLICY);
@@ -439,26 +523,44 @@ int main(int argc,
                                    &root))
   {
     (void) write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_ERROR, NULL, 0);
+    EXTRACTOR_plugin_remove_all (plugins);
+#if WINDOWS
+    GNUNET_free ((void*) argv);
+#endif
     return 2;
   }
   /* signal that we're done counting files, so that a percentage of 
      progress can now be calculated */
   if (GNUNET_OK !=
       write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_COUNTING_DONE, NULL, 0))
+  {
+    EXTRACTOR_plugin_remove_all (plugins);
+#if WINDOWS
+    GNUNET_free ((void*) argv);
+#endif
     return 3;  
-  if (GNUNET_OK !=
-      extract_files (root))
+  }
+  if (NULL != root)
   {
-    (void) write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_ERROR, NULL, 0);
+    if (GNUNET_OK !=
+       extract_files (root))
+    {
+      (void) write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_ERROR, NULL, 0);
+      free_tree (root);
+      EXTRACTOR_plugin_remove_all (plugins);
+#if WINDOWS
+      GNUNET_free ((void*) argv);
+#endif
+      return 4;
+    }
     free_tree (root);
-    return 4;
   }
-  free_tree (root);
   /* enable "clean" shutdown by telling parent that we are done */
   (void) write_message (GNUNET_MESSAGE_TYPE_FS_PUBLISH_HELPER_FINISHED, NULL, 0);
-  if (NULL != plugins)
-    EXTRACTOR_plugin_remove_all (plugins);
-
+  EXTRACTOR_plugin_remove_all (plugins);
+#if WINDOWS
+  GNUNET_free ((void*) argv);  
+#endif
   return 0;
 }