Fix sending of "." attributes for implied-dot-dir.
[rsync.git] / fileio.c
1 /*
2  * File IO utilities used in rsync.
3  *
4  * Copyright (C) 1998 Andrew Tridgell
5  * Copyright (C) 2002 Martin Pool
6  * Copyright (C) 2004-2009 Wayne Davison
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, visit the http://fsf.org website.
20  */
21
22 #include "rsync.h"
23
24 #ifndef ENODATA
25 #define ENODATA EAGAIN
26 #endif
27
28 extern int sparse_files;
29
30 static OFF_T sparse_seek = 0;
31
32 int sparse_end(int f, OFF_T size)
33 {
34         int ret;
35
36         if (!sparse_seek)
37                 return 0;
38
39 #ifdef HAVE_FTRUNCATE
40         ret = do_ftruncate(f, size);
41 #else
42         if (do_lseek(f, sparse_seek-1, SEEK_CUR) != size-1)
43                 ret = -1;
44         else {
45                 do {
46                         ret = write(f, "", 1);
47                 } while (ret < 0 && errno == EINTR);
48
49                 ret = ret <= 0 ? -1 : 0;
50         }
51 #endif
52
53         sparse_seek = 0;
54
55         return ret;
56 }
57
58
59 static int write_sparse(int f, char *buf, int len)
60 {
61         int l1 = 0, l2 = 0;
62         int ret;
63
64         for (l1 = 0; l1 < len && buf[l1] == 0; l1++) {}
65         for (l2 = 0; l2 < len-l1 && buf[len-(l2+1)] == 0; l2++) {}
66
67         sparse_seek += l1;
68
69         if (l1 == len)
70                 return len;
71
72         if (sparse_seek)
73                 do_lseek(f, sparse_seek, SEEK_CUR);
74         sparse_seek = l2;
75
76         while ((ret = write(f, buf + l1, len - (l1+l2))) <= 0) {
77                 if (ret < 0 && errno == EINTR)
78                         continue;
79                 return ret;
80         }
81
82         if (ret != (int)(len - (l1+l2)))
83                 return l1+ret;
84
85         return len;
86 }
87
88
89 static char *wf_writeBuf;
90 static size_t wf_writeBufSize;
91 static size_t wf_writeBufCnt;
92
93 int flush_write_file(int f)
94 {
95         int ret = 0;
96         char *bp = wf_writeBuf;
97
98         while (wf_writeBufCnt > 0) {
99                 if ((ret = write(f, bp, wf_writeBufCnt)) < 0) {
100                         if (errno == EINTR)
101                                 continue;
102                         return ret;
103                 }
104                 wf_writeBufCnt -= ret;
105                 bp += ret;
106         }
107         return ret;
108 }
109
110
111 /*
112  * write_file does not allow incomplete writes.  It loops internally
113  * until len bytes are written or errno is set.
114  */
115 int write_file(int f, char *buf, int len)
116 {
117         int ret = 0;
118
119         while (len > 0) {
120                 int r1;
121                 if (sparse_files > 0) {
122                         int len1 = MIN(len, SPARSE_WRITE_SIZE);
123                         r1 = write_sparse(f, buf, len1);
124                 } else {
125                         if (!wf_writeBuf) {
126                                 wf_writeBufSize = WRITE_SIZE * 8;
127                                 wf_writeBufCnt  = 0;
128                                 wf_writeBuf = new_array(char, wf_writeBufSize);
129                                 if (!wf_writeBuf)
130                                         out_of_memory("write_file");
131                         }
132                         r1 = (int)MIN((size_t)len, wf_writeBufSize - wf_writeBufCnt);
133                         if (r1) {
134                                 memcpy(wf_writeBuf + wf_writeBufCnt, buf, r1);
135                                 wf_writeBufCnt += r1;
136                         }
137                         if (wf_writeBufCnt == wf_writeBufSize) {
138                                 if (flush_write_file(f) < 0)
139                                         return -1;
140                                 if (!r1 && len)
141                                         continue;
142                         }
143                 }
144                 if (r1 <= 0) {
145                         if (ret > 0)
146                                 return ret;
147                         return r1;
148                 }
149                 len -= r1;
150                 buf += r1;
151                 ret += r1;
152         }
153         return ret;
154 }
155
156
157 /* This provides functionality somewhat similar to mmap() but using read().
158  * It gives sliding window access to a file.  mmap() is not used because of
159  * the possibility of another program (such as a mailer) truncating the
160  * file thus giving us a SIGBUS. */
161 struct map_struct *map_file(int fd, OFF_T len, int32 read_size,
162                             int32 blk_size)
163 {
164         struct map_struct *map;
165
166         if (!(map = new0(struct map_struct)))
167                 out_of_memory("map_file");
168
169         if (blk_size && (read_size % blk_size))
170                 read_size += blk_size - (read_size % blk_size);
171
172         map->fd = fd;
173         map->file_size = len;
174         map->def_window_size = read_size;
175
176         return map;
177 }
178
179
180 /* slide the read window in the file */
181 char *map_ptr(struct map_struct *map, OFF_T offset, int32 len)
182 {
183         int32 nread;
184         OFF_T window_start, read_start;
185         int32 window_size, read_size, read_offset;
186
187         if (len == 0)
188                 return NULL;
189         if (len < 0) {
190                 rprintf(FERROR, "invalid len passed to map_ptr: %ld\n",
191                         (long)len);
192                 exit_cleanup(RERR_FILEIO);
193         }
194
195         /* in most cases the region will already be available */
196         if (offset >= map->p_offset && offset+len <= map->p_offset+map->p_len)
197                 return map->p + (offset - map->p_offset);
198
199         /* nope, we are going to have to do a read. Work out our desired window */
200         window_start = offset;
201         window_size = map->def_window_size;
202         if (window_start + window_size > map->file_size)
203                 window_size = (int32)(map->file_size - window_start);
204         if (len > window_size)
205                 window_size = len;
206
207         /* make sure we have allocated enough memory for the window */
208         if (window_size > map->p_size) {
209                 map->p = realloc_array(map->p, char, window_size);
210                 if (!map->p)
211                         out_of_memory("map_ptr");
212                 map->p_size = window_size;
213         }
214
215         /* Now try to avoid re-reading any bytes by reusing any bytes
216          * from the previous buffer. */
217         if (window_start >= map->p_offset &&
218             window_start < map->p_offset + map->p_len &&
219             window_start + window_size >= map->p_offset + map->p_len) {
220                 read_start = map->p_offset + map->p_len;
221                 read_offset = (int32)(read_start - window_start);
222                 read_size = window_size - read_offset;
223                 memmove(map->p, map->p + (map->p_len - read_offset), read_offset);
224         } else {
225                 read_start = window_start;
226                 read_size = window_size;
227                 read_offset = 0;
228         }
229
230         if (read_size <= 0) {
231                 rprintf(FERROR, "invalid read_size of %ld in map_ptr\n",
232                         (long)read_size);
233                 exit_cleanup(RERR_FILEIO);
234         }
235
236         if (map->p_fd_offset != read_start) {
237                 OFF_T ret = do_lseek(map->fd, read_start, SEEK_SET);
238                 if (ret != read_start) {
239                         rsyserr(FERROR, errno, "lseek returned %.0f, not %.0f",
240                                 (double)ret, (double)read_start);
241                         exit_cleanup(RERR_FILEIO);
242                 }
243                 map->p_fd_offset = read_start;
244         }
245         map->p_offset = window_start;
246         map->p_len = window_size;
247
248         while (read_size > 0) {
249                 nread = read(map->fd, map->p + read_offset, read_size);
250                 if (nread <= 0) {
251                         if (!map->status)
252                                 map->status = nread ? errno : ENODATA;
253                         /* The best we can do is zero the buffer -- the file
254                          * has changed mid transfer! */
255                         memset(map->p + read_offset, 0, read_size);
256                         break;
257                 }
258                 map->p_fd_offset += nread;
259                 read_offset += nread;
260                 read_size -= nread;
261         }
262
263         return map->p;
264 }
265
266
267 int unmap_file(struct map_struct *map)
268 {
269         int     ret;
270
271         if (map->p) {
272                 free(map->p);
273                 map->p = NULL;
274         }
275         ret = map->status;
276         memset(map, 0, sizeof map[0]);
277         free(map);
278
279         return ret;
280 }