source: mainline/uspace/srv/fs/devfs/devfs_ops.c@ 852b801

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 852b801 was 852b801, checked in by Jakub Jermar <jakub@…>, 16 years ago

Introduce VFS_IN_FSTAT and VFS_OUT_STAT.
Provide libc fstat() and devfs_stat().
This functionality replaces VFS_IN_NODE
and VFS_IN/OUT_DEVICE. FAT and TMPFS
still do not implement this and VFS_IN_STAT
and stat() need implementation as well.

  • Property mode set to 100644
File size: 13.1 KB
Line 
1/*
2 * Copyright (c) 2009 Martin Decky
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 devfs_ops.c
35 * @brief Implementation of VFS operations for the devfs file system server.
36 */
37
38#include <ipc/ipc.h>
39#include <bool.h>
40#include <errno.h>
41#include <malloc.h>
42#include <string.h>
43#include <libfs.h>
44#include <fibril_sync.h>
45#include <adt/hash_table.h>
46#include <sys/stat.h>
47#include "devfs.h"
48#include "devfs_ops.h"
49
50#define PLB_GET_CHAR(pos) (devfs_reg.plb_ro[pos % PLB_SIZE])
51
52/** Opened devices structure */
53typedef struct {
54 dev_handle_t handle;
55 int phone;
56 size_t refcount;
57 link_t link;
58} device_t;
59
60/** Hash table of opened devices */
61static hash_table_t devices;
62
63/** Hash table mutex */
64static FIBRIL_MUTEX_INITIALIZE(devices_mutex);
65
66#define DEVICES_KEYS 1
67#define DEVICES_KEY_HANDLE 0
68#define DEVICES_BUCKETS 256
69
70/* Implementation of hash table interface for the nodes hash table. */
71static hash_index_t devices_hash(unsigned long key[])
72{
73 return key[DEVICES_KEY_HANDLE] % DEVICES_BUCKETS;
74}
75
76static int devices_compare(unsigned long key[], hash_count_t keys, link_t *item)
77{
78 device_t *dev = hash_table_get_instance(item, device_t, link);
79 return (dev->handle == (dev_handle_t) key[DEVICES_KEY_HANDLE]);
80}
81
82static void devices_remove_callback(link_t *item)
83{
84 free(hash_table_get_instance(item, device_t, link));
85}
86
87static hash_table_operations_t devices_ops = {
88 .hash = devices_hash,
89 .compare = devices_compare,
90 .remove_callback = devices_remove_callback
91};
92
93bool devfs_init(void)
94{
95 if (!hash_table_create(&devices, DEVICES_BUCKETS,
96 DEVICES_KEYS, &devices_ops))
97 return false;
98
99 if (devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING) < 0)
100 return false;
101
102 return true;
103}
104
105void devfs_mounted(ipc_callid_t rid, ipc_call_t *request)
106{
107 /* Accept the mount options */
108 ipc_callid_t callid;
109 size_t size;
110 if (!ipc_data_write_receive(&callid, &size)) {
111 ipc_answer_0(callid, EINVAL);
112 ipc_answer_0(rid, EINVAL);
113 return;
114 }
115
116 char *opts = malloc(size + 1);
117 if (!opts) {
118 ipc_answer_0(callid, ENOMEM);
119 ipc_answer_0(rid, ENOMEM);
120 return;
121 }
122
123 ipcarg_t retval = ipc_data_write_finalize(callid, opts, size);
124 if (retval != EOK) {
125 ipc_answer_0(rid, retval);
126 free(opts);
127 return;
128 }
129
130 free(opts);
131
132 ipc_answer_3(rid, EOK, 0, 0, 0);
133}
134
135void devfs_mount(ipc_callid_t rid, ipc_call_t *request)
136{
137 ipc_answer_0(rid, ENOTSUP);
138}
139
140void devfs_lookup(ipc_callid_t rid, ipc_call_t *request)
141{
142 ipcarg_t first = IPC_GET_ARG1(*request);
143 ipcarg_t last = IPC_GET_ARG2(*request);
144 dev_handle_t dev_handle = IPC_GET_ARG3(*request);
145 ipcarg_t lflag = IPC_GET_ARG4(*request);
146 fs_index_t index = IPC_GET_ARG5(*request);
147
148 /* Hierarchy is flat, no altroot is supported */
149 if (index != 0) {
150 ipc_answer_0(rid, ENOENT);
151 return;
152 }
153
154 if ((lflag & L_LINK) || (lflag & L_UNLINK)) {
155 ipc_answer_0(rid, ENOTSUP);
156 return;
157 }
158
159 /* Eat slash */
160 if (PLB_GET_CHAR(first) == '/') {
161 first++;
162 first %= PLB_SIZE;
163 }
164
165 if (first >= last) {
166 /* Root entry */
167 if (lflag & L_DIRECTORY)
168 ipc_answer_5(rid, EOK, devfs_reg.fs_handle, dev_handle, 0, 0, 0);
169 else
170 ipc_answer_0(rid, ENOENT);
171 } else {
172 if (lflag & L_FILE) {
173 size_t len;
174 if (last >= first)
175 len = last - first + 1;
176 else
177 len = first + PLB_SIZE - last + 1;
178
179 char *name = (char *) malloc(len + 1);
180 if (name == NULL) {
181 ipc_answer_0(rid, ENOMEM);
182 return;
183 }
184
185 size_t i;
186 for (i = 0; i < len; i++)
187 name[i] = PLB_GET_CHAR(first + i);
188
189 name[len] = 0;
190
191 dev_handle_t handle;
192 if (devmap_device_get_handle(name, &handle, 0) != EOK) {
193 free(name);
194 ipc_answer_0(rid, ENOENT);
195 return;
196 }
197
198 if (lflag & L_OPEN) {
199 unsigned long key[] = {
200 [DEVICES_KEY_HANDLE] = (unsigned long) handle
201 };
202
203 fibril_mutex_lock(&devices_mutex);
204 link_t *lnk = hash_table_find(&devices, key);
205 if (lnk == NULL) {
206 int phone = devmap_device_connect(handle, 0);
207 if (phone < 0) {
208 fibril_mutex_unlock(&devices_mutex);
209 free(name);
210 ipc_answer_0(rid, ENOENT);
211 return;
212 }
213
214 device_t *dev = (device_t *) malloc(sizeof(device_t));
215 if (dev == NULL) {
216 fibril_mutex_unlock(&devices_mutex);
217 free(name);
218 ipc_answer_0(rid, ENOMEM);
219 return;
220 }
221
222 dev->handle = handle;
223 dev->phone = phone;
224 dev->refcount = 1;
225
226 hash_table_insert(&devices, key, &dev->link);
227 } else {
228 device_t *dev = hash_table_get_instance(lnk, device_t, link);
229 dev->refcount++;
230 }
231 fibril_mutex_unlock(&devices_mutex);
232 }
233
234 free(name);
235
236 ipc_answer_5(rid, EOK, devfs_reg.fs_handle, dev_handle, handle, 0, 1);
237 } else
238 ipc_answer_0(rid, ENOENT);
239 }
240}
241
242void devfs_open_node(ipc_callid_t rid, ipc_call_t *request)
243{
244 dev_handle_t handle = IPC_GET_ARG2(*request);
245
246 unsigned long key[] = {
247 [DEVICES_KEY_HANDLE] = (unsigned long) handle
248 };
249
250 fibril_mutex_lock(&devices_mutex);
251 link_t *lnk = hash_table_find(&devices, key);
252 if (lnk == NULL) {
253 int phone = devmap_device_connect(handle, 0);
254 if (phone < 0) {
255 fibril_mutex_unlock(&devices_mutex);
256 ipc_answer_0(rid, ENOENT);
257 return;
258 }
259
260 device_t *dev = (device_t *) malloc(sizeof(device_t));
261 if (dev == NULL) {
262 fibril_mutex_unlock(&devices_mutex);
263 ipc_answer_0(rid, ENOMEM);
264 return;
265 }
266
267 dev->handle = handle;
268 dev->phone = phone;
269 dev->refcount = 1;
270
271 hash_table_insert(&devices, key, &dev->link);
272 } else {
273 device_t *dev = hash_table_get_instance(lnk, device_t, link);
274 dev->refcount++;
275 }
276 fibril_mutex_unlock(&devices_mutex);
277
278 ipc_answer_3(rid, EOK, 0, 1, L_FILE);
279}
280
281void devfs_stat(ipc_callid_t rid, ipc_call_t *request)
282{
283 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
284 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
285
286 ipc_callid_t callid;
287 size_t size;
288 if (!ipc_data_read_receive(&callid, &size) ||
289 size < sizeof(struct stat)) {
290 ipc_answer_0(callid, EINVAL);
291 ipc_answer_0(rid, EINVAL);
292 return;
293 }
294
295 struct stat *stat = malloc(sizeof(struct stat));
296 if (!stat) {
297 ipc_answer_0(callid, ENOMEM);
298 ipc_answer_0(rid, ENOMEM);
299 return;
300 }
301 memset(stat, 0, sizeof(struct stat));
302
303 stat->fs_handle = devfs_reg.fs_handle;
304 stat->dev_handle = dev_handle;
305 stat->index = index;
306 stat->lnkcnt = 1;
307 stat->is_file = (index != 0);
308 stat->size = 0;
309
310 if (index != 0) {
311 unsigned long key[] = {
312 [DEVICES_KEY_HANDLE] = (unsigned long) index
313 };
314
315 fibril_mutex_lock(&devices_mutex);
316 link_t *lnk = hash_table_find(&devices, key);
317 if (lnk != NULL)
318 stat->devfs_stat.device = (dev_handle_t)index;
319 fibril_mutex_unlock(&devices_mutex);
320 }
321
322 ipc_data_read_finalize(callid, stat, sizeof(struct stat));
323 ipc_answer_0(rid, EOK);
324
325 free(stat);
326}
327
328void devfs_read(ipc_callid_t rid, ipc_call_t *request)
329{
330 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
331 off_t pos = (off_t) IPC_GET_ARG3(*request);
332
333 if (index != 0) {
334 unsigned long key[] = {
335 [DEVICES_KEY_HANDLE] = (unsigned long) index
336 };
337
338 fibril_mutex_lock(&devices_mutex);
339 link_t *lnk = hash_table_find(&devices, key);
340 if (lnk == NULL) {
341 fibril_mutex_unlock(&devices_mutex);
342 ipc_answer_0(rid, ENOENT);
343 return;
344 }
345
346 device_t *dev = hash_table_get_instance(lnk, device_t, link);
347
348 ipc_callid_t callid;
349 if (!ipc_data_read_receive(&callid, NULL)) {
350 fibril_mutex_unlock(&devices_mutex);
351 ipc_answer_0(callid, EINVAL);
352 ipc_answer_0(rid, EINVAL);
353 return;
354 }
355
356 /* Make a request at the driver */
357 ipc_call_t answer;
358 aid_t msg = async_send_3(dev->phone, IPC_GET_METHOD(*request),
359 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request),
360 IPC_GET_ARG3(*request), &answer);
361
362 /* Forward the IPC_M_DATA_READ request to the driver */
363 ipc_forward_fast(callid, dev->phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
364 fibril_mutex_unlock(&devices_mutex);
365
366 /* Wait for reply from the driver. */
367 ipcarg_t rc;
368 async_wait_for(msg, &rc);
369 size_t bytes = IPC_GET_ARG1(answer);
370
371 /* Driver reply is the final result of the whole operation */
372 ipc_answer_1(rid, rc, bytes);
373 } else {
374 ipc_callid_t callid;
375 size_t size;
376 if (!ipc_data_read_receive(&callid, &size)) {
377 ipc_answer_0(callid, EINVAL);
378 ipc_answer_0(rid, EINVAL);
379 return;
380 }
381
382 size_t count = devmap_device_get_count();
383 dev_desc_t *desc = malloc(count * sizeof(dev_desc_t));
384 if (desc == NULL) {
385 ipc_answer_0(callid, ENOMEM);
386 ipc_answer_1(rid, ENOMEM, 0);
387 return;
388 }
389
390 size_t max = devmap_device_get_devices(count, desc);
391
392 if (pos < max) {
393 ipc_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
394 } else {
395 ipc_answer_0(callid, ENOENT);
396 ipc_answer_1(rid, ENOENT, 0);
397 return;
398 }
399
400 free(desc);
401
402 ipc_answer_1(rid, EOK, 1);
403 }
404}
405
406void devfs_write(ipc_callid_t rid, ipc_call_t *request)
407{
408 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
409 off_t pos = (off_t) IPC_GET_ARG3(*request);
410
411 if (index != 0) {
412 unsigned long key[] = {
413 [DEVICES_KEY_HANDLE] = (unsigned long) index
414 };
415
416 fibril_mutex_lock(&devices_mutex);
417 link_t *lnk = hash_table_find(&devices, key);
418 if (lnk == NULL) {
419 fibril_mutex_unlock(&devices_mutex);
420 ipc_answer_0(rid, ENOENT);
421 return;
422 }
423
424 device_t *dev = hash_table_get_instance(lnk, device_t, link);
425
426 ipc_callid_t callid;
427 if (!ipc_data_write_receive(&callid, NULL)) {
428 fibril_mutex_unlock(&devices_mutex);
429 ipc_answer_0(callid, EINVAL);
430 ipc_answer_0(rid, EINVAL);
431 return;
432 }
433
434 /* Make a request at the driver */
435 ipc_call_t answer;
436 aid_t msg = async_send_3(dev->phone, IPC_GET_METHOD(*request),
437 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request),
438 IPC_GET_ARG3(*request), &answer);
439
440 /* Forward the IPC_M_DATA_WRITE request to the driver */
441 ipc_forward_fast(callid, dev->phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
442
443 fibril_mutex_unlock(&devices_mutex);
444
445 /* Wait for reply from the driver. */
446 ipcarg_t rc;
447 async_wait_for(msg, &rc);
448 size_t bytes = IPC_GET_ARG1(answer);
449
450 /* Driver reply is the final result of the whole operation */
451 ipc_answer_1(rid, rc, bytes);
452 } else {
453 /* Read-only filesystem */
454 ipc_answer_0(rid, ENOTSUP);
455 }
456}
457
458void devfs_truncate(ipc_callid_t rid, ipc_call_t *request)
459{
460 ipc_answer_0(rid, ENOTSUP);
461}
462
463void devfs_close(ipc_callid_t rid, ipc_call_t *request)
464{
465 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
466
467 if (index != 0) {
468 unsigned long key[] = {
469 [DEVICES_KEY_HANDLE] = (unsigned long) index
470 };
471
472 fibril_mutex_lock(&devices_mutex);
473 link_t *lnk = hash_table_find(&devices, key);
474 if (lnk == NULL) {
475 fibril_mutex_unlock(&devices_mutex);
476 ipc_answer_0(rid, ENOENT);
477 return;
478 }
479
480 device_t *dev = hash_table_get_instance(lnk, device_t, link);
481 dev->refcount--;
482
483 if (dev->refcount == 0) {
484 ipc_hangup(dev->phone);
485 hash_table_remove(&devices, key, DEVICES_KEYS);
486 }
487
488 fibril_mutex_unlock(&devices_mutex);
489
490 ipc_answer_0(rid, EOK);
491 } else
492 ipc_answer_0(rid, ENOTSUP);
493}
494
495void devfs_sync(ipc_callid_t rid, ipc_call_t *request)
496{
497 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
498
499 if (index != 0) {
500 unsigned long key[] = {
501 [DEVICES_KEY_HANDLE] = (unsigned long) index
502 };
503
504 fibril_mutex_lock(&devices_mutex);
505 link_t *lnk = hash_table_find(&devices, key);
506 if (lnk == NULL) {
507 fibril_mutex_unlock(&devices_mutex);
508 ipc_answer_0(rid, ENOENT);
509 return;
510 }
511
512 device_t *dev = hash_table_get_instance(lnk, device_t, link);
513
514 /* Make a request at the driver */
515 ipc_call_t answer;
516 aid_t msg = async_send_2(dev->phone, IPC_GET_METHOD(*request),
517 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request), &answer);
518
519 fibril_mutex_unlock(&devices_mutex);
520
521 /* Wait for reply from the driver */
522 ipcarg_t rc;
523 async_wait_for(msg, &rc);
524
525 /* Driver reply is the final result of the whole operation */
526 ipc_answer_0(rid, rc);
527 } else
528 ipc_answer_0(rid, ENOTSUP);
529}
530
531void devfs_destroy(ipc_callid_t rid, ipc_call_t *request)
532{
533 ipc_answer_0(rid, ENOTSUP);
534}
535
536/**
537 * @}
538 */
Note: See TracBrowser for help on using the repository browser.