(root)/
glib-2.79.0/
gio/
gwin32mount.c
       1  /* GIO - GLib Input, Output and Streaming Library
       2   * 
       3   * Copyright (C) 2006-2007 Red Hat, Inc.
       4   * Copyright (C) 2008 Hans Breuer
       5   *
       6   * SPDX-License-Identifier: LGPL-2.1-or-later
       7   *
       8   * This library is free software; you can redistribute it and/or
       9   * modify it under the terms of the GNU Lesser General Public
      10   * License as published by the Free Software Foundation; either
      11   * version 2.1 of the License, or (at your option) any later version.
      12   *
      13   * This library is distributed in the hope that it will be useful,
      14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16   * Lesser General Public License for more details.
      17   *
      18   * You should have received a copy of the GNU Lesser General
      19   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      20   *
      21   * Author: Alexander Larsson <alexl@redhat.com>
      22   *         David Zeuthen <davidz@redhat.com>
      23   *         Hans Breuer <hans@breuer.org>
      24   */
      25  
      26  #include "config.h"
      27  
      28  #include <string.h>
      29  #define WIN32_MEAN_AND_LEAN
      30  #define COBJMACROS
      31  #include <windows.h>
      32  #include <shlobj.h>
      33  #include <shlwapi.h>
      34  
      35  /* At the moment of writing IExtractIconW interface in Mingw-w64
      36   * is missing IUnknown members in its vtable. Use our own
      37   * fixed declaration for now.
      38   */
      39  #undef INTERFACE
      40  #define INTERFACE IMyExtractIconW
      41  DECLARE_INTERFACE_(IMyExtractIconW,IUnknown)
      42  {
      43      /*** IUnknown methods ***/
      44      STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
      45      STDMETHOD_(ULONG,AddRef)(THIS) PURE;
      46      STDMETHOD_(ULONG,Release)(THIS) PURE;
      47      /*** IMyExtractIconW methods ***/
      48      STDMETHOD(GetIconLocation)(THIS_ UINT uFlags, LPWSTR pszIconFile, UINT cchMax, int *piIndex, PUINT pwFlags) PURE;
      49      STDMETHOD(Extract)(THIS_ LPCWSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) PURE;
      50  };
      51  #undef INTERFACE
      52  
      53  #if !defined(__cplusplus) || defined(CINTERFACE)
      54  /*** IUnknown methods ***/
      55  #define IMyExtractIconW_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
      56  #define IMyExtractIconW_AddRef(p)             (p)->lpVtbl->AddRef(p)
      57  #define IMyExtractIconW_Release(p)            (p)->lpVtbl->Release(p)
      58  /*** IMyExtractIconW methods ***/
      59  #define IMyExtractIconW_GetIconLocation(p,a,b,c,d,e) (p)->lpVtbl->GetIconLocation(p,a,b,c,d,e)
      60  #define IMyExtractIconW_Extract(p,a,b,c,d,e)         (p)->lpVtbl->Extract(p,a,b,c,d,e)
      61  #else
      62  /*** IUnknown methods ***/
      63  #define IMyExtractIconW_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
      64  #define IMyExtractIconW_AddRef(p)             (p)->AddRef()
      65  #define IMyExtractIconW_Release(p)            (p)->Release()
      66  /*** IMyExtractIconW methods ***/
      67  #define IMyExtractIconW_GetIconLocation(p,a,b,c,d,e) (p)->GetIconLocation(p,a,b,c,d,e)
      68  #define IMyExtractIconW_Extract(p,a,b,c,d,e)         (p)->Extract(p,a,b,c,d,e)
      69  #endif
      70  
      71  
      72  #include <glib.h>
      73  #include "gwin32volumemonitor.h"
      74  #include "gwin32mount.h"
      75  #include "gmount.h"
      76  #include "gfile.h"
      77  #include "gmountprivate.h"
      78  #include "gvolumemonitor.h"
      79  #include "gthemedicon.h"
      80  #include "glibintl.h"
      81  
      82  
      83  struct _GWin32Mount {
      84    GObject parent;
      85  
      86    GVolumeMonitor   *volume_monitor;
      87  
      88    GWin32Volume      *volume; /* owned by volume monitor */
      89    int   drive_type;
      90  
      91    /* why does all this stuff need to be duplicated? It is in volume already! */
      92    char *name;
      93    GIcon *icon;
      94    GIcon *symbolic_icon;
      95    char *mount_path;
      96  
      97    gboolean can_eject;
      98  };
      99  
     100  static void g_win32_mount_mount_iface_init (GMountIface *iface);
     101  
     102  #define g_win32_mount_get_type _g_win32_mount_get_type
     103  G_DEFINE_TYPE_WITH_CODE (GWin32Mount, g_win32_mount, G_TYPE_OBJECT,
     104  			 G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT,
     105  						g_win32_mount_mount_iface_init))
     106  
     107  
     108  static void
     109  g_win32_mount_finalize (GObject *object)
     110  {
     111    GWin32Mount *mount;
     112    
     113    mount = G_WIN32_MOUNT (object);
     114  
     115    if (mount->volume_monitor != NULL)
     116      g_object_unref (mount->volume_monitor);
     117  #if 0
     118    if (mount->volume)
     119      _g_win32_volume_unset_mount (mount->volume, mount);
     120  #endif
     121    /* TODO: g_warn_if_fail (volume->volume == NULL); */
     122  
     123    if (mount->icon != NULL)
     124      g_object_unref (mount->icon);
     125    if (mount->symbolic_icon != NULL)
     126      g_object_unref (mount->symbolic_icon);
     127  
     128    g_free (mount->name);
     129    g_free (mount->mount_path);
     130    
     131    if (G_OBJECT_CLASS (g_win32_mount_parent_class)->finalize)
     132      (*G_OBJECT_CLASS (g_win32_mount_parent_class)->finalize) (object);
     133  }
     134  
     135  static void
     136  g_win32_mount_class_init (GWin32MountClass *klass)
     137  {
     138    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     139  
     140    gobject_class->finalize = g_win32_mount_finalize;
     141  }
     142  
     143  static void
     144  g_win32_mount_init (GWin32Mount *win32_mount)
     145  {
     146  }
     147  
     148  /* wdrive doesn't need to end with a path separator.
     149     wdrive must use backslashes as path separators, not slashes.
     150     IShellFolder::ParseDisplayName() takes non-const string as input,
     151     so wdrive can't be a const string.
     152     Returns the name on success (free with g_free),
     153     NULL otherwise.
     154   */
     155  static gchar *
     156  get_mount_display_name (gunichar2 *wdrive)
     157  {
     158    IShellFolder *desktop;
     159    PIDLIST_RELATIVE volume;
     160    STRRET volume_name;
     161    gchar *result = NULL;
     162  
     163    /* Get the desktop folder object reference */
     164    if (!SUCCEEDED (SHGetDesktopFolder (&desktop)))
     165      return result;
     166  
     167    if (SUCCEEDED (IShellFolder_ParseDisplayName (desktop, NULL, NULL, wdrive, NULL, &volume, NULL)))
     168      {
     169        volume_name.uType = STRRET_WSTR;
     170  
     171        if (SUCCEEDED (IShellFolder_GetDisplayNameOf (desktop, volume, SHGDN_FORADDRESSBAR, &volume_name)))
     172          {
     173            wchar_t *volume_name_wchar;
     174  
     175            if (SUCCEEDED (StrRetToStrW (&volume_name, volume, &volume_name_wchar)))
     176              {
     177                result = g_utf16_to_utf8 (volume_name_wchar, -1, NULL, NULL, NULL);
     178                CoTaskMemFree (volume_name_wchar);
     179              }
     180          }
     181        CoTaskMemFree (volume);
     182      }
     183  
     184    IShellFolder_Release (desktop);
     185  
     186    return result;
     187  }
     188  
     189  static gchar *
     190  _win32_get_displayname (const char *drive)
     191  {
     192    gunichar2 *wdrive = g_utf8_to_utf16 (drive, -1, NULL, NULL, NULL);
     193    gchar *name = get_mount_display_name (wdrive);
     194  
     195    g_free (wdrive);
     196    return name ? name : g_strdup (drive);
     197  }
     198  
     199  /*
     200   * _g_win32_mount_new:
     201   * @volume_monitor: a #GVolumeMonitor.
     202   * @path: a win32 path.
     203   * @volume: usually NULL
     204   * 
     205   * Returns: a #GWin32Mount for the given win32 path.
     206   **/
     207  GWin32Mount *
     208  _g_win32_mount_new (GVolumeMonitor  *volume_monitor,
     209                      const char      *path,
     210                      GWin32Volume    *volume)
     211  {
     212    GWin32Mount *mount;
     213    const gchar *drive = path; //fixme
     214    WCHAR *drive_utf16;
     215  
     216    drive_utf16 = g_utf8_to_utf16 (drive, -1, NULL, NULL, NULL);
     217  
     218  #if 0  
     219    /* No volume for mount: Ignore internal things */
     220    if (volume == NULL && !g_win32_mount_guess_should_display (mount_entry))
     221      return NULL;
     222  #endif
     223  
     224    mount = g_object_new (G_TYPE_WIN32_MOUNT, NULL);
     225    mount->volume_monitor = volume_monitor != NULL ? g_object_ref (volume_monitor) : NULL;
     226    mount->mount_path = g_strdup (path);
     227    mount->drive_type = GetDriveTypeW (drive_utf16);
     228    mount->can_eject = FALSE; /* TODO */
     229    mount->name = _win32_get_displayname (drive);
     230  
     231    /* need to do this last */
     232    mount->volume = volume;
     233  #if 0
     234    if (volume != NULL)
     235      _g_win32_volume_set_mount (volume, mount);
     236  #endif
     237  
     238    g_free (drive_utf16);
     239  
     240    return mount;
     241  }
     242  
     243  void
     244  _g_win32_mount_unmounted (GWin32Mount *mount)
     245  {
     246    if (mount->volume != NULL)
     247      {
     248  #if 0
     249        _g_win32_volume_unset_mount (mount->volume, mount);
     250  #endif
     251        mount->volume = NULL;
     252        g_signal_emit_by_name (mount, "changed");
     253        /* there's really no need to emit mount_changed on the volume monitor 
     254         * as we're going to be deleted.. */
     255      }
     256  }
     257  
     258  void
     259  _g_win32_mount_unset_volume (GWin32Mount  *mount,
     260  			     GWin32Volume *volume)
     261  {
     262    if (mount->volume == volume)
     263      {
     264        mount->volume = NULL;
     265        /* TODO: Emit changed in idle to avoid locking issues */
     266        g_signal_emit_by_name (mount, "changed");
     267        if (mount->volume_monitor != NULL)
     268          g_signal_emit_by_name (mount->volume_monitor, "mount-changed", mount);
     269      }
     270  }
     271  
     272  static GFile *
     273  g_win32_mount_get_root (GMount *mount)
     274  {
     275    GWin32Mount *win32_mount = G_WIN32_MOUNT (mount);
     276  
     277    return g_file_new_for_path (win32_mount->mount_path);
     278  }
     279  
     280  static const char *
     281  _win32_drive_type_to_icon (int type, gboolean use_symbolic)
     282  {
     283    switch (type)
     284    {
     285    case DRIVE_REMOVABLE : return use_symbolic ? "drive-removable-media-symbolic" : "drive-removable-media";
     286    case DRIVE_FIXED : return use_symbolic ? "drive-harddisk-symbolic" : "drive-harddisk";
     287    case DRIVE_REMOTE : return use_symbolic ? "folder-remote-symbolic" : "folder-remote";
     288    case DRIVE_CDROM : return use_symbolic ? "drive-optical-symbolic" : "drive-optical";
     289    default : return use_symbolic ? "folder-symbolic" : "folder";
     290    }
     291  }
     292  
     293  /* mount_path doesn't need to end with a path separator.
     294     mount_path must use backslashes as path separators, not slashes.
     295     IShellFolder::ParseDisplayName() takes non-const string as input,
     296     so mount_path can't be a const string.
     297     result_name and result_index must not be NULL.
     298     Returns TRUE when result_name is set (free with g_free),
     299     FALSE otherwise.
     300   */
     301  static gboolean
     302  get_icon_name_index (wchar_t  *mount_path,
     303                       wchar_t **result_name,
     304                       int      *result_index)
     305  {
     306    IShellFolder *desktop;
     307    PIDLIST_RELATIVE volume;
     308    IShellFolder *volume_parent;
     309    PCUITEMID_CHILD volume_relative;
     310    IMyExtractIconW *eicon;
     311    int icon_index;
     312    UINT icon_flags;
     313    wchar_t *name_buffer;
     314    gsize name_buffer_size;
     315    gsize arbitrary_reasonable_limit = 5000;
     316    gboolean result = FALSE;
     317  
     318    *result_name = NULL;
     319  
     320    /* Get the desktop folder object reference */
     321    if (!SUCCEEDED (SHGetDesktopFolder (&desktop)))
     322      return FALSE;
     323  
     324    /* Construct the volume IDList relative to desktop */
     325    if (SUCCEEDED (IShellFolder_ParseDisplayName (desktop, NULL, NULL, mount_path, NULL, &volume, NULL)))
     326      {
     327        /* Get the parent of the volume (transfer-full) and the IDList relative to parent (transfer-none) */
     328        if (SUCCEEDED (SHBindToParent (volume, &IID_IShellFolder, (void **) &volume_parent, &volume_relative)))
     329          {
     330            /* Get a reference to IExtractIcon object for the volume */
     331            if (SUCCEEDED (IShellFolder_GetUIObjectOf (volume_parent, NULL, 1, (LPCITEMIDLIST *) &volume_relative, &IID_IExtractIconW, NULL, (void **) &eicon)))
     332              {
     333                gboolean keep_going = TRUE;
     334                name_buffer = NULL;
     335                name_buffer_size = MAX_PATH / 2;
     336                while (keep_going)
     337                  {
     338                    name_buffer_size *= 2;
     339                    name_buffer = g_renew (wchar_t, name_buffer, name_buffer_size);
     340                    name_buffer[name_buffer_size - 1] = 0x1; /* sentinel */
     341                    keep_going = FALSE;
     342  
     343                    /* Try to get the icon location */
     344                    if (SUCCEEDED (IMyExtractIconW_GetIconLocation (eicon, GIL_FORSHELL, name_buffer, name_buffer_size, &icon_index, &icon_flags)))
     345                      {
     346                        if (name_buffer[name_buffer_size - 1] != 0x1)
     347                          {
     348                            if (name_buffer_size < arbitrary_reasonable_limit)
     349                              {
     350                                /* Buffer was too small, keep going */
     351                                keep_going = TRUE;
     352                                continue;
     353                              }
     354                            /* Else stop trying */
     355                          }
     356                        /* name_buffer might not contain a name */
     357                        else if ((icon_flags & GIL_NOTFILENAME) != GIL_NOTFILENAME)
     358                          {
     359                            *result_name = g_steal_pointer (&name_buffer);
     360                            *result_index = icon_index;
     361                            result = TRUE;
     362                          }
     363                      }
     364                  }
     365  
     366                g_free (name_buffer);
     367                IMyExtractIconW_Release (eicon);
     368              }
     369            IShellFolder_Release (volume_parent);
     370          }
     371        CoTaskMemFree (volume);
     372      }
     373  
     374    IShellFolder_Release (desktop);
     375  
     376    return result;
     377  }
     378  
     379  static GIcon *
     380  g_win32_mount_get_icon (GMount *mount)
     381  {
     382    GWin32Mount *win32_mount = G_WIN32_MOUNT (mount);
     383  
     384    g_return_val_if_fail (win32_mount->mount_path != NULL, NULL);
     385  
     386    /* lazy creation */
     387    if (!win32_mount->icon)
     388      {
     389        wchar_t *icon_path;
     390        int icon_index;
     391        wchar_t *p;
     392        wchar_t *wfn = g_utf8_to_utf16 (win32_mount->mount_path, -1, NULL, NULL, NULL);
     393  
     394        for (p = wfn; p != NULL && *p != 0; p++)
     395          if (*p == L'/')
     396            *p = L'\\';
     397  
     398        if (get_icon_name_index (wfn, &icon_path, &icon_index))
     399          {
     400  	  gchar *id = g_strdup_printf ("%S,%i", icon_path, icon_index);
     401  	  g_free (icon_path);
     402  	  win32_mount->icon = g_themed_icon_new (id);
     403  	  g_free (id);
     404  	}
     405        else
     406          {
     407            win32_mount->icon = g_themed_icon_new_with_default_fallbacks (_win32_drive_type_to_icon (win32_mount->drive_type, FALSE));
     408  	}
     409  
     410        g_free (wfn);
     411      }
     412  
     413    return g_object_ref (win32_mount->icon);
     414  }
     415  
     416  static GIcon *
     417  g_win32_mount_get_symbolic_icon (GMount *mount)
     418  {
     419    GWin32Mount *win32_mount = G_WIN32_MOUNT (mount);
     420  
     421    g_return_val_if_fail (win32_mount->mount_path != NULL, NULL);
     422  
     423    /* lazy creation */
     424    if (!win32_mount->symbolic_icon)
     425      {
     426        win32_mount->symbolic_icon = g_themed_icon_new_with_default_fallbacks (_win32_drive_type_to_icon (win32_mount->drive_type, TRUE));
     427      }
     428  
     429    return g_object_ref (win32_mount->symbolic_icon);
     430  }
     431  
     432  static char *
     433  g_win32_mount_get_uuid (GMount *mount)
     434  {
     435    return NULL;
     436  }
     437  
     438  static char *
     439  g_win32_mount_get_name (GMount *mount)
     440  {
     441    GWin32Mount *win32_mount = G_WIN32_MOUNT (mount);
     442    
     443    return g_strdup (win32_mount->name);
     444  }
     445  
     446  static GDrive *
     447  g_win32_mount_get_drive (GMount *mount)
     448  {
     449    GWin32Mount *win32_mount = G_WIN32_MOUNT (mount);
     450  
     451    if (win32_mount->volume != NULL)
     452      return g_volume_get_drive (G_VOLUME (win32_mount->volume));
     453  
     454    return NULL;
     455  }
     456  
     457  static GVolume *
     458  g_win32_mount_get_volume (GMount *mount)
     459  {
     460    GWin32Mount *win32_mount = G_WIN32_MOUNT (mount);
     461  
     462    if (win32_mount->volume)
     463      return G_VOLUME (g_object_ref (win32_mount->volume));
     464    
     465    return NULL;
     466  }
     467  
     468  static gboolean
     469  g_win32_mount_can_unmount (GMount *mount)
     470  {
     471    return FALSE;
     472  }
     473  
     474  static gboolean
     475  g_win32_mount_can_eject (GMount *mount)
     476  {
     477    GWin32Mount *win32_mount = G_WIN32_MOUNT (mount);
     478    return win32_mount->can_eject;
     479  }
     480  
     481  
     482  typedef struct {
     483    GWin32Mount *win32_mount;
     484    GAsyncReadyCallback callback;
     485    gpointer user_data;
     486    GCancellable *cancellable;
     487    int error_fd;
     488    GIOChannel *error_channel;
     489    guint error_channel_source_id;
     490    GString *error_string;
     491  } UnmountEjectOp;
     492  
     493  static void
     494  g_win32_mount_unmount (GMount              *mount,
     495  		       GMountUnmountFlags   flags,
     496  		       GCancellable        *cancellable,
     497  		       GAsyncReadyCallback  callback,
     498  		       gpointer             user_data)
     499  {
     500  }
     501  
     502  static gboolean
     503  g_win32_mount_unmount_finish (GMount        *mount,
     504  			      GAsyncResult  *result,
     505  			      GError       **error)
     506  {
     507    return FALSE;
     508  }
     509  
     510  static void
     511  g_win32_mount_eject (GMount              *mount,
     512  		     GMountUnmountFlags   flags,
     513  		     GCancellable        *cancellable,
     514  		     GAsyncReadyCallback  callback,
     515  		     gpointer             user_data)
     516  {
     517  }
     518  
     519  static gboolean
     520  g_win32_mount_eject_finish (GMount        *mount,
     521  			    GAsyncResult  *result,
     522  			    GError       **error)
     523  {
     524    return FALSE;
     525  }
     526  
     527  static void
     528  g_win32_mount_mount_iface_init (GMountIface *iface)
     529  {
     530    iface->get_root = g_win32_mount_get_root;
     531    iface->get_name = g_win32_mount_get_name;
     532    iface->get_icon = g_win32_mount_get_icon;
     533    iface->get_symbolic_icon = g_win32_mount_get_symbolic_icon;
     534    iface->get_uuid = g_win32_mount_get_uuid;
     535    iface->get_drive = g_win32_mount_get_drive;
     536    iface->get_volume = g_win32_mount_get_volume;
     537    iface->can_unmount = g_win32_mount_can_unmount;
     538    iface->can_eject = g_win32_mount_can_eject;
     539    iface->unmount = g_win32_mount_unmount;
     540    iface->unmount_finish = g_win32_mount_unmount_finish;
     541    iface->eject = g_win32_mount_eject;
     542    iface->eject_finish = g_win32_mount_eject_finish;
     543  }