~galfy/helenos/bird-port-mainline

« back to all changes in this revision

Viewing changes to uspace/srv/vfs/vfs_register.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2008 Jakub Jermar
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
9
 * - Redistributions of source code must retain the above copyright
 
10
 *   notice, this list of conditions and the following disclaimer.
 
11
 * - Redistributions in binary form must reproduce the above copyright
 
12
 *   notice, this list of conditions and the following disclaimer in the
 
13
 *   documentation and/or other materials provided with the distribution.
 
14
 * - The name of the author may not be used to endorse or promote products
 
15
 *   derived from this software without specific prior written permission.
 
16
 *
 
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
27
 */
 
28
 
 
29
/** @addtogroup fs
 
30
 * @{
 
31
 */
 
32
 
 
33
/**
 
34
 * @file vfs_register.c
 
35
 * @brief
 
36
 */
 
37
 
 
38
#include <ipc/ipc.h>
 
39
#include <ipc/services.h>
 
40
#include <async.h>
 
41
#include <fibril.h>
 
42
#include <errno.h>
 
43
#include <stdio.h>
 
44
#include <stdlib.h>
 
45
#include <string.h>
 
46
#include <ctype.h>
 
47
#include <bool.h>
 
48
#include <fibril_sync.h>
 
49
#include <adt/list.h>
 
50
#include <as.h>
 
51
#include <assert.h>
 
52
#include <atomic.h>
 
53
#include "vfs.h"
 
54
 
 
55
FIBRIL_CONDVAR_INITIALIZE(fs_head_cv);
 
56
FIBRIL_MUTEX_INITIALIZE(fs_head_lock);
 
57
LIST_INITIALIZE(fs_head);
 
58
 
 
59
atomic_t fs_handle_next = {
 
60
        .count = 1
 
61
};
 
62
 
 
63
/** Verify the VFS info structure.
 
64
 *
 
65
 * @param info          Info structure to be verified.
 
66
 *
 
67
 * @return              Non-zero if the info structure is sane, zero otherwise.
 
68
 */
 
69
static bool vfs_info_sane(vfs_info_t *info)
 
70
{
 
71
        int i;
 
72
 
 
73
        /*
 
74
         * Check if the name is non-empty and is composed solely of ASCII
 
75
         * characters [a-z]+[a-z0-9_-]*.
 
76
         */
 
77
        if (!islower(info->name[0])) {
 
78
                dprintf("The name doesn't start with a lowercase character.\n");
 
79
                return false;
 
80
        }
 
81
        for (i = 1; i < FS_NAME_MAXLEN; i++) {
 
82
                if (!(islower(info->name[i]) || isdigit(info->name[i])) &&
 
83
                    (info->name[i] != '-') && (info->name[i] != '_')) {
 
84
                        if (info->name[i] == '\0') {
 
85
                                break;
 
86
                        } else {
 
87
                                dprintf("The name contains illegal "
 
88
                                    "characters.\n");
 
89
                                return false;
 
90
                        }
 
91
                }
 
92
        }
 
93
        /*
 
94
         * This check is not redundant. It ensures that the name is
 
95
         * NULL-terminated, even if FS_NAME_MAXLEN characters are used.
 
96
         */
 
97
        if (info->name[i] != '\0') {
 
98
                dprintf("The name is not properly NULL-terminated.\n"); 
 
99
                return false;
 
100
        }
 
101
        
 
102
        return true;
 
103
}
 
104
 
 
105
/** VFS_REGISTER protocol function.
 
106
 *
 
107
 * @param rid           Hash of the call with the request.
 
108
 * @param request       Call structure with the request.
 
109
 */
 
110
void vfs_register(ipc_callid_t rid, ipc_call_t *request)
 
111
{
 
112
        ipc_callid_t callid;
 
113
        ipc_call_t call;
 
114
        int rc;
 
115
        size_t size;
 
116
 
 
117
        dprintf("Processing VFS_REGISTER request received from %p.\n",
 
118
            request->in_phone_hash);
 
119
 
 
120
        /*
 
121
         * The first call has to be IPC_M_DATA_SEND in which we receive the
 
122
         * VFS info structure from the client FS.
 
123
         */
 
124
        if (!ipc_data_write_receive(&callid, &size)) {
 
125
                /*
 
126
                 * The client doesn't obey the same protocol as we do.
 
127
                 */
 
128
                dprintf("Receiving of VFS info failed.\n");
 
129
                ipc_answer_0(callid, EINVAL);
 
130
                ipc_answer_0(rid, EINVAL);
 
131
                return;
 
132
        }
 
133
        
 
134
        dprintf("VFS info received, size = %d\n", size);
 
135
        
 
136
        /*
 
137
         * We know the size of the VFS info structure. See if the client
 
138
         * understands this easy concept too.
 
139
         */
 
140
        if (size != sizeof(vfs_info_t)) {
 
141
                /*
 
142
                 * The client is sending us something, which cannot be
 
143
                 * the info structure.
 
144
                 */
 
145
                dprintf("Received VFS info has bad size.\n");
 
146
                ipc_answer_0(callid, EINVAL);
 
147
                ipc_answer_0(rid, EINVAL);
 
148
                return;
 
149
        }
 
150
 
 
151
        /*
 
152
         * Allocate and initialize a buffer for the fs_info structure.
 
153
         */
 
154
        fs_info_t *fs_info;
 
155
        fs_info = (fs_info_t *) malloc(sizeof(fs_info_t));
 
156
        if (!fs_info) {
 
157
                dprintf("Could not allocate memory for FS info.\n");
 
158
                ipc_answer_0(callid, ENOMEM);
 
159
                ipc_answer_0(rid, ENOMEM);
 
160
                return;
 
161
        }
 
162
        link_initialize(&fs_info->fs_link);
 
163
        fibril_mutex_initialize(&fs_info->phone_lock);
 
164
                
 
165
        rc = ipc_data_write_finalize(callid, &fs_info->vfs_info, size);
 
166
        if (rc != EOK) {
 
167
                dprintf("Failed to deliver the VFS info into our AS, rc=%d.\n",
 
168
                    rc);
 
169
                free(fs_info);
 
170
                ipc_answer_0(callid, rc);
 
171
                ipc_answer_0(rid, rc);
 
172
                return;
 
173
        }
 
174
 
 
175
        dprintf("VFS info delivered.\n");
 
176
                
 
177
        if (!vfs_info_sane(&fs_info->vfs_info)) {
 
178
                free(fs_info);
 
179
                ipc_answer_0(callid, EINVAL);
 
180
                ipc_answer_0(rid, EINVAL);
 
181
                return;
 
182
        }
 
183
                
 
184
        fibril_mutex_lock(&fs_head_lock);
 
185
 
 
186
        /*
 
187
         * Check for duplicit registrations.
 
188
         */
 
189
        if (fs_name_to_handle(fs_info->vfs_info.name, false)) {
 
190
                /*
 
191
                 * We already register a fs like this.
 
192
                 */
 
193
                dprintf("FS is already registered.\n");
 
194
                fibril_mutex_unlock(&fs_head_lock);
 
195
                free(fs_info);
 
196
                ipc_answer_0(callid, EEXISTS);
 
197
                ipc_answer_0(rid, EEXISTS);
 
198
                return;
 
199
        }
 
200
 
 
201
        /*
 
202
         * Add fs_info to the list of registered FS's.
 
203
         */
 
204
        dprintf("Inserting FS into the list of registered file systems.\n");
 
205
        list_append(&fs_info->fs_link, &fs_head);
 
206
        
 
207
        /*
 
208
         * Now we want the client to send us the IPC_M_CONNECT_TO_ME call so
 
209
         * that a callback connection is created and we have a phone through
 
210
         * which to forward VFS requests to it.
 
211
         */
 
212
        callid = async_get_call(&call);
 
213
        if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) {
 
214
                dprintf("Unexpected call, method = %d\n", IPC_GET_METHOD(call));
 
215
                list_remove(&fs_info->fs_link);
 
216
                fibril_mutex_unlock(&fs_head_lock);
 
217
                free(fs_info);
 
218
                ipc_answer_0(callid, EINVAL);
 
219
                ipc_answer_0(rid, EINVAL);
 
220
                return;
 
221
        }
 
222
        fs_info->phone = IPC_GET_ARG5(call);
 
223
        ipc_answer_0(callid, EOK);
 
224
 
 
225
        dprintf("Callback connection to FS created.\n");
 
226
 
 
227
        /*
 
228
         * The client will want us to send him the address space area with PLB.
 
229
         */
 
230
 
 
231
        if (!ipc_share_in_receive(&callid, &size)) {
 
232
                dprintf("Unexpected call, method = %d\n", IPC_GET_METHOD(call));
 
233
                list_remove(&fs_info->fs_link);
 
234
                fibril_mutex_unlock(&fs_head_lock);
 
235
                ipc_hangup(fs_info->phone);
 
236
                free(fs_info);
 
237
                ipc_answer_0(callid, EINVAL);
 
238
                ipc_answer_0(rid, EINVAL);
 
239
                return;
 
240
        }
 
241
        
 
242
        /*
 
243
         * We can only send the client address space area PLB_SIZE bytes long.
 
244
         */
 
245
        if (size != PLB_SIZE) {
 
246
                dprintf("Client suggests wrong size of PFB, size = %d\n", size);
 
247
                list_remove(&fs_info->fs_link);
 
248
                fibril_mutex_unlock(&fs_head_lock);
 
249
                ipc_hangup(fs_info->phone);
 
250
                free(fs_info);
 
251
                ipc_answer_0(callid, EINVAL);
 
252
                ipc_answer_0(rid, EINVAL);
 
253
                return;
 
254
        }
 
255
 
 
256
        /*
 
257
         * Commit to read-only sharing the PLB with the client.
 
258
         */
 
259
        (void) ipc_share_in_finalize(callid, plb,
 
260
            AS_AREA_READ | AS_AREA_CACHEABLE);
 
261
 
 
262
        dprintf("Sharing PLB.\n");
 
263
 
 
264
        /*
 
265
         * That was it. The FS has been registered.
 
266
         * In reply to the VFS_REGISTER request, we assign the client file
 
267
         * system a global file system handle.
 
268
         */
 
269
        fs_info->fs_handle = (fs_handle_t) atomic_postinc(&fs_handle_next);
 
270
        ipc_answer_1(rid, EOK, (ipcarg_t) fs_info->fs_handle);
 
271
        
 
272
        fibril_condvar_broadcast(&fs_head_cv);
 
273
        fibril_mutex_unlock(&fs_head_lock);
 
274
        
 
275
        dprintf("\"%.*s\" filesystem successfully registered, handle=%d.\n",
 
276
            FS_NAME_MAXLEN, fs_info->vfs_info.name, fs_info->fs_handle);
 
277
}
 
278
 
 
279
/** For a given file system handle, implement policy for allocating a phone.
 
280
 *
 
281
 * @param handle        File system handle.
 
282
 *
 
283
 * @return              Phone over which a multi-call request can be safely
 
284
 *                      sent. Return 0 if no phone was found.
 
285
 */
 
286
int vfs_grab_phone(fs_handle_t handle)
 
287
{
 
288
        int phone;
 
289
 
 
290
        /*
 
291
         * For now, we don't try to be very clever and very fast.  We simply
 
292
         * lookup the phone in the fs_head list and duplicate it.  The duplicate
 
293
         * phone will be returned to the client and the client will use it for
 
294
         * communication.  In the future, we should cache the connections so
 
295
         * that they do not have to be reestablished over and over again.
 
296
         */
 
297
        fibril_mutex_lock(&fs_head_lock);
 
298
        link_t *cur;
 
299
        fs_info_t *fs;
 
300
        for (cur = fs_head.next; cur != &fs_head; cur = cur->next) {
 
301
                fs = list_get_instance(cur, fs_info_t, fs_link);
 
302
                if (fs->fs_handle == handle) {
 
303
                        fibril_mutex_unlock(&fs_head_lock);
 
304
                        fibril_mutex_lock(&fs->phone_lock);
 
305
                        phone = ipc_connect_me_to(fs->phone, 0, 0, 0);
 
306
                        fibril_mutex_unlock(&fs->phone_lock);
 
307
 
 
308
                        assert(phone > 0);
 
309
                        return phone;
 
310
                }
 
311
        }
 
312
        fibril_mutex_unlock(&fs_head_lock);
 
313
        return 0;
 
314
}
 
315
 
 
316
/** Tell VFS that the phone is not needed anymore.
 
317
 *
 
318
 * @param phone         Phone to FS task.
 
319
 */
 
320
void vfs_release_phone(int phone)
 
321
{
 
322
        /* TODO: implement connection caching */
 
323
        ipc_hangup(phone);
 
324
}
 
325
 
 
326
/** Convert file system name to its handle.
 
327
 *
 
328
 * @param name          File system name.
 
329
 * @param lock          If true, the function will lock and unlock the
 
330
 *                      fs_head_lock.
 
331
 *
 
332
 * @return              File system handle or zero if file system not found.
 
333
 */
 
334
fs_handle_t fs_name_to_handle(char *name, bool lock)
 
335
{
 
336
        int handle = 0;
 
337
        
 
338
        if (lock)
 
339
                fibril_mutex_lock(&fs_head_lock);
 
340
        link_t *cur;
 
341
        for (cur = fs_head.next; cur != &fs_head; cur = cur->next) {
 
342
                fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
 
343
                if (str_cmp(fs->vfs_info.name, name) == 0) { 
 
344
                        handle = fs->fs_handle;
 
345
                        break;
 
346
                }
 
347
        }
 
348
        if (lock)
 
349
                fibril_mutex_unlock(&fs_head_lock);
 
350
        return handle;
 
351
}
 
352
 
 
353
/**
 
354
 * @}
 
355
 */