X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=fd.c;h=33891d2e6c9f1c3568428b1df93b4b92e295ef61;hp=b8d0d77c3ba94091d753714d54b8181ce7c395ac;hb=HEAD;hpb=56df9bb38aa4725f9244a7898d765608d8a1fffa diff --git a/fd.c b/fd.c index b8d0d77c..1af902f9 100644 --- a/fd.c +++ b/fd.c @@ -1,8 +1,4 @@ -/* - * Copyright (C) 2006-2014 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ +/* Copyright (C) 2006 Andre Noll , see file COPYING. */ /** \file fd.c Helper functions for file descriptor handling. */ @@ -10,8 +6,6 @@ #include #include #include -#include -#include #include "para.h" #include "error.h" @@ -19,26 +13,51 @@ #include "fd.h" /** - * Write an array of buffers to a file descriptor. + * Change the name or location of a file. * - * \param fd The file descriptor. + * \param oldpath File to be moved. + * \param newpath Destination. + * + * This is just a simple wrapper for the rename(2) system call which returns a + * paraslash error code and prints an error message on failure. + * + * \return Standard. + * + * \sa rename(2). + */ +int xrename(const char *oldpath, const char *newpath) +{ + int ret = rename(oldpath, newpath); + + if (ret >= 0) + return 1; + ret = -ERRNO_TO_PARA_ERROR(errno); + PARA_ERROR_LOG("failed to rename %s -> %s\n", oldpath, newpath); + return ret; +} + +/** + * Write an array of buffers, handling non-fatal errors. + * + * \param fd The file descriptor to write to. * \param iov Pointer to one or more buffers. * \param iovcnt The number of buffers. * - * EAGAIN/EWOULDBLOCK is not considered a fatal error condition. For example - * DCCP 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. + * EAGAIN, EWOULDBLOCK and EINTR are not considered error conditions. If a + * write operation fails with EAGAIN or EWOULDBLOCK, the number of bytes that + * have been written so far is returned. In the EINTR case the operation is + * retried. Short writes are handled by issuing a subsequent write operation + * for the remaining part. * * \return Negative on fatal errors, number of bytes written else. * * For blocking file descriptors, this function returns either the sum of all - * buffer sizes, or the error code of the fatal error that caused the last - * write call to fail. + * buffer sizes or a negative error code which indicates the fatal error that + * caused a write call to fail. * - * For nonblocking file descriptors there is a third possibility: Any positive - * return value less than the sum of the buffer sizes indicates that some bytes - * have been written but the next write would block. + * For nonblocking file descriptors there is a third possibility: Any + * non-negative return value less than the sum of the buffer sizes indicates + * that a write operation returned EAGAIN/EWOULDBLOCK. * * \sa writev(2), \ref xwrite(). */ @@ -108,14 +127,15 @@ int xwrite(int fd, const char *buf, size_t len) } /** - * Write all data to a file descriptor. + * Write to a file descriptor, fail on short writes. * * \param fd The file descriptor. - * \param buf The buffer to be sent. - * \param len The length of \a buf. + * \param buf The buffer to be written. + * \param len The length of the buffer. * - * This is like \ref xwrite() but returns \p -E_SHORT_WRITE if not - * all data could be written. + * For blocking file descriptors this function behaves identical to \ref + * xwrite(). For non-blocking file descriptors it returns -E_SHORT_WRITE + * (rather than a value less than len) if not all data could be written. * * \return Number of bytes written on success, negative error code else. */ @@ -131,12 +151,20 @@ int write_all(int fd, const char *buf, size_t len) } /** - * Write a buffer given by a format string. + * A fprintf-like function for raw file descriptors. + * + * This function creates a string buffer according to the given format and + * writes this buffer to a file descriptor. * * \param fd The file descriptor. * \param fmt A format string. * + * The difference to fprintf(3) is that the first argument is a file + * descriptor, not a FILE pointer. This function does not rely on stdio. + * * \return The return value of the underlying call to \ref write_all(). + * + * \sa fprintf(3), \ref xvasprintf(). */ __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...) { @@ -146,6 +174,7 @@ __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...) va_start(ap, fmt); ret = xvasprintf(&msg, fmt, ap); + va_end(ap); ret = write_all(fd, msg, ret); free(msg); return ret; @@ -157,18 +186,15 @@ __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...) * \param fd The file descriptor to read from. * \param iov Scatter/gather array used in readv(). * \param iovcnt Number of elements in \a iov. - * \param rfds An optional fd set pointer. * \param num_bytes Result pointer. Contains the number of bytes read from \a fd. * - * If \a rfds is not \p NULL and the (non-blocking) file descriptor \a fd is - * not set in \a rfds, this function returns early without doing anything. - * Otherwise The function tries to read up to \a sz bytes from \a fd, where \a - * sz is the sum of the lengths of all vectors in \a iov. As for xwrite(), - * \p EAGAIN is not considered an error condition. However, \p EOF is. + * This function tries to read up to sz bytes from fd, where sz is the sum of + * the lengths of all vectors in iov. Like \ref xwrite(), EAGAIN and EINTR are + * not considered error conditions. However, EOF is. * * \return Zero or a negative error code. If the underlying call to readv(2) * returned zero (indicating an end of file condition) or failed for some - * reason other than \p EAGAIN, a negative error code is returned. + * reason other than EAGAIN or EINTR, a negative error code is returned. * * In any case, \a num_bytes contains the number of bytes that have been * successfully read from \a fd (zero if the first readv() call failed with @@ -178,24 +204,12 @@ __printf_2_3 int write_va_buffer(int fd, const char *fmt, ...) * * \sa \ref xwrite(), read(2), readv(2). */ -int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds, - size_t *num_bytes) +int readv_nonblock(int fd, struct iovec *iov, int iovcnt, size_t *num_bytes) { int ret, i, j; *num_bytes = 0; - /* - * Avoid a shortcoming of select(): Reads from a non-blocking fd might - * return EAGAIN even if FD_ISSET() returns true. However, FD_ISSET() - * returning false definitely means that no data can currently be read. - * This is the common case, so it is worth to avoid the overhead of the - * read() system call in this case. - */ - if (rfds && !FD_ISSET(fd, rfds)) - return 0; - for (i = 0, j = 0; i < iovcnt;) { - /* fix up the first iov */ assert(j < iov[i].iov_len); iov[i].iov_base += j; @@ -207,7 +221,7 @@ int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds, if (ret == 0) return -E_EOF; if (ret < 0) { - if (errno == EAGAIN) + if (errno == EAGAIN || errno == EINTR) return 0; return -ERRNO_TO_PARA_ERROR(errno); } @@ -232,7 +246,6 @@ int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds, * \param fd The file descriptor to read from. * \param buf The buffer to read data to. * \param sz The size of \a buf. - * \param rfds \see \ref readv_nonblock(). * \param num_bytes \see \ref readv_nonblock(). * * This is a simple wrapper for readv_nonblock() which uses an iovec with a single @@ -240,102 +253,51 @@ int readv_nonblock(int fd, struct iovec *iov, int iovcnt, fd_set *rfds, * * \return The return value of the underlying call to readv_nonblock(). */ -int read_nonblock(int fd, void *buf, size_t sz, fd_set *rfds, size_t *num_bytes) +int read_nonblock(int fd, void *buf, size_t sz, size_t *num_bytes) { struct iovec iov = {.iov_base = buf, .iov_len = sz}; - return readv_nonblock(fd, &iov, 1, rfds, num_bytes); + return readv_nonblock(fd, &iov, 1, num_bytes); } /** - * Read a buffer and check its content for a pattern. - * - * \param fd The file descriptor to receive from. - * \param pattern The expected pattern. - * \param bufsize The size of the internal buffer. - * \param rfds Passed to read_nonblock(). + * Read a buffer and compare its contents to a string, ignoring case. * - * This function tries to read at most \a bufsize bytes from the non-blocking - * file descriptor \a fd. If at least \p strlen(\a pattern) bytes have been - * received, the beginning of the received buffer is compared with \a pattern, - * ignoring case. + * \param fd The file descriptor to read from. + * \param expectation The expected string to compare to. * - * \return Positive if \a pattern was received, negative on errors, zero if no data - * was available to read. + * The given file descriptor is expected to be in non-blocking mode. The string + * comparison is performed using strncasecmp(3). * - * \sa \ref read_nonblock(), \sa strncasecmp(3). + * \return Zero if no data was available, positive if a buffer was read whose + * contents compare as equal to the expected string, negative otherwise. + * Possible errors: (a) not enough data was read, (b) the buffer contents + * compared as non-equal, (c) a read error occurred. In the first two cases, + * -E_READ_PATTERN is returned. In the read error case the (negative) return + * value of the underlying call to \ref read_nonblock() is returned. */ -int read_pattern(int fd, const char *pattern, size_t bufsize, fd_set *rfds) +int read_and_compare(int fd, const char *expectation) { - size_t n, len; - char *buf = para_malloc(bufsize + 1); - int ret = read_nonblock(fd, buf, bufsize, rfds, &n); + size_t n, len = strlen(expectation); + char *buf = alloc(len + 1); + int ret = read_nonblock(fd, buf, len, &n); - buf[n] = '\0'; if (ret < 0) goto out; + buf[n] = '\0'; ret = 0; if (n == 0) goto out; ret = -E_READ_PATTERN; - len = strlen(pattern); if (n < len) goto out; - if (strncasecmp(buf, pattern, len) != 0) + if (strncasecmp(buf, expectation, len) != 0) goto out; ret = 1; out: - if (ret < 0) { - PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); - PARA_NOTICE_LOG("recvd %zu bytes: %s\n", n, buf); - } free(buf); return ret; } -/** - * Check whether a file exists. - * - * \param fn The file name. - * - * \return Non-zero iff file exists. - */ -int file_exists(const char *fn) -{ - struct stat statbuf; - - return !stat(fn, &statbuf); -} - -/** - * Paraslash's wrapper for select(2). - * - * It calls select(2) (with no exceptfds) and starts over if select() was - * interrupted by a signal. - * - * \param n The highest-numbered descriptor in any of the two sets, plus 1. - * \param readfds fds that should be checked for readability. - * \param writefds fds that should be checked for writablility. - * \param timeout_tv upper bound on the amount of time elapsed before select() - * returns. - * - * \return The return value of the underlying select() call on success, the - * negative system error code on errors. - * - * All arguments are passed verbatim to select(2). - * \sa select(2) select_tut(2). - */ -int para_select(int n, fd_set *readfds, fd_set *writefds, - struct timeval *timeout_tv) -{ - int ret; - do - ret = select(n, readfds, writefds, NULL, timeout_tv); - while (ret < 0 && errno == EINTR); - if (ret < 0) - return -ERRNO_TO_PARA_ERROR(errno); - return ret; -} - /** * Set a file descriptor to blocking mode. * @@ -372,62 +334,6 @@ __must_check int mark_fd_nonblocking(int fd) return 1; } -/** - * Set a file descriptor in a fd_set. - * - * \param fd The file descriptor to be set. - * \param fds The file descriptor set. - * \param max_fileno Highest-numbered file descriptor. - * - * This wrapper for FD_SET() passes its first two arguments to \p FD_SET. Upon - * return, \a max_fileno contains the maximum of the old_value and \a fd. - * - * \sa para_select. -*/ -void para_fd_set(int fd, fd_set *fds, int *max_fileno) -{ - assert(fd >= 0 && fd < FD_SETSIZE); -#if 0 - { - int flags = fcntl(fd, F_GETFL); - if (!(flags & O_NONBLOCK)) { - PARA_EMERG_LOG("fd %d is a blocking file descriptor\n", fd); - exit(EXIT_FAILURE); - } - } -#endif - FD_SET(fd, fds); - *max_fileno = PARA_MAX(*max_fileno, fd); -} - -/** - * Paraslash's wrapper for fgets(3). - * - * \param line Pointer to the buffer to store the line. - * \param size The size of the buffer given by \a line. - * \param f The stream to read from. - * - * \return Unlike the standard fgets() function, an integer value - * is returned. On success, this function returns 1. On errors, -E_FGETS - * is returned. A zero return value indicates an end of file condition. - */ -__must_check int para_fgets(char *line, int size, FILE *f) -{ -again: - if (fgets(line, size, f)) - return 1; - if (feof(f)) - return 0; - if (!ferror(f)) - return -E_FGETS; - if (errno != EINTR) { - PARA_ERROR_LOG("%s\n", strerror(errno)); - return -E_FGETS; - } - clearerr(f); - goto again; -} - /** * Paraslash's wrapper for mmap. * @@ -436,22 +342,20 @@ again: * PROT_EXEC PROT_READ PROT_WRITE. * \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. * * \sa mmap(2). */ -int para_mmap(size_t length, int prot, int flags, int fd, off_t offset, - void *map) +int para_mmap(size_t length, int prot, int flags, int fd, void *map) { void **m = map; errno = EINVAL; if (!length) goto err; - *m = mmap(NULL, length, prot, flags, fd, offset); + *m = mmap(NULL, length, prot, flags, fd, (off_t)0); if (*m != MAP_FAILED) return 1; err: @@ -483,100 +387,32 @@ int para_open(const char *path, int flags, mode_t mode) } /** - * Wrapper for chdir(2). - * - * \param path The specified directory. - * - * \return Standard. - */ -int para_chdir(const char *path) -{ - int ret = chdir(path); - - if (ret >= 0) - return 1; - return -ERRNO_TO_PARA_ERROR(errno); -} - -/** - * Save the cwd and open a given directory. + * Create a directory, don't fail if it already exists. * - * \param dirname Path to the directory to open. - * \param dir Result pointer. - * \param cwd File descriptor of the current working directory. - * - * \return Standard. - * - * Opening the current directory (".") and calling fchdir() to return is - * usually faster and more reliable than saving cwd in some buffer and calling - * chdir() afterwards. - * - * If \a cwd is not \p NULL "." is opened and the resulting file descriptor is - * stored in \a cwd. If the function returns success, and \a cwd is not \p - * NULL, the caller must close this file descriptor (probably after calling - * fchdir(*cwd)). - * - * On errors, the function undos everything, so the caller needs neither close - * any files, nor change back to the original working directory. + * \param path Name of the directory to create. * - * \sa getcwd(3). + * This function passes the fixed mode value 0777 to mkdir(3) (which consults + * the file creation mask and restricts this value). * + * \return Zero if the path already existed as a directory or as a symbolic + * link which leads to a directory, one if the path did not exist and the + * directory has been created successfully, negative error code else. */ -static int para_opendir(const char *dirname, DIR **dir, int *cwd) +int para_mkdir(const char *path) { - int ret; + /* + * We call opendir(3) rather than relying on stat(2) because this way + * we don't need extra code to get the symlink case right. + */ + DIR *dir = opendir(path); - *dir = NULL; - if (cwd) { - ret = para_open(".", O_RDONLY, 0); - if (ret < 0) - return ret; - *cwd = ret; - } - ret = para_chdir(dirname); - if (ret < 0) - goto close_cwd; - *dir = opendir("."); - if (*dir) - return 1; - ret = -ERRNO_TO_PARA_ERROR(errno); - /* Ignore return value of fchdir() and close(). We're busted anyway. */ - if (cwd) { - int __a_unused ret2 = fchdir(*cwd); /* STFU, gcc */ + if (dir) { + closedir(dir); + return 0; } -close_cwd: - if (cwd) - close(*cwd); - return ret; -} - -/** - * A wrapper for fchdir(). - * - * \param fd An open file descriptor. - * - * \return Standard. - */ -static int para_fchdir(int fd) -{ - if (fchdir(fd) < 0) + if (errno != ENOENT) return -ERRNO_TO_PARA_ERROR(errno); - return 1; -} - -/** - * A wrapper for mkdir(2). - * - * \param path Name of the directory to create. - * \param mode The permissions to use. - * - * \return Standard. - */ -int para_mkdir(const char *path, mode_t mode) -{ - if (!mkdir(path, mode)) - return 1; - return -ERRNO_TO_PARA_ERROR(errno); + return mkdir(path, 0777) == 0? 1 : -ERRNO_TO_PARA_ERROR(errno); } /** @@ -634,7 +470,7 @@ int mmap_full_file(const char *path, int open_mode, void **map, if (S_ISDIR(file_status.st_mode)) goto out; - ret = para_mmap(*size, mmap_prot, mmap_flags, fd, 0, map); + ret = para_mmap(*size, mmap_prot, mmap_flags, fd, map); out: if (ret < 0 || !fd_ptr) close(fd); @@ -649,22 +485,62 @@ out: * \param start The start address of the memory mapping. * \param length The size of the mapping. * - * \return Standard. + * If NULL is passed as the start address, the length value is ignored and the + * function does nothing. * - * \sa munmap(2), mmap_full_file(). + * \return Zero if NULL was passed, one if the memory area was successfully + * unmapped, a negative error code otherwise. + * + * \sa munmap(2), \ref mmap_full_file(). */ int para_munmap(void *start, size_t length) { - int err; - if (!start) return 0; if (munmap(start, length) >= 0) return 1; - err = errno; - PARA_ERROR_LOG("munmap (%p/%zu) failed: %s\n", start, length, - strerror(err)); - return -ERRNO_TO_PARA_ERROR(err); + return -ERRNO_TO_PARA_ERROR(errno); +} + +/** + * Simple wrapper for poll(2). + * + * It calls poll(2) and starts over if the call was interrupted by a signal. + * + * \param fds See poll(2). + * \param nfds See poll(2). + * \param timeout See poll(2). + * + * \return The return value of the underlying poll() call on success, the + * negative paraslash error code on errors. + * + * All arguments are passed verbatim to poll(2). + */ +int xpoll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + int ret; + + do + ret = poll(fds, nfds, timeout); + while (ret < 0 && errno == EINTR); + return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : ret; +} + +/** + * Check a file descriptor for readability. + * + * \param fd The file descriptor. + * + * \return positive if fd is ready for reading, zero if it isn't, negative if + * an error occurred. + * + * \sa \ref write_ok(). + */ +int read_ok(int fd) +{ + struct pollfd pfd = {.fd = fd, .events = POLLIN}; + int ret = xpoll(&pfd, 1, 0); + return ret < 0? ret : pfd.revents & POLLIN; } /** @@ -674,18 +550,14 @@ int para_munmap(void *start, size_t length) * * \return positive if fd is ready for writing, zero if it isn't, negative if * an error occurred. + * + * \sa \ref read_ok(). */ - int write_ok(int fd) { - struct timeval tv; - fd_set wfds; - - FD_ZERO(&wfds); - FD_SET(fd, &wfds); - tv.tv_sec = 0; - tv.tv_usec = 0; - return para_select(fd + 1, NULL, &wfds, &tv); + struct pollfd pfd = {.fd = fd, .events = POLLOUT}; + int ret = xpoll(&pfd, 1, 0); + return ret < 0? ret : pfd.revents & POLLOUT; } /** @@ -693,8 +565,6 @@ int write_ok(int fd) * * 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) { @@ -708,65 +578,3 @@ 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; -}