5aa59c8cffe6c711607419516ab7f425191b364f
[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 "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 NTSTATUS do_atar(const char *rname_in, char *lname,
118                     struct file_info *finfo1);
119 static NTSTATUS 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 NTSTATUS 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         NTSTATUS status = NT_STATUS_OK;
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                 status = NT_STATUS_NO_MEMORY;
640                 goto cleanup;
641         }
642
643         ftype = '0'; /* An ordinary file ... */
644
645         ZERO_STRUCT(finfo);
646
647         finfo.size  = finfo1 -> size;
648         finfo.mode  = finfo1 -> mode;
649         finfo.uid   = finfo1 -> uid;
650         finfo.gid   = finfo1 -> gid;
651         finfo.mtime_ts = finfo1 -> mtime_ts;
652         finfo.atime_ts = finfo1 -> atime_ts;
653         finfo.ctime_ts = finfo1 -> ctime_ts;
654
655         if (dry_run) {
656                 DEBUG(3,("skipping file %s of size %12.0f bytes\n", finfo1->name,
657                                 (double)finfo.size));
658                 shallitime=0;
659                 ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
660                 ntarf++;
661                 goto cleanup;
662         }
663
664         rname = clean_name(ctx, rname_in);
665         if (!rname) {
666                 status = NT_STATUS_NO_MEMORY;
667                 goto cleanup;
668         }
669
670         status = cli_open(cli, rname, O_RDONLY, DENY_NONE, &fnum);
671         if (!NT_STATUS_IS_OK(status)) {
672                 DEBUG(0,("%s opening remote file %s (%s)\n",
673                                 cli_errstr(cli),rname, client_get_cur_dir()));
674                 goto cleanup;
675         }
676
677         finfo.name = string_create_s(strlen(rname));
678         if (finfo.name == NULL) {
679                 DEBUG(0, ("Unable to allocate space for finfo.name in do_atar\n"));
680                 status = NT_STATUS_NO_MEMORY;
681                 goto cleanup;
682         }
683
684         safe_strcpy(finfo.name,rname, strlen(rname));
685
686         DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode));
687
688         if (tar_inc && !(finfo.mode & aARCH)) {
689                 DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
690                 shallitime=0;
691         } else if (!tar_system && (finfo.mode & aSYSTEM)) {
692                 DEBUG(4, ("skipping %s - system bit is set\n", finfo.name));
693                 shallitime=0;
694         } else if (!tar_hidden && (finfo.mode & aHIDDEN)) {
695                 DEBUG(4, ("skipping %s - hidden bit is set\n", finfo.name));
696                 shallitime=0;
697         } else {
698                 bool wrote_tar_header = False;
699
700                 DEBUG(3,("getting file %s of size %.0f bytes as a tar file %s",
701                         finfo.name, (double)finfo.size, lname));
702
703                 do {
704
705                         DEBUG(3,("nread=%.0f\n",(double)nread));
706
707                         datalen = cli_read(cli, fnum, data, nread, read_size);
708
709                         if (datalen == -1) {
710                                 DEBUG(0,("Error reading file %s : %s\n", rname, cli_errstr(cli)));
711                                 status = cli_nt_error(cli);
712                                 break;
713                         }
714
715                         nread += datalen;
716
717                         /* Only if the first read succeeds, write out the tar header. */
718                         if (!wrote_tar_header) {
719                                 /* write a tar header, don't bother with mode - just set to 100644 */
720                                 writetarheader(tarhandle, rname, finfo.size,
721                                         finfo.mtime_ts.tv_sec, "100644 \0", ftype);
722                                 wrote_tar_header = True;
723                         }
724
725                         /* if file size has increased since we made file size query, truncate
726                                 read so tar header for this file will be correct.
727                         */
728
729                         if (nread > finfo.size) {
730                                 datalen -= nread - finfo.size;
731                                 DEBUG(0,("File size change - truncating %s to %.0f bytes\n",
732                                                         finfo.name, (double)finfo.size));
733                         }
734
735                         /* add received bits of file to buffer - dotarbuf will
736                         * write out in 512 byte intervals */
737
738                         if (dotarbuf(tarhandle,data,datalen) != datalen) {
739                                 DEBUG(0,("Error writing to tar file - %s\n", strerror(errno)));
740                                 status = map_nt_error_from_unix(errno);
741                                 break;
742                         }
743
744                         if ( (datalen == 0) && (finfo.size != 0) ) {
745                                 status = NT_STATUS_UNSUCCESSFUL;
746                                 DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname));
747                                 break;
748                         }
749
750                         datalen=0;
751                 } while ( nread < finfo.size );
752
753                 if (wrote_tar_header) {
754                         /* pad tar file with zero's if we couldn't get entire file */
755                         if (nread < finfo.size) {
756                                 DEBUG(0, ("Didn't get entire file. size=%.0f, nread=%d\n",
757                                                         (double)finfo.size, (int)nread));
758                                 if (padit(data, (uint64_t)sizeof(data), finfo.size - nread)) {
759                                         status = map_nt_error_from_unix(errno);
760                                         DEBUG(0,("Error writing tar file - %s\n", strerror(errno)));
761                                 }
762                         }
763
764                         /* round tar file to nearest block */
765                         if (finfo.size % TBLOCK)
766                                 dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
767
768                         ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
769                         ntarf++;
770                 } else {
771                         DEBUG(4, ("skipping %s - initial read failed (file was locked ?)\n", finfo.name));
772                         shallitime=0;
773                         status = NT_STATUS_UNSUCCESSFUL;
774                 }
775         }
776
777         cli_close(cli, fnum);
778         fnum = -1;
779
780         if (shallitime) {
781                 struct timespec tp_end;
782                 int this_time;
783
784                 /* if shallitime is true then we didn't skip */
785                 if (tar_reset && !dry_run)
786                         (void) do_setrattr(finfo.name, aARCH, ATTRRESET);
787
788                 clock_gettime_mono(&tp_end);
789                 this_time = (tp_end.tv_sec - tp_start.tv_sec)*1000 + (tp_end.tv_nsec - tp_start.tv_nsec)/1000000;
790                 get_total_time_ms += this_time;
791                 get_total_size += finfo.size;
792
793                 if (tar_noisy) {
794                         DEBUG(0, ("%12.0f (%7.1f kb/s) %s\n",
795                                 (double)finfo.size, finfo.size / MAX(0.001, (1.024*this_time)),
796                                 finfo.name));
797                 }
798
799                 /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */
800                 DEBUG(3,("(%g kb/s) (average %g kb/s)\n",
801                                 finfo.size / MAX(0.001, (1.024*this_time)),
802                                 get_total_size / MAX(0.001, (1.024*get_total_time_ms))));
803         }
804
805   cleanup:
806
807         if (fnum != (uint16_t)-1) {
808                 cli_close(cli, fnum);
809                 fnum = -1;
810         }
811         TALLOC_FREE(ctx);
812         SAFE_FREE(data);
813         return status;
814 }
815
816 /****************************************************************************
817 Append single file to tar file (or not)
818 ***************************************************************************/
819
820 static NTSTATUS do_tar(struct cli_state *cli_state, struct file_info *finfo,
821                    const char *dir)
822 {
823         TALLOC_CTX *ctx = talloc_stackframe();
824         NTSTATUS status = NT_STATUS_OK;
825
826         if (strequal(finfo->name,"..") || strequal(finfo->name,"."))
827                 return NT_STATUS_OK;
828
829         /* Is it on the exclude list ? */
830         if (!tar_excl && clipn) {
831                 char *exclaim;
832
833                 DEBUG(5, ("Excl: strlen(cur_dir) = %d\n", (int)strlen(client_get_cur_dir())));
834
835                 exclaim = talloc_asprintf(ctx,
836                                 "%s\\%s",
837                                 client_get_cur_dir(),
838                                 finfo->name);
839                 if (!exclaim) {
840                         return NT_STATUS_NO_MEMORY;
841                 }
842
843                 DEBUG(5, ("...tar_re_search: %d\n", tar_re_search));
844
845                 if ((!tar_re_search && clipfind(cliplist, clipn, exclaim)) ||
846                                 (tar_re_search && mask_match_list(exclaim, cliplist, clipn, True))) {
847                         DEBUG(3,("Skipping file %s\n", exclaim));
848                         TALLOC_FREE(exclaim);
849                         return NT_STATUS_OK;
850                 }
851                 TALLOC_FREE(exclaim);
852         }
853
854         if (finfo->mode & aDIR) {
855                 char *saved_curdir = NULL;
856                 char *new_cd = NULL;
857                 char *mtar_mask = NULL;
858
859                 saved_curdir = talloc_strdup(ctx, client_get_cur_dir());
860                 if (!saved_curdir) {
861                         return NT_STATUS_NO_MEMORY;
862                 }
863
864                 DEBUG(5, ("strlen(cur_dir)=%d, \
865 strlen(finfo->name)=%d\nname=%s,cur_dir=%s\n",
866                         (int)strlen(saved_curdir),
867                         (int)strlen(finfo->name), finfo->name, saved_curdir));
868
869                 new_cd = talloc_asprintf(ctx,
870                                 "%s%s\\",
871                                 client_get_cur_dir(),
872                                 finfo->name);
873                 if (!new_cd) {
874                         return NT_STATUS_NO_MEMORY;
875                 }
876                 client_set_cur_dir(new_cd);
877
878                 DEBUG(5, ("Writing a dir, Name = %s\n", client_get_cur_dir()));
879
880                 /* write a tar directory, don't bother with mode - just
881                  * set it to 40755 */
882                 writetarheader(tarhandle, client_get_cur_dir(), 0,
883                                 finfo->mtime_ts.tv_sec, "040755 \0", '5');
884                 if (tar_noisy) {
885                         DEBUG(0,("                directory %s\n",
886                                 client_get_cur_dir()));
887                 }
888                 ntarf++;  /* Make sure we have a file on there */
889                 mtar_mask = talloc_asprintf(ctx,
890                                 "%s*",
891                                 client_get_cur_dir());
892                 if (!mtar_mask) {
893                         return NT_STATUS_NO_MEMORY;
894                 }
895                 DEBUG(5, ("Doing list with mtar_mask: %s\n", mtar_mask));
896                 status = do_list(mtar_mask, attribute, do_tar, False, True);
897                 client_set_cur_dir(saved_curdir);
898                 TALLOC_FREE(saved_curdir);
899                 TALLOC_FREE(new_cd);
900                 TALLOC_FREE(mtar_mask);
901         } else {
902                 char *rname = talloc_asprintf(ctx,
903                                         "%s%s",
904                                         client_get_cur_dir(),
905                                         finfo->name);
906                 if (!rname) {
907                         return NT_STATUS_NO_MEMORY;
908                 }
909                 status = do_atar(rname,finfo->name,finfo);
910                 TALLOC_FREE(rname);
911         }
912         return status;
913 }
914
915 /****************************************************************************
916 Convert from UNIX to DOS file names
917 ***************************************************************************/
918
919 static void unfixtarname(char *tptr, char *fp, int l, bool first)
920 {
921         /* remove '.' from start of file name, convert from unix /'s to
922          * dos \'s in path. Kill any absolute path names. But only if first!
923          */
924
925         DEBUG(5, ("firstb=%lX, secondb=%lX, len=%i\n", (long)tptr, (long)fp, l));
926
927         if (first) {
928                 if (*fp == '.') {
929                         fp++;
930                         l--;
931                 }
932                 if (*fp == '\\' || *fp == '/') {
933                         fp++;
934                         l--;
935                 }
936         }
937
938         safe_strcpy(tptr, fp, l);
939         string_replace(tptr, '/', '\\');
940 }
941
942 /****************************************************************************
943 Move to the next block in the buffer, which may mean read in another set of
944 blocks. FIXME, we should allow more than one block to be skipped.
945 ****************************************************************************/
946
947 static int next_block(char *ltarbuf, char **bufferp, int bufsiz)
948 {
949         int bufread, total = 0;
950
951         DEBUG(5, ("Advancing to next block: %0lx\n", (unsigned long)*bufferp));
952         *bufferp += TBLOCK;
953         total = TBLOCK;
954
955         if (*bufferp >= (ltarbuf + bufsiz)) {
956
957                 DEBUG(5, ("Reading more data into ltarbuf ...\n"));
958
959                 /*
960                  * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>
961                  * Fixes bug where read can return short if coming from
962                  * a pipe.
963                  */
964
965                 bufread = read(tarhandle, ltarbuf, bufsiz);
966                 total = bufread;
967
968                 while (total < bufsiz) {
969                         if (bufread < 0) { /* An error, return false */
970                                 return (total > 0 ? -2 : bufread);
971                         }
972                         if (bufread == 0) {
973                                 if (total <= 0) {
974                                         return -2;
975                                 }
976                                 break;
977                         }
978                         bufread = read(tarhandle, &ltarbuf[total], bufsiz - total);
979                         total += bufread;
980                 }
981
982                 DEBUG(5, ("Total bytes read ... %i\n", total));
983
984                 *bufferp = ltarbuf;
985         }
986
987         return(total);
988 }
989
990 /* Skip a file, even if it includes a long file name? */
991 static int skip_file(int skipsize)
992 {
993         int dsize = skipsize;
994
995         DEBUG(5, ("Skiping file. Size = %i\n", skipsize));
996
997         /* FIXME, we should skip more than one block at a time */
998
999         while (dsize > 0) {
1000                 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
1001                         DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1002                         return(False);
1003                 }
1004                 dsize -= TBLOCK;
1005         }
1006
1007         return(True);
1008 }
1009
1010 /*************************************************************
1011  Get a file from the tar file and store it.
1012  When this is called, tarbuf already contains the first
1013  file block. This is a bit broken & needs fixing.
1014 **************************************************************/
1015
1016 static int get_file(file_info2 finfo)
1017 {
1018         uint16_t fnum = (uint16_t) -1;
1019         int pos = 0, dsize = 0, bpos = 0;
1020         uint64_t rsize = 0;
1021         NTSTATUS status;
1022
1023         DEBUG(5, ("get_file: file: %s, size %.0f\n", finfo.name, (double)finfo.size));
1024
1025         if (!ensurepath(finfo.name)) {
1026                 DEBUG(0, ("abandoning restore\n"));
1027                 return False;
1028         }
1029
1030         status = cli_open(cli, finfo.name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE, &fnum);
1031         if (!NT_STATUS_IS_OK(status)) {
1032                 DEBUG(0, ("abandoning restore\n"));
1033                 return False;
1034         }
1035
1036         /* read the blocks from the tar file and write to the remote file */
1037
1038         rsize = finfo.size;  /* This is how much to write */
1039
1040         while (rsize > 0) {
1041
1042                 /* We can only write up to the end of the buffer */
1043                 dsize = MIN(tbufsiz - (buffer_p - tarbuf) - bpos, 65520); /* Calculate the size to write */
1044                 dsize = MIN(dsize, rsize);  /* Should be only what is left */
1045                 DEBUG(5, ("writing %i bytes, bpos = %i ...\n", dsize, bpos));
1046
1047                 if (cli_write(cli, fnum, 0, buffer_p + bpos, pos, dsize) != dsize) {
1048                         DEBUG(0, ("Error writing remote file\n"));
1049                         return 0;
1050                 }
1051
1052                 rsize -= dsize;
1053                 pos += dsize;
1054
1055                 /* Now figure out how much to move in the buffer */
1056
1057                 /* FIXME, we should skip more than one block at a time */
1058
1059                 /* First, skip any initial part of the part written that is left over */
1060                 /* from the end of the first TBLOCK                                   */
1061
1062                 if ((bpos) && ((bpos + dsize) >= TBLOCK)) {
1063                         dsize -= (TBLOCK - bpos);  /* Get rid of the end of the first block */
1064                         bpos = 0;
1065
1066                         if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {  /* and skip the block */
1067                                 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1068                                 return False;
1069                         }
1070                 }
1071
1072                 /*
1073                  * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>.
1074                  * If the file being extracted is an exact multiple of
1075                  * TBLOCK bytes then we don't want to extract the next
1076                  * block from the tarfile here, as it will be done in
1077                  * the caller of get_file().
1078                  */
1079
1080                 while (((rsize != 0) && (dsize >= TBLOCK)) ||
1081                                 ((rsize == 0) && (dsize > TBLOCK))) {
1082
1083                         if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
1084                                 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1085                                 return False;
1086                         }
1087
1088                         dsize -= TBLOCK;
1089                 }
1090                 bpos = dsize;
1091         }
1092
1093         /* Now close the file ... */
1094
1095         if (!NT_STATUS_IS_OK(cli_close(cli, fnum))) {
1096                 DEBUG(0, ("Error %s closing remote file\n",
1097                         cli_errstr(cli)));
1098                 return(False);
1099         }
1100
1101         /* Now we update the creation date ... */
1102         DEBUG(5, ("Updating creation date on %s\n", finfo.name));
1103
1104         if (!NT_STATUS_IS_OK(cli_setatr(cli, finfo.name, finfo.mode, finfo.mtime_ts.tv_sec))) {
1105                 if (tar_real_noisy) {
1106                         DEBUG(0, ("Could not set time on file: %s\n", finfo.name));
1107                         /*return(False); */ /* Ignore, as Win95 does not allow changes */
1108                 }
1109         }
1110
1111         ntarf++;
1112         DEBUG(0, ("restore tar file %s of size %.0f bytes\n", finfo.name, (double)finfo.size));
1113         return(True);
1114 }
1115
1116 /* Create a directory.  We just ensure that the path exists and return as there
1117    is no file associated with a directory
1118 */
1119 static int get_dir(file_info2 finfo)
1120 {
1121         DEBUG(0, ("restore directory %s\n", finfo.name));
1122
1123         if (!ensurepath(finfo.name)) {
1124                 DEBUG(0, ("Problems creating directory\n"));
1125                 return(False);
1126         }
1127         ntarf++;
1128         return(True);
1129 }
1130
1131 /* Get a file with a long file name ... first file has file name, next file 
1132    has the data. We only want the long file name, as the loop in do_tarput
1133    will deal with the rest.
1134 */
1135 static char *get_longfilename(file_info2 finfo)
1136 {
1137         /* finfo.size here is the length of the filename as written by the "/./@LongLink" name
1138          * header call. */
1139         int namesize = finfo.size + strlen(client_get_cur_dir()) + 2;
1140         char *longname = (char *)SMB_MALLOC(namesize);
1141         int offset = 0, left = finfo.size;
1142         bool first = True;
1143
1144         DEBUG(5, ("Restoring a long file name: %s\n", finfo.name));
1145         DEBUG(5, ("Len = %.0f\n", (double)finfo.size));
1146
1147         if (longname == NULL) {
1148                 DEBUG(0, ("could not allocate buffer of size %d for longname\n", namesize));
1149                 return(NULL);
1150         }
1151
1152         /* First, add cur_dir to the long file name */
1153
1154         if (strlen(client_get_cur_dir()) > 0) {
1155                 strncpy(longname, client_get_cur_dir(), namesize);
1156                 offset = strlen(client_get_cur_dir());
1157         }
1158
1159         /* Loop through the blocks picking up the name */
1160
1161         while (left > 0) {
1162                 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
1163                         DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1164                         SAFE_FREE(longname);
1165                         return(NULL);
1166                 }
1167
1168                 unfixtarname(longname + offset, buffer_p, MIN(TBLOCK, finfo.size), first--);
1169                 DEBUG(5, ("UnfixedName: %s, buffer: %s\n", longname, buffer_p));
1170
1171                 offset += TBLOCK;
1172                 left -= TBLOCK;
1173         }
1174
1175         return(longname);
1176 }
1177
1178 static void do_tarput(void)
1179 {
1180         file_info2 finfo;
1181         struct timespec tp_start;
1182         char *longfilename = NULL, linkflag;
1183         int skip = False;
1184
1185         ZERO_STRUCT(finfo);
1186
1187         clock_gettime_mono(&tp_start);
1188         DEBUG(5, ("RJS do_tarput called ...\n"));
1189
1190         buffer_p = tarbuf + tbufsiz;  /* init this to force first read */
1191
1192         /* Now read through those files ... */
1193         while (True) {
1194                 /* Get us to the next block, or the first block first time around */
1195                 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
1196                         DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1197                         SAFE_FREE(longfilename);
1198                         return;
1199                 }
1200
1201                 DEBUG(5, ("Reading the next header ...\n"));
1202
1203                 switch (readtarheader((union hblock *) buffer_p,
1204                                         &finfo, client_get_cur_dir())) {
1205                         case -2:    /* Hmm, not good, but not fatal */
1206                                 DEBUG(0, ("Skipping %s...\n", finfo.name));
1207                                 if ((next_block(tarbuf, &buffer_p, tbufsiz) <= 0) && !skip_file(finfo.size)) {
1208                                         DEBUG(0, ("Short file, bailing out...\n"));
1209                                         return;
1210                                 }
1211                                 break;
1212
1213                         case -1:
1214                                 DEBUG(0, ("abandoning restore, -1 from read tar header\n"));
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                                 return;        /* Hmmm, bad here ... */
1220
1221                         default: 
1222                                 /* No action */
1223                                 break;
1224                 }
1225
1226                 /* Now, do we have a long file name? */
1227                 if (longfilename != NULL) {
1228                         SAFE_FREE(finfo.name);   /* Free the space already allocated */
1229                         finfo.name = longfilename;
1230                         longfilename = NULL;
1231                 }
1232
1233                 /* Well, now we have a header, process the file ...            */
1234                 /* Should we skip the file? We have the long name as well here */
1235                 skip = clipn && ((!tar_re_search && clipfind(cliplist, clipn, finfo.name) ^ tar_excl) ||
1236                                         (tar_re_search && mask_match_list(finfo.name, cliplist, clipn, True)));
1237
1238                 DEBUG(5, ("Skip = %i, cliplist=%s, file=%s\n", skip, (cliplist?cliplist[0]:NULL), finfo.name));
1239                 if (skip) {
1240                         skip_file(finfo.size);
1241                         continue;
1242                 }
1243
1244                 /* We only get this far if we should process the file */
1245                 linkflag = ((union hblock *)buffer_p) -> dbuf.linkflag;
1246                 switch (linkflag) {
1247                         case '0':  /* Should use symbolic names--FIXME */
1248                                 /*
1249                                  * Skip to the next block first, so we can get the file, FIXME, should
1250                                  * be in get_file ...
1251                                  * The 'finfo.size != 0' fix is from Bob Boehmer <boehmer@worldnet.att.net>
1252                                  * Fixes bug where file size in tarfile is zero.
1253                                  */
1254                                 if ((finfo.size != 0) && next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
1255                                         DEBUG(0, ("Short file, bailing out...\n"));
1256                                         return;
1257                                 }
1258                                 if (!get_file(finfo)) {
1259                                         DEBUG(0, ("Abandoning restore\n"));
1260                                         return;
1261                                 }
1262                                 break;
1263                         case '5':
1264                                 if (!get_dir(finfo)) {
1265                                         DEBUG(0, ("Abandoning restore \n"));
1266                                         return;
1267                                 }
1268                                 break;
1269                         case 'L':
1270                                 SAFE_FREE(longfilename);
1271                                 longfilename = get_longfilename(finfo);
1272                                 if (!longfilename) {
1273                                         DEBUG(0, ("abandoning restore\n"));
1274                                         return;
1275                                 }
1276                                 DEBUG(5, ("Long file name: %s\n", longfilename));
1277                                 break;
1278
1279                         default:
1280                                 skip_file(finfo.size);  /* Don't handle these yet */
1281                                 break;
1282                 }
1283         }
1284 }
1285
1286 /*
1287  * samba interactive commands
1288  */
1289
1290 /****************************************************************************
1291 Blocksize command
1292 ***************************************************************************/
1293
1294 int cmd_block(void)
1295 {
1296         TALLOC_CTX *ctx = talloc_tos();
1297         char *buf;
1298         int block;
1299
1300         if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1301                 DEBUG(0, ("blocksize <n>\n"));
1302                 return 1;
1303         }
1304
1305         block=atoi(buf);
1306         if (block < 0 || block > 65535) {
1307                 DEBUG(0, ("blocksize out of range"));
1308                 return 1;
1309         }
1310
1311         blocksize=block;
1312         DEBUG(2,("blocksize is now %d\n", blocksize));
1313         return 0;
1314 }
1315
1316 /****************************************************************************
1317 command to set incremental / reset mode
1318 ***************************************************************************/
1319
1320 int cmd_tarmode(void)
1321 {
1322         TALLOC_CTX *ctx = talloc_tos();
1323         char *buf;
1324
1325         while (next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1326                 if (strequal(buf, "full"))
1327                         tar_inc=False;
1328                 else if (strequal(buf, "inc"))
1329                         tar_inc=True;
1330                 else if (strequal(buf, "reset"))
1331                         tar_reset=True;
1332                 else if (strequal(buf, "noreset"))
1333                         tar_reset=False;
1334                 else if (strequal(buf, "system"))
1335                         tar_system=True;
1336                 else if (strequal(buf, "nosystem"))
1337                         tar_system=False;
1338                 else if (strequal(buf, "hidden"))
1339                         tar_hidden=True;
1340                 else if (strequal(buf, "nohidden"))
1341                         tar_hidden=False;
1342                 else if (strequal(buf, "verbose") || strequal(buf, "noquiet"))
1343                         tar_noisy=True;
1344                 else if (strequal(buf, "quiet") || strequal(buf, "noverbose"))
1345                         tar_noisy=False;
1346                 else
1347                         DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
1348                 TALLOC_FREE(buf);
1349         }
1350
1351         DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n",
1352                         tar_inc ? "incremental" : "full",
1353                         tar_system ? "system" : "nosystem",
1354                         tar_hidden ? "hidden" : "nohidden",
1355                         tar_reset ? "reset" : "noreset",
1356                         tar_noisy ? "verbose" : "quiet"));
1357         return 0;
1358 }
1359
1360 /****************************************************************************
1361 Feeble attrib command
1362 ***************************************************************************/
1363
1364 int cmd_setmode(void)
1365 {
1366         TALLOC_CTX *ctx = talloc_tos();
1367         char *q;
1368         char *buf;
1369         char *fname = NULL;
1370         uint16 attra[2];
1371         int direct=1;
1372
1373         attra[0] = attra[1] = 0;
1374
1375         if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1376                 DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1377                 return 1;
1378         }
1379
1380         fname = talloc_asprintf(ctx,
1381                                 "%s%s",
1382                                 client_get_cur_dir(),
1383                                 buf);
1384         if (!fname) {
1385                 return 1;
1386         }
1387
1388         while (next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1389                 q=buf;
1390
1391                 while(*q) {
1392                         switch (*q++) {
1393                                 case '+':
1394                                         direct=1;
1395                                         break;
1396                                 case '-':
1397                                         direct=0;
1398                                         break;
1399                                 case 'r':
1400                                         attra[direct]|=aRONLY;
1401                                         break;
1402                                 case 'h':
1403                                         attra[direct]|=aHIDDEN;
1404                                         break;
1405                                 case 's':
1406                                         attra[direct]|=aSYSTEM;
1407                                         break;
1408                                 case 'a':
1409                                         attra[direct]|=aARCH;
1410                                         break;
1411                                 default:
1412                                         DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
1413                                         return 1;
1414                         }
1415                 }
1416         }
1417
1418         if (attra[ATTRSET]==0 && attra[ATTRRESET]==0) {
1419                 DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1420                 return 1;
1421         }
1422
1423         DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
1424         do_setrattr(fname, attra[ATTRSET], ATTRSET);
1425         do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
1426         return 0;
1427 }
1428
1429 /**
1430  Convert list of tokens to array; dependent on above routine.
1431  Uses the global cmd_ptr from above - bit of a hack.
1432 **/
1433
1434 static char **toktocliplist(int *ctok, const char *sep)
1435 {
1436         char *s=(char *)cmd_ptr;
1437         int ictok=0;
1438         char **ret, **iret;
1439
1440         if (!sep)
1441                 sep = " \t\n\r";
1442
1443         while(*s && strchr_m(sep,*s))
1444                 s++;
1445
1446         /* nothing left? */
1447         if (!*s)
1448                 return(NULL);
1449
1450         do {
1451                 ictok++;
1452                 while(*s && (!strchr_m(sep,*s)))
1453                         s++;
1454                 while(*s && strchr_m(sep,*s))
1455                         *s++=0;
1456         } while(*s);
1457
1458         *ctok=ictok;
1459         s=(char *)cmd_ptr;
1460
1461         if (!(ret=iret=SMB_MALLOC_ARRAY(char *,ictok+1)))
1462                 return NULL;
1463
1464         while(ictok--) {
1465                 *iret++=s;
1466                 if (ictok > 0) {
1467                         while(*s++)
1468                                 ;
1469                         while(!*s)
1470                                 s++;
1471                 }
1472         }
1473
1474         ret[*ctok] = NULL;
1475         return ret;
1476 }
1477
1478 /****************************************************************************
1479 Principal command for creating / extracting
1480 ***************************************************************************/
1481
1482 int cmd_tar(void)
1483 {
1484         TALLOC_CTX *ctx = talloc_tos();
1485         char *buf;
1486         char **argl = NULL;
1487         int argcl = 0;
1488         int ret;
1489
1490         if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1491                 DEBUG(0,("tar <c|x>[IXbgan] <filename>\n"));
1492                 return 1;
1493         }
1494
1495         argl=toktocliplist(&argcl, NULL);
1496         if (!tar_parseargs(argcl, argl, buf, 0)) {
1497                 SAFE_FREE(argl);
1498                 return 1;
1499         }
1500
1501         ret = process_tar();
1502         SAFE_FREE(argl);
1503         return ret;
1504 }
1505
1506 /****************************************************************************
1507 Command line (option) version
1508 ***************************************************************************/
1509
1510 int process_tar(void)
1511 {
1512         TALLOC_CTX *ctx = talloc_tos();
1513         int rc = 0;
1514         initarbuf();
1515         switch(tar_type) {
1516                 case 'x':
1517
1518 #if 0
1519                         do_tarput2();
1520 #else
1521                         do_tarput();
1522 #endif
1523                         SAFE_FREE(tarbuf);
1524                         close(tarhandle);
1525                         break;
1526                 case 'r':
1527                 case 'c':
1528                         if (clipn && tar_excl) {
1529                                 int i;
1530                                 char *tarmac = NULL;
1531
1532                                 for (i=0; i<clipn; i++) {
1533                                         DEBUG(5,("arg %d = %s\n", i, cliplist[i]));
1534
1535                                         if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
1536                                                 *(cliplist[i]+strlen(cliplist[i])-1)='\0';
1537                                         }
1538
1539                                         if (strrchr_m(cliplist[i], '\\')) {
1540                                                 char *p;
1541                                                 char saved_char;
1542                                                 char *saved_dir = talloc_strdup(ctx,
1543                                                                         client_get_cur_dir());
1544                                                 if (!saved_dir) {
1545                                                         return 1;
1546                                                 }
1547
1548                                                 if (*cliplist[i]=='\\') {
1549                                                         tarmac = talloc_strdup(ctx,
1550                                                                         cliplist[i]);
1551                                                 } else {
1552                                                         tarmac = talloc_asprintf(ctx,
1553                                                                         "%s%s",
1554                                                                         client_get_cur_dir(),
1555                                                                         cliplist[i]);
1556                                                 }
1557                                                 if (!tarmac) {
1558                                                         return 1;
1559                                                 }
1560                                                 /*
1561                                                  * Strip off the last \\xxx
1562                                                  * xxx element of tarmac to set
1563                                                  * it as current directory.
1564                                                  */
1565                                                 p = strrchr_m(tarmac, '\\');
1566                                                 if (!p) {
1567                                                         return 1;
1568                                                 }
1569                                                 saved_char = p[1];
1570                                                 p[1] = '\0';
1571
1572                                                 client_set_cur_dir(tarmac);
1573
1574                                                 /*
1575                                                  * Restore the character we
1576                                                  * just replaced to
1577                                                  * put the pathname
1578                                                  * back as it was.
1579                                                  */
1580                                                 p[1] = saved_char;
1581
1582                                                 DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1583                                                 do_list(tarmac,attribute,do_tar, False, True);
1584
1585                                                 client_set_cur_dir(saved_dir);
1586
1587                                                 TALLOC_FREE(saved_dir);
1588                                                 TALLOC_FREE(tarmac);
1589                                         } else {
1590                                                 tarmac = talloc_asprintf(ctx,
1591                                                                 "%s%s",
1592                                                                 client_get_cur_dir(),
1593                                                                 cliplist[i]);
1594                                                 if (!tarmac) {
1595                                                         return 1;
1596                                                 }
1597                                                 DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1598                                                 do_list(tarmac,attribute,do_tar, False, True);
1599                                                 TALLOC_FREE(tarmac);
1600                                         }
1601                                 }
1602                         } else {
1603                                 char *mask = talloc_asprintf(ctx,
1604                                                         "%s\\*",
1605                                                         client_get_cur_dir());
1606                                 if (!mask) {
1607                                         return 1;
1608                                 }
1609                                 DEBUG(5, ("process_tar, do_list with mask: %s\n", mask));
1610                                 do_list(mask,attribute,do_tar,False, True);
1611                                 TALLOC_FREE(mask);
1612                         }
1613
1614                         if (ntarf) {
1615                                 dotareof(tarhandle);
1616                         }
1617                         close(tarhandle);
1618                         SAFE_FREE(tarbuf);
1619
1620                         DEBUG(0, ("tar: dumped %d files and directories\n", ntarf));
1621                         DEBUG(0, ("Total bytes written: %.0f\n", (double)ttarf));
1622                         break;
1623         }
1624
1625         if (must_free_cliplist) {
1626                 int i;
1627                 for (i = 0; i < clipn; ++i) {
1628                         SAFE_FREE(cliplist[i]);
1629                 }
1630                 SAFE_FREE(cliplist);
1631                 cliplist = NULL;
1632                 clipn = 0;
1633                 must_free_cliplist = False;
1634         }
1635         return rc;
1636 }
1637
1638 /****************************************************************************
1639 Find a token (filename) in a clip list
1640 ***************************************************************************/
1641
1642 static int clipfind(char **aret, int ret, char *tok)
1643 {
1644         if (aret==NULL)
1645                 return 0;
1646
1647         /* ignore leading slashes or dots in token */
1648         while(strchr_m("/\\.", *tok))
1649                 tok++;
1650
1651         while(ret--) {
1652                 char *pkey=*aret++;
1653
1654                 /* ignore leading slashes or dots in list */
1655                 while(strchr_m("/\\.", *pkey))
1656                         pkey++;
1657
1658                 if (!strslashcmp(pkey, tok))
1659                         return 1;
1660         }
1661         return 0;
1662 }
1663
1664 /****************************************************************************
1665 Read list of files to include from the file and initialize cliplist
1666 accordingly.
1667 ***************************************************************************/
1668
1669 static int read_inclusion_file(char *filename)
1670 {
1671         XFILE *inclusion = NULL;
1672         char buf[PATH_MAX + 1];
1673         char *inclusion_buffer = NULL;
1674         int inclusion_buffer_size = 0;
1675         int inclusion_buffer_sofar = 0;
1676         char *p;
1677         char *tmpstr;
1678         int i;
1679         int error = 0;
1680
1681         clipn = 0;
1682         buf[PATH_MAX] = '\0'; /* guarantee null-termination */
1683         if ((inclusion = x_fopen(filename, O_RDONLY, 0)) == NULL) {
1684                 /* XXX It would be better to include a reason for failure, but without
1685                  * autoconf, it's hard to use strerror, sys_errlist, etc.
1686                  */
1687                 DEBUG(0,("Unable to open inclusion file %s\n", filename));
1688                 return 0;
1689         }
1690
1691         while ((! error) && (x_fgets(buf, sizeof(buf)-1, inclusion))) {
1692                 if (inclusion_buffer == NULL) {
1693                         inclusion_buffer_size = 1024;
1694                         if ((inclusion_buffer = (char *)SMB_MALLOC(inclusion_buffer_size)) == NULL) {
1695                                 DEBUG(0,("failure allocating buffer to read inclusion file\n"));
1696                                 error = 1;
1697                                 break;
1698                         }
1699                 }
1700
1701                 if (buf[strlen(buf)-1] == '\n') {
1702                         buf[strlen(buf)-1] = '\0';
1703                 }
1704
1705                 if ((strlen(buf) + 1 + inclusion_buffer_sofar) >= inclusion_buffer_size) {
1706                         inclusion_buffer_size *= 2;
1707                         inclusion_buffer = (char *)SMB_REALLOC(inclusion_buffer,inclusion_buffer_size);
1708                         if (!inclusion_buffer) {
1709                                 DEBUG(0,("failure enlarging inclusion buffer to %d bytes\n",
1710                                                 inclusion_buffer_size));
1711                                 error = 1;
1712                                 break;
1713                         }
1714                 }
1715
1716                 safe_strcpy(inclusion_buffer + inclusion_buffer_sofar, buf, inclusion_buffer_size - inclusion_buffer_sofar);
1717                 inclusion_buffer_sofar += strlen(buf) + 1;
1718                 clipn++;
1719         }
1720         x_fclose(inclusion);
1721
1722         if (! error) {
1723                 /* Allocate an array of clipn + 1 char*'s for cliplist */
1724                 cliplist = SMB_MALLOC_ARRAY(char *, clipn + 1);
1725                 if (cliplist == NULL) {
1726                         DEBUG(0,("failure allocating memory for cliplist\n"));
1727                         error = 1;
1728                 } else {
1729                         cliplist[clipn] = NULL;
1730                         p = inclusion_buffer;
1731                         for (i = 0; (! error) && (i < clipn); i++) {
1732                                 /* set current item to NULL so array will be null-terminated even if
1733                                                 * malloc fails below. */
1734                                 cliplist[i] = NULL;
1735                                 if ((tmpstr = (char *)SMB_MALLOC(strlen(p)+1)) == NULL) {
1736                                         DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", i));
1737                                         error = 1;
1738                                 } else {
1739                                         unfixtarname(tmpstr, p, strlen(p) + 1, True);
1740                                         cliplist[i] = tmpstr;
1741                                         if ((p = strchr_m(p, '\000')) == NULL) {
1742                                                 DEBUG(0,("INTERNAL ERROR: inclusion_buffer is of unexpected contents.\n"));
1743                                                 abort();
1744                                         }
1745                                 }
1746                                 ++p;
1747                         }
1748                         must_free_cliplist = True;
1749                 }
1750         }
1751
1752         SAFE_FREE(inclusion_buffer);
1753         if (error) {
1754                 if (cliplist) {
1755                         char **pp;
1756                         /* We know cliplist is always null-terminated */
1757                         for (pp = cliplist; *pp; ++pp) {
1758                                 SAFE_FREE(*pp);
1759                         }
1760                         SAFE_FREE(cliplist);
1761                         cliplist = NULL;
1762                         must_free_cliplist = False;
1763                 }
1764                 return 0;
1765         }
1766
1767         /* cliplist and its elements are freed at the end of process_tar. */
1768         return 1;
1769 }
1770
1771 /****************************************************************************
1772 Parse tar arguments. Sets tar_type, tar_excl, etc.
1773 ***************************************************************************/
1774
1775 int tar_parseargs(int argc, char *argv[], const char *Optarg, int Optind)
1776 {
1777         int newOptind = Optind;
1778         char tar_clipfl='\0';
1779
1780         /* Reset back to defaults - could be from interactive version 
1781          * reset mode and archive mode left as they are though
1782          */
1783         tar_type='\0';
1784         tar_excl=True;
1785         dry_run=False;
1786
1787         while (*Optarg) {
1788                 switch(*Optarg++) {
1789                         case 'c':
1790                                 tar_type='c';
1791                                 break;
1792                         case 'x':
1793                                 if (tar_type=='c') {
1794                                         printf("Tar must be followed by only one of c or x.\n");
1795                                         return 0;
1796                                 }
1797                                 tar_type='x';
1798                                 break;
1799                         case 'b':
1800                                 if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) {
1801                                         DEBUG(0,("Option b must be followed by valid blocksize\n"));
1802                                         return 0;
1803                                 } else {
1804                                         Optind++;
1805                                         newOptind++;
1806                                 }
1807                                 break;
1808                         case 'g':
1809                                 tar_inc=True;
1810                                 break;
1811                         case 'N':
1812                                 if (Optind>=argc) {
1813                                         DEBUG(0,("Option N must be followed by valid file name\n"));
1814                                         return 0;
1815                                 } else {
1816                                         SMB_STRUCT_STAT stbuf;
1817
1818                                         if (sys_stat(argv[Optind], &stbuf,
1819                                                      false) == 0) {
1820                                                 newer_than = convert_timespec_to_time_t(
1821                                                         stbuf.st_ex_mtime);
1822                                                 DEBUG(1,("Getting files newer than %s",
1823                                                         time_to_asc(newer_than)));
1824                                                 newOptind++;
1825                                                 Optind++;
1826                                         } else {
1827                                                 DEBUG(0,("Error setting newer-than time\n"));
1828                                                 return 0;
1829                                         }
1830                                 }
1831                                 break;
1832                         case 'a':
1833                                 tar_reset=True;
1834                                 break;
1835                         case 'q':
1836                                 tar_noisy=False;
1837                                 break;
1838                         case 'I':
1839                                 if (tar_clipfl) {
1840                                         DEBUG(0,("Only one of I,X,F must be specified\n"));
1841                                         return 0;
1842                                 }
1843                                 tar_clipfl='I';
1844                                 break;
1845                         case 'X':
1846                                 if (tar_clipfl) {
1847                                         DEBUG(0,("Only one of I,X,F must be specified\n"));
1848                                         return 0;
1849                                 }
1850                                 tar_clipfl='X';
1851                                 break;
1852                         case 'F':
1853                                 if (tar_clipfl) {
1854                                         DEBUG(0,("Only one of I,X,F must be specified\n"));
1855                                         return 0;
1856                                 }
1857                                 tar_clipfl='F';
1858                                 break;
1859                         case 'r':
1860                                 DEBUG(0, ("tar_re_search set\n"));
1861                                 tar_re_search = True;
1862                                 break;
1863                         case 'n':
1864                                 if (tar_type == 'c') {
1865                                         DEBUG(0, ("dry_run set\n"));
1866                                         dry_run = True;
1867                                 } else {
1868                                         DEBUG(0, ("n is only meaningful when creating a tar-file\n"));
1869                                         return 0;
1870                                 }
1871                                 break;
1872                         default:
1873                                 DEBUG(0,("Unknown tar option\n"));
1874                                 return 0;
1875                 }
1876         }
1877
1878         if (!tar_type) {
1879                 printf("Option T must be followed by one of c or x.\n");
1880                 return 0;
1881         }
1882
1883         /* tar_excl is true if cliplist lists files to be included.
1884          * Both 'I' and 'F' mean include. */
1885         tar_excl=tar_clipfl!='X';
1886
1887         if (tar_clipfl=='F') {
1888                 if (argc-Optind-1 != 1) {
1889                         DEBUG(0,("Option F must be followed by exactly one filename.\n"));
1890                         return 0;
1891                 }
1892                 newOptind++;
1893                 /* Optind points at the tar output file, Optind+1 at the inclusion file. */
1894                 if (! read_inclusion_file(argv[Optind+1])) {
1895                         return 0;
1896                 }
1897         } else if (Optind+1<argc && !tar_re_search) { /* For backwards compatibility */
1898                 char *tmpstr;
1899                 char **tmplist;
1900                 int clipcount;
1901
1902                 cliplist=argv+Optind+1;
1903                 clipn=argc-Optind-1;
1904                 clipcount = clipn;
1905
1906                 if ((tmplist=SMB_MALLOC_ARRAY(char *,clipn)) == NULL) {
1907                         DEBUG(0, ("Could not allocate space to process cliplist, count = %i\n", clipn));
1908                         return 0;
1909                 }
1910
1911                 for (clipcount = 0; clipcount < clipn; clipcount++) {
1912
1913                         DEBUG(5, ("Processing an item, %s\n", cliplist[clipcount]));
1914
1915                         if ((tmpstr = (char *)SMB_MALLOC(strlen(cliplist[clipcount])+1)) == NULL) {
1916                                 DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", clipcount));
1917                                 SAFE_FREE(tmplist);
1918                                 return 0;
1919                         }
1920
1921                         unfixtarname(tmpstr, cliplist[clipcount], strlen(cliplist[clipcount]) + 1, True);
1922                         tmplist[clipcount] = tmpstr;
1923                         DEBUG(5, ("Processed an item, %s\n", tmpstr));
1924
1925                         DEBUG(5, ("Cliplist is: %s\n", cliplist[0]));
1926                 }
1927
1928                 cliplist = tmplist;
1929                 must_free_cliplist = True;
1930
1931                 newOptind += clipn;
1932         }
1933
1934         if (Optind+1<argc && tar_re_search && tar_clipfl != 'F') {
1935                 /* Doing regular expression seaches not from an inclusion file. */
1936                 clipn=argc-Optind-1;
1937                 cliplist=argv+Optind+1;
1938                 newOptind += clipn;
1939         }
1940
1941         if (Optind>=argc || !strcmp(argv[Optind], "-")) {
1942                 /* Sets tar handle to either 0 or 1, as appropriate */
1943                 tarhandle=(tar_type=='c');
1944                 /*
1945                  * Make sure that dbf points to stderr if we are using stdout for 
1946                  * tar output
1947                  */
1948                 if (tarhandle == 1)  {
1949                         dbf = x_stderr;
1950                 }
1951                 if (!argv[Optind]) {
1952                         DEBUG(0,("Must specify tar filename\n"));
1953                         return 0;
1954                 }
1955                 if (!strcmp(argv[Optind], "-")) {
1956                         newOptind++;
1957                 }
1958
1959         } else {
1960                 if (tar_type=='c' && dry_run) {
1961                         tarhandle=-1;
1962                 } else if ((tar_type=='x' && (tarhandle = sys_open(argv[Optind], O_RDONLY, 0)) == -1)
1963                                         || (tar_type=='c' && (tarhandle=sys_creat(argv[Optind], 0644)) < 0)) {
1964                         DEBUG(0,("Error opening local file %s - %s\n", argv[Optind], strerror(errno)));
1965                         return(0);
1966                 }
1967                 newOptind++;
1968         }
1969
1970         return newOptind;
1971 }