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))); }
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))); }
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)); }
/* 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); } }
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); }
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); }
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)); } }
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; }
/* 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 (); } }
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; }
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 {
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; }
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. */ } } }
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; }
/* Return an unambiguous printable representation of NAME, suitable for diagnostics. */ char const * quote (char const *name) { return quote_n (0, name); }
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; }