mirror of https://gitee.com/openkylin/gvfs.git
631 lines
14 KiB
C
631 lines
14 KiB
C
/* GIO - GLib Input, Output and Streaming Library
|
|
*
|
|
* Copyright (C) 2006-2007 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General
|
|
* Public License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*
|
|
* Author: Alexander Larsson <alexl@redhat.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <glib/gi18n-lib.h>
|
|
|
|
#include "gmountspec.h"
|
|
|
|
static GHashTable *unique_hash = NULL;
|
|
G_LOCK_DEFINE_STATIC(unique_hash);
|
|
|
|
static int
|
|
item_compare (const void *_a, const void *_b)
|
|
{
|
|
const GMountSpecItem *a = _a;
|
|
const GMountSpecItem *b = _b;
|
|
|
|
return strcmp (a->key, b->key);
|
|
}
|
|
|
|
GMountSpec *
|
|
g_mount_spec_new (const char *type)
|
|
{
|
|
GMountSpec *spec;
|
|
|
|
spec = g_new0 (GMountSpec, 1);
|
|
spec->ref_count = 1;
|
|
spec->items = g_array_new (FALSE, TRUE, sizeof (GMountSpecItem));
|
|
spec->mount_prefix = g_strdup ("/");
|
|
|
|
if (type != NULL)
|
|
g_mount_spec_set (spec, "type", type);
|
|
|
|
return spec;
|
|
}
|
|
|
|
/* Takes ownership of passed in data */
|
|
GMountSpec *
|
|
g_mount_spec_new_from_data (GArray *items,
|
|
char *mount_prefix)
|
|
{
|
|
GMountSpec *spec;
|
|
|
|
spec = g_new0 (GMountSpec, 1);
|
|
spec->ref_count = 1;
|
|
spec->items = items;
|
|
if (mount_prefix == NULL)
|
|
spec->mount_prefix = g_strdup ("/");
|
|
else
|
|
spec->mount_prefix = g_mount_spec_canonicalize_path (mount_prefix);
|
|
|
|
g_array_sort (spec->items, item_compare);
|
|
|
|
return spec;
|
|
}
|
|
|
|
GMountSpec *
|
|
g_mount_spec_get_unique_for (GMountSpec *spec)
|
|
{
|
|
GMountSpec *unique_spec;
|
|
|
|
if (spec->is_unique)
|
|
return g_mount_spec_ref (spec);
|
|
|
|
G_LOCK (unique_hash);
|
|
|
|
if (unique_hash == NULL)
|
|
unique_hash = g_hash_table_new (g_mount_spec_hash, (GEqualFunc)g_mount_spec_equal);
|
|
|
|
unique_spec = g_hash_table_lookup (unique_hash, spec);
|
|
|
|
if (unique_spec == NULL)
|
|
{
|
|
spec->is_unique = TRUE;
|
|
g_hash_table_insert (unique_hash, spec, spec);
|
|
unique_spec = spec;
|
|
}
|
|
|
|
g_mount_spec_ref (unique_spec);
|
|
|
|
G_UNLOCK (unique_hash);
|
|
|
|
return unique_spec;
|
|
}
|
|
|
|
void
|
|
g_mount_spec_set_mount_prefix (GMountSpec *spec,
|
|
const char *mount_prefix)
|
|
{
|
|
g_free (spec->mount_prefix);
|
|
spec->mount_prefix = g_mount_spec_canonicalize_path (mount_prefix);
|
|
}
|
|
|
|
|
|
static void
|
|
add_item (GMountSpec *spec,
|
|
const char *key,
|
|
char *value)
|
|
{
|
|
GMountSpecItem item;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
g_return_if_fail (value != NULL);
|
|
|
|
item.key = g_strdup (key);
|
|
item.value = value;
|
|
|
|
g_array_append_val (spec->items, item);
|
|
}
|
|
|
|
static void
|
|
g_mount_spec_set_with_len_internal (GMountSpec *spec,
|
|
const char *key,
|
|
const char *value,
|
|
int value_len,
|
|
gboolean copy)
|
|
{
|
|
int i;
|
|
char *value_copy;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
g_return_if_fail (value != NULL);
|
|
|
|
if (copy)
|
|
{
|
|
if (value_len == -1)
|
|
value_copy = g_strdup (value);
|
|
else
|
|
value_copy = g_strndup (value, value_len);
|
|
}
|
|
else
|
|
value_copy = (char*) value;
|
|
|
|
if (g_str_equal ("prefix", key))
|
|
{
|
|
g_mount_spec_set_mount_prefix (spec, value_copy);
|
|
g_free (value_copy);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < spec->items->len; i++)
|
|
{
|
|
GMountSpecItem *item = &g_array_index (spec->items, GMountSpecItem, i);
|
|
if (strcmp (item->key, key) == 0)
|
|
{
|
|
g_free (item->value);
|
|
item->value = value_copy;
|
|
return;
|
|
}
|
|
}
|
|
|
|
add_item (spec, key, value_copy);
|
|
g_array_sort (spec->items, item_compare);
|
|
}
|
|
|
|
void
|
|
g_mount_spec_set_with_len (GMountSpec *spec,
|
|
const char *key,
|
|
const char *value,
|
|
int value_len)
|
|
{
|
|
g_mount_spec_set_with_len_internal (spec, key, value, value_len, TRUE);
|
|
}
|
|
|
|
void
|
|
g_mount_spec_set (GMountSpec *spec,
|
|
const char *key,
|
|
const char *value)
|
|
{
|
|
g_mount_spec_set_with_len (spec, key, value, -1);
|
|
}
|
|
|
|
void
|
|
g_mount_spec_take (GMountSpec *spec,
|
|
const char *key,
|
|
char *value)
|
|
{
|
|
g_mount_spec_set_with_len_internal (spec, key, value, -1, FALSE);
|
|
}
|
|
|
|
GMountSpec *
|
|
g_mount_spec_copy (GMountSpec *spec)
|
|
{
|
|
GMountSpec *copy;
|
|
int i;
|
|
|
|
copy = g_mount_spec_new (NULL);
|
|
g_mount_spec_set_mount_prefix (copy, spec->mount_prefix);
|
|
|
|
for (i = 0; i < spec->items->len; i++)
|
|
{
|
|
GMountSpecItem *item = &g_array_index (spec->items, GMountSpecItem, i);
|
|
g_mount_spec_set (copy, item->key, item->value);
|
|
}
|
|
|
|
return copy;
|
|
}
|
|
|
|
GMountSpec *
|
|
g_mount_spec_ref (GMountSpec *spec)
|
|
{
|
|
g_atomic_int_inc (&spec->ref_count);
|
|
return spec;
|
|
}
|
|
|
|
void
|
|
g_mount_spec_unref (GMountSpec *spec)
|
|
{
|
|
int i;
|
|
|
|
if (g_atomic_int_dec_and_test (&spec->ref_count))
|
|
{
|
|
G_LOCK (unique_hash);
|
|
if (unique_hash != NULL &&
|
|
spec->is_unique)
|
|
g_hash_table_remove (unique_hash, spec);
|
|
G_UNLOCK (unique_hash);
|
|
|
|
g_free (spec->mount_prefix);
|
|
for (i = 0; i < spec->items->len; i++)
|
|
{
|
|
GMountSpecItem *item = &g_array_index (spec->items, GMountSpecItem, i);
|
|
g_free (item->key);
|
|
g_free (item->value);
|
|
}
|
|
g_array_free (spec->items, TRUE);
|
|
|
|
g_free (spec);
|
|
}
|
|
}
|
|
|
|
GMountSpec *
|
|
g_mount_spec_from_dbus (GVariant *value)
|
|
{
|
|
GMountSpec *spec;
|
|
const gchar *key;
|
|
const gchar *mount_prefix;
|
|
GVariantIter *iter_mount_spec_items;
|
|
GVariant *v;
|
|
|
|
mount_prefix = NULL;
|
|
g_variant_get (value, "(^&aya{sv})",
|
|
&mount_prefix,
|
|
&iter_mount_spec_items);
|
|
|
|
spec = g_mount_spec_new (NULL);
|
|
g_free (spec->mount_prefix);
|
|
spec->mount_prefix = NULL;
|
|
if (mount_prefix && mount_prefix[0])
|
|
spec->mount_prefix = g_strdup (mount_prefix);
|
|
|
|
while (g_variant_iter_loop (iter_mount_spec_items, "{&sv}", &key, &v))
|
|
{
|
|
add_item (spec, key, g_variant_dup_bytestring (v, NULL));
|
|
}
|
|
|
|
g_variant_iter_free (iter_mount_spec_items);
|
|
|
|
/* Sort on key */
|
|
g_array_sort (spec->items, item_compare);
|
|
|
|
return spec;
|
|
}
|
|
|
|
GVariant *
|
|
g_mount_spec_to_dbus_with_path (GMountSpec *spec,
|
|
const char *path)
|
|
{
|
|
GVariantBuilder builder;
|
|
GVariant *v;
|
|
int i;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
|
|
for (i = 0; i < spec->items->len; i++)
|
|
{
|
|
GMountSpecItem *item = &g_array_index (spec->items, GMountSpecItem, i);
|
|
|
|
g_variant_builder_add_value (&builder, g_variant_new ("{sv}",
|
|
item->key,
|
|
g_variant_new_bytestring (item->value)));
|
|
}
|
|
|
|
v = g_variant_new ("(^aya{sv})",
|
|
path ? path : "",
|
|
&builder);
|
|
g_variant_builder_clear (&builder);
|
|
|
|
return v;
|
|
}
|
|
|
|
GVariant *
|
|
g_mount_spec_to_dbus (GMountSpec *spec)
|
|
{
|
|
return g_mount_spec_to_dbus_with_path (spec, spec->mount_prefix);
|
|
}
|
|
|
|
static gboolean
|
|
items_equal (GArray *a,
|
|
GArray *b)
|
|
{
|
|
int i;
|
|
|
|
if (a->len != b->len)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < a->len; i++)
|
|
{
|
|
GMountSpecItem *item_a = &g_array_index (a, GMountSpecItem, i);
|
|
GMountSpecItem *item_b = &g_array_index (b, GMountSpecItem, i);
|
|
|
|
if (strcmp (item_a->key, item_b->key) != 0)
|
|
return FALSE;
|
|
if (strcmp (item_a->value, item_b->value) != 0)
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
path_has_prefix (const char *path,
|
|
const char *prefix)
|
|
{
|
|
int prefix_len;
|
|
|
|
if (prefix == NULL)
|
|
return TRUE;
|
|
|
|
prefix_len = strlen (prefix);
|
|
|
|
if (strncmp (path, prefix, prefix_len) == 0 &&
|
|
(prefix_len == 0 || /* empty prefix always matches */
|
|
prefix[prefix_len - 1] == '/' || /* last char in prefix was a /, so it must be in path too */
|
|
path[prefix_len] == 0 ||
|
|
path[prefix_len] == '/'))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
guint
|
|
g_mount_spec_hash (gconstpointer _mount)
|
|
{
|
|
GMountSpec *mount = (GMountSpec *) _mount;
|
|
guint hash;
|
|
int i;
|
|
|
|
hash = 0;
|
|
if (mount->mount_prefix)
|
|
hash ^= g_str_hash (mount->mount_prefix);
|
|
|
|
for (i = 0; i < mount->items->len; i++)
|
|
{
|
|
GMountSpecItem *item = &g_array_index (mount->items, GMountSpecItem, i);
|
|
hash ^= g_str_hash (item->value);
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
gboolean
|
|
g_mount_spec_equal (GMountSpec *mount1,
|
|
GMountSpec *mount2)
|
|
{
|
|
return items_equal (mount1->items, mount2->items) &&
|
|
((mount1->mount_prefix == mount2->mount_prefix) ||
|
|
(mount1->mount_prefix != NULL && mount2->mount_prefix != NULL &&
|
|
strcmp (mount1->mount_prefix, mount2->mount_prefix) == 0));
|
|
}
|
|
|
|
gboolean
|
|
g_mount_spec_match_with_path (GMountSpec *mount,
|
|
GMountSpec *spec,
|
|
const char *path)
|
|
{
|
|
if (items_equal (mount->items, spec->items) &&
|
|
path_has_prefix (path, mount->mount_prefix))
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
g_mount_spec_match (GMountSpec *mount,
|
|
GMountSpec *path)
|
|
{
|
|
return g_mount_spec_match_with_path (mount, path, path->mount_prefix);
|
|
}
|
|
|
|
const char *
|
|
g_mount_spec_get (GMountSpec *spec,
|
|
const char *key)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < spec->items->len; i++)
|
|
{
|
|
GMountSpecItem *item = &g_array_index (spec->items, GMountSpecItem, i);
|
|
|
|
if (strcmp (item->key, key) == 0)
|
|
return item->value;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const char *
|
|
g_mount_spec_get_type (GMountSpec *spec)
|
|
{
|
|
return g_mount_spec_get (spec, "type");
|
|
}
|
|
|
|
char *
|
|
g_mount_spec_to_string (GMountSpec *spec)
|
|
{
|
|
GString *str;
|
|
int i;
|
|
gboolean first;
|
|
|
|
if (spec == NULL)
|
|
return g_strdup ("(null)");
|
|
|
|
str = g_string_new (g_mount_spec_get_type (spec));
|
|
g_string_append_c (str, ':');
|
|
|
|
first = TRUE;
|
|
for (i = 0; i < spec->items->len; i++)
|
|
{
|
|
GMountSpecItem *item = &g_array_index (spec->items, GMountSpecItem, i);
|
|
|
|
if (strcmp (item->key, "type") == 0)
|
|
continue;
|
|
|
|
if (!first)
|
|
g_string_append_c (str, ',');
|
|
first = FALSE;
|
|
|
|
g_string_append_printf (str, "%s=", item->key);
|
|
g_string_append_uri_escaped (str, item->value,
|
|
"$&'()*+",
|
|
TRUE);
|
|
}
|
|
|
|
if (strcmp (spec->mount_prefix, "/") != 0)
|
|
{
|
|
g_string_append_printf (str, ",prefix=");
|
|
g_string_append_uri_escaped (str, spec->mount_prefix,
|
|
"$&'()*+",
|
|
TRUE);
|
|
}
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
GMountSpec *
|
|
g_mount_spec_new_from_string (const gchar *str,
|
|
GError **error)
|
|
{
|
|
GArray *items;
|
|
GMountSpec *mount_spec;
|
|
char **kv_pairs;
|
|
char *mount_prefix;
|
|
const char *colon;
|
|
GMountSpecItem item;
|
|
int i;
|
|
|
|
g_return_val_if_fail (str != NULL, NULL);
|
|
|
|
mount_spec = NULL;
|
|
mount_prefix = NULL;
|
|
items = g_array_new (FALSE, TRUE, sizeof (GMountSpecItem));
|
|
|
|
colon = strchr (str, ':');
|
|
if (colon)
|
|
{
|
|
item.key = g_strdup ("type");
|
|
item.value = g_strndup (str, colon - str);
|
|
g_array_append_val (items, item);
|
|
str = colon + 1;
|
|
}
|
|
|
|
kv_pairs = g_strsplit (str, ",", 0);
|
|
for (i = 0; kv_pairs[i] != NULL; i++)
|
|
{
|
|
char **tokens;
|
|
|
|
tokens = g_strsplit (kv_pairs[i], "=", 0);
|
|
if (g_strv_length (tokens) != 2)
|
|
{
|
|
g_set_error (error,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_ARGUMENT,
|
|
"Encountered invalid key/value pair '%s' while decoding GMountSpec",
|
|
kv_pairs[i]);
|
|
g_strfreev (tokens);
|
|
g_strfreev (kv_pairs);
|
|
goto fail;
|
|
}
|
|
|
|
item.value = g_uri_unescape_string (tokens[1], NULL);
|
|
if (strcmp (tokens[0], "prefix") == 0)
|
|
{
|
|
g_free (mount_prefix);
|
|
mount_prefix = item.value;
|
|
}
|
|
else
|
|
{
|
|
item.key = g_strdup (tokens[0]);
|
|
g_array_append_val (items, item);
|
|
}
|
|
|
|
g_strfreev (tokens);
|
|
}
|
|
g_strfreev (kv_pairs);
|
|
|
|
if (mount_prefix == NULL)
|
|
mount_prefix = g_strdup ("/");
|
|
|
|
/* this constructor takes ownership of the data we pass in */
|
|
mount_spec = g_mount_spec_new_from_data (items,
|
|
mount_prefix);
|
|
|
|
return mount_spec;
|
|
|
|
fail:
|
|
for (i = 0; i < items->len; i++)
|
|
{
|
|
GMountSpecItem *item = &g_array_index (items, GMountSpecItem, i);
|
|
g_free (item->key);
|
|
g_free (item->value);
|
|
}
|
|
g_array_free (items, TRUE);
|
|
g_free (mount_prefix);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
char *
|
|
g_mount_spec_canonicalize_path (const char *path)
|
|
{
|
|
char *canon, *start, *p, *q;
|
|
|
|
if (*path != '/')
|
|
canon = g_strconcat ("/", path, NULL);
|
|
else
|
|
canon = g_strdup (path);
|
|
|
|
/* Skip initial slash */
|
|
start = canon + 1;
|
|
|
|
p = start;
|
|
while (*p != 0)
|
|
{
|
|
if (p[0] == '.' && (p[1] == 0 || p[1] == '/'))
|
|
{
|
|
memmove (p, p+1, strlen (p+1)+1);
|
|
}
|
|
else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || p[2] == '/'))
|
|
{
|
|
q = p + 2;
|
|
/* Skip previous separator */
|
|
p = p - 2;
|
|
if (p < start)
|
|
p = start;
|
|
while (p > start && *p != '/')
|
|
p--;
|
|
if (*p == '/')
|
|
p++;
|
|
memmove (p, q, strlen (q)+1);
|
|
}
|
|
else
|
|
{
|
|
/* Skip until next separator */
|
|
while (*p != 0 && *p != '/')
|
|
p++;
|
|
|
|
/* Keep one separator */
|
|
if (*p != 0)
|
|
p++;
|
|
}
|
|
|
|
/* Remove additional separators */
|
|
q = p;
|
|
while (*q && *q == '/')
|
|
q++;
|
|
|
|
if (p != q)
|
|
memmove (p, q, strlen (q)+1);
|
|
}
|
|
|
|
/* Remove trailing slashes */
|
|
if (p > start && *(p-1) == '/')
|
|
*(p-1) = 0;
|
|
|
|
return canon;
|
|
}
|
|
|
|
GType
|
|
g_type_mount_spec_get_gtype (void)
|
|
{
|
|
static GType type_id = 0;
|
|
|
|
if (type_id == 0)
|
|
type_id = g_boxed_type_register_static (g_intern_static_string ("GMountSpec"),
|
|
(GBoxedCopyFunc) g_mount_spec_ref,
|
|
(GBoxedFreeFunc) g_mount_spec_unref);
|
|
return type_id;
|
|
}
|