Subversion Repositories HelenOS

Rev

Rev 2690 | Rev 2693 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2689 jermar 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_ops.c
35
 * @brief	Operations that VFS offers to its clients.
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 <bool.h>
47
#include <futex.h>
48
#include <rwlock.h>
49
#include <libadt/list.h>
50
#include <unistd.h>
51
#include <ctype.h>
52
#include <as.h>
53
#include <assert.h>
54
#include <atomic.h>
55
#include "vfs.h"
56
 
57
#define min(a, b)	((a) < (b) ? (a) : (b))
58
 
59
/**
60
 * This rwlock prevents the race between a triplet-to-VFS-node resolution and a
61
 * concurrent VFS operation which modifies the file system namespace.
62
 */
63
RWLOCK_INITIALIZE(namespace_rwlock);
64
 
65
atomic_t plb_futex = FUTEX_INITIALIZER;
66
link_t plb_head;	/**< PLB entry ring buffer. */
67
uint8_t *plb = NULL;
68
 
69
/** Perform a path lookup.
70
 *
71
 * @param path		Path to be resolved; it needn't be an ASCIIZ string.
72
 * @param len		Number of path characters pointed by path.
2691 jermar 73
 * @param result	Empty structure where the lookup result will be stored.
2689 jermar 74
 * @param altroot	If non-empty, will be used instead of rootfs as the root
75
 *			of the whole VFS tree.
76
 *
77
 * @return		EOK on success or an error code from errno.h.
78
 */
2691 jermar 79
int vfs_lookup_internal(char *path, size_t len, vfs_lookup_res_t *result,
80
    vfs_pair_t *altroot)
2689 jermar 81
{
82
	vfs_pair_t *root;
83
 
84
	if (!len)
85
		return EINVAL;
86
 
87
	if (altroot)
88
		root = altroot;
89
	else
90
		root = (vfs_pair_t *) &rootfs;
91
 
92
	if (!root->fs_handle)
93
		return ENOENT;
94
 
95
	futex_down(&plb_futex);
96
 
97
	plb_entry_t entry;
98
	link_initialize(&entry.plb_link);
99
	entry.len = len;
100
 
101
	off_t first;	/* the first free index */
102
	off_t last;	/* the last free index */
103
 
104
	if (list_empty(&plb_head)) {
105
		first = 0;
106
		last = PLB_SIZE - 1;
107
	} else {
108
		plb_entry_t *oldest = list_get_instance(plb_head.next,
109
		    plb_entry_t, plb_link);
110
		plb_entry_t *newest = list_get_instance(plb_head.prev,
111
		    plb_entry_t, plb_link);
112
 
113
		first = (newest->index + newest->len) % PLB_SIZE;
114
		last = (oldest->index - 1) % PLB_SIZE;
115
	}
116
 
117
	if (first <= last) {
118
		if ((last - first) + 1 < len) {
119
			/*
120
			 * The buffer cannot absorb the path.
121
			 */
122
			futex_up(&plb_futex);
123
			return ELIMIT;
124
		}
125
	} else {
126
		if (PLB_SIZE - ((first - last) + 1) < len) {
127
			/*
128
			 * The buffer cannot absorb the path.
129
			 */
130
			futex_up(&plb_futex);
131
			return ELIMIT;
132
		}
133
	}
134
 
135
	/*
136
	 * We know the first free index in PLB and we also know that there is
137
	 * enough space in the buffer to hold our path.
138
	 */
139
 
140
	entry.index = first;
141
	entry.len = len;
142
 
143
	/*
144
	 * Claim PLB space by inserting the entry into the PLB entry ring
145
	 * buffer.
146
	 */
147
	list_append(&entry.plb_link, &plb_head);
148
 
149
	futex_up(&plb_futex);
150
 
151
	/*
152
	 * Copy the path into PLB.
153
	 */
154
	size_t cnt1 = min(len, (PLB_SIZE - first) + 1);
155
	size_t cnt2 = len - cnt1;
156
 
157
	memcpy(&plb[first], path, cnt1);
158
	memcpy(plb, &path[cnt1], cnt2);
159
 
160
	ipc_call_t answer;
161
	int phone = vfs_grab_phone(root->fs_handle);
162
	aid_t req = async_send_3(phone, VFS_LOOKUP, (ipcarg_t) first,
163
	    (ipcarg_t) (first + len - 1) % PLB_SIZE,
164
	    (ipcarg_t) root->dev_handle, &answer);
165
	vfs_release_phone(phone);
166
 
167
	ipcarg_t rc;
168
	async_wait_for(req, &rc);
169
 
170
	futex_down(&plb_futex);
171
	list_remove(&entry.plb_link);
172
	/*
173
	 * Erasing the path from PLB will come handy for debugging purposes.
174
	 */
175
	memset(&plb[first], 0, cnt1);
176
	memset(plb, 0, cnt2);
177
	futex_up(&plb_futex);
178
 
179
	if (rc == EOK) {
2691 jermar 180
		result->triplet.fs_handle = (int) IPC_GET_ARG1(answer);
181
		result->triplet.dev_handle = (int) IPC_GET_ARG2(answer);
182
		result->triplet.index = (int) IPC_GET_ARG3(answer);
183
		result->size = (size_t) IPC_GET_ARG4(answer);
2689 jermar 184
	}
185
 
186
	return rc;
187
}
188
 
189
atomic_t rootfs_futex = FUTEX_INITIALIZER;
190
vfs_triplet_t rootfs = {
191
	.fs_handle = 0,
192
	.dev_handle = 0,
193
	.index = 0,
194
};
195
 
2691 jermar 196
static int lookup_root(int fs_handle, int dev_handle, vfs_lookup_res_t *result)
2689 jermar 197
{
198
	vfs_pair_t altroot = {
199
		.fs_handle = fs_handle,
200
		.dev_handle = dev_handle,
201
	};
202
 
2691 jermar 203
	return vfs_lookup_internal("/", strlen("/"), result, &altroot);
2689 jermar 204
}
205
 
206
void vfs_mount(ipc_callid_t rid, ipc_call_t *request)
207
{
208
	int dev_handle;
209
	vfs_node_t *mp_node = NULL;
210
 
211
	/*
212
	 * We expect the library to do the device-name to device-handle
213
	 * translation for us, thus the device handle will arrive as ARG1
214
	 * in the request.
215
	 */
216
	dev_handle = IPC_GET_ARG1(*request);
217
 
218
	/*
219
	 * For now, don't make use of ARG2 and ARG3, but they can be used to
220
	 * carry mount options in the future.
221
	 */
222
 
223
	ipc_callid_t callid;
224
	size_t size;
225
 
226
	/*
227
	 * Now, we expect the client to send us data with the name of the file
228
	 * system.
229
	 */
230
	if (!ipc_data_write_receive(&callid, &size)) {
231
		ipc_answer_0(callid, EINVAL);
232
		ipc_answer_0(rid, EINVAL);
233
		return;
234
	}
235
 
236
	/*
237
	 * Don't receive more than is necessary for storing a full file system
238
	 * name.
239
	 */
240
	if (size < 1 || size > FS_NAME_MAXLEN) {
241
		ipc_answer_0(callid, EINVAL);
242
		ipc_answer_0(rid, EINVAL);
243
		return;
244
	}
245
 
246
	/*
247
	 * Deliver the file system name.
248
	 */
249
	char fs_name[FS_NAME_MAXLEN + 1];
250
	(void) ipc_data_write_finalize(callid, fs_name, size);
251
	fs_name[size] = '\0';
252
 
253
	/*
254
	 * Check if we know a file system with the same name as is in fs_name.
255
	 * This will also give us its file system handle.
256
	 */
257
	int fs_handle = fs_name_to_handle(fs_name, true);
258
	if (!fs_handle) {
259
		ipc_answer_0(rid, ENOENT);
260
		return;
261
	}
262
 
263
	/*
264
	 * Now, we want the client to send us the mount point.
265
	 */
266
	if (!ipc_data_write_receive(&callid, &size)) {
267
		ipc_answer_0(callid, EINVAL);
268
		ipc_answer_0(rid, EINVAL);
269
		return;
270
	}
271
 
272
	/*
273
	 * Check whether size is reasonable wrt. the mount point.
274
	 */
275
	if (size < 1 || size > MAX_PATH_LEN) {
276
		ipc_answer_0(callid, EINVAL);
277
		ipc_answer_0(rid, EINVAL);
278
		return;
279
	}
280
	/*
281
	 * Allocate buffer for the mount point data being received.
282
	 */
283
	uint8_t *buf;
284
	buf = malloc(size);
285
	if (!buf) {
286
		ipc_answer_0(callid, ENOMEM);
287
		ipc_answer_0(rid, ENOMEM);
288
		return;
289
	}
290
 
291
	/*
292
	 * Deliver the mount point.
293
	 */
294
	(void) ipc_data_write_finalize(callid, buf, size);
295
 
296
	/*
297
	 * Lookup the root node of the filesystem being mounted.
298
	 * In this case, we don't need to take the namespace_futex as the root
299
	 * node cannot be removed. However, we do take a reference to it so
300
	 * that we can track how many times it has been mounted.
301
	 */
302
	int rc;
2691 jermar 303
	vfs_lookup_res_t mr_res;
304
	rc = lookup_root(fs_handle, dev_handle, &mr_res);
2689 jermar 305
	if (rc != EOK) {
306
		free(buf);
307
		ipc_answer_0(rid, rc);
308
		return;
309
	}
2691 jermar 310
	vfs_node_t *mr_node = vfs_node_get(&mr_res);
2689 jermar 311
	if (!mr_node) {
312
		free(buf);
313
		ipc_answer_0(rid, ENOMEM);
314
		return;
315
	}
316
 
317
	/*
318
	 * Finally, we need to resolve the path to the mountpoint. 
319
	 */
2691 jermar 320
	vfs_lookup_res_t mp_res;
2689 jermar 321
	futex_down(&rootfs_futex);
322
	if (rootfs.fs_handle) {
323
		/*
324
		 * We already have the root FS.
325
		 */
326
		rwlock_write_lock(&namespace_rwlock);
2691 jermar 327
		rc = vfs_lookup_internal(buf, size, &mp_res, NULL);
2689 jermar 328
		if (rc != EOK) {
329
			/*
330
			 * The lookup failed for some reason.
331
			 */
332
			rwlock_write_unlock(&namespace_rwlock);
333
			futex_up(&rootfs_futex);
334
			vfs_node_put(mr_node);	/* failed -> drop reference */
335
			free(buf);
336
			ipc_answer_0(rid, rc);
337
			return;
338
		}
2691 jermar 339
		mp_node = vfs_node_get(&mp_res);
2689 jermar 340
		if (!mp_node) {
341
			rwlock_write_unlock(&namespace_rwlock);
342
			futex_up(&rootfs_futex);
343
			vfs_node_put(mr_node);	/* failed -> drop reference */
344
			free(buf);
345
			ipc_answer_0(rid, ENOMEM);
346
			return;
347
		}
348
		/*
349
		 * Now we hold a reference to mp_node.
350
		 * It will be dropped upon the corresponding VFS_UNMOUNT.
351
		 * This prevents the mount point from being deleted.
352
		 */
353
		rwlock_write_unlock(&namespace_rwlock);
354
	} else {
355
		/*
356
		 * We still don't have the root file system mounted.
357
		 */
358
		if ((size == 1) && (buf[0] == '/')) {
359
			/*
360
			 * For this simple, but important case, we are done.
361
			 */
2691 jermar 362
			rootfs = mr_res.triplet;
2689 jermar 363
			futex_up(&rootfs_futex);
364
			free(buf);
365
			ipc_answer_0(rid, EOK);
366
			return;
367
		} else {
368
			/*
369
			 * We can't resolve this without the root filesystem
370
			 * being mounted first.
371
			 */
372
			futex_up(&rootfs_futex);
373
			free(buf);
374
			vfs_node_put(mr_node);	/* failed -> drop reference */
375
			ipc_answer_0(rid, ENOENT);
376
			return;
377
		}
378
	}
379
	futex_up(&rootfs_futex);
380
 
381
	free(buf);	/* The buffer is not needed anymore. */
382
 
383
	/*
384
	 * At this point, we have all necessary pieces: file system and device
385
	 * handles, and we know the mount point VFS node and also the root node
386
	 * of the file system being mounted.
387
	 */
388
 
2691 jermar 389
	int phone = vfs_grab_phone(mp_res.triplet.fs_handle);
2689 jermar 390
	/* Later we can use ARG3 to pass mode/flags. */
2691 jermar 391
	aid_t req1 = async_send_3(phone, VFS_MOUNT,
392
	    (ipcarg_t) mp_res.triplet.dev_handle,
393
	    (ipcarg_t) mp_res.triplet.index, 0, NULL);
2689 jermar 394
	/* The second call uses the same method. */
395
	aid_t req2 = async_send_3(phone, VFS_MOUNT,
2691 jermar 396
	    (ipcarg_t) mr_res.triplet.fs_handle,
397
	    (ipcarg_t) mr_res.triplet.dev_handle,
398
	    (ipcarg_t) mr_res.triplet.index, NULL);
2689 jermar 399
	vfs_release_phone(phone);
400
 
401
	ipcarg_t rc1;
402
	ipcarg_t rc2;
403
	async_wait_for(req1, &rc1);
404
	async_wait_for(req2, &rc2);
405
 
406
	if ((rc1 != EOK) || (rc2 != EOK)) {
407
		/* Mount failed, drop references to mr_node and mp_node. */
408
		vfs_node_put(mr_node);
409
		if (mp_node)
410
			vfs_node_put(mp_node);
411
	}
412
 
413
	if (rc2 == EOK)
414
		ipc_answer_0(rid, rc1);
415
	else if (rc1 == EOK)
416
		ipc_answer_0(rid, rc2);
417
	else
418
		ipc_answer_0(rid, rc1);
419
}
420
 
421
void vfs_open(ipc_callid_t rid, ipc_call_t *request)
422
{
423
	if (!vfs_files_init()) {
424
		ipc_answer_0(rid, ENOMEM);
425
		return;
426
	}
427
 
428
	/*
429
	 * The POSIX interface is open(path, flags, mode).
430
	 * We can receive flags and mode along with the VFS_OPEN call; the path
431
	 * will need to arrive in another call.
432
	 */
433
	int flags = IPC_GET_ARG1(*request);
434
	int mode = IPC_GET_ARG2(*request);
435
	size_t len;
436
 
437
	ipc_callid_t callid;
438
 
439
	if (!ipc_data_write_receive(&callid, &len)) {
440
		ipc_answer_0(callid, EINVAL);
441
		ipc_answer_0(rid, EINVAL);
442
		return;
443
	}
444
 
445
	/*
446
	 * Now we are on the verge of accepting the path.
447
	 *
448
	 * There is one optimization we could do in the future: copy the path
449
	 * directly into the PLB using some kind of a callback.
450
	 */
451
	char *path = malloc(len);
452
 
453
	if (!path) {
454
		ipc_answer_0(callid, ENOMEM);
455
		ipc_answer_0(rid, ENOMEM);
456
		return;
457
	}
458
 
459
	int rc;
460
	if ((rc = ipc_data_write_finalize(callid, path, len))) {
461
		ipc_answer_0(rid, rc);
462
		free(path);
463
		return;
464
	}
465
 
466
	/*
467
	 * Avoid the race condition in which the file can be deleted before we
468
	 * find/create-and-lock the VFS node corresponding to the looked-up
469
	 * triplet.
470
	 */
471
	rwlock_read_lock(&namespace_rwlock);
472
 
473
	/*
474
	 * The path is now populated and we can call vfs_lookup_internal().
475
	 */
2691 jermar 476
	vfs_lookup_res_t lr;
477
	rc = vfs_lookup_internal(path, len, &lr, NULL);
2689 jermar 478
	if (rc) {
479
		rwlock_read_unlock(&namespace_rwlock);
480
		ipc_answer_0(rid, rc);
481
		free(path);
482
		return;
483
	}
484
 
485
	/*
486
	 * Path is no longer needed.
487
	 */
488
	free(path);
489
 
2691 jermar 490
	vfs_node_t *node = vfs_node_get(&lr);
2689 jermar 491
	rwlock_read_unlock(&namespace_rwlock);
492
 
493
	/*
494
	 * Get ourselves a file descriptor and the corresponding vfs_file_t
495
	 * structure.
496
	 */
497
	int fd = vfs_fd_alloc();
498
	if (fd < 0) {
499
		vfs_node_put(node);
500
		ipc_answer_0(rid, fd);
501
		return;
502
	}
503
	vfs_file_t *file = vfs_file_get(fd);
504
	file->node = node;
505
 
506
	/*
507
	 * The following increase in reference count is for the fact that the
508
	 * file is being opened and that a file structure is pointing to it.
509
	 * It is necessary so that the file will not disappear when
510
	 * vfs_node_put() is called. The reference will be dropped by the
511
	 * respective VFS_CLOSE.
512
	 */
513
	vfs_node_addref(node);
514
	vfs_node_put(node);
515
 
516
	/*
517
	 * Success! Return the new file descriptor to the client.
518
	 */
519
	ipc_answer_1(rid, EOK, fd);
520
}
521
 
522
static void vfs_rdwr(ipc_callid_t rid, ipc_call_t *request, bool read)
523
{
524
 
525
	/*
526
	 * The following code strongly depends on the fact that the files data
527
	 * structure can be only accessed by a single fibril and all file
528
	 * operations are serialized (i.e. the reads and writes cannot
529
	 * interleave and a file cannot be closed while it is being read).
530
	 *
531
	 * Additional synchronization needs to be added once the table of
532
	 * open files supports parallel access!
533
	 */
534
 
535
	int fd = IPC_GET_ARG1(*request);
536
 
537
	/*
538
	 * Lookup the file structure corresponding to the file descriptor.
539
	 */
540
	vfs_file_t *file = vfs_file_get(fd);
541
	if (!file) {
542
		ipc_answer_0(rid, ENOENT);
543
		return;
544
	}
545
 
546
	/*
547
	 * Now we need to receive a call with client's
548
	 * IPC_M_DATA_READ/IPC_M_DATA_WRITE request.
549
	 */
550
	ipc_callid_t callid;
551
	int res;
552
	if (read)
553
		res = ipc_data_read_receive(&callid, NULL);
554
	else 
555
		res = ipc_data_write_receive(&callid, NULL);
556
	if (!res) {
557
		ipc_answer_0(callid, EINVAL);
558
		ipc_answer_0(rid, EINVAL);
559
		return;
560
	}
561
 
562
	/*
563
	 * Lock the open file structure so that no other thread can manipulate
564
	 * the same open file at a time.
565
	 */
566
	futex_down(&file->lock);
567
 
568
	/*
569
	 * Lock the file's node so that no other client can read/write to it at
570
	 * the same time.
571
	 */
572
	if (read)
573
		rwlock_read_lock(&file->node->contents_rwlock);
574
	else
575
		rwlock_write_lock(&file->node->contents_rwlock);
576
 
577
	int fs_phone = vfs_grab_phone(file->node->fs_handle);	
578
 
579
	/*
580
	 * Make a VFS_READ/VFS_WRITE request at the destination FS server.
581
	 */
582
	aid_t msg;
583
	ipc_call_t answer;
584
	msg = async_send_3(fs_phone, IPC_GET_METHOD(*request),
585
	    file->node->dev_handle, file->node->index, file->pos, &answer);
586
 
587
	/*
588
	 * Forward the IPC_M_DATA_READ/IPC_M_DATA_WRITE request to the
589
	 * destination FS server. The call will be routed as if sent by
590
	 * ourselves. Note that call arguments are immutable in this case so we
591
	 * don't have to bother.
592
	 */
593
	ipc_forward_fast(callid, fs_phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
594
 
595
	vfs_release_phone(fs_phone);
596
 
597
	/*
598
	 * Wait for reply from the FS server.
599
	 */
600
	ipcarg_t rc;
601
	async_wait_for(msg, &rc);
602
	size_t bytes = IPC_GET_ARG1(answer);
603
 
604
	/*
605
	 * Unlock the VFS node.
606
	 */
607
	if (read)
608
		rwlock_read_unlock(&file->node->contents_rwlock);
609
	else {
610
		/* Update the cached version of node's size. */
611
		file->node->size = IPC_GET_ARG2(answer); 
612
		rwlock_write_unlock(&file->node->contents_rwlock);
613
	}
614
 
615
	/*
616
	 * Update the position pointer and unlock the open file.
617
	 */
618
	file->pos += bytes;
619
	futex_up(&file->lock);
620
 
621
	/*
622
	 * FS server's reply is the final result of the whole operation we
623
	 * return to the client.
624
	 */
625
	ipc_answer_1(rid, rc, bytes);
626
}
627
 
628
void vfs_read(ipc_callid_t rid, ipc_call_t *request)
629
{
630
	vfs_rdwr(rid, request, true);
631
}
632
 
633
void vfs_write(ipc_callid_t rid, ipc_call_t *request)
634
{
635
	vfs_rdwr(rid, request, false);
636
}
637
 
638
void vfs_seek(ipc_callid_t rid, ipc_call_t *request)
639
{
640
	int fd = (int) IPC_GET_ARG1(*request);
641
	off_t off = (off_t) IPC_GET_ARG2(*request);
642
	int whence = (int) IPC_GET_ARG3(*request);
643
 
644
 
645
	/*
646
	 * Lookup the file structure corresponding to the file descriptor.
647
	 */
648
	vfs_file_t *file = vfs_file_get(fd);
649
	if (!file) {
650
		ipc_answer_0(rid, ENOENT);
651
		return;
652
	}
653
 
654
	off_t newpos;
655
	futex_down(&file->lock);
656
	if (whence == SEEK_SET) {
657
		file->pos = off;
658
		futex_up(&file->lock);
659
		ipc_answer_1(rid, EOK, off);
660
		return;
661
	}
662
	if (whence == SEEK_CUR) {
663
		if (file->pos + off < file->pos) {
664
			futex_up(&file->lock);
665
			ipc_answer_0(rid, EOVERFLOW);
666
			return;
667
		}
668
		file->pos += off;
669
		newpos = file->pos;
670
		futex_up(&file->lock);
671
		ipc_answer_1(rid, EOK, newpos);
672
		return;
673
	}
674
	if (whence == SEEK_END) {
675
		rwlock_read_lock(&file->node->contents_rwlock);
676
		size_t size = file->node->size;
677
		rwlock_read_unlock(&file->node->contents_rwlock);
678
		if (size + off < size) {
679
			futex_up(&file->lock);
680
			ipc_answer_0(rid, EOVERFLOW);
681
			return;
682
		}
683
		newpos = size + off;
684
		futex_up(&file->lock);
685
		ipc_answer_1(rid, EOK, newpos);
686
		return;
687
	}
688
	futex_up(&file->lock);
689
	ipc_answer_0(rid, EINVAL);
690
}
691
 
692
atomic_t fs_head_futex = FUTEX_INITIALIZER;
693
link_t fs_head;
694
 
695
atomic_t fs_handle_next = {
696
	.count = 1
697
};
698
 
699
/** Verify the VFS info structure.
700
 *
701
 * @param info		Info structure to be verified.
702
 *
703
 * @return		Non-zero if the info structure is sane, zero otherwise.
704
 */
705
static bool vfs_info_sane(vfs_info_t *info)
706
{
707
	int i;
708
 
709
	/*
710
	 * Check if the name is non-empty and is composed solely of ASCII
711
	 * characters [a-z]+[a-z0-9_-]*.
712
	 */
713
	if (!islower(info->name[0])) {
714
		dprintf("The name doesn't start with a lowercase character.\n");
715
		return false;
716
	}
717
	for (i = 1; i < FS_NAME_MAXLEN; i++) {
718
		if (!(islower(info->name[i]) || isdigit(info->name[i])) &&
719
		    (info->name[i] != '-') && (info->name[i] != '_')) {
720
			if (info->name[i] == '\0') {
721
				break;
722
			} else {
723
				dprintf("The name contains illegal "
724
				    "characters.\n");
725
				return false;
726
			}
727
		}
728
	}
729
	/*
730
	 * This check is not redundant. It ensures that the name is
731
	 * NULL-terminated, even if FS_NAME_MAXLEN characters are used.
732
	 */
733
	if (info->name[i] != '\0') {
734
		dprintf("The name is not properly NULL-terminated.\n");	
735
		return false;
736
	}
737
 
738
 
739
	/*
740
	 * Check if the FS implements mandatory VFS operations.
741
	 */
742
	if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_LOOKUP)] != VFS_OP_DEFINED) {
743
		dprintf("Operation VFS_LOOKUP not defined by the client.\n");
744
		return false;
745
	}
746
	if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_OPEN)] != VFS_OP_DEFINED) {
747
		dprintf("Operation VFS_OPEN not defined by the client.\n");
748
		return false;
749
	}
750
	if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_CLOSE)] != VFS_OP_DEFINED) {
751
		dprintf("Operation VFS_CLOSE not defined by the client.\n");
752
		return false;
753
	}
754
	if (info->ops[IPC_METHOD_TO_VFS_OP(VFS_READ)] != VFS_OP_DEFINED) {
755
		dprintf("Operation VFS_READ not defined by the client.\n");
756
		return false;
757
	}
758
 
759
	/*
760
	 * Check if each operation is either not defined, defined or default.
761
	 */
2690 jermar 762
	for (i = VFS_FIRST; i < VFS_LAST_CLNT; i++) {
2689 jermar 763
		if ((info->ops[IPC_METHOD_TO_VFS_OP(i)] != VFS_OP_NULL) && 
764
		    (info->ops[IPC_METHOD_TO_VFS_OP(i)] != VFS_OP_DEFAULT) && 
765
		    (info->ops[IPC_METHOD_TO_VFS_OP(i)] != VFS_OP_DEFINED)) {
766
			dprintf("Operation info not understood.\n");
767
			return false;
768
		}
769
	}
770
	return true;
771
}
772
 
773
/** VFS_REGISTER protocol function.
774
 *
775
 * @param rid		Hash of the call with the request.
776
 * @param request	Call structure with the request.
777
 */
778
void vfs_register(ipc_callid_t rid, ipc_call_t *request)
779
{
780
	ipc_callid_t callid;
781
	ipc_call_t call;
782
	int rc;
783
	size_t size;
784
 
785
	dprintf("Processing VFS_REGISTER request received from %p.\n",
786
	    request->in_phone_hash);
787
 
788
	/*
789
	 * The first call has to be IPC_M_DATA_SEND in which we receive the
790
	 * VFS info structure from the client FS.
791
	 */
792
	if (!ipc_data_write_receive(&callid, &size)) {
793
		/*
794
		 * The client doesn't obey the same protocol as we do.
795
		 */
796
		dprintf("Receiving of VFS info failed.\n");
797
		ipc_answer_0(callid, EINVAL);
798
		ipc_answer_0(rid, EINVAL);
799
		return;
800
	}
801
 
802
	dprintf("VFS info received, size = %d\n", size);
803
 
804
	/*
805
	 * We know the size of the VFS info structure. See if the client
806
	 * understands this easy concept too.
807
	 */
808
	if (size != sizeof(vfs_info_t)) {
809
		/*
810
		 * The client is sending us something, which cannot be
811
		 * the info structure.
812
		 */
813
		dprintf("Received VFS info has bad size.\n");
814
		ipc_answer_0(callid, EINVAL);
815
		ipc_answer_0(rid, EINVAL);
816
		return;
817
	}
818
 
819
	/*
820
	 * Allocate and initialize a buffer for the fs_info structure.
821
	 */
822
	fs_info_t *fs_info;
823
	fs_info = (fs_info_t *) malloc(sizeof(fs_info_t));
824
	if (!fs_info) {
825
		dprintf("Could not allocate memory for FS info.\n");
826
		ipc_answer_0(callid, ENOMEM);
827
		ipc_answer_0(rid, ENOMEM);
828
		return;
829
	}
830
	link_initialize(&fs_info->fs_link);
831
	futex_initialize(&fs_info->phone_futex, 1);
832
 
833
	rc = ipc_data_write_finalize(callid, &fs_info->vfs_info, size);
834
	if (rc != EOK) {
835
		dprintf("Failed to deliver the VFS info into our AS, rc=%d.\n",
836
		    rc);
837
		free(fs_info);
838
		ipc_answer_0(callid, rc);
839
		ipc_answer_0(rid, rc);
840
		return;
841
	}
842
 
843
	dprintf("VFS info delivered.\n");
844
 
845
	if (!vfs_info_sane(&fs_info->vfs_info)) {
846
		free(fs_info);
847
		ipc_answer_0(callid, EINVAL);
848
		ipc_answer_0(rid, EINVAL);
849
		return;
850
	}
851
 
852
	futex_down(&fs_head_futex);
853
 
854
	/*
855
	 * Check for duplicit registrations.
856
	 */
857
	if (fs_name_to_handle(fs_info->vfs_info.name, false)) {
858
		/*
859
		 * We already register a fs like this.
860
		 */
861
		dprintf("FS is already registered.\n");
862
		futex_up(&fs_head_futex);
863
		free(fs_info);
864
		ipc_answer_0(callid, EEXISTS);
865
		ipc_answer_0(rid, EEXISTS);
866
		return;
867
	}
868
 
869
	/*
870
	 * Add fs_info to the list of registered FS's.
871
	 */
872
	dprintf("Inserting FS into the list of registered file systems.\n");
873
	list_append(&fs_info->fs_link, &fs_head);
874
 
875
	/*
876
	 * Now we want the client to send us the IPC_M_CONNECT_TO_ME call so
877
	 * that a callback connection is created and we have a phone through
878
	 * which to forward VFS requests to it.
879
	 */
880
	callid = async_get_call(&call);
881
	if (IPC_GET_METHOD(call) != IPC_M_CONNECT_TO_ME) {
882
		dprintf("Unexpected call, method = %d\n", IPC_GET_METHOD(call));
883
		list_remove(&fs_info->fs_link);
884
		futex_up(&fs_head_futex);
885
		free(fs_info);
886
		ipc_answer_0(callid, EINVAL);
887
		ipc_answer_0(rid, EINVAL);
888
		return;
889
	}
890
	fs_info->phone = IPC_GET_ARG5(call);
891
	ipc_answer_0(callid, EOK);
892
 
893
	dprintf("Callback connection to FS created.\n");
894
 
895
	/*
896
	 * The client will want us to send him the address space area with PLB.
897
	 */
898
 
899
	if (!ipc_share_in_receive(&callid, &size)) {
900
		dprintf("Unexpected call, method = %d\n", IPC_GET_METHOD(call));
901
		list_remove(&fs_info->fs_link);
902
		futex_up(&fs_head_futex);
903
		ipc_hangup(fs_info->phone);
904
		free(fs_info);
905
		ipc_answer_0(callid, EINVAL);
906
		ipc_answer_0(rid, EINVAL);
907
		return;
908
	}
909
 
910
	/*
911
	 * We can only send the client address space area PLB_SIZE bytes long.
912
	 */
913
	if (size != PLB_SIZE) {
914
		dprintf("Client suggests wrong size of PFB, size = %d\n", size);
915
		list_remove(&fs_info->fs_link);
916
		futex_up(&fs_head_futex);
917
		ipc_hangup(fs_info->phone);
918
		free(fs_info);
919
		ipc_answer_0(callid, EINVAL);
920
		ipc_answer_0(rid, EINVAL);
921
		return;
922
	}
923
 
924
	/*
925
	 * Commit to read-only sharing the PLB with the client.
926
	 */
927
	(void) ipc_share_in_finalize(callid, plb,
928
	    AS_AREA_READ | AS_AREA_CACHEABLE);
929
 
930
	dprintf("Sharing PLB.\n");
931
 
932
	/*
933
	 * That was it. The FS has been registered.
934
	 * In reply to the VFS_REGISTER request, we assign the client file
935
	 * system a global file system handle.
936
	 */
937
	fs_info->fs_handle = (int) atomic_postinc(&fs_handle_next);
938
	ipc_answer_1(rid, EOK, (ipcarg_t) fs_info->fs_handle);
939
 
940
	futex_up(&fs_head_futex);
941
 
942
	dprintf("\"%.*s\" filesystem successfully registered, handle=%d.\n",
943
	    FS_NAME_MAXLEN, fs_info->vfs_info.name, fs_info->fs_handle);
944
}
945
 
946
/** For a given file system handle, implement policy for allocating a phone.
947
 *
948
 * @param handle	File system handle.
949
 *
950
 * @return		Phone over which a multi-call request can be safely
951
 *			sent. Return 0 if no phone was found.
952
 */
953
int vfs_grab_phone(int handle)
954
{
955
	/*
956
	 * For now, we don't try to be very clever and very fast.
957
	 * We simply lookup the phone in the fs_head list. We currently don't
958
	 * open any additional phones (even though that itself would be pretty
959
	 * straightforward; housekeeping multiple open phones to a FS task would
960
	 * be more demanding). Instead, we simply take the respective
961
	 * phone_futex and keep it until vfs_release_phone().
962
	 */
963
	futex_down(&fs_head_futex);
964
	link_t *cur;
965
	fs_info_t *fs;
966
	for (cur = fs_head.next; cur != &fs_head; cur = cur->next) {
967
		fs = list_get_instance(cur, fs_info_t, fs_link);
968
		if (fs->fs_handle == handle) {
969
			futex_up(&fs_head_futex);
970
			/*
971
			 * For now, take the futex unconditionally.
972
			 * Oh yeah, serialization rocks.
973
			 * It will be up'ed in vfs_release_phone().
974
			 */
975
			futex_down(&fs->phone_futex);
976
			/*
977
			 * Avoid deadlock with other fibrils in the same thread
978
			 * by disabling fibril preemption.
979
			 */
980
			fibril_inc_sercount();
981
			return fs->phone; 
982
		}
983
	}
984
	futex_up(&fs_head_futex);
985
	return 0;
986
}
987
 
988
/** Tell VFS that the phone is in use for any request.
989
 *
990
 * @param phone		Phone to FS task.
991
 */
992
void vfs_release_phone(int phone)
993
{
994
	bool found = false;
995
 
996
	/*
997
	 * Undo the fibril_inc_sercount() done in vfs_grab_phone().
998
	 */
999
	fibril_dec_sercount();
1000
 
1001
	futex_down(&fs_head_futex);
1002
	link_t *cur;
1003
	for (cur = fs_head.next; cur != &fs_head; cur = cur->next) {
1004
		fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
1005
		if (fs->phone == phone) {
1006
			found = true;
1007
			futex_up(&fs_head_futex);
1008
			futex_up(&fs->phone_futex);
1009
			return;
1010
		}
1011
	}
1012
	futex_up(&fs_head_futex);
1013
 
1014
	/*
1015
	 * Not good to get here.
1016
	 */
1017
	assert(found == true);
1018
}
1019
 
1020
/** Convert file system name to its handle.
1021
 *
1022
 * @param name		File system name.
1023
 * @param lock		If true, the function will down and up the
1024
 * 			fs_head_futex.
1025
 *
1026
 * @return		File system handle or zero if file system not found.
1027
 */
1028
int fs_name_to_handle(char *name, bool lock)
1029
{
1030
	int handle = 0;
1031
 
1032
	if (lock)
1033
		futex_down(&fs_head_futex);
1034
	link_t *cur;
1035
	for (cur = fs_head.next; cur != &fs_head; cur = cur->next) {
1036
		fs_info_t *fs = list_get_instance(cur, fs_info_t, fs_link);
1037
		if (strncmp(fs->vfs_info.name, name,
1038
		    sizeof(fs->vfs_info.name)) == 0) { 
1039
			handle = fs->fs_handle;
1040
			break;
1041
		}
1042
	}
1043
	if (lock)
1044
		futex_up(&fs_head_futex);
1045
	return handle;
1046
}
1047
 
1048
/**
1049
 * @}
1050
 */