X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=fd.c;h=1c67f22bb284ab11ffda80bb90c0693661b0328c;hp=d0b5c895a5f25d02b9f566fbe693a38898057203;hb=337d7784b0f8bc30b1fa9af56ace503acd97d40d;hpb=cb4dd13c2327c13d486b6abe572f4af2398b65de diff --git a/fd.c b/fd.c index d0b5c895..1c67f22b 100644 --- a/fd.c +++ b/fd.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -14,7 +14,7 @@ #include "para.h" #include "error.h" - +#include "string.h" /** * Write a buffer to a file descriptor, re-write on short writes. * @@ -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); } /** @@ -363,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); @@ -427,3 +459,86 @@ 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; + } + } +} + +/** + * Traverse the given directory recursively. + * + * \param dirname The directory to traverse. + * \param func The function to call for each entry. + * \param private_data Pointer to an arbitrary data structure. + * + * For each regular file under \a dirname, the supplied function \a func is + * called. The full path of the regular file and the \a private_data pointer + * are passed to \a func. Directories for which the calling process has no + * permissions to change to are silently ignored. + * + * \return Standard. + */ +int for_each_file_in_dir(const char *dirname, + int (*func)(const char *, void *), void *private_data) +{ + DIR *dir; + struct dirent *entry; + int cwd_fd, ret2, ret = para_opendir(dirname, &dir, &cwd_fd); + + if (ret < 0) + return ret == -ERRNO_TO_PARA_ERROR(EACCES)? 1 : ret; + /* scan cwd recursively */ + while ((entry = readdir(dir))) { + mode_t m; + char *tmp; + struct stat s; + + if (!strcmp(entry->d_name, ".")) + continue; + if (!strcmp(entry->d_name, "..")) + continue; + if (lstat(entry->d_name, &s) == -1) + continue; + m = s.st_mode; + if (!S_ISREG(m) && !S_ISDIR(m)) + continue; + tmp = make_message("%s/%s", dirname, entry->d_name); + if (!S_ISDIR(m)) { + ret = func(tmp, private_data); + free(tmp); + if (ret < 0) + goto out; + continue; + } + /* directory */ + ret = for_each_file_in_dir(tmp, func, private_data); + free(tmp); + if (ret < 0) + goto out; + } + ret = 1; +out: + closedir(dir); + ret2 = para_fchdir(cwd_fd); + if (ret2 < 0 && ret >= 0) + ret = ret2; + close(cwd_fd); + return ret; +}