2 Unix SMB/CIFS implementation.
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
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 {
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 allocate */
56 file_info2 *next, *prev; /* Used in the stack ... */
64 #define SEPARATORS " \t\n\r"
65 extern time_t newer_than;
66 extern struct cli_state *cli;
68 /* These defines are for the do_setrattr routine, to indicate
69 * setting and reseting of file attributes in the function call */
73 static uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
75 #ifndef CLIENT_TIMEOUT
76 #define CLIENT_TIMEOUT (30*1000)
79 static char *tarbuf, *buffer_p;
80 static int tp, ntarf, tbufsiz;
82 /* Incremental mode */
83 static bool tar_inc=False;
84 /* Reset archive bit */
85 static bool tar_reset=False;
86 /* Include / exclude mode (true=include, false=exclude) */
87 static bool tar_excl=True;
88 /* use regular expressions for search on file names */
89 static bool tar_re_search=False;
90 /* Do not dump anything, just calculate sizes */
91 static bool dry_run=False;
92 /* Dump files with System attribute */
93 static bool tar_system=True;
94 /* Dump files with Hidden attribute */
95 static bool tar_hidden=True;
96 /* Be noisy - make a catalogue */
97 static bool tar_noisy=True;
98 static bool tar_real_noisy=False; /* Don't want to be really noisy by default */
101 static char **cliplist=NULL;
103 static bool must_free_cliplist = False;
105 extern file_info def_finfo;
106 extern bool lowercase;
108 extern bool readbraw_supported;
110 extern pstring cur_dir;
111 extern int get_total_time_ms;
112 extern int get_total_size;
114 static int blocksize=20;
115 static int tarhandle;
117 static void writetarheader(int f, const char *aname, SMB_BIG_UINT size, time_t mtime,
118 const char *amode, unsigned char ftype);
119 static void do_atar(char *rname,char *lname,file_info *finfo1);
120 static void do_tar(file_info *finfo);
121 static void oct_it(SMB_BIG_UINT 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, 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)
146 tmp = (char *)SMB_MALLOC(size+1);
149 DEBUG(0, ("Out of memory in string_create_s\n"));
155 /****************************************************************************
156 Write a tar header to buffer
157 ****************************************************************************/
159 static void writetarheader(int f, const char *aname, SMB_BIG_UINT size, time_t mtime,
160 const char *amode, unsigned char ftype)
166 DEBUG(5, ("WriteTarHdr, Type = %c, Size= %.0f, Name = %s\n", ftype, (double)size, aname));
168 memset(hb.dummy, 0, sizeof(hb.dummy));
171 /* We will be prepending a '.' in fixtarheader so use +2 to
172 * take care of the . and terminating zero. JRA.
175 /* write a GNU tar style long header */
177 b = (char *)SMB_MALLOC(l+TBLOCK+100);
179 DEBUG(0,("out of memory\n"));
182 writetarheader(f, "/./@LongLink", l+2, 0, " 0 \0", 'L');
183 memset(b, 0, l+TBLOCK+100);
184 fixtarname(b, aname, l+2);
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));
191 fixtarname(hb.dbuf.name, aname, (l+2 >= NAMSIZ) ? NAMSIZ : l + 2);
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((SMB_BIG_UINT)0, 8, hb.dbuf.uid);
201 oct_it((SMB_BIG_UINT)0, 8, hb.dbuf.gid);
202 oct_it((SMB_BIG_UINT) size, 13, hb.dbuf.size);
204 if (size > (SMB_BIG_UINT)077777777777LL) {
206 if (size > (SMB_BIG_UINT)077777777777) {
209 /* This is a non-POSIX compatible extention to store files
212 memset(hb.dbuf.size, 0, 4);
214 for (i = 8, jp=(char*)&size; i; i--)
215 hb.dbuf.size[i+3] = *(jp++);
217 oct_it((SMB_BIG_UINT) mtime, 13, hb.dbuf.mtime);
218 memcpy(hb.dbuf.chksum, " ", sizeof(hb.dbuf.chksum));
219 memset(hb.dbuf.linkname, 0, NAMSIZ);
220 hb.dbuf.linkflag=ftype;
222 for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;)
225 oct_it((SMB_BIG_UINT) chk, 8, hb.dbuf.chksum);
226 hb.dbuf.chksum[6] = '\0';
228 (void) dotarbuf(f, hb.dummy, sizeof(hb.dummy));
231 /****************************************************************************
232 Read a tar header into a hblock structure, and validate
233 ***************************************************************************/
235 static long readtarheader(union hblock *hb, file_info2 *finfo, char *prefix)
242 * read in a "standard" tar format header - we're not that interested
243 * in that many fields, though
246 /* check the checksum */
247 for (chk=0, i=sizeof(hb->dummy), jp=hb->dummy; --i>=0;)
253 /* compensate for blanks in chksum header */
254 for (i=sizeof(hb->dbuf.chksum), jp=hb->dbuf.chksum; --i>=0;)
257 chk += ' ' * sizeof(hb->dbuf.chksum);
259 fchk=unoct(hb->dbuf.chksum, sizeof(hb->dbuf.chksum));
261 DEBUG(5, ("checksum totals chk=%ld fchk=%ld chksum=%s\n",
262 chk, fchk, hb->dbuf.chksum));
265 DEBUG(0, ("checksums don't match %ld %ld\n", fchk, chk));
266 dump_data(5, (uint8 *)hb - TBLOCK, TBLOCK *3);
270 if ((finfo->name = string_create_s(strlen(prefix) + strlen(hb -> dbuf.name) + 3)) == NULL) {
271 DEBUG(0, ("Out of space creating file_info2 for %s\n", hb -> dbuf.name));
275 safe_strcpy(finfo->name, prefix, strlen(prefix) + strlen(hb -> dbuf.name) + 3);
277 /* use l + 1 to do the null too; do prefix - prefcnt to zap leading slash */
278 unfixtarname(finfo->name + strlen(prefix), hb->dbuf.name,
279 strlen(hb->dbuf.name) + 1, True);
281 /* can't handle some links at present */
282 if ((hb->dbuf.linkflag != '0') && (hb -> dbuf.linkflag != '5')) {
283 if (hb->dbuf.linkflag == 0) {
284 DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n",
287 if (hb -> dbuf.linkflag == 'L') { /* We have a longlink */
288 /* Do nothing here at the moment. do_tarput will handle this
289 as long as the longlink gets back to it, as it has to advance
290 the buffer pointer, etc */
292 DEBUG(0, ("this tar file appears to contain some kind \
293 of link other than a GNUtar Longlink - ignoring\n"));
299 if ((unoct(hb->dbuf.mode, sizeof(hb->dbuf.mode)) & S_IFDIR) ||
300 (*(finfo->name+strlen(finfo->name)-1) == '\\')) {
303 finfo->mode=0; /* we don't care about mode at the moment, we'll
304 * just make it a regular file */
308 * Bug fix by richard@sj.co.uk
310 * REC: restore times correctly (as does tar)
311 * We only get the modification time of the file; set the creation time
312 * from the mod. time, and the access time to current time
314 finfo->mtime_ts = finfo->ctime_ts =
315 convert_time_t_to_timespec((time_t)strtol(hb->dbuf.mtime, NULL, 8));
316 finfo->atime_ts = convert_time_t_to_timespec(time(NULL));
317 finfo->size = unoct(hb->dbuf.size, sizeof(hb->dbuf.size));
322 /****************************************************************************
323 Write out the tar buffer to tape or wherever
324 ****************************************************************************/
326 static int dotarbuf(int f, char *b, int n)
333 /* This routine and the next one should be the only ones that do write()s */
334 if (tp + n >= tbufsiz) {
338 memcpy(tarbuf + tp, b, diff);
339 fail=fail && (1+write(f, tarbuf, tbufsiz));
344 while (n >= tbufsiz) {
345 fail=fail && (1 + write(f, b, tbufsiz));
352 memcpy(tarbuf+tp, b, n);
356 return(fail ? writ : 0);
359 /****************************************************************************
360 Write zeros to buffer / tape
361 ****************************************************************************/
363 static void dozerobuf(int f, int n)
365 /* short routine just to write out n zeros to buffer -
366 * used to round files to nearest block
367 * and to do tar EOFs */
372 if (n+tp >= tbufsiz) {
373 memset(tarbuf+tp, 0, tbufsiz-tp);
374 write(f, tarbuf, tbufsiz);
375 memset(tarbuf, 0, (tp+=n-tbufsiz));
377 memset(tarbuf+tp, 0, n);
382 /****************************************************************************
384 ****************************************************************************/
386 static void initarbuf(void)
388 /* initialize tar buffer */
389 tbufsiz=blocksize*TBLOCK;
390 tarbuf=(char *)SMB_MALLOC(tbufsiz); /* FIXME: We might not get the buffer */
392 /* reset tar buffer pointer and tar file counter and total dumped */
393 tp=0; ntarf=0; ttarf=0;
396 /****************************************************************************
397 Write two zero blocks at end of file
398 ****************************************************************************/
400 static void dotareof(int f)
402 SMB_STRUCT_STAT stbuf;
403 /* Two zero blocks at end of file, write out full buffer */
408 (void) dozerobuf(f, TBLOCK);
409 (void) dozerobuf(f, TBLOCK);
411 if (sys_fstat(f, &stbuf) == -1) {
412 DEBUG(0, ("Couldn't stat file handle\n"));
416 /* Could be a pipe, in which case S_ISREG should fail,
417 * and we should write out at full size */
419 write(f, tarbuf, S_ISREG(stbuf.st_mode) ? tp : tbufsiz);
422 /****************************************************************************
423 (Un)mangle DOS pathname, make nonabsolute
424 ****************************************************************************/
426 static void fixtarname(char *tptr, const char *fp, size_t l)
428 /* add a '.' to start of file name, convert from ugly dos \'s in path
429 * to lovely unix /'s :-} */
433 StrnCpy(tptr, fp, l-1);
434 string_replace(tptr, '\\', '/');
437 /****************************************************************************
438 Convert from decimal to octal string
439 ****************************************************************************/
441 static void oct_it (SMB_BIG_UINT value, int ndgs, char *p)
443 /* Converts long to octal string, pads with leading zeros */
445 /* skip final null, but do final space */
449 /* Loop does at least one digit */
451 p[--ndgs] = '0' + (char) (value & 7);
453 } while (ndgs > 0 && value != 0);
455 /* Do leading zeros */
460 /****************************************************************************
461 Convert from octal string to long
462 ***************************************************************************/
464 static long unoct(char *p, int ndgs)
467 /* Converts octal string to long, ignoring any non-digit */
470 if (isdigit((int)*p))
471 value = (value << 3) | (long) (*p - '0');
479 /****************************************************************************
480 Compare two strings in a slash insensitive way, allowing s1 to match s2
481 if s1 is an "initial" string (up to directory marker). Thus, if s2 is
482 a file in any subdirectory of s1, declare a match.
483 ***************************************************************************/
485 static int strslashcmp(char *s1, char *s2)
489 while(*s1 && *s2 && (*s1 == *s2 || tolower_ascii(*s1) == tolower_ascii(*s2) ||
490 (*s1 == '\\' && *s2=='/') || (*s1 == '/' && *s2=='\\'))) {
494 /* if s1 has a trailing slash, it compared equal, so s1 is an "initial"
497 if (!*s1 && s1 != s1_0 && (*(s1-1) == '/' || *(s1-1) == '\\'))
500 /* ignore trailing slash on s1 */
501 if (!*s2 && (*s1 == '/' || *s1 == '\\') && !*(s1+1))
504 /* check for s1 is an "initial" string of s2 */
505 if ((*s2 == '/' || *s2 == '\\') && !*s1)
511 /****************************************************************************
512 Ensure a remote path exists (make if necessary)
513 ***************************************************************************/
515 static bool ensurepath(char *fname)
517 /* *must* be called with buffer ready malloc'ed */
518 /* ensures path exists */
520 char *partpath, *ffname;
521 char *p=fname, *basehack;
523 DEBUG(5, ( "Ensurepath called with: %s\n", fname));
525 partpath = string_create_s(strlen(fname));
526 ffname = string_create_s(strlen(fname));
528 if ((partpath == NULL) || (ffname == NULL)){
529 DEBUG(0, ("Out of memory in ensurepath: %s\n", fname));
537 /* fname copied to ffname so can strtok */
539 safe_strcpy(ffname, fname, strlen(fname));
541 /* do a `basename' on ffname, so don't try and make file name directory */
542 if ((basehack=strrchr_m(ffname, '\\')) == NULL)
547 p=strtok(ffname, "\\");
550 safe_strcat(partpath, p, strlen(fname) + 1);
552 if (!cli_chkpath(cli, partpath)) {
553 if (!cli_mkdir(cli, partpath)) {
554 DEBUG(0, ("Error mkdirhiering\n"));
557 DEBUG(3, ("mkdirhiering %s\n", partpath));
561 safe_strcat(partpath, "\\", strlen(fname) + 1);
562 p = strtok(NULL,"/\\");
568 static int padit(char *buf, SMB_BIG_UINT bufsize, SMB_BIG_UINT padsize)
573 DEBUG(5, ("Padding with %0.f zeros\n", (double)padsize));
574 memset(buf, 0, (size_t)bufsize);
575 while( !berr && padsize > 0 ) {
576 bytestowrite= (int)MIN(bufsize, padsize);
577 berr = dotarbuf(tarhandle, buf, bytestowrite) != bytestowrite;
578 padsize -= bytestowrite;
584 static void do_setrattr(char *name, uint16 attr, int set)
588 if (!cli_getatr(cli, name, &oldattr, NULL, NULL)) return;
590 if (set == ATTRSET) {
593 attr = oldattr & ~attr;
596 if (!cli_setatr(cli, name, attr, 0)) {
597 DEBUG(1,("setatr failed: %s\n", cli_errstr(cli)));
601 /****************************************************************************
602 append one remote file to the tar file
603 ***************************************************************************/
605 static void do_atar(char *rname,char *lname,file_info *finfo1)
608 SMB_BIG_UINT nread=0;
611 bool close_done = False;
612 bool shallitime=True;
614 int read_size = 65520;
617 struct timeval tp_start;
619 GetTimeOfDay(&tp_start);
621 ftype = '0'; /* An ordinary file ... */
624 finfo.size = finfo1 -> size;
625 finfo.mode = finfo1 -> mode;
626 finfo.uid = finfo1 -> uid;
627 finfo.gid = finfo1 -> gid;
628 finfo.mtime_ts = finfo1 -> mtime_ts;
629 finfo.atime_ts = finfo1 -> atime_ts;
630 finfo.ctime_ts = finfo1 -> ctime_ts;
631 finfo.name = finfo1 -> name;
633 finfo.size = def_finfo.size;
634 finfo.mode = def_finfo.mode;
635 finfo.uid = def_finfo.uid;
636 finfo.gid = def_finfo.gid;
637 finfo.mtime_ts = def_finfo.mtime_ts;
638 finfo.atime_ts = def_finfo.atime_ts;
639 finfo.ctime_ts = def_finfo.ctime_ts;
640 finfo.name = def_finfo.name;
644 DEBUG(3,("skipping file %s of size %12.0f bytes\n", finfo.name,
645 (double)finfo.size));
647 ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
652 fnum = cli_open(cli, rname, O_RDONLY, DENY_NONE);
657 DEBUG(0,("%s opening remote file %s (%s)\n",
658 cli_errstr(cli),rname, cur_dir));
662 finfo.name = string_create_s(strlen(rname));
663 if (finfo.name == NULL) {
664 DEBUG(0, ("Unable to allocate space for finfo.name in do_atar\n"));
668 safe_strcpy(finfo.name,rname, strlen(rname));
671 if (!cli_getattrE(cli, fnum, &finfo.mode, &finfo.size, NULL, &atime, &mtime)) {
672 DEBUG(0, ("getattrE: %s\n", cli_errstr(cli)));
675 finfo.atime_ts = convert_time_t_to_timespec(atime);
676 finfo.mtime_ts = convert_time_t_to_timespec(mtime);
677 finfo.ctime_ts = finfo.mtime_ts;
680 DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode));
682 if (tar_inc && !(finfo.mode & aARCH)) {
683 DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
685 } else if (!tar_system && (finfo.mode & aSYSTEM)) {
686 DEBUG(4, ("skipping %s - system bit is set\n", finfo.name));
688 } else if (!tar_hidden && (finfo.mode & aHIDDEN)) {
689 DEBUG(4, ("skipping %s - hidden bit is set\n", finfo.name));
692 bool wrote_tar_header = False;
694 DEBUG(3,("getting file %s of size %.0f bytes as a tar file %s",
695 finfo.name, (double)finfo.size, lname));
697 while (nread < finfo.size && !close_done) {
699 DEBUG(3,("nread=%.0f\n",(double)nread));
701 datalen = cli_read(cli, fnum, data, nread, read_size);
704 DEBUG(0,("Error reading file %s : %s\n", rname, cli_errstr(cli)));
710 /* Only if the first read succeeds, write out the tar header. */
711 if (!wrote_tar_header) {
712 /* write a tar header, don't bother with mode - just set to 100644 */
713 writetarheader(tarhandle, rname, finfo.size,
714 finfo.mtime_ts.tv_sec, "100644 \0", ftype);
715 wrote_tar_header = True;
718 /* if file size has increased since we made file size query, truncate
719 read so tar header for this file will be correct.
722 if (nread > finfo.size) {
723 datalen -= nread - finfo.size;
724 DEBUG(0,("File size change - truncating %s to %.0f bytes\n",
725 finfo.name, (double)finfo.size));
728 /* add received bits of file to buffer - dotarbuf will
729 * write out in 512 byte intervals */
731 if (dotarbuf(tarhandle,data,datalen) != datalen) {
732 DEBUG(0,("Error writing to tar file - %s\n", strerror(errno)));
737 DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname));
744 if (wrote_tar_header) {
745 /* pad tar file with zero's if we couldn't get entire file */
746 if (nread < finfo.size) {
747 DEBUG(0, ("Didn't get entire file. size=%.0f, nread=%d\n",
748 (double)finfo.size, (int)nread));
749 if (padit(data, (SMB_BIG_UINT)sizeof(data), finfo.size - nread))
750 DEBUG(0,("Error writing tar file - %s\n", strerror(errno)));
753 /* round tar file to nearest block */
754 if (finfo.size % TBLOCK)
755 dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
757 ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
760 DEBUG(4, ("skipping %s - initial read failed (file was locked ?)\n", finfo.name));
765 cli_close(cli, fnum);
768 struct timeval tp_end;
771 /* if shallitime is true then we didn't skip */
772 if (tar_reset && !dry_run)
773 (void) do_setrattr(finfo.name, aARCH, ATTRRESET);
775 GetTimeOfDay(&tp_end);
776 this_time = (tp_end.tv_sec - tp_start.tv_sec)*1000 + (tp_end.tv_usec - tp_start.tv_usec)/1000;
777 get_total_time_ms += this_time;
778 get_total_size += finfo.size;
781 DEBUG(0, ("%12.0f (%7.1f kb/s) %s\n",
782 (double)finfo.size, finfo.size / MAX(0.001, (1.024*this_time)),
786 /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */
787 DEBUG(3,("(%g kb/s) (average %g kb/s)\n",
788 finfo.size / MAX(0.001, (1.024*this_time)),
789 get_total_size / MAX(0.001, (1.024*get_total_time_ms))));
793 /****************************************************************************
794 Append single file to tar file (or not)
795 ***************************************************************************/
797 static void do_tar(file_info *finfo)
801 if (strequal(finfo->name,"..") || strequal(finfo->name,"."))
804 /* Is it on the exclude list ? */
805 if (!tar_excl && clipn) {
808 DEBUG(5, ("Excl: strlen(cur_dir) = %d\n", (int)strlen(cur_dir)));
810 pstrcpy(exclaim, cur_dir);
811 *(exclaim+strlen(exclaim)-1)='\0';
813 pstrcat(exclaim, "\\");
814 pstrcat(exclaim, finfo->name);
816 DEBUG(5, ("...tar_re_search: %d\n", tar_re_search));
818 if ((!tar_re_search && clipfind(cliplist, clipn, exclaim)) ||
819 (tar_re_search && mask_match_list(exclaim, cliplist, clipn, True))) {
820 DEBUG(3,("Skipping file %s\n", exclaim));
825 if (finfo->mode & aDIR) {
826 pstring saved_curdir;
829 pstrcpy(saved_curdir, cur_dir);
831 DEBUG(5, ("Sizeof(cur_dir)=%d, strlen(cur_dir)=%d, \
832 strlen(finfo->name)=%d\nname=%s,cur_dir=%s\n",
833 (int)sizeof(cur_dir), (int)strlen(cur_dir),
834 (int)strlen(finfo->name), finfo->name, cur_dir));
836 pstrcat(cur_dir,finfo->name);
837 pstrcat(cur_dir,"\\");
839 DEBUG(5, ("Writing a dir, Name = %s\n", cur_dir));
841 /* write a tar directory, don't bother with mode - just set it to
843 writetarheader(tarhandle, cur_dir, 0, finfo->mtime_ts.tv_sec, "040755 \0", '5');
845 DEBUG(0,(" directory %s\n", cur_dir));
847 ntarf++; /* Make sure we have a file on there */
848 pstrcpy(mtar_mask,cur_dir);
849 pstrcat(mtar_mask,"*");
850 DEBUG(5, ("Doing list with mtar_mask: %s\n", mtar_mask));
851 do_list(mtar_mask, attribute, do_tar, False, True);
852 pstrcpy(cur_dir,saved_curdir);
854 pstrcpy(rname,cur_dir);
855 pstrcat(rname,finfo->name);
856 do_atar(rname,finfo->name,finfo);
860 /****************************************************************************
861 Convert from UNIX to DOS file names
862 ***************************************************************************/
864 static void unfixtarname(char *tptr, char *fp, int l, bool first)
866 /* remove '.' from start of file name, convert from unix /'s to
867 * dos \'s in path. Kill any absolute path names. But only if first!
870 DEBUG(5, ("firstb=%lX, secondb=%lX, len=%i\n", (long)tptr, (long)fp, l));
877 if (*fp == '\\' || *fp == '/') {
883 safe_strcpy(tptr, fp, l);
884 string_replace(tptr, '/', '\\');
887 /****************************************************************************
888 Move to the next block in the buffer, which may mean read in another set of
889 blocks. FIXME, we should allow more than one block to be skipped.
890 ****************************************************************************/
892 static int next_block(char *ltarbuf, char **bufferp, int bufsiz)
894 int bufread, total = 0;
896 DEBUG(5, ("Advancing to next block: %0lx\n", (unsigned long)*bufferp));
900 if (*bufferp >= (ltarbuf + bufsiz)) {
902 DEBUG(5, ("Reading more data into ltarbuf ...\n"));
905 * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>
906 * Fixes bug where read can return short if coming from
910 bufread = read(tarhandle, ltarbuf, bufsiz);
913 while (total < bufsiz) {
914 if (bufread < 0) { /* An error, return false */
915 return (total > 0 ? -2 : bufread);
923 bufread = read(tarhandle, <arbuf[total], bufsiz - total);
927 DEBUG(5, ("Total bytes read ... %i\n", total));
935 /* Skip a file, even if it includes a long file name? */
936 static int skip_file(int skipsize)
938 int dsize = skipsize;
940 DEBUG(5, ("Skiping file. Size = %i\n", skipsize));
942 /* FIXME, we should skip more than one block at a time */
945 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
946 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
955 /*************************************************************
956 Get a file from the tar file and store it.
957 When this is called, tarbuf already contains the first
958 file block. This is a bit broken & needs fixing.
959 **************************************************************/
961 static int get_file(file_info2 finfo)
963 int fnum = -1, pos = 0, dsize = 0, bpos = 0;
964 SMB_BIG_UINT rsize = 0;
966 DEBUG(5, ("get_file: file: %s, size %.0f\n", finfo.name, (double)finfo.size));
968 if (ensurepath(finfo.name) &&
969 (fnum=cli_open(cli, finfo.name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) == -1) {
970 DEBUG(0, ("abandoning restore\n"));
974 /* read the blocks from the tar file and write to the remote file */
976 rsize = finfo.size; /* This is how much to write */
980 /* We can only write up to the end of the buffer */
981 dsize = MIN(tbufsiz - (buffer_p - tarbuf) - bpos, 65520); /* Calculate the size to write */
982 dsize = MIN(dsize, rsize); /* Should be only what is left */
983 DEBUG(5, ("writing %i bytes, bpos = %i ...\n", dsize, bpos));
985 if (cli_write(cli, fnum, 0, buffer_p + bpos, pos, dsize) != dsize) {
986 DEBUG(0, ("Error writing remote file\n"));
993 /* Now figure out how much to move in the buffer */
995 /* FIXME, we should skip more than one block at a time */
997 /* First, skip any initial part of the part written that is left over */
998 /* from the end of the first TBLOCK */
1000 if ((bpos) && ((bpos + dsize) >= TBLOCK)) {
1001 dsize -= (TBLOCK - bpos); /* Get rid of the end of the first block */
1004 if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) { /* and skip the block */
1005 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1011 * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>.
1012 * If the file being extracted is an exact multiple of
1013 * TBLOCK bytes then we don't want to extract the next
1014 * block from the tarfile here, as it will be done in
1015 * the caller of get_file().
1018 while (((rsize != 0) && (dsize >= TBLOCK)) ||
1019 ((rsize == 0) && (dsize > TBLOCK))) {
1021 if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
1022 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1031 /* Now close the file ... */
1033 if (!cli_close(cli, fnum)) {
1034 DEBUG(0, ("Error closing remote file\n"));
1038 /* Now we update the creation date ... */
1039 DEBUG(5, ("Updating creation date on %s\n", finfo.name));
1041 if (!cli_setatr(cli, finfo.name, finfo.mode, finfo.mtime_ts.tv_sec)) {
1042 if (tar_real_noisy) {
1043 DEBUG(0, ("Could not set time on file: %s\n", finfo.name));
1044 /*return(False); */ /* Ignore, as Win95 does not allow changes */
1049 DEBUG(0, ("restore tar file %s of size %.0f bytes\n", finfo.name, (double)finfo.size));
1053 /* Create a directory. We just ensure that the path exists and return as there
1054 is no file associated with a directory
1056 static int get_dir(file_info2 finfo)
1058 DEBUG(0, ("restore directory %s\n", finfo.name));
1060 if (!ensurepath(finfo.name)) {
1061 DEBUG(0, ("Problems creating directory\n"));
1068 /* Get a file with a long file name ... first file has file name, next file
1069 has the data. We only want the long file name, as the loop in do_tarput
1070 will deal with the rest.
1072 static char *get_longfilename(file_info2 finfo)
1074 /* finfo.size here is the length of the filename as written by the "/./@LongLink" name
1076 int namesize = finfo.size + strlen(cur_dir) + 2;
1077 char *longname = (char *)SMB_MALLOC(namesize);
1078 int offset = 0, left = finfo.size;
1081 DEBUG(5, ("Restoring a long file name: %s\n", finfo.name));
1082 DEBUG(5, ("Len = %.0f\n", (double)finfo.size));
1084 if (longname == NULL) {
1085 DEBUG(0, ("could not allocate buffer of size %d for longname\n", namesize));
1089 /* First, add cur_dir to the long file name */
1091 if (strlen(cur_dir) > 0) {
1092 strncpy(longname, cur_dir, namesize);
1093 offset = strlen(cur_dir);
1096 /* Loop through the blocks picking up the name */
1099 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
1100 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1101 SAFE_FREE(longname);
1105 unfixtarname(longname + offset, buffer_p, MIN(TBLOCK, finfo.size), first--);
1106 DEBUG(5, ("UnfixedName: %s, buffer: %s\n", longname, buffer_p));
1115 static void do_tarput(void)
1118 struct timeval tp_start;
1119 char *longfilename = NULL, linkflag;
1124 GetTimeOfDay(&tp_start);
1125 DEBUG(5, ("RJS do_tarput called ...\n"));
1127 buffer_p = tarbuf + tbufsiz; /* init this to force first read */
1129 /* Now read through those files ... */
1131 /* Get us to the next block, or the first block first time around */
1132 if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
1133 DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1134 SAFE_FREE(longfilename);
1138 DEBUG(5, ("Reading the next header ...\n"));
1140 switch (readtarheader((union hblock *) buffer_p, &finfo, cur_dir)) {
1141 case -2: /* Hmm, not good, but not fatal */
1142 DEBUG(0, ("Skipping %s...\n", finfo.name));
1143 if ((next_block(tarbuf, &buffer_p, tbufsiz) <= 0) && !skip_file(finfo.size)) {
1144 DEBUG(0, ("Short file, bailing out...\n"));
1150 DEBUG(0, ("abandoning restore, -1 from read tar header\n"));
1153 case 0: /* chksum is zero - looks like an EOF */
1154 DEBUG(0, ("tar: restored %d files and directories\n", ntarf));
1155 return; /* Hmmm, bad here ... */
1162 /* Now, do we have a long file name? */
1163 if (longfilename != NULL) {
1164 SAFE_FREE(finfo.name); /* Free the space already allocated */
1165 finfo.name = longfilename;
1166 longfilename = NULL;
1169 /* Well, now we have a header, process the file ... */
1170 /* Should we skip the file? We have the long name as well here */
1171 skip = clipn && ((!tar_re_search && clipfind(cliplist, clipn, finfo.name) ^ tar_excl) ||
1172 (tar_re_search && mask_match_list(finfo.name, cliplist, clipn, True)));
1174 DEBUG(5, ("Skip = %i, cliplist=%s, file=%s\n", skip, (cliplist?cliplist[0]:NULL), finfo.name));
1176 skip_file(finfo.size);
1180 /* We only get this far if we should process the file */
1181 linkflag = ((union hblock *)buffer_p) -> dbuf.linkflag;
1183 case '0': /* Should use symbolic names--FIXME */
1185 * Skip to the next block first, so we can get the file, FIXME, should
1186 * be in get_file ...
1187 * The 'finfo.size != 0' fix is from Bob Boehmer <boehmer@worldnet.att.net>
1188 * Fixes bug where file size in tarfile is zero.
1190 if ((finfo.size != 0) && next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
1191 DEBUG(0, ("Short file, bailing out...\n"));
1194 if (!get_file(finfo)) {
1195 DEBUG(0, ("Abandoning restore\n"));
1200 if (!get_dir(finfo)) {
1201 DEBUG(0, ("Abandoning restore \n"));
1206 SAFE_FREE(longfilename);
1207 longfilename = get_longfilename(finfo);
1208 if (!longfilename) {
1209 DEBUG(0, ("abandoning restore\n"));
1212 DEBUG(5, ("Long file name: %s\n", longfilename));
1216 skip_file(finfo.size); /* Don't handle these yet */
1223 * samba interactive commands
1226 /****************************************************************************
1228 ***************************************************************************/
1235 if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1236 DEBUG(0, ("blocksize <n>\n"));
1241 if (block < 0 || block > 65535) {
1242 DEBUG(0, ("blocksize out of range"));
1247 DEBUG(2,("blocksize is now %d\n", blocksize));
1252 /****************************************************************************
1253 command to set incremental / reset mode
1254 ***************************************************************************/
1256 int cmd_tarmode(void)
1260 while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1261 if (strequal(buf, "full"))
1263 else if (strequal(buf, "inc"))
1265 else if (strequal(buf, "reset"))
1267 else if (strequal(buf, "noreset"))
1269 else if (strequal(buf, "system"))
1271 else if (strequal(buf, "nosystem"))
1273 else if (strequal(buf, "hidden"))
1275 else if (strequal(buf, "nohidden"))
1277 else if (strequal(buf, "verbose") || strequal(buf, "noquiet"))
1279 else if (strequal(buf, "quiet") || strequal(buf, "noverbose"))
1282 DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
1285 DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n",
1286 tar_inc ? "incremental" : "full",
1287 tar_system ? "system" : "nosystem",
1288 tar_hidden ? "hidden" : "nohidden",
1289 tar_reset ? "reset" : "noreset",
1290 tar_noisy ? "verbose" : "quiet"));
1294 /****************************************************************************
1295 Feeble attrib command
1296 ***************************************************************************/
1298 int cmd_setmode(void)
1306 attra[0] = attra[1] = 0;
1308 if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1309 DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1313 pstrcpy(fname, cur_dir);
1314 pstrcat(fname, buf);
1316 while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1328 attra[direct]|=aRONLY;
1331 attra[direct]|=aHIDDEN;
1334 attra[direct]|=aSYSTEM;
1337 attra[direct]|=aARCH;
1340 DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
1346 if (attra[ATTRSET]==0 && attra[ATTRRESET]==0) {
1347 DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1351 DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
1352 do_setrattr(fname, attra[ATTRSET], ATTRSET);
1353 do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
1357 /****************************************************************************
1358 Principal command for creating / extracting
1359 ***************************************************************************/
1368 if (!next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1369 DEBUG(0,("tar <c|x>[IXbgan] <filename>\n"));
1373 argl=toktocliplist(&argcl, NULL);
1374 if (!tar_parseargs(argcl, argl, buf, 0))
1377 ret = process_tar();
1382 /****************************************************************************
1383 Command line (option) version
1384 ***************************************************************************/
1386 int process_tar(void)
1403 if (clipn && tar_excl) {
1407 for (i=0; i<clipn; i++) {
1408 DEBUG(5,("arg %d = %s\n", i, cliplist[i]));
1410 if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
1411 *(cliplist[i]+strlen(cliplist[i])-1)='\0';
1414 if (strrchr_m(cliplist[i], '\\')) {
1417 pstrcpy(saved_dir, cur_dir);
1419 if (*cliplist[i]=='\\') {
1420 pstrcpy(tarmac, cliplist[i]);
1422 pstrcpy(tarmac, cur_dir);
1423 pstrcat(tarmac, cliplist[i]);
1425 pstrcpy(cur_dir, tarmac);
1426 *(strrchr_m(cur_dir, '\\')+1)='\0';
1428 DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1429 do_list(tarmac,attribute,do_tar, False, True);
1430 pstrcpy(cur_dir,saved_dir);
1432 pstrcpy(tarmac, cur_dir);
1433 pstrcat(tarmac, cliplist[i]);
1434 DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1435 do_list(tarmac,attribute,do_tar, False, True);
1440 pstrcpy(mask,cur_dir);
1441 DEBUG(5, ("process_tar, do_list with mask: %s\n", mask));
1442 pstrcat(mask,"\\*");
1443 do_list(mask,attribute,do_tar,False, True);
1447 dotareof(tarhandle);
1451 DEBUG(0, ("tar: dumped %d files and directories\n", ntarf));
1452 DEBUG(0, ("Total bytes written: %.0f\n", (double)ttarf));
1456 if (must_free_cliplist) {
1458 for (i = 0; i < clipn; ++i) {
1459 SAFE_FREE(cliplist[i]);
1461 SAFE_FREE(cliplist);
1464 must_free_cliplist = False;
1469 /****************************************************************************
1470 Find a token (filename) in a clip list
1471 ***************************************************************************/
1473 static int clipfind(char **aret, int ret, char *tok)
1478 /* ignore leading slashes or dots in token */
1479 while(strchr_m("/\\.", *tok))
1485 /* ignore leading slashes or dots in list */
1486 while(strchr_m("/\\.", *pkey))
1489 if (!strslashcmp(pkey, tok))
1495 /****************************************************************************
1496 Read list of files to include from the file and initialize cliplist
1498 ***************************************************************************/
1500 static int read_inclusion_file(char *filename)
1502 XFILE *inclusion = NULL;
1503 char buf[PATH_MAX + 1];
1504 char *inclusion_buffer = NULL;
1505 int inclusion_buffer_size = 0;
1506 int inclusion_buffer_sofar = 0;
1513 buf[PATH_MAX] = '\0'; /* guarantee null-termination */
1514 if ((inclusion = x_fopen(filename, O_RDONLY, 0)) == NULL) {
1515 /* XXX It would be better to include a reason for failure, but without
1516 * autoconf, it's hard to use strerror, sys_errlist, etc.
1518 DEBUG(0,("Unable to open inclusion file %s\n", filename));
1522 while ((! error) && (x_fgets(buf, sizeof(buf)-1, inclusion))) {
1523 if (inclusion_buffer == NULL) {
1524 inclusion_buffer_size = 1024;
1525 if ((inclusion_buffer = (char *)SMB_MALLOC(inclusion_buffer_size)) == NULL) {
1526 DEBUG(0,("failure allocating buffer to read inclusion file\n"));
1532 if (buf[strlen(buf)-1] == '\n') {
1533 buf[strlen(buf)-1] = '\0';
1536 if ((strlen(buf) + 1 + inclusion_buffer_sofar) >= inclusion_buffer_size) {
1537 inclusion_buffer_size *= 2;
1538 inclusion_buffer = (char *)SMB_REALLOC(inclusion_buffer,inclusion_buffer_size);
1539 if (!inclusion_buffer) {
1540 DEBUG(0,("failure enlarging inclusion buffer to %d bytes\n",
1541 inclusion_buffer_size));
1547 safe_strcpy(inclusion_buffer + inclusion_buffer_sofar, buf, inclusion_buffer_size - inclusion_buffer_sofar);
1548 inclusion_buffer_sofar += strlen(buf) + 1;
1551 x_fclose(inclusion);
1554 /* Allocate an array of clipn + 1 char*'s for cliplist */
1555 cliplist = SMB_MALLOC_ARRAY(char *, clipn + 1);
1556 if (cliplist == NULL) {
1557 DEBUG(0,("failure allocating memory for cliplist\n"));
1560 cliplist[clipn] = NULL;
1561 p = inclusion_buffer;
1562 for (i = 0; (! error) && (i < clipn); i++) {
1563 /* set current item to NULL so array will be null-terminated even if
1564 * malloc fails below. */
1566 if ((tmpstr = (char *)SMB_MALLOC(strlen(p)+1)) == NULL) {
1567 DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", i));
1570 unfixtarname(tmpstr, p, strlen(p) + 1, True);
1571 cliplist[i] = tmpstr;
1572 if ((p = strchr_m(p, '\000')) == NULL) {
1573 DEBUG(0,("INTERNAL ERROR: inclusion_buffer is of unexpected contents.\n"));
1579 must_free_cliplist = True;
1583 SAFE_FREE(inclusion_buffer);
1587 /* We know cliplist is always null-terminated */
1588 for (pp = cliplist; *pp; ++pp) {
1591 SAFE_FREE(cliplist);
1593 must_free_cliplist = False;
1598 /* cliplist and its elements are freed at the end of process_tar. */
1602 /****************************************************************************
1603 Parse tar arguments. Sets tar_type, tar_excl, etc.
1604 ***************************************************************************/
1606 int tar_parseargs(int argc, char *argv[], const char *Optarg, int Optind)
1608 int newOptind = Optind;
1609 char tar_clipfl='\0';
1611 /* Reset back to defaults - could be from interactive version
1612 * reset mode and archive mode left as they are though
1624 if (tar_type=='c') {
1625 printf("Tar must be followed by only one of c or x.\n");
1631 if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) {
1632 DEBUG(0,("Option b must be followed by valid blocksize\n"));
1644 DEBUG(0,("Option N must be followed by valid file name\n"));
1647 SMB_STRUCT_STAT stbuf;
1649 if (sys_stat(argv[Optind], &stbuf) == 0) {
1650 newer_than = stbuf.st_mtime;
1651 DEBUG(1,("Getting files newer than %s",
1652 time_to_asc(newer_than)));
1656 DEBUG(0,("Error setting newer-than time\n"));
1669 DEBUG(0,("Only one of I,X,F must be specified\n"));
1676 DEBUG(0,("Only one of I,X,F must be specified\n"));
1683 DEBUG(0,("Only one of I,X,F must be specified\n"));
1689 DEBUG(0, ("tar_re_search set\n"));
1690 tar_re_search = True;
1693 if (tar_type == 'c') {
1694 DEBUG(0, ("dry_run set\n"));
1697 DEBUG(0, ("n is only meaningful when creating a tar-file\n"));
1702 DEBUG(0,("Unknown tar option\n"));
1708 printf("Option T must be followed by one of c or x.\n");
1712 /* tar_excl is true if cliplist lists files to be included.
1713 * Both 'I' and 'F' mean include. */
1714 tar_excl=tar_clipfl!='X';
1716 if (tar_clipfl=='F') {
1717 if (argc-Optind-1 != 1) {
1718 DEBUG(0,("Option F must be followed by exactly one filename.\n"));
1722 /* Optind points at the tar output file, Optind+1 at the inclusion file. */
1723 if (! read_inclusion_file(argv[Optind+1])) {
1726 } else if (Optind+1<argc && !tar_re_search) { /* For backwards compatibility */
1731 cliplist=argv+Optind+1;
1732 clipn=argc-Optind-1;
1735 if ((tmplist=SMB_MALLOC_ARRAY(char *,clipn)) == NULL) {
1736 DEBUG(0, ("Could not allocate space to process cliplist, count = %i\n", clipn));
1740 for (clipcount = 0; clipcount < clipn; clipcount++) {
1742 DEBUG(5, ("Processing an item, %s\n", cliplist[clipcount]));
1744 if ((tmpstr = (char *)SMB_MALLOC(strlen(cliplist[clipcount])+1)) == NULL) {
1745 DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", clipcount));
1750 unfixtarname(tmpstr, cliplist[clipcount], strlen(cliplist[clipcount]) + 1, True);
1751 tmplist[clipcount] = tmpstr;
1752 DEBUG(5, ("Processed an item, %s\n", tmpstr));
1754 DEBUG(5, ("Cliplist is: %s\n", cliplist[0]));
1758 must_free_cliplist = True;
1763 if (Optind+1<argc && tar_re_search && tar_clipfl != 'F') {
1764 /* Doing regular expression seaches not from an inclusion file. */
1765 clipn=argc-Optind-1;
1766 cliplist=argv+Optind+1;
1770 if (Optind>=argc || !strcmp(argv[Optind], "-")) {
1771 /* Sets tar handle to either 0 or 1, as appropriate */
1772 tarhandle=(tar_type=='c');
1774 * Make sure that dbf points to stderr if we are using stdout for
1777 if (tarhandle == 1) {
1780 if (!argv[Optind]) {
1781 DEBUG(0,("Must specify tar filename\n"));
1784 if (!strcmp(argv[Optind], "-")) {
1789 if (tar_type=='c' && dry_run) {
1791 } else if ((tar_type=='x' && (tarhandle = sys_open(argv[Optind], O_RDONLY, 0)) == -1)
1792 || (tar_type=='c' && (tarhandle=sys_creat(argv[Optind], 0644)) < 0)) {
1793 DEBUG(0,("Error opening local file %s - %s\n", argv[Optind], strerror(errno)));