Rolling clitar.c back to the previous rev 1.22 as the current one
[samba.git] / source / client / clitar.c
index 2de09c66c114983287752f59821c4f2f8013b204..32731200b0d2a6f2e64e9ff14e04c4eb5dcefe92 100644 (file)
@@ -2,7 +2,7 @@
    Unix SMB/Netbios implementation.
    Version 1.9.
    Tar Extensions
-   Copyright (C) Ricky Poulten 1995
+   Copyright (C) Ricky Poulten 1995-1998
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
+/* The following changes developed by Richard Sharpe for Canon Information
+   Systems Research Australia (CISRA) are Copyright (C) 1998 by CISRA and are 
+   made available under the terms of the GPL as listed above:
+
+   1. Restore can now restore files with long file names
+   2. Save now saves directory information so that we can restore 
+      directory creation times
+   3. tar now accepts both UNIX path names and DOS path names. I prefer
+      those lovely /'s to those UGLY \'s :-)
+
+*/
 
 
 #include "includes.h"
 #include "clitar.h"
+#include <regex.h>
 
 extern BOOL recurse;
 
@@ -41,13 +53,20 @@ static int attribute = aDIR | aSYSTEM | aHIDDEN;
 #endif
 
 static char *tarbuf;
-static int tp, ntarf, tbufsiz;
+static int tp, ntarf, tbufsiz, ttarf;
 /* Incremental mode */
 BOOL tar_inc=False;
 /* Reset archive bit */
 BOOL tar_reset=False;
 /* Include / exclude mode (true=include, false=exclude) */
 BOOL tar_excl=True;
+/* Dump files with System attribute */
+BOOL tar_system=False;
+/* Dump files with Hidden attribute */
+BOOL tar_hidden=True;
+/* Be noisy - make a catalogue */
+BOOL tar_noisy=True;
+
 char tar_type='\0';
 static char **cliplist=NULL;
 static int clipn=0;
@@ -65,25 +84,15 @@ extern int Protocol;
 int blocksize=20;
 int tarhandle;
 
-static void writetarheader();
-static void do_atar();
-static void do_tar();
-static void oct_it();
-static void fixtarname();
-static int dotarbuf();
-static void dozerobuf();
-static void dotareof();
-static void initarbuf();
-static int do_setrattr();
-void cmd_tar();
-int process_tar();
-char **toktocliplist();
-int clipfind();
-/* restore functions */
-static long readtarheader();
-static long unoct();
-static void do_tarput();
-static void unfixtarname();
+static void writetarheader(int f,  char *aname, int size, time_t mtime,
+                          char *amode, unsigned char ftype);
+
+/* Forward references. */
+static void fixtarname(char *tptr, char *fp, int l);
+static int dotarbuf(int f, char *b, int n);
+static void oct_it (long value, int ndgs, char *p);
+static long unoct(char *p, int ndgs);
+static void unfixtarname(char *tptr, char *fp, int l);
 
 /*
  * tar specific utitlities
@@ -93,19 +102,32 @@ static void unfixtarname();
 Write a tar header to buffer
 ****************************************************************************/
 static void writetarheader(int f,  char *aname, int size, time_t mtime,
-                   char *amode)
+                          char *amode, unsigned char ftype)
 {
   union hblock hb;
   int i, chk, l;
   char *jp;
 
+  DEBUG(5, ("WriteTarHdr, Type = %c, Name = %s\n", ftype, aname));
+
   memset(hb.dummy, 0, sizeof(hb.dummy));
   
   l=strlen(aname);
-  if (l >= NAMSIZ)
-    {
-      DEBUG(0, ("tar file %s name length exceeds NAMSIZ\n", aname));
-    }
+  if (l >= NAMSIZ) {
+         /* write a GNU tar style long header */
+         char *b;
+         b = (char *)malloc(l+TBLOCK+100);
+         if (!b) {
+                 DEBUG(0,("out of memory\n"));
+                 exit(1);
+         }
+         writetarheader(f, "/./@LongLink", l+1, 0, "     0 \0", 'L');
+         memset(b, 0, l+TBLOCK+100);
+         fixtarname(b, aname, l+1);
+         i = strlen(b)+1;
+         dotarbuf(f, b, TBLOCK*(((i-1)/TBLOCK)+1));
+         free(b);
+  }
 
   /* use l + 1 to do the null too */
   fixtarname(hb.dbuf.name, aname, (l >= NAMSIZ) ? NAMSIZ : l + 1);
@@ -122,8 +144,8 @@ static void writetarheader(int f,  char *aname, int size, time_t mtime,
   oct_it((long) size, 13, hb.dbuf.size);
   oct_it((long) mtime, 13, hb.dbuf.mtime);
   memcpy(hb.dbuf.chksum, "        ", sizeof(hb.dbuf.chksum));
-  hb.dbuf.linkflag='0';
   memset(hb.dbuf.linkname, 0, NAMSIZ);
+  hb.dbuf.linkflag=ftype;
   
   for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;) chk+=(0xFF & *jp++);
 
@@ -176,14 +198,21 @@ static long readtarheader(union hblock *hb, file_info *finfo, char *prefix)
   unfixtarname(finfo->name + strlen(prefix), hb->dbuf.name,
               strlen(hb->dbuf.name) + 1);
 
-/* can't handle links at present */
-  if (hb->dbuf.linkflag != '0') {
+/* can't handle some links at present */
+  if ((hb->dbuf.linkflag != '0') && (hb -> dbuf.linkflag != '5')) {
     if (hb->dbuf.linkflag == 0) {
       DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n",
                finfo->name));
     } else { 
-      DEBUG(0, ("this tar file appears to contain some kind of link - ignoring\n"));
-      return -2;
+      if (hb -> dbuf.linkflag == 'L') { /* We have a longlink */
+         /* Do nothing here at the moment. do_tarput will handle this
+            as long as the longlink gets back to it, as it has to advance 
+            the buffer pointer, etc */
+
+      } else {
+        DEBUG(0, ("this tar file appears to contain some kind of link other than a GNUtar Longlink - ignoring\n"));
+        return -2;
+      }
     }
   }
     
@@ -244,7 +273,7 @@ static int dotarbuf(int f, char *b, int n)
 }
 
 /****************************************************************************
-Write zeros to buffer / tape
+Write zeros to buffer / tape
 ****************************************************************************/
 static void dozerobuf(int f, int n)
 {
@@ -255,6 +284,7 @@ static void dozerobuf(int f, int n)
   if (n+tp >= tbufsiz)
     {
       memset(tarbuf+tp, 0, tbufsiz-tp);
+
       write(f, tarbuf, tbufsiz);
       memset(tarbuf, 0, (tp+=n-tbufsiz));
     }
@@ -268,14 +298,14 @@ static void dozerobuf(int f, int n)
 /****************************************************************************
 Malloc tape buffer
 ****************************************************************************/
-static void initarbuf()
+static void initarbuf(void)
 {
   /* initialize tar buffer */
   tbufsiz=blocksize*TBLOCK;
-  tarbuf=malloc(tbufsiz);
+  tarbuf=malloc(tbufsiz);      /* FIXME: We might not get the buffer */
 
-  /* reset tar buffer pointer and tar file counter */
-  tp=0; ntarf=0;
+  /* reset tar buffer pointer and tar file counter and total dumped */
+  tp=0; ntarf=0; ttarf=0;
 }
 
 /****************************************************************************
@@ -309,15 +339,18 @@ static void fixtarname(char *tptr, char *fp, int l)
    * to lovely unix /'s :-} */
 
   *tptr++='.';
-#ifdef KANJI
+
   while (l > 0) {
-    if (is_shift_jis (*fp)) {
-      *tptr++ = *fp++;
-      *tptr++ = *fp++;
-      l -= 2;
-    } else if (is_kana (*fp)) {
-      *tptr++ = *fp++;
-      l--;
+    int skip;
+    if((skip = skip_multibyte_char( *fp)) != 0) {
+      if (skip == 2) {
+        *tptr++ = *fp++;
+        *tptr++ = *fp++;
+        l -= 2;
+      } else if (skip == 1) {
+        *tptr++ = *fp++;
+        l--;
+      }
     } else if (*fp == '\\') {
       *tptr++ = '/';
       fp++;
@@ -327,15 +360,12 @@ static void fixtarname(char *tptr, char *fp, int l)
       l--;
     }
   }
-#else
-  while (l--) { *tptr=(*fp == '\\') ? '/' : *fp; tptr++; fp++; }
-#endif
 }
 
 /****************************************************************************
 Convert from decimal to octal string
 ****************************************************************************/
-static void oct_it (register long value, register int ndgs, register char *p)
+static void oct_it (long value, int ndgs, char *p)
 {
   /* Converts long to octal string, pads with leading zeros */
 
@@ -375,10 +405,14 @@ static long unoct(char *p, int ndgs)
 }
 
 /****************************************************************************
-Compare two strings in a slash insensitive way
+Compare two strings in a slash insensitive way, allowing s1 to match s2 
+if s1 is an "initial" string (up to directory marker).  Thus, if s2 is 
+a file in any subdirectory of s1, declare a match.
 ***************************************************************************/
-int strslashcmp(const char *s1, const char *s2)
+static int strslashcmp(char *s1, char *s2)
 {
+  char *s1_0=s1;
+
   while(*s1 && *s2 &&
        (*s1 == *s2
         || tolower(*s1) == tolower(*s2)
@@ -387,12 +421,87 @@ int strslashcmp(const char *s1, const char *s2)
          s1++; s2++;
   }
 
+  /* if s1 has a trailing slash, it compared equal, so s1 is an "initial" 
+     string of s2.
+   */
+  if (!*s1 && s1 != s1_0 && (*(s1-1) == '/' || *(s1-1) == '\\')) return 0;
+
+  /* ignore trailing slash on s1 */
+  if (!*s2 && (*s1 == '/' || *s1 == '\\') && !*(s1+1)) return 0;
+
+  /* check for s1 is an "initial" string of s2 */
+  if (*s2 == '/' || *s2 == '\\') return 0;
+
   return *s1-*s2;
 }
 
 /*
  * general smb utility functions
  */
+/**********************************************************************
+do_setrtime, set time on a file or dir ...
+**********************************************************************/
+
+static int do_setrtime(char *fname, int mtime)
+{
+  char *inbuf, *outbuf, *p;
+  char *name;
+
+  name = (char *)malloc(strlen(fname) + 1 + 1);
+  if (name == NULL) {
+
+     DEBUG(0, ("Failed to allocate space while setting time on file: %s", fname));
+     return False;
+
+  }
+
+  strcpy(name, fname);
+  strcpy(fname, "\\");
+  strcat(fname, name);
+
+  inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+  outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN);
+
+  if (!inbuf || !outbuf) {
+
+    DEBUG(0, ("Could not allocate memory for inbuf or outbuf while changing time on: %s\n", fname));
+    return False;
+
+  }
+
+  memset(outbuf, 0, smb_size);
+  set_message(outbuf, 8, 4 + strlen(fname), True);
+  CVAL(outbuf, smb_com) = SMBsetatr;
+  SSVAL(outbuf, smb_tid, cnum);
+  cli_setup_pkt(outbuf);
+
+  SSVAL(outbuf, smb_vwv0, 0);
+  put_dos_date3(outbuf, smb_vwv1, mtime);
+
+  p = smb_buf(outbuf);
+  *p++ = 4;
+  strcpy(p, fname);
+  p+= (strlen(fname)+1);
+
+  *p++ = 4;
+  *p++ = 0;
+
+  send_smb(Client, outbuf);
+  client_receive_smb(Client, inbuf, CLIENT_TIMEOUT);
+
+  if (CVAL(inbuf,smb_rcls) != 0)
+    {
+      DEBUG(0,("%s setting attributes on file %s\n",
+           smb_errstr(inbuf), fname));
+      free(inbuf);free(outbuf);
+      return(False);
+    }
+
+  free(inbuf);free(outbuf);
+  return(True);
+
+}
+
 /****************************************************************************
 Set DOS file attributes
 ***************************************************************************/
@@ -425,7 +534,7 @@ static int do_setrattr(char *fname, int attr, int setit)
   set_message(outbuf,0,2 + strlen(fname),True);
   CVAL(outbuf,smb_com) = SMBgetatr;
   SSVAL(outbuf,smb_tid,cnum);
-  setup_pkt(outbuf);
+  cli_setup_pkt(outbuf);
 
   p = smb_buf(outbuf);
   *p++ = 4;
@@ -436,7 +545,7 @@ static int do_setrattr(char *fname, int attr, int setit)
   *p++ = 0;
 
   send_smb(Client,outbuf);
-  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
 
   if (CVAL(inbuf,smb_rcls) != 0)
     DEBUG(5,("getatr: %s\n",smb_errstr(inbuf)));
@@ -461,10 +570,10 @@ static int do_setrattr(char *fname, int attr, int setit)
   set_message(outbuf,8,4 + strlen(fname),True);
   CVAL(outbuf,smb_com) = SMBsetatr;
   SSVAL(outbuf,smb_tid,cnum);
-  setup_pkt(outbuf);
+  cli_setup_pkt(outbuf);
 
   SSVAL(outbuf,smb_vwv0,attr);
-
+  
   p = smb_buf(outbuf);
   *p++ = 4;      
   strcpy(p,fname);
@@ -474,7 +583,7 @@ static int do_setrattr(char *fname, int attr, int setit)
   *p++ = 0;
 
   send_smb(Client,outbuf);
-  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
   
   if (CVAL(inbuf,smb_rcls) != 0)
     {
@@ -496,12 +605,12 @@ static BOOL smbcreat(file_info finfo, int *fnum, char *inbuf, char *outbuf)
   char *p;
   /* *must* be called with buffer ready malloc'ed */
   /* open remote file */
-  
+
   memset(outbuf,0,smb_size);
   set_message(outbuf,3,2 + strlen(finfo.name),True);
   CVAL(outbuf,smb_com) = SMBcreate;
   SSVAL(outbuf,smb_tid,cnum);
-  setup_pkt(outbuf);
+  cli_setup_pkt(outbuf);
   
   SSVAL(outbuf,smb_vwv0,finfo.mode);
   put_dos_date3(outbuf,smb_vwv1,finfo.mtime);
@@ -511,7 +620,7 @@ static BOOL smbcreat(file_info finfo, int *fnum, char *inbuf, char *outbuf)
   strcpy(p,finfo.name);
   
   send_smb(Client,outbuf);
-  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
   
   if (CVAL(inbuf,smb_rcls) != 0)
     {
@@ -540,7 +649,7 @@ static BOOL smbwrite(int fnum, int n, int low, int high, int left,
   set_message(outbuf,5,n + 3, False);
   CVAL(outbuf,smb_com) = SMBwrite;
   SSVAL(outbuf,smb_tid,cnum);
-  setup_pkt(outbuf);
+  cli_setup_pkt(outbuf);
   
   SSVAL(outbuf,smb_vwv0,fnum);
   SSVAL(outbuf,smb_vwv1,n);
@@ -550,7 +659,7 @@ static BOOL smbwrite(int fnum, int n, int low, int high, int left,
   SSVAL(smb_buf(outbuf),1,n);
 
   send_smb(Client,outbuf); 
-  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
   
   if (CVAL(inbuf,smb_rcls) != 0)
     {
@@ -579,7 +688,7 @@ static BOOL smbshut(file_info finfo, int fnum, char *inbuf, char *outbuf)
   set_message(outbuf,3,0,True);
   CVAL(outbuf,smb_com) = SMBclose;
   SSVAL(outbuf,smb_tid,cnum);
-  setup_pkt(outbuf);
+  cli_setup_pkt(outbuf);
   
   SSVAL(outbuf,smb_vwv0,fnum);
   put_dos_date3(outbuf,smb_vwv1,finfo.mtime);
@@ -589,7 +698,7 @@ static BOOL smbshut(file_info finfo, int fnum, char *inbuf, char *outbuf)
           finfo.mtime));
   
   send_smb(Client,outbuf);
-  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
   
   if (CVAL(inbuf,smb_rcls) != 0)
     {
@@ -612,14 +721,14 @@ static BOOL smbchkpath(char *fname, char *inbuf, char *outbuf)
   set_message(outbuf,0,4 + strlen(fname),True);
   CVAL(outbuf,smb_com) = SMBchkpth;
   SSVAL(outbuf,smb_tid,cnum);
-  setup_pkt(outbuf);
+  cli_setup_pkt(outbuf);
 
   p = smb_buf(outbuf);
   *p++ = 4;
   strcpy(p,fname);
 
   send_smb(Client,outbuf);
-  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
 
   DEBUG(5,("smbchkpath: %s\n",smb_errstr(inbuf)));
 
@@ -639,14 +748,14 @@ static BOOL smbmkdir(char *fname, char *inbuf, char *outbuf)
   
   CVAL(outbuf,smb_com) = SMBmkdir;
   SSVAL(outbuf,smb_tid,cnum);
-  setup_pkt(outbuf);
+  cli_setup_pkt(outbuf);
   
   p = smb_buf(outbuf);
   *p++ = 4;      
   strcpy(p,fname);
   
   send_smb(Client,outbuf);
-  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
   
   if (CVAL(inbuf,smb_rcls) != 0)
     {
@@ -669,6 +778,8 @@ static BOOL ensurepath(char *fname, char *inbuf, char *outbuf)
   pstring partpath, ffname;
   char *p=fname, *basehack;
 
+  DEBUG(5, ( "Ensurepath called with: %s\n", fname));
+
   *partpath = 0;
 
   /* fname copied to ffname so can strtok */
@@ -705,6 +816,22 @@ static BOOL ensurepath(char *fname, char *inbuf, char *outbuf)
     return True;
 }
 
+int padit(char *buf, int bufsize, int padsize)
+{
+  int berr= 0;
+  int bytestowrite;
+  
+  DEBUG(0, ("Padding with %d zeros\n", padsize));
+  memset(buf, 0, bufsize);
+  while( !berr && padsize > 0 ) {
+    bytestowrite= MIN(bufsize, padsize);
+    berr = dotarbuf(tarhandle, buf, bytestowrite) != bytestowrite;
+    padsize -= bytestowrite;
+  }
+  
+  return berr;
+}
+
 /*
  * smbclient functions
  */
@@ -715,7 +842,7 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
 {
   int fnum;
   uint32 nread=0;
-  char *p;
+  char *p, ftype;
   char *inbuf,*outbuf;
   file_info finfo;
   BOOL close_done = False;
@@ -727,6 +854,8 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
   struct timeval tp_start;
   GetTimeOfDay(&tp_start);
 
+  ftype = '0'; /* An ordinary file ... */
+
   if (finfo1) 
     finfo = *finfo1;
   else
@@ -746,7 +875,7 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
 
   CVAL(outbuf,smb_com) = SMBopenX;
   SSVAL(outbuf,smb_tid,cnum);
-  setup_pkt(outbuf);
+  cli_setup_pkt(outbuf);
 
   SSVAL(outbuf,smb_vwv0,0xFF);
   SSVAL(outbuf,smb_vwv2,1);
@@ -776,13 +905,13 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
     }
   
   send_smb(Client,outbuf);
-  receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+  client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
 
   if (CVAL(inbuf,smb_rcls) != 0)
     {
       if (CVAL(inbuf,smb_rcls) == ERRSRV &&
          SVAL(inbuf,smb_err) == ERRnoresource &&
-         reopen_connection(inbuf,outbuf))
+         cli_reopen_connection(inbuf,outbuf))
        {
          do_atar(rname,lname,finfo1);
          free(inbuf);free(outbuf);
@@ -812,6 +941,16 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
       DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
       shallitime=0;
     }
+  else if (!tar_system && (finfo.mode & aSYSTEM))
+    {
+      DEBUG(4, ("skipping %s - system bit is set\n", finfo.name));
+      shallitime=0;
+    }
+  else if (!tar_hidden && (finfo.mode & aHIDDEN))
+    {
+      DEBUG(4, ("skipping %s - hidden bit is set\n", finfo.name));
+      shallitime=0;
+    }
   else
     {
       if (SVAL(inbuf,smb_vwv0) == SMBreadX)
@@ -832,7 +971,7 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
               lname));
       
       /* write a tar header, don't bother with mode - just set to 100644 */
-      writetarheader(tarhandle, rname, finfo.size, finfo.mtime, "100644 \0");
+      writetarheader(tarhandle, rname, finfo.size, finfo.mtime, "100644 \0", ftype);
       
       while (nread < finfo.size && !close_done)
        {
@@ -878,7 +1017,7 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
              set_message(outbuf,10,0,True);
              CVAL(outbuf,smb_com) = SMBreadX;
              SSVAL(outbuf,smb_tid,cnum);
-             setup_pkt(outbuf);
+             cli_setup_pkt(outbuf);
              
              if (close_done)
                {
@@ -910,7 +1049,7 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
                }
              
              send_smb(Client,outbuf);
-             receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+             client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
              
              if (CVAL(inbuf,smb_rcls) != 0)
                {
@@ -944,7 +1083,7 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
                set_message(outbuf,8,0,True);
                CVAL(outbuf,smb_com) = SMBreadbraw;
                SSVAL(outbuf,smb_tid,cnum);
-               setup_pkt(outbuf);
+               cli_setup_pkt(outbuf);
                SSVAL(outbuf,smb_vwv0,fnum);
                SIVAL(outbuf,smb_vwv1,nread);
                SSVAL(outbuf,smb_vwv3,MIN(finfo.size-nread,readbraw_size));
@@ -996,7 +1135,7 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
              set_message(outbuf,5,0,True);
              CVAL(outbuf,smb_com) = SMBread;
              SSVAL(outbuf,smb_tid,cnum);
-             setup_pkt(outbuf);
+             cli_setup_pkt(outbuf);
              
              SSVAL(outbuf,smb_vwv0,fnum);
              SSVAL(outbuf,smb_vwv1,MIN(max_xmit-200,finfo.size - nread));
@@ -1004,7 +1143,7 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
              SSVAL(outbuf,smb_vwv4,finfo.size - nread);
              
              send_smb(Client,outbuf);
-             receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+             client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
              
              if (CVAL(inbuf,smb_rcls) != 0)
                {
@@ -1022,7 +1161,7 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
           * write out in 512 byte intervals */
          if (dotarbuf(tarhandle,dataptr,datalen) != datalen)
            {
-             DEBUG(0,("Error writing local file\n"));
+             DEBUG(0,("Error writing to tar file - %s\n", strerror(errno)));
              break;
            }
          
@@ -1036,11 +1175,20 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
          dataptr=NULL;
          datalen=0;
        }
-      
+
+       /* pad tar file with zero's if we couldn't get entire file */
+       if (nread < finfo.size)
+        {
+          DEBUG(0, ("Didn't get entire file. size=%d, nread=%d\n", finfo.size, nread));
+          if (padit(inbuf, BUFFER_SIZE, finfo.size - nread))
+              DEBUG(0,("Error writing tar file - %s\n", strerror(errno)));
+        }
+
       /* round tar file to nearest block */
       if (finfo.size % TBLOCK)
        dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
       
+      ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
       ntarf++;
     }
   
@@ -1050,13 +1198,13 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
       set_message(outbuf,3,0,True);
       CVAL(outbuf,smb_com) = SMBclose;
       SSVAL(outbuf,smb_tid,cnum);
-      setup_pkt(outbuf);
+      cli_setup_pkt(outbuf);
       
       SSVAL(outbuf,smb_vwv0,fnum);
       SIVALS(outbuf,smb_vwv1,-1);
       
       send_smb(Client,outbuf);
-      receive_smb(Client,inbuf,CLIENT_TIMEOUT);
+      client_receive_smb(Client,inbuf,CLIENT_TIMEOUT);
       
       if (!ignore_close_error && CVAL(inbuf,smb_rcls) != 0)
        {
@@ -1085,6 +1233,13 @@ static void do_atar(char *rname,char *lname,file_info *finfo1)
       DEBUG(2,("(%g kb/s) (average %g kb/s)\n",
               finfo.size / MAX(0.001, (1.024*this_time)),
               get_total_size / MAX(0.001, (1.024*get_total_time_ms))));
+      if (tar_noisy)
+       {
+         printf("%10d (%7.1f kb/s) %s\n",
+              finfo.size, finfo.size / MAX(0.001, (1.024*this_time)),
+               finfo.name);
+       }
+
     }
   
   free(inbuf);free(outbuf);
@@ -1097,7 +1252,7 @@ static void do_tar(file_info *finfo)
 {
   pstring rname;
 
-  if (strequal(finfo->name,".") || strequal(finfo->name,".."))
+  if (strequal(finfo->name,".."))
     return;
 
   /* Is it on the exclude list ? */
@@ -1107,11 +1262,6 @@ static void do_tar(file_info *finfo)
     strcpy(exclaim, cur_dir);
     *(exclaim+strlen(exclaim)-1)='\0';
 
-    if (clipfind(cliplist, clipn, exclaim)) {
-      DEBUG(3,("Skipping directory %s\n", exclaim));
-      return;
-    }
-
     strcat(exclaim, "\\");
     strcat(exclaim, finfo->name);
 
@@ -1141,14 +1291,16 @@ static void do_tar(file_info *finfo)
       strcat(cur_dir,finfo->name);
       strcat(cur_dir,"\\");
 
+      DEBUG(5, ("Writing a dir, Name = %s\n", cur_dir));
+
       /* write a tar directory, don't bother with mode - just set it to
        * 40755 */
-      writetarheader(tarhandle, cur_dir, 0, finfo->mtime, "040755 \0");
+      writetarheader(tarhandle, cur_dir, 0, finfo->mtime, "040755 \0", '5');
+      ntarf++;  /* Make sure we have a file on there */
       strcpy(mtar_mask,cur_dir);
       strcat(mtar_mask,"*");
-      
-      do_dir((char *)inbuf,(char *)outbuf,mtar_mask,attribute,do_tar,recurse);
-      strcpy(cur_dir,saved_curdir);
+      /*      do_dir((char *)inbuf,(char *)outbuf,mtar_mask,attribute,do_tar,recurse,True); */
+             strcpy(cur_dir,saved_curdir);
       free(inbuf);free(outbuf);
     }
   else
@@ -1171,15 +1323,17 @@ static void unfixtarname(char *tptr, char *fp, int l)
   if (*fp == '.') fp++;
   if (*fp == '\\' || *fp == '/') fp++;
 
-#ifdef KANJI
   while (l > 0) {
-    if (is_shift_jis (*fp)) {
-      *tptr++ = *fp++;
-      *tptr++ = *fp++;
-      l -= 2;
-    } else if (is_kana (*fp)) {
-      *tptr++ = *fp++;
-      l--;
+    int skip;
+    if(( skip = skip_multibyte_char( *fp )) != 0) {
+      if (skip == 2) {
+        *tptr++ = *fp++;
+        *tptr++ = *fp++;
+        l -= 2;
+      } else if (skip == 1) {
+        *tptr++ = *fp++;
+        l--;
+      }
     } else if (*fp == '/') {
       *tptr++ = '\\';
       fp++;
@@ -1189,16 +1343,13 @@ static void unfixtarname(char *tptr, char *fp, int l)
       l--;
     }
   }
-#else
-  while (l--) { *tptr=(*fp == '/') ? '\\' : *fp; tptr++; fp++; }
-#endif
 }
 
-static void do_tarput()
+static void do_tarput(void)
 {
   file_info finfo;
   int nread=0, bufread;
-  char *inbuf,*outbuf; 
+  char *inbuf,*outbuf, *longname = NULL
   int fsize=0;
   int fnum;
   struct timeval tp_start;
@@ -1261,26 +1412,93 @@ static void do_tarput()
     do {
       if (!fsize)
        {
-         switch (readtarheader((union hblock *) bufferp, &finfo, cur_dir))
-           {
-           case -2:             /* something dodgy but not fatal about this */
-             DEBUG(0, ("skipping %s...\n", finfo.name));
-             bufferp+=TBLOCK;   /* header - like a link */
-             continue;
-           case -1:
-             DEBUG(0, ("abandoning restore\n"));
-             free(inbuf); free(outbuf);
-             return;
-           case 0: /* chksum is zero - we assume that one all zero
-                    *header block will do for eof */
-             DEBUG(0,
-                   ("total of %d tar files restored to share\n", ntarf));
-             free(inbuf); free(outbuf);
-             return;
-           default:
-             break;
-           }
+          int next_header = 1;  /* Want at least one header */
+          while (next_header) 
+            {  
+            if (bufferp >= endofbuffer) {
+
+              bufread = read(tarhandle, tarbuf, tbufsiz);
+              bufferp = tarbuf;
+
+            }
+            next_header = 0;    /* Don't want the next one ... */
+           switch (readtarheader((union hblock *) bufferp, &finfo, cur_dir))
+             {
+             case -2:             /* something dodgy but not fatal about this */
+               DEBUG(0, ("skipping %s...\n", finfo.name));
+               bufferp+=TBLOCK;   /* header - like a link */
+               continue;
+             case -1:
+               DEBUG(0, ("abandoning restore, -1 from readtarheader\n"));
+               free(inbuf); free(outbuf);
+               return;
+             case 0: /* chksum is zero - we assume that one all zero
+                      *header block will do for eof */
+               DEBUG(0,
+                     ("total of %d tar files restored to share\n", ntarf));
+               free(inbuf); free(outbuf);
+               return;
+             default:
+               break;
+             }
+
+            /* If we have a longname left from the last time through, 
+               copy it into finfo.name and free it.
+
+               The size of a pstring is the limiting factor on filenames
+               and directory names now. The total pathname length must be
+               less than sizeof(pstring) - 1, which is currently 1023. */
+
+            if (longname != NULL) {
+
+              strncpy(finfo.name, longname, sizeof(pstring) - 1);
+              free(longname);
+              longname = NULL;
+
+            }
+
+            /* Check if a long-link. We do this before the clip checking
+               because clip-checking should clip on real name - RJS */
 
+            if (((union hblock *)bufferp) -> dbuf.linkflag == 'L') {
+
+              /* Skip this header, but pick up length, get the name and 
+                 fix the name and skip the name. Hmmm, what about end of
+                 buffer??? */
+
+              longname = malloc(finfo.size + strlen(cur_dir) + 1);
+              if (longname == NULL) {
+
+                 DEBUG(0, ("could not allocate buffer of size %d for longname\n",
+                          finfo.size + strlen(cur_dir) + 1)
+                      );
+                 free(inbuf); free(outbuf);
+                 return;
+              }
+
+              bufferp += TBLOCK;   /* Skip that longlink header */
+
+              /* This needs restructuring ... */
+
+             if (bufferp >= endofbuffer) {
+
+               bufread = read(tarhandle, tarbuf, tbufsiz);
+
+               bufferp = tarbuf;
+
+              }
+
+              strncpy(longname, cur_dir, strlen(cur_dir)); 
+              unfixtarname(longname+strlen(cur_dir), bufferp, finfo.size);
+
+              /* Next rounds up to next TBLOCK and takes care of us being right
+                 on a TBLOCK boundary */
+
+              bufferp += (((finfo.size - 1)/TBLOCK)+1)*TBLOCK;
+              next_header = 1;  /* Force read of next header */
+
+            }
+          }
          tskip=clipn
            && (clipfind(cliplist, clipn, finfo.name) ^ tar_excl);
          if (tskip) {
@@ -1300,17 +1518,32 @@ static void do_tarput()
            }
          }
 
+         DEBUG(5, ("do_tarput: File is: %s\n", finfo.name));
+
          if (finfo.mode & aDIR)
            {
-             if (!smbchkpath(finfo.name, inbuf, outbuf)
-                 && !smbmkdir(finfo.name, inbuf, outbuf))
+             if (!ensurepath(finfo.name, inbuf, outbuf))
+/*           if (!smbchkpath(finfo.name, inbuf, outbuf)
+                 && !smbmkdir(finfo.name, inbuf, outbuf))*/
                {
-                 DEBUG(0, ("abandoning restore\n"));
+                 DEBUG(0, ("abandoning restore, problems ensuring path\n"));
                  free(inbuf); free(outbuf);
                  return;
              }
              else
                {
+                 /* Now we update the creation date ... */
+
+                 DEBUG(5, ("Updating creation date on %s\n", finfo.name));
+
+                 if (!do_setrtime(finfo.name, finfo.mtime)) {
+
+                    DEBUG(0, ("Could not set time on file: %s\n", finfo.name));
+                    return;
+
+                  }
+
+                 ntarf++;
                  bufferp+=TBLOCK;
                  continue;
                }
@@ -1326,9 +1559,17 @@ static void do_tarput()
              return;
            }
 
-         DEBUG(0,("restore tar file %s of size %d bytes\n",
+         DEBUG(0 ,("restore tar file %s of size %d bytes\n",
                   finfo.name,finfo.size));
 
+          if (!finfo.size) {
+           if (!smbshut(finfo, fnum, inbuf, outbuf)){
+              DEBUG(0, ("Error closing remote file of length 0: %s\n", finfo.name));
+             free(inbuf);free(outbuf);
+              return;
+            }
+         }
+
          nread=0;
          if ((bufferp+=TBLOCK) >= endofbuffer) break;    
        } /* if (!fsize) */
@@ -1359,7 +1600,7 @@ static void do_tarput()
        bufferp+=minichunk; nread+=minichunk;
        chunk-=minichunk;
       }
-      
+
       if (nread>=fsize)
        {
          if (!smbshut(finfo, fnum, inbuf, outbuf))
@@ -1390,7 +1631,7 @@ static void do_tarput()
 /****************************************************************************
 Blocksize command
 ***************************************************************************/
-void cmd_block(void)
+void cmd_block(char *dum_in, char *dum_out)
 {
   fstring buf;
   int block;
@@ -1415,7 +1656,7 @@ void cmd_block(void)
 /****************************************************************************
 command to set incremental / reset mode
 ***************************************************************************/
-void cmd_tarmode(void)
+void cmd_tarmode(char *dum_in, char *dum_out)
 {
   fstring buf;
 
@@ -1428,18 +1669,34 @@ void cmd_tarmode(void)
       tar_reset=True;
     else if (strequal(buf, "noreset"))
       tar_reset=False;
+    else if (strequal(buf, "system"))
+      tar_system=True;
+    else if (strequal(buf, "nosystem"))
+      tar_system=False;
+    else if (strequal(buf, "hidden"))
+      tar_hidden=True;
+    else if (strequal(buf, "nohidden"))
+      tar_hidden=False;
+    else if (strequal(buf, "verbose") || strequal(buf, "noquiet"))
+      tar_noisy=True;
+    else if (strequal(buf, "quiet") || strequal(buf, "noverbose"))
+      tar_noisy=False;
     else DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
   }
 
-  DEBUG(0, ("tarmode is now %s, %s\n",
+  DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n",
            tar_inc ? "incremental" : "full",
-           tar_reset ? "reset" : "noreset"));
+           tar_system ? "system" : "nosystem",
+           tar_hidden ? "hidden" : "nohidden",
+           tar_reset ? "reset" : "noreset",
+           tar_noisy ? "verbose" : "quiet"));
+
 }
 
 /****************************************************************************
 Feeble attrib command
 ***************************************************************************/
-void cmd_setmode(void)
+void cmd_setmode(char *dum_in, char *dum_out)
 {
   char *q;
   fstring buf;
@@ -1486,7 +1743,7 @@ void cmd_setmode(void)
       return;
     }
 
-DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
+  DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
   (void) do_setrattr(fname, attra[ATTRSET], ATTRSET);
   (void) do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
 }
@@ -1554,19 +1811,19 @@ int process_tar(char *inbuf, char *outbuf)
          strcpy(cur_dir, tarmac);
          *(strrchr(cur_dir, '\\')+1)='\0';
 
-         do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse);
+         do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse, True);
          strcpy(cur_dir,saved_dir);
        } else {
          strcpy(tarmac, cur_dir);
          strcat(tarmac, cliplist[i]);
-         do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse);
+         do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse, True);
        }
       }
     } else {
       pstring mask;
       strcpy(mask,cur_dir);
       strcat(mask,"\\*");
-      do_dir((char *)inbuf,(char *)outbuf,mask,attribute,do_tar,recurse);
+      do_dir((char *)inbuf,(char *)outbuf,mask,attribute,do_tar,recurse, True);
     }
     
     if (ntarf) dotareof(tarhandle);
@@ -1574,6 +1831,7 @@ int process_tar(char *inbuf, char *outbuf)
     free(tarbuf);
     
     DEBUG(0, ("tar: dumped %d tar files\n", ntarf));
+    DEBUG(0, ("Total bytes written: %d\n", ttarf));
     break;
   }
 
@@ -1684,21 +1942,50 @@ int tar_parseargs(int argc, char *argv[], char *Optarg, int Optind)
     return 0;
   }
 
+  tar_excl=tar_clipfl!='X';
+  if (Optind+1<argc) {
+    char *tmpstr;
+    char **tmplist;
+    int clipcount;
+
+    cliplist=argv+Optind+1;
+    clipn=argc-Optind-1;
+    clipcount = clipn;
+
+    if ((tmplist=malloc(clipn*sizeof(char *))) == NULL) {
+      DEBUG(0, ("Could not allocate space to process cliplist, count = %i\n", 
+               clipn)
+           );
+      return 0;
+    }
+
+    for (clipcount = 0; clipcount < clipn; clipcount++) {
+
+      DEBUG(5, ("Processing an item, %s\n", cliplist[clipcount]));
+
+      if ((tmpstr = (char *)malloc(strlen(cliplist[clipcount])+1)) == NULL) {
+        DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n",
+                 clipcount)
+             );
+        return 0;
+      }
+      unfixtarname(tmpstr, cliplist[clipcount], strlen(cliplist[clipcount]) + 1);
+      tmplist[clipcount] = tmpstr;
+      DEBUG(5, ("Processed an item, %s\n", tmpstr));
+
+      DEBUG(5, ("Cliplist is: %s\n", cliplist[0]));
+    }
+    cliplist = tmplist;
+  }
   if (Optind>=argc || !strcmp(argv[Optind], "-")) {
     /* Sets tar handle to either 0 or 1, as appropriate */
     tarhandle=(tar_type=='c');
   } else {
-    tar_excl=tar_clipfl!='X';
-    
-    if (Optind+1<argc) {
-      cliplist=argv+Optind+1;
-      clipn=argc-Optind-1;
-    }
-
     if ((tar_type=='x' && (tarhandle = open(argv[Optind], O_RDONLY)) == -1)
        || (tar_type=='c' && (tarhandle=creat(argv[Optind], 0644)) < 0))
       {
-       DEBUG(0,("Error opening local file %s\n",argv[Optind]));
+       DEBUG(0,("Error opening local file %s - %s\n",
+                argv[Optind], strerror(errno)));
        return(0);
       }
   }