From fde35591e3eb98f89ef872fb9b3132bf585414b0 Mon Sep 17 00:00:00 2001
From: Steve Lhomme <robux4@ycbcr.xyz>
Date: Wed, 5 Mar 2025 15:30:46 +0100
Subject: [PATCH] unc: use Windows API to handle shares

Using the wide char API allows handling pathes with non ANSI characters.

Using the native Windows API allows retrieving just the flags we want.

This should work in UWP apart from the network connection with the credentials.
---
 modules/access/unc.c | 86 +++++++++++++++++++++++++++-----------------
 1 file changed, 53 insertions(+), 33 deletions(-)

diff --git a/modules/access/unc.c b/modules/access/unc.c
index 159024d71474..75070c47f8e5 100644
--- a/modules/access/unc.c
+++ b/modules/access/unc.c
@@ -45,8 +45,8 @@
 
 typedef struct
 {
-    int i_smb;
-    uint64_t size;
+    HANDLE h;
+    LARGE_INTEGER size;
     vlc_url_t url;
 } access_sys_t;
 
@@ -120,15 +120,16 @@ static int Seek( stream_t *p_access, uint64_t i_pos )
     access_sys_t *p_sys = p_access->p_sys;
     int64_t      i_ret;
 
-    if( i_pos >= INT64_MAX )
+    if( i_pos >= LONG_LONG_MAX )
         return VLC_EGENERIC;
 
     msg_Dbg( p_access, "seeking to %"PRId64, i_pos );
 
-    i_ret = lseek( p_sys->i_smb, i_pos, SEEK_SET );
-    if( i_ret == -1 )
+    LARGE_INTEGER pos;
+    pos.QuadPart = i_pos;
+    if( !SetFilePointerEx( p_sys->h, pos, NULL, FILE_BEGIN) )
     {
-        msg_Err( p_access, "seek failed (%s)", vlc_strerror_c(errno) );
+        msg_Err( p_access, "seek failed (%lu)", GetLastError() );
         return VLC_EGENERIC;
     }
 
@@ -138,13 +139,12 @@ static int Seek( stream_t *p_access, uint64_t i_pos )
 static ssize_t Read( stream_t *p_access, void *p_buffer, size_t i_len )
 {
     access_sys_t *p_sys = p_access->p_sys;
-    int i_read;
+    DWORD i_read;
 
-    i_read = read( p_sys->i_smb, p_buffer, i_len );
-    if( i_read < 0 )
+    if( !ReadFile(p_sys->h, p_buffer, i_len, &i_read, NULL) )
     {
-        msg_Err( p_access, "read failed (%s)", vlc_strerror_c(errno) );
-        i_read = 0;
+        msg_Err( p_access, "read failed (%lu)", GetLastError() );
+        return -1;
     }
 
     return i_read;
@@ -246,7 +246,9 @@ static int Control( stream_t *p_access, int i_query, va_list args )
     case STREAM_GET_SIZE:
         if( p_access->pf_readdir != NULL )
             return VLC_EGENERIC;
-        *va_arg( args, uint64_t * ) = sys->size;
+        if( sys->size.QuadPart == LONG_LONG_MIN )
+            return VLC_EGENERIC;
+        *va_arg( args, uint64_t * ) = sys->size.QuadPart;
         break;
 
     case STREAM_GET_PTS_DELAY:
@@ -270,9 +272,9 @@ static int Open(vlc_object_t *obj)
     stream_t *access = (stream_t *)obj;
     vlc_url_t url;
     vlc_credential credential;
-    char *psz_decoded_path = NULL, *psz_uri = NULL, *psz_var_domain = NULL;
-    int fd;
-    uint64_t size;
+    char *psz_decoded_path = NULL, *psz_var_domain = NULL;
+    HANDLE h;
+    wchar_t *w_uri;
     bool is_dir;
 
     if (vlc_UrlParseFixup(&url, access->psz_url) != 0)
@@ -305,7 +307,7 @@ static int Open(vlc_object_t *obj)
 
     for (;;)
     {
-        struct stat st;
+        char *psz_uri;
 
         if (smb_get_uri(access, &psz_uri,
                         credential.psz_username, credential.psz_password,
@@ -318,19 +320,20 @@ static int Open(vlc_object_t *obj)
             return VLC_ENOMEM;
         }
 
-        if (stat(psz_uri, &st) == 0)
+        w_uri = ToWide( psz_uri );
+        free( psz_uri );
+        if( unlikely(w_uri == NULL) )
+            break;
+
+        DWORD f = GetFileAttributesW( w_uri );
+        if (f == INVALID_FILE_ATTRIBUTES)
         {
-            is_dir = S_ISDIR(st.st_mode) != 0;
-            size = st.st_size;
+            free( w_uri );
+            w_uri = NULL;
             break;
         }
 
-        /* stat() fails with servers or shares. Assume directory. */
-        is_dir = true;
-        size = 0;
-
-        if (errno != EACCES)
-            break;
+        is_dir = (f & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
 
         errno = 0;
         if (vlc_credential_get(&credential, access, "smb-user",
@@ -344,11 +347,17 @@ static int Open(vlc_object_t *obj)
     free(psz_var_domain);
     free(psz_decoded_path);
 
+    if ( w_uri == NULL)
+    {
+        vlc_UrlClean(&url);
+        return VLC_EGENERIC;
+    }
+
     /* Init access */
     access_sys_t *sys = vlc_obj_calloc(obj, 1, sizeof (*sys));
     if (unlikely(sys == NULL))
     {
-        free(psz_uri);
+        free(w_uri);
         vlc_UrlClean(&url);
         return VLC_ENOMEM;
     }
@@ -360,20 +369,31 @@ static int Open(vlc_object_t *obj)
         sys->url = url;
         access->pf_readdir = DirRead;
         access->pf_control = access_vaDirectoryControlHelper;
-        fd = -1;
+        h = INVALID_HANDLE_VALUE;
     }
     else
     {
+        vlc_UrlClean(&url);
+        h = CreateFileW(w_uri, GENERIC_READ,
+            FILE_SHARE_READ | FILE_SHARE_WRITE,
+            NULL, OPEN_EXISTING,
+            FILE_FLAG_RANDOM_ACCESS, NULL);
+        if (h == INVALID_HANDLE_VALUE)
+        {
+            free(w_uri);
+            return VLC_EACCES;
+        }
+
         access->pf_read = Read;
         access->pf_control = Control;
         access->pf_seek = Seek;
-        fd = open(psz_uri, O_RDONLY, 0);
-        vlc_UrlClean(&url);
+
+        if (!GetFileSizeEx(h, &sys->size))
+            sys->size.QuadPart = LONG_LONG_MIN;
     }
-    free(psz_uri);
+    free(w_uri);
 
-    sys->size = size;
-    sys->i_smb = fd;
+    sys->h = h;
 
     return VLC_SUCCESS;
 }
@@ -384,7 +404,7 @@ static void Close(vlc_object_t *obj)
     access_sys_t *sys = access->p_sys;
 
     vlc_UrlClean(&sys->url);
-    close(sys->i_smb);
+    CloseHandle(sys->h);
 }
 
 vlc_module_begin()
-- 
GitLab