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