1ae5eac3e0bb58fa9844f2fd5af44b7225889f62
[rsync/rsync.git] / fileio.c
1 /* 
2    Copyright (C) Andrew Tridgell 1998
3    
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8    
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13    
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 /*
20   File IO utilities used in rsync 
21   */
22 #include "rsync.h"
23
24 static char last_byte;
25 static int last_sparse;
26 extern int sparse_files;
27
28 int sparse_end(int f)
29 {
30         if (last_sparse) {
31                 do_lseek(f,-1,SEEK_CUR);
32                 return (write(f,&last_byte,1) == 1 ? 0 : -1);
33         }
34         last_sparse = 0;
35         return 0;
36 }
37
38
39 static int write_sparse(int f,char *buf,int len)
40 {
41         int l1=0,l2=0;
42         int ret;
43
44         for (l1=0;l1<len && buf[l1]==0;l1++) ;
45         for (l2=0;l2<(len-l1) && buf[len-(l2+1)]==0;l2++) ;
46
47         last_byte = buf[len-1];
48
49         if (l1 == len || l2 > 0)
50                 last_sparse=1;
51
52         if (l1 > 0)
53                 do_lseek(f,l1,SEEK_CUR);  
54
55         if (l1 == len) 
56                 return len;
57
58         if ((ret=write(f,buf+l1,len-(l1+l2))) != len-(l1+l2)) {
59                 if (ret == -1 || ret == 0) return ret;
60                 return (l1+ret);
61         }
62
63         if (l2 > 0)
64                 do_lseek(f,l2,SEEK_CUR);
65         
66         return len;
67 }
68
69
70
71 int write_file(int f,char *buf,int len)
72 {
73         int ret = 0;
74
75         if (!sparse_files) 
76                 return write(f,buf,len);
77
78         while (len>0) {
79                 int len1 = MIN(len, SPARSE_WRITE_SIZE);
80                 int r1 = write_sparse(f, buf, len1);
81                 if (r1 <= 0) {
82                         if (ret > 0) return ret;
83                         return r1;
84                 }
85                 len -= r1;
86                 buf += r1;
87                 ret += r1;
88         }
89         return ret;
90 }
91
92
93
94 struct map_struct *map_file(int fd,OFF_T len)
95 {
96         struct map_struct *ret;
97         ret = (struct map_struct *)malloc(sizeof(*ret));
98         if (!ret) out_of_memory("map_file");
99
100         ret->map = NULL;
101         ret->fd = fd;
102         ret->size = len;
103         ret->p = NULL;
104         ret->p_size = 0;
105         ret->p_offset = 0;
106         ret->p_len = 0;
107
108 #ifdef USE_MMAP
109         len = MIN(len, MAX_MAP_SIZE);
110         ret->map = (char *)do_mmap(NULL,len,PROT_READ,MAP_SHARED,fd,0);
111         if (ret->map == (char *)-1) {
112                 ret->map = NULL;
113         } else {
114                 ret->p_len = len;
115         }
116 #endif
117         return ret;
118 }
119
120
121 char *map_ptr(struct map_struct *map,OFF_T offset,int len)
122 {
123         int nread;
124
125         if (len == 0) 
126                 return NULL;
127
128         if (len > (map->size-offset))
129                 len = map->size-offset;
130
131 #ifdef USE_MMAP
132         if (map->map) {
133                 if (offset >= map->p_offset && 
134                     offset+len <= map->p_offset+map->p_len) {
135                         return (map->map + (offset - map->p_offset));
136                 }
137                 if (munmap(map->map, map->p_len) != 0) {
138                         rprintf(FERROR,"munmap failed : %s\n", strerror(errno));
139                         exit_cleanup(1);
140                 }
141
142                 /* align the mmap region on a nice boundary back a bit from
143                    where it is asked for to allow for some seeking */
144                 if (offset > 2*CHUNK_SIZE) {
145                         map->p_offset = offset - 2*CHUNK_SIZE;
146                         map->p_offset &= ~((OFF_T)(CHUNK_SIZE-1));
147                 } else {
148                         map->p_offset = 0;
149                 }
150                 
151                 /* map up to MAX_MAP_SIZE */
152                 map->p_len = MAX(len, MAX_MAP_SIZE);
153                 map->p_len = MIN(map->p_len, map->size - map->p_offset);
154
155                 map->map = (char *)do_mmap(NULL,map->p_len,PROT_READ,
156                                            MAP_SHARED,map->fd,map->p_offset);
157
158                 if (map->map == (char *)-1) {
159                         map->map = NULL;
160                         map->p_len = 0;
161                         map->p_offset = 0;
162                 } else {
163                         return (map->map + (offset - map->p_offset));
164                 }
165         }
166 #endif
167
168         if (offset >= map->p_offset && 
169             offset+len <= map->p_offset+map->p_len) {
170                 return (map->p + (offset - map->p_offset));
171         }
172
173         len = MAX(len,CHUNK_SIZE);
174         if (len > (map->size-offset))
175                 len = map->size-offset;
176
177         if (len > map->p_size) {
178                 if (map->p) free(map->p);
179                 map->p = (char *)malloc(len);
180                 if (!map->p) out_of_memory("map_ptr");
181                 map->p_size = len;
182         }
183
184         map->p_offset = offset;
185         map->p_len = len;
186
187         if (do_lseek(map->fd,offset,SEEK_SET) != offset) {
188                 rprintf(FERROR,"lseek failed in map_ptr\n");
189                 exit_cleanup(1);
190         }
191
192         if ((nread=read(map->fd,map->p,len)) != len) {          
193                 if (nread < 0) nread = 0;
194                 /* the best we can do is zero the buffer - the file
195                    has changed mid transfer! */
196                 memset(map->p+nread, 0, len - nread);
197         }
198   
199         return map->p; 
200 }
201
202
203 void unmap_file(struct map_struct *map)
204 {
205 #ifdef USE_MMAP
206         if (map->map) {
207                 munmap(map->map,map->p_len);
208                 map->map = NULL;
209         }
210 #endif
211         if (map->p) {
212                 free(map->p);
213                 map->p = NULL;
214         }
215         memset(map, 0, sizeof(*map));
216         free(map);
217 }
218