int guac_rdp_fs_open()

in src/protocols/rdp/fs.c [213:411]


int guac_rdp_fs_open(guac_rdp_fs* fs, const char* path,
        int access, int file_attributes, int create_disposition,
        int create_options) {

    char real_path[GUAC_RDP_FS_MAX_PATH];
    char normalized_path[GUAC_RDP_FS_MAX_PATH];

    struct stat file_stat;
    int fd;
    int file_id;
    guac_rdp_fs_file* file;

    int flags = 0;

    guac_client_log(fs->client, GUAC_LOG_DEBUG,
            "%s: path=\"%s\", access=0x%x, file_attributes=0x%x, "
            "create_disposition=0x%x, create_options=0x%x",
            __func__, path, access, file_attributes,
            create_disposition, create_options);

    /* If no files available, return too many open */
    if (fs->open_files >= GUAC_RDP_FS_MAX_FILES) {
        guac_client_log(fs->client, GUAC_LOG_DEBUG,
                "%s: Too many open files.",
                __func__, path);
        return GUAC_RDP_FS_ENFILE;
    }

    /* If path empty, transform to root path */
    if (path[0] == '\0')
        path = "\\";

    /* If path is relative, the file does not exist */
    else if (path[0] != '\\' && path[0] != '/') {
        guac_client_log(fs->client, GUAC_LOG_DEBUG,
                "%s: Access denied - supplied path \"%s\" is relative.",
                __func__, path);
        return GUAC_RDP_FS_ENOENT;
    }

    /* Translate access into flags */
    if (access & GENERIC_ALL)
        flags = O_RDWR;
    else if ((access & ( GENERIC_WRITE
                       | FILE_WRITE_DATA
                       | FILE_APPEND_DATA))
          && (access & (GENERIC_READ  | FILE_READ_DATA)))
        flags = O_RDWR;
    else if (access & ( GENERIC_WRITE
                      | FILE_WRITE_DATA
                      | FILE_APPEND_DATA))
        flags = O_WRONLY;
    else
        flags = O_RDONLY;

    /* Normalize path, return no-such-file if invalid  */
    if (guac_rdp_fs_normalize_path(path, normalized_path)) {
        guac_client_log(fs->client, GUAC_LOG_DEBUG,
                "%s: Normalization of path \"%s\" failed.", __func__, path);
        return GUAC_RDP_FS_ENOENT;
    }

    guac_client_log(fs->client, GUAC_LOG_DEBUG,
            "%s: Normalized path \"%s\" to \"%s\".",
            __func__, path, normalized_path);

    /* Translate normalized path to real path */
    __guac_rdp_fs_translate_path(fs, normalized_path, real_path);

    guac_client_log(fs->client, GUAC_LOG_DEBUG,
            "%s: Translated path \"%s\" to \"%s\".",
            __func__, normalized_path, real_path);

    switch (create_disposition) {

        /* Create if not exist, fail otherwise */
        case FILE_CREATE:
            flags |= O_CREAT | O_EXCL;
            break;

        /* Open file if exists and do not overwrite, fail otherwise */
        case FILE_OPEN:
            /* No flag necessary - default functionality of open */
            break;

        /* Open if exists, create otherwise */
        case FILE_OPEN_IF:
            flags |= O_CREAT;
            break;

        /* Overwrite if exists, fail otherwise */
        case FILE_OVERWRITE:
            flags |= O_TRUNC;
            break;

        /* Overwrite if exists, create otherwise */
        case FILE_OVERWRITE_IF:
            flags |= O_CREAT | O_TRUNC;
            break;

        /* Supersede (replace) if exists, otherwise create */
        case FILE_SUPERSEDE:
            unlink(real_path);
            flags |= O_CREAT | O_TRUNC;
            break;

        /* Unrecognised disposition */
        default:
            return GUAC_RDP_FS_ENOSYS;

    }

    /* Create directory first, if necessary */
    if ((create_options & FILE_DIRECTORY_FILE) && (flags & O_CREAT)) {

        /* Create directory */
        if (mkdir(real_path, S_IRWXU)) {
            if (errno != EEXIST || (flags & O_EXCL)) {
                guac_client_log(fs->client, GUAC_LOG_DEBUG,
                        "%s: mkdir() failed: %s",
                        __func__, strerror(errno));
                return guac_rdp_fs_get_errorcode(errno);
            }
        }

        /* Unset O_CREAT and O_EXCL as directory must exist before open() */
        flags &= ~(O_CREAT | O_EXCL);

    }

    guac_client_log(fs->client, GUAC_LOG_DEBUG,
            "%s: native open: real_path=\"%s\", flags=0x%x",
            __func__, real_path, flags);

    /* Open file */
    fd = open(real_path, flags, S_IRUSR | S_IWUSR);

    /* If file open failed as we're trying to write a dir, retry as read-only */
    if (fd == -1 && errno == EISDIR) {
        flags &= ~(O_WRONLY | O_RDWR);
        flags |= O_RDONLY;
        fd = open(real_path, flags, S_IRUSR | S_IWUSR);
    }

    if (fd == -1) {
        guac_client_log(fs->client, GUAC_LOG_DEBUG,
                "%s: open() failed: %s", __func__, strerror(errno));
        return guac_rdp_fs_get_errorcode(errno);
    }

    /* Get file ID, init file */
    file_id = guac_pool_next_int_below_or_die(fs->file_id_pool, GUAC_RDP_FS_MAX_FILES);
    file = &(fs->files[file_id]);
    file->id = file_id;
    file->fd  = fd;
    file->dir = NULL;
    file->dir_pattern[0] = '\0';
    file->absolute_path = guac_strdup(normalized_path);
    file->real_path = guac_strdup(real_path);
    file->bytes_written = 0;

    guac_client_log(fs->client, GUAC_LOG_DEBUG,
            "%s: Opened \"%s\" as file_id=%i",
            __func__, normalized_path, file_id);

    /* Attempt to pull file information */
    if (fstat(fd, &file_stat) == 0) {

        /* Load size and times */
        file->size  = file_stat.st_size;
        file->ctime = WINDOWS_TIME(file_stat.st_ctime);
        file->mtime = WINDOWS_TIME(file_stat.st_mtime);
        file->atime = WINDOWS_TIME(file_stat.st_atime);

        /* Set type */
        if (S_ISDIR(file_stat.st_mode))
            file->attributes = FILE_ATTRIBUTE_DIRECTORY;
        else
            file->attributes = FILE_ATTRIBUTE_NORMAL;

    }

    /* If information cannot be retrieved, fake it */
    else {

        /* Init information to 0, lacking any alternative */
        file->size  = 0;
        file->ctime = 0;
        file->mtime = 0;
        file->atime = 0;
        file->attributes = FILE_ATTRIBUTE_NORMAL;

    }

    fs->open_files++;

    return file_id;

}