Annotation of embedaddon/rsync/fileio.c, revision 1.1.1.4

1.1       misho       1: /*
                      2:  * File IO utilities used in rsync.
                      3:  *
                      4:  * Copyright (C) 1998 Andrew Tridgell
                      5:  * Copyright (C) 2002 Martin Pool
1.1.1.4 ! misho       6:  * Copyright (C) 2004-2020 Wayne Davison
1.1       misho       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"
1.1.1.2   misho      23: #include "inums.h"
1.1       misho      24: 
                     25: #ifndef ENODATA
                     26: #define ENODATA EAGAIN
                     27: #endif
                     28: 
1.1.1.4 ! misho      29: /* We want all reads to be aligned on 1K boundaries. */
        !            30: #define ALIGN_BOUNDARY 1024
1.1.1.2   misho      31: /* How far past the boundary is an offset? */
1.1.1.4 ! misho      32: #define ALIGNED_OVERSHOOT(oft) ((oft) & (ALIGN_BOUNDARY-1))
1.1.1.2   misho      33: /* Round up a length to the next boundary */
1.1.1.4 ! misho      34: #define ALIGNED_LENGTH(len) ((((len) - 1) | (ALIGN_BOUNDARY-1)) + 1)
1.1.1.2   misho      35: 
1.1       misho      36: extern int sparse_files;
1.1.1.4 ! misho      37: extern int sparse_files_block_size;
        !            38: 
        !            39: OFF_T preallocated_len = 0;
1.1       misho      40: 
                     41: static OFF_T sparse_seek = 0;
1.1.1.4 ! misho      42: static OFF_T sparse_past_write = 0;
1.1       misho      43: 
                     44: int sparse_end(int f, OFF_T size)
                     45: {
                     46:        int ret;
                     47: 
1.1.1.4 ! misho      48:        sparse_past_write = 0;
        !            49: 
1.1       misho      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: 
1.1.1.4 ! misho      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)
1.1       misho      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: 
1.1.1.4 ! misho      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:        }
1.1       misho      97:        sparse_seek = l2;
1.1.1.4 ! misho      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:        }
1.1       misho     106: 
                    107:        while ((ret = write(f, buf + l1, len - (l1+l2))) <= 0) {
                    108:                if (ret < 0 && errno == EINTR)
                    109:                        continue;
1.1.1.2   misho     110:                sparse_seek = 0;
1.1       misho     111:                return ret;
                    112:        }
                    113: 
1.1.1.2   misho     114:        if (ret != (int)(len - (l1+l2))) {
                    115:                sparse_seek = 0;
1.1       misho     116:                return l1+ret;
1.1.1.2   misho     117:        }
1.1       misho     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: 
1.1.1.4 ! misho     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)
1.1       misho     147: {
                    148:        int ret = 0;
                    149: 
                    150:        while (len > 0) {
                    151:                int r1;
                    152:                if (sparse_files > 0) {
1.1.1.4 ! misho     153:                        int len1 = MIN(len, sparse_files_block_size);
        !           154:                        r1 = write_sparse(f, use_seek, offset, buf, len1);
        !           155:                        offset += r1;
1.1       misho     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: 
1.1.1.4 ! misho     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: }
1.1       misho     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. */
1.1.1.2   misho     215: struct map_struct *map_file(int fd, OFF_T len, int32 read_size, int32 blk_size)
1.1       misho     216: {
                    217:        struct map_struct *map;
                    218: 
1.1.1.4 ! misho     219:        map = new0(struct map_struct);
1.1       misho     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;
1.1.1.2   misho     226:        map->def_window_size = ALIGNED_LENGTH(read_size);
1.1       misho     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;
1.1.1.2   misho     236:        int32 window_size, read_size, read_offset, align_fudge;
1.1       misho     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 */
1.1.1.2   misho     251:        align_fudge = (int32)ALIGNED_OVERSHOOT(offset);
                    252:        window_start = offset - align_fudge;
1.1       misho     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);
1.1.1.2   misho     256:        if (window_size < len + align_fudge)
                    257:                window_size = ALIGNED_LENGTH(len + align_fudge);
1.1       misho     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: 
1.1.1.2   misho     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) {
1.1       misho     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) {
1.1.1.2   misho     287:                        rsyserr(FERROR, errno, "lseek returned %s, not %s",
                    288:                                big_num(ret), big_num(read_start));
1.1       misho     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) {
1.1.1.2   misho     297:                int32 nread = read(map->fd, map->p + read_offset, read_size);
1.1       misho     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: 
1.1.1.2   misho     311:        return map->p + align_fudge;
1.1       misho     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;
1.1.1.4 ! misho     323: #if 0 /* I don't think we really need this. */
        !           324:        force_memzero(map, sizeof map[0]);
        !           325: #endif
1.1       misho     326:        free(map);
                    327: 
                    328:        return ret;
                    329: }

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