source: mainline/uspace/srv/vfs/vfs_file.c@ 79ea5af

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

Merge dup2() into vfs_clone()

  • Property mode set to 100644
File size: 10.5 KB
RevLine 
[320c884]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>
[19f857a]40#include <str.h>
[320c884]41#include <assert.h>
[3e6a98c5]42#include <stdbool.h>
[26360f7]43#include <fibril.h>
[1e4cada]44#include <fibril_synch.h>
[27b76ca]45#include <adt/list.h>
[e2ab36f1]46#include <task.h>
[58898d1d]47#include <vfs/vfs.h>
[320c884]48#include "vfs.h"
49
[79ae36dd]50#define VFS_DATA ((vfs_client_data_t *) async_get_client_data())
[b75e929]51#define FILES (VFS_DATA->files)
52
53typedef struct {
54 fibril_mutex_t lock;
[27b76ca]55 fibril_condvar_t cv;
56 list_t passed_handles;
[b75e929]57 vfs_file_t **files;
58} vfs_client_data_t;
[320c884]59
[27b76ca]60typedef struct {
61 link_t link;
[bb9ec2d]62 vfs_node_t *node;
63 int permissions;
[27b76ca]64} vfs_boxed_handle_t;
65
[2bc13887]66static int _vfs_fd_free(vfs_client_data_t *, int);
67
[320c884]68/** Initialize the table of open files. */
[2bc13887]69static bool vfs_files_init(vfs_client_data_t *vfs_data)
[320c884]70{
[2bc13887]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);
[320c884]76 return false;
[b75e929]77 }
[2bc13887]78 memset(vfs_data->files, 0, MAX_OPEN_FILES * sizeof(vfs_file_t *));
[320c884]79 }
[2bc13887]80 fibril_mutex_unlock(&vfs_data->lock);
[320c884]81 return true;
82}
83
[f29a3a2]84/** Cleanup the table of open files. */
[2bc13887]85static void vfs_files_done(vfs_client_data_t *vfs_data)
[f29a3a2]86{
87 int i;
88
[2bc13887]89 if (!vfs_data->files)
[f29a3a2]90 return;
91
92 for (i = 0; i < MAX_OPEN_FILES; i++) {
[2bc13887]93 if (vfs_data->files[i])
94 (void) _vfs_fd_free(vfs_data, i);
[f29a3a2]95 }
96
[2bc13887]97 free(vfs_data->files);
[27b76ca]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 }
[b75e929]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);
[27b76ca]118 fibril_condvar_initialize(&vfs_data->cv);
119 list_initialize(&vfs_data->passed_handles);
[b75e929]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
[2bc13887]130 vfs_files_done(vfs_data);
[b75e929]131 free(vfs_data);
132}
133
[25bef0ff]134/** Close the file in the endpoint FS server. */
135static int vfs_file_close_remote(vfs_file_t *file)
136{
137 assert(!file->refcnt);
[79ae36dd]138
139 async_exch_t *exch = vfs_exchange_grab(file->node->fs_handle);
140
141 ipc_call_t answer;
[15f3c3f]142 aid_t msg = async_send_2(exch, VFS_OUT_CLOSE, file->node->service_id,
[25bef0ff]143 file->node->index, &answer);
[79ae36dd]144
145 vfs_exchange_release(exch);
146
147 sysarg_t rc;
[25bef0ff]148 async_wait_for(msg, &rc);
[79ae36dd]149
[25bef0ff]150 return IPC_GET_ARG1(answer);
151}
152
[b75e929]153/** Increment reference count of VFS file structure.
154 *
155 * @param file File structure that will have reference count
156 * incremented.
157 */
[2bc13887]158static void vfs_file_addref(vfs_client_data_t *vfs_data, vfs_file_t *file)
[b75e929]159{
[2bc13887]160 assert(fibril_mutex_is_locked(&vfs_data->lock));
[b75e929]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 */
[2bc13887]170static int vfs_file_delref(vfs_client_data_t *vfs_data, vfs_file_t *file)
[b75e929]171{
[25bef0ff]172 int rc = EOK;
173
[2bc13887]174 assert(fibril_mutex_is_locked(&vfs_data->lock));
[b75e929]175
176 if (file->refcnt-- == 1) {
177 /*
[25bef0ff]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.
[b75e929]180 */
[5126f80]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 }
[b75e929]188 free(file);
189 }
[25bef0ff]190
191 return rc;
[f29a3a2]192}
193
[c577a9a]194static int _vfs_fd_alloc(vfs_client_data_t *vfs_data, vfs_file_t **file, bool desc)
[320c884]195{
[2bc13887]196 if (!vfs_files_init(vfs_data))
[ac23b9d3]197 return ENOMEM;
198
199 unsigned int i;
[2b88074b]200 if (desc)
[42fa698]201 i = MAX_OPEN_FILES - 1;
[2b88074b]202 else
203 i = 0;
204
[2bc13887]205 fibril_mutex_lock(&vfs_data->lock);
[2b88074b]206 while (true) {
[2bc13887]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);
[320c884]211 return ENOMEM;
[b75e929]212 }
[ac23b9d3]213
[c577a9a]214
[2bc13887]215 memset(vfs_data->files[i], 0, sizeof(vfs_file_t));
[c577a9a]216
217 fibril_mutex_initialize(&vfs_data->files[i]->_lock);
218 fibril_mutex_lock(&vfs_data->files[i]->_lock);
[2bc13887]219 vfs_file_addref(vfs_data, vfs_data->files[i]);
[c577a9a]220
221 *file = vfs_data->files[i];
222 vfs_file_addref(vfs_data, *file);
223
[2bc13887]224 fibril_mutex_unlock(&vfs_data->lock);
[ac23b9d3]225 return (int) i;
[320c884]226 }
[2b88074b]227
228 if (desc) {
229 if (i == 0)
230 break;
231
232 i--;
233 } else {
[d8f92868]234 if (i == MAX_OPEN_FILES - 1)
[2b88074b]235 break;
236
237 i++;
238 }
[320c884]239 }
[2bc13887]240 fibril_mutex_unlock(&vfs_data->lock);
[ac23b9d3]241
[320c884]242 return EMFILE;
243}
244
[2bc13887]245/** Allocate a file descriptor.
[320c884]246 *
[c577a9a]247 * @param file Is set to point to the newly created file structure. Must be put afterwards.
[2bc13887]248 * @param desc If true, look for an available file descriptor
249 * in a descending order.
[b7f9087]250 *
[2bc13887]251 * @return First available file descriptor or a negative error
252 * code.
[320c884]253 */
[c577a9a]254int vfs_fd_alloc(vfs_file_t **file, bool desc)
[2bc13887]255{
[c577a9a]256 return _vfs_fd_alloc(VFS_DATA, file, desc);
[2bc13887]257}
258
259static int _vfs_fd_free(vfs_client_data_t *vfs_data, int fd)
[320c884]260{
[25bef0ff]261 int rc;
262
[2bc13887]263 if (!vfs_files_init(vfs_data))
[ac23b9d3]264 return ENOMEM;
[b75e929]265
[2bc13887]266 fibril_mutex_lock(&vfs_data->lock);
267 if ((fd < 0) || (fd >= MAX_OPEN_FILES) || !vfs_data->files[fd]) {
268 fibril_mutex_unlock(&vfs_data->lock);
[b7f9087]269 return EBADF;
[b75e929]270 }
[ac23b9d3]271
[2bc13887]272 rc = vfs_file_delref(vfs_data, vfs_data->files[fd]);
273 vfs_data->files[fd] = NULL;
274 fibril_mutex_unlock(&vfs_data->lock);
[ac23b9d3]275
[25bef0ff]276 return rc;
[320c884]277}
278
[2bc13887]279/** Release file descriptor.
280 *
281 * @param fd File descriptor being released.
282 *
283 * @return EOK on success or EBADF if fd is an invalid file
284 * descriptor.
285 */
286int vfs_fd_free(int fd)
287{
288 return _vfs_fd_free(VFS_DATA, fd);
289}
290
[2b88074b]291/** Assign a file to a file descriptor.
292 *
293 * @param file File to assign.
294 * @param fd File descriptor to assign to.
295 *
296 * @return EOK on success or EINVAL if fd is an invalid or already
297 * used file descriptor.
298 *
299 */
300int vfs_fd_assign(vfs_file_t *file, int fd)
301{
[2bc13887]302 if (!vfs_files_init(VFS_DATA))
[2b88074b]303 return ENOMEM;
[b75e929]304
305 fibril_mutex_lock(&VFS_DATA->lock);
[fcab7ef]306 if ((fd < 0) || (fd >= MAX_OPEN_FILES)) {
[b75e929]307 fibril_mutex_unlock(&VFS_DATA->lock);
[fcab7ef]308 return EBADF;
309 }
310 if (FILES[fd] != NULL) {
311 fibril_mutex_unlock(&VFS_DATA->lock);
312 return EEXIST;
[b75e929]313 }
[2b88074b]314
[b75e929]315 FILES[fd] = file;
[2bc13887]316 vfs_file_addref(VFS_DATA, FILES[fd]);
[b75e929]317 fibril_mutex_unlock(&VFS_DATA->lock);
[2b88074b]318
319 return EOK;
320}
321
[5126f80]322static void _vfs_file_put(vfs_client_data_t *vfs_data, vfs_file_t *file)
323{
324 fibril_mutex_unlock(&file->_lock);
325
326 fibril_mutex_lock(&vfs_data->lock);
327 vfs_file_delref(vfs_data, file);
328 fibril_mutex_unlock(&vfs_data->lock);
329}
330
[2bc13887]331static vfs_file_t *_vfs_file_get(vfs_client_data_t *vfs_data, int fd)
[320c884]332{
[2bc13887]333 if (!vfs_files_init(vfs_data))
[ac23b9d3]334 return NULL;
335
[2bc13887]336 fibril_mutex_lock(&vfs_data->lock);
[b75e929]337 if ((fd >= 0) && (fd < MAX_OPEN_FILES)) {
[2bc13887]338 vfs_file_t *file = vfs_data->files[fd];
[6639ae1]339 if (file != NULL) {
[2bc13887]340 vfs_file_addref(vfs_data, file);
341 fibril_mutex_unlock(&vfs_data->lock);
[5126f80]342
[c577a9a]343 fibril_mutex_lock(&file->_lock);
[5126f80]344 if (file->node == NULL) {
345 _vfs_file_put(vfs_data, file);
346 return NULL;
347 }
348 assert(file != NULL);
349 assert(file->node != NULL);
[6639ae1]350 return file;
351 }
[b75e929]352 }
[2bc13887]353 fibril_mutex_unlock(&vfs_data->lock);
[ac23b9d3]354
[ebd9392]355 return NULL;
[320c884]356}
357
[2bc13887]358/** Find VFS file structure for a given file descriptor.
359 *
360 * @param fd File descriptor.
361 *
362 * @return VFS file structure corresponding to fd.
363 */
364vfs_file_t *vfs_file_get(int fd)
365{
366 return _vfs_file_get(VFS_DATA, fd);
367}
368
[4fe94c66]369/** Stop using a file structure.
370 *
371 * @param file VFS file structure.
372 */
373void vfs_file_put(vfs_file_t *file)
374{
[2bc13887]375 _vfs_file_put(VFS_DATA, file);
376}
377
[354b642]378void vfs_op_pass_handle(task_id_t donor_id, task_id_t acceptor_id, int donor_fd)
[2bc13887]379{
380 vfs_client_data_t *donor_data = NULL;
381 vfs_client_data_t *acceptor_data = NULL;
382 vfs_file_t *donor_file = NULL;
[27b76ca]383 vfs_boxed_handle_t *bh;
[2bc13887]384
[e2ab36f1]385 acceptor_data = async_get_client_data_by_id(acceptor_id);
[2bc13887]386 if (!acceptor_data)
[27b76ca]387 return;
388
389 bh = malloc(sizeof(vfs_boxed_handle_t));
390 assert(bh);
391
392 link_initialize(&bh->link);
[bb9ec2d]393 bh->node = NULL;
[27b76ca]394
[e2ab36f1]395 donor_data = async_get_client_data_by_id(donor_id);
[27b76ca]396 if (!donor_data)
[2bc13887]397 goto out;
398
399 donor_file = _vfs_file_get(donor_data, donor_fd);
400 if (!donor_file)
401 goto out;
402
403 /*
404 * Add a new reference to the underlying VFS node.
405 */
406 vfs_node_addref(donor_file->node);
[bb9ec2d]407 bh->node = donor_file->node;
408 bh->permissions = donor_file->permissions;
[354b642]409
[2bc13887]410out:
[27b76ca]411 fibril_mutex_lock(&acceptor_data->lock);
412 list_append(&bh->link, &acceptor_data->passed_handles);
413 fibril_condvar_broadcast(&acceptor_data->cv);
414 fibril_mutex_unlock(&acceptor_data->lock);
415
[2bc13887]416 if (donor_data)
[e2ab36f1]417 async_put_client_data_by_id(donor_id);
[2bc13887]418 if (acceptor_data)
[e2ab36f1]419 async_put_client_data_by_id(acceptor_id);
[2bc13887]420 if (donor_file)
421 _vfs_file_put(donor_data, donor_file);
[27b76ca]422}
423
[bb9ec2d]424int vfs_wait_handle_internal(bool high_fd)
[27b76ca]425{
426 vfs_client_data_t *vfs_data = VFS_DATA;
427
428 fibril_mutex_lock(&vfs_data->lock);
429 while (list_empty(&vfs_data->passed_handles))
430 fibril_condvar_wait(&vfs_data->cv, &vfs_data->lock);
431 link_t *lnk = list_first(&vfs_data->passed_handles);
432 list_remove(lnk);
433 fibril_mutex_unlock(&vfs_data->lock);
434
435 vfs_boxed_handle_t *bh = list_get_instance(lnk, vfs_boxed_handle_t, link);
436
[bb9ec2d]437 vfs_file_t *file;
438 int fd = _vfs_fd_alloc(vfs_data, &file, high_fd);
439 if (fd < 0) {
440 vfs_node_delref(bh->node);
441 free(bh);
442 return fd;
443 }
444
445 file->node = bh->node;
446 file->permissions = bh->permissions;
447 vfs_file_put(file);
448 free(bh);
[27b76ca]449 return fd;
[4fe94c66]450}
451
[320c884]452/**
453 * @}
[ac23b9d3]454 */
Note: See TracBrowser for help on using the repository browser.