ac891aa7e8c613be530af33348f0bfafd290d31d
[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                                         SAFE_FREE(longfilename);
1210                                         return;
1211                                 }
1212                                 break;
1213
1214                         case -1:
1215                                 DEBUG(0, ("abandoning restore, -1 from read tar header\n"));
1216                                 SAFE_FREE(longfilename);
1217                                 return;
1218
1219                         case 0: /* chksum is zero - looks like an EOF */
1220                                 DEBUG(0, ("tar: restored %d files and directories\n", ntarf));
1221                                 SAFE_FREE(longfilename);
1222                                 return;        /* Hmmm, bad here ... */
1223
1224                         default: 
1225                                 /* No action */
1226                                 break;
1227                 }
1228
1229                 /* Now, do we have a long file name? */
1230                 if (longfilename != NULL) {
1231                         SAFE_FREE(finfo.name);   /* Free the space already allocated */
1232                         finfo.name = longfilename;
1233                         longfilename = NULL;
1234                 }
1235
1236                 /* Well, now we have a header, process the file ...            */
1237                 /* Should we skip the file? We have the long name as well here */
1238                 skip = clipn && ((!tar_re_search && clipfind(cliplist, clipn, finfo.name) ^ tar_excl) ||
1239                                         (tar_re_search && mask_match_list(finfo.name, cliplist, clipn, True)));
1240
1241                 DEBUG(5, ("Skip = %i, cliplist=%s, file=%s\n", skip, (cliplist?cliplist[0]:NULL), finfo.name));
1242                 if (skip) {
1243                         skip_file(finfo.size);
1244                         continue;
1245                 }
1246
1247                 /* We only get this far if we should process the file */
1248                 linkflag = ((union hblock *)buffer_p) -> dbuf.linkflag;
1249                 switch (linkflag) {
1250                         case '0':  /* Should use symbolic names--FIXME */
1251                                 /*
1252                                  * Skip to the next block first, so we can get the file, FIXME, should
1253                                  * be in get_file ...
1254                                  * The 'finfo.size != 0' fix is from Bob Boehmer <boehmer@worldnet.att.net>
1255                                  * Fixes bug where file size in tarfile is zero.
1256                                  */
1257                                 if ((finfo.size != 0) && next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
1258                                         DEBUG(0, ("Short file, bailing out...\n"));
1259                                         return;
1260                                 }
1261                                 if (!get_file(finfo)) {
1262                                         DEBUG(0, ("Abandoning restore\n"));
1263                                         return;
1264                                 }
1265                                 break;
1266                         case '5':
1267                                 if (!get_dir(finfo)) {
1268                                         DEBUG(0, ("Abandoning restore \n"));
1269                                         return;
1270                                 }
1271                                 break;
1272                         case 'L':
1273                                 SAFE_FREE(longfilename);
1274                                 longfilename = get_longfilename(finfo);
1275                                 if (!longfilename) {
1276                                         DEBUG(0, ("abandoning restore\n"));
1277                                         return;
1278                                 }
1279                                 DEBUG(5, ("Long file name: %s\n", longfilename));
1280                                 break;
1281
1282                         default:
1283                                 skip_file(finfo.size);  /* Don't handle these yet */
1284                                 break;
1285                 }
1286         }
1287 }
1288
1289 /*
1290  * samba interactive commands
1291  */
1292
1293 /****************************************************************************
1294 Blocksize command
1295 ***************************************************************************/
1296
1297 int cmd_block(void)
1298 {
1299         TALLOC_CTX *ctx = talloc_tos();
1300         char *buf;
1301         int block;
1302
1303         if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1304                 DEBUG(0, ("blocksize <n>\n"));
1305                 return 1;
1306         }
1307
1308         block=atoi(buf);
1309         if (block < 0 || block > 65535) {
1310                 DEBUG(0, ("blocksize out of range"));
1311                 return 1;
1312         }
1313
1314         blocksize=block;
1315         DEBUG(2,("blocksize is now %d\n", blocksize));
1316         return 0;
1317 }
1318
1319 /****************************************************************************
1320 command to set incremental / reset mode
1321 ***************************************************************************/
1322
1323 int cmd_tarmode(void)
1324 {
1325         TALLOC_CTX *ctx = talloc_tos();
1326         char *buf;
1327
1328         while (next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1329                 if (strequal(buf, "full"))
1330                         tar_inc=False;
1331                 else if (strequal(buf, "inc"))
1332                         tar_inc=True;
1333                 else if (strequal(buf, "reset"))
1334                         tar_reset=True;
1335                 else if (strequal(buf, "noreset"))
1336                         tar_reset=False;
1337                 else if (strequal(buf, "system"))
1338                         tar_system=True;
1339                 else if (strequal(buf, "nosystem"))
1340                         tar_system=False;
1341                 else if (strequal(buf, "hidden"))
1342                         tar_hidden=True;
1343                 else if (strequal(buf, "nohidden"))
1344                         tar_hidden=False;
1345                 else if (strequal(buf, "verbose") || strequal(buf, "noquiet"))
1346                         tar_noisy=True;
1347                 else if (strequal(buf, "quiet") || strequal(buf, "noverbose"))
1348                         tar_noisy=False;
1349                 else
1350                         DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
1351                 TALLOC_FREE(buf);
1352         }
1353
1354         DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n",
1355                         tar_inc ? "incremental" : "full",
1356                         tar_system ? "system" : "nosystem",
1357                         tar_hidden ? "hidden" : "nohidden",
1358                         tar_reset ? "reset" : "noreset",
1359                         tar_noisy ? "verbose" : "quiet"));
1360         return 0;
1361 }
1362
1363 /****************************************************************************
1364 Feeble attrib command
1365 ***************************************************************************/
1366
1367 int cmd_setmode(void)
1368 {
1369         TALLOC_CTX *ctx = talloc_tos();
1370         char *q;
1371         char *buf;
1372         char *fname = NULL;
1373         uint16 attra[2];
1374         int direct=1;
1375
1376         attra[0] = attra[1] = 0;
1377
1378         if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1379                 DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1380                 return 1;
1381         }
1382
1383         fname = talloc_asprintf(ctx,
1384                                 "%s%s",
1385                                 client_get_cur_dir(),
1386                                 buf);
1387         if (!fname) {
1388                 return 1;
1389         }
1390
1391         while (next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1392                 q=buf;
1393
1394                 while(*q) {
1395                         switch (*q++) {
1396                                 case '+':
1397                                         direct=1;
1398                                         break;
1399                                 case '-':
1400                                         direct=0;
1401                                         break;
1402                                 case 'r':
1403                                         attra[direct]|=aRONLY;
1404                                         break;
1405                                 case 'h':
1406                                         attra[direct]|=aHIDDEN;
1407                                         break;
1408                                 case 's':
1409                                         attra[direct]|=aSYSTEM;
1410                                         break;
1411                                 case 'a':
1412                                         attra[direct]|=aARCH;
1413                                         break;
1414                                 default:
1415                                         DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
1416                                         return 1;
1417                         }
1418                 }
1419         }
1420
1421         if (attra[ATTRSET]==0 && attra[ATTRRESET]==0) {
1422                 DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1423                 return 1;
1424         }
1425
1426         DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
1427         do_setrattr(fname, attra[ATTRSET], ATTRSET);
1428         do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
1429         return 0;
1430 }
1431
1432 /**
1433  Convert list of tokens to array; dependent on above routine.
1434  Uses the global cmd_ptr from above - bit of a hack.
1435 **/
1436
1437 static char **toktocliplist(int *ctok, const char *sep)
1438 {
1439         char *s=(char *)cmd_ptr;
1440         int ictok=0;
1441         char **ret, **iret;
1442
1443         if (!sep)
1444                 sep = " \t\n\r";
1445
1446         while(*s && strchr_m(sep,*s))
1447                 s++;
1448
1449         /* nothing left? */
1450         if (!*s)
1451                 return(NULL);
1452
1453         do {
1454                 ictok++;
1455                 while(*s && (!strchr_m(sep,*s)))
1456                         s++;
1457                 while(*s && strchr_m(sep,*s))
1458                         *s++=0;
1459         } while(*s);
1460
1461         *ctok=ictok;
1462         s=(char *)cmd_ptr;
1463
1464         if (!(ret=iret=SMB_MALLOC_ARRAY(char *,ictok+1)))
1465                 return NULL;
1466
1467         while(ictok--) {
1468                 *iret++=s;
1469                 if (ictok > 0) {
1470                         while(*s++)
1471                                 ;
1472                         while(!*s)
1473                                 s++;
1474                 }
1475         }
1476
1477         ret[*ctok] = NULL;
1478         return ret;
1479 }
1480
1481 /****************************************************************************
1482 Principal command for creating / extracting
1483 ***************************************************************************/
1484
1485 int cmd_tar(void)
1486 {
1487         TALLOC_CTX *ctx = talloc_tos();
1488         char *buf;
1489         char **argl = NULL;
1490         int argcl = 0;
1491         int ret;
1492
1493         if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1494                 DEBUG(0,("tar <c|x>[IXbgan] <filename>\n"));
1495                 return 1;
1496         }
1497
1498         argl=toktocliplist(&argcl, NULL);
1499         if (!tar_parseargs(argcl, argl, buf, 0)) {
1500                 SAFE_FREE(argl);
1501                 return 1;
1502         }
1503
1504         ret = process_tar();
1505         SAFE_FREE(argl);
1506         return ret;
1507 }
1508
1509 /****************************************************************************
1510 Command line (option) version
1511 ***************************************************************************/
1512
1513 int process_tar(void)
1514 {
1515         TALLOC_CTX *ctx = talloc_tos();
1516         int rc = 0;
1517         initarbuf();
1518         switch(tar_type) {
1519                 case 'x':
1520
1521 #if 0
1522                         do_tarput2();
1523 #else
1524                         do_tarput();
1525 #endif
1526                         SAFE_FREE(tarbuf);
1527                         close(tarhandle);
1528                         break;
1529                 case 'r':
1530                 case 'c':
1531                         if (clipn && tar_excl) {
1532                                 int i;
1533                                 char *tarmac = NULL;
1534
1535                                 for (i=0; i<clipn; i++) {
1536                                         DEBUG(5,("arg %d = %s\n", i, cliplist[i]));
1537
1538                                         if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
1539                                                 *(cliplist[i]+strlen(cliplist[i])-1)='\0';
1540                                         }
1541
1542                                         if (strrchr_m(cliplist[i], '\\')) {
1543                                                 char *p;
1544                                                 char saved_char;
1545                                                 char *saved_dir = talloc_strdup(ctx,
1546                                                                         client_get_cur_dir());
1547                                                 if (!saved_dir) {
1548                                                         return 1;
1549                                                 }
1550
1551                                                 if (*cliplist[i]=='\\') {
1552                                                         tarmac = talloc_strdup(ctx,
1553                                                                         cliplist[i]);
1554                                                 } else {
1555                                                         tarmac = talloc_asprintf(ctx,
1556                                                                         "%s%s",
1557                                                                         client_get_cur_dir(),
1558                                                                         cliplist[i]);
1559                                                 }
1560                                                 if (!tarmac) {
1561                                                         return 1;
1562                                                 }
1563                                                 /*
1564                                                  * Strip off the last \\xxx
1565                                                  * xxx element of tarmac to set
1566                                                  * it as current directory.
1567                                                  */
1568                                                 p = strrchr_m(tarmac, '\\');
1569                                                 if (!p) {
1570                                                         return 1;
1571                                                 }
1572                                                 saved_char = p[1];
1573                                                 p[1] = '\0';
1574
1575                                                 client_set_cur_dir(tarmac);
1576
1577                                                 /*
1578                                                  * Restore the character we
1579                                                  * just replaced to
1580                                                  * put the pathname
1581                                                  * back as it was.
1582                                                  */
1583                                                 p[1] = saved_char;
1584
1585                                                 DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1586                                                 do_list(tarmac,attribute,do_tar, False, True);
1587
1588                                                 client_set_cur_dir(saved_dir);
1589
1590                                                 TALLOC_FREE(saved_dir);
1591                                                 TALLOC_FREE(tarmac);
1592                                         } else {
1593                                                 tarmac = talloc_asprintf(ctx,
1594                                                                 "%s%s",
1595                                                                 client_get_cur_dir(),
1596                                                                 cliplist[i]);
1597                                                 if (!tarmac) {
1598                                                         return 1;
1599                                                 }
1600                                                 DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1601                                                 do_list(tarmac,attribute,do_tar, False, True);
1602                                                 TALLOC_FREE(tarmac);
1603                                         }
1604                                 }
1605                         } else {
1606                                 char *mask = talloc_asprintf(ctx,
1607                                                         "%s\\*",
1608                                                         client_get_cur_dir());
1609                                 if (!mask) {
1610                                         return 1;
1611                                 }
1612                                 DEBUG(5, ("process_tar, do_list with mask: %s\n", mask));
1613                                 do_list(mask,attribute,do_tar,False, True);
1614                                 TALLOC_FREE(mask);
1615                         }
1616
1617                         if (ntarf) {
1618                                 dotareof(tarhandle);
1619                         }
1620                         close(tarhandle);
1621                         SAFE_FREE(tarbuf);
1622
1623                         DEBUG(0, ("tar: dumped %d files and directories\n", ntarf));
1624                         DEBUG(0, ("Total bytes written: %.0f\n", (double)ttarf));
1625                         break;
1626         }
1627
1628         if (must_free_cliplist) {
1629                 int i;
1630                 for (i = 0; i < clipn; ++i) {
1631                         SAFE_FREE(cliplist[i]);
1632                 }
1633                 SAFE_FREE(cliplist);
1634                 cliplist = NULL;
1635                 clipn = 0;
1636                 must_free_cliplist = False;
1637         }
1638         return rc;
1639 }
1640
1641 /****************************************************************************
1642 Find a token (filename) in a clip list
1643 ***************************************************************************/
1644
1645 static int clipfind(char **aret, int ret, char *tok)
1646 {
1647         if (aret==NULL)
1648                 return 0;
1649
1650         /* ignore leading slashes or dots in token */
1651         while(strchr_m("/\\.", *tok))
1652                 tok++;
1653
1654         while(ret--) {
1655                 char *pkey=*aret++;
1656
1657                 /* ignore leading slashes or dots in list */
1658                 while(strchr_m("/\\.", *pkey))
1659                         pkey++;
1660
1661                 if (!strslashcmp(pkey, tok))
1662                         return 1;
1663         }
1664         return 0;
1665 }
1666
1667 /****************************************************************************
1668 Read list of files to include from the file and initialize cliplist
1669 accordingly.
1670 ***************************************************************************/
1671
1672 static int read_inclusion_file(char *filename)
1673 {
1674         XFILE *inclusion = NULL;
1675         char buf[PATH_MAX + 1];
1676         char *inclusion_buffer = NULL;
1677         int inclusion_buffer_size = 0;
1678         int inclusion_buffer_sofar = 0;
1679         char *p;
1680         char *tmpstr;
1681         int i;
1682         int error = 0;
1683
1684         clipn = 0;
1685         buf[PATH_MAX] = '\0'; /* guarantee null-termination */
1686         if ((inclusion = x_fopen(filename, O_RDONLY, 0)) == NULL) {
1687                 /* XXX It would be better to include a reason for failure, but without
1688                  * autoconf, it's hard to use strerror, sys_errlist, etc.
1689                  */
1690                 DEBUG(0,("Unable to open inclusion file %s\n", filename));
1691                 return 0;
1692         }
1693
1694         while ((! error) && (x_fgets(buf, sizeof(buf)-1, inclusion))) {
1695                 if (inclusion_buffer == NULL) {
1696                         inclusion_buffer_size = 1024;
1697                         if ((inclusion_buffer = (char *)SMB_MALLOC(inclusion_buffer_size)) == NULL) {
1698                                 DEBUG(0,("failure allocating buffer to read inclusion file\n"));
1699                                 error = 1;
1700                                 break;
1701                         }
1702                 }
1703
1704                 if (buf[strlen(buf)-1] == '\n') {
1705                         buf[strlen(buf)-1] = '\0';
1706                 }
1707
1708                 if ((strlen(buf) + 1 + inclusion_buffer_sofar) >= inclusion_buffer_size) {
1709                         inclusion_buffer_size *= 2;
1710                         inclusion_buffer = (char *)SMB_REALLOC(inclusion_buffer,inclusion_buffer_size);
1711                         if (!inclusion_buffer) {
1712                                 DEBUG(0,("failure enlarging inclusion buffer to %d bytes\n",
1713                                                 inclusion_buffer_size));
1714                                 error = 1;
1715                                 break;
1716                         }
1717                 }
1718
1719                 safe_strcpy(inclusion_buffer + inclusion_buffer_sofar, buf, inclusion_buffer_size - inclusion_buffer_sofar);
1720                 inclusion_buffer_sofar += strlen(buf) + 1;
1721                 clipn++;
1722         }
1723         x_fclose(inclusion);
1724
1725         if (! error) {
1726                 /* Allocate an array of clipn + 1 char*'s for cliplist */
1727                 cliplist = SMB_MALLOC_ARRAY(char *, clipn + 1);
1728                 if (cliplist == NULL) {
1729                         DEBUG(0,("failure allocating memory for cliplist\n"));
1730                         error = 1;
1731                 } else {
1732                         cliplist[clipn] = NULL;
1733                         p = inclusion_buffer;
1734                         for (i = 0; (! error) && (i < clipn); i++) {
1735                                 /* set current item to NULL so array will be null-terminated even if
1736                                                 * malloc fails below. */
1737                                 cliplist[i] = NULL;
1738                                 if ((tmpstr = (char *)SMB_MALLOC(strlen(p)+1)) == NULL) {
1739                                         DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", i));
1740                                         error = 1;
1741                                 } else {
1742                                         unfixtarname(tmpstr, p, strlen(p) + 1, True);
1743                                         cliplist[i] = tmpstr;
1744                                         if ((p = strchr_m(p, '\000')) == NULL) {
1745                                                 DEBUG(0,("INTERNAL ERROR: inclusion_buffer is of unexpected contents.\n"));
1746                                                 abort();
1747                                         }
1748                                 }
1749                                 ++p;
1750                         }
1751                         must_free_cliplist = True;
1752                 }
1753         }
1754
1755         SAFE_FREE(inclusion_buffer);
1756         if (error) {
1757                 if (cliplist) {
1758                         char **pp;
1759                         /* We know cliplist is always null-terminated */
1760                         for (pp = cliplist; *pp; ++pp) {
1761                                 SAFE_FREE(*pp);
1762                         }
1763                         SAFE_FREE(cliplist);
1764                         cliplist = NULL;
1765                         must_free_cliplist = False;
1766                 }
1767                 return 0;
1768         }
1769
1770         /* cliplist and its elements are freed at the end of process_tar. */
1771         return 1;
1772 }
1773
1774 /****************************************************************************
1775 Parse tar arguments. Sets tar_type, tar_excl, etc.
1776 ***************************************************************************/
1777
1778 int tar_parseargs(int argc, char *argv[], const char *Optarg, int Optind)
1779 {
1780         int newOptind = Optind;
1781         char tar_clipfl='\0';
1782
1783         /* Reset back to defaults - could be from interactive version 
1784          * reset mode and archive mode left as they are though
1785          */
1786         tar_type='\0';
1787         tar_excl=True;
1788         dry_run=False;
1789
1790         while (*Optarg) {
1791                 switch(*Optarg++) {
1792                         case 'c':
1793                                 tar_type='c';
1794                                 break;
1795                         case 'x':
1796                                 if (tar_type=='c') {
1797                                         printf("Tar must be followed by only one of c or x.\n");
1798                                         return 0;
1799                                 }
1800                                 tar_type='x';
1801                                 break;
1802                         case 'b':
1803                                 if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) {
1804                                         DEBUG(0,("Option b must be followed by valid blocksize\n"));
1805                                         return 0;
1806                                 } else {
1807                                         Optind++;
1808                                         newOptind++;
1809                                 }
1810                                 break;
1811                         case 'g':
1812                                 tar_inc=True;
1813                                 break;
1814                         case 'N':
1815                                 if (Optind>=argc) {
1816                                         DEBUG(0,("Option N must be followed by valid file name\n"));
1817                                         return 0;
1818                                 } else {
1819                                         SMB_STRUCT_STAT stbuf;
1820
1821                                         if (sys_stat(argv[Optind], &stbuf,
1822                                                      false) == 0) {
1823                                                 newer_than = convert_timespec_to_time_t(
1824                                                         stbuf.st_ex_mtime);
1825                                                 DEBUG(1,("Getting files newer than %s",
1826                                                         time_to_asc(newer_than)));
1827                                                 newOptind++;
1828                                                 Optind++;
1829                                         } else {
1830                                                 DEBUG(0,("Error setting newer-than time\n"));
1831                                                 return 0;
1832                                         }
1833                                 }
1834                                 break;
1835                         case 'a':
1836                                 tar_reset=True;
1837                                 break;
1838                         case 'q':
1839                                 tar_noisy=False;
1840                                 break;
1841                         case 'I':
1842                                 if (tar_clipfl) {
1843                                         DEBUG(0,("Only one of I,X,F must be specified\n"));
1844                                         return 0;
1845                                 }
1846                                 tar_clipfl='I';
1847                                 break;
1848                         case 'X':
1849                                 if (tar_clipfl) {
1850                                         DEBUG(0,("Only one of I,X,F must be specified\n"));
1851                                         return 0;
1852                                 }
1853                                 tar_clipfl='X';
1854                                 break;
1855                         case 'F':
1856                                 if (tar_clipfl) {
1857                                         DEBUG(0,("Only one of I,X,F must be specified\n"));
1858                                         return 0;
1859                                 }
1860                                 tar_clipfl='F';
1861                                 break;
1862                         case 'r':
1863                                 DEBUG(0, ("tar_re_search set\n"));
1864                                 tar_re_search = True;
1865                                 break;
1866                         case 'n':
1867                                 if (tar_type == 'c') {
1868                                         DEBUG(0, ("dry_run set\n"));
1869                                         dry_run = True;
1870                                 } else {
1871                                         DEBUG(0, ("n is only meaningful when creating a tar-file\n"));
1872                                         return 0;
1873                                 }
1874                                 break;
1875                         default:
1876                                 DEBUG(0,("Unknown tar option\n"));
1877                                 return 0;
1878                 }
1879         }
1880
1881         if (!tar_type) {
1882                 printf("Option T must be followed by one of c or x.\n");
1883                 return 0;
1884         }
1885
1886         /* tar_excl is true if cliplist lists files to be included.
1887          * Both 'I' and 'F' mean include. */
1888         tar_excl=tar_clipfl!='X';
1889
1890         if (tar_clipfl=='F') {
1891                 if (argc-Optind-1 != 1) {
1892                         DEBUG(0,("Option F must be followed by exactly one filename.\n"));
1893                         return 0;
1894                 }
1895                 newOptind++;
1896                 /* Optind points at the tar output file, Optind+1 at the inclusion file. */
1897                 if (! read_inclusion_file(argv[Optind+1])) {
1898                         return 0;
1899                 }
1900         } else if (Optind+1<argc && !tar_re_search) { /* For backwards compatibility */
1901                 char *tmpstr;
1902                 char **tmplist;
1903                 int clipcount;
1904
1905                 cliplist=argv+Optind+1;
1906                 clipn=argc-Optind-1;
1907                 clipcount = clipn;
1908
1909                 if ((tmplist=SMB_MALLOC_ARRAY(char *,clipn)) == NULL) {
1910                         DEBUG(0, ("Could not allocate space to process cliplist, count = %i\n", clipn));
1911                         return 0;
1912                 }
1913
1914                 for (clipcount = 0; clipcount < clipn; clipcount++) {
1915
1916                         DEBUG(5, ("Processing an item, %s\n", cliplist[clipcount]));
1917
1918                         if ((tmpstr = (char *)SMB_MALLOC(strlen(cliplist[clipcount])+1)) == NULL) {
1919                                 DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", clipcount));
1920                                 SAFE_FREE(tmplist);
1921                                 return 0;
1922                         }
1923
1924                         unfixtarname(tmpstr, cliplist[clipcount], strlen(cliplist[clipcount]) + 1, True);
1925                         tmplist[clipcount] = tmpstr;
1926                         DEBUG(5, ("Processed an item, %s\n", tmpstr));
1927
1928                         DEBUG(5, ("Cliplist is: %s\n", cliplist[0]));
1929                 }
1930
1931                 cliplist = tmplist;
1932                 must_free_cliplist = True;
1933
1934                 newOptind += clipn;
1935         }
1936
1937         if (Optind+1<argc && tar_re_search && tar_clipfl != 'F') {
1938                 /* Doing regular expression seaches not from an inclusion file. */
1939                 clipn=argc-Optind-1;
1940                 cliplist=argv+Optind+1;
1941                 newOptind += clipn;
1942         }
1943
1944         if (Optind>=argc || !strcmp(argv[Optind], "-")) {
1945                 /* Sets tar handle to either 0 or 1, as appropriate */
1946                 tarhandle=(tar_type=='c');
1947                 /*
1948                  * Make sure that dbf points to stderr if we are using stdout for 
1949                  * tar output
1950                  */
1951                 if (tarhandle == 1)  {
1952                         setup_logging("smbclient", DEBUG_STDERR);
1953                 }
1954                 if (!argv[Optind]) {
1955                         DEBUG(0,("Must specify tar filename\n"));
1956                         return 0;
1957                 }
1958                 if (!strcmp(argv[Optind], "-")) {
1959                         newOptind++;
1960                 }
1961
1962         } else {
1963                 if (tar_type=='c' && dry_run) {
1964                         tarhandle=-1;
1965                 } else if ((tar_type=='x' && (tarhandle = sys_open(argv[Optind], O_RDONLY, 0)) == -1)
1966                                         || (tar_type=='c' && (tarhandle=sys_creat(argv[Optind], 0644)) < 0)) {
1967                         DEBUG(0,("Error opening local file %s - %s\n", argv[Optind], strerror(errno)));
1968                         return(0);
1969                 }
1970                 newOptind++;
1971         }
1972
1973         return newOptind;
1974 }