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 static __inline
int test_root(fsw_u32 a
, int b
)
85 static int fsw_ext4_group_sparse(fsw_u32 group
)
91 return (test_root(group
, 7) || test_root(group
, 5) ||
95 /* calculate the first block number of the group */
96 static __inline fsw_u32
97 fsw_ext4_group_first_block_no(struct ext4_super_block
*sb
, fsw_u32 group_no
)
99 return group_no
* (fsw_u32
)EXT4_BLOCKS_PER_GROUP(sb
) +
100 sb
->s_first_data_block
;
104 * Mount an ext4 volume. Reads the superblock and constructs the
105 * root directory dnode.
108 static fsw_status_t
fsw_ext4_volume_mount(struct fsw_ext4_volume
*vol
)
113 fsw_u32 groupcnt
, groupno
, gdesc_per_block
, gdesc_bno
, gdesc_index
, metabg_of_gdesc
;
114 struct ext4_group_desc
*gdesc
;
118 // allocate memory to keep the superblock around
119 status
= fsw_alloc(sizeof(struct ext4_super_block
), &vol
->sb
);
123 // read the superblock into its buffer
124 fsw_set_blocksize(vol
, EXT4_SUPERBLOCK_BLOCKSIZE
, EXT4_SUPERBLOCK_BLOCKSIZE
);
125 status
= fsw_block_get(vol
, EXT4_SUPERBLOCK_BLOCKNO
, 0, &buffer
);
128 fsw_memcpy(vol
->sb
, buffer
, sizeof(struct ext4_super_block
));
129 fsw_block_release(vol
, EXT4_SUPERBLOCK_BLOCKNO
, buffer
);
131 // check the superblock
132 if (vol
->sb
->s_magic
!= EXT4_SUPER_MAGIC
)
133 return FSW_UNSUPPORTED
;
134 if (vol
->sb
->s_rev_level
!= EXT4_GOOD_OLD_REV
&&
135 vol
->sb
->s_rev_level
!= EXT4_DYNAMIC_REV
)
136 return FSW_UNSUPPORTED
;
138 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_volume_mount: Incompat flag %x\n"), vol
->sb
->s_feature_incompat
));
140 if (vol
->sb
->s_rev_level
== EXT4_DYNAMIC_REV
&&
141 (vol
->sb
->s_feature_incompat
& ~(EXT4_FEATURE_INCOMPAT_FILETYPE
| EXT4_FEATURE_INCOMPAT_RECOVER
|
142 EXT4_FEATURE_INCOMPAT_EXTENTS
| EXT4_FEATURE_INCOMPAT_FLEX_BG
|
143 EXT4_FEATURE_INCOMPAT_META_BG
)))
144 return FSW_UNSUPPORTED
;
147 if (vol
->sb
->s_rev_level
== EXT4_DYNAMIC_REV
&&
148 (vol
->sb
->s_feature_incompat
& EXT4_FEATURE_INCOMPAT_RECOVER
))
150 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_volume_mount: This ext3 file system needs recovery\n")));
151 // Print(L"Ext4 WARNING: This file system needs recovery, trying to use it anyway.\n");
154 blocksize
= EXT4_BLOCK_SIZE(vol
->sb
);
155 if (blocksize
< EXT4_MIN_BLOCK_SIZE
|| blocksize
> EXT4_MAX_BLOCK_SIZE
)
156 return FSW_UNSUPPORTED
;
158 // set real blocksize
159 fsw_set_blocksize(vol
, blocksize
, blocksize
);
161 // get other info from superblock
162 vol
->ind_bcnt
= EXT4_ADDR_PER_BLOCK(vol
->sb
);
163 vol
->dind_bcnt
= vol
->ind_bcnt
* vol
->ind_bcnt
;
164 vol
->inode_size
= vol
->sb
->s_inode_size
;//EXT4_INODE_SIZE(vol->sb);
166 for (i
= 0; i
< 16; i
++)
167 if (vol
->sb
->s_volume_name
[i
] == 0)
169 s
.type
= FSW_STRING_TYPE_ISO88591
;
171 s
.data
= vol
->sb
->s_volume_name
;
172 status
= fsw_strdup_coerce(&vol
->g
.label
, vol
->g
.host_string_type
, &s
);
176 // size of group descriptor depends on feature....
177 if (!(vol
->sb
->s_feature_incompat
& EXT4_FEATURE_INCOMPAT_64BIT
)) {
178 // Default minimal group descriptor size... (this might not be set in old ext2 filesystems, therefor set it!)
179 vol
->sb
->s_desc_size
= EXT4_MIN_DESC_SIZE
;
182 // Calculate group descriptor count the way the kernel does it...
183 groupcnt
= (vol
->sb
->s_blocks_count_lo
- vol
->sb
->s_first_data_block
+
184 vol
->sb
->s_blocks_per_group
- 1) / vol
->sb
->s_blocks_per_group
;
186 // Descriptors in one block... s_desc_size needs to be set! (Usually 128 since normal block
187 // descriptors are 32 byte and block size is 4096)
188 gdesc_per_block
= EXT4_DESC_PER_BLOCK(vol
->sb
);
190 // Read the group descriptors to get inode table offsets
191 status
= fsw_alloc(sizeof(fsw_u32
) * groupcnt
, &vol
->inotab_bno
);
195 // Loop through all block group descriptors in order to get inode table locations
196 for (groupno
= 0; groupno
< groupcnt
; groupno
++) {
198 // Calculate the block number which contains the block group descriptor we look for
199 if(vol
->sb
->s_feature_incompat
& EXT4_FEATURE_INCOMPAT_META_BG
&& groupno
>= vol
->sb
->s_first_meta_bg
)
201 // If option meta_bg is set, the block group descriptor is in meta block group...
202 metabg_of_gdesc
= (fsw_u32
)(groupno
/ gdesc_per_block
) * gdesc_per_block
;
203 gdesc_bno
= fsw_ext4_group_first_block_no(vol
->sb
, metabg_of_gdesc
);
204 // We need to know if the block group in questition has a super block, if yes, the
205 // block group descriptors are in the next block number
206 if(!(vol
->sb
->s_feature_ro_compat
& EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER
) || fsw_ext4_group_sparse(metabg_of_gdesc
))
211 // All group descriptors follow the super block (+1)
212 gdesc_bno
= (vol
->sb
->s_first_data_block
+ 1) + groupno
/ gdesc_per_block
;
214 gdesc_index
= groupno
% gdesc_per_block
;
216 // Get block if necessary...
217 status
= fsw_block_get(vol
, gdesc_bno
, 1, (void **)&buffer
);
221 // Get group descriptor table and block number of inode table...
222 gdesc
= (struct ext4_group_desc
*)((char *)buffer
+ gdesc_index
* vol
->sb
->s_desc_size
);
223 vol
->inotab_bno
[groupno
] = gdesc
->bg_inode_table_lo
;
225 fsw_block_release(vol
, gdesc_bno
, buffer
);
228 // setup the root dnode
229 status
= fsw_dnode_create_root(vol
, EXT4_ROOT_INO
, &vol
->g
.root
);
233 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_volume_mount: success, blocksize %d\n"), blocksize
));
239 * Free the volume data structure. Called by the core after an unmount or after
240 * an unsuccessful mount to release the memory used by the file system type specific
241 * part of the volume structure.
244 static void fsw_ext4_volume_free(struct fsw_ext4_volume
*vol
)
249 fsw_free(vol
->inotab_bno
);
253 * Get in-depth information on a volume.
256 static fsw_status_t
fsw_ext4_volume_stat(struct fsw_ext4_volume
*vol
, struct fsw_volume_stat
*sb
)
258 sb
->total_bytes
= (fsw_u64
)vol
->sb
->s_blocks_count_lo
* vol
->g
.log_blocksize
;
259 sb
->free_bytes
= (fsw_u64
)vol
->sb
->s_free_blocks_count_lo
* vol
->g
.log_blocksize
;
264 * Get full information on a dnode from disk. This function is called by the core
265 * whenever it needs to access fields in the dnode structure that may not
266 * be filled immediately upon creation of the dnode. In the case of ext4, we
267 * delay fetching of the inode structure until dnode_fill is called. The size and
268 * type fields are invalid until this function has been called.
271 static fsw_status_t
fsw_ext4_dnode_fill(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
)
274 fsw_u32 groupno
, ino_in_group
, ino_bno
, ino_index
;
281 // read the inode block
282 groupno
= (fsw_u32
) (dno
->g
.dnode_id
- 1) / vol
->sb
->s_inodes_per_group
;
283 ino_in_group
= (fsw_u32
) (dno
->g
.dnode_id
- 1) % vol
->sb
->s_inodes_per_group
;
284 ino_bno
= vol
->inotab_bno
[groupno
] +
285 ino_in_group
/ (vol
->g
.phys_blocksize
/ vol
->inode_size
);
286 ino_index
= ino_in_group
% (vol
->g
.phys_blocksize
/ vol
->inode_size
);
287 status
= fsw_block_get(vol
, ino_bno
, 2, (void **)&buffer
);
292 // keep our inode around
293 status
= fsw_memdup((void **)&dno
->raw
, buffer
+ ino_index
* vol
->inode_size
, vol
->inode_size
);
294 fsw_block_release(vol
, ino_bno
, buffer
);
298 // get info from the inode
299 dno
->g
.size
= dno
->raw
->i_size_lo
; // TODO: check docs for 64-bit sized files
301 if (S_ISREG(dno
->raw
->i_mode
))
302 dno
->g
.type
= FSW_DNODE_TYPE_FILE
;
303 else if (S_ISDIR(dno
->raw
->i_mode
))
304 dno
->g
.type
= FSW_DNODE_TYPE_DIR
;
305 else if (S_ISLNK(dno
->raw
->i_mode
))
306 dno
->g
.type
= FSW_DNODE_TYPE_SYMLINK
;
308 dno
->g
.type
= FSW_DNODE_TYPE_SPECIAL
;
310 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_dnode_fill: inode flags %x\n"), dno
->raw
->i_flags
));
311 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_dnode_fill: i_mode %x\n"), dno
->raw
->i_mode
));
316 * Free the dnode data structure. Called by the core when deallocating a dnode
317 * structure to release the memory used by the file system type specific part
318 * of the dnode structure.
321 static void fsw_ext4_dnode_free(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
)
328 * Get in-depth information on a dnode. The core makes sure that fsw_ext4_dnode_fill
329 * has been called on the dnode before this function is called. Note that some
330 * data is not directly stored into the structure, but passed to a host-specific
331 * callback that converts it to the host-specific format.
334 static fsw_status_t
fsw_ext4_dnode_stat(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
335 struct fsw_dnode_stat
*sb
)
337 sb
->used_bytes
= dno
->raw
->i_blocks_lo
* EXT4_BLOCK_SIZE(vol
->sb
); // very, very strange...
338 sb
->store_time_posix(sb
, FSW_DNODE_STAT_CTIME
, dno
->raw
->i_ctime
);
339 sb
->store_time_posix(sb
, FSW_DNODE_STAT_ATIME
, dno
->raw
->i_atime
);
340 sb
->store_time_posix(sb
, FSW_DNODE_STAT_MTIME
, dno
->raw
->i_mtime
);
341 sb
->store_attr_posix(sb
, dno
->raw
->i_mode
);
347 * Retrieve file data mapping information. This function is called by the core when
348 * fsw_shandle_read needs to know where on the disk the required piece of the file's
349 * data can be found. The core makes sure that fsw_ext4_dnode_fill has been called
350 * on the dnode before. Our task here is to get the physical disk block number for
351 * the requested logical block number.
353 * The ext4 file system usually uses extents do to store those disk block numbers.
354 * However, since ext4 is backward compatible, depending on inode flags the old direct
355 * and indirect addressing scheme can still be in place...
358 static fsw_status_t
fsw_ext4_get_extent(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
359 struct fsw_extent
*extent
)
361 // Preconditions: The caller has checked that the requested logical block
362 // is within the file's size. The dnode has complete information, i.e.
363 // fsw_ext4_dnode_read_info was called successfully on it.
364 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_extent: inode %d, block %d\n"), dno
->g
.dnode_id
, extent
->log_start
));
365 extent
->type
= FSW_EXTENT_TYPE_PHYSBLOCK
;
366 extent
->log_count
= 1;
368 if(dno
->raw
->i_flags
& 1 << EXT4_INODE_EXTENTS
)
370 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_extent: inode %d uses extents\n"), dno
->g
.dnode_id
));
371 return fsw_ext4_get_by_extent(vol
, dno
, extent
);
375 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_extent: inode %d uses direct/indirect block addressing\n"),
377 return fsw_ext4_get_by_blkaddr(vol
, dno
, extent
);
382 * New ext4 extents...
384 static fsw_status_t
fsw_ext4_get_by_extent(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
385 struct fsw_extent
*extent
)
388 fsw_u32 bno
, buf_offset
;
392 struct ext4_extent_header
*ext4_extent_header
;
393 struct ext4_extent_idx
*ext4_extent_idx
;
394 struct ext4_extent
*ext4_extent
;
396 // Logical block requested by core...
397 bno
= extent
->log_start
;
399 // First buffer is the i_block field from inode...
400 buffer
= (void *)dno
->raw
->i_block
;
403 ext4_extent_header
= (struct ext4_extent_header
*)((char *)buffer
+ buf_offset
);
404 buf_offset
+= sizeof(struct ext4_extent_header
);
405 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: extent header with %d entries\n"),
406 ext4_extent_header
->eh_entries
));
407 if(ext4_extent_header
->eh_magic
!= EXT4_EXT_MAGIC
)
408 return FSW_VOLUME_CORRUPTED
;
410 for(ext_cnt
= 0;ext_cnt
< ext4_extent_header
->eh_entries
;ext_cnt
++)
412 if(ext4_extent_header
->eh_depth
== 0)
414 // Leaf node, the header follows actual extents
415 ext4_extent
= (struct ext4_extent
*)((char *)buffer
+ buf_offset
);
416 buf_offset
+= sizeof(struct ext4_extent
);
417 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: extent node cover %d...\n"), ext4_extent
->ee_block
));
419 // Is the requested block in this extent?
420 if(bno
>= ext4_extent
->ee_block
&& bno
< ext4_extent
->ee_block
+ ext4_extent
->ee_len
)
422 extent
->phys_start
= ext4_extent
->ee_start_lo
+ (bno
- ext4_extent
->ee_block
);
423 extent
->log_count
= ext4_extent
->ee_len
- (bno
- ext4_extent
->ee_block
);
429 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: index extents, depth %d\n"),
430 ext4_extent_header
->eh_depth
));
431 ext4_extent_idx
= (struct ext4_extent_idx
*)((char *)buffer
+ buf_offset
);
432 buf_offset
+= sizeof(struct ext4_extent_idx
);
434 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_get_by_extent: index node covers block %d...\n"),
435 ext4_extent_idx
->ei_block
));
436 if(bno
>= ext4_extent_idx
->ei_block
)
438 // Follow extent tree...
439 status
= fsw_block_get(vol
, ext4_extent_idx
->ei_leaf_lo
, 1, (void **)&buffer
);
449 return FSW_NOT_FOUND
;
453 * The ext2/ext3 file system does not use extents, but stores a list of block numbers
454 * using the usual direct, indirect, double-indirect, triple-indirect scheme. To
455 * optimize access, this function checks if the following file blocks are mapped
456 * to consecutive disk blocks and returns a combined extent if possible.
458 static fsw_status_t
fsw_ext4_get_by_blkaddr(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
459 struct fsw_extent
*extent
)
462 fsw_u32 bno
, release_bno
, buf_bcnt
, file_bcnt
;
465 bno
= extent
->log_start
;
467 // try direct block pointers in the inode
468 if (bno
< EXT4_NDIR_BLOCKS
) {
472 bno
-= EXT4_NDIR_BLOCKS
;
474 // try indirect block
475 if (bno
< vol
->ind_bcnt
) {
476 path
[0] = EXT4_IND_BLOCK
;
480 bno
-= vol
->ind_bcnt
;
482 // try double-indirect block
483 if (bno
< vol
->dind_bcnt
) {
484 path
[0] = EXT4_DIND_BLOCK
;
485 path
[1] = bno
/ vol
->ind_bcnt
;
486 path
[2] = bno
% vol
->ind_bcnt
;
489 bno
-= vol
->dind_bcnt
;
491 // use the triple-indirect block
492 path
[0] = EXT4_TIND_BLOCK
;
493 path
[1] = bno
/ vol
->dind_bcnt
;
494 path
[2] = (bno
/ vol
->ind_bcnt
) % vol
->ind_bcnt
;
495 path
[3] = bno
% vol
->ind_bcnt
;
501 // follow the indirection path
502 buffer
= dno
->raw
->i_block
;
503 buf_bcnt
= EXT4_NDIR_BLOCKS
;
506 bno
= buffer
[path
[i
]];
508 extent
->type
= FSW_EXTENT_TYPE_SPARSE
;
510 fsw_block_release(vol
, release_bno
, buffer
);
517 fsw_block_release(vol
, release_bno
, buffer
);
518 status
= fsw_block_get(vol
, bno
, 1, (void **)&buffer
);
522 buf_bcnt
= vol
->ind_bcnt
;
524 extent
->phys_start
= bno
;
526 // check if the following blocks can be aggregated into one extent
527 file_bcnt
= (fsw_u32
)((dno
->g
.size
+ vol
->g
.log_blocksize
- 1) & (vol
->g
.log_blocksize
- 1));
528 while (path
[i
] + extent
->log_count
< buf_bcnt
&& // indirect block has more block pointers
529 extent
->log_start
+ extent
->log_count
< file_bcnt
) { // file has more blocks
530 if (buffer
[path
[i
] + extent
->log_count
] == buffer
[path
[i
] + extent
->log_count
- 1] + 1)
537 fsw_block_release(vol
, release_bno
, buffer
);
542 * Lookup a directory's child dnode by name. This function is called on a directory
543 * to retrieve the directory entry with the given name. A dnode is constructed for
544 * this entry and returned. The core makes sure that fsw_ext4_dnode_fill has been called
545 * and the dnode is actually a directory.
548 static fsw_status_t
fsw_ext4_dir_lookup(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
549 struct fsw_string
*lookup_name
, struct fsw_ext4_dnode
**child_dno_out
)
552 struct fsw_shandle shand
;
554 struct ext4_dir_entry entry
;
555 struct fsw_string entry_name
;
557 // Preconditions: The caller has checked that dno is a directory node.
559 entry_name
.type
= FSW_STRING_TYPE_ISO88591
;
561 // setup handle to read the directory
562 status
= fsw_shandle_open(dno
, &shand
);
566 // scan the directory for the file
568 while (child_ino
== 0) {
570 status
= fsw_ext4_read_dentry(&shand
, &entry
);
573 if (entry
.inode
== 0) {
574 // end of directory reached
575 status
= FSW_NOT_FOUND
;
580 entry_name
.len
= entry_name
.size
= entry
.name_len
;
581 entry_name
.data
= entry
.name
;
582 if (fsw_streq(lookup_name
, &entry_name
)) {
583 child_ino
= entry
.inode
;
588 // setup a dnode for the child item
589 status
= fsw_dnode_create(dno
, child_ino
, FSW_DNODE_TYPE_UNKNOWN
, &entry_name
, child_dno_out
);
592 fsw_shandle_close(&shand
);
597 * Get the next directory entry when reading a directory. This function is called during
598 * directory iteration to retrieve the next directory entry. A dnode is constructed for
599 * the entry and returned. The core makes sure that fsw_ext4_dnode_fill has been called
600 * and the dnode is actually a directory. The shandle provided by the caller is used to
601 * record the position in the directory between calls.
604 static fsw_status_t
fsw_ext4_dir_read(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
605 struct fsw_shandle
*shand
, struct fsw_ext4_dnode
**child_dno_out
)
608 struct ext4_dir_entry entry
;
609 struct fsw_string entry_name
;
611 // Preconditions: The caller has checked that dno is a directory node. The caller
612 // has opened a storage handle to the directory's storage and keeps it around between
614 FSW_MSG_DEBUG((FSW_MSGSTR("fsw_ext4_dir_read: started reading dir\n")));
618 status
= fsw_ext4_read_dentry(shand
, &entry
);
621 if (entry
.inode
== 0) // end of directory
622 return FSW_NOT_FOUND
;
625 if ((entry
.name_len
== 1 && entry
.name
[0] == '.') ||
626 (entry
.name_len
== 2 && entry
.name
[0] == '.' && entry
.name
[1] == '.'))
632 entry_name
.type
= FSW_STRING_TYPE_ISO88591
;
633 entry_name
.len
= entry_name
.size
= entry
.name_len
;
634 entry_name
.data
= entry
.name
;
636 // setup a dnode for the child item
637 status
= fsw_dnode_create(dno
, entry
.inode
, FSW_DNODE_TYPE_UNKNOWN
, &entry_name
, child_dno_out
);
643 * Read a directory entry from the directory's raw data. This internal function is used
644 * to read a raw ext2 directory entry into memory. The shandle's position pointer is adjusted
645 * to point to the next entry.
648 static fsw_status_t
fsw_ext4_read_dentry(struct fsw_shandle
*shand
, struct ext4_dir_entry
*entry
)
654 // read dir_entry header (fixed length)
656 status
= fsw_shandle_read(shand
, &buffer_size
, entry
);
660 if (buffer_size
< 8 || entry
->rec_len
== 0) {
661 // end of directory reached
665 if (entry
->rec_len
< 8)
666 return FSW_VOLUME_CORRUPTED
;
667 if (entry
->inode
!= 0) {
668 // this entry is used
669 if (entry
->rec_len
< 8 + entry
->name_len
)
670 return FSW_VOLUME_CORRUPTED
;
674 // valid, but unused entry, skip it
675 shand
->pos
+= entry
->rec_len
- 8;
678 // read file name (variable length)
679 buffer_size
= entry
->name_len
;
680 status
= fsw_shandle_read(shand
, &buffer_size
, entry
->name
);
683 if (buffer_size
< entry
->name_len
)
684 return FSW_VOLUME_CORRUPTED
;
686 // skip any remaining padding
687 shand
->pos
+= entry
->rec_len
- (8 + entry
->name_len
);
693 * Get the target path of a symbolic link. This function is called when a symbolic
694 * link needs to be resolved. The core makes sure that the fsw_ext4_dnode_fill has been
695 * called on the dnode and that it really is a symlink.
697 * For ext4, the target path can be stored inline in the inode structure (in the space
698 * otherwise occupied by the block pointers) or in the inode's data. There is no flag
699 * indicating this, only the number of blocks entry (i_blocks) can be used as an
700 * indication. The check used here comes from the Linux kernel.
703 static fsw_status_t
fsw_ext4_readlink(struct fsw_ext4_volume
*vol
, struct fsw_ext4_dnode
*dno
,
704 struct fsw_string
*link_target
)
710 if (dno
->g
.size
> FSW_PATH_MAX
)
711 return FSW_VOLUME_CORRUPTED
;
713 /* Linux kernels ext4_inode_is_fast_symlink... */
714 ea_blocks
= dno
->raw
->i_file_acl_lo
? (vol
->g
.log_blocksize
>> 9) : 0;
716 if (dno
->raw
->i_blocks_lo
- ea_blocks
== 0) {
717 // "fast" symlink, path is stored inside the inode
718 s
.type
= FSW_STRING_TYPE_ISO88591
;
719 s
.size
= s
.len
= (int)dno
->g
.size
;
720 s
.data
= dno
->raw
->i_block
;
721 status
= fsw_strdup_coerce(link_target
, vol
->g
.host_string_type
, &s
);
723 // "slow" symlink, path is stored in normal inode data
724 status
= fsw_dnode_readlink_data(dno
, link_target
);