/*
 * Copyright(C) 2000 Проект ИОК
 *
 * Этот файл содержит информацию, являющуюся
 * собственностью компании Крипто Про.
 *
 * Любая часть этого файла не может быть скопирована,
 * исправлена, переведена на другие языки,
 * локализована или модифицирована любым способом,
 * откомпилирована, передана по сети с или на
 * любую компьютерную систему без предварительного
 * заключения соглашения с компанией Крипто Про.
 */

#include "fat12prj.h" /*+ Project (READER/FAT12) include.
    Мастер include файл для проекта (READER/FAT12). +*/

static DWORD full_file_path(const TCHAR *base_path, const TCHAR *folder,
    const TCHAR *fname, TCHAR **out_path)
{
    TCHAR *full_path = NULL;
    size_t full_path_length = 0;

    full_path_length += _tcslen(base_path) * sizeof(TCHAR);
    if (folder)
	full_path_length += (_tcslen(folder) + 1) * sizeof(TCHAR);
    full_path_length += (_tcslen(fname) + 1) * sizeof(TCHAR);

    full_path = malloc(full_path_length);
    if (full_path == NULL)
	return (DWORD)NTE_NO_MEMORY;

    _tcscpy(full_path, base_path);
    if (folder) {
	_tcscat(full_path, folder);
	_tcscat(full_path, _TEXT("/"));
    }
    _tcscat(full_path, fname);

    *out_path = full_path;
    return (DWORD)ERROR_SUCCESS;
}

#ifdef UNIX

DWORD check_container_access_by_file(const TFat12Context *ctx, const TCHAR* folder, const TCHAR* file)
{
    TCHAR* full_file_name = NULL;
    //получим полный путь до файла - если доступа к нему нет, контейнер не получится использовать.
    DWORD code = full_file_path(ctx->path, folder, file, &full_file_name);
    if (ERROR_SUCCESS != code) {
	return code;
    }
    code = support_thread_actualize_uids();
    if (ERROR_SUCCESS != code) {
	free(full_file_name);
	return code;
    }

    //Проверим, что файл существует и у нас есть права на чтение. 
    //альтернативный способ не работает в кс2. access возвращает успех, но последующий fopen на чтение не проходит.
    //if (access(full_file_name, F_OK | R_OK) != -1)  
    FILE * file_handle = fopen(full_file_name, "r"); //стоимость fopen не сильно больше, чем у stat
    if (NULL != file_handle) {
	fclose(file_handle);
	code = ERROR_SUCCESS;
    }
    else {
	code = (DWORD)ERROR_FILE_NOT_FOUND;
    }

    free(full_file_name);
    support_thread_deactualize_uids();
    return code;
}

static DWORD file_open(const TCHAR *file_name, TReaderInfoOpenFlags file_mode,
    int local_machine, DWORD *out_access, FILE **out_file_handle)
{
    char str_mode[3] = { 0 };
    FILE *file_handle = NULL;
    DWORD code = ERROR_SUCCESS;

    UNUSED(out_access);
    code = support_thread_actualize_uids();
    if (code)
	return code;

    /* r |  +   -   ?   ?  | --
    -- w |  +   +   +   -  | --
    -- c |  +   +   -   ?  | --
    ---- | --------------- | --
    mode |  w+  w   r+  r  | */
    if (file_mode.o_write && file_mode.o_create) {
	str_mode[0] = 'w';
	if (file_mode.o_read)
	    str_mode[1] = '+';
    }
    else {
	str_mode[0] = 'r';
	if (file_mode.o_write)
	    str_mode[1] = '+';
    }
    file_handle = fopen(file_name, str_mode);
    if (file_handle == NULL) {
	/* Проверить на Write Protect */
	/* Проверить на наличие свободного места. */
	if (errno == ENOENT)
	    code = (DWORD)ERROR_FILE_NOT_FOUND;
	else if (errno == EACCES)
	    code = (DWORD)NTE_PERM;
	else
	    code = fat12_os_error();

	support_thread_deactualize_uids();
	return code;
    }
    support_thread_deactualize_uids();

    if (file_mode.o_create) {
	mode_t mode = S_IRUSR | S_IWUSR;
	if (local_machine)
	    mode |= S_IRGRP | S_IWGRP;

	if (fchmod(fileno(file_handle), mode) && errno != EPERM) {
	    code = fat12_os_error();
	    fclose(file_handle);
	    return code;
	}
    }

    *out_file_handle = file_handle;
    return (DWORD)ERROR_SUCCESS;
}

#else /* UNIX */

//Возвращает ERROR_SUCCESS, если в контейнере есть указанный файл
//Помогает быстрее отфильтровать папки без контейнеров на Windows - не показывать их carrier'у
DWORD check_container_access_by_file(const TFat12Context *ctx, const TCHAR* folder, const TCHAR* file)
{
    TCHAR* full_file_name = NULL;
    DWORD code = full_file_path(ctx->path, folder, file, &full_file_name);
    if (ERROR_SUCCESS != code) {
	return code;
    }

    //Проверим, что файл существует
    DWORD dwAttrib = GetFileAttributes(full_file_name);
    free(full_file_name);

    if (dwAttrib != INVALID_FILE_ATTRIBUTES) {
	code = ERROR_SUCCESS;
    }
    else {
	code = (DWORD)ERROR_FILE_NOT_FOUND;
    }
    return code;
}

static DWORD file_open(const TCHAR *file_name, TReaderInfoOpenFlags file_mode,
    int local_machine, DWORD *out_access, HANDLE *out_file_handle)
{
    DWORD dwCreationDisposition = 0;
    int tries = SHARING_VIOLATION_TRIES;
    HANDLE file_handle = INVALID_HANDLE_VALUE;
    DWORD access = 0;
    DWORD code = ERROR_SUCCESS;

    UNUSED(local_machine);
    if (file_mode.o_read)
	access |= GENERIC_READ;
    if (file_mode.o_write)
	access |= GENERIC_WRITE;

    if (file_mode.o_create)
	dwCreationDisposition |= CREATE_ALWAYS;
    else
	dwCreationDisposition |= OPEN_EXISTING;

    while ((tries--) > 0) {
	file_handle = CreateFile(file_name, access,
	    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
	    dwCreationDisposition, FILE_ATTRIBUTE_ARCHIVE, NULL);
	if (file_handle == INVALID_HANDLE_VALUE) {
	    code = fat12_os_error_inside();
	    if (code == ERROR_CONTINUE) {
		code = (DWORD)SCARD_E_READER_UNAVAILABLE;
		Sleep(5);
	    }
	    else return code;
	}
	else break;
    }

    if (file_mode.o_write) {
	if (!FlushFileBuffers(file_handle)) {
	    code = fat12_os_error();
	    CloseHandle(file_handle);
	    return code;
	}
    }

    *out_access = access;
    *out_file_handle = file_handle;
    return (DWORD)ERROR_SUCCESS;
}
#endif /* UNIX */

/*++++
* Функция открытия файла.
++++*/
DWORD fat12_open(TSupSysContext *context, TSupSysInfo *info)
{
    TReaderInfoOpen *inf = (TReaderInfoOpen*)info;
    TFat12Context *ctx = (TFat12Context*)context;
    DWORD code = ERROR_SUCCESS;
    TCHAR *file_name = NULL;

#ifdef UNIX
    DWORD access = 0;
    FILE *file_handle = NULL;
    SUPSYS_PRE(ctx->handle == NULL);
#else /* UNIX */
    DWORD access = ctx->access;
    HANDLE file_handle = NULL;
    SUPSYS_PRE(ctx->handle == INVALID_HANDLE_VALUE);
#endif /* UNIX */
    SUPSYS_PRE_CONTEXT(context, TFat12Context);
    SUPSYS_PRE_INFO(info, TReaderInfoOpen);
    SUPSYS_PRE(ctx->file_name == NULL);
    SUPSYS_PRE(inf->mode_bits >= READER_MODEFLAG_BITS);
    SUPSYS_PRE(inf->name.length);
    SUPSYS_PRE_STRING_PTRS(inf->name.text, inf->name.length);
    SUPSYS_PRE(ctx->path);

    code = full_file_path(ctx->path, ctx->folder, inf->name.text, //-V1004
	&file_name);
    if (code)
	return code;

    code = file_open(file_name, inf->mode, ctx->machine, &access,
	&file_handle);
    if (code) {
	free(file_name);
	return code;
    }

#ifndef UNIX
    ctx->access = access;
#endif /* !UNIX */
    ctx->file_name = file_name;
    ctx->handle = file_handle;
    return (DWORD)ERROR_SUCCESS;
}
