source: mainline/uspace/srv/vfs/vfs_file.c@ 63d46341

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 63d46341 was 777832e, checked in by Jiri Svoboda <jiri@…>, 7 years ago

fgetpos, fsetpos, perror.

  • 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 errno_t _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(VFS_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, VFS_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 < VFS_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 errno_t 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 errno_t 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 errno_t vfs_file_delref(vfs_client_data_t *vfs_data, vfs_file_t *file)
171{
172 errno_t 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 errno_t _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 = VFS_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 == VFS_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 */
256errno_t 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 errno_t _vfs_fd_free_locked(vfs_client_data_t *vfs_data, int fd)
262{
263 if ((fd < 0) || (fd >= VFS_MAX_OPEN_FILES) || !vfs_data->files[fd]) {
264 return EBADF;
265 }
266
267 errno_t rc = vfs_file_delref(vfs_data, vfs_data->files[fd]);
268 vfs_data->files[fd] = NULL;
269 return rc;
270}
271
272static errno_t _vfs_fd_free(vfs_client_data_t *vfs_data, int fd)
273{
274 errno_t 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 */
293errno_t 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 */
307errno_t 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 >= VFS_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 < VFS_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
431errno_t 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 errno_t 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.