s3: use monotonic clock for time deltas in smbtar
[Samba.git] / source3 / client / clitar.c
blob534b7218945818833f9c1d72bc5050fb4e52d6bc
1 /*
2 Unix SMB/CIFS implementation.
3 Tar Extensions
4 Copyright (C) Ricky Poulten 1995-1998
5 Copyright (C) Richard Sharpe 1998
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.
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.
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/>.
20 /* The following changes developed by Richard Sharpe for Canon Information
21 Systems Research Australia (CISRA)
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:
31 -TcrX file.tar "*.(obj|exe)"
33 will skip all .obj and .exe files
37 #include "includes.h"
38 #include "clitar.h"
39 #include "client/client_proto.h"
41 static int clipfind(char **aret, int ret, char *tok);
43 typedef struct file_info_struct file_info2;
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 ... */
58 typedef struct {
59 file_info2 *top;
60 int items;
61 } stack;
63 #define SEPARATORS " \t\n\r"
64 extern time_t newer_than;
65 extern struct cli_state *cli;
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
72 static uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
74 #ifndef CLIENT_TIMEOUT
75 #define CLIENT_TIMEOUT (30*1000)
76 #endif
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 */
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;
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;
112 static int blocksize=20;
113 static int tarhandle;
115 static void writetarheader(int f, const char *aname, uint64_t size, time_t mtime,
116 const char *amode, unsigned char ftype);
117 static void do_atar(const char *rname_in, char *lname,
118 struct file_info *finfo1);
119 static void do_tar(struct cli_state *cli_state, struct file_info *finfo,
120 const char *dir);
121 static void oct_it(uint64_t value, int ndgs, char *p);
122 static void fixtarname(char *tptr, const char *fp, size_t l);
123 static int dotarbuf(int f, char *b, int n);
124 static void dozerobuf(int f, int n);
125 static void dotareof(int f);
126 static void initarbuf(void);
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);
135 * tar specific utitlities
138 /*******************************************************************
139 Create a string of size size+1 (for the null)
140 *******************************************************************/
142 static char *string_create_s(int size)
144 char *tmp;
146 tmp = (char *)SMB_MALLOC(size+1);
148 if (tmp == NULL) {
149 DEBUG(0, ("Out of memory in string_create_s\n"));
152 return(tmp);
155 /****************************************************************************
156 Write a tar header to buffer
157 ****************************************************************************/
159 static void writetarheader(int f, const char *aname, uint64_t size, time_t mtime,
160 const char *amode, unsigned char ftype)
162 union hblock hb;
163 int i, chk, l;
164 char *jp;
166 DEBUG(5, ("WriteTarHdr, Type = %c, Size= %.0f, Name = %s\n", ftype, (double)size, aname));
168 memset(hb.dummy, 0, sizeof(hb.dummy));
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.
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);
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);
191 fixtarname(hb.dbuf.name, aname, (l+2 >= NAMSIZ) ? NAMSIZ : l + 2);
193 if (lowercase)
194 strlower_m(hb.dbuf.name);
196 /* write out a "standard" tar format header */
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. */
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++);
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;
217 for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;)
218 chk+=(0xFF & *jp++);
220 oct_it((uint64_t) chk, 8, hb.dbuf.chksum);
221 hb.dbuf.chksum[6] = '\0';
223 (void) dotarbuf(f, hb.dummy, sizeof(hb.dummy));
226 /****************************************************************************
227 Read a tar header into a hblock structure, and validate
228 ***************************************************************************/
230 static long readtarheader(union hblock *hb, file_info2 *finfo, const char *prefix)
232 long chk, fchk;
233 int i;
234 char *jp;
237 * read in a "standard" tar format header - we're not that interested
238 * in that many fields, though
241 /* check the checksum */
242 for (chk=0, i=sizeof(hb->dummy), jp=hb->dummy; --i>=0;)
243 chk+=(0xFF & *jp++);
245 if (chk == 0)
246 return chk;
248 /* compensate for blanks in chksum header */
249 for (i=sizeof(hb->dbuf.chksum), jp=hb->dbuf.chksum; --i>=0;)
250 chk-=(0xFF & *jp++);
252 chk += ' ' * sizeof(hb->dbuf.chksum);
254 fchk=unoct(hb->dbuf.chksum, sizeof(hb->dbuf.chksum));
256 DEBUG(5, ("checksum totals chk=%ld fchk=%ld chksum=%s\n",
257 chk, fchk, hb->dbuf.chksum));
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;
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);
270 safe_strcpy(finfo->name, prefix, strlen(prefix) + strlen(hb -> dbuf.name) + 3);
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);
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;
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 */
303 * Bug fix by richard@sj.co.uk
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
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));
314 return True;
317 /****************************************************************************
318 Write out the tar buffer to tape or wherever
319 ****************************************************************************/
321 static int dotarbuf(int f, char *b, int n)
323 int fail=1, writ=n;
325 if (dry_run) {
326 return writ;
328 /* This routine and the next one should be the only ones that do write()s */
329 if (tp + n >= tbufsiz) {
330 int diff;
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;
339 while (n >= tbufsiz) {
340 fail=fail && (1 + sys_write(f, b, tbufsiz));
341 n-=tbufsiz;
342 b+=tbufsiz;
346 if (n>0) {
347 memcpy(tarbuf+tp, b, n);
348 tp+=n;
351 return(fail ? writ : 0);
354 /****************************************************************************
355 Write zeros to buffer / tape
356 ****************************************************************************/
358 static void dozerobuf(int f, int n)
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 */
364 if (dry_run)
365 return;
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;
373 memset(tarbuf, 0, (tp+=n-tbufsiz));
374 } else {
375 memset(tarbuf+tp, 0, n);
376 tp+=n;
380 /****************************************************************************
381 Malloc tape buffer
382 ****************************************************************************/
384 static void initarbuf(void)
386 /* initialize tar buffer */
387 tbufsiz=blocksize*TBLOCK;
388 tarbuf=(char *)SMB_MALLOC(tbufsiz); /* FIXME: We might not get the buffer */
390 /* reset tar buffer pointer and tar file counter and total dumped */
391 tp=0; ntarf=0; ttarf=0;
394 /****************************************************************************
395 Write two zero blocks at end of file
396 ****************************************************************************/
398 static void dotareof(int f)
400 SMB_STRUCT_STAT stbuf;
401 /* Two zero blocks at end of file, write out full buffer */
403 if (dry_run)
404 return;
406 (void) dozerobuf(f, TBLOCK);
407 (void) dozerobuf(f, TBLOCK);
409 if (sys_fstat(f, &stbuf, false) == -1) {
410 DEBUG(0, ("Couldn't stat file handle\n"));
411 return;
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"));
424 /****************************************************************************
425 (Un)mangle DOS pathname, make nonabsolute
426 ****************************************************************************/
428 static void fixtarname(char *tptr, const char *fp, size_t l)
430 /* add a '.' to start of file name, convert from ugly dos \'s in path
431 * to lovely unix /'s :-} */
432 *tptr++='.';
433 l--;
435 StrnCpy(tptr, fp, l-1);
436 string_replace(tptr, '\\', '/');
439 /****************************************************************************
440 Convert from decimal to octal string
441 ****************************************************************************/
443 static void oct_it (uint64_t value, int ndgs, char *p)
445 /* Converts long to octal string, pads with leading zeros */
447 /* skip final null, but do final space */
448 --ndgs;
449 p[--ndgs] = ' ';
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);
457 /* Do leading zeros */
458 while (ndgs > 0)
459 p[--ndgs] = '0';
462 /****************************************************************************
463 Convert from octal string to long
464 ***************************************************************************/
466 static long unoct(char *p, int ndgs)
468 long value=0;
469 /* Converts octal string to long, ignoring any non-digit */
471 while (--ndgs) {
472 if (isdigit((int)*p))
473 value = (value << 3) | (long) (*p - '0');
475 p++;
478 return value;
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 ***************************************************************************/
487 static int strslashcmp(char *s1, char *s2)
489 char *s1_0=s1;
491 while(*s1 && *s2 && (*s1 == *s2 || tolower_ascii(*s1) == tolower_ascii(*s2) ||
492 (*s1 == '\\' && *s2=='/') || (*s1 == '/' && *s2=='\\'))) {
493 s1++; s2++;
496 /* if s1 has a trailing slash, it compared equal, so s1 is an "initial"
497 string of s2.
499 if (!*s1 && s1 != s1_0 && (*(s1-1) == '/' || *(s1-1) == '\\'))
500 return 0;
502 /* ignore trailing slash on s1 */
503 if (!*s2 && (*s1 == '/' || *s1 == '\\') && !*(s1+1))
504 return 0;
506 /* check for s1 is an "initial" string of s2 */
507 if ((*s2 == '/' || *s2 == '\\') && !*s1)
508 return 0;
510 return *s1-*s2;
513 /****************************************************************************
514 Ensure a remote path exists (make if necessary)
515 ***************************************************************************/
517 static bool ensurepath(const char *fname)
519 /* *must* be called with buffer ready malloc'ed */
520 /* ensures path exists */
522 char *partpath, *ffname;
523 const char *p=fname;
524 char *basehack;
525 char *saveptr;
527 DEBUG(5, ( "Ensurepath called with: %s\n", fname));
529 partpath = string_create_s(strlen(fname));
530 ffname = string_create_s(strlen(fname));
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);
539 *partpath = 0;
541 /* fname copied to ffname so can strtok_r */
543 safe_strcpy(ffname, fname, strlen(fname));
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';
554 p=strtok_r(ffname, "\\", &saveptr);
556 while (p) {
557 safe_strcat(partpath, p, strlen(fname) + 1);
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));
570 safe_strcat(partpath, "\\", strlen(fname) + 1);
571 p = strtok_r(NULL, "/\\", &saveptr);
574 SAFE_FREE(partpath);
575 SAFE_FREE(ffname);
576 return True;
579 static int padit(char *buf, uint64_t bufsize, uint64_t padsize)
581 int berr= 0;
582 int bytestowrite;
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;
592 return berr;
595 static void do_setrattr(char *name, uint16 attr, int set)
597 uint16 oldattr;
599 if (!NT_STATUS_IS_OK(cli_getatr(cli, name, &oldattr, NULL, NULL))) {
600 return;
603 if (set == ATTRSET) {
604 attr |= oldattr;
605 } else {
606 attr = oldattr & ~attr;
609 if (!NT_STATUS_IS_OK(cli_setatr(cli, name, attr, 0))) {
610 DEBUG(1,("setatr failed: %s\n", cli_errstr(cli)));
614 /****************************************************************************
615 append one remote file to the tar file
616 ***************************************************************************/
618 static void do_atar(const char *rname_in, char *lname,
619 struct file_info *finfo1)
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();
632 struct timespec tp_start;
634 clock_gettime_mono(&tp_start);
636 data = SMB_MALLOC_ARRAY(char, read_size);
637 if (!data) {
638 DEBUG(0,("do_atar: out of memory.\n"));
639 goto cleanup;
642 ftype = '0'; /* An ordinary file ... */
644 ZERO_STRUCT(finfo);
646 finfo.size = finfo1 -> size;
647 finfo.mode = finfo1 -> mode;
648 finfo.uid = finfo1 -> uid;
649 finfo.gid = finfo1 -> gid;
650 finfo.mtime_ts = finfo1 -> mtime_ts;
651 finfo.atime_ts = finfo1 -> atime_ts;
652 finfo.ctime_ts = finfo1 -> ctime_ts;
654 if (dry_run) {
655 DEBUG(3,("skipping file %s of size %12.0f bytes\n", finfo1->name,
656 (double)finfo.size));
657 shallitime=0;
658 ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
659 ntarf++;
660 goto cleanup;
663 rname = clean_name(ctx, rname_in);
664 if (!rname) {
665 goto cleanup;
668 if (!NT_STATUS_IS_OK(cli_open(cli, rname, O_RDONLY, DENY_NONE, &fnum))) {
669 DEBUG(0,("%s opening remote file %s (%s)\n",
670 cli_errstr(cli),rname, client_get_cur_dir()));
671 goto cleanup;
674 finfo.name = string_create_s(strlen(rname));
675 if (finfo.name == NULL) {
676 DEBUG(0, ("Unable to allocate space for finfo.name in do_atar\n"));
677 goto cleanup;
680 safe_strcpy(finfo.name,rname, strlen(rname));
682 DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode));
684 if (tar_inc && !(finfo.mode & aARCH)) {
685 DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
686 shallitime=0;
687 } else if (!tar_system && (finfo.mode & aSYSTEM)) {
688 DEBUG(4, ("skipping %s - system bit is set\n", finfo.name));
689 shallitime=0;
690 } else if (!tar_hidden && (finfo.mode & aHIDDEN)) {
691 DEBUG(4, ("skipping %s - hidden bit is set\n", finfo.name));
692 shallitime=0;
693 } else {
694 bool wrote_tar_header = False;
696 DEBUG(3,("getting file %s of size %.0f bytes as a tar file %s",
697 finfo.name, (double)finfo.size, lname));
699 do {
701 DEBUG(3,("nread=%.0f\n",(double)nread));
703 datalen = cli_read(cli, fnum, data, nread, read_size);
705 if (datalen == -1) {
706 DEBUG(0,("Error reading file %s : %s\n", rname, cli_errstr(cli)));
707 break;
710 nread += datalen;
712 /* Only if the first read succeeds, write out the tar header. */
713 if (!wrote_tar_header) {
714 /* write a tar header, don't bother with mode - just set to 100644 */
715 writetarheader(tarhandle, rname, finfo.size,
716 finfo.mtime_ts.tv_sec, "100644 \0", ftype);
717 wrote_tar_header = True;
720 /* if file size has increased since we made file size query, truncate
721 read so tar header for this file will be correct.
724 if (nread > finfo.size) {
725 datalen -= nread - finfo.size;
726 DEBUG(0,("File size change - truncating %s to %.0f bytes\n",
727 finfo.name, (double)finfo.size));
730 /* add received bits of file to buffer - dotarbuf will
731 * write out in 512 byte intervals */
733 if (dotarbuf(tarhandle,data,datalen) != datalen) {
734 DEBUG(0,("Error writing to tar file - %s\n", strerror(errno)));
735 break;
738 if ( (datalen == 0) && (finfo.size != 0) ) {
739 DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname));
740 break;
743 datalen=0;
744 } while ( nread < finfo.size );
746 if (wrote_tar_header) {
747 /* pad tar file with zero's if we couldn't get entire file */
748 if (nread < finfo.size) {
749 DEBUG(0, ("Didn't get entire file. size=%.0f, nread=%d\n",
750 (double)finfo.size, (int)nread));
751 if (padit(data, (uint64_t)sizeof(data), finfo.size - nread))
752 DEBUG(0,("Error writing tar file - %s\n", strerror(errno)));
755 /* round tar file to nearest block */
756 if (finfo.size % TBLOCK)
757 dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
759 ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
760 ntarf++;
761 } else {
762 DEBUG(4, ("skipping %s - initial read failed (file was locked ?)\n", finfo.name));
763 shallitime=0;
767 cli_close(cli, fnum);
768 fnum = -1;
770 if (shallitime) {
771 struct timespec tp_end;
772 int this_time;
774 /* if shallitime is true then we didn't skip */
775 if (tar_reset && !dry_run)
776 (void) do_setrattr(finfo.name, aARCH, ATTRRESET);
778 clock_gettime_mono(&tp_end);
779 this_time = (tp_end.tv_sec - tp_start.tv_sec)*1000 + (tp_end.tv_nsec - tp_start.tv_nsec)/1000000;
780 get_total_time_ms += this_time;
781 get_total_size += finfo.size;
783 if (tar_noisy) {
784 DEBUG(0, ("%12.0f (%7.1f kb/s) %s\n",
785 (double)finfo.size, finfo.size / MAX(0.001, (1.024*this_time)),
786 finfo.name));
789 /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */
790 DEBUG(3,("(%g kb/s) (average %g kb/s)\n",
791 finfo.size / MAX(0.001, (1.024*this_time)),
792 get_total_size / MAX(0.001, (1.024*get_total_time_ms))));
795 cleanup:
797 if (fnum != (uint16_t)-1) {
798 cli_close(cli, fnum);
799 fnum = -1;
801 TALLOC_FREE(ctx);
802 SAFE_FREE(data);
805 /****************************************************************************
806 Append single file to tar file (or not)
807 ***************************************************************************/
809 static void do_tar(struct cli_state *cli_state, struct file_info *finfo,
810 const char *dir)
812 TALLOC_CTX *ctx = talloc_stackframe();
814 if (strequal(finfo->name,"..") || strequal(finfo->name,"."))
815 return;
817 /* Is it on the exclude list ? */
818 if (!tar_excl && clipn) {
819 char *exclaim;
821 DEBUG(5, ("Excl: strlen(cur_dir) = %d\n", (int)strlen(client_get_cur_dir())));
823 exclaim = talloc_asprintf(ctx,
824 "%s\\%s",
825 client_get_cur_dir(),
826 finfo->name);
827 if (!exclaim) {
828 return;
831 DEBUG(5, ("...tar_re_search: %d\n", tar_re_search));
833 if ((!tar_re_search && clipfind(cliplist, clipn, exclaim)) ||
834 (tar_re_search && mask_match_list(exclaim, cliplist, clipn, True))) {
835 DEBUG(3,("Skipping file %s\n", exclaim));
836 TALLOC_FREE(exclaim);
837 return;
839 TALLOC_FREE(exclaim);
842 if (finfo->mode & aDIR) {
843 char *saved_curdir = NULL;
844 char *new_cd = NULL;
845 char *mtar_mask = NULL;
847 saved_curdir = talloc_strdup(ctx, client_get_cur_dir());
848 if (!saved_curdir) {
849 return;
852 DEBUG(5, ("strlen(cur_dir)=%d, \
853 strlen(finfo->name)=%d\nname=%s,cur_dir=%s\n",
854 (int)strlen(saved_curdir),
855 (int)strlen(finfo->name), finfo->name, saved_curdir));
857 new_cd = talloc_asprintf(ctx,
858 "%s%s\\",
859 client_get_cur_dir(),
860 finfo->name);
861 if (!new_cd) {
862 return;
864 client_set_cur_dir(new_cd);
866 DEBUG(5, ("Writing a dir, Name = %s\n", client_get_cur_dir()));
868 /* write a tar directory, don't bother with mode - just
869 * set it to 40755 */
870 writetarheader(tarhandle, client_get_cur_dir(), 0,
871 finfo->mtime_ts.tv_sec, "040755 \0", '5');
872 if (tar_noisy) {
873 DEBUG(0,(" directory %s\n",
874 client_get_cur_dir()));
876 ntarf++; /* Make sure we have a file on there */
877 mtar_mask = talloc_asprintf(ctx,
878 "%s*",
879 client_get_cur_dir());
880 if (!mtar_mask) {
881 return;
883 DEBUG(5, ("Doing list with mtar_mask: %s\n", mtar_mask));
884 do_list(mtar_mask, attribute, do_tar, False, True);
885 client_set_cur_dir(saved_curdir);
886 TALLOC_FREE(saved_curdir);
887 TALLOC_FREE(new_cd);
888 TALLOC_FREE(mtar_mask);
889 } else {
890 char *rname = talloc_asprintf(ctx,
891 "%s%s",
892 client_get_cur_dir(),
893 finfo->name);
894 if (!rname) {
895 return;
897 do_atar(rname,finfo->name,finfo);
898 TALLOC_FREE(rname);
902 /****************************************************************************
903 Convert from UNIX to DOS file names
904 ***************************************************************************/
906 static void unfixtarname(char *tptr, char *fp, int l, bool first)
908 /* remove '.' from start of file name, convert from unix /'s to
909 * dos \'s in path. Kill any absolute path names. But only if first!
912 DEBUG(5, ("firstb=%lX, secondb=%lX, len=%i\n", (long)tptr, (long)fp, l));
914 if (first) {
915 if (*fp == '.') {
916 fp++;
917 l--;
919 if (*fp == '\\' || *fp == '/') {
920 fp++;
921 l--;
925 safe_strcpy(tptr, fp, l);
926 string_replace(tptr, '/', '\\');
929 /****************************************************************************
930 Move to the next block in the buffer, which may mean read in another set of
931 blocks. FIXME, we should allow more than one block to be skipped.
932 ****************************************************************************/
934 static int next_block(char *ltarbuf, char **bufferp, int bufsiz)
936 int bufread, total = 0;
938 DEBUG(5, ("Advancing to next block: %0lx\n", (unsigned long)*bufferp));
939 *bufferp += TBLOCK;
940 total = TBLOCK;
942 if (*bufferp >= (ltarbuf + bufsiz)) {
944 DEBUG(5, ("Reading more data into ltarbuf ...\n"));
947 * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>
948 * Fixes bug where read can return short if coming from
949 * a pipe.
952 bufread = read(tarhandle, ltarbuf, bufsiz);
953 total = bufread;
955 while (total < bufsiz) {
956 if (bufread < 0) { /* An error, return false */
957 return (total > 0 ? -2 : bufread);
959 if (bufread == 0) {
960 if (total <= 0) {
961 return -2;
963 break;
965 bufread = read(tarhandle, &ltarbuf[total], bufsiz - total);
966 total += bufread;
969 DEBUG(5, ("Total bytes read ... %i\n", total));
971 *bufferp = ltarbuf;
974 return(total);
977 /* Skip a file, even if it includes a long file name? */
978 static int skip_file(int skipsize)
980 int dsize = skipsize;
982 DEBUG(5, ("Skiping file. Size = %i\n", skipsize));
984 /* FIXME, we should skip more than one block at a time */
986 while (dsize > 0) {
987 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
988 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
989 return(False);
991 dsize -= TBLOCK;
994 return(True);
997 /*************************************************************
998 Get a file from the tar file and store it.
999 When this is called, tarbuf already contains the first
1000 file block. This is a bit broken & needs fixing.
1001 **************************************************************/
1003 static int get_file(file_info2 finfo)
1005 uint16_t fnum = (uint16_t) -1;
1006 int pos = 0, dsize = 0, bpos = 0;
1007 uint64_t rsize = 0;
1008 NTSTATUS status;
1010 DEBUG(5, ("get_file: file: %s, size %.0f\n", finfo.name, (double)finfo.size));
1012 if (!ensurepath(finfo.name)) {
1013 DEBUG(0, ("abandoning restore\n"));
1014 return False;
1017 status = cli_open(cli, finfo.name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE, &fnum);
1018 if (!NT_STATUS_IS_OK(status)) {
1019 DEBUG(0, ("abandoning restore\n"));
1020 return False;
1023 /* read the blocks from the tar file and write to the remote file */
1025 rsize = finfo.size; /* This is how much to write */
1027 while (rsize > 0) {
1029 /* We can only write up to the end of the buffer */
1030 dsize = MIN(tbufsiz - (buffer_p - tarbuf) - bpos, 65520); /* Calculate the size to write */
1031 dsize = MIN(dsize, rsize); /* Should be only what is left */
1032 DEBUG(5, ("writing %i bytes, bpos = %i ...\n", dsize, bpos));
1034 if (cli_write(cli, fnum, 0, buffer_p + bpos, pos, dsize) != dsize) {
1035 DEBUG(0, ("Error writing remote file\n"));
1036 return 0;
1039 rsize -= dsize;
1040 pos += dsize;
1042 /* Now figure out how much to move in the buffer */
1044 /* FIXME, we should skip more than one block at a time */
1046 /* First, skip any initial part of the part written that is left over */
1047 /* from the end of the first TBLOCK */
1049 if ((bpos) && ((bpos + dsize) >= TBLOCK)) {
1050 dsize -= (TBLOCK - bpos); /* Get rid of the end of the first block */
1051 bpos = 0;
1053 if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) { /* and skip the block */
1054 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1055 return False;
1060 * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>.
1061 * If the file being extracted is an exact multiple of
1062 * TBLOCK bytes then we don't want to extract the next
1063 * block from the tarfile here, as it will be done in
1064 * the caller of get_file().
1067 while (((rsize != 0) && (dsize >= TBLOCK)) ||
1068 ((rsize == 0) && (dsize > TBLOCK))) {
1070 if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
1071 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1072 return False;
1075 dsize -= TBLOCK;
1077 bpos = dsize;
1080 /* Now close the file ... */
1082 if (!NT_STATUS_IS_OK(cli_close(cli, fnum))) {
1083 DEBUG(0, ("Error %s closing remote file\n",
1084 cli_errstr(cli)));
1085 return(False);
1088 /* Now we update the creation date ... */
1089 DEBUG(5, ("Updating creation date on %s\n", finfo.name));
1091 if (!NT_STATUS_IS_OK(cli_setatr(cli, finfo.name, finfo.mode, finfo.mtime_ts.tv_sec))) {
1092 if (tar_real_noisy) {
1093 DEBUG(0, ("Could not set time on file: %s\n", finfo.name));
1094 /*return(False); */ /* Ignore, as Win95 does not allow changes */
1098 ntarf++;
1099 DEBUG(0, ("restore tar file %s of size %.0f bytes\n", finfo.name, (double)finfo.size));
1100 return(True);
1103 /* Create a directory. We just ensure that the path exists and return as there
1104 is no file associated with a directory
1106 static int get_dir(file_info2 finfo)
1108 DEBUG(0, ("restore directory %s\n", finfo.name));
1110 if (!ensurepath(finfo.name)) {
1111 DEBUG(0, ("Problems creating directory\n"));
1112 return(False);
1114 ntarf++;
1115 return(True);
1118 /* Get a file with a long file name ... first file has file name, next file
1119 has the data. We only want the long file name, as the loop in do_tarput
1120 will deal with the rest.
1122 static char *get_longfilename(file_info2 finfo)
1124 /* finfo.size here is the length of the filename as written by the "/./@LongLink" name
1125 * header call. */
1126 int namesize = finfo.size + strlen(client_get_cur_dir()) + 2;
1127 char *longname = (char *)SMB_MALLOC(namesize);
1128 int offset = 0, left = finfo.size;
1129 bool first = True;
1131 DEBUG(5, ("Restoring a long file name: %s\n", finfo.name));
1132 DEBUG(5, ("Len = %.0f\n", (double)finfo.size));
1134 if (longname == NULL) {
1135 DEBUG(0, ("could not allocate buffer of size %d for longname\n", namesize));
1136 return(NULL);
1139 /* First, add cur_dir to the long file name */
1141 if (strlen(client_get_cur_dir()) > 0) {
1142 strncpy(longname, client_get_cur_dir(), namesize);
1143 offset = strlen(client_get_cur_dir());
1146 /* Loop through the blocks picking up the name */
1148 while (left > 0) {
1149 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
1150 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1151 SAFE_FREE(longname);
1152 return(NULL);
1155 unfixtarname(longname + offset, buffer_p, MIN(TBLOCK, finfo.size), first--);
1156 DEBUG(5, ("UnfixedName: %s, buffer: %s\n", longname, buffer_p));
1158 offset += TBLOCK;
1159 left -= TBLOCK;
1162 return(longname);
1165 static void do_tarput(void)
1167 file_info2 finfo;
1168 struct timespec tp_start;
1169 char *longfilename = NULL, linkflag;
1170 int skip = False;
1172 ZERO_STRUCT(finfo);
1174 clock_gettime_mono(&tp_start);
1175 DEBUG(5, ("RJS do_tarput called ...\n"));
1177 buffer_p = tarbuf + tbufsiz; /* init this to force first read */
1179 /* Now read through those files ... */
1180 while (True) {
1181 /* Get us to the next block, or the first block first time around */
1182 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
1183 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1184 SAFE_FREE(longfilename);
1185 return;
1188 DEBUG(5, ("Reading the next header ...\n"));
1190 switch (readtarheader((union hblock *) buffer_p,
1191 &finfo, client_get_cur_dir())) {
1192 case -2: /* Hmm, not good, but not fatal */
1193 DEBUG(0, ("Skipping %s...\n", finfo.name));
1194 if ((next_block(tarbuf, &buffer_p, tbufsiz) <= 0) && !skip_file(finfo.size)) {
1195 DEBUG(0, ("Short file, bailing out...\n"));
1196 return;
1198 break;
1200 case -1:
1201 DEBUG(0, ("abandoning restore, -1 from read tar header\n"));
1202 return;
1204 case 0: /* chksum is zero - looks like an EOF */
1205 DEBUG(0, ("tar: restored %d files and directories\n", ntarf));
1206 return; /* Hmmm, bad here ... */
1208 default:
1209 /* No action */
1210 break;
1213 /* Now, do we have a long file name? */
1214 if (longfilename != NULL) {
1215 SAFE_FREE(finfo.name); /* Free the space already allocated */
1216 finfo.name = longfilename;
1217 longfilename = NULL;
1220 /* Well, now we have a header, process the file ... */
1221 /* Should we skip the file? We have the long name as well here */
1222 skip = clipn && ((!tar_re_search && clipfind(cliplist, clipn, finfo.name) ^ tar_excl) ||
1223 (tar_re_search && mask_match_list(finfo.name, cliplist, clipn, True)));
1225 DEBUG(5, ("Skip = %i, cliplist=%s, file=%s\n", skip, (cliplist?cliplist[0]:NULL), finfo.name));
1226 if (skip) {
1227 skip_file(finfo.size);
1228 continue;
1231 /* We only get this far if we should process the file */
1232 linkflag = ((union hblock *)buffer_p) -> dbuf.linkflag;
1233 switch (linkflag) {
1234 case '0': /* Should use symbolic names--FIXME */
1236 * Skip to the next block first, so we can get the file, FIXME, should
1237 * be in get_file ...
1238 * The 'finfo.size != 0' fix is from Bob Boehmer <boehmer@worldnet.att.net>
1239 * Fixes bug where file size in tarfile is zero.
1241 if ((finfo.size != 0) && next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
1242 DEBUG(0, ("Short file, bailing out...\n"));
1243 return;
1245 if (!get_file(finfo)) {
1246 DEBUG(0, ("Abandoning restore\n"));
1247 return;
1249 break;
1250 case '5':
1251 if (!get_dir(finfo)) {
1252 DEBUG(0, ("Abandoning restore \n"));
1253 return;
1255 break;
1256 case 'L':
1257 SAFE_FREE(longfilename);
1258 longfilename = get_longfilename(finfo);
1259 if (!longfilename) {
1260 DEBUG(0, ("abandoning restore\n"));
1261 return;
1263 DEBUG(5, ("Long file name: %s\n", longfilename));
1264 break;
1266 default:
1267 skip_file(finfo.size); /* Don't handle these yet */
1268 break;
1274 * samba interactive commands
1277 /****************************************************************************
1278 Blocksize command
1279 ***************************************************************************/
1281 int cmd_block(void)
1283 TALLOC_CTX *ctx = talloc_tos();
1284 char *buf;
1285 int block;
1287 if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1288 DEBUG(0, ("blocksize <n>\n"));
1289 return 1;
1292 block=atoi(buf);
1293 if (block < 0 || block > 65535) {
1294 DEBUG(0, ("blocksize out of range"));
1295 return 1;
1298 blocksize=block;
1299 DEBUG(2,("blocksize is now %d\n", blocksize));
1300 return 0;
1303 /****************************************************************************
1304 command to set incremental / reset mode
1305 ***************************************************************************/
1307 int cmd_tarmode(void)
1309 TALLOC_CTX *ctx = talloc_tos();
1310 char *buf;
1312 while (next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1313 if (strequal(buf, "full"))
1314 tar_inc=False;
1315 else if (strequal(buf, "inc"))
1316 tar_inc=True;
1317 else if (strequal(buf, "reset"))
1318 tar_reset=True;
1319 else if (strequal(buf, "noreset"))
1320 tar_reset=False;
1321 else if (strequal(buf, "system"))
1322 tar_system=True;
1323 else if (strequal(buf, "nosystem"))
1324 tar_system=False;
1325 else if (strequal(buf, "hidden"))
1326 tar_hidden=True;
1327 else if (strequal(buf, "nohidden"))
1328 tar_hidden=False;
1329 else if (strequal(buf, "verbose") || strequal(buf, "noquiet"))
1330 tar_noisy=True;
1331 else if (strequal(buf, "quiet") || strequal(buf, "noverbose"))
1332 tar_noisy=False;
1333 else
1334 DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
1335 TALLOC_FREE(buf);
1338 DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n",
1339 tar_inc ? "incremental" : "full",
1340 tar_system ? "system" : "nosystem",
1341 tar_hidden ? "hidden" : "nohidden",
1342 tar_reset ? "reset" : "noreset",
1343 tar_noisy ? "verbose" : "quiet"));
1344 return 0;
1347 /****************************************************************************
1348 Feeble attrib command
1349 ***************************************************************************/
1351 int cmd_setmode(void)
1353 TALLOC_CTX *ctx = talloc_tos();
1354 char *q;
1355 char *buf;
1356 char *fname = NULL;
1357 uint16 attra[2];
1358 int direct=1;
1360 attra[0] = attra[1] = 0;
1362 if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1363 DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1364 return 1;
1367 fname = talloc_asprintf(ctx,
1368 "%s%s",
1369 client_get_cur_dir(),
1370 buf);
1371 if (!fname) {
1372 return 1;
1375 while (next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1376 q=buf;
1378 while(*q) {
1379 switch (*q++) {
1380 case '+':
1381 direct=1;
1382 break;
1383 case '-':
1384 direct=0;
1385 break;
1386 case 'r':
1387 attra[direct]|=aRONLY;
1388 break;
1389 case 'h':
1390 attra[direct]|=aHIDDEN;
1391 break;
1392 case 's':
1393 attra[direct]|=aSYSTEM;
1394 break;
1395 case 'a':
1396 attra[direct]|=aARCH;
1397 break;
1398 default:
1399 DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
1400 return 1;
1405 if (attra[ATTRSET]==0 && attra[ATTRRESET]==0) {
1406 DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1407 return 1;
1410 DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
1411 do_setrattr(fname, attra[ATTRSET], ATTRSET);
1412 do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
1413 return 0;
1417 Convert list of tokens to array; dependent on above routine.
1418 Uses the global cmd_ptr from above - bit of a hack.
1421 static char **toktocliplist(int *ctok, const char *sep)
1423 char *s=(char *)cmd_ptr;
1424 int ictok=0;
1425 char **ret, **iret;
1427 if (!sep)
1428 sep = " \t\n\r";
1430 while(*s && strchr_m(sep,*s))
1431 s++;
1433 /* nothing left? */
1434 if (!*s)
1435 return(NULL);
1437 do {
1438 ictok++;
1439 while(*s && (!strchr_m(sep,*s)))
1440 s++;
1441 while(*s && strchr_m(sep,*s))
1442 *s++=0;
1443 } while(*s);
1445 *ctok=ictok;
1446 s=(char *)cmd_ptr;
1448 if (!(ret=iret=SMB_MALLOC_ARRAY(char *,ictok+1)))
1449 return NULL;
1451 while(ictok--) {
1452 *iret++=s;
1453 if (ictok > 0) {
1454 while(*s++)
1456 while(!*s)
1457 s++;
1461 ret[*ctok] = NULL;
1462 return ret;
1465 /****************************************************************************
1466 Principal command for creating / extracting
1467 ***************************************************************************/
1469 int cmd_tar(void)
1471 TALLOC_CTX *ctx = talloc_tos();
1472 char *buf;
1473 char **argl = NULL;
1474 int argcl = 0;
1475 int ret;
1477 if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL)) {
1478 DEBUG(0,("tar <c|x>[IXbgan] <filename>\n"));
1479 return 1;
1482 argl=toktocliplist(&argcl, NULL);
1483 if (!tar_parseargs(argcl, argl, buf, 0)) {
1484 SAFE_FREE(argl);
1485 return 1;
1488 ret = process_tar();
1489 SAFE_FREE(argl);
1490 return ret;
1493 /****************************************************************************
1494 Command line (option) version
1495 ***************************************************************************/
1497 int process_tar(void)
1499 TALLOC_CTX *ctx = talloc_tos();
1500 int rc = 0;
1501 initarbuf();
1502 switch(tar_type) {
1503 case 'x':
1505 #if 0
1506 do_tarput2();
1507 #else
1508 do_tarput();
1509 #endif
1510 SAFE_FREE(tarbuf);
1511 close(tarhandle);
1512 break;
1513 case 'r':
1514 case 'c':
1515 if (clipn && tar_excl) {
1516 int i;
1517 char *tarmac = NULL;
1519 for (i=0; i<clipn; i++) {
1520 DEBUG(5,("arg %d = %s\n", i, cliplist[i]));
1522 if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
1523 *(cliplist[i]+strlen(cliplist[i])-1)='\0';
1526 if (strrchr_m(cliplist[i], '\\')) {
1527 char *p;
1528 char saved_char;
1529 char *saved_dir = talloc_strdup(ctx,
1530 client_get_cur_dir());
1531 if (!saved_dir) {
1532 return 1;
1535 if (*cliplist[i]=='\\') {
1536 tarmac = talloc_strdup(ctx,
1537 cliplist[i]);
1538 } else {
1539 tarmac = talloc_asprintf(ctx,
1540 "%s%s",
1541 client_get_cur_dir(),
1542 cliplist[i]);
1544 if (!tarmac) {
1545 return 1;
1548 * Strip off the last \\xxx
1549 * xxx element of tarmac to set
1550 * it as current directory.
1552 p = strrchr_m(tarmac, '\\');
1553 if (!p) {
1554 return 1;
1556 saved_char = p[1];
1557 p[1] = '\0';
1559 client_set_cur_dir(tarmac);
1562 * Restore the character we
1563 * just replaced to
1564 * put the pathname
1565 * back as it was.
1567 p[1] = saved_char;
1569 DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1570 do_list(tarmac,attribute,do_tar, False, True);
1572 client_set_cur_dir(saved_dir);
1574 TALLOC_FREE(saved_dir);
1575 TALLOC_FREE(tarmac);
1576 } else {
1577 tarmac = talloc_asprintf(ctx,
1578 "%s%s",
1579 client_get_cur_dir(),
1580 cliplist[i]);
1581 if (!tarmac) {
1582 return 1;
1584 DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1585 do_list(tarmac,attribute,do_tar, False, True);
1586 TALLOC_FREE(tarmac);
1589 } else {
1590 char *mask = talloc_asprintf(ctx,
1591 "%s\\*",
1592 client_get_cur_dir());
1593 if (!mask) {
1594 return 1;
1596 DEBUG(5, ("process_tar, do_list with mask: %s\n", mask));
1597 do_list(mask,attribute,do_tar,False, True);
1598 TALLOC_FREE(mask);
1601 if (ntarf) {
1602 dotareof(tarhandle);
1604 close(tarhandle);
1605 SAFE_FREE(tarbuf);
1607 DEBUG(0, ("tar: dumped %d files and directories\n", ntarf));
1608 DEBUG(0, ("Total bytes written: %.0f\n", (double)ttarf));
1609 break;
1612 if (must_free_cliplist) {
1613 int i;
1614 for (i = 0; i < clipn; ++i) {
1615 SAFE_FREE(cliplist[i]);
1617 SAFE_FREE(cliplist);
1618 cliplist = NULL;
1619 clipn = 0;
1620 must_free_cliplist = False;
1622 return rc;
1625 /****************************************************************************
1626 Find a token (filename) in a clip list
1627 ***************************************************************************/
1629 static int clipfind(char **aret, int ret, char *tok)
1631 if (aret==NULL)
1632 return 0;
1634 /* ignore leading slashes or dots in token */
1635 while(strchr_m("/\\.", *tok))
1636 tok++;
1638 while(ret--) {
1639 char *pkey=*aret++;
1641 /* ignore leading slashes or dots in list */
1642 while(strchr_m("/\\.", *pkey))
1643 pkey++;
1645 if (!strslashcmp(pkey, tok))
1646 return 1;
1648 return 0;
1651 /****************************************************************************
1652 Read list of files to include from the file and initialize cliplist
1653 accordingly.
1654 ***************************************************************************/
1656 static int read_inclusion_file(char *filename)
1658 XFILE *inclusion = NULL;
1659 char buf[PATH_MAX + 1];
1660 char *inclusion_buffer = NULL;
1661 int inclusion_buffer_size = 0;
1662 int inclusion_buffer_sofar = 0;
1663 char *p;
1664 char *tmpstr;
1665 int i;
1666 int error = 0;
1668 clipn = 0;
1669 buf[PATH_MAX] = '\0'; /* guarantee null-termination */
1670 if ((inclusion = x_fopen(filename, O_RDONLY, 0)) == NULL) {
1671 /* XXX It would be better to include a reason for failure, but without
1672 * autoconf, it's hard to use strerror, sys_errlist, etc.
1674 DEBUG(0,("Unable to open inclusion file %s\n", filename));
1675 return 0;
1678 while ((! error) && (x_fgets(buf, sizeof(buf)-1, inclusion))) {
1679 if (inclusion_buffer == NULL) {
1680 inclusion_buffer_size = 1024;
1681 if ((inclusion_buffer = (char *)SMB_MALLOC(inclusion_buffer_size)) == NULL) {
1682 DEBUG(0,("failure allocating buffer to read inclusion file\n"));
1683 error = 1;
1684 break;
1688 if (buf[strlen(buf)-1] == '\n') {
1689 buf[strlen(buf)-1] = '\0';
1692 if ((strlen(buf) + 1 + inclusion_buffer_sofar) >= inclusion_buffer_size) {
1693 inclusion_buffer_size *= 2;
1694 inclusion_buffer = (char *)SMB_REALLOC(inclusion_buffer,inclusion_buffer_size);
1695 if (!inclusion_buffer) {
1696 DEBUG(0,("failure enlarging inclusion buffer to %d bytes\n",
1697 inclusion_buffer_size));
1698 error = 1;
1699 break;
1703 safe_strcpy(inclusion_buffer + inclusion_buffer_sofar, buf, inclusion_buffer_size - inclusion_buffer_sofar);
1704 inclusion_buffer_sofar += strlen(buf) + 1;
1705 clipn++;
1707 x_fclose(inclusion);
1709 if (! error) {
1710 /* Allocate an array of clipn + 1 char*'s for cliplist */
1711 cliplist = SMB_MALLOC_ARRAY(char *, clipn + 1);
1712 if (cliplist == NULL) {
1713 DEBUG(0,("failure allocating memory for cliplist\n"));
1714 error = 1;
1715 } else {
1716 cliplist[clipn] = NULL;
1717 p = inclusion_buffer;
1718 for (i = 0; (! error) && (i < clipn); i++) {
1719 /* set current item to NULL so array will be null-terminated even if
1720 * malloc fails below. */
1721 cliplist[i] = NULL;
1722 if ((tmpstr = (char *)SMB_MALLOC(strlen(p)+1)) == NULL) {
1723 DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", i));
1724 error = 1;
1725 } else {
1726 unfixtarname(tmpstr, p, strlen(p) + 1, True);
1727 cliplist[i] = tmpstr;
1728 if ((p = strchr_m(p, '\000')) == NULL) {
1729 DEBUG(0,("INTERNAL ERROR: inclusion_buffer is of unexpected contents.\n"));
1730 abort();
1733 ++p;
1735 must_free_cliplist = True;
1739 SAFE_FREE(inclusion_buffer);
1740 if (error) {
1741 if (cliplist) {
1742 char **pp;
1743 /* We know cliplist is always null-terminated */
1744 for (pp = cliplist; *pp; ++pp) {
1745 SAFE_FREE(*pp);
1747 SAFE_FREE(cliplist);
1748 cliplist = NULL;
1749 must_free_cliplist = False;
1751 return 0;
1754 /* cliplist and its elements are freed at the end of process_tar. */
1755 return 1;
1758 /****************************************************************************
1759 Parse tar arguments. Sets tar_type, tar_excl, etc.
1760 ***************************************************************************/
1762 int tar_parseargs(int argc, char *argv[], const char *Optarg, int Optind)
1764 int newOptind = Optind;
1765 char tar_clipfl='\0';
1767 /* Reset back to defaults - could be from interactive version
1768 * reset mode and archive mode left as they are though
1770 tar_type='\0';
1771 tar_excl=True;
1772 dry_run=False;
1774 while (*Optarg) {
1775 switch(*Optarg++) {
1776 case 'c':
1777 tar_type='c';
1778 break;
1779 case 'x':
1780 if (tar_type=='c') {
1781 printf("Tar must be followed by only one of c or x.\n");
1782 return 0;
1784 tar_type='x';
1785 break;
1786 case 'b':
1787 if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) {
1788 DEBUG(0,("Option b must be followed by valid blocksize\n"));
1789 return 0;
1790 } else {
1791 Optind++;
1792 newOptind++;
1794 break;
1795 case 'g':
1796 tar_inc=True;
1797 break;
1798 case 'N':
1799 if (Optind>=argc) {
1800 DEBUG(0,("Option N must be followed by valid file name\n"));
1801 return 0;
1802 } else {
1803 SMB_STRUCT_STAT stbuf;
1805 if (sys_stat(argv[Optind], &stbuf,
1806 false) == 0) {
1807 newer_than = convert_timespec_to_time_t(
1808 stbuf.st_ex_mtime);
1809 DEBUG(1,("Getting files newer than %s",
1810 time_to_asc(newer_than)));
1811 newOptind++;
1812 Optind++;
1813 } else {
1814 DEBUG(0,("Error setting newer-than time\n"));
1815 return 0;
1818 break;
1819 case 'a':
1820 tar_reset=True;
1821 break;
1822 case 'q':
1823 tar_noisy=False;
1824 break;
1825 case 'I':
1826 if (tar_clipfl) {
1827 DEBUG(0,("Only one of I,X,F must be specified\n"));
1828 return 0;
1830 tar_clipfl='I';
1831 break;
1832 case 'X':
1833 if (tar_clipfl) {
1834 DEBUG(0,("Only one of I,X,F must be specified\n"));
1835 return 0;
1837 tar_clipfl='X';
1838 break;
1839 case 'F':
1840 if (tar_clipfl) {
1841 DEBUG(0,("Only one of I,X,F must be specified\n"));
1842 return 0;
1844 tar_clipfl='F';
1845 break;
1846 case 'r':
1847 DEBUG(0, ("tar_re_search set\n"));
1848 tar_re_search = True;
1849 break;
1850 case 'n':
1851 if (tar_type == 'c') {
1852 DEBUG(0, ("dry_run set\n"));
1853 dry_run = True;
1854 } else {
1855 DEBUG(0, ("n is only meaningful when creating a tar-file\n"));
1856 return 0;
1858 break;
1859 default:
1860 DEBUG(0,("Unknown tar option\n"));
1861 return 0;
1865 if (!tar_type) {
1866 printf("Option T must be followed by one of c or x.\n");
1867 return 0;
1870 /* tar_excl is true if cliplist lists files to be included.
1871 * Both 'I' and 'F' mean include. */
1872 tar_excl=tar_clipfl!='X';
1874 if (tar_clipfl=='F') {
1875 if (argc-Optind-1 != 1) {
1876 DEBUG(0,("Option F must be followed by exactly one filename.\n"));
1877 return 0;
1879 newOptind++;
1880 /* Optind points at the tar output file, Optind+1 at the inclusion file. */
1881 if (! read_inclusion_file(argv[Optind+1])) {
1882 return 0;
1884 } else if (Optind+1<argc && !tar_re_search) { /* For backwards compatibility */
1885 char *tmpstr;
1886 char **tmplist;
1887 int clipcount;
1889 cliplist=argv+Optind+1;
1890 clipn=argc-Optind-1;
1891 clipcount = clipn;
1893 if ((tmplist=SMB_MALLOC_ARRAY(char *,clipn)) == NULL) {
1894 DEBUG(0, ("Could not allocate space to process cliplist, count = %i\n", clipn));
1895 return 0;
1898 for (clipcount = 0; clipcount < clipn; clipcount++) {
1900 DEBUG(5, ("Processing an item, %s\n", cliplist[clipcount]));
1902 if ((tmpstr = (char *)SMB_MALLOC(strlen(cliplist[clipcount])+1)) == NULL) {
1903 DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", clipcount));
1904 SAFE_FREE(tmplist);
1905 return 0;
1908 unfixtarname(tmpstr, cliplist[clipcount], strlen(cliplist[clipcount]) + 1, True);
1909 tmplist[clipcount] = tmpstr;
1910 DEBUG(5, ("Processed an item, %s\n", tmpstr));
1912 DEBUG(5, ("Cliplist is: %s\n", cliplist[0]));
1915 cliplist = tmplist;
1916 must_free_cliplist = True;
1918 newOptind += clipn;
1921 if (Optind+1<argc && tar_re_search && tar_clipfl != 'F') {
1922 /* Doing regular expression seaches not from an inclusion file. */
1923 clipn=argc-Optind-1;
1924 cliplist=argv+Optind+1;
1925 newOptind += clipn;
1928 if (Optind>=argc || !strcmp(argv[Optind], "-")) {
1929 /* Sets tar handle to either 0 or 1, as appropriate */
1930 tarhandle=(tar_type=='c');
1932 * Make sure that dbf points to stderr if we are using stdout for
1933 * tar output
1935 if (tarhandle == 1) {
1936 dbf = x_stderr;
1938 if (!argv[Optind]) {
1939 DEBUG(0,("Must specify tar filename\n"));
1940 return 0;
1942 if (!strcmp(argv[Optind], "-")) {
1943 newOptind++;
1946 } else {
1947 if (tar_type=='c' && dry_run) {
1948 tarhandle=-1;
1949 } else if ((tar_type=='x' && (tarhandle = sys_open(argv[Optind], O_RDONLY, 0)) == -1)
1950 || (tar_type=='c' && (tarhandle=sys_creat(argv[Optind], 0644)) < 0)) {
1951 DEBUG(0,("Error opening local file %s - %s\n", argv[Optind], strerror(errno)));
1952 return(0);
1954 newOptind++;
1957 return newOptind;