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