VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_core.c@ 93115

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.4 KB
Line 
1/* $Id: fsw_core.c 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * fsw_core.c - Core file system wrapper abstraction layer code.
4 */
5
6/*
7 * Copyright (C) 2010-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 * ---------------------------------------------------------------------------
26 * This code is based on:
27 *
28 * Copyright (c) 2006 Christoph Pfisterer
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions are
32 * met:
33 *
34 * * Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 *
37 * * Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the
40 * distribution.
41 *
42 * * Neither the name of Christoph Pfisterer nor the names of the
43 * contributors may be used to endorse or promote products derived
44 * from this software without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
47 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
48 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
49 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
50 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
51 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
52 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
56 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57 */
58
59#include "fsw_core.h"
60
61
62// functions
63
64static void fsw_blockcache_free(struct fsw_volume *vol);
65
66#define MAX_CACHE_LEVEL (5)
67
68
69/**
70 * Mount a volume with a given file system driver. This function is called by the
71 * host driver to make a volume accessible. The file system driver to use is specified
72 * by a pointer to its dispatch table. The file system driver will look at the
73 * data on the volume to determine if it can read the format. If the volume is found
74 * unsuitable, FSW_UNSUPPORTED is returned.
75 *
76 * If this function returns FSW_SUCCESS, *vol_out points at a valid volume data
77 * structure. The caller must release it later by calling fsw_unmount.
78 *
79 * If this function returns an error status, the caller only needs to clean up its
80 * own buffers that may have been allocated through the read_block interface.
81 */
82
83fsw_status_t fsw_mount(void *host_data,
84 struct fsw_host_table *host_table,
85 struct fsw_fstype_table *fstype_table,
86 struct fsw_volume **vol_out)
87{
88 fsw_status_t status;
89 struct fsw_volume *vol;
90
91 // allocate memory for the structure
92 status = fsw_alloc_zero(fstype_table->volume_struct_size, (void **)&vol);
93 if (status)
94 return status;
95
96 // initialize fields
97 vol->phys_blocksize = 512;
98 vol->log_blocksize = 512;
99 vol->label.type = FSW_STRING_TYPE_EMPTY;
100 vol->host_data = host_data;
101 vol->host_table = host_table;
102 vol->fstype_table = fstype_table;
103 vol->host_string_type = host_table->native_string_type;
104
105 // let the fs driver mount the file system
106 status = vol->fstype_table->volume_mount(vol);
107 if (status)
108 goto errorexit;
109
110 /// @todo anything else?
111
112 *vol_out = vol;
113 return FSW_SUCCESS;
114
115errorexit:
116 fsw_unmount(vol);
117 return status;
118}
119
120/**
121 * Unmount a volume by releasing all memory associated with it. This function is
122 * called by the host driver when a volume is no longer needed. It is also called
123 * by the core after a failed mount to clean up any allocated memory.
124 *
125 * Note that all dnodes must have been released before calling this function.
126 */
127
128void fsw_unmount(struct fsw_volume *vol)
129{
130 if (vol->root)
131 fsw_dnode_release(vol->root);
132 /// @todo check that no other dnodes are still around
133
134 vol->fstype_table->volume_free(vol);
135
136 fsw_blockcache_free(vol);
137 fsw_strfree(&vol->label);
138 fsw_free(vol);
139}
140
141/**
142 * Get in-depth information on the volume. This function can be called by the host
143 * driver to get additional information on the volume.
144 */
145
146fsw_status_t fsw_volume_stat(struct fsw_volume *vol, struct fsw_volume_stat *sb)
147{
148 return vol->fstype_table->volume_stat(vol, sb);
149}
150
151/**
152 * Set the physical and logical block sizes of the volume. This functions is called by
153 * the file system driver to announce the block sizes it wants to use for accessing
154 * the disk (physical) and for addressing file contents (logical).
155 * Usually both sizes will be the same but there may be file systems that need to access
156 * metadata at a smaller block size than the allocation unit for files.
157 *
158 * Calling this function causes the block cache to be dropped. All pointers returned
159 * from fsw_block_get become invalid. This function should only be called while
160 * mounting the file system, not as a part of file access operations.
161 *
162 * Both sizes are measured in bytes, must be powers of 2, and must not be smaller
163 * than 512 bytes. The logical block size cannot be smaller than the physical block size.
164 */
165
166void fsw_set_blocksize(struct fsw_volume *vol, fsw_u32 phys_blocksize, fsw_u32 log_blocksize)
167{
168 /// @todo Check the sizes. Both must be powers of 2. log_blocksize must not be smaller than
169 // phys_blocksize.
170
171 // drop core block cache if present
172 fsw_blockcache_free(vol);
173
174 // signal host driver to drop caches etc.
175 vol->host_table->change_blocksize(vol,
176 vol->phys_blocksize, vol->log_blocksize,
177 phys_blocksize, log_blocksize);
178
179 vol->phys_blocksize = phys_blocksize;
180 vol->log_blocksize = log_blocksize;
181}
182
183/**
184 * Get a block of data from the disk. This function is called by the file system driver
185 * or by core functions. It calls through to the host driver's device access routine.
186 * Given a physical block number, it reads the block into memory (or fetches it from the
187 * block cache) and returns the address of the memory buffer. The caller should provide
188 * an indication of how important the block is in the cache_level parameter. Blocks with
189 * a low level are purged first. Some suggestions for cache levels:
190 *
191 * - 0: File data
192 * - 1: Directory data, symlink data
193 * - 2: File system metadata
194 * - 3..5: File system metadata with a high rate of access
195 *
196 * If this function returns successfully, the returned data pointer is valid until the
197 * caller calls fsw_block_release.
198 */
199
200fsw_status_t fsw_block_get(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, fsw_u32 cache_level, void **buffer_out)
201{
202 fsw_status_t status;
203 fsw_u32 i, discard_level, new_bcache_size;
204 struct fsw_blockcache *new_bcache;
205
206 /// @todo allow the host driver to do its own caching; just call through if
207 // the appropriate function pointers are set
208
209 if (cache_level > MAX_CACHE_LEVEL)
210 cache_level = MAX_CACHE_LEVEL;
211
212 // check block cache
213 for (i = 0; i < vol->bcache_size; i++) {
214 if (vol->bcache[i].phys_bno == phys_bno) {
215 // cache hit!
216 if (vol->bcache[i].cache_level < cache_level)
217 vol->bcache[i].cache_level = cache_level; // promote the entry
218 vol->bcache[i].refcount++;
219 *buffer_out = vol->bcache[i].data;
220 return FSW_SUCCESS;
221 }
222 }
223
224 // find a free entry in the cache table
225 for (i = 0; i < vol->bcache_size; i++) {
226 if (vol->bcache[i].phys_bno == FSW_INVALID_BNO)
227 break;
228 }
229 if (i >= vol->bcache_size) {
230 for (discard_level = 0; discard_level <= MAX_CACHE_LEVEL; discard_level++) {
231 for (i = 0; i < vol->bcache_size; i++) {
232 if (vol->bcache[i].refcount == 0 && vol->bcache[i].cache_level <= discard_level)
233 break;
234 }
235 if (i < vol->bcache_size)
236 break;
237 }
238 }
239 if (i >= vol->bcache_size) {
240 // enlarge / create the cache
241 if (vol->bcache_size < 16)
242 new_bcache_size = 16;
243 else
244 new_bcache_size = vol->bcache_size << 1;
245 status = fsw_alloc(new_bcache_size * sizeof(struct fsw_blockcache), &new_bcache);
246 if (status)
247 return status;
248 if (vol->bcache_size > 0)
249 fsw_memcpy(new_bcache, vol->bcache, vol->bcache_size * sizeof(struct fsw_blockcache));
250 for (i = vol->bcache_size; i < new_bcache_size; i++) {
251 new_bcache[i].refcount = 0;
252 new_bcache[i].cache_level = 0;
253 new_bcache[i].phys_bno = FSW_INVALID_BNO;
254 new_bcache[i].data = NULL;
255 }
256 i = vol->bcache_size;
257
258 // switch caches
259 if (vol->bcache != NULL)
260 fsw_free(vol->bcache);
261 vol->bcache = new_bcache;
262 vol->bcache_size = new_bcache_size;
263 }
264 vol->bcache[i].phys_bno = FSW_INVALID_BNO;
265
266 // read the data
267 if (vol->bcache[i].data == NULL) {
268 status = fsw_alloc(vol->phys_blocksize, &vol->bcache[i].data);
269 if (status)
270 return status;
271 }
272 status = vol->host_table->read_block(vol, phys_bno, vol->bcache[i].data);
273 if (status)
274 return status;
275
276 vol->bcache[i].phys_bno = phys_bno;
277 vol->bcache[i].cache_level = cache_level;
278 vol->bcache[i].refcount = 1;
279 *buffer_out = vol->bcache[i].data;
280 return FSW_SUCCESS;
281}
282
283/**
284 * Releases a disk block. This function must be called to release disk blocks returned
285 * from fsw_block_get.
286 */
287
288void fsw_block_release(struct VOLSTRUCTNAME *vol, fsw_u32 phys_bno, void *buffer)
289{
290 fsw_u32 i;
291
292 /// @todo allow the host driver to do its own caching; just call through if
293 // the appropriate function pointers are set
294
295 // update block cache
296 for (i = 0; i < vol->bcache_size; i++) {
297 if (vol->bcache[i].phys_bno == phys_bno && vol->bcache[i].refcount > 0)
298 vol->bcache[i].refcount--;
299 }
300}
301
302/**
303 * Release the block cache. Called internally when changing block sizes and when
304 * unmounting the volume. It frees all data occupied by the generic block cache.
305 */
306
307static void fsw_blockcache_free(struct fsw_volume *vol)
308{
309 fsw_u32 i;
310
311 for (i = 0; i < vol->bcache_size; i++) {
312 if (vol->bcache[i].data != NULL)
313 fsw_free(vol->bcache[i].data);
314 }
315 if (vol->bcache != NULL) {
316 fsw_free(vol->bcache);
317 vol->bcache = NULL;
318 }
319 vol->bcache_size = 0;
320}
321
322/**
323 * Add a new dnode to the list of known dnodes. This internal function is used when a
324 * dnode is created to add it to the dnode list that is used to search for existing
325 * dnodes by id.
326 */
327
328static void fsw_dnode_register(struct fsw_volume *vol, struct fsw_dnode *dno)
329{
330 dno->next = vol->dnode_head;
331 if (vol->dnode_head != NULL)
332 vol->dnode_head->prev = dno;
333 dno->prev = NULL;
334 vol->dnode_head = dno;
335}
336
337/**
338 * Create a dnode representing the root directory. This function is called by the file system
339 * driver while mounting the file system. The root directory is special because it has no parent
340 * dnode, its name is defined to be empty, and its type is also fixed. Otherwise, this functions
341 * behaves in the same way as fsw_dnode_create.
342 */
343
344fsw_status_t fsw_dnode_create_root(struct fsw_volume *vol, fsw_u32 dnode_id, struct fsw_dnode **dno_out)
345{
346 fsw_status_t status;
347 struct fsw_dnode *dno;
348
349 // allocate memory for the structure
350 status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno);
351 if (status)
352 return status;
353
354 // fill the structure
355 dno->vol = vol;
356 dno->parent = NULL;
357 dno->dnode_id = dnode_id;
358 dno->type = FSW_DNODE_TYPE_DIR;
359 dno->refcount = 1;
360 dno->name.type = FSW_STRING_TYPE_EMPTY;
361 /// @todo instead, call a function to create an empty string in the native string type
362
363 fsw_dnode_register(vol, dno);
364
365 *dno_out = dno;
366 return FSW_SUCCESS;
367}
368
369/**
370 * Create a new dnode representing a file system object. This function is called by
371 * the file system driver in response to directory lookup or read requests. Note that
372 * if there already is a dnode with the given dnode_id on record, then no new object
373 * is created. Instead, the existing dnode is returned and its reference count
374 * increased. All other parameters are ignored in this case.
375 *
376 * The type passed into this function may be FSW_DNODE_TYPE_UNKNOWN. It is sufficient
377 * to fill the type field during the dnode_fill call.
378 *
379 * The name parameter must describe a string with the object's name. A copy will be
380 * stored in the dnode structure for future reference. The name will not be used to
381 * shortcut directory lookups, but may be used to reconstruct paths.
382 *
383 * If the function returns successfully, *dno_out contains a pointer to the dnode
384 * that must be released by the caller with fsw_dnode_release.
385 */
386
387fsw_status_t fsw_dnode_create(struct fsw_dnode *parent_dno, fsw_u32 dnode_id, int type,
388 struct fsw_string *name, struct fsw_dnode **dno_out)
389{
390 fsw_status_t status;
391 struct fsw_volume *vol = parent_dno->vol;
392 struct fsw_dnode *dno;
393
394 // check if we already have a dnode with the same id
395 for (dno = vol->dnode_head; dno; dno = dno->next) {
396 if (dno->dnode_id == dnode_id) {
397 fsw_dnode_retain(dno);
398 *dno_out = dno;
399 return FSW_SUCCESS;
400 }
401 }
402
403 // allocate memory for the structure
404 status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno);
405 if (status)
406 return status;
407
408 // fill the structure
409 dno->vol = vol;
410 dno->parent = parent_dno;
411 fsw_dnode_retain(dno->parent);
412 dno->dnode_id = dnode_id;
413 dno->type = type;
414 dno->refcount = 1;
415 status = fsw_strdup_coerce(&dno->name, vol->host_table->native_string_type, name);
416 if (status) {
417 fsw_free(dno);
418 return status;
419 }
420
421 fsw_dnode_register(vol, dno);
422
423 *dno_out = dno;
424 return FSW_SUCCESS;
425}
426
427/**
428 * Increases the reference count of a dnode. This must be balanced with
429 * fsw_dnode_release calls. Note that some dnode functions return a retained
430 * dnode pointer to their caller.
431 */
432
433void fsw_dnode_retain(struct fsw_dnode *dno)
434{
435 dno->refcount++;
436}
437
438/**
439 * Release a dnode pointer, deallocating it if this was the last reference.
440 * This function decrements the reference counter of the dnode. If the counter
441 * reaches zero, the dnode is freed. Since the parent dnode is released
442 * during that process, this function may cause it to be freed, too.
443 */
444
445void fsw_dnode_release(struct fsw_dnode *dno)
446{
447 struct fsw_volume *vol = dno->vol;
448 struct fsw_dnode *parent_dno;
449
450 dno->refcount--;
451
452 if (dno->refcount == 0) {
453 parent_dno = dno->parent;
454
455 // de-register from volume's list
456 if (dno->next)
457 dno->next->prev = dno->prev;
458 if (dno->prev)
459 dno->prev->next = dno->next;
460 if (vol->dnode_head == dno)
461 vol->dnode_head = dno->next;
462
463 // run fstype-specific cleanup
464 vol->fstype_table->dnode_free(vol, dno);
465
466 fsw_strfree(&dno->name);
467 fsw_free(dno);
468
469 // release our pointer to the parent, possibly deallocating it, too
470 if (parent_dno)
471 fsw_dnode_release(parent_dno);
472 }
473}
474
475/**
476 * Get full information about a dnode from disk. This function is called by the host
477 * driver as well as by the core functions. Some file systems defer reading full
478 * information on a dnode until it is actually needed (i.e. separation between
479 * directory and inode information). This function makes sure that all information
480 * is available in the dnode structure. The following fields may not have a correct
481 * value until fsw_dnode_fill has been called:
482 *
483 * type, size
484 */
485
486fsw_status_t fsw_dnode_fill(struct fsw_dnode *dno)
487{
488 /// @todo check a flag right here, call fstype's dnode_fill only once per dnode
489
490 return dno->vol->fstype_table->dnode_fill(dno->vol, dno);
491}
492
493/**
494 * Get extended information about a dnode. This function can be called by the host
495 * driver to get a full compliment of information about a dnode in addition to the
496 * fields of the fsw_dnode structure itself.
497 *
498 * Some data requires host-specific conversion to be useful (i.e. timestamps) and
499 * will be passed to callback functions instead of being written into the structure.
500 * These callbacks must be filled in by the caller.
501 */
502
503fsw_status_t fsw_dnode_stat(struct fsw_dnode *dno, struct fsw_dnode_stat *sb)
504{
505 fsw_status_t status;
506
507 status = fsw_dnode_fill(dno);
508 if (status)
509 return status;
510
511 sb->used_bytes = 0;
512 status = dno->vol->fstype_table->dnode_stat(dno->vol, dno, sb);
513 if (!status && !sb->used_bytes)
514 sb->used_bytes = FSW_U64_DIV(dno->size + dno->vol->log_blocksize - 1, dno->vol->log_blocksize);
515 return status;
516}
517
518/**
519 * Lookup a directory entry by name. This function is called by the host driver.
520 * Given a directory dnode and a file name, it looks up the named entry in the
521 * directory.
522 *
523 * If the dnode is not a directory, the call will fail. The caller is responsible for
524 * resolving symbolic links before calling this function.
525 *
526 * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory
527 * entry. The caller must call fsw_dnode_release on it.
528 */
529
530fsw_status_t fsw_dnode_lookup(struct fsw_dnode *dno,
531 struct fsw_string *lookup_name, struct fsw_dnode **child_dno_out)
532{
533 fsw_status_t status;
534
535 status = fsw_dnode_fill(dno);
536 if (status)
537 return status;
538 if (dno->type != FSW_DNODE_TYPE_DIR)
539 return FSW_UNSUPPORTED;
540
541 return dno->vol->fstype_table->dir_lookup(dno->vol, dno, lookup_name, child_dno_out);
542}
543
544/**
545 * Find a file system object by path. This function is called by the host driver.
546 * Given a directory dnode and a relative or absolute path, it walks the directory
547 * tree until it finds the target dnode. If an intermediate node turns out to be
548 * a symlink, it is resolved automatically. If the target node is a symlink, it
549 * is not resolved.
550 *
551 * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory
552 * entry. The caller must call fsw_dnode_release on it.
553 */
554
555fsw_status_t fsw_dnode_lookup_path(struct fsw_dnode *dno,
556 struct fsw_string *lookup_path, char separator,
557 struct fsw_dnode **child_dno_out)
558{
559 fsw_status_t status;
560 struct fsw_volume *vol = dno->vol;
561 struct fsw_dnode *child_dno = NULL;
562 struct fsw_string lookup_name;
563 struct fsw_string remaining_path;
564 int root_if_empty;
565
566 remaining_path = *lookup_path;
567 fsw_dnode_retain(dno);
568
569 // loop over the path
570 for (root_if_empty = 1; fsw_strlen(&remaining_path) > 0; root_if_empty = 0) {
571 // parse next path component
572 fsw_strsplit(&lookup_name, &remaining_path, separator);
573
574 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: split into %d '%s' and %d '%s'\n"),
575 lookup_name.len, lookup_name.data,
576 remaining_path.len, remaining_path.data));
577
578 if (fsw_strlen(&lookup_name) == 0) { // empty path component
579 if (root_if_empty)
580 child_dno = vol->root;
581 else
582 child_dno = dno;
583 fsw_dnode_retain(child_dno);
584
585 } else {
586 // do an actual directory lookup
587
588 // ensure we have full information
589 status = fsw_dnode_fill(dno);
590 if (status)
591 goto errorexit;
592
593 // resolve symlink if necessary
594 if (dno->type == FSW_DNODE_TYPE_SYMLINK) {
595 status = fsw_dnode_resolve(dno, &child_dno);
596 if (status)
597 goto errorexit;
598
599 // symlink target becomes the new dno
600 fsw_dnode_release(dno);
601 dno = child_dno; // is already retained
602 child_dno = NULL;
603
604 // ensure we have full information
605 status = fsw_dnode_fill(dno);
606 if (status)
607 goto errorexit;
608 }
609
610 // make sure we operate on a directory
611 if (dno->type != FSW_DNODE_TYPE_DIR) {
612 return FSW_UNSUPPORTED;
613 goto errorexit;
614 }
615
616 // check special paths
617 if (fsw_streq_cstr(&lookup_name, ".")) { // self directory
618 child_dno = dno;
619 fsw_dnode_retain(child_dno);
620
621 } else if (fsw_streq_cstr(&lookup_name, "..")) { // parent directory
622 if (dno->parent == NULL) {
623 // We cannot go up from the root directory. Caution: Certain apps like the EFI shell
624 // rely on this behaviour!
625 status = FSW_NOT_FOUND;
626 goto errorexit;
627 }
628 child_dno = dno->parent;
629 fsw_dnode_retain(child_dno);
630
631 } else {
632 // do an actual lookup
633 status = vol->fstype_table->dir_lookup(vol, dno, &lookup_name, &child_dno);
634 if (status)
635 goto errorexit;
636 }
637 }
638
639 // child_dno becomes the new dno
640 fsw_dnode_release(dno);
641 dno = child_dno; // is already retained
642 child_dno = NULL;
643
644 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: now at inode %d\n"), dno->dnode_id));
645 }
646
647 *child_dno_out = dno;
648 return FSW_SUCCESS;
649
650errorexit:
651 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: leaving with error %d\n"), status));
652 fsw_dnode_release(dno);
653 if (child_dno != NULL)
654 fsw_dnode_release(child_dno);
655 return status;
656}
657
658/**
659 * Get the next directory item in sequential order. This function is called by the
660 * host driver to read the complete contents of a directory in sequential (file system
661 * defined) order. Calling this function returns the next entry. Iteration state is
662 * kept by a shandle on the directory's dnode. The caller must set up the shandle
663 * when starting the iteration.
664 *
665 * When the end of the directory is reached, this function returns FSW_NOT_FOUND.
666 * If the function returns FSW_SUCCESS, *child_dno_out points to the next directory
667 * entry. The caller must call fsw_dnode_release on it.
668 */
669
670fsw_status_t fsw_dnode_dir_read(struct fsw_shandle *shand, struct fsw_dnode **child_dno_out)
671{
672 fsw_status_t status;
673 struct fsw_dnode *dno = shand->dnode;
674 fsw_u64 saved_pos;
675
676 if (dno->type != FSW_DNODE_TYPE_DIR)
677 return FSW_UNSUPPORTED;
678
679 saved_pos = shand->pos;
680 status = dno->vol->fstype_table->dir_read(dno->vol, dno, shand, child_dno_out);
681 if (status)
682 shand->pos = saved_pos;
683 return status;
684}
685
686/**
687 * Read the target path of a symbolic link. This function is called by the host driver
688 * to read the "content" of a symbolic link, that is the relative or absolute path
689 * it points to.
690 *
691 * If the function returns FSW_SUCCESS, the string handle provided by the caller is
692 * filled with a string in the host's preferred encoding. The caller is responsible
693 * for calling fsw_strfree on the string.
694 */
695
696fsw_status_t fsw_dnode_readlink(struct fsw_dnode *dno, struct fsw_string *target_name)
697{
698 fsw_status_t status;
699
700 status = fsw_dnode_fill(dno);
701 if (status)
702 return status;
703 if (dno->type != FSW_DNODE_TYPE_SYMLINK)
704 return FSW_UNSUPPORTED;
705
706 return dno->vol->fstype_table->readlink(dno->vol, dno, target_name);
707}
708
709/**
710 * Read the target path of a symbolic link by accessing file data. This function can
711 * be called by the file system driver if the file system stores the target path
712 * as normal file data. This function will open an shandle, read the whole content
713 * of the file into a buffer, and build a string from that. Currently the encoding
714 * for the string is fixed as FSW_STRING_TYPE_ISO88591.
715 *
716 * If the function returns FSW_SUCCESS, the string handle provided by the caller is
717 * filled with a string in the host's preferred encoding. The caller is responsible
718 * for calling fsw_strfree on the string.
719 */
720
721fsw_status_t fsw_dnode_readlink_data(struct fsw_dnode *dno, struct fsw_string *link_target)
722{
723 fsw_status_t status;
724 struct fsw_shandle shand;
725 fsw_u32 buffer_size;
726 char buffer[FSW_PATH_MAX];
727
728 struct fsw_string s;
729
730 if (dno->size > FSW_PATH_MAX)
731 return FSW_VOLUME_CORRUPTED;
732
733 s.type = FSW_STRING_TYPE_ISO88591;
734 s.size = s.len = (int)dno->size;
735 s.data = buffer;
736
737 // open shandle and read the data
738 status = fsw_shandle_open(dno, &shand);
739 if (status)
740 return status;
741 buffer_size = (fsw_u32)s.size;
742 status = fsw_shandle_read(&shand, &buffer_size, buffer);
743 fsw_shandle_close(&shand);
744 if (status)
745 return status;
746 if ((int)buffer_size < s.size)
747 return FSW_VOLUME_CORRUPTED;
748
749 status = fsw_strdup_coerce(link_target, dno->vol->host_string_type, &s);
750 return status;
751}
752
753/**
754 * Resolve a symbolic link. This function can be called by the host driver to make
755 * sure the a dnode is fully resolved instead of pointing at a symlink. If the dnode
756 * passed in is not a symlink, it is returned unmodified.
757 *
758 * Note that absolute paths will be resolved relative to the root directory of the
759 * volume. If the host is an operating system with its own VFS layer, it should
760 * resolve symlinks on its own.
761 *
762 * If the function returns FSW_SUCCESS, *target_dno_out points at a dnode that is
763 * not a symlink. The caller is responsible for calling fsw_dnode_release on it.
764 */
765
766fsw_status_t fsw_dnode_resolve(struct fsw_dnode *dno, struct fsw_dnode **target_dno_out)
767{
768 fsw_status_t status;
769 struct fsw_string target_name;
770 struct fsw_dnode *target_dno;
771
772 fsw_dnode_retain(dno);
773
774 while (1) {
775 // get full information
776 status = fsw_dnode_fill(dno);
777 if (status)
778 goto errorexit;
779 if (dno->type != FSW_DNODE_TYPE_SYMLINK) {
780 // found a non-symlink target, return it
781 *target_dno_out = dno;
782 return FSW_SUCCESS;
783 }
784 if (dno->parent == NULL) { // safety measure, cannot happen in theory
785 status = FSW_NOT_FOUND;
786 goto errorexit;
787 }
788
789 // read the link's target
790 status = fsw_dnode_readlink(dno, &target_name);
791 if (status)
792 goto errorexit;
793
794 // resolve it
795 status = fsw_dnode_lookup_path(dno->parent, &target_name, '/', &target_dno);
796 fsw_strfree(&target_name);
797 if (status)
798 goto errorexit;
799
800 // target_dno becomes the new dno
801 fsw_dnode_release(dno);
802 dno = target_dno; // is already retained
803 }
804
805errorexit:
806 fsw_dnode_release(dno);
807 return status;
808}
809
810/**
811 * Set up a shandle (storage handle) to access a file's data. This function is called
812 * by the host driver and by the core when they need to access a file's data. It is also
813 * used in accessing the raw data of directories and symlinks if the file system uses
814 * the same mechanisms for storing the data of those items.
815 *
816 * The storage for the fsw_shandle structure is provided by the caller. The dnode and pos
817 * fields may be accessed, pos may also be written to to set the file pointer. The file's
818 * data size is available as shand->dnode->size.
819 *
820 * If this function returns FSW_SUCCESS, the caller must call fsw_shandle_close to release
821 * the dnode reference held by the shandle.
822 */
823
824fsw_status_t fsw_shandle_open(struct fsw_dnode *dno, struct fsw_shandle *shand)
825{
826 fsw_status_t status;
827 struct fsw_volume *vol = dno->vol;
828
829 // read full dnode information into memory
830 status = vol->fstype_table->dnode_fill(vol, dno);
831 if (status)
832 return status;
833
834 // setup shandle
835 fsw_dnode_retain(dno);
836
837 shand->dnode = dno;
838 shand->pos = 0;
839 shand->extent.type = FSW_EXTENT_TYPE_INVALID;
840
841 return FSW_SUCCESS;
842}
843
844/**
845 * Close a shandle after accessing the dnode's data. This function is called by the host
846 * driver or core functions when they are finished with accessing a file's data. It
847 * releases the dnode reference and frees any buffers associated with the shandle itself.
848 * The dnode is only released if this was the last reference using it.
849 */
850
851void fsw_shandle_close(struct fsw_shandle *shand)
852{
853 if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER)
854 fsw_free(shand->extent.buffer);
855 fsw_dnode_release(shand->dnode);
856}
857
858/**
859 * Read data from a shandle (storage handle for a dnode). This function is called by the
860 * host driver or internally when data is read from a file. TODO: more
861 */
862
863fsw_status_t fsw_shandle_read(struct fsw_shandle *shand, fsw_u32 *buffer_size_inout, void *buffer_in)
864{
865 fsw_status_t status;
866 struct fsw_dnode *dno = shand->dnode;
867 struct fsw_volume *vol = dno->vol;
868 fsw_u8 *buffer, *block_buffer;
869 fsw_u32 buflen, copylen, pos;
870 fsw_u32 log_bno, pos_in_extent, phys_bno, pos_in_physblock;
871 fsw_u32 cache_level;
872
873 if (shand->pos >= dno->size) { // already at EOF
874 *buffer_size_inout = 0;
875 return FSW_SUCCESS;
876 }
877
878 // initialize vars
879 buffer = buffer_in;
880 buflen = *buffer_size_inout;
881 pos = (fsw_u32)shand->pos;
882 cache_level = (dno->type != FSW_DNODE_TYPE_FILE) ? 1 : 0;
883 // restrict read to file size
884 if (buflen > dno->size - pos)
885 buflen = (fsw_u32)(dno->size - pos);
886
887 while (buflen > 0) {
888 // get extent for the current logical block
889 log_bno = pos / vol->log_blocksize;
890 if (shand->extent.type == FSW_EXTENT_TYPE_INVALID ||
891 log_bno < shand->extent.log_start ||
892 log_bno >= shand->extent.log_start + shand->extent.log_count) {
893
894 if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER)
895 fsw_free(shand->extent.buffer);
896
897 // ask the file system for the proper extent
898 shand->extent.log_start = log_bno;
899 status = vol->fstype_table->get_extent(vol, dno, &shand->extent);
900 if (status) {
901 shand->extent.type = FSW_EXTENT_TYPE_INVALID;
902 return status;
903 }
904 }
905
906 pos_in_extent = pos - shand->extent.log_start * vol->log_blocksize;
907
908 // dispatch by extent type
909 if (shand->extent.type == FSW_EXTENT_TYPE_PHYSBLOCK) {
910 // convert to physical block number and offset
911 phys_bno = shand->extent.phys_start + pos_in_extent / vol->phys_blocksize;
912 pos_in_physblock = pos_in_extent & (vol->phys_blocksize - 1);
913 copylen = vol->phys_blocksize - pos_in_physblock;
914 if (copylen > buflen)
915 copylen = buflen;
916
917 // get one physical block
918 status = fsw_block_get(vol, phys_bno, cache_level, (void **)&block_buffer);
919 if (status)
920 return status;
921
922 // copy data from it
923 fsw_memcpy(buffer, block_buffer + pos_in_physblock, copylen);
924 fsw_block_release(vol, phys_bno, block_buffer);
925
926 } else if (shand->extent.type == FSW_EXTENT_TYPE_BUFFER) {
927 copylen = shand->extent.log_count * vol->log_blocksize - pos_in_extent;
928 if (copylen > buflen)
929 copylen = buflen;
930 fsw_memcpy(buffer, (fsw_u8 *)shand->extent.buffer + pos_in_extent, copylen);
931
932 } else { // _SPARSE or _INVALID
933 copylen = shand->extent.log_count * vol->log_blocksize - pos_in_extent;
934 if (copylen > buflen)
935 copylen = buflen;
936 fsw_memzero(buffer, copylen);
937
938 }
939
940 buffer += copylen;
941 buflen -= copylen;
942 pos += copylen;
943 }
944
945 *buffer_size_inout = (fsw_u32)(pos - shand->pos);
946 shand->pos = pos;
947
948 return FSW_SUCCESS;
949}
950
951// EOF
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use