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