Compare commits

...

2 Commits

Author SHA1 Message Date
Vincent Torri a6915ecfc5 mimic access() behavior on Windows 2024-03-25 10:18:44 -07:00
Vincent Torri 9e5bb18af1 Eina: add eina_file_access() API
this addition is motivated by the fact that the access() API on
Windows just check if a file is read only or read/write. See

https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess?view=msvc-170#remarks

This API now also manage if a file/dir is executable or not.

On Unix, access() is just called.
2024-03-25 10:18:44 -07:00
4 changed files with 137 additions and 1 deletions

View File

@ -148,6 +148,21 @@ typedef enum {
EINA_FILE_REMOVE /**< This memory is to be released and any content will be lost. Subsequent accesses will succeed but return fresh memory as if accessed for the first time. This may not succeed if the filesystem does not support it. @since 1.8 */
} Eina_File_Populate;
/**
* @typedef Eina_File_Access_Mode
* @brief Type for enumeration of a file access mode.
* @details This type is used with eina_file_access(). Enumerations can be
* combined bitwise with the OR operator.
* @since 1.28
*/
typedef enum
{
EINA_FILE_ACCESS_MODE_EXIST = 0, /**< existence test: F_OK */
EINA_FILE_ACCESS_MODE_EXEC = 1 << 0, /**< exec permission: X_OK */
EINA_FILE_ACCESS_MODE_WRITE = 1 << 1, /**< write permission: W_OK */
EINA_FILE_ACCESS_MODE_READ = 1 << 2, /**< read permission: R_OK */
} Eina_File_Access_Mode;
/* Why do this? Well PATH_MAX may vary from when eina itself is compiled
* to when the app using eina is compiled. Exposing the path buffer below
* can't safely and portably vary based on how/when you compile. It should
@ -830,6 +845,30 @@ EINA_API void eina_file_statgen_enable(void);
*/
EINA_API void eina_file_statgen_disable(void);
/**
* @brief Determine the accessibility of a file or path.
*
* @param[in] path The path to check.
* @param[in] mode Access permissions to be checked, or existence test.
* @return #EINA_TRUE it @p path satisfies the tests, #EINA_FALSE otherwise.
*
* On Linux, this function just calls the access() function. On Windows, it
* mimics as best as possible the behavior of access():
* - Existence is always checked.
* - As on Windows, a file is either read only or read/write, read permission
* is equivalent to existence. so Write permission is equivalent to not
* being read only.
* - A directory is always executable, except if greater privilege is needed.
*
* The @p mode has the same values than F_OK, X_OK, W_OK and R_OK, and the
* usage is the same than the access() function.
*
* If @p path is NULL or the epty string, this function returns #EINA_FALSE.
*
* @since 1.28
*/
EINA_API Eina_Bool eina_file_access(const char *path, Eina_File_Access_Mode mode);
/**
* @}
*/

View File

@ -1573,3 +1573,13 @@ eina_file_mkdtemp(const char *templatename, Eina_Tmpstr **path)
if (path) *path = eina_tmpstr_add(tmpdirname);
return EINA_TRUE;
}
EINA_API Eina_Bool
eina_file_access(const char *path, Eina_File_Access_Mode mode)
{
if (!path || !*path)
return EINA_FALSE;
return access(path, mode) == 0;
}

View File

@ -1286,3 +1286,48 @@ eina_file_mkdtemp(const char *templatename, Eina_Tmpstr **path)
if (path) *path = eina_tmpstr_add(tmpdirname);
return EINA_TRUE;
}
EINA_API Eina_Bool
eina_file_access(const char *path, Eina_File_Access_Mode mode)
{
DWORD attr;
if (!path || !*path)
return EINA_FALSE;
if ((mode != EINA_FILE_ACCESS_MODE_EXIST) &&
((mode >> 3) != 0))
return EINA_FALSE;
/*
* Always check for existence for both files and directories
*/
attr = GetFileAttributes(path);
if (attr == INVALID_FILE_ATTRIBUTES)
return EINA_FALSE;
/*
* On Windows a file or path is either read/write or read only.
* So if it exists, it has at least read access.
* So do something only if mode is EXEC or WRITE
*/
if (mode & EINA_FILE_ACCESS_MODE_EXEC)
{
if (!(attr & FILE_ATTRIBUTE_DIRECTORY) &&
!eina_str_has_extension(path, ".exe") &&
!eina_str_has_extension(path, ".bat"))
return EINA_FALSE;
}
if (mode & EINA_FILE_ACCESS_MODE_WRITE)
{
if (attr == INVALID_FILE_ATTRIBUTES)
return EINA_FALSE;
if (attr & FILE_ATTRIBUTE_READONLY)
return EINA_FALSE;
}
return EINA_TRUE;
}

View File

@ -897,6 +897,48 @@ EFL_START_TEST(eina_test_file_unlink)
}
EFL_END_TEST
EFL_START_TEST(eina_test_file_access)
{
typedef struct
{
const char *path;
Eina_File_Access_Mode mode;
Eina_Bool expected;
} Paths;
Paths paths[] = {
#ifdef _WIN32
{ "c:\\Windows", EINA_FILE_ACCESS_MODE_EXIST, EINA_TRUE },
{ "c:\\Windows", EINA_FILE_ACCESS_MODE_EXEC, EINA_TRUE },
{ "c:\\Windows\\notepad.exe", EINA_FILE_ACCESS_MODE_EXIST, EINA_TRUE },
{ "c:\\Windows\\notepad.exe", EINA_FILE_ACCESS_MODE_EXEC, EINA_TRUE },
{ "c:\\Windows\\notepad.exe", EINA_FILE_ACCESS_MODE_WRITE, EINA_TRUE },
{ "c:\\Windows\\notepad.exe", EINA_FILE_ACCESS_MODE_READ, EINA_TRUE },
#else
{ "/usr", EINA_FILE_ACCESS_MODE_EXIST, EINA_TRUE },
{ "/usr", EINA_FILE_ACCESS_MODE_EXEC, EINA_TRUE },
{ "/usr", EINA_FILE_ACCESS_MODE_READ, EINA_TRUE },
{ "/root", EINA_FILE_ACCESS_MODE_WRITE, EINA_FALSE },
#endif
{ NULL, EINA_FILE_ACCESS_MODE_EXIST, EINA_FALSE },
};
size_t i;
Eina_Bool result;
result = eina_file_access(NULL, EINA_FILE_ACCESS_MODE_EXIST);
fail_if(result == EINA_TRUE);
result = eina_file_access("", EINA_FILE_ACCESS_MODE_EXIST);
fail_if(result == EINA_TRUE);
for (i = 0; paths[i].path; i++)
{
result = eina_file_access(paths[i].path, paths[i].mode);
fail_if(result != paths[i].expected);
}
}
EFL_END_TEST
void
eina_test_file(TCase *tc)
{
@ -914,5 +956,5 @@ eina_test_file(TCase *tc)
tcase_add_test(tc, eina_test_file_statat);
tcase_add_test(tc, eina_test_file_mktemp);
tcase_add_test(tc, eina_test_file_unlink);
tcase_add_test(tc, eina_test_file_access);
}