summaryrefslogtreecommitdiff
blob: f4eeb6d7441e9d361fb99494c1a722128f48215a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
revision 1.12
date: 2005/03/03 23:13:30;  author: gray;  state: Exp;  lines: +1 -1
(sparse_scan_file): Bugfix. offset had incorrect type.

revision 1.11
date: 2005/02/02 11:01:49;  author: gray;  state: Exp;  lines: +59 -14
Extract sparse files even if the output fd is not seekable.

Index: src/sparse.c
===================================================================
RCS file: /cvsroot/tar/tar/src/sparse.c,v
retrieving revision 1.10
retrieving revision 1.12
diff -u -p -r1.10 -r1.12
--- src/sparse.c	6 Sep 2004 14:28:56 -0000	1.10
+++ src/sparse.c	3 Mar 2005 23:13:30 -0000	1.12
@@ -46,6 +46,9 @@ struct tar_sparse_optab
 struct tar_sparse_file
 {
   int fd;                           /* File descriptor */
+  bool seekable;                    /* Is fd seekable? */
+  size_t offset;                    /* Current offset in fd if seekable==false.
+				       Otherwise unused */
   size_t dumped_size;               /* Number of bytes actually written
 				       to the archive */
   struct tar_stat_info *stat_info;  /* Information about the file */
@@ -54,6 +57,39 @@ struct tar_sparse_file
 				       reqiure */
 };
 
+/* Dump zeros to file->fd until offset is reached. It is used instead of
+   lseek if the output file is not seekable */
+static long
+dump_zeros (struct tar_sparse_file *file, off_t offset)
+{
+  char buf[BLOCKSIZE];
+  
+  if (offset - file->offset < 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  memset (buf, 0, sizeof buf);
+  while (file->offset < offset)
+    {
+      size_t size = offset - file->offset;
+      size_t wrbytes;
+      
+      if (size > sizeof buf)
+	size = sizeof buf;
+      wrbytes = write (file->fd, buf, size);
+      if (wrbytes <= 0)
+	{
+	  if (wrbytes == 0)
+	    errno = EINVAL;
+	  return -1;
+	}
+      file->offset += wrbytes;
+    }
+  return file->offset;
+}
+
 static bool
 tar_sparse_member_p (struct tar_sparse_file *file)
 {
@@ -130,9 +166,16 @@ tar_sparse_fixup_header (struct tar_spar
 
 
 static bool
-lseek_or_error (struct tar_sparse_file *file, off_t offset, int whence)
+lseek_or_error (struct tar_sparse_file *file, off_t offset)
 {
-  if (lseek (file->fd, offset, whence) < 0)
+  off_t off;
+
+  if (file->seekable)
+    off = lseek (file->fd, offset, SEEK_SET);
+  else
+    off = dump_zeros (file, offset);
+  
+  if (off < 0)
     {
       seek_diag_details (file->stat_info->orig_file_name, offset);
       return false;
@@ -182,10 +225,10 @@ sparse_scan_file (struct tar_sparse_file
 {
   static char buffer[BLOCKSIZE];
   size_t count;
-  size_t offset = 0;
+  off_t offset = 0;
   struct sp_array sp = {0, 0};
 
-  if (!lseek_or_error (file, 0, SEEK_SET))
+  if (!lseek_or_error (file, 0))
     return false;
   clear_block (buffer);
 
@@ -269,8 +312,7 @@ sparse_dump_region (struct tar_sparse_fi
   union block *blk;
   off_t bytes_left = file->stat_info->sparse_map[i].numbytes;
 
-  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset,
-		       SEEK_SET))
+  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
     return false;
 
   while (bytes_left > 0)
@@ -304,8 +346,7 @@ sparse_extract_region (struct tar_sparse
 {
   size_t write_size;
 
-  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset,
-		       SEEK_SET))
+  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
     return false;
 
   write_size = file->stat_info->sparse_map[i].numbytes;
@@ -313,7 +354,7 @@ sparse_extract_region (struct tar_sparse
   if (write_size == 0)
     {
       /* Last block of the file is a hole */
-      if (sys_truncate (file->fd))
+      if (file->seekable && sys_truncate (file->fd))
 	truncate_warn (file->stat_info->orig_file_name);
     }
   else while (write_size > 0)
@@ -330,6 +371,7 @@ sparse_extract_region (struct tar_sparse
       count = full_write (file->fd, blk->buffer, wrbytes);
       write_size -= count;
       file->dumped_size += count;
+      file->offset += count;
       if (count != wrbytes)
 	{
 	  write_error_details (file->stat_info->orig_file_name,
@@ -351,7 +393,9 @@ sparse_dump_file (int fd, struct tar_sta
 
   file.stat_info = st;
   file.fd = fd;
-
+  file.seekable = true; /* File *must* be seekable for dump to work */
+  file.offset = 0;
+  
   if (!sparse_select_optab (&file)
       || !tar_sparse_init (&file))
     return dump_status_not_implemented;
@@ -414,7 +458,9 @@ sparse_extract_file (int fd, struct tar_
 
   file.stat_info = st;
   file.fd = fd;
-
+  file.seekable = lseek (fd, 0, SEEK_SET) == 0;
+  file.offset = 0;
+  
   if (!sparse_select_optab (&file)
       || !tar_sparse_init (&file))
     return dump_status_not_implemented;
@@ -450,7 +496,7 @@ static char diff_buffer[BLOCKSIZE];
 static bool
 check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
 {
-  if (!lseek_or_error (file, beg, SEEK_SET))
+  if (!lseek_or_error (file, beg))
     return false;
 
   while (beg < end)
@@ -486,8 +532,7 @@ check_data_region (struct tar_sparse_fil
 {
   size_t size_left;
 
-  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset,
-		       SEEK_SET))
+  if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
     return false;
   size_left = file->stat_info->sparse_map[i].numbytes;
   while (size_left > 0)