Example #1
0
/*
 * Called here for each record from read_records()
 */
static bool record_cb(DCR *dcr, DEV_RECORD *rec)
{
   int status;
   JCR *jcr = dcr->jcr;

   if (rec->FileIndex < 0) {
      return true;                    /* we don't want labels */
   }

   /* File Attributes stream */

   switch (rec->maskedStream) {
   case STREAM_UNIX_ATTRIBUTES:
   case STREAM_UNIX_ATTRIBUTES_EX:

      /* If extracting, it was from previous stream, so
       * close the output file.
       */
      if (extract) {
         if (!is_bopen(&bfd)) {
            Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
         }
         set_attributes(jcr, attr, &bfd);
         extract = false;
      }

      if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
         Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
      }

      if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
         attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
         if (!is_restore_stream_supported(attr->data_stream)) {
            if (!non_support_data++) {
               Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"),
                  stream_to_ascii(attr->data_stream));
            }
            extract = false;
            return true;
         }

         build_attr_output_fnames(jcr, attr);

         if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */
            Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname);
            extract = false;
            return true;
         }

         extract = false;
         status = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);
         switch (status) {
         case CF_ERROR:
         case CF_SKIP:
            break;
         case CF_EXTRACT:
            extract = true;
            print_ls_output(jcr, attr);
            num_files++;
            fileAddr = 0;
            break;
         case CF_CREATED:
            set_attributes(jcr, attr, &bfd);
            print_ls_output(jcr, attr);
            num_files++;
            fileAddr = 0;
            break;
         }
      }
      break;

   case STREAM_RESTORE_OBJECT:
      /* nothing to do */
      break;

   /* Data stream and extracting */
   case STREAM_FILE_DATA:
   case STREAM_SPARSE_DATA:
   case STREAM_WIN32_DATA:

      if (extract) {
         if (rec->maskedStream == STREAM_SPARSE_DATA) {
            ser_declare;
            uint64_t faddr;
            wbuf = rec->data + OFFSET_FADDR_SIZE;
            wsize = rec->data_len - OFFSET_FADDR_SIZE;
            ser_begin(rec->data, OFFSET_FADDR_SIZE);
            unser_uint64(faddr);
            if (fileAddr != faddr) {
               fileAddr = faddr;
               if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
                  berrno be;
                  Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"),
                     attr->ofname, be.bstrerror());
               }
            }
         } else {
            wbuf = rec->data;
            wsize = rec->data_len;
         }
         total += wsize;
         Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
         store_data(&bfd, wbuf, wsize);
         fileAddr += wsize;
      }
      break;

   /* GZIP data stream */
   case STREAM_GZIP_DATA:
   case STREAM_SPARSE_GZIP_DATA:
   case STREAM_WIN32_GZIP_DATA:
#ifdef HAVE_LIBZ
      if (extract) {
         uLong compress_len = compress_buf_size;
         int status = Z_BUF_ERROR;

         if (rec->maskedStream == STREAM_SPARSE_GZIP_DATA) {
            ser_declare;
            uint64_t faddr;
            char ec1[50];
            wbuf = rec->data + OFFSET_FADDR_SIZE;
            wsize = rec->data_len - OFFSET_FADDR_SIZE;
            ser_begin(rec->data, OFFSET_FADDR_SIZE);
            unser_uint64(faddr);
            if (fileAddr != faddr) {
               fileAddr = faddr;
               if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
                  berrno be;
                  Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
                     edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
                  extract = false;
                  return true;
               }
            }
         } else {
            wbuf = rec->data;
            wsize = rec->data_len;
         }

         while (compress_len < 10000000 && (status = uncompress((Byte *)compress_buf, &compress_len,
                                 (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR) {
            /* The buffer size is too small, try with a bigger one */
            compress_len = 2 * compress_len;
            compress_buf = check_pool_memory_size(compress_buf,
                                                  compress_len);
         }
         if (status != Z_OK) {
            Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), status);
            extract = false;
            return true;
         }

         Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
         store_data(&bfd, compress_buf, compress_len);
         total += compress_len;
         fileAddr += compress_len;
         Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
            compress_len);
      }
#else
      if (extract) {
         Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n"));
         extract = false;
         return true;
      }
#endif
      break;

   /* Compressed data stream */
   case STREAM_COMPRESSED_DATA:
   case STREAM_SPARSE_COMPRESSED_DATA:
   case STREAM_WIN32_COMPRESSED_DATA:
      if (extract) {
         uint32_t comp_magic, comp_len;
         uint16_t comp_level, comp_version;
#ifdef HAVE_LZO
         lzo_uint compress_len;
         const unsigned char *cbuf;
         int r, real_compress_len;
#endif

         if (rec->maskedStream == STREAM_SPARSE_COMPRESSED_DATA) {
            ser_declare;
            uint64_t faddr;
            char ec1[50];
            wbuf = rec->data + OFFSET_FADDR_SIZE;
            wsize = rec->data_len - OFFSET_FADDR_SIZE;
            ser_begin(rec->data, OFFSET_FADDR_SIZE);
            unser_uint64(faddr);
            if (fileAddr != faddr) {
               fileAddr = faddr;
               if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) {
                  berrno be;
                  Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"),
                     edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror());
                  extract = false;
                  return true;
               }
            }
         } else {
            wbuf = rec->data;
            wsize = rec->data_len;
         }

         /* read compress header */
         unser_declare;
         unser_begin(wbuf, sizeof(comp_stream_header));
         unser_uint32(comp_magic);
         unser_uint32(comp_len);
         unser_uint16(comp_level);
         unser_uint16(comp_version);
         Dmsg4(200, "Compressed data stream found: magic=0x%x, len=%d, level=%d, ver=0x%x\n", comp_magic, comp_len,
                                 comp_level, comp_version);

         /* version check */
         if (comp_version != COMP_HEAD_VERSION) {
            Emsg1(M_ERROR, 0, _("Compressed header version error. version=0x%x\n"), comp_version);
            return false;
         }
         /* size check */
         if (comp_len + sizeof(comp_stream_header) != wsize) {
            Emsg2(M_ERROR, 0, _("Compressed header size error. comp_len=%d, msglen=%d\n"),
                 comp_len, wsize);
            return false;
         }

          switch(comp_magic) {
#ifdef HAVE_LZO
            case COMPRESS_LZO1X:
               compress_len = compress_buf_size;
               cbuf = (const unsigned char*) wbuf + sizeof(comp_stream_header);
               real_compress_len = wsize - sizeof(comp_stream_header);
               Dmsg2(200, "Comp_len=%d msglen=%d\n", compress_len, wsize);
               while ((r=lzo1x_decompress_safe(cbuf, real_compress_len,
                                               (unsigned char *)compress_buf, &compress_len, NULL)) == LZO_E_OUTPUT_OVERRUN)
               {

                  /* The buffer size is too small, try with a bigger one */
                  compress_len = 2 * compress_len;
                  compress_buf = check_pool_memory_size(compress_buf,
                                                  compress_len);
               }
               if (r != LZO_E_OK) {
                  Emsg1(M_ERROR, 0, _("LZO uncompression error. ERR=%d\n"), r);
                  extract = false;
                  return true;
               }
               Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
               store_data(&bfd, compress_buf, compress_len);
               total += compress_len;
               fileAddr += compress_len;
               Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len, compress_len);
               break;
#endif
            default:
               Emsg1(M_ERROR, 0, _("Compression algorithm 0x%x found, but not supported!\n"), comp_magic);
               extract = false;
               return true;
         }

      }
      break;

   case STREAM_MD5_DIGEST:
   case STREAM_SHA1_DIGEST:
   case STREAM_SHA256_DIGEST:
   case STREAM_SHA512_DIGEST:
      break;

   case STREAM_SIGNED_DIGEST:
   case STREAM_ENCRYPTED_SESSION_DATA:
      // TODO landonf: Investigate crypto support in the storage daemon
      break;

   case STREAM_PROGRAM_NAMES:
   case STREAM_PROGRAM_DATA:
      if (!prog_name_msg) {
         Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n"));
         prog_name_msg++;
      }
      break;

   case STREAM_UNIX_ACCESS_ACL:          /* Deprecated Standard ACL attributes on UNIX */
   case STREAM_UNIX_DEFAULT_ACL:         /* Deprecated Default ACL attributes on UNIX */
   case STREAM_ACL_AIX_TEXT:
   case STREAM_ACL_DARWIN_ACCESS_ACL:
   case STREAM_ACL_FREEBSD_DEFAULT_ACL:
   case STREAM_ACL_FREEBSD_ACCESS_ACL:
   case STREAM_ACL_HPUX_ACL_ENTRY:
   case STREAM_ACL_IRIX_DEFAULT_ACL:
   case STREAM_ACL_IRIX_ACCESS_ACL:
   case STREAM_ACL_LINUX_DEFAULT_ACL:
   case STREAM_ACL_LINUX_ACCESS_ACL:
   case STREAM_ACL_TRU64_DEFAULT_ACL:
   case STREAM_ACL_TRU64_DEFAULT_DIR_ACL:
   case STREAM_ACL_TRU64_ACCESS_ACL:
   case STREAM_ACL_SOLARIS_ACLENT:
   case STREAM_ACL_SOLARIS_ACE:
   case STREAM_ACL_AFS_TEXT:
   case STREAM_ACL_AIX_AIXC:
   case STREAM_ACL_AIX_NFS4:
   case STREAM_ACL_FREEBSD_NFS4_ACL:
   case STREAM_ACL_HURD_DEFAULT_ACL:
   case STREAM_ACL_HURD_ACCESS_ACL:
      if (extract) {
         wbuf = rec->data;
         wsize = rec->data_len;
         pm_strcpy(acl_data.last_fname, attr->fname);

         parse_acl_streams(jcr, &acl_data, rec->maskedStream, wbuf, wsize);
      }
      break;

   case STREAM_XATTR_HURD:
   case STREAM_XATTR_IRIX:
   case STREAM_XATTR_TRU64:
   case STREAM_XATTR_AIX:
   case STREAM_XATTR_OPENBSD:
   case STREAM_XATTR_SOLARIS_SYS:
   case STREAM_XATTR_SOLARIS:
   case STREAM_XATTR_DARWIN:
   case STREAM_XATTR_FREEBSD:
   case STREAM_XATTR_LINUX:
   case STREAM_XATTR_NETBSD:
      if (extract) {
         wbuf = rec->data;
         wsize = rec->data_len;
         pm_strcpy(xattr_data.last_fname, attr->fname);

         parse_xattr_streams(jcr, &xattr_data, rec->maskedStream, wbuf, wsize);
      }
      break;

   case STREAM_NDMP_SEPERATOR:
      break;

   default:
      /* If extracting, weird stream (not 1 or 2), close output file anyway */
      if (extract) {
         if (!is_bopen(&bfd)) {
            Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
         }
         set_attributes(jcr, attr, &bfd);
         extract = false;
      }
      Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"),
         rec->Stream);
      break;

   } /* end switch */
   return true;
}
Example #2
0
/**
 * Set file modes, permissions and times
 *
 *  fname is the original filename
 *  ofile is the output filename (may be in a different directory)
 *
 * Returns:  true  on success
 *           false on failure
 */
bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
{
   struct utimbuf ut;
   mode_t old_mask;
   bool ok = true;
   boffset_t fsize;
 
   if (uid_set) {
      my_uid = getuid();
      my_gid = getgid();
      uid_set = true;
   }

#if defined(HAVE_WIN32)
   if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
       set_win32_attributes(jcr, attr, ofd)) {
       if (is_bopen(ofd)) {
           bclose(ofd);
       }
       pm_strcpy(attr->ofname, "*none*");
       return true;
   }
   if (attr->data_stream == STREAM_WIN32_DATA ||
       attr->data_stream == STREAM_WIN32_GZIP_DATA ||
       attr->data_stream == STREAM_WIN32_COMPRESSED_DATA) {
      if (is_bopen(ofd)) {
         bclose(ofd);
      }
      pm_strcpy(attr->ofname, "*none*");
      return true;
   }


   /**
    * If Windows stuff failed, e.g. attempt to restore Unix file
    *  to Windows, simply fall through and we will do it the
    *  universal way.
    */
#endif

   old_mask = umask(0);
   if (is_bopen(ofd)) {
      char ec1[50], ec2[50];
      fsize = blseek(ofd, 0, SEEK_END);
      bclose(ofd);                    /* first close file */
      if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 && 
                        fsize != (boffset_t)attr->statp.st_size) {
         Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
            attr->ofname, edit_uint64(attr->statp.st_size, ec1),
            edit_uint64(fsize, ec2));
      }
   }

   /**
    * We do not restore sockets, so skip trying to restore their
    *   attributes.
    */
   if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
      goto bail_out;
   }

   ut.actime = attr->statp.st_atime;
   ut.modtime = attr->statp.st_mtime;

   /* ***FIXME**** optimize -- don't do if already correct */
   /**
    * For link, change owner of link using lchown, but don't
    *   try to do a chmod as that will update the file behind it.
    */
   if (attr->type == FT_LNK) {
      /** Change owner of link, not of real file */
      if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
            attr->ofname, be.bstrerror());
         ok = false;
      }
   } else {
      if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
            attr->ofname, be.bstrerror());
         ok = false;
      }
      if (chmod(attr->ofname, attr->statp.st_mode) < 0 && my_uid == 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
            attr->ofname, be.bstrerror());
         ok = false;
      }

      /**
       * Reset file times.
       */
      if (utime(attr->ofname, &ut) < 0 && my_uid == 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
            attr->ofname, be.bstrerror());
         ok = false;
      }
#ifdef HAVE_CHFLAGS
      /**
       * FreeBSD user flags
       *
       * Note, this should really be done before the utime() above,
       *  but if the immutable bit is set, it will make the utimes()
       *  fail.
       */
      if (chflags(attr->ofname, attr->statp.st_flags) < 0 && my_uid == 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
            attr->ofname, be.bstrerror());
         ok = false;
      }
#endif
   }

bail_out:
   pm_strcpy(attr->ofname, "*none*");
   umask(old_mask);
   return ok;
}
Example #3
0
/*
 * Restore all file attributes like owner, mode and file times.
 */
static inline bool restore_file_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
{
    bool ok = true;
    bool suppress_errors;
#if defined(HAVE_FCHOWN) || defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(FUTIMENS)
    bool file_is_open;

    /*
     * Save if we are working on an open file.
     */
    file_is_open = is_bopen(ofd);
#endif

    /*
     * See if we want to print errors.
     */
    suppress_errors = (debug_level >= 100 || my_uid != 0);

    /*
     * Restore owner and group.
     */
#ifdef HAVE_FCHOWN
    if (file_is_open) {
        if (fchown(ofd->fid, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    } else {
#else
    {
#endif
        if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    }

    /*
     * Restore filemode.
     */
#ifdef HAVE_FCHMOD
    if (file_is_open) {
        if (fchmod(ofd->fid, attr->statp.st_mode) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    } else {
#else
    {
#endif
#if defined(HAVE_WIN32)
        if (win32_chmod(attr->ofname, attr->statp.st_mode, attr->statp.st_rdev) < 0 && !suppress_errors) {
#else
        if (lchmod(attr->ofname, attr->statp.st_mode) < 0 && !suppress_errors) {
#endif
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    }

    /*
     * Reset file times.
     */
#if defined(HAVE_FUTIMES)
    if (file_is_open) {
        struct timeval restore_times[2];

        restore_times[0].tv_sec = attr->statp.st_atime;
        restore_times[0].tv_usec = 0;
        restore_times[1].tv_sec = attr->statp.st_mtime;
        restore_times[1].tv_usec = 0;

        if (futimes(ofd->fid, restore_times) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    } else {
#elif defined(HAVE_FUTIMENS)
    if (file_is_open) {
        struct timespec restore_times[2];

        restore_times[0].tv_sec = attr->statp.st_atime;
        restore_times[0].tv_nsec = 0;
        restore_times[1].tv_sec = attr->statp.st_mtime;
        restore_times[1].tv_nsec = 0;

        if (futimens(ofd->fid, restore_times) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    } else {
#else
    {
#endif
#if defined(HAVE_LUTIMES)
        struct timeval restore_times[2];

        restore_times[0].tv_sec = attr->statp.st_atime;
        restore_times[0].tv_usec = 0;
        restore_times[1].tv_sec = attr->statp.st_mtime;
        restore_times[1].tv_usec = 0;

        if (lutimes(attr->ofname, restore_times) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
#elif defined(HAVE_UTIMES)
        struct timeval restore_times[2];

        restore_times[0].tv_sec = attr->statp.st_atime;
        restore_times[0].tv_usec = 0;
        restore_times[1].tv_sec = attr->statp.st_mtime;
        restore_times[1].tv_usec = 0;

        if (utimes(attr->ofname, restore_times) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
#else
        struct utimbuf restore_times;

        restore_times.actime = attr->statp.st_atime;
        restore_times.modtime = attr->statp.st_mtime;

        if (utime(attr->ofname, &restore_times) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
#endif /* HAVE_LUTIMES */
    }

    return ok;
}

/**
 * Set file modes, permissions and times
 *
 *  fname is the original filename
 *  ofile is the output filename (may be in a different directory)
 *
 * Returns:  true  on success
 *           false on failure
 */
bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
{
    mode_t old_mask;
    bool ok = true;
    bool suppress_errors;

    if (uid_set) {
        my_uid = getuid();
        my_gid = getgid();
        uid_set = true;
    }

    /*
     * See if we want to print errors.
     */
    suppress_errors = (debug_level >= 100 || my_uid != 0);

#if defined(HAVE_WIN32)
    if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
            set_win32_attributes(jcr, attr, ofd)) {
        if (is_bopen(ofd)) {
            bclose(ofd);
        }
        pm_strcpy(attr->ofname, "*None*");
        return true;
    }

    if (attr->data_stream == STREAM_WIN32_DATA ||
            attr->data_stream == STREAM_WIN32_GZIP_DATA ||
            attr->data_stream == STREAM_WIN32_COMPRESSED_DATA) {
        if (is_bopen(ofd)) {
            bclose(ofd);
        }
        pm_strcpy(attr->ofname, "*None*");
        return true;
    }

    /**
     * If Windows stuff failed, e.g. attempt to restore Unix file to Windows, simply fall
     * through and we will do it the universal way.
     */
#endif

    old_mask = umask(0);
    if (is_bopen(ofd)) {
        boffset_t fsize;
        char ec1[50], ec2[50];

        fsize = blseek(ofd, 0, SEEK_END);
        if (attr->type == FT_REG &&
                fsize > 0 &&
                attr->statp.st_size > 0 &&
                fsize != (boffset_t)attr->statp.st_size) {
            Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
                  attr->ofname, edit_uint64(attr->statp.st_size, ec1), edit_uint64(fsize, ec2));
        }
    } else {
        struct stat st;
        char ec1[50], ec2[50];

        if (lstat(attr->ofname, &st) == 0) {
            if (attr->type == FT_REG &&
                    st.st_size > 0 &&
                    attr->statp.st_size > 0 &&
                    st.st_size != attr->statp.st_size) {
                Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
                      attr->ofname, edit_uint64(attr->statp.st_size, ec1), edit_uint64(st.st_size, ec2));
            }
        }
    }

    /**
     * We do not restore sockets, so skip trying to restore their attributes.
     */
    if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
        goto bail_out;
    }

    /* ***FIXME**** optimize -- don't do if already correct */
    /**
     * For link, change owner of link using lchown, but don't try to do a chmod as that will update the file behind it.
     */
    if (attr->type == FT_LNK) {
        /*
         * Change owner of link, not of real file
         */
        if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }

#ifdef HAVE_LCHMOD
        if (lchmod(attr->ofname, attr->statp.st_mode) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
#endif
    } else {
        if (!ofd->cmd_plugin) {
            ok = restore_file_attributes(jcr, attr, ofd);

#ifdef HAVE_CHFLAGS
            /**
             * FreeBSD user flags
             *
             * Note, this should really be done before the utime() above,
             * but if the immutable bit is set, it will make the utimes()
             * fail.
             */
            if (chflags(attr->ofname, attr->statp.st_flags) < 0 && !suppress_errors) {
                berrno be;
                Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"), attr->ofname, be.bstrerror());
                ok = false;
            }
#endif
        }
    }

bail_out:
    if (is_bopen(ofd)) {
        bclose(ofd);
    }

    pm_strcpy(attr->ofname, "*None*");
    umask(old_mask);

    return ok;
}

#if !defined(HAVE_WIN32)
/*=============================================================*/
/*                                                             */
/*                 * * *  U n i x * * * *                      */
/*                                                             */
/*=============================================================*/

/**
 * It is possible to piggyback additional data e.g. ACLs on
 *   the encode_stat() data by returning the extended attributes
 *   here.  They must be "self-contained" (i.e. you keep track
 *   of your own length), and they must be in ASCII string
 *   format. Using this feature is not recommended.
 * The code below shows how to return nothing.  See the Win32
 *   code below for returning something in the attributes.
 */
int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
{
#ifdef HAVE_DARWIN_OS
    /**
     * We save the Mac resource fork length so that on a
     * restore, we can be sure we put back the whole resource.
     */
    char *p;

    *attribsEx = 0;                 /* no extended attributes (yet) */
    if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
        return STREAM_UNIX_ATTRIBUTES;
    }
    p = attribsEx;
    if (bit_is_set(FO_HFSPLUS, ff_pkt->flags)) {
        p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
    }
    *p = 0;
#else
    *attribsEx = 0;                    /* no extended attributes */
#endif
    return STREAM_UNIX_ATTRIBUTES;
}
#else
/*=============================================================*/
/*                                                             */
/*                 * * *  W i n 3 2 * * * *                    */
/*                                                             */
/*=============================================================*/
int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
{
    char *p = attribsEx;
    WIN32_FILE_ATTRIBUTE_DATA atts;
    ULARGE_INTEGER li;

    attribsEx[0] = 0;                  /* no extended attributes */

    if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
        return STREAM_UNIX_ATTRIBUTES;
    }

    unix_name_to_win32(ff_pkt->sys_fname, ff_pkt->fname);
    if (p_GetFileAttributesExW)  {
        /**
         * Try unicode version
         */
        POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
        make_win32_path_UTF8_2_wchar(pwszBuf, ff_pkt->fname);

        BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard,
                                      (LPVOID)&atts);
        free_pool_memory(pwszBuf);

        if (!b) {
            win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
            return STREAM_UNIX_ATTRIBUTES;
        }
    } else {
        if (!p_GetFileAttributesExA)
            return STREAM_UNIX_ATTRIBUTES;

        if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
                                    (LPVOID)&atts)) {
            win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
            return STREAM_UNIX_ATTRIBUTES;
        }
    }

    /*
     * Instead of using the current dwFileAttributes use the
     * ff_pkt->statp.st_rdev which contains the actual fileattributes we
     * want to save for this file.
     */
    atts.dwFileAttributes = ff_pkt->statp.st_rdev;

    p += to_base64((uint64_t)atts.dwFileAttributes, p);
    *p++ = ' ';                        /* separate fields with a space */
    li.LowPart = atts.ftCreationTime.dwLowDateTime;
    li.HighPart = atts.ftCreationTime.dwHighDateTime;
    p += to_base64((uint64_t)li.QuadPart, p);
    *p++ = ' ';
    li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
    li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
    p += to_base64((uint64_t)li.QuadPart, p);
    *p++ = ' ';
    li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
    li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
    p += to_base64((uint64_t)li.QuadPart, p);
    *p++ = ' ';
    p += to_base64((uint64_t)atts.nFileSizeHigh, p);
    *p++ = ' ';
    p += to_base64((uint64_t)atts.nFileSizeLow, p);
    *p = 0;

    return STREAM_UNIX_ATTRIBUTES_EX;
}