VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/fs/extvfs.cpp@ 96407

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

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 108.7 KB
Line 
1/* $Id: extvfs.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT - Ext2/3/4 Virtual Filesystem.
4 */
5
6/*
7 * Copyright (C) 2012-2022 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
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_FS
42#include <iprt/fsvfs.h>
43
44#include <iprt/asm.h>
45#include <iprt/assert.h>
46#include <iprt/avl.h>
47#include <iprt/file.h>
48#include <iprt/err.h>
49#include <iprt/list.h>
50#include <iprt/log.h>
51#include <iprt/mem.h>
52#include <iprt/string.h>
53#include <iprt/vfs.h>
54#include <iprt/vfslowlevel.h>
55#include <iprt/formats/ext.h>
56
57
58/*********************************************************************************************************************************
59* Defined Constants And Macros *
60*********************************************************************************************************************************/
61/** The maximum block group cache size (in bytes). */
62#if ARCH_BITS >= 64
63# define RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE _512K
64#else
65# define RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE _128K
66#endif
67/** The maximum inode cache size (in bytes). */
68#if ARCH_BITS >= 64
69# define RTFSEXT_MAX_INODE_CACHE_SIZE _512K
70#else
71# define RTFSEXT_MAX_INODE_CACHE_SIZE _128K
72#endif
73/** The maximum extent/block map cache size (in bytes). */
74#if ARCH_BITS >= 64
75# define RTFSEXT_MAX_BLOCK_CACHE_SIZE _512K
76#else
77# define RTFSEXT_MAX_BLOCK_CACHE_SIZE _128K
78#endif
79
80/** All supported incompatible features. */
81#define RTFSEXT_INCOMPAT_FEATURES_SUPP ( EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE | EXT_SB_FEAT_INCOMPAT_EXTENTS | EXT_SB_FEAT_INCOMPAT_64BIT \
82 | EXT_SB_FEAT_INCOMPAT_FLEX_BG)
83
84
85/*********************************************************************************************************************************
86* Structures and Typedefs *
87*********************************************************************************************************************************/
88/** Pointer to the ext filesystem data. */
89typedef struct RTFSEXTVOL *PRTFSEXTVOL;
90
91
92/**
93 * Cached block group descriptor data.
94 */
95typedef struct RTFSEXTBLKGRP
96{
97 /** AVL tree node, indexed by the block group number. */
98 AVLU32NODECORE Core;
99 /** List node for the LRU list used for eviction. */
100 RTLISTNODE NdLru;
101 /** Reference counter. */
102 volatile uint32_t cRefs;
103 /** Block number where the inode table is store. */
104 uint64_t iBlockInodeTbl;
105 /** Pointer to the inode bitmap. */
106 uint8_t *pabInodeBitmap;
107 /** Block bitmap - variable in size (depends on the block size
108 * and number of blocks per group). */
109 uint8_t abBlockBitmap[1];
110} RTFSEXTBLKGRP;
111/** Pointer to block group descriptor data. */
112typedef RTFSEXTBLKGRP *PRTFSEXTBLKGRP;
113
114
115/**
116 * In-memory inode.
117 */
118typedef struct RTFSEXTINODE
119{
120 /** AVL tree node, indexed by the inode number. */
121 AVLU32NODECORE Core;
122 /** List node for the inode LRU list used for eviction. */
123 RTLISTNODE NdLru;
124 /** Reference counter. */
125 volatile uint32_t cRefs;
126 /** Byte offset in the backing file where the inode is stored.. */
127 uint64_t offInode;
128 /** Inode data. */
129 RTFSOBJINFO ObjInfo;
130 /** Inode flags (copied from the on disk inode). */
131 uint32_t fFlags;
132 /** Copy of the block map/extent tree. */
133 uint32_t aiBlocks[EXT_INODE_BLOCK_ENTRIES];
134} RTFSEXTINODE;
135/** Pointer to an in-memory inode. */
136typedef RTFSEXTINODE *PRTFSEXTINODE;
137
138
139/**
140 * Block cache entry.
141 */
142typedef struct RTFSEXTBLOCKENTRY
143{
144 /** AVL tree node, indexed by the filesystem block number. */
145 AVLU64NODECORE Core;
146 /** List node for the inode LRU list used for eviction. */
147 RTLISTNODE NdLru;
148 /** Reference counter. */
149 volatile uint32_t cRefs;
150 /** The block data. */
151 uint8_t abData[1];
152} RTFSEXTBLOCKENTRY;
153/** Pointer to a block cache entry. */
154typedef RTFSEXTBLOCKENTRY *PRTFSEXTBLOCKENTRY;
155
156
157/**
158 * Open directory instance.
159 */
160typedef struct RTFSEXTDIR
161{
162 /** Volume this directory belongs to. */
163 PRTFSEXTVOL pVol;
164 /** The underlying inode structure. */
165 PRTFSEXTINODE pInode;
166 /** Set if we've reached the end of the directory enumeration. */
167 bool fNoMoreFiles;
168 /** Current offset into the directory where the next entry should be read. */
169 uint64_t offEntry;
170 /** Next entry index (for logging purposes). */
171 uint32_t idxEntry;
172} RTFSEXTDIR;
173/** Pointer to an open directory instance. */
174typedef RTFSEXTDIR *PRTFSEXTDIR;
175
176
177/**
178 * Open file instance.
179 */
180typedef struct RTFSEXTFILE
181{
182 /** Volume this directory belongs to. */
183 PRTFSEXTVOL pVol;
184 /** The underlying inode structure. */
185 PRTFSEXTINODE pInode;
186 /** Current offset into the file for I/O. */
187 RTFOFF offFile;
188} RTFSEXTFILE;
189/** Pointer to an open file instance. */
190typedef RTFSEXTFILE *PRTFSEXTFILE;
191
192
193/**
194 * Ext2/3/4 filesystem volume.
195 */
196typedef struct RTFSEXTVOL
197{
198 /** Handle to itself. */
199 RTVFS hVfsSelf;
200 /** The file, partition, or whatever backing the ext volume. */
201 RTVFSFILE hVfsBacking;
202 /** The size of the backing thingy. */
203 uint64_t cbBacking;
204
205 /** RTVFSMNT_F_XXX. */
206 uint32_t fMntFlags;
207 /** RTFSEXTVFS_F_XXX (currently none defined). */
208 uint32_t fExtFlags;
209
210 /** Flag whether the filesystem is 64bit. */
211 bool f64Bit;
212 /** Size of one block. */
213 size_t cbBlock;
214 /** Number of bits to shift left for fast conversion of block numbers to offsets. */
215 uint32_t cBlockShift;
216 /** Number of blocks in one group. */
217 uint32_t cBlocksPerGroup;
218 /** Number of inodes in each block group. */
219 uint32_t cInodesPerGroup;
220 /** Number of blocks groups in the volume. */
221 uint32_t cBlockGroups;
222 /** Size of the block bitmap. */
223 size_t cbBlockBitmap;
224 /** Size of the inode bitmap. */
225 size_t cbInodeBitmap;
226 /** Size of block group descriptor. */
227 size_t cbBlkGrpDesc;
228 /** Size of an inode. */
229 size_t cbInode;
230
231 /** Incompatible features selected for this filesystem. */
232 uint32_t fFeaturesIncompat;
233
234 /** @name Block group cache.
235 * @{ */
236 /** LRU list anchor. */
237 RTLISTANCHOR LstBlockGroupLru;
238 /** Root of the cached block group tree. */
239 AVLU32TREE BlockGroupRoot;
240 /** Size of the cached block groups. */
241 size_t cbBlockGroups;
242 /** @} */
243
244 /** @name Inode cache.
245 * @{ */
246 /** LRU list anchor for the inode cache. */
247 RTLISTANCHOR LstInodeLru;
248 /** Root of the cached inode tree. */
249 AVLU32TREE InodeRoot;
250 /** Size of the cached inodes. */
251 size_t cbInodes;
252 /** @} */
253
254 /** @name Block cache.
255 * @{ */
256 /** LRU list anchor for the block cache. */
257 RTLISTANCHOR LstBlockLru;
258 /** Root of the cached block tree. */
259 AVLU64TREE BlockRoot;
260 /** Size of cached blocks. */
261 size_t cbBlocks;
262 /** @} */
263} RTFSEXTVOL;
264
265
266
267/*********************************************************************************************************************************
268* Internal Functions *
269*********************************************************************************************************************************/
270static int rtFsExtVol_OpenDirByInode(PRTFSEXTVOL pThis, uint32_t iInode, PRTVFSDIR phVfsDir);
271
272#ifdef LOG_ENABLED
273/**
274 * Logs the ext filesystem superblock.
275 *
276 * @returns nothing.
277 * @param pSb Pointer to the superblock.
278 */
279static void rtFsExtSb_Log(PCEXTSUPERBLOCK pSb)
280{
281 if (LogIs2Enabled())
282 {
283 RTTIMESPEC Spec;
284 char sz[80];
285
286 Log2(("EXT: Superblock:\n"));
287 Log2(("EXT: cInodesTotal %RU32\n", RT_LE2H_U32(pSb->cInodesTotal)));
288 Log2(("EXT: cBlocksTotalLow %RU32\n", RT_LE2H_U32(pSb->cBlocksTotalLow)));
289 Log2(("EXT: cBlocksRsvdForSuperUserLow %RU32\n", RT_LE2H_U32(pSb->cBlocksRsvdForSuperUserLow)));
290 Log2(("EXT: cBlocksFreeLow %RU32\n", RT_LE2H_U32(pSb->cBlocksFreeLow)));
291 Log2(("EXT: cInodesFree %RU32\n", RT_LE2H_U32(pSb->cInodesFree)));
292 Log2(("EXT: iBlockOfSuperblock %RU32\n", RT_LE2H_U32(pSb->iBlockOfSuperblock)));
293 Log2(("EXT: cLogBlockSize %RU32\n", RT_LE2H_U32(pSb->cLogBlockSize)));
294 Log2(("EXT: cLogClusterSize %RU32\n", RT_LE2H_U32(pSb->cLogClusterSize)));
295 Log2(("EXT: cBlocksPerGroup %RU32\n", RT_LE2H_U32(pSb->cBlocksPerGroup)));
296 Log2(("EXT: cClustersPerBlockGroup %RU32\n", RT_LE2H_U32(pSb->cClustersPerBlockGroup)));
297 Log2(("EXT: cInodesPerBlockGroup %RU32\n", RT_LE2H_U32(pSb->cInodesPerBlockGroup)));
298 Log2(("EXT: u32LastMountTime %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastMountTime),
299 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastMountTime)), sz, sizeof(sz))));
300 Log2(("EXT: u32LastWrittenTime %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastWrittenTime),
301 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastWrittenTime)), sz, sizeof(sz))));
302 Log2(("EXT: cMountsSinceLastCheck %RU16\n", RT_LE2H_U32(pSb->cMountsSinceLastCheck)));
303 Log2(("EXT: cMaxMountsUntilCheck %RU16\n", RT_LE2H_U32(pSb->cMaxMountsUntilCheck)));
304 Log2(("EXT: u16Signature %#RX16\n", RT_LE2H_U32(pSb->u16Signature)));
305 Log2(("EXT: u16FilesystemState %#RX16\n", RT_LE2H_U32(pSb->u16FilesystemState)));
306 Log2(("EXT: u16ActionOnError %#RX16\n", RT_LE2H_U32(pSb->u16ActionOnError)));
307 Log2(("EXT: u16RevLvlMinor %#RX16\n", RT_LE2H_U32(pSb->u16RevLvlMinor)));
308 Log2(("EXT: u32LastCheckTime %#RX32 %s\n", RT_LE2H_U32(pSb->u32LastCheckTime),
309 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32LastCheckTime)), sz, sizeof(sz))));
310 Log2(("EXT: u32CheckInterval %RU32\n", RT_LE2H_U32(pSb->u32CheckInterval)));
311 Log2(("EXT: u32OsIdCreator %#RX32\n", RT_LE2H_U32(pSb->u32OsIdCreator)));
312 Log2(("EXT: u32RevLvl %#RX32\n", RT_LE2H_U32(pSb->u32RevLvl)));
313 Log2(("EXT: u16UidReservedBlocks %#RX16\n", RT_LE2H_U32(pSb->u16UidReservedBlocks)));
314 Log2(("EXT: u16GidReservedBlocks %#RX16\n", RT_LE2H_U32(pSb->u16GidReservedBlocks)));
315 if (RT_LE2H_U32(pSb->u32RevLvl) == EXT_SB_REV_V2_DYN_INODE_SZ)
316 {
317 Log2(("EXT: iFirstInodeNonRsvd %#RX32\n", RT_LE2H_U32(pSb->iFirstInodeNonRsvd)));
318 Log2(("EXT: cbInode %#RX16\n", RT_LE2H_U32(pSb->cbInode)));
319 Log2(("EXT: iBlkGrpSb %#RX16\n", RT_LE2H_U32(pSb->iBlkGrpSb)));
320 Log2(("EXT: fFeaturesCompat %#RX32%s%s%s%s%s%s%s%s%s%s\n", RT_LE2H_U32(pSb->fFeaturesCompat),
321 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_DIR_PREALLOC ? " dir-prealloc" : "",
322 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_IMAGIC_INODES ? " imagic-inode" : "",
323 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_HAS_JOURNAL ? " has-journal" : "",
324 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXT_ATTR ? " ext-attrs" : "",
325 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_RESIZE_INODE ? " resize-inode" : "",
326 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_DIR_INDEX ? " dir-index" : "",
327 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_LAZY_BG ? " lazy-bg" : "",
328 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXCLUDE_INODE ? " excl-inode" : "",
329 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_EXCLUDE_BITMAP ? " excl-bitmap" : "",
330 RT_LE2H_U32(pSb->fFeaturesCompat) & EXT_SB_FEAT_COMPAT_SPARSE_SUPER2 ? " sparse-super2" : ""));
331 Log2(("EXT: fFeaturesIncompat %#RX32%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", RT_LE2H_U32(pSb->fFeaturesIncompat),
332 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_COMPRESSION ? " compression" : "",
333 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE ? " dir-filetype" : "",
334 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_RECOVER ? " recovery" : "",
335 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_JOURNAL_DEV ? " journal-dev" : "",
336 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_META_BG ? " meta-bg" : "",
337 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_EXTENTS ? " extents" : "",
338 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_64BIT ? " 64bit" : "",
339 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_MMP ? " mmp" : "",
340 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_FLEX_BG ? " flex-bg" : "",
341 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_EXT_ATTR_INODE ? " extattr-inode" : "",
342 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_DIRDATA ? " dir-data" : "",
343 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_CSUM_SEED ? " csum-seed" : "",
344 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_LARGE_DIR ? " large-dir" : "",
345 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_INLINE_DATA ? " inline-data" : "",
346 RT_LE2H_U32(pSb->fFeaturesIncompat) & EXT_SB_FEAT_INCOMPAT_ENCRYPT ? " encrypt" : ""));
347 Log2(("EXT: fFeaturesCompatRo %#RX32%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", RT_LE2H_U32(pSb->fFeaturesCompatRo),
348 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_SPARSE_SUPER ? " sparse-super" : "",
349 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_LARGE_FILE ? " large-file" : "",
350 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_BTREE_DIR ? " btree-dir" : "",
351 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_HUGE_FILE ? " huge-file" : "",
352 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_GDT_CHSKUM ? " gdt-chksum" : "",
353 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_DIR_NLINK ? " dir-nlink" : "",
354 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_EXTRA_INODE_SZ ? " extra-inode" : "",
355 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_HAS_SNAPSHOTS ? " snapshots" : "",
356 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_QUOTA ? " quota" : "",
357 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_BIGALLOC ? " big-alloc" : "",
358 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_METADATA_CHKSUM ? " meta-chksum" : "",
359 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_REPLICA ? " replica" : "",
360 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_READONLY ? " ro" : "",
361 RT_LE2H_U32(pSb->fFeaturesCompatRo) & EXT_SB_FEAT_COMPAT_RO_PROJECT ? " project" : ""));
362 Log2(("EXT: au8Uuid <todo>\n"));
363 Log2(("EXT: achVolumeName %16s\n", &pSb->achVolumeName[0]));
364 Log2(("EXT: achLastMounted %64s\n", &pSb->achLastMounted[0]));
365 Log2(("EXT: u32AlgoUsageBitmap %#RX32\n", RT_LE2H_U32(pSb->u32AlgoUsageBitmap)));
366 Log2(("EXT: cBlocksPrealloc %RU8\n", pSb->cBlocksPrealloc));
367 Log2(("EXT: cBlocksPreallocDirectory %RU8\n", pSb->cBlocksPreallocDirectory));
368 Log2(("EXT: cGdtEntriesRsvd %RU16\n", pSb->cGdtEntriesRsvd));
369 Log2(("EXT: au8JournalUuid <todo>\n"));
370 Log2(("EXT: iJournalInode %#RX32\n", RT_LE2H_U32(pSb->iJournalInode)));
371 Log2(("EXT: u32JournalDev %#RX32\n", RT_LE2H_U32(pSb->u32JournalDev)));
372 Log2(("EXT: u32LastOrphan %#RX32\n", RT_LE2H_U32(pSb->u32LastOrphan)));
373 Log2(("EXT: au32HashSeedHtree[0] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[0])));
374 Log2(("EXT: au32HashSeedHtree[1] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[1])));
375 Log2(("EXT: au32HashSeedHtree[2] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[2])));
376 Log2(("EXT: au32HashSeedHtree[3] %#RX32\n", RT_LE2H_U32(pSb->au32HashSeedHtree[3])));
377 Log2(("EXT: u8HashVersionDef %#RX8\n", pSb->u8HashVersionDef));
378 Log2(("EXT: u8JnlBackupType %#RX8\n", pSb->u8JnlBackupType));
379 Log2(("EXT: cbGroupDesc %RU16\n", RT_LE2H_U16(pSb->cbGroupDesc)));
380 Log2(("EXT: fMntOptsDef %#RX32\n", RT_LE2H_U32(pSb->fMntOptsDef)));
381 Log2(("EXT: iFirstMetaBg %#RX32\n", RT_LE2H_U32(pSb->iFirstMetaBg)));
382 Log2(("EXT: u32TimeFsCreation %#RX32 %s\n", RT_LE2H_U32(pSb->u32TimeFsCreation),
383 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pSb->u32TimeFsCreation)), sz, sizeof(sz))));
384 for (unsigned i = 0; i < RT_ELEMENTS(pSb->au32JnlBlocks); i++)
385 Log2(("EXT: au32JnlBlocks[%u] %#RX32\n", i, RT_LE2H_U32(pSb->au32JnlBlocks[i])));
386 Log2(("EXT: cBlocksTotalHigh %#RX32\n", RT_LE2H_U32(pSb->cBlocksTotalHigh)));
387 Log2(("EXT: cBlocksRsvdForSuperUserHigh %#RX32\n", RT_LE2H_U32(pSb->cBlocksRsvdForSuperUserHigh)));
388 Log2(("EXT: cBlocksFreeHigh %#RX32\n", RT_LE2H_U32(pSb->cBlocksFreeHigh)));
389 Log2(("EXT: cbInodesExtraMin %#RX16\n", RT_LE2H_U16(pSb->cbInodesExtraMin)));
390 Log2(("EXT: cbNewInodesRsv %#RX16\n", RT_LE2H_U16(pSb->cbInodesExtraMin)));
391 Log2(("EXT: fFlags %#RX32\n", RT_LE2H_U32(pSb->fFlags)));
392 Log2(("EXT: cRaidStride %RU16\n", RT_LE2H_U16(pSb->cRaidStride)));
393 Log2(("EXT: cSecMmpInterval %RU16\n", RT_LE2H_U16(pSb->cSecMmpInterval)));
394 Log2(("EXT: iMmpBlock %#RX64\n", RT_LE2H_U64(pSb->iMmpBlock)));
395 Log2(("EXT: cRaidStrideWidth %#RX32\n", RT_LE2H_U32(pSb->cRaidStrideWidth)));
396 Log2(("EXT: cLogGroupsPerFlex %RU8\n", pSb->cLogGroupsPerFlex));
397 Log2(("EXT: u8ChksumType %RX8\n", pSb->u8ChksumType));
398 Log2(("EXT: cKbWritten %#RX64\n", RT_LE2H_U64(pSb->cKbWritten)));
399 Log2(("EXT: iSnapshotInode %#RX32\n", RT_LE2H_U32(pSb->iSnapshotInode)));
400 Log2(("EXT: iSnapshotId %#RX32\n", RT_LE2H_U32(pSb->iSnapshotId)));
401 Log2(("EXT: cSnapshotRsvdBlocks %#RX64\n", RT_LE2H_U64(pSb->cSnapshotRsvdBlocks)));
402 Log2(("EXT: iSnapshotListInode %#RX32\n", RT_LE2H_U32(pSb->iSnapshotListInode)));
403 Log2(("EXT: cErrorsSeen %#RX32\n", RT_LE2H_U32(pSb->cErrorsSeen)));
404 Log2(("EXT: [...]\n")); /** @todo Missing fields if becoming interesting. */
405 Log2(("EXT: iInodeLostFound %#RX32\n", RT_LE2H_U32(pSb->iInodeLostFound)));
406 Log2(("EXT: iInodeProjQuota %#RX32\n", RT_LE2H_U32(pSb->iInodeProjQuota)));
407 Log2(("EXT: u32ChksumSeed %#RX32\n", RT_LE2H_U32(pSb->u32ChksumSeed)));
408 Log2(("EXT: [...]\n")); /** @todo Missing fields if becoming interesting. */
409 Log2(("EXT: u32Chksum %#RX32\n", RT_LE2H_U32(pSb->u32Chksum)));
410 }
411 }
412}
413
414
415/**
416 * Logs a ext filesystem block group descriptor.
417 *
418 * @returns nothing.
419 * @param pThis The ext volume instance.
420 * @param iBlockGroup Block group number.
421 * @param pBlockGroup Pointer to the block group.
422 */
423static void rtFsExtBlockGroup_Log(PRTFSEXTVOL pThis, uint32_t iBlockGroup, PCEXTBLOCKGROUPDESC pBlockGroup)
424{
425 if (LogIs2Enabled())
426 {
427 uint64_t iBlockStart = (uint64_t)iBlockGroup * pThis->cBlocksPerGroup;
428 Log2(("EXT: Block group %#RX32 (blocks %#RX64 to %#RX64):\n",
429 iBlockGroup, iBlockStart, iBlockStart + pThis->cBlocksPerGroup - 1));
430 Log2(("EXT: offBlockBitmapLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offBlockBitmapLow)));
431 Log2(("EXT: offInodeBitmapLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offInodeBitmapLow)));
432 Log2(("EXT: offInodeTableLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offInodeTableLow)));
433 Log2(("EXT: cBlocksFreeLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cBlocksFreeLow)));
434 Log2(("EXT: cInodesFreeLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cInodesFreeLow)));
435 Log2(("EXT: cDirectoriesLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cDirectoriesLow)));
436 Log2(("EXT: fFlags %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.fFlags)));
437 Log2(("EXT: offSnapshotExclBitmapLow %#RX32\n", RT_LE2H_U32(pBlockGroup->v32.offSnapshotExclBitmapLow)));
438 Log2(("EXT: u16ChksumBlockBitmapLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16ChksumBlockBitmapLow)));
439 Log2(("EXT: u16ChksumInodeBitmapLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16ChksumInodeBitmapLow)));
440 Log2(("EXT: cInodeTblUnusedLow %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.cInodeTblUnusedLow)));
441 Log2(("EXT: u16Chksum %#RX16\n", RT_LE2H_U16(pBlockGroup->v32.u16Chksum)));
442 if (pThis->cbBlkGrpDesc == sizeof(EXTBLOCKGROUPDESC64))
443 {
444 Log2(("EXT: offBlockBitmapHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offBlockBitmapHigh)));
445 Log2(("EXT: offInodeBitmapHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offInodeBitmapHigh)));
446 Log2(("EXT: offInodeTableHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offInodeTableHigh)));
447 Log2(("EXT: cBlocksFreeHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cBlocksFreeHigh)));
448 Log2(("EXT: cInodesFreeHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cInodesFreeHigh)));
449 Log2(("EXT: cDirectoriesHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cDirectoriesHigh)));
450 Log2(("EXT: cInodeTblUnusedHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.cInodeTblUnusedHigh)));
451 Log2(("EXT: offSnapshotExclBitmapHigh %#RX32\n", RT_LE2H_U32(pBlockGroup->v64.offSnapshotExclBitmapHigh)));
452 Log2(("EXT: u16ChksumBlockBitmapHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.u16ChksumBlockBitmapHigh)));
453 Log2(("EXT: u16ChksumInodeBitmapHigh %#RX16\n", RT_LE2H_U16(pBlockGroup->v64.u16ChksumInodeBitmapHigh)));
454 }
455 }
456}
457
458
459/**
460 * Logs a ext filesystem inode.
461 *
462 * @returns nothing.
463 * @param pThis The ext volume instance.
464 * @param iInode Inode number.
465 * @param pInode Pointer to the inode.
466 */
467static void rtFsExtInode_Log(PRTFSEXTVOL pThis, uint32_t iInode, PCEXTINODECOMB pInode)
468{
469 if (LogIs2Enabled())
470 {
471 RTTIMESPEC Spec;
472 char sz[80];
473
474 Log2(("EXT: Inode %#RX32:\n", iInode));
475 Log2(("EXT: fMode %#RX16\n", RT_LE2H_U16(pInode->Core.fMode)));
476 Log2(("EXT: uUidLow %#RX16\n", RT_LE2H_U16(pInode->Core.uUidLow)));
477 Log2(("EXT: cbSizeLow %#RX32\n", RT_LE2H_U32(pInode->Core.cbSizeLow)));
478 Log2(("EXT: u32TimeLastAccess %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastAccess),
479 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastAccess)), sz, sizeof(sz))));
480 Log2(("EXT: u32TimeLastChange %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastChange),
481 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastChange)), sz, sizeof(sz))));
482 Log2(("EXT: u32TimeLastModification %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeLastModification),
483 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeLastModification)), sz, sizeof(sz))));
484 Log2(("EXT: u32TimeDeletion %#RX32 %s\n", RT_LE2H_U32(pInode->Core.u32TimeDeletion),
485 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Core.u32TimeDeletion)), sz, sizeof(sz))));
486 Log2(("EXT: uGidLow %#RX16\n", RT_LE2H_U16(pInode->Core.uGidLow)));
487 Log2(("EXT: cHardLinks %#RU16\n", RT_LE2H_U16(pInode->Core.cHardLinks)));
488 Log2(("EXT: cBlocksLow %#RX32\n", RT_LE2H_U32(pInode->Core.cBlocksLow)));
489 Log2(("EXT: fFlags %#RX32\n", RT_LE2H_U32(pInode->Core.fFlags)));
490 Log2(("EXT: Osd1.u32LnxVersion %#RX32\n", RT_LE2H_U32(pInode->Core.Osd1.u32LnxVersion)));
491 for (unsigned i = 0; i < RT_ELEMENTS(pInode->Core.au32Block); i++)
492 Log2(("EXT: au32Block[%u] %#RX32\n", i, RT_LE2H_U32(pInode->Core.au32Block[i])));
493 Log2(("EXT: u32Version %#RX32\n", RT_LE2H_U32(pInode->Core.u32Version)));
494 Log2(("EXT: offExtAttrLow %#RX32\n", RT_LE2H_U32(pInode->Core.offExtAttrLow)));
495 Log2(("EXT: cbSizeHigh %#RX32\n", RT_LE2H_U32(pInode->Core.cbSizeHigh)));
496 Log2(("EXT: u32FragmentAddrObs %#RX32\n", RT_LE2H_U32(pInode->Core.u32FragmentAddrObs)));
497 Log2(("EXT: Osd2.Lnx.cBlocksHigh %#RX32\n", RT_LE2H_U32(pInode->Core.Osd2.Lnx.cBlocksHigh)));
498 Log2(("EXT: Osd2.Lnx.offExtAttrHigh %#RX32\n", RT_LE2H_U32(pInode->Core.Osd2.Lnx.offExtAttrHigh)));
499 Log2(("EXT: Osd2.Lnx.uUidHigh %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.uUidHigh)));
500 Log2(("EXT: Osd2.Lnx.uGidHigh %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.uGidHigh)));
501 Log2(("EXT: Osd2.Lnx.u16ChksumLow %#RX16\n", RT_LE2H_U16(pInode->Core.Osd2.Lnx.u16ChksumLow)));
502
503 if (pThis->cbInode >= sizeof(EXTINODECOMB))
504 {
505 Log2(("EXT: cbInodeExtra %#RU16\n", RT_LE2H_U16(pInode->Extra.cbInodeExtra)));
506 Log2(("EXT: u16ChksumHigh %#RX16\n", RT_LE2H_U16(pInode->Extra.u16ChksumHigh)));
507 Log2(("EXT: u32ExtraTimeLastChange %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastChange)));
508 Log2(("EXT: u32ExtraTimeLastModification %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastModification)));
509 Log2(("EXT: u32ExtraTimeLastAccess %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeLastAccess)));
510 Log2(("EXT: u32TimeCreation %#RX32 %s\n", RT_LE2H_U32(pInode->Extra.u32TimeCreation),
511 RTTimeSpecToString(RTTimeSpecSetSeconds(&Spec, RT_LE2H_U32(pInode->Extra.u32TimeCreation)), sz, sizeof(sz))));
512 Log2(("EXT: u32ExtraTimeCreation %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ExtraTimeCreation)));
513 Log2(("EXT: u32VersionHigh %#RX32\n", RT_LE2H_U16(pInode->Extra.u32VersionHigh)));
514 Log2(("EXT: u32ProjectId %#RX32\n", RT_LE2H_U16(pInode->Extra.u32ProjectId)));
515 }
516 }
517}
518
519
520/**
521 * Logs a ext filesystem directory entry.
522 *
523 * @returns nothing.
524 * @param pThis The ext volume instance.
525 * @param idxDirEntry Directory entry index number.
526 * @param pDirEntry The directory entry.
527 */
528static void rtFsExtDirEntry_Log(PRTFSEXTVOL pThis, uint32_t idxDirEntry, PCEXTDIRENTRYEX pDirEntry)
529{
530 if (LogIs2Enabled())
531 {
532 int cbName = 0;
533
534 Log2(("EXT: Directory entry %#RX32:\n", idxDirEntry));
535 Log2(("EXT: iInodeRef %#RX32\n", RT_LE2H_U32(pDirEntry->Core.iInodeRef)));
536 Log2(("EXT: cbRecord %#RX32\n", RT_LE2H_U32(pDirEntry->Core.cbRecord)));
537 if (pThis->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE)
538 {
539 Log2(("EXT: cbName %#RU8\n", pDirEntry->Core.u.v2.cbName));
540 Log2(("EXT: uType %#RX8\n", pDirEntry->Core.u.v2.uType));
541 cbName = pDirEntry->Core.u.v2.cbName;
542 }
543 else
544 {
545 Log2(("EXT: cbName %#RU16\n", RT_LE2H_U16(pDirEntry->Core.u.v1.cbName)));
546 cbName = RT_LE2H_U16(pDirEntry->Core.u.v1.cbName);
547 }
548 Log2(("EXT: achName %*s\n", cbName, &pDirEntry->Core.achName[0]));
549 }
550}
551
552
553/**
554 * Logs an extent header.
555 *
556 * @returns nothing.
557 * @param pExtentHdr The extent header node.
558 */
559static void rtFsExtExtentHdr_Log(PCEXTEXTENTHDR pExtentHdr)
560{
561 if (LogIs2Enabled())
562 {
563 Log2(("EXT: Extent header:\n"));
564 Log2(("EXT: u16Magic %#RX16\n", RT_LE2H_U32(pExtentHdr->u16Magic)));
565 Log2(("EXT: cEntries %#RX16\n", RT_LE2H_U32(pExtentHdr->cEntries)));
566 Log2(("EXT: cMax %#RX16\n", RT_LE2H_U32(pExtentHdr->cMax)));
567 Log2(("EXT: uDepth %#RX16\n", RT_LE2H_U32(pExtentHdr->uDepth)));
568 Log2(("EXT: cGeneration %#RX32\n", RT_LE2H_U32(pExtentHdr->cGeneration)));
569 }
570}
571
572
573/**
574 * Logs an extent index node.
575 *
576 * @returns nothing.
577 * @param pExtentIdx The extent index node.
578 */
579static void rtFsExtExtentIdx_Log(PCEXTEXTENTIDX pExtentIdx)
580{
581 if (LogIs2Enabled())
582 {
583 Log2(("EXT: Extent index node:\n"));
584 Log2(("EXT: iBlock %#RX32\n", RT_LE2H_U32(pExtentIdx->iBlock)));
585 Log2(("EXT: offChildLow %#RX32\n", RT_LE2H_U32(pExtentIdx->offChildLow)));
586 Log2(("EXT: offChildHigh %#RX16\n", RT_LE2H_U16(pExtentIdx->offChildHigh)));
587 }
588}
589
590
591/**
592 * Logs an extent.
593 *
594 * @returns nothing.
595 * @param pExtent The extent.
596 */
597static void rtFsExtExtent_Log(PCEXTEXTENT pExtent)
598{
599 if (LogIs2Enabled())
600 {
601 Log2(("EXT: Extent:\n"));
602 Log2(("EXT: iBlock %#RX32\n", RT_LE2H_U32(pExtent->iBlock)));
603 Log2(("EXT: cBlocks %#RX16\n", RT_LE2H_U16(pExtent->cBlocks)));
604 Log2(("EXT: offStartHigh %#RX16\n", RT_LE2H_U32(pExtent->offStartHigh)));
605 Log2(("EXT: offStartLow %#RX16\n", RT_LE2H_U16(pExtent->offStartLow)));
606 }
607}
608#endif
609
610
611/**
612 * Converts a block number to a byte offset.
613 *
614 * @returns Offset in bytes for the given block number.
615 * @param pThis The ext volume instance.
616 * @param iBlock The block number to convert.
617 */
618DECLINLINE(uint64_t) rtFsExtBlockIdxToDiskOffset(PRTFSEXTVOL pThis, uint64_t iBlock)
619{
620 return iBlock << pThis->cBlockShift;
621}
622
623
624/**
625 * Converts a byte offset to a block number.
626 *
627 * @returns Block number.
628 * @param pThis The ext volume instance.
629 * @param iBlock The offset to convert.
630 */
631DECLINLINE(uint64_t) rtFsExtDiskOffsetToBlockIdx(PRTFSEXTVOL pThis, uint64_t off)
632{
633 return off >> pThis->cBlockShift;
634}
635
636
637/**
638 * Creates the proper block number from the given low and high parts in case a 64bit
639 * filesystem is used.
640 *
641 * @returns 64bit block number.
642 * @param pThis The ext volume instance.
643 * @param uLow The lower 32bit part.
644 * @param uHigh The upper 32bit part.
645 */
646DECLINLINE(uint64_t) rtFsExtBlockFromLowHigh(PRTFSEXTVOL pThis, uint32_t uLow, uint32_t uHigh)
647{
648 return pThis->f64Bit ? RT_MAKE_U64(uLow, uHigh): uLow;
649}
650
651
652/**
653 * Converts the given high and low parts of the block number to a byte offset.
654 *
655 * @returns Offset in bytes for the given block number.
656 * @param uLow The lower 32bit part of the block number.
657 * @param uHigh The upper 32bit part of the block number.
658 */
659DECLINLINE(uint64_t) rtFsExtBlockIdxLowHighToDiskOffset(PRTFSEXTVOL pThis, uint32_t uLow, uint32_t uHigh)
660{
661 uint64_t iBlock = rtFsExtBlockFromLowHigh(pThis, uLow, uHigh);
662 return rtFsExtBlockIdxToDiskOffset(pThis, iBlock);
663}
664
665
666/**
667 * Allocates a new block group.
668 *
669 * @returns Pointer to the new block group descriptor or NULL if out of memory.
670 * @param pThis The ext volume instance.
671 * @param cbAlloc How much to allocate.
672 * @param iBlockGroup Block group number.
673 */
674static PRTFSEXTBLOCKENTRY rtFsExtVol_BlockAlloc(PRTFSEXTVOL pThis, size_t cbAlloc, uint64_t iBlock)
675{
676 PRTFSEXTBLOCKENTRY pBlock = (PRTFSEXTBLOCKENTRY)RTMemAllocZ(cbAlloc);
677 if (RT_LIKELY(pBlock))
678 {
679 pBlock->Core.Key = iBlock;
680 pBlock->cRefs = 0;
681 pThis->cbBlocks += cbAlloc;
682 }
683
684 return pBlock;
685}
686
687
688/**
689 * Returns a new block entry utilizing the cache if possible.
690 *
691 * @returns Pointer to the new block entry or NULL if out of memory.
692 * @param pThis The ext volume instance.
693 * @param iBlock Block number.
694 */
695static PRTFSEXTBLOCKENTRY rtFsExtVol_BlockGetNew(PRTFSEXTVOL pThis, uint64_t iBlock)
696{
697 PRTFSEXTBLOCKENTRY pBlock = NULL;
698 size_t cbAlloc = RT_UOFFSETOF_DYN(RTFSEXTBLOCKENTRY, abData[pThis->cbBlock]);
699 if (pThis->cbBlocks + cbAlloc <= RTFSEXT_MAX_BLOCK_CACHE_SIZE)
700 pBlock = rtFsExtVol_BlockAlloc(pThis, cbAlloc, iBlock);
701 else
702 {
703 pBlock = RTListRemoveLast(&pThis->LstBlockLru, RTFSEXTBLOCKENTRY, NdLru);
704 if (!pBlock)
705 pBlock = rtFsExtVol_BlockAlloc(pThis, cbAlloc, iBlock);
706 else
707 {
708 /* Remove the block group from the tree because it gets a new key. */
709 PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->BlockRoot, pBlock->Core.Key);
710 Assert(pCore == &pBlock->Core); RT_NOREF(pCore);
711 }
712 }
713
714 Assert(!pBlock->cRefs);
715 pBlock->Core.Key = iBlock;
716 pBlock->cRefs = 1;
717
718 return pBlock;
719}
720
721
722/**
723 * Frees the given block.
724 *
725 * @returns nothing.
726 * @param pThis The ext volume instance.
727 * @param pBlock The block to free.
728 */
729static void rtFsExtVol_BlockFree(PRTFSEXTVOL pThis, PRTFSEXTBLOCKENTRY pBlock)
730{
731 Assert(!pBlock->cRefs);
732
733 /*
734 * Put it into the cache if the limit wasn't exceeded, otherwise the block group
735 * is freed right away.
736 */
737 if (pThis->cbBlocks <= RTFSEXT_MAX_BLOCK_CACHE_SIZE)
738 {
739 /* Put onto the LRU list. */
740 RTListPrepend(&pThis->LstBlockLru, &pBlock->NdLru);
741 }
742 else
743 {
744 /* Remove from the tree and free memory. */
745 PAVLU64NODECORE pCore = RTAvlU64Remove(&pThis->BlockRoot, pBlock->Core.Key);
746 Assert(pCore == &pBlock->Core); RT_NOREF(pCore);
747 RTMemFree(pBlock);
748 pThis->cbBlocks -= RT_UOFFSETOF_DYN(RTFSEXTBLOCKENTRY, abData[pThis->cbBlock]);
749 }
750}
751
752
753/**
754 * Gets the specified block data from the volume.
755 *
756 * @returns IPRT status code.
757 * @param pThis The ext volume instance.
758 * @param iBlock The filesystem block to load.
759 * @param ppBlock Where to return the pointer to the block entry on success.
760 * @param ppvData Where to return the pointer to the block data on success.
761 */
762static int rtFsExtVol_BlockLoad(PRTFSEXTVOL pThis, uint64_t iBlock, PRTFSEXTBLOCKENTRY *ppBlock, void **ppvData)
763{
764 int rc = VINF_SUCCESS;
765
766 /* Try to fetch the block group from the cache first. */
767 PRTFSEXTBLOCKENTRY pBlock = (PRTFSEXTBLOCKENTRY)RTAvlU64Get(&pThis->BlockRoot, iBlock);
768 if (!pBlock)
769 {
770 /* Slow path, load from disk. */
771 pBlock = rtFsExtVol_BlockGetNew(pThis, iBlock);
772 if (RT_LIKELY(pBlock))
773 {
774 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, iBlock);
775 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlock->abData[0], pThis->cbBlock, NULL);
776 if (RT_SUCCESS(rc))
777 {
778 bool fIns = RTAvlU64Insert(&pThis->BlockRoot, &pBlock->Core);
779 Assert(fIns); RT_NOREF(fIns);
780 }
781 }
782 else
783 rc = VERR_NO_MEMORY;
784 }
785 else
786 {
787 /* Remove from current LRU list position and add to the beginning. */
788 uint32_t cRefs = ASMAtomicIncU32(&pBlock->cRefs);
789 if (cRefs == 1) /* Blocks get removed from the LRU list if they are referenced. */
790 RTListNodeRemove(&pBlock->NdLru);
791 }
792
793 if (RT_SUCCESS(rc))
794 {
795 *ppBlock = pBlock;
796 *ppvData = &pBlock->abData[0];
797 }
798 else if (pBlock)
799 {
800 ASMAtomicDecU32(&pBlock->cRefs);
801 rtFsExtVol_BlockFree(pThis, pBlock); /* Free the block. */
802 }
803
804 return rc;
805}
806
807
808/**
809 * Releases a reference of the given block.
810 *
811 * @returns nothing.
812 * @param pThis The ext volume instance.
813 * @param pBlock The block to release.
814 */
815static void rtFsExtVol_BlockRelease(PRTFSEXTVOL pThis, PRTFSEXTBLOCKENTRY pBlock)
816{
817 uint32_t cRefs = ASMAtomicDecU32(&pBlock->cRefs);
818 if (!cRefs)
819 rtFsExtVol_BlockFree(pThis, pBlock);
820}
821
822
823/**
824 * Allocates a new block group.
825 *
826 * @returns Pointer to the new block group descriptor or NULL if out of memory.
827 * @param pThis The ext volume instance.
828 * @param cbAlloc How much to allocate.
829 * @param iBlockGroup Block group number.
830 */
831static PRTFSEXTBLKGRP rtFsExtBlockGroupAlloc(PRTFSEXTVOL pThis, size_t cbAlloc, uint32_t iBlockGroup)
832{
833 PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)RTMemAllocZ(cbAlloc);
834 if (RT_LIKELY(pBlockGroup))
835 {
836 pBlockGroup->Core.Key = iBlockGroup;
837 pBlockGroup->cRefs = 0;
838 pBlockGroup->pabInodeBitmap = &pBlockGroup->abBlockBitmap[pThis->cbBlockBitmap];
839 pThis->cbBlockGroups += cbAlloc;
840 }
841
842 return pBlockGroup;
843}
844
845
846/**
847 * Frees the given block group.
848 *
849 * @returns nothing.
850 * @param pThis The ext volume instance.
851 * @param pBlockGroup The block group to free.
852 */
853static void rtFsExtBlockGroupFree(PRTFSEXTVOL pThis, PRTFSEXTBLKGRP pBlockGroup)
854{
855 Assert(!pBlockGroup->cRefs);
856
857 /*
858 * Put it into the cache if the limit wasn't exceeded, otherwise the block group
859 * is freed right away.
860 */
861 if (pThis->cbBlockGroups <= RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE)
862 {
863 /* Put onto the LRU list. */
864 RTListPrepend(&pThis->LstBlockGroupLru, &pBlockGroup->NdLru);
865 }
866 else
867 {
868 /* Remove from the tree and free memory. */
869 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->BlockGroupRoot, pBlockGroup->Core.Key);
870 Assert(pCore == &pBlockGroup->Core); RT_NOREF(pCore);
871 RTMemFree(pBlockGroup);
872 pThis->cbBlockGroups -= sizeof(RTFSEXTBLKGRP) + pThis->cbBlockBitmap + pThis->cbInodeBitmap;
873 }
874}
875
876
877/**
878 * Returns a new block group utilizing the cache if possible.
879 *
880 * @returns Pointer to the new block group descriptor or NULL if out of memory.
881 * @param pThis The ext volume instance.
882 * @param iBlockGroup Block group number.
883 */
884static PRTFSEXTBLKGRP rtFsExtBlockGroupGetNew(PRTFSEXTVOL pThis, uint32_t iBlockGroup)
885{
886 PRTFSEXTBLKGRP pBlockGroup = NULL;
887 size_t cbAlloc = sizeof(RTFSEXTBLKGRP) + pThis->cbBlockBitmap + pThis->cbInodeBitmap;
888 if (pThis->cbBlockGroups + cbAlloc <= RTFSEXT_MAX_BLOCK_GROUP_CACHE_SIZE)
889 pBlockGroup = rtFsExtBlockGroupAlloc(pThis, cbAlloc, iBlockGroup);
890 else
891 {
892 pBlockGroup = RTListRemoveLast(&pThis->LstBlockGroupLru, RTFSEXTBLKGRP, NdLru);
893 if (!pBlockGroup)
894 pBlockGroup = rtFsExtBlockGroupAlloc(pThis, cbAlloc, iBlockGroup);
895 else
896 {
897 /* Remove the block group from the tree because it gets a new key. */
898 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->BlockGroupRoot, pBlockGroup->Core.Key);
899 Assert(pCore == &pBlockGroup->Core); RT_NOREF(pCore);
900 }
901 }
902
903 Assert(!pBlockGroup->cRefs);
904 pBlockGroup->Core.Key = iBlockGroup;
905 pBlockGroup->cRefs = 1;
906
907 return pBlockGroup;
908}
909
910
911/**
912 * Loads the given block group number and returns it on success.
913 *
914 * @returns IPRT status code.
915 * @param pThis The ext volume instance.
916 * @param iBlockGroup The block group to load.
917 * @param ppBlockGroup Where to store the block group on success.
918 */
919static int rtFsExtBlockGroupLoad(PRTFSEXTVOL pThis, uint32_t iBlockGroup, PRTFSEXTBLKGRP *ppBlockGroup)
920{
921 int rc = VINF_SUCCESS;
922
923 /* Try to fetch the block group from the cache first. */
924 PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)RTAvlU32Get(&pThis->BlockGroupRoot, iBlockGroup);
925 if (!pBlockGroup)
926 {
927 /* Slow path, load from disk. */
928 pBlockGroup = rtFsExtBlockGroupGetNew(pThis, iBlockGroup);
929 if (RT_LIKELY(pBlockGroup))
930 {
931 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, pThis->cbBlock == _1K ? 2 : 1)
932 + (uint64_t)iBlockGroup * pThis->cbBlkGrpDesc;
933 EXTBLOCKGROUPDESC BlockGroupDesc;
934 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &BlockGroupDesc, pThis->cbBlkGrpDesc, NULL);
935 if (RT_SUCCESS(rc))
936 {
937#ifdef LOG_ENABLED
938 rtFsExtBlockGroup_Log(pThis, iBlockGroup, &BlockGroupDesc);
939#endif
940 pBlockGroup->iBlockInodeTbl = RT_LE2H_U32(BlockGroupDesc.v32.offInodeTableLow)
941 | ((pThis->cbBlkGrpDesc == sizeof(EXTBLOCKGROUPDESC64))
942 ? (uint64_t)RT_LE2H_U32(BlockGroupDesc.v64.offInodeTableHigh) << 32
943 : 0);
944
945 offRead = rtFsExtBlockIdxLowHighToDiskOffset(pThis, RT_LE2H_U32(BlockGroupDesc.v32.offBlockBitmapLow),
946 RT_LE2H_U32(BlockGroupDesc.v64.offBlockBitmapHigh));
947 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlockGroup->abBlockBitmap[0], pThis->cbBlockBitmap, NULL);
948 if (RT_SUCCESS(rc))
949 {
950 offRead = rtFsExtBlockIdxLowHighToDiskOffset(pThis, RT_LE2H_U32(BlockGroupDesc.v32.offInodeBitmapLow),
951 RT_LE2H_U32(BlockGroupDesc.v64.offInodeBitmapHigh));
952 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &pBlockGroup->pabInodeBitmap[0], pThis->cbInodeBitmap, NULL);
953 if (RT_SUCCESS(rc))
954 {
955 bool fIns = RTAvlU32Insert(&pThis->BlockGroupRoot, &pBlockGroup->Core);
956 Assert(fIns); RT_NOREF(fIns);
957 }
958 }
959 }
960 }
961 else
962 rc = VERR_NO_MEMORY;
963 }
964 else
965 {
966 /* Remove from current LRU list position and add to the beginning. */
967 uint32_t cRefs = ASMAtomicIncU32(&pBlockGroup->cRefs);
968 if (cRefs == 1) /* Block groups get removed from the LRU list if they are referenced. */
969 RTListNodeRemove(&pBlockGroup->NdLru);
970 }
971
972 if (RT_SUCCESS(rc))
973 *ppBlockGroup = pBlockGroup;
974 else if (pBlockGroup)
975 {
976 ASMAtomicDecU32(&pBlockGroup->cRefs);
977 rtFsExtBlockGroupFree(pThis, pBlockGroup); /* Free the block group. */
978 }
979
980 return rc;
981}
982
983
984/**
985 * Releases a reference of the given block group.
986 *
987 * @returns nothing.
988 * @param pThis The ext volume instance.
989 * @param pBlockGroup The block group to release.
990 */
991static void rtFsExtBlockGroupRelease(PRTFSEXTVOL pThis, PRTFSEXTBLKGRP pBlockGroup)
992{
993 uint32_t cRefs = ASMAtomicDecU32(&pBlockGroup->cRefs);
994 if (!cRefs)
995 rtFsExtBlockGroupFree(pThis, pBlockGroup);
996}
997
998
999/**
1000 * Allocates a new inode.
1001 *
1002 * @returns Pointer to the new inode or NULL if out of memory.
1003 * @param pThis The ext volume instance.
1004 * @param iInode Inode number.
1005 */
1006static PRTFSEXTINODE rtFsExtInodeAlloc(PRTFSEXTVOL pThis, uint32_t iInode)
1007{
1008 PRTFSEXTINODE pInode = (PRTFSEXTINODE)RTMemAllocZ(sizeof(RTFSEXTINODE));
1009 if (RT_LIKELY(pInode))
1010 {
1011 pInode->Core.Key = iInode;
1012 pInode->cRefs = 0;
1013 pThis->cbInodes += sizeof(RTFSEXTINODE);
1014 }
1015
1016 return pInode;
1017}
1018
1019
1020/**
1021 * Frees the given inode.
1022 *
1023 * @returns nothing.
1024 * @param pThis The ext volume instance.
1025 * @param pInode The inode to free.
1026 */
1027static void rtFsExtInodeFree(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode)
1028{
1029 Assert(!pInode->cRefs);
1030
1031 /*
1032 * Put it into the cache if the limit wasn't exceeded, otherwise the inode
1033 * is freed right away.
1034 */
1035 if (pThis->cbInodes <= RTFSEXT_MAX_INODE_CACHE_SIZE)
1036 {
1037 /* Put onto the LRU list. */
1038 RTListPrepend(&pThis->LstInodeLru, &pInode->NdLru);
1039 }
1040 else
1041 {
1042 /* Remove from the tree and free memory. */
1043 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->InodeRoot, pInode->Core.Key);
1044 Assert(pCore == &pInode->Core); RT_NOREF(pCore);
1045 RTMemFree(pInode);
1046 pThis->cbInodes -= sizeof(RTFSEXTINODE);
1047 }
1048}
1049
1050
1051/**
1052 * Returns a new inodep utilizing the cache if possible.
1053 *
1054 * @returns Pointer to the new inode or NULL if out of memory.
1055 * @param pThis The ext volume instance.
1056 * @param iInode Inode number.
1057 */
1058static PRTFSEXTINODE rtFsExtInodeGetNew(PRTFSEXTVOL pThis, uint32_t iInode)
1059{
1060 PRTFSEXTINODE pInode = NULL;
1061 if (pThis->cbInodes + sizeof(RTFSEXTINODE) <= RTFSEXT_MAX_INODE_CACHE_SIZE)
1062 pInode = rtFsExtInodeAlloc(pThis, iInode);
1063 else
1064 {
1065 pInode = RTListRemoveLast(&pThis->LstInodeLru, RTFSEXTINODE, NdLru);
1066 if (!pInode)
1067 pInode = rtFsExtInodeAlloc(pThis, iInode);
1068 else
1069 {
1070 /* Remove the block group from the tree because it gets a new key. */
1071 PAVLU32NODECORE pCore = RTAvlU32Remove(&pThis->InodeRoot, pInode->Core.Key);
1072 Assert(pCore == &pInode->Core); RT_NOREF(pCore);
1073 }
1074 }
1075
1076 Assert(!pInode->cRefs);
1077 pInode->Core.Key = iInode;
1078 pInode->cRefs = 1;
1079
1080 return pInode;
1081}
1082
1083
1084/**
1085 * Loads the given inode number and returns it on success.
1086 *
1087 * @returns IPRT status code.
1088 * @param pThis The ext volume instance.
1089 * @param iInode The inode to load.
1090 * @param ppInode Where to store the inode on success.
1091 */
1092static int rtFsExtInodeLoad(PRTFSEXTVOL pThis, uint32_t iInode, PRTFSEXTINODE *ppInode)
1093{
1094 int rc = VINF_SUCCESS;
1095
1096 /* Try to fetch the inode from the cache first. */
1097 PRTFSEXTINODE pInode = (PRTFSEXTINODE)RTAvlU32Get(&pThis->InodeRoot, iInode);
1098 if (!pInode)
1099 {
1100 /* Slow path, load from disk. */
1101 pInode = rtFsExtInodeGetNew(pThis, iInode);
1102 if (RT_LIKELY(pInode))
1103 {
1104 /* Calculate the block group and load that one first to get at the inode table location. */
1105 PRTFSEXTBLKGRP pBlockGroup = NULL;
1106 rc = rtFsExtBlockGroupLoad(pThis, (iInode - 1) / pThis->cInodesPerGroup, &pBlockGroup);
1107 if (RT_SUCCESS(rc))
1108 {
1109 uint32_t idxInodeInTbl = (iInode - 1) % pThis->cInodesPerGroup;
1110 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, pBlockGroup->iBlockInodeTbl)
1111 + idxInodeInTbl * pThis->cbInode;
1112
1113 /* Release block group here already as it is not required. */
1114 rtFsExtBlockGroupRelease(pThis, pBlockGroup);
1115
1116 EXTINODECOMB Inode;
1117 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead, &Inode, RT_MIN(sizeof(Inode), pThis->cbInode), NULL);
1118 if (RT_SUCCESS(rc))
1119 {
1120#ifdef LOG_ENABLED
1121 rtFsExtInode_Log(pThis, iInode, &Inode);
1122#endif
1123 pInode->offInode = offRead;
1124 pInode->fFlags = RT_LE2H_U32(Inode.Core.fFlags);
1125 pInode->ObjInfo.cbObject = (uint64_t)RT_LE2H_U32(Inode.Core.cbSizeHigh) << 32
1126 | (uint64_t)RT_LE2H_U32(Inode.Core.cbSizeLow);
1127 pInode->ObjInfo.cbAllocated = ( (uint64_t)RT_LE2H_U16(Inode.Core.Osd2.Lnx.cBlocksHigh) << 32
1128 | (uint64_t)RT_LE2H_U32(Inode.Core.cBlocksLow)) * pThis->cbBlock;
1129 RTTimeSpecSetSeconds(&pInode->ObjInfo.AccessTime, RT_LE2H_U32(Inode.Core.u32TimeLastAccess));
1130 RTTimeSpecSetSeconds(&pInode->ObjInfo.ModificationTime, RT_LE2H_U32(Inode.Core.u32TimeLastModification));
1131 RTTimeSpecSetSeconds(&pInode->ObjInfo.ChangeTime, RT_LE2H_U32(Inode.Core.u32TimeLastChange));
1132 pInode->ObjInfo.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
1133 pInode->ObjInfo.Attr.u.Unix.uid = (uint32_t)RT_LE2H_U16(Inode.Core.Osd2.Lnx.uUidHigh) << 16
1134 | (uint32_t)RT_LE2H_U16(Inode.Core.uUidLow);
1135 pInode->ObjInfo.Attr.u.Unix.gid = (uint32_t)RT_LE2H_U16(Inode.Core.Osd2.Lnx.uGidHigh) << 16
1136 | (uint32_t)RT_LE2H_U16(Inode.Core.uGidLow);
1137 pInode->ObjInfo.Attr.u.Unix.cHardlinks = RT_LE2H_U16(Inode.Core.cHardLinks);
1138 pInode->ObjInfo.Attr.u.Unix.INodeIdDevice = 0;
1139 pInode->ObjInfo.Attr.u.Unix.INodeId = iInode;
1140 pInode->ObjInfo.Attr.u.Unix.fFlags = 0;
1141 pInode->ObjInfo.Attr.u.Unix.GenerationId = RT_LE2H_U32(Inode.Core.u32Version);
1142 pInode->ObjInfo.Attr.u.Unix.Device = 0;
1143 if (pThis->cbInode >= sizeof(EXTINODECOMB))
1144 RTTimeSpecSetSeconds(&pInode->ObjInfo.BirthTime, RT_LE2H_U32(Inode.Extra.u32TimeCreation));
1145 else
1146 RTTimeSpecSetSeconds(&pInode->ObjInfo.BirthTime, RT_LE2H_U32(Inode.Core.u32TimeLastChange));
1147 for (unsigned i = 0; i < RT_ELEMENTS(pInode->aiBlocks); i++)
1148 pInode->aiBlocks[i] = RT_LE2H_U32(Inode.Core.au32Block[i]);
1149
1150 /* Fill in the mode. */
1151 pInode->ObjInfo.Attr.fMode = 0;
1152 uint32_t fInodeMode = RT_LE2H_U32(Inode.Core.fMode);
1153 switch (EXT_INODE_MODE_TYPE_GET_TYPE(fInodeMode))
1154 {
1155 case EXT_INODE_MODE_TYPE_FIFO:
1156 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_FIFO;
1157 break;
1158 case EXT_INODE_MODE_TYPE_CHAR:
1159 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DEV_CHAR;
1160 break;
1161 case EXT_INODE_MODE_TYPE_DIR:
1162 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DIRECTORY;
1163 break;
1164 case EXT_INODE_MODE_TYPE_BLOCK:
1165 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_DEV_BLOCK;
1166 break;
1167 case EXT_INODE_MODE_TYPE_REGULAR:
1168 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_FILE;
1169 break;
1170 case EXT_INODE_MODE_TYPE_SYMLINK:
1171 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_SYMLINK;
1172 break;
1173 case EXT_INODE_MODE_TYPE_SOCKET:
1174 pInode->ObjInfo.Attr.fMode |= RTFS_TYPE_SOCKET;
1175 break;
1176 default:
1177 rc = VERR_VFS_BOGUS_FORMAT;
1178 }
1179 if (fInodeMode & EXT_INODE_MODE_EXEC_OTHER)
1180 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXOTH;
1181 if (fInodeMode & EXT_INODE_MODE_WRITE_OTHER)
1182 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWOTH;
1183 if (fInodeMode & EXT_INODE_MODE_READ_OTHER)
1184 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IROTH;
1185 if (fInodeMode & EXT_INODE_MODE_EXEC_GROUP)
1186 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXGRP;
1187 if (fInodeMode & EXT_INODE_MODE_WRITE_GROUP)
1188 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWGRP;
1189 if (fInodeMode & EXT_INODE_MODE_READ_GROUP)
1190 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IRGRP;
1191 if (fInodeMode & EXT_INODE_MODE_EXEC_OWNER)
1192 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IXUSR;
1193 if (fInodeMode & EXT_INODE_MODE_WRITE_OWNER)
1194 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IWUSR;
1195 if (fInodeMode & EXT_INODE_MODE_READ_OWNER)
1196 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_IRUSR;
1197 if (fInodeMode & EXT_INODE_MODE_STICKY)
1198 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISTXT;
1199 if (fInodeMode & EXT_INODE_MODE_SET_GROUP_ID)
1200 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISGID;
1201 if (fInodeMode & EXT_INODE_MODE_SET_USER_ID)
1202 pInode->ObjInfo.Attr.fMode |= RTFS_UNIX_ISUID;
1203 }
1204 }
1205
1206 if (RT_SUCCESS(rc))
1207 {
1208 bool fIns = RTAvlU32Insert(&pThis->InodeRoot, &pInode->Core);
1209 Assert(fIns); RT_NOREF(fIns);
1210 }
1211 }
1212 else
1213 rc = VERR_NO_MEMORY;
1214 }
1215 else
1216 {
1217 /* Remove from current LRU list position and add to the beginning. */
1218 uint32_t cRefs = ASMAtomicIncU32(&pInode->cRefs);
1219 if (cRefs == 1) /* Inodes get removed from the LRU list if they are referenced. */
1220 RTListNodeRemove(&pInode->NdLru);
1221 }
1222
1223 if (RT_SUCCESS(rc))
1224 *ppInode = pInode;
1225 else if (pInode)
1226 {
1227 ASMAtomicDecU32(&pInode->cRefs);
1228 rtFsExtInodeFree(pThis, pInode); /* Free the inode. */
1229 }
1230
1231 return rc;
1232}
1233
1234
1235/**
1236 * Releases a reference of the given inode.
1237 *
1238 * @returns nothing.
1239 * @param pThis The ext volume instance.
1240 * @param pInode The inode to release.
1241 */
1242static void rtFsExtInodeRelease(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode)
1243{
1244 uint32_t cRefs = ASMAtomicDecU32(&pInode->cRefs);
1245 if (!cRefs)
1246 rtFsExtInodeFree(pThis, pInode);
1247}
1248
1249
1250/**
1251 * Worker for various QueryInfo methods.
1252 *
1253 * @returns IPRT status code.
1254 * @param pInode The inode structure to return info for.
1255 * @param pObjInfo Where to return object info.
1256 * @param enmAddAttr What additional info to return.
1257 */
1258static int rtFsExtInode_QueryInfo(PRTFSEXTINODE pInode, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1259{
1260 RT_ZERO(*pObjInfo);
1261
1262 pObjInfo->cbObject = pInode->ObjInfo.cbObject;
1263 pObjInfo->cbAllocated = pInode->ObjInfo.cbAllocated;
1264 pObjInfo->AccessTime = pInode->ObjInfo.AccessTime;
1265 pObjInfo->ModificationTime = pInode->ObjInfo.ModificationTime;
1266 pObjInfo->ChangeTime = pInode->ObjInfo.ChangeTime;
1267 pObjInfo->BirthTime = pInode->ObjInfo.BirthTime;
1268 pObjInfo->Attr.fMode = pInode->ObjInfo.Attr.fMode;
1269 pObjInfo->Attr.enmAdditional = enmAddAttr;
1270 switch (enmAddAttr)
1271 {
1272 case RTFSOBJATTRADD_UNIX:
1273 memcpy(&pObjInfo->Attr.u.Unix, &pInode->ObjInfo.Attr.u.Unix, sizeof(pInode->ObjInfo.Attr.u.Unix));
1274 break;
1275
1276 case RTFSOBJATTRADD_UNIX_OWNER:
1277 pObjInfo->Attr.u.UnixOwner.uid = pInode->ObjInfo.Attr.u.Unix.uid;
1278 break;
1279
1280 case RTFSOBJATTRADD_UNIX_GROUP:
1281 pObjInfo->Attr.u.UnixGroup.gid = pInode->ObjInfo.Attr.u.Unix.gid;
1282 break;
1283
1284 default:
1285 break;
1286 }
1287
1288 return VINF_SUCCESS;
1289}
1290
1291
1292/**
1293 * Validates a given extent header.
1294 *
1295 * @returns Flag whether the extent header appears to be valid.
1296 * @param pExtentHdr The extent header to validate.
1297 */
1298DECLINLINE(bool) rtFsExtInode_ExtentHdrValidate(PCEXTEXTENTHDR pExtentHdr)
1299{
1300 return RT_LE2H_U16(pExtentHdr->u16Magic) == EXT_EXTENT_HDR_MAGIC
1301 && RT_LE2H_U16(pExtentHdr->cEntries) <= RT_LE2H_U16(pExtentHdr->cMax)
1302 && RT_LE2H_U16(pExtentHdr->uDepth) <= EXT_EXTENT_HDR_DEPTH_MAX;
1303}
1304
1305
1306/**
1307 * Parses the given extent, checking whether it intersects with the given block.
1308 *
1309 * @returns Flag whether the extent maps the given range (at least partly).
1310 * @param pExtent The extent to parse.
1311 * @param iBlock The starting inode block to map.
1312 * @param cBlocks Number of blocks requested.
1313 * @param piBlockFs Where to store the filesystem block on success.
1314 * @param pcBlocks Where to store the number of contiguous blocks on success.
1315 * @param pfSparse Where to store the sparse flag on success.
1316 */
1317DECLINLINE(bool) rtFsExtInode_ExtentParse(PCEXTEXTENT pExtent, uint64_t iBlock, size_t cBlocks,
1318 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1319{
1320#ifdef LOG_ENABLED
1321 rtFsExtExtent_Log(pExtent);
1322#endif
1323
1324 uint32_t iExtentBlock = RT_LE2H_U32(pExtent->iBlock);
1325 uint16_t cExtentLength = RT_LE2H_U16(pExtent->cBlocks);
1326
1327 /* Length over EXT_EXTENT_LENGTH_LIMIT blocks indicate a sparse extent. */
1328 if (cExtentLength > EXT_EXTENT_LENGTH_LIMIT)
1329 {
1330 *pfSparse = true;
1331 cExtentLength -= EXT_EXTENT_LENGTH_LIMIT;
1332 }
1333 else
1334 *pfSparse = false;
1335
1336 if ( iExtentBlock <= iBlock
1337 && iExtentBlock + cExtentLength > iBlock)
1338 {
1339 uint32_t iBlockRel = iBlock - iExtentBlock;
1340 *pcBlocks = RT_MIN(cBlocks, cExtentLength - iBlockRel);
1341 *piBlockFs = ( ((uint64_t)RT_LE2H_U16(pExtent->offStartHigh)) << 32
1342 | ((uint64_t)RT_LE2H_U32(pExtent->offStartLow))) + iBlockRel;
1343 return true;
1344 }
1345
1346 return false;
1347}
1348
1349
1350/**
1351 * Locates the location of the next level in the extent tree mapping the given block.
1352 *
1353 * @returns Filesystem block number where the next level of the extent is stored.
1354 * @param paExtentIdx Pointer to the array of extent index nodes.
1355 * @param cEntries Number of entries in the extent index node array.
1356 * @param iBlock The block to resolve.
1357 */
1358DECLINLINE(uint64_t) rtFsExtInode_ExtentIndexLocateNextLvl(PCEXTEXTENTIDX paExtentIdx, uint16_t cEntries, uint64_t iBlock)
1359{
1360 for (uint32_t i = 1; i < cEntries; i++)
1361 {
1362 PCEXTEXTENTIDX pPrev = &paExtentIdx[i - 1];
1363 PCEXTEXTENTIDX pCur = &paExtentIdx[i];
1364
1365#ifdef LOG_ENABLED
1366 rtFsExtExtentIdx_Log(pPrev);
1367#endif
1368
1369 if ( RT_LE2H_U32(pPrev->iBlock) <= iBlock
1370 && RT_LE2H_U32(pCur->iBlock) > iBlock)
1371 return (uint64_t)RT_LE2H_U16(pPrev->offChildHigh) << 32
1372 | (uint64_t)RT_LE2H_U32(pPrev->offChildLow);
1373 }
1374
1375 /* Nothing found so far, the blast extent index must cover the block as the array is sorted. */
1376 PCEXTEXTENTIDX pLast = &paExtentIdx[cEntries - 1];
1377#ifdef LOG_ENABLED
1378 rtFsExtExtentIdx_Log(pLast);
1379#endif
1380
1381 return (uint64_t)RT_LE2H_U16(pLast->offChildHigh) << 32
1382 | (uint64_t)RT_LE2H_U32(pLast->offChildLow);
1383}
1384
1385
1386/**
1387 * Maps the given inode block to the destination filesystem block using the embedded extent tree.
1388 *
1389 * @returns IPRT status code.
1390 * @param pThis The ext volume instance.
1391 * @param pInode The inode structure to read from.
1392 * @param iBlock The starting inode block to map.
1393 * @param cBlocks Number of blocks requested.
1394 * @param piBlockFs Where to store the filesystem block on success.
1395 * @param pcBlocks Where to store the number of contiguous blocks on success.
1396 * @param pfSparse Where to store the sparse flag on success.
1397 *
1398 * @todo Optimize
1399 */
1400static int rtFsExtInode_MapBlockToFsViaExtent(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t iBlock, size_t cBlocks,
1401 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1402{
1403 int rc = VINF_SUCCESS;
1404
1405 /* The root of the extent tree is located in the block data of the inode. */
1406 PCEXTEXTENTHDR pExtentHdr = (PCEXTEXTENTHDR)&pInode->aiBlocks[0];
1407
1408#ifdef LOG_ENABLED
1409 rtFsExtExtentHdr_Log(pExtentHdr);
1410#endif
1411
1412 /*
1413 * Some validation, the top level is located inside the inode block data
1414 * and has a maxmimum of 4 entries.
1415 */
1416 if ( rtFsExtInode_ExtentHdrValidate(pExtentHdr)
1417 && RT_LE2H_U16(pExtentHdr->cMax) <= 4)
1418 {
1419 uint16_t uDepthCur = RT_LE2H_U16(pExtentHdr->uDepth);
1420 if (!uDepthCur)
1421 {
1422 PCEXTEXTENT pExtent = (PCEXTEXTENT)(pExtentHdr + 1);
1423
1424 rc = VERR_VFS_BOGUS_FORMAT;
1425 for (uint32_t i = 0; i < RT_LE2H_U16(pExtentHdr->cEntries); i++)
1426 {
1427 /* Check whether the extent intersects with the block. */
1428 if (rtFsExtInode_ExtentParse(pExtent, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse))
1429 {
1430 rc = VINF_SUCCESS;
1431 break;
1432 }
1433 pExtent++;
1434 }
1435 }
1436 else
1437 {
1438 uint8_t *pbExtent = NULL;
1439 PRTFSEXTBLOCKENTRY pBlock = NULL;
1440 uint64_t iBlockNext = 0;
1441 PCEXTEXTENTIDX paExtentIdx = (PCEXTEXTENTIDX)(pExtentHdr + 1);
1442 uint16_t cEntries = RT_LE2H_U16(pExtentHdr->cEntries);
1443
1444 /* Descend the tree until we reached the leaf nodes. */
1445 do
1446 {
1447 iBlockNext = rtFsExtInode_ExtentIndexLocateNextLvl(paExtentIdx, cEntries, iBlock);
1448 /* Read in the full block. */
1449 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&pbExtent);
1450 if (RT_SUCCESS(rc))
1451 {
1452 pExtentHdr = (PCEXTEXTENTHDR)pbExtent;
1453
1454#ifdef LOG_ENABLED
1455 rtFsExtExtentHdr_Log(pExtentHdr);
1456#endif
1457
1458 if ( rtFsExtInode_ExtentHdrValidate(pExtentHdr)
1459 && RT_LE2H_U16(pExtentHdr->cMax) <= (pThis->cbBlock - sizeof(EXTEXTENTHDR)) / sizeof(EXTEXTENTIDX)
1460 && RT_LE2H_U16(pExtentHdr->uDepth) == uDepthCur - 1)
1461 {
1462 uDepthCur--;
1463 cEntries = RT_LE2H_U16(pExtentHdr->cEntries);
1464 paExtentIdx = (PCEXTEXTENTIDX)(pExtentHdr + 1);
1465 if (uDepthCur)
1466 rtFsExtVol_BlockRelease(pThis, pBlock);
1467 }
1468 else
1469 rc = VERR_VFS_BOGUS_FORMAT;
1470 }
1471 }
1472 while ( uDepthCur > 0
1473 && RT_SUCCESS(rc));
1474
1475 if (RT_SUCCESS(rc))
1476 {
1477 Assert(!uDepthCur);
1478
1479 /* We reached the leaf nodes. */
1480 PCEXTEXTENT pExtent = (PCEXTEXTENT)(pExtentHdr + 1);
1481 for (uint32_t i = 0; i < RT_LE2H_U16(pExtentHdr->cEntries); i++)
1482 {
1483 /* Check whether the extent intersects with the block. */
1484 if (rtFsExtInode_ExtentParse(pExtent, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse))
1485 {
1486 rc = VINF_SUCCESS;
1487 break;
1488 }
1489 pExtent++;
1490 }
1491 }
1492
1493 if (pBlock)
1494 rtFsExtVol_BlockRelease(pThis, pBlock);
1495 }
1496 }
1497 else
1498 rc = VERR_VFS_BOGUS_FORMAT;
1499
1500 return rc;
1501}
1502
1503
1504/**
1505 * Maps the given inode block to the destination filesystem block using the original block mapping scheme.
1506 *
1507 * @returns IPRT status code.
1508 * @param pThis The ext volume instance.
1509 * @param pInode The inode structure to read from.
1510 * @param iBlock The inode block to map.
1511 * @param cBlocks Number of blocks requested.
1512 * @param piBlockFs Where to store the filesystem block on success.
1513 * @param pcBlocks Where to store the number of contiguous blocks on success.
1514 * @param pfSparse Where to store the sparse flag on success.
1515 *
1516 * @todo Optimize and handle sparse files.
1517 */
1518static int rtFsExtInode_MapBlockToFsViaBlockMap(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t iBlock, size_t cBlocks,
1519 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1520{
1521 int rc = VINF_SUCCESS;
1522 RT_NOREF(cBlocks);
1523
1524 *pfSparse = false;
1525 *pcBlocks = 1;
1526
1527 /* The first 12 inode blocks are directly mapped from the inode. */
1528 if (iBlock <= 11)
1529 *piBlockFs = pInode->aiBlocks[iBlock];
1530 else
1531 {
1532 uint32_t cEntriesPerBlockMap = (uint32_t)(pThis->cbBlock >> sizeof(uint32_t));
1533
1534 if (iBlock <= cEntriesPerBlockMap + 11)
1535 {
1536 /* Indirect block. */
1537 PRTFSEXTBLOCKENTRY pBlock = NULL;
1538 uint32_t *paBlockMap = NULL;
1539 rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[12], &pBlock, (void **)&paBlockMap);
1540 if (RT_SUCCESS(rc))
1541 {
1542 *piBlockFs = RT_LE2H_U32(paBlockMap[iBlock - 12]);
1543 rtFsExtVol_BlockRelease(pThis, pBlock);
1544 }
1545 }
1546 else if (iBlock <= cEntriesPerBlockMap * cEntriesPerBlockMap + cEntriesPerBlockMap + 11)
1547 {
1548 /* Double indirect block. */
1549 PRTFSEXTBLOCKENTRY pBlock = NULL;
1550 uint32_t *paBlockMap = NULL;
1551
1552 iBlock -= 12 + cEntriesPerBlockMap;
1553 rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[13], &pBlock, (void **)&paBlockMap);
1554 if (RT_SUCCESS(rc))
1555 {
1556 uint32_t idxBlockL2 = iBlock / cEntriesPerBlockMap;
1557 uint32_t idxBlockL1 = iBlock % cEntriesPerBlockMap;
1558 uint32_t iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL2]);
1559
1560 rtFsExtVol_BlockRelease(pThis, pBlock);
1561 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
1562 if (RT_SUCCESS(rc))
1563 {
1564 *piBlockFs = RT_LE2H_U32(paBlockMap[idxBlockL1]);
1565 rtFsExtVol_BlockRelease(pThis, pBlock);
1566 }
1567 }
1568 }
1569 else
1570 {
1571 /* Triple indirect block. */
1572 PRTFSEXTBLOCKENTRY pBlock = NULL;
1573 uint32_t *paBlockMap = NULL;
1574
1575 iBlock -= 12 + cEntriesPerBlockMap * cEntriesPerBlockMap + cEntriesPerBlockMap;
1576 rc = rtFsExtVol_BlockLoad(pThis, pInode->aiBlocks[14], &pBlock, (void **)&paBlockMap);
1577 if (RT_SUCCESS(rc))
1578 {
1579 uint32_t idxBlockL3 = iBlock / (cEntriesPerBlockMap * cEntriesPerBlockMap);
1580 uint32_t iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL3]);
1581
1582 rtFsExtVol_BlockRelease(pThis, pBlock);
1583 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
1584 if (RT_SUCCESS(rc))
1585 {
1586 uint32_t idxBlockL2 = (iBlock % (cEntriesPerBlockMap * cEntriesPerBlockMap)) / cEntriesPerBlockMap;
1587 uint32_t idxBlockL1 = iBlock % cEntriesPerBlockMap;
1588 iBlockNext = RT_LE2H_U32(paBlockMap[idxBlockL2]);
1589
1590 rtFsExtVol_BlockRelease(pThis, pBlock);
1591 rc = rtFsExtVol_BlockLoad(pThis, iBlockNext, &pBlock, (void **)&paBlockMap);
1592 if (RT_SUCCESS(rc))
1593 {
1594 *piBlockFs = RT_LE2H_U32(paBlockMap[idxBlockL1]);
1595 rtFsExtVol_BlockRelease(pThis, pBlock);
1596 }
1597 }
1598 }
1599 }
1600 }
1601
1602 return rc;
1603}
1604
1605
1606/**
1607 * Maps the given inode block to the destination filesystem block.
1608 *
1609 * @returns IPRT status code.
1610 * @param pThis The ext volume instance.
1611 * @param pInode The inode structure to read from.
1612 * @param iBlock The inode block to map.
1613 * @param cBlocks Number of blocks requested.
1614 * @param piBlockFs Where to store the filesystem block on success.
1615 * @param pcBlocks Where to store the number of contiguous blocks on success.
1616 * @param pfSparse Where to store the sparse flag on success.
1617 *
1618 * @todo Optimize
1619 */
1620static int rtFsExtInode_MapBlockToFs(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t iBlock, size_t cBlocks,
1621 uint64_t *piBlockFs, size_t *pcBlocks, bool *pfSparse)
1622{
1623 if (pInode->fFlags & EXT_INODE_F_EXTENTS)
1624 return rtFsExtInode_MapBlockToFsViaExtent(pThis, pInode, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse);
1625 else
1626 return rtFsExtInode_MapBlockToFsViaBlockMap(pThis, pInode, iBlock, cBlocks, piBlockFs, pcBlocks, pfSparse);
1627}
1628
1629
1630/**
1631 * Reads data from the given inode at the given byte offset.
1632 *
1633 * @returns IPRT status code.
1634 * @param pThis The ext volume instance.
1635 * @param pInode The inode structure to read from.
1636 * @param off The byte offset to start reading from.
1637 * @param pvBuf Where to store the read data to.
1638 * @param pcbRead Where to return the amount of data read.
1639 */
1640static int rtFsExtInode_Read(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, uint64_t off, void *pvBuf, size_t cbRead, size_t *pcbRead)
1641{
1642 int rc = VINF_SUCCESS;
1643 uint8_t *pbBuf = (uint8_t *)pvBuf;
1644
1645 if (((uint64_t)pInode->ObjInfo.cbObject < off + cbRead))
1646 {
1647 if (!pcbRead)
1648 return VERR_EOF;
1649 else
1650 cbRead = (uint64_t)pInode->ObjInfo.cbObject - off;
1651 }
1652
1653 while ( cbRead
1654 && RT_SUCCESS(rc))
1655 {
1656 uint64_t iBlockStart = rtFsExtDiskOffsetToBlockIdx(pThis, off);
1657 uint32_t offBlockStart = off % pThis->cbBlock;
1658
1659 /* Resolve the inode block to the proper filesystem block. */
1660 uint64_t iBlockFs = 0;
1661 size_t cBlocks = 0;
1662 bool fSparse = false;
1663 rc = rtFsExtInode_MapBlockToFs(pThis, pInode, iBlockStart, 1, &iBlockFs, &cBlocks, &fSparse);
1664 if (RT_SUCCESS(rc))
1665 {
1666 Assert(cBlocks == 1);
1667
1668 size_t cbThisRead = RT_MIN(cbRead, pThis->cbBlock - offBlockStart);
1669
1670 if (!fSparse)
1671 {
1672 uint64_t offRead = rtFsExtBlockIdxToDiskOffset(pThis, iBlockFs);
1673 rc = RTVfsFileReadAt(pThis->hVfsBacking, offRead + offBlockStart, pbBuf, cbThisRead, NULL);
1674 }
1675 else
1676 memset(pbBuf, 0, cbThisRead);
1677
1678 if (RT_SUCCESS(rc))
1679 {
1680 pbBuf += cbThisRead;
1681 cbRead -= cbThisRead;
1682 off += cbThisRead;
1683 if (pcbRead)
1684 *pcbRead += cbThisRead;
1685 }
1686 }
1687 }
1688
1689 return rc;
1690}
1691
1692
1693
1694/*
1695 *
1696 * File operations.
1697 * File operations.
1698 * File operations.
1699 *
1700 */
1701
1702/**
1703 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
1704 */
1705static DECLCALLBACK(int) rtFsExtFile_Close(void *pvThis)
1706{
1707 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1708 LogFlow(("rtFsExtFile_Close(%p/%p)\n", pThis, pThis->pInode));
1709
1710 rtFsExtInodeRelease(pThis->pVol, pThis->pInode);
1711 pThis->pInode = NULL;
1712 pThis->pVol = NULL;
1713 return VINF_SUCCESS;
1714}
1715
1716
1717/**
1718 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
1719 */
1720static DECLCALLBACK(int) rtFsExtFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
1721{
1722 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1723 return rtFsExtInode_QueryInfo(pThis->pInode, pObjInfo, enmAddAttr);
1724}
1725
1726
1727/**
1728 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
1729 */
1730static DECLCALLBACK(int) rtFsExtFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
1731{
1732 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1733 AssertReturn(pSgBuf->cSegs == 1, VERR_INTERNAL_ERROR_3);
1734 RT_NOREF(fBlocking);
1735
1736 if (off == -1)
1737 off = pThis->offFile;
1738 else
1739 AssertReturn(off >= 0, VERR_INTERNAL_ERROR_3);
1740
1741 int rc;
1742 size_t cbRead = pSgBuf->paSegs[0].cbSeg;
1743 if (!pcbRead)
1744 {
1745 rc = rtFsExtInode_Read(pThis->pVol, pThis->pInode, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1746 if (RT_SUCCESS(rc))
1747 pThis->offFile = off + cbRead;
1748 Log6(("rtFsExtFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc\n", off, pSgBuf->paSegs[0].cbSeg, rc));
1749 }
1750 else
1751 {
1752 PRTFSEXTINODE pInode = pThis->pInode;
1753 if (off >= pInode->ObjInfo.cbObject)
1754 {
1755 *pcbRead = 0;
1756 rc = VINF_EOF;
1757 }
1758 else
1759 {
1760 if ((uint64_t)off + cbRead <= (uint64_t)pInode->ObjInfo.cbObject)
1761 rc = rtFsExtInode_Read(pThis->pVol, pThis->pInode, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1762 else
1763 {
1764 /* Return VINF_EOF if beyond end-of-file. */
1765 cbRead = (size_t)(pInode->ObjInfo.cbObject - off);
1766 rc = rtFsExtInode_Read(pThis->pVol, pThis->pInode, off, pSgBuf->paSegs[0].pvSeg, cbRead, NULL);
1767 if (RT_SUCCESS(rc))
1768 rc = VINF_EOF;
1769 }
1770 if (RT_SUCCESS(rc))
1771 {
1772 pThis->offFile = off + cbRead;
1773 *pcbRead = cbRead;
1774 }
1775 else
1776 *pcbRead = 0;
1777 }
1778 Log6(("rtFsExtFile_Read: off=%#RX64 cbSeg=%#x -> %Rrc *pcbRead=%#x\n", off, pSgBuf->paSegs[0].cbSeg, rc, *pcbRead));
1779 }
1780
1781 return rc;
1782}
1783
1784
1785/**
1786 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
1787 */
1788static DECLCALLBACK(int) rtFsExtFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
1789{
1790 RT_NOREF(pvThis, off, pSgBuf, fBlocking, pcbWritten);
1791 return VERR_WRITE_PROTECT;
1792}
1793
1794
1795/**
1796 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
1797 */
1798static DECLCALLBACK(int) rtFsExtFile_Flush(void *pvThis)
1799{
1800 RT_NOREF(pvThis);
1801 return VINF_SUCCESS;
1802}
1803
1804
1805/**
1806 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
1807 */
1808static DECLCALLBACK(int) rtFsExtFile_Tell(void *pvThis, PRTFOFF poffActual)
1809{
1810 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1811 *poffActual = pThis->offFile;
1812 return VINF_SUCCESS;
1813}
1814
1815
1816/**
1817 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
1818 */
1819static DECLCALLBACK(int) rtFsExtFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
1820{
1821 RT_NOREF(pvThis, fMode, fMask);
1822 return VERR_WRITE_PROTECT;
1823}
1824
1825
1826/**
1827 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
1828 */
1829static DECLCALLBACK(int) rtFsExtFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
1830 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
1831{
1832 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
1833 return VERR_WRITE_PROTECT;
1834}
1835
1836
1837/**
1838 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
1839 */
1840static DECLCALLBACK(int) rtFsExtFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
1841{
1842 RT_NOREF(pvThis, uid, gid);
1843 return VERR_WRITE_PROTECT;
1844}
1845
1846
1847/**
1848 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
1849 */
1850static DECLCALLBACK(int) rtFsExtFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
1851{
1852 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1853 RTFOFF offNew;
1854 switch (uMethod)
1855 {
1856 case RTFILE_SEEK_BEGIN:
1857 offNew = offSeek;
1858 break;
1859 case RTFILE_SEEK_END:
1860 offNew = pThis->pInode->ObjInfo.cbObject + offSeek;
1861 break;
1862 case RTFILE_SEEK_CURRENT:
1863 offNew = (RTFOFF)pThis->offFile + offSeek;
1864 break;
1865 default:
1866 return VERR_INVALID_PARAMETER;
1867 }
1868 if (offNew >= 0)
1869 {
1870 pThis->offFile = offNew;
1871 *poffActual = offNew;
1872 return VINF_SUCCESS;
1873 }
1874 return VERR_NEGATIVE_SEEK;
1875}
1876
1877
1878/**
1879 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
1880 */
1881static DECLCALLBACK(int) rtFsExtFile_QuerySize(void *pvThis, uint64_t *pcbFile)
1882{
1883 PRTFSEXTFILE pThis = (PRTFSEXTFILE)pvThis;
1884 *pcbFile = (uint64_t)pThis->pInode->ObjInfo.cbObject;
1885 return VINF_SUCCESS;
1886}
1887
1888
1889/**
1890 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
1891 */
1892static DECLCALLBACK(int) rtFsExtFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
1893{
1894 RT_NOREF(pvThis, cbFile, fFlags);
1895 return VERR_WRITE_PROTECT;
1896}
1897
1898
1899/**
1900 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
1901 */
1902static DECLCALLBACK(int) rtFsExtFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
1903{
1904 RT_NOREF(pvThis);
1905 *pcbMax = INT64_MAX; /** @todo */
1906 return VINF_SUCCESS;
1907}
1908
1909
1910/**
1911 * EXT file operations.
1912 */
1913static const RTVFSFILEOPS g_rtFsExtFileOps =
1914{
1915 { /* Stream */
1916 { /* Obj */
1917 RTVFSOBJOPS_VERSION,
1918 RTVFSOBJTYPE_FILE,
1919 "EXT File",
1920 rtFsExtFile_Close,
1921 rtFsExtFile_QueryInfo,
1922 NULL,
1923 RTVFSOBJOPS_VERSION
1924 },
1925 RTVFSIOSTREAMOPS_VERSION,
1926 RTVFSIOSTREAMOPS_FEAT_NO_SG,
1927 rtFsExtFile_Read,
1928 rtFsExtFile_Write,
1929 rtFsExtFile_Flush,
1930 NULL /*PollOne*/,
1931 rtFsExtFile_Tell,
1932 NULL /*pfnSkip*/,
1933 NULL /*pfnZeroFill*/,
1934 RTVFSIOSTREAMOPS_VERSION,
1935 },
1936 RTVFSFILEOPS_VERSION,
1937 0,
1938 { /* ObjSet */
1939 RTVFSOBJSETOPS_VERSION,
1940 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
1941 rtFsExtFile_SetMode,
1942 rtFsExtFile_SetTimes,
1943 rtFsExtFile_SetOwner,
1944 RTVFSOBJSETOPS_VERSION
1945 },
1946 rtFsExtFile_Seek,
1947 rtFsExtFile_QuerySize,
1948 rtFsExtFile_SetSize,
1949 rtFsExtFile_QueryMaxSize,
1950 RTVFSFILEOPS_VERSION
1951};
1952
1953
1954/**
1955 * Creates a new VFS file from the given regular file inode.
1956 *
1957 * @returns IPRT status code.
1958 * @param pThis The ext volume instance.
1959 * @param fOpen Open flags passed.
1960 * @param iInode The inode for the file.
1961 * @param phVfsFile Where to store the VFS file handle on success.
1962 * @param pErrInfo Where to record additional error information on error, optional.
1963 * @param pszWhat Logging prefix.
1964 */
1965static int rtFsExtVol_NewFile(PRTFSEXTVOL pThis, uint64_t fOpen, uint32_t iInode,
1966 PRTVFSFILE phVfsFile, PRTERRINFO pErrInfo, const char *pszWhat)
1967{
1968 /*
1969 * Load the inode and check that it really is a file.
1970 */
1971 PRTFSEXTINODE pInode = NULL;
1972 int rc = rtFsExtInodeLoad(pThis, iInode, &pInode);
1973 if (RT_SUCCESS(rc))
1974 {
1975 if (RTFS_IS_FILE(pInode->ObjInfo.Attr.fMode))
1976 {
1977 PRTFSEXTFILE pNewFile;
1978 rc = RTVfsNewFile(&g_rtFsExtFileOps, sizeof(*pNewFile), fOpen, pThis->hVfsSelf, NIL_RTVFSLOCK,
1979 phVfsFile, (void **)&pNewFile);
1980 if (RT_SUCCESS(rc))
1981 {
1982 pNewFile->pVol = pThis;
1983 pNewFile->pInode = pInode;
1984 pNewFile->offFile = 0;
1985 }
1986 }
1987 else
1988 rc = RTERRINFO_LOG_SET_F(pErrInfo, VERR_NOT_A_FILE, "%s: fMode=%#RX32", pszWhat, pInode->ObjInfo.Attr.fMode);
1989
1990 if (RT_FAILURE(rc))
1991 rtFsExtInodeRelease(pThis, pInode);
1992 }
1993
1994 return rc;
1995}
1996
1997
1998
1999/*
2000 *
2001 * EXT directory code.
2002 * EXT directory code.
2003 * EXT directory code.
2004 *
2005 */
2006
2007/**
2008 * Looks up an entry in the given directory inode.
2009 *
2010 * @returns IPRT status code.
2011 * @param pThis The ext volume instance.
2012 * @param pInode The directory inode structure to.
2013 * @param pszEntry The entry to lookup.
2014 * @param piInode Where to store the inode number if the entry was found.
2015 */
2016static int rtFsExtDir_Lookup(PRTFSEXTVOL pThis, PRTFSEXTINODE pInode, const char *pszEntry, uint32_t *piInode)
2017{
2018 uint64_t offEntry = 0;
2019 int rc = VERR_FILE_NOT_FOUND;
2020 uint32_t idxDirEntry = 0;
2021 size_t cchEntry = strlen(pszEntry);
2022
2023 if (cchEntry > 255)
2024 return VERR_FILENAME_TOO_LONG;
2025
2026 while (offEntry < (uint64_t)pInode->ObjInfo.cbObject)
2027 {
2028 EXTDIRENTRYEX DirEntry;
2029 size_t cbThis = RT_MIN(sizeof(DirEntry), (uint64_t)pInode->ObjInfo.cbObject - offEntry);
2030 int rc2 = rtFsExtInode_Read(pThis, pInode, offEntry, &DirEntry, cbThis, NULL);
2031 if (RT_SUCCESS(rc2))
2032 {
2033#ifdef LOG_ENABLED
2034 rtFsExtDirEntry_Log(pThis, idxDirEntry, &DirEntry);
2035#endif
2036
2037 uint16_t cbName = pThis->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE
2038 ? DirEntry.Core.u.v2.cbName
2039 : RT_LE2H_U16(DirEntry.Core.u.v1.cbName);
2040 if ( cchEntry == cbName
2041 && !memcmp(pszEntry, &DirEntry.Core.achName[0], cchEntry))
2042 {
2043 *piInode = RT_LE2H_U32(DirEntry.Core.iInodeRef);
2044 rc = VINF_SUCCESS;
2045 break;
2046 }
2047
2048 offEntry += RT_LE2H_U16(DirEntry.Core.cbRecord);
2049 idxDirEntry++;
2050 }
2051 else
2052 {
2053 rc = rc2;
2054 break;
2055 }
2056 }
2057
2058 return rc;
2059}
2060
2061
2062
2063/*
2064 *
2065 * Directory instance methods
2066 * Directory instance methods
2067 * Directory instance methods
2068 *
2069 */
2070
2071/**
2072 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
2073 */
2074static DECLCALLBACK(int) rtFsExtDir_Close(void *pvThis)
2075{
2076 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2077 LogFlowFunc(("pThis=%p\n", pThis));
2078 rtFsExtInodeRelease(pThis->pVol, pThis->pInode);
2079 pThis->pInode = NULL;
2080 return VINF_SUCCESS;
2081}
2082
2083
2084/**
2085 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
2086 */
2087static DECLCALLBACK(int) rtFsExtDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2088{
2089 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2090 LogFlowFunc(("\n"));
2091 return rtFsExtInode_QueryInfo(pThis->pInode, pObjInfo, enmAddAttr);
2092}
2093
2094
2095/**
2096 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
2097 */
2098static DECLCALLBACK(int) rtFsExtDir_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
2099{
2100 LogFlowFunc(("\n"));
2101 RT_NOREF(pvThis, fMode, fMask);
2102 return VERR_WRITE_PROTECT;
2103}
2104
2105
2106/**
2107 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
2108 */
2109static DECLCALLBACK(int) rtFsExtDir_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
2110 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
2111{
2112 LogFlowFunc(("\n"));
2113 RT_NOREF(pvThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
2114 return VERR_WRITE_PROTECT;
2115}
2116
2117
2118/**
2119 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
2120 */
2121static DECLCALLBACK(int) rtFsExtDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
2122{
2123 LogFlowFunc(("\n"));
2124 RT_NOREF(pvThis, uid, gid);
2125 return VERR_WRITE_PROTECT;
2126}
2127
2128
2129/**
2130 * @interface_method_impl{RTVFSDIROPS,pfnOpen}
2131 */
2132static DECLCALLBACK(int) rtFsExtDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen,
2133 uint32_t fFlags, PRTVFSOBJ phVfsObj)
2134{
2135 LogFlowFunc(("pszEntry='%s' fOpen=%#RX64 fFlags=%#x\n", pszEntry, fOpen, fFlags));
2136 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2137 PRTFSEXTVOL pVol = pThis->pVol;
2138 int rc = VINF_SUCCESS;
2139
2140 RT_NOREF(fFlags);
2141
2142 /*
2143 * We cannot create or replace anything, just open stuff.
2144 */
2145 if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
2146 || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE)
2147 { /* likely */ }
2148 else
2149 return VERR_WRITE_PROTECT;
2150
2151 /*
2152 * Lookup the entry.
2153 */
2154 uint32_t iInode = 0;
2155 rc = rtFsExtDir_Lookup(pVol, pThis->pInode, pszEntry, &iInode);
2156 if (RT_SUCCESS(rc))
2157 {
2158 PRTFSEXTINODE pInode = NULL;
2159 rc = rtFsExtInodeLoad(pVol, iInode, &pInode);
2160 if (RT_SUCCESS(rc))
2161 {
2162 if (RTFS_IS_DIRECTORY(pInode->ObjInfo.Attr.fMode))
2163 {
2164 RTVFSDIR hVfsDir;
2165 rc = rtFsExtVol_OpenDirByInode(pVol, iInode, &hVfsDir);
2166 if (RT_SUCCESS(rc))
2167 {
2168 *phVfsObj = RTVfsObjFromDir(hVfsDir);
2169 RTVfsDirRelease(hVfsDir);
2170 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2171 }
2172 }
2173 else if (RTFS_IS_FILE(pInode->ObjInfo.Attr.fMode))
2174 {
2175 RTVFSFILE hVfsFile;
2176 rc = rtFsExtVol_NewFile(pVol, fOpen, iInode, &hVfsFile, NULL, pszEntry);
2177 if (RT_SUCCESS(rc))
2178 {
2179 *phVfsObj = RTVfsObjFromFile(hVfsFile);
2180 RTVfsFileRelease(hVfsFile);
2181 AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
2182 }
2183 }
2184 else
2185 rc = VERR_NOT_SUPPORTED;
2186 }
2187 }
2188
2189 LogFlow(("rtFsExtDir_Open(%s): returns %Rrc\n", pszEntry, rc));
2190 return rc;
2191}
2192
2193
2194/**
2195 * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
2196 */
2197static DECLCALLBACK(int) rtFsExtDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
2198{
2199 RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
2200 LogFlowFunc(("\n"));
2201 return VERR_WRITE_PROTECT;
2202}
2203
2204
2205/**
2206 * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
2207 */
2208static DECLCALLBACK(int) rtFsExtDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
2209{
2210 RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
2211 LogFlowFunc(("\n"));
2212 return VERR_NOT_SUPPORTED;
2213}
2214
2215
2216/**
2217 * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
2218 */
2219static DECLCALLBACK(int) rtFsExtDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
2220 RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
2221{
2222 RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
2223 LogFlowFunc(("\n"));
2224 return VERR_WRITE_PROTECT;
2225}
2226
2227
2228/**
2229 * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
2230 */
2231static DECLCALLBACK(int) rtFsExtDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
2232{
2233 RT_NOREF(pvThis, pszEntry, fType);
2234 LogFlowFunc(("\n"));
2235 return VERR_WRITE_PROTECT;
2236}
2237
2238
2239/**
2240 * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
2241 */
2242static DECLCALLBACK(int) rtFsExtDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
2243{
2244 RT_NOREF(pvThis, pszEntry, fType, pszNewName);
2245 LogFlowFunc(("\n"));
2246 return VERR_WRITE_PROTECT;
2247}
2248
2249
2250/**
2251 * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
2252 */
2253static DECLCALLBACK(int) rtFsExtDir_RewindDir(void *pvThis)
2254{
2255 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2256 LogFlowFunc(("\n"));
2257
2258 pThis->fNoMoreFiles = false;
2259 pThis->offEntry = 0;
2260 pThis->idxEntry = 0;
2261 return VINF_SUCCESS;
2262}
2263
2264
2265/**
2266 * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
2267 */
2268static DECLCALLBACK(int) rtFsExtDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
2269 RTFSOBJATTRADD enmAddAttr)
2270{
2271 PRTFSEXTDIR pThis = (PRTFSEXTDIR)pvThis;
2272 PRTFSEXTINODE pInode = pThis->pInode;
2273 LogFlowFunc(("\n"));
2274
2275 if (pThis->fNoMoreFiles)
2276 return VERR_NO_MORE_FILES;
2277
2278 EXTDIRENTRYEX DirEntry;
2279 size_t cbThis = RT_MIN(sizeof(DirEntry), (uint64_t)pInode->ObjInfo.cbObject - pThis->offEntry);
2280 int rc = rtFsExtInode_Read(pThis->pVol, pInode, pThis->offEntry, &DirEntry, cbThis, NULL);
2281 if (RT_SUCCESS(rc))
2282 {
2283#ifdef LOG_ENABLED
2284 rtFsExtDirEntry_Log(pThis->pVol, pThis->idxEntry, &DirEntry);
2285#endif
2286
2287 /* 0 inode entry means unused entry. */
2288 /** @todo Can there be unused entries somewhere in the middle? */
2289 uint32_t iInodeRef = RT_LE2H_U32(DirEntry.Core.iInodeRef);
2290 if (iInodeRef != 0)
2291 {
2292 uint16_t cbName = pThis->pVol->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_DIR_FILETYPE
2293 ? DirEntry.Core.u.v2.cbName
2294 : RT_LE2H_U16(DirEntry.Core.u.v1.cbName);
2295
2296 if (cbName <= 255)
2297 {
2298 size_t const cbDirEntry = *pcbDirEntry;
2299
2300 *pcbDirEntry = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cbName + 2]);
2301 if (*pcbDirEntry <= cbDirEntry)
2302 {
2303 /* Load the referenced inode. */
2304 PRTFSEXTINODE pInodeRef;
2305 rc = rtFsExtInodeLoad(pThis->pVol, iInodeRef, &pInodeRef);
2306 if (RT_SUCCESS(rc))
2307 {
2308 memcpy(&pDirEntry->szName[0], &DirEntry.Core.achName[0], cbName);
2309 pDirEntry->szName[cbName] = '\0';
2310 pDirEntry->cbName = cbName;
2311 rc = rtFsExtInode_QueryInfo(pInodeRef, &pDirEntry->Info, enmAddAttr);
2312 if (RT_SUCCESS(rc))
2313 {
2314 pThis->offEntry += RT_LE2H_U16(DirEntry.Core.cbRecord);
2315 pThis->idxEntry++;
2316 rtFsExtInodeRelease(pThis->pVol, pInodeRef);
2317 return VINF_SUCCESS;
2318 }
2319 rtFsExtInodeRelease(pThis->pVol, pInodeRef);
2320 }
2321 }
2322 else
2323 rc = VERR_BUFFER_OVERFLOW;
2324 }
2325 else
2326 rc = VERR_FILENAME_TOO_LONG;
2327 }
2328 else
2329 {
2330 rc = VERR_NO_MORE_FILES;
2331 LogFlowFunc(("no more files\n"));
2332 pThis->fNoMoreFiles = true;
2333 }
2334 }
2335
2336 return rc;
2337}
2338
2339
2340/**
2341 * EXT directory operations.
2342 */
2343static const RTVFSDIROPS g_rtFsExtDirOps =
2344{
2345 { /* Obj */
2346 RTVFSOBJOPS_VERSION,
2347 RTVFSOBJTYPE_DIR,
2348 "EXT Dir",
2349 rtFsExtDir_Close,
2350 rtFsExtDir_QueryInfo,
2351 NULL,
2352 RTVFSOBJOPS_VERSION
2353 },
2354 RTVFSDIROPS_VERSION,
2355 0,
2356 { /* ObjSet */
2357 RTVFSOBJSETOPS_VERSION,
2358 RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
2359 rtFsExtDir_SetMode,
2360 rtFsExtDir_SetTimes,
2361 rtFsExtDir_SetOwner,
2362 RTVFSOBJSETOPS_VERSION
2363 },
2364 rtFsExtDir_Open,
2365 NULL /* pfnFollowAbsoluteSymlink */,
2366 NULL /* pfnOpenFile */,
2367 NULL /* pfnOpenDir */,
2368 rtFsExtDir_CreateDir,
2369 rtFsExtDir_OpenSymlink,
2370 rtFsExtDir_CreateSymlink,
2371 NULL /* pfnQueryEntryInfo */,
2372 rtFsExtDir_UnlinkEntry,
2373 rtFsExtDir_RenameEntry,
2374 rtFsExtDir_RewindDir,
2375 rtFsExtDir_ReadDir,
2376 RTVFSDIROPS_VERSION,
2377};
2378
2379
2380/**
2381 * Opens a directory by the given inode.
2382 *
2383 * @returns IPRT status code.
2384 * @param pThis The ext volume instance.
2385 * @param iInode The inode to open.
2386 * @param phVfsDir Where to store the handle to the VFS directory on success.
2387 */
2388static int rtFsExtVol_OpenDirByInode(PRTFSEXTVOL pThis, uint32_t iInode, PRTVFSDIR phVfsDir)
2389{
2390 PRTFSEXTINODE pInode = NULL;
2391 int rc = rtFsExtInodeLoad(pThis, iInode, &pInode);
2392 if (RT_SUCCESS(rc))
2393 {
2394 if (RTFS_IS_DIRECTORY(pInode->ObjInfo.Attr.fMode))
2395 {
2396 PRTFSEXTDIR pNewDir;
2397 rc = RTVfsNewDir(&g_rtFsExtDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf, NIL_RTVFSLOCK,
2398 phVfsDir, (void **)&pNewDir);
2399 if (RT_SUCCESS(rc))
2400 {
2401 pNewDir->fNoMoreFiles = false;
2402 pNewDir->pVol = pThis;
2403 pNewDir->pInode = pInode;
2404 }
2405 }
2406 else
2407 rc = VERR_VFS_BOGUS_FORMAT;
2408
2409 if (RT_FAILURE(rc))
2410 rtFsExtInodeRelease(pThis, pInode);
2411 }
2412
2413 return rc;
2414}
2415
2416
2417
2418/*
2419 *
2420 * Volume level code.
2421 * Volume level code.
2422 * Volume level code.
2423 *
2424 */
2425
2426/**
2427 * Checks whether the block range in the given block group is in use by checking the
2428 * block bitmap.
2429 *
2430 * @returns Flag whether the range is in use.
2431 * @param pBlkGrpDesc The block group to check for.
2432 * @param iBlockStart The starting block to check relative from the beginning of the block group.
2433 * @param cBlocks How many blocks to check.
2434 */
2435static bool rtFsExtIsBlockRangeInUse(PRTFSEXTBLKGRP pBlkGrpDesc, uint64_t iBlockStart, size_t cBlocks)
2436{
2437 /** @todo Optimize with ASMBitFirstSet(). */
2438 while (cBlocks)
2439 {
2440 uint32_t idxByte = iBlockStart / 8;
2441 uint32_t iBit = iBlockStart % 8;
2442
2443 if (pBlkGrpDesc->abBlockBitmap[idxByte] & RT_BIT(iBit))
2444 return true;
2445
2446 cBlocks--;
2447 iBlockStart++;
2448 }
2449
2450 return false;
2451}
2452
2453
2454static DECLCALLBACK(int) rtFsExtVolBlockGroupTreeDestroy(PAVLU32NODECORE pCore, void *pvUser)
2455{
2456 RT_NOREF(pvUser);
2457
2458 PRTFSEXTBLKGRP pBlockGroup = (PRTFSEXTBLKGRP)pCore;
2459 Assert(!pBlockGroup->cRefs);
2460 RTMemFree(pBlockGroup);
2461 return VINF_SUCCESS;
2462}
2463
2464
2465static DECLCALLBACK(int) rtFsExtVolInodeTreeDestroy(PAVLU32NODECORE pCore, void *pvUser)
2466{
2467 RT_NOREF(pvUser);
2468
2469 PRTFSEXTINODE pInode = (PRTFSEXTINODE)pCore;
2470 Assert(!pInode->cRefs);
2471 RTMemFree(pInode);
2472 return VINF_SUCCESS;
2473}
2474
2475
2476static DECLCALLBACK(int) rtFsExtVolBlockTreeDestroy(PAVLU64NODECORE pCore, void *pvUser)
2477{
2478 RT_NOREF(pvUser);
2479
2480 PRTFSEXTBLOCKENTRY pBlock = (PRTFSEXTBLOCKENTRY)pCore;
2481 Assert(!pBlock->cRefs);
2482 RTMemFree(pBlock);
2483 return VINF_SUCCESS;
2484}
2485
2486
2487/**
2488 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
2489 */
2490static DECLCALLBACK(int) rtFsExtVol_Close(void *pvThis)
2491{
2492 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
2493
2494 /* Destroy the block group tree. */
2495 RTAvlU32Destroy(&pThis->BlockGroupRoot, rtFsExtVolBlockGroupTreeDestroy, pThis);
2496 pThis->BlockGroupRoot = NULL;
2497 RTListInit(&pThis->LstBlockGroupLru);
2498
2499 /* Destroy the inode tree. */
2500 RTAvlU32Destroy(&pThis->InodeRoot, rtFsExtVolInodeTreeDestroy, pThis);
2501 pThis->InodeRoot = NULL;
2502 RTListInit(&pThis->LstInodeLru);
2503
2504 /* Destroy the block cache tree. */
2505 RTAvlU64Destroy(&pThis->BlockRoot, rtFsExtVolBlockTreeDestroy, pThis);
2506 pThis->BlockRoot = NULL;
2507 RTListInit(&pThis->LstBlockLru);
2508
2509 /*
2510 * Backing file and handles.
2511 */
2512 RTVfsFileRelease(pThis->hVfsBacking);
2513 pThis->hVfsBacking = NIL_RTVFSFILE;
2514 pThis->hVfsSelf = NIL_RTVFS;
2515
2516 return VINF_SUCCESS;
2517}
2518
2519
2520/**
2521 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
2522 */
2523static DECLCALLBACK(int) rtFsExtVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
2524{
2525 RT_NOREF(pvThis, pObjInfo, enmAddAttr);
2526 return VERR_WRONG_TYPE;
2527}
2528
2529
2530/**
2531 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnOpenRoot}
2532 */
2533static DECLCALLBACK(int) rtFsExtVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
2534{
2535 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
2536 int rc = rtFsExtVol_OpenDirByInode(pThis, EXT_INODE_NR_ROOT_DIR, phVfsDir);
2537 LogFlowFunc(("returns %Rrc\n", rc));
2538 return rc;
2539}
2540
2541
2542/**
2543 * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryRangeState}
2544 */
2545static DECLCALLBACK(int) rtFsExtVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
2546{
2547 int rc = VINF_SUCCESS;
2548 PRTFSEXTVOL pThis = (PRTFSEXTVOL)pvThis;
2549
2550 *pfUsed = false;
2551
2552 uint64_t iBlock = rtFsExtDiskOffsetToBlockIdx(pThis, off);
2553 uint64_t cBlocks = rtFsExtDiskOffsetToBlockIdx(pThis, cb) + (cb % pThis->cbBlock ? 1 : 0);
2554 while (cBlocks > 0)
2555 {
2556 uint32_t const iBlockGroup = iBlock / pThis->cBlocksPerGroup;
2557 uint32_t const iBlockRelStart = iBlock - iBlockGroup * pThis->cBlocksPerGroup;
2558 PRTFSEXTBLKGRP pBlockGroup = NULL;
2559
2560 rc = rtFsExtBlockGroupLoad(pThis, iBlockGroup, &pBlockGroup);
2561 if (RT_FAILURE(rc))
2562 break;
2563
2564 uint64_t cBlocksThis = RT_MIN(cBlocks, iBlockRelStart - pThis->cBlocksPerGroup);
2565 if (rtFsExtIsBlockRangeInUse(pBlockGroup, iBlockRelStart, cBlocksThis))
2566 {
2567 *pfUsed = true;
2568 break;
2569 }
2570
2571 rtFsExtBlockGroupRelease(pThis, pBlockGroup);
2572 cBlocks -= cBlocksThis;
2573 iBlock += cBlocksThis;
2574 }
2575
2576 return rc;
2577}
2578
2579
2580DECL_HIDDEN_CONST(const RTVFSOPS) g_rtFsExtVolOps =
2581{
2582 /* .Obj = */
2583 {
2584 /* .uVersion = */ RTVFSOBJOPS_VERSION,
2585 /* .enmType = */ RTVFSOBJTYPE_VFS,
2586 /* .pszName = */ "ExtVol",
2587 /* .pfnClose = */ rtFsExtVol_Close,
2588 /* .pfnQueryInfo = */ rtFsExtVol_QueryInfo,
2589 /* .pfnQueryInfoEx = */ NULL,
2590 /* .uEndMarker = */ RTVFSOBJOPS_VERSION
2591 },
2592 /* .uVersion = */ RTVFSOPS_VERSION,
2593 /* .fFeatures = */ 0,
2594 /* .pfnOpenRoot = */ rtFsExtVol_OpenRoot,
2595 /* .pfnQueryRangeState = */ rtFsExtVol_QueryRangeState,
2596 /* .uEndMarker = */ RTVFSOPS_VERSION
2597};
2598
2599
2600
2601/**
2602 * Loads the parameters from the given original ext2 format superblock (EXT_SB_REV_ORIG).
2603 *
2604 * @returns IPRT status code.
2605 * @param pThis The ext volume instance.
2606 * @param pSb The superblock to load.
2607 * @param pErrInfo Where to return additional error info.
2608 */
2609static int rtFsExtVolLoadAndParseSuperBlockV0(PRTFSEXTVOL pThis, PCEXTSUPERBLOCK pSb, PRTERRINFO pErrInfo)
2610{
2611 RT_NOREF(pErrInfo);
2612
2613 /*
2614 * Linux never supported a differing cluster (also called fragment) size for
2615 * the original ext2 layout so we reject such filesystems as it is not clear what
2616 * the purpose is really.
2617 */
2618 if (RT_LE2H_U32(pSb->cLogBlockSize) != RT_LE2H_U32(pSb->cLogClusterSize))
2619 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem cluster and block size differ");
2620
2621 pThis->f64Bit = false;
2622 pThis->cBlockShift = 10 + RT_LE2H_U32(pSb->cLogBlockSize);
2623 pThis->cbBlock = UINT64_C(1) << pThis->cBlockShift;
2624 pThis->cbInode = sizeof(EXTINODE);
2625 pThis->cbBlkGrpDesc = sizeof(EXTBLOCKGROUPDESC32);
2626 pThis->cBlocksPerGroup = RT_LE2H_U32(pSb->cBlocksPerGroup);
2627 pThis->cInodesPerGroup = RT_LE2H_U32(pSb->cInodesPerBlockGroup);
2628 pThis->cBlockGroups = RT_LE2H_U32(pSb->cBlocksTotalLow) / pThis->cBlocksPerGroup;
2629 pThis->cbBlockBitmap = pThis->cBlocksPerGroup / 8;
2630 if (pThis->cBlocksPerGroup % 8)
2631 pThis->cbBlockBitmap++;
2632 pThis->cbInodeBitmap = pThis->cInodesPerGroup / 8;
2633 if (pThis->cInodesPerGroup % 8)
2634 pThis->cbInodeBitmap++;
2635
2636 return VINF_SUCCESS;
2637}
2638
2639
2640/**
2641 * Loads the parameters from the given ext superblock (EXT_SB_REV_V2_DYN_INODE_SZ).
2642 *
2643 * @returns IPRT status code.
2644 * @param pThis The ext volume instance.
2645 * @param pSb The superblock to load.
2646 * @param pErrInfo Where to return additional error info.
2647 */
2648static int rtFsExtVolLoadAndParseSuperBlockV1(PRTFSEXTVOL pThis, PCEXTSUPERBLOCK pSb, PRTERRINFO pErrInfo)
2649{
2650 if ((RT_LE2H_U32(pSb->fFeaturesIncompat) & ~RTFSEXT_INCOMPAT_FEATURES_SUPP) != 0)
2651 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains unsupported incompatible features: %RX32",
2652 RT_LE2H_U32(pSb->fFeaturesIncompat) & ~RTFSEXT_INCOMPAT_FEATURES_SUPP);
2653 if ( RT_LE2H_U32(pSb->fFeaturesCompatRo) != 0
2654 && !(pThis->fMntFlags & RTVFSMNT_F_READ_ONLY))
2655 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains unsupported readonly features: %RX32",
2656 RT_LE2H_U32(pSb->fFeaturesCompatRo));
2657
2658 pThis->fFeaturesIncompat = RT_LE2H_U32(pSb->fFeaturesIncompat);
2659 pThis->f64Bit = RT_BOOL(pThis->fFeaturesIncompat & EXT_SB_FEAT_INCOMPAT_64BIT);
2660 pThis->cBlockShift = 10 + RT_LE2H_U32(pSb->cLogBlockSize);
2661 pThis->cbBlock = UINT64_C(1) << pThis->cBlockShift;
2662 pThis->cbInode = RT_LE2H_U16(pSb->cbInode);
2663 pThis->cbBlkGrpDesc = pThis->f64Bit ? RT_LE2H_U16(pSb->cbGroupDesc) : sizeof(EXTBLOCKGROUPDESC32);
2664 pThis->cBlocksPerGroup = RT_LE2H_U32(pSb->cBlocksPerGroup);
2665 pThis->cInodesPerGroup = RT_LE2H_U32(pSb->cInodesPerBlockGroup);
2666 pThis->cBlockGroups = RT_LE2H_U32(pSb->cBlocksTotalLow) / pThis->cBlocksPerGroup;
2667 pThis->cbBlockBitmap = pThis->cBlocksPerGroup / 8;
2668 if (pThis->cBlocksPerGroup % 8)
2669 pThis->cbBlockBitmap++;
2670 pThis->cbInodeBitmap = pThis->cInodesPerGroup / 8;
2671 if (pThis->cInodesPerGroup % 8)
2672 pThis->cbInodeBitmap++;
2673
2674 return VINF_SUCCESS;
2675}
2676
2677
2678/**
2679 * Loads and parses the superblock of the filesystem.
2680 *
2681 * @returns IPRT status code.
2682 * @param pThis The ext volume instance.
2683 * @param pErrInfo Where to return additional error info.
2684 */
2685static int rtFsExtVolLoadAndParseSuperblock(PRTFSEXTVOL pThis, PRTERRINFO pErrInfo)
2686{
2687 int rc = VINF_SUCCESS;
2688 EXTSUPERBLOCK Sb;
2689 rc = RTVfsFileReadAt(pThis->hVfsBacking, EXT_SB_OFFSET, &Sb, sizeof(EXTSUPERBLOCK), NULL);
2690 if (RT_FAILURE(rc))
2691 return RTERRINFO_LOG_SET(pErrInfo, rc, "Error reading super block");
2692
2693 /* Validate the superblock. */
2694 if (RT_LE2H_U16(Sb.u16Signature) != EXT_SB_SIGNATURE)
2695 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_VFS_UNKNOWN_FORMAT, "Not EXT - Signature mismatch: %RX16", RT_LE2H_U16(Sb.u16Signature));
2696
2697#ifdef LOG_ENABLED
2698 rtFsExtSb_Log(&Sb);
2699#endif
2700
2701 if (RT_LE2H_U16(Sb.u16FilesystemState) == EXT_SB_STATE_ERRORS)
2702 return RTERRINFO_LOG_SET(pErrInfo, VERR_VFS_UNSUPPORTED_FORMAT, "EXT filesystem contains errors");
2703
2704 if (RT_LE2H_U32(Sb.u32RevLvl) == EXT_SB_REV_ORIG)
2705 rc = rtFsExtVolLoadAndParseSuperBlockV0(pThis, &Sb, pErrInfo);
2706 else
2707 rc = rtFsExtVolLoadAndParseSuperBlockV1(pThis, &Sb, pErrInfo);
2708
2709 return rc;
2710}
2711
2712
2713RTDECL(int) RTFsExtVolOpen(RTVFSFILE hVfsFileIn, uint32_t fMntFlags, uint32_t fExtFlags, PRTVFS phVfs, PRTERRINFO pErrInfo)
2714{
2715 AssertPtrReturn(phVfs, VERR_INVALID_POINTER);
2716 AssertReturn(!(fMntFlags & ~RTVFSMNT_F_VALID_MASK), VERR_INVALID_FLAGS);
2717 AssertReturn(!fExtFlags, VERR_INVALID_FLAGS);
2718
2719 uint32_t cRefs = RTVfsFileRetain(hVfsFileIn);
2720 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
2721
2722 /*
2723 * Create a VFS instance and initialize the data so rtFsExtVol_Close works.
2724 */
2725 RTVFS hVfs;
2726 PRTFSEXTVOL pThis;
2727 int rc = RTVfsNew(&g_rtFsExtVolOps, sizeof(*pThis), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
2728 if (RT_SUCCESS(rc))
2729 {
2730 pThis->hVfsBacking = hVfsFileIn;
2731 pThis->hVfsSelf = hVfs;
2732 pThis->fMntFlags = fMntFlags;
2733 pThis->fExtFlags = fExtFlags;
2734 pThis->BlockGroupRoot = NULL;
2735 pThis->InodeRoot = NULL;
2736 pThis->BlockRoot = NULL;
2737 pThis->cbBlockGroups = 0;
2738 pThis->cbInodes = 0;
2739 pThis->cbBlocks = 0;
2740 RTListInit(&pThis->LstBlockGroupLru);
2741 RTListInit(&pThis->LstInodeLru);
2742 RTListInit(&pThis->LstBlockLru);
2743
2744 rc = RTVfsFileQuerySize(pThis->hVfsBacking, &pThis->cbBacking);
2745 if (RT_SUCCESS(rc))
2746 {
2747 rc = rtFsExtVolLoadAndParseSuperblock(pThis, pErrInfo);
2748 if (RT_SUCCESS(rc))
2749 {
2750 *phVfs = hVfs;
2751 return VINF_SUCCESS;
2752 }
2753 }
2754
2755 RTVfsRelease(hVfs);
2756 *phVfs = NIL_RTVFS;
2757 }
2758 else
2759 RTVfsFileRelease(hVfsFileIn);
2760
2761 return rc;
2762}
2763
2764
2765/**
2766 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
2767 */
2768static DECLCALLBACK(int) rtVfsChainExtVol_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
2769 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
2770{
2771 RT_NOREF(pProviderReg);
2772
2773 /*
2774 * Basic checks.
2775 */
2776 if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
2777 return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
2778 if ( pElement->enmType != RTVFSOBJTYPE_VFS
2779 && pElement->enmType != RTVFSOBJTYPE_DIR)
2780 return VERR_VFS_CHAIN_ONLY_DIR_OR_VFS;
2781 if (pElement->cArgs > 1)
2782 return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
2783
2784 /*
2785 * Parse the flag if present, save in pElement->uProvider.
2786 */
2787 bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
2788 if (pElement->cArgs > 0)
2789 {
2790 const char *psz = pElement->paArgs[0].psz;
2791 if (*psz)
2792 {
2793 if (!strcmp(psz, "ro"))
2794 fReadOnly = true;
2795 else if (!strcmp(psz, "rw"))
2796 fReadOnly = false;
2797 else
2798 {
2799 *poffError = pElement->paArgs[0].offSpec;
2800 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
2801 }
2802 }
2803 }
2804
2805 pElement->uProvider = fReadOnly ? RTVFSMNT_F_READ_ONLY : 0;
2806 return VINF_SUCCESS;
2807}
2808
2809
2810/**
2811 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
2812 */
2813static DECLCALLBACK(int) rtVfsChainExtVol_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
2814 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
2815 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
2816{
2817 RT_NOREF(pProviderReg, pSpec, poffError);
2818
2819 int rc;
2820 RTVFSFILE hVfsFileIn = RTVfsObjToFile(hPrevVfsObj);
2821 if (hVfsFileIn != NIL_RTVFSFILE)
2822 {
2823 RTVFS hVfs;
2824 rc = RTFsExtVolOpen(hVfsFileIn, (uint32_t)pElement->uProvider, (uint32_t)(pElement->uProvider >> 32), &hVfs, pErrInfo);
2825 RTVfsFileRelease(hVfsFileIn);
2826 if (RT_SUCCESS(rc))
2827 {
2828 *phVfsObj = RTVfsObjFromVfs(hVfs);
2829 RTVfsRelease(hVfs);
2830 if (*phVfsObj != NIL_RTVFSOBJ)
2831 return VINF_SUCCESS;
2832 rc = VERR_VFS_CHAIN_CAST_FAILED;
2833 }
2834 }
2835 else
2836 rc = VERR_VFS_CHAIN_CAST_FAILED;
2837 return rc;
2838}
2839
2840
2841/**
2842 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
2843 */
2844static DECLCALLBACK(bool) rtVfsChainExtVol_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
2845 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
2846 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
2847{
2848 RT_NOREF(pProviderReg, pSpec, pReuseSpec);
2849 if ( pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider
2850 || !pReuseElement->paArgs[0].uProvider)
2851 return true;
2852 return false;
2853}
2854
2855
2856/** VFS chain element 'ext'. */
2857static RTVFSCHAINELEMENTREG g_rtVfsChainExtVolReg =
2858{
2859 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
2860 /* fReserved = */ 0,
2861 /* pszName = */ "ext",
2862 /* ListEntry = */ { NULL, NULL },
2863 /* pszHelp = */ "Open a EXT file system, requires a file object on the left side.\n"
2864 "First argument is an optional 'ro' (read-only) or 'rw' (read-write) flag.\n",
2865 /* pfnValidate = */ rtVfsChainExtVol_Validate,
2866 /* pfnInstantiate = */ rtVfsChainExtVol_Instantiate,
2867 /* pfnCanReuseElement = */ rtVfsChainExtVol_CanReuseElement,
2868 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
2869};
2870
2871RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainExtVolReg, rtVfsChainExtVolReg);
2872
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette