# include "config.h"
#endif
+#include "wimlib/error.h"
#include "wimlib/file_io.h"
+#include "wimlib/util.h"
#ifdef __WIN32__
# include "wimlib/win32.h" /* For pread(), pwrite() replacements */
#else
#include <unistd.h>
-/* Like read(), but keep trying until everything has been written or we know for
- * sure that there was an error (or end-of-file). */
-size_t
-full_read(int fd, void *buf, size_t count)
+/* Wrapper around read() that checks for errors keeps retrying until all
+ * requested bytes have been read or until end-of file has occurred.
+ *
+ * Return values:
+ * WIMLIB_ERR_SUCCESS (0)
+ * WIMLIB_ERR_READ (errno set)
+ * WIMLIB_ERR_UNEXPECTED_END_OF_FILE (errno set to 0)
+ */
+int
+full_read(struct filedes *fd, void *buf, size_t count)
{
ssize_t bytes_read;
size_t bytes_remaining;
bytes_remaining != 0;
bytes_remaining -= bytes_read, buf += bytes_read)
{
- bytes_read = read(fd, buf, bytes_remaining);
- if (bytes_read <= 0) {
- if (bytes_read == 0)
- errno = EIO;
- else if (errno == EINTR)
+ bytes_read = read(fd->fd, buf, bytes_remaining);
+ if (unlikely(bytes_read <= 0)) {
+ if (bytes_read == 0) {
+ errno = 0;
+ return WIMLIB_ERR_UNEXPECTED_END_OF_FILE;
+ } else if (errno == EINTR) {
continue;
- break;
+ } else {
+ return WIMLIB_ERR_READ;
+ }
}
}
- return count - bytes_remaining;
+ count -= bytes_remaining;
+ fd->offset += count;
+ return 0;
}
-/* Like write(), but keep trying until everything has been written or we know
- * for sure that there was an error. */
-size_t
-full_write(int fd, const void *buf, size_t count)
+static int
+pipe_read(struct filedes *fd, void *buf, size_t count, off_t offset)
{
- ssize_t bytes_written;
+ int ret;
+
+ /* Verify the offset. */
+ if (offset < fd->offset) {
+ ERROR("Can't seek backwards in pipe "
+ "(offset %"PRIu64" => %"PRIu64").\n"
+ " Make sure the WIM was captured as "
+ "pipable.", fd->offset, offset);
+ errno = ESPIPE;
+ return WIMLIB_ERR_RESOURCE_ORDER;
+ }
+
+ /* Manually seek to the requested position. */
+ while (fd->offset != offset) {
+ size_t bytes_to_read = min(offset - fd->offset, BUFFER_SIZE);
+ u8 dummy[bytes_to_read];
+
+ ret = full_read(fd, dummy, bytes_to_read);
+ if (ret)
+ return ret;
+ }
+
+ /* Do the actual read. */
+ return full_read(fd, buf, count);
+}
+
+/* Wrapper around pread() that checks for errors and keeps retrying until all
+ * requested bytes have been read or until end-of file has occurred. This also
+ * transparently handle reading from pipe files, but the caller needs to be sure
+ * the requested offset is greater than or equal to the current offset, or else
+ * WIMLIB_ERR_RESOURCE_ORDER will be returned.
+ *
+ * Return values:
+ * WIMLIB_ERR_SUCCESS (0)
+ * WIMLIB_ERR_READ (errno set)
+ * WIMLIB_ERR_UNEXPECTED_END_OF_FILE (errno set to 0)
+ * WIMLIB_ERR_RESOURCE_ORDER (errno set to ESPIPE)
+ */
+int
+full_pread(struct filedes *fd, void *buf, size_t count, off_t offset)
+{
+ ssize_t bytes_read;
size_t bytes_remaining;
+ if (fd->is_pipe)
+ goto is_pipe;
+
for (bytes_remaining = count;
bytes_remaining != 0;
- bytes_remaining -= bytes_written, buf += bytes_written)
+ bytes_remaining -= bytes_read, buf += bytes_read,
+ offset += bytes_read)
{
- bytes_written = write(fd, buf, bytes_remaining);
- if (bytes_written < 0) {
- if (errno == EINTR)
+ bytes_read = pread(fd->fd, buf, bytes_remaining, offset);
+ if (unlikely(bytes_read <= 0)) {
+ if (bytes_read == 0) {
+ errno = 0;
+ return WIMLIB_ERR_UNEXPECTED_END_OF_FILE;
+ } else if (errno == EINTR) {
continue;
- break;
+ } else if (errno == ESPIPE) {
+ fd->is_pipe = 1;
+ goto is_pipe;
+ } else {
+ return WIMLIB_ERR_READ;
+ }
}
}
- return count - bytes_remaining;
+ return 0;
+
+is_pipe:
+ return pipe_read(fd, buf, count, offset);
}
-/* Like pread(), but keep trying until everything has been read or we know for
- * sure that there was an error (or end-of-file) */
-size_t
-full_pread(int fd, void *buf, size_t count, off_t offset)
+/* Wrapper around write() that checks for errors and keeps retrying until all
+ * requested bytes have been written.
+ *
+ * Return values:
+ * WIMLIB_ERR_SUCCESS (0)
+ * WIMLIB_ERR_WRITE (errno set)
+ */
+int
+full_write(struct filedes *fd, const void *buf, size_t count)
{
- ssize_t bytes_read;
+ ssize_t bytes_written;
size_t bytes_remaining;
for (bytes_remaining = count;
bytes_remaining != 0;
- bytes_remaining -= bytes_read, buf += bytes_read,
- offset += bytes_read)
+ bytes_remaining -= bytes_written, buf += bytes_written)
{
- bytes_read = pread(fd, buf, bytes_remaining, offset);
- if (bytes_read <= 0) {
- if (bytes_read == 0)
- errno = EIO;
- else if (errno == EINTR)
+ bytes_written = write(fd->fd, buf, bytes_remaining);
+ if (unlikely(bytes_written < 0)) {
+ if (errno == EINTR)
continue;
- break;
+ return WIMLIB_ERR_WRITE;
}
}
- return count - bytes_remaining;
+ fd->offset += count;
+ return 0;
}
-/* Like pwrite(), but keep trying until everything has been written or we know
- * for sure that there was an error. */
-size_t
-full_pwrite(int fd, const void *buf, size_t count, off_t offset)
+
+/* Wrapper around pwrite() that checks for errors and keeps retrying until all
+ * requested bytes have been written.
+ *
+ * Return values:
+ * WIMLIB_ERR_SUCCESS (0)
+ * WIMLIB_ERR_WRITE (errno set)
+ * */
+int
+full_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset)
{
ssize_t bytes_written;
size_t bytes_remaining;
for (bytes_remaining = count;
bytes_remaining != 0;
bytes_remaining -= bytes_written, buf += bytes_written,
- offset += bytes_written)
+ offset += bytes_written)
{
- bytes_written = pwrite(fd, buf, bytes_remaining, offset);
- if (bytes_written < 0) {
+ bytes_written = pwrite(fd->fd, buf, bytes_remaining, offset);
+ if (unlikely(bytes_written < 0)) {
if (errno == EINTR)
continue;
- break;
+ return WIMLIB_ERR_WRITE;
}
}
- return count - bytes_remaining;
+ return 0;
}
-/* Like writev(), but keep trying until everything has been written or we know
- * for sure that there was an error. */
-size_t
-full_writev(int fd, struct iovec *iov, int iovcnt)
+#if 0
+/* Wrapper around writev() that checks for errors and keep retrying until all
+ * requested bytes have been written.
+ *
+ * Return values:
+ * WIMLIB_ERR_SUCCESS (0)
+ * WIMLIB_ERR_WRITE (errno set)
+ * */
+int
+full_writev(struct filedes *fd, struct iovec *iov, int iovcnt)
{
size_t total_bytes_written = 0;
while (iovcnt > 0) {
ssize_t bytes_written;
- bytes_written = writev(fd, iov, iovcnt);
- if (bytes_written < 0) {
+ bytes_written = writev(fd->fd, iov, iovcnt);
+ if (unlikely(bytes_written < 0)) {
if (errno == EINTR)
continue;
- break;
+ return WIMLIB_ERR_WRITE;
}
total_bytes_written += bytes_written;
while (bytes_written) {
}
}
}
- return total_bytes_written;
+ fd->offset += total_bytes_written;
+ return 0;
+}
+#endif
+
+ssize_t
+raw_pread(struct filedes *fd, void *buf, size_t count, off_t offset)
+{
+ return pread(fd->fd, buf, count, offset);
+}
+
+ssize_t
+raw_pwrite(struct filedes *fd, const void *buf, size_t count, off_t offset)
+{
+ return pwrite(fd->fd, buf, count, offset);
+}
+
+off_t filedes_seek(struct filedes *fd, off_t offset)
+{
+ if (fd->is_pipe) {
+ errno = ESPIPE;
+ return -1;
+ }
+ if (fd->offset != offset) {
+ if (lseek(fd->fd, offset, SEEK_SET) == -1)
+ return -1;
+ fd->offset = offset;
+ }
+ return offset;
}
-off_t
-filedes_offset(int fd)
+bool filedes_is_seekable(struct filedes *fd)
{
- return lseek(fd, 0, SEEK_CUR);
+ return !fd->is_pipe && lseek(fd->fd, 0, SEEK_CUR) != -1;
}