/* Copyright (C) Andrew Tridgell 1996 Copyright (C) Paul Mackerras 1996 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. */ /* generate and receive file lists */ #include "rsync.h" extern int verbose; extern int am_server; extern int always_checksum; extern off_t total_size; extern int cvs_exclude; extern int one_file_system; extern int make_backups; extern int preserve_links; extern int preserve_perms; extern int preserve_devices; extern int preserve_uid; extern int preserve_gid; extern int preserve_times; static char **local_exclude_list = NULL; static void clean_fname(char *name); /* This function is used to check if a file should be included/excluded from the list of files based on its name and type etc */ static int match_file_name(char *fname,struct stat *st) { if (check_exclude(fname,local_exclude_list)) { if (verbose > 2) fprintf(stderr,"excluding file %s\n",fname); return 0; } return 1; } /* used by the one_file_system code */ static dev_t filesystem_dev; static void set_filesystem(char *fname) { struct stat st; if (lstat(fname,&st) != 0) return; filesystem_dev = st.st_dev; } static void send_directory(int f,struct file_list *flist,char *dir); static char *flist_dir = NULL; static void send_file_entry(struct file_struct *file,int f) { if (f == -1) return; write_int(f,strlen(file->name)); write_buf(f,file->name,strlen(file->name)); write_int(f,(int)file->modtime); write_int(f,(int)file->length); write_int(f,(int)file->mode); if (preserve_uid) write_int(f,(int)file->uid); if (preserve_gid) write_int(f,(int)file->gid); if (preserve_devices) { if (verbose > 2) fprintf(stderr,"dev=0x%x\n",(int)file->dev); write_int(f,(int)file->dev); } #if SUPPORT_LINKS if (preserve_links && S_ISLNK(file->mode)) { write_int(f,strlen(file->link)); write_buf(f,file->link,strlen(file->link)); } #endif if (always_checksum) { write_buf(f,file->sum,SUM_LENGTH); } } static struct file_struct *make_file(int recurse,char *fname) { static struct file_struct file; struct stat st; char sum[SUM_LENGTH]; bzero(sum,SUM_LENGTH); if (lstat(fname,&st) != 0) { fprintf(stderr,"%s: %s\n", fname,strerror(errno)); return NULL; } if (S_ISDIR(st.st_mode) && !recurse) { fprintf(stderr,"skipping directory %s\n",fname); return NULL; } if (one_file_system && st.st_dev != filesystem_dev) return NULL; if (!match_file_name(fname,&st)) return NULL; if (verbose > 2) fprintf(stderr,"make_file(%s)\n",fname); file.name = strdup(fname); file.modtime = st.st_mtime; file.length = st.st_size; file.mode = st.st_mode; file.uid = st.st_uid; file.gid = st.st_gid; #ifdef HAVE_ST_RDEV file.dev = st.st_rdev; #endif #if SUPPORT_LINKS if (S_ISLNK(st.st_mode)) { int l; char lnk[MAXPATHLEN]; if ((l=readlink(fname,lnk,MAXPATHLEN-1)) == -1) { fprintf(stderr,"readlink %s : %s\n",fname,strerror(errno)); return NULL; } lnk[l] = 0; file.link = strdup(lnk); } #endif if (always_checksum && S_ISREG(st.st_mode)) { file_checksum(fname,file.sum,st.st_size); } if (flist_dir) file.dir = strdup(flist_dir); else file.dir = NULL; if (!S_ISDIR(st.st_mode)) total_size += st.st_size; return &file; } static void send_file_name(int f,struct file_list *flist, int recurse,char *fname) { struct file_struct *file; file = make_file(recurse,fname); if (!file) return; if (flist->count >= flist->malloced) { flist->malloced += 100; flist->files = (struct file_struct *)realloc(flist->files, sizeof(flist->files[0])* flist->malloced); if (!flist->files) out_of_memory("send_file_name"); } if (strcmp(file->name,".") && strcmp(file->name,"/")) { flist->files[flist->count++] = *file; send_file_entry(file,f); } if (S_ISDIR(file->mode) && recurse) { char **last_exclude_list = local_exclude_list; send_directory(f,flist,file->name); local_exclude_list = last_exclude_list; return; } } static void send_directory(int f,struct file_list *flist,char *dir) { DIR *d; struct dirent *di; char fname[MAXPATHLEN]; int l; char *p; d = opendir(dir); if (!d) { fprintf(stderr,"%s: %s\n", dir,strerror(errno)); return; } strcpy(fname,dir); l = strlen(fname); if (fname[l-1] != '/') strcat(fname,"/"); p = fname + strlen(fname); if (cvs_exclude) { strcpy(p,".cvsignore"); local_exclude_list = make_exclude_list(fname,NULL,0); } for (di=readdir(d); di; di=readdir(d)) { if (strcmp(di->d_name,".")==0 || strcmp(di->d_name,"..")==0) continue; strcpy(p,di->d_name); send_file_name(f,flist,1,fname); } closedir(d); } struct file_list *send_file_list(int f,int recurse,int argc,char *argv[]) { int i,l; struct stat st; char *p,*dir; char dbuf[MAXPATHLEN]; struct file_list *flist; if (verbose && recurse) { fprintf(am_server?stderr:stdout,"building file list ... "); fflush(am_server?stderr:stdout); } flist = (struct file_list *)malloc(sizeof(flist[0])); if (!flist) out_of_memory("send_file_list"); flist->count=0; flist->malloced = 100; flist->files = (struct file_struct *)malloc(sizeof(flist->files[0])* flist->malloced); if (!flist->files) out_of_memory("send_file_list"); for (i=0;i 2) fprintf(stderr,"recv_file_list starting\n"); flist = (struct file_list *)malloc(sizeof(flist[0])); if (!flist) goto oom; flist->count=0; flist->malloced=100; flist->files = (struct file_struct *)malloc(sizeof(flist->files[0])* flist->malloced); if (!flist->files) goto oom; for (l=read_int(f); l; l=read_int(f)) { int i = flist->count; if (i >= flist->malloced) { flist->malloced += 100; flist->files =(struct file_struct *)realloc(flist->files, sizeof(flist->files[0])* flist->malloced); if (!flist->files) goto oom; } flist->files[i].name = (char *)malloc(l+1); if (!flist->files[i].name) goto oom; read_buf(f,flist->files[i].name,l); flist->files[i].name[l] = 0; flist->files[i].modtime = (time_t)read_int(f); flist->files[i].length = (off_t)read_int(f); flist->files[i].mode = (mode_t)read_int(f); if (preserve_uid) flist->files[i].uid = (uid_t)read_int(f); if (preserve_gid) flist->files[i].gid = (gid_t)read_int(f); if (preserve_devices) flist->files[i].dev = (dev_t)read_int(f); #if SUPPORT_LINKS if (preserve_links && S_ISLNK(flist->files[i].mode)) { int l = read_int(f); flist->files[i].link = (char *)malloc(l+1); read_buf(f,flist->files[i].link,l); flist->files[i].link[l] = 0; } #endif if (always_checksum) read_buf(f,flist->files[i].sum,SUM_LENGTH); if (S_ISREG(flist->files[i].mode)) total_size += flist->files[i].length; flist->count++; if (verbose > 2) fprintf(stderr,"recv_file_name(%s)\n",flist->files[i].name); } if (verbose > 2) fprintf(stderr,"received %d names\n",flist->count); clean_flist(flist); return flist; oom: out_of_memory("recv_file_list"); return NULL; /* not reached */ } static int flist_compare(struct file_struct *f1,struct file_struct *f2) { if (!f1->name && !f2->name) return 0; if (!f1->name) return -1; if (!f2->name) return 1; return strcmp(f1->name,f2->name); } int flist_find(struct file_list *flist,struct file_struct *f) { int low=0,high=flist->count; while (low != high) { int mid = (low+high)/2; int ret = flist_compare(&flist->files[mid],f); if (ret == 0) return mid; if (ret > 0) high=mid; else low=mid+1; } if (flist_compare(&flist->files[low],f) == 0) return low; return -1; } static 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; while (*p) { p[0] = p[2]; p++; } } l = strlen(p=name); if (l > 1 && p[l-1] == '/') { modified = 1; p[l-1] = 0; } } } /* * 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 i; if (!flist || flist->count == 0) return; for (i=0;icount;i++) { clean_fname(flist->files[i].name); } qsort(flist->files,flist->count, sizeof(flist->files[0]), (int (*)())flist_compare); for (i=1;icount;i++) { if (flist->files[i].name && strcmp(flist->files[i].name,flist->files[i-1].name) == 0) { if (verbose > 1 && !am_server) fprintf(stderr,"removing duplicate name %s from file list\n", flist->files[i].name); free(flist->files[i-1].name); flist->files[i-1].name = NULL; } } }