Ejemplo n.º 1
0
static void mps_lib_assert_fail_default(const char *file,
                                        unsigned line,
                                        const char *condition)
{
  fflush(stdout); /* synchronize */
  fprintf(stderr, "%s:%u: MPS ASSERTION FAILED: %s\n", file, line, condition);
  fflush(stderr); /* make sure the message is output */
  ASSERT_ABORT(); /* see config.h */
}
Ejemplo n.º 2
0
static void mps_lib_assert_fail_default(const char *file, unsigned line,
                                        const char *condition)
{
  /* Synchronize with stdout. */
  (void)fflush(stdout);
  (void)fprintf(stderr,
                "The MPS detected a problem!\n"
                "%s:%u: MPS ASSERTION FAILED: %s\n"
                "See the \"Assertions\" section in the reference manual:\n"
                "http://www.ravenbrook.com/project/mps/master/manual/html/topic/error.html#assertions\n",
                file, line, condition);
  /* Ensure the message is output even if stderr is buffered. */
  (void)fflush(stderr);
  mps_telemetry_flush();
  ASSERT_ABORT(); /* see config.h */
}
Ejemplo n.º 3
0
/*
 *    This function is called by the handleConnection() function in
 *    rwtransfer.c once the connection has been established.  This
 *    function returns -1 on error, 0 if no files were transferred, or
 *    1 if one or more files were successfully received.
 */
int
transferFiles(
    sk_msg_queue_t     *q,
    skm_channel_t       channel,
    transfer_t         *sndr)
{
    static pthread_mutex_t open_file_mutex = PTHREAD_MUTEX_INITIALIZER;
    int fd = -1;
    uint64_t size = 0;
    uint64_t pa_size = 0;
    uint8_t *map = NULL;
    char *name = NULL;
    char *dotname = NULL;
    char dotpath[PATH_MAX];
    char destpath[sizeof(dotpath)-1];
    struct stat st;
    ino_t *inode;
    int proto_err;
    int rv;
    sk_dll_iter_t iter;
    const char *duplicate_dir;
    enum transfer_state {File_info, File_info_ack,
                         Send_file, Complete_ack, Error} state;
    int thread_exit;
    int transferred_file = 0;

    state = File_info;
    proto_err = 0;
    thread_exit = 0;
    destpath[0] = '\0';
    dotpath[0] = '\0';
    memset(&st, 0, sizeof(st));

    while (!shuttingdown && !proto_err && !thread_exit && !sndr->disconnect
           && (state != Error))
    {
        sk_msg_t *msg;

        /* Handle reads */
        switch (state) {
          case File_info:
          case Send_file:
            rv = skMsgQueueGetMessage(q, &msg);
            if (rv == -1) {
                ASSERT_ABORT(shuttingdown);
                continue;
            }
            if (handleDisconnect(msg, sndr->ident)) {
                state = Error;
            }
            break;
          case Error:
            ASSERT_ABORT(0);
            break;
          default:
            msg = NULL;
        }

        /* Handle all states */
        switch (state) {
          case File_info:
            /* Create the placeholder and dot files and mmap() the
             * space. */
            {
                file_info_t *finfo;
                uint32_t len;
                mode_t mode;
                off_t offrv;

                if ((proto_err = checkMsg(msg, q, CONN_NEW_FILE))) {
                    break;
                }
                DEBUG_PRINT1("Received CONN_NEW_FILE");
                finfo = (file_info_t *)skMsgMessage(msg);
                size = (uint64_t)ntohl(finfo->high_filesize) << 32 |
                       ntohl(finfo->low_filesize);
                pa_size = size;
                /* blocksize = ntohl(finfo->block_size); --- UNUSED */
                mode = ntohl(finfo->mode) & 0777;
                len = skMsgLength(msg) - offsetof(file_info_t, filename);
                dotname = (char *)calloc(1, len + 1);
                CHECK_ALLOC(dotname);
                name = dotname + 1;
                dotname[0] = '.';
                memcpy(name, finfo->filename, len);
                if (!memchr(name, '\0', len)) {
                    sendString(q, channel, EXTERNAL, SEND_CONN_REJECT(sndr),
                               LOG_WARNING, "Illegal filename (from %s)",
                               sndr->ident);
                    state = FILE_INFO_ERROR_STATE(sndr);
                    break;
                }

                INFOMSG("Receiving from %s: '%s' (%" PRIu64 " bytes)",
                        sndr->ident, name, size);

                /* Check filesystem for enough space for file */
                if (CHECK_DISK_SPACE(pa_size)) {
                    WARNINGMSG(("Not enough space on filesystem for %" PRIu64
                                " byte file '%s'"),
                               pa_size, name);
                    pa_size = 0;
                    state = FILESYSTEM_FULL_ERROR_STATE(sndr);
                    break;
                }

                /* Create the placeholder file */
                rv = snprintf(destpath, sizeof(destpath), "%s/%s",
                              destination_dir, name);
                if ((size_t)rv >= sizeof(destpath)) {
                    sendString(q, channel, EXTERNAL,
                               SEND_CONN_REJECT(sndr),
                               LOG_WARNING, "Filename too long (from %s)",
                               sndr->ident);
                    state = FILE_INFO_ERROR_STATE(sndr);
                    destpath[0] = '\0';
                    break;
                }

                assert((size_t)rv < sizeof(destpath));

                pthread_mutex_lock(&open_file_mutex);
              reopen:
                fd = open(destpath, O_CREAT | O_EXCL | O_WRONLY, 0);
                if (fd == -1) {
                    if (errno != EEXIST) {
                        CRITMSG("Could not create '%s': %s",
                                destpath, strerror(errno));
                        thread_exit = 1;
                        pthread_mutex_unlock(&open_file_mutex);
                        break;
                    }

                    if (stat(destpath, &st) == -1) {
                        WARNINGMSG("Unable to stat '%s': %s",
                                   destpath, strerror(errno));
                    } else if (S_ISREG(st.st_mode)
                               && ((st.st_mode & 0777) == 0)
                               && ((st.st_size == 0)))
                    {
                        /* looks like a placeholder file.  are we
                         * receiving a file with the same name from a
                         * different rwsender? */
                        int found = 0;
                        skDLLAssignIter(&iter, open_file_list);
                        while (skDLLIterForward(&iter, (void **)&inode) == 0) {
                            if (st.st_ino == *inode) {
                                WARNINGMSG(("Multiple rwsenders attempting"
                                            " to send file '%s'"),
                                           name);
                                found = 1;
                                break;
                            }
                        }
                        if (!found) {
                            WARNINGMSG(("Filename already exists (from a"
                                        " previous run?). Removing '%s'"),
                                       destpath);
                            if (unlink(destpath) == 0) {
                                goto reopen;
                            }
                            WARNINGMSG("Failed to unlink '%s': %s",
                                       destpath, strerror(errno));
                            /* treat file as a duplicate */
                        }
                    }
                    /* else file is a duplicate */
                    st.st_ino = 0;
                    destpath[0] = dotpath[0] = '\0';
                    sendString(q, channel, EXTERNAL,
                               SEND_CONN_DUPLICATE(sndr),
                               LOG_WARNING,
                               "Filename already exists (from %s)",
                               sndr->ident);
                    state = FILE_INFO_ERROR_STATE(sndr);
                    pthread_mutex_unlock(&open_file_mutex);
                    break;
                }
                /* else, successfully opened placeholder file */
                if (fstat(fd, &st) == -1) {
                    CRITMSG("Could not fstat newly created file '%s': %s",
                            destpath, strerror(errno));
                    st.st_ino = 0;
                    thread_exit = 1;
                    pthread_mutex_unlock(&open_file_mutex);
                    break;
                }
                if (skDLListPushTail(open_file_list, &st.st_ino)) {
                    CRITMSG("Unable to grow open file list");
                    st.st_ino = 0;
                    thread_exit = 1;
                    pthread_mutex_unlock(&open_file_mutex);
                    break;
                }
                pthread_mutex_unlock(&open_file_mutex);

                DEBUGMSG("Created '%s'", destpath);

                rv = close(fd);
                fd = -1;
                if (rv == -1) {
                    CRITMSG("Could not close file '%s': %s",
                            destpath, strerror(errno));
                    thread_exit = 1;
                    break;
                }

                /* Create the dotfile */
                rv = snprintf(dotpath, sizeof(dotpath), "%s/%s",
                              destination_dir, dotname);
              reopen2:
                fd = open(dotpath, O_RDWR | O_CREAT | O_EXCL, mode);
                if (fd == -1) {
                    int saveerrno = errno;
                    if (errno == EEXIST) {
                        WARNINGMSG("Filename already exists. Removing '%s'",
                                   dotpath);
                        if (unlink(dotpath) == 0) {
                            goto reopen2;
                        }
                        WARNINGMSG("Failed to unlink '%s': %s",
                                   dotpath, strerror(errno));
                    }
                    CRITMSG("Could not create '%s': %s",
                            dotpath, strerror(saveerrno));
                    thread_exit = 1;
                    dotpath[0] = '\0';
                    break;
                }
                DEBUGMSG("Created '%s'", dotpath);

                /* Allocate space on disk */
                offrv = lseek(fd, size - 1, SEEK_SET);
                if (offrv == -1) {
                    CRITMSG("Could not allocate disk space for '%s': %s",
                            dotpath, strerror(errno));
                    thread_exit = 1;
                    break;
                }
                rv = write(fd, "", 1);
                if (rv == -1) {
                    CRITMSG("Could not allocate disk space for '%s': %s",
                            dotpath, strerror(errno));
                    thread_exit = 1;
                    break;
                }

                /* Map space */
                map = (uint8_t *)mmap(0, size, PROT_READ | PROT_WRITE,
                                      MAP_SHARED, fd, 0);
                if ((void *)map == MAP_FAILED) {
                    CRITMSG("Could not map '%s': %s",
                            dotpath, strerror(errno));
                    thread_exit = 1;
                    break;
                }
                rv = close(fd);
                fd = -1;
                if (rv == -1) {
                    CRITMSG("Could not close file '%s': %s",
                            dotpath, strerror(errno));
                    thread_exit = 1;
                    break;
                }
                GOT_DISK_SPACE(pa_size);
                pa_size = 0;
                state = File_info_ack;
            }
            break;

          case File_info_ack:
            DEBUG_PRINT1("Sending CONN_NEW_FILE_READY");
            proto_err = skMsgQueueSendMessage(q, channel,
                                              CONN_NEW_FILE_READY, NULL, 0);
            state = Send_file;
            break;

          case Send_file:
            /* Get the content of the file and write into the dot file */
            {
                block_info_t *block;
                uint64_t offset;
                uint32_t len;

                if (skMsgType(msg) != CONN_FILE_BLOCK) {
                    if ((proto_err = checkMsg(msg, q, CONN_FILE_COMPLETE))) {
                        break;
                    }
                    DEBUG_PRINT1("Received CONN_FILE_COMPLETE");
                    state = Complete_ack;
                    break;
                }

                block = (block_info_t *)skMsgMessage(msg);
                len = skMsgLength(msg) - offsetof(block_info_t, block);
                offset = (uint64_t)ntohl(block->high_offset) << 32 |
                         ntohl(block->low_offset);
                DEBUG_CONTENT_PRINT("Receiving offset=%" PRIu64 " len=%" PRIu32,
                                    offset, len);
                if (offset + len > size) {
                    sendString(q, channel, EXTERNAL, CONN_DISCONNECT,
                               LOG_WARNING,
                               ("Illegal block (offset/size %" PRIu64
                                "/%" PRIu32 ")"), offset, len);
                    state = Error;
                    break;
                }
                memcpy(map + offset, block->block, len);
            }
            break;

          case Complete_ack:
            /* Un-mmap() the file, create any duplicate files, and
             * move the dotfile over the placeholder file */
            rv = munmap(map, size);
            map = NULL;
            if (rv == -1) {
                CRITMSG("Could not unmap file '%s': %s",
                        dotpath, strerror(errno));
                thread_exit = 1;
                break;
            }

            /* Handle duplicate-destinations. Any errors here are
             * simply logged and processing continues. */
            skDLLAssignIter(&iter, duplicate_dirs);
            while (skDLLIterForward(&iter, (void **)&duplicate_dir) == 0) {
                char path[sizeof(destpath)];

                snprintf(path, sizeof(path), "%s/%s", duplicate_dir, name);
                if (unique_duplicates) {
                    rv = skCopyFile(dotpath, path);
                    if (rv != 0) {
                        WARNINGMSG("Could not copy '%s' to '%s': %s",
                                   dotpath, path, strerror(rv));
                    }
                } else {
                    DEBUGMSG("Linking '%s' as '%s'", dotpath, path);
                    rv = link(dotpath, path);
                    if (EXDEV == errno) {
                        DEBUGMSG("Link failed EXDEV; copying '%s' to '%s'",
                                 dotpath, path);
                        rv = skCopyFile(dotpath, path);
                        if (rv != 0) {
                            WARNINGMSG("Could not copy '%s' to '%s': %s",
                                       dotpath, path, strerror(rv));
                        }
                    } else if (rv != 0) {
                        WARNINGMSG("Could not link '%s' as '%s': %s",
                                   dotpath, path, strerror(errno));
                    }
                }
            }

            DEBUGMSG("Renaming '%s' to '%s'", dotpath, destpath);
            rv = rename(dotpath, destpath);
            if (rv != 0) {
                CRITMSG("Failed rename of '%s' to '%s': %s",
                        dotpath, destpath, strerror(errno));
                thread_exit = 1;
                break;
            }

            /* remove the file from the open_file_list */
            pthread_mutex_lock(&open_file_mutex);
            skDLLAssignIter(&iter, open_file_list);
            while (skDLLIterForward(&iter, (void **)&inode) == 0) {
                if (st.st_ino == *inode) {
                    skDLLIterDel(&iter);
                    break;
                }
            }
            st.st_ino = 0;
            pthread_mutex_unlock(&open_file_mutex);

            DEBUG_PRINT1("Sending CONN_FILE_COMPLETE");
            proto_err = skMsgQueueSendMessage(q, channel,
                                              CONN_FILE_COMPLETE, NULL, 0);
            if (proto_err == 0) {
                /* Run the post command on the file */
                if (post_command) {
                    runPostCommand(post_command, destpath, sndr->ident);
                }
                destpath[0] = '\0';
                INFOMSG("Finished receiving from %s: '%s'", sndr->ident, name);
                free(dotname);
                dotname = NULL;
            }

            destpath[0] = dotpath[0] = '\0';
            transferred_file = 1;

            state = File_info;
            break;

          case Error:
            break;
        }

        if (msg != NULL) {
            skMsgDestroy(msg);
        }
    }

    if (fd != -1) {
        close(fd);
    }
    if (map != NULL) {
        munmap(map, size);
    }
    if (dotname != NULL) {
        free(dotname);
    }
    if (dotpath[0] != '\0') {
        DEBUGMSG("Removing '%s'", dotpath);
        unlink(dotpath);
    }
    if (destpath[0] != '\0') {
        DEBUGMSG("Removing '%s'", destpath);
        unlink(destpath);
    }
    if (st.st_ino != 0) {
        skDLLAssignIter(&iter, open_file_list);
        while (skDLLIterForward(&iter, (void **)&inode) == 0) {
            if (st.st_ino == *inode) {
                skDLLIterDel(&iter);
                break;
            }
        }
    }
    if (pa_size) {
        GOT_DISK_SPACE(pa_size);
    }
    if (thread_exit) {
        return -1;
    }

    return transferred_file;
}