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, 2005, 2006 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 2 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, write to the Free Software Foundation, Inc., 20 * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. 21 */ 22 23#include "rsync.h" 24 25#ifndef ENODATA 26#define ENODATA EAGAIN 27#endif 28 29extern int sparse_files; 30 31static char last_byte; 32static int last_sparse; 33 34int sparse_end(int f) 35{ 36 if (last_sparse) { 37 do_lseek(f,-1,SEEK_CUR); 38 return (write(f,&last_byte,1) == 1 ? 0 : -1); 39 } 40 last_sparse = 0; 41 return 0; 42} 43 44 45static int write_sparse(int f,char *buf,size_t len) 46{ 47 size_t l1=0, l2=0; 48 int ret; 49 50 for (l1 = 0; l1 < len && buf[l1] == 0; l1++) {} 51 for (l2 = 0; l2 < len-l1 && buf[len-(l2+1)] == 0; l2++) {} 52 53 last_byte = buf[len-1]; 54 55 if (l1 == len || l2 > 0) 56 last_sparse=1; 57 58 if (l1 > 0) { 59 do_lseek(f,l1,SEEK_CUR); 60 } 61 62 if (l1 == len) 63 return len; 64 65 ret = write(f, buf + l1, len - (l1+l2)); 66 if (ret == -1 || ret == 0) 67 return ret; 68 else if (ret != (int) (len - (l1+l2))) 69 return (l1+ret); 70 71 if (l2 > 0) 72 do_lseek(f,l2,SEEK_CUR); 73 74 return len; 75} 76 77 78static char *wf_writeBuf; 79static size_t wf_writeBufSize; 80static size_t wf_writeBufCnt; 81 82int flush_write_file(int f) 83{ 84 int ret = 0; 85 char *bp = wf_writeBuf; 86 87 while (wf_writeBufCnt > 0) { 88 if ((ret = write(f, bp, wf_writeBufCnt)) < 0) { 89 if (errno == EINTR) 90 continue; 91 return ret; 92 } 93 wf_writeBufCnt -= ret; 94 bp += ret; 95 } 96 return ret; 97} 98 99 100/* 101 * write_file does not allow incomplete writes. It loops internally 102 * until len bytes are written or errno is set. 103 */ 104int write_file(int f,char *buf,size_t len) 105{ 106 int ret = 0; 107 108 while (len > 0) { 109 int r1; 110 if (sparse_files) { 111 int len1 = MIN(len, SPARSE_WRITE_SIZE); 112 r1 = write_sparse(f, buf, len1); 113 } else { 114 if (!wf_writeBuf) { 115 wf_writeBufSize = WRITE_SIZE * 8; 116 wf_writeBufCnt = 0; 117 wf_writeBuf = new_array(char, wf_writeBufSize); 118 if (!wf_writeBuf) 119 out_of_memory("write_file"); 120 } 121 r1 = MIN(len, wf_writeBufSize - wf_writeBufCnt); 122 if (r1) { 123 memcpy(wf_writeBuf + wf_writeBufCnt, buf, r1); 124 wf_writeBufCnt += r1; 125 } 126 if (wf_writeBufCnt == wf_writeBufSize) { 127 if (flush_write_file(f) < 0) 128 return -1; 129 if (!r1 && len) 130 continue; 131 } 132 } 133 if (r1 <= 0) { 134 if (ret > 0) 135 return ret; 136 return r1; 137 } 138 len -= r1; 139 buf += r1; 140 ret += r1; 141 } 142 return ret; 143} 144 145 146/* This provides functionality somewhat similar to mmap() but using read(). 147 * It gives sliding window access to a file. mmap() is not used because of 148 * the possibility of another program (such as a mailer) truncating the 149 * file thus giving us a SIGBUS. */ 150struct map_struct *map_file(int fd, OFF_T len, int32 read_size, 151 int32 blk_size) 152{ 153 struct map_struct *map; 154 155 if (!(map = new(struct map_struct))) 156 out_of_memory("map_file"); 157 158 if (blk_size && (read_size % blk_size)) 159 read_size += blk_size - (read_size % blk_size); 160 161 memset(map, 0, sizeof map[0]); 162 map->fd = fd; 163 map->file_size = len; 164 map->def_window_size = read_size; 165 166 return map; 167} 168 169 170/* slide the read window in the file */ 171char *map_ptr(struct map_struct *map, OFF_T offset, int32 len) 172{ 173 int32 nread; 174 OFF_T window_start, read_start; 175 int32 window_size, read_size, read_offset; 176 177 if (len == 0) 178 return NULL; 179 if (len < 0) { 180 rprintf(FERROR, "invalid len passed to map_ptr: %ld\n", 181 (long)len); 182 exit_cleanup(RERR_FILEIO); 183 } 184 185 /* in most cases the region will already be available */ 186 if (offset >= map->p_offset && offset+len <= map->p_offset+map->p_len) 187 return map->p + (offset - map->p_offset); 188 189 /* nope, we are going to have to do a read. Work out our desired window */ 190 window_start = offset; 191 window_size = map->def_window_size; 192 if (window_start + window_size > map->file_size) 193 window_size = map->file_size - window_start; 194 if (len > window_size) 195 window_size = len; 196 197 /* make sure we have allocated enough memory for the window */ 198 if (window_size > map->p_size) { 199 map->p = realloc_array(map->p, char, window_size); 200 if (!map->p) 201 out_of_memory("map_ptr"); 202 map->p_size = window_size; 203 } 204 205 /* Now try to avoid re-reading any bytes by reusing any bytes 206 * from the previous buffer. */ 207 if (window_start >= map->p_offset && 208 window_start < map->p_offset + map->p_len && 209 window_start + window_size >= map->p_offset + map->p_len) { 210 read_start = map->p_offset + map->p_len; 211 read_offset = read_start - window_start; 212 read_size = window_size - read_offset; 213 memmove(map->p, map->p + (map->p_len - read_offset), read_offset); 214 } else { 215 read_start = window_start; 216 read_size = window_size; 217 read_offset = 0; 218 } 219 220 if (read_size <= 0) { 221 rprintf(FERROR, "invalid read_size of %ld in map_ptr\n", 222 (long)read_size); 223 exit_cleanup(RERR_FILEIO); 224 } 225 226 if (map->p_fd_offset != read_start) { 227 OFF_T ret = do_lseek(map->fd, read_start, SEEK_SET); 228 if (ret != read_start) { 229 rsyserr(FERROR, errno, "lseek returned %.0f, not %.0f", 230 (double)ret, (double)read_start); 231 exit_cleanup(RERR_FILEIO); 232 } 233 map->p_fd_offset = read_start; 234 } 235 map->p_offset = window_start; 236 map->p_len = window_size; 237 238 while (read_size > 0) { 239 nread = read(map->fd, map->p + read_offset, read_size); 240 if (nread <= 0) { 241 if (!map->status) 242 map->status = nread ? errno : ENODATA; 243 /* The best we can do is zero the buffer -- the file 244 * has changed mid transfer! */ 245 memset(map->p + read_offset, 0, read_size); 246 break; 247 } 248 map->p_fd_offset += nread; 249 read_offset += nread; 250 read_size -= nread; 251 } 252 253 return map->p; 254} 255 256 257int unmap_file(struct map_struct *map) 258{ 259 int ret; 260 261 if (map->p) { 262 free(map->p); 263 map->p = NULL; 264 } 265 ret = map->status; 266 memset(map, 0, sizeof map[0]); 267 free(map); 268 269 return ret; 270} 271