Merge commit 'meins/master'
[paraslash.git] / fd.c
diff --git a/fd.c b/fd.c
index 5885172..8759d50 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2009 Andre Noll <maan@systemlinux.org>
  *
  * Licensed under the GPL v2. For licencing details see COPYING.
  */
@@ -40,6 +40,45 @@ int write_all(int fd, const char *buf, size_t *len)
        return 1;
 }
 
+/**
+ * Write a buffer to a non-blocking file descriptor.
+ *
+ * \param fd The file descriptor.
+ * \param buf the buffer to write.
+ * \param len the number of bytes of \a buf.
+ * \param max_bytes_per_write Do not write more than that many bytes at once.
+ *
+ * If \a max_bytes_per_write is non-zero, do not send more than that many bytes
+ * per write().
+ *
+ * EAGAIN is not considered an error condition.  For example CCID3 has a
+ * sending wait queue which fills up and is emptied asynchronously. The EAGAIN
+ * case means that there is currently no space in the wait queue, but this can
+ * change at any moment.
+ *
+ * \return Negative on errors, number of bytes written else.
+ */
+int write_nonblock(int fd, const char *buf, size_t len,
+               size_t max_bytes_per_write)
+{
+       size_t written = 0;
+       int ret = 0;
+
+       while (written < len) {
+               size_t num = len - written;
+
+               if (max_bytes_per_write && max_bytes_per_write < num)
+                       num = max_bytes_per_write;
+               ret = write(fd, buf + written, num);
+               if (ret < 0 && errno == EAGAIN)
+                       return written;
+               if (ret < 0)
+                       return -ERRNO_TO_PARA_ERROR(errno);
+               written += ret;
+       }
+       return written;
+}
+
 /**
  * Check whether a file exists.
  *
@@ -135,11 +174,7 @@ __must_check int mark_fd_nonblocking(int fd)
 */
 void para_fd_set(int fd, fd_set *fds, int *max_fileno)
 {
-
-       if (fd < 0 || fd >= FD_SETSIZE) {
-               PARA_EMERG_LOG("fatal: tried to add invalid fd %d\n", fd);
-               exit(EXIT_FAILURE);
-       }
+       assert(fd >= 0 && fd < FD_SETSIZE);
 #if 0
        {
                int flags = fcntl(fd, F_GETFL);
@@ -190,19 +225,26 @@ again:
  * \param flags Exactly one of MAP_SHARED and MAP_PRIVATE.
  * \param fd The file to mmap from.
  * \param offset Mmap start.
+ * \param map Result pointer.
  *
- * \return This function either returns a valid pointer to the mapped area
- * or calls exit() on errors.
+ * \return Standard.
+ *
+ * \sa mmap(2).
  */
-void *para_mmap(size_t length, int prot, int flags, int fd, off_t offset)
+int para_mmap(size_t length, int prot, int flags, int fd, off_t offset,
+               void *map)
 {
-       void *ret = mmap(NULL, length, prot, flags, fd, offset);
-       if (ret != MAP_FAILED)
-               return ret;
-       PARA_EMERG_LOG("mmap failed: %s\n", strerror(errno));
-       PARA_EMERG_LOG("length: %zu, flags: %d, fd: %d, offset: %zu\n",
-               length, flags, fd, (size_t)offset);
-       exit(EXIT_FAILURE);
+       void **m = map;
+
+       errno = EINVAL;
+       if (!length)
+               goto err;
+       *m = mmap(NULL, length, prot, flags, fd, offset);
+       if (*m != MAP_FAILED)
+               return 1;
+err:
+       *m = NULL;
+       return -ERRNO_TO_PARA_ERROR(errno);
 }
 
 /**
@@ -285,9 +327,10 @@ int para_opendir(const char *dirname, DIR **dir, int *cwd)
        if (*dir)
                return 1;
        ret = -ERRNO_TO_PARA_ERROR(errno);
-/* Ignore return value of fchdir() and close(). We're busted anyway. */
-       if (cwd)
-               fchdir(*cwd);
+       /* Ignore return value of fchdir() and close(). We're busted anyway. */
+       if (cwd) {
+               int __a_unused ret2 = fchdir(*cwd); /* STFU, gcc */
+       }
 close_cwd:
        if (cwd)
                close(*cwd);
@@ -362,17 +405,7 @@ int mmap_full_file(const char *path, int open_mode, void **map,
                goto out;
        }
        *size = file_status.st_size;
-       ret = -E_EMPTY;
-       PARA_DEBUG_LOG("%s: size %zu\n", path, *size);
-       if (!*size)
-               goto out;
-       *map = mmap(NULL, *size, mmap_prot, mmap_flags, fd, 0);
-       if (*map == MAP_FAILED) {
-               *map = NULL;
-               ret = -E_MMAP;
-               goto out;
-       }
-       ret = 1;
+       ret = para_mmap(*size, mmap_prot, mmap_flags, fd, 0, map);
 out:
        if (ret < 0 || !fd_ptr)
                close(fd);
@@ -426,3 +459,24 @@ again:
                goto again;
        return ret;
 }
+
+/**
+ * Ensure that file descriptors 0, 1, and 2 are valid.
+ *
+ * Common approach that opens /dev/null until it gets a file descriptor greater
+ * than two.
+ *
+ * \sa okir's Black Hats Manual.
+ */
+void valid_fd_012(void)
+{
+       while (1) {
+               int fd = open("/dev/null", O_RDWR);
+               if (fd < 0)
+                       exit(EXIT_FAILURE);
+               if (fd > 2) {
+                       close(fd);
+                       break;
+               }
+       }
+}