Beispiel #1
0
void
symlink_error (char const *contents, char const *name)
{
  int e = errno;
  ERROR ((0, e, _("%s: Cannot create symlink to %s"),
	  quotearg_colon (name), quote_n (1, contents)));
}
Beispiel #2
0
void
link_error (char const *target, char const *source)
{
  int e = errno;
  ERROR ((0, e, _("%s: Cannot hard link to %s"),
	  quotearg_colon (source), quote_n (1, target)));
}
Beispiel #3
0
void
argmatch_invalid (const char *context, const char *value, ptrdiff_t problem)
{
    char const *format = (problem == -1
                          ? _("invalid argument %s for %s")
                          : _("ambiguous argument %s for %s"));

    error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value),
           quote_n (1, context));
}
Beispiel #4
0
/* Try to restore the recently backed up file to its original name.
   This is usually only needed after a failed extraction.  */
void
undo_last_backup (void)
{
  if (after_backup_name)
    {
      if (rename (after_backup_name, before_backup_name) != 0)
	{
	  int e = errno;
	  ERROR ((0, e, _("%s: Cannot rename to %s"),
		  quotearg_colon (after_backup_name),
		  quote_n (1, before_backup_name)));
	}
      if (verbose_option)
	fprintf (stdlis, _("Renaming %s back to %s\n"),
		 quote_n (0, after_backup_name),
		 quote_n (1, before_backup_name));
      assign_string (&after_backup_name, 0);
    }
}
Beispiel #5
0
static void
display_transfer_ended_msg(bool upload, DCUserConn *uc, bool success, const char *extras_fmt, ...)
{
    char timebuf[LONGEST_ELAPSED_TIME+1];
    char ratebuf[LONGEST_HUMAN_READABLE+1];
    char sizebuf[LONGEST_HUMAN_READABLE+1];
    char *str1;
    char *str2;
    time_t now;
    uint64_t len;
    va_list args;

    len = uc->transfer_pos - uc->transfer_start;

    if (uc->transfer_time == (time_t) -1) {
        str2 = xstrdup("");
    } else {
        time(&now);
        if (now == (time_t) -1) {
            warn(_("Cannot get current time - %s\n"), errstr);
            str2 = xstrdup("");
        } else {
            str2 = xasprintf(_(" in %s (%s/s)"),
                             elapsed_time_to_string(now - uc->transfer_time, timebuf),
                             human_readable(
                                 len/MAX(now - uc->transfer_time, 1), ratebuf,
                                 human_suppress_point_zero|human_autoscale|human_base_1024|human_SI|human_B,
                                 1, 1));
        }
    }

    va_start(args, extras_fmt);
    str1 = xvasprintf(extras_fmt, args);
    va_end(args);

    flag_putf(upload ? DC_DF_UPLOAD : DC_DF_DOWNLOAD,
              _("%s: %s of %s %s%s. %s %s%s.\n"),
              quotearg_n(0, uc->info->nick),
              upload ? _("Upload") : _("Download"),
              quote_n(1, base_name(uc->transfer_file)),
              success ? _("succeeded") : _("failed"),
              str1,
              human_readable(len, sizebuf, human_suppress_point_zero|human_autoscale|human_base_1024|human_SI|human_B, 1, 1),
              ngettext("transferred", "transferred", len),
              str2);

    free(str1);
    free(str2);
}
Beispiel #6
0
static void
update_user_connection_name(DCUserConn *uc, const char *format, ...)
{
    char *newname;
    va_list args;

    va_start(args, format);
    newname = xvasprintf(format, args);
    va_end(args);

    hmap_remove(user_conns, uc->name);
    flag_putf(DC_DF_CONNECTIONS, _("User connection %s renamed to %s.\n"), quote_n(0, uc->name), quote_n(1, newname));
    if (strchr(uc->name, '|') == NULL)
        ptrv_append(user_conn_unknown_free, uc->name);
    else
        free(uc->name);
    uc->name = newname;
    hmap_put(user_conns, uc->name, uc);
}
Beispiel #7
0
void
muscle_percent_define_check_values (char const * const *values)
{
  for (; *values; ++values)
    {
      char const * const *variablep = values;
      uniqstr name = muscle_name (*variablep, NULL);
      char *value = string_decode (name);
      muscle_percent_define_check_kind (*variablep, muscle_keyword);
      if (value)
        {
          for (++values; *values; ++values)
            {
              if (STREQ (value, *values))
                break;
            }
          if (!*values)
            {
              unsigned i = 0;
              location loc = muscle_percent_define_get_loc (*variablep);
              complain_indent (&loc, complaint, &i,
                               _("invalid value for %%define variable %s: %s"),
                               quote (*variablep), quote_n (1, value));
              i += SUB_INDENT;
              for (values = variablep + 1; *values; ++values)
                complain_indent (&loc, complaint | no_caret | silent, &i,
                                 _("accepted value: %s"), quote (*values));
            }
          else
            {
              while (*values)
                ++values;
            }
          free (value);
        }
      else
        complain (NULL, fatal, _("%s: undefined %%define variable %s"),
                  "muscle_percent_define_check_values", quote (*variablep));
    }
}
Beispiel #8
0
static struct directory *
procdir (const char *name_buffer, struct stat *stat_data,
	 dev_t device,
	 int flag,
	 char *entry)
{
  struct directory *directory;
  bool nfs = NFS_FILE_STAT (*stat_data);

  if ((directory = find_directory (name_buffer)) != NULL)
    {
      if (DIR_IS_INITED (directory))
	{
	  if (flag & PD_FORCE_INIT)
	    {
	      assign_string (&directory->name, name_buffer);
	    }
	  else
	    {
	      *entry = 'N'; /* Avoid duplicating this directory */
	      return directory;
	    }
	}

      if (strcmp (directory->name, name_buffer))
	{
	  *entry = 'N';
	  return directory;
	}
      
      /* With NFS, the same file can have two different devices
	 if an NFS directory is mounted in multiple locations,
	 which is relatively common when automounting.
	 To avoid spurious incremental redumping of
	 directories, consider all NFS devices as equal,
	 relying on the i-node to establish differences.  */
      
      if (! ((!check_device_option
	      || (DIR_IS_NFS (directory) && nfs)
	      || directory->device_number == stat_data->st_dev)
	     && directory->inode_number == stat_data->st_ino))
	{
	  /* FIXME: find_directory_meta ignores nfs */
	  struct directory *d = find_directory_meta (stat_data->st_dev,
						     stat_data->st_ino);
	  if (d)
	    {
	      if (strcmp (d->name, name_buffer))
		{
		  WARNOPT (WARN_RENAME_DIRECTORY,
			   (0, 0,
			    _("%s: Directory has been renamed from %s"),
			    quotearg_colon (name_buffer),
			    quote_n (1, d->name)));
		  directory->orig = d;
		  DIR_SET_FLAG (directory, DIRF_RENAMED);
		  dirlist_replace_prefix (d->name, name_buffer);
		}
	      directory->children = CHANGED_CHILDREN;
	    }
	  else
	    {
	      WARNOPT (WARN_RENAME_DIRECTORY,
		       (0, 0, _("%s: Directory has been renamed"),
			quotearg_colon (name_buffer)));
	      directory->children = ALL_CHILDREN;
	      directory->device_number = stat_data->st_dev;
	      directory->inode_number = stat_data->st_ino;
	    }
	  if (nfs)
	    DIR_SET_FLAG (directory, DIRF_NFS);
	}
      else
	directory->children = CHANGED_CHILDREN;
      
      DIR_SET_FLAG (directory, DIRF_FOUND);
    }
  else
    {
      struct directory *d = find_directory_meta (stat_data->st_dev,
						 stat_data->st_ino);
      
      directory = note_directory (name_buffer,
				  get_stat_mtime(stat_data),
				  stat_data->st_dev,
				  stat_data->st_ino,
				  nfs,
				  true,
				  NULL);

      if (d)
	{
	  if (strcmp (d->name, name_buffer))
	    {
	      WARNOPT (WARN_RENAME_DIRECTORY,
		       (0, 0, _("%s: Directory has been renamed from %s"),
			quotearg_colon (name_buffer),
			quote_n (1, d->name)));
	      directory->orig = d;
	      DIR_SET_FLAG (directory, DIRF_RENAMED);
	      dirlist_replace_prefix (d->name, name_buffer);
	    }
	  directory->children = CHANGED_CHILDREN;
	}
      else
	{
	  DIR_SET_FLAG (directory, DIRF_NEW);
	  WARNOPT (WARN_NEW_DIRECTORY,
		   (0, 0, _("%s: Directory is new"),
		    quotearg_colon (name_buffer)));
	  directory->children =
	    (listed_incremental_option
	     || (OLDER_STAT_TIME (*stat_data, m)
		 || (after_date_option
		     && OLDER_STAT_TIME (*stat_data, c))))
	    ? ALL_CHILDREN
	    : CHANGED_CHILDREN;
	}
    }

  /* If the directory is on another device and --one-file-system was given,
     omit it... */
  if (one_file_system_option && device != stat_data->st_dev
      /* ... except if it was explicitely given in the command line */
      && !is_individual_file (name_buffer))
    /* FIXME: 
	WARNOPT (WARN_XDEV,
		 (0, 0,
		  _("%s: directory is on a different filesystem; not dumped"),
		  quotearg_colon (directory->name)));
    */
    directory->children = NO_CHILDREN;
  else if (flag & PD_FORCE_CHILDREN)
    {
      directory->children = PD_CHILDREN(flag);
      if (directory->children == NO_CHILDREN)
	*entry = 'N';
    }
	  
  DIR_SET_FLAG (directory, DIRF_INIT);

  if (directory->children != NO_CHILDREN)
    {
      const char *tag_file_name;

      switch (check_exclusion_tags (name_buffer, &tag_file_name))
	{
	case exclusion_tag_all:
	  /* This warning can be duplicated by code in dump_file0, but only
	     in case when the topmost directory being archived contains
	     an exclusion tag. */
	  exclusion_tag_warning (name_buffer, tag_file_name,
				 _("directory not dumped"));
	  *entry = 'N';
	  directory->children = NO_CHILDREN;
	  break;

	case exclusion_tag_contents:
	  exclusion_tag_warning (name_buffer, tag_file_name,
				 _("contents not dumped"));
	  directory->children = NO_CHILDREN;
	  break;
	  
	case exclusion_tag_under:
	  exclusion_tag_warning (name_buffer, tag_file_name,
				 _("contents not dumped"));
	  directory->tagfile = tag_file_name;
	  break;
	  
	case exclusion_tag_none:
	  break;
	}
    }

  return directory;
}
Beispiel #9
0
/* This function is called once for every file system object that fts
   encounters.  fts performs a depth-first traversal.
   A directory is usually processed twice, first with fts_info == FTS_D,
   and later, after all of its entries have been processed, with FTS_DP.
   Return RM_ERROR upon error, RM_USER_DECLINED for a negative response
   to an interactive prompt, and otherwise, RM_OK.  */
static enum RM_status
rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x)
{
  switch (ent->fts_info)
    {
    case FTS_D:			/* preorder directory */
      if (! x->recursive
          && !(x->remove_empty_directories
               && is_empty_dir (fts->fts_cwd_fd, ent->fts_accpath)))
        {
          /* This is the first (pre-order) encounter with a directory
             that we cannot delete.
             Not recursive, and it's not an empty directory (if we're removing
             them) so arrange to skip contents.  */
          int err = x->remove_empty_directories ? ENOTEMPTY : EISDIR;
          error (0, err, _("cannot remove %s"), quote (ent->fts_path));
          mark_ancestor_dirs (ent);
          fts_skip_tree (fts, ent);
          return RM_ERROR;
        }

      /* Perform checks that can apply only for command-line arguments.  */
      if (ent->fts_level == FTS_ROOTLEVEL)
        {
          /* POSIX says:
             If the basename of a command line argument is "." or "..",
             diagnose it and do nothing more with that argument.  */
          if (dot_or_dotdot (last_component (ent->fts_accpath)))
            {
              error (0, 0,
                     _("refusing to remove %s or %s directory: skipping %s"),
                     quote_n (0, "."), quote_n (1, ".."),
                     quote_n (2, ent->fts_path));
              fts_skip_tree (fts, ent);
              return RM_ERROR;
            }

          /* POSIX also says:
             If a command line argument resolves to "/" (and --preserve-root
             is in effect -- default) diagnose and skip it.  */
          if (ROOT_DEV_INO_CHECK (x->root_dev_ino, ent->fts_statp))
            {
              ROOT_DEV_INO_WARN (ent->fts_path);
              fts_skip_tree (fts, ent);
              return RM_ERROR;
            }
        }

      {
        Ternary is_empty_directory;
        enum RM_status s = prompt (fts, ent, true /*is_dir*/, x,
                                   PA_DESCEND_INTO_DIR, &is_empty_directory);

        if (s == RM_OK && is_empty_directory == T_YES)
          {
            /* When we know (from prompt when in interactive mode)
               that this is an empty directory, don't prompt twice.  */
            s = excise (fts, ent, x, true);
            fts_skip_tree (fts, ent);
          }

        if (s != RM_OK)
          {
            mark_ancestor_dirs (ent);
            fts_skip_tree (fts, ent);
          }

        return s;
      }

    case FTS_F:			/* regular file */
    case FTS_NS:		/* stat(2) failed */
    case FTS_SL:		/* symbolic link */
    case FTS_SLNONE:		/* symbolic link without target */
    case FTS_DP:		/* postorder directory */
    case FTS_DNR:		/* unreadable directory */
    case FTS_NSOK:		/* e.g., dangling symlink */
    case FTS_DEFAULT:		/* none of the above */
      {
        /* With --one-file-system, do not attempt to remove a mount point.
           fts' FTS_XDEV ensures that we don't process any entries under
           the mount point.  */
        if (ent->fts_info == FTS_DP
            && x->one_file_system
            && FTS_ROOTLEVEL < ent->fts_level
            && ent->fts_statp->st_dev != fts->fts_dev)
          {
            mark_ancestor_dirs (ent);
            error (0, 0, _("skipping %s, since it's on a different device"),
                   quote (ent->fts_path));
            return RM_ERROR;
          }

        bool is_dir = ent->fts_info == FTS_DP || ent->fts_info == FTS_DNR;
        enum RM_status s = prompt (fts, ent, is_dir, x, PA_REMOVE_DIR, NULL);
        if (s != RM_OK)
          return s;
        return excise (fts, ent, x, is_dir);
      }

    case FTS_DC:		/* directory that causes cycles */
      emit_cycle_warning (ent->fts_path);
      fts_skip_tree (fts, ent);
      return RM_ERROR;

    case FTS_ERR:
      /* Various failures, from opendir to ENOMEM, to failure to "return"
         to preceding directory, can provoke this.  */
      error (0, ent->fts_errno, _("traversal failed: %s"),
             quote (ent->fts_path));
      fts_skip_tree (fts, ent);
      return RM_ERROR;

    default:
      error (0, 0, _("unexpected failure: fts_info=%d: %s\n"
                     "please report to %s"),
             ent->fts_info,
             quote (ent->fts_path),
             PACKAGE_BUGREPORT);
      abort ();
    }
}
Beispiel #10
0
struct urlpos *
append_url (const char *link_uri, int position, int size,
            struct map_context *ctx)
{
  int link_has_scheme = url_has_scheme (link_uri);
  struct urlpos *newel;
  const char *base = ctx->base ? ctx->base : ctx->parent_base;
  struct url *url;

  struct iri *iri = iri_new ();
  set_uri_encoding (iri, opt.locale, true);
  iri->utf8_encode = true;

  if (!base)
    {
      DEBUGP (("%s: no base, merge will use \"%s\".\n",
               ctx->document_file, link_uri));

      if (!link_has_scheme)
        {
          /* Base URL is unavailable, and the link does not have a
             location attached to it -- we have to give up.  Since
             this can only happen when using `--force-html -i', print
             a warning.  */
          logprintf (LOG_NOTQUIET,
                     _("%s: Cannot resolve incomplete link %s.\n"),
                     ctx->document_file, link_uri);
          return NULL;
        }

      url = url_parse (link_uri, NULL, iri, false);
      if (!url)
        {
          DEBUGP (("%s: link \"%s\" doesn't parse.\n",
                   ctx->document_file, link_uri));
          return NULL;
        }
    }
  else
    {
      /* Merge BASE with LINK_URI, but also make sure the result is
         canonicalized, i.e. that "../" have been resolved.
         (parse_url will do that for us.) */

      char *complete_uri = uri_merge (base, link_uri);

      DEBUGP (("%s: merge(%s, %s) -> %s\n",
               quotearg_n_style (0, escape_quoting_style, ctx->document_file),
               quote_n (1, base),
               quote_n (2, link_uri),
               quotearg_n_style (3, escape_quoting_style, complete_uri)));

      url = url_parse (complete_uri, NULL, iri, false);
      if (!url)
        {
          DEBUGP (("%s: merged link \"%s\" doesn't parse.\n",
                   ctx->document_file, complete_uri));
          xfree (complete_uri);
          return NULL;
        }
      xfree (complete_uri);
    }

  iri_free (iri);

  DEBUGP (("appending %s to urlpos.\n", quote (url->url)));

  newel = xnew0 (struct urlpos);
  newel->url = url;
  newel->pos = position;
  newel->size = size;

  /* A URL is relative if the host is not named, and the name does not
     start with `/'.  */
  if (!link_has_scheme && *link_uri != '/')
    newel->link_relative_p = 1;
  else if (link_has_scheme)
    newel->link_complete_p = 1;

  /* Append the new URL maintaining the order by position.  */
  if (ctx->head == NULL)
    ctx->head = newel;
  else
    {
      struct urlpos *it, *prev = NULL;

      it = ctx->head;
      while (it && position > it->pos)
        {
          prev = it;
          it = it->next;
        }

      newel->next = it;

      if (prev)
        prev->next = newel;
      else
        ctx->head = newel;
    }

  return newel;
}
Beispiel #11
0
bool
ssl_check_certificate (int fd, const char *host)
{
  X509 *cert;
  char common_name[256];
  long vresult;
  bool success = true;

  /* If the user has specified --no-check-cert, we still want to warn
     him about problems with the server's certificate.  */
  const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");

  struct openssl_transport_context *ctx = fd_transport_context (fd);
  SSL *conn = ctx->conn;
  assert (conn != NULL);

  cert = SSL_get_peer_certificate (conn);
  if (!cert)
    {
      logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
                 severity, quotearg_style (escape_quoting_style, host));
      success = false;
      goto no_cert;             /* must bail out since CERT is NULL */
    }

  IF_DEBUG
    {
      char *subject = X509_NAME_oneline (X509_get_subject_name (cert), 0, 0);
      char *issuer = X509_NAME_oneline (X509_get_issuer_name (cert), 0, 0);
      DEBUGP (("certificate:\n  subject: %s\n  issuer:  %s\n",
               quotearg_n_style (0, escape_quoting_style, subject),
               quotearg_n_style (1, escape_quoting_style, issuer)));
      OPENSSL_free (subject);
      OPENSSL_free (issuer);
    }

  vresult = SSL_get_verify_result (conn);
  if (vresult != X509_V_OK)
    {
      char *issuer = X509_NAME_oneline (X509_get_issuer_name (cert), 0, 0);
      logprintf (LOG_NOTQUIET,
                 _("%s: cannot verify %s's certificate, issued by %s:\n"),
                 severity, quotearg_n_style (0, escape_quoting_style, host),
                 quote_n (1, issuer));
      /* Try to print more user-friendly (and translated) messages for
         the frequent verification errors.  */
      switch (vresult)
        {
        case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
          logprintf (LOG_NOTQUIET,
                     _("  Unable to locally verify the issuer's authority.\n"));
          break;
        case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
        case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
          logprintf (LOG_NOTQUIET, _("  Self-signed certificate encountered.\n"));
          break;
        case X509_V_ERR_CERT_NOT_YET_VALID:
          logprintf (LOG_NOTQUIET, _("  Issued certificate not yet valid.\n"));
          break;
        case X509_V_ERR_CERT_HAS_EXPIRED:
          logprintf (LOG_NOTQUIET, _("  Issued certificate has expired.\n"));
          break;
        default:
          /* For the less frequent error strings, simply provide the
             OpenSSL error message.  */
          logprintf (LOG_NOTQUIET, "  %s\n",
                     X509_verify_cert_error_string (vresult));
        }
      success = false;
      /* Fall through, so that the user is warned about *all* issues
         with the cert (important with --no-check-certificate.)  */
    }

  /* Check that HOST matches the common name in the certificate.
     #### The following remains to be done:

     - It should use dNSName/ipAddress subjectAltName extensions if
       available; according to rfc2818: "If a subjectAltName extension
       of type dNSName is present, that MUST be used as the identity."

     - When matching against common names, it should loop over all
       common names and choose the most specific one, i.e. the last
       one, not the first one, which the current code picks.

     - Ensure that ASN1 strings from the certificate are encoded as
       UTF-8 which can be meaningfully compared to HOST.  */
  {
  X509_NAME *xname = X509_get_subject_name(cert);
  common_name[0] = '\0';
  X509_NAME_get_text_by_NID (xname, NID_commonName, common_name,
                             sizeof (common_name));

  if (!pattern_match (common_name, host))
    {
      logprintf (LOG_NOTQUIET, _("\
%s: certificate common name %s doesn't match requested host name %s.\n"),
                 severity, quote_n (0, common_name), quote_n (1, host));
      success = false;
    }
  else
    {
Beispiel #12
0
bool
ssl_check_certificate (int fd, const char *host)
{
  X509 *cert;
  GENERAL_NAMES *subjectAltNames;
  char common_name[256];
  long vresult;
  bool success = true;
  bool alt_name_checked = false;

  /* If the user has specified --no-check-cert, we still want to warn
     him about problems with the server's certificate.  */
  const char *severity = opt.check_cert ? _("ERROR") : _("WARNING");

  struct openssl_transport_context *ctx = fd_transport_context (fd);
  SSL *conn = ctx->conn;
  assert (conn != NULL);

  cert = SSL_get_peer_certificate (conn);
  if (!cert)
    {
      logprintf (LOG_NOTQUIET, _("%s: No certificate presented by %s.\n"),
                 severity, quotearg_style (escape_quoting_style, host));
      success = false;
      goto no_cert;             /* must bail out since CERT is NULL */
    }

  IF_DEBUG
    {
      char *subject = _get_rfc2253_formatted (X509_get_subject_name (cert));
      char *issuer = _get_rfc2253_formatted (X509_get_issuer_name (cert));
      DEBUGP (("certificate:\n  subject: %s\n  issuer:  %s\n",
               quotearg_n_style (0, escape_quoting_style, subject),
               quotearg_n_style (1, escape_quoting_style, issuer)));
      xfree (subject);
      xfree (issuer);
    }

  vresult = SSL_get_verify_result (conn);
  if (vresult != X509_V_OK)
    {
      char *issuer = _get_rfc2253_formatted (X509_get_issuer_name (cert));
      logprintf (LOG_NOTQUIET,
                 _("%s: cannot verify %s's certificate, issued by %s:\n"),
                 severity, quotearg_n_style (0, escape_quoting_style, host),
                 quote_n (1, issuer));
      xfree(issuer);

      /* Try to print more user-friendly (and translated) messages for
         the frequent verification errors.  */
      switch (vresult)
        {
        case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
          logprintf (LOG_NOTQUIET,
                     _("  Unable to locally verify the issuer's authority.\n"));
          break;
        case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
        case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
          logprintf (LOG_NOTQUIET,
                     _("  Self-signed certificate encountered.\n"));
          break;
        case X509_V_ERR_CERT_NOT_YET_VALID:
          logprintf (LOG_NOTQUIET, _("  Issued certificate not yet valid.\n"));
          break;
        case X509_V_ERR_CERT_HAS_EXPIRED:
          logprintf (LOG_NOTQUIET, _("  Issued certificate has expired.\n"));
          break;
        default:
          /* For the less frequent error strings, simply provide the
             OpenSSL error message.  */
          logprintf (LOG_NOTQUIET, "  %s\n",
                     X509_verify_cert_error_string (vresult));
        }
      success = false;
      /* Fall through, so that the user is warned about *all* issues
         with the cert (important with --no-check-certificate.)  */
    }

  /* Check that HOST matches the common name in the certificate.
     #### The following remains to be done:

     - When matching against common names, it should loop over all
       common names and choose the most specific one, i.e. the last
       one, not the first one, which the current code picks.

     - Ensure that ASN1 strings from the certificate are encoded as
       UTF-8 which can be meaningfully compared to HOST.  */

  subjectAltNames = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL);

  if (subjectAltNames)
    {
      /* Test subject alternative names */

      /* Do we want to check for dNSNAmes or ipAddresses (see RFC 2818)?
       * Signal it by host_in_octet_string. */
      ASN1_OCTET_STRING *host_in_octet_string = a2i_IPADDRESS (host);

      int numaltnames = sk_GENERAL_NAME_num (subjectAltNames);
      int i;
      for (i=0; i < numaltnames; i++)
        {
          const GENERAL_NAME *name =
            sk_GENERAL_NAME_value (subjectAltNames, i);
          if (name)
            {
              if (host_in_octet_string)
                {
                  if (name->type == GEN_IPADD)
                    {
                      /* Check for ipAddress */
                      /* TODO: Should we convert between IPv4-mapped IPv6
                       * addresses and IPv4 addresses? */
                      alt_name_checked = true;
                      if (!ASN1_STRING_cmp (host_in_octet_string,
                            name->d.iPAddress))
                        break;
                    }
                }
              else if (name->type == GEN_DNS)
                {
                  /* dNSName should be IA5String (i.e. ASCII), however who
                   * does trust CA? Convert it into UTF-8 for sure. */
                  unsigned char *name_in_utf8 = NULL;

                  /* Check for dNSName */
                  alt_name_checked = true;

                  if (0 <= ASN1_STRING_to_UTF8 (&name_in_utf8, name->d.dNSName))
                    {
                      /* Compare and check for NULL attack in ASN1_STRING */
                      if (pattern_match ((char *)name_in_utf8, host) &&
                            (strlen ((char *)name_in_utf8) ==
                                (size_t) ASN1_STRING_length (name->d.dNSName)))
                        {
                          OPENSSL_free (name_in_utf8);
                          break;
                        }
                      OPENSSL_free (name_in_utf8);
                    }
                }
            }
        }
      sk_GENERAL_NAME_pop_free(subjectAltNames, GENERAL_NAME_free);
      if (host_in_octet_string)
        ASN1_OCTET_STRING_free(host_in_octet_string);

      if (alt_name_checked == true && i >= numaltnames)
        {
          logprintf (LOG_NOTQUIET,
              _("%s: no certificate subject alternative name matches\n"
                "\trequested host name %s.\n"),
                     severity, quote_n (1, host));
          success = false;
        }
    }

  if (alt_name_checked == false)
    {
      /* Test commomName */
      X509_NAME *xname = X509_get_subject_name(cert);
      common_name[0] = '\0';
      X509_NAME_get_text_by_NID (xname, NID_commonName, common_name,
                                 sizeof (common_name));

      if (!pattern_match (common_name, host))
        {
          logprintf (LOG_NOTQUIET, _("\
    %s: certificate common name %s doesn't match requested host name %s.\n"),
                     severity, quote_n (0, common_name), quote_n (1, host));
          success = false;
        }
Beispiel #13
0
static void
user_result_fd_readable(DCUserConn *uc)
{
    int res;

    res = msgq_read(uc->get_mq);
    if (res == 0 || (res < 0 && errno != EAGAIN)) {
        warn_socket_error(res, false, _("user process %s"), quote(uc->name));
        user_disconnect(uc); /* MSG: socket error above */
        return;
    }
    while (msgq_has_complete_msg(uc->get_mq)) {
        int id;

        msgq_peek(uc->get_mq, MSGQ_INT, &id, MSGQ_END);
        switch (id) {
        case DC_MSG_SCREEN_PUT: {
            char *msg;
            uint32_t flag;

            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_INT32, &flag, MSGQ_STR, &msg, MSGQ_END);
            flag_putf((DCDisplayFlag)flag, _("User %s: %s"), quotearg(uc->name), msg); /* msg already quoted */
            free(msg);
            break;
        }
        case DC_MSG_WANT_DOWNLOAD: {
            bool reply;

            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_END);
            reply = !has_user_conn(uc->info, DC_DIR_RECEIVE)
                    && (uc->queue_pos < uc->info->download_queue->cur);
            msgq_put(uc->put_mq, MSGQ_BOOL, reply, MSGQ_END);
            FD_SET(uc->put_mq->fd, &write_fds);
            break;
        }
        case DC_MSG_VALIDATE_DIR: {
            DCTransferDirection dir;
            bool reply;

            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_INT, &dir, MSGQ_END);
            reply = validate_direction(uc, dir);
            msgq_put(uc->put_mq, MSGQ_BOOL, reply, MSGQ_END);
            FD_SET(uc->put_mq->fd, &write_fds);
            break;
        }
        case DC_MSG_VALIDATE_NICK: {
            char *nick;
            bool reply;

            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_STR, &nick, MSGQ_END);
            reply = validate_nick(uc, nick);
            free(nick);
            if (uc->info != NULL) {
                /* The user can only be DC_ACTIVE_SENT_PASSIVE if we are passive.
                 * So if we managed to connect to them even though we were passive,
                 * they must be active.
                 */
                if (uc->info->active_state == DC_ACTIVE_SENT_PASSIVE)
                    uc->info->active_state = DC_ACTIVE_KNOWN_ACTIVE;
                /* The user can only be DC_ACTIVE_SENT_ACTIVE if we are active.
                 * We must set to DC_ACTIVE_UNKNOWN because otherwise
                 * hub.c:hub_connect_user might say "ConnectToMe already sent to
                 * user" next time we're trying to connect to them.
                 */
                if (uc->info->active_state == DC_ACTIVE_SENT_ACTIVE)
                    uc->info->active_state = DC_ACTIVE_UNKNOWN;
            }
            msgq_put(uc->put_mq, MSGQ_BOOL, reply, MSGQ_END);
            FD_SET(uc->put_mq->fd, &write_fds);
            break;
        }
        case DC_MSG_GET_MY_NICK:
            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_END);
            msgq_put(uc->put_mq, MSGQ_STR, my_nick, MSGQ_END);
            FD_SET(uc->put_mq->fd, &write_fds);
            break;
        case DC_MSG_TRANSFER_STATUS:
            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_INT64, &uc->transfer_pos, MSGQ_END);
            break;
        case DC_MSG_TRANSFER_START: {
            char *share_filename, *local_filename;

            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_STR, &local_filename, MSGQ_STR, &share_filename, MSGQ_INT64
                , &uc->transfer_start, MSGQ_INT64, &uc->transfer_total, MSGQ_END);
            if (uc->transfer_start != 0) {
                flag_putf(DC_DF_CONNECTIONS, _("%s: Starting %s of %s (%" PRIu64 " of %" PRIu64 " %s).\n"),
                          quotearg_n(0, uc->info->nick),
                          uc->dir == DC_DIR_SEND ? _("upload") : _("download"),
                          quote_n(1, base_name(share_filename)),
                          uc->transfer_total - uc->transfer_start,
                          uc->transfer_total,
                          ngettext("byte", "bytes", uc->transfer_total));
            } else {
                flag_putf(DC_DF_CONNECTIONS, _("%s: Starting %s of %s (%" PRIu64 " %s).\n"),
                          quotearg_n(0, uc->info->nick),
                          uc->dir == DC_DIR_SEND ? _("upload") : _("download"),
                          quote_n(1, base_name(share_filename)),
                          uc->transfer_total,
                          ngettext("byte", "bytes", uc->transfer_total));
            }
            free(uc->transfer_file);
            uc->transfer_file = share_filename;
            free(uc->local_file);
            uc->local_file = local_filename;
            uc->transferring = true;
            uc->transfer_pos = uc->transfer_start;
            uc->transfer_time = time(NULL);
            if (uc->transfer_time == (time_t) -1)
                warn(_("Cannot get current time - %s\n"), errstr);
            break;
        }
        case DC_MSG_CHECK_DOWNLOAD:
            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_END);
            for (; uc->queue_pos < uc->info->download_queue->cur; uc->queue_pos++) {
                DCQueuedFile *queued;
                char *local_file;

                queued = (DCQueuedFile*) uc->info->download_queue->buf[uc->queue_pos];
                if (queued->status != DC_QS_DONE) {
                    local_file = resolve_download_file(uc->info, queued);
                    uc->queued_valid = true;
                    uc->transfer_file = xstrdup(queued->filename);
                    uc->local_file = xstrdup(local_file);
                    uc->occupied_slot = true;
                    queued->status = DC_QS_PROCESSING;
                    used_dl_slots++;
                    msgq_put(uc->put_mq, MSGQ_STR, local_file, MSGQ_STR, uc->transfer_file, MSGQ_INT64, queued->length, MSGQ_INT, queued->flag, MSGQ_END);
                    FD_SET(uc->put_mq->fd, &write_fds);
                    free(local_file);
                    return;
                }
            }
            msgq_put(uc->put_mq, MSGQ_STR, NULL, MSGQ_STR, NULL, MSGQ_INT64, (uint64_t) 0, MSGQ_INT, DC_TF_NORMAL, MSGQ_END);
            FD_SET(uc->put_mq->fd, &write_fds);
            break;
        case DC_MSG_DOWNLOAD_ENDED: {
            bool success;
            char *reason;

            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_BOOL, &success, MSGQ_STR, &reason, MSGQ_END);
            handle_ended_download(uc, success, reason);
            free(reason);
            break;
        }
        case DC_MSG_CHECK_UPLOAD: {
            char *remote_file;
            char *local_file;
            int type;
            uint64_t size = 0;
            DCTransferFlag flag = DC_TF_NORMAL;
            bool permit_transfer = false;

            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_INT, &type, MSGQ_STR, &remote_file, MSGQ_END);
            local_file = (char*) resolve_upload_file(uc->info, (DCAdcgetType) type, remote_file, &flag, &size);
            free(remote_file);
            uc->transfer_file = NULL;
            if (local_file != NULL) {
                if (flag == DC_TF_LIST || (flag == DC_TF_NORMAL && size <= minislot_size)) {
                    if (used_mini_slots < minislot_count) {
                        used_mini_slots ++;
                        uc->occupied_minislot = true;
                        permit_transfer = true;
                    } else if (used_ul_slots < my_ul_slots || uc->info->slot_granted) {
                        used_ul_slots ++;
                        uc->occupied_slot = true;
                        permit_transfer = true;
                    }
                } else if (flag == DC_TF_NORMAL && size > minislot_size) {
                    if (used_ul_slots < my_ul_slots || uc->info->slot_granted) {
                        used_ul_slots ++;
                        uc->occupied_slot = true;
                        permit_transfer = true;
                    }
                }
                if (permit_transfer) {
                    uc->transfer_file = local_file;
                } else {
                    free(local_file);
                    local_file = NULL;
                }
            } else {
                permit_transfer = true;
            }
            msgq_put(uc->put_mq, MSGQ_BOOL, permit_transfer, MSGQ_STR, local_file, MSGQ_END);
            FD_SET(uc->put_mq->fd, &write_fds);
            break;
        }
        case DC_MSG_UPLOAD_ENDED: {
            bool success;
            char *reason;

            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_BOOL, &success, MSGQ_STR, &reason, MSGQ_END);
            handle_ended_upload(uc, success, reason);
            free(reason);
            break;
        }
        case DC_MSG_TERMINATING:
            /* The TERMINATING message will be sent from users when they're about to
             * shut down properly.
            * XXX: This message may contain the reason it was terminated in the future.
            */
            msgq_get(uc->get_mq, MSGQ_INT, &id, MSGQ_END);
            user_disconnect(uc); /* MSG: reason from DC_MSG_TERMINATING */
            return; /* not break! this is the last message. */
        default:
            warn(_("Received unknown message %d from user process, shutting down process.\n"), id);
            user_disconnect(uc); /* MSG: local communication error? */
            return; /* not break! this is the last message. */
        }
    }
}
Beispiel #14
0
static void
handle_ended_download(DCUserConn *uc, bool success, const char *reason)
{
    bytes_received += uc->transfer_pos - uc->transfer_start;
    if (uc->occupied_slot) {
        used_dl_slots--;
        uc->occupied_slot = false;
    }
    /* If we removed from the queued item during download,
     * queue_pos will point outside the queue. */
    if (uc->queued_valid) {
        DCQueuedFile *queued;

        /*
        queued = uc->info->download_queue->buf[uc->queue_pos];
        uc->queue_pos++;
        */
        queued = (DCQueuedFile*) ptrv_remove(uc->info->download_queue, uc->queue_pos);
        uc->queued_valid = false;
        if (success) {
            queued->status = DC_QS_DONE;
            if (queued->flag == DC_TF_LIST) {
                if (uc->local_file != NULL) {
                    ptrv_append(delete_files, xstrdup(uc->local_file));
                }
                if (browse_user != NULL && strcmp(browse_user->nick, uc->info->nick) == 0 && browse_list == NULL) {
                    add_parse_request(browse_list_parsed, uc->local_file, xstrdup(browse_user->nick));
                }
            } else {
                char *final_file = xstrndup(uc->local_file, strlen(uc->local_file)-5);
                if (safe_rename(uc->local_file, final_file) != 0) {
                    warn(_("%s: Cannot rename file to %s - %s\n"), quotearg_n(0, uc->local_file), quote_n(1, final_file), errstr);
                    reason = _("cannot rename file"); /* XXX: would like a more elaborate error message here perhaps? */
                    queued->status = DC_QS_ERROR;
                    success = false;
                }
                free(final_file);
            }
        } else {
            queued->status = DC_QS_ERROR;
        }
        display_transfer_ended_msg(false, uc, success, " (%s)", reason);
    } else {
        display_transfer_ended_msg(false, uc, success, " but unqueued (%s)", reason);
    }
    free(uc->transfer_file);
    free(uc->local_file);
    uc->transfer_file = NULL;
    uc->local_file = NULL;
    uc->transfer_start = 0;
    uc->transfer_pos = 0;
    uc->transferring = false;
}
Beispiel #15
0
/* Return an unambiguous printable representation of NAME,
   suitable for diagnostics.  */
char const *
quote (char const *name)
{
  return quote_n (0, name);
}
Beispiel #16
0
static int
copy_internal (const char *src_path, const char *dst_path,
	       int new_dst,
	       dev_t device,
	       struct dir_list *ancestors,
	       const struct cp_options *x,
	       int command_line_arg,
	       int *copy_into_self,
	       int *rename_succeeded)
{
  struct stat src_sb;
  struct stat dst_sb;
  mode_t src_mode;
  mode_t src_type;
  char *earlier_file = NULL;
  char *dst_backup = NULL;
  int backup_succeeded = 0;
  int delayed_fail;
  int copied_as_regular = 0;
  int ran_chown = 0;
  int preserve_metadata;

  if (x->move_mode && rename_succeeded)
    *rename_succeeded = 0;

  *copy_into_self = 0;
  if ((*(x->xstat)) (src_path, &src_sb))
    {
      error (0, errno, _("cannot stat %s"), quote (src_path));
      return 1;
    }

  src_type = src_sb.st_mode;

  src_mode = src_sb.st_mode;

  if (S_ISDIR (src_type) && !x->recursive)
    {
      error (0, 0, _("omitting directory %s"), quote (src_path));
      return 1;
    }

  /* Detect the case in which the same source file appears more than
     once on the command line and no backup option has been selected.
     If so, simply warn and don't copy it the second time.
     This check is enabled only if x->src_info is non-NULL.  */
  if (command_line_arg)
    {
      if ( ! S_ISDIR (src_sb.st_mode)
	   && x->backup_type == none
	   && seen_file (x->src_info, src_path, &src_sb))
	{
	  error (0, 0, _("warning: source file %s specified more than once"),
		 quote (src_path));
	  return 0;
	}

      record_file (x->src_info, src_path, &src_sb);
    }

  if (!new_dst)
    {
      if ((*(x->xstat)) (dst_path, &dst_sb))
	{
	  if (errno != ENOENT)
	    {
	      error (0, errno, _("cannot stat %s"), quote (dst_path));
	      return 1;
	    }
	  else
	    {
	      new_dst = 1;
	    }
	}
      else
	{
	  int return_now;
	  int ok = same_file_ok (src_path, &src_sb, dst_path, &dst_sb,
				 x, &return_now);
	  if (return_now)
	    return 0;

	  if (! ok)
	    {
	      error (0, 0, _("%s and %s are the same file"),
		     quote_n (0, src_path), quote_n (1, dst_path));
	      return 1;
	    }

	  if (!S_ISDIR (dst_sb.st_mode))
	    {
	      if (S_ISDIR (src_type))
		{
		  error (0, 0,
		     _("cannot overwrite non-directory %s with directory %s"),
			 quote_n (0, dst_path), quote_n (1, src_path));
		  return 1;
		}

	      /* Don't let the user destroy their data, even if they try hard:
		 This mv command must fail (likewise for cp):
		   rm -rf a b c; mkdir a b c; touch a/f b/f; mv a/f b/f c
		 Otherwise, the contents of b/f would be lost.
		 In the case of `cp', b/f would be lost if the user simulated
		 a move using cp and rm.
		 Note that it works fine if you use --backup=numbered.  */
	      if (command_line_arg
		  && x->backup_type != numbered
		  && seen_file (x->dest_info, dst_path, &dst_sb))
		{
		  error (0, 0,
			 _("will not overwrite just-created %s with %s"),
			 quote_n (0, dst_path), quote_n (1, src_path));
		  return 1;
		}
	    }

	  if (!S_ISDIR (src_type))
	    {
	      if (S_ISDIR (dst_sb.st_mode))
		{
		  error (0, 0,
		       _("cannot overwrite directory %s with non-directory"),
			 quote (dst_path));
		  return 1;
		}

	      if (x->update && MTIME_CMP (src_sb, dst_sb) <= 0)
		{
		  /* We're using --update and the source file is older
		     than the destination file, so there is no need to
		     copy or move.  */
		  /* Pretend the rename succeeded, so the caller (mv)
		     doesn't end up removing the source file.  */
		  if (rename_succeeded)
		    *rename_succeeded = 1;
		  return 0;
		}
	    }

	  /* When there is an existing destination file, we may end up
	     returning early, and hence not copying/moving the file.
	     This may be due to an interactive `negative' reply to the
	     prompt about the existing file.  It may also be due to the
	     use of the --reply=no option.  */
	  if (!S_ISDIR (src_type))
	    {
	      /* cp and mv treat -i and -f differently.  */
	      if (x->move_mode)
		{
		  if ((x->interactive == I_ALWAYS_NO
		       && UNWRITABLE (dst_path, dst_sb.st_mode))
		      || ((x->interactive == I_ASK_USER
			   || (x->interactive == I_UNSPECIFIED
			       && x->stdin_tty
			       && UNWRITABLE (dst_path, dst_sb.st_mode)))
			  && (overwrite_prompt (dst_path, &dst_sb), 1)
			  && ! yesno ()))
		    {
		      /* Pretend the rename succeeded, so the caller (mv)
			 doesn't end up removing the source file.  */
		      if (rename_succeeded)
			*rename_succeeded = 1;
		      return 0;
		    }
		}
	      else
		{
		  if (x->interactive == I_ALWAYS_NO
		      || (x->interactive == I_ASK_USER
			  && (overwrite_prompt (dst_path, &dst_sb), 1)
			  && ! yesno ()))
		    {
		      return 0;
		    }
		}
	    }

	  if (x->move_mode)
	    {
	      /* In move_mode, DEST may not be an existing directory.  */
	      if (S_ISDIR (dst_sb.st_mode))
		{
		  error (0, 0, _("cannot overwrite directory %s"),
			 quote (dst_path));
		  return 1;
		}

	      /* Don't allow user to move a directory onto a non-directory.  */
	      if (S_ISDIR (src_sb.st_mode) && !S_ISDIR (dst_sb.st_mode))
		{
		  error (0, 0,
		       _("cannot move directory onto non-directory: %s -> %s"),
			 quote_n (0, src_path), quote_n (0, dst_path));
		  return 1;
		}
	    }

	  if (x->backup_type != none && !S_ISDIR (dst_sb.st_mode))
	    {
	      char *tmp_backup = find_backup_file_name (dst_path,
							x->backup_type);
	      if (tmp_backup == NULL)
		xalloc_die ();

	      /* Detect (and fail) when creating the backup file would
		 destroy the source file.  Before, running the commands
		 cd /tmp; rm -f a a~; : > a; echo A > a~; cp --b=simple a~ a
		 would leave two zero-length files: a and a~.  */
	      /* FIXME: but simply change e.g., the final a~ to `./a~'
		 and the source will still be destroyed.  */
	      if (STREQ (tmp_backup, src_path))
		{
		  const char *fmt;
		  fmt = (x->move_mode
		 ? _("backing up %s would destroy source;  %s not moved")
		 : _("backing up %s would destroy source;  %s not copied"));
		  error (0, 0, fmt,
			 quote_n (0, dst_path),
			 quote_n (1, src_path));
		  free (tmp_backup);
		  return 1;
		}

	      dst_backup = (char *) alloca (strlen (tmp_backup) + 1);
	      strcpy (dst_backup, tmp_backup);
	      free (tmp_backup);
	      if (rename (dst_path, dst_backup))
		{
		  if (errno != ENOENT)
		    {
		      error (0, errno, _("cannot backup %s"), quote (dst_path));
		      return 1;
		    }
		  else
		    {
		      dst_backup = NULL;
		    }
		}
	      else
		{
		  backup_succeeded = 1;
		}
	      new_dst = 1;
	    }
	  else if (! S_ISDIR (dst_sb.st_mode)
		   && (x->unlink_dest_before_opening
		       || (x->xstat == lstat
			   && ! S_ISREG (src_sb.st_mode))))
	    {
	      if (unlink (dst_path) && errno != ENOENT)
		{
		  error (0, errno, _("cannot remove %s"), quote (dst_path));
		  return 1;
		}
	      new_dst = 1;
	    }
	}
    }

  /* If the source is a directory, we don't always create the destination
     directory.  So --verbose should not announce anything until we're
     sure we'll create a directory. */
  if (x->verbose && !S_ISDIR (src_type))
    {
      printf ("%s -> %s", quote_n (0, src_path), quote_n (1, dst_path));
      if (backup_succeeded)
	printf (_(" (backup: %s)"), quote (dst_backup));
      putchar ('\n');
    }

  /* Associate the destination path with the source device and inode
     so that if we encounter a matching dev/ino pair in the source tree
     we can arrange to create a hard link between the corresponding names
     in the destination tree.

     Sometimes, when preserving links, we have to record dev/ino even
     though st_nlink == 1:
     - when using -H and processing a command line argument;
	that command line argument could be a symlink pointing to another
	command line argument.  With `cp -H --preserve=link', we hard-link
	those two destination files.
     - likewise for -L except that it applies to all files, not just
	command line arguments.

     Also record directory dev/ino when using --recursive.  We'll use that
     info to detect this problem: cp -R dir dir.  FIXME-maybe: ideally,
     directory info would be recorded in a separate hash table, since
     such entries are useful only while a single command line hierarchy
     is being copied -- so that separate table could be cleared between
     command line args.  Using the same hash table to preserve hard
     links means that it may not be cleared.  */

  if ((x->preserve_links
       && (1 < src_sb.st_nlink
	   || (command_line_arg
	       && x->dereference == DEREF_COMMAND_LINE_ARGUMENTS)
	   || x->dereference == DEREF_ALWAYS))
      || (x->recursive && S_ISDIR (src_type)))
    {
      earlier_file = remember_copied (dst_path, src_sb.st_ino, src_sb.st_dev);
    }

  /* Did we copy this inode somewhere else (in this command line argument)
     and therefore this is a second hard link to the inode?  */

  if (earlier_file)
    {
      /* Avoid damaging the destination filesystem by refusing to preserve
	 hard-linked directories (which are found at least in Netapp snapshot
	 directories).  */
      if (S_ISDIR (src_type))
	{
	  /* If src_path and earlier_file refer to the same directory entry,
	     then warn about copying a directory into itself.  */
	  if (same_name (src_path, earlier_file))
	    {
	      error (0, 0, _("cannot copy a directory, %s, into itself, %s"),
		     quote_n (0, top_level_src_path),
		     quote_n (1, top_level_dst_path));
	      *copy_into_self = 1;
	    }
	  else
	    {
	      error (0, 0, _("will not create hard link %s to directory %s"),
		     quote_n (0, dst_path), quote_n (1, earlier_file));
	    }

	  goto un_backup;
	}

      {
	int link_failed;

	link_failed = link (earlier_file, dst_path);

	/* If the link failed because of an existing destination,
	   remove that file and then call link again.  */
	if (link_failed && errno == EEXIST)
	  {
	    if (unlink (dst_path))
	      {
		error (0, errno, _("cannot remove %s"), quote (dst_path));
		goto un_backup;
	      }
	    link_failed = link (earlier_file, dst_path);
	  }

	if (link_failed)
	  {
	    error (0, errno, _("cannot create hard link %s to %s"),
		   quote_n (0, dst_path), quote_n (1, earlier_file));
	    goto un_backup;
	  }

	return 0;
      }
    }

  if (x->move_mode)
    {
      if (rename (src_path, dst_path) == 0)
	{
	  if (x->verbose && S_ISDIR (src_type))
	    printf ("%s -> %s\n", quote_n (0, src_path), quote_n (1, dst_path));
	  if (rename_succeeded)
	    *rename_succeeded = 1;

	  if (command_line_arg)
	    {
	      /* Record destination dev/ino/filename, so that if we are asked
		 to overwrite that file again, we can detect it and fail.  */
	      /* It's fine to use the _source_ stat buffer (src_sb) to get the
	         _destination_ dev/ino, since the rename above can't have
		 changed those, and `mv' always uses lstat.
		 We could limit it further by operating
		 only on non-directories.  */
	      record_file (x->dest_info, dst_path, &src_sb);
	    }

	  return 0;
	}

      /* FIXME: someday, consider what to do when moving a directory into
	 itself but when source and destination are on different devices.  */

      /* This happens when attempting to rename a directory to a
	 subdirectory of itself.  */
      if (errno == EINVAL

	  /* When src_path is on an NFS file system, some types of
	     clients, e.g., SunOS4.1.4 and IRIX-5.3, set errno to EIO
	     instead.  Testing for this here risks misinterpreting a real
	     I/O error as an attempt to move a directory into itself, so
	     FIXME: consider not doing this.  */
	  || errno == EIO

	  /* And with SunOS-4.1.4 client and OpenBSD-2.3 server,
	     we get ENOTEMPTY.  */
	  || errno == ENOTEMPTY)
	{
	  /* FIXME: this is a little fragile in that it relies on rename(2)
	     failing with a specific errno value.  Expect problems on
	     non-POSIX systems.  */
	  error (0, 0, _("cannot move %s to a subdirectory of itself, %s"),
		 quote_n (0, top_level_src_path),
		 quote_n (1, top_level_dst_path));

	  /* Note that there is no need to call forget_created here,
	     (compare with the other calls in this file) since the
	     destination directory didn't exist before.  */

	  *copy_into_self = 1;
	  /* FIXME-cleanup: Don't return zero here; adjust mv.c accordingly.
	     The only caller that uses this code (mv.c) ends up setting its
	     exit status to nonzero when copy_into_self is nonzero.  */
	  return 0;
	}

      /* WARNING: there probably exist systems for which an inter-device
	 rename fails with a value of errno not handled here.
	 If/as those are reported, add them to the condition below.
	 If this happens to you, please do the following and send the output
	 to the bug-reporting address (e.g., in the output of cp --help):
	   touch k; perl -e 'rename "k","/tmp/k" or print "$!(",$!+0,")\n"'
	 where your current directory is on one partion and /tmp is the other.
	 Also, please try to find the E* errno macro name corresponding to
	 the diagnostic and parenthesized integer, and include that in your
	 e-mail.  One way to do that is to run a command like this
	   find /usr/include/. -type f \
	     | xargs grep 'define.*\<E[A-Z]*\>.*\<18\>' /dev/null
	 where you'd replace `18' with the integer in parentheses that
	 was output from the perl one-liner above.
	 If necessary, of course, change `/tmp' to some other directory.  */
      if (errno != EXDEV)
	{
	  /* There are many ways this can happen due to a race condition.
	     When something happens between the initial xstat and the
	     subsequent rename, we can get many different types of errors.
	     For example, if the destination is initially a non-directory
	     or non-existent, but it is created as a directory, the rename
	     fails.  If two `mv' commands try to rename the same file at
	     about the same time, one will succeed and the other will fail.
	     If the permissions on the directory containing the source or
	     destination file are made too restrictive, the rename will
	     fail.  Etc.  */
	  error (0, errno,
		 _("cannot move %s to %s"),
		 quote_n (0, src_path), quote_n (1, dst_path));
	  forget_created (src_sb.st_ino, src_sb.st_dev);
	  return 1;
	}

      /* The rename attempt has failed.  Remove any existing destination
	 file so that a cross-device `mv' acts as if it were really using
	 the rename syscall.  */
      if (unlink (dst_path) && errno != ENOENT)
	{
	  error (0, errno,
	     _("inter-device move failed: %s to %s; unable to remove target"),
		 quote_n (0, src_path), quote_n (1, dst_path));
	  forget_created (src_sb.st_ino, src_sb.st_dev);
	  return 1;
	}

      new_dst = 1;
    }

  delayed_fail = 0;

  /* In certain modes (cp's --symbolic-link), and for certain file types
     (symlinks and hard links) it doesn't make sense to preserve metadata,
     or it's possible to preserve only some of it.
     In such cases, set this variable to zero.  */
  preserve_metadata = 1;

  if (S_ISDIR (src_type))
    {
      struct dir_list *dir;

      /* If this directory has been copied before during the
         recursion, there is a symbolic link to an ancestor
         directory of the symbolic link.  It is impossible to
         continue to copy this, unless we've got an infinite disk.  */

      if (is_ancestor (&src_sb, ancestors))
	{
	  error (0, 0, _("cannot copy cyclic symbolic link %s"),
		 quote (src_path));
	  goto un_backup;
	}

      /* Insert the current directory in the list of parents.  */

      dir = (struct dir_list *) alloca (sizeof (struct dir_list));
      dir->parent = ancestors;
      dir->ino = src_sb.st_ino;
      dir->dev = src_sb.st_dev;

      if (new_dst || !S_ISDIR (dst_sb.st_mode))
	{
	  /* Create the new directory writable and searchable, so
             we can create new entries in it.  */

	  if (mkdir (dst_path, (src_mode & x->umask_kill) | S_IRWXU))
	    {
	      error (0, errno, _("cannot create directory %s"),
		     quote (dst_path));
	      goto un_backup;
	    }

	  /* Insert the created directory's inode and device
             numbers into the search structure, so that we can
             avoid copying it again.  */

	  if (remember_created (dst_path))
	    goto un_backup;

	  if (x->verbose)
	    printf ("%s -> %s\n", quote_n (0, src_path), quote_n (1, dst_path));
	}

      /* Are we crossing a file system boundary?  */
      if (x->one_file_system && device != 0 && device != src_sb.st_dev)
	return 0;

      /* Copy the contents of the directory.  */

      if (copy_dir (src_path, dst_path, new_dst, &src_sb, dir, x,
		    copy_into_self))
	{
	  /* Don't just return here -- otherwise, the failure to read a
	     single file in a source directory would cause the containing
	     destination directory not to have owner/perms set properly.  */
	  delayed_fail = 1;
	}
    }
#ifdef S_ISLNK
  else if (x->symbolic_link)
    {
      preserve_metadata = 0;

      if (*src_path != '/')
	{
	  /* Check that DST_PATH denotes a file in the current directory.  */
	  struct stat dot_sb;
	  struct stat dst_parent_sb;
	  char *dst_parent;
	  int in_current_dir;

	  dst_parent = dir_name (dst_path);

	  in_current_dir = (STREQ (".", dst_parent)
			    /* If either stat call fails, it's ok not to report
			       the failure and say dst_path is in the current
			       directory.  Other things will fail later.  */
			    || stat (".", &dot_sb)
			    || stat (dst_parent, &dst_parent_sb)
			    || SAME_INODE (dot_sb, dst_parent_sb));
	  free (dst_parent);

	  if (! in_current_dir)
	    {
	      error (0, 0,
	   _("%s: can make relative symbolic links only in current directory"),
		     quote (dst_path));
	      goto un_backup;
	    }
	}
      if (symlink (src_path, dst_path))
	{
	  error (0, errno, _("cannot create symbolic link %s to %s"),
		 quote_n (0, dst_path), quote_n (1, src_path));
	  goto un_backup;
	}
    }
#endif
  else if (x->hard_link)
    {
      preserve_metadata = 0;
      if (link (src_path, dst_path))
	{
	  error (0, errno, _("cannot create link %s"), quote (dst_path));
	  goto un_backup;
	}
    }
  else if (S_ISREG (src_type)
	   || (x->copy_as_regular && !S_ISDIR (src_type)
#ifdef S_ISLNK
	       && !S_ISLNK (src_type)
#endif
	       ))
    {
      copied_as_regular = 1;
      /* POSIX says the permission bits of the source file must be
	 used as the 3rd argument in the open call, but that's not consistent
	 with historical practice.  */
      if (copy_reg (src_path, dst_path, x,
		    get_dest_mode (x, src_mode), &new_dst, &src_sb))
	goto un_backup;
    }
  else
#ifdef S_ISFIFO
  if (S_ISFIFO (src_type))
    {
      if (mkfifo (dst_path, get_dest_mode (x, src_mode)))
	{
	  error (0, errno, _("cannot create fifo %s"), quote (dst_path));
	  goto un_backup;
	}
    }
  else
#endif
    if (S_ISBLK (src_type) || S_ISCHR (src_type)
#ifdef S_ISSOCK
	|| S_ISSOCK (src_type)
#endif
	)
    {
      if (mknod (dst_path, get_dest_mode (x, src_mode), src_sb.st_rdev))
	{
	  error (0, errno, _("cannot create special file %s"),
		 quote (dst_path));
	  goto un_backup;
	}
    }
  else
#ifdef S_ISLNK
  if (S_ISLNK (src_type))
    {
      char *src_link_val = xreadlink (src_path);
      if (src_link_val == NULL)
	{
	  error (0, errno, _("cannot read symbolic link %s"), quote (src_path));
	  goto un_backup;
	}

      if (!symlink (src_link_val, dst_path))
	free (src_link_val);
      else
	{
	  int saved_errno = errno;
	  int same_link = 0;
	  if (x->update && !new_dst && S_ISLNK (dst_sb.st_mode))
	    {
	      /* See if the destination is already the desired symlink.  */
	      size_t src_link_len = strlen (src_link_val);
	      char *dest_link_val = (char *) alloca (src_link_len + 1);
	      int dest_link_len = readlink (dst_path, dest_link_val,
					    src_link_len + 1);
	      if ((size_t) dest_link_len == src_link_len
		  && strncmp (dest_link_val, src_link_val, src_link_len) == 0)
		same_link = 1;
	    }
	  free (src_link_val);

	  if (! same_link)
	    {
	      error (0, saved_errno, _("cannot create symbolic link %s"),
		     quote (dst_path));
	      goto un_backup;
	    }
	}

      /* There's no need to preserve timestamps or permissions.  */
      preserve_metadata = 0;

      if (x->preserve_ownership)
	{
	  /* Preserve the owner and group of the just-`copied'
	     symbolic link, if possible.  */
# if HAVE_LCHOWN
	  if (DO_CHOWN (lchown, dst_path, src_sb.st_uid, src_sb.st_gid))
	    {
	      error (0, errno, _("failed to preserve ownership for %s"),
		     dst_path);
	      goto un_backup;
	    }
# else
	  /* Can't preserve ownership of symlinks.
	     FIXME: maybe give a warning or even error for symlinks
	     in directories with the sticky bit set -- there, not
	     preserving owner/group is a potential security problem.  */
# endif
	}
    }
  else
#endif
    {
      error (0, 0, _("%s has unknown file type"), quote (src_path));
      goto un_backup;
    }

  if (command_line_arg)
    record_file (x->dest_info, dst_path, NULL);

  if ( ! preserve_metadata)
    return 0;

  /* POSIX says that `cp -p' must restore the following:
     - permission bits
     - setuid, setgid bits
     - owner and group
     If it fails to restore any of those, we may give a warning but
     the destination must not be removed.
     FIXME: implement the above. */

  /* Adjust the times (and if possible, ownership) for the copy.
     chown turns off set[ug]id bits for non-root,
     so do the chmod last.  */

  if (x->preserve_timestamps)
    {
      struct utimbuf utb;

      /* There's currently no interface to set file timestamps with
	 better than 1-second resolution, so discard any fractional
	 part of the source timestamp.  */

      utb.actime = src_sb.st_atime;
      utb.modtime = src_sb.st_mtime;

      if (utime (dst_path, &utb))
	{
	  error (0, errno, _("preserving times for %s"), quote (dst_path));
	  if (x->require_preserve)
	    return 1;
	}
    }

  /* Avoid calling chown if we know it's not necessary.  */
  if (x->preserve_ownership
      && (new_dst || !SAME_OWNER_AND_GROUP (src_sb, dst_sb)))
    {
      ran_chown = 1;
      if (DO_CHOWN (chown, dst_path, src_sb.st_uid, src_sb.st_gid))
	{
	  error (0, errno, _("failed to preserve ownership for %s"),
		 quote (dst_path));
	  if (x->require_preserve)
	    return 1;
	}
    }

#if HAVE_STRUCT_STAT_ST_AUTHOR
  /* Preserve the st_author field.  */
  {
    file_t file = file_name_lookup (dst_path, 0, 0);
    if (file_chauthor (file, src_sb.st_author))
      error (0, errno, _("failed to preserve authorship for %s"),
	     quote (dst_path));
    mach_port_deallocate (mach_task_self (), file);
  }
#endif

  /* Permissions of newly-created regular files were set upon `open' in
     copy_reg.  But don't return early if there were any special bits and
     we had to run chown, because the chown must have reset those bits.  */
  if ((new_dst && copied_as_regular)
      && !(ran_chown && (src_mode & ~S_IRWXUGO)))
    return delayed_fail;

  if ((x->preserve_mode || new_dst)
      && (x->copy_as_regular || S_ISREG (src_type) || S_ISDIR (src_type)))
    {
      if (chmod (dst_path, get_dest_mode (x, src_mode)))
	{
	  error (0, errno, _("setting permissions for %s"), quote (dst_path));
	  if (x->set_mode || x->require_preserve)
	    return 1;
	}
    }

  return delayed_fail;

un_backup:

  /* We have failed to create the destination file.
     If we've just added a dev/ino entry via the remember_copied
     call above (i.e., unless we've just failed to create a hard link),
     remove the entry associating the source dev/ino with the
     destination file name, so we don't try to `preserve' a link
     to a file we didn't create.  */
  if (earlier_file == NULL)
    forget_created (src_sb.st_ino, src_sb.st_dev);

  if (dst_backup)
    {
      if (rename (dst_backup, dst_path))
	error (0, errno, _("cannot un-backup %s"), quote (dst_path));
      else
	{
	  if (x->verbose)
	    printf (_("%s -> %s (unbackup)\n"),
		    quote_n (0, dst_backup), quote_n (1, dst_path));
	}
    }
  return 1;
}