This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch master
in repository efl.

View the commit online.

commit 9e5bb18af1fe7c360b13ed93d422c4f3d001ae6f
Author: Vincent Torri <vto...@outlook.fr>
AuthorDate: Thu Jan 18 03:54:03 2024 +0100

    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.
---
 src/lib/eina/eina_file.h        |  39 ++++++++++
 src/lib/eina/eina_file_posix.c  |  10 +++
 src/lib/eina/eina_file_win32.c  | 160 ++++++++++++++++++++++++++++++++++++++++
 src/tests/eina/eina_test_file.c |  45 ++++++++++-
 4 files changed, 253 insertions(+), 1 deletion(-)

diff --git a/src/lib/eina/eina_file.h b/src/lib/eina/eina_file.h
index b180bb6a1d..316b94db45 100644
--- a/src/lib/eina/eina_file.h
+++ b/src/lib/eina/eina_file.h
@@ -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);
+
 /**
  * @}
  */
diff --git a/src/lib/eina/eina_file_posix.c b/src/lib/eina/eina_file_posix.c
index c92a56ca57..41807e7c79 100644
--- a/src/lib/eina/eina_file_posix.c
+++ b/src/lib/eina/eina_file_posix.c
@@ -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;
+}
diff --git a/src/lib/eina/eina_file_win32.c b/src/lib/eina/eina_file_win32.c
index 5f16788929..76113516ce 100644
--- a/src/lib/eina/eina_file_win32.c
+++ b/src/lib/eina/eina_file_win32.c
@@ -549,6 +549,93 @@ eina_file_cleanup(Eina_Tmpstr *path)
    return result;
 }
 
+static Eina_Bool
+_eina_file_is_binary(const char *path)
+{
+   HANDLE h;
+   HANDLE fm;
+   unsigned char *base;
+   unsigned char *base_nt;
+   LARGE_INTEGER sz;
+   WORD characteristics;
+   Eina_Bool res;
+
+   res = EINA_FALSE;
+
+   /*
+    * we parse the file to check if it is a PE file (EXE or DLL)
+    * and we finally check whether it's a DLL or not.
+    * Reference :
+    * https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
+    */
+
+   h = CreateFile(path,
+                  GENERIC_READ,
+                  FILE_SHARE_READ,
+                  NULL,
+                  OPEN_EXISTING,
+                  FILE_ATTRIBUTE_NORMAL,
+                  NULL);
+   if (h == INVALID_HANDLE_VALUE)
+     return EINA_FALSE;
+
+   if (!GetFileSizeEx(h, &sz))
+     goto close_h;
+
+   /* a PE file must contain at least the DOS and NT headers */
+   if (sz.QuadPart < (LONGLONG)(sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)))
+     goto close_h;
+
+   fm = CreateFileMapping(h, NULL, PAGE_READONLY, 0, 0, NULL);
+   if (fm == NULL)
+     goto close_h;
+
+   base = (unsigned char *)MapViewOfFile(fm, FILE_MAP_READ, 0, 0, 0);
+   CloseHandle(fm);
+   if (base == NULL)
+     goto close_h;
+
+   /*
+    * the PE file begins with the DOS header.
+    * First magic number : the DOS header must begin with a DOS magic
+    * number, that is "MZ", that is 0x5a4d, stored in a WORD.
+    */
+   if (*((WORD *)base) != 0x5a4d)
+     goto unmap_view;
+
+   /*
+    * The position of the NT header is located at the offset 0x3c.
+    */
+   base_nt = base + *((DWORD *)(base + 0x3c));
+
+   /*
+    * The NT header begins with the magic number "PE\0\0", that is
+    * 0x00004550, stored in a DWORD.
+    */
+   if (*((DWORD *)base_nt) != 0x00004550)
+     goto unmap_view;
+
+   /*
+    * to get informations about executable (EXE or DLL), we look at
+    * the 'Characteristics' member of the NT header, located at the offset
+    * 22 (4 for the magic number, 18 for the offset) from base_nt.
+    * https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#characteristics
+    */
+   characteristics = *((WORD *)(base_nt + 4 + 18));
+
+   /*
+    * 0x0002 : if set, EXE or DLL
+    * 0x2000 : if set, DLL
+    */
+   res = (characteristics & 0x0002) && !(characteristics & 0x2000);
+
+ unmap_view:
+   UnmapViewOfFile(base);
+ close_h:
+   CloseHandle(h);
+   return res;
+}
+
 /*============================================================================*
  *                                   API                                      *
  *============================================================================*/
@@ -1286,3 +1373,76 @@ 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)
+          {
+             /*
+              * Some directories have restricted access, like
+              * c:\Windows\System32\WDI
+              */
+             HANDLE h;
+             Eina_Bool ret = EINA_FALSE;
+
+             h = CreateFile(path,
+                            GENERIC_READ,
+                            FILE_SHARE_READ,
+                            NULL,
+                            OPEN_EXISTING,
+                            FILE_ATTRIBUTE_ARCHIVE | FILE_FLAG_BACKUP_SEMANTICS,
+                            NULL);
+             ret = (h == INVALID_HANDLE_VALUE);
+             CloseHandle(h);
+             if (ret)
+               return EINA_FALSE;
+          }
+        else
+          {
+             /*
+              * For files, check if it is a binary.
+              */
+             if (!_eina_file_is_binary(path))
+               return EINA_FALSE;
+          }
+     }
+
+   if (mode & EINA_FILE_ACCESS_MODE_WRITE)
+     {
+        DWORD attr;
+
+        attr = GetFileAttributes(path);
+        if (attr == INVALID_FILE_ATTRIBUTES)
+          return EINA_FALSE;
+
+        if (attr & FILE_ATTRIBUTE_READONLY)
+          return EINA_FALSE;
+     }
+
+   return EINA_TRUE;
+}
diff --git a/src/tests/eina/eina_test_file.c b/src/tests/eina/eina_test_file.c
index 0c71600360..915ce74446 100644
--- a/src/tests/eina/eina_test_file.c
+++ b/src/tests/eina/eina_test_file.c
@@ -897,6 +897,49 @@ 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\\System32\\WDI", EINA_FILE_ACCESS_MODE_EXEC, EINA_FALSE },
+      { "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 +957,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);
 }

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to