Future changes regarding dry runs will require simulating filesystem
access by operating on copies of files necessary for the program logic.
Add some general utility functions to aid in creating and initialising
these file copies.
---
 src/common.h |   4 ++
 src/util.c   | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 154 insertions(+)

diff --git a/src/common.h b/src/common.h
index 38521b685bc3..b759e3dba8f1 100644
--- a/src/common.h
+++ b/src/common.h
@@ -302,6 +302,10 @@ int map_name( const char *arg, int argl, char **result, 
uint reserve, const char
 
 int mkdir_p( char *path, int len );
 
+off_t filecpy( int dst, int src );
+int open_temp_copy_fd( int fd, char **new );
+int open_temp_copy( const char *path, char **new, int force );
+
 #define DEFINE_ARRAY_TYPE(T) \
        typedef struct { \
                T *data; \
diff --git a/src/util.c b/src/util.c
index 22e639f57d68..4d17cae5f5d8 100644
--- a/src/util.c
+++ b/src/util.c
@@ -813,6 +813,156 @@ mkdir_p( char *path, int len )
        return mkdir( path, 0700 );
 }
 
+/* See filecpy(). */
+static off_t
+do_filecpy( int dst, int src, void *buf, size_t size )
+{
+       assert( buf );
+       assert( size <= SSIZE_MAX );
+
+       off_t total = 0;
+       while (1) {
+               ssize_t rd_size = read( src, buf, size );
+               if (!rd_size)
+                       return total;
+               if (rd_size < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       return -1;
+               }
+               total += rd_size;
+
+               ssize_t copied = 0;
+               while (copied < rd_size) {
+                       ssize_t wr_size = write( dst, buf, rd_size );
+                       if (wr_size < 0) {
+                               if (errno == EINTR)
+                                       continue;
+                               return -1;
+                       }
+                       copied += wr_size;
+               }
+       }
+}
+
+/*
+ * Like strcpy() but for regular files; copies file content from 'src' to 
'dst'.
+ * The copied content includes all data from the start of 'src' until EOF.
+ * This data is written to 'dst' from the current file offset, extending the
+ * file size if necessary.
+ *
+ * 'dst' should be an open file descriptor with write access.
+ * 'src' should be an open file descriptor with read access.
+ * 'dst' and 'src' must not refer to the same open file description.
+ *
+ * The file offset of 'dst' is advanced by the amount copied.
+ * The file offset of 'src' is unchanged.
+ *
+ * The function is not thread safe between concurrent calls acting on shared
+ * open file descriptions.
+ * No file locking is attempted.
+ *
+ * On success, returns the number of bytes copied.
+ * Otherwise, returns -1 and preserves the relevant errno value.
+ *
+ * Note that on failure, a partial copy may have occurred.
+ * So the file offset of 'dst', as well as the file contents from its initial
+ * file offset onwards, should be considered undefined.
+ */
+off_t
+filecpy( int dst, int src )
+{
+       off_t offset_bak = lseek( src, 0, SEEK_CUR );
+       if (offset_bak < 0)
+               return -1;
+
+       size_t buf_size = 1U << 12; // TODO Try page size?
+       void *buf = malloc( buf_size );
+       if (!buf)
+               return -1;
+
+       lseek( src, 0, SEEK_SET );
+       off_t copied = do_filecpy( dst, src, buf, buf_size );
+
+       int errno_bak = errno;
+       lseek( src, offset_bak, SEEK_SET );
+       errno = errno_bak;
+
+       free( buf );
+       return copied;
+}
+
+/* See open_temp_copy_fd(). */
+static int
+do_open_temp_copy_fd( int fd, char *path )
+{
+       assert( path );
+
+       int cpy_fd = mkstemp( path );
+       if (cpy_fd < 0)
+               return -1;
+       if (fd < 0)
+               return cpy_fd;
+
+       off_t cpy_size = filecpy( cpy_fd, fd );
+       if (cpy_size < 0) {
+               int errno_bak = errno;
+               close( cpy_fd );
+               errno = errno_bak;
+               return -1;
+       }
+       return cpy_fd;
+}
+
+/*
+ * Creates a temporary file and, if 'fd' is nonnegative, initialises its file
+ * content as a copy of 'fd'.
+ * If 'new' is non-NULL, the path of the temporary file is stored in it.
+ *
+ * 'fd' should either be negative or an open file descriptor with read access.
+ *
+ * On success, returns an open file descriptor for the new temporary file.
+ * Otherwise, returns -1 and preserves the relevant errno value.
+ */
+int
+open_temp_copy_fd( int fd, char **new )
+{
+       char *cpy_path = strdup( "/tmp/" EXE "-XXXXXX" );
+       if (!cpy_path)
+               return -1;
+
+       int cpy_fd = do_open_temp_copy_fd( fd, cpy_path );
+       if (cpy_fd >= 0 && new)
+               *new = cpy_path;
+       else
+               free( cpy_path );
+       return cpy_fd;
+}
+
+/*
+ * Like open_temp_copy_fd() but takes a filepath instead of a file descriptor.
+ * If the file located by 'path' cannot be read from, the behaviour depends on
+ * the value of 'force'.
+ * In this situation:
+ * - If 'force' is zero, failure occurs.
+ * - If 'force' is nonzero, the created file will be empty.
+ */
+int
+open_temp_copy( const char *path, char **new, int force )
+{
+       assert( path );
+
+       int fd = open( path, O_RDONLY );
+       if (fd < 0 && !force)
+               return -1;
+
+       int cpy_fd = open_temp_copy_fd( fd, new );
+       int errno_bak = errno;
+       close( fd );
+       errno = errno_bak;
+       return cpy_fd;
+}
+
 static int
 compare_uints( const void *l, const void *r )
 {
-- 
2.47.3



_______________________________________________
isync-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to