source: mainline/uspace/srv/vfs/vfs_file.c@ a274a5f

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

Merge from lp:~zarevucky-jiri/helenos/vfs-2.5/ revisions 1932-1936

Original commit messages:

1936: Jiri Zarevucky 2013-08-05 Modifications to vfs_rdwr.
1935: Jiri Zarevucky 2013-08-05 Fix a bug in read/write.
1934: Jiri Zarevucky 2013-08-05 Fix a hidden bug in handle passing.
1933: Jiri Zarevucky 2013-08-05 Add VFS_IN_CLONE.
1932: Jiri Zarevucky 2013-08-05 Add functions for passing handles around.

Modifications:

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