diff options
author | Bastien Nocera <hadess@hadess.net> | 2016-03-23 19:34:57 +0100 |
---|---|---|
committer | Bastien Nocera <hadess@hadess.net> | 2016-06-09 12:49:57 +0200 |
commit | e5f242f191262e958c73b9a4df4b952bfbfb77f1 (patch) | |
tree | fe28a27189775e985e457c5db3dd3c8dc49fd314 | |
parent | 1655b3dbb6fb94e54af25ca4cb4c74a8859d9893 (diff) | |
download | gvfs-e5f242f191262e958c73b9a4df4b952bfbfb77f1.tar.gz |
afc: Limit the number of HouseArrest services we start
To avoid being unable to navigate within applications because we ran out
of HouseArrest services, keep track of file operations within each
application, and disconnect from unused ones when trying to access an
unconnected one.
The tracking only covers file operations, and not enumerations, and the
garbage collection algorithm is quite naive, but could easily be
extended.
https://bugzilla.gnome.org/show_bug.cgi?id=676188
-rw-r--r-- | daemon/gvfsbackendafc.c | 152 |
1 files changed, 131 insertions, 21 deletions
diff --git a/daemon/gvfsbackendafc.c b/daemon/gvfsbackendafc.c index 45ce9687..b2285d47 100644 --- a/daemon/gvfsbackendafc.c +++ b/daemon/gvfsbackendafc.c @@ -51,6 +51,7 @@ typedef enum { typedef struct { guint64 fd; afc_client_t afc_cli; + char *app; } FileHandle; typedef struct { @@ -58,6 +59,7 @@ typedef struct { char *id; char *icon_path; house_arrest_client_t house_arrest; + guint num_users; afc_client_t afc_cli; } AppInfo; @@ -856,6 +858,100 @@ out: return !retry; } +static FileHandle * +g_vfs_backend_file_handle_new (GVfsBackendAfc *self, + const char *app) +{ + AppInfo *info; + FileHandle *handle; + + handle = g_new0 (FileHandle, 1); + + if (app == NULL) + return handle; + + g_mutex_lock (&self->apps_lock); + info = g_hash_table_lookup (self->apps, app); + info->num_users++; + g_mutex_unlock (&self->apps_lock); + + handle->app = g_strdup (app); + + return handle; +} + +static void +g_vfs_backend_file_handle_free (GVfsBackendAfc *self, + FileHandle *fh) +{ + AppInfo *info; + + if (fh == NULL) + return; + + if (fh->app == NULL) + goto out; + + g_mutex_lock (&self->apps_lock); + info = g_hash_table_lookup (self->apps, fh->app); + g_assert (info->num_users != 0); + info->num_users--; + g_mutex_unlock (&self->apps_lock); + +out: + if (self->connected) + afc_file_close (fh->afc_cli, fh->fd); + g_free (fh->app); + g_free (fh); +} + +/* If we succeeded in removing access to at least one + * HouseArrest service, return TRUE */ +static gboolean +g_vfs_backend_gc_house_arrest (GVfsBackendAfc *self, + const char *app) +{ + GList *apps, *l; + gboolean ret = FALSE; + + g_mutex_lock (&self->apps_lock); + + apps = g_hash_table_get_values (self->apps); + /* XXX: We might want to sort the apps so the + * oldest used gets cleaned up first */ + + for (l = apps; l != NULL; l = l->next) + { + AppInfo *info = l->data; + + /* Don't close the same app we're trying to + * connect to the service, but return as it's + * already setup */ + if (g_strcmp0 (info->id, app) == 0) + { + g_debug ("A HouseArrest service for '%s' is already setup\n", app); + ret = TRUE; + break; + } + + if (info->afc_cli == NULL || + info->num_users > 0) + continue; + + g_clear_pointer (&info->afc_cli, afc_client_free); + g_clear_pointer (&info->house_arrest, house_arrest_client_free); + + g_debug ("Managed to free HouseArrest service from '%s', for '%s'\n", + info->id, app); + ret = TRUE; + break; + } + + g_mutex_unlock (&self->apps_lock); + + return ret; +} + /* If force_afc_mount is TRUE, then we'll try to mount * the app if there's one in the path, otherwise, we'll hold on */ static char * @@ -898,7 +994,12 @@ g_vfs_backend_parse_house_arrest_path (GVfsBackendAfc *self, if (app != NULL && setup_afc) { - g_vfs_backend_setup_afc_for_app (self, app); + if (!g_vfs_backend_setup_afc_for_app (self, app)) + { + g_debug ("Ran out of HouseArrest clients for app '%s', trying again\n", app); + g_vfs_backend_gc_house_arrest (self, app); + g_vfs_backend_setup_afc_for_app (self, app); + } } return app; @@ -915,13 +1016,13 @@ g_vfs_backend_afc_open_for_read (GVfsBackend *backend, char *new_path; afc_client_t afc_cli; FileHandle *handle; + char *app = NULL; self = G_VFS_BACKEND_AFC(backend); g_return_if_fail (self->connected); if (self->mode == ACCESS_MODE_HOUSE_ARREST) { - char *app; AppInfo *info; gboolean is_doc_root; @@ -935,7 +1036,6 @@ g_vfs_backend_afc_open_for_read (GVfsBackend *backend, goto not_found_bail; afc_cli = info->afc_cli; - g_free (app); } else { @@ -947,6 +1047,7 @@ g_vfs_backend_afc_open_for_read (GVfsBackend *backend, { is_dir_bail: g_free (new_path); + g_free (app); g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, _("Can't open directory")); @@ -957,6 +1058,7 @@ is_dir_bail: { not_found_bail: g_free (new_path); + g_free (app); g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("File doesn't exist")); @@ -967,13 +1069,16 @@ not_found_bail: new_path ? new_path : path, AFC_FOPEN_RDONLY, &fd), G_VFS_JOB(job)))) { + g_free (app); return; } - handle = g_new0 (FileHandle, 1); + handle = g_vfs_backend_file_handle_new (self, app); handle->fd = fd; handle->afc_cli = afc_cli; + g_free (app); + g_vfs_job_open_for_read_set_handle (job, handle); g_vfs_job_open_for_read_set_can_seek (job, TRUE); g_vfs_job_succeeded (G_VFS_JOB(job)); @@ -990,7 +1095,7 @@ g_vfs_backend_afc_create (GVfsBackend *backend, { uint64_t fd = 0; GVfsBackendAfc *self; - char *new_path, *app; + char *new_path, *app = NULL; afc_client_t afc_cli; FileHandle *fh; @@ -1012,11 +1117,11 @@ g_vfs_backend_afc_create (GVfsBackend *backend, info = g_hash_table_lookup (self->apps, app); if (info == NULL) { + g_free (app); g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB(job)); return; } afc_cli = info->afc_cli; - g_free (app); } else { @@ -1029,15 +1134,18 @@ g_vfs_backend_afc_create (GVfsBackend *backend, G_VFS_JOB(job)))) { g_free (new_path); + g_free (app); return; } g_free (new_path); - fh = g_new0 (FileHandle, 1); + fh = g_vfs_backend_file_handle_new (self, app); fh->fd = fd; fh->afc_cli = afc_cli; + g_free (app); + g_vfs_job_open_for_write_set_handle (job, fh); g_vfs_job_open_for_write_set_can_seek (job, TRUE); g_vfs_job_open_for_write_set_can_truncate (job, TRUE); @@ -1056,7 +1164,7 @@ g_vfs_backend_afc_append_to (GVfsBackend *backend, uint64_t fd = 0; uint64_t off = 0; GVfsBackendAfc *self; - char *new_path, *app; + char *new_path, *app = NULL; afc_client_t afc_cli; FileHandle *fh; @@ -1078,11 +1186,11 @@ g_vfs_backend_afc_append_to (GVfsBackend *backend, info = g_hash_table_lookup (self->apps, app); if (info == NULL) { + g_free (app); g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB(job)); return; } afc_cli = info->afc_cli; - g_free (app); } else { @@ -1095,6 +1203,7 @@ g_vfs_backend_afc_append_to (GVfsBackend *backend, G_VFS_JOB(job)))) { g_free (new_path); + g_free (app); return; } @@ -1105,6 +1214,7 @@ g_vfs_backend_afc_append_to (GVfsBackend *backend, G_VFS_JOB(job)))) { afc_file_close (afc_cli, fd); + g_free (app); return; } @@ -1113,13 +1223,16 @@ g_vfs_backend_afc_append_to (GVfsBackend *backend, G_VFS_JOB(job)))) { afc_file_close (afc_cli, fd); + g_free (app); return; } - fh = g_new0 (FileHandle, 1); + fh = g_vfs_backend_file_handle_new (self, app); fh->fd = fd; fh->afc_cli = afc_cli; + g_free (app); + g_vfs_job_open_for_write_set_handle (job, fh); g_vfs_job_open_for_write_set_can_seek (job, TRUE); g_vfs_job_open_for_write_set_can_truncate (job, TRUE); @@ -1139,7 +1252,7 @@ g_vfs_backend_afc_replace (GVfsBackend *backend, { uint64_t fd = 0; GVfsBackendAfc *self; - char *new_path, *app; + char *new_path, *app = NULL; afc_client_t afc_cli; FileHandle *fh; @@ -1171,11 +1284,11 @@ g_vfs_backend_afc_replace (GVfsBackend *backend, info = g_hash_table_lookup (self->apps, app); if (info == NULL) { + g_free (app); g_vfs_backend_afc_check (AFC_E_OBJECT_NOT_FOUND, G_VFS_JOB(job)); return; } afc_cli = info->afc_cli; - g_free (app); } else { @@ -1188,15 +1301,18 @@ g_vfs_backend_afc_replace (GVfsBackend *backend, G_VFS_JOB(job)))) { g_free (new_path); + g_free (app); return; } g_free (new_path); - fh = g_new0 (FileHandle, 1); + fh = g_vfs_backend_file_handle_new (self, app); fh->fd = fd; fh->afc_cli = afc_cli; + g_free (app); + g_vfs_job_open_for_write_set_handle (job, fh); g_vfs_job_open_for_write_set_can_seek (job, TRUE); g_vfs_job_open_for_write_set_can_truncate (job, TRUE); @@ -1218,10 +1334,7 @@ g_vfs_backend_afc_close_read (GVfsBackend *backend, self = G_VFS_BACKEND_AFC(backend); - if (self->connected) - afc_file_close (fh->afc_cli, fh->fd); - - g_free (fh); + g_vfs_backend_file_handle_free (self, fh); g_vfs_job_succeeded (G_VFS_JOB(job)); } @@ -1238,10 +1351,7 @@ g_vfs_backend_afc_close_write (GVfsBackend *backend, self = G_VFS_BACKEND_AFC(backend); - if (self->connected) - afc_file_close(fh->afc_cli, fh->fd); - - g_free (fh); + g_vfs_backend_file_handle_free (self, fh); g_vfs_job_succeeded (G_VFS_JOB(job)); } |