r15141: Fix for #3592 inspired by Justin Best <justinb@pdxmission.org>.
[kai/samba.git] / source / client / clitar.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Tar Extensions
4    Copyright (C) Ricky Poulten 1995-1998
5    Copyright (C) Richard Sharpe 1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21 /* The following changes developed by Richard Sharpe for Canon Information
22    Systems Research Australia (CISRA)
23
24    1. Restore can now restore files with long file names
25    2. Save now saves directory information so that we can restore 
26       directory creation times
27    3. tar now accepts both UNIX path names and DOS path names. I prefer
28       those lovely /'s to those UGLY \'s :-)
29    4. the files to exclude can be specified as a regular expression by adding
30       an r flag to the other tar flags. Eg:
31
32          -TcrX file.tar "*.(obj|exe)"
33
34       will skip all .obj and .exe files
35 */
36
37
38 #include "includes.h"
39 #include "clitar.h"
40 #include "client/client_proto.h"
41
42 static int clipfind(char **aret, int ret, char *tok);
43
44 typedef struct file_info_struct file_info2;
45
46 struct file_info_struct {
47         SMB_OFF_T size;
48         uint16 mode;
49         uid_t uid;
50         gid_t gid;
51         /* These times are normally kept in GMT */
52         time_t mtime;
53         time_t atime;
54         time_t ctime;
55         char *name;     /* This is dynamically allocate */
56
57         file_info2 *next, *prev;  /* Used in the stack ... */
58 };
59
60 typedef struct {
61         file_info2 *top;
62         int items;
63 } stack;
64
65 #define SEPARATORS " \t\n\r"
66 extern time_t newer_than;
67 extern struct cli_state *cli;
68
69 /* These defines are for the do_setrattr routine, to indicate
70  * setting and reseting of file attributes in the function call */
71 #define ATTRSET 1
72 #define ATTRRESET 0
73
74 static uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
75
76 #ifndef CLIENT_TIMEOUT
77 #define CLIENT_TIMEOUT (30*1000)
78 #endif
79
80 static char *tarbuf, *buffer_p;
81 static int tp, ntarf, tbufsiz;
82 static double ttarf;
83 /* Incremental mode */
84 static BOOL tar_inc=False;
85 /* Reset archive bit */
86 static BOOL tar_reset=False;
87 /* Include / exclude mode (true=include, false=exclude) */
88 static BOOL tar_excl=True;
89 /* use regular expressions for search on file names */
90 static BOOL tar_re_search=False;
91 /* Do not dump anything, just calculate sizes */
92 static BOOL dry_run=False;
93 /* Dump files with System attribute */
94 static BOOL tar_system=True;
95 /* Dump files with Hidden attribute */
96 static BOOL tar_hidden=True;
97 /* Be noisy - make a catalogue */
98 static BOOL tar_noisy=True;
99 static BOOL tar_real_noisy=False;  /* Don't want to be really noisy by default */
100
101 char tar_type='\0';
102 static char **cliplist=NULL;
103 static int clipn=0;
104 static BOOL must_free_cliplist = False;
105
106 extern file_info def_finfo;
107 extern BOOL lowercase;
108 extern uint16 cnum;
109 extern BOOL readbraw_supported;
110 extern int max_xmit;
111 extern pstring cur_dir;
112 extern int get_total_time_ms;
113 extern int get_total_size;
114
115 static int blocksize=20;
116 static int tarhandle;
117
118 static void writetarheader(int f,  const char *aname, SMB_BIG_UINT size, time_t mtime,
119                            const char *amode, unsigned char ftype);
120 static void do_atar(char *rname,char *lname,file_info *finfo1);
121 static void do_tar(file_info *finfo);
122 static void oct_it(SMB_BIG_UINT value, int ndgs, char *p);
123 static void fixtarname(char *tptr, const char *fp, size_t l);
124 static int dotarbuf(int f, char *b, int n);
125 static void dozerobuf(int f, int n);
126 static void dotareof(int f);
127 static void initarbuf(void);
128
129 /* restore functions */
130 static long readtarheader(union hblock *hb, file_info2 *finfo, char *prefix);
131 static long unoct(char *p, int ndgs);
132 static void do_tarput(void);
133 static void unfixtarname(char *tptr, char *fp, int l, BOOL first);
134
135 /*
136  * tar specific utitlities
137  */
138
139 /*******************************************************************
140 Create  a string of size size+1 (for the null)
141 *******************************************************************/
142
143 static char *string_create_s(int size)
144 {
145         char *tmp;
146
147         tmp = (char *)SMB_MALLOC(size+1);
148
149         if (tmp == NULL) {
150                 DEBUG(0, ("Out of memory in string_create_s\n"));
151         }
152
153         return(tmp);
154 }
155
156 /****************************************************************************
157 Write a tar header to buffer
158 ****************************************************************************/
159
160 static void writetarheader(int f, const char *aname, SMB_BIG_UINT size, time_t mtime,
161                            const char *amode, unsigned char ftype)
162 {
163         union hblock hb;
164         int i, chk, l;
165         char *jp;
166
167         DEBUG(5, ("WriteTarHdr, Type = %c, Size= %.0f, Name = %s\n", ftype, (double)size, aname));
168
169         memset(hb.dummy, 0, sizeof(hb.dummy));
170   
171         l=strlen(aname);
172         /* We will be prepending a '.' in fixtarheader so use +2 to
173          * take care of the . and terminating zero. JRA.
174          */
175         if (l+2 >= NAMSIZ) {
176                 /* write a GNU tar style long header */
177                 char *b;
178                 b = (char *)SMB_MALLOC(l+TBLOCK+100);
179                 if (!b) {
180                         DEBUG(0,("out of memory\n"));
181                         exit(1);
182                 }
183                 writetarheader(f, "/./@LongLink", l+2, 0, "     0 \0", 'L');
184                 memset(b, 0, l+TBLOCK+100);
185                 fixtarname(b, aname, l+2);
186                 i = strlen(b)+1;
187                 DEBUG(5, ("File name in tar file: %s, size=%d, \n", b, (int)strlen(b)));
188                 dotarbuf(f, b, TBLOCK*(((i-1)/TBLOCK)+1));
189                 SAFE_FREE(b);
190         }
191
192         fixtarname(hb.dbuf.name, aname, (l+2 >= NAMSIZ) ? NAMSIZ : l + 2);
193
194         if (lowercase)
195                 strlower_m(hb.dbuf.name);
196
197         /* write out a "standard" tar format header */
198
199         hb.dbuf.name[NAMSIZ-1]='\0';
200         safe_strcpy(hb.dbuf.mode, amode, sizeof(hb.dbuf.mode)-1);
201         oct_it((SMB_BIG_UINT)0, 8, hb.dbuf.uid);
202         oct_it((SMB_BIG_UINT)0, 8, hb.dbuf.gid);
203         oct_it((SMB_BIG_UINT) size, 13, hb.dbuf.size);
204         if (size > (SMB_BIG_UINT)077777777777LL) {    
205
206                 /* This is a non-POSIX compatible extention to store files
207                         greater than 8GB. */
208
209                 memset(hb.dbuf.size, 0, 4);
210                 hb.dbuf.size[0]=128;
211                 for (i = 8, jp=(char*)&size; i; i--)
212                         hb.dbuf.size[i+3] = *(jp++);
213         }
214         oct_it((SMB_BIG_UINT) mtime, 13, hb.dbuf.mtime);
215         memcpy(hb.dbuf.chksum, "        ", sizeof(hb.dbuf.chksum));
216         memset(hb.dbuf.linkname, 0, NAMSIZ);
217         hb.dbuf.linkflag=ftype;
218   
219         for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;)
220                 chk+=(0xFF & *jp++);
221
222         oct_it((SMB_BIG_UINT) chk, 8, hb.dbuf.chksum);
223         hb.dbuf.chksum[6] = '\0';
224
225         (void) dotarbuf(f, hb.dummy, sizeof(hb.dummy));
226 }
227
228 /****************************************************************************
229 Read a tar header into a hblock structure, and validate
230 ***************************************************************************/
231
232 static long readtarheader(union hblock *hb, file_info2 *finfo, char *prefix)
233 {
234         long chk, fchk;
235         int i;
236         char *jp;
237
238         /*
239          * read in a "standard" tar format header - we're not that interested
240          * in that many fields, though
241          */
242
243         /* check the checksum */
244         for (chk=0, i=sizeof(hb->dummy), jp=hb->dummy; --i>=0;)
245                 chk+=(0xFF & *jp++);
246
247         if (chk == 0)
248                 return chk;
249
250         /* compensate for blanks in chksum header */
251         for (i=sizeof(hb->dbuf.chksum), jp=hb->dbuf.chksum; --i>=0;)
252                 chk-=(0xFF & *jp++);
253
254         chk += ' ' * sizeof(hb->dbuf.chksum);
255
256         fchk=unoct(hb->dbuf.chksum, sizeof(hb->dbuf.chksum));
257
258         DEBUG(5, ("checksum totals chk=%ld fchk=%ld chksum=%s\n",
259                         chk, fchk, hb->dbuf.chksum));
260
261         if (fchk != chk) {
262                 DEBUG(0, ("checksums don't match %ld %ld\n", fchk, chk));
263                 dump_data(5, (char *)hb - TBLOCK, TBLOCK *3);
264                 return -1;
265         }
266
267         if ((finfo->name = string_create_s(strlen(prefix) + strlen(hb -> dbuf.name) + 3)) == NULL) {
268                 DEBUG(0, ("Out of space creating file_info2 for %s\n", hb -> dbuf.name));
269                 return(-1);
270         }
271
272         safe_strcpy(finfo->name, prefix, strlen(prefix) + strlen(hb -> dbuf.name) + 3);
273
274         /* use l + 1 to do the null too; do prefix - prefcnt to zap leading slash */
275         unfixtarname(finfo->name + strlen(prefix), hb->dbuf.name,
276                 strlen(hb->dbuf.name) + 1, True);
277
278         /* can't handle some links at present */
279         if ((hb->dbuf.linkflag != '0') && (hb -> dbuf.linkflag != '5')) {
280                 if (hb->dbuf.linkflag == 0) {
281                         DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n",
282                                 finfo->name));
283                 } else { 
284                         if (hb -> dbuf.linkflag == 'L') { /* We have a longlink */
285                                 /* Do nothing here at the moment. do_tarput will handle this
286                                         as long as the longlink gets back to it, as it has to advance 
287                                         the buffer pointer, etc */
288                         } else {
289                                 DEBUG(0, ("this tar file appears to contain some kind \
290 of link other than a GNUtar Longlink - ignoring\n"));
291                                 return -2;
292                         }
293                 }
294         }
295     
296         if ((unoct(hb->dbuf.mode, sizeof(hb->dbuf.mode)) & S_IFDIR) ||
297                                 (*(finfo->name+strlen(finfo->name)-1) == '\\')) {
298                 finfo->mode=aDIR;
299         } else {
300                 finfo->mode=0; /* we don't care about mode at the moment, we'll
301                                 * just make it a regular file */
302         }
303
304         /*
305          * Bug fix by richard@sj.co.uk
306          *
307          * REC: restore times correctly (as does tar)
308          * We only get the modification time of the file; set the creation time
309          * from the mod. time, and the access time to current time
310          */
311         finfo->mtime = finfo->ctime = strtol(hb->dbuf.mtime, NULL, 8);
312         finfo->atime = time(NULL);
313         finfo->size = unoct(hb->dbuf.size, sizeof(hb->dbuf.size));
314
315         return True;
316 }
317
318 /****************************************************************************
319 Write out the tar buffer to tape or wherever
320 ****************************************************************************/
321
322 static int dotarbuf(int f, char *b, int n)
323 {
324         int fail=1, writ=n;
325
326         if (dry_run) {
327                 return writ;
328         }
329         /* This routine and the next one should be the only ones that do write()s */
330         if (tp + n >= tbufsiz) {
331                 int diff;
332
333                 diff=tbufsiz-tp;
334                 memcpy(tarbuf + tp, b, diff);
335                 fail=fail && (1+write(f, tarbuf, tbufsiz));
336                 n-=diff;
337                 b+=diff;
338                 tp=0;
339
340                 while (n >= tbufsiz) {
341                         fail=fail && (1 + write(f, b, tbufsiz));
342                         n-=tbufsiz;
343                         b+=tbufsiz;
344                 }
345         }
346
347         if (n>0) {
348                 memcpy(tarbuf+tp, b, n);
349                 tp+=n;
350         }
351
352         return(fail ? writ : 0);
353 }
354
355 /****************************************************************************
356 Write zeros to buffer / tape
357 ****************************************************************************/
358
359 static void dozerobuf(int f, int n)
360 {
361         /* short routine just to write out n zeros to buffer -
362          * used to round files to nearest block
363          * and to do tar EOFs */
364
365         if (dry_run)
366                 return;
367   
368         if (n+tp >= tbufsiz) {
369                 memset(tarbuf+tp, 0, tbufsiz-tp);
370                 write(f, tarbuf, tbufsiz);
371                 memset(tarbuf, 0, (tp+=n-tbufsiz));
372         } else {
373                 memset(tarbuf+tp, 0, n);
374                 tp+=n;
375         }
376 }
377
378 /****************************************************************************
379 Malloc tape buffer
380 ****************************************************************************/
381
382 static void initarbuf(void)
383 {
384         /* initialize tar buffer */
385         tbufsiz=blocksize*TBLOCK;
386         tarbuf=SMB_MALLOC(tbufsiz);      /* FIXME: We might not get the buffer */
387
388         /* reset tar buffer pointer and tar file counter and total dumped */
389         tp=0; ntarf=0; ttarf=0;
390 }
391
392 /****************************************************************************
393 Write two zero blocks at end of file
394 ****************************************************************************/
395
396 static void dotareof(int f)
397 {
398         SMB_STRUCT_STAT stbuf;
399         /* Two zero blocks at end of file, write out full buffer */
400
401         if (dry_run)
402                 return;
403
404         (void) dozerobuf(f, TBLOCK);
405         (void) dozerobuf(f, TBLOCK);
406
407         if (sys_fstat(f, &stbuf) == -1) {
408                 DEBUG(0, ("Couldn't stat file handle\n"));
409                 return;
410         }
411
412         /* Could be a pipe, in which case S_ISREG should fail,
413                 * and we should write out at full size */
414         if (tp > 0)
415                 write(f, tarbuf, S_ISREG(stbuf.st_mode) ? tp : tbufsiz);
416 }
417
418 /****************************************************************************
419 (Un)mangle DOS pathname, make nonabsolute
420 ****************************************************************************/
421
422 static void fixtarname(char *tptr, const char *fp, size_t l)
423 {
424         /* add a '.' to start of file name, convert from ugly dos \'s in path
425          * to lovely unix /'s :-} */
426         *tptr++='.';
427         l--;
428
429         StrnCpy(tptr, fp, l-1);
430         string_replace(tptr, '\\', '/');
431 }
432
433 /****************************************************************************
434 Convert from decimal to octal string
435 ****************************************************************************/
436
437 static void oct_it (SMB_BIG_UINT value, int ndgs, char *p)
438 {
439         /* Converts long to octal string, pads with leading zeros */
440
441         /* skip final null, but do final space */
442         --ndgs;
443         p[--ndgs] = ' ';
444  
445         /* Loop does at least one digit */
446         do {
447                 p[--ndgs] = '0' + (char) (value & 7);
448                 value >>= 3;
449         } while (ndgs > 0 && value != 0);
450  
451         /* Do leading zeros */
452         while (ndgs > 0)
453                 p[--ndgs] = '0';
454 }
455
456 /****************************************************************************
457 Convert from octal string to long
458 ***************************************************************************/
459
460 static long unoct(char *p, int ndgs)
461 {
462         long value=0;
463         /* Converts octal string to long, ignoring any non-digit */
464
465         while (--ndgs) {
466                 if (isdigit((int)*p))
467                         value = (value << 3) | (long) (*p - '0');
468
469                 p++;
470         }
471
472         return value;
473 }
474
475 /****************************************************************************
476 Compare two strings in a slash insensitive way, allowing s1 to match s2 
477 if s1 is an "initial" string (up to directory marker).  Thus, if s2 is 
478 a file in any subdirectory of s1, declare a match.
479 ***************************************************************************/
480
481 static int strslashcmp(char *s1, char *s2)
482 {
483         char *s1_0=s1;
484
485         while(*s1 && *s2 && (*s1 == *s2 || tolower_ascii(*s1) == tolower_ascii(*s2) ||
486                                 (*s1 == '\\' && *s2=='/') || (*s1 == '/' && *s2=='\\'))) {
487                 s1++; s2++;
488         }
489
490         /* if s1 has a trailing slash, it compared equal, so s1 is an "initial" 
491                 string of s2.
492         */
493         if (!*s1 && s1 != s1_0 && (*(s1-1) == '/' || *(s1-1) == '\\'))
494                 return 0;
495
496         /* ignore trailing slash on s1 */
497         if (!*s2 && (*s1 == '/' || *s1 == '\\') && !*(s1+1))
498                 return 0;
499
500         /* check for s1 is an "initial" string of s2 */
501         if ((*s2 == '/' || *s2 == '\\') && !*s1)
502                 return 0;
503
504         return *s1-*s2;
505 }
506
507 /****************************************************************************
508 Ensure a remote path exists (make if necessary)
509 ***************************************************************************/
510
511 static BOOL ensurepath(char *fname)
512 {
513         /* *must* be called with buffer ready malloc'ed */
514         /* ensures path exists */
515
516         char *partpath, *ffname;
517         char *p=fname, *basehack;
518
519         DEBUG(5, ( "Ensurepath called with: %s\n", fname));
520
521         partpath = string_create_s(strlen(fname));
522         ffname = string_create_s(strlen(fname));
523
524         if ((partpath == NULL) || (ffname == NULL)){
525                 DEBUG(0, ("Out of memory in ensurepath: %s\n", fname));
526                 SAFE_FREE(partpath);
527                 SAFE_FREE(ffname);
528                 return(False);
529         }
530
531         *partpath = 0;
532
533         /* fname copied to ffname so can strtok */
534
535         safe_strcpy(ffname, fname, strlen(fname));
536
537         /* do a `basename' on ffname, so don't try and make file name directory */
538         if ((basehack=strrchr_m(ffname, '\\')) == NULL)
539                 return True;
540         else
541                 *basehack='\0';
542
543         p=strtok(ffname, "\\");
544
545         while (p) {
546                 safe_strcat(partpath, p, strlen(fname) + 1);
547
548                 if (!cli_chkpath(cli, partpath)) {
549                         if (!cli_mkdir(cli, partpath)) {
550                                 DEBUG(0, ("Error mkdirhiering\n"));
551                                 return False;
552                         } else {
553                                 DEBUG(3, ("mkdirhiering %s\n", partpath));
554                         }
555                 }
556
557                 safe_strcat(partpath, "\\", strlen(fname) + 1);
558                 p = strtok(NULL,"/\\");
559         }
560
561         return True;
562 }
563
564 static int padit(char *buf, SMB_BIG_UINT bufsize, SMB_BIG_UINT padsize)
565 {
566         int berr= 0;
567         int bytestowrite;
568   
569         DEBUG(5, ("Padding with %0.f zeros\n", (double)padsize));
570         memset(buf, 0, (size_t)bufsize);
571         while( !berr && padsize > 0 ) {
572                 bytestowrite= (int)MIN(bufsize, padsize);
573                 berr = dotarbuf(tarhandle, buf, bytestowrite) != bytestowrite;
574                 padsize -= bytestowrite;
575         }
576   
577         return berr;
578 }
579
580 static void do_setrattr(char *name, uint16 attr, int set)
581 {
582         uint16 oldattr;
583
584         if (!cli_getatr(cli, name, &oldattr, NULL, NULL)) return;
585
586         if (set == ATTRSET) {
587                 attr |= oldattr;
588         } else {
589                 attr = oldattr & ~attr;
590         }
591
592         if (!cli_setatr(cli, name, attr, 0)) {
593                 DEBUG(1,("setatr failed: %s\n", cli_errstr(cli)));
594         }
595 }
596
597 /****************************************************************************
598 append one remote file to the tar file
599 ***************************************************************************/
600
601 static void do_atar(char *rname,char *lname,file_info *finfo1)
602 {
603         int fnum;
604         SMB_BIG_UINT nread=0;
605         char ftype;
606         file_info2 finfo;
607         BOOL close_done = False;
608         BOOL shallitime=True;
609         char data[65520];
610         int read_size = 65520;
611         int datalen=0;
612
613         struct timeval tp_start;
614
615         GetTimeOfDay(&tp_start);
616
617         ftype = '0'; /* An ordinary file ... */
618
619         if (finfo1) {
620                 finfo.size  = finfo1 -> size;
621                 finfo.mode  = finfo1 -> mode;
622                 finfo.uid   = finfo1 -> uid;
623                 finfo.gid   = finfo1 -> gid;
624                 finfo.mtime = finfo1 -> mtime;
625                 finfo.atime = finfo1 -> atime;
626                 finfo.ctime = finfo1 -> ctime;
627                 finfo.name  = finfo1 -> name;
628         } else {
629                 finfo.size  = def_finfo.size;
630                 finfo.mode  = def_finfo.mode;
631                 finfo.uid   = def_finfo.uid;
632                 finfo.gid   = def_finfo.gid;
633                 finfo.mtime = def_finfo.mtime;
634                 finfo.atime = def_finfo.atime;
635                 finfo.ctime = def_finfo.ctime;
636                 finfo.name  = def_finfo.name;
637         }
638
639         if (dry_run) {
640                 DEBUG(3,("skipping file %s of size %12.0f bytes\n", finfo.name,
641                                 (double)finfo.size));
642                 shallitime=0;
643                 ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
644                 ntarf++;
645                 return;
646         }
647
648         fnum = cli_open(cli, rname, O_RDONLY, DENY_NONE);
649
650         dos_clean_name(rname);
651
652         if (fnum == -1) {
653                 DEBUG(0,("%s opening remote file %s (%s)\n",
654                                 cli_errstr(cli),rname, cur_dir));
655                 return;
656         }
657
658         finfo.name = string_create_s(strlen(rname));
659         if (finfo.name == NULL) {
660                 DEBUG(0, ("Unable to allocate space for finfo.name in do_atar\n"));
661                 return;
662         }
663
664         safe_strcpy(finfo.name,rname, strlen(rname));
665         if (!finfo1) {
666                 if (!cli_getattrE(cli, fnum, &finfo.mode, &finfo.size, NULL, &finfo.atime, &finfo.mtime)) {
667                         DEBUG(0, ("getattrE: %s\n", cli_errstr(cli)));
668                         return;
669                 }
670                 finfo.ctime = finfo.mtime;
671         }
672
673         DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode));
674
675         if (tar_inc && !(finfo.mode & aARCH)) {
676                 DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
677                 shallitime=0;
678         } else if (!tar_system && (finfo.mode & aSYSTEM)) {
679                 DEBUG(4, ("skipping %s - system bit is set\n", finfo.name));
680                 shallitime=0;
681         } else if (!tar_hidden && (finfo.mode & aHIDDEN)) {
682                 DEBUG(4, ("skipping %s - hidden bit is set\n", finfo.name));
683                 shallitime=0;
684         } else {
685                 BOOL wrote_tar_header = False;
686
687                 DEBUG(3,("getting file %s of size %.0f bytes as a tar file %s",
688                         finfo.name, (double)finfo.size, lname));
689       
690                 while (nread < finfo.size && !close_done) {
691               
692                         DEBUG(3,("nread=%.0f\n",(double)nread));
693               
694                         datalen = cli_read(cli, fnum, data, nread, read_size);
695               
696                         if (datalen == -1) {
697                                 DEBUG(0,("Error reading file %s : %s\n", rname, cli_errstr(cli)));
698                                 break;
699                         }
700               
701                         nread += datalen;
702
703                         /* Only if the first read succeeds, write out the tar header. */
704                         if (!wrote_tar_header) {
705                                 /* write a tar header, don't bother with mode - just set to 100644 */
706                                 writetarheader(tarhandle, rname, finfo.size, finfo.mtime, "100644 \0", ftype);
707                                 wrote_tar_header = True;
708                         }
709
710                         /* if file size has increased since we made file size query, truncate
711                                 read so tar header for this file will be correct.
712                         */
713
714                         if (nread > finfo.size) {
715                                 datalen -= nread - finfo.size;
716                                 DEBUG(0,("File size change - truncating %s to %.0f bytes\n",
717                                                         finfo.name, (double)finfo.size));
718                         }
719
720                         /* add received bits of file to buffer - dotarbuf will
721                         * write out in 512 byte intervals */
722
723                         if (dotarbuf(tarhandle,data,datalen) != datalen) {
724                                 DEBUG(0,("Error writing to tar file - %s\n", strerror(errno)));
725                                 break;
726                         }
727               
728                         if (datalen == 0) {
729                                 DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname));
730                                 break;
731                         }
732
733                         datalen=0;
734                 }
735
736                 if (wrote_tar_header) {
737                         /* pad tar file with zero's if we couldn't get entire file */
738                         if (nread < finfo.size) {
739                                 DEBUG(0, ("Didn't get entire file. size=%.0f, nread=%d\n",
740                                                         (double)finfo.size, (int)nread));
741                                 if (padit(data, (SMB_BIG_UINT)sizeof(data), finfo.size - nread))
742                                         DEBUG(0,("Error writing tar file - %s\n", strerror(errno)));
743                         }
744
745                         /* round tar file to nearest block */
746                         if (finfo.size % TBLOCK)
747                                 dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
748       
749                         ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
750                         ntarf++;
751                 } else {
752                         DEBUG(4, ("skipping %s - initial read failed (file was locked ?)\n", finfo.name));
753                         shallitime=0;
754                 }
755         }
756   
757         cli_close(cli, fnum);
758
759         if (shallitime) {
760                 struct timeval tp_end;
761                 int this_time;
762
763                 /* if shallitime is true then we didn't skip */
764                 if (tar_reset && !dry_run)
765                         (void) do_setrattr(finfo.name, aARCH, ATTRRESET);
766       
767                 GetTimeOfDay(&tp_end);
768                 this_time = (tp_end.tv_sec - tp_start.tv_sec)*1000 + (tp_end.tv_usec - tp_start.tv_usec)/1000;
769                 get_total_time_ms += this_time;
770                 get_total_size += finfo.size;
771
772                 if (tar_noisy) {
773                         DEBUG(0, ("%12.0f (%7.1f kb/s) %s\n",
774                                 (double)finfo.size, finfo.size / MAX(0.001, (1.024*this_time)),
775                                 finfo.name));
776                 }
777
778                 /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */
779                 DEBUG(3,("(%g kb/s) (average %g kb/s)\n",
780                                 finfo.size / MAX(0.001, (1.024*this_time)),
781                                 get_total_size / MAX(0.001, (1.024*get_total_time_ms))));
782         }
783 }
784
785 /****************************************************************************
786 Append single file to tar file (or not)
787 ***************************************************************************/
788
789 static void do_tar(file_info *finfo)
790 {
791         pstring rname;
792
793         if (strequal(finfo->name,"..") || strequal(finfo->name,"."))
794                 return;
795
796         /* Is it on the exclude list ? */
797         if (!tar_excl && clipn) {
798                 pstring exclaim;
799
800                 DEBUG(5, ("Excl: strlen(cur_dir) = %d\n", (int)strlen(cur_dir)));
801
802                 pstrcpy(exclaim, cur_dir);
803                 *(exclaim+strlen(exclaim)-1)='\0';
804
805                 pstrcat(exclaim, "\\");
806                 pstrcat(exclaim, finfo->name);
807
808                 DEBUG(5, ("...tar_re_search: %d\n", tar_re_search));
809
810                 if ((!tar_re_search && clipfind(cliplist, clipn, exclaim)) ||
811                                 (tar_re_search && mask_match_list(exclaim, cliplist, clipn, True))) {
812                         DEBUG(3,("Skipping file %s\n", exclaim));
813                         return;
814                 }
815         }
816
817         if (finfo->mode & aDIR) {
818                 pstring saved_curdir;
819                 pstring mtar_mask;
820
821                 pstrcpy(saved_curdir, cur_dir);
822
823                 DEBUG(5, ("Sizeof(cur_dir)=%d, strlen(cur_dir)=%d, \
824 strlen(finfo->name)=%d\nname=%s,cur_dir=%s\n",
825                         (int)sizeof(cur_dir), (int)strlen(cur_dir),
826                         (int)strlen(finfo->name), finfo->name, cur_dir));
827
828                 pstrcat(cur_dir,finfo->name);
829                 pstrcat(cur_dir,"\\");
830
831                 DEBUG(5, ("Writing a dir, Name = %s\n", cur_dir));
832
833                 /* write a tar directory, don't bother with mode - just set it to
834                         * 40755 */
835                 writetarheader(tarhandle, cur_dir, 0, finfo->mtime, "040755 \0", '5');
836                 if (tar_noisy) {
837                         DEBUG(0,("                directory %s\n", cur_dir));
838                 }
839                 ntarf++;  /* Make sure we have a file on there */
840                 pstrcpy(mtar_mask,cur_dir);
841                 pstrcat(mtar_mask,"*");
842                 DEBUG(5, ("Doing list with mtar_mask: %s\n", mtar_mask));
843                 do_list(mtar_mask, attribute, do_tar, False, True);
844                 pstrcpy(cur_dir,saved_curdir);
845         } else {
846                 pstrcpy(rname,cur_dir);
847                 pstrcat(rname,finfo->name);
848                 do_atar(rname,finfo->name,finfo);
849         }
850 }
851
852 /****************************************************************************
853 Convert from UNIX to DOS file names
854 ***************************************************************************/
855
856 static void unfixtarname(char *tptr, char *fp, int l, BOOL first)
857 {
858         /* remove '.' from start of file name, convert from unix /'s to
859          * dos \'s in path. Kill any absolute path names. But only if first!
860          */
861
862         DEBUG(5, ("firstb=%lX, secondb=%lX, len=%i\n", (long)tptr, (long)fp, l));
863
864         if (first) {
865                 if (*fp == '.') {
866                         fp++;
867                         l--;
868                 }
869                 if (*fp == '\\' || *fp == '/') {
870                         fp++;
871                         l--;
872                 }
873         }
874
875         safe_strcpy(tptr, fp, l);
876         string_replace(tptr, '/', '\\');
877 }
878
879 /****************************************************************************
880 Move to the next block in the buffer, which may mean read in another set of
881 blocks. FIXME, we should allow more than one block to be skipped.
882 ****************************************************************************/
883
884 static int next_block(char *ltarbuf, char **bufferp, int bufsiz)
885 {
886         int bufread, total = 0;
887
888         DEBUG(5, ("Advancing to next block: %0lx\n", (unsigned long)*bufferp));
889         *bufferp += TBLOCK;
890         total = TBLOCK;
891
892         if (*bufferp >= (ltarbuf + bufsiz)) {
893
894                 DEBUG(5, ("Reading more data into ltarbuf ...\n"));
895
896                 /*
897                  * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>
898                  * Fixes bug where read can return short if coming from
899                  * a pipe.
900                  */
901
902                 bufread = read(tarhandle, ltarbuf, bufsiz);
903                 total = bufread;
904
905                 while (total < bufsiz) {
906                         if (bufread < 0) { /* An error, return false */
907                                 return (total > 0 ? -2 : bufread);
908                         }
909                         if (bufread == 0) {
910                                 if (total <= 0) {
911                                         return -2;
912                                 }
913                                 break;
914                         }
915                         bufread = read(tarhandle, &ltarbuf[total], bufsiz - total);
916                         total += bufread;
917                 }
918
919                 DEBUG(5, ("Total bytes read ... %i\n", total));
920
921                 *bufferp = ltarbuf;
922         }
923
924         return(total);
925 }
926
927 /* Skip a file, even if it includes a long file name? */
928 static int skip_file(int skipsize)
929 {
930         int dsize = skipsize;
931
932         DEBUG(5, ("Skiping file. Size = %i\n", skipsize));
933
934         /* FIXME, we should skip more than one block at a time */
935
936         while (dsize > 0) {
937                 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
938                         DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
939                         return(False);
940                 }
941                 dsize -= TBLOCK;
942         }
943
944         return(True);
945 }
946
947 /*************************************************************
948  Get a file from the tar file and store it.
949  When this is called, tarbuf already contains the first
950  file block. This is a bit broken & needs fixing.
951 **************************************************************/
952
953 static int get_file(file_info2 finfo)
954 {
955         int fnum = -1, pos = 0, dsize = 0, bpos = 0;
956         SMB_BIG_UINT rsize = 0;
957
958         DEBUG(5, ("get_file: file: %s, size %.0f\n", finfo.name, (double)finfo.size));
959
960         if (ensurepath(finfo.name) && 
961                         (fnum=cli_open(cli, finfo.name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) == -1) {
962                 DEBUG(0, ("abandoning restore\n"));
963                 return(False);
964         }
965
966         /* read the blocks from the tar file and write to the remote file */
967
968         rsize = finfo.size;  /* This is how much to write */
969
970         while (rsize > 0) {
971
972                 /* We can only write up to the end of the buffer */
973                 dsize = MIN(tbufsiz - (buffer_p - tarbuf) - bpos, 65520); /* Calculate the size to write */
974                 dsize = MIN(dsize, rsize);  /* Should be only what is left */
975                 DEBUG(5, ("writing %i bytes, bpos = %i ...\n", dsize, bpos));
976
977                 if (cli_write(cli, fnum, 0, buffer_p + bpos, pos, dsize) != dsize) {
978                         DEBUG(0, ("Error writing remote file\n"));
979                         return 0;
980                 }
981
982                 rsize -= dsize;
983                 pos += dsize;
984
985                 /* Now figure out how much to move in the buffer */
986
987                 /* FIXME, we should skip more than one block at a time */
988
989                 /* First, skip any initial part of the part written that is left over */
990                 /* from the end of the first TBLOCK                                   */
991
992                 if ((bpos) && ((bpos + dsize) >= TBLOCK)) {
993                         dsize -= (TBLOCK - bpos);  /* Get rid of the end of the first block */
994                         bpos = 0;
995
996                         if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {  /* and skip the block */
997                                 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
998                                 return False;
999                         }
1000                 }
1001
1002                 /*
1003                  * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>.
1004                  * If the file being extracted is an exact multiple of
1005                  * TBLOCK bytes then we don't want to extract the next
1006                  * block from the tarfile here, as it will be done in
1007                  * the caller of get_file().
1008                  */
1009
1010                 while (((rsize != 0) && (dsize >= TBLOCK)) ||
1011                                 ((rsize == 0) && (dsize > TBLOCK))) {
1012
1013                         if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
1014                                 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1015                                 return False;
1016                         }
1017
1018                         dsize -= TBLOCK;
1019                 }
1020                 bpos = dsize;
1021         }
1022
1023         /* Now close the file ... */
1024
1025         if (!cli_close(cli, fnum)) {
1026                 DEBUG(0, ("Error closing remote file\n"));
1027                 return(False);
1028         }
1029
1030         /* Now we update the creation date ... */
1031         DEBUG(5, ("Updating creation date on %s\n", finfo.name));
1032
1033         if (!cli_setatr(cli, finfo.name, finfo.mode, finfo.mtime)) {
1034                 if (tar_real_noisy) {
1035                         DEBUG(0, ("Could not set time on file: %s\n", finfo.name));
1036                         /*return(False); */ /* Ignore, as Win95 does not allow changes */
1037                 }
1038         }
1039
1040         ntarf++;
1041         DEBUG(0, ("restore tar file %s of size %.0f bytes\n", finfo.name, (double)finfo.size));
1042         return(True);
1043 }
1044
1045 /* Create a directory.  We just ensure that the path exists and return as there
1046    is no file associated with a directory 
1047 */
1048 static int get_dir(file_info2 finfo)
1049 {
1050         DEBUG(0, ("restore directory %s\n", finfo.name));
1051
1052         if (!ensurepath(finfo.name)) {
1053                 DEBUG(0, ("Problems creating directory\n"));
1054                 return(False);
1055         }
1056         ntarf++;
1057         return(True);
1058 }
1059
1060 /* Get a file with a long file name ... first file has file name, next file 
1061    has the data. We only want the long file name, as the loop in do_tarput
1062    will deal with the rest.
1063 */
1064 static char *get_longfilename(file_info2 finfo)
1065 {
1066         /* finfo.size here is the length of the filename as written by the "/./@LongLink" name
1067          * header call. */
1068         int namesize = finfo.size + strlen(cur_dir) + 2;
1069         char *longname = SMB_MALLOC(namesize);
1070         int offset = 0, left = finfo.size;
1071         BOOL first = True;
1072
1073         DEBUG(5, ("Restoring a long file name: %s\n", finfo.name));
1074         DEBUG(5, ("Len = %.0f\n", (double)finfo.size));
1075
1076         if (longname == NULL) {
1077                 DEBUG(0, ("could not allocate buffer of size %d for longname\n", namesize));
1078                 return(NULL);
1079         }
1080
1081         /* First, add cur_dir to the long file name */
1082
1083         if (strlen(cur_dir) > 0) {
1084                 strncpy(longname, cur_dir, namesize);
1085                 offset = strlen(cur_dir);
1086         }
1087
1088         /* Loop through the blocks picking up the name */
1089
1090         while (left > 0) {
1091                 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
1092                         DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1093                         SAFE_FREE(longname);
1094                         return(NULL);
1095                 }
1096
1097                 unfixtarname(longname + offset, buffer_p, MIN(TBLOCK, finfo.size), first--);
1098                 DEBUG(5, ("UnfixedName: %s, buffer: %s\n", longname, buffer_p));
1099
1100                 offset += TBLOCK;
1101                 left -= TBLOCK;
1102         }
1103
1104         return(longname);
1105 }
1106
1107 static void do_tarput(void)
1108 {
1109         file_info2 finfo;
1110         struct timeval tp_start;
1111         char *longfilename = NULL, linkflag;
1112         int skip = False;
1113
1114         ZERO_STRUCT(finfo);
1115
1116         GetTimeOfDay(&tp_start);
1117         DEBUG(5, ("RJS do_tarput called ...\n"));
1118
1119         buffer_p = tarbuf + tbufsiz;  /* init this to force first read */
1120
1121         /* Now read through those files ... */
1122         while (True) {
1123                 /* Get us to the next block, or the first block first time around */
1124                 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
1125                         DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1126                         SAFE_FREE(longfilename);
1127                         return;
1128                 }
1129
1130                 DEBUG(5, ("Reading the next header ...\n"));
1131
1132                 switch (readtarheader((union hblock *) buffer_p, &finfo, cur_dir)) {
1133                         case -2:    /* Hmm, not good, but not fatal */
1134                                 DEBUG(0, ("Skipping %s...\n", finfo.name));
1135                                 if ((next_block(tarbuf, &buffer_p, tbufsiz) <= 0) && !skip_file(finfo.size)) {
1136                                         DEBUG(0, ("Short file, bailing out...\n"));
1137                                         return;
1138                                 }
1139                                 break;
1140
1141                         case -1:
1142                                 DEBUG(0, ("abandoning restore, -1 from read tar header\n"));
1143                                 return;
1144
1145                         case 0: /* chksum is zero - looks like an EOF */
1146                                 DEBUG(0, ("tar: restored %d files and directories\n", ntarf));
1147                                 return;        /* Hmmm, bad here ... */
1148
1149                         default: 
1150                                 /* No action */
1151                                 break;
1152                 }
1153
1154                 /* Now, do we have a long file name? */
1155                 if (longfilename != NULL) {
1156                         SAFE_FREE(finfo.name);   /* Free the space already allocated */
1157                         finfo.name = longfilename;
1158                         longfilename = NULL;
1159                 }
1160
1161                 /* Well, now we have a header, process the file ...            */
1162                 /* Should we skip the file? We have the long name as well here */
1163                 skip = clipn && ((!tar_re_search && clipfind(cliplist, clipn, finfo.name) ^ tar_excl) ||
1164                                         (tar_re_search && mask_match_list(finfo.name, cliplist, clipn, True)));
1165
1166                 DEBUG(5, ("Skip = %i, cliplist=%s, file=%s\n", skip, (cliplist?cliplist[0]:NULL), finfo.name));
1167                 if (skip) {
1168                         skip_file(finfo.size);
1169                         continue;
1170                 }
1171
1172                 /* We only get this far if we should process the file */
1173                 linkflag = ((union hblock *)buffer_p) -> dbuf.linkflag;
1174                 switch (linkflag) {
1175                         case '0':  /* Should use symbolic names--FIXME */
1176                                 /* 
1177                                  * Skip to the next block first, so we can get the file, FIXME, should
1178                                  * be in get_file ...
1179                                  * The 'finfo.size != 0' fix is from Bob Boehmer <boehmer@worldnet.att.net>
1180                                  * Fixes bug where file size in tarfile is zero.
1181                                  */
1182                                 if ((finfo.size != 0) && next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
1183                                         DEBUG(0, ("Short file, bailing out...\n"));
1184                                         return;
1185                                 }
1186                                 if (!get_file(finfo)) {
1187                                         DEBUG(0, ("Abandoning restore\n"));
1188                                         return;
1189                                 }
1190                                 break;
1191                         case '5':
1192                                 if (!get_dir(finfo)) {
1193                                         DEBUG(0, ("Abandoning restore \n"));
1194                                         return;
1195                                 }
1196                                 break;
1197                         case 'L':
1198                                 SAFE_FREE(longfilename);
1199                                 longfilename = get_longfilename(finfo);
1200                                 if (!longfilename) {
1201                                         DEBUG(0, ("abandoning restore\n"));
1202                                         return;
1203                                 }
1204                                 DEBUG(5, ("Long file name: %s\n", longfilename));
1205                                 break;
1206
1207                         default:
1208                                 skip_file(finfo.size);  /* Don't handle these yet */
1209                                 break;
1210                 }
1211         }
1212 }
1213
1214 /*
1215  * samba interactive commands
1216  */
1217
1218 /****************************************************************************
1219 Blocksize command
1220 ***************************************************************************/
1221
1222 int cmd_block(void)
1223 {
1224         fstring buf;
1225         int block;
1226
1227         if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1228                 DEBUG(0, ("blocksize <n>\n"));
1229                 return 1;
1230         }
1231
1232         block=atoi(buf);
1233         if (block < 0 || block > 65535) {
1234                 DEBUG(0, ("blocksize out of range"));
1235                 return 1;
1236         }
1237
1238         blocksize=block;
1239         DEBUG(2,("blocksize is now %d\n", blocksize));
1240
1241         return 0;
1242 }
1243
1244 /****************************************************************************
1245 command to set incremental / reset mode
1246 ***************************************************************************/
1247
1248 int cmd_tarmode(void)
1249 {
1250         fstring buf;
1251
1252         while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1253                 if (strequal(buf, "full"))
1254                         tar_inc=False;
1255                 else if (strequal(buf, "inc"))
1256                         tar_inc=True;
1257                 else if (strequal(buf, "reset"))
1258                         tar_reset=True;
1259                 else if (strequal(buf, "noreset"))
1260                         tar_reset=False;
1261                 else if (strequal(buf, "system"))
1262                         tar_system=True;
1263                 else if (strequal(buf, "nosystem"))
1264                         tar_system=False;
1265                 else if (strequal(buf, "hidden"))
1266                         tar_hidden=True;
1267                 else if (strequal(buf, "nohidden"))
1268                         tar_hidden=False;
1269                 else if (strequal(buf, "verbose") || strequal(buf, "noquiet"))
1270                         tar_noisy=True;
1271                 else if (strequal(buf, "quiet") || strequal(buf, "noverbose"))
1272                         tar_noisy=False;
1273                 else
1274                         DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
1275         }
1276
1277         DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n",
1278                         tar_inc ? "incremental" : "full",
1279                         tar_system ? "system" : "nosystem",
1280                         tar_hidden ? "hidden" : "nohidden",
1281                         tar_reset ? "reset" : "noreset",
1282                         tar_noisy ? "verbose" : "quiet"));
1283         return 0;
1284 }
1285
1286 /****************************************************************************
1287 Feeble attrib command
1288 ***************************************************************************/
1289
1290 int cmd_setmode(void)
1291 {
1292         char *q;
1293         fstring buf;
1294         pstring fname;
1295         uint16 attra[2];
1296         int direct=1;
1297
1298         attra[0] = attra[1] = 0;
1299
1300         if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1301                 DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1302                 return 1;
1303         }
1304
1305         pstrcpy(fname, cur_dir);
1306         pstrcat(fname, buf);
1307
1308         while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1309                 q=buf;
1310
1311                 while(*q) {
1312                         switch (*q++) {
1313                                 case '+':
1314                                         direct=1;
1315                                         break;
1316                                 case '-':
1317                                         direct=0;
1318                                         break;
1319                                 case 'r':
1320                                         attra[direct]|=aRONLY;
1321                                         break;
1322                                 case 'h':
1323                                         attra[direct]|=aHIDDEN;
1324                                         break;
1325                                 case 's':
1326                                         attra[direct]|=aSYSTEM;
1327                                         break;
1328                                 case 'a':
1329                                         attra[direct]|=aARCH;
1330                                         break;
1331                                 default:
1332                                         DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
1333                                         return 1;
1334                         }
1335                 }
1336         }
1337
1338         if (attra[ATTRSET]==0 && attra[ATTRRESET]==0) {
1339                 DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1340                 return 1;
1341         }
1342
1343         DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
1344         do_setrattr(fname, attra[ATTRSET], ATTRSET);
1345         do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
1346         return 0;
1347 }
1348
1349 /****************************************************************************
1350 Principal command for creating / extracting
1351 ***************************************************************************/
1352
1353 int cmd_tar(void)
1354 {
1355         fstring buf;
1356         char **argl = NULL;
1357         int argcl = 0;
1358         int ret;
1359
1360         if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1361                 DEBUG(0,("tar <c|x>[IXbgan] <filename>\n"));
1362                 return 1;
1363         }
1364
1365         argl=toktocliplist(&argcl, NULL);
1366         if (!tar_parseargs(argcl, argl, buf, 0))
1367                 return 1;
1368
1369         ret = process_tar();
1370         SAFE_FREE(argl);
1371         return ret;
1372 }
1373
1374 /****************************************************************************
1375 Command line (option) version
1376 ***************************************************************************/
1377
1378 int process_tar(void)
1379 {
1380         int rc = 0;
1381         initarbuf();
1382         switch(tar_type) {
1383                 case 'x':
1384
1385 #if 0
1386                         do_tarput2();
1387 #else
1388                         do_tarput();
1389 #endif
1390                         SAFE_FREE(tarbuf);
1391                         close(tarhandle);
1392                         break;
1393                 case 'r':
1394                 case 'c':
1395                         if (clipn && tar_excl) {
1396                                 int i;
1397                                 pstring tarmac;
1398
1399                                 for (i=0; i<clipn; i++) {
1400                                         DEBUG(5,("arg %d = %s\n", i, cliplist[i]));
1401
1402                                         if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
1403                                                 *(cliplist[i]+strlen(cliplist[i])-1)='\0';
1404                                         }
1405         
1406                                         if (strrchr_m(cliplist[i], '\\')) {
1407                                                 pstring saved_dir;
1408           
1409                                                 pstrcpy(saved_dir, cur_dir);
1410           
1411                                                 if (*cliplist[i]=='\\') {
1412                                                         pstrcpy(tarmac, cliplist[i]);
1413                                                 } else {
1414                                                         pstrcpy(tarmac, cur_dir);
1415                                                         pstrcat(tarmac, cliplist[i]);
1416                                                 }
1417                                                 pstrcpy(cur_dir, tarmac);
1418                                                 *(strrchr_m(cur_dir, '\\')+1)='\0';
1419
1420                                                 DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1421                                                 do_list(tarmac,attribute,do_tar, False, True);
1422                                                 pstrcpy(cur_dir,saved_dir);
1423                                         } else {
1424                                                 pstrcpy(tarmac, cur_dir);
1425                                                 pstrcat(tarmac, cliplist[i]);
1426                                                 DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1427                                                 do_list(tarmac,attribute,do_tar, False, True);
1428                                         }
1429                                 }
1430                         } else {
1431                                 pstring mask;
1432                                 pstrcpy(mask,cur_dir);
1433                                 DEBUG(5, ("process_tar, do_list with mask: %s\n", mask));
1434                                 pstrcat(mask,"\\*");
1435                                 do_list(mask,attribute,do_tar,False, True);
1436                         }
1437     
1438                         if (ntarf)
1439                                 dotareof(tarhandle);
1440                         close(tarhandle);
1441                         SAFE_FREE(tarbuf);
1442     
1443                         DEBUG(0, ("tar: dumped %d files and directories\n", ntarf));
1444                         DEBUG(0, ("Total bytes written: %.0f\n", (double)ttarf));
1445                         break;
1446         }
1447
1448         if (must_free_cliplist) {
1449                 int i;
1450                 for (i = 0; i < clipn; ++i) {
1451                         SAFE_FREE(cliplist[i]);
1452                 }
1453                 SAFE_FREE(cliplist);
1454                 cliplist = NULL;
1455                 clipn = 0;
1456                 must_free_cliplist = False;
1457         }
1458         return rc;
1459 }
1460
1461 /****************************************************************************
1462 Find a token (filename) in a clip list
1463 ***************************************************************************/
1464
1465 static int clipfind(char **aret, int ret, char *tok)
1466 {
1467         if (aret==NULL)
1468                 return 0;
1469
1470         /* ignore leading slashes or dots in token */
1471         while(strchr_m("/\\.", *tok))
1472                 tok++;
1473
1474         while(ret--) {
1475                 char *pkey=*aret++;
1476
1477                 /* ignore leading slashes or dots in list */
1478                 while(strchr_m("/\\.", *pkey))
1479                         pkey++;
1480
1481                 if (!strslashcmp(pkey, tok))
1482                         return 1;
1483         }
1484         return 0;
1485 }
1486
1487 /****************************************************************************
1488 Read list of files to include from the file and initialize cliplist
1489 accordingly.
1490 ***************************************************************************/
1491
1492 static int read_inclusion_file(char *filename)
1493 {
1494         XFILE *inclusion = NULL;
1495         char buf[PATH_MAX + 1];
1496         char *inclusion_buffer = NULL;
1497         int inclusion_buffer_size = 0;
1498         int inclusion_buffer_sofar = 0;
1499         char *p;
1500         char *tmpstr;
1501         int i;
1502         int error = 0;
1503
1504         clipn = 0;
1505         buf[PATH_MAX] = '\0'; /* guarantee null-termination */
1506         if ((inclusion = x_fopen(filename, O_RDONLY, 0)) == NULL) {
1507                 /* XXX It would be better to include a reason for failure, but without
1508                  * autoconf, it's hard to use strerror, sys_errlist, etc.
1509                  */
1510                 DEBUG(0,("Unable to open inclusion file %s\n", filename));
1511                 return 0;
1512         }
1513
1514         while ((! error) && (x_fgets(buf, sizeof(buf)-1, inclusion))) {
1515                 if (inclusion_buffer == NULL) {
1516                         inclusion_buffer_size = 1024;
1517                         if ((inclusion_buffer = SMB_MALLOC(inclusion_buffer_size)) == NULL) {
1518                                 DEBUG(0,("failure allocating buffer to read inclusion file\n"));
1519                                 error = 1;
1520                                 break;
1521                         }
1522                 }
1523     
1524                 if (buf[strlen(buf)-1] == '\n') {
1525                         buf[strlen(buf)-1] = '\0';
1526                 }
1527     
1528                 if ((strlen(buf) + 1 + inclusion_buffer_sofar) >= inclusion_buffer_size) {
1529                         inclusion_buffer_size *= 2;
1530                         inclusion_buffer = SMB_REALLOC(inclusion_buffer,inclusion_buffer_size);
1531                         if (!inclusion_buffer) {
1532                                 DEBUG(0,("failure enlarging inclusion buffer to %d bytes\n",
1533                                                 inclusion_buffer_size));
1534                                 error = 1;
1535                                 break;
1536                         }
1537                 }
1538     
1539                 safe_strcpy(inclusion_buffer + inclusion_buffer_sofar, buf, inclusion_buffer_size - inclusion_buffer_sofar);
1540                 inclusion_buffer_sofar += strlen(buf) + 1;
1541                 clipn++;
1542         }
1543         x_fclose(inclusion);
1544
1545         if (! error) {
1546                 /* Allocate an array of clipn + 1 char*'s for cliplist */
1547                 cliplist = SMB_MALLOC_ARRAY(char *, clipn + 1);
1548                 if (cliplist == NULL) {
1549                         DEBUG(0,("failure allocating memory for cliplist\n"));
1550                         error = 1;
1551                 } else {
1552                         cliplist[clipn] = NULL;
1553                         p = inclusion_buffer;
1554                         for (i = 0; (! error) && (i < clipn); i++) {
1555                                 /* set current item to NULL so array will be null-terminated even if
1556                                                 * malloc fails below. */
1557                                 cliplist[i] = NULL;
1558                                 if ((tmpstr = (char *)SMB_MALLOC(strlen(p)+1)) == NULL) {
1559                                         DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", i));
1560                                         error = 1;
1561                                 } else {
1562                                         unfixtarname(tmpstr, p, strlen(p) + 1, True);
1563                                         cliplist[i] = tmpstr;
1564                                         if ((p = strchr_m(p, '\000')) == NULL) {
1565                                                 DEBUG(0,("INTERNAL ERROR: inclusion_buffer is of unexpected contents.\n"));
1566                                                 abort();
1567                                         }
1568                                 }
1569                                 ++p;
1570                         }
1571                         must_free_cliplist = True;
1572                 }
1573         }
1574
1575         SAFE_FREE(inclusion_buffer);
1576         if (error) {
1577                 if (cliplist) {
1578                         char **pp;
1579                         /* We know cliplist is always null-terminated */
1580                         for (pp = cliplist; *pp; ++pp) {
1581                                 SAFE_FREE(*pp);
1582                         }
1583                         SAFE_FREE(cliplist);
1584                         cliplist = NULL;
1585                         must_free_cliplist = False;
1586                 }
1587                 return 0;
1588         }
1589   
1590         /* cliplist and its elements are freed at the end of process_tar. */
1591         return 1;
1592 }
1593
1594 /****************************************************************************
1595 Parse tar arguments. Sets tar_type, tar_excl, etc.
1596 ***************************************************************************/
1597
1598 int tar_parseargs(int argc, char *argv[], const char *Optarg, int Optind)
1599 {
1600         int newOptind = Optind;
1601         char tar_clipfl='\0';
1602
1603         /* Reset back to defaults - could be from interactive version 
1604          * reset mode and archive mode left as they are though
1605          */
1606         tar_type='\0';
1607         tar_excl=True;
1608         dry_run=False;
1609
1610         while (*Optarg) {
1611                 switch(*Optarg++) {
1612                         case 'c':
1613                                 tar_type='c';
1614                                 break;
1615                         case 'x':
1616                                 if (tar_type=='c') {
1617                                         printf("Tar must be followed by only one of c or x.\n");
1618                                         return 0;
1619                                 }
1620                                 tar_type='x';
1621                                 break;
1622                         case 'b':
1623                                 if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) {
1624                                         DEBUG(0,("Option b must be followed by valid blocksize\n"));
1625                                         return 0;
1626                                 } else {
1627                                         Optind++;
1628                                         newOptind++;
1629                                 }
1630                                 break;
1631                         case 'g':
1632                                 tar_inc=True;
1633                                 break;
1634                         case 'N':
1635                                 if (Optind>=argc) {
1636                                         DEBUG(0,("Option N must be followed by valid file name\n"));
1637                                         return 0;
1638                                 } else {
1639                                         SMB_STRUCT_STAT stbuf;
1640         
1641                                         if (sys_stat(argv[Optind], &stbuf) == 0) {
1642                                                 newer_than = stbuf.st_mtime;
1643                                                 DEBUG(1,("Getting files newer than %s",
1644                                                         asctime(localtime(&newer_than))));
1645                                                 newOptind++;
1646                                                 Optind++;
1647                                         } else {
1648                                                 DEBUG(0,("Error setting newer-than time\n"));
1649                                                 return 0;
1650                                         }
1651                                 }
1652                                 break;
1653                         case 'a':
1654                                 tar_reset=True;
1655                                 break;
1656                         case 'q':
1657                                 tar_noisy=False;
1658                                 break;
1659                         case 'I':
1660                                 if (tar_clipfl) {
1661                                         DEBUG(0,("Only one of I,X,F must be specified\n"));
1662                                         return 0;
1663                                 }
1664                                 tar_clipfl='I';
1665                                 break;
1666                         case 'X':
1667                                 if (tar_clipfl) {
1668                                         DEBUG(0,("Only one of I,X,F must be specified\n"));
1669                                         return 0;
1670                                 }
1671                                 tar_clipfl='X';
1672                                 break;
1673                         case 'F':
1674                                 if (tar_clipfl) {
1675                                         DEBUG(0,("Only one of I,X,F must be specified\n"));
1676                                         return 0;
1677                                 }
1678                                 tar_clipfl='F';
1679                                 break;
1680                         case 'r':
1681                                 DEBUG(0, ("tar_re_search set\n"));
1682                                 tar_re_search = True;
1683                                 break;
1684                         case 'n':
1685                                 if (tar_type == 'c') {
1686                                         DEBUG(0, ("dry_run set\n"));
1687                                         dry_run = True;
1688                                 } else {
1689                                         DEBUG(0, ("n is only meaningful when creating a tar-file\n"));
1690                                         return 0;
1691                                 }
1692                                 break;
1693                         default:
1694                                 DEBUG(0,("Unknown tar option\n"));
1695                                 return 0;
1696                 }
1697         }
1698
1699         if (!tar_type) {
1700                 printf("Option T must be followed by one of c or x.\n");
1701                 return 0;
1702         }
1703
1704         /* tar_excl is true if cliplist lists files to be included.
1705          * Both 'I' and 'F' mean include. */
1706         tar_excl=tar_clipfl!='X';
1707
1708         if (tar_clipfl=='F') {
1709                 if (argc-Optind-1 != 1) {
1710                         DEBUG(0,("Option F must be followed by exactly one filename.\n"));
1711                         return 0;
1712                 }
1713                 newOptind++;
1714                 /* Optind points at the tar output file, Optind+1 at the inclusion file. */
1715                 if (! read_inclusion_file(argv[Optind+1])) {
1716                         return 0;
1717                 }
1718         } else if (Optind+1<argc && !tar_re_search) { /* For backwards compatibility */
1719                 char *tmpstr;
1720                 char **tmplist;
1721                 int clipcount;
1722
1723                 cliplist=argv+Optind+1;
1724                 clipn=argc-Optind-1;
1725                 clipcount = clipn;
1726
1727                 if ((tmplist=SMB_MALLOC_ARRAY(char *,clipn)) == NULL) {
1728                         DEBUG(0, ("Could not allocate space to process cliplist, count = %i\n", clipn));
1729                         return 0;
1730                 }
1731
1732                 for (clipcount = 0; clipcount < clipn; clipcount++) {
1733
1734                         DEBUG(5, ("Processing an item, %s\n", cliplist[clipcount]));
1735
1736                         if ((tmpstr = (char *)SMB_MALLOC(strlen(cliplist[clipcount])+1)) == NULL) {
1737                                 DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", clipcount));
1738                                 SAFE_FREE(tmplist);
1739                                 return 0;
1740                         }
1741
1742                         unfixtarname(tmpstr, cliplist[clipcount], strlen(cliplist[clipcount]) + 1, True);
1743                         tmplist[clipcount] = tmpstr;
1744                         DEBUG(5, ("Processed an item, %s\n", tmpstr));
1745
1746                         DEBUG(5, ("Cliplist is: %s\n", cliplist[0]));
1747                 }
1748
1749                 cliplist = tmplist;
1750                 must_free_cliplist = True;
1751
1752                 newOptind += clipn;
1753         }
1754
1755         if (Optind+1<argc && tar_re_search && tar_clipfl != 'F') {
1756                 /* Doing regular expression seaches not from an inclusion file. */
1757                 clipn=argc-Optind-1;
1758                 cliplist=argv+Optind+1;
1759                 newOptind += clipn;
1760         }
1761
1762         if (Optind>=argc || !strcmp(argv[Optind], "-")) {
1763                 /* Sets tar handle to either 0 or 1, as appropriate */
1764                 tarhandle=(tar_type=='c');
1765                 /*
1766                  * Make sure that dbf points to stderr if we are using stdout for 
1767                  * tar output
1768                  */
1769                 if (tarhandle == 1)  {
1770                         dbf = x_stderr;
1771                 }
1772                 if (!argv[Optind]) {
1773                         DEBUG(0,("Must specify tar filename\n"));
1774                         return 0;
1775                 }
1776                 if (!strcmp(argv[Optind], "-")) {
1777                         newOptind++;
1778                 }
1779
1780         } else {
1781                 if (tar_type=='c' && dry_run) {
1782                         tarhandle=-1;
1783                 } else if ((tar_type=='x' && (tarhandle = sys_open(argv[Optind], O_RDONLY, 0)) == -1)
1784                                         || (tar_type=='c' && (tarhandle=sys_creat(argv[Optind], 0644)) < 0)) {
1785                         DEBUG(0,("Error opening local file %s - %s\n", argv[Optind], strerror(errno)));
1786                         return(0);
1787                 }
1788                 newOptind++;
1789         }
1790
1791         return newOptind;
1792 }