WiP
[oweals/gnunet.git] / src / util / disk.c
index 65509166e1bac25da8bcb4928d0996f9d273669a..e293ccda2e05a0ebd93ce1ebf2be26685973f893 100644 (file)
 #include "gnunet_disk_lib.h"
 #include "gnunet_scheduler_lib.h"
 #include "gnunet_strings_lib.h"
+#include "gnunet_crypto_lib.h"
 #include "disk.h"
 
+#define DEBUG_NPIPE GNUNET_YES
 
 /**
  * Block size for IO for copying files.
@@ -101,6 +103,34 @@ struct GetFileSizeData
 };
 
 
+int translate_unix_perms(enum GNUNET_DISK_AccessPermissions perm)
+{
+  int mode;
+
+  mode = 0;
+  if (perm & GNUNET_DISK_PERM_USER_READ)
+    mode |= S_IRUSR;
+  if (perm & GNUNET_DISK_PERM_USER_WRITE)
+    mode |= S_IWUSR;
+  if (perm & GNUNET_DISK_PERM_USER_EXEC)
+    mode |= S_IXUSR;
+  if (perm & GNUNET_DISK_PERM_GROUP_READ)
+    mode |= S_IRGRP;
+  if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
+    mode |= S_IWGRP;
+  if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
+    mode |= S_IXGRP;
+  if (perm & GNUNET_DISK_PERM_OTHER_READ)
+    mode |= S_IROTH;
+  if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
+    mode |= S_IWOTH;
+  if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
+    mode |= S_IXOTH;
+
+  return mode;
+}
+
+
 /**
  * Iterate over all files in the given directory and 
  * accumulate their size.
@@ -810,10 +840,6 @@ GNUNET_DISK_directory_scan (const char *dirName,
  */
 struct GNUNET_DISK_DirectoryIterator
 {
-  /**
-   * Our scheduler.
-   */
-  struct GNUNET_SCHEDULER_Handle *sched;
 
   /**
    * Function to call on directory entries.
@@ -906,8 +932,7 @@ GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
       GNUNET_DISK_directory_iterator_next (iter, GNUNET_YES);
       return GNUNET_NO;
     }
-  GNUNET_SCHEDULER_add_with_priority (iter->sched,
-                                      iter->priority,
+  GNUNET_SCHEDULER_add_with_priority (iter->priority,
                                       &directory_iterator_task, iter);
   return GNUNET_YES;
 }
@@ -919,15 +944,13 @@ GNUNET_DISK_directory_iterator_next (struct GNUNET_DISK_DirectoryIterator
  * If a scheduler does not need to be used, GNUNET_DISK_directory_scan
  * may provide a simpler API.
  *
- * @param sched scheduler to use
  * @param prio priority to use
  * @param dirName the name of the directory
  * @param callback the method to call for each file
  * @param callback_cls closure for callback
  */
 void
-GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
-                                      enum GNUNET_SCHEDULER_Priority prio,
+GNUNET_DISK_directory_iterator_start (enum GNUNET_SCHEDULER_Priority prio,
                                       const char *dirName,
                                       GNUNET_DISK_DirectoryIteratorCallback
                                       callback, void *callback_cls)
@@ -935,7 +958,6 @@ GNUNET_DISK_directory_iterator_start (struct GNUNET_SCHEDULER_Handle *sched,
   struct GNUNET_DISK_DirectoryIterator *di;
 
   di = GNUNET_malloc (sizeof (struct GNUNET_DISK_DirectoryIterator));
-  di->sched = sched;
   di->callback = callback;
   di->callback_cls = callback_cls;
   di->directory = OPENDIR (dirName);
@@ -1267,24 +1289,7 @@ GNUNET_DISK_file_open (const char *fn,
     {
       (void) GNUNET_DISK_directory_create_for_file (expfn);
       oflags |= O_CREAT;
-      if (perm & GNUNET_DISK_PERM_USER_READ)
-        mode |= S_IRUSR;
-      if (perm & GNUNET_DISK_PERM_USER_WRITE)
-        mode |= S_IWUSR;
-      if (perm & GNUNET_DISK_PERM_USER_EXEC)
-        mode |= S_IXUSR;
-      if (perm & GNUNET_DISK_PERM_GROUP_READ)
-        mode |= S_IRGRP;
-      if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
-        mode |= S_IWGRP;
-      if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
-        mode |= S_IXGRP;
-      if (perm & GNUNET_DISK_PERM_OTHER_READ)
-        mode |= S_IROTH;
-      if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
-        mode |= S_IWOTH;
-      if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
-        mode |= S_IXOTH;
+      mode = translate_unix_perms(perm);
     }
 
   fd = open (expfn, oflags | O_LARGEFILE, mode);
@@ -1355,6 +1360,7 @@ GNUNET_DISK_file_open (const char *fn,
   ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
 #ifdef MINGW
   ret->h = h;
+  ret->type = GNUNET_DISK_FILE;
 #else
   ret->fd = fd;
 #endif
@@ -1462,9 +1468,9 @@ GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
     }
   va_end (ap);
   if ((ret[strlen (ret) - 1] != '/') && (ret[strlen (ret) - 1] != '\\'))
-    GNUNET_DISK_directory_create_for_file (ret);
+    (void) GNUNET_DISK_directory_create_for_file (ret);
   else
-    GNUNET_DISK_directory_create (ret);
+    (void) GNUNET_DISK_directory_create (ret);
   return ret;
 }
 
@@ -1474,17 +1480,17 @@ GNUNET_DISK_get_home_filename (const struct GNUNET_CONFIGURATION_Handle *cfg,
  */
 struct GNUNET_DISK_MapHandle
 {
+  /**
+   * Address where the map is in memory.
+   */
+  void *addr;
+
 #ifdef MINGW
   /**
    * Underlying OS handle.
    */
   HANDLE h;
 #else
-  /**
-   * Address where the map is in memory.
-   */
-  void *addr;
-
   /**
    * Number of bytes mapped.
    */
@@ -1519,7 +1525,6 @@ GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
 
 #ifdef MINGW
   DWORD mapAccess, protect;
-  void *ret;
 
   if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
       (access & GNUNET_DISK_MAP_TYPE_WRITE))
@@ -1552,15 +1557,15 @@ GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
       return NULL;
     }
 
-  ret = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
-  if (!ret)
+  (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
+  if (!(*m)->addr)
     {
       SetErrnoFromWinError (GetLastError ());
       CloseHandle ((*m)->h);
       GNUNET_free (*m);
     }
 
-  return ret;
+  return (*m)->addr;
 #else
   int prot;
 
@@ -1598,7 +1603,7 @@ GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
     }
 
 #ifdef MINGW
-  ret = UnmapViewOfFile (h->h) ? GNUNET_OK : GNUNET_SYSERR;
+  ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
   if (ret != GNUNET_OK)
     SetErrnoFromWinError (GetLastError ());
   if (!CloseHandle (h->h) && (ret == GNUNET_OK))
@@ -1642,6 +1647,7 @@ GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
 #endif
 }
 
+
 /**
  * Creates an interprocess channel
  *
@@ -1672,24 +1678,34 @@ GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
   ret = pipe (fd);
   if (ret == -1)
     {
+      eno = errno;
+      GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe");
       GNUNET_free (p);
+      errno = eno;
       return NULL;
     }
   p->fd[0]->fd = fd[0];
   p->fd[1]->fd = fd[1];
   ret = 0;
   flags = fcntl (fd[0], F_GETFL);
-  flags |= FD_CLOEXEC;
   if (!blocking)
     flags |= O_NONBLOCK;
   if (0 > fcntl (fd[0], F_SETFL, flags))
     ret = -1;
-  flags = fcntl (fd[1], F_GETFL);
+  flags = fcntl (fd[0], F_GETFD);
   flags |= FD_CLOEXEC;
+  if (0 > fcntl (fd[0], F_SETFD, flags))
+    ret = -1;
+
+  flags = fcntl (fd[1], F_GETFL);
   if (!blocking)
     flags |= O_NONBLOCK;
   if (0 > fcntl (fd[1], F_SETFL, flags))
     ret = -1;
+  flags = fcntl (fd[1], F_GETFD);
+  flags |= FD_CLOEXEC;
+  if (0 > fcntl (fd[1], F_SETFD, flags))
+    ret = -1;
   if (ret == -1)
     {
       eno = errno;
@@ -1745,6 +1761,8 @@ GNUNET_DISK_pipe (int blocking, int inherit_read, int inherit_write)
       SetNamedPipeHandleState (p->fd[1]->h, &mode, NULL, NULL);
       /* this always fails on Windows 95, so we don't care about error handling */
     }
+  p->fd[0]->type = GNUNET_PIPE;
+  p->fd[1]->type = GNUNET_PIPE;
 #endif
   return p;
 }
@@ -1859,6 +1877,190 @@ GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
 }
 
 
+/**
+ * Creates a named pipe/FIFO and opens it
+ * @param fn pointer to the name of the named pipe or to NULL
+ * @param flags open flags
+ * @param perm access permissions
+ * @return pipe handle on success, NULL on error
+ */
+struct GNUNET_DISK_FileHandle *
+GNUNET_DISK_npipe_create (char **fn,
+                          enum GNUNET_DISK_OpenFlags flags,
+                          enum GNUNET_DISK_AccessPermissions perm)
+{
+#ifdef MINGW
+  struct GNUNET_DISK_FileHandle *ret;
+  HANDLE h = NULL;
+  DWORD openMode;
+  char *name;
+
+  openMode = 0;
+  if (flags & GNUNET_DISK_OPEN_READWRITE)
+    openMode = PIPE_ACCESS_DUPLEX;
+  else if (flags & GNUNET_DISK_OPEN_READ)
+    openMode = PIPE_ACCESS_INBOUND;
+  else if (flags & GNUNET_DISK_OPEN_WRITE)
+    openMode = PIPE_ACCESS_OUTBOUND;
+
+  if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
+    openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
+
+  while (h == NULL)
+    {
+      DWORD error_code;
+      name = NULL;
+      if (*fn != NULL)
+        {
+          GNUNET_asprintf(&name, "\\\\.\\pipe\\%.246s", fn);
+#if DEBUG_NPIPE
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to create an instance of named pipe `%s'\n", name);
+#endif
+          h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
+              PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL);
+        }
+      else
+        {
+          GNUNET_asprintf(fn, "\\\\.\\pipe\\gnunet-%llu",
+              GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX));
+#if DEBUG_NPIPE
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n", *fn);
+#endif
+          h = CreateNamedPipe (*fn, openMode | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
+              PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0, NULL);
+        }
+      error_code = GetLastError ();
+      if (name)
+          GNUNET_free(name);
+      /* don't re-set name to NULL yet */
+      if (h == INVALID_HANDLE_VALUE)
+        {
+          SetErrnoFromWinError(error_code);
+#if DEBUG_NPIPE
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe creation have failed because of %d, errno is %d\n", error_code, errno);
+#endif
+          if (name == NULL)
+            {
+#if DEBUG_NPIPE
+              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe was to be unique, considering re-creation\n");
+#endif
+              GNUNET_free (*fn);
+              *fn = NULL;
+              if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
+                {
+                  return NULL;
+                }
+#if DEBUG_NPIPE
+              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Pipe name was not unique, trying again\n");
+#endif
+              h = NULL;
+            }
+          else
+              return NULL;
+        }
+    }
+  errno = 0;
+
+  ret = GNUNET_malloc(sizeof(*ret));
+  ret->h = h;
+  ret->type = GNUNET_PIPE;
+
+  return ret;
+#else
+  if (*fn == NULL)
+    {
+      char dir[] = "/tmp/gnunet-pipe-XXXXXX";
+
+      if (mkdtemp(dir) == NULL)
+        {
+          GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
+          return NULL;
+        }
+      GNUNET_asprintf(fn, "%s/child-control", dir);
+    }
+
+  if (mkfifo(*fn, translate_unix_perms(perm)) == -1)
+    {
+      if ( (errno != EEXIST) ||
+          (0 != (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)) )
+        return NULL;
+    }
+
+  flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
+  return GNUNET_DISK_file_open(*fn, flags, perm);
+#endif
+}
+
+/**
+ * Opens already existing named pipe/FIFO
+ *
+ * @param fn name of an existing named pipe
+ * @param flags open flags
+ * @param perm access permissions
+ * @return pipe handle on success, NULL on error
+ */
+struct GNUNET_DISK_FileHandle *
+GNUNET_DISK_npipe_open (const char *fn,
+                        enum GNUNET_DISK_OpenFlags flags,
+                        enum GNUNET_DISK_AccessPermissions perm)
+{
+#ifdef MINGW
+  struct GNUNET_DISK_FileHandle *ret;
+  HANDLE h;
+  DWORD openMode;
+
+  openMode = 0;
+  if (flags & GNUNET_DISK_OPEN_READWRITE)
+    openMode = GENERIC_WRITE | GENERIC_READ;
+  else if (flags & GNUNET_DISK_OPEN_READ)
+    openMode = GENERIC_READ;
+  else if (flags & GNUNET_DISK_OPEN_WRITE)
+    openMode = GENERIC_WRITE;
+
+  h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
+      FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
+  if (h == INVALID_HANDLE_VALUE)
+    {
+      SetErrnoFromWinError(GetLastError());
+      return NULL;
+    }
+
+  ret = GNUNET_malloc(sizeof(*ret));
+  ret->h = h;
+  ret->type = GNUNET_PIPE;
+
+  return ret;
+#else
+  flags = flags & (~GNUNET_DISK_OPEN_FAILIFEXISTS);
+  return GNUNET_DISK_file_open(fn, flags, perm);
+#endif
+}
+
+/**
+ * Closes a named pipe/FIFO
+ * @param pipe named pipe
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_DISK_npipe_close (struct GNUNET_DISK_FileHandle *pipe)
+{
+#ifndef MINGW
+  return close(pipe->fd) == 0 ? GNUNET_OK : GNUNET_SYSERR;
+#else
+  BOOL ret;
+
+  ret = CloseHandle(pipe->h);
+  if (!ret)
+    {
+      SetErrnoFromWinError(GetLastError());
+      return GNUNET_SYSERR;
+    }
+  else
+    return GNUNET_OK;
+#endif
+}
+
+
 /**
  * Get the handle to a particular pipe end
  *