/* Copyright (C) Andrew Tridgell 1996 Copyright (C) Paul Mackerras 1996 Copyright (C) 2001, 2002 by Martin Pool This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file flist.c * Generate and receive file lists * * @todo Get rid of the string_area optimization. Efficiently * allocating blocks is the responsibility of the system's malloc * library, not of rsync. * * @sa http://lists.samba.org/pipermail/rsync/2000-June/002351.html * **/ #include #include #include #include #include #include #include #include "flist.h" extern struct stats stats; extern int verbose; extern int do_progress; extern int am_server; extern int always_checksum; extern int cvs_exclude; extern int recurse; extern int one_file_system; extern int make_backups; extern int relative_paths; extern int copy_links; extern int copy_unsafe_links; extern int remote_version; extern int io_error; extern int sanitize_paths; extern int read_batch; extern int write_batch; static struct file_struct null_file; static int to_wire_mode(mode_t mode) { if (S_ISLNK(mode) && (_S_IFLNK != 0120000)) { return (mode & ~(_S_IFMT)) | 0120000; } return (int) mode; } static mode_t from_wire_mode(int mode) { if ((mode & (_S_IFMT)) == 0120000 && (_S_IFLNK != 0120000)) { return (mode & ~(_S_IFMT)) | _S_IFLNK; } return (mode_t) mode; } /* we need this function because of the silly way in which duplicate entries are handled in the file lists - we can't change this without breaking existing versions */ static int flist_up(struct file_list *flist, int i) { while (!flist->files[i]->basename) i++; return i; } /** * Make sure @p flist is big enough to hold at least @p flist->count * entries. **/ void flist_expand(struct file_list *flist) { if (flist->count >= flist->malloced) { size_t new_bytes; void *new_ptr; if (flist->malloced < 1000) flist->malloced += 1000; else flist->malloced *= 2; new_bytes = sizeof(flist->files[0]) * flist->malloced; if (flist->files) new_ptr = realloc(flist->files, new_bytes); else new_ptr = malloc(new_bytes); flist->files = (struct file_struct **) new_ptr; } } /* Like strncpy but does not 0 fill the buffer and always null * terminates. bufsize is the size of the destination buffer. * * Returns the index of the terminating byte. */ size_t strlcpy(char *d, const char *s, size_t bufsize) { size_t len = strlen(s); size_t ret = len; if (bufsize <= 0) return 0; if (len >= bufsize) len = bufsize-1; memcpy(d, s, len); d[len] = 0; return ret; } /* * return the full filename of a flist entry * * This function is too expensive at the moment, because it copies * strings when often we only want to compare them. In any case, * using strlcat is silly because it will walk the string repeatedly. */ char *f_name(struct file_struct *f) { static char names[10][MAXPATHLEN]; static int n; char *p = names[n]; if (!f || !f->basename) return NULL; n = (n + 1) % 10; if (f->dirname) { int off; off = strlcpy(p, f->dirname, MAXPATHLEN); off += strlcpy(p + off, "/", MAXPATHLEN - off); off += strlcpy(p + off, f->basename, MAXPATHLEN - off); } else { strlcpy(p, f->basename, MAXPATHLEN); } return p; } /* we need to supply our own strcmp function for file list comparisons to ensure that signed/unsigned usage is consistent between machines. */ static int u_strcmp(const char *cs1, const char *cs2) { const uchar *s1 = (const uchar *)cs1; const uchar *s2 = (const uchar *)cs2; while (*s1 && *s2 && (*s1 == *s2)) { s1++; s2++; } return (int)*s1 - (int)*s2; } /* * XXX: This is currently the hottest function while building the file * list, because building f_name()s every time is expensive. **/ int file_compare(struct file_struct **f1, struct file_struct **f2) { if (!(*f1)->basename && !(*f2)->basename) return 0; if (!(*f1)->basename) return -1; if (!(*f2)->basename) return 1; if ((*f1)->dirname == (*f2)->dirname) return u_strcmp((*f1)->basename, (*f2)->basename); return u_strcmp(f_name(*f1), f_name(*f2)); } int flist_find(struct file_list *flist, struct file_struct *f) { int low = 0, high = flist->count - 1; if (flist->count <= 0) return -1; while (low != high) { int mid = (low + high) / 2; int ret = file_compare(&flist->files[flist_up(flist, mid)], &f); if (ret == 0) return flist_up(flist, mid); if (ret > 0) { high = mid; } else { low = mid + 1; } } if (file_compare(&flist->files[flist_up(flist, low)], &f) == 0) return flist_up(flist, low); return -1; } /* * free up one file */ void free_file(struct file_struct *file) { if (!file) return; if (file->basename) free(file->basename); if ( file->dirnameAlloc ) free(file->dirname); if (file->link) free(file->link); if (file->sum) free(file->sum); *file = null_file; } /* * allocate a new file list */ struct file_list *flist_new(void) { struct file_list *flist; flist = (struct file_list *) malloc(sizeof(*flist)); memset(flist, 0, sizeof(*flist)); flist->count = 0; flist->malloced = 0; flist->files = NULL; return flist; } /* * free up all elements in a flist */ void flist_free(struct file_list *flist) { int i; for ( i = 0; i < flist->count; i++ ) { free_file(flist->files[i]); free(flist->files[i]); } /* FIXME: I don't think we generally need to blank the flist * since it's about to be freed. This will just cause more * memory traffic. If you want a freed-memory debugger, you * know where to get it. */ memset((char *) flist->files, 0, sizeof(flist->files[0]) * flist->count); free(flist->files); if ( flist->outBuf ) free(flist->outBuf); memset((char *) flist, 0, sizeof(*flist)); free(flist); } /* * This routine ensures we don't have any duplicate names in our file list. * duplicate names can cause corruption because of the pipelining */ void clean_flist(struct file_list *flist, int strip_root) { int i; if (!flist || flist->count == 0) return; qsort(flist->files, flist->count, sizeof(flist->files[0]), (int (*)()) file_compare); for (i = 1; i < flist->count; i++) { if (flist->files[i]->basename && flist->files[i - 1]->basename && strcmp(f_name(flist->files[i]), f_name(flist->files[i - 1])) == 0) { /* it's not great that the flist knows the semantics of the * file memory usage, but i'd rather not add a flag byte * to that struct. XXX can i use a bit in the flags field? */ free_file(flist->files[i]); } } /* FIXME: There is a bug here when filenames are repeated more * than once, because we don't handle freed files when doing * the comparison. */ if (strip_root) { /* we need to strip off the root directory in the case of relative paths, but this must be done _after_ the sorting phase */ for (i = 0; i < flist->count; i++) { if (flist->files[i]->dirname && flist->files[i]->dirname[0] == '/') { memmove(&flist->files[i]->dirname[0], &flist->files[i]->dirname[1], strlen(flist->files[i]->dirname)); } if (flist->files[i]->dirname && !flist->files[i]->dirname[0]) { flist->files[i]->dirname = NULL; } } } /* * reset the flag so it can be used by the rsync code */ for (i = 0; i < flist->count; i++) { flist->files[i]->flags = 0; } } void clean_fname(char *name) { char *p; int l; int modified = 1; if (!name) return; while (modified) { modified = 0; if ((p=strstr(name,"/./"))) { modified = 1; while (*p) { p[0] = p[2]; p++; } } if ((p=strstr(name,"//"))) { modified = 1; while (*p) { p[0] = p[1]; p++; } } if (strncmp(p=name,"./",2) == 0) { modified = 1; do { p[0] = p[2]; } while (*p++); } l = strlen(p=name); if (l > 1 && p[l-1] == '/') { modified = 1; p[l-1] = 0; } } } static void readfd(struct file_list *f, unsigned char *buffer, size_t N) { if ( f->inError || f->inPosn + N > f->inLen ) { memset(buffer, 0, N); f->inError = 1; return; } memcpy(buffer, f->inBuf + f->inPosn, N); f->inPosn += N; } int32 read_int(struct file_list *f) { unsigned char b[4]; int32 ret; readfd(f,b,4); ret = b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24; if (ret == (int32)0xffffffff) return -1; return ret; } double read_longint(struct file_list *f) { char b[8]; int32 ret = read_int(f); double d; if (ret != (int32)0xffffffff) { return ret; } d = (uint32)read_int(f); d += ((uint32)read_int(f)) * 65536.0 * 65536.0; return d; } void read_buf(struct file_list *f,char *buf,size_t len) { readfd(f, buf, len); } void read_sbuf(struct file_list *f,char *buf,size_t len) { read_buf(f,buf,len); buf[len] = 0; } unsigned char read_byte(struct file_list *f) { unsigned char c; read_buf (f, (char *)&c, 1); return c; } void receive_file_entry(struct file_list *f, struct file_struct **fptr, unsigned flags) { char thisname[MAXPATHLEN]; char lastname[MAXPATHLEN]; unsigned int l1 = 0, l2 = 0; char *p; char *lastdir = NULL; struct file_struct file, *filePtr; memset((char *) &file, 0, sizeof(file)); if (flags & SAME_NAME) l1 = read_byte(f); if (flags & LONG_NAME) l2 = read_int(f); else l2 = read_byte(f); if (l2 >= MAXPATHLEN - l1) { printf("overflow: flags=0x%x l1=%d l2=%d, lastname=%s\n", flags, l1, l2, f->lastname); f->fatalError = 1; return; } strlcpy(thisname, f->lastname, l1 + 1); read_sbuf(f, &thisname[l1], l2); thisname[l1 + l2] = 0; strlcpy(lastname, thisname, MAXPATHLEN); lastname[MAXPATHLEN - 1] = 0; clean_fname(thisname); #if 0 if (sanitize_paths) { sanitize_path(thisname, NULL); } #endif if ((p = strrchr(thisname, '/'))) { *p = 0; if ( f->lastdir && strcmp(thisname, f->lastdir) == 0) { file.dirname = f->lastdir; file.dirnameAlloc = 0; } else { file.dirname = strdup(thisname); lastdir = file.dirname; file.dirnameAlloc = 1; } file.basename = strdup(p + 1); } else { file.dirname = NULL; file.basename = strdup(thisname); } if (!file.basename) { printf("out of memory on basename\n"); free_file(&file); f->fatalError = 1; return; } file.flags = flags; file.length = read_longint(f); file.modtime = (flags & SAME_TIME) ? f->last_time : (time_t) read_int(f); file.mode = (flags & SAME_MODE) ? f->last_mode : from_wire_mode(read_int(f)); if (f->preserve_uid) file.uid = (flags & SAME_UID) ? f->last_uid : (uid_t) read_int(f); if (f->preserve_gid) file.gid = (flags & SAME_GID) ? f->last_gid : (gid_t) read_int(f); if (f->preserve_devices && IS_DEVICE(file.mode)) file.rdev = (flags & SAME_RDEV) ? f->last_rdev : (dev_t) read_int(f); if (f->preserve_links && S_ISLNK(file.mode)) { int l = read_int(f); if (l < 0) { printf("overflow on symlink: l=%d\n", l); f->fatalError = 1; free_file(&file); return; } file.link = (char *) malloc(l + 1); read_sbuf(f, file.link, l); #if 0 if (sanitize_paths) { sanitize_path(file.link, file.dirname); } #endif } if (f->preserve_hard_links && S_ISREG(file.mode)) { if (f->remote_version < 26) { file.dev = read_int(f); file.inode = read_int(f); } else { file.dev = read_longint(f); file.inode = read_longint(f); } } if ( f->always_checksum ) { file.sum = (char *) malloc(MD4_SUM_LENGTH); if ( f->remote_version < 21 ) { read_buf(f, file.sum, 2); } else { read_buf(f, file.sum, MD4_SUM_LENGTH); } } /* * It's important that we don't update anything in f before * this point. If we ran out of input bytes then we need to * resume after the caller appends more bytes. */ if ( f->inError ) { free_file(&file); return; } strlcpy(f->lastname, lastname, MAXPATHLEN); f->lastname[MAXPATHLEN - 1] = 0; if ( lastdir ) f->lastdir = lastdir; f->last_mode = file.mode; f->last_rdev = file.rdev; f->last_uid = file.uid; f->last_gid = file.gid; f->last_time = file.modtime; filePtr = (struct file_struct *) malloc(sizeof(file)); memcpy(filePtr, &file, sizeof(file)); *fptr = filePtr; } static void writefd(struct file_list *f, char *buf, size_t len) { if ( !f->outBuf ) { f->outLen = 32768 + len; f->outBuf = malloc(f->outLen); } else if ( f->outPosn + len > f->outLen ) { f->outLen = 32768 + f->outPosn + len; f->outBuf = realloc(f->outBuf, f->outLen); } memcpy(f->outBuf + f->outPosn, buf, len); f->outPosn += len; } static void write_int(struct file_list *f,int32 x) { char b[4]; b[0] = x >> 0; b[1] = x >> 8; b[2] = x >> 16; b[3] = x >> 24; writefd(f,b,4); } /* * Note: int64 may actually be a 32-bit type if ./configure couldn't find any * 64-bit types on this platform. */ static void write_longint(struct file_list *f, double x) { if (f->remote_version < 16 || x <= 0x7FFFFFFF) { write_int(f, (int)x); return; } write_int(f, (int32)0xFFFFFFFF); write_int(f, (uint32)(fmod(x, 65536.0 * 65536.0))); write_int(f, (uint32)(x / (65536.0 * 65536.0))); } static void write_buf(struct file_list *f,char *buf,size_t len) { writefd(f,buf,len); } /* write a string to the connection */ static void write_sbuf(struct file_list *f,char *buf) { write_buf(f, buf, strlen(buf)); } static void write_byte(struct file_list *f,unsigned char c) { write_buf(f,(char *)&c,1); } void send_file_entry(struct file_list *f, struct file_struct *file) { unsigned char flags; char *fname; int l1, l2; if ( !file ) { write_byte(f, 0); return; } fname = f_name(file); flags = 0; if (file->mode == f->last_mode) flags |= SAME_MODE; if (file->rdev == f->last_rdev) flags |= SAME_RDEV; if (file->uid == f->last_uid) flags |= SAME_UID; if (file->gid == f->last_gid) flags |= SAME_GID; if (file->modtime == f->last_time) flags |= SAME_TIME; for (l1 = 0; f->lastname[l1] && (fname[l1] == f->lastname[l1]) && (l1 < 255); l1++); l2 = strlen(fname) - l1; if (l1 > 0) flags |= SAME_NAME; if (l2 > 255) flags |= LONG_NAME; /* we must make sure we don't send a zero flags byte or the other end will terminate the flist transfer */ if (flags == 0 && !S_ISDIR(file->mode)) flags |= FLAG_DELETE; if (flags == 0) flags |= LONG_NAME; write_byte(f, flags); if (flags & SAME_NAME) write_byte(f, l1); if (flags & LONG_NAME) write_int(f, l2); else write_byte(f, l2); write_buf(f, fname + l1, l2); write_longint(f, file->length); if (!(flags & SAME_TIME)) write_int(f, (int) file->modtime); if (!(flags & SAME_MODE)) write_int(f, to_wire_mode(file->mode)); if (f->preserve_uid && !(flags & SAME_UID)) { write_int(f, (int) file->uid); } if (f->preserve_gid && !(flags & SAME_GID)) { write_int(f, (int) file->gid); } if (f->preserve_devices && IS_DEVICE(file->mode) && !(flags & SAME_RDEV)) write_int(f, (int) file->rdev); if (f->preserve_links && S_ISLNK(file->mode)) { write_int(f, strlen(file->link)); write_buf(f, file->link, strlen(file->link)); } if (f->preserve_hard_links && S_ISREG(file->mode)) { if (f->remote_version < 26) { /* 32-bit dev_t and ino_t */ write_int(f, (int) file->dev); write_int(f, (int) file->inode); } else { /* 64-bit dev_t and ino_t */ write_longint(f, file->dev); write_longint(f, file->inode); } } if (f->always_checksum) { if (f->remote_version < 21) { write_buf(f, file->sum, 2); } else { write_buf(f, file->sum, MD4_SUM_LENGTH); } } f->last_mode = file->mode; f->last_rdev = file->rdev; f->last_uid = file->uid; f->last_gid = file->gid; f->last_time = file->modtime; strlcpy(f->lastname, fname, MAXPATHLEN); f->lastname[MAXPATHLEN - 1] = 0; } int flistDecodeBytes(struct file_list *f, unsigned char *bytes, uint32 nBytes) { unsigned char flags; f->inBuf = bytes; f->inLen = nBytes; f->inFileStart = f->inPosn = 0; f->inError = 0; f->fatalError = 0; f->decodeDone = 0; for ( flags = read_byte(f); flags; flags = read_byte(f) ) { int i = f->count; flist_expand(f); receive_file_entry(f, &f->files[i], flags); if ( f->inError ) { /* printf("Returning on input error, posn = %d\n", f->inPosn); */ break; } #if 0 if (S_ISREG(f->files[i]->mode)) stats.total_size += f->files[i]->length; #endif f->count++; f->inFileStart = f->inPosn; } if ( f->fatalError ) { return -1; } else if ( f->inError ) { return f->inFileStart; } else { f->decodeDone = 1; return f->inPosn; } }