3 * ext4 file system driver code.
7 * Copyright (c) 2012 Stefan Agner
8 * Portions Copyright (c) 2006 Christoph Pfisterer
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 static fsw_status_t
fsw_ext4_volume_mount(struct fsw_ext4_volume
*vol
);
31 static void fsw_ext4_volume_free(struct fsw_ext4_volume
*vol
);
32 static fsw_status_t
fsw_ext4_volume_stat(struct fsw_ext4_volume
*vol
, struct fsw_volume_stat
*sb
);
34 static fsw_status_t
fsw_ext4_dnode_fill(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
);
35 static void fsw_ext4_dnode_free(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
);
36 static fsw_status_t
fsw_ext4_dnode_stat(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
37 struct fsw_dnode_stat
*sb
);
38 static fsw_status_t
fsw_ext4_get_extent(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
39 struct fsw_extent
*extent
);
40 static fsw_status_t
fsw_ext4_get_by_blkaddr(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
41 struct fsw_extent
*extent
);
42 static fsw_status_t
fsw_ext4_get_by_extent(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
43 struct fsw_extent
*extent
);
45 static fsw_status_t
fsw_ext4_dir_lookup(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
46 struct fsw_string
*lookup_name
, struct fsw_ext4_dnode
**child_dno
);
47 static fsw_status_t
fsw_ext4_dir_read(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
48 struct fsw_shandle
*shand
, struct fsw_ext4_dnode
**child_dno
);
49 static fsw_status_t
fsw_ext4_read_dentry(struct fsw_shandle
*shand
, struct ext4_dir_entry
*entry
);
51 static fsw_status_t
fsw_ext4_readlink(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
52 struct fsw_string
*link
);
58 struct fsw_fstype_table
FSW_FSTYPE_TABLE_NAME(ext4
) = {
59 { FSW_STRING_TYPE_ISO88591
, 4, 4, "ext4" },
60 sizeof(struct fsw_ext4_volume
),
61 sizeof(struct fsw_ext4_dnode
),
63 fsw_ext4_volume_mount
,
76 * Mount an ext4 volume. Reads the superblock and constructs the
77 * root directory dnode.
80 static fsw_status_t
fsw_ext4_volume_mount(struct fsw_ext4_volume
*vol
)
85 fsw_u32 groupcnt
, groupno
, gdesc_per_block
, gdesc_bno
, gdesc_index
;
86 struct ext4_group_desc
*gdesc
;
90 // allocate memory to keep the superblock around
91 status
= fsw_alloc(sizeof(struct ext4_super_block
), &vol
->sb
);
95 // read the superblock into its buffer
96 fsw_set_blocksize(vol
, EXT4_SUPERBLOCK_BLOCKSIZE
, EXT4_SUPERBLOCK_BLOCKSIZE
);
97 status
= fsw_block_get(vol
, EXT4_SUPERBLOCK_BLOCKNO
, 0, &buffer
);
100 fsw_memcpy(vol
->sb
, buffer
, sizeof(struct ext4_super_block
));
101 fsw_block_release(vol
, EXT4_SUPERBLOCK_BLOCKNO
, buffer
);
103 // check the superblock
104 if (vol
->sb
->s_magic
!= EXT4_SUPER_MAGIC
)
105 return FSW_UNSUPPORTED
;
106 if (vol
->sb
->s_rev_level
!= EXT4_GOOD_OLD_REV
&&
107 vol
->sb
->s_rev_level
!= EXT4_DYNAMIC_REV
)
108 return FSW_UNSUPPORTED
;
110 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_volume_mount: Incompat flag %x\n"), vol
->sb
->s_feature_incompat
));
112 if (vol
->sb
->s_rev_level
== EXT4_DYNAMIC_REV
&&
113 (vol
->sb
->s_feature_incompat
& ~(EXT4_FEATURE_INCOMPAT_FILETYPE
| EXT4_FEATURE_INCOMPAT_RECOVER
|
114 EXT4_FEATURE_INCOMPAT_EXTENTS
| EXT4_FEATURE_INCOMPAT_FLEX_BG
)))
115 return FSW_UNSUPPORTED
;
118 if (vol
->sb
->s_rev_level
== EXT4_DYNAMIC_REV
&&
119 (vol
->sb
->s_feature_incompat
& EXT4_FEATURE_INCOMPAT_RECOVER
))
121 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_volume_mount: This ext3 file system needs recovery\n")));
122 // Print(L"Ext4 WARNING: This file system needs recovery, trying to use it anyway.\n");
125 blocksize
= EXT4_BLOCK_SIZE(vol
->sb
);
126 if (blocksize
< EXT4_MIN_BLOCK_SIZE
|| blocksize
> EXT4_MAX_BLOCK_SIZE
)
127 return FSW_UNSUPPORTED
;
129 // set real blocksize
130 fsw_set_blocksize(vol
, blocksize
, blocksize
);
132 // get other info from superblock
133 vol
->ind_bcnt
= EXT4_ADDR_PER_BLOCK(vol
->sb
);
134 vol
->dind_bcnt
= vol
->ind_bcnt
* vol
->ind_bcnt
;
135 vol
->inode_size
= vol
->sb
->s_inode_size
;//EXT4_INODE_SIZE(vol->sb);
137 for (i
= 0; i
< 16; i
++)
138 if (vol
->sb
->s_volume_name
[i
] == 0)
140 s
.type
= FSW_STRING_TYPE_ISO88591
;
142 s
.data
= vol
->sb
->s_volume_name
;
143 status
= fsw_strdup_coerce(&vol
->g
.label
, vol
->g
.host_string_type
, &s
);
147 // size of group descriptor depends on feature....
148 if (!(vol
->sb
->s_feature_incompat
& EXT4_FEATURE_INCOMPAT_64BIT
)) {
149 // Default minimal group descriptor size...
150 vol
->sb
->s_desc_size
= EXT4_MIN_DESC_SIZE
;
153 // Calculate group descriptor count the way the kernel does it...
154 groupcnt
= (vol
->sb
->s_blocks_count_lo
- vol
->sb
->s_first_data_block
+
155 vol
->sb
->s_blocks_per_group
- 1) / vol
->sb
->s_blocks_per_group
;
156 // Descriptors in one block... s_desc_size needs to be set!
157 gdesc_per_block
= EXT4_DESC_PER_BLOCK(vol
->sb
);
159 // Read the group descriptors to get inode table offsets
160 status
= fsw_alloc(sizeof(fsw_u32
) * groupcnt
, &vol
->inotab_bno
);
163 for (groupno
= 0; groupno
< groupcnt
; groupno
++) {
164 // get the block group descriptor
165 gdesc_bno
= (vol
->sb
->s_first_data_block
+ 1) + groupno
/ gdesc_per_block
;
166 gdesc_index
= groupno
% gdesc_per_block
;
167 status
= fsw_block_get(vol
, gdesc_bno
, 1, (void **)&buffer
);
170 gdesc
= (struct ext4_group_desc
*)(buffer
+ gdesc_index
* vol
->sb
->s_desc_size
);
171 vol
->inotab_bno
[groupno
] = gdesc
->bg_inode_table_lo
;
172 fsw_block_release(vol
, gdesc_bno
, buffer
);
175 // setup the root dnode
176 status
= fsw_dnode_create_root(vol
, EXT4_ROOT_INO
, &vol
->g
.root
);
180 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_volume_mount: success, blocksize %d\n"), blocksize
));
186 * Free the volume data structure. Called by the core after an unmount or after
187 * an unsuccessful mount to release the memory used by the file system type specific
188 * part of the volume structure.
191 static void fsw_ext4_volume_free(struct fsw_ext4_volume
*vol
)
196 fsw_free(vol
->inotab_bno
);
200 * Get in-depth information on a volume.
203 static fsw_status_t
fsw_ext4_volume_stat(struct fsw_ext4_volume
*vol
, struct fsw_volume_stat
*sb
)
205 sb
->total_bytes
= (fsw_u64
)vol
->sb
->s_blocks_count_lo
* vol
->g
.log_blocksize
;
206 sb
->free_bytes
= (fsw_u64
)vol
->sb
->s_free_blocks_count_lo
* vol
->g
.log_blocksize
;
211 * Get full information on a dnode from disk. This function is called by the core
212 * whenever it needs to access fields in the dnode structure that may not
213 * be filled immediately upon creation of the dnode. In the case of ext4, we
214 * delay fetching of the inode structure until dnode_fill is called. The size and
215 * type fields are invalid until this function has been called.
218 static fsw_status_t
fsw_ext4_dnode_fill(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
)
221 fsw_u32 groupno
, ino_in_group
, ino_bno
, ino_index
;
228 // read the inode block
229 groupno
= (dno
->g
.dnode_id
- 1) / vol
->sb
->s_inodes_per_group
;
230 ino_in_group
= (dno
->g
.dnode_id
- 1) % vol
->sb
->s_inodes_per_group
;
231 ino_bno
= vol
->inotab_bno
[groupno
] +
232 ino_in_group
/ (vol
->g
.phys_blocksize
/ vol
->inode_size
);
233 ino_index
= ino_in_group
% (vol
->g
.phys_blocksize
/ vol
->inode_size
);
234 status
= fsw_block_get(vol
, ino_bno
, 2, (void **)&buffer
);
239 // keep our inode around
240 status
= fsw_memdup((void **)&dno
->raw
, buffer
+ ino_index
* vol
->inode_size
, vol
->inode_size
);
241 fsw_block_release(vol
, ino_bno
, buffer
);
245 // get info from the inode
246 dno
->g
.size
= dno
->raw
->i_size_lo
; // TODO: check docs for 64-bit sized files
248 if (S_ISREG(dno
->raw
->i_mode
))
249 dno
->g
.type
= FSW_DNODE_TYPE_FILE
;
250 else if (S_ISDIR(dno
->raw
->i_mode
))
251 dno
->g
.type
= FSW_DNODE_TYPE_DIR
;
252 else if (S_ISLNK(dno
->raw
->i_mode
))
253 dno
->g
.type
= FSW_DNODE_TYPE_SYMLINK
;
255 dno
->g
.type
= FSW_DNODE_TYPE_SPECIAL
;
257 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_dnode_fill: inode flags %x\n"), dno
->raw
->i_flags
));
258 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_dnode_fill: i_mode %x\n"), dno
->raw
->i_mode
));
263 * Free the dnode data structure. Called by the core when deallocating a dnode
264 * structure to release the memory used by the file system type specific part
265 * of the dnode structure.
268 static void fsw_ext4_dnode_free(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
)
275 * Get in-depth information on a dnode. The core makes sure that fsw_ext4_dnode_fill
276 * has been called on the dnode before this function is called. Note that some
277 * data is not directly stored into the structure, but passed to a host-specific
278 * callback that converts it to the host-specific format.
281 static fsw_status_t
fsw_ext4_dnode_stat(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
282 struct fsw_dnode_stat
*sb
)
284 sb
->used_bytes
= dno
->raw
->i_blocks_lo
* EXT4_BLOCK_SIZE(vol
->sb
); // very, very strange...
285 sb
->store_time_posix(sb
, FSW_DNODE_STAT_CTIME
, dno
->raw
->i_ctime
);
286 sb
->store_time_posix(sb
, FSW_DNODE_STAT_ATIME
, dno
->raw
->i_atime
);
287 sb
->store_time_posix(sb
, FSW_DNODE_STAT_MTIME
, dno
->raw
->i_mtime
);
288 sb
->store_attr_posix(sb
, dno
->raw
->i_mode
);
294 * Retrieve file data mapping information. This function is called by the core when
295 * fsw_shandle_read needs to know where on the disk the required piece of the file's
296 * data can be found. The core makes sure that fsw_ext4_dnode_fill has been called
297 * on the dnode before. Our task here is to get the physical disk block number for
298 * the requested logical block number.
300 * The ext4 file system usually uses extents do to store those disk block numbers.
301 * However, since ext4 is backward compatible, depending on inode flags the old direct
302 * and indirect addressing scheme can still be in place...
305 static fsw_status_t
fsw_ext4_get_extent(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
306 struct fsw_extent
*extent
)
308 // Preconditions: The caller has checked that the requested logical block
309 // is within the file's size. The dnode has complete information, i.e.
310 // fsw_ext4_dnode_read_info was called successfully on it.
311 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_extent: inode %d, block %d\n"), dno
->g
.dnode_id
, extent
->log_start
));
312 extent
->type
= FSW_EXTENT_TYPE_PHYSBLOCK
;
313 extent
->log_count
= 1;
315 if(dno
->raw
->i_flags
& 1 << EXT4_INODE_EXTENTS
)
317 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_extent: inode %d uses extents\n"), dno
->g
.dnode_id
));
318 return fsw_ext4_get_by_extent(vol
, dno
, extent
);
322 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_extent: inode %d uses direct/indirect block addressing\n"),
324 return fsw_ext4_get_by_blkaddr(vol
, dno
, extent
);
329 * New ext4 extents...
331 static fsw_status_t
fsw_ext4_get_by_extent(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
332 struct fsw_extent
*extent
)
335 fsw_u32 bno
, release_bno
, buf_bcnt
, buf_offset
, file_bcnt
;
340 struct ext4_extent_header
*ext4_extent_header
;
341 struct ext4_extent_idx
*ext4_extent_idx
;
342 struct ext4_extent
*ext4_extent
;
344 // Logical block requested by core...
345 bno
= extent
->log_start
;
347 // First buffer is the i_block field from inode...
348 buffer
= (void *)dno
->raw
->i_block
;
349 buf_bcnt
= EXT4_NDIR_BLOCKS
;
352 ext4_extent_header
= (struct ext4_extent_header
*)buffer
+ buf_offset
;
353 buf_offset
+= sizeof(struct ext4_extent_header
);
354 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: extent header with %d entries\n"),
355 ext4_extent_header
->eh_entries
));
356 if(ext4_extent_header
->eh_magic
!= EXT4_EXT_MAGIC
)
357 return FSW_VOLUME_CORRUPTED
;
359 for(ext_cnt
= 0;ext_cnt
< ext4_extent_header
->eh_entries
;ext_cnt
++)
361 if(ext4_extent_header
->eh_depth
== 0)
363 // Leaf node, the header follows actual extents
364 ext4_extent
= (struct ext4_extent
*)(buffer
+ buf_offset
);
365 buf_offset
+= sizeof(struct ext4_extent
);
366 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: extent node cover %d...\n"), ext4_extent
->ee_block
));
368 // Is the requested block in this extent?
369 if(bno
>= ext4_extent
->ee_block
&& bno
< ext4_extent
->ee_block
+ ext4_extent
->ee_len
)
371 extent
->phys_start
= ext4_extent
->ee_start_lo
+ (bno
- ext4_extent
->ee_block
);
372 extent
->log_count
= ext4_extent
->ee_len
- (bno
- ext4_extent
->ee_block
);
378 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: index extents, depth %d\n"),
379 ext4_extent_header
->eh_depth
));
380 ext4_extent_idx
= (struct ext4_extent_idx
*)(buffer
+ buf_offset
);
381 buf_offset
+= sizeof(struct ext4_extent_idx
);
383 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: index node covers block %d...\n"),
384 ext4_extent_idx
->ei_block
));
385 if(bno
>= ext4_extent_idx
->ei_block
)
387 // Follow extent tree...
388 status
= fsw_block_get(vol
, ext4_extent_idx
->ei_leaf_lo
, 1, (void **)&buffer
);
398 return FSW_NOT_FOUND
;
402 * The ext2/ext3 file system does not use extents, but stores a list of block numbers
403 * using the usual direct, indirect, double-indirect, triple-indirect scheme. To
404 * optimize access, this function checks if the following file blocks are mapped
405 * to consecutive disk blocks and returns a combined extent if possible.
407 static fsw_status_t
fsw_ext4_get_by_blkaddr(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
408 struct fsw_extent
*extent
)
411 fsw_u32 bno
, release_bno
, buf_bcnt
, file_bcnt
;
414 bno
= extent
->log_start
;
416 // try direct block pointers in the inode
417 if (bno
< EXT4_NDIR_BLOCKS
) {
421 bno
-= EXT4_NDIR_BLOCKS
;
423 // try indirect block
424 if (bno
< vol
->ind_bcnt
) {
425 path
[0] = EXT4_IND_BLOCK
;
429 bno
-= vol
->ind_bcnt
;
431 // try double-indirect block
432 if (bno
< vol
->dind_bcnt
) {
433 path
[0] = EXT4_DIND_BLOCK
;
434 path
[1] = bno
/ vol
->ind_bcnt
;
435 path
[2] = bno
% vol
->ind_bcnt
;
438 bno
-= vol
->dind_bcnt
;
440 // use the triple-indirect block
441 path
[0] = EXT4_TIND_BLOCK
;
442 path
[1] = bno
/ vol
->dind_bcnt
;
443 path
[2] = (bno
/ vol
->ind_bcnt
) % vol
->ind_bcnt
;
444 path
[3] = bno
% vol
->ind_bcnt
;
450 // follow the indirection path
451 buffer
= dno
->raw
->i_block
;
452 buf_bcnt
= EXT4_NDIR_BLOCKS
;
455 bno
= buffer
[path
[i
]];
457 extent
->type
= FSW_EXTENT_TYPE_SPARSE
;
459 fsw_block_release(vol
, release_bno
, buffer
);
466 fsw_block_release(vol
, release_bno
, buffer
);
467 status
= fsw_block_get(vol
, bno
, 1, (void **)&buffer
);
471 buf_bcnt
= vol
->ind_bcnt
;
473 extent
->phys_start
= bno
;
475 // check if the following blocks can be aggregated into one extent
476 file_bcnt
= (fsw_u32
)((dno
->g
.size
+ vol
->g
.log_blocksize
- 1) & (vol
->g
.log_blocksize
- 1));
477 while (path
[i
] + extent
->log_count
< buf_bcnt
&& // indirect block has more block pointers
478 extent
->log_start
+ extent
->log_count
< file_bcnt
) { // file has more blocks
479 if (buffer
[path
[i
] + extent
->log_count
] == buffer
[path
[i
] + extent
->log_count
- 1] + 1)
486 fsw_block_release(vol
, release_bno
, buffer
);
491 * Lookup a directory's child dnode by name. This function is called on a directory
492 * to retrieve the directory entry with the given name. A dnode is constructed for
493 * this entry and returned. The core makes sure that fsw_ext4_dnode_fill has been called
494 * and the dnode is actually a directory.
497 static fsw_status_t
fsw_ext4_dir_lookup(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
498 struct fsw_string
*lookup_name
, struct fsw_ext4_dnode
**child_dno_out
)
501 struct fsw_shandle shand
;
503 struct ext4_dir_entry entry
;
504 struct fsw_string entry_name
;
506 // Preconditions: The caller has checked that dno is a directory node.
508 entry_name
.type
= FSW_STRING_TYPE_ISO88591
;
510 // setup handle to read the directory
511 status
= fsw_shandle_open(dno
, &shand
);
515 // scan the directory for the file
517 while (child_ino
== 0) {
519 status
= fsw_ext4_read_dentry(&shand
, &entry
);
522 if (entry
.inode
== 0) {
523 // end of directory reached
524 status
= FSW_NOT_FOUND
;
529 entry_name
.len
= entry_name
.size
= entry
.name_len
;
530 entry_name
.data
= entry
.name
;
531 if (fsw_streq(lookup_name
, &entry_name
)) {
532 child_ino
= entry
.inode
;
537 // setup a dnode for the child item
538 status
= fsw_dnode_create(dno
, child_ino
, FSW_DNODE_TYPE_UNKNOWN
, &entry_name
, child_dno_out
);
541 fsw_shandle_close(&shand
);
546 * Get the next directory entry when reading a directory. This function is called during
547 * directory iteration to retrieve the next directory entry. A dnode is constructed for
548 * the entry and returned. The core makes sure that fsw_ext4_dnode_fill has been called
549 * and the dnode is actually a directory. The shandle provided by the caller is used to
550 * record the position in the directory between calls.
553 static fsw_status_t
fsw_ext4_dir_read(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
554 struct fsw_shandle
*shand
, struct fsw_ext4_dnode
**child_dno_out
)
557 struct ext4_dir_entry entry
;
558 struct fsw_string entry_name
;
560 // Preconditions: The caller has checked that dno is a directory node. The caller
561 // has opened a storage handle to the directory's storage and keeps it around between
563 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_dir_read: started reading dir\n")));
567 status
= fsw_ext4_read_dentry(shand
, &entry
);
570 if (entry
.inode
== 0) // end of directory
571 return FSW_NOT_FOUND
;
574 if ((entry
.name_len
== 1 && entry
.name
[0] == '.') ||
575 (entry
.name_len
== 2 && entry
.name
[0] == '.' && entry
.name
[1] == '.'))
581 entry_name
.type
= FSW_STRING_TYPE_ISO88591
;
582 entry_name
.len
= entry_name
.size
= entry
.name_len
;
583 entry_name
.data
= entry
.name
;
585 // setup a dnode for the child item
586 status
= fsw_dnode_create(dno
, entry
.inode
, FSW_DNODE_TYPE_UNKNOWN
, &entry_name
, child_dno_out
);
592 * Read a directory entry from the directory's raw data. This internal function is used
593 * to read a raw ext2 directory entry into memory. The shandle's position pointer is adjusted
594 * to point to the next entry.
597 static fsw_status_t
fsw_ext4_read_dentry(struct fsw_shandle
*shand
, struct ext4_dir_entry
*entry
)
603 // read dir_entry header (fixed length)
605 status
= fsw_shandle_read(shand
, &buffer_size
, entry
);
609 if (buffer_size
< 8 || entry
->rec_len
== 0) {
610 // end of directory reached
614 if (entry
->rec_len
< 8)
615 return FSW_VOLUME_CORRUPTED
;
616 if (entry
->inode
!= 0) {
617 // this entry is used
618 if (entry
->rec_len
< 8 + entry
->name_len
)
619 return FSW_VOLUME_CORRUPTED
;
623 // valid, but unused entry, skip it
624 shand
->pos
+= entry
->rec_len
- 8;
627 // read file name (variable length)
628 buffer_size
= entry
->name_len
;
629 status
= fsw_shandle_read(shand
, &buffer_size
, entry
->name
);
632 if (buffer_size
< entry
->name_len
)
633 return FSW_VOLUME_CORRUPTED
;
635 // skip any remaining padding
636 shand
->pos
+= entry
->rec_len
- (8 + entry
->name_len
);
642 * Get the target path of a symbolic link. This function is called when a symbolic
643 * link needs to be resolved. The core makes sure that the fsw_ext4_dnode_fill has been
644 * called on the dnode and that it really is a symlink.
646 * For ext4, the target path can be stored inline in the inode structure (in the space
647 * otherwise occupied by the block pointers) or in the inode's data. There is no flag
648 * indicating this, only the number of blocks entry (i_blocks) can be used as an
649 * indication. The check used here comes from the Linux kernel.
652 static fsw_status_t
fsw_ext4_readlink(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
653 struct fsw_string
*link_target
)
659 if (dno
->g
.size
> FSW_PATH_MAX
)
660 return FSW_VOLUME_CORRUPTED
;
662 /* Linux kernels ext4_inode_is_fast_symlink... */
663 ea_blocks
= dno
->raw
->i_file_acl_lo
? (vol
->g
.log_blocksize
>> 9) : 0;
665 if (dno
->raw
->i_blocks_lo
- ea_blocks
== 0) {
666 // "fast" symlink, path is stored inside the inode
667 s
.type
= FSW_STRING_TYPE_ISO88591
;
668 s
.size
= s
.len
= (int)dno
->g
.size
;
669 s
.data
= dno
->raw
->i_block
;
670 status
= fsw_strdup_coerce(link_target
, vol
->g
.host_string_type
, &s
);
672 // "slow" symlink, path is stored in normal inode data
673 status
= fsw_dnode_readlink_data(dno
, link_target
);