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

/*!
 * \file $RCSfile$
 * \version $Revision: 189561 $
 * \date $Date:: 2019-02-26 11:32:22 +0300#$
 * \author $Author: markova $
 * \brief Функция открытия папки.
 */

#include "fat12prj.h"

static TCHAR * full_folder_path(const TCHAR * path, const TCHAR * folder, BOOL long_names)
{
    TCHAR * full_path = NULL;
    size_t full_path_length;
    if (long_names) {
	full_path_length = _tcslen(folder) + 1 + 1 + _tcslen(path) + 1;
    }
    else {
	size_t i,j;
	for (i = 0, j = 0; i < 12 && folder[i]; i++) {
	    if (folder[i] == '.') {
		if (j != 0)
		    return NULL;
		j++;
		continue;
	    }
	    if (j)
		j++;
	    if (fat12_convert(folder[i]) != folder[i])
		return NULL;
	}
	if (j > 4)
	    return NULL;
	full_path_length = (12 + _tcslen(folder) + 1 + 1 + _tcslen(path) + 1);
    }
    full_path = (TCHAR*)malloc((full_path_length + 1) * sizeof(TCHAR));
    if (full_path == NULL)
	return NULL;
    _tcscpy(full_path, path);
    _tcscat(full_path, folder);
    return full_path;
}

/*!
 * \ingroup fat12_fun_reader
 * \brief Функция открытия папки.
 * \param context [in] контекст считывателя. 
 * \param info [in/out] структура #TReaderInfoFolderOpen
 * \sa TReaderInfoFolderOpen, READER_FUN_FOLDER_OPEN, 
 *  TSupSysFunction, fat12_folder_close, #rdr_folder_open, #rdr_folder_close
 */
DWORD fat12_folder_open( 
    TSupSysContext *context, 
    TSupSysInfo *info )
{
    TReaderInfoFolderOpen *inf = (TReaderInfoFolderOpen*)info;
    TCHAR *path;
    TFat12Context *ctx = (TFat12Context*)context;
    int exist;
    DWORD code;

    SUPSYS_PRE_CONTEXT( context, TFat12Context );
    SUPSYS_PRE_INFO( info, TReaderInfoFolderOpen );

    if (inf->name.text && ctx->folder) {
	if (_tcscmp(ctx->folder, inf->name.text) == 0)
	    return ERROR_SUCCESS;
    }

    if (ctx->folder)
	fat12_folder_close(context, NULL);
    if( !inf->name.text )
	return ERROR_SUCCESS;
    if (!ctx->long_names && inf->name.length > 12)
	return (DWORD)ERROR_INVALID_PARAMETER;

    SUPSYS_PRE_READ_PTRS(inf->name.text, (inf->name.length + 1)	* sizeof(TCHAR));
    SUPSYS_PRE( !(inf->name.text [inf->name.length]) );
    SUPSYS_PRE( _tcslen( inf->name.text ) == inf->name.length );

    path = full_folder_path(ctx->path, inf->name.text, ctx->long_names);
    if (!path)
	return (DWORD)NTE_NO_MEMORY;
    ctx->folder = support_tcsdup(inf->name.text);
    if (!ctx->folder) {
	free(path);
	return (DWORD)NTE_NO_MEMORY;
    }

    /* Проверка корректности соединения. */
    exist = fat12_is_dir_exist( path );
    if (exist == DIR_WRONG_NAME) {
	free(path);
	free(ctx->folder); ctx->folder = NULL;
	return (DWORD)ERROR_INVALID_NAME;
    }
    if( !inf->mode.o_create && ( exist == DIR_INVALID || !exist ) )
    {
	free( path );
	free( ctx->folder ); ctx->folder = NULL;
	return (DWORD)ERROR_FILE_NOT_FOUND;
    }
    if( inf->mode.o_create && ( exist == DIR_INVALID || exist ) )
    {
	free( path );
	free( ctx->folder ); ctx->folder = NULL;
	return (DWORD)NTE_EXISTS;
    }
    if( !inf->mode.o_create )
    {
	free( path );
	return ERROR_SUCCESS;
    }
#ifdef UNIX
    {
	code = support_thread_actualize_uids ();
	if ( code ) {
	    free( path );
	    free( ctx->folder ); ctx->folder = NULL;
	    return code;
	}
    }
    exist = !_tmkdir( path, ((TFat12Context *)ctx)->machine?S_IRWXG|S_IRWXU:S_IRWXU );
    if (!exist)
    {
	code = fat12_os_error();
	free( path );
	free( ctx->folder ); ctx->folder = NULL;
	return code;
    }
    exist = exist && !chmod( path, ((TFat12Context *)ctx)->machine?S_IRWXG|S_IRWXU:S_IRWXU );
    if ( !exist && errno == EPERM ) {
        exist = 1;
    }
    support_thread_deactualize_uids ();
    if (!exist) {
	code = fat12_os_error();
	free(path);
	free(ctx->folder); ctx->folder = NULL;
	return code;
    }
#else
    if( !CreateDirectory( path, NULL ) )
    {
	code = fat12_os_error();
	free( path );
	free( ctx->folder ); ctx->folder = NULL;
	return code;
    }
    if (ctx->pSD) {
	code = support_folder_put_sdecr(path, ctx->SecInfo, ctx->pSD);
	if (code)
	    return code;
	LocalFree(ctx->pSD);
	ctx->pSD = NULL;
    }
#endif /* UNIX */

    free( path );
    return ERROR_SUCCESS;
}

#ifdef UNIX
int fat12_is_dir_exist( const TCHAR *path )
{ 
    struct stat buf;
    int rc; 
    if ( support_thread_actualize_uids () )
	return DIR_INVALID;
    rc = stat(path, &buf);
    support_thread_deactualize_uids ();
    if ( rc ) 
	return (errno==ENOENT)?DIR_NOTEXIST:DIR_INVALID;
    if (!S_ISDIR(buf.st_mode))
	return DIR_INVALID;
    return DIR_EXIST;
}
#elif defined _WIN32
int fat12_is_dir_exist( const TCHAR *path )
{
    DWORD attrib;
    attrib = GetFileAttributes( path ); 
    if (attrib == INVALID_FILE_ATTRIBUTES) {
	DWORD code = GetLastError();
	if (code == ERROR_INVALID_NAME)
	    return DIR_WRONG_NAME;
	return DIR_NOTEXIST;
    }
    if( !( attrib & FILE_ATTRIBUTE_DIRECTORY ) )
	return DIR_INVALID;
    if( attrib & ( FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_SYSTEM
	| FILE_ATTRIBUTE_HIDDEN ) )
	return DIR_INVALID;
    return DIR_EXIST;
}
#else
#error fat12_is_dir_exist not implemented.
#endif /* _WIN32, UNIX */


#ifdef _WIN32
DWORD fat12_folder_get_sdecr(
    TSupSysContext *context,
    TSupSysInfo *info)
{
    TReaderInfoSecDescrRW *inf = (TReaderInfoSecDescrRW*)info;
    DWORD code;
    TFat12Context *ctx = (TFat12Context*)context;
    DWORD destlen;
    TCHAR * path;

    SUPSYS_PRE_CONTEXT(context, TFat12Context);
    SUPSYS_PRE_INFO(info, TReaderInfoSecDescrRW);

    destlen = (DWORD)inf->sec_descr.length;

    if (!ctx->folder) {
	// еще не создана папка, поэтому вернем запасённый SD
	if (ctx->pSD) {
	    if (destlen == 0) {
		inf->sec_descr.length = ctx->pSD_size;
		return 0;
	    }
	    if (destlen < ctx->pSD_size) {
		inf->sec_descr.length = ctx->pSD_size;
		return (DWORD)ERROR_MORE_DATA;
	    }
	    memcpy(inf->sec_descr.info, ctx->pSD, ctx->pSD_size);
	    inf->sec_descr.length = ctx->pSD_size;
	    return 0;
	}
	// если не запасали заранее, взять его не откуда
	else {
	    return (DWORD)ERROR_FILE_NOT_FOUND;
	}
    }

    path = full_folder_path(ctx->path, ctx->folder, ctx->long_names);
    if (!path)
	return (DWORD)NTE_NO_MEMORY;

    code = support_folder_get_sdecr(path, (DWORD)inf->sec_info, inf->sec_descr.info, &destlen);
    free(path);
    if (code && (code != (DWORD)ERROR_MORE_DATA))
	return code;
    inf->sec_descr.length = (size_t)destlen;
    return code;
}

DWORD fat12_folder_put_sdecr(
    TSupSysContext *context,
    TSupSysInfo *info)
{
    TReaderInfoSecDescrRW *inf = (TReaderInfoSecDescrRW*)info;
    DWORD code = 0;
    TFat12Context *ctx = (TFat12Context*)context;
    TCHAR * path;

    SUPSYS_PRE_CONTEXT(context, TFat12Context);
    SUPSYS_PRE_INFO(info, TReaderInfoSecDescrRW);

    if (!ctx->folder) {
	// отложим установку до реального создания папки
	ctx->pSD = inf->sec_descr.info;
	ctx->pSD_size = inf->sec_descr.length;
	ctx->SecInfo = (DWORD)inf->sec_info;
	code = (DWORD)ERROR_SUCCESS;
    }

    path = full_folder_path(ctx->path, ctx->folder, ctx->long_names);
    if (!path)
	return (DWORD)NTE_NO_MEMORY;
    code = support_folder_put_sdecr(path, (DWORD)inf->sec_info, inf->sec_descr.info);
    free(path);
    return code;
}

#endif //_WIN32
/* end of file: $Id: f12dopen.c 189561 2019-02-26 08:32:22Z markova $ */
