X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=fd.c;h=3dc490cde1056db92e94108cb7ad3c97131d32d5;hp=c675aaffd4b99083d3ab0e26445574c2b87ad51b;hb=1dbe0b9828c0fe0fb642361473dedbcef7defc4c;hpb=40de1dd2fdbb054444d585aa70e2d50166a66e07 diff --git a/fd.c b/fd.c index c675aaff..3dc490cd 100644 --- a/fd.c +++ b/fd.c @@ -1,11 +1,12 @@ /* - * Copyright (C) 2006-2008 Andre Noll + * Copyright (C) 2006-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file fd.c Helper functions for file descriptor handling. */ +#include #include #include #include @@ -14,6 +15,7 @@ #include "para.h" #include "error.h" +#include "string.h" /** * Write a buffer to a file descriptor, re-write on short writes. @@ -40,6 +42,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. * @@ -75,11 +116,10 @@ int file_exists(const char *fn) int para_select(int n, fd_set *readfds, fd_set *writefds, struct timeval *timeout_tv) { - int ret, err; - do { + int ret; + do ret = select(n, readfds, writefds, NULL, timeout_tv); - err = errno; - } while (ret < 0 && err == EINTR); + while (ret < 0 && errno == EINTR); if (ret < 0) return -ERRNO_TO_PARA_ERROR(errno); return ret; @@ -135,11 +175,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 +226,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 Standard. * - * \return This function either returns a valid pointer to the mapped area - * or calls exit() on errors. + * \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 +406,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); @@ -414,18 +447,14 @@ int para_munmap(void *start, size_t length) int write_ok(int fd) { - struct timeval tv = {0, 0}; + struct timeval tv; fd_set wfds; - int ret; -again: + FD_ZERO(&wfds); FD_SET(fd, &wfds); tv.tv_sec = 0; tv.tv_usec = 0; - ret = select(fd + 1, NULL, &wfds, NULL, &tv); - if (ret < 0 && errno == EINTR) - goto again; - return ret; + return para_select(fd + 1, NULL, &wfds, &tv); } /** @@ -448,3 +477,65 @@ void valid_fd_012(void) } } } + +/** + * 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; +}