Commit 3e9e69098af2014a0c3fe9e46cddcb5365dd538b

Shawn O. Pearce 2008-11-03T17:14:25

Redefine git_fread, git_fwrite to transfer the whole unit We never want to accept a short read or a short write when transferring data to or from a local file. Either the entire read (or write) completes or the operation failed and we will not recover gracefully from it. Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

diff --git a/include/git/os/unix.h b/include/git/os/unix.h
index dd58389..56f77b9 100644
--- a/include/git/os/unix.h
+++ b/include/git/os/unix.h
@@ -54,58 +54,55 @@ typedef int git_file;
  * - O_WRONLY: Open the file for writing.
  * - O_RDWR: Open the file for both reading and writing.
  *
+ * @param out descriptor storage to populate on success.
  * @param path path name of the file to open.
  * @param flags bitmask of access requested to the file.
- * @return the opened file descriptor; <0 if the open failed.
- */
-static inline git_file git_fopen(const char *path, int flags)
-{
-	return open(path, flags);
-}
-
-/**
- * Close an open file descriptor.
- * @param fd descriptor to close.
- * @return 0 on success; <0 if the descriptor close failed.
+ * @return
+ * - On success, GIT_SUCCESS.
+ * - On error, <0.
  */
-static inline int git_fclose(git_file fd)
-{
-	return close(fd);
-}
+GIT_EXTERN(int) git_fopen(git_file *out, const char *path, int flags);
 
 /**
  * Read from an open file descriptor at the current position.
  *
- * Less than the number of requested bytes may be read.  The
- * read is automatically restarted if it fails due to a signal
- * being delivered to the calling thread.
+ * Exactly the requested number of bytes is read.  If the stream
+ * ends early, an error is indicated, and the exact number of bytes
+ * transferred is unspecified.
  *
  * @param fd open descriptor.
  * @param buf buffer to store the read data into.
  * @param cnt number of bytes to transfer.
  * @return
- *  - On success, actual number of bytes read.
- *  - On EOF, 0.
- *  - On failure, <0.
+ * - On success, GIT_SUCCESS.
+ * - On error, <0.
  */
-GIT_EXTERN(ssize_t) git_fread(git_file fd, void *buf, size_t cnt);
+GIT_EXTERN(int) git_fread(git_file fd, void *buf, size_t cnt);
 
 /**
  * Write to an open file descriptor at the current position.
  *
- * Less than the number of requested bytes may be written.  The
- * write is automatically restarted if it fails due to a signal
- * being delivered to the calling thread.
+ * Exactly the requested number of bytes is written.  If the stream
+ * ends early, an error is indicated, and the exact number of bytes
+ * transferred is unspecified.
  *
  * @param fd open descriptor.
  * @param buf buffer to write data from.
  * @param cnt number of bytes to transfer.
  * @return
- *  - On success, actual number of bytes written.
- *  - On EOF, 0.
- *  - On failure, <0.
+ * - On success, GIT_SUCCESS.
+ * - On error, <0.
+ */
+GIT_EXTERN(int) git_fwrite(git_file fd, void *buf, size_t cnt);
+
+/**
+ * Close an open file descriptor.
+ * @param fd descriptor to close.
+ * @return
+ * - On success, GIT_SUCCESS.
+ * - On error, <0.
  */
-GIT_EXTERN(ssize_t) git_fwrite(git_file fd, void *buf, size_t cnt);
+#define git_fclose(fd) close(fd)
 
 /** @} */
 GIT_END_DECL
diff --git a/src/os/unix.c b/src/os/unix.c
index d132e97..767f20c 100644
--- a/src/os/unix.c
+++ b/src/os/unix.c
@@ -26,22 +26,51 @@
 #include <errno.h>
 #include "git/common.h"
 
-ssize_t git_fread(git_file fd, void *buf, size_t cnt)
+int git_fopen(git_file *out, const char *path, int flags)
 {
-	for (;;) {
-		ssize_t r = read(fd, buf, cnt);
-		if (r < 0 && (errno == EINTR || errno == EAGAIN))
-			continue;
-		return r;
+	int r = open(path, flags);
+	if (r < 0)
+		return -1;
+	*out = r;
+	return GIT_SUCCESS;
+}
+
+int git_fread(git_file fd, void *buf, size_t cnt)
+{
+	char *b = buf;
+	while (cnt) {
+		ssize_t r = read(fd, b, cnt);
+		if (r < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			return -1;
+		}
+		if (!r) {
+			errno = EPIPE;
+			return -1;
+		}
+		cnt -= r;
+		b += r;
 	}
+	return GIT_SUCCESS;
 }
 
-ssize_t git_fwrite(git_file fd, void *buf, size_t cnt)
+int git_fwrite(git_file fd, void *buf, size_t cnt)
 {
-	for (;;) {
-		ssize_t r = write(fd, buf, cnt);
-		if (r < 0 && (errno == EINTR || errno == EAGAIN))
-			continue;
-		return r;
+	char *b = buf;
+	while (cnt) {
+		ssize_t r = write(fd, b, cnt);
+		if (r < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			return -1;
+		}
+		if (!r) {
+			errno = EPIPE;
+			return -1;
+		}
+		cnt -= r;
+		b += r;
 	}
+	return GIT_SUCCESS;
 }