summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--gio/gfileinfo.h1
-rw-r--r--gio/glocalfileinfo.c235
3 files changed, 236 insertions, 2 deletions
diff --git a/TODO b/TODO
index a777744f..8169fb55 100644
--- a/TODO
+++ b/TODO
@@ -26,8 +26,6 @@ Finish general attribute copy in gfile.c move fallback.
handle attributes in atomic save backup copy
GFileCopyFlags could have a FAIL_ON_ATTRIBUTE_COPY?
-Set owner:user, owner:group for local files
-
HAL implementation of VolumeMonitor
Handle the conflicting globs (*.pcf) case and fallback to sniffing
diff --git a/gio/gfileinfo.h b/gio/gfileinfo.h
index a71ec096..b800e37c 100644
--- a/gio/gfileinfo.h
+++ b/gio/gfileinfo.h
@@ -105,6 +105,7 @@ struct _GFileInfoClass
/* Owner attributes */
#define G_FILE_ATTRIBUTE_OWNER_USER "owner:user" /* string */
+#define G_FILE_ATTRIBUTE_OWNER_USER_REAL "owner:user_real" /* string */
#define G_FILE_ATTRIBUTE_OWNER_GROUP "owner:group" /* string */
/* File system info (for g_file_get_filesystem_info) */
diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c
index 83d99697..837b6d00 100644
--- a/gio/glocalfileinfo.c
+++ b/gio/glocalfileinfo.c
@@ -7,6 +7,8 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#endif
@@ -32,6 +34,17 @@
#include "gcontenttype.h"
#include "gcontenttypeprivate.h"
+typedef struct {
+ char *user_name;
+ char *real_name;
+} UidData;
+
+G_LOCK_DEFINE_STATIC (uid_cache);
+GHashTable *uid_cache = NULL;
+
+G_LOCK_DEFINE_STATIC (gid_cache);
+GHashTable *gid_cache = NULL;
+
char *
_g_local_file_info_create_etag (struct stat *statbuf)
{
@@ -801,6 +814,197 @@ set_info_from_stat (GFileInfo *info, struct stat *statbuf,
}
+static char *
+make_valid_utf8 (const char *name)
+{
+ GString *string;
+ const gchar *remainder, *invalid;
+ gint remaining_bytes, valid_bytes;
+
+ string = NULL;
+ remainder = name;
+ remaining_bytes = strlen (name);
+
+ while (remaining_bytes != 0)
+ {
+ if (g_utf8_validate (remainder, remaining_bytes, &invalid))
+ break;
+ valid_bytes = invalid - remainder;
+
+ if (string == NULL)
+ string = g_string_sized_new (remaining_bytes);
+
+ g_string_append_len (string, remainder, valid_bytes);
+ /* append U+FFFD REPLACEMENT CHARACTER */
+ g_string_append (string, "\357\277\275");
+
+ remaining_bytes -= valid_bytes + 1;
+ remainder = invalid + 1;
+ }
+
+ if (string == NULL)
+ return g_strdup (name);
+
+ g_string_append (string, remainder);
+
+ g_assert (g_utf8_validate (string->str, -1, NULL));
+
+ return g_string_free (string, FALSE);
+}
+
+static char *
+convert_pwd_string_to_utf8 (char *pwd_str)
+{
+ char *utf8_string;
+
+ if (!g_utf8_validate (pwd_str, -1, NULL))
+ {
+ utf8_string = g_locale_to_utf8 (pwd_str, -1, NULL, NULL, NULL);
+ if (utf8_string == NULL)
+ utf8_string = make_valid_utf8 (pwd_str);
+ }
+ else
+ utf8_string = g_strdup (pwd_str);
+
+ return utf8_string;
+}
+
+static void
+uid_data_free (UidData *data)
+{
+ g_free (data->user_name);
+ g_free (data->real_name);
+ g_free (data);
+}
+
+/* called with lock held */
+static UidData *
+lookup_uid_data (uid_t uid)
+{
+ UidData *data;
+ char buffer[4096];
+ struct passwd pwbuf;
+ struct passwd *pwbufp;
+ char *gecos, *comma;
+
+ if (uid_cache == NULL)
+ uid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)uid_data_free);
+
+ data = g_hash_table_lookup (uid_cache, GINT_TO_POINTER (uid));
+
+ if (data)
+ return data;
+
+ data = g_new0 (UidData, 1);
+
+ getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer), &pwbufp);
+
+ if (pwbufp != NULL)
+ {
+ if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0)
+ data->user_name = convert_pwd_string_to_utf8 (pwbufp->pw_name);
+
+ gecos = pwbufp->pw_gecos;
+
+ if (gecos)
+ {
+ comma = strchr (gecos, ',');
+ if (comma)
+ *comma = 0;
+ data->real_name = convert_pwd_string_to_utf8 (gecos);
+ }
+ }
+
+ /* Default fallbacks */
+ if (data->real_name == NULL)
+ {
+ if (data->user_name != NULL)
+ data->real_name = g_strdup (data->user_name);
+ else
+ data->real_name = g_strdup_printf("user #%d", (int)uid);
+ }
+
+ if (data->user_name == NULL)
+ data->user_name = g_strdup_printf("%d", (int)uid);
+
+ g_hash_table_replace (uid_cache, GINT_TO_POINTER (uid), data);
+
+ return data;
+}
+
+static char *
+get_username_from_uid (uid_t uid)
+{
+ char *res;
+ UidData *data;
+
+ G_LOCK (uid_cache);
+ data = lookup_uid_data (uid);
+ res = g_strdup (data->user_name);
+ G_UNLOCK (uid_cache);
+
+ return res;
+}
+
+static char *
+get_realname_from_uid (uid_t uid)
+{
+ char *res;
+ UidData *data;
+
+ G_LOCK (uid_cache);
+ data = lookup_uid_data (uid);
+ res = g_strdup (data->real_name);
+ G_UNLOCK (uid_cache);
+
+ return res;
+}
+
+
+/* called with lock held */
+static char *
+lookup_gid_name (gid_t gid)
+{
+ char *name;
+ char buffer[4096];
+ struct group gbuf;
+ struct group *gbufp;
+
+ if (gid_cache == NULL)
+ gid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
+
+ name = g_hash_table_lookup (gid_cache, GINT_TO_POINTER (gid));
+
+ if (name)
+ return name;
+
+ getgrgid_r (gid, &gbuf, buffer, sizeof(buffer), &gbufp);
+
+ if (gbufp != NULL &&
+ gbufp->gr_name != NULL &&
+ gbufp->gr_name[0] != 0)
+ name = convert_pwd_string_to_utf8 (gbufp->gr_name);
+ else
+ name = g_strdup_printf("%d", (int)gid);
+
+ g_hash_table_replace (gid_cache, GINT_TO_POINTER (gid), name);
+
+ return name;
+}
+
+static char *
+get_groupname_from_gid (gid_t gid)
+{
+ char *res;
+ char *name;
+
+ G_LOCK (gid_cache);
+ name = lookup_gid_name (gid);
+ res = g_strdup (name);
+ G_UNLOCK (gid_cache);
+ return res;
+}
+
GFileInfo *
_g_local_file_info_get (const char *basename,
const char *path,
@@ -968,6 +1172,37 @@ _g_local_file_info_get (const char *basename,
/* TODO */
}
+ if (g_file_attribute_matcher_matches (attribute_matcher,
+ G_FILE_ATTRIBUTE_OWNER_USER))
+ {
+ char *name;
+
+ name = get_username_from_uid (statbuf.st_uid);
+ if (name)
+ g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER, name);
+ }
+
+ if (g_file_attribute_matcher_matches (attribute_matcher,
+ G_FILE_ATTRIBUTE_OWNER_USER_REAL))
+ {
+ char *name;
+
+ name = get_realname_from_uid (statbuf.st_uid);
+ if (name)
+ g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER_REAL, name);
+ }
+
+ if (g_file_attribute_matcher_matches (attribute_matcher,
+ G_FILE_ATTRIBUTE_OWNER_GROUP))
+ {
+ char *name;
+
+ name = get_groupname_from_gid (statbuf.st_gid);
+ if (name)
+ g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_GROUP, name);
+ }
+
+
get_access_rights (attribute_matcher, info, path, &statbuf, parent_info);
get_selinux_context (path, info, attribute_matcher, (flags & G_FILE_GET_INFO_NOFOLLOW_SYMLINKS) == 0);