source: mainline/uspace/srv/vfs/vfs_file.c@ 25a179e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 25a179e was 25a179e, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

IPC return values are always errno constants. Adjust types to reflect that.

In principle, IPC server is not allowed to return non-errno values via
the "main" return value, because kernel interprets it (e.g. EHANGUP).
It's still possible to return arbitrary additional return values alongside EOK,
which are not interpreted in normal communication.

  • Property mode set to 100644
File size: 10.7 KB
Line 
1/*
2 * Copyright (c) 2007 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_file.c
35 * @brief Various operations on files have their home in this file.
36 */
37
38#include <errno.h>
39#include <stdlib.h>
40#include <str.h>
41#include <assert.h>
42#include <stdbool.h>
43#include <fibril.h>
44#include <fibril_synch.h>
45#include <adt/list.h>
46#include <task.h>
47#include <vfs/vfs.h>
48#include "vfs.h"
49
50#define VFS_DATA ((vfs_client_data_t *) async_get_client_data())
51#define FILES (VFS_DATA->files)
52
53typedef struct {
54 fibril_mutex_t lock;
55 fibril_condvar_t cv;
56 list_t passed_handles;
57 vfs_file_t **files;
58} vfs_client_data_t;
59
60typedef struct {
61 link_t link;
62 vfs_node_t *node;
63 int permissions;
64} vfs_boxed_handle_t;
65
66static int _vfs_fd_free(vfs_client_data_t *, int);
67
68/** Initialize the table of open files. */
69static bool vfs_files_init(vfs_client_data_t *vfs_data)
70{
71 fibril_mutex_lock(&vfs_data->lock);
72 if (!vfs_data->files) {
73 vfs_data->files = malloc(MAX_OPEN_FILES * sizeof(vfs_file_t *));
74 if (!vfs_data->files) {
75 fibril_mutex_unlock(&vfs_data->lock);
76 return false;
77 }
78 memset(vfs_data->files, 0, MAX_OPEN_FILES * sizeof(vfs_file_t *));
79 }
80 fibril_mutex_unlock(&vfs_data->lock);
81 return true;
82}
83
84/** Cleanup the table of open files. */
85static void vfs_files_done(vfs_client_data_t *vfs_data)
86{
87 int i;
88
89 if (!vfs_data->files)
90 return;
91
92 for (i = 0; i < MAX_OPEN_FILES; i++) {
93 if (vfs_data->files[i])
94 (void) _vfs_fd_free(vfs_data, i);
95 }
96
97 free(vfs_data->files);
98
99 while (!list_empty(&vfs_data->passed_handles)) {
100 link_t *lnk;
101 vfs_boxed_handle_t *bh;
102
103 lnk = list_first(&vfs_data->passed_handles);
104 list_remove(lnk);
105
106 bh = list_get_instance(lnk, vfs_boxed_handle_t, link);
107 free(bh);
108 }
109}
110
111void *vfs_client_data_create(void)
112{
113 vfs_client_data_t *vfs_data;
114
115 vfs_data = malloc(sizeof(vfs_client_data_t));
116 if (vfs_data) {
117 fibril_mutex_initialize(&vfs_data->lock);
118 fibril_condvar_initialize(&vfs_data->cv);
119 list_initialize(&vfs_data->passed_handles);
120 vfs_data->files = NULL;
121 }
122
123 return vfs_data;
124}
125
126void vfs_client_data_destroy(void *data)
127{
128 vfs_client_data_t *vfs_data = (vfs_client_data_t *) data;
129
130 vfs_files_done(vfs_data);
131 free(vfs_data);
132}
133
134/** Close the file in the endpoint FS server. */
135static int vfs_file_close_remote(vfs_file_t *file)
136{
137 assert(!file->refcnt);
138
139 async_exch_t *exch = vfs_exchange_grab(file->node->fs_handle);
140
141 ipc_call_t answer;
142 aid_t msg = async_send_2(exch, VFS_OUT_CLOSE, file->node->service_id,
143 file->node->index, &answer);
144
145 vfs_exchange_release(exch);
146
147 int rc;
148 async_wait_for(msg, &rc);
149
150 return IPC_GET_RETVAL(answer);
151}
152
153/** Increment reference count of VFS file structure.
154 *
155 * @param file File structure that will have reference count
156 * incremented.
157 */
158static void vfs_file_addref(vfs_client_data_t *vfs_data, vfs_file_t *file)
159{
160 assert(fibril_mutex_is_locked(&vfs_data->lock));
161
162 file->refcnt++;
163}
164
165/** Decrement reference count of VFS file structure.
166 *
167 * @param file File structure that will have reference count
168 * decremented.
169 */
170static int vfs_file_delref(vfs_client_data_t *vfs_data, vfs_file_t *file)
171{
172 int rc = EOK;
173
174 assert(fibril_mutex_is_locked(&vfs_data->lock));
175
176 if (file->refcnt-- == 1) {
177 /*
178 * Lost the last reference to a file, need to close it in the
179 * endpoint FS and drop our reference to the underlying VFS node.
180 */
181
182 if (file->node != NULL) {
183 if (file->open_read || file->open_write) {
184 rc = vfs_file_close_remote(file);
185 }
186 vfs_node_delref(file->node);
187 }
188 free(file);
189 }
190
191 return rc;
192}
193
194static int _vfs_fd_alloc(vfs_client_data_t *vfs_data, vfs_file_t **file, bool desc, int *out_fd)
195{
196 if (!vfs_files_init(vfs_data))
197 return ENOMEM;
198
199 unsigned int i;
200 if (desc)
201 i = MAX_OPEN_FILES - 1;
202 else
203 i = 0;
204
205 fibril_mutex_lock(&vfs_data->lock);
206 while (true) {
207 if (!vfs_data->files[i]) {
208 vfs_data->files[i] = (vfs_file_t *) malloc(sizeof(vfs_file_t));
209 if (!vfs_data->files[i]) {
210 fibril_mutex_unlock(&vfs_data->lock);
211 return ENOMEM;
212 }
213
214
215 memset(vfs_data->files[i], 0, sizeof(vfs_file_t));
216
217 fibril_mutex_initialize(&vfs_data->files[i]->_lock);
218 fibril_mutex_lock(&vfs_data->files[i]->_lock);
219 vfs_file_addref(vfs_data, vfs_data->files[i]);
220
221 *file = vfs_data->files[i];
222 vfs_file_addref(vfs_data, *file);
223
224 fibril_mutex_unlock(&vfs_data->lock);
225 *out_fd = (int) i;
226 return EOK;
227 }
228
229 if (desc) {
230 if (i == 0)
231 break;
232
233 i--;
234 } else {
235 if (i == MAX_OPEN_FILES - 1)
236 break;
237
238 i++;
239 }
240 }
241 fibril_mutex_unlock(&vfs_data->lock);
242
243 return EMFILE;
244}
245
246/** Allocate a file descriptor.
247 *
248 * @param file Is set to point to the newly created file structure. Must be put afterwards.
249 * @param desc If true, look for an available file descriptor
250 * in a descending order.
251 *
252 * @param[out] out_fd First available file descriptor
253 *
254 * @return Error code.
255 */
256int vfs_fd_alloc(vfs_file_t **file, bool desc, int *out_fd)
257{
258 return _vfs_fd_alloc(VFS_DATA, file, desc, out_fd);
259}
260
261static int _vfs_fd_free_locked(vfs_client_data_t *vfs_data, int fd)
262{
263 if ((fd < 0) || (fd >= MAX_OPEN_FILES) || !vfs_data->files[fd]) {
264 return EBADF;
265 }
266
267 int rc = vfs_file_delref(vfs_data, vfs_data->files[fd]);
268 vfs_data->files[fd] = NULL;
269 return rc;
270}
271
272static int _vfs_fd_free(vfs_client_data_t *vfs_data, int fd)
273{
274 int rc;
275
276 if (!vfs_files_init(vfs_data))
277 return ENOMEM;
278
279 fibril_mutex_lock(&vfs_data->lock);
280 rc = _vfs_fd_free_locked(vfs_data, fd);
281 fibril_mutex_unlock(&vfs_data->lock);
282
283 return rc;
284}
285
286/** Release file descriptor.
287 *
288 * @param fd File descriptor being released.
289 *
290 * @return EOK on success or EBADF if fd is an invalid file
291 * descriptor.
292 */
293int vfs_fd_free(int fd)
294{
295 return _vfs_fd_free(VFS_DATA, fd);
296}
297
298/** Assign a file to a file descriptor.
299 *
300 * @param file File to assign.
301 * @param fd File descriptor to assign to.
302 *
303 * @return EOK on success or EINVAL if fd is an invalid or already
304 * used file descriptor.
305 *
306 */
307int vfs_fd_assign(vfs_file_t *file, int fd)
308{
309 if (!vfs_files_init(VFS_DATA))
310 return ENOMEM;
311
312 fibril_mutex_lock(&VFS_DATA->lock);
313 if ((fd < 0) || (fd >= MAX_OPEN_FILES)) {
314 fibril_mutex_unlock(&VFS_DATA->lock);
315 return EBADF;
316 }
317
318 /* Make sure fd is closed. */
319 (void) _vfs_fd_free_locked(VFS_DATA, fd);
320 assert(FILES[fd] == NULL);
321
322 FILES[fd] = file;
323 vfs_file_addref(VFS_DATA, FILES[fd]);
324 fibril_mutex_unlock(&VFS_DATA->lock);
325
326 return EOK;
327}
328
329static void _vfs_file_put(vfs_client_data_t *vfs_data, vfs_file_t *file)
330{
331 fibril_mutex_unlock(&file->_lock);
332
333 fibril_mutex_lock(&vfs_data->lock);
334 vfs_file_delref(vfs_data, file);
335 fibril_mutex_unlock(&vfs_data->lock);
336}
337
338static vfs_file_t *_vfs_file_get(vfs_client_data_t *vfs_data, int fd)
339{
340 if (!vfs_files_init(vfs_data))
341 return NULL;
342
343 fibril_mutex_lock(&vfs_data->lock);
344 if ((fd >= 0) && (fd < MAX_OPEN_FILES)) {
345 vfs_file_t *file = vfs_data->files[fd];
346 if (file != NULL) {
347 vfs_file_addref(vfs_data, file);
348 fibril_mutex_unlock(&vfs_data->lock);
349
350 fibril_mutex_lock(&file->_lock);
351 if (file->node == NULL) {
352 _vfs_file_put(vfs_data, file);
353 return NULL;
354 }
355 assert(file != NULL);
356 assert(file->node != NULL);
357 return file;
358 }
359 }
360 fibril_mutex_unlock(&vfs_data->lock);
361
362 return NULL;
363}
364
365/** Find VFS file structure for a given file descriptor.
366 *
367 * @param fd File descriptor.
368 *
369 * @return VFS file structure corresponding to fd.
370 */
371vfs_file_t *vfs_file_get(int fd)
372{
373 return _vfs_file_get(VFS_DATA, fd);
374}
375
376/** Stop using a file structure.
377 *
378 * @param file VFS file structure.
379 */
380void vfs_file_put(vfs_file_t *file)
381{
382 _vfs_file_put(VFS_DATA, file);
383}
384
385void vfs_op_pass_handle(task_id_t donor_id, task_id_t acceptor_id, int donor_fd)
386{
387 vfs_client_data_t *donor_data = NULL;
388 vfs_client_data_t *acceptor_data = NULL;
389 vfs_file_t *donor_file = NULL;
390 vfs_boxed_handle_t *bh;
391
392 acceptor_data = async_get_client_data_by_id(acceptor_id);
393 if (!acceptor_data)
394 return;
395
396 bh = malloc(sizeof(vfs_boxed_handle_t));
397 assert(bh);
398
399 link_initialize(&bh->link);
400 bh->node = NULL;
401
402 donor_data = async_get_client_data_by_id(donor_id);
403 if (!donor_data)
404 goto out;
405
406 donor_file = _vfs_file_get(donor_data, donor_fd);
407 if (!donor_file)
408 goto out;
409
410 /*
411 * Add a new reference to the underlying VFS node.
412 */
413 vfs_node_addref(donor_file->node);
414 bh->node = donor_file->node;
415 bh->permissions = donor_file->permissions;
416
417out:
418 fibril_mutex_lock(&acceptor_data->lock);
419 list_append(&bh->link, &acceptor_data->passed_handles);
420 fibril_condvar_broadcast(&acceptor_data->cv);
421 fibril_mutex_unlock(&acceptor_data->lock);
422
423 if (donor_data)
424 async_put_client_data_by_id(donor_id);
425 if (acceptor_data)
426 async_put_client_data_by_id(acceptor_id);
427 if (donor_file)
428 _vfs_file_put(donor_data, donor_file);
429}
430
431int vfs_wait_handle_internal(bool high_fd, int *out_fd)
432{
433 vfs_client_data_t *vfs_data = VFS_DATA;
434
435 fibril_mutex_lock(&vfs_data->lock);
436 while (list_empty(&vfs_data->passed_handles))
437 fibril_condvar_wait(&vfs_data->cv, &vfs_data->lock);
438 link_t *lnk = list_first(&vfs_data->passed_handles);
439 list_remove(lnk);
440 fibril_mutex_unlock(&vfs_data->lock);
441
442 vfs_boxed_handle_t *bh = list_get_instance(lnk, vfs_boxed_handle_t, link);
443
444 vfs_file_t *file;
445 int rc = _vfs_fd_alloc(vfs_data, &file, high_fd, out_fd);
446 if (rc != EOK) {
447 vfs_node_delref(bh->node);
448 free(bh);
449 return rc;
450 }
451
452 file->node = bh->node;
453 file->permissions = bh->permissions;
454 vfs_file_put(file);
455 free(bh);
456 return EOK;
457}
458
459/**
460 * @}
461 */
Note: See TracBrowser for help on using the repository browser.