source: mainline/uspace/srv/fs/udf/udf_ops.c@ 70253688

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 70253688 was 70253688, checked in by Vojtech Horky <vojtechhorky@…>, 13 years ago

Merge mainline changes

  • Property mode set to 100644
File size: 12.1 KB
Line 
1/*
2 * Copyright (c) 2008 Jakub Jermar
3 * Copyright (c) 2012 Julia Medvedeva
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup fs
31 * @{
32 */
33/**
34 * @file udf_ops.c
35 * @brief Implementation of VFS operations for the UDF file system
36 * server.
37 */
38
39#include <libfs.h>
40#include <block.h>
41#include <ipc/services.h>
42#include <ipc/loc.h>
43#include <macros.h>
44#include <async.h>
45#include <errno.h>
46#include <str.h>
47#include <byteorder.h>
48#include <adt/hash_table.h>
49#include <adt/list.h>
50#include <assert.h>
51#include <fibril_synch.h>
52#include <sys/mman.h>
53#include <align.h>
54#include <malloc.h>
55#include <inttypes.h>
56#include <io/log.h>
57#include "../../vfs/vfs.h"
58#include "udf.h"
59#include "udf_cksum.h"
60#include "udf_volume.h"
61#include "udf_idx.h"
62#include "udf_file.h"
63#include "udf_osta.h"
64
65/** Mutex protecting the list of cached free nodes. */
66static FIBRIL_MUTEX_INITIALIZE(ffn_mutex);
67
68/** List of cached free nodes. */
69static LIST_INITIALIZE(ffn_list);
70
71static int udf_node_get(fs_node_t **rfn, service_id_t service_id,
72 fs_index_t index)
73{
74 udf_instance_t *instance;
75 int rc = fs_instance_get(service_id, (void **) &instance);
76 if (rc != EOK)
77 return rc;
78
79 udf_node_t *node;
80 rc = udf_idx_get(&node, instance, index);
81 if (rc != EOK) {
82 rc = udf_idx_add(&node, instance, index);
83 if (rc != EOK)
84 return rc;
85
86 rc = udf_node_get_core(node);
87 if (rc != EOK) {
88 udf_idx_del(node);
89 return rc;
90 }
91 }
92
93 *rfn = FS_NODE(node);
94 return EOK;
95}
96
97static int udf_root_get(fs_node_t **rfn, service_id_t service_id)
98{
99 udf_instance_t *instance;
100 int rc = fs_instance_get(service_id, (void **) &instance);
101 if (rc != EOK)
102 return rc;
103
104 return udf_node_get(rfn, service_id,
105 instance->volumes[DEFAULT_VOL].root_dir);
106}
107
108static service_id_t udf_service_get(fs_node_t *node)
109{
110 udf_node_t *udfn = UDF_NODE(node);
111 if (udfn)
112 return udfn->instance->service_id;
113
114 return 0;
115}
116
117static int udf_match(fs_node_t **rfn, fs_node_t *pfn, const char *component)
118{
119 char *name = malloc(MAX_FILE_NAME_LEN + 1);
120 if (name == NULL)
121 return ENOMEM;
122
123 block_t *block = NULL;
124 udf_file_identifier_descriptor_t *fid = NULL;
125 size_t pos = 0;
126
127 while (udf_get_fid(&fid, &block, UDF_NODE(pfn), pos) == EOK) {
128 udf_long_ad_t long_ad = fid->icb;
129
130 udf_to_unix_name(name, MAX_FILE_NAME_LEN,
131 (char *) fid->implementation_use + FLE16(fid->lenght_iu),
132 fid->lenght_file_id, &UDF_NODE(pfn)->instance->charset);
133
134 if (stricmp(name, component) == 0) {
135 int rc = udf_node_get(rfn, udf_service_get(pfn),
136 udf_long_ad_to_pos(UDF_NODE(pfn)->instance, &long_ad));
137
138 if (block != NULL)
139 block_put(block);
140
141 free(name);
142 return rc;
143 }
144
145 if (block != NULL) {
146 int rc = block_put(block);
147 if (rc != EOK)
148 return rc;
149 }
150
151 pos++;
152 }
153
154 free(name);
155 return ENOENT;
156}
157
158static int udf_node_open(fs_node_t *fn)
159{
160 return EOK;
161}
162
163static int udf_node_put(fs_node_t *fn)
164{
165 udf_node_t *node = UDF_NODE(fn);
166 if (!node)
167 return EINVAL;
168
169 fibril_mutex_lock(&node->lock);
170 node->ref_cnt--;
171 fibril_mutex_unlock(&node->lock);
172
173 /* Delete node from hash table and memory */
174 if (!node->ref_cnt)
175 udf_idx_del(node);
176
177 return EOK;
178}
179
180static int udf_create_node(fs_node_t **rfn, service_id_t service_id, int flags)
181{
182 return ENOTSUP;
183}
184
185static int udf_destroy_node(fs_node_t *fn)
186{
187 return ENOTSUP;
188}
189
190static int udf_link(fs_node_t *pfn, fs_node_t *cfn, const char *name)
191{
192 return ENOTSUP;
193}
194
195static int udf_unlink(fs_node_t *pfn, fs_node_t *cfn, const char *nm)
196{
197 return ENOTSUP;
198}
199
200static int udf_has_children(bool *has_children, fs_node_t *fn)
201{
202 *has_children = true;
203 return EOK;
204}
205
206static fs_index_t udf_index_get(fs_node_t *fn)
207{
208 udf_node_t *node = UDF_NODE(fn);
209 if (node)
210 return node->index;
211
212 return 0;
213}
214
215static aoff64_t udf_size_get(fs_node_t *fn)
216{
217 udf_node_t *node = UDF_NODE(fn);
218 if (node)
219 return node->data_size;
220
221 return 0;
222}
223
224static unsigned int udf_lnkcnt_get(fs_node_t *fn)
225{
226 udf_node_t *node = UDF_NODE(fn);
227 if (node)
228 return node->link_cnt;
229
230 return 0;
231}
232
233static bool udf_is_directory(fs_node_t *fn)
234{
235 udf_node_t *node = UDF_NODE(fn);
236 if (node)
237 return node->type == NODE_DIR;
238
239 return false;
240}
241
242static bool udf_is_file(fs_node_t *fn)
243{
244 udf_node_t *node = UDF_NODE(fn);
245 if (node)
246 return node->type == NODE_FILE;
247
248 return false;
249}
250
251libfs_ops_t udf_libfs_ops = {
252 .root_get = udf_root_get,
253 .match = udf_match,
254 .node_get = udf_node_get,
255 .node_open = udf_node_open,
256 .node_put = udf_node_put,
257 .create = udf_create_node,
258 .destroy = udf_destroy_node,
259 .link = udf_link,
260 .unlink = udf_unlink,
261 .has_children = udf_has_children,
262 .index_get = udf_index_get,
263 .size_get = udf_size_get,
264 .lnkcnt_get = udf_lnkcnt_get,
265 .is_directory = udf_is_directory,
266 .is_file = udf_is_file,
267 .service_get = udf_service_get
268};
269
270static int udf_mounted(service_id_t service_id, const char *opts,
271 fs_index_t *index, aoff64_t *size, unsigned *linkcnt)
272{
273 enum cache_mode cmode;
274
275 /* Check for option enabling write through. */
276 if (str_cmp(opts, "wtcache") == 0)
277 cmode = CACHE_MODE_WT;
278 else
279 cmode = CACHE_MODE_WB;
280
281 udf_instance_t *instance = malloc(sizeof(udf_instance_t));
282 if (!instance)
283 return ENOMEM;
284
285 instance->sector_size = 0;
286
287 /* Check for block size. Will be enhanced later */
288 if (str_cmp(opts, "bs=512") == 0)
289 instance->sector_size = 512;
290 else if (str_cmp(opts, "bs=1024") == 0)
291 instance->sector_size = 1024;
292 else if (str_cmp(opts, "bs=2048") == 0)
293 instance->sector_size = 2048;
294
295 /* initialize block cache */
296 int rc = block_init(EXCHANGE_SERIALIZE, service_id, MAX_SIZE);
297 if (rc != EOK)
298 return rc;
299
300 rc = fs_instance_create(service_id, instance);
301 if (rc != EOK) {
302 free(instance);
303 block_fini(service_id);
304 return rc;
305 }
306
307 instance->service_id = service_id;
308 instance->open_nodes_count = 0;
309
310 /* Check Volume Recognition Sequence */
311 rc = udf_volume_recongnition(service_id);
312 if (rc != EOK) {
313 log_msg(LOG_DEFAULT, LVL_NOTE, "VRS failed");
314 fs_instance_destroy(service_id);
315 free(instance);
316 block_fini(service_id);
317 return rc;
318 }
319
320 /* Search for Anchor Volume Descriptor */
321 udf_anchor_volume_descriptor_t avd;
322 rc = udf_get_anchor_volume_descriptor(service_id, &avd);
323 if (rc != EOK) {
324 log_msg(LOG_DEFAULT, LVL_NOTE, "Anchor read failed");
325 fs_instance_destroy(service_id);
326 free(instance);
327 block_fini(service_id);
328 return rc;
329 }
330
331 log_msg(LOG_DEFAULT, LVL_DEBUG,
332 "Volume: Anchor volume descriptor found. Sector size=%" PRIu32,
333 instance->sector_size);
334 log_msg(LOG_DEFAULT, LVL_DEBUG,
335 "Anchor: main sequence [length=%" PRIu32 " (bytes), start=%"
336 PRIu32 " (sector)]", avd.main_extent.length,
337 avd.main_extent.location);
338 log_msg(LOG_DEFAULT, LVL_DEBUG,
339 "Anchor: reserve sequence [length=%" PRIu32 " (bytes), start=%"
340 PRIu32 " (sector)]", avd.reserve_extent.length,
341 avd.reserve_extent.location);
342
343 /* Initialize the block cache */
344 rc = block_cache_init(service_id, instance->sector_size, 0, cmode);
345 if (rc != EOK) {
346 fs_instance_destroy(service_id);
347 free(instance);
348 block_fini(service_id);
349 return rc;
350 }
351
352 /* Read Volume Descriptor Sequence */
353 rc = udf_read_volume_descriptor_sequence(service_id, avd.main_extent);
354 if (rc != EOK) {
355 log_msg(LOG_DEFAULT, LVL_NOTE, "Volume Descriptor Sequence read failed");
356 fs_instance_destroy(service_id);
357 free(instance);
358 block_cache_fini(service_id);
359 block_fini(service_id);
360 return rc;
361 }
362
363 fs_node_t *rfn;
364 rc = udf_node_get(&rfn, service_id, instance->volumes[DEFAULT_VOL].root_dir);
365 if (rc != EOK) {
366 log_msg(LOG_DEFAULT, LVL_NOTE, "Can't create root node");
367 fs_instance_destroy(service_id);
368 free(instance);
369 block_cache_fini(service_id);
370 block_fini(service_id);
371 return rc;
372 }
373
374 udf_node_t *node = UDF_NODE(rfn);
375 *index = instance->volumes[DEFAULT_VOL].root_dir;
376 *size = node->data_size;
377 *linkcnt = node->link_cnt;
378
379 return EOK;
380}
381
382static int udf_unmounted(service_id_t service_id)
383{
384 fs_node_t *fn;
385 int rc = udf_root_get(&fn, service_id);
386 if (rc != EOK)
387 return rc;
388
389 udf_node_t *nodep = UDF_NODE(fn);
390 udf_instance_t *instance = nodep->instance;
391
392 /*
393 * We expect exactly two references on the root node.
394 * One for the udf_root_get() above and one created in
395 * udf_mounted().
396 */
397 if (nodep->ref_cnt != 2) {
398 udf_node_put(fn);
399 return EBUSY;
400 }
401
402 /*
403 * Put the root node twice.
404 */
405 udf_node_put(fn);
406 udf_node_put(fn);
407
408 fs_instance_destroy(service_id);
409 free(instance);
410 block_cache_fini(service_id);
411 block_fini(service_id);
412
413 return EOK;
414}
415
416static int udf_read(service_id_t service_id, fs_index_t index, aoff64_t pos,
417 size_t *rbytes)
418{
419 udf_instance_t *instance;
420 int rc = fs_instance_get(service_id, (void **) &instance);
421 if (rc != EOK)
422 return rc;
423
424 fs_node_t *rfn;
425 rc = udf_node_get(&rfn, service_id, index);
426 if (rc != EOK)
427 return rc;
428
429 udf_node_t *node = UDF_NODE(rfn);
430
431 ipc_callid_t callid;
432 size_t len = 0;
433 if (!async_data_read_receive(&callid, &len)) {
434 async_answer_0(callid, EINVAL);
435 udf_node_put(rfn);
436 return EINVAL;
437 }
438
439 if (node->type == NODE_FILE) {
440 if (pos >= node->data_size) {
441 *rbytes = 0;
442 async_data_read_finalize(callid, NULL, 0);
443 udf_node_put(rfn);
444 return EOK;
445 }
446
447 size_t read_len = 0;
448 if (node->data == NULL)
449 rc = udf_read_file(&read_len, callid, node, pos, len);
450 else {
451 /* File in allocation descriptors area */
452 read_len = (len < node->data_size) ? len : node->data_size;
453 async_data_read_finalize(callid, node->data + pos, read_len);
454 rc = EOK;
455 }
456
457 *rbytes = read_len;
458 (void) udf_node_put(rfn);
459 return rc;
460 } else {
461 block_t *block = NULL;
462 udf_file_identifier_descriptor_t *fid = NULL;
463 if (udf_get_fid(&fid, &block, node, pos) == EOK) {
464 char *name = malloc(MAX_FILE_NAME_LEN + 1);
465
466 // FIXME: Check for NULL return value
467
468 udf_to_unix_name(name, MAX_FILE_NAME_LEN,
469 (char *) fid->implementation_use + FLE16(fid->lenght_iu),
470 fid->lenght_file_id, &node->instance->charset);
471
472 async_data_read_finalize(callid, name, str_size(name) + 1);
473 *rbytes = 1;
474 free(name);
475 udf_node_put(rfn);
476
477 if (block != NULL)
478 return block_put(block);
479
480 return EOK;
481 } else {
482 *rbytes = 0;
483 udf_node_put(rfn);
484 async_answer_0(callid, ENOENT);
485 return ENOENT;
486 }
487 }
488}
489
490static int udf_close(service_id_t service_id, fs_index_t index)
491{
492 return EOK;
493}
494
495static int udf_sync(service_id_t service_id, fs_index_t index)
496{
497 return ENOTSUP;
498}
499
500static int udf_write(service_id_t service_id, fs_index_t index, aoff64_t pos,
501 size_t *wbytes, aoff64_t *nsize)
502{
503 return ENOTSUP;
504}
505
506static int udf_truncate(service_id_t service_id, fs_index_t index,
507 aoff64_t size)
508{
509 return ENOTSUP;
510}
511
512static int udf_destroy(service_id_t service_id, fs_index_t index)
513{
514 return ENOTSUP;
515}
516
517vfs_out_ops_t udf_ops = {
518 .mounted = udf_mounted,
519 .unmounted = udf_unmounted,
520 .read = udf_read,
521 .write = udf_write,
522 .truncate = udf_truncate,
523 .close = udf_close,
524 .destroy = udf_destroy,
525 .sync = udf_sync
526};
527
528/**
529 * @}
530 */
Note: See TracBrowser for help on using the repository browser.