more byte efficient flist routines
[rsync/rsync.git] / flist.c
CommitLineData
c627d613
AT
1/*
2 Copyright (C) Andrew Tridgell 1996
3 Copyright (C) Paul Mackerras 1996
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18*/
19
20/* generate and receive file lists */
21
22#include "rsync.h"
23
24extern int verbose;
25extern int am_server;
26extern int always_checksum;
27extern off_t total_size;
28
29extern int cvs_exclude;
30
31extern int one_file_system;
32extern int make_backups;
33extern int preserve_links;
34extern int preserve_perms;
35extern int preserve_devices;
36extern int preserve_uid;
37extern int preserve_gid;
38extern int preserve_times;
39
40static char **local_exclude_list = NULL;
41
42static void clean_fname(char *name);
43
44
45/*
46 This function is used to check if a file should be included/excluded
47 from the list of files based on its name and type etc
48 */
49static int match_file_name(char *fname,struct stat *st)
50{
51 if (check_exclude(fname,local_exclude_list)) {
52 if (verbose > 2)
53 fprintf(stderr,"excluding file %s\n",fname);
54 return 0;
55 }
56 return 1;
57}
58
59/* used by the one_file_system code */
60static dev_t filesystem_dev;
61
62static void set_filesystem(char *fname)
63{
64 struct stat st;
65 if (lstat(fname,&st) != 0) return;
66 filesystem_dev = st.st_dev;
67}
68
69
70static void send_directory(int f,struct file_list *flist,char *dir);
71
72static char *flist_dir = NULL;
73
182dca5c
AT
74#define FILE_VALID 1
75#define SAME_MODE (1<<1)
76#define SAME_DEV (1<<2)
77#define SAME_UID (1<<3)
78#define SAME_GID (1<<4)
79#define SAME_DIR (1<<5)
80
c627d613
AT
81static void send_file_entry(struct file_struct *file,int f)
82{
182dca5c
AT
83 unsigned char flags;
84 static mode_t last_mode=0;
85 static dev_t last_dev=0;
86 static uid_t last_uid=0;
87 static gid_t last_gid=0;
88 static char lastdir[MAXPATHLEN]="";
89 char *p=NULL;
90
c627d613
AT
91 if (f == -1) return;
92
182dca5c
AT
93 if (!file) {
94 write_byte(f,0);
95 return;
96 }
97
98 flags = FILE_VALID;
99
100 if (file->mode == last_mode) flags |= SAME_MODE;
101 if (file->dev == last_dev) flags |= SAME_DEV;
102 if (file->uid == last_uid) flags |= SAME_UID;
103 if (file->gid == last_gid) flags |= SAME_GID;
104
105 if (strncmp(file->name,lastdir,strlen(lastdir)) == 0) {
106 flags |= SAME_DIR;
107 p = file->name + strlen(lastdir);
108 } else {
109 p = file->name;
110 }
111
112 write_byte(f,flags);
113 write_byte(f,strlen(p));
114 write_buf(f,p,strlen(p));
c627d613
AT
115 write_int(f,(int)file->modtime);
116 write_int(f,(int)file->length);
182dca5c
AT
117 if (!(flags & SAME_MODE))
118 write_int(f,(int)file->mode);
119 if (preserve_uid && !(flags & SAME_UID))
c627d613 120 write_int(f,(int)file->uid);
182dca5c 121 if (preserve_gid && !(flags & SAME_GID))
c627d613 122 write_int(f,(int)file->gid);
182dca5c 123 if (preserve_devices && IS_DEVICE(file->mode) && !(flags & SAME_DEV))
c627d613 124 write_int(f,(int)file->dev);
c627d613
AT
125
126#if SUPPORT_LINKS
127 if (preserve_links && S_ISLNK(file->mode)) {
128 write_int(f,strlen(file->link));
129 write_buf(f,file->link,strlen(file->link));
130 }
131#endif
132
133 if (always_checksum) {
134 write_buf(f,file->sum,SUM_LENGTH);
135 }
182dca5c
AT
136
137 last_mode = file->mode;
138 last_dev = file->dev;
139 last_uid = file->uid;
140 last_gid = file->gid;
141 p = strrchr(file->name,'/');
142 if (p) {
143 int l = (int)(p - file->name) + 1;
144 strncpy(lastdir,file->name,l);
145 lastdir[l] = 0;
146 } else {
147 strcpy(lastdir,"");
148 }
149}
150
151
152
153static void receive_file_entry(struct file_struct *file,
154 unsigned char flags,int f)
155{
156 static mode_t last_mode=0;
157 static dev_t last_dev=0;
158 static uid_t last_uid=0;
159 static gid_t last_gid=0;
160 static char lastdir[MAXPATHLEN]="";
161 char *p=NULL;
162 int l1,l2;
163
164 l1 = read_byte(f);
165 if (flags & SAME_DIR)
166 l2 = strlen(lastdir);
167 else
168 l2 = 0;
169
170 file->name = (char *)malloc(l1+l2+1);
171 if (!file->name) out_of_memory("receive_file_entry");
172
173 strncpy(file->name,lastdir,l2);
174 read_buf(f,file->name+l2,l1);
175 file->name[l1+l2] = 0;
176
177 file->modtime = (time_t)read_int(f);
178 file->length = (off_t)read_int(f);
179 file->mode = (flags & SAME_MODE) ? last_mode : (mode_t)read_int(f);
180 if (preserve_uid)
181 file->uid = (flags & SAME_UID) ? last_uid : (uid_t)read_int(f);
182 if (preserve_gid)
183 file->gid = (flags & SAME_GID) ? last_gid : (gid_t)read_int(f);
184 if (preserve_devices && IS_DEVICE(file->mode))
185 file->dev = (flags & SAME_DEV) ? last_dev : (dev_t)read_int(f);
186
187#if SUPPORT_LINKS
188 if (preserve_links && S_ISLNK(file->mode)) {
189 int l = read_int(f);
190 file->link = (char *)malloc(l+1);
191 if (!file->link) out_of_memory("receive_file_entry");
192 read_buf(f,file->link,l);
193 file->link[l] = 0;
194 }
195#endif
196
197 if (always_checksum)
198 read_buf(f,file->sum,SUM_LENGTH);
199
200 last_mode = file->mode;
201 last_dev = file->dev;
202 last_uid = file->uid;
203 last_gid = file->gid;
204 p = strrchr(file->name,'/');
205 if (p) {
206 int l = (int)(p - file->name) + 1;
207 strncpy(lastdir,file->name,l);
208 lastdir[l] = 0;
209 } else {
210 strcpy(lastdir,"");
211 }
c627d613
AT
212}
213
214
215static struct file_struct *make_file(int recurse,char *fname)
216{
217 static struct file_struct file;
218 struct stat st;
219 char sum[SUM_LENGTH];
220
221 bzero(sum,SUM_LENGTH);
222
223 if (lstat(fname,&st) != 0) {
224 fprintf(stderr,"%s: %s\n",
225 fname,strerror(errno));
226 return NULL;
227 }
228
229 if (S_ISDIR(st.st_mode) && !recurse) {
230 fprintf(stderr,"skipping directory %s\n",fname);
231 return NULL;
232 }
233
234 if (one_file_system && st.st_dev != filesystem_dev)
235 return NULL;
236
237 if (!match_file_name(fname,&st))
238 return NULL;
239
240 if (verbose > 2)
241 fprintf(stderr,"make_file(%s)\n",fname);
242
243 file.name = strdup(fname);
244 file.modtime = st.st_mtime;
245 file.length = st.st_size;
246 file.mode = st.st_mode;
247 file.uid = st.st_uid;
248 file.gid = st.st_gid;
249#ifdef HAVE_ST_RDEV
250 file.dev = st.st_rdev;
251#endif
252
253#if SUPPORT_LINKS
254 if (S_ISLNK(st.st_mode)) {
255 int l;
256 char lnk[MAXPATHLEN];
257 if ((l=readlink(fname,lnk,MAXPATHLEN-1)) == -1) {
258 fprintf(stderr,"readlink %s : %s\n",fname,strerror(errno));
259 return NULL;
260 }
261 lnk[l] = 0;
262 file.link = strdup(lnk);
263 }
264#endif
265
266 if (always_checksum && S_ISREG(st.st_mode)) {
267 file_checksum(fname,file.sum,st.st_size);
268 }
269
270 if (flist_dir)
271 file.dir = strdup(flist_dir);
272 else
273 file.dir = NULL;
274
275 if (!S_ISDIR(st.st_mode))
276 total_size += st.st_size;
277
278 return &file;
279}
280
281
282
283static void send_file_name(int f,struct file_list *flist,
284 int recurse,char *fname)
285{
286 struct file_struct *file;
287
288 file = make_file(recurse,fname);
289
290 if (!file) return;
291
292 if (flist->count >= flist->malloced) {
293 flist->malloced += 100;
294 flist->files = (struct file_struct *)realloc(flist->files,
295 sizeof(flist->files[0])*
296 flist->malloced);
297 if (!flist->files)
298 out_of_memory("send_file_name");
299 }
300
301 if (strcmp(file->name,".") && strcmp(file->name,"/")) {
302 flist->files[flist->count++] = *file;
303 send_file_entry(file,f);
304 }
305
306 if (S_ISDIR(file->mode) && recurse) {
307 char **last_exclude_list = local_exclude_list;
308 send_directory(f,flist,file->name);
309 local_exclude_list = last_exclude_list;
310 return;
311 }
312}
313
314
315
316static void send_directory(int f,struct file_list *flist,char *dir)
317{
318 DIR *d;
319 struct dirent *di;
320 char fname[MAXPATHLEN];
321 int l;
322 char *p;
323
324 d = opendir(dir);
325 if (!d) {
326 fprintf(stderr,"%s: %s\n",
327 dir,strerror(errno));
328 return;
329 }
330
331 strcpy(fname,dir);
332 l = strlen(fname);
333 if (fname[l-1] != '/')
334 strcat(fname,"/");
335 p = fname + strlen(fname);
336
337 if (cvs_exclude) {
338 strcpy(p,".cvsignore");
339 local_exclude_list = make_exclude_list(fname,NULL,0);
340 }
341
342 for (di=readdir(d); di; di=readdir(d)) {
343 if (strcmp(di->d_name,".")==0 ||
344 strcmp(di->d_name,"..")==0)
345 continue;
346 strcpy(p,di->d_name);
347 send_file_name(f,flist,1,fname);
348 }
349
350 closedir(d);
351}
352
353
354
355struct file_list *send_file_list(int f,int recurse,int argc,char *argv[])
356{
357 int i,l;
358 struct stat st;
359 char *p,*dir;
360 char dbuf[MAXPATHLEN];
361 struct file_list *flist;
362
363 if (verbose && recurse) {
364 fprintf(am_server?stderr:stdout,"building file list ... ");
365 fflush(am_server?stderr:stdout);
366 }
367
368 flist = (struct file_list *)malloc(sizeof(flist[0]));
369 if (!flist) out_of_memory("send_file_list");
370
371 flist->count=0;
372 flist->malloced = 100;
373 flist->files = (struct file_struct *)malloc(sizeof(flist->files[0])*
374 flist->malloced);
375 if (!flist->files) out_of_memory("send_file_list");
376
377 for (i=0;i<argc;i++) {
378 char fname2[MAXPATHLEN];
379 char *fname = fname2;
380
381 strcpy(fname,argv[i]);
382
383 l = strlen(fname);
384 if (l != 1 && fname[l-1] == '/') {
385 strcat(fname,".");
386 }
387
388 if (lstat(fname,&st) != 0) {
389 fprintf(stderr,"%s : %s\n",fname,strerror(errno));
390 continue;
391 }
392
393 if (S_ISDIR(st.st_mode) && !recurse) {
394 fprintf(stderr,"skipping directory %s\n",fname);
395 continue;
396 }
397
398 dir = NULL;
399 p = strrchr(fname,'/');
400 if (p) {
401 *p = 0;
402 dir = fname;
403 fname = p+1;
404 }
405 if (!*fname)
406 fname = ".";
407
408 if (dir && *dir) {
409 if (getcwd(dbuf,MAXPATHLEN-1) == NULL) {
410 fprintf(stderr,"getwd : %s\n",strerror(errno));
411 exit(1);
412 }
413 if (chdir(dir) != 0) {
414 fprintf(stderr,"chdir %s : %s\n",dir,strerror(errno));
415 continue;
416 }
417 flist_dir = dir;
418 if (one_file_system)
419 set_filesystem(fname);
420 send_file_name(f,flist,recurse,fname);
421 flist_dir = NULL;
422 if (chdir(dbuf) != 0) {
423 fprintf(stderr,"chdir %s : %s\n",dbuf,strerror(errno));
424 exit(1);
425 }
426 continue;
427 }
428
429 if (one_file_system)
430 set_filesystem(fname);
431 send_file_name(f,flist,recurse,fname);
432 }
433
434 if (f != -1) {
182dca5c 435 send_file_entry(NULL,f);
c627d613
AT
436 write_flush(f);
437 }
438
439 clean_flist(flist);
440
441 if (verbose && recurse)
442 fprintf(am_server?stderr:stdout,"done\n");
443
444 return flist;
445}
446
447
448struct file_list *recv_file_list(int f)
449{
c627d613 450 struct file_list *flist;
182dca5c 451 unsigned char flags;
c627d613
AT
452
453 if (verbose > 2)
454 fprintf(stderr,"recv_file_list starting\n");
455
456 flist = (struct file_list *)malloc(sizeof(flist[0]));
457 if (!flist)
458 goto oom;
459
460 flist->count=0;
461 flist->malloced=100;
462 flist->files = (struct file_struct *)malloc(sizeof(flist->files[0])*
463 flist->malloced);
464 if (!flist->files)
465 goto oom;
466
467
182dca5c 468 for (flags=read_byte(f); flags; flags=read_byte(f)) {
c627d613
AT
469 int i = flist->count;
470
471 if (i >= flist->malloced) {
472 flist->malloced += 100;
473 flist->files =(struct file_struct *)realloc(flist->files,
474 sizeof(flist->files[0])*
475 flist->malloced);
476 if (!flist->files)
477 goto oom;
478 }
479
182dca5c 480 receive_file_entry(&flist->files[i],flags,f);
c627d613
AT
481
482 if (S_ISREG(flist->files[i].mode))
483 total_size += flist->files[i].length;
484
485 flist->count++;
486
487 if (verbose > 2)
488 fprintf(stderr,"recv_file_name(%s)\n",flist->files[i].name);
489 }
490
491
492 if (verbose > 2)
493 fprintf(stderr,"received %d names\n",flist->count);
494
495 clean_flist(flist);
496
497 return flist;
498
499oom:
500 out_of_memory("recv_file_list");
501 return NULL; /* not reached */
502}
503
504
505static int flist_compare(struct file_struct *f1,struct file_struct *f2)
506{
507 if (!f1->name && !f2->name) return 0;
508 if (!f1->name) return -1;
509 if (!f2->name) return 1;
510 return strcmp(f1->name,f2->name);
511}
512
513
514int flist_find(struct file_list *flist,struct file_struct *f)
515{
516 int low=0,high=flist->count;
517
518 while (low != high) {
519 int mid = (low+high)/2;
520 int ret = flist_compare(&flist->files[mid],f);
521 if (ret == 0) return mid;
522 if (ret > 0)
523 high=mid;
524 else
525 low=mid+1;
526 }
527 if (flist_compare(&flist->files[low],f) == 0)
528 return low;
529 return -1;
530}
531
532
533static void clean_fname(char *name)
534{
535 char *p;
536 int l;
537 int modified = 1;
538
539 if (!name) return;
540
541 while (modified) {
542 modified = 0;
543
544 if ((p=strstr(name,"/./"))) {
545 modified = 1;
546 while (*p) {
547 p[0] = p[2];
548 p++;
549 }
550 }
551
552 if ((p=strstr(name,"//"))) {
553 modified = 1;
554 while (*p) {
555 p[0] = p[1];
556 p++;
557 }
558 }
559
560 if (strncmp(p=name,"./",2) == 0) {
561 modified = 1;
562 while (*p) {
563 p[0] = p[2];
564 p++;
565 }
566 }
567
568 l = strlen(p=name);
569 if (l > 1 && p[l-1] == '/') {
570 modified = 1;
571 p[l-1] = 0;
572 }
573 }
574}
575
576
577/*
578 * This routine ensures we don't have any duplicate names in our file list.
579 * duplicate names can cause corruption because of the pipelining
580 */
581void clean_flist(struct file_list *flist)
582{
583 int i;
584
585 if (!flist || flist->count == 0)
586 return;
587
588 for (i=0;i<flist->count;i++) {
589 clean_fname(flist->files[i].name);
590 }
591
592 qsort(flist->files,flist->count,
593 sizeof(flist->files[0]),
594 (int (*)())flist_compare);
595
596 for (i=1;i<flist->count;i++) {
597 if (flist->files[i].name &&
598 strcmp(flist->files[i].name,flist->files[i-1].name) == 0) {
599 if (verbose > 1 && !am_server)
600 fprintf(stderr,"removing duplicate name %s from file list\n",
601 flist->files[i].name);
602 free(flist->files[i-1].name);
603 flist->files[i-1].name = NULL;
604 }
605 }
606}
607