VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use