1 /* $Id: fsw_core.c 29125 2010-05-06 09:43:05Z vboxsync $ */
3 * fsw_core.c - Core file system wrapper abstraction layer code.
7 * This code is based on:
9 * Copyright (c) 2006 Christoph Pfisterer
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions are
15 * * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
18 * * Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the
23 * * Neither the name of Christoph Pfisterer nor the names of the
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 static void fsw_blockcache_free(struct fsw_volume
*vol
);
47 #define MAX_CACHE_LEVEL (5)
51 * Mount a volume with a given file system driver. This function is called by the
52 * host driver to make a volume accessible. The file system driver to use is specified
53 * by a pointer to its dispatch table. The file system driver will look at the
54 * data on the volume to determine if it can read the format. If the volume is found
55 * unsuitable, FSW_UNSUPPORTED is returned.
57 * If this function returns FSW_SUCCESS, *vol_out points at a valid volume data
58 * structure. The caller must release it later by calling fsw_unmount.
60 * If this function returns an error status, the caller only needs to clean up its
61 * own buffers that may have been allocated through the read_block interface.
64 fsw_status_t
fsw_mount(void *host_data
,
65 struct fsw_host_table
*host_table
,
66 struct fsw_fstype_table
*fstype_table
,
67 struct fsw_volume
**vol_out
)
70 struct fsw_volume
*vol
;
72 // allocate memory for the structure
73 status
= fsw_alloc_zero(fstype_table
->volume_struct_size
, (void **)&vol
);
78 vol
->phys_blocksize
= 512;
79 vol
->log_blocksize
= 512;
80 vol
->label
.type
= FSW_STRING_TYPE_EMPTY
;
81 vol
->host_data
= host_data
;
82 vol
->host_table
= host_table
;
83 vol
->fstype_table
= fstype_table
;
84 vol
->host_string_type
= host_table
->native_string_type
;
86 // let the fs driver mount the file system
87 status
= vol
->fstype_table
->volume_mount(vol
);
91 // TODO: anything else?
102 * Unmount a volume by releasing all memory associated with it. This function is
103 * called by the host driver when a volume is no longer needed. It is also called
104 * by the core after a failed mount to clean up any allocated memory.
106 * Note that all dnodes must have been released before calling this function.
109 void fsw_unmount(struct fsw_volume
*vol
)
112 fsw_dnode_release(vol
->root
);
113 // TODO: check that no other dnodes are still around
115 vol
->fstype_table
->volume_free(vol
);
117 fsw_blockcache_free(vol
);
118 fsw_strfree(&vol
->label
);
123 * Get in-depth information on the volume. This function can be called by the host
124 * driver to get additional information on the volume.
127 fsw_status_t
fsw_volume_stat(struct fsw_volume
*vol
, struct fsw_volume_stat
*sb
)
129 return vol
->fstype_table
->volume_stat(vol
, sb
);
133 * Set the physical and logical block sizes of the volume. This functions is called by
134 * the file system driver to announce the block sizes it wants to use for accessing
135 * the disk (physical) and for addressing file contents (logical).
136 * Usually both sizes will be the same but there may be file systems that need to access
137 * metadata at a smaller block size than the allocation unit for files.
139 * Calling this function causes the block cache to be dropped. All pointers returned
140 * from fsw_block_get become invalid. This function should only be called while
141 * mounting the file system, not as a part of file access operations.
143 * Both sizes are measured in bytes, must be powers of 2, and must not be smaller
144 * than 512 bytes. The logical block size cannot be smaller than the physical block size.
147 void fsw_set_blocksize(struct fsw_volume
*vol
, fsw_u32 phys_blocksize
, fsw_u32 log_blocksize
)
149 // TODO: Check the sizes. Both must be powers of 2. log_blocksize must not be smaller than
152 // drop core block cache if present
153 fsw_blockcache_free(vol
);
155 // signal host driver to drop caches etc.
156 vol
->host_table
->change_blocksize(vol
,
157 vol
->phys_blocksize
, vol
->log_blocksize
,
158 phys_blocksize
, log_blocksize
);
160 vol
->phys_blocksize
= phys_blocksize
;
161 vol
->log_blocksize
= log_blocksize
;
165 * Get a block of data from the disk. This function is called by the file system driver
166 * or by core functions. It calls through to the host driver's device access routine.
167 * Given a physical block number, it reads the block into memory (or fetches it from the
168 * block cache) and returns the address of the memory buffer. The caller should provide
169 * an indication of how important the block is in the cache_level parameter. Blocks with
170 * a low level are purged first. Some suggestions for cache levels:
173 * - 1: Directory data, symlink data
174 * - 2: File system metadata
175 * - 3..5: File system metadata with a high rate of access
177 * If this function returns successfully, the returned data pointer is valid until the
178 * caller calls fsw_block_release.
181 fsw_status_t
fsw_block_get(struct VOLSTRUCTNAME
*vol
, fsw_u32 phys_bno
, fsw_u32 cache_level
, void **buffer_out
)
184 fsw_u32 i
, discard_level
, new_bcache_size
;
185 struct fsw_blockcache
*new_bcache
;
187 // TODO: allow the host driver to do its own caching; just call through if
188 // the appropriate function pointers are set
190 if (cache_level
> MAX_CACHE_LEVEL
)
191 cache_level
= MAX_CACHE_LEVEL
;
194 for (i
= 0; i
< vol
->bcache_size
; i
++) {
195 if (vol
->bcache
[i
].phys_bno
== phys_bno
) {
197 if (vol
->bcache
[i
].cache_level
< cache_level
)
198 vol
->bcache
[i
].cache_level
= cache_level
; // promote the entry
199 vol
->bcache
[i
].refcount
++;
200 *buffer_out
= vol
->bcache
[i
].data
;
205 // find a free entry in the cache table
206 for (i
= 0; i
< vol
->bcache_size
; i
++) {
207 if (vol
->bcache
[i
].phys_bno
== (fsw_u32
)FSW_INVALID_BNO
)
210 if (i
>= vol
->bcache_size
) {
211 for (discard_level
= 0; discard_level
<= MAX_CACHE_LEVEL
; discard_level
++) {
212 for (i
= 0; i
< vol
->bcache_size
; i
++) {
213 if (vol
->bcache
[i
].refcount
== 0 && vol
->bcache
[i
].cache_level
<= discard_level
)
216 if (i
< vol
->bcache_size
)
220 if (i
>= vol
->bcache_size
) {
221 // enlarge / create the cache
222 if (vol
->bcache_size
< 16)
223 new_bcache_size
= 16;
225 new_bcache_size
= vol
->bcache_size
<< 1;
226 status
= fsw_alloc(new_bcache_size
* sizeof(struct fsw_blockcache
), &new_bcache
);
229 if (vol
->bcache_size
> 0)
230 fsw_memcpy(new_bcache
, vol
->bcache
, vol
->bcache_size
* sizeof(struct fsw_blockcache
));
231 for (i
= vol
->bcache_size
; i
< new_bcache_size
; i
++) {
232 new_bcache
[i
].refcount
= 0;
233 new_bcache
[i
].cache_level
= 0;
234 new_bcache
[i
].phys_bno
= (fsw_u32
)FSW_INVALID_BNO
;
235 new_bcache
[i
].data
= NULL
;
237 i
= vol
->bcache_size
;
240 if (vol
->bcache
!= NULL
)
241 fsw_free(vol
->bcache
);
242 vol
->bcache
= new_bcache
;
243 vol
->bcache_size
= new_bcache_size
;
245 vol
->bcache
[i
].phys_bno
= (fsw_u32
)FSW_INVALID_BNO
;
248 if (vol
->bcache
[i
].data
== NULL
) {
249 status
= fsw_alloc(vol
->phys_blocksize
, &vol
->bcache
[i
].data
);
253 status
= vol
->host_table
->read_block(vol
, phys_bno
, vol
->bcache
[i
].data
);
257 vol
->bcache
[i
].phys_bno
= phys_bno
;
258 vol
->bcache
[i
].cache_level
= cache_level
;
259 vol
->bcache
[i
].refcount
= 1;
260 *buffer_out
= vol
->bcache
[i
].data
;
265 * Releases a disk block. This function must be called to release disk blocks returned
266 * from fsw_block_get.
269 void fsw_block_release(struct VOLSTRUCTNAME
*vol
, fsw_u32 phys_bno
, void *buffer
)
273 // TODO: allow the host driver to do its own caching; just call through if
274 // the appropriate function pointers are set
276 // update block cache
277 for (i
= 0; i
< vol
->bcache_size
; i
++) {
278 if (vol
->bcache
[i
].phys_bno
== phys_bno
&& vol
->bcache
[i
].refcount
> 0)
279 vol
->bcache
[i
].refcount
--;
284 * Release the block cache. Called internally when changing block sizes and when
285 * unmounting the volume. It frees all data occupied by the generic block cache.
288 static void fsw_blockcache_free(struct fsw_volume
*vol
)
292 for (i
= 0; i
< vol
->bcache_size
; i
++) {
293 if (vol
->bcache
[i
].data
!= NULL
)
294 fsw_free(vol
->bcache
[i
].data
);
296 if (vol
->bcache
!= NULL
) {
297 fsw_free(vol
->bcache
);
300 vol
->bcache_size
= 0;
304 * Add a new dnode to the list of known dnodes. This internal function is used when a
305 * dnode is created to add it to the dnode list that is used to search for existing
309 static void fsw_dnode_register(struct fsw_volume
*vol
, struct fsw_dnode
*dno
)
311 dno
->next
= vol
->dnode_head
;
312 if (vol
->dnode_head
!= NULL
)
313 vol
->dnode_head
->prev
= dno
;
315 vol
->dnode_head
= dno
;
319 * Create a dnode representing the root directory. This function is called by the file system
320 * driver while mounting the file system. The root directory is special because it has no parent
321 * dnode, its name is defined to be empty, and its type is also fixed. Otherwise, this functions
322 * behaves in the same way as fsw_dnode_create.
325 fsw_status_t
fsw_dnode_create_root(struct fsw_volume
*vol
, fsw_u32 dnode_id
, struct fsw_dnode
**dno_out
)
328 struct fsw_dnode
*dno
;
330 // allocate memory for the structure
331 status
= fsw_alloc_zero(vol
->fstype_table
->dnode_struct_size
, (void **)&dno
);
335 // fill the structure
338 dno
->dnode_id
= dnode_id
;
339 dno
->type
= FSW_DNODE_TYPE_DIR
;
341 dno
->name
.type
= FSW_STRING_TYPE_EMPTY
;
342 // TODO: instead, call a function to create an empty string in the native string type
344 fsw_dnode_register(vol
, dno
);
351 * Create a new dnode representing a file system object. This function is called by
352 * the file system driver in response to directory lookup or read requests. Note that
353 * if there already is a dnode with the given dnode_id on record, then no new object
354 * is created. Instead, the existing dnode is returned and its reference count
355 * increased. All other parameters are ignored in this case.
357 * The type passed into this function may be FSW_DNODE_TYPE_UNKNOWN. It is sufficient
358 * to fill the type field during the dnode_fill call.
360 * The name parameter must describe a string with the object's name. A copy will be
361 * stored in the dnode structure for future reference. The name will not be used to
362 * shortcut directory lookups, but may be used to reconstruct paths.
364 * If the function returns successfully, *dno_out contains a pointer to the dnode
365 * that must be released by the caller with fsw_dnode_release.
368 fsw_status_t
fsw_dnode_create(struct fsw_dnode
*parent_dno
, fsw_u32 dnode_id
, int type
,
369 struct fsw_string
*name
, struct fsw_dnode
**dno_out
)
372 struct fsw_volume
*vol
= parent_dno
->vol
;
373 struct fsw_dnode
*dno
;
375 // check if we already have a dnode with the same id
376 for (dno
= vol
->dnode_head
; dno
; dno
= dno
->next
) {
377 if (dno
->dnode_id
== dnode_id
) {
378 fsw_dnode_retain(dno
);
384 // allocate memory for the structure
385 status
= fsw_alloc_zero(vol
->fstype_table
->dnode_struct_size
, (void **)&dno
);
389 // fill the structure
391 dno
->parent
= parent_dno
;
392 fsw_dnode_retain(dno
->parent
);
393 dno
->dnode_id
= dnode_id
;
396 status
= fsw_strdup_coerce(&dno
->name
, vol
->host_table
->native_string_type
, name
);
402 fsw_dnode_register(vol
, dno
);
409 * Increases the reference count of a dnode. This must be balanced with
410 * fsw_dnode_release calls. Note that some dnode functions return a retained
411 * dnode pointer to their caller.
414 void fsw_dnode_retain(struct fsw_dnode
*dno
)
420 * Release a dnode pointer, deallocating it if this was the last reference.
421 * This function decrements the reference counter of the dnode. If the counter
422 * reaches zero, the dnode is freed. Since the parent dnode is released
423 * during that process, this function may cause it to be freed, too.
426 void fsw_dnode_release(struct fsw_dnode
*dno
)
428 struct fsw_volume
*vol
= dno
->vol
;
429 struct fsw_dnode
*parent_dno
;
433 if (dno
->refcount
== 0) {
434 parent_dno
= dno
->parent
;
436 // de-register from volume's list
438 dno
->next
->prev
= dno
->prev
;
440 dno
->prev
->next
= dno
->next
;
441 if (vol
->dnode_head
== dno
)
442 vol
->dnode_head
= dno
->next
;
444 // run fstype-specific cleanup
445 vol
->fstype_table
->dnode_free(vol
, dno
);
447 fsw_strfree(&dno
->name
);
450 // release our pointer to the parent, possibly deallocating it, too
452 fsw_dnode_release(parent_dno
);
457 * Get full information about a dnode from disk. This function is called by the host
458 * driver as well as by the core functions. Some file systems defer reading full
459 * information on a dnode until it is actually needed (i.e. separation between
460 * directory and inode information). This function makes sure that all information
461 * is available in the dnode structure. The following fields may not have a correct
462 * value until fsw_dnode_fill has been called:
467 fsw_status_t
fsw_dnode_fill(struct fsw_dnode
*dno
)
469 // TODO: check a flag right here, call fstype's dnode_fill only once per dnode
471 return dno
->vol
->fstype_table
->dnode_fill(dno
->vol
, dno
);
475 * Get extended information about a dnode. This function can be called by the host
476 * driver to get a full compliment of information about a dnode in addition to the
477 * fields of the fsw_dnode structure itself.
479 * Some data requires host-specific conversion to be useful (i.e. timestamps) and
480 * will be passed to callback functions instead of being written into the structure.
481 * These callbacks must be filled in by the caller.
484 fsw_status_t
fsw_dnode_stat(struct fsw_dnode
*dno
, struct fsw_dnode_stat
*sb
)
488 status
= fsw_dnode_fill(dno
);
493 status
= dno
->vol
->fstype_table
->dnode_stat(dno
->vol
, dno
, sb
);
494 if (!status
&& !sb
->used_bytes
)
495 sb
->used_bytes
= FSW_U64_DIV(dno
->size
+ dno
->vol
->log_blocksize
- 1, dno
->vol
->log_blocksize
);
500 * Lookup a directory entry by name. This function is called by the host driver.
501 * Given a directory dnode and a file name, it looks up the named entry in the
504 * If the dnode is not a directory, the call will fail. The caller is responsible for
505 * resolving symbolic links before calling this function.
507 * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory
508 * entry. The caller must call fsw_dnode_release on it.
511 fsw_status_t
fsw_dnode_lookup(struct fsw_dnode
*dno
,
512 struct fsw_string
*lookup_name
, struct fsw_dnode
**child_dno_out
)
516 status
= fsw_dnode_fill(dno
);
519 if (dno
->type
!= FSW_DNODE_TYPE_DIR
)
520 return FSW_UNSUPPORTED
;
522 return dno
->vol
->fstype_table
->dir_lookup(dno
->vol
, dno
, lookup_name
, child_dno_out
);
526 * Find a file system object by path. This function is called by the host driver.
527 * Given a directory dnode and a relative or absolute path, it walks the directory
528 * tree until it finds the target dnode. If an intermediate node turns out to be
529 * a symlink, it is resolved automatically. If the target node is a symlink, it
532 * If the function returns FSW_SUCCESS, *child_dno_out points to the requested directory
533 * entry. The caller must call fsw_dnode_release on it.
536 fsw_status_t
fsw_dnode_lookup_path(struct fsw_dnode
*dno
,
537 struct fsw_string
*lookup_path
, char separator
,
538 struct fsw_dnode
**child_dno_out
)
541 struct fsw_volume
*vol
= dno
->vol
;
542 struct fsw_dnode
*child_dno
= NULL
;
543 struct fsw_string lookup_name
;
544 struct fsw_string remaining_path
;
547 remaining_path
= *lookup_path
;
548 fsw_dnode_retain(dno
);
550 // loop over the path
551 for (root_if_empty
= 1; fsw_strlen(&remaining_path
) > 0; root_if_empty
= 0) {
552 // parse next path component
553 fsw_strsplit(&lookup_name
, &remaining_path
, separator
);
555 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: split into %d '%s' and %d '%s'\n"),
556 lookup_name
.len
, lookup_name
.data
,
557 remaining_path
.len
, remaining_path
.data
));
559 if (fsw_strlen(&lookup_name
) == 0) { // empty path component
561 child_dno
= vol
->root
;
564 fsw_dnode_retain(child_dno
);
567 // do an actual directory lookup
569 // ensure we have full information
570 status
= fsw_dnode_fill(dno
);
574 // resolve symlink if necessary
575 if (dno
->type
== FSW_DNODE_TYPE_SYMLINK
) {
576 status
= fsw_dnode_resolve(dno
, &child_dno
);
580 // symlink target becomes the new dno
581 fsw_dnode_release(dno
);
582 dno
= child_dno
; // is already retained
585 // ensure we have full information
586 status
= fsw_dnode_fill(dno
);
591 // make sure we operate on a directory
592 if (dno
->type
!= FSW_DNODE_TYPE_DIR
) {
593 return FSW_UNSUPPORTED
;
597 // check special paths
598 if (fsw_streq_cstr(&lookup_name
, ".")) { // self directory
600 fsw_dnode_retain(child_dno
);
602 } else if (fsw_streq_cstr(&lookup_name
, "..")) { // parent directory
603 if (dno
->parent
== NULL
) {
604 // We cannot go up from the root directory. Caution: Certain apps like the EFI shell
605 // rely on this behaviour!
606 status
= FSW_NOT_FOUND
;
609 child_dno
= dno
->parent
;
610 fsw_dnode_retain(child_dno
);
613 // do an actual lookup
614 status
= vol
->fstype_table
->dir_lookup(vol
, dno
, &lookup_name
, &child_dno
);
620 // child_dno becomes the new dno
621 fsw_dnode_release(dno
);
622 dno
= child_dno
; // is already retained
625 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: now at inode %d\n"), dno
->dnode_id
));
628 *child_dno_out
= dno
;
632 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: leaving with error %d\n"), status
));
633 fsw_dnode_release(dno
);
634 if (child_dno
!= NULL
)
635 fsw_dnode_release(child_dno
);
640 * Get the next directory item in sequential order. This function is called by the
641 * host driver to read the complete contents of a directory in sequential (file system
642 * defined) order. Calling this function returns the next entry. Iteration state is
643 * kept by a shandle on the directory's dnode. The caller must set up the shandle
644 * when starting the iteration.
646 * When the end of the directory is reached, this function returns FSW_NOT_FOUND.
647 * If the function returns FSW_SUCCESS, *child_dno_out points to the next directory
648 * entry. The caller must call fsw_dnode_release on it.
651 fsw_status_t
fsw_dnode_dir_read(struct fsw_shandle
*shand
, struct fsw_dnode
**child_dno_out
)
654 struct fsw_dnode
*dno
= shand
->dnode
;
657 if (dno
->type
!= FSW_DNODE_TYPE_DIR
)
658 return FSW_UNSUPPORTED
;
660 saved_pos
= shand
->pos
;
661 status
= dno
->vol
->fstype_table
->dir_read(dno
->vol
, dno
, shand
, child_dno_out
);
663 shand
->pos
= saved_pos
;
668 * Read the target path of a symbolic link. This function is called by the host driver
669 * to read the "content" of a symbolic link, that is the relative or absolute path
672 * If the function returns FSW_SUCCESS, the string handle provided by the caller is
673 * filled with a string in the host's preferred encoding. The caller is responsible
674 * for calling fsw_strfree on the string.
677 fsw_status_t
fsw_dnode_readlink(struct fsw_dnode
*dno
, struct fsw_string
*target_name
)
681 status
= fsw_dnode_fill(dno
);
684 if (dno
->type
!= FSW_DNODE_TYPE_SYMLINK
)
685 return FSW_UNSUPPORTED
;
687 return dno
->vol
->fstype_table
->readlink(dno
->vol
, dno
, target_name
);
691 * Read the target path of a symbolic link by accessing file data. This function can
692 * be called by the file system driver if the file system stores the target path
693 * as normal file data. This function will open an shandle, read the whole content
694 * of the file into a buffer, and build a string from that. Currently the encoding
695 * for the string is fixed as FSW_STRING_TYPE_ISO88591.
697 * If the function returns FSW_SUCCESS, the string handle provided by the caller is
698 * filled with a string in the host's preferred encoding. The caller is responsible
699 * for calling fsw_strfree on the string.
702 fsw_status_t
fsw_dnode_readlink_data(struct fsw_dnode
*dno
, struct fsw_string
*link_target
)
705 struct fsw_shandle shand
;
707 char buffer
[FSW_PATH_MAX
];
711 if (dno
->size
> FSW_PATH_MAX
)
712 return FSW_VOLUME_CORRUPTED
;
714 s
.type
= FSW_STRING_TYPE_ISO88591
;
715 s
.size
= s
.len
= (int)dno
->size
;
718 // open shandle and read the data
719 status
= fsw_shandle_open(dno
, &shand
);
722 buffer_size
= (fsw_u32
)s
.size
;
723 status
= fsw_shandle_read(&shand
, &buffer_size
, buffer
);
724 fsw_shandle_close(&shand
);
727 if ((int)buffer_size
< s
.size
)
728 return FSW_VOLUME_CORRUPTED
;
730 status
= fsw_strdup_coerce(link_target
, dno
->vol
->host_string_type
, &s
);
735 * Resolve a symbolic link. This function can be called by the host driver to make
736 * sure the a dnode is fully resolved instead of pointing at a symlink. If the dnode
737 * passed in is not a symlink, it is returned unmodified.
739 * Note that absolute paths will be resolved relative to the root directory of the
740 * volume. If the host is an operating system with its own VFS layer, it should
741 * resolve symlinks on its own.
743 * If the function returns FSW_SUCCESS, *target_dno_out points at a dnode that is
744 * not a symlink. The caller is responsible for calling fsw_dnode_release on it.
747 fsw_status_t
fsw_dnode_resolve(struct fsw_dnode
*dno
, struct fsw_dnode
**target_dno_out
)
750 struct fsw_string target_name
;
751 struct fsw_dnode
*target_dno
;
753 fsw_dnode_retain(dno
);
756 // get full information
757 status
= fsw_dnode_fill(dno
);
760 if (dno
->type
!= FSW_DNODE_TYPE_SYMLINK
) {
761 // found a non-symlink target, return it
762 *target_dno_out
= dno
;
765 if (dno
->parent
== NULL
) { // safety measure, cannot happen in theory
766 status
= FSW_NOT_FOUND
;
770 // read the link's target
771 status
= fsw_dnode_readlink(dno
, &target_name
);
776 status
= fsw_dnode_lookup_path(dno
->parent
, &target_name
, '/', &target_dno
);
777 fsw_strfree(&target_name
);
781 // target_dno becomes the new dno
782 fsw_dnode_release(dno
);
783 dno
= target_dno
; // is already retained
787 fsw_dnode_release(dno
);
792 * Set up a shandle (storage handle) to access a file's data. This function is called
793 * by the host driver and by the core when they need to access a file's data. It is also
794 * used in accessing the raw data of directories and symlinks if the file system uses
795 * the same mechanisms for storing the data of those items.
797 * The storage for the fsw_shandle structure is provided by the caller. The dnode and pos
798 * fields may be accessed, pos may also be written to to set the file pointer. The file's
799 * data size is available as shand->dnode->size.
801 * If this function returns FSW_SUCCESS, the caller must call fsw_shandle_close to release
802 * the dnode reference held by the shandle.
805 fsw_status_t
fsw_shandle_open(struct fsw_dnode
*dno
, struct fsw_shandle
*shand
)
808 struct fsw_volume
*vol
= dno
->vol
;
810 // read full dnode information into memory
811 status
= vol
->fstype_table
->dnode_fill(vol
, dno
);
816 fsw_dnode_retain(dno
);
820 shand
->extent
.type
= FSW_EXTENT_TYPE_INVALID
;
826 * Close a shandle after accessing the dnode's data. This function is called by the host
827 * driver or core functions when they are finished with accessing a file's data. It
828 * releases the dnode reference and frees any buffers associated with the shandle itself.
829 * The dnode is only released if this was the last reference using it.
832 void fsw_shandle_close(struct fsw_shandle
*shand
)
834 if (shand
->extent
.type
== FSW_EXTENT_TYPE_BUFFER
)
835 fsw_free(shand
->extent
.buffer
);
836 fsw_dnode_release(shand
->dnode
);
840 * Read data from a shandle (storage handle for a dnode). This function is called by the
841 * host driver or internally when data is read from a file. TODO: more
844 fsw_status_t
fsw_shandle_read(struct fsw_shandle
*shand
, fsw_u32
*buffer_size_inout
, void *buffer_in
)
847 struct fsw_dnode
*dno
= shand
->dnode
;
848 struct fsw_volume
*vol
= dno
->vol
;
849 fsw_u8
*buffer
, *block_buffer
;
850 fsw_u32 buflen
, copylen
, pos
;
851 fsw_u32 log_bno
, pos_in_extent
, phys_bno
, pos_in_physblock
;
854 if (shand
->pos
>= dno
->size
) { // already at EOF
855 *buffer_size_inout
= 0;
861 buflen
= *buffer_size_inout
;
862 pos
= (fsw_u32
)shand
->pos
;
863 cache_level
= (dno
->type
!= FSW_DNODE_TYPE_FILE
) ? 1 : 0;
864 // restrict read to file size
865 if (buflen
> dno
->size
- pos
)
866 buflen
= (fsw_u32
)(dno
->size
- pos
);
869 // get extent for the current logical block
870 log_bno
= pos
/ vol
->log_blocksize
;
871 if (shand
->extent
.type
== FSW_EXTENT_TYPE_INVALID
||
872 log_bno
< shand
->extent
.log_start
||
873 log_bno
>= shand
->extent
.log_start
+ shand
->extent
.log_count
) {
875 if (shand
->extent
.type
== FSW_EXTENT_TYPE_BUFFER
)
876 fsw_free(shand
->extent
.buffer
);
878 // ask the file system for the proper extent
879 shand
->extent
.log_start
= log_bno
;
880 status
= vol
->fstype_table
->get_extent(vol
, dno
, &shand
->extent
);
882 shand
->extent
.type
= FSW_EXTENT_TYPE_INVALID
;
887 pos_in_extent
= pos
- shand
->extent
.log_start
* vol
->log_blocksize
;
889 // dispatch by extent type
890 if (shand
->extent
.type
== FSW_EXTENT_TYPE_PHYSBLOCK
) {
891 // convert to physical block number and offset
892 phys_bno
= shand
->extent
.phys_start
+ pos_in_extent
/ vol
->phys_blocksize
;
893 pos_in_physblock
= pos_in_extent
& (vol
->phys_blocksize
- 1);
894 copylen
= vol
->phys_blocksize
- pos_in_physblock
;
895 if (copylen
> buflen
)
898 // get one physical block
899 status
= fsw_block_get(vol
, phys_bno
, cache_level
, (void **)&block_buffer
);
904 fsw_memcpy(buffer
, block_buffer
+ pos_in_physblock
, copylen
);
905 fsw_block_release(vol
, phys_bno
, block_buffer
);
907 } else if (shand
->extent
.type
== FSW_EXTENT_TYPE_BUFFER
) {
908 copylen
= shand
->extent
.log_count
* vol
->log_blocksize
- pos_in_extent
;
909 if (copylen
> buflen
)
911 fsw_memcpy(buffer
, (fsw_u8
*)shand
->extent
.buffer
+ pos_in_extent
, copylen
);
913 } else { // _SPARSE or _INVALID
914 copylen
= shand
->extent
.log_count
* vol
->log_blocksize
- pos_in_extent
;
915 if (copylen
> buflen
)
917 fsw_memzero(buffer
, copylen
);
926 *buffer_size_inout
= (fsw_u32
)(pos
- shand
->pos
);