File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / rsync / fileio.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:32:36 2021 UTC (3 years, 3 months ago) by misho
Branches: rsync, MAIN
CVS tags: v3_2_3, HEAD
rsync 3.2.3

    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-2020 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: #include "inums.h"
   24: 
   25: #ifndef ENODATA
   26: #define ENODATA EAGAIN
   27: #endif
   28: 
   29: /* We want all reads to be aligned on 1K boundaries. */
   30: #define ALIGN_BOUNDARY 1024
   31: /* How far past the boundary is an offset? */
   32: #define ALIGNED_OVERSHOOT(oft) ((oft) & (ALIGN_BOUNDARY-1))
   33: /* Round up a length to the next boundary */
   34: #define ALIGNED_LENGTH(len) ((((len) - 1) | (ALIGN_BOUNDARY-1)) + 1)
   35: 
   36: extern int sparse_files;
   37: extern int sparse_files_block_size;
   38: 
   39: OFF_T preallocated_len = 0;
   40: 
   41: static OFF_T sparse_seek = 0;
   42: static OFF_T sparse_past_write = 0;
   43: 
   44: int sparse_end(int f, OFF_T size)
   45: {
   46: 	int ret;
   47: 
   48: 	sparse_past_write = 0;
   49: 
   50: 	if (!sparse_seek)
   51: 		return 0;
   52: 
   53: #ifdef HAVE_FTRUNCATE
   54: 	ret = do_ftruncate(f, size);
   55: #else
   56: 	if (do_lseek(f, sparse_seek-1, SEEK_CUR) != size-1)
   57: 		ret = -1;
   58: 	else {
   59: 		do {
   60: 			ret = write(f, "", 1);
   61: 		} while (ret < 0 && errno == EINTR);
   62: 
   63: 		ret = ret <= 0 ? -1 : 0;
   64: 	}
   65: #endif
   66: 
   67: 	sparse_seek = 0;
   68: 
   69: 	return ret;
   70: }
   71: 
   72: /* Note that the offset is just the caller letting us know where
   73:  * the current file position is in the file. The use_seek arg tells
   74:  * us that we should seek over matching data instead of writing it. */
   75: static int write_sparse(int f, int use_seek, OFF_T offset, const char *buf, int len)
   76: {
   77: 	int l1 = 0, l2 = 0;
   78: 	int ret;
   79: 
   80: 	for (l1 = 0; l1 < len && buf[l1] == 0; l1++) {}
   81: 	for (l2 = 0; l2 < len-l1 && buf[len-(l2+1)] == 0; l2++) {}
   82: 
   83: 	sparse_seek += l1;
   84: 
   85: 	if (l1 == len)
   86: 		return len;
   87: 
   88: 	if (sparse_seek) {
   89: 		if (sparse_past_write >= preallocated_len) {
   90: 			if (do_lseek(f, sparse_seek, SEEK_CUR) < 0)
   91: 				return -1;
   92: 		} else if (do_punch_hole(f, sparse_past_write, sparse_seek) < 0) {
   93: 			sparse_seek = 0;
   94: 			return -1;
   95: 		}
   96: 	}
   97: 	sparse_seek = l2;
   98: 	sparse_past_write = offset + len - l2;
   99: 
  100: 	if (use_seek) {
  101: 		/* The in-place data already matches. */
  102: 		if (do_lseek(f, len - (l1+l2), SEEK_CUR) < 0)
  103: 			return -1;
  104: 		return len;
  105: 	}
  106: 
  107: 	while ((ret = write(f, buf + l1, len - (l1+l2))) <= 0) {
  108: 		if (ret < 0 && errno == EINTR)
  109: 			continue;
  110: 		sparse_seek = 0;
  111: 		return ret;
  112: 	}
  113: 
  114: 	if (ret != (int)(len - (l1+l2))) {
  115: 		sparse_seek = 0;
  116: 		return l1+ret;
  117: 	}
  118: 
  119: 	return len;
  120: }
  121: 
  122: static char *wf_writeBuf;
  123: static size_t wf_writeBufSize;
  124: static size_t wf_writeBufCnt;
  125: 
  126: int flush_write_file(int f)
  127: {
  128: 	int ret = 0;
  129: 	char *bp = wf_writeBuf;
  130: 
  131: 	while (wf_writeBufCnt > 0) {
  132: 		if ((ret = write(f, bp, wf_writeBufCnt)) < 0) {
  133: 			if (errno == EINTR)
  134: 				continue;
  135: 			return ret;
  136: 		}
  137: 		wf_writeBufCnt -= ret;
  138: 		bp += ret;
  139: 	}
  140: 	return ret;
  141: }
  142: 
  143: /* write_file does not allow incomplete writes.  It loops internally
  144:  * until len bytes are written or errno is set.  Note that use_seek and
  145:  * offset are only used in sparse processing (see write_sparse()). */
  146: int write_file(int f, int use_seek, OFF_T offset, const char *buf, int len)
  147: {
  148: 	int ret = 0;
  149: 
  150: 	while (len > 0) {
  151: 		int r1;
  152: 		if (sparse_files > 0) {
  153: 			int len1 = MIN(len, sparse_files_block_size);
  154: 			r1 = write_sparse(f, use_seek, offset, buf, len1);
  155: 			offset += r1;
  156: 		} else {
  157: 			if (!wf_writeBuf) {
  158: 				wf_writeBufSize = WRITE_SIZE * 8;
  159: 				wf_writeBufCnt  = 0;
  160: 				wf_writeBuf = new_array(char, wf_writeBufSize);
  161: 			}
  162: 			r1 = (int)MIN((size_t)len, wf_writeBufSize - wf_writeBufCnt);
  163: 			if (r1) {
  164: 				memcpy(wf_writeBuf + wf_writeBufCnt, buf, r1);
  165: 				wf_writeBufCnt += r1;
  166: 			}
  167: 			if (wf_writeBufCnt == wf_writeBufSize) {
  168: 				if (flush_write_file(f) < 0)
  169: 					return -1;
  170: 				if (!r1 && len)
  171: 					continue;
  172: 			}
  173: 		}
  174: 		if (r1 <= 0) {
  175: 			if (ret > 0)
  176: 				return ret;
  177: 			return r1;
  178: 		}
  179: 		len -= r1;
  180: 		buf += r1;
  181: 		ret += r1;
  182: 	}
  183: 	return ret;
  184: }
  185: 
  186: /* An in-place update found identical data at an identical location. We either
  187:  * just seek past it, or (for an in-place sparse update), we give the data to
  188:  * the sparse processor with the use_seek flag set. */
  189: int skip_matched(int fd, OFF_T offset, const char *buf, int len)
  190: {
  191: 	OFF_T pos;
  192: 
  193: 	if (sparse_files > 0) {
  194: 		if (write_file(fd, 1, offset, buf, len) != len)
  195: 			return -1;
  196: 		return 0;
  197: 	}
  198: 
  199: 	if (flush_write_file(fd) < 0)
  200: 		return -1;
  201: 
  202: 	if ((pos = do_lseek(fd, len, SEEK_CUR)) != offset + len) {
  203: 		rsyserr(FERROR_XFER, errno, "lseek returned %s, not %s",
  204: 			big_num(pos), big_num(offset));
  205: 		return -1;
  206: 	}
  207: 
  208: 	return 0;
  209: }
  210: 
  211: /* This provides functionality somewhat similar to mmap() but using read().
  212:  * It gives sliding window access to a file.  mmap() is not used because of
  213:  * the possibility of another program (such as a mailer) truncating the
  214:  * file thus giving us a SIGBUS. */
  215: struct map_struct *map_file(int fd, OFF_T len, int32 read_size, int32 blk_size)
  216: {
  217: 	struct map_struct *map;
  218: 
  219: 	map = new0(struct map_struct);
  220: 
  221: 	if (blk_size && (read_size % blk_size))
  222: 		read_size += blk_size - (read_size % blk_size);
  223: 
  224: 	map->fd = fd;
  225: 	map->file_size = len;
  226: 	map->def_window_size = ALIGNED_LENGTH(read_size);
  227: 
  228: 	return map;
  229: }
  230: 
  231: 
  232: /* slide the read window in the file */
  233: char *map_ptr(struct map_struct *map, OFF_T offset, int32 len)
  234: {
  235: 	OFF_T window_start, read_start;
  236: 	int32 window_size, read_size, read_offset, align_fudge;
  237: 
  238: 	if (len == 0)
  239: 		return NULL;
  240: 	if (len < 0) {
  241: 		rprintf(FERROR, "invalid len passed to map_ptr: %ld\n",
  242: 			(long)len);
  243: 		exit_cleanup(RERR_FILEIO);
  244: 	}
  245: 
  246: 	/* in most cases the region will already be available */
  247: 	if (offset >= map->p_offset && offset+len <= map->p_offset+map->p_len)
  248: 		return map->p + (offset - map->p_offset);
  249: 
  250: 	/* nope, we are going to have to do a read. Work out our desired window */
  251: 	align_fudge = (int32)ALIGNED_OVERSHOOT(offset);
  252: 	window_start = offset - align_fudge;
  253: 	window_size = map->def_window_size;
  254: 	if (window_start + window_size > map->file_size)
  255: 		window_size = (int32)(map->file_size - window_start);
  256: 	if (window_size < len + align_fudge)
  257: 		window_size = ALIGNED_LENGTH(len + align_fudge);
  258: 
  259: 	/* make sure we have allocated enough memory for the window */
  260: 	if (window_size > map->p_size) {
  261: 		map->p = realloc_array(map->p, char, window_size);
  262: 		map->p_size = window_size;
  263: 	}
  264: 
  265: 	/* Now try to avoid re-reading any bytes by reusing any bytes from the previous buffer. */
  266: 	if (window_start >= map->p_offset && window_start < map->p_offset + map->p_len
  267: 	 && window_start + window_size >= map->p_offset + map->p_len) {
  268: 		read_start = map->p_offset + map->p_len;
  269: 		read_offset = (int32)(read_start - window_start);
  270: 		read_size = window_size - read_offset;
  271: 		memmove(map->p, map->p + (map->p_len - read_offset), read_offset);
  272: 	} else {
  273: 		read_start = window_start;
  274: 		read_size = window_size;
  275: 		read_offset = 0;
  276: 	}
  277: 
  278: 	if (read_size <= 0) {
  279: 		rprintf(FERROR, "invalid read_size of %ld in map_ptr\n",
  280: 			(long)read_size);
  281: 		exit_cleanup(RERR_FILEIO);
  282: 	}
  283: 
  284: 	if (map->p_fd_offset != read_start) {
  285: 		OFF_T ret = do_lseek(map->fd, read_start, SEEK_SET);
  286: 		if (ret != read_start) {
  287: 			rsyserr(FERROR, errno, "lseek returned %s, not %s",
  288: 				big_num(ret), big_num(read_start));
  289: 			exit_cleanup(RERR_FILEIO);
  290: 		}
  291: 		map->p_fd_offset = read_start;
  292: 	}
  293: 	map->p_offset = window_start;
  294: 	map->p_len = window_size;
  295: 
  296: 	while (read_size > 0) {
  297: 		int32 nread = read(map->fd, map->p + read_offset, read_size);
  298: 		if (nread <= 0) {
  299: 			if (!map->status)
  300: 				map->status = nread ? errno : ENODATA;
  301: 			/* The best we can do is zero the buffer -- the file
  302: 			 * has changed mid transfer! */
  303: 			memset(map->p + read_offset, 0, read_size);
  304: 			break;
  305: 		}
  306: 		map->p_fd_offset += nread;
  307: 		read_offset += nread;
  308: 		read_size -= nread;
  309: 	}
  310: 
  311: 	return map->p + align_fudge;
  312: }
  313: 
  314: int unmap_file(struct map_struct *map)
  315: {
  316: 	int	ret;
  317: 
  318: 	if (map->p) {
  319: 		free(map->p);
  320: 		map->p = NULL;
  321: 	}
  322: 	ret = map->status;
  323: #if 0 /* I don't think we really need this. */
  324: 	force_memzero(map, sizeof map[0]);
  325: #endif
  326: 	free(map);
  327: 
  328: 	return ret;
  329: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>