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