VirtualBox

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

Last change on this file was 100908, checked in by vboxsync, 9 months ago

IPRT,Storage,Puel: Changed the pfnRead and pfnWrite VFS methods and the RTVfsIoStrmSgRead, RTVfsIoStrmSgWrite, RTVfsFileSgRead and RTVfsFileSgWrite APIs to advance pSgBuf and respect the incoming position just like RTFileSgRead & RTFileSgWrite.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use