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