13df5fef9add0c96b2296870f925dbb3c0ad8c52
[kai/samba.git] / source3 / client / clitar.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Tar Extensions
5    Copyright (C) Ricky Poulten 1995
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
22
23 #include "includes.h"
24 #include "clitar.h"
25
26 extern BOOL recurse;
27
28 #define SEPARATORS " \t\n\r"
29 extern int DEBUGLEVEL;
30 extern int Client;
31
32 /* These defines are for the do_setrattr routine, to indicate
33  * setting and reseting of file attributes in the function call */
34 #define ATTRSET 1
35 #define ATTRRESET 0
36
37 static int attribute = aDIR | aSYSTEM | aHIDDEN;
38
39 #ifndef CLIENT_TIMEOUT
40 #define CLIENT_TIMEOUT (30*1000)
41 #endif
42
43 static char *tarbuf;
44 static int tp, ntarf, tbufsiz;
45 /* Incremental mode */
46 BOOL tar_inc=False;
47 /* Reset archive bit */
48 BOOL tar_reset=False;
49 /* Include / exclude mode (true=include, false=exclude) */
50 BOOL tar_excl=True;
51 char tar_type='\0';
52 static char **cliplist=NULL;
53 static int clipn=0;
54
55 extern file_info def_finfo;
56 extern BOOL lowercase;
57 extern int cnum;
58 extern BOOL readbraw_supported;
59 extern int max_xmit;
60 extern pstring cur_dir;
61 extern int get_total_time_ms;
62 extern int get_total_size;
63 extern int Protocol;
64
65 int blocksize=20;
66 int tarhandle;
67
68 static void writetarheader();
69 static void do_atar();
70 static void do_tar();
71 static void oct_it();
72 static void fixtarname();
73 static int dotarbuf();
74 static void dozerobuf();
75 static void dotareof();
76 static void initarbuf();
77 static int do_setrattr();
78
79 /* restore functions */
80 static long readtarheader();
81 static long unoct();
82 static void do_tarput();
83 static void unfixtarname();
84
85 /*
86  * tar specific utitlities
87  */
88
89 /****************************************************************************
90 Write a tar header to buffer
91 ****************************************************************************/
92 static void writetarheader(int f,  char *aname, int size, time_t mtime,
93                     char *amode)
94 {
95   union hblock hb;
96   int i, chk, l;
97   char *jp;
98
99   memset(hb.dummy, 0, sizeof(hb.dummy));
100   
101   l=strlen(aname);
102   if (l >= NAMSIZ)
103     {
104       DEBUG(0, ("tar file %s name length exceeds NAMSIZ\n", aname));
105     }
106
107   /* use l + 1 to do the null too */
108   fixtarname(hb.dbuf.name, aname, (l >= NAMSIZ) ? NAMSIZ : l + 1);
109
110   if (lowercase)
111     strlower(hb.dbuf.name);
112
113   /* write out a "standard" tar format header */
114
115   hb.dbuf.name[NAMSIZ-1]='\0';
116   strcpy(hb.dbuf.mode, amode);
117   oct_it(0L, 8, hb.dbuf.uid);
118   oct_it(0L, 8, hb.dbuf.gid);
119   oct_it((long) size, 13, hb.dbuf.size);
120   oct_it((long) mtime, 13, hb.dbuf.mtime);
121   memcpy(hb.dbuf.chksum, "        ", sizeof(hb.dbuf.chksum));
122   hb.dbuf.linkflag='0';
123   memset(hb.dbuf.linkname, 0, NAMSIZ);
124   
125   for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;) chk+=(0xFF & *jp++);
126
127   oct_it((long) chk, 8, hb.dbuf.chksum);
128   hb.dbuf.chksum[6] = '\0';
129
130   (void) dotarbuf(f, hb.dummy, sizeof(hb.dummy));
131 }
132
133 /****************************************************************************
134 Read a tar header into a hblock structure, and validate
135 ***************************************************************************/
136 static long readtarheader(union hblock *hb, file_info *finfo, char *prefix)
137 {
138   long chk, fchk;
139   int i;
140   char *jp;
141
142   /*
143    * read in a "standard" tar format header - we're not that interested
144    * in that many fields, though
145    */
146
147   /* check the checksum */
148   for (chk=0, i=sizeof(hb->dummy), jp=hb->dummy; --i>=0;) chk+=(0xFF & *jp++);
149
150   if (chk == 0)
151     return chk;
152
153   /* compensate for blanks in chksum header */
154   for (i=sizeof(hb->dbuf.chksum), jp=hb->dbuf.chksum; --i>=0;)
155     chk-=(0xFF & *jp++);
156
157   chk += ' ' * sizeof(hb->dbuf.chksum);
158
159   fchk=unoct(hb->dbuf.chksum, sizeof(hb->dbuf.chksum));
160
161   DEBUG(5, ("checksum totals chk=%d fchk=%d chksum=%s\n",
162             chk, fchk, hb->dbuf.chksum));
163
164   if (fchk != chk)
165     {
166       DEBUG(0, ("checksums don't match %d %d\n", fchk, chk));
167       return -1;
168     }
169
170   strcpy(finfo->name, prefix);
171
172   /* use l + 1 to do the null too; do prefix - prefcnt to zap leading slash */
173   unfixtarname(finfo->name + strlen(prefix), hb->dbuf.name,
174                strlen(hb->dbuf.name) + 1);
175
176 /* can't handle links at present */
177   if (hb->dbuf.linkflag != '0') {
178     if (hb->dbuf.linkflag == 0) {
179       DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n",
180                 finfo->name));
181     } else { 
182       DEBUG(0, ("this tar file appears to contain some kind of link - ignoring\n"));
183       return -2;
184     }
185   }
186     
187   if ((unoct(hb->dbuf.mode, sizeof(hb->dbuf.mode)) & S_IFDIR)
188     || (*(finfo->name+strlen(finfo->name)-1) == '\\'))
189     {
190       finfo->mode=aDIR;
191     }
192   else
193     finfo->mode=0; /* we don't care about mode at the moment, we'll
194                     * just make it a regular file */
195   /*
196    * Bug fix by richard@sj.co.uk
197    *
198    * REC: restore times correctly (as does tar)
199    * We only get the modification time of the file; set the creation time
200    * from the mod. time, and the access time to current time
201    */
202   finfo->mtime = finfo->ctime = strtol(hb->dbuf.mtime, NULL, 8);
203   finfo->atime = time(NULL);
204   finfo->size = unoct(hb->dbuf.size, sizeof(hb->dbuf.size));
205
206   return True;
207 }
208
209 /****************************************************************************
210 Write out the tar buffer to tape or wherever
211 ****************************************************************************/
212 static int dotarbuf(int f, char *b, int n)
213 {
214   int fail=1, writ=n;
215
216   /* This routine and the next one should be the only ones that do write()s */
217   if (tp + n >= tbufsiz)
218     {
219       int diff;
220
221       diff=tbufsiz-tp;
222       memcpy(tarbuf + tp, b, diff);
223       fail=fail && (1+write(f, tarbuf, tbufsiz));
224       n-=diff;
225       b+=diff;
226       tp=0;
227
228       while (n >= tbufsiz)
229         {
230           fail=fail && (1 + write(f, b, tbufsiz));
231           n-=tbufsiz;
232           b+=tbufsiz;
233         }
234     }
235   if (n>0) {
236     memcpy(tarbuf+tp, b, n);
237     tp+=n;
238   }
239
240   return(fail ? writ : 0);
241 }
242
243 /****************************************************************************
244 Write a zeros to buffer / tape
245 ****************************************************************************/
246 static void dozerobuf(int f, int n)
247 {
248   /* short routine just to write out n zeros to buffer -
249    * used to round files to nearest block
250    * and to do tar EOFs */
251
252   if (n+tp >= tbufsiz)
253     {
254       memset(tarbuf+tp, 0, tbufsiz-tp);
255       write(f, tarbuf, tbufsiz);
256       memset(tarbuf, 0, (tp+=n-tbufsiz));
257     }
258   else
259     {
260       memset(tarbuf+tp, 0, n);
261       tp+=n;
262     }
263 }
264
265 /****************************************************************************
266 Malloc tape buffer
267 ****************************************************************************/
268 static void initarbuf()
269 {
270   /* initialize tar buffer */
271   tbufsiz=blocksize*TBLOCK;
272   tarbuf=malloc(tbufsiz);
273
274   /* reset tar buffer pointer and tar file counter */
275   tp=0; ntarf=0;
276 }
277
278 /****************************************************************************
279 Write two zero blocks at end of file
280 ****************************************************************************/
281 static void dotareof(int f)
282 {
283   struct stat stbuf;
284   /* Two zero blocks at end of file, write out full buffer */
285
286   (void) dozerobuf(f, TBLOCK);
287   (void) dozerobuf(f, TBLOCK);
288
289   if (fstat(f, &stbuf) == -1)
290     {
291       DEBUG(0, ("Couldn't stat file handle\n"));
292       return;
293     }
294
295   /* Could be a pipe, in which case S_ISREG should fail,
296    * and we should write out at full size */
297   if (tp > 0) write(f, tarbuf, S_ISREG(stbuf.st_mode) ? tp : tbufsiz);
298 }
299
300 /****************************************************************************
301 (Un)mangle DOS pathname, make nonabsolute
302 ****************************************************************************/
303 static void fixtarname(char *tptr, char *fp, int l)
304 {
305   /* add a '.' to start of file name, convert from ugly dos \'s in path
306    * to lovely unix /'s :-} */
307
308   *tptr++='.';
309 #ifdef KANJI
310   while (l > 0) {
311     if (is_shift_jis (*fp)) {
312       *tptr++ = *fp++;
313       *tptr++ = *fp++;
314       l -= 2;
315     } else if (is_kana (*fp)) {
316       *tptr++ = *fp++;
317       l--;
318     } else if (*fp == '\\') {
319       *tptr++ = '/';
320       fp++;
321       l--;
322     } else {
323       *tptr++ = *fp++;
324       l--;
325     }
326   }
327 #else
328   while (l--) { *tptr=(*fp == '\\') ? '/' : *fp; tptr++; fp++; }
329 #endif
330 }
331
332 /****************************************************************************
333 Convert from decimal to octal string
334 ****************************************************************************/
335 static void oct_it (register long value, register int ndgs, register char *p)
336 {
337   /* Converts long to octal string, pads with leading zeros */
338
339   /* skip final null, but do final space */
340   --ndgs;
341   p[--ndgs] = ' ';
342  
343   /* Loop does at least one digit */
344   do {
345       p[--ndgs] = '0' + (char) (value & 7);
346       value >>= 3;
347     }
348   while (ndgs > 0 && value != 0);
349  
350   /* Do leading zeros */
351   while (ndgs > 0)
352     p[--ndgs] = '0';
353 }
354
355 /****************************************************************************
356 Convert from octal string to long
357 ***************************************************************************/
358 static long unoct(char *p, int ndgs)
359 {
360   long value=0;
361   /* Converts octal string to long, ignoring any non-digit */
362
363   while (--ndgs)
364     {
365       if (isdigit(*p))
366         value = (value << 3) | (long) (*p - '0');
367
368       p++;
369     }
370
371   return value;
372 }
373
374 /****************************************************************************
375 Compare two strings in a slash insensitive way
376 ***************************************************************************/
377 int strslashcmp(char *s1,char *s2)
378 {
379   while(*s1 && *s2 &&
380         (*s1 == *s2
381          || tolower(*s1) == tolower(*s2)
382          || (*s1 == '\\' && *s2=='/')
383          || (*s1 == '/' && *s2=='\\'))) {
384           s1++; s2++;
385   }
386
387   return *s1-*s2;
388 }
389
390 /*
391  * general smb utility functions
392  */
393 /****************************************************************************
394 Set DOS file attributes
395 ***************************************************************************/
396 static int do_setrattr(char *fname, int attr, int setit)
397 {
398   /*
399    * First get the existing attribs from existing file
400    */
401   char *inbuf,*outbuf;
402   char *p;
403   pstring name;
404   int fattr;
405
406   strcpy(name,fname);
407   strcpy(fname,"\\");
408   strcat(fname,name);
409
410   inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
411   outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
412
413   if (!inbuf || !outbuf)
414     {
415       DEBUG(0,("out of memory\n"));
416       return False;
417     }
418
419   /* send an smb getatr message */
420
421   memset(outbuf,0,smb_size);
422   set_message(outbuf,0,2 + strlen(fname),True);
423   CVAL(outbuf,smb_com) = SMBgetatr;
424   SSVAL(outbuf,smb_tid,cnum);
425   setup_pkt(outbuf);
426
427   p = smb_buf(outbuf);
428   *p++ = 4;
429   strcpy(p,fname);
430   p += (strlen(fname)+1);
431   
432   *p++ = 4;
433   *p++ = 0;
434
435   send_smb(Client,outbuf);
436   receive_smb(Client,inbuf,CLIENT_TIMEOUT);
437
438   if (CVAL(inbuf,smb_rcls) != 0)
439     DEBUG(5,("getatr: %s\n",smb_errstr(inbuf)));
440   else
441     {
442       DEBUG(5,("\nattr 0x%X  time %d  size %d\n",
443                (int)CVAL(inbuf,smb_vwv0),
444                SVAL(inbuf,smb_vwv1),
445                SVAL(inbuf,smb_vwv3)));
446     }
447
448   fattr=CVAL(inbuf,smb_vwv0);
449
450   /* combine found attributes with bits to be set or reset */
451
452   attr=setit ? (fattr | attr) : (fattr & ~attr);
453
454   /* now try and set attributes by sending smb reset message */
455
456   /* clear out buffer and start again */
457   memset(outbuf,0,smb_size);
458   set_message(outbuf,8,4 + strlen(fname),True);
459   CVAL(outbuf,smb_com) = SMBsetatr;
460   SSVAL(outbuf,smb_tid,cnum);
461   setup_pkt(outbuf);
462
463   SSVAL(outbuf,smb_vwv0,attr);
464
465   p = smb_buf(outbuf);
466   *p++ = 4;      
467   strcpy(p,fname);
468   p += (strlen(fname)+1);
469   
470   *p++ = 4;
471   *p++ = 0;
472
473   send_smb(Client,outbuf);
474   receive_smb(Client,inbuf,CLIENT_TIMEOUT);
475   
476   if (CVAL(inbuf,smb_rcls) != 0)
477     {
478       DEBUG(0,("%s setting attributes on file %s\n",
479             smb_errstr(inbuf), fname));
480       free(inbuf);free(outbuf);
481       return(False);
482     }
483
484   free(inbuf);free(outbuf);
485   return(True);
486 }
487
488 /****************************************************************************
489 Create a file on a share
490 ***************************************************************************/
491 static BOOL smbcreat(file_info finfo, int *fnum, char *inbuf, char *outbuf)
492 {
493   char *p;
494   /* *must* be called with buffer ready malloc'ed */
495   /* open remote file */
496   
497   memset(outbuf,0,smb_size);
498   set_message(outbuf,3,2 + strlen(finfo.name),True);
499   CVAL(outbuf,smb_com) = SMBcreate;
500   SSVAL(outbuf,smb_tid,cnum);
501   setup_pkt(outbuf);
502   
503   SSVAL(outbuf,smb_vwv0,finfo.mode);
504   put_dos_date3(outbuf,smb_vwv1,finfo.mtime);
505   
506   p = smb_buf(outbuf);
507   *p++ = 4;      
508   strcpy(p,finfo.name);
509   
510   send_smb(Client,outbuf);
511   receive_smb(Client,inbuf,CLIENT_TIMEOUT);
512   
513   if (CVAL(inbuf,smb_rcls) != 0)
514     {
515       DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),
516                finfo.name));
517       return 0;
518     }
519   
520   *fnum = SVAL(inbuf,smb_vwv0);
521   return True;
522 }
523
524 /****************************************************************************
525 Write a file to a share
526 ***************************************************************************/
527 static BOOL smbwrite(int fnum, int n, int low, int high, int left,
528                      char *bufferp, char *inbuf, char *outbuf)
529 {
530   /* *must* be called with buffer ready malloc'ed */
531
532   memset(outbuf,0,smb_size);
533   set_message(outbuf,5,n + 3,True);
534   
535   memcpy(smb_buf(outbuf)+3, bufferp, n);
536   
537   set_message(outbuf,5,n + 3, False);
538   CVAL(outbuf,smb_com) = SMBwrite;
539   SSVAL(outbuf,smb_tid,cnum);
540   setup_pkt(outbuf);
541   
542   SSVAL(outbuf,smb_vwv0,fnum);
543   SSVAL(outbuf,smb_vwv1,n);
544   SIVAL(outbuf,smb_vwv2,low);
545   SSVAL(outbuf,smb_vwv4,left);
546   CVAL(smb_buf(outbuf),0) = 1;
547   SSVAL(smb_buf(outbuf),1,n);
548
549   send_smb(Client,outbuf); 
550   receive_smb(Client,inbuf,CLIENT_TIMEOUT);
551   
552   if (CVAL(inbuf,smb_rcls) != 0)
553     {
554       DEBUG(0,("%s writing remote file\n",smb_errstr(inbuf)));
555       return False;
556     }
557   
558   if (n != SVAL(inbuf,smb_vwv0))
559     {
560       DEBUG(0,("Error: only wrote %d bytes out of %d\n",
561                SVAL(inbuf,smb_vwv0), n));
562       return False;
563     }
564
565   return True;
566 }
567
568 /****************************************************************************
569 Close a file on a share
570 ***************************************************************************/
571 static BOOL smbshut(file_info finfo, int fnum, char *inbuf, char *outbuf)
572 {
573   /* *must* be called with buffer ready malloc'ed */
574
575   memset(outbuf,0,smb_size);
576   set_message(outbuf,3,0,True);
577   CVAL(outbuf,smb_com) = SMBclose;
578   SSVAL(outbuf,smb_tid,cnum);
579   setup_pkt(outbuf);
580   
581   SSVAL(outbuf,smb_vwv0,fnum);
582   put_dos_date3(outbuf,smb_vwv1,finfo.mtime);
583   
584   DEBUG(3,("Setting date to %s (0x%X)",
585            asctime(LocalTime(&finfo.mtime)),
586            finfo.mtime));
587   
588   send_smb(Client,outbuf);
589   receive_smb(Client,inbuf,CLIENT_TIMEOUT);
590   
591   if (CVAL(inbuf,smb_rcls) != 0)
592     {
593       DEBUG(0,("%s closing remote file %s\n",smb_errstr(inbuf),
594                finfo.name));
595       return False;
596     }
597
598   return True;
599 }
600
601 /****************************************************************************
602 Verify existence of path on share
603 ***************************************************************************/
604 static BOOL smbchkpath(char *fname, char *inbuf, char *outbuf)
605 {
606   char *p;
607
608   memset(outbuf,0,smb_size);
609   set_message(outbuf,0,4 + strlen(fname),True);
610   CVAL(outbuf,smb_com) = SMBchkpth;
611   SSVAL(outbuf,smb_tid,cnum);
612   setup_pkt(outbuf);
613
614   p = smb_buf(outbuf);
615   *p++ = 4;
616   strcpy(p,fname);
617
618   send_smb(Client,outbuf);
619   receive_smb(Client,inbuf,CLIENT_TIMEOUT);
620
621   DEBUG(5,("smbchkpath: %s\n",smb_errstr(inbuf)));
622
623   return(CVAL(inbuf,smb_rcls) == 0);
624 }
625
626 /****************************************************************************
627 Make a directory on share
628 ***************************************************************************/
629 static BOOL smbmkdir(char *fname, char *inbuf, char *outbuf)
630 {
631   /* *must* be called with buffer ready malloc'ed */
632   char *p;
633
634   memset(outbuf,0,smb_size);
635   set_message(outbuf,0,2 + strlen(fname),True);
636   
637   CVAL(outbuf,smb_com) = SMBmkdir;
638   SSVAL(outbuf,smb_tid,cnum);
639   setup_pkt(outbuf);
640   
641   p = smb_buf(outbuf);
642   *p++ = 4;      
643   strcpy(p,fname);
644   
645   send_smb(Client,outbuf);
646   receive_smb(Client,inbuf,CLIENT_TIMEOUT);
647   
648   if (CVAL(inbuf,smb_rcls) != 0)
649     {
650       DEBUG(0,("%s making remote directory %s\n",
651                smb_errstr(inbuf),fname));
652       return(False);
653     }
654
655   return(True);
656 }
657
658 /****************************************************************************
659 Ensure a remote path exists (make if necessary)
660 ***************************************************************************/
661 static BOOL ensurepath(char *fname, char *inbuf, char *outbuf)
662 {
663   /* *must* be called with buffer ready malloc'ed */
664   /* ensures path exists */
665
666   pstring partpath, ffname;
667   char *p=fname, *basehack;
668
669   *partpath = 0;
670
671   /* fname copied to ffname so can strtok */
672
673   strcpy(ffname, fname);
674
675   /* do a `basename' on ffname, so don't try and make file name directory */
676   if ((basehack=strrchr(ffname, '\\')) == NULL)
677     return True;
678   else
679     *basehack='\0';
680
681   p=strtok(ffname, "\\");
682
683   while (p)
684     {
685       strcat(partpath, p);
686
687       if (!smbchkpath(partpath, inbuf, outbuf)) {
688         if (!smbmkdir(partpath, inbuf, outbuf))
689           {
690             DEBUG(0, ("Error mkdirhiering\n"));
691             return False;
692           }
693         else
694           DEBUG(3, ("mkdirhiering %s\n", partpath));
695
696       }
697
698       strcat(partpath, "\\");
699       p = strtok(NULL,"/\\");
700     }
701
702     return True;
703 }
704
705 /*
706  * smbclient functions
707  */
708 /****************************************************************************
709 append one remote file to the tar file
710 ***************************************************************************/
711 static void do_atar(char *rname,char *lname,file_info *finfo1)
712 {
713   int fnum;
714   uint32 nread=0;
715   char *p;
716   char *inbuf,*outbuf;
717   file_info finfo;
718   BOOL close_done = False;
719   BOOL shallitime=True;
720   BOOL ignore_close_error = False;
721   char *dataptr=NULL;
722   int datalen=0;
723
724   struct timeval tp_start;
725   GetTimeOfDay(&tp_start);
726
727   if (finfo1) 
728     finfo = *finfo1;
729   else
730     finfo = def_finfo;
731
732   inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
733   outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
734
735   if (!inbuf || !outbuf)
736     {
737       DEBUG(0,("out of memory\n"));
738       return;
739     }
740
741   memset(outbuf,0,smb_size);
742   set_message(outbuf,15,1 + strlen(rname),True);
743
744   CVAL(outbuf,smb_com) = SMBopenX;
745   SSVAL(outbuf,smb_tid,cnum);
746   setup_pkt(outbuf);
747
748   SSVAL(outbuf,smb_vwv0,0xFF);
749   SSVAL(outbuf,smb_vwv2,1);
750   SSVAL(outbuf,smb_vwv3,(DENY_NONE<<4));
751   SSVAL(outbuf,smb_vwv4,aSYSTEM | aHIDDEN);
752   SSVAL(outbuf,smb_vwv5,aSYSTEM | aHIDDEN);
753   SSVAL(outbuf,smb_vwv8,1);
754
755   p = smb_buf(outbuf);
756   strcpy(p,rname);
757   p = skip_string(p,1);
758
759   dos_clean_name(rname);
760
761   /* do a chained openX with a readX? */  
762   if (finfo.size > 0)
763     {
764       SSVAL(outbuf,smb_vwv0,SMBreadX);
765       SSVAL(outbuf,smb_vwv1,PTR_DIFF(p,outbuf) - 4);
766       memset(p,0,200);
767       p -= smb_wct;
768       SSVAL(p,smb_wct,10);
769       SSVAL(p,smb_vwv0,0xFF);
770       SSVAL(p,smb_vwv5,MIN(max_xmit-500,finfo.size));
771       SSVAL(p,smb_vwv9,MIN(0xFFFF,finfo.size));
772       smb_setlen(outbuf,smb_len(outbuf)+11*2+1);  
773     }
774   
775   send_smb(Client,outbuf);
776   receive_smb(Client,inbuf,CLIENT_TIMEOUT);
777
778   if (CVAL(inbuf,smb_rcls) != 0)
779     {
780       if (CVAL(inbuf,smb_rcls) == ERRSRV &&
781           SVAL(inbuf,smb_err) == ERRnoresource &&
782           reopen_connection(inbuf,outbuf))
783         {
784           do_atar(rname,lname,finfo1);
785           free(inbuf);free(outbuf);
786           return;
787         }
788
789       DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),rname));
790       free(inbuf);free(outbuf);
791       return;
792     }
793
794   strcpy(finfo.name,rname);
795   if (!finfo1)
796     {
797       finfo.mode = SVAL(inbuf,smb_vwv3);
798       finfo.size = IVAL(inbuf,smb_vwv4);
799       finfo.mtime = make_unix_date3(inbuf+smb_vwv6);
800       finfo.atime = finfo.ctime = finfo.mtime;
801     }
802
803   DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode));
804
805   fnum = SVAL(inbuf,smb_vwv2);
806
807   if (tar_inc && !(finfo.mode & aARCH))
808     {
809       DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
810       shallitime=0;
811     }
812   else
813     {
814       if (SVAL(inbuf,smb_vwv0) == SMBreadX)
815         {
816           p = (inbuf+4+SVAL(inbuf,smb_vwv1)) - smb_wct;
817           datalen = SVAL(p,smb_vwv5);
818           dataptr = inbuf + 4 + SVAL(p,smb_vwv6);
819         }
820       else
821         {
822           dataptr = NULL;
823           datalen = 0;
824         }
825
826       DEBUG(2,("getting file %s of size %d bytes as a tar file %s",
827                finfo.name,
828                finfo.size,
829                lname));
830       
831       /* write a tar header, don't bother with mode - just set to 100644 */
832       writetarheader(tarhandle, rname, finfo.size, finfo.mtime, "100644 \0");
833       
834       while (nread < finfo.size && !close_done)
835         {
836           int method = -1;
837           static BOOL can_chain_close=True;
838
839           p=NULL;
840           
841           DEBUG(3,("nread=%d\n",nread));
842           
843           /* 3 possible read types. readbraw if a large block is required.
844              readX + close if not much left and read if neither is supported */
845
846           /* we might have already read some data from a chained readX */
847           if (dataptr && datalen>0)
848             method=3;
849           
850           /* if we can finish now then readX+close */
851           if (method<0 && can_chain_close && (Protocol >= PROTOCOL_LANMAN1) && 
852               ((finfo.size - nread) < 
853                (max_xmit - (2*smb_size + 13*SIZEOFWORD + 300))))
854             method = 0;
855           
856           /* if we support readraw then use that */
857           if (method<0 && readbraw_supported)
858             method = 1;
859           
860           /* if we can then use readX */
861           if (method<0 && (Protocol >= PROTOCOL_LANMAN1))
862             method = 2;
863           
864           
865           switch (method)
866             {
867               /* use readX */
868             case 0:
869             case 2:
870               if (method == 0)
871                 close_done = True;
872               
873               /* use readX + close */
874               memset(outbuf,0,smb_size);
875               set_message(outbuf,10,0,True);
876               CVAL(outbuf,smb_com) = SMBreadX;
877               SSVAL(outbuf,smb_tid,cnum);
878               setup_pkt(outbuf);
879               
880               if (close_done)
881                 {
882                   CVAL(outbuf,smb_vwv0) = SMBclose;
883                   SSVAL(outbuf,smb_vwv1,PTR_DIFF(smb_buf(outbuf),outbuf) - 4);
884                 }
885               else
886                 CVAL(outbuf,smb_vwv0) = 0xFF;         
887               
888               
889               SSVAL(outbuf,smb_vwv2,fnum);
890               SIVAL(outbuf,smb_vwv3,nread);
891               SSVAL(outbuf,smb_vwv5,MIN(max_xmit-200,finfo.size - nread));
892               SSVAL(outbuf,smb_vwv6,0);
893               SIVAL(outbuf,smb_vwv7,0);
894               SSVAL(outbuf,smb_vwv9,MIN(0xFFFF,finfo.size-nread));
895               
896               if (close_done)
897                 {
898                   p = smb_buf(outbuf);
899                   memset(p,0,9);
900                   
901                   CVAL(p,0) = 3;
902                   SSVAL(p,1,fnum);
903                   SIVALS(p,3,-1);
904                   
905                   /* now set the total packet length */
906                   smb_setlen(outbuf,smb_len(outbuf)+9);
907                 }
908               
909               send_smb(Client,outbuf);
910               receive_smb(Client,inbuf,CLIENT_TIMEOUT);
911               
912               if (CVAL(inbuf,smb_rcls) != 0)
913                 {
914                   DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf)));
915                   break;
916                 }
917               
918               if (close_done &&
919                   SVAL(inbuf,smb_vwv0) != SMBclose)
920                 {
921                   /* NOTE: WfWg sometimes just ignores the chained
922                      command! This seems to break the spec? */
923                   DEBUG(3,("Rejected chained close?\n"));
924                   close_done = False;
925                   can_chain_close = False;
926                   ignore_close_error = True;
927                 }
928               
929               datalen = SVAL(inbuf,smb_vwv5);
930               dataptr = inbuf + 4 + SVAL(inbuf,smb_vwv6);
931               break;
932               
933               
934               /* use readbraw */
935             case 1:
936               {
937                 static int readbraw_size = 0xFFFF;
938                 
939                 extern int Client;
940                 memset(outbuf,0,smb_size);
941                 set_message(outbuf,8,0,True);
942                 CVAL(outbuf,smb_com) = SMBreadbraw;
943                 SSVAL(outbuf,smb_tid,cnum);
944                 setup_pkt(outbuf);
945                 SSVAL(outbuf,smb_vwv0,fnum);
946                 SIVAL(outbuf,smb_vwv1,nread);
947                 SSVAL(outbuf,smb_vwv3,MIN(finfo.size-nread,readbraw_size));
948                 SSVAL(outbuf,smb_vwv4,0);
949                 SIVALS(outbuf,smb_vwv5,-1);
950                 send_smb(Client,outbuf);
951                 
952                 /* Now read the raw data into the buffer and write it */          
953                 if(read_smb_length(Client,inbuf,0) == -1) {
954                   DEBUG(0,("Failed to read length in readbraw\n"));         
955                   exit(1);
956                 }
957                 
958                 /* Even though this is not an smb message, smb_len
959                    returns the generic length of an smb message */
960                 datalen = smb_len(inbuf);
961                 
962                 if (datalen == 0)
963                   {
964                     /* we got a readbraw error */
965                     DEBUG(4,("readbraw error - reducing size\n"));
966                     readbraw_size = (readbraw_size * 9) / 10;
967                     
968                     if (readbraw_size < max_xmit)
969                       {
970                         DEBUG(0,("disabling readbraw\n"));
971                         readbraw_supported = False;
972                       }
973
974                     dataptr=NULL;
975                     continue;
976                   }
977
978                 if(read_data(Client,inbuf,datalen) != datalen) {
979                   DEBUG(0,("Failed to read data in readbraw\n"));
980                   exit(1);
981                 }
982                 dataptr = inbuf;
983               }
984               break;
985
986             case 3:
987               /* we've already read some data with a chained readX */
988               break;
989               
990             default:
991               /* use plain read */
992               memset(outbuf,0,smb_size);
993               set_message(outbuf,5,0,True);
994               CVAL(outbuf,smb_com) = SMBread;
995               SSVAL(outbuf,smb_tid,cnum);
996               setup_pkt(outbuf);
997               
998               SSVAL(outbuf,smb_vwv0,fnum);
999               SSVAL(outbuf,smb_vwv1,MIN(max_xmit-200,finfo.size - nread));
1000               SIVAL(outbuf,smb_vwv2,nread);
1001               SSVAL(outbuf,smb_vwv4,finfo.size - nread);
1002               
1003               send_smb(Client,outbuf);
1004               receive_smb(Client,inbuf,CLIENT_TIMEOUT);
1005               
1006               if (CVAL(inbuf,smb_rcls) != 0)
1007                 {
1008                   DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf)));
1009                   break;
1010                 }
1011               
1012               datalen = SVAL(inbuf,smb_vwv0);
1013               dataptr = smb_buf(inbuf) + 3;
1014               break;
1015             }
1016           
1017           
1018           /* add received bits of file to buffer - dotarbuf will
1019            * write out in 512 byte intervals */
1020           if (dotarbuf(tarhandle,dataptr,datalen) != datalen)
1021             {
1022               DEBUG(0,("Error writing local file\n"));
1023               break;
1024             }
1025           
1026           nread += datalen;
1027           if (datalen == 0) 
1028             {
1029               DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname));
1030               break;
1031             }
1032
1033           dataptr=NULL;
1034           datalen=0;
1035         }
1036       
1037       /* round tar file to nearest block */
1038       if (finfo.size % TBLOCK)
1039         dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
1040       
1041       ntarf++;
1042     }
1043   
1044   if (!close_done)
1045     {
1046       memset(outbuf,0,smb_size);
1047       set_message(outbuf,3,0,True);
1048       CVAL(outbuf,smb_com) = SMBclose;
1049       SSVAL(outbuf,smb_tid,cnum);
1050       setup_pkt(outbuf);
1051       
1052       SSVAL(outbuf,smb_vwv0,fnum);
1053       SIVALS(outbuf,smb_vwv1,-1);
1054       
1055       send_smb(Client,outbuf);
1056       receive_smb(Client,inbuf,CLIENT_TIMEOUT);
1057       
1058       if (!ignore_close_error && CVAL(inbuf,smb_rcls) != 0)
1059         {
1060           DEBUG(0,("Error %s closing remote file\n",smb_errstr(inbuf)));
1061           free(inbuf);free(outbuf);
1062           return;
1063         }
1064     }
1065
1066   if (shallitime)
1067     {
1068       struct timeval tp_end;
1069       int this_time;
1070
1071       /* if shallitime is true then we didn't skip */
1072       if (tar_reset) (void) do_setrattr(finfo.name, aARCH, ATTRRESET);
1073       
1074       GetTimeOfDay(&tp_end);
1075       this_time = 
1076         (tp_end.tv_sec - tp_start.tv_sec)*1000 +
1077           (tp_end.tv_usec - tp_start.tv_usec)/1000;
1078       get_total_time_ms += this_time;
1079       get_total_size += finfo.size;
1080
1081       /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */
1082       DEBUG(2,("(%g kb/s) (average %g kb/s)\n",
1083                finfo.size / MAX(0.001, (1.024*this_time)),
1084                get_total_size / MAX(0.001, (1.024*get_total_time_ms))));
1085     }
1086   
1087   free(inbuf);free(outbuf);
1088 }
1089
1090 /****************************************************************************
1091 Append single file to tar file (or not)
1092 ***************************************************************************/
1093 static void do_tar(file_info *finfo)
1094 {
1095   pstring rname;
1096
1097   if (strequal(finfo->name,".") || strequal(finfo->name,".."))
1098     return;
1099
1100   /* Is it on the exclude list ? */
1101   if (!tar_excl && clipn) {
1102     pstring exclaim;
1103
1104     strcpy(exclaim, cur_dir);
1105     *(exclaim+strlen(exclaim)-1)='\0';
1106
1107     if (clipfind(cliplist, clipn, exclaim)) {
1108       DEBUG(3,("Skipping directory %s\n", exclaim));
1109       return;
1110     }
1111
1112     strcat(exclaim, "\\");
1113     strcat(exclaim, finfo->name);
1114
1115     if (clipfind(cliplist, clipn, exclaim)) {
1116       DEBUG(3,("Skipping file %s\n", exclaim));
1117       return;
1118     }
1119   }
1120
1121   if (finfo->mode & aDIR)
1122     {
1123       pstring saved_curdir;
1124       pstring mtar_mask;
1125       char *inbuf,*outbuf;
1126
1127       inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
1128       outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
1129
1130       if (!inbuf || !outbuf)
1131         {
1132           DEBUG(0,("out of memory\n"));
1133           return;
1134         }
1135
1136       strcpy(saved_curdir,cur_dir);
1137
1138       strcat(cur_dir,finfo->name);
1139       strcat(cur_dir,"\\");
1140
1141       /* write a tar directory, don't bother with mode - just set it to
1142        * 40755 */
1143       writetarheader(tarhandle, cur_dir, 0, finfo->mtime, "040755 \0");
1144       strcpy(mtar_mask,cur_dir);
1145       strcat(mtar_mask,"*");
1146       
1147       do_dir((char *)inbuf,(char *)outbuf,mtar_mask,attribute,do_tar,recurse);
1148       strcpy(cur_dir,saved_curdir);
1149       free(inbuf);free(outbuf);
1150     }
1151   else
1152     {
1153       strcpy(rname,cur_dir);
1154       strcat(rname,finfo->name);
1155       do_atar(rname,finfo->name,finfo);
1156     }
1157 }
1158
1159 /****************************************************************************
1160 Convert from UNIX to DOS file names
1161 ***************************************************************************/
1162 static void unfixtarname(char *tptr, char *fp, int l)
1163 {
1164   /* remove '.' from start of file name, convert from unix /'s to
1165    * dos \'s in path. Kill any absolute path names.
1166    */
1167
1168   if (*fp == '.') fp++;
1169   if (*fp == '\\' || *fp == '/') fp++;
1170
1171 #ifdef KANJI
1172   while (l > 0) {
1173     if (is_shift_jis (*fp)) {
1174       *tptr++ = *fp++;
1175       *tptr++ = *fp++;
1176       l -= 2;
1177     } else if (is_kana (*fp)) {
1178       *tptr++ = *fp++;
1179       l--;
1180     } else if (*fp == '/') {
1181       *tptr++ = '\\';
1182       fp++;
1183       l--;
1184     } else {
1185       *tptr++ = *fp++;
1186       l--;
1187     }
1188   }
1189 #else
1190   while (l--) { *tptr=(*fp == '/') ? '\\' : *fp; tptr++; fp++; }
1191 #endif
1192 }
1193
1194 static void do_tarput()
1195 {
1196   file_info finfo;
1197   int nread=0, bufread;
1198   char *inbuf,*outbuf; 
1199   int fsize=0;
1200   int fnum;
1201   struct timeval tp_start;
1202   BOOL tskip=False;       /* We'll take each file as it comes */
1203
1204   GetTimeOfDay(&tp_start);
1205   
1206   inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
1207   outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
1208   
1209   if (!inbuf || !outbuf)
1210     {
1211       DEBUG(0,("out of memory\n"));
1212       return;
1213     }
1214   
1215   /*
1216    * Must read in tbufsiz dollops
1217    */
1218
1219   /* These should be the only reads in clitar.c */
1220   while ((bufread=read(tarhandle, tarbuf, tbufsiz))>0) {
1221     char *bufferp, *endofbuffer;
1222     int chunk;
1223
1224     /* Code to handle a short read.
1225      * We always need a TBLOCK full of stuff
1226      */
1227     if (bufread % TBLOCK) {
1228       int lchunk=TBLOCK-(bufread % TBLOCK);
1229       int lread;
1230
1231       /* It's a shorty - a short read that is */
1232       DEBUG(3, ("Short read, read %d so far (need %d)\n", bufread, lchunk));
1233
1234       while ((lread=read(tarhandle, tarbuf+bufread, lchunk))>0) {
1235         bufread+=lread;
1236         if (!(lchunk-=lread)) break;
1237       }
1238
1239       /* If we've reached EOF then that must be a short file */
1240       if (lread<=0) break;
1241     }
1242
1243     bufferp=tarbuf; 
1244     endofbuffer=tarbuf+bufread;
1245
1246     if (tskip) {
1247       if (fsize<bufread) {
1248         tskip=False;
1249         bufferp+=fsize;
1250         fsize=0;
1251       } else {
1252         if (fsize==bufread) tskip=False;
1253         fsize-=bufread;
1254         continue;
1255       }
1256     }
1257
1258     do {
1259       if (!fsize)
1260         {
1261           switch (readtarheader((union hblock *) bufferp, &finfo, cur_dir))
1262             {
1263             case -2:             /* something dodgy but not fatal about this */
1264               DEBUG(0, ("skipping %s...\n", finfo.name));
1265               bufferp+=TBLOCK;   /* header - like a link */
1266               continue;
1267             case -1:
1268               DEBUG(0, ("abandoning restore\n"));
1269               free(inbuf); free(outbuf);
1270               return;
1271             case 0: /* chksum is zero - we assume that one all zero
1272                      *header block will do for eof */
1273               DEBUG(0,
1274                     ("total of %d tar files restored to share\n", ntarf));
1275               free(inbuf); free(outbuf);
1276               return;
1277             default:
1278               break;
1279             }
1280
1281           tskip=clipn
1282             && (clipfind(cliplist, clipn, finfo.name) ^ tar_excl);
1283           if (tskip) {
1284             bufferp+=TBLOCK;
1285             if (finfo.mode & aDIR)
1286               continue;
1287             else if ((fsize=finfo.size) % TBLOCK) {
1288               fsize+=TBLOCK-(fsize%TBLOCK);
1289             }
1290             if (fsize<endofbuffer-bufferp) {
1291               bufferp+=fsize;
1292               fsize=0;
1293               continue;
1294             } else {
1295               fsize-=endofbuffer-bufferp;
1296               break;
1297             }
1298           }
1299
1300           if (finfo.mode & aDIR)
1301             {
1302               if (!smbchkpath(finfo.name, inbuf, outbuf)
1303                   && !smbmkdir(finfo.name, inbuf, outbuf))
1304                 {
1305                   DEBUG(0, ("abandoning restore\n"));
1306                   free(inbuf); free(outbuf);
1307                   return;
1308               }
1309               else
1310                 {
1311                   bufferp+=TBLOCK;
1312                   continue;
1313                 }
1314             }
1315           
1316           fsize=finfo.size;
1317
1318           if (ensurepath(finfo.name, inbuf, outbuf)
1319               && !smbcreat(finfo, &fnum, inbuf, outbuf))
1320             {
1321               DEBUG(0, ("abandoning restore\n"));
1322               free(inbuf);free(outbuf);
1323               return;
1324             }
1325
1326           DEBUG(0,("restore tar file %s of size %d bytes\n",
1327                    finfo.name,finfo.size));
1328
1329           nread=0;
1330           if ((bufferp+=TBLOCK) >= endofbuffer) break;    
1331         } /* if (!fsize) */
1332         
1333       /* write out the file in chunk sized chunks - don't
1334        * go past end of buffer though */
1335       chunk=(fsize-nread < endofbuffer - bufferp)
1336         ? fsize - nread : endofbuffer - bufferp;
1337       
1338       while (chunk > 0) {
1339         int minichunk=MIN(chunk, max_xmit-200);
1340         
1341         if (!smbwrite(fnum, /* file descriptor */
1342                       minichunk, /* n */
1343                       nread, /* offset low */
1344                       0, /* offset high - not implemented */
1345                       fsize-nread, /* left - only hint to server */
1346                       bufferp,
1347                       inbuf,
1348                       outbuf))
1349           {
1350             DEBUG(0, ("Error writing remote file\n"));
1351             free(inbuf); free(outbuf);
1352             return;
1353           }
1354         DEBUG(5, ("chunk writing fname=%s fnum=%d nread=%d minichunk=%d chunk=%d size=%d\n", finfo.name, fnum, nread, minichunk, chunk, fsize));
1355         
1356         bufferp+=minichunk; nread+=minichunk;
1357         chunk-=minichunk;
1358       }
1359       
1360       if (nread>=fsize)
1361         {
1362           if (!smbshut(finfo, fnum, inbuf, outbuf))
1363             {
1364               DEBUG(0, ("Error closing remote file\n"));
1365               free(inbuf);free(outbuf);
1366               return;
1367             }
1368           if (fsize % TBLOCK) bufferp+=TBLOCK - (fsize % TBLOCK);
1369           DEBUG(5, ("bufferp is now %d (psn=%d)\n",
1370                     (long) bufferp, (long)(bufferp - tarbuf)));
1371           ntarf++;
1372           fsize=0;
1373         }
1374     } while (bufferp < endofbuffer);
1375   }
1376
1377   DEBUG(0, ("premature eof on tar file ?\n"));
1378   DEBUG(0,("total of %d tar files restored to share\n", ntarf));
1379
1380   free(inbuf); free(outbuf);
1381 }
1382
1383 /*
1384  * samba interactive commands
1385  */
1386
1387 /****************************************************************************
1388 Blocksize command
1389 ***************************************************************************/
1390 void cmd_block(void)
1391 {
1392   fstring buf;
1393   int block;
1394
1395   if (!next_token(NULL,buf,NULL))
1396     {
1397       DEBUG(0, ("blocksize <n>\n"));
1398       return;
1399     }
1400
1401   block=atoi(buf);
1402   if (block < 0 || block > 65535)
1403     {
1404       DEBUG(0, ("blocksize out of range"));
1405       return;
1406     }
1407
1408   blocksize=block;
1409   DEBUG(2,("blocksize is now %d\n", blocksize));
1410 }
1411
1412 /****************************************************************************
1413 command to set incremental / reset mode
1414 ***************************************************************************/
1415 void cmd_tarmode(void)
1416 {
1417   fstring buf;
1418
1419   while (next_token(NULL,buf,NULL)) {
1420     if (strequal(buf, "full"))
1421       tar_inc=False;
1422     else if (strequal(buf, "inc"))
1423       tar_inc=True;
1424     else if (strequal(buf, "reset"))
1425       tar_reset=True;
1426     else if (strequal(buf, "noreset"))
1427       tar_reset=False;
1428     else DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
1429   }
1430
1431   DEBUG(0, ("tarmode is now %s, %s\n",
1432             tar_inc ? "incremental" : "full",
1433             tar_reset ? "reset" : "noreset"));
1434 }
1435
1436 /****************************************************************************
1437 Feeble attrib command
1438 ***************************************************************************/
1439 void cmd_setmode(void)
1440 {
1441   char *q;
1442   fstring buf;
1443   pstring fname;
1444   int attra[2];
1445   int direct=1;
1446
1447   attra[0] = attra[1] = 0;
1448
1449   if (!next_token(NULL,buf,NULL))
1450     {
1451       DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
1452       return;
1453     }
1454
1455   strcpy(fname, cur_dir);
1456   strcat(fname, buf);
1457
1458   while (next_token(NULL,buf,NULL)) {
1459     q=buf;
1460
1461     while(*q)
1462       switch (*q++) {
1463       case '+': direct=1;
1464         break;
1465       case '-': direct=0;
1466         break;
1467       case 'r': attra[direct]|=aRONLY;
1468         break;
1469       case 'h': attra[direct]|=aHIDDEN;
1470         break;
1471       case 's': attra[direct]|=aSYSTEM;
1472         break;
1473       case 'a': attra[direct]|=aARCH;
1474         break;
1475       default: DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
1476         return;
1477       }
1478   }
1479
1480   if (attra[ATTRSET]==0 && attra[ATTRRESET]==0)
1481     {
1482       DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
1483       return;
1484     }
1485
1486   DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
1487   (void) do_setrattr(fname, attra[ATTRSET], ATTRSET);
1488   (void) do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
1489 }
1490
1491 /****************************************************************************
1492 Principal command for creating / extracting
1493 ***************************************************************************/
1494 void cmd_tar(char *inbuf, char *outbuf)
1495 {
1496   fstring buf;
1497   char **argl;
1498   int argcl;
1499
1500   if (!next_token(NULL,buf,NULL))
1501     {
1502       DEBUG(0,("tar <c|x>[IXbga] <filename>\n"));
1503       return;
1504     }
1505
1506   argl=toktocliplist(&argcl, NULL);
1507   if (!tar_parseargs(argcl, argl, buf, 0))
1508     return;
1509
1510   process_tar(inbuf, outbuf);
1511
1512   free(argl);
1513 }
1514
1515 /****************************************************************************
1516 Command line (option) version
1517 ***************************************************************************/
1518 int process_tar(char *inbuf, char *outbuf)
1519 {
1520   initarbuf();
1521   switch(tar_type) {
1522   case 'x':
1523     do_tarput();
1524     free(tarbuf);
1525     close(tarhandle);
1526     break;
1527   case 'r':
1528   case 'c':
1529     if (clipn && tar_excl) {
1530       int i;
1531       pstring tarmac;
1532
1533       for (i=0; i<clipn; i++) {
1534         DEBUG(0,("arg %d = %s\n", i, cliplist[i]));
1535
1536         if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
1537           *(cliplist[i]+strlen(cliplist[i])-1)='\0';
1538         }
1539         
1540         if (strrchr(cliplist[i], '\\')) {
1541           pstring saved_dir;
1542           
1543           strcpy(saved_dir, cur_dir);
1544           
1545           if (*cliplist[i]=='\\') {
1546             strcpy(tarmac, cliplist[i]);
1547           } else {
1548             strcpy(tarmac, cur_dir);
1549             strcat(tarmac, cliplist[i]);
1550           }
1551           strcpy(cur_dir, tarmac);
1552           *(strrchr(cur_dir, '\\')+1)='\0';
1553
1554           do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse);
1555           strcpy(cur_dir,saved_dir);
1556         } else {
1557           strcpy(tarmac, cur_dir);
1558           strcat(tarmac, cliplist[i]);
1559           do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse);
1560         }
1561       }
1562     } else {
1563       pstring mask;
1564       strcpy(mask,cur_dir);
1565       strcat(mask,"\\*");
1566       do_dir((char *)inbuf,(char *)outbuf,mask,attribute,do_tar,recurse);
1567     }
1568     
1569     if (ntarf) dotareof(tarhandle);
1570     close(tarhandle);
1571     free(tarbuf);
1572     
1573     DEBUG(0, ("tar: dumped %d tar files\n", ntarf));
1574     break;
1575   }
1576
1577   return(0);
1578 }
1579
1580 /****************************************************************************
1581 Find a token (filename) in a clip list
1582 ***************************************************************************/
1583 int clipfind(char **aret, int ret, char *tok)
1584 {
1585   if (aret==NULL) return 0;
1586
1587   /* ignore leading slashes or dots in token */
1588   while(strchr("/\\.", *tok)) tok++;
1589
1590   while(ret--) {
1591     char *pkey=*aret++;
1592
1593     /* ignore leading slashes or dots in list */
1594     while(strchr("/\\.", *pkey)) pkey++;
1595
1596     if (!strslashcmp(pkey, tok)) return 1;
1597   }
1598
1599   return 0;
1600 }
1601
1602 /****************************************************************************
1603 Parse tar arguments. Sets tar_type, tar_excl, etc.
1604 ***************************************************************************/
1605 int tar_parseargs(int argc, char *argv[], char *Optarg, int Optind)
1606 {
1607   char tar_clipfl='\0';
1608
1609   /* Reset back to defaults - could be from interactive version 
1610    * reset mode and archive mode left as they are though
1611    */
1612   tar_type='\0';
1613   tar_excl=True;
1614
1615   while (*Optarg) 
1616     switch(*Optarg++) {
1617     case 'c':
1618       tar_type='c';
1619       break;
1620     case 'x':
1621       if (tar_type=='c') {
1622         printf("Tar must be followed by only one of c or x.\n");
1623         return 0;
1624       }
1625       tar_type='x';
1626       break;
1627     case 'b':
1628       if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) {
1629         DEBUG(0,("Option b must be followed by valid blocksize\n"));
1630         return 0;
1631       } else {
1632         Optind++;
1633       }
1634       break;
1635     case 'g':
1636       tar_inc=True;
1637       break;
1638     case 'N':
1639       if (Optind>=argc) {
1640         DEBUG(0,("Option N must be followed by valid file name\n"));
1641         return 0;
1642       } else {
1643         struct stat stbuf;
1644         extern time_t newer_than;
1645         
1646         if (sys_stat(argv[Optind], &stbuf) == 0) {
1647           newer_than = stbuf.st_mtime;
1648           DEBUG(1,("Getting files newer than %s",
1649                    asctime(LocalTime(&newer_than))));
1650           Optind++;
1651         } else {
1652           DEBUG(0,("Error setting newer-than time\n"));
1653           return 0;
1654         }
1655       }
1656       break;
1657     case 'a':
1658       tar_reset=True;
1659       break;
1660     case 'I':
1661       if (tar_clipfl) {
1662         DEBUG(0,("Only one of I,X must be specified\n"));
1663         return 0;
1664       }
1665       tar_clipfl='I';
1666       break;
1667     case 'X':
1668       if (tar_clipfl) {
1669         DEBUG(0,("Only one of I,X must be specified\n"));
1670         return 0;
1671       }
1672       tar_clipfl='X';
1673       break;
1674     default:
1675       DEBUG(0,("Unknown tar option\n"));
1676       return 0;
1677     }
1678
1679   if (!tar_type) {
1680     printf("Option T must be followed by one of c or x.\n");
1681     return 0;
1682   }
1683
1684   if (Optind>=argc || !strcmp(argv[Optind], "-")) {
1685     /* Sets tar handle to either 0 or 1, as appropriate */
1686     tarhandle=(tar_type=='c');
1687   } else {
1688     tar_excl=tar_clipfl!='X';
1689     
1690     if (Optind+1<argc) {
1691       cliplist=argv+Optind+1;
1692       clipn=argc-Optind-1;
1693     }
1694
1695     if ((tar_type=='x' && (tarhandle = open(argv[Optind], O_RDONLY)) == -1)
1696         || (tar_type=='c' && (tarhandle=creat(argv[Optind], 0644)) < 0))
1697       {
1698         DEBUG(0,("Error opening local file %s\n",argv[Optind]));
1699         return(0);
1700       }
1701   }
1702
1703   return 1;
1704 }