Reuse existing CaptiveVfsObject to support multiple LUFS threads (=channels).
[captive.git] / src / client / lufs / captivefs-vfs.c
1 /* $Id$
2  * lufs interface module vfs objects implementation for libcaptive
3  * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #include "captivefs-vfs.h"      /* self */
23 #include <glib/gmessages.h>
24 #include "captivefs-misc.h"
25 #include <unistd.h>
26
27 #include <captive/client-vfs.h>
28 #include <captive/options.h>
29 #include <captive/macros.h>
30
31 #include <lufs/fs.h>
32
33
34 gboolean captivefs_vfs_validate(struct captivefs_vfs *captivefs_vfs)
35 {
36         g_return_val_if_fail(captivefs_vfs!=NULL,FALSE);
37
38         G_LOCK(libcaptive);
39         g_assert(captivefs_vfs->inits>0);
40         g_assert(captivefs_vfs->mounts>=0);
41         g_assert(captivefs_vfs->mounts<=captivefs_vfs->inits);
42         if (!captivefs_vfs->captive_vfs_object) {
43 GnomeVFSResult errvfsresult;
44
45                 if (captivefs_vfs->parent_pid==getpid()) {
46                         G_UNLOCK(libcaptive);
47                         return FALSE;
48                         }
49
50                 errvfsresult=captive_vfs_new(&captivefs_vfs->captive_vfs_object,&captivefs_vfs->options);
51
52                 if (errvfsresult!=GNOME_VFS_OK) {
53                         G_UNLOCK(libcaptive);
54                         g_return_val_if_reached(FALSE);
55                         }
56                 }
57         if (!CAPTIVE_VFS_IS_OBJECT(captivefs_vfs->captive_vfs_object)) {
58                 G_UNLOCK(libcaptive);
59                 g_return_val_if_reached(FALSE);
60                 }
61         G_UNLOCK(libcaptive);
62
63         return TRUE;
64 }
65
66
67 /* Initialization
68  * Here we allocate a structure to hold all the file system local info 
69  * (localfs_local). This structure will then be passed as a parameter to 
70  * the other functions.
71  * global_ctx holds info about another structure that can be shared between all
72  * instances of the filesystem. If the pointer is NULL, then this is the
73  * first instance and the structure should be allocated.
74  * ! You must implement  locking/usage-count/deallocation logic when using
75  *   a global context. (locking is omited here)
76  * ! Most filesystems don't need a global context so you can safely ignore the
77  *   global_ctx parameter.  
78  */
79 struct captivefs_vfs *captivefs_init
80                 (struct list_head *cfg,struct dir_cache *cache,const struct credentials *cred,struct captivefs_vfs **global_ctx)
81 {
82 struct captivefs_vfs *captivefs_vfs;
83 const struct poptOption *cpopt;
84 GPtrArray *arg_array;
85 const gchar *cgs;
86 poptContext context;
87 int errint;
88
89         g_return_val_if_fail(global_ctx!=NULL,NULL);
90
91         /* Do not: g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_init");
92          * as we do not yet have 'VFS_DEBUG_MESSAGES(captive_vfs_object)' set.
93          * Generally we make all g_log() conditional here as we do not want to mess
94          * with overriden GLog handlers of libcaptive itself.
95          */
96
97         G_LOCK(libcaptive);
98         if ((captivefs_vfs=*global_ctx)) {
99                 g_assert(captivefs_vfs->inits>0);
100                 /* We do not support multiple LUFS threads if they could not be cross-locked. */
101                 g_return_val_if_fail(g_thread_supported(),NULL);
102                 captivefs_vfs->inits++;
103                 G_UNLOCK(libcaptive);
104                 return captivefs_vfs;
105                 }
106         G_UNLOCK(libcaptive);
107
108         if (!g_thread_supported())
109                 g_thread_init(NULL);    /* g_thread_init() fails on second initialization */
110
111         arg_array=g_ptr_array_new();
112         if (!(cgs=lu_opt_getchar(cfg,"MOUNT","fs")))
113                 cgs="captivefs";
114         g_ptr_array_add(arg_array,(/* de-const */ char *)cgs);
115         for (cpopt=captive_popt;cpopt->argInfo || cpopt->longName;cpopt++) {
116 const gchar *dashdashname,*value;
117
118                 if (!cpopt->longName)
119                         continue;
120                 dashdashname=captive_printf_alloca("--%s",cpopt->longName);
121                 if (1
122                                 && !(value=lu_opt_getchar(cfg,"MOUNT",(/* de-const */ char *)dashdashname))
123                                 && !(value=lu_opt_getchar(cfg,"MOUNT",(/* de-const */ char *)cpopt->longName)))
124                         continue;
125                 g_ptr_array_add(arg_array,(/* de-const */ char *)captive_printf_alloca("%s=%s",dashdashname,value));
126                 }
127         g_ptr_array_add(arg_array,NULL);
128         g_assert(arg_array->len>=2);
129         g_assert(arg_array->pdata[arg_array->len-2]!=NULL);
130         g_assert(arg_array->pdata[arg_array->len-1]==NULL);
131
132         /* Initialize GObject subsystem of GLib. */
133         g_type_init();
134
135         captive_new(captivefs_vfs);
136         captivefs_vfs->captive_vfs_object=NULL;
137         captivefs_vfs->parent_pid=getpid();
138         captivefs_vfs->global_ctx=global_ctx;
139         *global_ctx=captivefs_vfs;
140         captivefs_vfs->inits=1;
141         captivefs_vfs->mounts=0;
142
143         captive_options_init(&captivefs_vfs->options);
144         captive_options=&captivefs_vfs->options;        /* for parsing by 'CAPTIVE_POPT_INCLUDE' */
145
146         context=poptGetContext(
147                         PACKAGE,        /* name */
148                         arg_array->len-1,       /* argc */
149                         (const char **)arg_array->pdata,        /* argv */
150                         captive_popt,   /* options */
151                         POPT_CONTEXT_POSIXMEHARDER);    /* flags; && !POPT_CONTEXT_KEEP_FIRST */
152         g_ptr_array_free(arg_array,
153                         FALSE); /* free_seg */
154         if (context==NULL) {
155                 g_warning("%s: argument recognization args_error","captivefs_init");
156                 goto fail_free_options;
157                 }
158         errint=poptReadDefaultConfig(context,
159                         TRUE);  /* useEnv */
160         if (errint!=0) {
161                 g_warning("%s: argument recognization args_error","captivefs_init");
162                 goto fail_free_options;
163                 }
164         errint=poptGetNextOpt(context);
165         if (errint!=-1) {
166                 g_warning("%s: some non-callbacked argument reached","captivefs_init");
167                 goto fail_free_options;
168                 }
169         cgs=poptPeekArg(context);
170         if (cgs) {
171                 g_warning("%s: some non-option argument reached","captivefs_init");
172                 goto fail_free_options;
173                 }
174
175         captive_options=NULL;   /* already parsed by 'CAPTIVE_POPT_INCLUDE' */
176
177         if (captivefs_vfs->options.debug_messages)
178                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_init");
179
180         /* image_iochannel */
181         if ((cgs=lu_opt_getchar(cfg,"MOUNT","image"))) {
182                 g_assert(captivefs_vfs->options.image_iochannel==NULL);
183                 if (!(captivefs_vfs->options.image_iochannel=g_io_channel_new_file(
184                                 cgs,    /* filename */
185                                 (captivefs_vfs->options.rwmode==CAPTIVE_OPTION_RWMODE_RW ? "r+" : "r"), /* mode */
186                                 NULL))) {       /* error */
187                         g_warning(_("%s: image_iochannel open failed"),"captivefs_init");
188                         goto fail_free_options;
189                         }
190                 }
191
192         return captivefs_vfs;
193
194 fail_free_options:
195         captive_options_free(&captivefs_vfs->options);
196 /* fail: */
197         return NULL;
198 }
199
200
201 /* Cleanup
202  * Check the global context count and free it if necessary.
203  * Deallocate memory and free all resources.
204  */
205 void captivefs_free(struct captivefs_vfs *captivefs_vfs)
206 {
207         g_return_if_fail(captivefs_vfs_validate(captivefs_vfs));
208         g_return_if_fail(captivefs_vfs->inits>0);
209
210         if (captivefs_vfs->options.debug_messages)
211                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_free");
212         
213         if (--captivefs_vfs->inits>0) {
214                 g_assert(captivefs_vfs_validate(captivefs_vfs));
215                 return;
216                 }
217         g_assert(captivefs_vfs->mounts==0);
218         G_LOCK(libcaptive);
219         *captivefs_vfs->global_ctx=NULL;
220         G_UNLOCK(libcaptive);
221
222         g_assert(G_OBJECT(captivefs_vfs->captive_vfs_object)->ref_count==1);
223
224         G_LOCK(libcaptive);
225         g_object_unref(captivefs_vfs->captive_vfs_object);
226         G_UNLOCK(libcaptive);
227
228         g_io_channel_unref(captivefs_vfs->options.image_iochannel);
229         captive_options_free(&captivefs_vfs->options);
230         g_free(captivefs_vfs);
231 }
232
233
234 /* Mount the file system.
235  * Called when a mount operation is performed.
236  * Initialize specific connections, login, etc.
237  *
238  * Notes:
239  *     By default, LUFS may attempt multiple connections at once.  If your
240  * filesystem doesn't support this, you need to specificy -c 1 on the
241  * lufsmount command line or connections=1 in the mount options.
242  *     See ftpfs for an example of how to read configuration options
243  * from a configuration file if you want to, for example, be able to set
244  * default values.
245  */
246 int captivefs_mount(struct captivefs_vfs *captivefs_vfs)
247 {
248         /* We may be called from the parent. */
249         g_return_val_if_fail(captivefs_vfs!=NULL,FALSE);
250         captivefs_vfs_validate(captivefs_vfs);  /* It may return FALSE. */
251
252         if (captivefs_vfs->options.debug_messages)
253                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_mount");
254
255         captivefs_vfs->mounts++;
256
257         captivefs_vfs_validate(captivefs_vfs);  /* It may return FALSE. */
258
259         return 1;       /* NEVER return 0 */
260 }
261
262
263 /* Unmount the  file system
264  * Called when the file system is unmounted.
265  */
266 void captivefs_umount(struct captivefs_vfs *captivefs_vfs)
267 {
268         g_return_if_fail(captivefs_vfs_validate(captivefs_vfs));
269         g_return_if_fail(captivefs_vfs->mounts>0);
270
271         if (captivefs_vfs->options.debug_messages)
272                 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"captivefs_umount");
273
274         captivefs_vfs->mounts--;
275
276         g_assert(captivefs_vfs_validate(captivefs_vfs));
277 }