sparse file issue?, dd(1) works well but tar(1) not on different partition

Tim Kientzle kientzle at freebsd.org
Sun Jan 25 21:50:41 PST 2009


Daichi GOTO wrote:
> 
>   # cd /localapps/qemu/
>   # tar cpf - disk0image | tar xpf - -S -C /nfshome/user/
> 
> gets follow error message.
> 
>   # tar cpf - disk0image | tar xpf - -S -C /nfshome/user/
>   disk0image: Write request too large
>   tar: Error exit delayed from previous errors.

Please try the attached patch to
lib/libarchive/archive_write_disk.c, which should fix this.

Cheers,

Tim
-------------- next part --------------
Index: archive_write_disk.c
===================================================================
--- archive_write_disk.c	(revision 187703)
+++ archive_write_disk.c	(revision 187704)
@@ -178,6 +178,8 @@
 	int			 fd;
 	/* Current offset for writing data to the file. */
 	off_t			 offset;
+	/* Last offset actually written to disk. */
+	off_t			 fd_offset;
 	/* Maximum size of file, -1 if unknown. */
 	off_t			 filesize;
 	/* Dir we were in before this restore; only for deep paths. */
@@ -187,8 +189,6 @@
 	/* UID/GID to use in restoring this entry. */
 	uid_t			 uid;
 	gid_t			 gid;
-	/* Last offset written to disk. */
-	off_t			 last_offset;
 };
 
 /*
@@ -235,7 +235,7 @@
 static gid_t	trivial_lookup_gid(void *, const char *, gid_t);
 static uid_t	trivial_lookup_uid(void *, const char *, uid_t);
 static ssize_t	write_data_block(struct archive_write_disk *,
-		    const char *, size_t, off_t);
+		    const char *, size_t);
 
 static struct archive_vtable *archive_write_disk_vtable(void);
 
@@ -337,7 +337,7 @@
 	}
 	a->entry = archive_entry_clone(entry);
 	a->fd = -1;
-	a->last_offset = 0;
+	a->fd_offset = 0;
 	a->offset = 0;
 	a->uid = a->user_uid;
 	a->mode = archive_entry_mode(a->entry);
@@ -513,9 +513,9 @@
 }
 
 static ssize_t
-write_data_block(struct archive_write_disk *a,
-    const char *buff, size_t size, off_t offset)
+write_data_block(struct archive_write_disk *a, const char *buff, size_t size)
 {
+	uint64_t start_size = size;
 	ssize_t bytes_written = 0;
 	ssize_t block_size = 0, bytes_to_write;
 
@@ -538,8 +538,9 @@
 #endif
 	}
 
-	if (a->filesize >= 0 && (off_t)(offset + size) > a->filesize)
-		size = (size_t)(a->filesize - offset);
+	/* If this write would run beyond the file size, truncate it. */
+	if (a->filesize >= 0 && (off_t)(a->offset + size) > a->filesize)
+		start_size = size = (size_t)(a->filesize - a->offset);
 
 	/* Write the data. */
 	while (size > 0) {
@@ -555,7 +556,7 @@
 				if (*p != '\0')
 					break;
 			}
-			offset += p - buff;
+			a->offset += p - buff;
 			size -= p - buff;
 			buff = p;
 			if (size == 0)
@@ -563,22 +564,25 @@
 
 			/* Calculate next block boundary after offset. */
 			block_end
-			    = (offset / block_size) * block_size + block_size;
+			    = (a->offset / block_size + 1) * block_size;
 
 			/* If the adjusted write would cross block boundary,
 			 * truncate it to the block boundary. */
 			bytes_to_write = size;
-			if (offset + bytes_to_write > block_end)
-				bytes_to_write = block_end - offset;
+			if (a->offset + bytes_to_write > block_end)
+				bytes_to_write = block_end - a->offset;
 		}
 
 		/* Seek if necessary to the specified offset. */
-		if (offset != a->last_offset) {
-			if (lseek(a->fd, offset, SEEK_SET) < 0) {
+		if (a->offset != a->fd_offset) {
+			if (lseek(a->fd, a->offset, SEEK_SET) < 0) {
 				archive_set_error(&a->archive, errno,
 				    "Seek failed");
 				return (ARCHIVE_FATAL);
 			}
+			a->fd_offset = a->offset;
+			a->archive.file_position = a->offset;
+			a->archive.raw_position = a->offset;
  		}
 		bytes_written = write(a->fd, buff, bytes_to_write);
 		if (bytes_written < 0) {
@@ -587,12 +591,12 @@
 		}
 		buff += bytes_written;
 		size -= bytes_written;
-		offset += bytes_written;
+		a->offset += bytes_written;
 		a->archive.file_position += bytes_written;
 		a->archive.raw_position += bytes_written;
-		a->last_offset = a->offset = offset;
+		a->fd_offset = a->offset;
 	}
-	return (bytes_written);
+	return (start_size - size);
 }
 
 static ssize_t
@@ -605,9 +609,9 @@
 	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
 	    ARCHIVE_STATE_DATA, "archive_write_disk_block");
 
-	r = write_data_block(a, buff, size, offset);
-
-	if (r < 0)
+	a->offset = offset;
+	r = write_data_block(a, buff, size);
+	if (r < ARCHIVE_OK)
 		return (r);
 	if ((size_t)r < size) {
 		archive_set_error(&a->archive, 0,
@@ -625,7 +629,7 @@
 	__archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
 	    ARCHIVE_STATE_DATA, "archive_write_data");
 
-	return (write_data_block(a, buff, size, a->offset));
+	return (write_data_block(a, buff, size));
 }
 
 static int
@@ -646,7 +650,7 @@
 		/* There's no file. */
 	} else if (a->filesize < 0) {
 		/* File size is unknown, so we can't set the size. */
-	} else if (a->last_offset == a->filesize) {
+	} else if (a->fd_offset == a->filesize) {
 		/* Last write ended at exactly the filesize; we're done. */
 		/* Hopefully, this is the common case. */
 	} else {


More information about the freebsd-current mailing list